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