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