/* * Copyright (c) 1983 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include "restore.h" #include #include #include #include #include #include #include /* for expdev */ #include #include #include #include #define MAXINO 65535 /* KLUDGE */ #define MAXTAPES 128 static size_t fssize = MAXBSIZE; /* preferred size of writes to filesystem */ int mt = -1; static int continuemap = 0; char magtape[BUFSIZ]; int pipein = 0; char *host; /* used in dumprmt.c */ daddr32_t rec_position; static char *archivefile; /* used in metamucil.c */ static int bct; /* block # index into tape record buffer */ static int numtrec; /* # of logical blocks in current tape record */ static char *tbf = NULL; static size_t tbfsize = 0; static int recsread; union u_spcl u_spcl; static union u_spcl endoftapemark; static struct s_spcl dumpinfo; static long blksread; /* # of logical blocks actually read/touched */ static long tapea; /* current logical block # on tape */ static uchar_t tapesread[MAXTAPES]; static jmp_buf restart; static int gettingfile = 0; /* restart has a valid frame */ static int ofile; static char *map, *beginmap; static char *endmap; static char lnkbuf[MAXPATHLEN + 2]; static int pathlen; static int inodeinfo; /* Have starting volume information */ static int hostinfo; /* Have dump host information */ static int autoload_tape(void); static void setdumpnum(void); static void metacheck(struct s_spcl *); static void xtrmeta(char *, size_t); static void metaskip(char *, size_t); static void xtrfile(char *, size_t); static void xtrskip(char *, size_t); static void xtrlnkfile(char *, size_t); static void xtrlnkskip(char *, size_t); static void xtrmap(char *, size_t); static void xtrmapskip(char *, size_t); static void readtape(char *); static int checkvol(struct s_spcl *, int); static void accthdr(struct s_spcl *); static int ishead(struct s_spcl *); static int checktype(struct s_spcl *, int); static void metaset(char *name); /* * Set up an input source */ void setinput(char *source, char *archive) { flsht(); archivefile = archive; if (bflag == 0) { ntrec = ((CARTRIDGETREC > HIGHDENSITYTREC) ? (NTREC > CARTRIDGETREC ? NTREC : CARTRIDGETREC) : (NTREC > HIGHDENSITYTREC ? NTREC : HIGHDENSITYTREC)); saved_ntrec = (ntrec * (tp_bsize/DEV_BSIZE)); } newtapebuf(ntrec); terminal = stdin; if (source == NULL) { /* A can't-happen */ (void) fprintf(stderr, gettext("Internal consistency check failed.\n")); done(1); } if (strchr(source, ':')) { char *tape; host = source; tape = strchr(host, ':'); *tape++ = '\0'; if (strlen(tape) > (sizeof (magtape) - 1)) { (void) fprintf(stderr, gettext("Tape name too long\n")); done(1); } (void) strcpy(magtape, tape); if (rmthost(host, ntrec) == 0) done(1); } else { if (strlen(source) > (sizeof (magtape) - 1)) { (void) fprintf(stderr, gettext("Tape name too long\n")); done(1); } /* Not remote, no need for privileges */ __priv_relinquish(); host = NULL; if (strcmp(source, "-") == 0) { /* * Since input is coming from a pipe we must establish * our own connection to the terminal. */ terminal = fopen("/dev/tty", "r"); if (terminal == NULL) { int saverr = errno; char *msg = gettext("Cannot open(\"/dev/tty\")"); errno = saverr; perror(msg); terminal = fopen("/dev/null", "r"); if (terminal == NULL) { saverr = errno; msg = gettext( "Cannot open(\"/dev/null\")"); errno = saverr; perror(msg); done(1); } } pipein++; if (archive) { (void) fprintf(stderr, gettext( "Cannot specify an archive file when reading from a pipe\n")); done(1); } } (void) strcpy(magtape, source); } } void newtapebuf(size_t size) { size_t nsize; nsize = size * tp_bsize; ntrec = size; if (nsize <= tbfsize) return; if (tbf != NULL) free(tbf); tbf = (char *)malloc(nsize); if (tbf == NULL) { (void) fprintf(stderr, gettext("Cannot allocate space for buffer\n")); done(1); } tbfsize = nsize; } /* * Verify that the tape drive can be accessed and * that it actually is a dump tape. */ void setup(void) { int i, j; int32_t *ip; struct stat stbuf; size_t mapsize; char *syment = RESTORESYMTABLE; vprintf(stdout, gettext("Verify volume and initialize maps\n")); if (archivefile) { mt = open(archivefile, O_RDONLY|O_LARGEFILE); if (mt < 0) { perror(archivefile); done(1); } volno = 0; } else if (host) { if ((mt = rmtopen(magtape, O_RDONLY)) < 0) { perror(magtape); done(1); } volno = 1; } else { if (pipein) mt = 0; else if ((mt = open(magtape, O_RDONLY|O_LARGEFILE)) < 0) { perror(magtape); done(1); } volno = 1; } setdumpnum(); flsht(); if (!pipein && !bflag) if (archivefile) findtapeblksize(ARCHIVE_FILE); else findtapeblksize(TAPE_FILE); if (bflag == 1) { tape_rec_size = saved_ntrec * DEV_BSIZE; } /* * Get the first header. If c_magic is NOT NFS_MAGIC or if * the checksum is in error, it will fail. The magic could then * be either OFS_MAGIC or MTB_MAGIC. If OFS_MAGIC, assume we * have an old dump, and try to convert it. If it is MTB_MAGIC, we * procees this after. */ if ((gethead(&spcl) == FAIL) && (spcl.c_magic != MTB_MAGIC)) { bct--; /* push back this block */ blksread--; tapea--; cvtflag++; if (gethead(&spcl) == FAIL) { (void) fprintf(stderr, gettext("Volume is not in dump format\n")); done(1); } (void) fprintf(stderr, gettext("Converting to new file system format.\n")); } /* * The above gethead will have failed if the magic is * MTB_MAGIC. If that is true, we need to adjust tp_bsize. * We have assumed to this time that tp_bsize was 1024, if * this is a newer dump, get the real tp_bsize from the header, * and recalculate ntrec, numtrec. */ if (spcl.c_magic == MTB_MAGIC) { tp_bsize = spcl.c_tpbsize; if ((tp_bsize % TP_BSIZE_MIN != 0) || (tp_bsize > TP_BSIZE_MAX)) { (void) fprintf(stderr, gettext("Volume is not in dump format\n")); done(1); } ntrec = (tape_rec_size/tp_bsize); numtrec = ntrec; newtapebuf(ntrec); bct--; /* push back this block */ blksread--; tapea--; /* we have to re-do this in case checksum is wrong */ if (gethead(&spcl) == FAIL) { (void) fprintf(stderr, gettext("Volume is not in dump format\n")); done(1); } } if (vflag) byteorder_banner(byteorder, stdout); if (pipein) { endoftapemark.s_spcl.c_magic = cvtflag ? OFS_MAGIC : ((tp_bsize == TP_BSIZE_MIN) ? NFS_MAGIC : MTB_MAGIC); endoftapemark.s_spcl.c_type = TS_END; /* * include this since the `resync' loop in findinode * expects to find a header with the c_date field * filled in. */ endoftapemark.s_spcl.c_date = spcl.c_date; ip = (int32_t *)&endoftapemark; /*LINTED [assertion always true]*/ assert((sizeof (endoftapemark) % sizeof (int32_t)) == 0); j = sizeof (endoftapemark) / sizeof (int32_t); i = 0; do i += *ip++; while (--j) ; endoftapemark.s_spcl.c_checksum = CHECKSUM - i; } if (vflag && command != 't') printdumpinfo(); dumptime = spcl.c_ddate; dumpdate = spcl.c_date; if (stat(".", &stbuf) < 0) { perror(gettext("cannot stat .")); done(1); } if (stbuf.st_blksize >= tp_bsize && stbuf.st_blksize <= MAXBSIZE) { /* LINTED: value fits in a size_t */ fssize = stbuf.st_blksize; } else { fssize = MAXBSIZE; } if (checkvol(&spcl, 1) == FAIL) { (void) fprintf(stderr, gettext("This is not volume 1 of the dump\n")); done(1); } if (readhdr(&spcl) == FAIL) panic(gettext("no header after volume mark!\n")); findinode(&spcl); /* sets curfile, resyncs the tape if need be */ if (checktype(&spcl, TS_CLRI) == FAIL) { (void) fprintf(stderr, gettext("Cannot find file removal list\n")); done(1); } maxino = (unsigned)((spcl.c_count * tp_bsize * NBBY) + 1); dprintf(stdout, "maxino = %lu\n", maxino); /* * Allocate space for at least MAXINO inodes to allow us * to restore partial dump tapes written before dump was * fixed to write out the entire inode map. */ if (maxino > ULONG_MAX) { (void) fprintf(stderr, gettext("file system too large\n")); done(1); } /* LINTED maxino size-checked above */ mapsize = (size_t)d_howmany(maxino > MAXINO ? maxino : MAXINO, NBBY); beginmap = map = calloc((size_t)1, mapsize); if (map == (char *)NIL) { (void) fprintf(stderr, gettext("no memory for file removal list\n")); done(1); } endmap = map + mapsize; clrimap = map; curfile.action = USING; continuemap = 1; getfile(xtrmap, xtrmapskip); if (MAXINO > maxino) maxino = MAXINO; if (checktype(&spcl, TS_BITS) == FAIL) { /* if we have TS_CLRI then no TS_BITS then a TS_END */ /* then we have an empty dump file */ if (gethead(&spcl) == GOOD && checktype(&spcl, TS_END) == GOOD) { if ((command == 'r') || (command == 'R')) { initsymtable(syment); dumpsymtable(syment, volno); } done(0); } /* otherwise we have an error */ (void) fprintf(stderr, gettext("Cannot find file dump list\n")); done(1); } /* LINTED maxino size-checked above */ mapsize = (size_t)d_howmany(maxino, NBBY); beginmap = map = calloc((size_t)1, mapsize); if (map == (char *)NULL) { (void) fprintf(stderr, gettext("no memory for file dump list\n")); done(1); } endmap = map + mapsize; dumpmap = map; curfile.action = USING; continuemap = 1; getfile(xtrmap, xtrmapskip); continuemap = 0; } /* * Initialize fssize variable for 'R' command to work. */ void setupR(void) { struct stat stbuf; if (stat(".", &stbuf) < 0) { perror(gettext("cannot stat .")); done(1); } if (stbuf.st_blksize >= tp_bsize && stbuf.st_blksize <= MAXBSIZE) { /* LINTED: value fits in a size_t */ fssize = stbuf.st_blksize; } else { fssize = MAXBSIZE; } } /* * 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. * * first_time is used with archive files and can have 1 of 3 states: * FT_STATE_1 Tape has not been read yet * FT_STATE_2 Tape has been read but not positioned past directory * information * FT_STATE_3 Tape has been read and is reading file information */ #define FT_STATE_1 1 #define FT_STATE_2 2 #define FT_STATE_3 3 void getvol(int nextvol) { int newvol; long savecnt, savetapea, wantnext; long i; union u_spcl tmpspcl; #define tmpbuf tmpspcl.s_spcl char buf[TP_BSIZE_MAX]; static int first_time = FT_STATE_1; if (tbf == NULL) { (void) fprintf(stderr, gettext( "Internal consistency failure in getvol: tbf is NULL\n")); done(1); } if (nextvol == 1) { for (i = 0; i < MAXTAPES; i++) tapesread[i] = 0; gettingfile = 0; } if (pipein) { if (nextvol != 1) panic(gettext("changing volumes on pipe input\n")); if (volno == 1) return; goto gethdr; } savecnt = blksread; /* ignore volume verification tape i/o */ savetapea = tapea; again: if (pipein) done(1); /* pipes do not get a second chance */ if (command == 'R' || command == 'r' || curfile.action != SKIP) { wantnext = 1; newvol = nextvol; } else { wantnext = 0; newvol = 0; } if (autoload) { if ((volno == 1) && (nextvol == 1)) { tapesread[volno-1]++; return; } if (autoload_tape()) { wantnext = 1; newvol = nextvol; goto gethdr; } } while (newvol <= 0) { int n = 0; for (i = 0; i < MAXTAPES; i++) if (tapesread[i]) n++; if (n == 0) { (void) fprintf(stderr, "%s", gettext( "You have not read any volumes yet.\n\ Unless you know which volume your file(s) are on you should start\n\ with the last volume and work towards the first.\n")); } else { (void) fprintf(stderr, gettext("You have read volumes")); (void) strcpy(tbf, ": "); for (i = 0; i < MAXTAPES; i++) if (tapesread[i]) { (void) fprintf(stderr, "%s%ld", tbf, i+1); (void) strcpy(tbf, ", "); } (void) fprintf(stderr, "\n"); } do { (void) fprintf(stderr, gettext("Specify next volume #: ")); (void) fflush(stderr); /* LINTED tbfsize is limited to a few MB */ (void) fgets(tbf, (int)tbfsize, terminal); } while (!feof(terminal) && tbf[0] == '\n'); if (feof(terminal)) done(1); newvol = atoi(tbf); if (newvol <= 0) { (void) fprintf(stderr, gettext( "Volume numbers are positive numerics\n")); } if (newvol > MAXTAPES) { (void) fprintf(stderr, gettext( "This program can only deal with %d volumes\n"), MAXTAPES); newvol = 0; } } if (newvol == volno) { tapesread[volno-1]++; return; } closemt(ALLOW_OFFLINE); /* * XXX: if we are switching devices, we should probably try * the device once without prompting to enable unattended * operation. */ if (host) (void) fprintf(stderr, gettext( "Mount volume %d\nthen enter volume name on host %s (default: %s) "), newvol, host, magtape); else (void) fprintf(stderr, gettext( "Mount volume %d\nthen enter volume name (default: %s) "), newvol, magtape); (void) fflush(stderr); /* LINTED tbfsize is limited to a few MB */ (void) fgets(tbf, (int)tbfsize, terminal); if (feof(terminal)) done(1); /* * XXX We don't allow rotating among tape hosts, just drives. */ if (tbf[0] != '\n') { (void) strncpy(magtape, tbf, sizeof (magtape)); magtape[sizeof (magtape) - 1] = '\0'; /* LINTED unsigned -> signed conversion ok */ i = (int)strlen(magtape); if (magtape[i - 1] == '\n') magtape[i - 1] = '\0'; } if ((host != NULL && (mt = rmtopen(magtape, O_RDONLY)) == -1) || (host == NULL && (mt = open(magtape, O_RDONLY|O_LARGEFILE)) == -1)) { int error = errno; (void) fprintf(stderr, gettext("Cannot open %s: %s\n"), magtape, strerror(error)); volno = -1; goto again; } gethdr: volno = newvol; setdumpnum(); flsht(); if (!pipein && !bflag && archivefile && (first_time == FT_STATE_1)) { first_time = FT_STATE_2; findtapeblksize(TAPE_FILE); } if (readhdr(&tmpbuf) == FAIL) { (void) fprintf(stderr, gettext("volume is not in dump format\n")); volno = 0; goto again; } if (checkvol(&tmpbuf, volno) == FAIL) { (void) fprintf(stderr, gettext("Wrong volume (%d)\n"), tmpbuf.c_volume); volno = 0; goto again; } if (((time_t)(tmpbuf.c_date) != dumpdate) || ((time_t)(tmpbuf.c_ddate) != dumptime)) { char *tmp_ct; time_t lc_date = (time_t)tmpbuf.c_date; /* * This is used to save the return value from lctime(), * since that's volatile across lctime() invocations. */ tmp_ct = strdup(lctime(&lc_date)); if (tmp_ct == (char *)0) { (void) fprintf(stderr, gettext( "Cannot allocate space for time string\n")); done(1); } (void) fprintf(stderr, gettext("Wrong dump date\n\tgot: %s\twanted: %s"), tmp_ct, lctime(&dumpdate)); volno = 0; free(tmp_ct); goto again; } tapesread[volno-1]++; blksread = savecnt; tapea = savetapea; /* * 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. */ if (tmpbuf.c_type == TS_TAPE && (tmpbuf.c_flags & DR_NEWHEADER)) { if (!wantnext) { if (archivefile && first_time == FT_STATE_2) { first_time = FT_STATE_3; } recsread = tmpbuf.c_firstrec; tapea = tmpbuf.c_tapea; dprintf(stdout, "restore skipping %d records\n", tmpbuf.c_count); for (i = tmpbuf.c_count; i > 0; i--) readtape(buf); } else if (tmpbuf.c_firstrec != 0) { savecnt = blksread; savetapea = tapea; if (archivefile && first_time == FT_STATE_2) { /* * subtract 2, 1 for archive file's TS_END * and 1 for tape's TS_TAPE */ first_time = FT_STATE_3; i = tapea - tmpbuf.c_tapea - 2; } else { i = tapea - tmpbuf.c_tapea; } if (i > 0) dprintf(stdout, gettext( "restore skipping %d duplicate records\n"), i); else if (i < 0) dprintf(stdout, gettext( "restore duplicate record botch (%d)\n"), i); while (--i >= 0) readtape(buf); blksread = savecnt; tapea = savetapea + 1; /* <= (void) gethead() below */ } } if (curfile.action == USING) { if (volno == 1) panic(gettext("active file into volume 1\n")); return; } (void) gethead(&spcl); findinode(&spcl); /* do we always restart files in full? */ if (gettingfile) { /* i.e. will we lose metadata? */ gettingfile = 0; longjmp(restart, 1); /* will this set f1 & f2? */ } } /* * handle multiple dumps per tape by skipping forward to the * appropriate one. Note we don't use absolute positioning, * as that may take a very long time. */ static void setdumpnum(void) { struct mtop tcom; int retval; if (dumpnum == 1 || volno != 1) return; if (pipein) { (void) fprintf(stderr, gettext("Cannot have multiple dumps on pipe input\n")); done(1); } tcom.mt_op = MTFSF; tcom.mt_count = dumpnum - 1; if (host) retval = rmtioctl(MTFSF, dumpnum - 1); else retval = ioctl(mt, (int)MTIOCTOP, (char *)&tcom); if (retval < 0) perror("ioctl MTFSF"); } void printdumpinfo(void) { int i; time_t date; static char *epoch = NULL; if (epoch == NULL) { epoch = strdup(gettext("the epoch\n")); if (epoch == NULL) { (void) fprintf(stderr, gettext("Out of memory\n")); return; } } date = (time_t)dumpinfo.c_date; (void) fprintf(stdout, gettext("Dump date: %s"), lctime(&date)); date = (time_t)dumpinfo.c_ddate; (void) fprintf(stdout, gettext("Dumped from: %s"), (dumpinfo.c_ddate == 0) ? epoch : lctime(&date)); if (hostinfo) { (void) fprintf(stdout, gettext("Level %d dump of %s on %.*s:%s\n"), dumpinfo.c_level, dumpinfo.c_filesys, sizeof (dumpinfo.c_host), dumpinfo.c_host, dumpinfo.c_dev); (void) fprintf(stdout, gettext("Label: %.*s\n"), sizeof (dumpinfo.c_label), dumpinfo.c_label); } if (inodeinfo) { (void) fprintf(stdout, gettext("Starting inode numbers by volume:\n")); for (i = 1; i <= dumpinfo.c_volume; i++) (void) fprintf(stdout, gettext("\tVolume %d: %6d\n"), i, dumpinfo.c_inos[i]); } } int extractfile(char *name) { static int complained_chown = 0; static int complained_lchown = 0; static int complained_chmod = 0; static int complained_utime = 0; static int complained_mknod = 0; mode_t mode; time_t timep[2]; struct entry *ep; uid_t uid; gid_t gid; char *errmsg; int result, saverr; dev_t full_dev; int dfd; char *rname; curfile.name = name; curfile.action = USING; timep[0] = (time_t)curfile.dip->di_atime; timep[1] = (time_t)curfile.dip->di_mtime; mode = curfile.dip->di_mode; uid = curfile.dip->di_suid == UID_LONG ? curfile.dip->di_uid : (uid_t)curfile.dip->di_suid; gid = curfile.dip->di_sgid == GID_LONG ? curfile.dip->di_gid : (gid_t)curfile.dip->di_sgid; resolve(name, &dfd, &rname); if (dfd != AT_FDCWD) { if (fchdir(dfd) < 0) { saverr = errno; (void) fprintf(stderr, gettext( "%s: unable to set attribute context: %s\n"), rname, strerror(saverr)); skipfile(); (void) close(dfd); return (FAIL); } } switch (mode & IFMT) { default: (void) fprintf(stderr, gettext("%s: unknown file mode 0%lo\n"), rname, (ulong_t)(mode&IFMT)); skipfile(); result = FAIL; break; case IFSOCK: vprintf(stdout, gettext("skipped socket %s\n"), rname); skipfile(); result = GOOD; break; case IFDIR: if (mflag) { ep = lookupname(name); if (ep == NIL || ep->e_flags & EXTRACT) { panic(gettext( "directory %s was not restored\n"), rname); skipfile(); result = FAIL; break; } skipfile(); result = GOOD; break; } vprintf(stdout, gettext("extract file %s\n"), rname); result = genliteraldir(rname, curfile.ino); break; case IFLNK: lnkbuf[0] = '\0'; pathlen = 0; getfile(xtrlnkfile, xtrlnkskip); if (pathlen == 0) { vprintf(stdout, gettext( "%s: zero length symbolic link (ignored)\n"), rname); result = GOOD; break; } if ((result = lf_linkit(lnkbuf, rname, SYMLINK)) != GOOD) break; /* 1254700: set uid/gid (previously missing) */ if (lchown(rname, uid, gid) < 0 && !complained_lchown) { /* Just a warning */ saverr = errno; errmsg = gettext( "Unable to restore ownership of symlink %s: %s\n"); (void) fprintf(stderr, errmsg, rname, strerror(saverr)); (void) fprintf(stderr, gettext( "Additional such failures will be ignored.\n")); complained_lchown = 1; } metaset(rname); result = GOOD; break; case IFCHR: case IFBLK: case IFIFO: vprintf(stdout, gettext("extract special file %s\n"), rname); /* put device rdev into dev_t expanded format */ /* XXX does this always do the right thing? */ /* XXX does dump do the right thing? */ if (((curfile.dip->di_ordev & 0xFFFF0000) == 0) || ((curfile.dip->di_ordev & 0xFFFF0000) == 0xFFFF0000)) { full_dev = expdev((unsigned)(curfile.dip->di_ordev)); } else { /* LINTED sign extension ok */ full_dev = (unsigned)(curfile.dip->di_ordev); } if (mknod(rname, mode, full_dev) < 0) { struct stat64 s[1]; saverr = errno; if ((stat64(rname, s)) || ((s->st_mode & S_IFMT) != (mode & S_IFMT)) || (s->st_rdev != full_dev)) { if (saverr != EPERM || !complained_mknod) { (void) fprintf(stderr, "%s: ", rname); (void) fflush(stderr); errno = saverr; perror(gettext( "cannot create special file")); if (saverr == EPERM) { (void) fprintf(stderr, gettext( "Additional such failures will be ignored.\n")); complained_mknod = 1; } } skipfile(); result = FAIL; break; } } if (chown(rname, uid, gid) < 0 && !complained_chown) { /* Just a warning */ saverr = errno; errmsg = gettext( "Unable to restore ownership of %s: %s\n"); (void) fprintf(stderr, errmsg, rname, strerror(saverr)); (void) fprintf(stderr, gettext( "Additional such failures will be ignored.\n")); complained_chown = 1; } if (chmod(rname, mode) < 0 && !complained_chmod) { saverr = errno; errmsg = gettext( "Unable to restore permissions on %s: %s\n"); (void) fprintf(stderr, errmsg, rname, strerror(saverr)); (void) fprintf(stderr, gettext( "Additional such failures will be ignored.\n")); complained_chmod = 1; } skipfile(); metaset(rname); /* skipfile() got the metadata, if any */ if (utime(rname, (struct utimbuf *)timep) < 0 && !complained_utime) { saverr = errno; errmsg = gettext( "Unable to restore times on %s: %s\n"); (void) fprintf(stderr, errmsg, rname, strerror(saverr)); (void) fprintf(stderr, gettext( "Additional such failures will be ignored.\n")); complained_utime = 1; } result = GOOD; break; case IFREG: vprintf(stdout, gettext("extract file %s\n"), rname); /* * perform a restrictive creat(2) initally, we'll * fchmod(2) according to the archive later after * we've written the blocks. */ ofile = creat64(rname, 0600); if (ofile < 0) { saverr = errno; errmsg = gettext("cannot create file"); (void) fprintf(stderr, "%s: ", rname); (void) fflush(stderr); errno = saverr; perror(errmsg); skipfile(); result = FAIL; break; } if (fchown(ofile, uid, gid) < 0 && !complained_chown) { /* Just a warning */ saverr = errno; errmsg = gettext( "Unable to restore ownership of %s: %s\n"); (void) fprintf(stderr, errmsg, rname, strerror(saverr)); (void) fprintf(stderr, gettext( "Additional such failures will be ignored.\n")); complained_chown = 1; } getfile(xtrfile, xtrskip); metaset(rname); /* * the fchmod(2) has to come after getfile() as some POSIX * implementations clear the S_ISUID and S_ISGID bits of the * file after every write(2). */ if (fchmod(ofile, mode) < 0 && !complained_chmod) { saverr = errno; errmsg = gettext( "Unable to restore permissions on %s: %s\n"); (void) fprintf(stderr, errmsg, rname, strerror(saverr)); (void) fprintf(stderr, gettext( "Additional such failures will be ignored.\n")); complained_chmod = 1; } /* * Some errors don't get reported until we close(2), so * check for them. * XXX unlink the file if an error is reported? */ if (close(ofile) < 0) { saverr = errno; errmsg = gettext("error closing file"); (void) fprintf(stderr, "%s: ", rname); (void) fflush(stderr); errno = saverr; perror(errmsg); result = FAIL; break; } if (utime(rname, (struct utimbuf *)timep) < 0 && !complained_utime) { saverr = errno; errmsg = gettext( "Unable to restore times on %s: %s\n"); (void) fprintf(stderr, errmsg, rname, strerror(saverr)); (void) fprintf(stderr, gettext( "Additional such failures will be ignored.\n")); complained_utime = 1; } result = GOOD; break; } if (dfd != AT_FDCWD) { fchdir(savepwd); (void) close(dfd); } return (result); } /* * skip over bit maps on the tape */ void skipmaps(void) { continuemap = 1; while (checktype(&spcl, TS_CLRI) == GOOD || checktype(&spcl, TS_BITS) == GOOD) skipfile(); continuemap = 0; } /* * skip over a file on the tape */ void skipfile(void) { curfile.action = SKIP; getfile(null, null); } /* * Do the file extraction, calling the supplied functions * with the blocks */ void getfile(void (*f1)(), void (*f2)()) { int i; size_t curblk = 0; offset_t size = (offset_t)spcl.c_dinode.di_size; static char clearedbuf[MAXBSIZE]; char buf[TP_BSIZE_MAX]; char *bufptr; char junk[TP_BSIZE_MAX]; assert(MAXBSIZE >= tp_bsize); metaset(NULL); /* flush old metadata */ if (checktype(&spcl, TS_END) == GOOD) { panic(gettext("ran off end of volume\n")); return; } if (ishead(&spcl) == FAIL) { panic(gettext("not at beginning of a file\n")); return; } metacheck(&spcl); /* check for metadata in header */ if (!gettingfile && setjmp(restart) != 0) { gettingfile = 0; /* paranoia; longjmp'er should do */ return; } gettingfile++; loop: if ((spcl.c_dinode.di_mode & IFMT) == IFSHAD) { f1 = xtrmeta; f2 = metaskip; } for (i = 0, bufptr = buf; i < spcl.c_count; i++) { if ((i >= TP_NINDIR) || (spcl.c_addr[i])) { readtape(bufptr); bufptr += tp_bsize; curblk++; if (curblk == (fssize / tp_bsize)) { (*f1)(buf, size > tp_bsize ? (size_t)(fssize) : /* LINTED size <= tp_bsize */ (curblk - 1) * tp_bsize + (size_t)size); curblk = 0; bufptr = buf; } } else { if (curblk > 0) { (*f1)(buf, size > tp_bsize ? (size_t)(curblk * tp_bsize) : /* LINTED size <= tp_bsize */ (curblk - 1) * tp_bsize + (size_t)size); curblk = 0; bufptr = buf; } (*f2)(clearedbuf, size > tp_bsize ? /* LINTED size <= tp_bsize */ (long)tp_bsize : (size_t)size); } if ((size -= tp_bsize) <= 0) { for (i++; i < spcl.c_count; i++) if ((i >= TP_NINDIR) || (spcl.c_addr[i])) readtape(junk); break; } } if (curblk > 0) { /* * Ok to cast size to size_t here. The above for loop reads * data into the buffer then writes it to the output file. The * call to f1 here is to write out the data that's in the * buffer that has not yet been written to the file. * This will be less than N-KB of data, since the * above loop writes to the file in filesystem- * blocksize chunks. */ /* LINTED: size fits into a size_t at this point */ (*f1)(buf, (curblk * tp_bsize) + (size_t)size); curblk = 0; bufptr = buf; } if ((readhdr(&spcl) == GOOD) && (checktype(&spcl, TS_ADDR) == GOOD)) { if (continuemap) size = (offset_t)spcl.c_count * tp_bsize; /* big bitmap */ else if ((size <= 0) && ((spcl.c_dinode.di_mode & IFMT) == IFSHAD)) { /* LINTED unsigned to signed conversion ok */ size = spcl.c_dinode.di_size; } if (size > 0) goto loop; } if (size > 0) dprintf(stdout, gettext("Missing address (header) block for %s\n"), curfile.name); findinode(&spcl); gettingfile = 0; } /* * The next routines are called during file extraction to * put the data into the right form and place. */ static void xtrfile(char *buf, size_t size) { if (write(ofile, buf, (size_t)size) == -1) { int saverr = errno; (void) fprintf(stderr, gettext("write error extracting inode %d, name %s\n"), curfile.ino, curfile.name); errno = saverr; perror("write"); done(1); } } /* * Even though size is a size_t, it's seeking to a relative * offset. Thus, the seek could go beyond 2 GB, so lseek64 is needed. */ /*ARGSUSED*/ static void xtrskip(char *buf, size_t size) { if (lseek64(ofile, (offset_t)size, 1) == -1) { int saverr = errno; (void) fprintf(stderr, gettext("seek error extracting inode %d, name %s\n"), curfile.ino, curfile.name); errno = saverr; perror("lseek64"); done(1); } } /* these are local to the next five functions */ static char *metadata = NULL; static size_t metasize = 0; static void metacheck(struct s_spcl *head) { if (! (head->c_flags & DR_HASMETA)) return; if ((metadata = malloc(metasize = (size_t)sizeof (head->c_shadow))) == NULL) { (void) fprintf(stderr, gettext("Cannot malloc for metadata\n")); done(1); } bcopy(&(head->c_shadow), metadata, metasize); } static void xtrmeta(char *buf, size_t size) { if ((metadata == NULL) && ((spcl.c_dinode.di_mode & IFMT) != IFSHAD)) return; if ((metadata = realloc(metadata, metasize + size)) == NULL) { (void) fprintf(stderr, gettext("Cannot malloc for metadata\n")); done(1); } bcopy(buf, metadata + metasize, size); metasize += size; } /* ARGSUSED */ static void metaskip(char *buf, size_t size) { if (metadata == NULL) return; if ((metadata = realloc(metadata, metasize + size)) == NULL) { (void) fprintf(stderr, gettext("Cannot malloc for metadata\n")); done(1); } bzero(metadata + metasize, size); metasize += size; } static void metaset(char *name) { if (metadata == NULL) return; if (name != NULL) metaproc(name, metadata, metasize); (void) free(metadata); metadata = NULL; metasize = 0; } void metaget(char **data, size_t *size) { *data = metadata; *size = metasize; } static void fsd_acl(char *name, char *aclp, unsigned size) { static aclent_t *aclent = NULL; ufs_acl_t *diskacl; static int n = 0; acl_t *set_aclp; uint_t i; int saverr, j; if (aclp == NULL) { if (aclent != NULL) free(aclent); aclent = NULL; n = 0; return; } /*LINTED [aclp is malloc'd]*/ diskacl = (ufs_acl_t *)aclp; /* LINTED: result fits in an int */ j = size / sizeof (*diskacl); normacls(byteorder, diskacl, j); i = n; n += j; aclent = realloc(aclent, n * (size_t)sizeof (*aclent)); if (aclent == NULL) { (void) fprintf(stderr, gettext("Cannot malloc acl list\n")); done(1); } j = 0; while (i < n) { aclent[i].a_type = diskacl[j].acl_tag; aclent[i].a_id = diskacl[j].acl_who; aclent[i].a_perm = diskacl[j].acl_perm; ++i; ++j; } set_aclp = acl_to_aclp(ACLENT_T, aclent, n); if (set_aclp == NULL) { (void) fprintf(stderr, gettext("Cannot build acl_t\n")); done(1); } if (acl_set(name, set_aclp) == -1) { static int once = 0; /* * Treat some errors from the acl subsystem specially to * avoid being too noisy: * * ENOSYS - ACLs not supported on this file system * EPERM - not the owner or not privileged * * The following is also supported for backwards compat. * since acl(2) used to return the wrong errno: * * EINVAL - not the owner of the object */ if (errno == ENOSYS || errno == EPERM || errno == EINVAL) { if (once == 0) { saverr = errno; ++once; fprintf(stderr, gettext("setacl failed: %s\n"), strerror(saverr)); } } else { saverr = errno; fprintf(stderr, gettext("setacl on %s failed: %s\n"), name, strerror(saverr)); } } acl_free(set_aclp); } static struct fsdtypes { int type; void (*function)(); } fsdtypes[] = { {FSD_ACL, fsd_acl}, {FSD_DFACL, fsd_acl}, {0, NULL} }; void metaproc(char *name, char *mdata, size_t msize) { struct fsdtypes *fsdtype; ufs_fsd_t *fsd; char *c; /* * for the whole shadow inode, dispatch each piece * to the appropriate function. */ c = mdata; /* LINTED (c - mdata) fits into a size_t */ while ((size_t)(c - mdata) < msize) { /*LINTED [mdata is malloc'd]*/ fsd = (ufs_fsd_t *)c; assert((fsd->fsd_size % 4) == 0); /* LINTED: lint thinks pointers are signed */ c += FSD_RECSZ(fsd, fsd->fsd_size); if ((fsd->fsd_type == FSD_FREE) || ((unsigned)(fsd->fsd_size) <= sizeof (ufs_fsd_t)) || (c > (mdata + msize))) break; for (fsdtype = fsdtypes; fsdtype->type; fsdtype++) if (fsdtype->type == fsd->fsd_type) (*fsdtype->function)(name, fsd->fsd_data, (unsigned)(fsd->fsd_size) - sizeof (fsd->fsd_type) - sizeof (fsd->fsd_size)); /* ^^^ be sure to change if fsd ever changes ^^^ */ } /* reset the state of all the functions */ for (fsdtype = fsdtypes; fsdtype->type; fsdtype++) (*fsdtype->function)(NULL, NULL, 0); } static void xtrlnkfile(char *buf, size_t size) { /* LINTED: signed/unsigned mix ok */ pathlen += size; if (pathlen > MAXPATHLEN) { (void) fprintf(stderr, gettext("symbolic link name: %s->%s%s; too long %d\n"), curfile.name, lnkbuf, buf, pathlen); done(1); } buf[size] = '\0'; (void) strcat(lnkbuf, buf); /* add an extra NULL to make this a legal complex string */ lnkbuf[pathlen+1] = '\0'; } /*ARGSUSED*/ static void xtrlnkskip(char *buf, size_t size) { (void) fprintf(stderr, gettext("unallocated block in symbolic link %s\n"), curfile.name); done(1); } static void xtrmap(char *buf, size_t size) { if ((map+size) > endmap) { int64_t mapsize, increment; int64_t diff; if (spcl.c_type != TS_ADDR) { (void) fprintf(stderr, gettext("xtrmap: current record not TS_ADDR\n")); done(1); } if ((spcl.c_count < 0) || (spcl.c_count > TP_NINDIR)) { (void) fprintf(stderr, gettext("xtrmap: illegal c_count field (%d)\n"), spcl.c_count); done(1); } increment = d_howmany( ((spcl.c_count * tp_bsize * NBBY) + 1), NBBY); mapsize = endmap - beginmap + increment; if (mapsize > UINT_MAX) { (void) fprintf(stderr, gettext("xtrmap: maximum bitmap size exceeded")); done(1); } diff = map - beginmap; /* LINTED mapsize checked above */ beginmap = realloc(beginmap, (size_t)mapsize); if (beginmap == NULL) { (void) fprintf(stderr, gettext("xtrmap: realloc failed\n")); done(1); } map = beginmap + diff; endmap = beginmap + mapsize; /* LINTED endmap - map cannot exceed 32 bits */ bzero(map, (size_t)(endmap - map)); maxino = NBBY * mapsize + 1; } bcopy(buf, map, size); /* LINTED character pointers aren't signed */ map += size; } /*ARGSUSED*/ static void xtrmapskip(char *buf, size_t size) { (void) fprintf(stderr, gettext("hole in map\n")); done(1); } /*ARGSUSED*/ void null(char *buf, size_t size) { } /* * Do the tape i/o, dealing with volume changes * etc.. */ static void readtape(char *b) { int i; int rd, newvol; int cnt; struct s_spcl *sp; int32_t expected_magic; if (tbf == NULL) { (void) fprintf(stderr, gettext( "Internal consistency failure in readtape: tbf is NULL\n")); done(1); } expected_magic = ((tp_bsize == TP_BSIZE_MIN) ? NFS_MAGIC : MTB_MAGIC); top: if (bct < numtrec) { /* * check for old-dump floppy EOM -- it may appear in * the middle of a buffer. The Dflag used to be used for * this, but since it doesn't hurt to always do this we * got rid of the Dflag. */ /*LINTED [tbf = malloc()]*/ sp = &((union u_spcl *)&tbf[bct*tp_bsize])->s_spcl; if (sp->c_magic == expected_magic && sp->c_type == TS_EOM && (time_t)(sp->c_date) == dumpdate && (time_t)(sp->c_ddate) == dumptime) { for (i = 0; i < ntrec; i++) /*LINTED [tbf = malloc()]*/ ((struct s_spcl *) &tbf[i*tp_bsize])->c_magic = 0; bct = 0; rd = 0; i = 0; goto nextvol; } bcopy(&tbf[(bct++*tp_bsize)], b, (size_t)tp_bsize); blksread++; tapea++; return; } /*LINTED [assertion always true]*/ assert(sizeof (union u_spcl) == TP_BSIZE_MAX); for (i = 0; i < ntrec; i++) /*LINTED [tbf = malloc()]*/ ((struct s_spcl *)&tbf[i*sizeof (struct s_spcl)])->c_magic = 0; if (numtrec == 0) { /* LINTED unsigned/signed assignment ok */ numtrec = ntrec; } /* LINTED unsigned/signed assignment ok */ cnt = ntrec*tp_bsize; rd = 0; getmore: if (host) i = rmtread(&tbf[rd], cnt); else i = read(mt, &tbf[rd], cnt); /* * Check for mid-tape short read error. * If found, return rest of buffer. */ if (numtrec < ntrec && i != 0) { /* LINTED unsigned/signed assignment ok */ numtrec = ntrec; goto top; } /* * Handle partial block read. */ if (i > 0 && i != ntrec*tp_bsize) { if (pipein) { rd += i; cnt -= i; if (cnt > 0) goto getmore; i = rd; } else { if (i % tp_bsize != 0) panic(gettext( "partial block read: %d should be %d\n"), i, ntrec * tp_bsize); numtrec = i / tp_bsize; if (numtrec == 0) /* * it's possible to read only 512 bytes * from a QIC device... */ i = 0; } } /* * Handle read error. */ if (i < 0) { switch (curfile.action) { default: (void) fprintf(stderr, gettext( "Read error while trying to set up volume\n")); break; case UNKNOWN: (void) fprintf(stderr, gettext( "Read error while trying to resynchronize\n")); break; case USING: (void) fprintf(stderr, gettext( "Read error while restoring %s\n"), curfile.name); break; case SKIP: (void) fprintf(stderr, gettext( "Read error while skipping over inode %d\n"), curfile.ino); break; } if (!yflag && !reply(gettext("continue"))) done(1); /* LINTED: unsigned->signed conversion ok */ i = (int)(ntrec*tp_bsize); bzero(tbf, (size_t)i); if ((host != 0 && rmtseek(i, 1) < 0) || (host == 0 && (lseek64(mt, (offset_t)i, 1) == (off64_t)-1))) { perror(gettext("continuation failed")); done(1); } } /* * Handle end of tape. The Dflag used to be used, but since it doesn't * hurt to always check we got rid if it. */ /* * if the first record in the buffer just read is EOM, * change volumes. */ /*LINTED [tbf = malloc()]*/ sp = &((union u_spcl *)tbf)->s_spcl; if (i != 0 && sp->c_magic == expected_magic && sp->c_type == TS_EOM && (time_t)(sp->c_date) == dumpdate && (time_t)(sp->c_ddate) == dumptime) { i = 0; } nextvol: if (i == 0) { if (!pipein) { newvol = volno + 1; volno = 0; numtrec = 0; getvol(newvol); readtape(b); /* XXX tail recursion, not goto top? */ return; } /* XXX if panic returns, should we round rd up? */ /* XXX if we do, then we should zero the intervening space */ if (rd % tp_bsize != 0) panic(gettext("partial block read: %d should be %d\n"), rd, ntrec * tp_bsize); bcopy((char *)&endoftapemark, &tbf[rd], (size_t)tp_bsize); } bct = 0; bcopy(&tbf[(bct++*tp_bsize)], b, (size_t)tp_bsize); blksread++; recsread++; tapea++; rec_position++; } void findtapeblksize(int arfile) { int i; if (tbf == NULL) { (void) fprintf(stderr, gettext( "Internal consistency failure in findtapeblksize: " "tbf is NULL\n")); assert(tbf != NULL); done(1); } for (i = 0; i < ntrec; i++) /*LINTED [tbf = malloc()]*/ ((struct s_spcl *)&tbf[i * tp_bsize])->c_magic = 0; bct = 0; if (host && arfile == TAPE_FILE) tape_rec_size = rmtread(tbf, ntrec * tp_bsize); else tape_rec_size = read(mt, tbf, ntrec * tp_bsize); recsread++; rec_position++; if (tape_rec_size == (ssize_t)-1) { int saverr = errno; char *errmsg = gettext("Media read error"); errno = saverr; perror(errmsg); done(1); } if (tape_rec_size % tp_bsize != 0) { (void) fprintf(stderr, gettext( "Record size (%d) is not a multiple of dump block size (%d)\n"), tape_rec_size, tp_bsize); done(1); } ntrec = (int)tape_rec_size / tp_bsize; /* LINTED unsigned/signed assignment ok */ numtrec = ntrec; vprintf(stdout, gettext("Media block size is %d\n"), ntrec*2); } void flsht(void) { /* LINTED unsigned/signed assignment ok */ bct = ntrec+1; } void closemt(int mode) { /* * If mode == FORCE_OFFLINE then we're not done but * we need to change tape. So, rewind and unload current * tape before loading the new one. */ static struct mtop mtop = { MTOFFL, 0 }; if (mt < 0) return; if (offline || mode == FORCE_OFFLINE) (void) fprintf(stderr, gettext("Rewinding tape\n")); if (host) { if (offline || mode == FORCE_OFFLINE) (void) rmtioctl(MTOFFL, 1); rmtclose(); } else if (pipein) { char buffy[MAXBSIZE]; while (read(mt, buffy, sizeof (buffy)) > 0) { continue; /*LINTED [assertion always true]*/ } (void) close(mt); } else { /* * Only way to tell if this is a floppy is to issue an ioctl * but why waste one - if the eject fails, tough! */ if (offline || mode == FORCE_OFFLINE) (void) ioctl(mt, MTIOCTOP, &mtop); (void) ioctl(mt, FDEJECT, 0); (void) close(mt); } mt = -1; } static int checkvol(struct s_spcl *b, int t) { if (b->c_volume != t) return (FAIL); return (GOOD); } int readhdr(struct s_spcl *b) { if (gethead(b) == FAIL) { dprintf(stdout, gettext("readhdr fails at %ld blocks\n"), blksread); return (FAIL); } return (GOOD); } /* * read the tape into buf, then return whether or * or not it is a header block. */ int gethead(struct s_spcl *buf) { int i; union u_ospcl { char dummy[TP_BSIZE_MIN]; struct s_ospcl { int32_t c_type; int32_t c_date; int32_t c_ddate; int32_t c_volume; int32_t c_tapea; ushort_t c_inumber; int32_t c_magic; int32_t c_checksum; struct odinode { unsigned short odi_mode; ushort_t odi_nlink; ushort_t odi_uid; ushort_t odi_gid; int32_t odi_size; int32_t odi_rdev; char odi_addr[36]; int32_t odi_atime; int32_t odi_mtime; int32_t odi_ctime; } c_dinode; int32_t c_count; char c_baddr[256]; } s_ospcl; } u_ospcl; if (cvtflag) { readtape((char *)(&u_ospcl.s_ospcl)); bzero((char *)buf, (size_t)TP_BSIZE_MIN); buf->c_type = u_ospcl.s_ospcl.c_type; buf->c_date = u_ospcl.s_ospcl.c_date; buf->c_ddate = u_ospcl.s_ospcl.c_ddate; buf->c_volume = u_ospcl.s_ospcl.c_volume; buf->c_tapea = u_ospcl.s_ospcl.c_tapea; buf->c_inumber = u_ospcl.s_ospcl.c_inumber; buf->c_checksum = u_ospcl.s_ospcl.c_checksum; buf->c_magic = u_ospcl.s_ospcl.c_magic; buf->c_dinode.di_mode = u_ospcl.s_ospcl.c_dinode.odi_mode; /* LINTED: unsigned/signed combination ok */ buf->c_dinode.di_nlink = u_ospcl.s_ospcl.c_dinode.odi_nlink; buf->c_dinode.di_size = (unsigned)(u_ospcl.s_ospcl.c_dinode.odi_size); buf->c_dinode.di_uid = u_ospcl.s_ospcl.c_dinode.odi_uid; buf->c_dinode.di_gid = u_ospcl.s_ospcl.c_dinode.odi_gid; buf->c_dinode.di_suid = UID_LONG; buf->c_dinode.di_sgid = GID_LONG; buf->c_dinode.di_ordev = u_ospcl.s_ospcl.c_dinode.odi_rdev; buf->c_dinode.di_atime = u_ospcl.s_ospcl.c_dinode.odi_atime; buf->c_dinode.di_mtime = u_ospcl.s_ospcl.c_dinode.odi_mtime; buf->c_dinode.di_ctime = u_ospcl.s_ospcl.c_dinode.odi_ctime; buf->c_count = u_ospcl.s_ospcl.c_count; bcopy(u_ospcl.s_ospcl.c_baddr, buf->c_addr, sizeof (u_ospcl.s_ospcl.c_baddr)); /*CONSTANTCONDITION*/ assert(sizeof (u_ospcl.s_ospcl) < sizeof (union u_spcl)); /* we byte-swap the new spclrec, but checksum the old */ /* (see comments in normspcl()) */ if (normspcl(byteorder, buf, (int *)(&u_ospcl.s_ospcl), sizeof (u_ospcl.s_ospcl), OFS_MAGIC)) return (FAIL); buf->c_magic = ((tp_bsize == TP_BSIZE_MIN) ? NFS_MAGIC : MTB_MAGIC); } else { readtape((char *)buf); if (normspcl(byteorder, buf, (int *)buf, tp_bsize, ((tp_bsize == TP_BSIZE_MIN) ? NFS_MAGIC : MTB_MAGIC))) return (FAIL); } 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 = (offset_t)buf->c_count * tp_bsize; for (i = 0; i < buf->c_count && i < TP_NINDIR; i++) buf->c_addr[i] = 1; break; case TS_TAPE: case TS_END: if (dumpinfo.c_date == 0) { dumpinfo.c_date = spcl.c_date; dumpinfo.c_ddate = spcl.c_ddate; } if (!hostinfo && spcl.c_host[0] != '\0') { bcopy(spcl.c_label, dumpinfo.c_label, sizeof (spcl.c_label)); bcopy(spcl.c_filesys, dumpinfo.c_filesys, sizeof (spcl.c_filesys)); bcopy(spcl.c_dev, dumpinfo.c_dev, sizeof (spcl.c_dev)); bcopy(spcl.c_host, dumpinfo.c_host, sizeof (spcl.c_host)); dumpinfo.c_level = spcl.c_level; hostinfo++; if (c_label != NULL && strncmp(c_label, spcl.c_label, sizeof (spcl.c_label)) != 0) { (void) fprintf(stderr, gettext( "Incorrect tape label. Expected `%s', got `%.*s'\n"), c_label, sizeof (spcl.c_label), spcl.c_label); done(1); } } if (!inodeinfo && (spcl.c_flags & DR_INODEINFO)) { dumpinfo.c_volume = spcl.c_volume; bcopy(spcl.c_inos, dumpinfo.c_inos, sizeof (spcl.c_inos)); inodeinfo++; } buf->c_inumber = 0; break; case TS_INODE: case TS_ADDR: break; default: panic(gettext("%s: unknown inode type %d\n"), "gethead", buf->c_type); return (FAIL); } if (dflag) accthdr(buf); return (GOOD); } /* * Check that a header is where it belongs and predict the next header */ static void accthdr(struct s_spcl *header) { static ino_t previno = (ino_t)(unsigned)-1; static int prevtype; static long predict; int blks, i; if (header->c_type == TS_TAPE) { if (header->c_firstrec) (void) fprintf(stderr, gettext("Volume header begins with record %d"), header->c_firstrec); else (void) fprintf(stderr, gettext("Volume header")); (void) fprintf(stderr, "\n"); previno = (ino_t)(unsigned)-1; return; } if (previno == (ino_t)(unsigned)-1) goto newcalc; switch (prevtype) { case TS_BITS: (void) fprintf(stderr, gettext("Dump mask header")); break; case TS_CLRI: (void) fprintf(stderr, gettext("Remove mask header")); break; case TS_INODE: (void) fprintf(stderr, gettext("File header, ino %d at record %d"), previno, rec_position); break; case TS_ADDR: (void) fprintf(stderr, gettext("File continuation header, ino %d"), previno); break; case TS_END: (void) fprintf(stderr, gettext("End of media header")); break; } if (predict != blksread - 1) (void) fprintf(stderr, gettext("; predicted %ld blocks, got %ld blocks"), predict, blksread - 1); (void) fprintf(stderr, "\n"); newcalc: blks = 0; if (header->c_type != TS_END) for (i = 0; i < header->c_count; i++) if ((i >= TP_NINDIR) || (header->c_addr[i] != 0)) blks++; predict = blks; blksread = 0; prevtype = header->c_type; previno = header->c_inumber; } /* * Try to determine which volume a file resides on. */ int volnumber(ino_t inum) { int i; if (inodeinfo == 0) return (0); for (i = 1; i <= dumpinfo.c_volume; i++) if (inum < (ino_t)(unsigned)(dumpinfo.c_inos[i])) break; return (i - 1); } /* * Find an inode header. * Note that *header must be stable storage, as curfile will end up with * pointers into it. */ void findinode(struct s_spcl *header) { long skipcnt = 0; int i; char buf[TP_BSIZE_MAX]; curfile.name = gettext(""); curfile.action = UNKNOWN; curfile.dip = (struct dinode *)NULL; curfile.ino = 0; curfile.ts = 0; if (ishead(header) == FAIL) { skipcnt++; while (gethead(header) == FAIL || (time_t)(header->c_date) != dumpdate) skipcnt++; } for (;;) { if (checktype(header, TS_ADDR) == GOOD) { /* * Skip up to the beginning of the next record */ for (i = 0; i < header->c_count; i++) if ((i >= TP_NINDIR) || (header->c_addr[i])) readtape(buf); (void) gethead(header); continue; } if (checktype(header, TS_INODE) == GOOD) { curfile.dip = &header->c_dinode; if (curfile.dip->di_suid != UID_LONG) curfile.dip->di_uid = curfile.dip->di_suid; if (curfile.dip->di_sgid != GID_LONG) curfile.dip->di_gid = curfile.dip->di_sgid; curfile.ino = header->c_inumber; curfile.ts = TS_INODE; break; } if (checktype(header, TS_END) == GOOD) { curfile.ino = maxino; curfile.ts = TS_END; break; } if (checktype(header, TS_CLRI) == GOOD) { curfile.name = gettext(""); curfile.ts = TS_CLRI; break; } if (checktype(header, TS_BITS) == GOOD) { curfile.name = gettext(""); curfile.ts = TS_BITS; break; } while (gethead(header) == FAIL) skipcnt++; } if (skipcnt > 0) (void) fprintf(stderr, gettext("resync restore, skipped %d blocks\n"), skipcnt); } /* * return whether or not the buffer contains a header block */ static int ishead(struct s_spcl *buf) { if (buf->c_magic != ((tp_bsize == TP_BSIZE_MIN) ? NFS_MAGIC : MTB_MAGIC)) return (FAIL); return (GOOD); } static int checktype(struct s_spcl *b, int t) { if (b->c_type != t) return (FAIL); return (GOOD); } /* * If autoloading is enabled, attempt to do it. If we succeed, * return non-zero. */ static int autoload_tape(void) { int result = 0; /* assume failure */ int tries; int fd; if (autoload) { /* * Wait for the tape to autoload. Note that the delay * period doesn't take into account however long it takes * for the open to fail (measured at 21 seconds for an * Exabyte 8200 under 2.7 on an Ultra 2). */ /* rewind tape and offline drive before loading new tape */ closemt(FORCE_OFFLINE); (void) fprintf(stderr, gettext("Attempting to autoload next volume\n")); for (tries = 0; tries < autoload_tries; tries++) { if (host) { if (rmtopen(magtape, O_RDONLY) >= 0) { rmtclose(); result = 1; break; } } else { if ((fd = open(magtape, O_RDONLY|O_LARGEFILE, 0600)) >= 0) { (void) close(fd); result = 1; break; } } (void) sleep(autoload_period); } if (result == 0) { /* Assume caller will deal with manual change-over */ (void) fprintf(stderr, gettext("Autoload timed out\n")); } else { if ((host != NULL && (mt = rmtopen(magtape, O_RDONLY)) == -1) || (host == NULL && (mt = open(magtape, O_RDONLY|O_LARGEFILE)) == -1)) { (void) fprintf(stderr, gettext( "Autoload could not re-open tape\n")); result = 0; } else { (void) fprintf(stderr, gettext( "Tape loaded\n")); } } } return (result); }