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