view usr/src/cmd/backup/restore/main.c @ 0:c9caec207d52 b86

Initial porting based on b86
author Koji Uno <koji.uno@sun.com>
date Tue, 02 Jun 2009 18:56:50 +0900
parents
children 1a15d5aaf794
line wrap: on
line source

/*
 * Copyright 2005 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	"@(#)main.c	1.42	05/12/06 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);

int
main(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++;
			break;
		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 */
	}
	return (0);
}

/*
 * 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);
	}
}