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.7 (Berkeley) 05/01/95"; 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/ufs/dinode.h> 23 #include <ufs/ufs/dir.h> 24 #include <ufs/ffs/fs.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) { 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 } 250 251 /* 252 * Lookup a pathname which is always assumed to start from the ROOTINO. 253 */ 254 struct direct * 255 pathsearch(pathname) 256 const char *pathname; 257 { 258 ino_t ino; 259 struct direct *dp; 260 char *path, *name, buffer[MAXPATHLEN]; 261 262 strcpy(buffer, pathname); 263 path = buffer; 264 ino = ROOTINO; 265 while (*path == '/') 266 path++; 267 dp = NULL; 268 while ((name = strsep(&path, "/")) != NULL && *name != NULL) { 269 if ((dp = searchdir(ino, name)) == NULL) 270 return (NULL); 271 ino = dp->d_ino; 272 } 273 return (dp); 274 } 275 276 /* 277 * Lookup the requested name in directory inum. 278 * Return its inode number if found, zero if it does not exist. 279 */ 280 static struct direct * 281 searchdir(inum, name) 282 ino_t inum; 283 char *name; 284 { 285 register struct direct *dp; 286 register struct inotab *itp; 287 int len; 288 289 itp = inotablookup(inum); 290 if (itp == NULL) 291 return (NULL); 292 rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); 293 len = strlen(name); 294 do { 295 dp = rst_readdir(dirp); 296 if (dp == NULL) 297 return (NULL); 298 } while (dp->d_namlen != len || strncmp(dp->d_name, name, len) != 0); 299 return (dp); 300 } 301 302 /* 303 * Put the directory entries in the directory file 304 */ 305 static void 306 putdir(buf, size) 307 char *buf; 308 long size; 309 { 310 struct direct cvtbuf; 311 register struct odirect *odp; 312 struct odirect *eodp; 313 register struct direct *dp; 314 long loc, i; 315 316 if (cvtflag) { 317 eodp = (struct odirect *)&buf[size]; 318 for (odp = (struct odirect *)buf; odp < eodp; odp++) 319 if (odp->d_ino != 0) { 320 dcvt(odp, &cvtbuf); 321 putent(&cvtbuf); 322 } 323 } else { 324 for (loc = 0; loc < size; ) { 325 dp = (struct direct *)(buf + loc); 326 if (Bcvt) 327 swabst((u_char *)"ls", (u_char *) dp); 328 if (oldinofmt && dp->d_ino != 0) { 329 # if BYTE_ORDER == BIG_ENDIAN 330 if (Bcvt) 331 dp->d_namlen = dp->d_type; 332 # else 333 if (!Bcvt) 334 dp->d_namlen = dp->d_type; 335 # endif 336 dp->d_type = DT_UNKNOWN; 337 } 338 i = DIRBLKSIZ - (loc & (DIRBLKSIZ - 1)); 339 if ((dp->d_reclen & 0x3) != 0 || 340 dp->d_reclen > i || 341 dp->d_reclen < DIRSIZ(0, dp) || 342 dp->d_namlen > NAME_MAX) { 343 vprintf(stdout, "Mangled directory: "); 344 if ((dp->d_reclen & 0x3) != 0) 345 vprintf(stdout, 346 "reclen not multiple of 4 "); 347 if (dp->d_reclen < DIRSIZ(0, dp)) 348 vprintf(stdout, 349 "reclen less than DIRSIZ (%d < %d) ", 350 dp->d_reclen, DIRSIZ(0, dp)); 351 if (dp->d_namlen > NAME_MAX) 352 vprintf(stdout, 353 "reclen name too big (%d > %d) ", 354 dp->d_namlen, NAME_MAX); 355 vprintf(stdout, "\n"); 356 loc += i; 357 continue; 358 } 359 loc += dp->d_reclen; 360 if (dp->d_ino != 0) { 361 putent(dp); 362 } 363 } 364 } 365 } 366 367 /* 368 * These variables are "local" to the following two functions. 369 */ 370 char dirbuf[DIRBLKSIZ]; 371 long dirloc = 0; 372 long prev = 0; 373 374 /* 375 * add a new directory entry to a file. 376 */ 377 static void 378 putent(dp) 379 struct direct *dp; 380 { 381 dp->d_reclen = DIRSIZ(0, dp); 382 if (dirloc + dp->d_reclen > DIRBLKSIZ) { 383 ((struct direct *)(dirbuf + prev))->d_reclen = 384 DIRBLKSIZ - prev; 385 (void) fwrite(dirbuf, 1, DIRBLKSIZ, df); 386 dirloc = 0; 387 } 388 memmove(dirbuf + dirloc, dp, (long)dp->d_reclen); 389 prev = dirloc; 390 dirloc += dp->d_reclen; 391 } 392 393 /* 394 * flush out a directory that is finished. 395 */ 396 static void 397 flushent() 398 { 399 ((struct direct *)(dirbuf + prev))->d_reclen = DIRBLKSIZ - prev; 400 (void) fwrite(dirbuf, (int)dirloc, 1, df); 401 seekpt = ftell(df); 402 dirloc = 0; 403 } 404 405 static void 406 dcvt(odp, ndp) 407 register struct odirect *odp; 408 register struct direct *ndp; 409 { 410 411 memset(ndp, 0, (long)(sizeof *ndp)); 412 ndp->d_ino = odp->d_ino; 413 ndp->d_type = DT_UNKNOWN; 414 (void) strncpy(ndp->d_name, odp->d_name, ODIRSIZ); 415 ndp->d_namlen = strlen(ndp->d_name); 416 ndp->d_reclen = DIRSIZ(0, ndp); 417 } 418 419 /* 420 * Seek to an entry in a directory. 421 * Only values returned by rst_telldir should be passed to rst_seekdir. 422 * This routine handles many directories in a single file. 423 * It takes the base of the directory in the file, plus 424 * the desired seek offset into it. 425 */ 426 static void 427 rst_seekdir(dirp, loc, base) 428 register RST_DIR *dirp; 429 long loc, base; 430 { 431 432 if (loc == rst_telldir(dirp)) 433 return; 434 loc -= base; 435 if (loc < 0) 436 fprintf(stderr, "bad seek pointer to rst_seekdir %d\n", loc); 437 (void) lseek(dirp->dd_fd, base + (loc & ~(DIRBLKSIZ - 1)), SEEK_SET); 438 dirp->dd_loc = loc & (DIRBLKSIZ - 1); 439 if (dirp->dd_loc != 0) 440 dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, DIRBLKSIZ); 441 } 442 443 /* 444 * get next entry in a directory. 445 */ 446 struct direct * 447 rst_readdir(dirp) 448 register RST_DIR *dirp; 449 { 450 register struct direct *dp; 451 452 for (;;) { 453 if (dirp->dd_loc == 0) { 454 dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, 455 DIRBLKSIZ); 456 if (dirp->dd_size <= 0) { 457 dprintf(stderr, "error reading directory\n"); 458 return (NULL); 459 } 460 } 461 if (dirp->dd_loc >= dirp->dd_size) { 462 dirp->dd_loc = 0; 463 continue; 464 } 465 dp = (struct direct *)(dirp->dd_buf + dirp->dd_loc); 466 if (dp->d_reclen == 0 || 467 dp->d_reclen > DIRBLKSIZ + 1 - dirp->dd_loc) { 468 dprintf(stderr, "corrupted directory: bad reclen %d\n", 469 dp->d_reclen); 470 return (NULL); 471 } 472 dirp->dd_loc += dp->d_reclen; 473 if (dp->d_ino == 0 && strcmp(dp->d_name, "/") == 0) 474 return (NULL); 475 if (dp->d_ino >= maxino) { 476 dprintf(stderr, "corrupted directory: bad inum %d\n", 477 dp->d_ino); 478 continue; 479 } 480 return (dp); 481 } 482 } 483 484 /* 485 * Simulate the opening of a directory 486 */ 487 RST_DIR * 488 rst_opendir(name) 489 const char *name; 490 { 491 struct inotab *itp; 492 RST_DIR *dirp; 493 ino_t ino; 494 495 if ((ino = dirlookup(name)) > 0 && 496 (itp = inotablookup(ino)) != NULL) { 497 dirp = opendirfile(dirfile); 498 rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); 499 return (dirp); 500 } 501 return (NULL); 502 } 503 504 /* 505 * In our case, there is nothing to do when closing a directory. 506 */ 507 void 508 rst_closedir(dirp) 509 RST_DIR *dirp; 510 { 511 512 (void)close(dirp->dd_fd); 513 free(dirp); 514 return; 515 } 516 517 /* 518 * Simulate finding the current offset in the directory. 519 */ 520 static long 521 rst_telldir(dirp) 522 RST_DIR *dirp; 523 { 524 return ((long)lseek(dirp->dd_fd, 525 (off_t)0, SEEK_CUR) - dirp->dd_size + dirp->dd_loc); 526 } 527 528 /* 529 * Open a directory file. 530 */ 531 static RST_DIR * 532 opendirfile(name) 533 const char *name; 534 { 535 register RST_DIR *dirp; 536 register int fd; 537 538 if ((fd = open(name, O_RDONLY)) == -1) 539 return (NULL); 540 if ((dirp = malloc(sizeof(RST_DIR))) == NULL) { 541 (void)close(fd); 542 return (NULL); 543 } 544 dirp->dd_fd = fd; 545 dirp->dd_loc = 0; 546 return (dirp); 547 } 548 549 /* 550 * Set the mode, owner, and times for all new or changed directories 551 */ 552 void 553 setdirmodes(flags) 554 int flags; 555 { 556 FILE *mf; 557 struct modeinfo node; 558 struct entry *ep; 559 char *cp; 560 561 vprintf(stdout, "Set directory mode, owner, and times.\n"); 562 (void) sprintf(modefile, "%s/rstmode%d", _PATH_TMP, dumpdate); 563 mf = fopen(modefile, "r"); 564 if (mf == NULL) { 565 fprintf(stderr, "fopen: %s\n", strerror(errno)); 566 fprintf(stderr, "cannot open mode file %s\n", modefile); 567 fprintf(stderr, "directory mode, owner, and times not set\n"); 568 return; 569 } 570 clearerr(mf); 571 for (;;) { 572 (void) fread((char *)&node, 1, sizeof(struct modeinfo), mf); 573 if (feof(mf)) 574 break; 575 ep = lookupino(node.ino); 576 if (command == 'i' || command == 'x') { 577 if (ep == NULL) 578 continue; 579 if ((flags & FORCE) == 0 && ep->e_flags & EXISTED) { 580 ep->e_flags &= ~NEW; 581 continue; 582 } 583 if (node.ino == ROOTINO && 584 reply("set owner/mode for '.'") == FAIL) 585 continue; 586 } 587 if (ep == NULL) { 588 panic("cannot find directory inode %d\n", node.ino); 589 } else { 590 cp = myname(ep); 591 (void) chown(cp, node.uid, node.gid); 592 (void) chmod(cp, node.mode); 593 (void) chflags(cp, node.flags); 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 = open(name, O_WRONLY | O_CREAT | O_TRUNC, 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; 687 node.timep[0].tv_usec = dip->di_atimensec / 1000; 688 node.timep[1].tv_sec = dip->di_mtime; 689 node.timep[1].tv_usec = dip->di_mtimensec / 1000; 690 node.mode = dip->di_mode; 691 node.flags = dip->di_flags; 692 node.uid = dip->di_uid; 693 node.gid = dip->di_gid; 694 (void) fwrite((char *)&node, 1, sizeof(struct modeinfo), mf); 695 return (itp); 696 } 697 698 /* 699 * Look up an inode in the table of directories 700 */ 701 static struct inotab * 702 inotablookup(ino) 703 ino_t ino; 704 { 705 register struct inotab *itp; 706 707 for (itp = inotab[INOHASH(ino)]; itp != NULL; itp = itp->t_next) 708 if (itp->t_ino == ino) 709 return (itp); 710 return (NULL); 711 } 712 713 /* 714 * Clean up and exit 715 */ 716 __dead void 717 done(exitcode) 718 int exitcode; 719 { 720 721 closemt(); 722 if (modefile[0] != '#') 723 (void) unlink(modefile); 724 if (dirfile[0] != '#') 725 (void) unlink(dirfile); 726 exit(exitcode); 727 } 728