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