1 /* 2 * Copyright (c) 1983, 1993 3 * The Regents of the University of California. All rights reserved. 4 * (c) UNIX System Laboratories, Inc. 5 * All or some portions of this file are derived from material licensed 6 * to the University of California by American Telephone and Telegraph 7 * Co. or Unix System Laboratories, Inc. and are reproduced herein with 8 * the permission of UNIX System Laboratories, Inc. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 4. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #ifndef lint 36 #if 0 37 static char sccsid[] = "@(#)dirs.c 8.7 (Berkeley) 5/1/95"; 38 #endif 39 static const char rcsid[] = 40 "$FreeBSD$"; 41 #endif /* not lint */ 42 43 #include <sys/param.h> 44 #include <sys/file.h> 45 #include <sys/stat.h> 46 #include <sys/time.h> 47 48 #include <ufs/ufs/dinode.h> 49 #include <ufs/ufs/dir.h> 50 #include <protocols/dumprestore.h> 51 52 #include <err.h> 53 #include <errno.h> 54 #include <limits.h> 55 #include <paths.h> 56 #include <stdio.h> 57 #include <stdlib.h> 58 #include <string.h> 59 #include <unistd.h> 60 61 #include "restore.h" 62 #include "extern.h" 63 64 /* 65 * Symbol table of directories read from tape. 66 */ 67 #define HASHSIZE 1000 68 #define INOHASH(val) (val % HASHSIZE) 69 struct inotab { 70 struct inotab *t_next; 71 ino_t t_ino; 72 int32_t t_seekpt; 73 int32_t t_size; 74 }; 75 static struct inotab *inotab[HASHSIZE]; 76 77 /* 78 * Information retained about directories. 79 */ 80 struct modeinfo { 81 ino_t ino; 82 struct timeval ctimep[2]; 83 struct timeval mtimep[2]; 84 mode_t mode; 85 uid_t uid; 86 gid_t gid; 87 int flags; 88 }; 89 90 /* 91 * Definitions for library routines operating on directories. 92 */ 93 #undef DIRBLKSIZ 94 #define DIRBLKSIZ 1024 95 struct rstdirdesc { 96 int dd_fd; 97 int32_t dd_loc; 98 int32_t dd_size; 99 char dd_buf[DIRBLKSIZ]; 100 }; 101 102 /* 103 * Global variables for this file. 104 */ 105 static long seekpt; 106 static FILE *df, *mf; 107 static RST_DIR *dirp; 108 static char dirfile[MAXPATHLEN] = "#"; /* No file */ 109 static char modefile[MAXPATHLEN] = "#"; /* No file */ 110 static char dot[2] = "."; /* So it can be modified */ 111 112 /* 113 * Format of old style directories. 114 */ 115 #define ODIRSIZ 14 116 struct odirect { 117 u_short d_ino; 118 char d_name[ODIRSIZ]; 119 }; 120 121 static struct inotab *allocinotab(struct context *, long); 122 static void dcvt(struct odirect *, struct direct *); 123 static void flushent(void); 124 static struct inotab *inotablookup(ino_t); 125 static RST_DIR *opendirfile(const char *); 126 static void putdir(char *, long); 127 static void putent(struct direct *); 128 static void rst_seekdir(RST_DIR *, long, long); 129 static long rst_telldir(RST_DIR *); 130 static struct direct *searchdir(ino_t, char *); 131 132 /* 133 * Extract directory contents, building up a directory structure 134 * on disk for extraction by name. 135 * If genmode is requested, save mode, owner, and times for all 136 * directories on the tape. 137 */ 138 void 139 extractdirs(int genmode) 140 { 141 struct inotab *itp; 142 struct direct nulldir; 143 int i, fd; 144 const char *tmpdir; 145 146 vprintf(stdout, "Extract directories from tape\n"); 147 if ((tmpdir = getenv("TMPDIR")) == NULL || tmpdir[0] == '\0') 148 tmpdir = _PATH_TMP; 149 (void) sprintf(dirfile, "%s/rstdir%d", tmpdir, dumpdate); 150 if (command != 'r' && command != 'R') { 151 (void *) strcat(dirfile, "-XXXXXX"); 152 fd = mkstemp(dirfile); 153 } else 154 fd = open(dirfile, O_RDWR|O_CREAT|O_EXCL, 0666); 155 if (fd == -1 || (df = fdopen(fd, "w")) == NULL) { 156 if (fd != -1) 157 close(fd); 158 warn("%s - cannot create directory temporary\nfopen", dirfile); 159 done(1); 160 } 161 if (genmode != 0) { 162 (void) sprintf(modefile, "%s/rstmode%d", tmpdir, dumpdate); 163 if (command != 'r' && command != 'R') { 164 (void *) strcat(modefile, "-XXXXXX"); 165 fd = mkstemp(modefile); 166 } else 167 fd = open(modefile, O_RDWR|O_CREAT|O_EXCL, 0666); 168 if (fd == -1 || (mf = fdopen(fd, "w")) == NULL) { 169 if (fd != -1) 170 close(fd); 171 warn("%s - cannot create modefile\nfopen", modefile); 172 done(1); 173 } 174 } 175 nulldir.d_ino = 0; 176 nulldir.d_type = DT_DIR; 177 nulldir.d_namlen = 1; 178 (void) strcpy(nulldir.d_name, "/"); 179 nulldir.d_reclen = DIRSIZ(0, &nulldir); 180 for (;;) { 181 curfile.name = "<directory file - name unknown>"; 182 curfile.action = USING; 183 if (curfile.mode == 0 || (curfile.mode & IFMT) != IFDIR) { 184 (void) fclose(df); 185 dirp = opendirfile(dirfile); 186 if (dirp == NULL) 187 fprintf(stderr, "opendirfile: %s\n", 188 strerror(errno)); 189 if (mf != NULL) 190 (void) fclose(mf); 191 i = dirlookup(dot); 192 if (i == 0) 193 panic("Root directory is not on tape\n"); 194 return; 195 } 196 itp = allocinotab(&curfile, seekpt); 197 getfile(putdir, xtrnull); 198 putent(&nulldir); 199 flushent(); 200 itp->t_size = seekpt - itp->t_seekpt; 201 } 202 } 203 204 /* 205 * skip over all the directories on the tape 206 */ 207 void 208 skipdirs(void) 209 { 210 211 while (curfile.ino && (curfile.mode & IFMT) == IFDIR) { 212 skipfile(); 213 } 214 } 215 216 /* 217 * Recursively find names and inumbers of all files in subtree 218 * pname and pass them off to be processed. 219 */ 220 void 221 treescan(char *pname, ino_t ino, long (*todo)(char *, ino_t, int)) 222 { 223 struct inotab *itp; 224 struct direct *dp; 225 int namelen; 226 long bpt; 227 char locname[MAXPATHLEN + 1]; 228 229 itp = inotablookup(ino); 230 if (itp == NULL) { 231 /* 232 * Pname is name of a simple file or an unchanged directory. 233 */ 234 (void) (*todo)(pname, ino, LEAF); 235 return; 236 } 237 /* 238 * Pname is a dumped directory name. 239 */ 240 if ((*todo)(pname, ino, NODE) == FAIL) 241 return; 242 /* 243 * begin search through the directory 244 * skipping over "." and ".." 245 */ 246 (void) strncpy(locname, pname, sizeof(locname) - 1); 247 locname[sizeof(locname) - 1] = '\0'; 248 (void) strncat(locname, "/", sizeof(locname) - strlen(locname)); 249 namelen = strlen(locname); 250 rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); 251 dp = rst_readdir(dirp); /* "." */ 252 if (dp != NULL && strcmp(dp->d_name, ".") == 0) 253 dp = rst_readdir(dirp); /* ".." */ 254 else 255 fprintf(stderr, "Warning: `.' missing from directory %s\n", 256 pname); 257 if (dp != NULL && strcmp(dp->d_name, "..") == 0) 258 dp = rst_readdir(dirp); /* first real entry */ 259 else 260 fprintf(stderr, "Warning: `..' missing from directory %s\n", 261 pname); 262 bpt = rst_telldir(dirp); 263 /* 264 * a zero inode signals end of directory 265 */ 266 while (dp != NULL) { 267 locname[namelen] = '\0'; 268 if (namelen + dp->d_namlen >= sizeof(locname)) { 269 fprintf(stderr, "%s%s: name exceeds %d char\n", 270 locname, dp->d_name, sizeof(locname) - 1); 271 } else { 272 (void) strncat(locname, dp->d_name, (int)dp->d_namlen); 273 treescan(locname, dp->d_ino, todo); 274 rst_seekdir(dirp, bpt, itp->t_seekpt); 275 } 276 dp = rst_readdir(dirp); 277 bpt = rst_telldir(dirp); 278 } 279 } 280 281 /* 282 * Lookup a pathname which is always assumed to start from the ROOTINO. 283 */ 284 struct direct * 285 pathsearch(const char *pathname) 286 { 287 ino_t ino; 288 struct direct *dp; 289 char *path, *name, buffer[MAXPATHLEN]; 290 291 strcpy(buffer, pathname); 292 path = buffer; 293 ino = ROOTINO; 294 while (*path == '/') 295 path++; 296 dp = NULL; 297 while ((name = strsep(&path, "/")) != NULL && *name != '\0') { 298 if ((dp = searchdir(ino, name)) == NULL) 299 return (NULL); 300 ino = dp->d_ino; 301 } 302 return (dp); 303 } 304 305 /* 306 * Lookup the requested name in directory inum. 307 * Return its inode number if found, zero if it does not exist. 308 */ 309 static struct direct * 310 searchdir(ino_t inum, char *name) 311 { 312 struct direct *dp; 313 struct inotab *itp; 314 int len; 315 316 itp = inotablookup(inum); 317 if (itp == NULL) 318 return (NULL); 319 rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); 320 len = strlen(name); 321 do { 322 dp = rst_readdir(dirp); 323 if (dp == NULL) 324 return (NULL); 325 } while (dp->d_namlen != len || strncmp(dp->d_name, name, len) != 0); 326 return (dp); 327 } 328 329 /* 330 * Put the directory entries in the directory file 331 */ 332 static void 333 putdir(char *buf, long size) 334 { 335 struct direct cvtbuf; 336 struct odirect *odp; 337 struct odirect *eodp; 338 struct direct *dp; 339 long loc, i; 340 341 for (loc = 0; loc < size; ) { 342 dp = (struct direct *)(buf + loc); 343 if (Bcvt) 344 swabst((u_char *)"ls", (u_char *) dp); 345 i = DIRBLKSIZ - (loc & (DIRBLKSIZ - 1)); 346 if ((dp->d_reclen & 0x3) != 0 || 347 dp->d_reclen > i || 348 dp->d_reclen < DIRSIZ(0, dp) 349 #if NAME_MAX < 255 350 || dp->d_namlen > NAME_MAX 351 #endif 352 ) { 353 vprintf(stdout, "Mangled directory: "); 354 if ((dp->d_reclen & 0x3) != 0) 355 vprintf(stdout, 356 "reclen not multiple of 4 "); 357 if (dp->d_reclen < DIRSIZ(0, dp)) 358 vprintf(stdout, 359 "reclen less than DIRSIZ (%d < %d) ", 360 dp->d_reclen, DIRSIZ(0, dp)); 361 #if NAME_MAX < 255 362 if (dp->d_namlen > NAME_MAX) 363 vprintf(stdout, 364 "reclen name too big (%d > %d) ", 365 dp->d_namlen, NAME_MAX); 366 #endif 367 vprintf(stdout, "\n"); 368 loc += i; 369 continue; 370 } 371 loc += dp->d_reclen; 372 if (dp->d_ino != 0) { 373 putent(dp); 374 } 375 } 376 } 377 378 /* 379 * These variables are "local" to the following two functions. 380 */ 381 char dirbuf[DIRBLKSIZ]; 382 long dirloc = 0; 383 long prev = 0; 384 385 /* 386 * add a new directory entry to a file. 387 */ 388 static void 389 putent(struct direct *dp) 390 { 391 dp->d_reclen = DIRSIZ(0, dp); 392 if (dirloc + dp->d_reclen > DIRBLKSIZ) { 393 ((struct direct *)(dirbuf + prev))->d_reclen = 394 DIRBLKSIZ - prev; 395 (void) fwrite(dirbuf, 1, DIRBLKSIZ, df); 396 dirloc = 0; 397 } 398 memmove(dirbuf + dirloc, dp, (long)dp->d_reclen); 399 prev = dirloc; 400 dirloc += dp->d_reclen; 401 } 402 403 /* 404 * flush out a directory that is finished. 405 */ 406 static void 407 flushent(void) 408 { 409 ((struct direct *)(dirbuf + prev))->d_reclen = DIRBLKSIZ - prev; 410 (void) fwrite(dirbuf, (int)dirloc, 1, df); 411 seekpt = ftell(df); 412 dirloc = 0; 413 } 414 415 static void 416 dcvt(struct odirect *odp, struct direct *ndp) 417 { 418 419 memset(ndp, 0, (long)(sizeof *ndp)); 420 ndp->d_ino = odp->d_ino; 421 ndp->d_type = DT_UNKNOWN; 422 (void) strncpy(ndp->d_name, odp->d_name, ODIRSIZ); 423 ndp->d_namlen = strlen(ndp->d_name); 424 ndp->d_reclen = DIRSIZ(0, ndp); 425 } 426 427 /* 428 * Seek to an entry in a directory. 429 * Only values returned by rst_telldir should be passed to rst_seekdir. 430 * This routine handles many directories in a single file. 431 * It takes the base of the directory in the file, plus 432 * the desired seek offset into it. 433 */ 434 static void 435 rst_seekdir(RST_DIR *dirp, long loc, long base) 436 { 437 438 if (loc == rst_telldir(dirp)) 439 return; 440 loc -= base; 441 if (loc < 0) 442 fprintf(stderr, "bad seek pointer to rst_seekdir %ld\n", loc); 443 (void) lseek(dirp->dd_fd, base + (loc & ~(DIRBLKSIZ - 1)), SEEK_SET); 444 dirp->dd_loc = loc & (DIRBLKSIZ - 1); 445 if (dirp->dd_loc != 0) 446 dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, DIRBLKSIZ); 447 } 448 449 /* 450 * get next entry in a directory. 451 */ 452 struct direct * 453 rst_readdir(RST_DIR *dirp) 454 { 455 struct direct *dp; 456 457 for (;;) { 458 if (dirp->dd_loc == 0) { 459 dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, 460 DIRBLKSIZ); 461 if (dirp->dd_size <= 0) { 462 dprintf(stderr, "error reading directory\n"); 463 return (NULL); 464 } 465 } 466 if (dirp->dd_loc >= dirp->dd_size) { 467 dirp->dd_loc = 0; 468 continue; 469 } 470 dp = (struct direct *)(dirp->dd_buf + dirp->dd_loc); 471 if (dp->d_reclen == 0 || 472 dp->d_reclen > DIRBLKSIZ + 1 - dirp->dd_loc) { 473 dprintf(stderr, "corrupted directory: bad reclen %d\n", 474 dp->d_reclen); 475 return (NULL); 476 } 477 dirp->dd_loc += dp->d_reclen; 478 if (dp->d_ino == 0 && strcmp(dp->d_name, "/") == 0) 479 return (NULL); 480 if (dp->d_ino >= maxino) { 481 dprintf(stderr, "corrupted directory: bad inum %d\n", 482 dp->d_ino); 483 continue; 484 } 485 return (dp); 486 } 487 } 488 489 /* 490 * Simulate the opening of a directory 491 */ 492 void * 493 rst_opendir(const char *name) 494 { 495 struct inotab *itp; 496 RST_DIR *dirp; 497 ino_t ino; 498 499 if ((ino = dirlookup(name)) > 0 && 500 (itp = inotablookup(ino)) != NULL) { 501 dirp = opendirfile(dirfile); 502 rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); 503 return (dirp); 504 } 505 return (NULL); 506 } 507 508 /* 509 * In our case, there is nothing to do when closing a directory. 510 */ 511 void 512 rst_closedir(void *arg) 513 { 514 RST_DIR *dirp; 515 516 dirp = arg; 517 (void)close(dirp->dd_fd); 518 free(dirp); 519 return; 520 } 521 522 /* 523 * Simulate finding the current offset in the directory. 524 */ 525 static long 526 rst_telldir(RST_DIR *dirp) 527 { 528 return ((long)lseek(dirp->dd_fd, 529 (off_t)0, SEEK_CUR) - dirp->dd_size + dirp->dd_loc); 530 } 531 532 /* 533 * Open a directory file. 534 */ 535 static RST_DIR * 536 opendirfile(const char *name) 537 { 538 RST_DIR *dirp; 539 int fd; 540 541 if ((fd = open(name, O_RDONLY)) == -1) 542 return (NULL); 543 if ((dirp = malloc(sizeof(RST_DIR))) == NULL) { 544 (void)close(fd); 545 return (NULL); 546 } 547 dirp->dd_fd = fd; 548 dirp->dd_loc = 0; 549 return (dirp); 550 } 551 552 /* 553 * Set the mode, owner, and times for all new or changed directories 554 */ 555 void 556 setdirmodes(int flags) 557 { 558 FILE *mf; 559 struct modeinfo node; 560 struct entry *ep; 561 char *cp; 562 const char *tmpdir; 563 564 vprintf(stdout, "Set directory mode, owner, and times.\n"); 565 if ((tmpdir = getenv("TMPDIR")) == NULL || tmpdir[0] == '\0') 566 tmpdir = _PATH_TMP; 567 if (command == 'r' || command == 'R') 568 (void) sprintf(modefile, "%s/rstmode%d", tmpdir, dumpdate); 569 if (modefile[0] == '#') { 570 panic("modefile not defined\n"); 571 fprintf(stderr, "directory mode, owner, and times not set\n"); 572 return; 573 } 574 mf = fopen(modefile, "r"); 575 if (mf == NULL) { 576 fprintf(stderr, "fopen: %s\n", strerror(errno)); 577 fprintf(stderr, "cannot open mode file %s\n", modefile); 578 fprintf(stderr, "directory mode, owner, and times not set\n"); 579 return; 580 } 581 clearerr(mf); 582 for (;;) { 583 (void) fread((char *)&node, 1, sizeof(struct modeinfo), mf); 584 if (feof(mf)) 585 break; 586 ep = lookupino(node.ino); 587 if (command == 'i' || command == 'x') { 588 if (ep == NULL) 589 continue; 590 if ((flags & FORCE) == 0 && ep->e_flags & EXISTED) { 591 ep->e_flags &= ~NEW; 592 continue; 593 } 594 if (node.ino == ROOTINO && 595 reply("set owner/mode for '.'") == FAIL) 596 continue; 597 } 598 if (ep == NULL) { 599 panic("cannot find directory inode %d\n", node.ino); 600 } else { 601 cp = myname(ep); 602 if (!Nflag) { 603 (void) chown(cp, node.uid, node.gid); 604 (void) chmod(cp, node.mode); 605 utimes(cp, node.ctimep); 606 utimes(cp, node.mtimep); 607 (void) chflags(cp, node.flags); 608 } 609 ep->e_flags &= ~NEW; 610 } 611 } 612 if (ferror(mf)) 613 panic("error setting directory modes\n"); 614 (void) fclose(mf); 615 } 616 617 /* 618 * Generate a literal copy of a directory. 619 */ 620 int 621 genliteraldir(char *name, ino_t ino) 622 { 623 struct inotab *itp; 624 int ofile, dp, i, size; 625 char buf[BUFSIZ]; 626 627 itp = inotablookup(ino); 628 if (itp == NULL) 629 panic("Cannot find directory inode %d named %s\n", ino, name); 630 if ((ofile = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) { 631 fprintf(stderr, "%s: ", name); 632 (void) fflush(stderr); 633 fprintf(stderr, "cannot create file: %s\n", strerror(errno)); 634 return (FAIL); 635 } 636 rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); 637 dp = dup(dirp->dd_fd); 638 for (i = itp->t_size; i > 0; i -= BUFSIZ) { 639 size = i < BUFSIZ ? i : BUFSIZ; 640 if (read(dp, buf, (int) size) == -1) { 641 fprintf(stderr, 642 "write error extracting inode %d, name %s\n", 643 curfile.ino, curfile.name); 644 fprintf(stderr, "read: %s\n", strerror(errno)); 645 done(1); 646 } 647 if (!Nflag && write(ofile, buf, (int) size) == -1) { 648 fprintf(stderr, 649 "write error extracting inode %d, name %s\n", 650 curfile.ino, curfile.name); 651 fprintf(stderr, "write: %s\n", strerror(errno)); 652 done(1); 653 } 654 } 655 (void) close(dp); 656 (void) close(ofile); 657 return (GOOD); 658 } 659 660 /* 661 * Determine the type of an inode 662 */ 663 int 664 inodetype(ino_t ino) 665 { 666 struct inotab *itp; 667 668 itp = inotablookup(ino); 669 if (itp == NULL) 670 return (LEAF); 671 return (NODE); 672 } 673 674 /* 675 * Allocate and initialize a directory inode entry. 676 * If requested, save its pertinent mode, owner, and time info. 677 */ 678 static struct inotab * 679 allocinotab(struct context *ctxp, long seekpt) 680 { 681 struct inotab *itp; 682 struct modeinfo node; 683 684 itp = calloc(1, sizeof(struct inotab)); 685 if (itp == NULL) 686 panic("no memory directory table\n"); 687 itp->t_next = inotab[INOHASH(ctxp->ino)]; 688 inotab[INOHASH(ctxp->ino)] = itp; 689 itp->t_ino = ctxp->ino; 690 itp->t_seekpt = seekpt; 691 if (mf == NULL) 692 return (itp); 693 node.ino = ctxp->ino; 694 node.mtimep[0].tv_sec = ctxp->atime_sec; 695 node.mtimep[0].tv_usec = ctxp->atime_nsec / 1000; 696 node.mtimep[1].tv_sec = ctxp->mtime_sec; 697 node.mtimep[1].tv_usec = ctxp->mtime_nsec / 1000; 698 node.ctimep[0].tv_sec = ctxp->atime_sec; 699 node.ctimep[0].tv_usec = ctxp->atime_nsec / 1000; 700 node.ctimep[1].tv_sec = ctxp->birthtime_sec; 701 node.ctimep[1].tv_usec = ctxp->birthtime_nsec / 1000; 702 node.mode = ctxp->mode; 703 node.flags = ctxp->file_flags; 704 node.uid = ctxp->uid; 705 node.gid = ctxp->gid; 706 (void) fwrite((char *)&node, 1, sizeof(struct modeinfo), mf); 707 return (itp); 708 } 709 710 /* 711 * Look up an inode in the table of directories 712 */ 713 static struct inotab * 714 inotablookup(ino_t ino) 715 { 716 struct inotab *itp; 717 718 for (itp = inotab[INOHASH(ino)]; itp != NULL; itp = itp->t_next) 719 if (itp->t_ino == ino) 720 return (itp); 721 return (NULL); 722 } 723 724 /* 725 * Clean up and exit 726 */ 727 void 728 done(int exitcode) 729 { 730 731 closemt(); 732 if (modefile[0] != '#') 733 (void) unlink(modefile); 734 if (dirfile[0] != '#') 735 (void) unlink(dirfile); 736 exit(exitcode); 737 } 738