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 <paths.h> 59 #include <stdio.h> 60 #include <stdlib.h> 61 #include <string.h> 62 #include <unistd.h> 63 64 #include "restore.h" 65 #include "extern.h" 66 67 /* 68 * Symbol table of directories read from tape. 69 */ 70 #define HASHSIZE 1000 71 #define INOHASH(val) (val % HASHSIZE) 72 struct inotab { 73 struct inotab *t_next; 74 ino_t t_ino; 75 int32_t t_seekpt; 76 int32_t t_size; 77 }; 78 static struct inotab *inotab[HASHSIZE]; 79 80 /* 81 * Information retained about directories. 82 */ 83 struct modeinfo { 84 ino_t ino; 85 struct timeval timep[2]; 86 mode_t mode; 87 uid_t uid; 88 gid_t gid; 89 int flags; 90 }; 91 92 /* 93 * Definitions for library routines operating on directories. 94 */ 95 #undef DIRBLKSIZ 96 #define DIRBLKSIZ 1024 97 struct rstdirdesc { 98 int dd_fd; 99 int32_t dd_loc; 100 int32_t dd_size; 101 char dd_buf[DIRBLKSIZ]; 102 }; 103 104 /* 105 * Global variables for this file. 106 */ 107 static long seekpt; 108 static FILE *df, *mf; 109 static RST_DIR *dirp; 110 static char dirfile[MAXPATHLEN] = "#"; /* No file */ 111 static char modefile[MAXPATHLEN] = "#"; /* No file */ 112 static char dot[2] = "."; /* So it can be modified */ 113 114 /* 115 * Format of old style directories. 116 */ 117 #define ODIRSIZ 14 118 struct odirect { 119 u_short d_ino; 120 char d_name[ODIRSIZ]; 121 }; 122 123 static struct inotab *allocinotab(ino_t, struct dinode *, long); 124 static void dcvt(struct odirect *, struct direct *); 125 static void flushent(void); 126 static struct inotab *inotablookup(ino_t); 127 static RST_DIR *opendirfile(const char *); 128 static void putdir(char *, long); 129 static void putent(struct direct *); 130 static void rst_seekdir(RST_DIR *, long, long); 131 static long rst_telldir(RST_DIR *); 132 static struct direct *searchdir(ino_t, char *); 133 134 /* 135 * Extract directory contents, building up a directory structure 136 * on disk for extraction by name. 137 * If genmode is requested, save mode, owner, and times for all 138 * directories on the tape. 139 */ 140 void 141 extractdirs(int genmode) 142 { 143 int i; 144 struct dinode *ip; 145 struct inotab *itp; 146 struct direct nulldir; 147 int 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 ip = curfile.dip; 188 if (ip == NULL || (ip->di_mode & IFMT) != IFDIR) { 189 (void) fclose(df); 190 dirp = opendirfile(dirfile); 191 if (dirp == NULL) 192 fprintf(stderr, "opendirfile: %s\n", 193 strerror(errno)); 194 if (mf != NULL) 195 (void) fclose(mf); 196 i = dirlookup(dot); 197 if (i == 0) 198 panic("Root directory is not on tape\n"); 199 return; 200 } 201 itp = allocinotab(curfile.ino, ip, seekpt); 202 getfile(putdir, xtrnull); 203 putent(&nulldir); 204 flushent(); 205 itp->t_size = seekpt - itp->t_seekpt; 206 } 207 } 208 209 /* 210 * skip over all the directories on the tape 211 */ 212 void 213 skipdirs(void) 214 { 215 216 while (curfile.dip && (curfile.dip->di_mode & IFMT) == IFDIR) { 217 skipfile(); 218 } 219 } 220 221 /* 222 * Recursively find names and inumbers of all files in subtree 223 * pname and pass them off to be processed. 224 */ 225 void 226 treescan(char *pname, ino_t ino, long (*todo)(char *, ino_t, int)) 227 { 228 struct inotab *itp; 229 struct direct *dp; 230 int namelen; 231 long bpt; 232 char locname[MAXPATHLEN + 1]; 233 234 itp = inotablookup(ino); 235 if (itp == NULL) { 236 /* 237 * Pname is name of a simple file or an unchanged directory. 238 */ 239 (void) (*todo)(pname, ino, LEAF); 240 return; 241 } 242 /* 243 * Pname is a dumped directory name. 244 */ 245 if ((*todo)(pname, ino, NODE) == FAIL) 246 return; 247 /* 248 * begin search through the directory 249 * skipping over "." and ".." 250 */ 251 (void) strncpy(locname, pname, sizeof(locname) - 1); 252 locname[sizeof(locname) - 1] = '\0'; 253 (void) strncat(locname, "/", sizeof(locname) - strlen(locname)); 254 namelen = strlen(locname); 255 rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); 256 dp = rst_readdir(dirp); /* "." */ 257 if (dp != NULL && strcmp(dp->d_name, ".") == 0) 258 dp = rst_readdir(dirp); /* ".." */ 259 else 260 fprintf(stderr, "Warning: `.' missing from directory %s\n", 261 pname); 262 if (dp != NULL && strcmp(dp->d_name, "..") == 0) 263 dp = rst_readdir(dirp); /* first real entry */ 264 else 265 fprintf(stderr, "Warning: `..' missing from directory %s\n", 266 pname); 267 bpt = rst_telldir(dirp); 268 /* 269 * a zero inode signals end of directory 270 */ 271 while (dp != NULL) { 272 locname[namelen] = '\0'; 273 if (namelen + dp->d_namlen >= sizeof(locname)) { 274 fprintf(stderr, "%s%s: name exceeds %d char\n", 275 locname, dp->d_name, sizeof(locname) - 1); 276 } else { 277 (void) strncat(locname, dp->d_name, (int)dp->d_namlen); 278 treescan(locname, dp->d_ino, todo); 279 rst_seekdir(dirp, bpt, itp->t_seekpt); 280 } 281 dp = rst_readdir(dirp); 282 bpt = rst_telldir(dirp); 283 } 284 } 285 286 /* 287 * Lookup a pathname which is always assumed to start from the ROOTINO. 288 */ 289 struct direct * 290 pathsearch(const char *pathname) 291 { 292 ino_t ino; 293 struct direct *dp; 294 char *path, *name, buffer[MAXPATHLEN]; 295 296 strcpy(buffer, pathname); 297 path = buffer; 298 ino = ROOTINO; 299 while (*path == '/') 300 path++; 301 dp = NULL; 302 while ((name = strsep(&path, "/")) != NULL && *name != '\0') { 303 if ((dp = searchdir(ino, name)) == NULL) 304 return (NULL); 305 ino = dp->d_ino; 306 } 307 return (dp); 308 } 309 310 /* 311 * Lookup the requested name in directory inum. 312 * Return its inode number if found, zero if it does not exist. 313 */ 314 static struct direct * 315 searchdir(ino_t inum, char *name) 316 { 317 struct direct *dp; 318 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(char *buf, long size) 339 { 340 struct direct cvtbuf; 341 struct odirect *odp; 342 struct odirect *eodp; 343 struct direct *dp; 344 long loc, i; 345 346 if (cvtflag) { 347 eodp = (struct odirect *)&buf[size]; 348 for (odp = (struct odirect *)buf; odp < eodp; odp++) 349 if (odp->d_ino != 0) { 350 dcvt(odp, &cvtbuf); 351 putent(&cvtbuf); 352 } 353 } else { 354 for (loc = 0; loc < size; ) { 355 dp = (struct direct *)(buf + loc); 356 if (Bcvt) 357 swabst((u_char *)"ls", (u_char *) dp); 358 if (oldinofmt && dp->d_ino != 0) { 359 # if BYTE_ORDER == BIG_ENDIAN 360 if (Bcvt) 361 dp->d_namlen = dp->d_type; 362 # else 363 if (!Bcvt) 364 dp->d_namlen = dp->d_type; 365 # endif 366 dp->d_type = DT_UNKNOWN; 367 } 368 i = DIRBLKSIZ - (loc & (DIRBLKSIZ - 1)); 369 if ((dp->d_reclen & 0x3) != 0 || 370 dp->d_reclen > i || 371 dp->d_reclen < DIRSIZ(0, dp) || 372 dp->d_namlen > NAME_MAX) { 373 vprintf(stdout, "Mangled directory: "); 374 if ((dp->d_reclen & 0x3) != 0) 375 vprintf(stdout, 376 "reclen not multiple of 4 "); 377 if (dp->d_reclen < DIRSIZ(0, dp)) 378 vprintf(stdout, 379 "reclen less than DIRSIZ (%d < %d) ", 380 dp->d_reclen, DIRSIZ(0, dp)); 381 if (dp->d_namlen > NAME_MAX) 382 vprintf(stdout, 383 "reclen name too big (%d > %d) ", 384 dp->d_namlen, NAME_MAX); 385 vprintf(stdout, "\n"); 386 loc += i; 387 continue; 388 } 389 loc += dp->d_reclen; 390 if (dp->d_ino != 0) { 391 putent(dp); 392 } 393 } 394 } 395 } 396 397 /* 398 * These variables are "local" to the following two functions. 399 */ 400 char dirbuf[DIRBLKSIZ]; 401 long dirloc = 0; 402 long prev = 0; 403 404 /* 405 * add a new directory entry to a file. 406 */ 407 static void 408 putent(struct direct *dp) 409 { 410 dp->d_reclen = DIRSIZ(0, dp); 411 if (dirloc + dp->d_reclen > DIRBLKSIZ) { 412 ((struct direct *)(dirbuf + prev))->d_reclen = 413 DIRBLKSIZ - prev; 414 (void) fwrite(dirbuf, 1, DIRBLKSIZ, df); 415 dirloc = 0; 416 } 417 memmove(dirbuf + dirloc, dp, (long)dp->d_reclen); 418 prev = dirloc; 419 dirloc += dp->d_reclen; 420 } 421 422 /* 423 * flush out a directory that is finished. 424 */ 425 static void 426 flushent(void) 427 { 428 ((struct direct *)(dirbuf + prev))->d_reclen = DIRBLKSIZ - prev; 429 (void) fwrite(dirbuf, (int)dirloc, 1, df); 430 seekpt = ftell(df); 431 dirloc = 0; 432 } 433 434 static void 435 dcvt(struct odirect *odp, struct direct *ndp) 436 { 437 438 memset(ndp, 0, (long)(sizeof *ndp)); 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 %d\n", 501 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 534 (void)close(dirp->dd_fd); 535 free(dirp); 536 return; 537 } 538 539 /* 540 * Simulate finding the current offset in the directory. 541 */ 542 static long 543 rst_telldir(RST_DIR *dirp) 544 { 545 return ((long)lseek(dirp->dd_fd, 546 (off_t)0, SEEK_CUR) - dirp->dd_size + dirp->dd_loc); 547 } 548 549 /* 550 * Open a directory file. 551 */ 552 static RST_DIR * 553 opendirfile(const char *name) 554 { 555 RST_DIR *dirp; 556 int fd; 557 558 if ((fd = open(name, O_RDONLY)) == -1) 559 return (NULL); 560 if ((dirp = malloc(sizeof(RST_DIR))) == NULL) { 561 (void)close(fd); 562 return (NULL); 563 } 564 dirp->dd_fd = fd; 565 dirp->dd_loc = 0; 566 return (dirp); 567 } 568 569 /* 570 * Set the mode, owner, and times for all new or changed directories 571 */ 572 void 573 setdirmodes(int flags) 574 { 575 FILE *mf; 576 struct modeinfo node; 577 struct entry *ep; 578 char *cp; 579 const char *tmpdir; 580 581 vprintf(stdout, "Set directory mode, owner, and times.\n"); 582 if ((tmpdir = getenv("TMPDIR")) == NULL || tmpdir[0] == '\0') 583 tmpdir = _PATH_TMP; 584 if (command == 'r' || command == 'R') 585 (void) sprintf(modefile, "%s/rstmode%d", tmpdir, dumpdate); 586 if (modefile[0] == '#') { 587 panic("modefile not defined\n"); 588 fprintf(stderr, "directory mode, owner, and times not set\n"); 589 return; 590 } 591 mf = fopen(modefile, "r"); 592 if (mf == NULL) { 593 fprintf(stderr, "fopen: %s\n", strerror(errno)); 594 fprintf(stderr, "cannot open mode file %s\n", modefile); 595 fprintf(stderr, "directory mode, owner, and times not set\n"); 596 return; 597 } 598 clearerr(mf); 599 for (;;) { 600 (void) fread((char *)&node, 1, sizeof(struct modeinfo), mf); 601 if (feof(mf)) 602 break; 603 ep = lookupino(node.ino); 604 if (command == 'i' || command == 'x') { 605 if (ep == NULL) 606 continue; 607 if ((flags & FORCE) == 0 && ep->e_flags & EXISTED) { 608 ep->e_flags &= ~NEW; 609 continue; 610 } 611 if (node.ino == ROOTINO && 612 reply("set owner/mode for '.'") == FAIL) 613 continue; 614 } 615 if (ep == NULL) { 616 panic("cannot find directory inode %d\n", node.ino); 617 } else { 618 cp = myname(ep); 619 if (!Nflag) { 620 (void) chown(cp, node.uid, node.gid); 621 (void) chmod(cp, node.mode); 622 utimes(cp, node.timep); 623 (void) chflags(cp, node.flags); 624 } 625 ep->e_flags &= ~NEW; 626 } 627 } 628 if (ferror(mf)) 629 panic("error setting directory modes\n"); 630 (void) fclose(mf); 631 } 632 633 /* 634 * Generate a literal copy of a directory. 635 */ 636 int 637 genliteraldir(char *name, ino_t ino) 638 { 639 struct inotab *itp; 640 int ofile, dp, i, size; 641 char buf[BUFSIZ]; 642 643 itp = inotablookup(ino); 644 if (itp == NULL) 645 panic("Cannot find directory inode %d named %s\n", ino, name); 646 if ((ofile = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) { 647 fprintf(stderr, "%s: ", name); 648 (void) fflush(stderr); 649 fprintf(stderr, "cannot create file: %s\n", strerror(errno)); 650 return (FAIL); 651 } 652 rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); 653 dp = dup(dirp->dd_fd); 654 for (i = itp->t_size; i > 0; i -= BUFSIZ) { 655 size = i < BUFSIZ ? i : BUFSIZ; 656 if (read(dp, buf, (int) size) == -1) { 657 fprintf(stderr, 658 "write error extracting inode %d, name %s\n", 659 curfile.ino, curfile.name); 660 fprintf(stderr, "read: %s\n", strerror(errno)); 661 done(1); 662 } 663 if (!Nflag && write(ofile, buf, (int) size) == -1) { 664 fprintf(stderr, 665 "write error extracting inode %d, name %s\n", 666 curfile.ino, curfile.name); 667 fprintf(stderr, "write: %s\n", strerror(errno)); 668 done(1); 669 } 670 } 671 (void) close(dp); 672 (void) close(ofile); 673 return (GOOD); 674 } 675 676 /* 677 * Determine the type of an inode 678 */ 679 int 680 inodetype(ino_t ino) 681 { 682 struct inotab *itp; 683 684 itp = inotablookup(ino); 685 if (itp == NULL) 686 return (LEAF); 687 return (NODE); 688 } 689 690 /* 691 * Allocate and initialize a directory inode entry. 692 * If requested, save its pertinent mode, owner, and time info. 693 */ 694 static struct inotab * 695 allocinotab(ino_t ino, struct dinode *dip, long seekpt) 696 { 697 struct inotab *itp; 698 struct modeinfo node; 699 700 itp = calloc(1, sizeof(struct inotab)); 701 if (itp == NULL) 702 panic("no memory directory table\n"); 703 itp->t_next = inotab[INOHASH(ino)]; 704 inotab[INOHASH(ino)] = itp; 705 itp->t_ino = ino; 706 itp->t_seekpt = seekpt; 707 if (mf == NULL) 708 return (itp); 709 node.ino = ino; 710 node.timep[0].tv_sec = dip->di_atime; 711 node.timep[0].tv_usec = dip->di_atimensec / 1000; 712 node.timep[1].tv_sec = dip->di_mtime; 713 node.timep[1].tv_usec = dip->di_mtimensec / 1000; 714 node.mode = dip->di_mode; 715 node.flags = dip->di_flags; 716 node.uid = dip->di_uid; 717 node.gid = dip->di_gid; 718 (void) fwrite((char *)&node, 1, sizeof(struct modeinfo), mf); 719 return (itp); 720 } 721 722 /* 723 * Look up an inode in the table of directories 724 */ 725 static struct inotab * 726 inotablookup(ino_t ino) 727 { 728 struct inotab *itp; 729 730 for (itp = inotab[INOHASH(ino)]; itp != NULL; itp = itp->t_next) 731 if (itp->t_ino == ino) 732 return (itp); 733 return (NULL); 734 } 735 736 /* 737 * Clean up and exit 738 */ 739 void 740 done(int exitcode) 741 { 742 743 closemt(); 744 if (modefile[0] != '#') 745 (void) unlink(modefile); 746 if (dirfile[0] != '#') 747 (void) unlink(dirfile); 748 exit(exitcode); 749 } 750