X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/f78e116cfca57bb09c42128781e6e31fa03212f3..9a0abc6288eadc9c872c99ab1523e171bd806a59:/usr/src/sbin/restore/tape.c diff --git a/usr/src/sbin/restore/tape.c b/usr/src/sbin/restore/tape.c index a53a626a39..f539cfff06 100644 --- a/usr/src/sbin/restore/tape.c +++ b/usr/src/sbin/restore/tape.c @@ -1,226 +1,492 @@ -/* Copyright (c) 1983 Regents of the University of California */ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * (c) UNIX System Laboratories, Inc. + * All or some portions of this file are derived from material licensed + * to the University of California by American Telephone and Telegraph + * Co. or Unix System Laboratories, Inc. and are reproduced herein with + * the permission of UNIX System Laboratories, Inc. + * + * %sccs.include.redist.c% + */ #ifndef lint -static char sccsid[] = "@(#)tape.c 3.1 (Berkeley) 83/02/18"; -#endif +static char sccsid[] = "@(#)tape.c 8.4 (Berkeley) %G%"; +#endif /* not lint */ -#include "restore.h" -#include +#include +#include #include #include +#include + +#include +#include + +#include #include -#include -#include - -long fssize; -int mt = -1; -char *magtape; -int insetup = 0; -int bct = NTREC+1; -char tbf[NTREC*TP_BSIZE]; -jmp_buf restart; -int gettingfile = 0; /* restart has a valid frame */ - -int ofile; -char *map; -char lnkbuf[MAXPATHLEN + 1]; -int pathlen; +#include +#include +#include +#include + +#include "restore.h" +#include "extern.h" +#include "pathnames.h" + +static long fssize = MAXBSIZE; +static int mt = -1; +static int pipein = 0; +static char magtape[BUFSIZ]; +static int blkcnt; +static int numtrec; +static char *tapebuf; +static union u_spcl endoftapemark; +static long blksread; /* blocks read since last header */ +static long tpblksread = 0; /* TP_BSIZE blocks read */ +static long tapesread; +static jmp_buf restart; +static int gettingfile = 0; /* restart has a valid frame */ +static char *host = NULL; + +static int ofile; +static char *map; +static char lnkbuf[MAXPATHLEN + 1]; +static int pathlen; + +int oldinofmt; /* old inode format conversion required */ +int Bcvt; /* Swap Bytes (for CCI or sun) */ +static int Qcvt; /* Swap quads (for sun) */ + +#define FLUSHTAPEBUF() blkcnt = ntrec + 1 + +static void accthdr __P((struct s_spcl *)); +static int checksum __P((int *)); +static void findinode __P((struct s_spcl *)); +static void findtapeblksize __P((void)); +static int gethead __P((struct s_spcl *)); +static void readtape __P((char *)); +static void setdumpnum __P((void)); +static u_long swabl __P((u_long)); +static u_char *swablong __P((u_char *, int)); +static u_char *swabshort __P((u_char *, int)); +static void terminateinput __P((void)); +static void xtrfile __P((char *, long)); +static void xtrlnkfile __P((char *, long)); +static void xtrlnkskip __P((char *, long)); +static void xtrmap __P((char *, long)); +static void xtrmapskip __P((char *, long)); +static void xtrskip __P((char *, long)); /* * Set up an input source */ +void setinput(source) char *source; { -#ifdef RRESTOR - char *host; - char *index(); - - host = source; - magtape = index(host, ':'); - if (magtape == 0) { -nohost: - msg("need keyletter ``f'' and device ``host:tape''"); - done(1); + FLUSHTAPEBUF(); + if (bflag) + newtapebuf(ntrec); + else + newtapebuf(NTREC > HIGHDENSITYTREC ? NTREC : HIGHDENSITYTREC); + terminal = stdin; + +#ifdef RRESTORE + if (index(source, ':')) { + host = source; + source = index(host, ':'); + *source++ = '\0'; + if (rmthost(host) == 0) + done(1); + } else +#endif + 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++; } - *magtape++ = '\0'; - if (rmthost(host) == 0) - done(1); setuid(getuid()); /* no longer need or want root privileges */ -#else - magtape = source; -#endif RRESTOR + (void) strcpy(magtape, source); +} + +void +newtapebuf(size) + long size; +{ + static tapebufsize = -1; + + ntrec = size; + if (size <= tapebufsize) + return; + if (tapebuf != NULL) + free(tapebuf); + tapebuf = malloc(size * TP_BSIZE); + if (tapebuf == NULL) { + fprintf(stderr, "Cannot allocate space for tape buffer\n"); + done(1); + } + tapebufsize = size; } /* * Verify that the tape drive can be accessed and * that it actually is a dump tape. */ +void setup() { - struct mtop tcom; + int i, j, *ip; struct stat stbuf; - extern char *ctime(); - extern int xtrmap(), xtrmapskip(); vprintf(stdout, "Verify tape and initialize maps\n"); - insetup = 1; -#ifdef RRESTOR - if ((mt = rmtopen(magtape, 0)) < 0) -#else - if ((mt = open(magtape, 0)) < 0) +#ifdef RRESTORE + if (host) + mt = rmtopen(magtape, 0); + else #endif - { - fprintf(stderr, "%s: cannot open tape\n", magtape); + if (pipein) + mt = 0; + else + mt = open(magtape, O_RDONLY, 0); + if (mt < 0) { + fprintf(stderr, "%s: %s\n", magtape, strerror(errno)); done(1); } - if (dumpnum != 1) { - tcom.mt_op = MTFSF; - tcom.mt_count = dumpnum -1; -#ifdef RRESTOR - rmtioctl(MTFSF,dumpnum - 1); -#else - if (ioctl(mt,MTIOCTOP,&tcom) < 0) - perror("ioctl MTFSF"); -#endif - } - flsht(); - if (readhdr(&spcl) == 0) { - bct--; /* push back this block */ + volno = 1; + setdumpnum(); + FLUSHTAPEBUF(); + if (!pipein && !bflag) + findtapeblksize(); + if (gethead(&spcl) == FAIL) { + blkcnt--; /* push back this block */ + blksread--; + tpblksread--; cvtflag++; - if (readhdr(&spcl) == 0) { + if (gethead(&spcl) == FAIL) { fprintf(stderr, "Tape is not a dump tape\n"); done(1); } fprintf(stderr, "Converting to new file system format.\n"); } - vprintf(stdout, "Dump date: %s", ctime(&spcl.c_date)); - vprintf(stdout, "Dumped from: %s", ctime(&spcl.c_ddate)); + 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) { - fprintf(stderr, "cannot stat .\n"); + fprintf(stderr, "cannot stat .: %s\n", strerror(errno)); done(1); } - fssize = stbuf.st_blksize; - if (fssize <= 0 || ((fssize - 1) & fssize) != 0) { + 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) == 0) { + if (spcl.c_volume != 1) { fprintf(stderr, "Tape is not volume 1 of the dump\n"); done(1); } - if (readhdr(&spcl) == 0 || checktype(&spcl, TS_CLRI) != 1) { + if (gethead(&spcl) == FAIL) { + dprintf(stdout, "header read failed at %d blocks\n", blksread); + panic("no header after volume mark!\n"); + } + findinode(&spcl); + if (spcl.c_type != TS_CLRI) { fprintf(stderr, "Cannot find file removal list\n"); done(1); } - maxino = spcl.c_count * TP_BSIZE + 1; - map = (char *)calloc(1, (int)howmany(maxino, NBBY)); - if (map == (char *)NIL) + maxino = (spcl.c_count * TP_BSIZE * NBBY) + 1; + dprintf(stdout, "maxino = %d\n", maxino); + map = calloc((unsigned)1, (unsigned)howmany(maxino, NBBY)); + if (map == NULL) panic("no memory for file removal list\n"); + clrimap = map; curfile.action = USING; getfile(xtrmap, xtrmapskip); - clrimap = map; - if (checktype(&spcl, TS_BITS) != 1) { + if (spcl.c_type != TS_BITS) { fprintf(stderr, "Cannot find file dump list\n"); done(1); } - map = (char *)calloc(1, (int)howmany(maxino, NBBY)); + 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); - dumpmap = map; - insetup = 0; } +/* + * 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. + */ +void getvol(nextvol) long nextvol; { - long newvol; + long newvol, savecnt, wantnext, i; union u_spcl tmpspcl; # define tmpbuf tmpspcl.s_spcl + char buf[TP_BSIZE]; - if (dumpnum > 1) { - /* - * if this is a multi-dump tape we always start with - * volume 1, so as to avoid accidentally restoring - * from a different dump! - */ - resetmt(); - dumpnum = 1; - volno = 1; - goto rbits; + 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 (command == 'R' || command == 'r' || curfile.action != SKIP) + if (pipein) + done(1); /* pipes do not get a second chance */ + if (command == 'R' || command == 'r' || curfile.action != SKIP) { newvol = nextvol; - else + wantnext = 1; + } else { newvol = 0; + wantnext = 0; + } while (newvol <= 0) { - fprintf(stderr, "Specify volume #: "); - if (gets(tbf) == NULL) - return; - newvol = atoi(tbf); + 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(buf, ": "); + for (i = 1; i < 32; i++) + if (tapesread & (1 << i)) { + fprintf(stderr, "%s%d", buf, i); + strcpy(buf, ", "); + } + fprintf(stderr, "\n"); + } + do { + fprintf(stderr, "Specify next volume #: "); + (void) fflush(stderr); + (void) fgets(buf, BUFSIZ, terminal); + } while (!feof(terminal) && buf[0] == '\n'); + if (feof(terminal)) + done(1); + newvol = atoi(buf); if (newvol <= 0) { fprintf(stderr, "Volume numbers are positive numerics\n"); } } - if (newvol == volno) + if (newvol == volno) { + tapesread |= 1 << volno; return; + } closemt(); - fprintf(stderr, "Mount tape volume %d then type return: ", newvol); - while (getchar() != '\n') - ; -#ifdef RRESTOR - if ((mt = rmtopen(magtape, 0)) == -1) -#else - if ((mt = open(magtape, 0)) == -1) + 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(buf, BUFSIZ, terminal); + if (feof(terminal)) + done(1); + if (!strcmp(buf, "none\n")) { + terminateinput(); + return; + } + if (buf[0] != '\n') { + (void) strcpy(magtape, buf); + magtape[strlen(magtape) - 1] = '\0'; + } +#ifdef RRESTORE + if (host) + mt = rmtopen(magtape, 0); + else #endif - { - fprintf(stderr, "Cannot open tape!\n"); + mt = open(magtape, O_RDONLY, 0); + + if (mt == -1) { + fprintf(stderr, "Cannot open %s\n", magtape); + volno = -1; goto again; } +gethdr: volno = newvol; - flsht(); - if (readhdr(&tmpbuf) == 0) { + setdumpnum(); + FLUSHTAPEBUF(); + if (gethead(&tmpbuf) == FAIL) { + dprintf(stdout, "header read failed at %d blocks\n", blksread); fprintf(stderr, "tape is not dump tape\n"); volno = 0; goto again; } - if (checkvol(&tmpbuf, volno) == 0) { + if (tmpbuf.c_volume != volno) { fprintf(stderr, "Wrong volume (%d)\n", tmpbuf.c_volume); volno = 0; goto again; } -rbits: + 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 continuing from the previous volume, skip over any + * blocks read already at the end of the previous volume. + * + * If coming to this volume at random, skip to the beginning + * of the next record. + */ + dprintf(stdout, "read %ld recs, tape starts with %ld\n", + tpblksread, tmpbuf.c_firstrec); + if (tmpbuf.c_type == TS_TAPE && (tmpbuf.c_flags & DR_NEWHEADER)) { + if (!wantnext) { + tpblksread = tmpbuf.c_firstrec; + for (i = tmpbuf.c_count; i > 0; i--) + readtape(buf); + } else if (tmpbuf.c_firstrec > 0 && + tmpbuf.c_firstrec < tpblksread - 1) { + /* + * -1 since we've read the volume header + */ + i = tpblksread - tmpbuf.c_firstrec - 1; + dprintf(stderr, "Skipping %d duplicate record%s.\n", + i, i > 1 ? "s" : ""); + while (--i >= 0) + readtape(buf); + } + } if (curfile.action == USING) { if (volno == 1) panic("active file into volume 1\n"); return; } - findinode(&spcl, curfile.action == UNKNOWN ? 1 : 0); + /* + * 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 unexpected EOF. + */ +static void +terminateinput() +{ + + if (gettingfile && curfile.action == USING) { + printf("Warning: %s %s\n", + "End-of-input encountered while extracting", curfile.name); + } + curfile.name = ""; + curfile.action = UNKNOWN; + curfile.dip = NULL; + curfile.ino = maxino; if (gettingfile) { gettingfile = 0; longjmp(restart, 1); } } +/* + * handle multiple dumps per tape by skipping forward to the + * appropriate one. + */ +static void +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 + if (host) + rmtioctl(MTFSF, dumpnum - 1); + else +#endif + if (ioctl(mt, (int)MTIOCTOP, (char *)&tcom) < 0) + fprintf(stderr, "ioctl MTFSF: %s\n", strerror(errno)); +} + +void +printdumpinfo() +{ + 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); +} + +int extractfile(name) char *name; { - int mode; - time_t timep[2]; + int flags; + mode_t mode; + struct timeval timep[2]; struct entry *ep; - extern int xtrlnkfile(), xtrlnkskip(); - extern int xtrfile(), xtrskip(); curfile.name = name; curfile.action = USING; - timep[0] = curfile.dip->di_atime; - timep[1] = curfile.dip->di_mtime; + timep[0].tv_sec = curfile.dip->di_atime.ts_sec; + timep[0].tv_usec = curfile.dip->di_atime.ts_nsec / 1000; + timep[1].tv_sec = curfile.dip->di_mtime.ts_sec; + timep[1].tv_usec = curfile.dip->di_mtime.ts_nsec / 1000; mode = curfile.dip->di_mode; + flags = curfile.dip->di_flags; switch (mode & IFMT) { default: @@ -228,10 +494,15 @@ extractfile(name) 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) + if (ep == NULL || ep->e_flags & EXTRACT) panic("unextracted directory %s\n", name); skipfile(); return (GOOD); @@ -246,72 +517,117 @@ extractfile(name) if (pathlen == 0) { vprintf(stdout, "%s: zero length symbolic link (ignored)\n", name); - } else if (symlink(lnkbuf, name) < 0) { - fprintf(stderr, "%s: cannot create symbolic link\n", - name); - return (FAIL); - } else - vprintf(stdout, "extract symbolic link %s\n", name); - return (GOOD); + 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: cannot create special file\n", - name); + fprintf(stderr, "%s: cannot create special file: %s\n", + name, strerror(errno)); + skipfile(); + return (FAIL); + } + (void) chown(name, curfile.dip->di_uid, curfile.dip->di_gid); + (void) chmod(name, mode); + (void) chflags(name, flags); + skipfile(); + utimes(name, timep); + return (GOOD); + + case IFIFO: + vprintf(stdout, "extract fifo %s\n", name); + if (Nflag) { + skipfile(); + return (GOOD); + } + if (mkfifo(name, mode) < 0) { + fprintf(stderr, "%s: cannot create fifo: %s\n", + name, strerror(errno)); skipfile(); return (FAIL); } - chown(name, curfile.dip->di_uid, curfile.dip->di_gid); - chmod(name, mode); + (void) chown(name, curfile.dip->di_uid, curfile.dip->di_gid); + (void) chmod(name, mode); + (void) chflags(name, flags); skipfile(); - utime(name, timep); + utimes(name, timep); return (GOOD); case IFREG: vprintf(stdout, "extract file %s\n", name); - if ((ofile = open(name, FWRONLY|FCREATE, 0666)) < 0) { - fprintf(stderr, "%s: cannot create file\n", name); + if (Nflag) { + skipfile(); + return (GOOD); + } + if ((ofile = creat(name, 0666)) < 0) { + fprintf(stderr, "%s: cannot create file: %s\n", + name, strerror(errno)); skipfile(); return (FAIL); } - fchown(ofile, curfile.dip->di_uid, curfile.dip->di_gid); - fchmod(ofile, mode); + (void) fchown(ofile, curfile.dip->di_uid, curfile.dip->di_gid); + (void) fchmod(ofile, mode); + (void) fchflags(ofile, flags); getfile(xtrfile, xtrskip); - close(ofile); - utime(name, timep); + (void) close(ofile); + utimes(name, timep); return (GOOD); } /* NOTREACHED */ } +/* + * skip over bit maps on the tape + */ +void +skipmaps() +{ + + while (spcl.c_type == TS_BITS || spcl.c_type == TS_CLRI) + skipfile(); +} + +/* + * skip over a file on the tape + */ +void skipfile() { - extern int null(); curfile.action = SKIP; - getfile(null, null); + getfile(xtrnull, xtrnull); } /* - * Do the file extraction, calling the supplied functions - * with the blocks + * Extract a file from the tape. + * When an allocated block is found it is passed to the fill function; + * when an unallocated block (hole) is found, a zeroed buffer is passed + * to the skip function. */ -getfile(f1, f2) - int (*f2)(), (*f1)(); +void +getfile(fill, skip) + void (*fill) __P((char *, long)); + void (*skip) __P((char *, long)); { register int i; int curblk = 0; - off_t size = spcl.c_dinode.di_size; + long 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) == 1) + if (spcl.c_type == TS_END) panic("ran off end of tape\n"); - if (checktype(&spcl, TS_INODE) == 0) + if (spcl.c_magic != NFS_MAGIC) panic("not at beginning of a file\n"); - if (setjmp(restart) != 0) + if (!gettingfile && setjmp(restart) != 0) return; gettingfile++; loop: @@ -319,73 +635,82 @@ loop: if (spcl.c_addr[i]) { readtape(&buf[curblk++][0]); if (curblk == fssize / TP_BSIZE) { - (*f1)(buf, size > TP_BSIZE ? + (*fill)((char *)buf, size > TP_BSIZE ? (long) (fssize) : (curblk - 1) * TP_BSIZE + size); curblk = 0; } } else { if (curblk > 0) { - (*f1)(buf, size > TP_BSIZE ? + (*fill)((char *)buf, size > TP_BSIZE ? (long) (curblk * TP_BSIZE) : (curblk - 1) * TP_BSIZE + size); curblk = 0; } - (*f2)(clearedbuf, size > TP_BSIZE ? + (*skip)(clearedbuf, size > TP_BSIZE ? (long) TP_BSIZE : size); } if ((size -= TP_BSIZE) <= 0) { - gethead(&spcl); - goto out; + for (i++; i < spcl.c_count; i++) + if (spcl.c_addr[i]) + readtape(junk); + break; } } - if (gethead(&spcl) == 0 || checktype(&spcl, TS_ADDR) == 0) { - fprintf(stderr, "Missing address (header) block for %s\n", - curfile.name); - goto out; - } - goto loop; -out: - if (curblk > 0) { - (*f1)(buf, (curblk * TP_BSIZE) + size); + if (gethead(&spcl) == GOOD && size > 0) { + if (spcl.c_type == TS_ADDR) + goto loop; + dprintf(stdout, + "Missing address (header) block for %s at %d blocks\n", + curfile.name, blksread); } - findinode(&spcl, 1); + if (curblk > 0) + (*fill)((char *)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. + * Write out the next block of a file. */ +static void 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"); + fprintf(stderr, + "write error extracting inode %d, name %s\nwrite: %s\n", + curfile.ino, curfile.name, strerror(errno)); done(1); } } +/* + * Skip over a hole in a file. + */ +/* ARGSUSED */ +static void 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"); + if (lseek(ofile, size, SEEK_CUR) == -1) { + fprintf(stderr, + "seek error extracting inode %d, name %s\nlseek: %s\n", + curfile.ino, curfile.name, strerror(errno)); done(1); } } +/* + * Collect the next block of a symbolic link. + */ +static void xtrlnkfile(buf, size) char *buf; long size; @@ -397,212 +722,288 @@ xtrlnkfile(buf, size) curfile.name, lnkbuf, buf, pathlen); done(1); } - strcat(lnkbuf, buf); + (void) strcat(lnkbuf, buf); } +/* + * Skip over a hole in a symbolic link (should never happen). + */ +/* ARGSUSED */ +static void 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); } +/* + * Collect the next block of a bit map. + */ +static void xtrmap(buf, size) char *buf; long size; { bcopy(buf, map, size); + map += size; } +/* + * Skip over a hole in a bit map (should never happen). + */ +/* ARGSUSED */ +static void xtrmapskip(buf, size) char *buf; long size; { -#ifdef lint - buf = buf; - size = size; -#endif panic("hole in map\n"); + map += size; } -null() {;} +/* + * Noop, when an extraction function is not needed. + */ +/* ARGSUSED */ +void +xtrnull(buf, size) + char *buf; + long size; +{ + + return; +} /* - * Do the tape i/o, dealing with volume changes - * etc.. + * Read TP_BSIZE blocks from the input. + * Handle read errors, and end of media. */ -readtape(b) - char *b; +static void +readtape(buf) + char *buf; { - register long i; - long newvol; - - if (bct >= NTREC) { - for (i = 0; i < NTREC; i++) - ((struct s_spcl *)&tbf[i*TP_BSIZE])->c_magic = 0; - bct = 0; -#ifdef RRESTOR - if ((i = rmtread(tbf, NTREC*TP_BSIZE)) < 0) -#else - if ((i = read(mt, tbf, NTREC*TP_BSIZE)) < 0) + long rd, newvol, i; + int cnt, seek_failed; + + if (blkcnt < numtrec) { + bcopy(&tapebuf[(blkcnt++ * TP_BSIZE)], buf, (long)TP_BSIZE); + blksread++; + tpblksread++; + return; + } + for (i = 0; i < ntrec; i++) + ((struct s_spcl *)&tapebuf[i * TP_BSIZE])->c_magic = 0; + if (numtrec == 0) + numtrec = ntrec; + cnt = ntrec * TP_BSIZE; + rd = 0; +getmore: +#ifdef RRESTORE + if (host) + i = rmtread(&tapebuf[rd], cnt); + else #endif - { - fprintf(stderr, "Tape read error while "); - switch (curfile.action) { - case UNKNOWN: - fprintf(stderr, "trying to resyncronize\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 RRESTOR - if (rmtseek(i, 1) < 0) -#else - if (lseek(mt, i, 1) == (long)-1) + i = read(mt, &tapebuf[rd], cnt); + /* + * Check for mid-tape short read error. + * If found, skip rest of buffer and start with the next. + */ + if (!pipein && numtrec < ntrec && i > 0) { + dprintf(stdout, "mid-media short read error.\n"); + numtrec = ntrec; + } + /* + * Handle partial block read. + */ + if (pipein && i == 0 && rd > 0) + i = rd; + else if (i > 0 && i != ntrec * TP_BSIZE) { + if (pipein) { + rd += i; + cnt -= i; + if (cnt > 0) + goto getmore; + i = rd; + } else { + /* + * Short read. Process the blocks read. + */ + if (i % TP_BSIZE != 0) + vprintf(stdout, + "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(tapebuf, i); +#ifdef RRESTORE + if (host) + seek_failed = (rmtseek(i, 1) < 0); + else #endif - { - fprintf(stderr, "continuation failed\n"); - done(1); - } + seek_failed = (lseek(mt, i, SEEK_CUR) == (off_t)-1); + + if (seek_failed) { + fprintf(stderr, + "continuation failed: %s\n", strerror(errno)); + done(1); } - if (i == 0) { + } + /* + * Handle end of tape. + */ + if (i == 0) { + vprintf(stdout, "End-of-tape encountered\n"); + if (!pipein) { newvol = volno + 1; volno = 0; + numtrec = 0; getvol(newvol); - readtape(b); + readtape(buf); return; } + if (rd % TP_BSIZE != 0) + panic("partial block read: %d should be %d\n", + rd, ntrec * TP_BSIZE); + terminateinput(); + bcopy((char *)&endoftapemark, &tapebuf[rd], (long)TP_BSIZE); } - bcopy(&tbf[(bct++*TP_BSIZE)], b, (long)TP_BSIZE); + blkcnt = 0; + bcopy(&tapebuf[(blkcnt++ * TP_BSIZE)], buf, (long)TP_BSIZE); + blksread++; + tpblksread++; } -flsht() +static void +findtapeblksize() { + register long i; - bct = NTREC+1; -} - -resetmt() -{ - struct mtop tcom; - - if (dumpnum > 1) - tcom.mt_op = MTBSF; + for (i = 0; i < ntrec; i++) + ((struct s_spcl *)&tapebuf[i * TP_BSIZE])->c_magic = 0; + blkcnt = 0; +#ifdef RRESTORE + if (host) + i = rmtread(tapebuf, ntrec * TP_BSIZE); else - tcom.mt_op = MTREW; - tcom.mt_count = 1; - flsht(); -#ifdef RRESTOR - if (rmtioctl(tcom.mt_op, 1) == -1) { - /* kludge for disk dumps */ - rmtseek((long)0, 0); - } -#else - if (ioctl(mt,MTIOCTOP,&tcom) == -1) { - /* kludge for disk dumps */ - (void) lseek(mt, (long)0, 0); - } -#endif - if (dumpnum > 1) { -#ifdef RRESTOR - rmtioctl(MTFSF, 1); -#else - tcom.mt_op = MTFSF; - tcom.mt_count = 1; - ioctl(mt,MTIOCTOP,&tcom); #endif + i = read(mt, tapebuf, ntrec * TP_BSIZE); + + if (i <= 0) { + fprintf(stderr, "tape read error: %s\n", strerror(errno)); + 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); } +void closemt() { + if (mt < 0) return; -#ifdef RRESTOR - rmtclose(); -#else - close(mt); +#ifdef RRESTORE + if (host) + rmtclose(); + else #endif -} - -checkvol(b, t) - struct s_spcl *b; - long t; -{ - - if (b->c_volume == t) - return(1); - return(0); -} - -readhdr(b) - struct s_spcl *b; -{ - - if (gethead(b) == 0) - return(0); - if (checktype(b, TS_TAPE) == 0) - return(0); - return(1); + (void) close(mt); } /* - * read the tape into buf, then return whether or - * or not it is a header block. + * Read the next block from the tape. + * Check to see if it is one of several vintage headers. + * If it is an old style header, convert it to a new style header. + * If it is not any valid header, return an error. */ +static int gethead(buf) struct s_spcl *buf; { + long i; + union { + quad_t qval; + long val[2]; + } qcvt; union u_ospcl { char dummy[TP_BSIZE]; struct s_ospcl { - int c_type; - time_t c_date; - time_t c_ddate; - int c_volume; - daddr_t c_tapea; - ino_t c_inumber; - int c_magic; - int c_checksum; + 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; - short odi_nlink; - short odi_uid; - short odi_gid; - off_t odi_size; - daddr_t odi_rdev; + u_short odi_nlink; + u_short odi_uid; + u_short odi_gid; + long odi_size; + long odi_rdev; char odi_addr[36]; - time_t odi_atime; - time_t odi_mtime; - time_t odi_ctime; + long odi_atime; + long odi_mtime; + long odi_ctime; } c_dinode; - int c_count; - char c_addr[TP_NINDIR]; + long c_count; + char c_addr[256]; } s_ospcl; } u_ospcl; if (!cvtflag) { readtape((char *)buf); - if (buf->c_magic != NFS_MAGIC || checksum((int *)buf) == 0) - return(0); - return(1); + 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((u_char *)"8l4s31l", (u_char *)buf); + goto good; } readtape((char *)(&u_ospcl.s_ospcl)); bzero((char *)buf, (long)TP_BSIZE); @@ -620,106 +1021,325 @@ gethead(buf) 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_dinode.di_atime.ts_sec = u_ospcl.s_ospcl.c_dinode.odi_atime; + buf->c_dinode.di_mtime.ts_sec = u_ospcl.s_ospcl.c_dinode.odi_mtime; + buf->c_dinode.di_ctime.ts_sec = 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)TP_NINDIR); + 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)) == 0) - return(0); + checksum((int *)(&u_ospcl.s_ospcl)) == FAIL) + return(FAIL); buf->c_magic = NFS_MAGIC; - return(1); + +good: + if ((buf->c_dinode.di_size == 0 || buf->c_dinode.di_size > 0xfffffff) && + (buf->c_dinode.di_mode & IFMT) == IFDIR && Qcvt == 0) { + qcvt.qval = buf->c_dinode.di_size; + if (qcvt.val[0] || qcvt.val[1]) { + printf("Note: Doing Quad swapping\n"); + Qcvt = 1; + } + } + if (Qcvt) { + qcvt.qval = buf->c_dinode.di_size; + i = qcvt.val[1]; + qcvt.val[1] = qcvt.val[0]; + qcvt.val[0] = i; + buf->c_dinode.di_size = qcvt.qval; + } + + 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: + if ((buf->c_flags & DR_NEWINODEFMT) == 0) + oldinofmt = 1; + /* fall through */ + 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 we are restoring a filesystem with old format inodes, + * copy the uid/gid to the new location. + */ + if (oldinofmt) { + buf->c_dinode.di_uid = buf->c_dinode.di_ouid; + buf->c_dinode.di_gid = buf->c_dinode.di_ogid; + } + if (dflag) + accthdr(buf); + return(GOOD); +} + +/* + * Check that a header is where it belongs and predict the next header + */ +static void +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 (%s inode format) ", + oldinofmt ? "old" : "new"); + if (header->c_firstrec) + fprintf(stderr, "begins with record %d", + header->c_firstrec); + fprintf(stderr, "\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, complain) +static void +findinode(header) struct s_spcl *header; - int complain; { - static int skipcnt = 0; + static long skipcnt = 0; + long i; + char buf[TP_BSIZE]; curfile.name = ""; curfile.action = UNKNOWN; - curfile.dip = (struct dinode *)NIL; + curfile.dip = NULL; curfile.ino = 0; - if (ishead(header) == 0) - while (gethead(header) == 0) + do { + if (header->c_magic != NFS_MAGIC) { skipcnt++; - for (;;) { - if (checktype(header, TS_INODE) == 1) { + while (gethead(header) == FAIL || + header->c_date != dumpdate) + skipcnt++; + } + switch (header->c_type) { + + case TS_ADDR: + /* + * Skip up to the beginning of the next record + */ + for (i = 0; i < header->c_count; i++) + if (header->c_addr[i]) + readtape(buf); + while (gethead(header) == FAIL || + header->c_date != dumpdate) + skipcnt++; + break; + + case TS_INODE: curfile.dip = &header->c_dinode; curfile.ino = header->c_inumber; break; - } - if (checktype(header, TS_END) == 1) { + + case TS_END: curfile.ino = maxino; break; - } - if (insetup && checktype(header, TS_CLRI) == 1) { + + case TS_CLRI: curfile.name = ""; - header->c_dinode.di_size = header->c_count * TP_BSIZE; break; - } - if (insetup && checktype(header, TS_BITS) == 1) { + + case TS_BITS: curfile.name = ""; - header->c_dinode.di_size = header->c_count * TP_BSIZE; break; + + case TS_TAPE: + panic("unexpected tape header\n"); + /* NOTREACHED */ + + default: + panic("unknown tape header type %d\n", spcl.c_type); + /* NOTREACHED */ + } - while (gethead(header) == 0) - skipcnt++; - } - if (skipcnt > 0 && complain) - fprintf(stderr, "resync restor, skipped %d blocks\n", skipcnt); + } while (header->c_type == TS_ADDR); + 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; +static int +checksum(buf) + register int *buf; { + register int i, j; + + j = sizeof(union u_spcl) / sizeof(int); + i = 0; + if(!Bcvt) { + do + i += *buf++; + while (--j); + } else { + /* What happens if we want to read restore tapes + for a 16bit int machine??? */ + do + i += swabl(*buf++); + 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 +#if __STDC__ +#include +#else +#include +#endif - if (buf->c_magic != NFS_MAGIC) - return(0); - return(1); +void +#if __STDC__ +msg(const char *fmt, ...) +#else +msg(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)vfprintf(stderr, fmt, ap); + va_end(ap); } +#endif /* RRESTORE */ -checktype(b, t) - struct s_spcl *b; - int t; +static u_char * +swabshort(sp, n) + register u_char *sp; + register int n; { + char c; - return(b->c_type == t); + while (--n >= 0) { + c = sp[0]; sp[0] = sp[1]; sp[1] = c; + sp += 2; + } + return (sp); } -checksum(b) - register int *b; +static u_char * +swablong(sp, n) + register u_char *sp; + register int n; { - register int i, j; + char c; - j = sizeof(union u_spcl) / sizeof(int); - i = 0; - do - i += *b++; - while (--j); - if (i != CHECKSUM) { - fprintf(stderr, "Checksum error %o, inode %d file %s\n", i, - curfile.ino, curfile.name); - return(0); + 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(1); + return (sp); } -#ifdef RRESTOR -msg(cp, a1, a2, a3) - char *cp; +void +swabst(cp, sp) + register u_char *cp, *sp; { + int n = 0; + + 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; - fprintf(stderr, cp, a1, a2, a3); + default: /* Any other character, like 'b' counts as byte. */ + if (n == 0) + n = 1; + sp += n; + break; + } + cp++; + n = 0; + } +} + +static u_long +swabl(x) + u_long x; +{ + swabst((u_char *)"l", (u_char *)&x); + return (x); } -#endif RRESTOR