1 /* $OpenBSD: dirs.c,v 1.38 2014/09/07 19:43:35 guenther 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 <endian.h> 48 #include <err.h> 49 #include <errno.h> 50 #include <fcntl.h> 51 #include <paths.h> 52 #include <stdio.h> 53 #include <stdlib.h> 54 #include <string.h> 55 #include <unistd.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%lld", tmpdir, 144 (long long)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 int saved_errno = errno; 152 if (fd != -1) 153 close(fd); 154 errc(1, saved_errno, 155 "cannot create directory temporary %s", dirfile); 156 } 157 if (genmode != 0) { 158 (void)snprintf(modefile, sizeof(modefile), "%s/rstmode%lld", 159 tmpdir, (long long)dumpdate); 160 if (command != 'r' && command != 'R') { 161 strlcat(modefile, "-XXXXXXXXXX", sizeof(modefile)); 162 fd = mkstemp(modefile); 163 } else 164 fd = open(modefile, O_RDWR|O_CREAT|O_EXCL, 0666); 165 if (fd == -1 || (mf = fdopen(fd, "w")) == NULL) { 166 int saved_errno = errno; 167 if (fd != -1) 168 close(fd); 169 errc(1, saved_errno, 170 "cannot create modefile %s", modefile); 171 } 172 } 173 nulldir.d_ino = 0; 174 nulldir.d_type = DT_DIR; 175 nulldir.d_namlen = 1; 176 nulldir.d_name[0] = '/'; 177 nulldir.d_name[1] = '\0'; 178 nulldir.d_reclen = DIRSIZ(0, &nulldir); 179 for (;;) { 180 curfile.name = "<directory file - name unknown>"; 181 curfile.action = USING; 182 if (curfile.mode == 0 || (curfile.mode & IFMT) != IFDIR) { 183 (void)fclose(df); 184 dirp = opendirfile(dirfile); 185 if (dirp == NULL) 186 warn("opendirfile"); 187 if (mf != NULL) 188 (void)fclose(mf); 189 i = dirlookup(dot); 190 if (i == 0) 191 panic("Root directory is not on tape\n"); 192 return; 193 } 194 itp = allocinotab(mf, &curfile, seekpt); 195 getfile(putdir, xtrnull); 196 putent(&nulldir); 197 flushent(); 198 itp->t_size = seekpt - itp->t_seekpt; 199 } 200 } 201 202 /* 203 * skip over all the directories on the tape 204 */ 205 void 206 skipdirs(void) 207 { 208 209 while (curfile.ino && (curfile.mode & IFMT) == IFDIR) { 210 skipfile(); 211 } 212 } 213 214 /* 215 * Recursively find names and inumbers of all files in subtree 216 * pname and pass them off to be processed. 217 */ 218 void 219 treescan(char *pname, ino_t ino, long (*todo)(char *, ino_t, int)) 220 { 221 struct inotab *itp; 222 struct direct *dp; 223 size_t namelen; 224 long bpt; 225 char locname[MAXPATHLEN + 1]; 226 227 itp = inotablookup(ino); 228 if (itp == NULL) { 229 /* 230 * Pname is name of a simple file or an unchanged directory. 231 */ 232 (void)(*todo)(pname, ino, LEAF); 233 return; 234 } 235 /* 236 * Pname is a dumped directory name. 237 */ 238 if ((*todo)(pname, ino, NODE) == FAIL) 239 return; 240 /* 241 * begin search through the directory 242 * skipping over "." and ".." 243 */ 244 namelen = strlcpy(locname, pname, sizeof(locname)); 245 if (namelen >= sizeof(locname) - 1) 246 namelen = sizeof(locname) - 2; 247 locname[namelen++] = '/'; 248 locname[namelen] = '\0'; 249 rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); 250 dp = rst_readdir(dirp); /* "." */ 251 if (dp != NULL && strcmp(dp->d_name, ".") == 0) 252 dp = rst_readdir(dirp); /* ".." */ 253 else 254 fprintf(stderr, "Warning: `.' missing from directory %s\n", 255 pname); 256 if (dp != NULL && strcmp(dp->d_name, "..") == 0) 257 dp = rst_readdir(dirp); /* first real entry */ 258 else 259 fprintf(stderr, "Warning: `..' missing from directory %s\n", 260 pname); 261 bpt = rst_telldir(dirp); 262 /* 263 * a zero inode signals end of directory 264 */ 265 while (dp != NULL) { 266 locname[namelen] = '\0'; 267 if (namelen + dp->d_namlen >= sizeof(locname)) { 268 fprintf(stderr, "%s%s: name exceeds %zd char\n", 269 locname, dp->d_name, sizeof(locname) - 1); 270 } else { 271 (void)strlcat(locname, dp->d_name, sizeof(locname)); 272 treescan(locname, dp->d_ino, todo); 273 rst_seekdir(dirp, bpt, itp->t_seekpt); 274 } 275 dp = rst_readdir(dirp); 276 bpt = rst_telldir(dirp); 277 } 278 } 279 280 /* 281 * Lookup a pathname which is always assumed to start from the ROOTINO. 282 */ 283 struct direct * 284 pathsearch(const char *pathname) 285 { 286 ino_t ino; 287 struct direct *dp; 288 char *path, *name, buffer[MAXPATHLEN]; 289 290 strlcpy(buffer, pathname, sizeof buffer); 291 path = buffer; 292 ino = ROOTINO; 293 while (*path == '/') 294 path++; 295 dp = NULL; 296 while ((name = strsep(&path, "/")) != NULL && *name != '\0') { 297 if ((dp = searchdir(ino, name)) == NULL) 298 return (NULL); 299 ino = dp->d_ino; 300 } 301 return (dp); 302 } 303 304 /* 305 * Lookup the requested name in directory inum. 306 * Return its inode number if found, zero if it does not exist. 307 */ 308 static struct direct * 309 searchdir(ino_t inum, char *name) 310 { 311 struct direct *dp; 312 struct inotab *itp; 313 int len; 314 315 itp = inotablookup(inum); 316 if (itp == NULL) 317 return (NULL); 318 rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); 319 len = strlen(name); 320 do { 321 dp = rst_readdir(dirp); 322 if (dp == NULL) 323 return (NULL); 324 } while (dp->d_namlen != len || strncmp(dp->d_name, name, len) != 0); 325 return (dp); 326 } 327 328 /* 329 * Put the directory entries in the directory file 330 */ 331 static void 332 putdir(char *buf, size_t size) 333 { 334 struct direct cvtbuf; 335 struct odirect *odp; 336 struct odirect *eodp; 337 struct direct *dp; 338 size_t loc, i; 339 340 if (cvtflag) { 341 eodp = (struct odirect *)&buf[size]; 342 for (odp = (struct odirect *)buf; odp < eodp; odp++) 343 if (odp->d_ino != 0) { 344 dcvt(odp, &cvtbuf); 345 putent(&cvtbuf); 346 } 347 } else { 348 for (loc = 0; loc < size; ) { 349 dp = (struct direct *)(buf + loc); 350 if (Bcvt) { 351 dp->d_ino = swap32(dp->d_ino); 352 dp->d_reclen = swap16(dp->d_reclen); 353 } 354 if (oldinofmt && dp->d_ino != 0) { 355 # if BYTE_ORDER == BIG_ENDIAN 356 if (Bcvt) 357 dp->d_namlen = dp->d_type; 358 # else 359 if (!Bcvt) 360 dp->d_namlen = dp->d_type; 361 # endif 362 dp->d_type = DT_UNKNOWN; 363 } 364 i = DIRBLKSIZ - (loc & (DIRBLKSIZ - 1)); 365 if ((dp->d_reclen & 0x3) != 0 || 366 dp->d_reclen > i || 367 dp->d_reclen < DIRSIZ(0, dp) || 368 dp->d_namlen > NAME_MAX) { 369 Vprintf(stdout, "Mangled directory: "); 370 if ((dp->d_reclen & 0x3) != 0) 371 Vprintf(stdout, 372 "reclen not multiple of 4 "); 373 if (dp->d_reclen < DIRSIZ(0, dp)) 374 Vprintf(stdout, 375 "reclen less than DIRSIZ (%u < %u) ", 376 (unsigned)dp->d_reclen, 377 (unsigned)DIRSIZ(0, dp)); 378 if (dp->d_namlen > NAME_MAX) 379 Vprintf(stdout, 380 "reclen name too big (%u > %u) ", 381 (unsigned)dp->d_namlen, NAME_MAX); 382 Vprintf(stdout, "\n"); 383 loc += i; 384 continue; 385 } 386 loc += dp->d_reclen; 387 if (dp->d_ino != 0) { 388 putent(dp); 389 } 390 } 391 } 392 } 393 394 /* 395 * These variables are "local" to the following two functions. 396 */ 397 char dirbuf[DIRBLKSIZ]; 398 long dirloc = 0; 399 long prev = 0; 400 401 /* 402 * add a new directory entry to a file. 403 */ 404 static void 405 putent(struct direct *dp) 406 { 407 dp->d_reclen = DIRSIZ(0, dp); 408 if (dirloc + dp->d_reclen > DIRBLKSIZ) { 409 ((struct direct *)(dirbuf + prev))->d_reclen = 410 DIRBLKSIZ - prev; 411 (void)fwrite(dirbuf, 1, DIRBLKSIZ, df); 412 dirloc = 0; 413 } 414 memcpy(dirbuf + dirloc, dp, dp->d_reclen); 415 prev = dirloc; 416 dirloc += dp->d_reclen; 417 } 418 419 /* 420 * flush out a directory that is finished. 421 */ 422 static void 423 flushent(void) 424 { 425 ((struct direct *)(dirbuf + prev))->d_reclen = DIRBLKSIZ - prev; 426 (void)fwrite(dirbuf, dirloc, 1, df); 427 seekpt = ftell(df); 428 dirloc = 0; 429 } 430 431 static void 432 dcvt(struct odirect *odp, struct direct *ndp) 433 { 434 435 memset(ndp, 0, sizeof *ndp); 436 if (Bcvt) 437 ndp->d_ino = swap16(odp->d_ino); 438 else 439 ndp->d_ino = odp->d_ino; 440 ndp->d_type = DT_UNKNOWN; 441 (void)strncpy(ndp->d_name, odp->d_name, ODIRSIZ); 442 ndp->d_namlen = strlen(ndp->d_name); 443 ndp->d_reclen = DIRSIZ(0, ndp); 444 } 445 446 /* 447 * Seek to an entry in a directory. 448 * Only values returned by rst_telldir should be passed to rst_seekdir. 449 * This routine handles many directories in a single file. 450 * It takes the base of the directory in the file, plus 451 * the desired seek offset into it. 452 */ 453 static void 454 rst_seekdir(RST_DIR *dirp, long loc, long base) 455 { 456 457 if (loc == rst_telldir(dirp)) 458 return; 459 loc -= base; 460 if (loc < 0) 461 fprintf(stderr, "bad seek pointer to rst_seekdir %ld\n", loc); 462 (void)lseek(dirp->dd_fd, base + (loc & ~(DIRBLKSIZ - 1)), SEEK_SET); 463 dirp->dd_loc = loc & (DIRBLKSIZ - 1); 464 if (dirp->dd_loc != 0) 465 dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, DIRBLKSIZ); 466 } 467 468 /* 469 * get next entry in a directory. 470 */ 471 struct direct * 472 rst_readdir(RST_DIR *dirp) 473 { 474 struct direct *dp; 475 476 for (;;) { 477 if (dirp->dd_loc == 0) { 478 dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, 479 DIRBLKSIZ); 480 if (dirp->dd_size <= 0) { 481 Dprintf(stderr, "error reading directory\n"); 482 return (NULL); 483 } 484 } 485 if (dirp->dd_loc >= dirp->dd_size) { 486 dirp->dd_loc = 0; 487 continue; 488 } 489 dp = (struct direct *)(dirp->dd_buf + dirp->dd_loc); 490 if (dp->d_reclen == 0 || 491 dp->d_reclen > DIRBLKSIZ + 1 - dirp->dd_loc) { 492 Dprintf(stderr, "corrupted directory: bad reclen %d\n", 493 dp->d_reclen); 494 return (NULL); 495 } 496 dirp->dd_loc += dp->d_reclen; 497 if (dp->d_ino == 0 && strcmp(dp->d_name, "/") == 0) 498 return (NULL); 499 if (dp->d_ino >= maxino) { 500 Dprintf(stderr, "corrupted directory: bad inum %llu\n", 501 (unsigned long long)dp->d_ino); 502 continue; 503 } 504 return (dp); 505 } 506 } 507 508 /* 509 * Simulate the opening of a directory 510 */ 511 RST_DIR * 512 rst_opendir(const char *name) 513 { 514 struct inotab *itp; 515 RST_DIR *dirp; 516 ino_t ino; 517 518 if ((ino = dirlookup(name)) > 0 && 519 (itp = inotablookup(ino)) != NULL) { 520 dirp = opendirfile(dirfile); 521 rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); 522 return (dirp); 523 } 524 return (NULL); 525 } 526 527 /* 528 * In our case, there is nothing to do when closing a directory. 529 */ 530 void 531 rst_closedir(RST_DIR *dirp) 532 { 533 (void)close(dirp->dd_fd); 534 free(dirp); 535 return; 536 } 537 538 /* 539 * Simulate finding the current offset in the directory. 540 */ 541 static long 542 rst_telldir(RST_DIR *dirp) 543 { 544 return ((long)lseek(dirp->dd_fd, 545 (off_t)0, SEEK_CUR) - dirp->dd_size + dirp->dd_loc); 546 } 547 548 /* 549 * Open a directory file. 550 */ 551 static RST_DIR * 552 opendirfile(const char *name) 553 { 554 RST_DIR *dirp; 555 int fd; 556 557 if ((fd = open(name, O_RDONLY)) == -1) 558 return (NULL); 559 if ((dirp = malloc(sizeof(RST_DIR))) == NULL) { 560 (void)close(fd); 561 return (NULL); 562 } 563 dirp->dd_fd = fd; 564 dirp->dd_loc = 0; 565 return (dirp); 566 } 567 568 /* 569 * Set the mode, owner, and times for all new or changed directories 570 */ 571 void 572 setdirmodes(int flags) 573 { 574 FILE *mf; 575 struct modeinfo node; 576 struct entry *ep; 577 char *cp; 578 579 Vprintf(stdout, "Set directory mode, owner, and times.\n"); 580 if (command == 'r' || command == 'R') 581 (void)snprintf(modefile, sizeof(modefile), "%s/rstmode%lld", 582 tmpdir, (long long)dumpdate); 583 if (modefile[0] == '#') { 584 panic("modefile not defined\n"); 585 fputs("directory mode, owner, and times not set\n", stderr); 586 return; 587 } 588 mf = fopen(modefile, "r"); 589 if (mf == NULL) { 590 warn("fopen"); 591 fprintf(stderr, "cannot open mode file %s\n", modefile); 592 fprintf(stderr, "directory mode, owner, and times not set\n"); 593 return; 594 } 595 clearerr(mf); 596 for (;;) { 597 (void)fread((char *)&node, 1, sizeof(struct modeinfo), mf); 598 if (feof(mf)) 599 break; 600 ep = lookupino(node.ino); 601 if (command == 'i' || command == 'x') { 602 if (ep == NULL) 603 continue; 604 if ((flags & FORCE) == 0 && ep->e_flags & EXISTED) { 605 ep->e_flags &= ~NEW; 606 continue; 607 } 608 if (node.ino == ROOTINO && 609 reply("set owner/mode for '.'") == FAIL) 610 continue; 611 } 612 if (ep == NULL) { 613 panic("cannot find directory inode %llu\n", 614 (unsigned long long)node.ino); 615 } else { 616 if (!Nflag) { 617 cp = myname(ep); 618 (void)chown(cp, node.uid, node.gid); 619 (void)chmod(cp, node.mode); 620 (void)chflags(cp, node.flags); 621 (void)utimes(cp, node.ctimep); 622 (void)utimes(cp, node.mtimep); 623 } 624 ep->e_flags &= ~NEW; 625 } 626 } 627 if (ferror(mf)) 628 panic("error setting directory modes\n"); 629 (void)fclose(mf); 630 } 631 632 /* 633 * Generate a literal copy of a directory. 634 */ 635 int 636 genliteraldir(char *name, ino_t ino) 637 { 638 struct inotab *itp; 639 int ofile, dp, i, size; 640 char buf[BUFSIZ]; 641 642 itp = inotablookup(ino); 643 if (itp == NULL) 644 panic("Cannot find directory inode %llu named %s\n", 645 (unsigned long long)ino, name); 646 if ((ofile = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) { 647 warn("%s: cannot create file", name); 648 return (FAIL); 649 } 650 rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); 651 dp = dup(dirp->dd_fd); 652 for (i = itp->t_size; i > 0; i -= BUFSIZ) { 653 size = i < BUFSIZ ? i : BUFSIZ; 654 if (read(dp, buf, size) == -1) 655 err(1, "read error extracting inode %llu, name %s", 656 (unsigned long long)curfile.ino, curfile.name); 657 xtrfile(buf, size); 658 } 659 (void)close(dp); 660 (void)close(ofile); 661 return (GOOD); 662 } 663 664 /* 665 * Determine the type of an inode 666 */ 667 int 668 inodetype(ino_t ino) 669 { 670 struct inotab *itp; 671 672 itp = inotablookup(ino); 673 if (itp == NULL) 674 return (LEAF); 675 return (NODE); 676 } 677 678 /* 679 * Allocate and initialize a directory inode entry. 680 * If requested, save its pertinent mode, owner, and time info. 681 */ 682 static struct inotab * 683 allocinotab(FILE *mf, struct context *ctxp, long seekpt) 684 { 685 struct inotab *itp; 686 struct modeinfo node; 687 688 itp = calloc(1, sizeof(struct inotab)); 689 if (itp == NULL) 690 panic("no memory directory table\n"); 691 itp->t_next = inotab[INOHASH(ctxp->ino)]; 692 inotab[INOHASH(ctxp->ino)] = itp; 693 itp->t_ino = ctxp->ino; 694 itp->t_seekpt = seekpt; 695 if (mf == NULL) 696 return (itp); 697 node.ino = ctxp->ino; 698 node.mtimep[0].tv_sec = ctxp->atime_sec; 699 node.mtimep[0].tv_usec = ctxp->atime_nsec / 1000; 700 node.mtimep[1].tv_sec = ctxp->mtime_sec; 701 node.mtimep[1].tv_usec = ctxp->mtime_nsec / 1000; 702 node.ctimep[0].tv_sec = ctxp->atime_sec; 703 node.ctimep[0].tv_usec = ctxp->atime_nsec / 1000; 704 node.ctimep[1].tv_sec = ctxp->birthtime_sec; 705 node.ctimep[1].tv_usec = ctxp->birthtime_nsec / 1000; 706 node.mode = ctxp->mode; 707 node.flags = ctxp->file_flags; 708 node.uid = ctxp->uid; 709 node.gid = ctxp->gid; 710 (void)fwrite((char *)&node, 1, sizeof(struct modeinfo), mf); 711 return (itp); 712 } 713 714 /* 715 * Look up an inode in the table of directories 716 */ 717 static struct inotab * 718 inotablookup(ino_t ino) 719 { 720 struct inotab *itp; 721 722 for (itp = inotab[INOHASH(ino)]; itp != NULL; itp = itp->t_next) 723 if (itp->t_ino == ino) 724 return (itp); 725 return (NULL); 726 } 727 728 /* 729 * Clean up and exit 730 */ 731 void 732 cleanup(void) 733 { 734 735 closemt(); 736 if (modefile[0] != '#') 737 (void)unlink(modefile); 738 if (dirfile[0] != '#') 739 (void)unlink(dirfile); 740 } 741