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