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.12 (Berkeley) 06/04/90"; 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 #define DIRBLKSIZ DEV_BSIZE 48 struct dirdesc { 49 int dd_fd; 50 long dd_loc; 51 long dd_size; 52 char dd_buf[DIRBLKSIZ]; 53 }; 54 extern DIR *opendirfile(); 55 extern long rst_telldir(); 56 extern void rst_seekdir(); 57 58 /* 59 * Global variables for this file. 60 */ 61 static daddr_t seekpt; 62 static FILE *df, *mf; 63 static DIR *dirp; 64 static char dirfile[32] = "#"; /* No file */ 65 static char modefile[32] = "#"; /* No file */ 66 extern ino_t search(); 67 struct direct *rst_readdir(); 68 extern void rst_seekdir(); 69 70 /* 71 * Format of old style directories. 72 */ 73 #define ODIRSIZ 14 74 struct odirect { 75 u_short d_ino; 76 char d_name[ODIRSIZ]; 77 }; 78 79 /* 80 * Extract directory contents, building up a directory structure 81 * on disk for extraction by name. 82 * If genmode is requested, save mode, owner, and times for all 83 * directories on the tape. 84 */ 85 extractdirs(genmode) 86 int genmode; 87 { 88 register int i; 89 register struct dinode *ip; 90 struct inotab *itp; 91 struct direct nulldir; 92 int putdir(), null(); 93 94 vprintf(stdout, "Extract directories from tape\n"); 95 (void) sprintf(dirfile, "%s/rstdir%d", _PATH_TMP, dumpdate); 96 df = fopen(dirfile, "w"); 97 if (df == 0) { 98 fprintf(stderr, 99 "restore: %s - cannot create directory temporary\n", 100 dirfile); 101 perror("fopen"); 102 done(1); 103 } 104 if (genmode != 0) { 105 (void) sprintf(modefile, "%s/rstmode%d", _PATH_TMP, dumpdate); 106 mf = fopen(modefile, "w"); 107 if (mf == 0) { 108 fprintf(stderr, 109 "restore: %s - cannot create modefile \n", 110 modefile); 111 perror("fopen"); 112 done(1); 113 } 114 } 115 nulldir.d_ino = 0; 116 nulldir.d_namlen = 1; 117 (void) strcpy(nulldir.d_name, "/"); 118 nulldir.d_reclen = DIRSIZ(&nulldir); 119 for (;;) { 120 curfile.name = "<directory file - name unknown>"; 121 curfile.action = USING; 122 ip = curfile.dip; 123 if (ip == NULL || (ip->di_mode & IFMT) != IFDIR) { 124 (void) fclose(df); 125 dirp = opendirfile(dirfile); 126 if (dirp == NULL) 127 perror("opendirfile"); 128 if (mf != NULL) 129 (void) fclose(mf); 130 i = dirlookup("."); 131 if (i == 0) 132 panic("Root directory is not on tape\n"); 133 return; 134 } 135 itp = allocinotab(curfile.ino, ip, seekpt); 136 getfile(putdir, null); 137 putent(&nulldir); 138 flushent(); 139 itp->t_size = seekpt - itp->t_seekpt; 140 } 141 } 142 143 /* 144 * skip over all the directories on the tape 145 */ 146 skipdirs() 147 { 148 149 while ((curfile.dip->di_mode & IFMT) == IFDIR) { 150 skipfile(); 151 } 152 } 153 154 /* 155 * Recursively find names and inumbers of all files in subtree 156 * pname and pass them off to be processed. 157 */ 158 treescan(pname, ino, todo) 159 char *pname; 160 ino_t ino; 161 long (*todo)(); 162 { 163 register struct inotab *itp; 164 register struct direct *dp; 165 register struct entry *np; 166 int namelen; 167 daddr_t bpt; 168 off_t rst_telldir(); 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 == 0 || dp->d_reclen > i) { 313 loc += i; 314 continue; 315 } 316 loc += dp->d_reclen; 317 if (dp->d_ino != 0) { 318 putent(dp); 319 } 320 } 321 } 322 } 323 324 /* 325 * These variables are "local" to the following two functions. 326 */ 327 char dirbuf[DIRBLKSIZ]; 328 long dirloc = 0; 329 long prev = 0; 330 331 /* 332 * add a new directory entry to a file. 333 */ 334 putent(dp) 335 struct direct *dp; 336 { 337 dp->d_reclen = DIRSIZ(dp); 338 if (dirloc + dp->d_reclen > DIRBLKSIZ) { 339 ((struct direct *)(dirbuf + prev))->d_reclen = 340 DIRBLKSIZ - prev; 341 (void) fwrite(dirbuf, 1, DIRBLKSIZ, df); 342 dirloc = 0; 343 } 344 bcopy((char *)dp, dirbuf + dirloc, (long)dp->d_reclen); 345 prev = dirloc; 346 dirloc += dp->d_reclen; 347 } 348 349 /* 350 * flush out a directory that is finished. 351 */ 352 flushent() 353 { 354 355 ((struct direct *)(dirbuf + prev))->d_reclen = DIRBLKSIZ - prev; 356 (void) fwrite(dirbuf, (int)dirloc, 1, df); 357 seekpt = ftell(df); 358 dirloc = 0; 359 } 360 361 dcvt(odp, ndp) 362 register struct odirect *odp; 363 register struct direct *ndp; 364 { 365 366 bzero((char *)ndp, (long)(sizeof *ndp)); 367 ndp->d_ino = odp->d_ino; 368 (void) strncpy(ndp->d_name, odp->d_name, ODIRSIZ); 369 ndp->d_namlen = strlen(ndp->d_name); 370 ndp->d_reclen = DIRSIZ(ndp); 371 } 372 373 /* 374 * Seek to an entry in a directory. 375 * Only values returned by rst_telldir should be passed to rst_seekdir. 376 * This routine handles many directories in a single file. 377 * It takes the base of the directory in the file, plus 378 * the desired seek offset into it. 379 */ 380 void 381 rst_seekdir(dirp, loc, base) 382 register DIR *dirp; 383 daddr_t loc, base; 384 { 385 off_t rst_telldir(); 386 387 if (loc == rst_telldir(dirp)) 388 return; 389 loc -= base; 390 if (loc < 0) 391 fprintf(stderr, "bad seek pointer to rst_seekdir %d\n", loc); 392 (void) lseek(dirp->dd_fd, base + (loc & ~(DIRBLKSIZ - 1)), 0); 393 dirp->dd_loc = loc & (DIRBLKSIZ - 1); 394 if (dirp->dd_loc != 0) 395 dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, DIRBLKSIZ); 396 } 397 398 /* 399 * get next entry in a directory. 400 */ 401 struct direct * 402 rst_readdir(dirp) 403 register DIR *dirp; 404 { 405 register struct direct *dp; 406 407 for (;;) { 408 if (dirp->dd_loc == 0) { 409 dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, 410 DIRBLKSIZ); 411 if (dirp->dd_size <= 0) { 412 dprintf(stderr, "error reading directory\n"); 413 return NULL; 414 } 415 } 416 if (dirp->dd_loc >= dirp->dd_size) { 417 dirp->dd_loc = 0; 418 continue; 419 } 420 dp = (struct direct *)(dirp->dd_buf + dirp->dd_loc); 421 if (dp->d_reclen == 0 || 422 dp->d_reclen > DIRBLKSIZ + 1 - dirp->dd_loc) { 423 dprintf(stderr, "corrupted directory: bad reclen %d\n", 424 dp->d_reclen); 425 return NULL; 426 } 427 dirp->dd_loc += dp->d_reclen; 428 if (dp->d_ino == 0 && strcmp(dp->d_name, "/") != 0) 429 continue; 430 if (dp->d_ino >= maxino) { 431 dprintf(stderr, "corrupted directory: bad inum %d\n", 432 dp->d_ino); 433 continue; 434 } 435 return (dp); 436 } 437 } 438 439 /* 440 * Simulate the opening of a directory 441 */ 442 DIR * 443 rst_opendir(name) 444 char *name; 445 { 446 struct inotab *itp; 447 ino_t ino; 448 449 if ((ino = dirlookup(name)) > 0 && 450 (itp = inotablookup(ino)) != NULL) { 451 rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); 452 return (dirp); 453 } 454 return (0); 455 } 456 457 /* 458 * Simulate finding the current offset in the directory. 459 */ 460 off_t 461 rst_telldir(dirp) 462 DIR *dirp; 463 { 464 off_t lseek(); 465 466 return (lseek(dirp->dd_fd, 0L, 1) - dirp->dd_size + dirp->dd_loc); 467 } 468 469 /* 470 * Open a directory file. 471 */ 472 DIR * 473 opendirfile(name) 474 char *name; 475 { 476 register DIR *dirp; 477 register int fd; 478 479 if ((fd = open(name, 0)) == -1) 480 return NULL; 481 if ((dirp = (DIR *)malloc(sizeof(DIR))) == NULL) { 482 close (fd); 483 return NULL; 484 } 485 dirp->dd_fd = fd; 486 dirp->dd_loc = 0; 487 return dirp; 488 } 489 490 /* 491 * Set the mode, owner, and times for all new or changed directories 492 */ 493 setdirmodes() 494 { 495 FILE *mf; 496 struct modeinfo node; 497 struct entry *ep; 498 char *cp; 499 500 vprintf(stdout, "Set directory mode, owner, and times.\n"); 501 (void) sprintf(modefile, "%s/rstmode%d", _PATH_TMP, dumpdate); 502 mf = fopen(modefile, "r"); 503 if (mf == NULL) { 504 perror("fopen"); 505 fprintf(stderr, "cannot open mode file %s\n", modefile); 506 fprintf(stderr, "directory mode, owner, and times not set\n"); 507 return; 508 } 509 clearerr(mf); 510 for (;;) { 511 (void) fread((char *)&node, 1, sizeof(struct modeinfo), mf); 512 if (feof(mf)) 513 break; 514 ep = lookupino(node.ino); 515 if (command == 'i' || command == 'x') { 516 if (ep == NIL) 517 continue; 518 if (ep->e_flags & EXISTED) { 519 ep->e_flags &= ~NEW; 520 continue; 521 } 522 if (node.ino == ROOTINO && 523 reply("set owner/mode for '.'") == FAIL) 524 continue; 525 } 526 if (ep == NIL) { 527 panic("cannot find directory inode %d\n", node.ino); 528 } else { 529 cp = myname(ep); 530 (void) chown(cp, node.uid, node.gid); 531 (void) chmod(cp, node.mode); 532 utimes(cp, node.timep); 533 ep->e_flags &= ~NEW; 534 } 535 } 536 if (ferror(mf)) 537 panic("error setting directory modes\n"); 538 (void) fclose(mf); 539 } 540 541 /* 542 * Generate a literal copy of a directory. 543 */ 544 genliteraldir(name, ino) 545 char *name; 546 ino_t ino; 547 { 548 register struct inotab *itp; 549 int ofile, dp, i, size; 550 char buf[BUFSIZ]; 551 552 itp = inotablookup(ino); 553 if (itp == NULL) 554 panic("Cannot find directory inode %d named %s\n", ino, name); 555 if ((ofile = creat(name, 0666)) < 0) { 556 fprintf(stderr, "%s: ", name); 557 (void) fflush(stderr); 558 perror("cannot create file"); 559 return (FAIL); 560 } 561 rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); 562 dp = dup(dirp->dd_fd); 563 for (i = itp->t_size; i > 0; i -= BUFSIZ) { 564 size = i < BUFSIZ ? i : BUFSIZ; 565 if (read(dp, buf, (int) size) == -1) { 566 fprintf(stderr, 567 "write error extracting inode %d, name %s\n", 568 curfile.ino, curfile.name); 569 perror("read"); 570 done(1); 571 } 572 if (!Nflag && write(ofile, buf, (int) size) == -1) { 573 fprintf(stderr, 574 "write error extracting inode %d, name %s\n", 575 curfile.ino, curfile.name); 576 perror("write"); 577 done(1); 578 } 579 } 580 (void) close(dp); 581 (void) close(ofile); 582 return (GOOD); 583 } 584 585 /* 586 * Determine the type of an inode 587 */ 588 inodetype(ino) 589 ino_t ino; 590 { 591 struct inotab *itp; 592 593 itp = inotablookup(ino); 594 if (itp == NULL) 595 return (LEAF); 596 return (NODE); 597 } 598 599 /* 600 * Allocate and initialize a directory inode entry. 601 * If requested, save its pertinent mode, owner, and time info. 602 */ 603 struct inotab * 604 allocinotab(ino, dip, seekpt) 605 ino_t ino; 606 struct dinode *dip; 607 daddr_t seekpt; 608 { 609 register struct inotab *itp; 610 struct modeinfo node; 611 612 itp = (struct inotab *)calloc(1, sizeof(struct inotab)); 613 if (itp == 0) 614 panic("no memory directory table\n"); 615 itp->t_next = inotab[INOHASH(ino)]; 616 inotab[INOHASH(ino)] = itp; 617 itp->t_ino = ino; 618 itp->t_seekpt = seekpt; 619 if (mf == NULL) 620 return(itp); 621 node.ino = ino; 622 node.timep[0].tv_sec = dip->di_atime; 623 node.timep[0].tv_usec = 0; 624 node.timep[1].tv_sec = dip->di_mtime; 625 node.timep[1].tv_usec = 0; 626 node.mode = dip->di_mode; 627 node.uid = dip->di_uid; 628 node.gid = dip->di_gid; 629 (void) fwrite((char *)&node, 1, sizeof(struct modeinfo), mf); 630 return(itp); 631 } 632 633 /* 634 * Look up an inode in the table of directories 635 */ 636 struct inotab * 637 inotablookup(ino) 638 ino_t ino; 639 { 640 register struct inotab *itp; 641 642 for (itp = inotab[INOHASH(ino)]; itp != NULL; itp = itp->t_next) 643 if (itp->t_ino == ino) 644 return(itp); 645 return ((struct inotab *)0); 646 } 647 648 /* 649 * Clean up and exit 650 */ 651 done(exitcode) 652 int exitcode; 653 { 654 655 closemt(); 656 if (modefile[0] != '#') 657 (void) unlink(modefile); 658 if (dirfile[0] != '#') 659 (void) unlink(dirfile); 660 exit(exitcode); 661 } 662