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