Research V7 development
authorKen Thompson <ken@research.uucp>
Thu, 24 May 1979 06:41:26 +0000 (01:41 -0500)
committerKen Thompson <ken@research.uucp>
Thu, 24 May 1979 06:41:26 +0000 (01:41 -0500)
Work on file usr/src/cmd/restor.c

Co-Authored-By: Dennis Ritchie <dmr@research.uucp>
Synthesized-from: v7

usr/src/cmd/restor.c [new file with mode: 0644]

diff --git a/usr/src/cmd/restor.c b/usr/src/cmd/restor.c
new file mode 100644 (file)
index 0000000..9dc1b40
--- /dev/null
@@ -0,0 +1,1147 @@
+#define MAXINO 3000
+#define BITS   8
+#define MAXXTR 60
+#define NCACHE 3
+
+#ifndef STANDALONE
+#include <stdio.h>
+#include <signal.h>
+#endif
+#include <sys/param.h>
+#include <sys/inode.h>
+#include <sys/ino.h>
+#include <sys/fblk.h>
+#include <sys/filsys.h>
+#include <sys/dir.h>
+#include <dumprestor.h>
+
+#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;i<NICFREE;i++)
+                       fbuf.df_free[i] = sblock.s_free[i];
+               sblock.s_nfree = 0;
+               dwrite(bn, fbuf.data);
+       }
+       sblock.s_free[sblock.s_nfree++] = bn;
+}
+
+/*
+ * allocate a block off the free list.
+ */
+daddr_t
+balloc()
+{
+       daddr_t bno;
+       register i;
+       static char zeroes[BSIZE];
+       union {
+               char    data[BSIZE];
+               struct  fblk frees;
+       } fbuf;
+
+       if(sblock.s_nfree == 0 || (bno=sblock.s_free[--sblock.s_nfree]) == 0) {
+#ifdef STANDALONE
+               printf("Out of space\n");
+#else
+               fprintf(stderr, "Out of space.\n");
+#endif
+               exit(1);
+       }
+       if(sblock.s_nfree == 0) {
+               dread(bno, fbuf.data, BSIZE);
+               sblock.s_nfree = fbuf.df_nfree;
+               for(i=0;i<NICFREE;i++)
+                       sblock.s_free[i] = fbuf.df_free[i];
+       }
+       dwrite(bno, zeroes);
+       return(bno);
+}
+
+/*
+ * map a block number into a block address, ensuring
+ * all of the correct indirect blocks are around. Allocate
+ * the block requested.
+ */
+daddr_t
+bmap(iaddr, bn)
+daddr_t        iaddr[NADDR];
+daddr_t        bn;
+{
+       register i;
+       int j, sh;
+       daddr_t nb, nnb;
+       daddr_t indir[NINDIR];
+
+       /*
+        * blocks 0..NADDR-4 are direct blocks
+        */
+       if(bn < NADDR-3) {
+               iaddr[bn] = nb = balloc();
+               return(nb);
+       }
+
+       /*
+        * addresses NADDR-3, NADDR-2, and NADDR-1
+        * have single, double, triple indirect blocks.
+        * the first step is to determine
+        * how many levels of indirection.
+        */
+       sh = 0;
+       nb = 1;
+       bn -= NADDR-3;
+       for(j=3; j>0; 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);
+}