0
|
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 "@(#)main.c 1.42 05/12/06 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 }
|