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