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