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