1 /* 2 * Copyright (c) 1983, 1993 3 * The Regents of the University of California. All rights reserved. 4 * (c) UNIX System Laboratories, Inc. 5 * All or some portions of this file are derived from material licensed 6 * to the University of California by American Telephone and Telegraph 7 * Co. or Unix System Laboratories, Inc. and are reproduced herein with 8 * the permission of UNIX System Laboratories, Inc. 9 * 10 * %sccs.include.redist.c% 11 */ 12 13 #ifndef lint 14 static char sccsid[] = "@(#)dirs.c 8.2 (Berkeley) 01/21/94"; 15 #endif /* not lint */ 16 17 #include <sys/param.h> 18 #include <sys/file.h> 19 #include <sys/stat.h> 20 #include <sys/time.h> 21 22 #include <ufs/ffs/fs.h> 23 #include <ufs/ufs/dinode.h> 24 #include <ufs/ufs/dir.h> 25 #include <protocols/dumprestore.h> 26 27 #include <errno.h> 28 #include <stdio.h> 29 #include <stdlib.h> 30 #include <string.h> 31 #include <unistd.h> 32 33 #include "pathnames.h" 34 #include "restore.h" 35 #include "extern.h" 36 37 /* 38 * Symbol table of directories read from tape. 39 */ 40 #define HASHSIZE 1000 41 #define INOHASH(val) (val % HASHSIZE) 42 struct inotab { 43 struct inotab *t_next; 44 ino_t t_ino; 45 long t_seekpt; 46 long t_size; 47 }; 48 static struct inotab *inotab[HASHSIZE]; 49 50 /* 51 * Information retained about directories. 52 */ 53 struct modeinfo { 54 ino_t ino; 55 struct timeval timep[2]; 56 short mode; 57 short uid; 58 short gid; 59 }; 60 61 /* 62 * Definitions for library routines operating on directories. 63 */ 64 #undef DIRBLKSIZ 65 #define DIRBLKSIZ 1024 66 struct rstdirdesc { 67 int dd_fd; 68 long dd_loc; 69 long dd_size; 70 char dd_buf[DIRBLKSIZ]; 71 }; 72 73 /* 74 * Global variables for this file. 75 */ 76 static long seekpt; 77 static FILE *df, *mf; 78 static RST_DIR *dirp; 79 static char dirfile[32] = "#"; /* No file */ 80 static char modefile[32] = "#"; /* No file */ 81 static char dot[2] = "."; /* So it can be modified */ 82 83 /* 84 * Format of old style directories. 85 */ 86 #define ODIRSIZ 14 87 struct odirect { 88 u_short d_ino; 89 char d_name[ODIRSIZ]; 90 }; 91 92 static struct inotab *allocinotab __P((ino_t, struct dinode *, long)); 93 static void dcvt __P((struct odirect *, struct direct *)); 94 static void flushent __P((void)); 95 static struct inotab *inotablookup __P((ino_t)); 96 static RST_DIR *opendirfile __P((const char *)); 97 static void putdir __P((char *, long)); 98 static void putent __P((struct direct *)); 99 static void rst_seekdir __P((RST_DIR *, long, long)); 100 static long rst_telldir __P((RST_DIR *)); 101 static struct direct *searchdir __P((ino_t, char *)); 102 103 /* 104 * Extract directory contents, building up a directory structure 105 * on disk for extraction by name. 106 * If genmode is requested, save mode, owner, and times for all 107 * directories on the tape. 108 */ 109 void 110 extractdirs(genmode) 111 int genmode; 112 { 113 register int i; 114 register struct dinode *ip; 115 struct inotab *itp; 116 struct direct nulldir; 117 118 vprintf(stdout, "Extract directories from tape\n"); 119 (void) sprintf(dirfile, "%s/rstdir%d", _PATH_TMP, dumpdate); 120 df = fopen(dirfile, "w"); 121 if (df == NULL) { 122 fprintf(stderr, 123 "restore: %s - cannot create directory temporary\n", 124 dirfile); 125 fprintf(stderr, "fopen: %s\n", strerror(errno)); 126 done(1); 127 } 128 if (genmode != 0) { 129 (void) sprintf(modefile, "%s/rstmode%d", _PATH_TMP, dumpdate); 130 mf = fopen(modefile, "w"); 131 if (mf == NULL) { 132 fprintf(stderr, 133 "restore: %s - cannot create modefile \n", 134 modefile); 135 fprintf(stderr, "fopen: %s\n", strerror(errno)); 136 done(1); 137 } 138 } 139 nulldir.d_ino = 0; 140 nulldir.d_type = DT_DIR; 141 nulldir.d_namlen = 1; 142 (void) strcpy(nulldir.d_name, "/"); 143 nulldir.d_reclen = DIRSIZ(0, &nulldir); 144 for (;;) { 145 curfile.name = "<directory file - name unknown>"; 146 curfile.action = USING; 147 ip = curfile.dip; 148 if (ip == NULL || (ip->di_mode & IFMT) != IFDIR) { 149 (void) fclose(df); 150 dirp = opendirfile(dirfile); 151 if (dirp == NULL) 152 fprintf(stderr, "opendirfile: %s\n", 153 strerror(errno)); 154 if (mf != NULL) 155 (void) fclose(mf); 156 i = dirlookup(dot); 157 if (i == 0) 158 panic("Root directory is not on tape\n"); 159 return; 160 } 161 itp = allocinotab(curfile.ino, ip, seekpt); 162 getfile(putdir, xtrnull); 163 putent(&nulldir); 164 flushent(); 165 itp->t_size = seekpt - itp->t_seekpt; 166 } 167 } 168 169 /* 170 * skip over all the directories on the tape 171 */ 172 void 173 skipdirs() 174 { 175 176 while ((curfile.dip->di_mode & IFMT) == IFDIR) { 177 skipfile(); 178 } 179 } 180 181 /* 182 * Recursively find names and inumbers of all files in subtree 183 * pname and pass them off to be processed. 184 */ 185 void 186 treescan(pname, ino, todo) 187 char *pname; 188 ino_t ino; 189 long (*todo) __P((char *, ino_t, int)); 190 { 191 register struct inotab *itp; 192 register struct direct *dp; 193 int namelen; 194 long bpt; 195 char locname[MAXPATHLEN + 1]; 196 197 itp = inotablookup(ino); 198 if (itp == NULL) { 199 /* 200 * Pname is name of a simple file or an unchanged directory. 201 */ 202 (void) (*todo)(pname, ino, LEAF); 203 return; 204 } 205 /* 206 * Pname is a dumped directory name. 207 */ 208 if ((*todo)(pname, ino, NODE) == FAIL) 209 return; 210 /* 211 * begin search through the directory 212 * skipping over "." and ".." 213 */ 214 (void) strncpy(locname, pname, MAXPATHLEN); 215 (void) strncat(locname, "/", MAXPATHLEN); 216 namelen = strlen(locname); 217 rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); 218 dp = rst_readdir(dirp); /* "." */ 219 if (dp != NULL && strcmp(dp->d_name, ".") == 0) 220 dp = rst_readdir(dirp); /* ".." */ 221 else 222 fprintf(stderr, "Warning: `.' missing from directory %s\n", 223 pname); 224 if (dp != NULL && strcmp(dp->d_name, "..") == 0) 225 dp = rst_readdir(dirp); /* first real entry */ 226 else 227 fprintf(stderr, "Warning: `..' missing from directory %s\n", 228 pname); 229 bpt = rst_telldir(dirp); 230 /* 231 * a zero inode signals end of directory 232 */ 233 while (dp != NULL && dp->d_ino != 0) { 234 locname[namelen] = '\0'; 235 if (namelen + dp->d_namlen >= MAXPATHLEN) { 236 fprintf(stderr, "%s%s: name exceeds %d char\n", 237 locname, dp->d_name, MAXPATHLEN); 238 } else { 239 (void) strncat(locname, dp->d_name, (int)dp->d_namlen); 240 treescan(locname, dp->d_ino, todo); 241 rst_seekdir(dirp, bpt, itp->t_seekpt); 242 } 243 dp = rst_readdir(dirp); 244 bpt = rst_telldir(dirp); 245 } 246 if (dp == NULL) 247 fprintf(stderr, "corrupted directory: %s.\n", locname); 248 } 249 250 /* 251 * Lookup a pathname which is always assumed to start from the ROOTINO. 252 */ 253 struct direct * 254 pathsearch(pathname) 255 const char *pathname; 256 { 257 ino_t ino; 258 struct direct *dp; 259 char *path, *name, buffer[MAXPATHLEN]; 260 261 strcpy(buffer, pathname); 262 path = buffer; 263 ino = ROOTINO; 264 while (*path == '/') 265 path++; 266 dp = NULL; 267 while ((name = strsep(&path, "/")) != NULL && *name != NULL) { 268 if ((dp = searchdir(ino, name)) == NULL) 269 return (NULL); 270 ino = dp->d_ino; 271 } 272 return (dp); 273 } 274 275 /* 276 * Lookup the requested name in directory inum. 277 * Return its inode number if found, zero if it does not exist. 278 */ 279 static struct direct * 280 searchdir(inum, name) 281 ino_t inum; 282 char *name; 283 { 284 register struct direct *dp; 285 register struct inotab *itp; 286 int len; 287 288 itp = inotablookup(inum); 289 if (itp == NULL) 290 return (NULL); 291 rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); 292 len = strlen(name); 293 do { 294 dp = rst_readdir(dirp); 295 if (dp == NULL || dp->d_ino == 0) 296 return (NULL); 297 } while (dp->d_namlen != len || strncmp(dp->d_name, name, len) != 0); 298 return (dp); 299 } 300 301 /* 302 * Put the directory entries in the directory file 303 */ 304 static void 305 putdir(buf, size) 306 char *buf; 307 long size; 308 { 309 struct direct cvtbuf; 310 register struct odirect *odp; 311 struct odirect *eodp; 312 register struct direct *dp; 313 long loc, i; 314 315 if (cvtflag) { 316 eodp = (struct odirect *)&buf[size]; 317 for (odp = (struct odirect *)buf; odp < eodp; odp++) 318 if (odp->d_ino != 0) { 319 dcvt(odp, &cvtbuf); 320 putent(&cvtbuf); 321 } 322 } else { 323 for (loc = 0; loc < size; ) { 324 dp = (struct direct *)(buf + loc); 325 if (oldinofmt) { 326 if (Bcvt) { 327 swabst((u_char *)"l2s", (u_char *) dp); 328 } 329 } else { 330 if (Bcvt) { 331 swabst((u_char *)"ls", (u_char *) dp); 332 } 333 } 334 i = DIRBLKSIZ - (loc & (DIRBLKSIZ - 1)); 335 if ((dp->d_reclen & 0x3) != 0 || 336 dp->d_reclen > i || 337 dp->d_reclen < DIRSIZ(0, dp) || 338 dp->d_namlen > NAME_MAX) { 339 vprintf(stdout, "Mangled directory: "); 340 if ((dp->d_reclen & 0x3) != 0) 341 vprintf(stdout, 342 "reclen not multiple of 4 "); 343 if (dp->d_reclen < DIRSIZ(0, dp)) 344 vprintf(stdout, 345 "reclen less than DIRSIZ (%d < %d) ", 346 dp->d_reclen, DIRSIZ(0, dp)); 347 if (dp->d_namlen > NAME_MAX) 348 vprintf(stdout, 349 "reclen name too big (%d > %d) ", 350 dp->d_namlen, NAME_MAX); 351 vprintf(stdout, "\n"); 352 loc += i; 353 continue; 354 } 355 loc += dp->d_reclen; 356 if (dp->d_ino != 0) { 357 putent(dp); 358 } 359 } 360 } 361 } 362 363 /* 364 * These variables are "local" to the following two functions. 365 */ 366 char dirbuf[DIRBLKSIZ]; 367 long dirloc = 0; 368 long prev = 0; 369 370 /* 371 * add a new directory entry to a file. 372 */ 373 static void 374 putent(dp) 375 struct direct *dp; 376 { 377 dp->d_reclen = DIRSIZ(0, dp); 378 if (dirloc + dp->d_reclen > DIRBLKSIZ) { 379 ((struct direct *)(dirbuf + prev))->d_reclen = 380 DIRBLKSIZ - prev; 381 (void) fwrite(dirbuf, 1, DIRBLKSIZ, df); 382 dirloc = 0; 383 } 384 bcopy((char *)dp, dirbuf + dirloc, (long)dp->d_reclen); 385 prev = dirloc; 386 dirloc += dp->d_reclen; 387 } 388 389 /* 390 * flush out a directory that is finished. 391 */ 392 static void 393 flushent() 394 { 395 ((struct direct *)(dirbuf + prev))->d_reclen = DIRBLKSIZ - prev; 396 (void) fwrite(dirbuf, (int)dirloc, 1, df); 397 seekpt = ftell(df); 398 dirloc = 0; 399 } 400 401 static void 402 dcvt(odp, ndp) 403 register struct odirect *odp; 404 register struct direct *ndp; 405 { 406 407 bzero((char *)ndp, (long)(sizeof *ndp)); 408 ndp->d_ino = odp->d_ino; 409 ndp->d_type = DT_UNKNOWN; 410 (void) strncpy(ndp->d_name, odp->d_name, ODIRSIZ); 411 ndp->d_namlen = strlen(ndp->d_name); 412 ndp->d_reclen = DIRSIZ(0, ndp); 413 } 414 415 /* 416 * Seek to an entry in a directory. 417 * Only values returned by rst_telldir should be passed to rst_seekdir. 418 * This routine handles many directories in a single file. 419 * It takes the base of the directory in the file, plus 420 * the desired seek offset into it. 421 */ 422 static void 423 rst_seekdir(dirp, loc, base) 424 register RST_DIR *dirp; 425 long loc, base; 426 { 427 428 if (loc == rst_telldir(dirp)) 429 return; 430 loc -= base; 431 if (loc < 0) 432 fprintf(stderr, "bad seek pointer to rst_seekdir %d\n", loc); 433 (void) lseek(dirp->dd_fd, base + (loc & ~(DIRBLKSIZ - 1)), SEEK_SET); 434 dirp->dd_loc = loc & (DIRBLKSIZ - 1); 435 if (dirp->dd_loc != 0) 436 dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, DIRBLKSIZ); 437 } 438 439 /* 440 * get next entry in a directory. 441 */ 442 struct direct * 443 rst_readdir(dirp) 444 register RST_DIR *dirp; 445 { 446 register struct direct *dp; 447 448 for (;;) { 449 if (dirp->dd_loc == 0) { 450 dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, 451 DIRBLKSIZ); 452 if (dirp->dd_size <= 0) { 453 dprintf(stderr, "error reading directory\n"); 454 return (NULL); 455 } 456 } 457 if (dirp->dd_loc >= dirp->dd_size) { 458 dirp->dd_loc = 0; 459 continue; 460 } 461 dp = (struct direct *)(dirp->dd_buf + dirp->dd_loc); 462 if (dp->d_reclen == 0 || 463 dp->d_reclen > DIRBLKSIZ + 1 - dirp->dd_loc) { 464 dprintf(stderr, "corrupted directory: bad reclen %d\n", 465 dp->d_reclen); 466 return (NULL); 467 } 468 dirp->dd_loc += dp->d_reclen; 469 if (dp->d_ino == 0 && strcmp(dp->d_name, "/") != 0) 470 continue; 471 if (dp->d_ino >= maxino) { 472 dprintf(stderr, "corrupted directory: bad inum %d\n", 473 dp->d_ino); 474 continue; 475 } 476 return (dp); 477 } 478 } 479 480 /* 481 * Simulate the opening of a directory 482 */ 483 RST_DIR * 484 rst_opendir(name) 485 const char *name; 486 { 487 struct inotab *itp; 488 RST_DIR *dirp; 489 ino_t ino; 490 491 if ((ino = dirlookup(name)) > 0 && 492 (itp = inotablookup(ino)) != NULL) { 493 dirp = opendirfile(dirfile); 494 rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); 495 return (dirp); 496 } 497 return (NULL); 498 } 499 500 /* 501 * In our case, there is nothing to do when closing a directory. 502 */ 503 void 504 rst_closedir(dirp) 505 RST_DIR *dirp; 506 { 507 508 (void)close(dirp->dd_fd); 509 free(dirp); 510 return; 511 } 512 513 /* 514 * Simulate finding the current offset in the directory. 515 */ 516 static long 517 rst_telldir(dirp) 518 RST_DIR *dirp; 519 { 520 return ((long)lseek(dirp->dd_fd, 521 (off_t)0, SEEK_CUR) - dirp->dd_size + dirp->dd_loc); 522 } 523 524 /* 525 * Open a directory file. 526 */ 527 static RST_DIR * 528 opendirfile(name) 529 const char *name; 530 { 531 register RST_DIR *dirp; 532 register int fd; 533 534 if ((fd = open(name, O_RDONLY)) == -1) 535 return (NULL); 536 if ((dirp = malloc(sizeof(RST_DIR))) == NULL) { 537 (void)close(fd); 538 return (NULL); 539 } 540 dirp->dd_fd = fd; 541 dirp->dd_loc = 0; 542 return (dirp); 543 } 544 545 /* 546 * Set the mode, owner, and times for all new or changed directories 547 */ 548 void 549 setdirmodes(flags) 550 int flags; 551 { 552 FILE *mf; 553 struct modeinfo node; 554 struct entry *ep; 555 char *cp; 556 557 vprintf(stdout, "Set directory mode, owner, and times.\n"); 558 (void) sprintf(modefile, "%s/rstmode%d", _PATH_TMP, dumpdate); 559 mf = fopen(modefile, "r"); 560 if (mf == NULL) { 561 fprintf(stderr, "fopen: %s\n", strerror(errno)); 562 fprintf(stderr, "cannot open mode file %s\n", modefile); 563 fprintf(stderr, "directory mode, owner, and times not set\n"); 564 return; 565 } 566 clearerr(mf); 567 for (;;) { 568 (void) fread((char *)&node, 1, sizeof(struct modeinfo), mf); 569 if (feof(mf)) 570 break; 571 ep = lookupino(node.ino); 572 if (command == 'i' || command == 'x') { 573 if (ep == NULL) 574 continue; 575 if ((flags & FORCE) == 0 && ep->e_flags & EXISTED) { 576 ep->e_flags &= ~NEW; 577 continue; 578 } 579 if (node.ino == ROOTINO && 580 reply("set owner/mode for '.'") == FAIL) 581 continue; 582 } 583 if (ep == NULL) { 584 panic("cannot find directory inode %d\n", node.ino); 585 } else { 586 cp = myname(ep); 587 (void) chown(cp, node.uid, node.gid); 588 (void) chmod(cp, node.mode); 589 utimes(cp, node.timep); 590 ep->e_flags &= ~NEW; 591 } 592 } 593 if (ferror(mf)) 594 panic("error setting directory modes\n"); 595 (void) fclose(mf); 596 } 597 598 /* 599 * Generate a literal copy of a directory. 600 */ 601 int 602 genliteraldir(name, ino) 603 char *name; 604 ino_t ino; 605 { 606 register struct inotab *itp; 607 int ofile, dp, i, size; 608 char buf[BUFSIZ]; 609 610 itp = inotablookup(ino); 611 if (itp == NULL) 612 panic("Cannot find directory inode %d named %s\n", ino, name); 613 if ((ofile = creat(name, 0666)) < 0) { 614 fprintf(stderr, "%s: ", name); 615 (void) fflush(stderr); 616 fprintf(stderr, "cannot create file: %s\n", strerror(errno)); 617 return (FAIL); 618 } 619 rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); 620 dp = dup(dirp->dd_fd); 621 for (i = itp->t_size; i > 0; i -= BUFSIZ) { 622 size = i < BUFSIZ ? i : BUFSIZ; 623 if (read(dp, buf, (int) size) == -1) { 624 fprintf(stderr, 625 "write error extracting inode %d, name %s\n", 626 curfile.ino, curfile.name); 627 fprintf(stderr, "read: %s\n", strerror(errno)); 628 done(1); 629 } 630 if (!Nflag && write(ofile, buf, (int) size) == -1) { 631 fprintf(stderr, 632 "write error extracting inode %d, name %s\n", 633 curfile.ino, curfile.name); 634 fprintf(stderr, "write: %s\n", strerror(errno)); 635 done(1); 636 } 637 } 638 (void) close(dp); 639 (void) close(ofile); 640 return (GOOD); 641 } 642 643 /* 644 * Determine the type of an inode 645 */ 646 int 647 inodetype(ino) 648 ino_t ino; 649 { 650 struct inotab *itp; 651 652 itp = inotablookup(ino); 653 if (itp == NULL) 654 return (LEAF); 655 return (NODE); 656 } 657 658 /* 659 * Allocate and initialize a directory inode entry. 660 * If requested, save its pertinent mode, owner, and time info. 661 */ 662 static struct inotab * 663 allocinotab(ino, dip, seekpt) 664 ino_t ino; 665 struct dinode *dip; 666 long seekpt; 667 { 668 register struct inotab *itp; 669 struct modeinfo node; 670 671 itp = calloc(1, sizeof(struct inotab)); 672 if (itp == NULL) 673 panic("no memory directory table\n"); 674 itp->t_next = inotab[INOHASH(ino)]; 675 inotab[INOHASH(ino)] = itp; 676 itp->t_ino = ino; 677 itp->t_seekpt = seekpt; 678 if (mf == NULL) 679 return (itp); 680 node.ino = ino; 681 node.timep[0].tv_sec = dip->di_atime.ts_sec; 682 node.timep[0].tv_usec = dip->di_atime.ts_nsec / 1000; 683 node.timep[1].tv_sec = dip->di_mtime.ts_sec; 684 node.timep[1].tv_usec = dip->di_mtime.ts_nsec / 1000; 685 node.mode = dip->di_mode; 686 node.uid = dip->di_uid; 687 node.gid = dip->di_gid; 688 (void) fwrite((char *)&node, 1, sizeof(struct modeinfo), mf); 689 return (itp); 690 } 691 692 /* 693 * Look up an inode in the table of directories 694 */ 695 static struct inotab * 696 inotablookup(ino) 697 ino_t ino; 698 { 699 register struct inotab *itp; 700 701 for (itp = inotab[INOHASH(ino)]; itp != NULL; itp = itp->t_next) 702 if (itp->t_ino == ino) 703 return (itp); 704 return (NULL); 705 } 706 707 /* 708 * Clean up and exit 709 */ 710 __dead void 711 done(exitcode) 712 int exitcode; 713 { 714 715 closemt(); 716 if (modefile[0] != '#') 717 (void) unlink(modefile); 718 if (dirfile[0] != '#') 719 (void) unlink(dirfile); 720 exit(exitcode); 721 } 722