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
main(int argc,char * argv[])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
set_tmpdir(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