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 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 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