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