/*
* 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 <utime.h>
#include <assert.h>
#include <limits.h>
#include <priv_utils.h>
#include <aclutils.h>
static int continuemap = 0;
int pipein = 0;
static int recsread;
static int ofile;
static char *endmap;
static int pathlen;
static int autoload_tape(void);
static void setdumpnum(void);
static void xtrlnkfile(char *, size_t);
static void xtrlnkskip(char *, size_t);
static void xtrmapskip(char *, size_t);
static void readtape(char *);
/*
* Set up an input source
*/
void
{
flsht();
if (bflag == 0) {
}
/* A can't-happen */
gettext("Internal consistency check failed.\n"));
done(1);
}
char *tape;
*tape++ = '\0';
done(1);
}
done(1);
} else {
done(1);
}
/* Not remote, no need for privileges */
/*
* Since input is coming from a pipe we must establish
* our own connection to the terminal.
*/
char *msg =
done(1);
}
}
pipein++;
if (archive) {
"Cannot specify an archive file when reading from a pipe\n"));
done(1);
}
}
}
}
void
{
return;
gettext("Cannot allocate space for buffer\n"));
done(1);
}
}
/*
* Verify that the tape drive can be accessed and
* that it actually is a dump tape.
*/
void
setup(void)
{
int i, j;
if (archivefile) {
if (mt < 0) {
done(1);
}
volno = 0;
} else if (host) {
done(1);
}
volno = 1;
} else {
if (pipein)
mt = 0;
done(1);
}
volno = 1;
}
setdumpnum();
flsht();
if (archivefile)
else
if (bflag == 1) {
}
/*
* 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.
*/
bct--; /* push back this block */
blksread--;
tapea--;
cvtflag++;
gettext("Volume is not in dump format\n"));
done(1);
}
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 ((tp_bsize % TP_BSIZE_MIN != 0) ||
(tp_bsize > TP_BSIZE_MAX)) {
gettext("Volume is not in dump format\n"));
done(1);
}
bct--; /* push back this block */
blksread--;
tapea--;
/* we have to re-do this in case checksum is wrong */
gettext("Volume is not in dump format\n"));
done(1);
}
}
if (vflag)
if (pipein) {
/*
* include this since the `resync' loop in findinode
* expects to find a header with the c_date field
* filled in.
*/
/*LINTED [assertion always true]*/
j = sizeof (endoftapemark) / sizeof (int32_t);
i = 0;
do
i += *ip++;
while (--j)
;
}
done(1);
}
/* LINTED: value fits in a size_t */
} else {
}
gettext("This is not volume 1 of the dump\n"));
done(1);
}
gettext("Cannot find file removal list\n"));
done(1);
}
/*
* 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.
*/
gettext("file system too large\n"));
done(1);
}
/* LINTED maxino size-checked above */
gettext("no memory for file removal list\n"));
done(1);
}
continuemap = 1;
/* if we have TS_CLRI then no TS_BITS then a TS_END */
/* then we have an empty dump file */
}
done(0);
}
/* otherwise we have an error */
done(1);
}
/* LINTED maxino size-checked above */
gettext("no memory for file dump list\n"));
done(1);
}
continuemap = 1;
continuemap = 0;
}
/*
* Initialize fssize variable for 'R' command to work.
*/
void
setupR(void)
{
done(1);
}
/* LINTED: value fits in a size_t */
} else {
}
}
/*
* 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
*/
void
{
int newvol;
long i;
"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)
if (volno == 1)
return;
goto gethdr;
}
if (pipein)
wantnext = 1;
} else {
wantnext = 0;
newvol = 0;
}
if (autoload) {
return;
}
if (autoload_tape()) {
wantnext = 1;
goto gethdr;
}
}
while (newvol <= 0) {
int n = 0;
for (i = 0; i < MAXTAPES; i++)
if (tapesread[i])
n++;
if (n == 0) {
"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 {
gettext("You have read volumes"));
for (i = 0; i < MAXTAPES; i++)
if (tapesread[i]) {
tbf, i+1);
}
}
do {
gettext("Specify next volume #: "));
/* LINTED tbfsize is limited to a few MB */
done(1);
if (newvol <= 0) {
"Volume numbers are positive numerics\n"));
}
"This program can only deal with %d volumes\n"),
MAXTAPES);
newvol = 0;
}
}
return;
}
/*
* XXX: if we are switching devices, we should probably try
* the device once without prompting to enable unattended
* operation.
*/
if (host)
"Mount volume %d\nthen enter volume name on host %s (default: %s) "),
else
"Mount volume %d\nthen enter volume name (default: %s) "),
/* LINTED tbfsize is limited to a few MB */
done(1);
/*
* XXX We don't allow rotating among tape hosts, just drives.
*/
if (tbf[0] != '\n') {
/* LINTED unsigned -> signed conversion ok */
}
volno = -1;
goto again;
}
setdumpnum();
flsht();
}
gettext("volume is not in dump format\n"));
volno = 0;
goto again;
}
volno = 0;
goto again;
}
char *tmp_ct;
/*
* This is used to save the return value from lctime(),
* since that's volatile across lctime() invocations.
*/
if (tmp_ct == (char *)0) {
"Cannot allocate space for time string\n"));
done(1);
}
gettext("Wrong dump date\n\tgot: %s\twanted: %s"),
volno = 0;
goto again;
}
/*
* 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 (!wantnext) {
}
"restore skipping %d records\n",
} else if (tmpbuf.c_firstrec != 0) {
/*
* subtract 2, 1 for archive file's TS_END
* and 1 for tape's TS_TAPE
*/
} else {
}
if (i > 0)
"restore skipping %d duplicate records\n"),
i);
else if (i < 0)
"restore duplicate record botch (%d)\n"),
i);
while (--i >= 0)
}
}
if (volno == 1)
return;
}
if (gettingfile) { /* i.e. will we lose metadata? */
gettingfile = 0;
}
}
/*
* 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)
{
int retval;
return;
if (pipein) {
gettext("Cannot have multiple dumps on pipe input\n"));
done(1);
}
if (host)
else
if (retval < 0)
perror("ioctl MTFSF");
}
void
printdumpinfo(void)
{
int i;
return;
}
}
if (hostinfo) {
gettext("Level %d dump of %s on %.*s:%s\n"),
gettext("Label: %.*s\n"),
}
if (inodeinfo) {
gettext("Starting inode numbers by volume:\n"));
}
}
int
{
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;
char *errmsg;
int dfd;
char *rname;
"%s: unable to set attribute context: %s\n"),
skipfile();
return (FAIL);
}
}
default:
skipfile();
break;
case IFSOCK:
skipfile();
break;
case IFDIR:
if (mflag) {
"directory %s was not restored\n"),
rname);
skipfile();
break;
}
skipfile();
break;
}
break;
case IFLNK:
lnkbuf[0] = '\0';
pathlen = 0;
if (pathlen == 0) {
"%s: zero length symbolic link (ignored)\n"),
rname);
break;
}
break;
/* Just a warning */
"Unable to restore ownership of symlink %s: %s\n");
"Additional such failures will be ignored.\n"));
complained_lchown = 1;
}
break;
case IFCHR:
case IFBLK:
case IFIFO:
/* put device rdev into dev_t expanded format */
/* XXX does this always do the right thing? */
/* XXX does dump do the right thing? */
} else {
/* LINTED sign extension ok */
}
struct stat64 s[1];
"cannot create special file"));
"Additional such failures will be ignored.\n"));
complained_mknod = 1;
}
}
skipfile();
break;
}
}
/* Just a warning */
"Unable to restore ownership of %s: %s\n");
"Additional such failures will be ignored.\n"));
complained_chown = 1;
}
"Unable to restore permissions on %s: %s\n");
"Additional such failures will be ignored.\n"));
complained_chmod = 1;
}
skipfile();
!complained_utime) {
"Unable to restore times on %s: %s\n");
"Additional such failures will be ignored.\n"));
complained_utime = 1;
}
break;
case IFREG:
/*
* perform a restrictive creat(2) initally, we'll
* fchmod(2) according to the archive later after
* we've written the blocks.
*/
if (ofile < 0) {
skipfile();
break;
}
/* Just a warning */
"Unable to restore ownership of %s: %s\n");
"Additional such failures will be ignored.\n"));
complained_chown = 1;
}
/*
* 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).
*/
"Unable to restore permissions on %s: %s\n");
"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?
*/
break;
}
!complained_utime) {
"Unable to restore times on %s: %s\n");
"Additional such failures will be ignored.\n"));
complained_utime = 1;
}
break;
}
}
return (result);
}
/*
* skip over bit maps on the tape
*/
void
skipmaps(void)
{
continuemap = 1;
skipfile();
continuemap = 0;
}
/*
* skip over a file on the tape
*/
void
skipfile(void)
{
}
/*
* Do the file extraction, calling the supplied functions
* with the blocks
*/
void
{
int i;
char *bufptr;
return;
}
return;
}
gettingfile = 0; /* paranoia; longjmp'er should do */
return;
}
gettingfile++;
loop:
}
curblk++;
/* LINTED size <= tp_bsize */
curblk = 0;
}
} else {
if (curblk > 0) {
/* LINTED size <= tp_bsize */
curblk = 0;
}
/* LINTED size <= tp_bsize */
}
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 */
curblk = 0;
}
if (continuemap)
/* big bitmap */
else if ((size <= 0) &&
/* LINTED unsigned to signed conversion ok */
}
if (size > 0)
goto loop;
}
if (size > 0)
gettext("Missing address (header) block for %s\n"),
gettingfile = 0;
}
/*
* The next routines are called during file extraction to
* put the data into the right form and place.
*/
static void
{
gettext("write error extracting inode %d, name %s\n"),
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
{
gettext("seek error extracting inode %d, name %s\n"),
perror("lseek64");
done(1);
}
}
/* these are local to the next five functions */
static void
{
return;
== NULL) {
gettext("Cannot malloc for metadata\n"));
done(1);
}
}
static void
{
return;
gettext("Cannot malloc for metadata\n"));
done(1);
}
}
/* ARGSUSED */
static void
{
return;
gettext("Cannot malloc for metadata\n"));
done(1);
}
}
static void
{
return;
metasize = 0;
}
void
char **data;
{
}
static void
unsigned size;
{
static int n = 0;
uint_t i;
int saverr, j;
n = 0;
return;
}
/*LINTED [aclp is malloc'd]*/
/* LINTED: result fits in an int */
i = n;
n += j;
done(1);
}
j = 0;
while (i < n) {
++i;
++j;
}
done(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 (once == 0) {
++once;
gettext("setacl failed: %s\n"),
}
} else {
}
}
}
static struct fsdtypes {
int type;
void (*function)();
} fsdtypes[] = {
{0, NULL}
};
void
{
char *c;
/*
* for the whole shadow inode, dispatch each piece
* to the appropriate function.
*/
c = mdata;
/* LINTED (c - mdata) fits into a size_t */
/*LINTED [mdata is malloc'd]*/
/* LINTED: lint thinks pointers are signed */
break;
/* ^^^ be sure to change if fsd ever changes ^^^ */
}
/* reset the state of all the functions */
}
static void
{
if (pathlen > MAXPATHLEN) {
gettext("symbolic link name: %s->%s%s; too long %d\n"),
done(1);
}
/* add an extra NULL to make this a legal complex string */
}
/*ARGSUSED*/
static void
{
gettext("unallocated block in symbolic link %s\n"),
done(1);
}
static void
{
gettext("xtrmap: current record not TS_ADDR\n"));
done(1);
}
gettext("xtrmap: illegal c_count field (%d)\n"),
done(1);
}
gettext("xtrmap: maximum bitmap size exceeded"));
done(1);
}
/* LINTED mapsize checked above */
gettext("xtrmap: realloc failed\n"));
done(1);
}
/* LINTED endmap - map cannot exceed 32 bits */
}
/* LINTED character pointers aren't signed */
}
/*ARGSUSED*/
static void
{
done(1);
}
/*ARGSUSED*/
void
{
}
/*
* Do the tape i/o, dealing with volume changes
* etc..
*/
static void
readtape(char *b)
{
int i;
int cnt;
"Internal consistency failure in readtape: tbf is NULL\n"));
done(1);
}
top:
/*
* 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()]*/
for (i = 0; i < ntrec; i++)
/*LINTED [tbf = malloc()]*/
((struct s_spcl *)
bct = 0;
rd = 0;
i = 0;
goto nextvol;
}
blksread++;
tapea++;
return;
}
/*LINTED [assertion always true]*/
for (i = 0; i < ntrec; i++)
/*LINTED [tbf = malloc()]*/
if (numtrec == 0) {
}
rd = 0;
if (host)
else
/*
* Check for mid-tape short read error.
* If found, return rest of buffer.
*/
goto top;
}
/*
* Handle partial block read.
*/
if (pipein) {
rd += i;
cnt -= i;
if (cnt > 0)
goto getmore;
i = rd;
} else {
if (i % tp_bsize != 0)
"partial block read: %d should be %d\n"),
if (numtrec == 0)
/*
* it's possible to read only 512 bytes
* from a QIC device...
*/
i = 0;
}
}
/*
* Handle read error.
*/
if (i < 0) {
default:
"Read error while trying to set up volume\n"));
break;
case UNKNOWN:
"Read error while trying to resynchronize\n"));
break;
case USING:
"Read error while restoring %s\n"),
break;
case SKIP:
"Read error while skipping over inode %d\n"),
break;
}
done(1);
/* LINTED: unsigned->signed conversion ok */
(off64_t)-1))) {
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()]*/
i = 0;
}
if (i == 0) {
if (!pipein) {
volno = 0;
numtrec = 0;
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 */
}
bct = 0;
blksread++;
recsread++;
tapea++;
rec_position++;
}
void
{
int i;
"Internal consistency failure in findtapeblksize: "
"tbf is NULL\n"));
done(1);
}
for (i = 0; i < ntrec; i++)
/*LINTED [tbf = malloc()]*/
bct = 0;
else
recsread++;
rec_position++;
done(1);
}
if (tape_rec_size % tp_bsize != 0) {
"Record size (%d) is not a multiple of dump block size (%d)\n"),
done(1);
}
}
void
flsht(void)
{
}
void
{
/*
* 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.
*/
if (mt < 0)
return;
if (host) {
rmtclose();
} else if (pipein) {
continue;
/*LINTED [assertion always true]*/
}
} else {
/*
* Only way to tell if this is a floppy is to issue an ioctl
* but why waste one - if the eject fails, tough!
*/
}
mt = -1;
}
static int
{
if (b->c_volume != t)
return (FAIL);
return (GOOD);
}
int
{
blksread);
return (FAIL);
}
return (GOOD);
}
/*
* read the tape into buf, then return whether or
* or not it is a header block.
*/
int
{
int i;
union u_ospcl {
struct s_ospcl {
struct odinode {
unsigned short odi_mode;
} c_dinode;
} s_ospcl;
} u_ospcl;
if (cvtflag) {
/*CONSTANTCONDITION*/
/* we byte-swap the new spclrec, but checksum the old */
/* (see comments in normspcl()) */
return (FAIL);
} else {
return (FAIL);
}
case TS_CLRI:
case TS_BITS:
/*
* Have to patch up missing information in bit map headers
*/
break;
case TS_TAPE:
case TS_END:
}
hostinfo++;
!= 0) {
"Incorrect tape label. Expected `%s', got `%.*s'\n"),
done(1);
}
}
inodeinfo++;
}
break;
case TS_INODE:
case TS_ADDR:
break;
default:
return (FAIL);
}
if (dflag)
return (GOOD);
}
/*
* Check that a header is where it belongs and predict the next header
*/
static void
{
static int prevtype;
static long predict;
int blks, i;
if (header->c_firstrec)
gettext("Volume header begins with record %d"),
header->c_firstrec);
else
return;
}
goto newcalc;
switch (prevtype) {
case TS_BITS:
break;
case TS_CLRI:
break;
case TS_INODE:
gettext("File header, ino %d at record %d"),
break;
case TS_ADDR:
gettext("File continuation header, ino %d"),
previno);
break;
case TS_END:
break;
}
gettext("; predicted %ld blocks, got %ld blocks"),
blks = 0;
blks++;
blksread = 0;
}
/*
* Try to determine which volume a file resides on.
*/
int
{
int i;
if (inodeinfo == 0)
return (0);
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
{
long skipcnt = 0;
int i;
skipcnt++;
skipcnt++;
}
for (;;) {
/*
* Skip up to the beginning of the next record
*/
continue;
}
break;
}
break;
}
break;
}
break;
}
skipcnt++;
}
if (skipcnt > 0)
gettext("resync restore, skipped %d blocks\n"),
skipcnt);
}
/*
* return whether or not the buffer contains a header block
*/
static int
{
return (FAIL);
return (GOOD);
}
static int
{
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 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 */
gettext("Attempting to autoload next volume\n"));
if (host) {
rmtclose();
result = 1;
break;
}
} else {
0600)) >= 0) {
result = 1;
break;
}
}
(void) sleep(autoload_period);
}
if (result == 0) {
/* Assume caller will deal with manual change-over */
gettext("Autoload timed out\n"));
} else {
"Autoload could not re-open tape\n"));
result = 0;
} else {
"Tape loaded\n"));
}
}
}
return (result);
}