xref: /illumos-gate/usr/src/cmd/backup/restore/main.c (revision 8a2b682e57a046b828f37bcde1776f131ef4629f)
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>
41 eucwidth_t wp;
42 
43 int	bflag = 0, dflag = 0, vflag = 0, yflag = 0;
44 int	hflag = 1, mflag = 1, paginating = 0, offline = 0, autoload = 0;
45 int	autoload_tries;
46 int	autoload_period;
47 int	cvtflag = 0;		/* Converting from old dump format */
48 char	command = '\0';
49 long	dumpnum = 1;
50 int	volno = 0;
51 uint_t	ntrec;			/* blocking factor, in KB */
52 uint_t	saved_ntrec;		/* saved blocking factor, in KB */
53 ssize_t	tape_rec_size = 0;	/* tape record size (ntrec * tp_bsize) */
54 size_t	newtapebuf_size = 0;	/* save size of last call to newtapebuf */
55 char	*progname;
56 char	*dumpmap;
57 char	*clrimap;
58 char	*c_label;		/* if non-NULL, we must see this tape label */
59 ino_t	maxino;
60 time_t	dumptime;
61 time_t	dumpdate;
62 FILE 	*terminal;
63 char	*tmpdir;
64 char	*pager_catenated;
65 char	**pager_vector;
66 int	pager_len;
67 int	inattrspace = 0;
68 int	savepwd;
69 int32_t	tp_bsize = TP_BSIZE_MIN;
70 struct byteorder_ctx *byteorder;
71 
72 static void set_tmpdir(void);
73 
74 int
75 main(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) {
164 usage:
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\
171 a requires an archive file name\n\
172 b requires a blocking factor\n\
173 f requires a dump file\n\
174 s requires a file number\n\
175 L requires a tape label\n\
176 If 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  */
523 void
524 set_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