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