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