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