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