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