1/*
2 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
3 * Use is subject to license terms.
4 */
5
6/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
7/*	  All Rights Reserved	*/
8
9/*
10 * Copyright (c) 1983 Regents of the University of California.
11 * All rights reserved.  The Berkeley software License Agreement
12 * specifies the terms and conditions for redistribution.
13 */
14
15/*
16 *	Modified to recursively extract all files within a subtree
17 *	(supressed by the h option) and recreate the heirarchical
18 *	structure of that subtree and move extracted files to their
19 *	proper homes (supressed by the m option).
20 *	Includes the s (skip files) option for use with multiple
21 *	dumps on a single tape.
22 *	8/29/80		by Mike Litzkow
23 *
24 *	Modified to work on the new file system and to recover from
25 *	tape read errors.
26 *	1/19/82		by Kirk McKusick
27 *
28 *	Full incremental restore running entirely in user code and
29 *	interactive tape browser.
30 *	1/19/83		by Kirk McKusick
31 */
32
33#include "restore.h"
34#include <signal.h>
35#include <byteorder.h>
36#include <priv_utils.h>
37
38#include <euc.h>
39#include <getwidth.h>
40#include <sys/mtio.h>
41eucwidth_t wp;
42
43int	bflag = 0, dflag = 0, vflag = 0, yflag = 0;
44int	hflag = 1, mflag = 1, paginating = 0, offline = 0, autoload = 0;
45int	autoload_tries;
46int	autoload_period;
47int	cvtflag = 0;		/* Converting from old dump format */
48char	command = '\0';
49long	dumpnum = 1;
50int	volno = 0;
51uint_t	ntrec;			/* blocking factor, in KB */
52uint_t	saved_ntrec;		/* saved blocking factor, in KB */
53ssize_t	tape_rec_size = 0;	/* tape record size (ntrec * tp_bsize) */
54size_t	newtapebuf_size = 0;	/* save size of last call to newtapebuf */
55char	*progname;
56char	*dumpmap;
57char	*clrimap;
58char	*c_label;		/* if non-NULL, we must see this tape label */
59ino_t	maxino;
60time_t	dumptime;
61time_t	dumpdate;
62FILE 	*terminal;
63char	*tmpdir;
64char	*pager_catenated;
65char	**pager_vector;
66int	pager_len;
67int	inattrspace = 0;
68int	savepwd;
69int32_t	tp_bsize = TP_BSIZE_MIN;
70struct byteorder_ctx *byteorder;
71
72static void set_tmpdir(void);
73
74int
75main(int argc, char *argv[])
76{
77	static struct arglist alist = { 0, 0, 0, 0, 0 };
78	int  count;
79	char *cp;
80	char *fname;
81	ino_t ino;
82	char *inputdev;
83	char *archivefile = 0;
84	char *symtbl = RESTORESYMTABLE;
85	char name[MAXPATHLEN];
86	int  fflag = 0;
87	struct sigaction sa, osa;
88	int multiplier;
89	char units;
90
91	if ((progname = strrchr(argv[0], '/')) != NULL)
92		progname++;
93	else
94		progname = argv[0];
95
96	if (strcmp("hsmrestore", progname) == 0) {
97		(void) fprintf(stderr,
98		    gettext("hsmrestore emulation is no longer supported.\n"));
99		done(1);
100	}
101
102	/*
103	 * Convert the effective uid of 0 to the single privilege
104	 * we really want.  When running with all privileges, this
105	 * is a no-op.  When the set-uid bit is stripped restore
106	 * still works for local tapes.  Fail when trying to access
107	 * a remote tape in that case and not immediately.
108	 */
109	(void) __init_suid_priv(0, PRIV_NET_PRIVADDR, (char *)NULL);
110
111	inputdev = DEFTAPE;
112
113	/*
114	 * This doesn't work because ufsrestore is statically linked:
115	 * (void) setlocale(LC_ALL, "");
116	 * The problem seems to be with LC_COLLATE, so set all the
117	 * others explicitly.  Bug 1157128 was created against the I18N
118	 * library.  When that bug is fixed this should go back to the way
119	 * it was.
120	 * XXX 1157128 was closed as a dup of 1099747.  That bug was fixed by
121	 * disallowing setlocale() to anything other than "C".  "" is
122	 * allowed, but only if none of the envars LC_ALL, LC_COLLATE, or LANG
123	 * select anything other than "C".
124	 */
125	(void) setlocale(LC_CTYPE, "");
126	(void) setlocale(LC_NUMERIC, "");
127	(void) setlocale(LC_TIME, "");
128	(void) setlocale(LC_MONETARY, "");
129	(void) setlocale(LC_MESSAGES, "");
130#if !defined(TEXT_DOMAIN)
131#define	TEXT_DOMAIN "SYS_TEST"
132#endif
133	(void) textdomain(TEXT_DOMAIN);
134	getwidth(&wp);
135	if ((byteorder = byteorder_create()) == NULL) {
136		(void) fprintf(stderr,
137		    gettext("Cannot create byteorder context\n"));
138		done(1);
139	}
140
141	if ((savepwd = open(".", O_RDONLY)) < 0) {
142		(void) fprintf(stderr,
143		    gettext("Cannot save current directory context\n"));
144		done(1);
145	}
146
147	set_tmpdir();
148
149	autoload_period = 12;
150	autoload_tries = 12;	/* traditional default of ~2.5 minutes */
151
152	sa.sa_handler = onintr;
153	sa.sa_flags = SA_RESTART;
154	(void) sigemptyset(&sa.sa_mask);
155
156	(void) sigaction(SIGINT, &sa, &osa);
157	if (osa.sa_handler == SIG_IGN)
158		(void) sigaction(SIGINT, &osa, (struct sigaction *)0);
159
160	(void) sigaction(SIGTERM, &sa, &osa);
161	if (osa.sa_handler == SIG_IGN)
162		(void) sigaction(SIGTERM, &osa, (struct sigaction *)0);
163	if (argc < 2) {
164usage:
165		(void) fprintf(stderr, gettext("Usage:\n\
166\t%s tabcdfhsvyLloT [file file ...]\n\
167\t%s xabcdfhmsvyLloT [file file ...]\n\
168\t%s iabcdfhmsvyLloT\n\
169\t%s rabcdfsvyLloT\n\
170\t%s RabcdfsvyLloT\n\n\
171a requires an archive file name\n\
172b requires a blocking factor\n\
173f requires a dump file\n\
174s requires a file number\n\
175L requires a tape label\n\
176If set, the envar TMPDIR selects where temporary files are kept\n"),
177		    progname, progname, progname, progname, progname);
178		done(1);
179	}
180
181	argv++;			/* the bag-of-options */
182	argc -= 2;		/* count of parameters to the options  */
183	command = '\0';
184	c_label = (char *)NULL;	/* any tape's acceptable */
185	for (cp = *argv++; *cp; cp++) {
186		switch (*cp) {		/* BE CAUTIOUS OF FALLTHROUGHS */
187		case 'T':
188			if (argc < 1) {
189				(void) fprintf(stderr, gettext(
190				    "Missing autoload timeout period\n"));
191				done(1);
192			}
193
194			count = atoi(*argv);
195			if (count < 1) {
196				(void) fprintf(stderr, gettext(
197			    "Unreasonable autoload timeout period `%s'\n"),
198					*argv);
199				done(1);
200			}
201			units = *(*argv + strlen(*argv) - 1);
202			switch (units) {
203			case 's':
204				multiplier = 1;
205				break;
206			case 'h':
207				multiplier = 3600;
208				break;
209			case '0': case '1': case '2': case '3': case '4':
210			case '5': case '6': case '7': case '8': case '9':
211			case 'm':
212				multiplier = 60;
213				break;
214			default:
215				(void) fprintf(stderr, gettext(
216				    "Unknown timeout units indicator `%c'\n"),
217				    units);
218				done(1);
219			}
220			autoload_tries = 1 +
221			    ((count * multiplier) / autoload_period);
222			argv++;
223			argc--;
224			break;
225		case 'l':
226			autoload++;
227			break;
228		case 'o':
229			offline++;
230			break;
231		case '-':
232			break;
233		case 'a':
234			if (argc < 1) {
235				(void) fprintf(stderr,
236					gettext("missing archive file name\n"));
237				done(1);
238			}
239			archivefile = *argv++;
240			if (*archivefile == '\0') {
241				(void) fprintf(stderr,
242				    gettext("empty archive file name\n"));
243				done(1);
244			}
245			argc--;
246			break;
247		case 'c':
248			cvtflag++;
249			break;
250		case 'd':
251			dflag++;
252			break;
253		case 'D':
254			/*
255			 * This used to be the Dflag, but it doesn't
256			 * hurt to always check, so was removed.  This
257			 * case is here for backward compatability.
258			 */
259			break;
260		case 'h':
261			hflag = 0;
262			break;
263		case 'm':
264			mflag = 0;
265			break;
266		case 'v':
267			vflag++;
268			break;
269		case 'y':
270			yflag++;
271			break;
272		case 'f':
273			if (argc < 1) {
274				(void) fprintf(stderr,
275				    gettext("missing device specifier\n"));
276				done(1);
277			}
278			inputdev = *argv++;
279			if (*inputdev == '\0') {
280				(void) fprintf(stderr,
281				    gettext("empty device specifier\n"));
282				done(1);
283			}
284			fflag++;
285			argc--;
286			break;
287		case 'b':
288			/*
289			 * change default tape blocksize
290			 */
291			bflag++;
292			if (argc < 1) {
293				(void) fprintf(stderr,
294					gettext("missing block size\n"));
295				done(1);
296			}
297			saved_ntrec = ntrec = atoi(*argv++);
298			if (ntrec == 0 || (ntrec&1)) {
299				(void) fprintf(stderr, gettext(
300			    "Block size must be a positive, even integer\n"));
301				done(1);
302			}
303			ntrec /= (tp_bsize/DEV_BSIZE);
304			argc--;
305			break;
306		case 's':
307			/*
308			 * dumpnum (skip to) for multifile dump tapes
309			 */
310			if (argc < 1) {
311				(void) fprintf(stderr,
312					gettext("missing dump number\n"));
313				done(1);
314			}
315			dumpnum = atoi(*argv++);
316			if (dumpnum <= 0) {
317				(void) fprintf(stderr, gettext(
318			    "Dump number must be a positive integer\n"));
319				done(1);
320			}
321			argc--;
322			break;
323		case 't':
324		case 'R':
325		case 'r':
326		case 'x':
327		case 'i':
328			if (command != '\0') {
329				(void) fprintf(stderr, gettext(
330				    "%c and %c are mutually exclusive\n"),
331				    (uchar_t)*cp, (uchar_t)command);
332				goto usage;
333			}
334			command = *cp;
335			break;
336		case 'L':
337			if (argc < 1 || **argv == '\0') {
338				(void) fprintf(stderr,
339				    gettext("Missing tape label name\n"));
340				done(1);
341			}
342			c_label = *argv++; /* must get tape with this label */
343			if (strlen(c_label) > (sizeof (spcl.c_label) - 1)) {
344				c_label[sizeof (spcl.c_label) - 1] = '\0';
345				(void) fprintf(stderr, gettext(
346		    "Truncating label to maximum supported length: `%s'\n"),
347				    c_label);
348			}
349			argc--;
350			break;
351
352		default:
353			(void) fprintf(stderr,
354			    gettext("Bad key character %c\n"), (uchar_t)*cp);
355			goto usage;
356		}
357	}
358	if (command == '\0') {
359		(void) fprintf(stderr,
360		    gettext("must specify i, t, r, R, or x\n"));
361		goto usage;
362	}
363	setinput(inputdev, archivefile);
364	if (argc == 0) {	/* re-use last argv slot for default */
365		argc = 1;
366		*--argv = mflag ? "." : "2";
367	}
368	switch (command) {
369
370	/*
371	 * Interactive mode.
372	 */
373	case 'i':
374		setup();
375		extractdirs(1);
376		initsymtable((char *)0);
377		initpagercmd();
378		runcmdshell();
379		done(0);
380		/* NOTREACHED */
381	/*
382	 * Incremental restoration of a file system.
383	 */
384	case 'r':
385		setup();
386		if (dumptime > 0) {
387			/*
388			 * This is an incremental dump tape.
389			 */
390			vprintf(stdout, gettext("Begin incremental restore\n"));
391			initsymtable(symtbl);
392			extractdirs(1);
393			removeoldleaves();
394			vprintf(stdout, gettext("Calculate node updates.\n"));
395			strcpy(name, ".");
396			name[2] = '\0';
397			treescan(name, ROOTINO, nodeupdates);
398			attrscan(1, nodeupdates);
399			findunreflinks();
400			removeoldnodes();
401		} else {
402			/*
403			 * This is a level zero dump tape.
404			 */
405			vprintf(stdout, gettext("Begin level 0 restore\n"));
406			initsymtable((char *)0);
407			extractdirs(1);
408			vprintf(stdout,
409			    gettext("Calculate extraction list.\n"));
410			strcpy(name, ".");
411			name[2] = '\0';
412			treescan(name, ROOTINO, nodeupdates);
413			attrscan(1, nodeupdates);
414		}
415		createleaves(symtbl);
416		createlinks();
417		setdirmodes();
418		checkrestore();
419		if (dflag) {
420			vprintf(stdout,
421			    gettext("Verify the directory structure\n"));
422			strcpy(name, ".");
423			name[2] = '\0';
424			treescan(name, ROOTINO, verifyfile);
425		}
426		dumpsymtable(symtbl, (long)1);
427		done(0);
428		/* NOTREACHED */
429	/*
430	 * Resume an incremental file system restoration.
431	 */
432	case 'R':
433		setupR();
434		initsymtable(symtbl);
435		skipmaps();
436		skipdirs();
437		createleaves(symtbl);
438		createlinks();
439		setdirmodes();
440		checkrestore();
441		dumpsymtable(symtbl, (long)1);
442		done(0);
443		/* NOTREACHED */
444	/*
445	 * List contents of tape.
446	 */
447	case 't':
448		setup();
449		extractdirs(0);
450		initsymtable((char *)0);
451		if (vflag)
452			printdumpinfo();
453		while (argc--) {
454			canon(*argv++, name, sizeof (name));
455			name[strlen(name)+1] = '\0';
456			ino = dirlookup(name);
457			if (ino == 0)
458				continue;
459			treescan(name, ino, listfile);
460		}
461		done(0);
462		/* NOTREACHED */
463	/*
464	 * Batch extraction of tape contents.
465	 */
466	case 'x':
467		setup();
468		extractdirs(1);
469		initsymtable((char *)0);
470		while (argc--) {
471			if (mflag) {
472				canon(*argv++, name, sizeof (name));
473				if (expand(name, 0, &alist) == 0) {
474					/* no meta-characters to expand */
475					ino = dirlookup(name);
476					if (ino == 0)
477						continue;
478					pathcheck(name);
479				} else {
480					/* add each of the expansions */
481					while ((alist.last - alist.head) > 0) {
482						fname = alist.head->fname;
483						ino = dirlookup(fname);
484						if (ino != 0) {
485							pathcheck(fname);
486							treescan(fname, ino,
487							    addfile);
488						}
489						freename(fname);
490						alist.head++;
491					}
492					alist.head = (struct afile *)NULL;
493					continue; /* argc loop */
494				}
495			} else {
496				ino = (ino_t)atol(*argv);
497				if ((*(*argv++) == '-') || ino < ROOTINO) {
498					(void) fprintf(stderr, gettext(
499					    "bad inode number: %ld\n"),
500					    ino);
501					done(1);
502				}
503				name[0] = '\0';
504			}
505			treescan(name, ino, addfile);
506			attrscan(0, addfile);
507		}
508		createfiles();
509		createlinks();
510		setdirmodes();
511		if (dflag)
512			checkrestore();
513		done(0);
514		/* NOTREACHED */
515	}
516	return (0);
517}
518
519/*
520 * Determine where the user wants us to put our temporary files,
521 * and make sure we can actually do so.  Bail out if there's a problem.
522 */
523void
524set_tmpdir(void)
525{
526	int fd;
527	char name[MAXPATHLEN];
528
529	tmpdir = getenv("TMPDIR");
530	if ((tmpdir == (char *)NULL) || (*tmpdir == '\0'))
531		tmpdir = "/tmp";
532
533	if (*tmpdir != '/') {
534		(void) fprintf(stderr,
535		    gettext("TMPDIR is not an absolute path (`%s').\n"),
536		    tmpdir);
537		done(1);
538	}
539
540	/*
541	 * The actual use of tmpdir is in dirs.c, and is of the form
542	 * tmpdir + "/rst" + type (three characters) + "%ld.XXXXXX" +
543	 * a trailing NUL, where %ld is an arbitrary time_t.
544	 *
545	 * Thus, the magic 31 is strlen(itoa(MAX_TIME_T)) + "/rst" +
546	 * ".XXXXXX" + '\0'.  A time_t is 64 bits, so MAX_TIME_T is
547	 * LONG_MAX - nineteen digits.  In theory, so many things in
548	 * ufsrestore will break once time_t's value goes beyond 32
549	 * bits that it's not worth worrying about this particular
550	 * instance at this time, but we've got to start somewhere.
551	 *
552	 * Note that the use of a pid below is just for testing the
553	 * validity of the named directory.
554	 */
555	if (strlen(tmpdir) > (MAXPATHLEN - 31)) {
556		(void) fprintf(stderr, gettext("TMPDIR too long\n"));
557		done(1);
558	}
559
560	/* Guaranteed to fit by above test (sizeof(time_t) >= sizeof(pid_t)) */
561	(void) snprintf(name, sizeof (name), "%s/rstdir.%ld", tmpdir, getpid());
562
563	/*
564	 * This is effectively a stripped-down version of safe_open(),
565	 * because if the file exists, we want to fail.
566	 */
567	fd = open(name, O_CREAT|O_EXCL|O_RDWR, 0600);
568	if (fd < 0) {
569		perror(gettext("Can not create temporary file"));
570		done(1);
571	}
572
573	(void) close(fd);
574	if (unlink(name) < 0) {
575		perror(gettext("Can not delete temporary file"));
576		done(1);
577	}
578}
579