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