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