bob (06/01/82)
: run this file as shell script mkdir recover inc echo extracting recover/README cat > recover/README <<'E*O*F' The recover program is a byproduct of my High Speed File Routines (the HSFR package is available on request). As a matter of fact, I had been editing and testing a subroutine of the HSFR for about 5 hours when I deleted something by mistake. I had the guts of a recover program from the HSFR subroutines, so I just wrote a user interface and recovered my deleted file. All of the subroutines and include files are taken unmodified from the HSFR package. That is why the subroutines have extra hooks that recover does not use. The include files in the inc directory are for V6 type filesystems. They serve no purpose on the v7 type filesystems and can be deleted. Recover has been tested on 4.1BSD and V7 PDP11. The HSFR were developed on a vax 4.1bsd system. HSFR has also been tested on V7, PWB and ISC. Since recover is a subset of HSFR, I feel quite confident that recover will work on all of these. Compile as follows or modify Makefile.vax: VAX: cc -o recover -DV7 *.c PDP-v7: cc -o recover -DV7 -DPDP11 *.c PWB: cc -o recover -DPWB *.c -lS ISC: cc -o recover -DISC *.c -lS You may wish to make recover setuid root so that anyone can recover files. Remember that SUID will allow anyone to look at deleted files. If you want diagnostics, you can set Debug to [1-7] in main.c. On the TODO WISHLIST is a version of recover that needs no arguments. Recover would copy blocks from the current working directory's freelist. After you delete something precious by mistake, you don't want to have to think much while someone may be taking your blocks. It is easy to merge 4.1's pwd.c into recover to accomplish this, but that code would not work on all other unix systems. Comments, suggestions and improvements are welcome. Bob Gray (duke!adiron!bob) PAR TECH CORP Seneca Plaza New Hartford, NY 13413 (315) 738-0600 E*O*F echo extracting recover/Makefile.vax cat > recover/Makefile.vax <<'E*O*F' CC= cc -O -DV7 SRC= alloc.c dskino.c errexit.c getmount.c main.c rw.c OBJ= alloc.o dskino.o errexit.o getmount.o main.o rw.o all: recover recover.man recover: $(OBJ) $(CC) $(OBJ) -o recover recover.man: recover.1 nroff -man recover.1 > recover.man clean: rm -i recover $(OBJ) recover.man E*O*F echo extracting recover/recover.1 cat > recover/recover.1 <<'E*O*F' .TH RECOVER local .SH NAME recover \- copy blocks from a freelist to restore a file .SH SYNOPSIS .B recover name [num] [destination] .SH DESCRIPTION .I recover copies .I num blocks (or a default number) from the freelist of the specified .I name and puts them in the files data00, data01 ... data\fInum\fR. The data files are created in .I destination directory if given or in /tmp. .PP .I Name is an absolute pathname of the file to be recovered or the .I special device name of the file system of interest. In the first case, only the beginning of the absolute pathname needs to be given. .I Recover uses it to lookup the .I special device in the mount table. .PP .I Recover will not save the day if someone takes blocks from the freelist before you can run recover. If recover cannot be run immediately and the file system is active, you may wish to .I pad your data in the freelist by removing some unneeded files in the same file system. .PP After you run recover, you will have files data00, data01 ... data\fInum\fR each 1 block (BSIZE bytes) long. You may wish to use .I file and or an editor on the .I data files to find the relevant ones. Unless your file happens to be a multiple of BSIZE, the last data block will have NULL bytes after your data. Compilers will complain. An editor can be used to truncate NULL bytes from the file. .SH EXAMPLE .nf rm precious.c : OOPS, I meant precious.o recover /u/bob/precious.c 100 : grab 100 blocks from freelist cd /tmp file data* : file should think that the first : data files are C or text cat data00 data01 data02 > XYZ : It turns out I only need 3 blocks vi XYZ : look at it and truncate NULLS rm data* : clean up mv XYZ /u/bob/precious.c .SH FILES /usr/local/recover /etc/mtab - to look up on which file system .I name was mounted. .br data00, data01, data02, ... .SH DIAGNOSTICS A usage message if you forget one of the arguments. A message if you do not have permission to read disk. .SH AUTHOR Bob Gray (duke!adiron!bob) .SH BUGS The algorithm used for looking up where file systems are mounted only works if file systems are mounted at the top level. This should be fixed. If an absolute pathname is given that refers to the root, getmount will fail. Use the root's special file. .I Num is checked for what the author thinks is reasonable. E*O*F echo extracting recover/alloc.c cat > recover/alloc.c <<'E*O*F' /* @(#)alloc.c 1.1 82/05/12 */ #include "defs.h" /* * alloc will return the next available * free disk block from the free list of * the specified device. * The super block has up to NICFREE remembered * free blocks; the last of these is read to * obtain NICFREE more . . . */ daddr_t alloc(fd, fp) int fd; /* raw file descriptor of filesystem */ register struct filsys *fp; /* pointer to incore superblock */ { daddr_t bno; register i; struct fblk fblk; /* struct will contain freeblock list */ do { if (fp->s_nfree <= 0) goto nospace; if (fp->s_nfree > NICFREE) { fprintf(stderr, "bad free count"); goto nospace; } bno = fp->s_free[--fp->s_nfree]; fp->s_free[fp->s_nfree] = (daddr_t)0; /* safety */ DEBUG(7,"s_nfree=%d",fp->s_nfree); DEBUG(7," s_free[s_nfree]=%ld",(long)bno); if (bno == 0) goto nospace; } while (badblock(fp, bno)); if (fp->s_nfree <= 0) { bread(fd, bno, (char *) &fblk, sizeof(fblk) ); fp->s_nfree = fblk.df_nfree; if(fp->s_nfree <=0 || fp->s_nfree > NICFREE) goto nospace; for(i=0;i<NICFREE;i++) fp->s_free[i] = fblk.df_free[i]; } fp->s_tfree--; /* total free blocks */ DEBUG(7," s_tfree=%ld\n",(long)fp->s_tfree); return (bno); nospace: fprintf(stderr, "file system full nfree=%d, tfree=%d\n", fp->s_nfree, fp->s_tfree); fp->s_nfree = 0; fp->s_tfree = 0; return ((daddr_t) 0 ); } /* * Check that a block number is in the * range between the I list and the size * of the device. * This is used mainly to check that a * garbage file system has not been mounted. */ badblock(fp, bn) register struct filsys *fp; daddr_t bn; { if (bn < fp->s_isize || bn >= fp->s_fsize) { fprintf(stderr, "bad block"); return(1); } return(0); } E*O*F echo extracting recover/defs.h cat > recover/defs.h <<'E*O*F' /* @(#)defs.h 1.1 82/05/12 */ #include <stdio.h> extern Debug; #define DEBUG(l,f,s) if(Debug>=l) fprintf(stderr,f,s) /* shorthand */ #define ISIZE(f) finfo[f].i.i_size #define IPOS(f) finfo[f].pos #define F(f,in) finfo[f].ch[in]. /*#define SMOUNT */ /* defining this means mounts will be fork/execed */ /* make sure MOUNT and UMOUNT have proper pathnames */ /* they are used with SMOUNT code */ #ifdef ISC #define MOUNT "/priv/mount" #define UMOUNT "/priv/unmount" /* Of the 8 bits for a minor dev, low 5 select a logical sub dsk, the next 2 bits select a drive on a controller and hi bit selects 1 of 2 controllers*/ #define DRIVE_CTL_MASK 0340 /* these 2 are for safety - to lessen mistakes */ /*#define SCANNERDSK 0200*/ /* allow writing to only certain filesystems*/ #define ILARG 010000 #define MTAB "/etc/mtab" #define MSIZE 64 #define MOFF 32 struct mnt { char path[MOFF]; char spec[MOFF]; }; #include "../inc/param.h" #include "../inc/inode.h" #include "../inc/stat.h" #include "../inc/filsys.h" #include "../inc/fblk.h" #endif #ifdef PWB #define MOUNT "/etc/mount" #define UMOUNT "/etc/umount" #define ILARG 010000 #define MTAB "/etc/mnttab" #define MSIZE 26 #define MOFF 10 struct mnt { char spec[MOFF]; char path[16]; }; #include "../inc/param.h" #include "../inc/inode.h" #include "../inc/stat.h" #include "../inc/filsys.h" #include "../inc/fblk.h" #endif #ifdef V7 /* v7 or VAX 4.1BAD */ #define MOUNT "xmount" #define UMOUNT "xumount" #define NEWFS #define MTAB "/etc/mtab" #define MSIZE 64 #define MOFF 32 struct mnt { char path[MOFF]; char spec[MOFF]; }; #include <sys/param.h> #include <sys/inode.h> #include <sys/ino.h> #include <sys/stat.h> #include <sys/filsys.h> #include <sys/fblk.h> #endif #ifdef NEWFS #define HIBLK (1<<30) /* this should be big enough */ #else #define HIBLK (daddr_t) ~0 /* biggest number - all 1 bits */ #endif struct chunk { daddr_t physblk; /* physical block of start of chunk */ daddr_t logblk; /* logical .... */ unsigned nblks; /* number of blocks in chunk */ }; #define CHLSTSZ 50 struct info /* one of these for each possible file opened */ { struct inode i; struct chunk ch[CHLSTSZ];/*eventually make this a pointer to dynamic */ off_t pos; /* R/W pointer - lseek */ }; extern struct info finfo[]; long lseek(); /* alloc.c */ daddr_t alloc(); /* return next freeblock given raw fd and superb */ int badblock(); /* return 1 if block is out of range */ /* bchunk.c */ /* bchunk() get blocks and build a chunklist */ /* bmap.c */ daddr_t bmap(); /* map logical to physical blocks */ /* buildf.c */ daddr_t findphys(); /* return physical block given logical block */ /* buildf() build a file by setting up indirect block pointers */ /* coal.c */ /* coalesce() take a chunklist and attempt to coalesce it */ int indircnt(); /* return # of indir blocks needed to represent a file */ /* errexit.c */ /* errexit() print message on stderr and exit with code */ /* dskino.c */ /* getdskino() given inum fill in inode info */ /* iexpand() expand disk inode info into incore inode */ /* setdskino() put incore inode info on disk */ /* getmount.c */ struct mnt * getmount(); /* get mount info */ char * rindex(); /* reverse index for PWB and ISC */ char * rawname(); /* make block dev name raw */ /* rw.c */ /* bread() block read */ /* bwrite() block write */ int c_creat(); int c_open(); int c_close(); int c_read(); int c_write(); long c_lseek(); # ifndef MIN # define MIN(a,b) ((((long)a)<((long)b))?((long)a):((long)b)) # endif E*O*F echo extracting recover/dskino.c cat > recover/dskino.c <<'E*O*F' /* @(#)dskino.c 1.1 82/05/12 */ #include "defs.h" #ifndef NEWFS #define dinode i6node /* Inode structure as it appears on the disk. (V6 type ) */ struct i6node { int i6_mode; char i6_nlink; char i6_uid; char i6_gid; char i6_size0; unsigned i6_size1; daddr_t i6_addr[8]; time_t i6_atime; time_t i6_mtime; }; #endif /* given inumber on filesystem, fill in inode */ getdskino(fd,inum,ip) struct inode *ip; ino_t inum; { struct dinode *dp; char buf[BSIZE]; bread(fd,itod(inum),buf,BSIZE); dp = (struct dinode *) &buf[0]; dp += itoo(inum); iexpand(ip,dp); } #ifdef NEWFS iexpand(ip, dp) /* expand disk inode info into incore inode*/ register struct inode *ip; register struct dinode *dp; { register char *p1, *p2; register int i; ip->i_mode = dp->di_mode; ip->i_nlink = dp->di_nlink; ip->i_uid = dp->di_uid; ip->i_gid = dp->di_gid; ip->i_size = dp->di_size; p1 = (char *)ip->i_un.i_addr; p2 = (char *)dp->di_addr; for(i=0; i<NADDR; i++) { #ifdef PDP11 *p1++ = *p2++; *p1++ = 0; *p1++ = *p2++; *p1++ = *p2++; #else *p1++ = *p2++; *p1++ = *p2++; *p1++ = *p2++; *p1++ = 0; #endif } } #else iexpand(ip, dp) register struct inode *ip; register struct i6node *dp; { register int i; ip->i_mode = dp->i6_mode; ip->i_nlink = dp->i6_nlink; ip->i_uid = dp->i6_uid; ip->i_gid = dp->i6_gid; ip->i_size = ((long) (dp->i6_size0 & 0377)) << 16; ip->i_size += (long)(unsigned) dp->i6_size1; DEBUG(6,"m=0%o ",ip->i_mode); DEBUG(6,"sz=%D\n",ip->i_size); for(i=0; i<8; i++) { ip->i_un.i_addr[i]= dp->i6_addr[i]; DEBUG(6,"a=%ld\n",(long)ip->i_un.i_addr[i]); } } #endif setdskino(fd,inum,ip) /* given a pointer to incore inode, put data on disk */ struct inode *ip; ino_t inum; { register char *p1, *p2; struct dinode *dp; register int i; char buf[BSIZE]; bread(fd,itod(inum),buf,BSIZE); /* read block containing inode */ dp = (struct dinode *) &buf[0]; dp += itoo(inum); /* get offset to our inode */ #ifdef NEWFS dp->di_size = ip->i_size; /* copy size */ p2 = (char *)ip->i_un.i_addr; p1 = (char *)dp->di_addr; for(i=0; i<NADDR; i++) /* copy addresses */ { DEBUG(4,"addr=%ld\n",(long)ip->i_un.i_addr[i]); #ifdef PDP11 *p1++ = *p2++; /* copy 32 bit addresses to 24 bit addresses */ p2++; *p1++ = *p2++; *p1++ = *p2++; #else *p1++ = *p2++; /* copy 32 bit addresses to 24 bit addresses */ *p1++ = *p2++; *p1++ = *p2++; p2++; #endif } #else dp->i6_mode = ip->i_mode; dp->i6_size0 = ( ip->i_size >> 16 ) & 0377; dp->i6_size1 = ip->i_size & 0177777; for(i=0; i<8; i++) { DEBUG(4,"addr=%ld\n",(long)ip->i_un.i_addr[i]); dp->i6_addr[i] = ip->i_un.i_addr[i]; } #endif bwrite(fd,itod(inum),buf,BSIZE); /* read block containing inode */ } E*O*F echo extracting recover/errexit.c cat > recover/errexit.c <<'E*O*F' /* @(#)errexit.c 1.1 82/05/12 */ #include <stdio.h> /* VARARGS2 */ errexit(code, fmt, arg1) char fmt[]; char arg1[]; { fflush(stdout); _doprnt(fmt, &arg1, stderr); /* print the error message */ perror("If sys call made error then:"); exit (code); /* consider it fatal and exit */ } E*O*F echo extracting recover/main.c cat > recover/main.c <<'E*O*F' char sccsid[]= "@(#)main.c 1.5 82/06/01"; /* a program to recover files from the free list */ /* Bob Gray PAR CORP (315) 738-0600 duke!adiron!bob */ #define DEFCNT 10 /* give him 10 blocks if he doesnt specify */ #define MAXBLK 1024 /* Is there any reason to allow more? */ #include "defs.h" int Debug = 0; /* default debug level - higher gives more*/ main(argc,argv) char **argv; { struct filsys filsys; /* to hold superblock */ int fd; /* file desc for disk */ int i; int f; /* file desc for each dataXXX file created */ int count = DEFCNT; /* number of blocks to grab from freelist */ daddr_t physical; char databuf[BSIZE]; char stringbuf[60]; char *disk; /* filesystem we want is on this disk */ char *dest; /* where do we creat dataXXX files? */ struct mnt *mountp; /* pointer to a /etc/mtab entry */ if(argc<2 || argc>4) errexit(7,"Usage: recover name [num] [destination]\n"); /* from first arg, we determine where desired filesystem is */ if( strncmp("/dev/",argv[1],5) == 0 ) disk = argv[1]; /* assume he gave us a char or blk dev */ else /* look up pathname to get disk */ { if( (mountp=getmount(argv[1]) ) == (struct mnt *) 0) errexit(9,"getmount info failed\n"); disk = rawname(mountp->spec); } count=atoi(argv[2]); if(count<1 || count>MAXBLK) errexit(8,"a count of %d seems strange\n",count); if(argc == 4) dest = argv[3]; else dest = "/tmp"; printf("copy %d blocks from %s and stick in %s\n",count,disk,dest); sync(); /* force cache buffers out to disk - for PDP11s */ if((fd=open(disk,0)) <= 0 ) errexit(10,"cannot open %s\n",disk); bread(fd, SUPERB, (char *) &filsys, sizeof(filsys) ); DEBUG(6,"s_tfree= %u\n",filsys.s_tfree); DEBUG(6,"s_tinode= %u\n",filsys.s_tinode); for(i=0;i<count;i++) { physical = alloc(fd, &filsys); sprintf(stringbuf,"%s/data%02d",dest,i); if( (f = creat(stringbuf,0644) ) < 0 ) errexit(41,"cannot creat in getmount\n"); bread(fd, physical, databuf, BSIZE); if(write(f,databuf,BSIZE) != BSIZE) errexit(33,"bad write\n"); close(f); } } #ifndef V7 strncmp(s1, s2, n) register char *s1, *s2; register n; { while (--n >= 0 && *s1 == *s2++) if (*s1++ == '\0') return(0); return(n<0 ? 0 : *s1 - *--s2); } #endif E*O*F echo extracting recover/rw.c cat > recover/rw.c <<'E*O*F' /* 82/05/12 @(#)rw.c 1.1 */ #include <stdio.h> #include "defs.h" bread(fi, bno, buf, cnt) daddr_t bno; char *buf; { #ifdef PWB seek(fi,bno,3); #else if( lseek(fi,(long) bno*BSIZE, 0) != (long)bno*BSIZE) errexit(32,"lseek failed %ld\n",(long)bno); #endif if (read(fi, buf, cnt) != cnt) errexit(1,"read error %ld\n", bno); } bwrite(fi, bno, buf, cnt) daddr_t bno; char *buf; { #ifdef PWB seek(fi,bno,3); #else if( lseek(fi,(long) bno*BSIZE, 0) != (long)bno*BSIZE) errexit(32,"lseek failed %ld\n",(long)bno); #endif if (write(fi, buf, cnt) != cnt) errexit(2,"write error %ld\n", bno); } E*O*F echo extracting recover/getmount.c cat > recover/getmount.c <<'E*O*F' /* @(#)getmount.c 1.1 82/05/12 */ /*given ful pathname sp of a file, return pointer to mnt struct or NULL */ /* special file has to be mounted on a top level directory - PWB restriction */ #include "defs.h" char *strcpy(), *strcat(); struct mnt * getmount(sp) char *sp; { char name[60]; struct mnt mbuf[NMOUNT]; static struct mnt mnt; int mfd; int i; char *p; char *s; /* slide along pathname and zap a NULL to terminate*/ mnt.spec[0] = 0;/* strcat depends on this buffer being empty */ strcpy(name,sp); p = &name[0]; s = p; /* p will always point to start of string */ if(*p != '/') return(NULL); /* check that full pathname was given */ /* keep only the first absolute directory -see above */ /* eg: given /image/landsat/band1 -> /image */ s++; /* skip over initial slash */ /* MTAB might have entries that look like: */ /* /image000000000000000hp1a000000000000000 */ while(*s) { if(*s == '/') { *s = NULL; break; } s++; } if( (mfd=open(MTAB,0)) < 0) /* see mtab(5) for details */ { fprintf(stderr,"cannot open %s\n",MTAB); return(NULL); } if(read(mfd,(char *)mbuf,sizeof(mbuf)) <= 0 ) errexit(29,"bad read of %s\n",MTAB); if(close(mfd) < 0) errexit(30,"bad close of %s\n",MTAB); for(i=0;i<NMOUNT;i++) { DEBUG(3,"spec=%s. ",mbuf[i].spec); DEBUG(3,"path=%s.\n",mbuf[i].path); if(strcmp(p,mbuf[i].path) == 0) /* FOUND */ break; } if(i>=NMOUNT) return( (struct mnt *) 0); /* NOT FOUND */ strcpy(mnt.path, mbuf[i].path); #ifndef ISC strcpy(mnt.spec,"/dev/"); #endif strcat(mnt.spec, mbuf[i].spec); DEBUG(3,"spec=%s. ",mnt.spec); DEBUG(3,"path=%s.\n",mnt.path); return(&mnt); } #ifndef V7 /* * Return the ptr in sp at which the character c last * appears; NULL if not found */ char * rindex(sp, c) register char *sp, c; { register char *r; r = NULL; do { if (*sp == c) r = sp; } while (*sp++); return(r); } #endif char * /* make block dev name raw dev name - EG: */ rawname(cp) /* /dev/hp1a ==> /dev/rhp1a */ char *cp; { char *rindex(); static char rawbuf[32]; char *dp = rindex(cp, '/'); if (dp == 0) return (0); *dp = 0; strcpy(rawbuf, cp); *dp = '/'; strcat(rawbuf, "/r"); strcat(rawbuf, dp+1); return (rawbuf); } E*O*F echo extracting inc/fblk.h cat > inc/fblk.h <<'E*O*F' /* fblk.h 4.2 81/02/19 */ struct fblk { int df_nfree; daddr_t df_free[NICFREE]; }; E*O*F echo extracting inc/filsys.h cat > inc/filsys.h <<'E*O*F' /* filsys.h 4.3 81/03/03 */ /* * Structure of the super-block */ struct filsys { unsigned s_isize; /* size in blocks of i-list */ daddr_t s_fsize; /* size in blocks of entire volume */ int s_nfree; /* number of addresses in s_free */ daddr_t s_free[NICFREE]; /* free block list */ int s_ninode; /* number of i-nodes in s_inode */ ino_t s_inode[NICINOD]; /* free i-node list */ char s_flock; /* lock during free list manipulation */ char s_ilock; /* lock during i-list manipulation */ char s_fmod; /* super block modified flag */ char s_ronly; /* mounted read-only flag */ time_t s_time; /* last super block update */ #ifdef NEWFS daddr_t s_tfree; /* total free blocks*/ ino_t s_tinode; /* total free inodes */ int s_dinfo[2]; /* interleave stuff */ #define s_m s_dinfo[0] #define s_n s_dinfo[1] char s_fsmnt[12]; /* ordinary file mounted on */ /* end not maintained */ ino_t s_lasti; /* start place for circular search */ ino_t s_nbehind; /* est # free inodes before s_lasti */ #else int pad[35]; long s_dtime; /* time file system was last dumped */ char s_dfile[6]; /* where last dump was taken to */ daddr_t s_tfree; /* Total free, for subsystem examination */ int s_tinode; /* Free inodes, for subsystem examination */ char s_fname[6]; /* File system name */ char s_fpack[6]; /* File system pack name */ #endif }; #ifdef KERNEL struct filsys *getfs(); #endif E*O*F echo extracting inc/param.h cat > inc/param.h <<'E*O*F' #define SUPERB ((daddr_t)1) /* block number of the super block */ #define NOFILE 15 /* this many files opened at once */ #define NMOUNT 20 #define CLSIZE 1 /* this many sectors per fs block */ #define BSIZE 512 /* size of secondary block (bytes) */ #define INOPB 16 /* 16 inodes per block */ #define BMASK 0777 /* BSIZE-1 */ #define BSHIFT 9 /* LOG2(BSIZE) */ #define NICINOD 100 /* number of superblock inodes */ #define NICFREE 100 /* number of superblock free blocks */ /* inumber to disk address and inumber to disk offset */ #define itod(x) ((daddr_t)((((unsigned)(x)+2*INOPB-1)/INOPB))) #define itoo(x) ((int)(((x)+2*INOPB-1)%INOPB)) /* file system blocks to disk blocks and back */ #define fsbtodb(b) ((b)*CLSIZE) #define dbtofsb(b) ((b)/CLSIZE) #ifdef PWB /* C compiler on PWB needs help for daddr_t x[NINDIR] */ #define NINDIR 256 #else #define NINDIR (BSIZE/sizeof(daddr_t)) #endif #define MAX(a,b) (((a)>(b))?(a):(b)) #define MIN(a,b) ((((long)a)<((long)b))?((long)a):((long)b)) typedef unsigned daddr_t; typedef char * caddr_t; typedef unsigned ino_t; typedef long time_t; typedef long off_t; typedef int dev_t; E*O*F echo extracting inc/inode.h cat > inc/inode.h <<'E*O*F' /* inode.h 4.5 81/03/09 */ #define NADDR 13 struct inode { char i_flag; char i_count; /* reference count */ dev_t i_dev; /* device where inode resides */ ino_t i_number; /* i number, 1-to-1 with device address */ unsigned i_mode; int i_nlink; /* directory entries */ int i_uid; /* owner */ int i_gid; /* group of owner */ off_t i_size; /* size of file */ union { daddr_t i_addr[NADDR]; /* if normal file/directory */ } i_un; }; /* modes */ #define IFMT 0170000 /* type of file */ #define IFDIR 0040000 /* directory */ #define IFCHR 0020000 /* character special */ #define IFBLK 0060000 /* block special */ #define IFREG 0100000 /* regular */ #define IFMPC 0030000 /* multiplexed char special */ #define IFMPB 0070000 /* multiplexed block special */ #define ISUID 04000 /* set user id on execution */ #define ISGID 02000 /* set group id on execution */ #define ISVTX 01000 /* save swapped text even after use */ #define IREAD 0400 /* read, write, execute permissions */ #define IWRITE 0200 #define IEXEC 0100 E*O*F echo extracting inc/stat.h cat > inc/stat.h <<'E*O*F' /* stat.h 4.2 81/02/19 */ struct stat { dev_t st_dev; ino_t st_ino; unsigned st_mode; int st_nlink; int st_uid; int st_gid; dev_t st_rdev; off_t st_size; time_t st_atime; time_t st_mtime; time_t st_ctime; }; #define S_IFMT 0170000 /* type of file */ #define S_IFDIR 0040000 /* directory */ #define S_IFCHR 0020000 /* character special */ #define S_IFBLK 0060000 /* block special */ #define S_IFREG 0100000 /* regular */ #define S_IFMPC 0030000 /* multiplexed char special */ #define S_IFMPB 0070000 /* multiplexed block special */ #define S_ISUID 0004000 /* set user id on execution */ #define S_ISGID 0002000 /* set group id on execution */ #define S_ISVTX 0001000 /* save swapped text even after use */ #define S_IREAD 0000400 /* read permission, owner */ #define S_IWRITE 0000200 /* write permission, owner */ #define S_IEXEC 0000100 /* execute/search permission, owner */ E*O*F echo extracting inc/README cat > inc/README <<'E*O*F' These files are used by v6 type systems (including ISC and PWB). V7 type systems get (including 4.1BSD) get theirs from /usr/include/sys. E*O*F