NeXT Computers Forum Index NeXT Computers
www.NeXTComputers.org
 
Log in to check your private messagesLog in to check your private messages

Log inLog in  RegisterRegister


Profile  Search  Memberlist  FAQ  Usergroups
Undelete or file recovery software?

 
Post new topic   Reply to topic    NeXT Computers Forum Index -> NEXTSTEP / OPENSTEP Software
View previous topic :: View next topic  
Author Message
barcher174



Joined: 07 Dec 2012
Posts: 558

PostPosted: Fri May 08, 2015 5:22 pm    Post subject: Undelete or file recovery software? Reply with quote

Does there exist any kind of undelete software for nextstep?

Thanks,
Brian
Back to top
View user's profile Send private message
nuss



Joined: 27 Apr 2006
Posts: 40
Location: Germany

PostPosted: Sat May 09, 2015 12:37 am    Post subject: Reply with quote

Hi Brian,
the saying in comp.sys.next usegroups was that it is not possible in a senseful way to recover files on NeXTstep (UNIX).
Some examples:
Undelete utility needed
UNDELETE? (directory is now lost!)

But in 1991 it was Mike Morton who has posted a recovery program into the public domain:
Recovering deleted files: saga and code

Please read the whole posting and take Mike's warning serious:
"Here's the program. Use it as you see fit, but make sure you know what
you're doing, since this is currently a completely special-purpose,
one-night job. No idea how it will work on any other machines, but
I'm placing it in the public domain in hopes that someone will think
about writing a better scavenger."


Cheers, Nuss
Back to top
View user's profile Send private message
nuss



Joined: 27 Apr 2006
Posts: 40
Location: Germany

PostPosted: Sat May 09, 2015 12:40 am    Post subject: Reply with quote

Mike's original code:
Code:

/*  Recover unallocated blocks from a file system and store them
    in a specified directory, which should not be on the same file
    system.  We attempt to select only blocks with ASCII; others
    might save everything, or have another criterion.

    No guarantees at all are made for this code.

    Mike Morton, 11 June 91
    Based very heavily on code from bp at NeXT
*/

/*  If you use the same handleBlock() code, this is the threshhold below
    which a block is assumed to be garbage.  We don't insist on 100% vanilla
    ASCII because some text files may contain non-ASCII stuff (ellipsis,
    curly quotes, etc).
*/
#define GOODPCT 0.90                    /* assume this %, or more, ASCII means worth saving */

#include  <stdio.h>
#include  <sys/file.h>
#include  <sys/param.h>
#include  <ufs/fs.h>

#define FRAGSIZE (MAXBSIZE/8)           /* size of one sub-block (better name for this?) */

static char *dirName = 0;               /* where to store output */

/*  save -- Write some data to a new file "filename" inside the directory
    specified in the command line args.
*/
static void save (filename, data, length)
  char *filename;                       /* INPUT: name to save under */
  unsigned char *data;                  /* INPUT: stuff to save */
  unsigned long length;                 /* INPUT: length of stuff to save */
{ char pathname [200];
  FILE *f;

  /* printf (filename); printf ("\n"); return; */

  strcpy (pathname, dirName);           /* get dir      */
  strcat (pathname, "/");               /* get dir/     */
  strcat (pathname, filename);          /* get dir/file */

#if 0 /* useful for writing out only some stuff during testing */
{ static long counter = 0;
  if ((counter++ % 200) != 0) return;
  printf ("\nsaving: %s", pathname);
}
#endif

  f = fopen (pathname, "w+");
  if (! f) {  printf ("Couldn't open %s\n", pathname); return; }
  if (fwrite (data, 1, length, f) != length)
    printf ("Write failed!\n", pathname);
  fclose (f);
}                                       /* end of save () */

/*  handleBlock -- This is called from findFreeData().  We're passed a partly or completely
    free block which has been read in, and write out either the block, fragments of it, or
    both.

    If you're trying to recover some other kind of data (other than ASCII), you want to
    change this function to save different stuff when findFreeData() calls it.
*/
static void handleBlock (block, blkNum, freeInfo)
  unsigned char *block;                 /* INPUT: pointer to MAXBSIZE bytes of data */
  unsigned long blkNum;                 /* INPUT: block number on disk */
  unsigned char freeInfo;               /* INPUT: bit-coded info for eight fragments of block */
                                        /* one-bits mean the frag is free (hence, interesting) */
{ unsigned int fragNum;                 /* ranges 0..7, for frags of the block */
  register unsigned char *subBlock;     /* one frag of data */
  register unsigned short count;
  register unsigned long goodChars;     /* count of apparently-ASCII chars in fragment or block */
  unsigned char fragBit;                /* bit compared against "freeInfo" */
  char filename [200];

  /*  If the 8-frag block is entirely free, write it out if it looks good.  We'll do the
      8-frag breakup below as well, if it seems good from that p.o.v. */
  if (freeInfo == 0xff)
  { subBlock = block;                   /* set up a working pointer... */
    count = MAXBSIZE;                   /* ...and a loop counter */
    goodChars = 0;
    /*  My hard disk appears to initialize a lot of stuff with many 0x04s, so use 0x05: */
    while (count--)
    { if ((*subBlock > 0x05)
          && ((*subBlock & 0x80) == 0)) /* appears to be ASCII? */
        goodChars++;
      subBlock++;
    }

    /*  If we beat the quota, save this stuff: */
    if (goodChars >= (GOODPCT * MAXBSIZE))
    { sprintf (filename, "Block-%03d-%ld", goodChars*100/MAXBSIZE, blkNum);
      save (filename, block, MAXBSIZE);
return; /* HACK -- save space and don't fragment blocks we already write out whole */
    }
  }                                     /* end of handling all-free block */

  /*  Do the same thing for each fragment. */
  for (fragNum = 0; fragNum <= 7; fragNum++)
  { fragBit = (0x80 >> fragNum);        /* bits run left-to-right (lower blocks are lefterly) */

    if (freeInfo & fragBit)             /* interesting fragment? */
    { subBlock = block + (fragNum * FRAGSIZE);  /* point to this frag of the block */

      count = FRAGSIZE;
      goodChars = 0;
      while (count--)                   /* tally the fragment */
      { if ((*subBlock > 0x05)
            && ((*subBlock & 0x80) == 0)) /* appears to be ASCII? */
          goodChars++;
        subBlock++;
      }

      /*  If we beat the quota, save this stuff: */
      if (goodChars >= (GOODPCT * FRAGSIZE))
      { sprintf (filename, "Frag-%03d-%ld-%d", goodChars*100/FRAGSIZE, blkNum, fragNum);
        subBlock = block + (fragNum * FRAGSIZE);  /* point (again) to this frag of the block */
        save (filename, subBlock, FRAGSIZE);
      }
    }                                   /* end of handling interesting frag */
  }                                     /* end of loop through frags */
}                                       /* end of handleBlock () */

/*  bread -- Read a block (specified by 1K-block number, not byte address). */
bread (fd, blk, buf, size)
  int fd;                               /* INPUT: file to read from */
  daddr_t blk;                          /* INPUT: block to start at */
  char *buf;                            /* OUTPUT: where to read to */
  int size;                             /* INPUT: how much to read */
{ if (lseek (fd, (long) dbtob (blk), 0) < 0)
  { fprintf(stderr, "bread: lseek error.\n");
    perror("lseek");
    return 1;
  }

  if (read (fd, buf, size) != size)
  { fprintf(stderr, "bread: read error.\n");
    perror("read");
    return 1;
  }

  return 0;
}                                       /* end of bread () */

/*  findFreeData -- Find all the free or partly-free blocks in a file system, and
    pass them to "func".
*/
void findFreeData (filesys, func)
  char *filesys;                        /* INPUT: pathname of file system */
  void (* func) ();                     /* INPUT: function to call with blocks */
{ int rawFd;                            /* file descriptor for raw device */
  char slop[MAXBSIZE];                  /* big enough to read a whole block */
  struct fs *sblock = (struct fs *) slop; /* superblock, overlaid on buffer */
  unsigned int cgNum;                   /* number of a cylinder group */
  char cgSlop[MAXBSIZE];                /* big enough to read a whole block */
  struct cg* theCg = (struct cg*) cgSlop; /* cylinder group struct, overlaid on buffer */
  unsigned char *freeMapPtr;            /* pointer into free-block list */
  unsigned long blk;                    /* index into same */
  char blockData [MAXBSIZE];            /* data from one block */
  unsigned long totBlksRead = 0;

  /*  Open the device */
  if ((rawFd = open(filesys, O_RDONLY, 0)) < 0)
    { perror("open");
      fprintf(stderr, "findFreeData: cannot open %s.\n", filesys);
      exit(1);
    }
  sync ();                              /* get the disk up to date */

  /*  Read, check, and chat about the superblock. */
  if (bread (rawFd, SBLOCK, sblock, SBSIZE))
    { fprintf(stderr, "Error reading superblock.\n"); exit(1); }
  if (sblock->fs_magic != FS_MAGIC)
  { fprintf(stderr, "Bad superblock magic number.\n");
    exit(1);
  }
  printf ("Free blocks %ld\n",  sblock->fs_cstotal.cs_nbfree);
  printf ("Free frags %ld\n",   sblock->fs_cstotal.cs_nffree);

  /*  Loop through all cylinder groups. */
  /*  @@@ Why must we use -1 in sblock->fs_ncg-1?  We get lots of errors without this... */
  for (cgNum = 0; cgNum < sblock->fs_ncg-1; cgNum++)
  { printf ("CYLINDER GROUP #%d\n\n", cgNum); fflush (stdout); /* help impatient humans */

    if (bread (rawFd, (daddr_t) cgtod (sblock, cgNum), theCg, MAXBSIZE))
      { printf ("Read of cgtod failed.\n"); return; }
    if (theCg->cg_magic != CG_MAGIC)
      { printf ("Magic constant in cylinder group info is wrong!\n"); return; }
    if (theCg->cg_cgx != cgNum)
      { printf ("Cylinder number in cylinder group info is wrong!\n"); return; }

    /*  Walk the array of free blocks at the end of the cylinder block.  Each byte in
        the array is bit-coded -- any '1' bits at all mean some of the block is free.
    */
    freeMapPtr = theCg->cg_free;
    for (blk = 0; blk < theCg->cg_ndblk; blk++)
    { if (freeMapPtr [blk])             /* nonzero value means partly or entirely free */
      { daddr_t absBlk;                 /* absolute block number on disk, suitable for bread() */

        absBlk = (8*blk) + cgdmin(sblock, cgNum); /* find start of 8K block */
        if (bread (rawFd, absBlk, blockData, MAXBSIZE))
          printf ("Read failed for block #%ld.\n", absBlk);
        else
        { ++totBlksRead;
          (* func) (blockData, absBlk,  freeMapPtr [blk]); /* pass block & free-info to whomever */
        }
      }                                 /* end of handling partly/entirely free block */
    }                                   /* end of loop through blocks */
  }                                     /* end of loop through cylinders */
  printf ("\nTotal blocks read: %ld.\n", totBlksRead);

  close(rawFd);
}                                       /* end of findFreeData () */


void main(argc, argv)
  int argc;
  char *argv[];
{ register int argNum = 0;              /* argument number */
  char *fileSystemName;

  argNum = 1;
  while (argNum < argc)
  { if (*argv[argNum] == '-')
    { switch (argv[argNum][1])
      { case 's':                       /* specify file System */
          if (++argNum == argc) usage(argv[0]); /* no more args? */
          fileSystemName = argv[argNum];
          break;

        case 'o':                       /* specify Output directory */
          if (++argNum == argc) usage(argv[0]); /* no more args? */
          dirName = argv[argNum];
          break;
        default:
          usage(argv[0]);
      }                                 /* end of switch on switch */
    }                                   /* end of handling "-" token */
    else usage(argv[0]);

    ++argNum;
  }                                     /* end of scanning args */

  if (fileSystemName == 0) usage(argv[0]); /* insist on a file system name */
  if (dirName == 0) usage(argv[0]);     /* insist on an output directory */

  /*  Pass every unallocated block or fragment to handleBlock() */
  findFreeData (fileSystemName, handleBlock);
}                                       /* end of main () */

/*  Print a usage message and die. */
usage(s)
  char *s;
{ printf("usage: %s -s filesystem -o outputdirectory\n", s);
  exit(1);
}
Back to top
View user's profile Send private message
nuss



Joined: 27 Apr 2006
Posts: 40
Location: Germany

PostPosted: Sat May 09, 2015 12:43 am    Post subject: Reply with quote

To compile Mike's code on OpenStep the following change was required:
Code:

131c131
< { if (lseek (fd, (long) dbtob (blk), 0) < 0)
---
> { if (lseek (fd, (long) dbtob (blk, size), 0) < 0)

Of course this is all fully untested and without any warranty!

PS: Sorry for the multi-post, but I could not find any forum feature to upload files or hide long text.
Back to top
View user's profile Send private message
barcher174



Joined: 07 Dec 2012
Posts: 558

PostPosted: Sat May 09, 2015 2:46 pm    Post subject: Reply with quote

Sorry if this is a dumb question but is this more effective than just running a 'cat' on the drive and then 'strings' on the output?

Thanks
Brian Archer
Back to top
View user's profile Send private message
gregwtmtno



Joined: 28 Aug 2011
Posts: 19

PostPosted: Sun May 10, 2015 7:15 pm    Post subject: Reply with quote

Have you tried something like photorec? It works directly on the data and ignores the filesystem. I don't see why it wouldn't work on a nextstep partition, though you'd probably access the drive from another computer. It does seem pretty portable though so you might even be able to compile it on NeXT.

http://www.cgsecurity.org/wiki/PhotoRec
Back to top
View user's profile Send private message
barcher174



Joined: 07 Dec 2012
Posts: 558

PostPosted: Sun May 10, 2015 7:36 pm    Post subject: Reply with quote

The problem is that I'm trying to do this on a MO disk so I have no way to mount it on a linux system.
Back to top
View user's profile Send private message
mikeboss



Joined: 07 Dec 2011
Posts: 365
Location: berne, switzerland

PostPosted: Mon May 11, 2015 12:42 am    Post subject: Reply with quote

I still have a canon L10132 (sans optical drive but of course including the SCSI converter board) I'm willing to part with...
_________________
October 12, 1988 Computing Advances To The NeXT Level
Back to top
View user's profile Send private message Visit poster's website
nuss



Joined: 27 Apr 2006
Posts: 40
Location: Germany

PostPosted: Mon May 11, 2015 8:23 am    Post subject: Reply with quote

Hi Brian,

I'd try to avoid direct recovery of the "defective" drive, but would make an image of the drive first.
Do you possibly have enough space to dd your MO disk to a harddrive?
Maybe this can remove requirement of a NeXT recovery tool.

As gregwtmtno wrote, when having a drive-image, I'd first try PhotoRec against the image (e.g. on Linux).
If PhotoRec fails on the image, you still can use cat/strings, or the posted recovery tool, without any harm to the MO drive.

Cheers, Nuss
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    NeXT Computers Forum Index -> NEXTSTEP / OPENSTEP Software All times are GMT - 7 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum



Powered by phpBB © 2017 phpBB Group