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