X-Git-Url: https://git.wh0rd.org/?p=dump.git;a=blobdiff_plain;f=restore%2Ftape.c;h=344ca3b46fadd47079622653e5fcc07f19ac47d4;hp=b00c4bb89b4da6daca962506906c88ec9c676f05;hb=8b7882a833092b5974aa73e06b2e55bb9d1a9e99;hpb=1227625a12a66e0ded78a1997c2d23f23202a382 diff --git a/restore/tape.c b/restore/tape.c index b00c4bb..344ca3b 100644 --- a/restore/tape.c +++ b/restore/tape.c @@ -1,8 +1,9 @@ /* * Ported to Linux's Second Extended File System as part of the * dump and restore backup suit - * Remy Card , 1994, 1995, 1996 - * + * Remy Card , 1994-1997 + * Stelian Pop , 1999-2000 + * Stelian Pop - AlcĂ´ve , 2000 */ /* @@ -44,51 +45,76 @@ */ #ifndef lint -static char sccsid[] = "@(#)tape.c 8.9 (Berkeley) 5/1/95"; +static const char rcsid[] = + "$Id: tape.c,v 1.47 2001/09/12 10:21:49 stelian Exp $"; #endif /* not lint */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + #include #include -#include #include #include #ifdef __linux__ #include +#include +#ifdef HAVE_EXT2FS_EXT2_FS_H +#include +#else #include +#endif +#include #include #else /* __linux__ */ #include #endif /* __linux__ */ #include -#include -#include -#include -#include -#include -#include +#ifdef HAVE_ZLIB +#include +#endif /* HAVE_ZLIB */ -#ifdef __linux__ -#include -#endif +#ifdef HAVE_BZLIB +#include +#endif /* HAVE_BZLIB */ #include "restore.h" #include "extern.h" #include "pathnames.h" +#ifdef USE_QFA +int noresyncmesg = 0; +#endif /* USE_QFA */ static long fssize = MAXBSIZE; static int mt = -1; static int pipein = 0; -static char magtape[BUFSIZ]; +static int magtapein = 0; /* input is from magtape */ +static char magtape[MAXPATHLEN]; +static char magtapeprefix[MAXPATHLEN]; static int blkcnt; static int numtrec; -static char *tapebuf; +static char *tapebuf; /* input buffer for read */ +static int bufsize; /* buffer size without prefix */ +static char *tbufptr = NULL; /* active tape buffer */ +#if defined(HAVE_ZLIB) || defined(HAVE_BZLIB) +static char *comprbuf; /* uncompress work buf */ +static size_t comprlen; /* size including prefix */ +#endif 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 sigjmp_buf restart; static int gettingfile = 0; /* restart has a valid frame */ static char *host = NULL; @@ -110,23 +136,50 @@ static void findtapeblksize __P((void)); static int gethead __P((struct s_spcl *)); static void readtape __P((char *)); static void setdumpnum __P((void)); +static u_int swabi __P((u_int)); +#if 0 static u_long swabl __P((u_long)); -static u_char *swablong __P((u_char *, int)); -static u_char *swabshort __P((u_char *, int)); +#endif +static u_char *swab64 __P((u_char *, int)); +static u_char *swab32 __P((u_char *, int)); +static u_char *swab16 __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)); +static void xtrfile __P((char *, size_t)); +static void xtrlnkfile __P((char *, size_t)); +static void xtrlnkskip __P((char *, size_t)); +static void xtrmap __P((char *, size_t)); +static void xtrmapskip __P((char *, size_t)); +static void xtrskip __P((char *, size_t)); +static void setmagtapein __P((void)); + +#if defined(HAVE_ZLIB) || defined(HAVE_BZLIB) +static void newcomprbuf __P((int)); +static void readtape_set __P((char *)); +static void readtape_uncompr __P((char *)); +static void readtape_comprfile __P((char *)); +static void readtape_comprtape __P((char *)); +static char *decompress_tapebuf __P((struct tapebuf *, int)); +static void msg_read_error __P((char *)); +#endif +static int read_a_block __P((int, char *, size_t, long *)); +#define PREFIXSIZE sizeof(struct tapebuf) + +#define COMPARE_ONTHEFLY 1 + +#if COMPARE_ONTHEFLY +static int ifile; /* input file for compare */ +static int cmperror; /* compare error */ +static void xtrcmpfile __P((char *, size_t)); +static void xtrcmpskip __P((char *, size_t)); +#endif + +static int readmapflag; /* - * Set up an input source + * Set up an input source. This is called from main.c before setup() is. */ void -setinput(source) - char *source; +setinput(char *source) { FLUSHTAPEBUF(); if (bflag) @@ -141,7 +194,7 @@ setinput(source) source = strchr(host, ':'); *source++ = '\0'; if (rmthost(host) == 0) - done(1); + exit(1); } else #endif if (strcmp(source, "-") == 0) { @@ -151,51 +204,76 @@ setinput(source) */ terminal = fopen(_PATH_TTY, "r"); if (terminal == NULL) { - (void)fprintf(stderr, "cannot open %s: %s\n", - _PATH_TTY, strerror(errno)); + warn("cannot open %s", _PATH_TTY); terminal = fopen(_PATH_DEVNULL, "r"); - if (terminal == NULL) { - (void)fprintf(stderr, "cannot open %s: %s\n", - _PATH_DEVNULL, strerror(errno)); - done(1); - } + if (terminal == NULL) + err(1, "cannot open %s", _PATH_DEVNULL); } pipein++; } setuid(getuid()); /* no longer need or want root privileges */ - (void) strcpy(magtape, source); + if (Mflag) { + strncpy(magtapeprefix, source, MAXPATHLEN); + magtapeprefix[MAXPATHLEN-1] = '\0'; + snprintf(magtape, MAXPATHLEN, "%s%03d", source, 1); + } + else + strncpy(magtape, source, MAXPATHLEN); + magtape[MAXPATHLEN - 1] = '\0'; } void -newtapebuf(size) - long size; +newtapebuf(long size) { - static tapebufsize = -1; + static int tapebufsize = -1; ntrec = size; + bufsize = ntrec * TP_BSIZE; 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); - } + tapebuf = malloc(size * TP_BSIZE + sizeof(struct tapebuf)); + if (tapebuf == NULL) + errx(1, "Cannot allocate space for tape buffer"); tapebufsize = size; } +#if defined(HAVE_ZLIB) || defined(HAVE_BZLIB) +static void +newcomprbuf(int size) +{ + size_t buf_size = (size+1) * TP_BSIZE + sizeof(struct tapebuf); + if (buf_size <= comprlen) + return; + comprlen = buf_size; + if (comprbuf != NULL) + free(comprbuf); + comprbuf = malloc(comprlen); + if (comprbuf == NULL) + errx(1, "Cannot allocate space for decompress buffer"); +} +#endif /* HAVE_ZLIB || HAVE_BZLIB */ + /* * Verify that the tape drive can be accessed and * that it actually is a dump tape. */ void -setup() +setup(void) { - int i, j, *ip; - struct stat stbuf; - - vprintf(stdout, "Verify tape and initialize maps\n"); + int i, j, *ip, bot_code; + struct STAT stbuf; + + Vprintf(stdout, "Verify tape and initialize maps\n"); + if (bot_script) { + msg("Launching %s\n", bot_script); + bot_code = system_command(bot_script, magtape, 1); + if (bot_code != 0 && bot_code != 1) { + msg("Restore aborted by the beginning of tape script\n"); + exit(1); + } + } #ifdef RRESTORE if (host) mt = rmtopen(magtape, 0); @@ -204,27 +282,32 @@ setup() if (pipein) mt = 0; else - mt = open(magtape, O_RDONLY, 0); - if (mt < 0) { - fprintf(stderr, "%s: %s\n", magtape, strerror(errno)); - done(1); - } + mt = OPEN(magtape, O_RDONLY, 0); + if (mt < 0) + err(1, "%s", magtape); volno = 1; + setmagtapein(); setdumpnum(); FLUSHTAPEBUF(); - if (!pipein && !bflag) - findtapeblksize(); + findtapeblksize(); if (gethead(&spcl) == FAIL) { blkcnt--; /* push back this block */ blksread--; tpblksread--; cvtflag++; - if (gethead(&spcl) == FAIL) { - fprintf(stderr, "Tape is not a dump tape\n"); - done(1); - } + if (gethead(&spcl) == FAIL) + errx(1, "Tape is not a dump tape"); fprintf(stderr, "Converting to new file system format.\n"); } + + if (zflag) { + fprintf(stderr, "Dump tape is compressed.\n"); +#if defined(HAVE_ZLIB) || defined(HAVE_BZLIB) + newcomprbuf(ntrec); +#else + errx(1,"This restore version doesn't support decompression"); +#endif /* HAVE_ZLIB */ + } if (pipein) { endoftapemark.s_spcl.c_magic = cvtflag ? OFS_MAGIC : NFS_MAGIC; endoftapemark.s_spcl.c_type = TS_END; @@ -238,49 +321,46 @@ setup() } if (vflag || command == 't' || command == 'C') printdumpinfo(); - if (filesys == NULL) { - filesys = spcl.c_filesys; + if (filesys[0] == '\0') { + char *dirptr; + strncpy(filesys, spcl.c_filesys, NAMELEN); + filesys[NAMELEN - 1] = '\0'; + dirptr = strstr(filesys, " (dir"); + if (dirptr != NULL) + *dirptr = '\0'; } dumptime = spcl.c_ddate; dumpdate = spcl.c_date; - if (stat(".", &stbuf) < 0) { - fprintf(stderr, "cannot stat .: %s\n", strerror(errno)); - done(1); - } - if (stbuf.st_blksize > 0 && stbuf.st_blksize <= MAXBSIZE) + if (STAT(".", &stbuf) < 0) + err(1, "cannot stat ."); + if (stbuf.st_blksize > 0 && stbuf.st_blksize < TP_BSIZE ) + fssize = TP_BSIZE; + if (stbuf.st_blksize >= TP_BSIZE && stbuf.st_blksize <= MAXBSIZE) fssize = stbuf.st_blksize; - if (((fssize - 1) & fssize) != 0) { - fprintf(stderr, "bad block size %d\n", fssize); - done(1); - } - if (spcl.c_volume != 1) { - fprintf(stderr, "Tape is not volume 1 of the dump\n"); - done(1); - } + if (((fssize - 1) & fssize) != 0) + errx(1, "bad block size %ld", fssize); + if (spcl.c_volume != 1) + errx(1, "Tape is not volume 1 of the dump"); if (gethead(&spcl) == FAIL) { - dprintf(stdout, "header read failed at %d blocks\n", blksread); + Dprintf(stdout, "header read failed at %ld blocks\n", (long)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); - } + if (spcl.c_type != TS_CLRI) + errx(1, "Cannot find file removal list"); maxino = (spcl.c_count * TP_BSIZE * NBBY) + 1; - dprintf(stdout, "maxino = %d\n", maxino); + Dprintf(stdout, "maxino = %ld\n", (long)maxino); map = calloc((unsigned)1, (unsigned)howmany(maxino, NBBY)); if (map == NULL) - panic("no memory for active inode map\n"); + errx(1, "no memory for active inode map"); usedinomap = map; curfile.action = USING; getfile(xtrmap, xtrmapskip); - if (spcl.c_type != TS_BITS) { - fprintf(stderr, "Cannot find file dump list\n"); - done(1); - } + if (spcl.c_type != TS_BITS) + errx(1, "Cannot find file dump list"); map = calloc((unsigned)1, (unsigned)howmany(maxino, NBBY)); if (map == (char *)NULL) - panic("no memory for file dump list\n"); + errx(1, "no memory for file dump list"); dumpmap = map; curfile.action = USING; getfile(xtrmap, xtrmapskip); @@ -297,17 +377,18 @@ setup() * 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 + * or incremental restores, but can be overridden by * the user when only extracting a subset of the files. */ void -getvol(nextvol) - long nextvol; +getvol(long nextvol) { - long newvol, savecnt, wantnext, i; + long newvol = 0, wantnext = 0, i; + long saved_blksread = 0, saved_tpblksread = 0; union u_spcl tmpspcl; # define tmpbuf tmpspcl.s_spcl char buf[TP_BSIZE]; + int haderror = 0, bot_code = 1; if (nextvol == 1) { tapesread = 0; @@ -320,14 +401,15 @@ getvol(nextvol) return; goto gethdr; } - savecnt = blksread; + saved_blksread = blksread; + saved_tpblksread = tpblksread; again: if (pipein) - done(1); /* pipes do not get a second chance */ + exit(1); /* pipes do not get a second chance */ if (command == 'R' || command == 'r' || curfile.action != SKIP) { newvol = nextvol; wantnext = 1; - } else { + } else { newvol = 0; wantnext = 0; } @@ -344,7 +426,7 @@ again: strcpy(buf, ": "); for (i = 1; i < 32; i++) if (tapesread & (1 << i)) { - fprintf(stderr, "%s%d", buf, i); + fprintf(stderr, "%s%ld", buf, (long)i); strcpy(buf, ", "); } fprintf(stderr, "\n"); @@ -352,10 +434,10 @@ again: do { fprintf(stderr, "Specify next volume #: "); (void) fflush(stderr); - (void) fgets(buf, BUFSIZ, terminal); + (void) fgets(buf, TP_BSIZE, terminal); } while (!feof(terminal) && buf[0] == '\n'); if (feof(terminal)) - done(1); + exit(1); newvol = atoi(buf); if (newvol <= 0) { fprintf(stderr, @@ -367,63 +449,88 @@ again: 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(buf, BUFSIZ, terminal); - if (feof(terminal)) - done(1); - if (!strcmp(buf, "none\n")) { - terminateinput(); - return; + if (Mflag) { + snprintf(magtape, MAXPATHLEN, "%s%03ld", magtapeprefix, newvol); + magtape[MAXPATHLEN - 1] = '\0'; + } + if (bot_script && !haderror) { + msg("Launching %s\n", bot_script); + bot_code = system_command(bot_script, magtape, newvol); + if (bot_code != 0 && bot_code != 1) { + msg("Restore aborted by the beginning of tape script\n"); + exit(1); + } } - if (buf[0] != '\n') { - (void) strcpy(magtape, buf); - magtape[strlen(magtape) - 1] = '\0'; + if (haderror || (bot_code && !Mflag)) { + haderror = 0; + fprintf(stderr, "Mount tape volume %ld\n", (long)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, TP_BSIZE, terminal); + if (feof(terminal)) + exit(1); + if (!strcmp(buf, "none\n")) { + terminateinput(); + return; + } + if (buf[0] != '\n') { + char *pos; + (void) strncpy(magtape, buf, sizeof(magtape)); + magtape[sizeof(magtape) - 1] = '\0'; + if ((pos = strchr(magtape, '\n'))) + magtape[pos - magtape] = '\0'; + } } #ifdef RRESTORE if (host) mt = rmtopen(magtape, 0); else #endif - mt = open(magtape, O_RDONLY, 0); + mt = OPEN(magtape, O_RDONLY, 0); if (mt == -1) { fprintf(stderr, "Cannot open %s\n", magtape); volno = -1; + haderror = 1; goto again; } gethdr: + setmagtapein(); volno = newvol; setdumpnum(); FLUSHTAPEBUF(); + findtapeblksize(); if (gethead(&tmpbuf) == FAIL) { - dprintf(stdout, "header read failed at %d blocks\n", blksread); + Dprintf(stdout, "header read failed at %ld blocks\n", (long)blksread); fprintf(stderr, "tape is not dump tape\n"); volno = 0; + haderror = 1; + blksread = saved_blksread; + tpblksread = saved_tpblksread; goto again; } if (tmpbuf.c_volume != volno) { fprintf(stderr, "Wrong volume (%d)\n", tmpbuf.c_volume); volno = 0; + haderror = 1; + blksread = saved_blksread; + tpblksread = saved_tpblksread; goto again; } if (tmpbuf.c_date != dumpdate || tmpbuf.c_ddate != dumptime) { -#ifdef __linux__ fprintf(stderr, "Wrong dump date\n\tgot: %s", ctime4(&tmpbuf.c_date)); - fprintf(stderr, "\twanted: %s", ctime4(&dumpdate)); -#else - fprintf(stderr, "Wrong dump date\n\tgot: %s", - ctime(&tmpbuf.c_date)); fprintf(stderr, "\twanted: %s", ctime(&dumpdate)); -#endif volno = 0; + haderror = 1; + blksread = saved_blksread; + tpblksread = saved_tpblksread; goto again; } tapesread |= 1 << volno; - blksread = savecnt; + blksread = saved_blksread; + tpblksread = saved_tpblksread; /* * If continuing from the previous volume, skip over any * blocks read already at the end of the previous volume. @@ -431,8 +538,8 @@ gethdr: * 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); + Dprintf(stdout, "read %ld recs, tape starts with %ld\n", + tpblksread, (long)tmpbuf.c_firstrec); if (tmpbuf.c_type == TS_TAPE && (tmpbuf.c_flags & DR_NEWHEADER)) { if (!wantnext) { tpblksread = tmpbuf.c_firstrec; @@ -444,8 +551,8 @@ gethdr: * -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" : ""); + Dprintf(stderr, "Skipping %ld duplicate record%s.\n", + (long)i, i > 1 ? "s" : ""); while (--i >= 0) readtape(buf); } @@ -465,7 +572,7 @@ gethdr: findinode(&spcl); if (gettingfile) { gettingfile = 0; - longjmp(restart, 1); + siglongjmp(restart, 1); } } @@ -473,7 +580,7 @@ gethdr: * Handle unexpected EOF. */ static void -terminateinput() +terminateinput(void) { if (gettingfile && curfile.action == USING) { @@ -486,7 +593,7 @@ terminateinput() curfile.ino = maxino; if (gettingfile) { gettingfile = 0; - longjmp(restart, 1); + siglongjmp(restart, 1); } } @@ -495,59 +602,45 @@ terminateinput() * appropriate one. */ static void -setdumpnum() +setdumpnum(void) { struct mtop tcom; if (dumpnum == 1 || volno != 1) return; - if (pipein) { - fprintf(stderr, "Cannot have multiple dumps on pipe input\n"); - done(1); - } + if (pipein) + errx(1, "Cannot have multiple dumps on pipe input"); tcom.mt_op = MTFSF; tcom.mt_count = dumpnum - 1; #ifdef RRESTORE if (host) rmtioctl(MTFSF, dumpnum - 1); - else + else #endif if (ioctl(mt, (int)MTIOCTOP, (char *)&tcom) < 0) - fprintf(stderr, "ioctl MTFSF: %s\n", strerror(errno)); + warn("ioctl MTFSF"); } void -printdumpinfo() +printdumpinfo(void) { -#ifdef __linux__ fprintf(stdout, "Dump date: %s", ctime4(&spcl.c_date)); fprintf(stdout, "Dumped from: %s", (spcl.c_ddate == 0) ? "the epoch\n" : ctime4(&spcl.c_ddate)); -#else - 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)); -#endif if (spcl.c_host[0] == '\0') return; - fprintf(stderr, "Level %d dump of %s on %s:%s\n", + fprintf(stdout, "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); + fprintf(stdout, "Label: %s\n", spcl.c_label); } int -extractfile(name) - char *name; +extractfile(char *name) { - int flags; + unsigned int flags; mode_t mode; struct timeval timep[2]; struct entry *ep; -#ifdef __linux__ - int err; - uid_t uid; - gid_t gid; -#endif curfile.name = name; curfile.action = USING; @@ -572,7 +665,7 @@ extractfile(name) return (FAIL); case IFSOCK: - vprintf(stdout, "skipped socket %s\n", name); + Vprintf(stdout, "skipped socket %s\n", name); skipfile(); return (GOOD); @@ -584,97 +677,106 @@ extractfile(name) skipfile(); return (GOOD); } - vprintf(stdout, "extract file %s\n", name); + Vprintf(stdout, "extract file %s\n", name); return (genliteraldir(name, curfile.ino)); case IFLNK: + { +#ifdef HAVE_LCHOWN + uid_t luid = curfile.dip->di_uid; + gid_t lgid = curfile.dip->di_gid; +#endif lnkbuf[0] = '\0'; pathlen = 0; -#ifdef __linux__ - uid = curfile.dip->di_uid; - gid = curfile.dip->di_gid; -#endif getfile(xtrlnkfile, xtrlnkskip); if (pathlen == 0) { - vprintf(stdout, + Vprintf(stdout, "%s: zero length symbolic link (ignored)\n", name); return (GOOD); } -#ifdef __linux__ - err = linkit(lnkbuf, name, SYMLINK); - if (err == GOOD) - (void) chown(name, uid, gid); - return (err); -#else - return (linkit(lnkbuf, name, SYMLINK)); + if (linkit(lnkbuf, name, SYMLINK) == FAIL) + return (FAIL); +#ifdef HAVE_LCHOWN + (void) lchown(name, luid, lgid); #endif + return (GOOD); + } - case IFCHR: - case IFBLK: - vprintf(stdout, "extract special file %s\n", name); + case IFIFO: + Vprintf(stdout, "extract fifo %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: %s\n", - name, strerror(errno)); + if (uflag && !Nflag) + (void)unlink(name); + if (mkfifo(name, mode) < 0) { + warn("%s: cannot create fifo", name); skipfile(); return (FAIL); } (void) chown(name, curfile.dip->di_uid, curfile.dip->di_gid); (void) chmod(name, mode); -#ifdef __linux__ - (void) fsetflags(name, flags); + if (flags) +#ifdef __linux__ + (void) fsetflags(name, flags); #else - (void) chflags(name, flags); + (void) chflags(name, flags); #endif skipfile(); utimes(name, timep); return (GOOD); - case IFIFO: - vprintf(stdout, "extract fifo %s\n", name); + case IFCHR: + case IFBLK: + Vprintf(stdout, "extract special file %s\n", name); if (Nflag) { skipfile(); return (GOOD); } - if (mkfifo(name, mode) < 0) { - fprintf(stderr, "%s: cannot create fifo: %s\n", - name, strerror(errno)); + if (uflag) + (void)unlink(name); + if (mknod(name, mode, (int)curfile.dip->di_rdev) < 0) { + warn("%s: cannot create special file", name); skipfile(); return (FAIL); } (void) chown(name, curfile.dip->di_uid, curfile.dip->di_gid); (void) chmod(name, mode); + if (flags) #ifdef __linux__ - (void) fsetflags(name, flags); + { + warn("%s: fsetflags called on a special file", name); + (void) fsetflags(name, flags); + } #else - (void) chflags(name, flags); + (void) chflags(name, flags); #endif skipfile(); utimes(name, timep); return (GOOD); case IFREG: - vprintf(stdout, "extract file %s\n", name); + Vprintf(stdout, "extract file %s\n", name); if (Nflag) { skipfile(); return (GOOD); } - if ((ofile = open(name, O_WRONLY | O_CREAT | O_TRUNC, + if (uflag) + (void)unlink(name); + if ((ofile = OPEN(name, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) { - fprintf(stderr, "%s: cannot create file: %s\n", - name, strerror(errno)); + warn("%s: cannot create file", name); skipfile(); return (FAIL); } (void) fchown(ofile, curfile.dip->di_uid, curfile.dip->di_gid); (void) fchmod(ofile, mode); + if (flags) #ifdef __linux__ - (void) fsetflags(ofile, flags); + (void) setflags(ofile, flags); #else - (void) fchflags(ofile, flags); + (void) fchflags(ofile, flags); #endif getfile(xtrfile, xtrskip); (void) close(ofile); @@ -688,7 +790,7 @@ extractfile(name) * skip over bit maps on the tape */ void -skipmaps() +skipmaps(void) { while (spcl.c_type == TS_BITS || spcl.c_type == TS_CLRI) @@ -699,7 +801,7 @@ skipmaps() * skip over a file on the tape */ void -skipfile() +skipfile(void) { curfile.action = SKIP; @@ -713,15 +815,13 @@ skipfile() * to the skip function. */ void -getfile(fill, skip) - void (*fill) __P((char *, long)); - void (*skip) __P((char *, long)); +getfile(void (*fill) __P((char *, size_t)), void (*skip) __P((char *, size_t))) { register int i; - int curblk = 0; - quad_t size = spcl.c_dinode.di_size; - int last_write_was_hole = 0; - long origsize = size; + volatile int curblk = 0; + volatile quad_t size = spcl.c_dinode.di_size; + volatile int last_write_was_hole = 0; + quad_t origsize = size; static char clearedbuf[MAXBSIZE]; char buf[MAXBSIZE / TP_BSIZE][TP_BSIZE]; char junk[TP_BSIZE]; @@ -735,17 +835,17 @@ getfile(fill, skip) gettingfile++; loop: for (i = 0; i < spcl.c_count; i++) { - if (spcl.c_addr[i]) { + if (readmapflag || spcl.c_addr[i]) { readtape(&buf[curblk++][0]); if (curblk == fssize / TP_BSIZE) { - (*fill)((char *)buf, (long)(size > TP_BSIZE ? + (*fill)((char *)buf, (size_t)(size > TP_BSIZE ? fssize : (curblk - 1) * TP_BSIZE + size)); curblk = 0; last_write_was_hole = 0; } } else { if (curblk > 0) { - (*fill)((char *)buf, (long)(size > TP_BSIZE ? + (*fill)((char *)buf, (size_t)(size > TP_BSIZE ? curblk * TP_BSIZE : (curblk - 1) * TP_BSIZE + size)); curblk = 0; @@ -756,7 +856,7 @@ loop: } if ((size -= TP_BSIZE) <= 0) { for (i++; i < spcl.c_count; i++) - if (spcl.c_addr[i]) + if (readmapflag || spcl.c_addr[i]) readtape(junk); break; } @@ -764,16 +864,25 @@ loop: 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); + Dprintf(stdout, + "Missing address (header) block for %s at %ld blocks\n", + curfile.name, (long)blksread); } if (curblk > 0) { - (*fill)((char *)buf, (curblk * TP_BSIZE) + size); + (*fill)((char *)buf, (size_t)(curblk * TP_BSIZE) + size); last_write_was_hole = 0; } + if (size > 0) { + fprintf(stderr, "Missing blocks at the end of %s, assuming hole\n", curfile.name); + while (size > 0) { + size_t skp = size > TP_BSIZE ? TP_BSIZE : size; + (*skip)(clearedbuf, skp); + size -= skp; + } + last_write_was_hole = 1; + } if (last_write_was_hole) { - ftruncate(ofile, origsize); + FTRUNCATE(ofile, origsize); } findinode(&spcl); gettingfile = 0; @@ -783,19 +892,14 @@ loop: * Write out the next block of a file. */ static void -xtrfile(buf, size) - char *buf; - long size; +xtrfile(char *buf, size_t size) { if (Nflag) return; - if (write(ofile, buf, (int) size) == -1) { - fprintf(stderr, - "write error extracting inode %d, name %s\nwrite: %s\n", - curfile.ino, curfile.name, strerror(errno)); - done(1); - } + if (write(ofile, buf, (int) size) == -1) + err(1, "write error extracting inode %lu, name %s\nwrite", + (unsigned long)curfile.ino, curfile.name); } /* @@ -803,34 +907,25 @@ xtrfile(buf, size) */ /* ARGSUSED */ static void -xtrskip(buf, size) - char *buf; - long size; +xtrskip(char *buf, size_t size) { - 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); - } + if (LSEEK(ofile, (off_t)size, SEEK_CUR) == -1) + err(1, "seek error extracting inode %lu, name %s\nlseek", + (unsigned long)curfile.ino, curfile.name); } /* * Collect the next block of a symbolic link. */ static void -xtrlnkfile(buf, size) - char *buf; - long size; +xtrlnkfile(char *buf, size_t size) { pathlen += size; - if (pathlen > MAXPATHLEN) { - fprintf(stderr, "symbolic link name: %s->%s%s; too long %d\n", + if (pathlen > MAXPATHLEN) + errx(1, "symbolic link name: %s->%s%s; too long %d", curfile.name, lnkbuf, buf, pathlen); - done(1); - } (void) strcat(lnkbuf, buf); } @@ -839,23 +934,17 @@ xtrlnkfile(buf, size) */ /* ARGSUSED */ static void -xtrlnkskip(buf, size) - char *buf; - long size; +xtrlnkskip(char *buf, size_t size) { - fprintf(stderr, "unallocated block in symbolic link %s\n", - curfile.name); - done(1); + errx(1, "unallocated block in symbolic link %s", curfile.name); } /* * Collect the next block of a bit map. */ static void -xtrmap(buf, size) - char *buf; - long size; +xtrmap(char *buf, size_t size) { memmove(map, buf, size); @@ -867,9 +956,7 @@ xtrmap(buf, size) */ /* ARGSUSED */ static void -xtrmapskip(buf, size) - char *buf; - long size; +xtrmapskip(char *buf, size_t size) { panic("hole in map\n"); @@ -881,32 +968,83 @@ xtrmapskip(buf, size) */ /* ARGSUSED */ void -xtrnull(buf, size) - char *buf; - long size; +xtrnull(char *buf, size_t size) { return; } -int +#if COMPARE_ONTHEFLY +/* + * Compare the next block of a file. + */ +static void +xtrcmpfile(char *buf, size_t size) +{ + static char cmpbuf[MAXBSIZE]; + + if (cmperror) + return; + + if (read(ifile, cmpbuf, size) != size) { + fprintf(stderr, "%s: size has changed.\n", + curfile.name); + cmperror = 1; + return; + } + + if (memcmp(buf, cmpbuf, size) != 0) { + fprintf(stderr, "%s: tape and disk copies are different\n", + curfile.name); + cmperror = 1; + return; + } +} + +/* + * Skip over a hole in a file. + */ +static void +xtrcmpskip(char *buf, size_t size) +{ + static char cmpbuf[MAXBSIZE]; + int i; + + if (cmperror) + return; + + if (read(ifile, cmpbuf, size) != size) { + fprintf(stderr, "%s: size has changed.\n", + curfile.name); + cmperror = 1; + return; + } + + for (i = 0; i < size; ++i) + if (cmpbuf[i] != '\0') { + fprintf(stderr, "%s: tape and disk copies are different\n", + curfile.name); + cmperror = 1; + return; + } +} +#endif /* COMPARE_ONTHEFLY */ + +#if !COMPARE_ONTHEFLY +static int do_cmpfiles(int fd_tape, int fd_disk, long size) { -#ifndef BUFSIZE -#define BUFSIZE 1024 -#endif static char buf_tape[BUFSIZ]; static char buf_disk[BUFSIZ]; - int left = size; - int n_tape; - int n_disk; + ssize_t n_tape; + ssize_t n_disk; while (size > 0) { - if ((n_tape = read(fd_tape, buf_tape, BUFSIZE)) < 1) { + if ((n_tape = read(fd_tape, buf_tape, sizeof(buf_tape))) < 1) { close(fd_tape), close(fd_disk); panic("do_cmpfiles: unexpected EOF[1]"); } - if ((n_disk = read(fd_disk, buf_disk, BUFSIZE)) < 1) { + if ((n_disk = read(fd_disk, buf_disk, sizeof(buf_tape))) < 1) { close(fd_tape), close(fd_disk); panic("do_cmpfiles: unexpected EOF[2]"); } @@ -914,7 +1052,7 @@ do_cmpfiles(int fd_tape, int fd_disk, long size) close(fd_tape), close(fd_disk); panic("do_cmpfiles: sizes different!"); } - if (memcmp(buf_tape, buf_disk, n_tape) != 0) return (1); + if (memcmp(buf_tape, buf_disk, (size_t)n_tape) != 0) return (1); size -= n_tape; } return (0); @@ -923,26 +1061,29 @@ do_cmpfiles(int fd_tape, int fd_disk, long size) /* for debugging compare problems */ #undef COMPARE_FAIL_KEEP_FILE +static #ifdef COMPARE_FAIL_KEEP_FILE /* return true if tapefile should be unlinked after compare */ int #else void #endif -cmpfiles(char *tapefile, char *diskfile, struct stat *sbuf_disk) +cmpfiles(char *tapefile, char *diskfile, struct STAT *sbuf_disk) { - struct stat sbuf_tape; + struct STAT sbuf_tape; int fd_tape, fd_disk; - if (stat(tapefile, &sbuf_tape) != 0) { + if (STAT(tapefile, &sbuf_tape) != 0) { panic("Can't lstat tmp file %s: %s\n", tapefile, strerror(errno)); + compare_errors = 1; } if (sbuf_disk->st_size != sbuf_tape.st_size) { fprintf(stderr, - "%s: size changed from %d to %d.\n", - diskfile, sbuf_tape.st_size, sbuf_disk->st_size); + "%s: size changed from %ld to %ld.\n", + diskfile, (long)sbuf_tape.st_size, (long)sbuf_disk->st_size); + compare_errors = 1; #ifdef COMPARE_FAIL_KEEP_FILE return (0); #else @@ -950,12 +1091,14 @@ cmpfiles(char *tapefile, char *diskfile, struct stat *sbuf_disk) #endif } - if ((fd_tape = open(tapefile, O_RDONLY)) < 0) { + if ((fd_tape = OPEN(tapefile, O_RDONLY)) < 0) { panic("Can't open %s: %s\n", tapefile, strerror(errno)); + compare_errors = 1; } - if ((fd_disk = open(diskfile, O_RDONLY)) < 0) { + if ((fd_disk = OPEN(diskfile, O_RDONLY)) < 0) { close(fd_tape); panic("Can't open %s: %s\n", diskfile, strerror(errno)); + compare_errors = 1; } if (do_cmpfiles(fd_tape, fd_disk, sbuf_tape.st_size)) { @@ -963,6 +1106,7 @@ cmpfiles(char *tapefile, char *diskfile, struct stat *sbuf_disk) diskfile); close(fd_tape); close(fd_disk); + compare_errors = 1; #ifdef COMPARE_FAIL_KEEP_FILE /* rename the file to live in /tmp */ /* rename `tapefile' to /tmp/ */ @@ -972,7 +1116,7 @@ cmpfiles(char *tapefile, char *diskfile, struct stat *sbuf_disk) if (!p) { panic("can't find / in %s\n", diskfile); } - sprintf(newname, "%s/debug/%s", tmpdir, p + 1); + snprintf(newname, sizeof(newname), "%s/debug/%s", tmpdir, p + 1); if (rename(tapefile, newname)) { panic("rename from %s to %s failed: %s\n", tapefile, newname, @@ -996,33 +1140,41 @@ cmpfiles(char *tapefile, char *diskfile, struct stat *sbuf_disk) return (1); #endif } +#endif /* !COMPARE_ONTHEFLY */ -static char tmpfilename[128]; +#if !COMPARE_ONTHEFLY +static char tmpfilename[MAXPATHLEN]; +#endif void -comparefile(name) - char *name; +comparefile(char *name) { - static char *tmpfile = NULL; int mode; - struct stat sb, stemp; + struct STAT sb; int r; +#if !COMPARE_ONTHEFLY + static char *tmpfile = NULL; + struct STAT stemp; +#endif - if ((r = lstat(name, &sb)) != 0) { - fprintf(stderr, "%s: does not exist (%d, %d).\n", name, r, errno); + if ((r = LSTAT(name, &sb)) != 0) { + warn("%s: does not exist (%d)", name, r); + compare_errors = 1; skipfile(); return; } curfile.name = name; + curfile.action = USING; mode = curfile.dip->di_mode; - vprintf(stdout, "comparing %s (size: %d, mode: 0%o)\n", name, - sb.st_size, mode); + Vprintf(stdout, "comparing %s (size: %ld, mode: 0%o)\n", name, + (long)sb.st_size, mode); if (sb.st_mode != mode) { fprintf(stderr, "%s: mode changed from 0%o to 0%o.\n", name, mode & 07777, sb.st_mode & 07777); + compare_errors = 1; } switch (mode & IFMT) { default: @@ -1044,6 +1196,7 @@ comparefile(name) if (!(sb.st_mode & S_IFLNK)) { fprintf(stderr, "%s: is no longer a symbolic link\n", name); + compare_errors = 1; return; } lnkbuf[0] = '\0'; @@ -1053,17 +1206,20 @@ comparefile(name) fprintf(stderr, "%s: zero length symbolic link (ignored)\n", name); + compare_errors = 1; return; } if ((lsize = readlink(name, lbuf, MAXPATHLEN)) < 0) { panic("readlink of %s failed: %s", name, strerror(errno)); + compare_errors = 1; } lbuf[lsize] = 0; if (strcmp(lbuf, lnkbuf) != 0) { fprintf(stderr, "%s: symbolic link changed from %s to %s.\n", name, lnkbuf, lbuf); + compare_errors = 1; return; } return; @@ -1074,6 +1230,7 @@ comparefile(name) if (!(sb.st_mode & (S_IFCHR|S_IFBLK))) { fprintf(stderr, "%s: no longer a special file\n", name); + compare_errors = 1; skipfile(); return; } @@ -1082,25 +1239,48 @@ comparefile(name) fprintf(stderr, "%s: device changed from %d,%d to %d,%d.\n", name, - ((int)curfile.dip->di_rdev >> 8) & 0xf, - (int)curfile.dip->di_rdev & 0xf, - (sb.st_rdev >> 8) & 0xf, - sb.st_rdev & 0xf); + ((int)curfile.dip->di_rdev >> 8) & 0xff, + (int)curfile.dip->di_rdev & 0xff, + ((int)sb.st_rdev >> 8) & 0xff, + (int)sb.st_rdev & 0xff); + compare_errors = 1; } skipfile(); return; case IFREG: +#if COMPARE_ONTHEFLY + if ((ifile = OPEN(name, O_RDONLY)) < 0) { + panic("Can't open %s: %s\n", name, strerror(errno)); + skipfile(); + compare_errors = 1; + } + else { + cmperror = 0; + getfile(xtrcmpfile, xtrcmpskip); + if (!cmperror) { + char c; + if (read(ifile, &c, 1) != 0) { + fprintf(stderr, "%s: size has changed.\n", + name); + cmperror = 1; + } + } + if (cmperror) + compare_errors = 1; + close(ifile); + } +#else if (tmpfile == NULL) { /* argument to mktemp() must not be in RO space: */ - sprintf(tmpfilename, "%s/restoreCXXXXXX", tmpdir); + snprintf(tmpfilename, sizeof(tmpfilename), "%s/restoreCXXXXXX", tmpdir); tmpfile = mktemp(&tmpfilename[0]); } - if ((stat(tmpfile, &stemp) == 0) && (unlink(tmpfile) != 0)) { + if ((STAT(tmpfile, &stemp) == 0) && (unlink(tmpfile) != 0)) { panic("cannot delete tmp file %s: %s\n", tmpfile, strerror(errno)); } - if ((ofile = creat(tmpfile, 0600)) < 0) { + if ((ofile = OPEN(tmpfile, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0) { panic("cannot create file temp file %s: %s\n", name, strerror(errno)); } @@ -1113,28 +1293,69 @@ comparefile(name) cmpfiles(tmpfile, name, &sb); unlink(tmpfile); #endif +#endif /* COMPARE_ONTHEFLY */ return; } /* NOTREACHED */ } +#if defined(HAVE_ZLIB) || defined(HAVE_BZLIB) +static void (*readtape_func)(char *) = readtape_set; + +/* + * Read TP_BSIZE blocks from the input. + * Handle read errors, and end of media. + * Decompress compressed blocks. + */ +static void +readtape(char *buf) +{ + (*readtape_func)(buf); /* call the actual processing routine */ +} + +/* + * Set function pointer for readtape() routine. zflag and magtapein must + * be correctly set before the first call to readtape(). + */ +static void +readtape_set(char *buf) +{ + if (!zflag) + readtape_func = readtape_uncompr; + else { + newcomprbuf(ntrec); + if (magtapein) + readtape_func = readtape_comprtape; + else + readtape_func = readtape_comprfile; + } + readtape(buf); +} + +#endif /* HAVE_ZLIB || HAVE_BZLIB */ + /* + * This is the original readtape(), it's used for reading uncompressed input. * Read TP_BSIZE blocks from the input. * Handle read errors, and end of media. */ static void -readtape(buf) - char *buf; +#if defined(HAVE_ZLIB) || defined(HAVE_BZLIB) +readtape_uncompr(char *buf) +#else +readtape(char *buf) +#endif { - long rd, newvol, i; + ssize_t rd, newvol, i; int cnt, seek_failed; if (blkcnt < numtrec) { - memmove(buf, &tapebuf[(blkcnt++ * TP_BSIZE)], (long)TP_BSIZE); + memmove(buf, &tbufptr[(blkcnt++ * TP_BSIZE)], TP_BSIZE); blksread++; tpblksread++; return; } + tbufptr = tapebuf; for (i = 0; i < ntrec; i++) ((struct s_spcl *)&tapebuf[i * TP_BSIZE])->c_magic = 0; if (numtrec == 0) @@ -1148,12 +1369,13 @@ getmore: else #endif 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"); + Dprintf(stdout, "mid-media short read error.\n"); numtrec = ntrec; } /* @@ -1173,9 +1395,9 @@ getmore: * 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); + Vprintf(stdout, + "partial block read: %ld should be %ld\n", + (long)i, ntrec * TP_BSIZE); numtrec = i / TP_BSIZE; } } @@ -1195,32 +1417,33 @@ getmore: fprintf(stderr, "restoring %s\n", curfile.name); break; case SKIP: - fprintf(stderr, "skipping over inode %d\n", - curfile.ino); + fprintf(stderr, "skipping over inode %lu\n", + (unsigned long)curfile.ino); break; } if (!yflag && !reply("continue")) - done(1); + exit(1); i = ntrec * TP_BSIZE; - memset(tapebuf, 0, i); + memset(tapebuf, 0, (size_t)i); #ifdef RRESTORE if (host) seek_failed = (rmtseek(i, 1) < 0); else #endif - seek_failed = (lseek(mt, i, SEEK_CUR) == (off_t)-1); + seek_failed = (LSEEK(mt, i, SEEK_CUR) == (off_t)-1); if (seek_failed) { - fprintf(stderr, - "continuation failed: %s\n", strerror(errno)); - done(1); + warn("continuation failed"); + if (!yflag && !reply("assume end-of-tape and continue")) + exit(1); + i = 0; } } /* * Handle end of tape. */ if (i == 0) { - vprintf(stdout, "End-of-tape encountered\n"); + Vprintf(stdout, "End-of-tape encountered\n"); if (!pipein) { newvol = volno + 1; volno = 0; @@ -1233,45 +1456,456 @@ getmore: panic("partial block read: %d should be %d\n", rd, ntrec * TP_BSIZE); terminateinput(); - memmove(&tapebuf[rd], &endoftapemark, (long)TP_BSIZE); + memmove(&tapebuf[rd], &endoftapemark, TP_BSIZE); } blkcnt = 0; - memmove(buf, &tapebuf[(blkcnt++ * TP_BSIZE)], (long)TP_BSIZE); + memmove(buf, &tbufptr[(blkcnt++ * TP_BSIZE)], TP_BSIZE); blksread++; tpblksread++; } +#if defined(HAVE_ZLIB) || defined(HAVE_BZLIB) + +/* + * Read a compressed format block from a file or pipe and uncompress it. + * Attempt to handle read errors, and end of file. + */ static void -findtapeblksize() +readtape_comprfile(char *buf) { - register long i; + long rl, size, i, ret; + int newvol; + struct tapebuf *tpb; + if (blkcnt < numtrec) { + memmove(buf, &tbufptr[(blkcnt++ * TP_BSIZE)], TP_BSIZE); + blksread++; + tpblksread++; + return; + } + /* need to read the next block */ + tbufptr = tapebuf; for (i = 0; i < ntrec; i++) ((struct s_spcl *)&tapebuf[i * TP_BSIZE])->c_magic = 0; + numtrec = ntrec; + tpb = (struct tapebuf *) tapebuf; + + /* read the block prefix */ + ret = read_a_block(mt, tapebuf, PREFIXSIZE, &rl); + + if (Vflag && (ret == 0 || rl < PREFIXSIZE || tpb->length == 0)) + ret = 0; + if (ret <= 0) + goto readerr; + + /* read the data */ + size = tpb->length; + if (size > bufsize) { + /* something's wrong */ + Vprintf(stdout, "Prefix size error, max size %d, got %ld\n", + bufsize, size); + size = bufsize; + tpb->length = bufsize; + } + ret = read_a_block(mt, tpb->buf, size, &rl); + if (ret <= 0) + goto readerr; + + tbufptr = decompress_tapebuf(tpb, rl + PREFIXSIZE); + if (tbufptr == NULL) { + msg_read_error("File decompression error while"); + if (!yflag && !reply("continue")) + exit(1); + memset(tapebuf, 0, bufsize); + tbufptr = tapebuf; + } + blkcnt = 0; + memmove(buf, &tbufptr[(blkcnt++ * TP_BSIZE)], TP_BSIZE); + blksread++; + tpblksread++; + return; + +readerr: + /* Errors while reading from a file or pipe are catastrophic. Since + * there are no block boundaries, it's impossible to bypass the + * block in error and find the start of the next block. + */ + if (ret == 0) { + /* It's possible to have multiple input files using -M + * and -f file1,file2... + */ + Vprintf(stdout, "End-of-File encountered\n"); + if (!pipein) { + newvol = volno + 1; + volno = 0; + numtrec = 0; + getvol(newvol); + readtape(buf); + return; + } + } + msg_read_error("Read error while"); + /* if (!yflag && !reply("continue")) */ + exit(1); +} + +/* + * Read compressed data from a tape and uncompress it. + * Handle read errors, and end of media. + * Since a tape consists of separate physical blocks, we try + * to recover from errors by repositioning the tape to the next + * block. + */ +static void +readtape_comprtape(char *buf) +{ + long rl, size, i; + int ret, newvol; + struct tapebuf *tpb; + struct mtop tcom; + + if (blkcnt < numtrec) { + memmove(buf, &tbufptr[(blkcnt++ * TP_BSIZE)], TP_BSIZE); + blksread++; + tpblksread++; + return; + } + /* need to read the next block */ + tbufptr = tapebuf; + for (i = 0; i < ntrec; i++) + ((struct s_spcl *)&tapebuf[i * TP_BSIZE])->c_magic = 0; + numtrec = ntrec; + tpb = (struct tapebuf *) tapebuf; + + /* read the block */ + size = bufsize + PREFIXSIZE; + ret = read_a_block(mt, tapebuf, size, &rl); + if (ret <= 0) + goto readerr; + + tbufptr = decompress_tapebuf(tpb, rl); + if (tbufptr == NULL) { + msg_read_error("Tape decompression error while"); + if (!yflag && !reply("continue")) + exit(1); + memset(tapebuf, 0, PREFIXSIZE + bufsize); + tbufptr = tapebuf; + } + goto moverecord; + +readerr: + /* Handle errors: EOT switches to the next volume, other errors + * attempt to position the tape to the next block. + */ + if (ret == 0) { + Vprintf(stdout, "End-of-tape encountered\n"); + newvol = volno + 1; + volno = 0; + numtrec = 0; + getvol(newvol); + readtape(buf); + return; + } + + msg_read_error("Tape read error while"); + if (!yflag && !reply("continue")) + exit(1); + memset(tapebuf, 0, PREFIXSIZE + bufsize); + tbufptr = tapebuf; + #ifdef RRESTORE if (host) - i = rmtread(tapebuf, ntrec * TP_BSIZE); + rl = rmtioctl(MTFSR, 1); else #endif - i = read(mt, tapebuf, ntrec * TP_BSIZE); + { + tcom.mt_op = MTFSR; + tcom.mt_count = 1; + rl = ioctl(mt, MTIOCTOP, &tcom); + } - if (i <= 0) { - fprintf(stderr, "tape read error: %s\n", strerror(errno)); - done(1); + if (rl < 0) { + warn("continuation failed"); + if (!yflag && !reply("assume end-of-tape and continue")) + exit(1); + ret = 0; /* end of tape */ + goto readerr; } + +moverecord: + blkcnt = 0; + memmove(buf, &tbufptr[(blkcnt++ * TP_BSIZE)], TP_BSIZE); + blksread++; + tpblksread++; +} + +/* + * Decompress a struct tapebuf into a buffer. readsize is the size read + * from the tape/file and is used for error messages. Returns a pointer + * to the location of the uncompressed buffer or NULL on errors. + * Adjust numtrec and complain for a short block. + */ +static char * +decompress_tapebuf(struct tapebuf *tpbin, int readsize) +{ + /* If zflag is on, all blocks have a struct tapebuf prefix */ + /* zflag gets set in setup() from the dump header */ + int cresult, blocklen; + unsigned long worklen; +#ifdef HAVE_BZLIB + unsigned int worklen2; +#endif + char *output = NULL,*reason = NULL, *lengtherr = NULL; + + /* build a length error message */ + blocklen = tpbin->length; + if (readsize < blocklen + PREFIXSIZE) + lengtherr = "short"; + else + if (readsize > blocklen + PREFIXSIZE) + lengtherr = "long"; + + worklen = comprlen; + cresult = 1; + if (tpbin->compressed) { + /* uncompress whatever we read, if it fails, complain later */ + if (tpbin->flags == COMPRESS_ZLIB) { +#ifndef HAVE_ZLIB + errx(1,"This restore version doesn't support zlib decompression"); +#else + cresult = uncompress(comprbuf, &worklen, + tpbin->buf, blocklen); + output = comprbuf; + switch (cresult) { + case Z_OK: + break; + case Z_MEM_ERROR: + reason = "not enough memory"; + break; + case Z_BUF_ERROR: + reason = "buffer too small"; + break; + case Z_DATA_ERROR: + reason = "data error"; + break; + default: + reason = "unknown"; + } + if (cresult == Z_OK) + cresult = 1; + else + cresult = 0; +#endif /* HAVE_ZLIB */ + } + if (tpbin->flags == COMPRESS_BZLIB) { +#ifndef HAVE_BZLIB + errx(1,"This restore version doesn't support bzlib decompression"); +#else + worklen2 = worklen; + cresult = BZ2_bzBuffToBuffDecompress( + comprbuf, &worklen2, + tpbin->buf, blocklen, 0, 0); + worklen = worklen2; + output = comprbuf; + switch (cresult) { + case BZ_OK: + break; + case BZ_MEM_ERROR: + reason = "not enough memory"; + break; + case BZ_OUTBUFF_FULL: + reason = "buffer too small"; + break; + case BZ_DATA_ERROR: + case BZ_DATA_ERROR_MAGIC: + case BZ_UNEXPECTED_EOF: + reason = "data error"; + break; + default: + reason = "unknown"; + } + if (cresult == BZ_OK) + cresult = 1; + else + cresult = 0; +#endif /* HAVE_BZLIB */ + } + } + else { + output = tpbin->buf; + worklen = blocklen; + } + if (cresult) { + numtrec = worklen / TP_BSIZE; + if (worklen % TP_BSIZE != 0) + reason = "length mismatch"; + } + if (reason) { + if (lengtherr) + fprintf(stderr, "%s compressed block: %d expected: %d\n", + lengtherr, readsize, tpbin->length + PREFIXSIZE); + fprintf(stderr, "decompression error, block %ld: %s\n", + tpblksread+1, reason); + if (!cresult) + output = NULL; + } + return output; +} + +/* + * Print an error message for a read error. + * This was exteracted from the original readtape(). + */ +static void +msg_read_error(char *m) +{ + switch (curfile.action) { + default: + fprintf(stderr, "%s trying to set up tape\n", m); + break; + case UNKNOWN: + fprintf(stderr, "%s trying to resynchronize\n", m); + break; + case USING: + fprintf(stderr, "%s restoring %s\n", m, curfile.name); + break; + case SKIP: + fprintf(stderr, "%s skipping over inode %lu\n", m, + (unsigned long)curfile.ino); + break; + } +} +#endif /* HAVE_ZLIB || HAVE_BZLIB */ + +/* + * Read the first block and get the blocksize from it. Test + * for a compressed dump tape/file. setup() will make the final + * determination by checking the compressed flag if gethead() + * finds a valid header. The test here is necessary to offset the buffer + * by the size of the compressed prefix. zflag is set here so that + * readtape_set can set the correct function pointer for readtape(). + * Note that the first block of each tape/file is not compressed + * and does not have a prefix. + */ +static void +findtapeblksize(void) +{ + long i; + size_t len; + struct tapebuf *tpb = (struct tapebuf *) tapebuf; + struct s_spcl *spclpt = (struct s_spcl *) tapebuf; + + for (i = 0; i < ntrec; i++) + ((struct s_spcl *)&tapebuf[i * TP_BSIZE])->c_magic = 0; + blkcnt = 0; + tbufptr = tapebuf; + /* + * For a pipe or file, read in the first record. For a tape, read + * the first block. + */ + len = magtapein ? ntrec * TP_BSIZE : TP_BSIZE; + + if (read_a_block(mt, tapebuf, len, &i) <= 0) + errx(1, "Tape read error on first record"); + + /* + * If the input is from a file or a pipe, we read TP_BSIZE + * bytes looking for a dump header. If the dump is compressed + * we need to read in the rest of the block, as determined + * by c_ntrec in the dump header. The first block of the + * dump is not compressed and does not have a prefix. + */ + if (!magtapein) { + if (spclpt->c_type == TS_TAPE + && spclpt->c_flags & DR_COMPRESSED) { + /* It's a compressed dump file, read in the */ + /* rest of the block based on spclpt->c_ntrec. */ + if (spclpt->c_ntrec > ntrec) + errx(1, "Tape blocksize is too large, use " + "\'-b %d\' ", spclpt->c_ntrec); + ntrec = spclpt->c_ntrec; + len = (ntrec - 1) * TP_BSIZE; + zflag = 1; + } + else { + /* read in the rest of the block based on bufsize */ + len = bufsize - TP_BSIZE; + } + if (read_a_block(mt, tapebuf+TP_BSIZE, len, &i) < 0 + || (i != len && i % TP_BSIZE != 0)) + errx(1,"Error reading dump file header"); + tbufptr = tapebuf; + numtrec = ntrec; + Vprintf(stdout, "Input block size is %ld\n", ntrec); + return; + } /* if (!magtapein) */ + + /* + * If the input is a tape, we tried to read ntrec * TP_BSIZE bytes. + * If the value of ntrec is too large, we read less than + * what we asked for; adjust the value of ntrec and test for + * a compressed dump tape. + */ + 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); + /* may be old format compressed dump tape with a prefix */ + spclpt = (struct s_spcl *) tpb->buf; + if (i % TP_BSIZE == PREFIXSIZE + && tpb->compressed == 0 + && spclpt->c_type == TS_TAPE + && spclpt->c_flags & DR_COMPRESSED) { + zflag = 1; + tbufptr = tpb->buf; + if (tpb->length > bufsize) + errx(1, "Tape blocksize is too large, use " + "\'-b %d\' ", tpb->length / TP_BSIZE); + } + else + errx(1, "Tape block size (%ld) is not a multiple of dump block size (%d)", + i, TP_BSIZE); } ntrec = i / TP_BSIZE; + if (spclpt->c_type == TS_TAPE) { + if (spclpt->c_flags & DR_COMPRESSED) + zflag = 1; + if (spclpt->c_ntrec > ntrec) + errx(1, "Tape blocksize is too large, use " + "\'-b %d\' ", spclpt->c_ntrec); + } numtrec = ntrec; - vprintf(stdout, "Tape block size is %d\n", ntrec); + Vprintf(stdout, "Tape block size is %ld\n", ntrec); +} + +/* + * Read a block of data handling all of the messy details. + */ +static int read_a_block(int fd, char *buf, size_t len, long *lengthread) +{ + long i = 1, size; + + size = len; + while (size > 0) { +#ifdef RRESTORE + if (host) + i = rmtread(buf, size); + else +#endif + i = read(fd, buf, size); + + if (i <= 0) + break; /* EOD or error */ + size -= i; + if (magtapein) + break; /* block at a time for mt */ + buf += i; + } + *lengthread = len - size; + return i; } void -closemt() +closemt(void) { if (mt < 0) @@ -1284,6 +1918,28 @@ closemt() (void) close(mt); } +static void +setmagtapein(void) { + struct mtget mt_stat; + static int done = 0; + if (done) + return; + done = 1; + if (!pipein) { + /* need to know if input is really from a tape */ +#ifdef RRESTORE + if (host) + magtapein = rmtioctl(MTNOP, 1) != -1; + else +#endif + magtapein = ioctl(mt, MTIOCGET, (char *)&mt_stat) == 0; + } + + Vprintf(stdout,"Input is from %s\n", + magtapein ? "tape" : + Vflag ? "multi-volume (no tape)" : "file/pipe"); +} + /* * Read the next block from the tape. * Check to see if it is one of several vintage headers. @@ -1291,38 +1947,37 @@ closemt() * If it is not any valid header, return an error. */ static int -gethead(buf) - struct s_spcl *buf; +gethead(struct s_spcl *buf) { - long i; + int32_t i; union { quad_t qval; - long val[2]; + int32_t val[2]; } qcvt; 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; + int32_t c_type; + int32_t c_date; + int32_t c_ddate; + int32_t c_volume; + int32_t c_tapea; + u_int16_t c_inumber; + int32_t c_magic; + int32_t 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; + u_int16_t odi_mode; + u_int16_t odi_nlink; + u_int16_t odi_uid; + u_int16_t odi_gid; + int32_t odi_size; + int32_t odi_rdev; char odi_addr[36]; - long odi_atime; - long odi_mtime; - long odi_ctime; + int32_t odi_atime; + int32_t odi_mtime; + int32_t odi_ctime; } c_dinode; - long c_count; + int32_t c_count; char c_addr[256]; } s_ospcl; } u_ospcl; @@ -1330,17 +1985,17 @@ gethead(buf) if (!cvtflag) { readtape((char *)buf); if (buf->c_magic != NFS_MAGIC) { - if (swabl(buf->c_magic) != NFS_MAGIC) + if (swabi(buf->c_magic) != NFS_MAGIC) return (FAIL); if (!Bcvt) { - vprintf(stdout, "Note: Doing Byte swapping\n"); + 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); + swabst((u_char *)"8i4s31i528bi192b2i", (u_char *)buf); goto good; } readtape((char *)(&u_ospcl.s_ospcl)); @@ -1391,6 +2046,7 @@ good: qcvt.val[0] = i; buf->c_dinode.di_size = qcvt.qval; } + readmapflag = 0; switch (buf->c_type) { @@ -1401,8 +2057,11 @@ good: */ 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]++; + if (buf->c_count > TP_NINDIR) + readmapflag = 1; + else + for (i = 0; i < buf->c_count; i++) + buf->c_addr[i]++; break; case TS_TAPE: @@ -1422,7 +2081,7 @@ good: break; } /* - * If we are restoring a filesystem with old format inodes, + * If we are restoring a filesystem with old format inodes, * copy the uid/gid to the new location. */ if (oldinofmt) { @@ -1438,10 +2097,9 @@ good: * Check that a header is where it belongs and predict the next header */ static void -accthdr(header) - struct s_spcl *header; +accthdr(struct s_spcl *header) { - static ino_t previno = 0x7fffffff; + static dump_ino_t previno = 0x7fffffff; static int prevtype; static long predict; long blks, i; @@ -1466,24 +2124,24 @@ accthdr(header) fprintf(stderr, "Used inodes map header"); break; case TS_INODE: - fprintf(stderr, "File header, ino %d", previno); + fprintf(stderr, "File header, ino %lu", (unsigned long)previno); break; case TS_ADDR: - fprintf(stderr, "File continuation header, ino %d", previno); + fprintf(stderr, "File continuation header, ino %ld", (long)previno); break; case TS_END: fprintf(stderr, "End of tape header"); break; } if (predict != blksread - 1) - fprintf(stderr, "; predicted %d blocks, got %d blocks", + fprintf(stderr, "; predicted %ld blocks, got %ld 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) + if (readmapflag || header->c_addr[i] != 0) blks++; predict = blks; blksread = 0; @@ -1496,8 +2154,7 @@ newcalc: * Complain if had to skip, and complain is set. */ static void -findinode(header) - struct s_spcl *header; +findinode(struct s_spcl *header) { static long skipcnt = 0; long i; @@ -1556,13 +2213,16 @@ findinode(header) } } while (header->c_type == TS_ADDR); if (skipcnt > 0) - fprintf(stderr, "resync restore, skipped %d blocks\n", skipcnt); +#ifdef USE_QFA + if (!noresyncmesg) +#endif + fprintf(stderr, "resync restore, skipped %ld blocks\n", + skipcnt); skipcnt = 0; } static int -checksum(buf) - register int *buf; +checksum(int *buf) { register int i, j; @@ -1575,28 +2235,28 @@ checksum(buf) } else { /* What happens if we want to read restore tapes for a 16bit int machine??? */ - do - i += swabl(*buf++); + do + i += swabi(*buf++); while (--j); } - + if (i != CHECKSUM) { - fprintf(stderr, "Checksum error %o, inode %d file %s\n", i, - curfile.ino, curfile.name); + fprintf(stderr, "Checksum error %o, inode %lu file %s\n", i, + (unsigned long)curfile.ino, curfile.name); return(FAIL); } return(GOOD); } #ifdef RRESTORE -#if __STDC__ +#ifdef __STDC__ #include #else #include #endif void -#if __STDC__ +#ifdef __STDC__ msg(const char *fmt, ...) #else msg(fmt, va_alist) @@ -1605,7 +2265,7 @@ msg(fmt, va_alist) #endif { va_list ap; -#if __STDC__ +#ifdef __STDC__ va_start(ap, fmt); #else va_start(ap); @@ -1616,9 +2276,7 @@ msg(fmt, va_alist) #endif /* RRESTORE */ static u_char * -swabshort(sp, n) - register u_char *sp; - register int n; +swab16(u_char *sp, int n) { char c; @@ -1630,23 +2288,35 @@ swabshort(sp, n) } static u_char * -swablong(sp, n) - register u_char *sp; - register int n; +swab32(u_char *sp, 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; + c = sp[1]; sp[1] = sp[2]; sp[2] = c; sp += 4; } return (sp); } +static u_char * +swab64(u_char *sp, int n) +{ + char c; + + while (--n >= 0) { + c = sp[0]; sp[0] = sp[7]; sp[7] = c; + c = sp[1]; sp[1] = sp[6]; sp[6] = c; + c = sp[2]; sp[2] = sp[5]; sp[5] = c; + c = sp[3]; sp[3] = sp[4]; sp[4] = c; + sp += 8; + } + return (sp); +} + void -swabst(cp, sp) - register u_char *cp, *sp; +swabst(u_char *cp, u_char *sp) { int n = 0; @@ -1656,17 +2326,23 @@ swabst(cp, sp) 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); + sp = swab16(sp, n); + break; + + case 'i': + if (n == 0) + n = 1; + sp = swab32(sp, n); break; case 'l': if (n == 0) n = 1; - sp = swablong(sp, n); + sp = swab64(sp, n); break; default: /* Any other character, like 'b' counts as byte. */ @@ -1680,10 +2356,87 @@ swabst(cp, sp) } } +static u_int +swabi(u_int x) +{ + swabst((u_char *)"i", (u_char *)&x); + return (x); +} + +#if 0 static u_long -swabl(x) - u_long x; +swabl(u_long x) { swabst((u_char *)"l", (u_char *)&x); return (x); } +#endif + +#ifdef USE_QFA +/* + * get the current position of the tape + */ +int +GetTapePos(long *pos) +{ + int err = 0; + + *pos = 0; + if (ioctl(mt, MTIOCPOS, pos) == -1) { + err = errno; + fprintf(stdout, "[%ld] error: %d (getting tapepos: %ld)\n", + (unsigned long)getpid(), err, *pos); + return err; + } + return err; +} + +typedef struct mt_pos { + short mt_op; + int mt_count; +} MTPosRec, *MTPosPtr; + +/* + * go to specified position on tape + */ +int +GotoTapePos(long pos) +{ + int err = 0; + struct mt_pos buf; + + buf.mt_op = MTSEEK; + buf.mt_count = pos; + if (ioctl(mt, MTIOCTOP, &buf) == -1) { + err = errno; + fprintf(stdout, "[%ld] error: %d (setting tapepos: %ld)\n", + (unsigned long)getpid(), err, pos); + return err; + } + return err; +} + +/* + * read next data from tape to re-sync + */ +void +ReReadFromTape(void) +{ + FLUSHTAPEBUF(); + noresyncmesg = 1; + if (gethead(&spcl) == FAIL) { +#ifdef DEBUG_QFA + fprintf(stdout, "DEBUG 1 gethead failed\n"); +#endif + } + findinode(&spcl); + noresyncmesg = 0; +} + +void +RequestVol(long tnum) +{ + FLUSHTAPEBUF(); + getvol(tnum); +} +#endif /* USE_QFA */