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