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