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