bob@reed.UUCP (Bob Ankeney) (04/10/91)
#!/bin/sh # shar: Shell Archiver (v1.22) # # Run the following text with /bin/sh to create: # README # FILES # NOTES # Makefile # defs.h # fs.h # idir.c # iname.c # io.c # ls.c # main.c # namei.c # restore.c # stat.c # sed 's/^X//' << 'SHAR_EOF' > README && X Fssal is a utility to salvage files from damaged BSD filesystems. XSuch damage might include a lost super-block, trashed cylinder group, Xdrive with bad blocks, or perhaps a filesystem with files that may get Xconsumed by a fsck. It requires the presence of a relatively intact Xfilesystem (an intact original or alternate super-block, some semblence Xof an inode-structure, and some intact cylinder-groups). Thus such Xthings as deleted files cannot be restored. X X The impetus for writing this program was to recover files from a Xfilesystem with the original super-block and first (of six) cylinder Xgroups walked on. What happened was a mkfs of a small system was Xaccidentally done, which wiped the initial data. Data from all other Xcylinder groups was still intact and recoverable. An alternate (spare) Xsuper-block is found within each cylinder group. X X All restored files are placed relative to the working directory, Xand all permissions, creation/modification times, and ownerships are Xrestored. Both real and symbolic links are handled, as well as Xcharacter and block special devices and sockets. X X At no time is the specified file-system written on, thus data is Xsafe. The specified file-system need not be mounted (and probably Xshouldn't be lest others modify it as a restore is in progress). X X The general usage is: X X fssal [-f device] [-s super-block] [-r root-inode] [-a] [-R] [-v] X X where: X -f device: is the file-system to use (do not use the raw device!!) X -s super-block: is an alternate super-block X -r root-inode: is an alternate directory inode to start at X X -a: stats all inodes in file-system and restores those that are X directories. The -R flag can be used to recursively walk each X sub-directory. Each directory found that has not yet been X restored will be created with the directory name '#inum', where X inum is the inode number of that directory. X X -R: recursively walk each sub-directory found. X X -v: verbose mode. Spews out lots of information as to what's been X restored. X X A link to fssal called fsls exists which does a directory listing on Xthe specified file system. The general usage is: X X fsls [-f device] [-s super-block] [-r root-inode] [-a] [-R] [-l] X X where: X -f device: is the file-system to use (do not use the raw device!!) X -s super-block: is an alternate super-block X -r root-inode: is an alternate directory inode to start at X X -a: stats all inodes in file-system and lists those that are X directories. The -R flag can be used to recursively walk each X sub-directory. X X -R: recursively walk each sub-directory found. X X -l: do a long directory list (like 'ls -liga') X X X Hopefully someone will take this under their wing and make improvements Xto it. Some suggestions are: X a) Make it interactive (ala restore -i). X b) Port it to other platforms; it has been used successfully on: X DEC VAX with Mt. Xinu X DEC VAX with Ultrix V2.3 X Sun-4 with SunOs V4.1 X Tektronix Utek V2.2 X c) A manual page. X X X Feel free to mail me any suggestions, bug fixes and enhancements. I'll Xtry to integrate them (it's been a while since I wrote this) and make occasional Xreleases. If it looks solid enough, I'll post to comp.sources.unix. X X Bob Ankeney X ...!tektronix!bob@reed.bitnet X SHAR_EOF chmod 0644 README || echo "restore of README fails" sed 's/^X//' << 'SHAR_EOF' > FILES && XREADME XFILES XNOTES XMakefile Xdefs.h Xfs.h Xidir.c Xiname.c Xio.c Xls.c Xmain.c Xnamei.c Xrestore.c Xstat.c SHAR_EOF chmod 0644 FILES || echo "restore of FILES fails" sed 's/^X//' << 'SHAR_EOF' > NOTES && XThings to check for: X X Error handling (failed reads especially). X X Freeing of malloced space. X XTo do: X X Read inode into buffer and get inode structures from it (fixes reads on X raw device). X X Cache ureads? (uread reads entire block and bcopies desired info). X Keep track of last block read to avoid duplicate reads. X SHAR_EOF chmod 0644 NOTES || echo "restore of NOTES fails" sed 's/^X//' << 'SHAR_EOF' > Makefile && X#CFLAGS = -g -DDEBUG XCFLAGS = -g -DBSD4_3 X XSRCS = io.c namei.c iname.c ls.c restore.c main.c stat.c idir.c XOBJS = io.o namei.o iname.o ls.o restore.o main.o stat.o idir.o X Xall: fssal fsls X Xfssal: $(OBJS) X rm -f fsls X cc $(CFLAGS) $(OBJS) -o fssal X Xfsls: fssal X ln fssal fsls X Xio.o: defs.h fs.h X Xnamei.o: defs.h fs.h X Xiname.o: defs.h fs.h X Xls.o: defs.h fs.h X Xrestore.o: defs.h fs.h X Xmain.o: defs.h fs.h X Xstat.o: defs.h fs.h X Xidir.o: defs.h fs.h X Xclean: X rm -f $(OBJS) X Xlint: X lint $(SRCS) X SHAR_EOF chmod 0644 Makefile || echo "restore of Makefile fails" sed 's/^X//' << 'SHAR_EOF' > defs.h && X X#define FS_TO_READ "/dev/dh60h" X X /* Maximum size for path name */ X#define MAX_PATH_LEN 128 X X /* Number of inode numbers per sturct idir segment */ X#define MAX_ICNT 32 X X#ifndef TRUE X#define TRUE (1) X#define FALSE (0) X#endif X SHAR_EOF chmod 0644 defs.h || echo "restore of defs.h fails" sed 's/^X//' << 'SHAR_EOF' > fs.h && X X#include <stdio.h> X#include <sys/types.h> X#include <sys/file.h> X#include <sys/param.h> X#ifdef BSD4_2 X#include <sys/fs.h> X#include <sys/inode.h> X#endif X#ifdef BSD4_3 X#include <sys/time.h> X#include <sys/vnode.h> X#include <ufs/fs.h> X#include <ufs/inode.h> X#endif X#include <sys/dir.h> X#include <sys/errno.h> X X#include "defs.h" X X/* X * itobo - Convert inode number to file system byte offset (for lseek). X * X * Converts inode to file system block (itod), then to device X * block (fsbtodb), multiplies by size of device block (DEV_BSIZE X * = 512), and adds offset into block of inode (itoo). X */ X#define itobo(x) (fsbtodb(&sb, itod(&sb, x)) * DEV_BSIZE + \ X itoo(&sb,x) * sizeof(struct icommon)) X X/* X * btobo - Convert file system block number to byte offset (for lseek). X */ X#define btobo(x) (fsbtodb(&sb, x) * DEV_BSIZE) X X#define ISIZE sizeof(struct icommon) X X X/* X * struct file_ptr is the structure returned from a uopen() call. X */ Xstruct file_ptr { X struct icommon *ip; /* Pointer to inode for file */ X long file_ptr; /* # bytes into file */ X long file_size; /* # bytes in file */ X}; X X/* X * struct iname_list is a linked list of paths returned by iname() call. X */ Xstruct iname_list { X char name[MAX_PATH_LEN]; X struct iname_list *next_iname; X}; X X/* X * struct idir is a linked list of arrays of directory inode numbers. X * inums[] is the array containing icnt inode numbers. next_idir X * points to the next list of numbers X */ Xstruct idir { X ino_t inums[MAX_ICNT]; X int icnt; X struct idir *next_idir; X}; X SHAR_EOF chmod 0644 fs.h || echo "restore of fs.h fails" sed 's/^X//' << 'SHAR_EOF' > idir.c && X X/* X * fssal - BSD file system salvage program X * X * Copyright 1989, Robert Ankeney X * Generic Computer Products X * 5315 S.W. 53rd Court X * Portland, Oregon 97221 X * (503) 244-3699 X * All Rights Reserved X * X * Distribution of any portion of this software as part of any product X * for sale is prohibited. X * X * Version 1.00 (Preliminary) X */ X X#include "fs.h" X#include <sys/stat.h> X X /* File descriptor filesystem is open on */ Xextern int fs_fd; X X /* struct holding super block */ Xextern struct fs sb; X X /* size of file system block (= 4096) */ X#define BLKSIZE sb.fs_bsize X X/* X * get_dir_inodes - stats inodes from from_inode to to_inode and, if X * a directory, adds to return_list. X */ Xget_dir_inodes(return_list, from_inode, to_inode) Xstruct idir **return_list; Xino_t from_inode, to_inode; X{ X struct stat statb; X struct idir *idir_list, *cur_ilist; X X ino_t i; X X new_idir(&idir_list); X cur_ilist = idir_list; X X for (i = from_inode; i < to_inode; i++) { X if (fs_stat (i, &statb)) { X printf ("error reading inode %d\n"); X break; X } X if ((statb.st_mode & S_IFMT) == S_IFDIR) { X /* Add inode number to list */ X add_dir_inode(&cur_ilist, i); X printf ("found directory at %d\n", i); X } X } X *return_list = idir_list; X} X X/* X * new_idir - makes new idir segment and returns pointer new_ptr X */ Xnew_idir(new_ptr) Xstruct idir **new_ptr; X{ X char *malloc(); X X *new_ptr = (struct idir *) malloc(sizeof(struct idir)); X (*new_ptr)->icnt = 0; X (*new_ptr)->next_idir = NULL; X} X X/* X * add_dir_inode - adds inode number to struct idir, mallocing a new X * one if needed. X */ Xadd_dir_inode(idir_ptr, inum) Xstruct idir **idir_ptr; Xino_t inum; X{ X if ((*idir_ptr)->icnt == MAX_ICNT) { X /* Current list full, add new segment */ X new_idir(&((*idir_ptr)->next_idir)); X *idir_ptr = (*idir_ptr)->next_idir; X } X (*idir_ptr)->inums[(*idir_ptr)->icnt++] = inum; X} X X/* X * in_idir - returns TRUE if inum is found in idir_ptr X */ Xin_idir(idir_ptr, inum) Xstruct idir *idir_ptr; Xino_t inum; X{ X struct idir *itmp_ptr; X X ino_t itmp; X X itmp_ptr = idir_ptr; X while (itmp_ptr) { X for (itmp = 0; itmp < itmp_ptr->icnt; itmp++) X if (itmp_ptr->inums[itmp] == inum) X return TRUE; X itmp_ptr = itmp_ptr->next_idir; X } X return FALSE; X} X SHAR_EOF chmod 0644 idir.c || echo "restore of idir.c fails" sed 's/^X//' << 'SHAR_EOF' > iname.c && X X/* X * fssal - BSD file system salvage program X * X * Copyright 1989, Robert Ankeney X * Generic Computer Products X * 5315 S.W. 53rd Court X * Portland, Oregon 97221 X * (503) 244-3699 X * All Rights Reserved X * X * Distribution of any portion of this software as part of any product X * for sale is prohibited. X * X * Version 1.00 (Preliminary) X */ X X#include "fs.h" X#include <sys/time.h> X X /* struct holding super block */ Xextern struct fs sb; X X /* File system block size */ X#define BLKSIZE sb.fs_bsize X X/* X * iname - generate list of file names based on given inode number. X * Given inode inum and maximum count icnt, returns linked list X * inames of path names and actual number found found_cnt. X * ipath is path name to inode istart, which is prepended to each X * iname. X * X * Used for determining links to regular files. X */ Xiname(ipath, istart, inum, icnt, inames, found_cnt) Xchar ipath[MAX_PATH_LEN]; Xino_t istart, inum; Xint icnt, *found_cnt; Xstruct iname_list **inames; X{ X *inames = NULL; X *found_cnt = 0; X if (ilist(ipath, istart, inum, icnt, inames, found_cnt)) X fprintf(stderr, "Inode %d is not a directory\n", inum); X} X Xilist(ipath, istart, inum, icnt, inames, found_cnt) Xchar ipath[MAX_PATH_LEN]; Xino_t istart, inum; Xint icnt, *found_cnt; Xstruct iname_list **inames; X{ X struct icommon fs_inode; /* Inode for directory */ X struct direct *fs_dir; /* Directory structure */ X struct file_ptr dir_ptr; /* Directory pointer */ X X char dir_buf[MAXBSIZE]; /* Directory buffer */ X int buf_cnt; /* # chars in buffer */ X int next_ent; /* Buf offset of next dir */ X X char cur_name[MAX_PATH_LEN]; /* Current directory pathname */ X X struct icommon file_inode; /* Inode for ls file */ X X static struct iname_list *cur_iname, *tmp_iname; X X char *strcpy(), *sprintf(), *strcat(); X char *malloc(); X X extern int errno; X X X /* Fetch inode */ X get_inode(istart, &fs_inode); X X /* Make sure it's a directory! */ X if ((fs_inode.ic_mode & IFMT) != IFDIR) { X errno = ENOTDIR; X return -1; X } X /* Open directory */ X uopen(&fs_inode, &dir_ptr); X X /* Read each directory block */ X do { X /* Read next block of directory */ X if ((buf_cnt = uread(&dir_ptr, dir_buf, BLKSIZE)) == -1) { X perror("Cannot read directory"); X exit(1); X } X X if (buf_cnt == 0) X break; X X /* For each file in directory, check inode # */ X next_ent = 0; X do { X /* Point to next directory entry */ X fs_dir = (struct direct *) &dir_buf[next_ent]; X X /* Directory entry here? */ X if (fs_dir->d_ino != 0) { X /* Get inode */ X get_inode(fs_dir->d_ino, &file_inode); X X /* Desired inode #? */ X if (fs_dir->d_ino == inum) { X /* Yep - Malloc new iname_list */ X tmp_iname = cur_iname; X cur_iname = (struct iname_list *) X malloc(sizeof(struct iname_list)); X cur_iname->next_iname = NULL; X if (tmp_iname) X tmp_iname->next_iname = cur_iname; X if (*inames == NULL) X *inames = cur_iname; X X /* Copy starting path */ X (void) strcpy(cur_iname->name, ipath); X (void) strcat(cur_iname->name, "/"); X /* Append actual file name */ X (void) strcat(cur_iname->name, fs_dir->d_name); X ++*found_cnt; X } X } X next_ent += fs_dir->d_reclen; X } while (next_ent < buf_cnt); X X } while (buf_cnt > 0); X X /* Open directory again */ X uopen(&fs_inode, &dir_ptr); X X /* Read each directory block */ X do { X /* Read next block of directory */ X if ((buf_cnt = uread(&dir_ptr, dir_buf, BLKSIZE)) == -1) { X perror("Cannot read directory"); X exit(1); X } X X if (buf_cnt == 0) X break; X X /* For each subdirectory in directory, check it */ X next_ent = 0; X do { X /* Point to next directory entry */ X fs_dir = (struct direct *) &dir_buf[next_ent]; X X /* Directory entry here? */ X if (fs_dir->d_ino != 0) { X /* Get inode */ X get_inode(fs_dir->d_ino, &file_inode); X X /* Make sure is dir and not . or .. */ X if ((file_inode.ic_mode & IFMT) == IFDIR) X if (strcmp(fs_dir->d_name, ".") && X strcmp(fs_dir->d_name, "..")) { X if (*ipath) X (void) sprintf(cur_name, "%s/%s", X ipath, fs_dir->d_name); X else X (void) strcpy(cur_name, fs_dir->d_name); X X /* Check this directory! */ X if (ilist(cur_name, fs_dir->d_ino, inum, X icnt - *found_cnt, inames, found_cnt)) { X fprintf(stderr, X "Inode %d is not a directory\n", inum); X return -1; X } X } X } X next_ent += fs_dir->d_reclen; X } while (next_ent < buf_cnt); X X } while (buf_cnt > 0); X return 0; X} SHAR_EOF chmod 0644 iname.c || echo "restore of iname.c fails" sed 's/^X//' << 'SHAR_EOF' > io.c && X X/* X * fssal - BSD file system salvage program X * X * Copyright 1989, Robert Ankeney X * Generic Computer Products X * 5315 S.W. 53rd Court X * Portland, Oregon 97221 X * (503) 244-3699 X * All Rights Reserved X * X * Distribution of any portion of this software as part of any product X * for sale is prohibited. X * X * Version 1.00 (Preliminary) X */ X X#include "fs.h" X X X /* File descriptor filesystem is open on */ Xextern int fs_fd; X X /* struct holding super block */ Xextern struct fs sb; X X /* size of file system block */ X#define BLKSIZE sb.fs_bsize X X/* X * getsb - read super block into struct sb X */ Xgetsb(fs_to_read, sb_block) Xchar *fs_to_read; Xint sb_block; X{ X long lseek(); X X /* Open file system */ X if ((fs_fd = open(fs_to_read, O_RDONLY)) == -1) { X perror("Cannot open filesystem"); X exit(1); X } X X /* Seek to super block */ X/* X (void) lseek(fs_fd, (long) BBSIZE, L_SET); X*/ X (void) lseek(fs_fd, (long) 512 * sb_block, L_SET); X X /* Read super block */ X if (read(fs_fd, (char *) &sb, sizeof(sb)) == -1) { X perror("Cannot read filesystem"); X exit(1); X } X X /* Check magic number */ X if (sb.fs_magic != FS_MAGIC) { X fprintf(stderr, "Bad magic number for super block\n"); X exit(1); X } X X} X X/* X * uopen - open file on unmounted file system. X * X * Input is a pointer to the icommon for the file to open, X * and a pointer (struct file_ptr) to the info to be X * returned. (Includes pointer to inode, index into file, X * and size of file). X */ Xuopen(ip, fp) Xstruct icommon *ip; Xstruct file_ptr *fp; X{ X fp->ip = ip; X fp->file_ptr = 0; X fp->file_size = fp->ip->ic_size.val[0]; X} X X/* X * uread - read data from uopened file. X * X * Input is pointer to struct file_ptr returned from uopen, X * pointer to buffer to place data in, and count of number X * of bytes to read. X * X * Note - Doesn't handle double-indirect blocks! X * (Gawd, I'm lazy!) X * X * Returns: X * -1 if no data was read, X * count if all data was transferred, X * else actual number of bytes transferred. X */ Xuread(fp, bp, count) Xstruct file_ptr *fp; Xchar *bp; Xlong count; X{ X /* # indirect pointers in an indirect block */ X#define NUM_IND_PTRS (BLKSIZE / sizeof(long)) X /* # bytes pointed to by an indirect block */ X#define IND_CNT (NUM_IND_PTRS * BLKSIZE) X X long direct_blk; /* Index into direct block numbers */ X long ind_ptr; /* Which byte in indirect block */ X int ind_blk; /* Which indirect pointer of NIADDR */ X int ind_off; /* Which indirect pointer in block */ Xstatic char ind_buf[MAXBSIZE]; /* Indirect block */ Xstatic int last_ind_blk; /* Last indirect block read */ X X int num_to_read; /* # bytes of block to read */ X int offset; /* Offset into block to start read */ X int nr; /* # bytes actually read */ X long file_cnt; /* # bytes left in file */ X long num_read; /* # bytes read so far */ X long block_to_read; /* Block # for read */ X X char *bufptr; /* Next byte in buffer to read into */ X X long lseek(); X X bufptr = bp; X num_read = 0; X X /* # bytes left in file */ X file_cnt = fp->file_size - fp->file_ptr; X X /* Limit count to size of file */ X if (count > file_cnt) X count = file_cnt; X X while (count != 0) { X X /* Get direct block # */ X direct_blk = fp->file_ptr / BLKSIZE; X X /* Into indirect blocks? */ X if (direct_blk >= NDADDR) { X ind_ptr = fp->file_ptr - NDADDR * BLKSIZE; X X /* Into double-indirect blocks? */ X if ((ind_blk = ind_ptr / IND_CNT) >= NIADDR) { X fprintf(stderr, "File too large\n"); X exit(1); X } X X ind_off = ind_ptr / BLKSIZE; X X if (last_ind_blk != fp->ip->ic_ib[ind_blk]) { X /* Read indirect block */ X#ifdef DEBUG X printf("Reading indirect block %d\n", X last_ind_blk); X#endif X last_ind_blk = fp->ip->ic_ib[ind_blk]; X (void) lseek(fs_fd, btobo(last_ind_blk), L_SET); X if (read(fs_fd, ind_buf, (int) BLKSIZE) == -1) { X perror("Cannot read indirect block"); X exit(1); X } X } X X block_to_read = * ((long *) &ind_buf[ind_off * X sizeof(long)]); X } else X block_to_read = fp->ip->ic_db[direct_blk]; X X /* Offset into block to start read */ X offset = fp->file_ptr % BLKSIZE; X X /* # bytes in block to read */ X num_to_read = ((BLKSIZE - offset) < count) ? X (BLKSIZE - offset) : count; X X /* Check for pointer to empty block */ X if (block_to_read == 0) { X for (nr = 0; nr < num_to_read; nr++) X bufptr[nr] = '\0'; X } else { X X#ifdef DEBUG X printf("Reading block %d\n", block_to_read); X#endif X /* Seek to block */ X (void) lseek(fs_fd, btobo(block_to_read), L_SET); X X /* Read them */ X if ((nr = read(fs_fd, bufptr, num_to_read)) == -1) X /* Return error if nothing read, X * else # bytes actually read */ X return (num_read == 0) ? -1 : num_read; X } X X count -= nr; X file_cnt -= nr; X fp->file_ptr += nr; X num_read += nr; X bufptr += nr; /* Point to next part of buffer */ X X /* Read short */ X if (nr < num_to_read) X return num_read; X } X return num_read; X} X SHAR_EOF chmod 0644 io.c || echo "restore of io.c fails" sed 's/^X//' << 'SHAR_EOF' > ls.c && X X/* X * fssal - BSD file system salvage program X * X * Copyright 1989, Robert Ankeney X * Generic Computer Products X * 5315 S.W. 53rd Court X * Portland, Oregon 97221 X * (503) 244-3699 X * All Rights Reserved X * X * Distribution of any portion of this software as part of any product X * for sale is prohibited. X * X * Version 1.00 (Preliminary) X */ X X#include "fs.h" X#include <sys/time.h> X X /* struct holding super block */ Xextern struct fs sb; X X /* File system block size */ X#define BLKSIZE sb.fs_bsize X X/* X * ls - Do a directory listing of given inode. Do a 'ls -l' X * if Lflag is set; do a 'ls -R' if Rflag is set. X * dir_name is name of passed directory. idir_list is X * updated with inode number of each listed directory. X */ Xls(ls_inode, dir_name, idir_list, Lflag, Rflag) Xino_t ls_inode; Xchar *dir_name; Xstruct idir **idir_list; Xchar Lflag, Rflag; X{ X struct icommon fs_inode; /* Inode for directory */ X struct direct *fs_dir; /* Directory structure */ X struct file_ptr dir_ptr; /* Directory pointer */ X X struct icommon sym_inode; /* Inode for symbolic link */ X struct file_ptr sym_ptr; /* Symbolic link pointer */ X long sym_size; /* Length of symbolic name */ X char sym_buf[MAX_PATH_LEN]; /* Buffer for symbolic name */ X X char dir_buf[MAXBSIZE]; /* Directory buffer */ X int buf_cnt; /* # chars in buffer */ X int next_ent; /* Offset into buf of next direct */ X X int mode; /* File mode */ X char modes[11]; /* File mode string */ X struct icommon file_inode; /* Inode for ls file */ X int i; X char *create_time; /* Creation date string */ X char cur_name[1024]; /* Current directory pathname */ X X char *ctime(); X char *strcpy(); X char *sprintf(); X X extern int errno; X X /* Fetch inode */ X get_inode(ls_inode, &fs_inode); X X /* Make sure it's a directory! */ X if ((fs_inode.ic_mode & IFMT) != IFDIR) { X errno = ENOTDIR; X return -1; X } X X /* Add directory inode number to list */ X add_dir_inode(idir_list, ls_inode); X X /* Open directory */ X uopen(&fs_inode, &dir_ptr); X X if (Rflag && *dir_name) X printf("%s:\n", dir_name); X X /* Read each directory block */ X do { X /* Read next block of directory */ X if ((buf_cnt = uread(&dir_ptr, dir_buf, BLKSIZE)) == -1) { X perror("Cannot read directory"); X exit(1); X } X X if (buf_cnt == 0) X break; X X /* For each file in directory, list it */ X next_ent = 0; X do { X /* Point to next directory entry */ X fs_dir = (struct direct *) &dir_buf[next_ent]; X X /* Directory entry here? */ X if (fs_dir->d_ino != 0) { X if (Lflag) { X /* Get inode */ X get_inode(fs_dir->d_ino, &file_inode); X X printf("%5d ", fs_dir->d_ino); /* Inode # */ X (void) strcpy(modes, "-rwxrwxrwx"); X mode = file_inode.ic_mode; X for (i = 8; i >= 0; i--) X if ((mode & (1 << i)) == 0) X modes[9 - i] = '-'; /* rwx bits */ X if ((mode & IFMT) == IFDIR) X modes[0] = 'd'; /* directory */ X if ((mode & IFMT) == IFLNK) X modes[0] = 'l'; /* sym link */ X X if (mode & ISUID) modes[3] = 's'; /* setuid */ X if (mode & ISGID) modes[6] = 's'; /* setgid */ X if (mode & ISVTX) modes[9] = 't'; /* sticky */ X printf("%s %3d %3d/%-3d %8ld", X modes, /* modes */ X file_inode.ic_nlink, /* link cnt */ X file_inode.ic_uid, /* uid */ X file_inode.ic_gid, /* gid */ X file_inode.ic_size.val[0]); /* size */ X create_time = ctime((long *) &file_inode.ic_ctime); X printf(" %-12.12s %-4.4s ", create_time + 4, X create_time + 20); X printf("%s", fs_dir->d_name); /* file name */ X X /* Symbolic link? */ X if ((mode & IFMT) == IFLNK) { X /* Yup - print name of link */ X get_inode(fs_dir->d_ino, &sym_inode); X sym_size = sym_inode.ic_size.val[0]; X if (sym_size >= sizeof(sym_buf)) X printf(" -> ?name too big"); X else { X uopen(&sym_inode, &sym_ptr); X if (uread(&sym_ptr, sym_buf, sym_size) X != sym_size) { X perror("Cannot read symlink"); X exit(1); X } X sym_buf[sym_size] = '\0'; X printf(" -> %s", sym_buf); X } X } X printf("\n"); X X } else X printf("%s\n", fs_dir->d_name); /* file name */ X } X next_ent += fs_dir->d_reclen; X } while (next_ent < buf_cnt); X X } while (buf_cnt > 0); X X printf("\n"); X X /* Recursive (ls -R)? */ X if (Rflag) { X /* Open directory again */ X uopen(&fs_inode, &dir_ptr); X X /* Read each directory block */ X do { X /* Read next block of directory */ X if ((buf_cnt = uread(&dir_ptr, dir_buf, BLKSIZE)) == -1) { X perror("Cannot read directory"); X exit(1); X } X X if (buf_cnt == 0) X break; X X /* For each directory in directory, list it */ X next_ent = 0; X do { X /* Point to next directory entry */ X fs_dir = (struct direct *) &dir_buf[next_ent]; X X /* Directory entry here? */ X if (fs_dir->d_ino != 0) { X /* Get inode */ X get_inode(fs_dir->d_ino, &file_inode); X X /* Make sure is dir and not . or .. */ X if ((file_inode.ic_mode & IFMT) == IFDIR) X if (strcmp(fs_dir->d_name, ".") && X strcmp(fs_dir->d_name, "..")) X X /* Make sure not already listed */ X if (in_idir(*idir_list, fs_dir->d_ino)) X fprintf(stderr, "Already ls'd inum %d, directory: %s !\n", fs_dir->d_ino, cur_name); X else { X if (*dir_name) X (void) sprintf(cur_name, "%s/%s", X dir_name, fs_dir->d_name); X else X (void) strcpy(cur_name, fs_dir->d_name); X X /* List this directory! */ X if (ls(fs_dir->d_ino, cur_name, idir_list, X Lflag, Rflag)) X return -1; X } X } X next_ent += fs_dir->d_reclen; X } while (next_ent < buf_cnt); X X } while (buf_cnt > 0); X } X return 0; X} X X Xls_all(Rflag, verbose) Xchar Rflag, verbose; X{ X struct idir *idir_list, *ls_list, *idir_ptr, *ls_ptr; X X ino_t inum; X int icnt = 0; X char iname[MAX_PATH_LEN]; X X /* Get list of directory inodes for filesystem */ X get_dir_inodes(&idir_list, (ino_t) 2, (ino_t) sb.fs_ncg * sb.fs_ipg); X X /* Make list of ls'd directory inodes */ X new_idir(&ls_list); X idir_ptr = idir_list; X while (idir_ptr) { X inum = idir_ptr->inums[icnt]; X /* Inode been ls'd? */ X if (!in_idir(ls_list, inum)) { X X /* Point to end of ls'd inode list */ X ls_ptr = ls_list; X while(ls_ptr->next_idir) X ls_ptr = ls_ptr->next_idir; X X /* Append to end of ls'd inode list */ X if (ls(inum, iname, &ls_ptr, Rflag, verbose)) X return -1; X } X if (++icnt == idir_ptr->icnt) { X icnt = 0; X idir_ptr = idir_ptr->next_idir; X } X } X return 0; X} SHAR_EOF chmod 0644 ls.c || echo "restore of ls.c fails" sed 's/^X//' << 'SHAR_EOF' > main.c && X X/* X * fssal - BSD file system salvage program X * X * Copyright 1989, Robert Ankeney X * Generic Computer Products X * 5315 S.W. 53rd Court X * Portland, Oregon 97221 X * (503) 244-3699 X * All Rights Reserved X * X * Distribution of any portion of this software as part of any product X * for sale is prohibited. X * X * Version 1.00 (Preliminary) X */ X X#include "fs.h" X#include <sys/stat.h> X X /* File descriptor filesystem is open on */ Xint fs_fd; X X /* struct holding super block */ Xstruct fs sb; X X /* Current root inode */ Xino_t root_inode = ROOTINO; X Xchar *fs_to_read = FS_TO_READ; Xint sb_block = BBSIZE / 512; X Xusage(fs_name) Xchar *fs_name; X{ X fprintf (stderr, X"usage: %s [-f device] [-s super-block] [-r root-inode] [-a] [-l] [-R] [-v]\n", X fs_name); X exit(1); X} X Xmain(argc, argv) Xint argc; Xchar *argv[]; X{ X char do_all, verbose, recurse, long_ls = FALSE; X char *fs_name = argv[0]; X struct stat statb; X struct idir *idir_list; X X for (fs_name = argv[0] + strlen(argv[0]); X (fs_name != argv[0]) && (*(fs_name - 1) != '/'); fs_name--) ; X X while (*++argv) { X if (**argv == '-') { X switch (*++*argv) { X case 'f': X fs_to_read = *++argv; X break; X case 's': X sb_block = atoi (*++argv); X break; X case 'r': X root_inode = atoi (*++argv); X break; X case 'a': X do_all = TRUE; X break; X case 'v': X verbose = TRUE; X break; X case 'R': X recurse = TRUE; X break; X case 'l': X long_ls = TRUE; X break; X X default: X usage(fs_name); X } X } X } X X /* Stat file and see if it's a block special device */ X if (stat(fs_to_read, &statb)) { X perror("Cannot stat file"); X exit(1); X } else X if ((statb.st_mode & S_IFMT) != S_IFBLK) { X fprintf(stderr, "%s not a block special device\n", X fs_to_read); X exit(1); X } X X /* Read in superblock */ X getsb(fs_to_read, sb_block); X X new_idir(&idir_list); X X if (!strcmp(fs_name, "fssal")) X if (do_all) X exit( restore_all(recurse, verbose) ); X else X exit( restore(".", root_inode, ".", X &idir_list, recurse, verbose) ); X X if (!strcmp(fs_name, "fsls")) X if (do_all) X exit( ls_all(long_ls, recurse) ); X else X exit( ls(root_inode, "", &idir_list, long_ls, recurse)); X X usage(fs_name); X X (void) close(fs_fd); X exit(0); X} SHAR_EOF chmod 0644 main.c || echo "restore of main.c fails" sed 's/^X//' << 'SHAR_EOF' > namei.c && X X/* X * fssal - BSD file system salvage program X * X * Copyright 1989, Robert Ankeney X * Generic Computer Products X * 5315 S.W. 53rd Court X * Portland, Oregon 97221 X * (503) 244-3699 X * All Rights Reserved X * X * Distribution of any portion of this software as part of any product X * for sale is prohibited. X * X * Version 1.00 (Preliminary) X */ X X#include "fs.h" X X X /* File descriptor filesystem is open on */ Xextern int fs_fd; X X /* struct holding super block */ Xextern struct fs sb; X X /* File system block size */ X#define BLKSIZE sb.fs_bsize X X/* X * get_inode - read inode inum into struct rd_inode X */ Xget_inode(inum, rd_inode) Xino_t inum; Xstruct icommon *rd_inode; X{ X long lseek(); X X /* Seek to desired inode */ X (void) lseek(fs_fd, (long) itobo(inum), L_SET); X X /* Read that inode */ X if (read(fs_fd, (char *) rd_inode, ISIZE) == -1) { X perror("Cannot read file system"); X exit(1); X } X} X X/* X * namei - Convert filename to inode number. X * X * First get root inode for file system. X * From there, search each directory in path for next X * component of path name, till final filename found. X * X * Returns: X * 0 if file not found (ENOENT), X * 0 if file in path was not a directory (ENOTDIR), X * else inode of file filename. X */ Xnamei(filename) Xchar *filename; X{ X char tmp_name[1024]; /* Copy of filename */ X ino_t cur_ino; /* Current inode in path */ X char *curnam; /* Points to next char in filename */ X char *next_name; /* Next name to scan */ X int next_slash; X X struct icommon fs_inode; /* Inode for file/directory */ X struct direct *fs_dir; /* Directory structure */ X struct file_ptr dir_ptr; /* Directory pointer */ X X char dir_buf[MAXBSIZE]; /* Directory buffer */ X int buf_cnt; /* # chars in buffer */ X X int next_ent; /* Offset into buf of next direct */ X X char *strcpy(); X X extern int errno; X X /* Save copy of path name */ X (void) strcpy(tmp_name, filename); X curnam = tmp_name; X X /* Start at root inode */ X cur_ino = ROOTINO; X X /* Search till nothing left of path name */ X while ((*curnam != '\0') && (cur_ino != 0)) { X X#ifdef DEBUG X printf("Reading inode %d\n", cur_ino); X#endif X /* Get current inode */ X get_inode(cur_ino, &fs_inode); X X /* Make sure it's a directory! */ X if ((fs_inode.ic_mode & IFMT) != IFDIR) { X errno = ENOTDIR; X return 0; X } X X /* Skip /'s in next component of name */ X while (*curnam == '/') curnam++; X X /* Get next component of path in next_name */ X for (next_slash = 0; X (curnam[next_slash] != '\0') && (curnam[next_slash] != '/'); X next_slash ++) ; X X next_name = curnam; X curnam += next_slash; X if (*curnam == '/') curnam++; X next_name[next_slash] = '\0'; X X#ifdef DEBUG X printf("Scanning directory for file '%s'.\n", next_name); X#endif X X /* Open directory */ X uopen(&fs_inode, &dir_ptr); X X /* X * Search each directory block for name. X * Return inode number in cur_ino when found. X */ X cur_ino = 0; X do { X X /* Read next block of directory */ X if ((buf_cnt = uread(&dir_ptr, dir_buf, BLKSIZE)) X == -1) { X perror("Cannot read directory"); X exit(1); X } X X if (buf_cnt == 0) X break; X X /* Scan directory block for desired name */ X next_ent = 0; X do { X /* Point to next directory entry */ X fs_dir = (struct direct *) X &dir_buf[next_ent]; X X /* Directory entry here? */ X if (fs_dir->d_ino != 0) { X#ifdef DEBUG X printf("Inode: %6d file: %s\n", X fs_dir->d_ino, fs_dir->d_name); X#endif X X /* Names match? */ X if (!strcmp(next_name, fs_dir->d_name)) X /* Yes- save inode */ X cur_ino = fs_dir->d_ino; X } X next_ent += fs_dir->d_reclen; X } while ((cur_ino == 0) && X (next_ent < buf_cnt)); X X } while ((cur_ino == 0) && (buf_cnt > 0)); X } X X if (cur_ino == 0) { X errno = ENOENT; X return 0; /* File not found - return 0 */ X } else X return cur_ino; /* Found - return inode number */ X} X SHAR_EOF chmod 0644 namei.c || echo "restore of namei.c fails" sed 's/^X//' << 'SHAR_EOF' > restore.c && X X/* X * fssal - BSD file system salvage program X * X * Copyright 1989, Robert Ankeney X * Generic Computer Products X * 5315 S.W. 53rd Court X * Portland, Oregon 97221 X * (503) 244-3699 X * All Rights Reserved X * X * Distribution of any portion of this software as part of any product X * for sale is prohibited. X * X * Version 1.00 (Preliminary) X */ X X#include "fs.h" X#include <sys/stat.h> X#include <sys/time.h> X X /* struct holding super block */ Xextern struct fs sb; X X /* Current root inode */ Xextern ino_t root_inode; X X /* File system block size */ X#define BLKSIZE sb.fs_bsize X Xchar *base_path; /* Initial restore path */ Xino_t base_inode; /* Initial restore inode # */ X X/* X * restore - Restore files from given directory. X * from_path is path name to from_inode, which is X * directory to be restored. Files are restored to X * to_path. If Rflag is set, restore is recursive X * ( cp -R ). idir_list is updated with inode number of X * each directory restored. X */ Xrestore(from_path, from_inode, to_path, idir_list, Rflag, verbose) Xino_t from_inode; Xchar *from_path, *to_path; Xstruct idir **idir_list; Xchar Rflag, verbose; X{ X /* Save initial restore path and inode # */ X base_path = to_path; X base_inode = from_inode; X return X rrestore(from_path, from_inode, to_path, idir_list, Rflag, verbose); X} X Xrrestore(from_path, from_inode, to_path, idir_list, Rflag, verbose) Xino_t from_inode; Xchar *from_path, *to_path; Xstruct idir **idir_list; Xchar Rflag, verbose; X{ X struct icommon fs_inode; /* Inode for directory */ X struct direct *fs_dir; /* Directory structure */ X struct file_ptr dir_ptr; /* Directory pointer */ X X struct icommon sym_inode; /* Inode for symbolic link */ X struct file_ptr sym_ptr; /* Symbolic link pointer */ X long sym_size; /* Length of symbolic name */ X char sym_buf[MAX_PATH_LEN]; /* Buffer for symbolic name */ X X char dir_buf[MAXBSIZE]; /* Directory buffer */ X int buf_cnt; /* # chars in buffer */ X int next_ent; /* Offset into buf of next direct */ X X int mode; /* File mode */ X struct icommon file_inode; /* Inode for ls file */ X char in_path[MAX_PATH_LEN]; /* Current input file name */ X char out_path[MAX_PATH_LEN]; /* Current output file name */ X X char set_times; /* TRUE to set creation time */ X struct timeval file_times[2]; X X struct iname_list *link_list; /* List of files with common inode */ X struct iname_list *temp_link; X int links_found; X struct stat file_stat; /* File status (for inode # to link) */ X int out_file; /* Output file descriptor */ X X char copy_file; /* TRUE to copy (not link) file */ X struct file_ptr in_ptr; /* File pointer for input file */ X int nr; X char buf[8192]; X X char *ctime(); X char *strcpy(), *strcat(); X X extern int errno; X X (void) umask(0); /* Create any mode file */ X X /* Fetch inode */ X get_inode(from_inode, &fs_inode); X X /* Make sure it's a directory! */ X if ((fs_inode.ic_mode & IFMT) != IFDIR) { X errno = ENOTDIR; X return -1; X } X X /* Add directory inode number to list */ X add_dir_inode(idir_list, from_inode); X X /* Open directory */ X uopen(&fs_inode, &dir_ptr); X X /* Read each directory block */ X do { X /* Read next block of directory */ X if ((buf_cnt = uread(&dir_ptr, dir_buf, BLKSIZE)) == -1) { X perror("Cannot read directory"); X exit(1); X } X X if (buf_cnt == 0) X break; X X /* For each file in directory, restore it */ X next_ent = 0; X do { X /* Point to next directory entry */ X fs_dir = (struct direct *) &dir_buf[next_ent]; X X /* Directory entry here? */ X if ((fs_dir->d_ino != 0) && strcmp(fs_dir->d_name, ".") X && strcmp(fs_dir->d_name, "..")) { X /* Get inode */ X get_inode(fs_dir->d_ino, &file_inode); X X (void) strcpy(in_path, from_path); X (void) strcat(in_path, "/"); X (void) strcat(in_path, fs_dir->d_name); X (void) strcpy(out_path, to_path); X (void) strcat(out_path, "/"); X (void) strcat(out_path, fs_dir->d_name); X X /* Set up access and mod times */ X set_times = TRUE; X file_times[0].tv_sec = file_inode.ic_atime; X file_times[0].tv_usec = file_times[1].tv_usec = 0; X file_times[1].tv_sec = file_inode.ic_mtime; X X mode = file_inode.ic_mode; X X switch (mode & IFMT) { X X case 0020000: /* character special */ X case 0060000: /* block special */ X case 0140000: /* socket - will it work? */ X if (verbose) X printf("Creating special file: %s\n", X out_path); X if (mknod(out_path, mode, X (int) file_inode.ic_db[0])) X { X fprintf(stderr, X "Cannot create special file: %s; ", X out_path); X perror(""); X set_times = FALSE; X } X break; X X case 0040000: /* directory */ X if (verbose) X printf("Creating directory: %s\n", X out_path); X if (mkdir(out_path, mode)) { X fprintf(stderr, X "Cannot create directory: %s; ", X out_path); X perror(""); X set_times = FALSE; X } X break; X X case 0100000: /* regular file */ X copy_file = (file_inode.ic_nlink == 1); X if (!copy_file) { X /* Oh shit; it's a link */ X /* Get filenames with inode # */ X iname(base_path, base_inode, X fs_dir->d_ino, file_inode.ic_nlink, X &link_list, &links_found); X X /* Search for existing file */ X /* to link to */ X for (temp_link = link_list; X temp_link; X temp_link = temp_link->next_iname) X { X if (stat(temp_link->name, X &file_stat)) { X if (errno != ENOENT) { X fprintf(stderr, X "Cannot stat %s: ", X temp_link->name); X perror(""); X } X } else X if (mode != file_stat.st_mode) { X fprintf(stderr, X "Mode mismatch on %s\n", X temp_link->name); X } else X break; X } X X if (temp_link) { X /* Found file to link to */ X if (verbose) X printf("Linking %s to %s\n", X temp_link->name, out_path); X if (link(temp_link->name, out_path)) X { X fprintf(stderr, X "Cannot link %s to %s: ", X temp_link->name, out_path); X perror(""); X copy_file = TRUE; X } else X set_times = FALSE; X } else X copy_file = TRUE; X } X X if (copy_file) { X /* Copy file */ X if (verbose) X printf("Copying %s to %s\n", X in_path, out_path); X uopen(&file_inode, &in_ptr); X if ((out_file = open(out_path, X O_CREAT | O_WRONLY, X mode)) == -1) { X fprintf(stderr, X "Cannot create %s: ", X out_path); X perror(""); X break; X } X X /* Copy file */ X do { X if ((nr = uread(&in_ptr, buf, X BLKSIZE)) == -1) { X fprintf(stderr, X "Cannot read %s: ", X in_path); X perror(""); X } X X if (nr > 0) X if (write(out_file, buf, nr) X == -1) { X fprintf(stderr, X "Cannot write %s: ", X out_path); X perror(""); X } X } while (nr > 0); X X (void) close(out_file); X } X break; X X case 0120000: /* symbolic link */ X /* Can't utime symlink */ X set_times = FALSE; X /* Get name of link */ X get_inode(fs_dir->d_ino, &sym_inode); X sym_size = sym_inode.ic_size.val[0]; X if (sym_size >= sizeof(sym_buf)) { X fprintf(stderr, X "Symbolic link to %s too long\n", X out_path); X set_times = FALSE; X } else { X uopen(&sym_inode, &sym_ptr); X if (uread(&sym_ptr, sym_buf, sym_size) X != sym_size) { X perror("Cannot read symlink"); X set_times = FALSE; X } X sym_buf[sym_size] = '\0'; X X if (verbose) X printf("Creating symbolic link %s -> %s\n", X sym_buf, out_path); X if (symlink(sym_buf, out_path)) { X fprintf(stderr, X "Cannot create symbolic link %s -> %s: ", X out_path, sym_buf); X perror(""); X#ifdef UTEK X if (errno != EDFSNOSUCHHOST) X set_times = FALSE; X#endif X } X } X break; X X default: X fprintf(stderr, "Unknown mode %o on file %s\n", X mode, in_path); X set_times = FALSE; X } X X if (set_times) { X /* Set creation times on file */ X if (utimes(out_path, file_times)) { X fprintf(stderr, X "Cannot set creation times on %s: ", X out_path); X perror(""); X } X /* Set owner and group */ X if (chown(out_path, file_inode.ic_uid, X file_inode.ic_gid)) { X fprintf(stderr, X "Cannot set owner on %s: ", X out_path); X perror(""); X } X } X X } X next_ent += fs_dir->d_reclen; X } while (next_ent < buf_cnt); X X } while (buf_cnt > 0); X X /* Recursive (cp -R)? */ X if (Rflag) { X /* Open directory again */ X uopen(&fs_inode, &dir_ptr); X X /* Read each directory block */ X do { X /* Read next block of directory */ X if ((buf_cnt = uread(&dir_ptr, dir_buf, BLKSIZE)) == -1) { X perror("Cannot read directory"); X exit(1); X } X X if (buf_cnt == 0) X break; X X /* For each sub-directory in directory, restore it */ X next_ent = 0; X do { X /* Point to next directory entry */ X fs_dir = (struct direct *) &dir_buf[next_ent]; X X /* Directory entry here? */ X if (fs_dir->d_ino != 0) { X /* Get inode */ X get_inode(fs_dir->d_ino, &file_inode); X X X /* Make sure is dir and not . or .. */ X if ((file_inode.ic_mode & IFMT) == IFDIR) X if (strcmp(fs_dir->d_name, ".") && X strcmp(fs_dir->d_name, "..")) { X X /* Make sure not restored yet */ X if (in_idir(*idir_list, fs_dir->d_ino)) { X fprintf(stderr, X "Already restored inum %d, directory: %s !\n", X fs_dir->d_ino, from_path); X } else { X (void) strcpy(in_path, from_path); X (void) strcat(in_path, "/"); X (void) strcat(in_path, fs_dir->d_name); X (void) strcpy(out_path, to_path); X (void) strcat(out_path, "/"); X (void) strcat(out_path, fs_dir->d_name); X X file_times[0].tv_sec = file_inode.ic_atime; X file_times[0].tv_usec = file_times[1].tv_usec X = 0; X file_times[1].tv_sec = file_inode.ic_mtime; X X /* Restore this directory! */ X if (rrestore(in_path, fs_dir->d_ino, out_path, X idir_list, Rflag, verbose)) X return -1; X X /* Set creation times on directory */ X if (utimes(out_path, file_times)) { X fprintf(stderr, X "Cannot set creation times on %s: ", X out_path); X perror(""); X } X } X } X } X next_ent += fs_dir->d_reclen; X } while (next_ent < buf_cnt); X X } while (buf_cnt > 0); X } X return 0; X} X Xrestore_all(Rflag, verbose) Xchar Rflag, verbose; X{ X struct idir *idir_list, *restore_list, *idir_ptr, *restore_ptr; X X ino_t inum; X int icnt = 0; X char iname[MAX_PATH_LEN]; X X char *sprintf(); X X /* Get list of directory inodes for filesystem */ X get_dir_inodes(&idir_list, (ino_t) 2, (ino_t) sb.fs_ncg * sb.fs_ipg); X X /* Make list of restored directory inodes */ X new_idir(&restore_list); X idir_ptr = idir_list; X while (idir_ptr) { X inum = idir_ptr->inums[icnt]; X /* Inode been restored? */ X if (!in_idir(restore_list, inum)) { X X /* Nope - create directory and restore */ X (void) sprintf(iname, "i%d", inum); X X if (verbose) X printf("Creating new base directory: %s\n", X iname); X if (mkdir(iname, 0775)) { X fprintf(stderr, X "Cannot create directory: %s; ", X iname); X perror(""); X } X X /* Point to end of restored inode list */ X restore_ptr = restore_list; X while(restore_ptr->next_idir) X restore_ptr = restore_ptr->next_idir; X X /* Append to end of restored inode list */ X if (restore(iname, inum, iname, &restore_ptr, X Rflag, verbose)) X return -1; X } X if (++icnt == idir_ptr->icnt) { X icnt = 0; X idir_ptr = idir_ptr->next_idir; X } X } X return 0; X} SHAR_EOF chmod 0644 restore.c || echo "restore of restore.c fails" sed 's/^X//' << 'SHAR_EOF' > stat.c && X X/* X * fssal - BSD file system salvage program X * X * Copyright 1989, Robert Ankeney X * Generic Computer Products X * 5315 S.W. 53rd Court X * Portland, Oregon 97221 X * (503) 244-3699 X * All Rights Reserved X * X * Distribution of any portion of this software as part of any product X * for sale is prohibited. X * X * Version 1.00 (Preliminary) X */ X X/* X * stat.c X * X * load an inode into memory X */ X X# include "fs.h" X# include <sys/stat.h> X Xextern struct fs sb; Xextern int fs_fd; X Xfs_stat (inum, statb) Xino_t inum; Xstruct stat *statb; X{ X long off; X struct dinode di; X X long lseek(); X X off = itobo (inum); X (void) lseek (fs_fd, off, L_SET); X if (read (fs_fd, (char *) &di, sizeof (di)) != sizeof (di)) X return -1; X statb->st_dev = 0; X statb->st_ino = inum; X statb->st_mode = di.di_mode; X statb->st_nlink = di.di_nlink; X statb->st_uid = di.di_uid; X statb->st_gid = di.di_gid; X statb->st_atime = di.di_atime; X statb->st_mtime = di.di_mtime; X statb->st_ctime = di.di_ctime; X statb->st_blocks = di.di_blocks; X return 0; X} SHAR_EOF chmod 0644 stat.c || echo "restore of stat.c fails" exit 0