/*
* 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 <setjmp.h>
#include "restore.h"
#include <byteorder.h>
#include <rmt.h>
#include <sys/mtio.h>
#include <utime.h>
#include <sys/errno.h>
#include <sys/fdio.h>
#include <sys/sysmacros.h> /* for expdev */
#include <assert.h>
#include <limits.h>
#include <priv_utils.h>
#include <aclutils.h>
#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;
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(data, size)
char **data;
size_t *size;
{
*data = metadata;
*size = metasize;
}
static void
fsd_acl(name, aclp, size)
char *name, *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("<name unknown>");
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("<file removal list>");
curfile.ts = TS_CLRI;
break;
}
if (checktype(header, TS_BITS) == GOOD) {
curfile.name = gettext("<file dump list>");
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);
}