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