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