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