+/*
+ * Copyright (c) 1983 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)tape.c 5.21 (Berkeley) 2/22/91";
+#endif /* not lint */
+
+#include "restore.h"
+#include <protocols/dumprestore.h>
+#include <sys/ioctl.h>
+#include <sys/mtio.h>
+#include <sys/file.h>
+#include <setjmp.h>
+#include <sys/stat.h>
+#include "pathnames.h"
+
+static long fssize = MAXBSIZE;
+static int mt = -1;
+static int pipein = 0;
+static char magtape[BUFSIZ];
+static int bct;
+static int numtrec;
+static char *tbf;
+static union u_spcl endoftapemark;
+static long blksread;
+static long tapesread;
+static jmp_buf restart;
+static int gettingfile = 0; /* restart has a valid frame */
+
+static int ofile;
+static char *map;
+static char lnkbuf[MAXPATHLEN + 1];
+static int pathlen;
+
+int Bcvt; /* Swap Bytes (for CCI or sun) */
+static int Qcvt; /* Swap quads (for sun) */
+u_long swabl();
+/*
+ * Set up an input source
+ */
+setinput(source)
+ char *source;
+{
+ extern int errno;
+#ifdef RRESTORE
+ char *host, *tape;
+#endif RRESTORE
+ char *strerror();
+
+ flsht();
+ if (bflag)
+ newtapebuf(ntrec);
+ else
+ newtapebuf(NTREC > HIGHDENSITYTREC ? NTREC : HIGHDENSITYTREC);
+ terminal = stdin;
+#ifdef RRESTORE
+ host = source;
+ tape = index(host, ':');
+ if (tape == 0) {
+nohost:
+ msg("need keyletter ``f'' and device ``host:tape''\n");
+ done(1);
+ }
+ *tape++ = '\0';
+ (void) strcpy(magtape, tape);
+ if (rmthost(host) == 0)
+ done(1);
+ setuid(getuid()); /* no longer need or want root privileges */
+#else
+ if (strcmp(source, "-") == 0) {
+ /*
+ * Since input is coming from a pipe we must establish
+ * our own connection to the terminal.
+ */
+ terminal = fopen(_PATH_TTY, "r");
+ if (terminal == NULL) {
+ (void)fprintf(stderr, "Cannot open %s: %s\n",
+ _PATH_TTY, strerror(errno));
+ terminal = fopen(_PATH_DEVNULL, "r");
+ if (terminal == NULL) {
+ (void)fprintf(stderr, "Cannot open %s: %s\n",
+ _PATH_DEVNULL, strerror(errno));
+ done(1);
+ }
+ }
+ pipein++;
+ }
+ (void) strcpy(magtape, source);
+#endif RRESTORE
+}
+
+newtapebuf(size)
+ long size;
+{
+ static tbfsize = -1;
+
+ ntrec = size;
+ if (size <= tbfsize)
+ return;
+ if (tbf != NULL)
+ free(tbf);
+ tbf = (char *)malloc(size * TP_BSIZE);
+ if (tbf == NULL) {
+ fprintf(stderr, "Cannot allocate space for tape buffer\n");
+ done(1);
+ }
+ tbfsize = size;
+}
+
+/*
+ * Verify that the tape drive can be accessed and
+ * that it actually is a dump tape.
+ */
+setup()
+{
+ int i, j, *ip;
+ struct stat stbuf;
+ extern int xtrmap(), xtrmapskip();
+
+ vprintf(stdout, "Verify tape and initialize maps\n");
+#ifdef RRESTORE
+ if ((mt = rmtopen(magtape, 0)) < 0)
+#else
+ if (pipein)
+ mt = 0;
+ else if ((mt = open(magtape, 0)) < 0)
+#endif
+ {
+ perror(magtape);
+ done(1);
+ }
+ volno = 1;
+ setdumpnum();
+ flsht();
+ if (!pipein && !bflag)
+ findtapeblksize();
+ if (gethead(&spcl) == FAIL) {
+ bct--; /* push back this block */
+ cvtflag++;
+ if (gethead(&spcl) == FAIL) {
+ fprintf(stderr, "Tape is not a dump tape\n");
+ done(1);
+ }
+ fprintf(stderr, "Converting to new file system format.\n");
+ }
+ if (pipein) {
+ endoftapemark.s_spcl.c_magic = cvtflag ? OFS_MAGIC : NFS_MAGIC;
+ endoftapemark.s_spcl.c_type = TS_END;
+ ip = (int *)&endoftapemark;
+ j = sizeof(union u_spcl) / sizeof(int);
+ i = 0;
+ do
+ i += *ip++;
+ while (--j);
+ endoftapemark.s_spcl.c_checksum = CHECKSUM - i;
+ }
+ if (vflag || command == 't')
+ printdumpinfo();
+ dumptime = spcl.c_ddate;
+ dumpdate = spcl.c_date;
+ if (stat(".", &stbuf) < 0) {
+ perror("cannot stat .");
+ done(1);
+ }
+ if (stbuf.st_blksize > 0 && stbuf.st_blksize <= MAXBSIZE)
+ fssize = stbuf.st_blksize;
+ if (((fssize - 1) & fssize) != 0) {
+ fprintf(stderr, "bad block size %d\n", fssize);
+ done(1);
+ }
+ if (checkvol(&spcl, (long)1) == FAIL) {
+ fprintf(stderr, "Tape is not volume 1 of the dump\n");
+ done(1);
+ }
+ if (readhdr(&spcl) == FAIL)
+ panic("no header after volume mark!\n");
+ findinode(&spcl);
+ if (checktype(&spcl, TS_CLRI) == FAIL) {
+ fprintf(stderr, "Cannot find file removal list\n");
+ done(1);
+ }
+ maxino = (spcl.c_count * TP_BSIZE * NBBY) + 1;
+ dprintf(stdout, "maxino = %d\n", maxino);
+ map = calloc((unsigned)1, (unsigned)howmany(maxino, NBBY));
+ if (map == (char *)NIL)
+ panic("no memory for file removal list\n");
+ clrimap = map;
+ curfile.action = USING;
+ getfile(xtrmap, xtrmapskip);
+ if (checktype(&spcl, TS_BITS) == FAIL) {
+ fprintf(stderr, "Cannot find file dump list\n");
+ done(1);
+ }
+ map = calloc((unsigned)1, (unsigned)howmany(maxino, NBBY));
+ if (map == (char *)NULL)
+ panic("no memory for file dump list\n");
+ dumpmap = map;
+ curfile.action = USING;
+ getfile(xtrmap, xtrmapskip);
+}
+
+/*
+ * Prompt user to load a new dump volume.
+ * "Nextvol" is the next suggested volume to use.
+ * This suggested volume is enforced when doing full
+ * or incremental restores, but can be overrridden by
+ * the user when only extracting a subset of the files.
+ */
+getvol(nextvol)
+ long nextvol;
+{
+ long newvol;
+ long savecnt, i;
+ union u_spcl tmpspcl;
+# define tmpbuf tmpspcl.s_spcl
+ char buf[TP_BSIZE];
+ extern char *ctime();
+
+ if (nextvol == 1) {
+ tapesread = 0;
+ gettingfile = 0;
+ }
+ if (pipein) {
+ if (nextvol != 1)
+ panic("Changing volumes on pipe input?\n");
+ if (volno == 1)
+ return;
+ goto gethdr;
+ }
+ savecnt = blksread;
+again:
+ if (pipein)
+ done(1); /* pipes do not get a second chance */
+ if (command == 'R' || command == 'r' || curfile.action != SKIP)
+ newvol = nextvol;
+ else
+ newvol = 0;
+ while (newvol <= 0) {
+ if (tapesread == 0) {
+ fprintf(stderr, "%s%s%s%s%s",
+ "You have not read any tapes yet.\n",
+ "Unless you know which volume your",
+ " file(s) are on you should start\n",
+ "with the last volume and work",
+ " towards towards the first.\n");
+ } else {
+ fprintf(stderr, "You have read volumes");
+ strcpy(tbf, ": ");
+ for (i = 1; i < 32; i++)
+ if (tapesread & (1 << i)) {
+ fprintf(stderr, "%s%d", tbf, i);
+ strcpy(tbf, ", ");
+ }
+ fprintf(stderr, "\n");
+ }
+ do {
+ fprintf(stderr, "Specify next volume #: ");
+ (void) fflush(stderr);
+ (void) fgets(tbf, BUFSIZ, terminal);
+ } while (!feof(terminal) && tbf[0] == '\n');
+ if (feof(terminal))
+ done(1);
+ newvol = atoi(tbf);
+ if (newvol <= 0) {
+ fprintf(stderr,
+ "Volume numbers are positive numerics\n");
+ }
+ }
+ if (newvol == volno) {
+ tapesread |= 1 << volno;
+ return;
+ }
+ closemt();
+ fprintf(stderr, "Mount tape volume %d\n", newvol);
+ fprintf(stderr, "Enter ``none'' if there are no more tapes\n");
+ fprintf(stderr, "otherwise enter tape name (default: %s) ", magtape);
+ (void) fflush(stderr);
+ (void) fgets(tbf, BUFSIZ, terminal);
+ if (feof(terminal))
+ done(1);
+ if (!strcmp(tbf, "none\n")) {
+ curfile.name = "<name unknown>";
+ curfile.action = UNKNOWN;
+ curfile.dip = (struct dinode *)NIL;
+ curfile.ino = maxino;
+ if (gettingfile) {
+ gettingfile = 0;
+ longjmp(restart, 1);
+ }
+ }
+ if (tbf[0] != '\n') {
+ (void) strcpy(magtape, tbf);
+ magtape[strlen(magtape) - 1] = '\0';
+ }
+#ifdef RRESTORE
+ if ((mt = rmtopen(magtape, 0)) == -1)
+#else
+ if ((mt = open(magtape, 0)) == -1)
+#endif
+ {
+ fprintf(stderr, "Cannot open %s\n", magtape);
+ volno = -1;
+ goto again;
+ }
+gethdr:
+ volno = newvol;
+ setdumpnum();
+ flsht();
+ if (readhdr(&tmpbuf) == FAIL) {
+ fprintf(stderr, "tape is not dump tape\n");
+ volno = 0;
+ goto again;
+ }
+ if (checkvol(&tmpbuf, volno) == FAIL) {
+ fprintf(stderr, "Wrong volume (%d)\n", tmpbuf.c_volume);
+ volno = 0;
+ goto again;
+ }
+ if (tmpbuf.c_date != dumpdate || tmpbuf.c_ddate != dumptime) {
+ fprintf(stderr, "Wrong dump date\n\tgot: %s",
+ ctime(&tmpbuf.c_date));
+ fprintf(stderr, "\twanted: %s", ctime(&dumpdate));
+ volno = 0;
+ goto again;
+ }
+ tapesread |= 1 << volno;
+ blksread = savecnt;
+ if (curfile.action == USING) {
+ if (volno == 1)
+ panic("active file into volume 1\n");
+ return;
+ }
+ /*
+ * Skip up to the beginning of the next record
+ */
+ if (tmpbuf.c_type == TS_TAPE && (tmpbuf.c_flags & DR_NEWHEADER))
+ for (i = tmpbuf.c_count; i > 0; i--)
+ readtape(buf);
+ (void) gethead(&spcl);
+ findinode(&spcl);
+ if (gettingfile) {
+ gettingfile = 0;
+ longjmp(restart, 1);
+ }
+}
+
+/*
+ * handle multiple dumps per tape by skipping forward to the
+ * appropriate one.
+ */
+setdumpnum()
+{
+ struct mtop tcom;
+
+ if (dumpnum == 1 || volno != 1)
+ return;
+ if (pipein) {
+ fprintf(stderr, "Cannot have multiple dumps on pipe input\n");
+ done(1);
+ }
+ tcom.mt_op = MTFSF;
+ tcom.mt_count = dumpnum - 1;
+#ifdef RRESTORE
+ rmtioctl(MTFSF, dumpnum - 1);
+#else
+ if (ioctl(mt, (int)MTIOCTOP, (char *)&tcom) < 0)
+ perror("ioctl MTFSF");
+#endif
+}
+
+printdumpinfo()
+{
+ extern char *ctime();
+
+ fprintf(stdout, "Dump date: %s", ctime(&spcl.c_date));
+ fprintf(stdout, "Dumped from: %s",
+ (spcl.c_ddate == 0) ? "the epoch\n" : ctime(&spcl.c_ddate));
+ if (spcl.c_host[0] == '\0')
+ return;
+ fprintf(stderr, "Level %d dump of %s on %s:%s\n",
+ spcl.c_level, spcl.c_filesys, spcl.c_host, spcl.c_dev);
+ fprintf(stderr, "Label: %s\n", spcl.c_label);
+}
+
+extractfile(name)
+ char *name;
+{
+ int mode;
+ struct timeval timep[2];
+ struct entry *ep;
+ extern int xtrlnkfile(), xtrlnkskip();
+ extern int xtrfile(), xtrskip();
+
+ curfile.name = name;
+ curfile.action = USING;
+ timep[0].tv_sec = curfile.dip->di_atime;
+ timep[0].tv_usec = 0;
+ timep[1].tv_sec = curfile.dip->di_mtime;
+ timep[1].tv_usec = 0;
+ mode = curfile.dip->di_mode;
+ switch (mode & IFMT) {
+
+ default:
+ fprintf(stderr, "%s: unknown file mode 0%o\n", name, mode);
+ skipfile();
+ return (FAIL);
+
+ case IFSOCK:
+ vprintf(stdout, "skipped socket %s\n", name);
+ skipfile();
+ return (GOOD);
+
+ case IFDIR:
+ if (mflag) {
+ ep = lookupname(name);
+ if (ep == NIL || ep->e_flags & EXTRACT)
+ panic("unextracted directory %s\n", name);
+ skipfile();
+ return (GOOD);
+ }
+ vprintf(stdout, "extract file %s\n", name);
+ return (genliteraldir(name, curfile.ino));
+
+ case IFLNK:
+ lnkbuf[0] = '\0';
+ pathlen = 0;
+ getfile(xtrlnkfile, xtrlnkskip);
+ if (pathlen == 0) {
+ vprintf(stdout,
+ "%s: zero length symbolic link (ignored)\n", name);
+ return (GOOD);
+ }
+ return (linkit(lnkbuf, name, SYMLINK));
+
+ case IFCHR:
+ case IFBLK:
+ vprintf(stdout, "extract special file %s\n", name);
+ if (Nflag) {
+ skipfile();
+ return (GOOD);
+ }
+ if (mknod(name, mode, (int)curfile.dip->di_rdev) < 0) {
+ fprintf(stderr, "%s: ", name);
+ (void) fflush(stderr);
+ perror("cannot create special file");
+ skipfile();
+ return (FAIL);
+ }
+ (void) chown(name, curfile.dip->di_uid, curfile.dip->di_gid);
+ (void) chmod(name, mode);
+ skipfile();
+ utimes(name, timep);
+ return (GOOD);
+
+ case IFREG:
+ vprintf(stdout, "extract file %s\n", name);
+ if (Nflag) {
+ skipfile();
+ return (GOOD);
+ }
+ if ((ofile = creat(name, 0666)) < 0) {
+ fprintf(stderr, "%s: ", name);
+ (void) fflush(stderr);
+ perror("cannot create file");
+ skipfile();
+ return (FAIL);
+ }
+ (void) fchown(ofile, curfile.dip->di_uid, curfile.dip->di_gid);
+ (void) fchmod(ofile, mode);
+ getfile(xtrfile, xtrskip);
+ (void) close(ofile);
+ utimes(name, timep);
+ return (GOOD);
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * skip over bit maps on the tape
+ */
+skipmaps()
+{
+
+ while (checktype(&spcl, TS_CLRI) == GOOD ||
+ checktype(&spcl, TS_BITS) == GOOD)
+ skipfile();
+}
+
+/*
+ * skip over a file on the tape
+ */
+skipfile()
+{
+ extern int null();
+
+ curfile.action = SKIP;
+ getfile(null, null);
+}
+
+/*
+ * Do the file extraction, calling the supplied functions
+ * with the blocks
+ */
+getfile(f1, f2)
+ int (*f2)(), (*f1)();
+{
+ register int i;
+ int curblk = 0;
+ off_t size = spcl.c_dinode.di_size;
+ static char clearedbuf[MAXBSIZE];
+ char buf[MAXBSIZE / TP_BSIZE][TP_BSIZE];
+ char junk[TP_BSIZE];
+
+ if (checktype(&spcl, TS_END) == GOOD)
+ panic("ran off end of tape\n");
+ if (ishead(&spcl) == FAIL)
+ panic("not at beginning of a file\n");
+ if (!gettingfile && setjmp(restart) != 0)
+ return;
+ gettingfile++;
+loop:
+ for (i = 0; i < spcl.c_count; i++) {
+ if (spcl.c_addr[i]) {
+ readtape(&buf[curblk++][0]);
+ if (curblk == fssize / TP_BSIZE) {
+ (*f1)(buf, size > TP_BSIZE ?
+ (long) (fssize) :
+ (curblk - 1) * TP_BSIZE + size);
+ curblk = 0;
+ }
+ } else {
+ if (curblk > 0) {
+ (*f1)(buf, size > TP_BSIZE ?
+ (long) (curblk * TP_BSIZE) :
+ (curblk - 1) * TP_BSIZE + size);
+ curblk = 0;
+ }
+ (*f2)(clearedbuf, size > TP_BSIZE ?
+ (long) TP_BSIZE : size);
+ }
+ if ((size -= TP_BSIZE) <= 0) {
+ for (i++; i < spcl.c_count; i++)
+ if (spcl.c_addr[i])
+ readtape(junk);
+ break;
+ }
+ }
+ if (readhdr(&spcl) == GOOD && size > 0) {
+ if (checktype(&spcl, TS_ADDR) == GOOD)
+ goto loop;
+ dprintf(stdout, "Missing address (header) block for %s\n",
+ curfile.name);
+ }
+ if (curblk > 0)
+ (*f1)(buf, (curblk * TP_BSIZE) + size);
+ findinode(&spcl);
+ gettingfile = 0;
+}
+
+/*
+ * The next routines are called during file extraction to
+ * put the data into the right form and place.
+ */
+xtrfile(buf, size)
+ char *buf;
+ long size;
+{
+
+ if (Nflag)
+ return;
+ if (write(ofile, buf, (int) size) == -1) {
+ fprintf(stderr, "write error extracting inode %d, name %s\n",
+ curfile.ino, curfile.name);
+ perror("write");
+ done(1);
+ }
+}
+
+xtrskip(buf, size)
+ char *buf;
+ long size;
+{
+
+#ifdef lint
+ buf = buf;
+#endif
+ if (lseek(ofile, size, 1) == (long)-1) {
+ fprintf(stderr, "seek error extracting inode %d, name %s\n",
+ curfile.ino, curfile.name);
+ perror("lseek");
+ done(1);
+ }
+}
+
+xtrlnkfile(buf, size)
+ char *buf;
+ long size;
+{
+
+ pathlen += size;
+ if (pathlen > MAXPATHLEN) {
+ fprintf(stderr, "symbolic link name: %s->%s%s; too long %d\n",
+ curfile.name, lnkbuf, buf, pathlen);
+ done(1);
+ }
+ (void) strcat(lnkbuf, buf);
+}
+
+xtrlnkskip(buf, size)
+ char *buf;
+ long size;
+{
+
+#ifdef lint
+ buf = buf, size = size;
+#endif
+ fprintf(stderr, "unallocated block in symbolic link %s\n",
+ curfile.name);
+ done(1);
+}
+
+xtrmap(buf, size)
+ char *buf;
+ long size;
+{
+
+ bcopy(buf, map, size);
+ map += size;
+}
+
+xtrmapskip(buf, size)
+ char *buf;
+ long size;
+{
+
+#ifdef lint
+ buf = buf;
+#endif
+ panic("hole in map\n");
+ map += size;
+}
+
+null() {;}
+
+/*
+ * Do the tape i/o, dealing with volume changes
+ * etc..
+ */
+readtape(b)
+ char *b;
+{
+ register long i;
+ long rd, newvol;
+ int cnt;
+
+top:
+ if (bct < numtrec) {
+ bcopy(&tbf[(bct++*TP_BSIZE)], b, (long)TP_BSIZE);
+ blksread++;
+ return;
+ }
+ for (i = 0; i < ntrec; i++)
+ ((struct s_spcl *)&tbf[i*TP_BSIZE])->c_magic = 0;
+ if (numtrec == 0)
+ numtrec = ntrec;
+ cnt = ntrec*TP_BSIZE;
+ rd = 0;
+getmore:
+#ifdef RRESTORE
+ i = rmtread(&tbf[rd], cnt);
+#else
+ i = read(mt, &tbf[rd], cnt);
+#endif
+ /*
+ * Check for mid-tape short read error.
+ * If found, return rest of buffer.
+ */
+ if (numtrec < ntrec && i != 0) {
+ numtrec = ntrec;
+ goto top;
+ }
+ /*
+ * Handle partial block read.
+ */
+ if (i > 0 && i != ntrec*TP_BSIZE) {
+ if (pipein) {
+ rd += i;
+ cnt -= i;
+ if (cnt > 0)
+ goto getmore;
+ i = rd;
+ } else {
+ if (i % TP_BSIZE != 0)
+ panic("partial block read: %d should be %d\n",
+ i, ntrec * TP_BSIZE);
+ numtrec = i / TP_BSIZE;
+ }
+ }
+ /*
+ * Handle read error.
+ */
+ if (i < 0) {
+ fprintf(stderr, "Tape read error while ");
+ switch (curfile.action) {
+ default:
+ fprintf(stderr, "trying to set up tape\n");
+ break;
+ case UNKNOWN:
+ fprintf(stderr, "trying to resynchronize\n");
+ break;
+ case USING:
+ fprintf(stderr, "restoring %s\n", curfile.name);
+ break;
+ case SKIP:
+ fprintf(stderr, "skipping over inode %d\n",
+ curfile.ino);
+ break;
+ }
+ if (!yflag && !reply("continue"))
+ done(1);
+ i = ntrec*TP_BSIZE;
+ bzero(tbf, i);
+#ifdef RRESTORE
+ if (rmtseek(i, 1) < 0)
+#else
+ if (lseek(mt, i, 1) == (long)-1)
+#endif
+ {
+ perror("continuation failed");
+ done(1);
+ }
+ }
+ /*
+ * Handle end of tape.
+ */
+ if (i == 0) {
+ if (!pipein) {
+ newvol = volno + 1;
+ volno = 0;
+ numtrec = 0;
+ getvol(newvol);
+ readtape(b);
+ return;
+ }
+ if (rd % TP_BSIZE != 0)
+ panic("partial block read: %d should be %d\n",
+ rd, ntrec * TP_BSIZE);
+ bcopy((char *)&endoftapemark, &tbf[rd], (long)TP_BSIZE);
+ }
+ bct = 0;
+ bcopy(&tbf[(bct++*TP_BSIZE)], b, (long)TP_BSIZE);
+ blksread++;
+}
+
+findtapeblksize()
+{
+ register long i;
+
+ for (i = 0; i < ntrec; i++)
+ ((struct s_spcl *)&tbf[i * TP_BSIZE])->c_magic = 0;
+ bct = 0;
+#ifdef RRESTORE
+ i = rmtread(tbf, ntrec * TP_BSIZE);
+#else
+ i = read(mt, tbf, ntrec * TP_BSIZE);
+#endif
+ if (i <= 0) {
+ perror("Tape read error");
+ done(1);
+ }
+ if (i % TP_BSIZE != 0) {
+ fprintf(stderr, "Tape block size (%d) %s (%d)\n",
+ i, "is not a multiple of dump block size", TP_BSIZE);
+ done(1);
+ }
+ ntrec = i / TP_BSIZE;
+ numtrec = ntrec;
+ vprintf(stdout, "Tape block size is %d\n", ntrec);
+}
+
+flsht()
+{
+
+ bct = ntrec+1;
+}
+
+closemt()
+{
+ if (mt < 0)
+ return;
+#ifdef RRESTORE
+ rmtclose();
+#else
+ (void) close(mt);
+#endif
+}
+
+checkvol(b, t)
+ struct s_spcl *b;
+ long t;
+{
+
+ if (b->c_volume != t)
+ return(FAIL);
+ return(GOOD);
+}
+
+readhdr(b)
+ struct s_spcl *b;
+{
+
+ if (gethead(b) == FAIL) {
+ dprintf(stdout, "readhdr fails at %d blocks\n", blksread);
+ return(FAIL);
+ }
+ return(GOOD);
+}
+
+/*
+ * read the tape into buf, then return whether or
+ * or not it is a header block.
+ */
+gethead(buf)
+ struct s_spcl *buf;
+{
+ long i;
+ union u_ospcl {
+ char dummy[TP_BSIZE];
+ struct s_ospcl {
+ long c_type;
+ long c_date;
+ long c_ddate;
+ long c_volume;
+ long c_tapea;
+ u_short c_inumber;
+ long c_magic;
+ long c_checksum;
+ struct odinode {
+ unsigned short odi_mode;
+ u_short odi_nlink;
+ u_short odi_uid;
+ u_short odi_gid;
+ long odi_size;
+ long odi_rdev;
+ char odi_addr[36];
+ long odi_atime;
+ long odi_mtime;
+ long odi_ctime;
+ } c_dinode;
+ long c_count;
+ char c_addr[256];
+ } s_ospcl;
+ } u_ospcl;
+
+ if (!cvtflag) {
+ readtape((char *)buf);
+ if (buf->c_magic != NFS_MAGIC) {
+ if (swabl(buf->c_magic) != NFS_MAGIC)
+ return (FAIL);
+ if (!Bcvt) {
+ vprintf(stdout, "Note: Doing Byte swapping\n");
+ Bcvt = 1;
+ }
+ }
+ if (checksum((int *)buf) == FAIL)
+ return (FAIL);
+ if (Bcvt)
+ swabst("8l4s31l", (char *)buf);
+ goto good;
+ }
+ readtape((char *)(&u_ospcl.s_ospcl));
+ bzero((char *)buf, (long)TP_BSIZE);
+ buf->c_type = u_ospcl.s_ospcl.c_type;
+ buf->c_date = u_ospcl.s_ospcl.c_date;
+ buf->c_ddate = u_ospcl.s_ospcl.c_ddate;
+ buf->c_volume = u_ospcl.s_ospcl.c_volume;
+ buf->c_tapea = u_ospcl.s_ospcl.c_tapea;
+ buf->c_inumber = u_ospcl.s_ospcl.c_inumber;
+ buf->c_checksum = u_ospcl.s_ospcl.c_checksum;
+ buf->c_magic = u_ospcl.s_ospcl.c_magic;
+ buf->c_dinode.di_mode = u_ospcl.s_ospcl.c_dinode.odi_mode;
+ buf->c_dinode.di_nlink = u_ospcl.s_ospcl.c_dinode.odi_nlink;
+ buf->c_dinode.di_uid = u_ospcl.s_ospcl.c_dinode.odi_uid;
+ buf->c_dinode.di_gid = u_ospcl.s_ospcl.c_dinode.odi_gid;
+ buf->c_dinode.di_size = u_ospcl.s_ospcl.c_dinode.odi_size;
+ buf->c_dinode.di_rdev = u_ospcl.s_ospcl.c_dinode.odi_rdev;
+ buf->c_dinode.di_atime = u_ospcl.s_ospcl.c_dinode.odi_atime;
+ buf->c_dinode.di_mtime = u_ospcl.s_ospcl.c_dinode.odi_mtime;
+ buf->c_dinode.di_ctime = u_ospcl.s_ospcl.c_dinode.odi_ctime;
+ buf->c_count = u_ospcl.s_ospcl.c_count;
+ bcopy(u_ospcl.s_ospcl.c_addr, buf->c_addr, (long)256);
+ if (u_ospcl.s_ospcl.c_magic != OFS_MAGIC ||
+ checksum((int *)(&u_ospcl.s_ospcl)) == FAIL)
+ return(FAIL);
+ buf->c_magic = NFS_MAGIC;
+
+good:
+ if (buf->c_dinode.di_size == 0 &&
+ (buf->c_dinode.di_mode & IFMT) == IFDIR && Qcvt == 0) {
+ if (buf->c_dinode.di_qsize.val[0] ||
+ buf->c_dinode.di_qsize.val[1]) {
+ printf("Note: Doing Quad swapping\n");
+ Qcvt = 1;
+ }
+ }
+ if (Qcvt) {
+ i = buf->c_dinode.di_qsize.val[1];
+ buf->c_dinode.di_qsize.val[1] = buf->c_dinode.di_qsize.val[0];
+ buf->c_dinode.di_qsize.val[0] = i;
+ }
+ switch (buf->c_type) {
+
+ case TS_CLRI:
+ case TS_BITS:
+ /*
+ * Have to patch up missing information in bit map headers
+ */
+ buf->c_inumber = 0;
+ buf->c_dinode.di_size = buf->c_count * TP_BSIZE;
+ for (i = 0; i < buf->c_count; i++)
+ buf->c_addr[i]++;
+ break;
+
+ case TS_TAPE:
+ case TS_END:
+ buf->c_inumber = 0;
+ break;
+
+ case TS_INODE:
+ case TS_ADDR:
+ break;
+
+ default:
+ panic("gethead: unknown inode type %d\n", buf->c_type);
+ break;
+ }
+ if (dflag)
+ accthdr(buf);
+ return(GOOD);
+}
+
+/*
+ * Check that a header is where it belongs and predict the next header
+ */
+accthdr(header)
+ struct s_spcl *header;
+{
+ static ino_t previno = 0x7fffffff;
+ static int prevtype;
+ static long predict;
+ long blks, i;
+
+ if (header->c_type == TS_TAPE) {
+ fprintf(stderr, "Volume header\n");
+ previno = 0x7fffffff;
+ return;
+ }
+ if (previno == 0x7fffffff)
+ goto newcalc;
+ switch (prevtype) {
+ case TS_BITS:
+ fprintf(stderr, "Dump mask header");
+ break;
+ case TS_CLRI:
+ fprintf(stderr, "Remove mask header");
+ break;
+ case TS_INODE:
+ fprintf(stderr, "File header, ino %d", previno);
+ break;
+ case TS_ADDR:
+ fprintf(stderr, "File continuation header, ino %d", previno);
+ break;
+ case TS_END:
+ fprintf(stderr, "End of tape header");
+ break;
+ }
+ if (predict != blksread - 1)
+ fprintf(stderr, "; predicted %d blocks, got %d blocks",
+ predict, blksread - 1);
+ fprintf(stderr, "\n");
+newcalc:
+ blks = 0;
+ if (header->c_type != TS_END)
+ for (i = 0; i < header->c_count; i++)
+ if (header->c_addr[i] != 0)
+ blks++;
+ predict = blks;
+ blksread = 0;
+ prevtype = header->c_type;
+ previno = header->c_inumber;
+}
+
+/*
+ * Find an inode header.
+ * Complain if had to skip, and complain is set.
+ */
+findinode(header)
+ struct s_spcl *header;
+{
+ static long skipcnt = 0;
+ long i;
+ char buf[TP_BSIZE];
+
+ curfile.name = "<name unknown>";
+ curfile.action = UNKNOWN;
+ curfile.dip = (struct dinode *)NIL;
+ curfile.ino = 0;
+ if (ishead(header) == FAIL) {
+ skipcnt++;
+ while (gethead(header) == FAIL || header->c_date != dumpdate)
+ skipcnt++;
+ }
+ for (;;) {
+ if (checktype(header, TS_ADDR) == GOOD) {
+ /*
+ * Skip up to the beginning of the next record
+ */
+ for (i = 0; i < header->c_count; i++)
+ if (header->c_addr[i])
+ readtape(buf);
+ (void) gethead(header);
+ continue;
+ }
+ if (checktype(header, TS_INODE) == GOOD) {
+ curfile.dip = &header->c_dinode;
+ curfile.ino = header->c_inumber;
+ break;
+ }
+ if (checktype(header, TS_END) == GOOD) {
+ curfile.ino = maxino;
+ break;
+ }
+ if (checktype(header, TS_CLRI) == GOOD) {
+ curfile.name = "<file removal list>";
+ break;
+ }
+ if (checktype(header, TS_BITS) == GOOD) {
+ curfile.name = "<file dump list>";
+ break;
+ }
+ while (gethead(header) == FAIL)
+ skipcnt++;
+ }
+ if (skipcnt > 0)
+ fprintf(stderr, "resync restore, skipped %d blocks\n", skipcnt);
+ skipcnt = 0;
+}
+
+/*
+ * return whether or not the buffer contains a header block
+ */
+ishead(buf)
+ struct s_spcl *buf;
+{
+
+ if (buf->c_magic != NFS_MAGIC)
+ return(FAIL);
+ return(GOOD);
+}
+
+checktype(b, t)
+ struct s_spcl *b;
+ int t;
+{
+
+ if (b->c_type != t)
+ return(FAIL);
+ return(GOOD);
+}
+
+checksum(b)
+ register int *b;
+{
+ register int i, j;
+
+ j = sizeof(union u_spcl) / sizeof(int);
+ i = 0;
+ if(!Bcvt) {
+ do
+ i += *b++;
+ while (--j);
+ } else {
+ /* What happens if we want to read restore tapes
+ for a 16bit int machine??? */
+ do
+ i += swabl(*b++);
+ while (--j);
+ }
+
+ if (i != CHECKSUM) {
+ fprintf(stderr, "Checksum error %o, inode %d file %s\n", i,
+ curfile.ino, curfile.name);
+ return(FAIL);
+ }
+ return(GOOD);
+}
+
+#ifdef RRESTORE
+/* VARARGS1 */
+msg(cp, a1, a2, a3)
+ char *cp;
+{
+
+ fprintf(stderr, cp, a1, a2, a3);
+}
+#endif RRESTORE
+
+u_char *
+swabshort(sp, n)
+ register u_char *sp;
+ register int n;
+{
+ char c;
+
+ while (--n >= 0) {
+ c = sp[0]; sp[0] = sp[1]; sp[1] = c;
+ sp += 2;
+ }
+ return (sp);
+}
+
+u_char *
+swablong(sp, n)
+ register u_char *sp;
+ register int n;
+{
+ char c;
+
+ while (--n >= 0) {
+ c = sp[0]; sp[0] = sp[3]; sp[3] = c;
+ c = sp[2]; sp[2] = sp[1]; sp[1] = c;
+ sp += 4;
+ }
+ return (sp);
+}
+
+swabst(cp, sp)
+ register u_char *cp, *sp;
+{
+ int n = 0;
+ u_char c;
+
+ while (*cp) {
+ switch (*cp) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ n = (n * 10) + (*cp++ - '0');
+ continue;
+
+ case 's': case 'w': case 'h':
+ if (n == 0)
+ n = 1;
+ sp = swabshort(sp, n);
+ break;
+
+ case 'l':
+ if (n == 0)
+ n = 1;
+ sp = swablong(sp, n);
+ break;
+
+ default: /* Any other character, like 'b' counts as byte. */
+ if (n == 0)
+ n = 1;
+ sp += n;
+ break;
+ }
+ cp++;
+ n = 0;
+ }
+}
+
+u_long
+swabl(x)
+ u_long x;
+{
+ swabst("l", (char *)&x);
+ return (x);
+}