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.2 (Berkeley) 06/18/85"; 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 np = lookupino(ino); 179 if (np == NULL) 180 panic("corrupted symbol table\n"); 181 fprintf(stderr, ". missing from directory %s\n", myname(np)); 182 } 183 if (dp != NULL && strcmp(dp->d_name, "..") == 0) { 184 dp = rst_readdir(dirp); /* first real entry */ 185 } else { 186 np = lookupino(ino); 187 if (np == NULL) 188 panic("corrupted symbol table\n"); 189 fprintf(stderr, ".. missing from directory %s\n", myname(np)); 190 } 191 bpt = telldir(dirp); 192 /* 193 * a zero inode signals end of directory 194 */ 195 while (dp != NULL && dp->d_ino != 0) { 196 locname[namelen] = '\0'; 197 if (namelen + dp->d_namlen >= MAXPATHLEN) { 198 fprintf(stderr, "%s%s: name exceeds %d char\n", 199 locname, dp->d_name, MAXPATHLEN); 200 } else { 201 (void) strncat(locname, dp->d_name, (int)dp->d_namlen); 202 treescan(locname, dp->d_ino, todo); 203 rst_seekdir(dirp, bpt, itp->t_seekpt); 204 } 205 dp = rst_readdir(dirp); 206 bpt = telldir(dirp); 207 } 208 if (dp == NULL) 209 fprintf(stderr, "corrupted directory: %s.\n", locname); 210 } 211 212 /* 213 * Search the directory tree rooted at inode ROOTINO 214 * for the path pointed at by n 215 */ 216 ino_t 217 psearch(n) 218 char *n; 219 { 220 register char *cp, *cp1; 221 ino_t ino; 222 char c; 223 224 ino = ROOTINO; 225 if (*(cp = n) == '/') 226 cp++; 227 next: 228 cp1 = cp + 1; 229 while (*cp1 != '/' && *cp1) 230 cp1++; 231 c = *cp1; 232 *cp1 = 0; 233 ino = search(ino, cp); 234 if (ino == 0) { 235 *cp1 = c; 236 return(0); 237 } 238 *cp1 = c; 239 if (c == '/') { 240 cp = cp1+1; 241 goto next; 242 } 243 return(ino); 244 } 245 246 /* 247 * search the directory inode ino 248 * looking for entry cp 249 */ 250 ino_t 251 search(inum, cp) 252 ino_t inum; 253 char *cp; 254 { 255 register struct direct *dp; 256 register struct inotab *itp; 257 int len; 258 259 itp = inotablookup(inum); 260 if (itp == NULL) 261 return(0); 262 rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); 263 len = strlen(cp); 264 do { 265 dp = rst_readdir(dirp); 266 if (dp == NULL || dp->d_ino == 0) 267 return (0); 268 } while (dp->d_namlen != len || strncmp(dp->d_name, cp, len) != 0); 269 return(dp->d_ino); 270 } 271 272 /* 273 * Put the directory entries in the directory file 274 */ 275 putdir(buf, size) 276 char *buf; 277 int size; 278 { 279 struct direct cvtbuf; 280 register struct odirect *odp; 281 struct odirect *eodp; 282 register struct direct *dp; 283 long loc, i; 284 285 if (cvtflag) { 286 eodp = (struct odirect *)&buf[size]; 287 for (odp = (struct odirect *)buf; odp < eodp; odp++) 288 if (odp->d_ino != 0) { 289 dcvt(odp, &cvtbuf); 290 putent(&cvtbuf); 291 } 292 } else { 293 for (loc = 0; loc < size; ) { 294 dp = (struct direct *)(buf + loc); 295 i = DIRBLKSIZ - (loc & (DIRBLKSIZ - 1)); 296 if (dp->d_reclen == 0 || dp->d_reclen > i) { 297 loc += i; 298 continue; 299 } 300 loc += dp->d_reclen; 301 if (dp->d_ino != 0) { 302 putent(dp); 303 } 304 } 305 } 306 } 307 308 /* 309 * These variables are "local" to the following two functions. 310 */ 311 char dirbuf[DIRBLKSIZ]; 312 long dirloc = 0; 313 long prev = 0; 314 315 /* 316 * add a new directory entry to a file. 317 */ 318 putent(dp) 319 struct direct *dp; 320 { 321 dp->d_reclen = DIRSIZ(dp); 322 if (dirloc + dp->d_reclen > DIRBLKSIZ) { 323 ((struct direct *)(dirbuf + prev))->d_reclen = 324 DIRBLKSIZ - prev; 325 (void) fwrite(dirbuf, 1, DIRBLKSIZ, df); 326 dirloc = 0; 327 } 328 bcopy((char *)dp, dirbuf + dirloc, (long)dp->d_reclen); 329 prev = dirloc; 330 dirloc += dp->d_reclen; 331 } 332 333 /* 334 * flush out a directory that is finished. 335 */ 336 flushent() 337 { 338 339 ((struct direct *)(dirbuf + prev))->d_reclen = DIRBLKSIZ - prev; 340 (void) fwrite(dirbuf, (int)dirloc, 1, df); 341 seekpt = ftell(df); 342 dirloc = 0; 343 } 344 345 dcvt(odp, ndp) 346 register struct odirect *odp; 347 register struct direct *ndp; 348 { 349 350 bzero((char *)ndp, (long)(sizeof *ndp)); 351 ndp->d_ino = odp->d_ino; 352 (void) strncpy(ndp->d_name, odp->d_name, ODIRSIZ); 353 ndp->d_namlen = strlen(ndp->d_name); 354 ndp->d_reclen = DIRSIZ(ndp); 355 } 356 357 /* 358 * Seek to an entry in a directory. 359 * Only values returned by ``telldir'' should be passed to rst_seekdir. 360 * This routine handles many directories in a single file. 361 * It takes the base of the directory in the file, plus 362 * the desired seek offset into it. 363 */ 364 void 365 rst_seekdir(dirp, loc, base) 366 register DIR *dirp; 367 daddr_t loc, base; 368 { 369 370 if (loc == telldir(dirp)) 371 return; 372 loc -= base; 373 if (loc < 0) 374 fprintf(stderr, "bad seek pointer to rst_seekdir %d\n", loc); 375 (void) lseek(dirp->dd_fd, base + (loc & ~(DIRBLKSIZ - 1)), 0); 376 dirp->dd_loc = loc & (DIRBLKSIZ - 1); 377 if (dirp->dd_loc != 0) 378 dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, DIRBLKSIZ); 379 } 380 381 /* 382 * get next entry in a directory. 383 */ 384 struct direct * 385 rst_readdir(dirp) 386 register DIR *dirp; 387 { 388 register struct direct *dp; 389 390 for (;;) { 391 if (dirp->dd_loc == 0) { 392 dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, 393 DIRBLKSIZ); 394 if (dirp->dd_size <= 0) { 395 dprintf(stderr, "error reading directory\n"); 396 return NULL; 397 } 398 } 399 if (dirp->dd_loc >= dirp->dd_size) { 400 dirp->dd_loc = 0; 401 continue; 402 } 403 dp = (struct direct *)(dirp->dd_buf + dirp->dd_loc); 404 if (dp->d_reclen == 0 || 405 dp->d_reclen > DIRBLKSIZ + 1 - dirp->dd_loc) { 406 dprintf(stderr, "corrupted directory: bad reclen %d\n", 407 dp->d_reclen); 408 return NULL; 409 } 410 dirp->dd_loc += dp->d_reclen; 411 if (dp->d_ino == 0 && strcmp(dp->d_name, "/") != 0) 412 continue; 413 if (dp->d_ino >= maxino) { 414 dprintf(stderr, "corrupted directory: bad inum %d\n", 415 dp->d_ino); 416 continue; 417 } 418 return (dp); 419 } 420 } 421 422 /* 423 * Simulate the opening of a directory 424 */ 425 DIR * 426 rst_opendir(name) 427 char *name; 428 { 429 struct inotab *itp; 430 ino_t ino; 431 432 if ((ino = dirlookup(name)) > 0 && 433 (itp = inotablookup(ino)) != NULL) { 434 rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); 435 return (dirp); 436 } 437 return (0); 438 } 439 440 /* 441 * Set the mode, owner, and times for all new or changed directories 442 */ 443 setdirmodes() 444 { 445 FILE *mf; 446 struct modeinfo node; 447 struct entry *ep; 448 char *cp; 449 450 vprintf(stdout, "Set directory mode, owner, and times.\n"); 451 (void) sprintf(modefile, "/tmp/rstmode%d", dumpdate); 452 mf = fopen(modefile, "r"); 453 if (mf == NULL) { 454 perror("fopen"); 455 fprintf(stderr, "cannot open mode file %s\n", modefile); 456 fprintf(stderr, "directory mode, owner, and times not set\n"); 457 return; 458 } 459 clearerr(mf); 460 for (;;) { 461 (void) fread((char *)&node, 1, sizeof(struct modeinfo), mf); 462 if (feof(mf)) 463 break; 464 ep = lookupino(node.ino); 465 if (command == 'i' || command == 'x') { 466 if (ep == NIL) 467 continue; 468 if (ep->e_flags & EXISTED) { 469 ep->e_flags &= ~NEW; 470 continue; 471 } 472 if (node.ino == ROOTINO && 473 reply("set owner/mode for '.'") == FAIL) 474 continue; 475 } 476 if (ep == NIL) 477 panic("cannot find directory inode %d\n", node.ino); 478 cp = myname(ep); 479 (void) chown(cp, node.uid, node.gid); 480 (void) chmod(cp, node.mode); 481 utime(cp, node.timep); 482 ep->e_flags &= ~NEW; 483 } 484 if (ferror(mf)) 485 panic("error setting directory modes\n"); 486 (void) fclose(mf); 487 } 488 489 /* 490 * Generate a literal copy of a directory. 491 */ 492 genliteraldir(name, ino) 493 char *name; 494 ino_t ino; 495 { 496 register struct inotab *itp; 497 int ofile, dp, i, size; 498 char buf[BUFSIZ]; 499 500 itp = inotablookup(ino); 501 if (itp == NULL) 502 panic("Cannot find directory inode %d named %s\n", ino, name); 503 if ((ofile = creat(name, 0666)) < 0) { 504 fprintf(stderr, "%s: ", name); 505 (void) fflush(stderr); 506 perror("cannot create file"); 507 return (FAIL); 508 } 509 rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); 510 dp = dup(dirp->dd_fd); 511 for (i = itp->t_size; i > 0; i -= BUFSIZ) { 512 size = i < BUFSIZ ? i : BUFSIZ; 513 if (read(dp, buf, (int) size) == -1) { 514 fprintf(stderr, 515 "write error extracting inode %d, name %s\n", 516 curfile.ino, curfile.name); 517 perror("read"); 518 done(1); 519 } 520 if (write(ofile, buf, (int) size) == -1) { 521 fprintf(stderr, 522 "write error extracting inode %d, name %s\n", 523 curfile.ino, curfile.name); 524 perror("write"); 525 done(1); 526 } 527 } 528 (void) close(dp); 529 (void) close(ofile); 530 return (GOOD); 531 } 532 533 /* 534 * Determine the type of an inode 535 */ 536 inodetype(ino) 537 ino_t ino; 538 { 539 struct inotab *itp; 540 541 itp = inotablookup(ino); 542 if (itp == NULL) 543 return (LEAF); 544 return (NODE); 545 } 546 547 /* 548 * Allocate and initialize a directory inode entry. 549 * If requested, save its pertinent mode, owner, and time info. 550 */ 551 struct inotab * 552 allocinotab(ino, dip, seekpt) 553 ino_t ino; 554 struct dinode *dip; 555 daddr_t seekpt; 556 { 557 register struct inotab *itp; 558 struct modeinfo node; 559 560 itp = (struct inotab *)calloc(1, sizeof(struct inotab)); 561 if (itp == 0) 562 panic("no memory directory table\n"); 563 itp->t_next = inotab[INOHASH(ino)]; 564 inotab[INOHASH(ino)] = itp; 565 itp->t_ino = ino; 566 itp->t_seekpt = seekpt; 567 if (mf == NULL) 568 return(itp); 569 node.ino = ino; 570 node.timep[0] = dip->di_atime; 571 node.timep[1] = dip->di_mtime; 572 node.mode = dip->di_mode; 573 node.uid = dip->di_uid; 574 node.gid = dip->di_gid; 575 (void) fwrite((char *)&node, 1, sizeof(struct modeinfo), mf); 576 return(itp); 577 } 578 579 /* 580 * Look up an inode in the table of directories 581 */ 582 struct inotab * 583 inotablookup(ino) 584 ino_t ino; 585 { 586 register struct inotab *itp; 587 588 for (itp = inotab[INOHASH(ino)]; itp != NULL; itp = itp->t_next) 589 if (itp->t_ino == ino) 590 return(itp); 591 return ((struct inotab *)0); 592 } 593 594 /* 595 * Clean up and exit 596 */ 597 done(exitcode) 598 int exitcode; 599 { 600 601 closemt(); 602 if (modefile[0] != '#') 603 (void) unlink(modefile); 604 if (dirfile[0] != '#') 605 (void) unlink(dirfile); 606 exit(exitcode); 607 } 608