#define MAXINO 3000 #define BITS 8 #define MAXXTR 60 #define NCACHE 3 #ifndef STANDALONE #include #include #endif #include #include #include #include #include #include #include #define MWORD(m,i) (m[(unsigned)(i-1)/MLEN]) #define MBIT(i) (1<<((unsigned)(i-1)%MLEN)) #define BIS(i,w) (MWORD(w,i) |= MBIT(i)) #define BIC(i,w) (MWORD(w,i) &= ~MBIT(i)) #define BIT(i,w) (MWORD(w,i) & MBIT(i)) struct filsys sblock; int fi; ino_t ino, maxi, curino; int mt; char tapename[] = "/dev/rmt1"; char *magtape = tapename; #ifdef STANDALONE char mbuf[50]; #endif #ifndef STANDALONE daddr_t seekpt; int df, ofile; char dirfile[] = "rstXXXXXX"; struct { ino_t t_ino; daddr_t t_seekpt; } inotab[MAXINO]; int ipos; #define ONTAPE 1 #define XTRACTD 2 #define XINUSE 4 struct xtrlist { ino_t x_ino; char x_flags; } xtrlist[MAXXTR]; char name[12]; char drblock[BSIZE]; int bpt; #endif int eflag; int volno = 1; struct dinode tino, dino; daddr_t taddr[NADDR]; daddr_t curbno; short dumpmap[MSIZ]; short clrimap[MSIZ]; int bct = NTREC+1; char tbf[NTREC*BSIZE]; struct cache { daddr_t c_bno; int c_time; char c_block[BSIZE]; } cache[NCACHE]; int curcache; main(argc, argv) char *argv[]; { register char *cp; char command; int done(); #ifndef STANDALONE mktemp(dirfile); if (argc < 2) { usage: printf("Usage: restor x file file..., restor r filesys, or restor t\n"); exit(1); } argv++; argc -= 2; for (cp = *argv++; *cp; cp++) { switch (*cp) { case '-': break; case 'f': magtape = *argv++; argc--; break; case 'r': case 'R': case 't': case 'x': command = *cp; break; default: printf("Bad key character %c\n", *cp); goto usage; } } if (command == 'x') { if (signal(SIGINT, done) == SIG_IGN) signal(SIGINT, SIG_IGN); if (signal(SIGTERM, done) == SIG_IGN) signal(SIGTERM, SIG_IGN); df = creat(dirfile, 0666); if (df < 0) { printf("restor: %s - cannot create directory temporary\n", dirfile); exit(1); } close(df); df = open(dirfile, 2); } doit(command, argc, argv); if (command == 'x') unlink(dirfile); exit(0); #else magtape = "tape"; doit('r', 1, 0); #endif } doit(command, argc, argv) char command; int argc; char *argv[]; { extern char *ctime(); register i, k; ino_t d; #ifndef STANDALONE int xtrfile(), skip(); #endif int rstrfile(), rstrskip(); struct dinode *ip, *ip1; #ifndef STANDALONE if ((mt = open(magtape, 0)) < 0) { printf("%s: cannot open tape\n", magtape); exit(1); } #else do { printf("Tape? "); gets(mbuf); mt = open(mbuf, 0); } while (mt == -1); magtape = mbuf; #endif switch(command) { #ifndef STANDALONE case 't': if (readhdr(&spcl) == 0) { printf("Tape is not a dump tape\n"); exit(1); } printf("Dump date: %s", ctime(&spcl.c_date)); printf("Dumped from: %s", ctime(&spcl.c_ddate)); return; case 'x': if (readhdr(&spcl) == 0) { printf("Tape is not a dump tape\n"); exit(1); } if (checkvol(&spcl, 1) == 0) { printf("Tape is not volume 1 of the dump\n"); exit(1); } pass1(); /* This sets the various maps on the way by */ i = 0; while (i < MAXXTR-1 && argc--) { if ((d = psearch(*argv)) == 0 || BIT(d, dumpmap) == 0) { printf("%s: not on the tape\n", *argv++); continue; } xtrlist[i].x_ino = d; xtrlist[i].x_flags |= XINUSE; printf("%s: inode %u\n", *argv, d); argv++; i++; } newvol: flsht(); close(mt); getvol: printf("Mount desired tape volume: Specify volume #: "); if (gets(tbf) == NULL) return; volno = atoi(tbf); if (volno <= 0) { printf("Volume numbers are positive numerics\n"); goto getvol; } mt = open(magtape, 0); if (readhdr(&spcl) == 0) { printf("tape is not dump tape\n"); goto newvol; } if (checkvol(&spcl, volno) == 0) { printf("Wrong volume (%d)\n", spcl.c_volume); goto newvol; } rbits: while (gethead(&spcl) == 0) ; if (checktype(&spcl, TS_INODE) == 1) { printf("Can't find inode mask!\n"); goto newvol; } if (checktype(&spcl, TS_BITS) == 0) goto rbits; readbits(dumpmap); i = 0; for (k = 0; xtrlist[k].x_flags; k++) { if (BIT(xtrlist[k].x_ino, dumpmap)) { xtrlist[k].x_flags |= ONTAPE; i++; } } while (i > 0) { again: if (ishead(&spcl) == 0) while(gethead(&spcl) == 0) ; if (checktype(&spcl, TS_END) == 1) { printf("end of tape\n"); checkdone: for (k = 0; xtrlist[k].x_flags; k++) if ((xtrlist[k].x_flags&XTRACTD) == 0) goto newvol; return; } if (checktype(&spcl, TS_INODE) == 0) { gethead(&spcl); goto again; } d = spcl.c_inumber; for (k = 0; xtrlist[k].x_flags; k++) { if (d == xtrlist[k].x_ino) { printf("extract file %u\n", xtrlist[k].x_ino); sprintf(name, "%u", xtrlist[k].x_ino); if ((ofile = creat(name, 0666)) < 0) { printf("%s: cannot create file\n", name); i--; continue; } chown(name, spcl.c_dinode.di_uid, spcl.c_dinode.di_gid); getfile(ino, xtrfile, skip, spcl.c_dinode.di_size); i--; xtrlist[k].x_flags |= XTRACTD; close(ofile); goto done; } } gethead(&spcl); done: ; } goto checkdone; #endif case 'r': case 'R': #ifndef STANDALONE if ((fi = open(*argv, 2)) < 0) { printf("%s: cannot open\n", *argv); exit(1); } #else do { char charbuf[50]; printf("Disk? "); gets(charbuf); fi = open(charbuf, 2); } while (fi == -1); #endif #ifndef STANDALONE if (command == 'R') { printf("Enter starting volume number: "); if (gets(tbf) == EOF) { volno = 1; printf("\n"); } else volno = atoi(tbf); } else #endif volno = 1; printf("Last chance before scribbling on %s. ", #ifdef STANDALONE "disk"); #else *argv); #endif while (getchar() != '\n'); dread((daddr_t)1, (char *)&sblock, sizeof(sblock)); maxi = (sblock.s_isize-2)*INOPB; if (readhdr(&spcl) == 0) { printf("Missing volume record\n"); exit(1); } if (checkvol(&spcl, volno) == 0) { printf("Tape is not volume %d\n", volno); exit(1); } gethead(&spcl); for (;;) { ragain: if (ishead(&spcl) == 0) { printf("Missing header block\n"); while (gethead(&spcl) == 0) ; eflag++; } if (checktype(&spcl, TS_END) == 1) { printf("End of tape\n"); close(mt); dwrite( (daddr_t) 1, (char *) &sblock); return; } if (checktype(&spcl, TS_CLRI) == 1) { readbits(clrimap); for (ino = 1; ino <= maxi; ino++) if (BIT(ino, clrimap) == 0) { getdino(ino, &tino); if (tino.di_mode == 0) continue; itrunc(&tino); clri(&tino); putdino(ino, &tino); } dwrite( (daddr_t) 1, (char *) &sblock); goto ragain; } if (checktype(&spcl, TS_BITS) == 1) { readbits(dumpmap); goto ragain; } if (checktype(&spcl, TS_INODE) == 0) { printf("Unknown header type\n"); eflag++; gethead(&spcl); goto ragain; } ino = spcl.c_inumber; if (eflag) printf("Resynced at inode %u\n", ino); eflag = 0; if (ino > maxi) { printf("%u: ilist too small\n", ino); gethead(&spcl); goto ragain; } dino = spcl.c_dinode; getdino(ino, &tino); curbno = 0; itrunc(&tino); clri(&tino); for (i = 0; i < NADDR; i++) taddr[i] = 0; l3tol(taddr, dino.di_addr, 1); getfile(ino, rstrfile, rstrskip, dino.di_size); ip = &tino; ltol3(ip->di_addr, taddr, NADDR); ip1 = &dino; ip->di_mode = ip1->di_mode; ip->di_nlink = ip1->di_nlink; ip->di_uid = ip1->di_uid; ip->di_gid = ip1->di_gid; ip->di_size = ip1->di_size; ip->di_atime = ip1->di_atime; ip->di_mtime = ip1->di_mtime; ip->di_ctime = ip1->di_ctime; putdino(ino, &tino); } } } /* * Read the tape, bulding up a directory structure for extraction * by name */ #ifndef STANDALONE pass1() { register i; struct dinode *ip; int putdir(), null(); while (gethead(&spcl) == 0) { printf("Can't find directory header!\n"); } for (;;) { if (checktype(&spcl, TS_BITS) == 1) { readbits(dumpmap); continue; } if (checktype(&spcl, TS_CLRI) == 1) { readbits(clrimap); continue; } if (checktype(&spcl, TS_INODE) == 0) { finish: flsh(); close(mt); return; } ip = &spcl.c_dinode; i = ip->di_mode & IFMT; if (i != IFDIR) { goto finish; } inotab[ipos].t_ino = spcl.c_inumber; inotab[ipos++].t_seekpt = seekpt; getfile(spcl.c_inumber, putdir, null, spcl.c_dinode.di_size); putent("\000\000/"); } } #endif /* * Do the file extraction, calling the supplied functions * with the blocks */ getfile(n, f1, f2, size) ino_t n; int (*f2)(), (*f1)(); long size; { register i; struct spcl addrblock; char buf[BSIZE]; addrblock = spcl; curino = n; goto start; for (;;) { if (gethead(&addrblock) == 0) { printf("Missing address (header) block\n"); goto eloop; } if (checktype(&addrblock, TS_ADDR) == 0) { spcl = addrblock; curino = 0; return; } start: for (i = 0; i < addrblock.c_count; i++) { if (addrblock.c_addr[i]) { readtape(buf); (*f1)(buf, size > BSIZE ? (long) BSIZE : size); } else { clearbuf(buf); (*f2)(buf, size > BSIZE ? (long) BSIZE : size); } if ((size -= BSIZE) <= 0) { eloop: while (gethead(&spcl) == 0) ; if (checktype(&spcl, TS_ADDR) == 1) goto eloop; curino = 0; return; } } } } /* * Do the tape i\/o, dealling with volume changes * etc.. */ readtape(b) char *b; { register i; struct spcl tmpbuf; if (bct >= NTREC) { for (i = 0; i < NTREC; i++) ((struct spcl *)&tbf[i*BSIZE])->c_magic = 0; bct = 0; if ((i = read(mt, tbf, NTREC*BSIZE)) < 0) { printf("Tape read error: inode %u\n", curino); eflag++; for (i = 0; i < NTREC; i++) clearbuf(&tbf[i*BSIZE]); } if (i == 0) { bct = NTREC + 1; volno++; loop: flsht(); close(mt); printf("Mount volume %d\n", volno); while (getchar() != '\n') ; if ((mt = open(magtape, 0)) == -1) { printf("Cannot open tape!\n"); goto loop; } if (readhdr(&tmpbuf) == 0) { printf("Not a dump tape.Try again\n"); goto loop; } if (checkvol(&tmpbuf, volno) == 0) { printf("Wrong tape. Try again\n"); goto loop; } readtape(b); return; } } copy(&tbf[(bct++*BSIZE)], b, BSIZE); } flsht() { bct = NTREC+1; } copy(f, t, s) register char *f, *t; { register i; i = s; do *t++ = *f++; while (--i); } clearbuf(cp) register char *cp; { register i; i = BSIZE; do *cp++ = 0; while (--i); } /* * Put and get the directory entries from the compressed * directory file */ #ifndef STANDALONE putent(cp) char *cp; { register i; for (i = 0; i < sizeof(ino_t); i++) writec(*cp++); for (i = 0; i < DIRSIZ; i++) { writec(*cp); if (*cp++ == 0) return; } return; } getent(bf) register char *bf; { register i; for (i = 0; i < sizeof(ino_t); i++) *bf++ = readc(); for (i = 0; i < DIRSIZ; i++) if ((*bf++ = readc()) == 0) return; return; } /* * read/write te directory file */ writec(c) char c; { drblock[bpt++] = c; seekpt++; if (bpt >= BSIZE) { bpt = 0; write(df, drblock, BSIZE); } } readc() { if (bpt >= BSIZE) { read(df, drblock, BSIZE); bpt = 0; } return(drblock[bpt++]); } mseek(pt) daddr_t pt; { bpt = BSIZE; lseek(df, pt, 0); } flsh() { write(df, drblock, bpt+1); } /* * search the directory inode ino * looking for entry cp */ ino_t search(inum, cp) ino_t inum; char *cp; { register i; struct direct dir; for (i = 0; i < MAXINO; i++) if (inotab[i].t_ino == inum) { goto found; } return(0); found: mseek(inotab[i].t_seekpt); do { getent((char *)&dir); if (direq(dir.d_name, "/")) return(0); } while (direq(dir.d_name, cp) == 0); return(dir.d_ino); } /* * Search the directory tree rooted at inode 2 * for the path pointed at by n */ psearch(n) char *n; { register char *cp, *cp1; char c; ino = 2; if (*(cp = n) == '/') cp++; next: cp1 = cp + 1; while (*cp1 != '/' && *cp1) cp1++; c = *cp1; *cp1 = 0; ino = search(ino, cp); if (ino == 0) { *cp1 = c; return(0); } *cp1 = c; if (c == '/') { cp = cp1+1; goto next; } return(ino); } direq(s1, s2) register char *s1, *s2; { register i; for (i = 0; i < DIRSIZ; i++) if (*s1++ == *s2) { if (*s2++ == 0) return(1); } else return(0); return(1); } #endif /* * read/write a disk block, be sure to update the buffer * cache if needed. */ dwrite(bno, b) daddr_t bno; char *b; { register i; for (i = 0; i < NCACHE; i++) { if (cache[i].c_bno == bno) { copy(b, cache[i].c_block, BSIZE); cache[i].c_time = 0; break; } else cache[i].c_time++; } lseek(fi, bno*BSIZE, 0); if(write(fi, b, BSIZE) != BSIZE) { #ifdef STANDALONE printf("disk write error %D\n", bno); #else fprintf(stderr, "disk write error %ld\n", bno); #endif exit(1); } } dread(bno, buf, cnt) daddr_t bno; char *buf; { register i, j; j = 0; for (i = 0; i < NCACHE; i++) { if (++curcache >= NCACHE) curcache = 0; if (cache[curcache].c_bno == bno) { copy(cache[curcache].c_block, buf, cnt); cache[curcache].c_time = 0; return; } else { cache[curcache].c_time++; if (cache[j].c_time < cache[curcache].c_time) j = curcache; } } lseek(fi, bno*BSIZE, 0); if (read(fi, cache[j].c_block, BSIZE) != BSIZE) { #ifdef STANDALONE printf("read error %D\n", bno); #else printf("read error %ld\n", bno); #endif exit(1); } copy(cache[j].c_block, buf, cnt); cache[j].c_time = 0; cache[j].c_bno = bno; } /* * the inode manpulation routines. Like the system. * * clri zeros the inode */ clri(ip) struct dinode *ip; { int i, *p; i = sizeof(struct dinode)/sizeof(int); p = (int *)ip; do *p++ = 0; while(--i); } /* * itrunc/tloop/bfree free all of the blocks pointed at by the inode */ itrunc(ip) register struct dinode *ip; { register i; daddr_t bn, iaddr[NADDR]; if (ip->di_mode == 0) return; i = ip->di_mode & IFMT; if (i != IFDIR && i != IFREG) return; l3tol(iaddr, ip->di_addr, NADDR); for(i=NADDR-1;i>=0;i--) { bn = iaddr[i]; if(bn == 0) continue; switch(i) { default: bfree(bn); break; case NADDR-3: tloop(bn, 0, 0); break; case NADDR-2: tloop(bn, 1, 0); break; case NADDR-1: tloop(bn, 1, 1); } } ip->di_size = 0; } tloop(bn, f1, f2) daddr_t bn; int f1, f2; { register i; daddr_t nb; union { char data[BSIZE]; daddr_t indir[NINDIR]; } ibuf; dread(bn, ibuf.data, BSIZE); for(i=NINDIR-1;i>=0;i--) { nb = ibuf.indir[i]; if(nb) { if(f1) tloop(nb, f2, 0); else bfree(nb); } } bfree(bn); } bfree(bn) daddr_t bn; { register i; union { char data[BSIZE]; struct fblk frees; } fbuf; if(sblock.s_nfree >= NICFREE) { fbuf.df_nfree = sblock.s_nfree; for(i=0;i0; j--) { sh += NSHIFT; nb <<= NSHIFT; if(bn < nb) break; bn -= nb; } if(j == 0) { return((daddr_t)0); } /* * fetch the address from the inode */ if((nb = iaddr[NADDR-j]) == 0) { iaddr[NADDR-j] = nb = balloc(); } /* * fetch through the indirect blocks */ for(; j<=3; j++) { dread(nb, (char *)indir, BSIZE); sh -= NSHIFT; i = (bn>>sh) & NMASK; nnb = indir[i]; if(nnb == 0) { nnb = balloc(); indir[i] = nnb; dwrite(nb, (char *)indir); } nb = nnb; } return(nb); } /* * read the tape into buf, then return whether or * or not it is a header block. */ gethead(buf) struct spcl *buf; { readtape((char *)buf); if (buf->c_magic != MAGIC || checksum((int *) buf) == 0) return(0); return(1); } /* * return whether or not the buffer contains a header block */ ishead(buf) struct spcl *buf; { if (buf->c_magic != MAGIC || checksum((int *) buf) == 0) return(0); return(1); } checktype(b, t) struct spcl *b; int t; { return(b->c_type == t); } checksum(b) int *b; { register i, j; j = BSIZE/sizeof(int); i = 0; do i += *b++; while (--j); if (i != CHECKSUM) { printf("Checksum error %o\n", i); return(0); } return(1); } checkvol(b, t) struct spcl *b; int t; { if (b->c_volume == t) return(1); return(0); } readhdr(b) struct spcl *b; { if (gethead(b) == 0) return(0); if (checktype(b, TS_TAPE) == 0) return(0); return(1); } /* * The next routines are called during file extraction to * put the data into the right form and place. */ #ifndef STANDALONE xtrfile(b, size) char *b; long size; { write(ofile, b, (int) size); } null() {;} skip() { lseek(ofile, (long) BSIZE, 1); } #endif rstrfile(b, s) char *b; long s; { daddr_t d; d = bmap(taddr, curbno); dwrite(d, b); curbno += 1; } rstrskip(b, s) char *b; long s; { curbno += 1; } #ifndef STANDALONE putdir(b) char *b; { register struct direct *dp; register i; for (dp = (struct direct *) b, i = 0; i < BSIZE; dp++, i += sizeof(*dp)) { if (dp->d_ino == 0) continue; putent((char *) dp); } } #endif /* * read/write an inode from the disk */ getdino(inum, b) ino_t inum; struct dinode *b; { daddr_t bno; char buf[BSIZE]; bno = (ino - 1)/INOPB; bno += 2; dread(bno, buf, BSIZE); copy(&buf[((inum-1)%INOPB)*sizeof(struct dinode)], (char *) b, sizeof(struct dinode)); } putdino(inum, b) ino_t inum; struct dinode *b; { daddr_t bno; char buf[BSIZE]; bno = ((ino - 1)/INOPB) + 2; dread(bno, buf, BSIZE); copy((char *) b, &buf[((inum-1)%INOPB)*sizeof(struct dinode)], sizeof(struct dinode)); dwrite(bno, buf); } /* * read a bit mask from the tape into m. */ readbits(m) short *m; { register i; i = spcl.c_count; while (i--) { readtape((char *) m); m += (BSIZE/(MLEN/BITS)); } while (gethead(&spcl) == 0) ; } done() { #ifndef STANDALONE unlink(dirfile); #endif exit(0); }