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