main.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* Copyright 1998, 2001-2003 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
* Copyright (c) 1983 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Modified to recursively extract all files within a subtree
* (supressed by the h option) and recreate the heirarchical
* structure of that subtree and move extracted files to their
* proper homes (supressed by the m option).
* Includes the s (skip files) option for use with multiple
* dumps on a single tape.
* 8/29/80 by Mike Litzkow
*
* Modified to work on the new file system and to recover from
* tape read errors.
* 1/19/82 by Kirk McKusick
*
* Full incremental restore running entirely in user code and
* interactive tape browser.
* 1/19/83 by Kirk McKusick
*/
#include "restore.h"
#include <signal.h>
#include <byteorder.h>
#include <priv_utils.h>
#include <euc.h>
#include <getwidth.h>
#include <sys/mtio.h>
eucwidth_t wp;
int bflag = 0, dflag = 0, vflag = 0, yflag = 0;
int hflag = 1, mflag = 1, paginating = 0, offline = 0, autoload = 0;
int autoload_tries;
int autoload_period;
int cvtflag = 0; /* Converting from old dump format */
char command = '\0';
long dumpnum = 1;
int volno = 0;
uint_t ntrec; /* blocking factor, in KB */
uint_t saved_ntrec; /* saved blocking factor, in KB */
ssize_t tape_rec_size = 0; /* tape record size (ntrec * tp_bsize) */
size_t newtapebuf_size = 0; /* save size of last call to newtapebuf */
char *progname;
char *dumpmap;
char *clrimap;
char *c_label; /* if non-NULL, we must see this tape label */
ino_t maxino;
time_t dumptime;
time_t dumpdate;
FILE *terminal;
char *tmpdir;
char *pager_catenated;
char **pager_vector;
int pager_len;
int inattrspace = 0;
int savepwd;
int32_t tp_bsize = TP_BSIZE_MIN;
struct byteorder_ctx *byteorder;
static void set_tmpdir(void);
main(argc, argv)
int argc;
char *argv[];
{
static struct arglist alist = { 0, 0, 0, 0, 0 };
int count;
char *cp;
char *fname;
ino_t ino;
char *inputdev;
char *archivefile = 0;
char *symtbl = RESTORESYMTABLE;
char name[MAXPATHLEN];
int fflag = 0;
struct sigaction sa, osa;
int multiplier;
char units;
if ((progname = strrchr(argv[0], '/')) != NULL)
progname++;
else
progname = argv[0];
if (strcmp("hsmrestore", progname) == 0) {
(void) fprintf(stderr,
gettext("hsmrestore emulation is no longer supported.\n"));
done(1);
}
/*
* Convert the effective uid of 0 to the single privilege
* we really want. When running with all privileges, this
* is a no-op. When the set-uid bit is stripped restore
* still works for local tapes. Fail when trying to access
* a remote tape in that case and not immediately.
*/
(void) __init_suid_priv(0, PRIV_NET_PRIVADDR, (char *)NULL);
inputdev = DEFTAPE;
/*
* This doesn't work because ufsrestore is statically linked:
* (void) setlocale(LC_ALL, "");
* The problem seems to be with LC_COLLATE, so set all the
* others explicitly. Bug 1157128 was created against the I18N
* library. When that bug is fixed this should go back to the way
* it was.
* XXX 1157128 was closed as a dup of 1099747. That bug was fixed by
* disallowing setlocale() to anything other than "C". "" is
* allowed, but only if none of the envars LC_ALL, LC_COLLATE, or LANG
* select anything other than "C".
*/
(void) setlocale(LC_CTYPE, "");
(void) setlocale(LC_NUMERIC, "");
(void) setlocale(LC_TIME, "");
(void) setlocale(LC_MONETARY, "");
(void) setlocale(LC_MESSAGES, "");
#if !defined(TEXT_DOMAIN)
#define TEXT_DOMAIN "SYS_TEST"
#endif
(void) textdomain(TEXT_DOMAIN);
getwidth(&wp);
if ((byteorder = byteorder_create()) == NULL) {
(void) fprintf(stderr,
gettext("Cannot create byteorder context\n"));
done(1);
}
if ((savepwd = open(".", O_RDONLY)) < 0) {
(void) fprintf(stderr,
gettext("Cannot save current directory context\n"));
done(1);
}
set_tmpdir();
autoload_period = 12;
autoload_tries = 12; /* traditional default of ~2.5 minutes */
sa.sa_handler = onintr;
sa.sa_flags = SA_RESTART;
(void) sigemptyset(&sa.sa_mask);
(void) sigaction(SIGINT, &sa, &osa);
if (osa.sa_handler == SIG_IGN)
(void) sigaction(SIGINT, &osa, (struct sigaction *)0);
(void) sigaction(SIGTERM, &sa, &osa);
if (osa.sa_handler == SIG_IGN)
(void) sigaction(SIGTERM, &osa, (struct sigaction *)0);
if (argc < 2) {
usage:
(void) fprintf(stderr, gettext("Usage:\n\
\t%s tabcdfhsvyLloT [file file ...]\n\
\t%s xabcdfhmsvyLloT [file file ...]\n\
\t%s iabcdfhmsvyLloT\n\
\t%s rabcdfsvyLloT\n\
\t%s RabcdfsvyLloT\n\n\
a requires an archive file name\n\
b requires a blocking factor\n\
f requires a dump file\n\
s requires a file number\n\
L requires a tape label\n\
If set, the envar TMPDIR selects where temporary files are kept\n"),
progname, progname, progname, progname, progname);
done(1);
}
argv++; /* the bag-of-options */
argc -= 2; /* count of parameters to the options */
command = '\0';
c_label = (char *)NULL; /* any tape's acceptable */
for (cp = *argv++; *cp; cp++) {
switch (*cp) { /* BE CAUTIOUS OF FALLTHROUGHS */
case 'T':
if (argc < 1) {
(void) fprintf(stderr, gettext(
"Missing autoload timeout period\n"));
done(1);
}
count = atoi(*argv);
if (count < 1) {
(void) fprintf(stderr, gettext(
"Unreasonable autoload timeout period `%s'\n"),
*argv);
done(1);
}
units = *(*argv + strlen(*argv) - 1);
switch (units) {
case 's':
multiplier = 1;
break;
case 'h':
multiplier = 3600;
break;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
case 'm':
multiplier = 60;
break;
default:
(void) fprintf(stderr, gettext(
"Unknown timeout units indicator `%c'\n"),
units);
done(1);
}
autoload_tries = 1 +
((count * multiplier) / autoload_period);
argv++;
argc--;
break;
case 'l':
autoload++;
/*FALLTHROUGH*/
case 'o':
offline++;
break;
case '-':
break;
case 'a':
if (argc < 1) {
(void) fprintf(stderr,
gettext("missing archive file name\n"));
done(1);
}
archivefile = *argv++;
if (*archivefile == '\0') {
(void) fprintf(stderr,
gettext("empty archive file name\n"));
done(1);
}
argc--;
break;
case 'c':
cvtflag++;
break;
case 'd':
dflag++;
break;
case 'D':
/*
* This used to be the Dflag, but it doesn't
* hurt to always check, so was removed. This
* case is here for backward compatability.
*/
break;
case 'h':
hflag = 0;
break;
case 'm':
mflag = 0;
break;
case 'v':
vflag++;
break;
case 'y':
yflag++;
break;
case 'f':
if (argc < 1) {
(void) fprintf(stderr,
gettext("missing device specifier\n"));
done(1);
}
inputdev = *argv++;
if (*inputdev == '\0') {
(void) fprintf(stderr,
gettext("empty device specifier\n"));
done(1);
}
fflag++;
argc--;
break;
case 'b':
/*
* change default tape blocksize
*/
bflag++;
if (argc < 1) {
(void) fprintf(stderr,
gettext("missing block size\n"));
done(1);
}
saved_ntrec = ntrec = atoi(*argv++);
if (ntrec == 0 || (ntrec&1)) {
(void) fprintf(stderr, gettext(
"Block size must be a positive, even integer\n"));
done(1);
}
ntrec /= (tp_bsize/DEV_BSIZE);
argc--;
break;
case 's':
/*
* dumpnum (skip to) for multifile dump tapes
*/
if (argc < 1) {
(void) fprintf(stderr,
gettext("missing dump number\n"));
done(1);
}
dumpnum = atoi(*argv++);
if (dumpnum <= 0) {
(void) fprintf(stderr, gettext(
"Dump number must be a positive integer\n"));
done(1);
}
argc--;
break;
case 't':
case 'R':
case 'r':
case 'x':
case 'i':
if (command != '\0') {
(void) fprintf(stderr, gettext(
"%c and %c are mutually exclusive\n"),
(uchar_t)*cp, (uchar_t)command);
goto usage;
}
command = *cp;
break;
case 'L':
if (argc < 1 || **argv == '\0') {
(void) fprintf(stderr,
gettext("Missing tape label name\n"));
done(1);
}
c_label = *argv++; /* must get tape with this label */
if (strlen(c_label) > (sizeof (spcl.c_label) - 1)) {
c_label[sizeof (spcl.c_label) - 1] = '\0';
(void) fprintf(stderr, gettext(
"Truncating label to maximum supported length: `%s'\n"),
c_label);
}
argc--;
break;
default:
(void) fprintf(stderr,
gettext("Bad key character %c\n"), (uchar_t)*cp);
goto usage;
}
}
if (command == '\0') {
(void) fprintf(stderr,
gettext("must specify i, t, r, R, or x\n"));
goto usage;
}
setinput(inputdev, archivefile);
if (argc == 0) { /* re-use last argv slot for default */
argc = 1;
*--argv = mflag ? "." : "2";
}
switch (command) {
/*
* Interactive mode.
*/
case 'i':
setup();
extractdirs(1);
initsymtable((char *)0);
initpagercmd();
runcmdshell();
done(0);
/* NOTREACHED */
/*
* Incremental restoration of a file system.
*/
case 'r':
setup();
if (dumptime > 0) {
/*
* This is an incremental dump tape.
*/
vprintf(stdout, gettext("Begin incremental restore\n"));
initsymtable(symtbl);
extractdirs(1);
removeoldleaves();
vprintf(stdout, gettext("Calculate node updates.\n"));
strcpy(name, ".");
name[2] = '\0';
treescan(name, ROOTINO, nodeupdates);
attrscan(1, nodeupdates);
findunreflinks();
removeoldnodes();
} else {
/*
* This is a level zero dump tape.
*/
vprintf(stdout, gettext("Begin level 0 restore\n"));
initsymtable((char *)0);
extractdirs(1);
vprintf(stdout,
gettext("Calculate extraction list.\n"));
strcpy(name, ".");
name[2] = '\0';
treescan(name, ROOTINO, nodeupdates);
attrscan(1, nodeupdates);
}
createleaves(symtbl);
createlinks();
setdirmodes();
checkrestore();
if (dflag) {
vprintf(stdout,
gettext("Verify the directory structure\n"));
strcpy(name, ".");
name[2] = '\0';
treescan(name, ROOTINO, verifyfile);
}
dumpsymtable(symtbl, (long)1);
done(0);
/* NOTREACHED */
/*
* Resume an incremental file system restoration.
*/
case 'R':
setupR();
initsymtable(symtbl);
skipmaps();
skipdirs();
createleaves(symtbl);
createlinks();
setdirmodes();
checkrestore();
dumpsymtable(symtbl, (long)1);
done(0);
/* NOTREACHED */
/*
* List contents of tape.
*/
case 't':
setup();
extractdirs(0);
initsymtable((char *)0);
if (vflag)
printdumpinfo();
while (argc--) {
canon(*argv++, name, sizeof (name));
name[strlen(name)+1] = '\0';
ino = dirlookup(name);
if (ino == 0)
continue;
treescan(name, ino, listfile);
}
done(0);
/* NOTREACHED */
/*
* Batch extraction of tape contents.
*/
case 'x':
setup();
extractdirs(1);
initsymtable((char *)0);
while (argc--) {
if (mflag) {
canon(*argv++, name, sizeof (name));
if (expand(name, 0, &alist) == 0) {
/* no meta-characters to expand */
ino = dirlookup(name);
if (ino == 0)
continue;
pathcheck(name);
} else {
/* add each of the expansions */
while ((alist.last - alist.head) > 0) {
fname = alist.head->fname;
ino = dirlookup(fname);
if (ino != 0) {
pathcheck(fname);
treescan(fname, ino,
addfile);
}
freename(fname);
alist.head++;
}
alist.head = (struct afile *)NULL;
continue; /* argc loop */
}
} else {
ino = (ino_t)atol(*argv);
if ((*(*argv++) == '-') || ino < ROOTINO) {
(void) fprintf(stderr, gettext(
"bad inode number: %ld\n"),
ino);
done(1);
}
name[0] = '\0';
}
treescan(name, ino, addfile);
attrscan(0, addfile);
}
createfiles();
createlinks();
setdirmodes();
if (dflag)
checkrestore();
done(0);
/* NOTREACHED */
}
#ifdef lint
return (0);
#endif
}
/*
* Determine where the user wants us to put our temporary files,
* and make sure we can actually do so. Bail out if there's a problem.
*/
void
set_tmpdir(void)
{
int fd;
char name[MAXPATHLEN];
tmpdir = getenv("TMPDIR");
if ((tmpdir == (char *)NULL) || (*tmpdir == '\0'))
tmpdir = "/tmp";
if (*tmpdir != '/') {
(void) fprintf(stderr,
gettext("TMPDIR is not an absolute path (`%s').\n"),
tmpdir);
done(1);
}
/*
* The actual use of tmpdir is in dirs.c, and is of the form
* tmpdir + "/rst" + type (three characters) + "%ld.XXXXXX" +
* a trailing NUL, where %ld is an arbitrary time_t.
*
* Thus, the magic 31 is strlen(itoa(MAX_TIME_T)) + "/rst" +
* ".XXXXXX" + '\0'. A time_t is 64 bits, so MAX_TIME_T is
* LONG_MAX - nineteen digits. In theory, so many things in
* ufsrestore will break once time_t's value goes beyond 32
* bits that it's not worth worrying about this particular
* instance at this time, but we've got to start somewhere.
*
* Note that the use of a pid below is just for testing the
* validity of the named directory.
*/
if (strlen(tmpdir) > (MAXPATHLEN - 31)) {
(void) fprintf(stderr, gettext("TMPDIR too long\n"));
done(1);
}
/* Guaranteed to fit by above test (sizeof(time_t) >= sizeof(pid_t)) */
(void) snprintf(name, sizeof (name), "%s/rstdir.%ld", tmpdir, getpid());
/*
* This is effectively a stripped-down version of safe_open(),
* because if the file exists, we want to fail.
*/
fd = open(name, O_CREAT|O_EXCL|O_RDWR, 0600);
if (fd < 0) {
perror(gettext("Can not create temporary file"));
done(1);
}
(void) close(fd);
if (unlink(name) < 0) {
perror(gettext("Can not delete temporary file"));
done(1);
}
}