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