2 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
6 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
7 /* All Rights Reserved */
10 * Copyright (c) 1983 Regents of the University of California.
11 * All rights reserved. The Berkeley software License Agreement
12 * specifies the terms and conditions for redistribution.
15 #pragma ident "%Z%%M% %I% %E% SMI"
18 #include <byteorder.h>
24 * Symbol table of directories read from tape.
27 #define INOHASH(val) (val % HASHSIZE)
29 struct inotab
*t_next
;
33 struct inotab
*t_xattr
;
35 static struct inotab
*inotab
[HASHSIZE
];
36 static struct inotab
*xattrlist
= NULL
;
39 * Information retained about directories.
41 static struct modeinfo
{
51 * Global variables for this file.
53 static off64_t g_seekpt
; /* some people have a local seekpt */
55 static char dirfile
[MAXPATHLEN
] = "#"; /* No file */
56 static char modefile
[MAXPATHLEN
] = "#"; /* No file */
60 #define INIT_TEMPFILE(name, type) \
61 if (name[0] == '#') { \
62 if (tmpdir == (char *)NULL) /* can't happen; be paranoid */ \
64 (void) snprintf(name, sizeof (name), \
65 "%s/rst" type "%ld.XXXXXX", tmpdir, dumpdate); \
66 (void) mktemp(name); \
69 #define INIT_DIRFILE() INIT_TEMPFILE(dirfile, "dir")
70 #define INIT_MODEFILE() INIT_TEMPFILE(modefile, "mode")
73 * Format of old style directories.
82 static ino_t
search(ino_t
, char *);
83 static void putdir(char *, size_t);
84 static void putent(struct direct
*);
85 static void skipmetadata(FILE *, size_t);
86 static void flushent(void);
87 static void dcvt(struct odirect
*, struct direct
*);
88 static RST_DIR
*rst_initdirfile(char *);
89 static offset_t
rst_telldir(RST_DIR
*);
90 static void rst_seekdir(RST_DIR
*, offset_t
, offset_t
);
91 static struct inotab
*allocinotab(ino_t
, struct dinode
*, off64_t
);
92 static void nodeflush(void);
93 static struct inotab
*inotablookup(ino_t
);
95 static ino_t
search();
98 static void skipmetadata();
99 static void flushent();
101 static RST_DIR
*rst_initdirfile();
102 static offset_t
rst_telldir();
103 static void rst_seekdir();
104 static struct inotab
*allocinotab();
105 static void nodeflush();
106 static struct inotab
*inotablookup();
110 * Extract directory contents, building up a directory structure
111 * on disk for extraction by name.
112 * If genmode is requested, save mode, owner, and times for all
113 * directories on the tape.
116 extractdirs(int genmode
)
122 struct direct nulldir
;
123 static char dotname
[] = "."; /* dirlookup/psearch writes to its arg */
125 vprintf(stdout
, gettext("Extract directories from tape\n"));
127 if ((df
= safe_fopen(dirfile
, "w", 0600)) == (FILE *)NULL
) {
129 (void) fprintf(stderr
,
130 gettext("%s: %s - cannot create directory temporary\n"),
138 if ((mf
= safe_fopen(modefile
, "w", 0600)) == (FILE *)NULL
) {
140 (void) fprintf(stderr
,
141 gettext("%s: %s - cannot create modefile \n"),
149 nulldir
.d_namlen
= 1;
150 (void) strcpy(nulldir
.d_name
, "/");
151 /* LINTED DIRSIZ will always fit into a ushort_t */
152 nulldir
.d_reclen
= (ushort_t
)DIRSIZ(&nulldir
);
153 /* LINTED sign extension ok in assert */
154 assert(DIRSIZ(&nulldir
) == (ulong_t
)nulldir
.d_reclen
);
156 curfile
.name
= gettext("<directory file - name unknown>");
157 curfile
.action
= USING
;
160 if (ts
!= TS_END
&& ts
!= TS_INODE
) {
164 if (ts
== TS_INODE
&& ip
== NULL
) {
165 (void) fprintf(stderr
, gettext(
166 "%s: extractdirs: Failed internal consistency check, curfile.dip is NULL\n"),
170 if ((ts
== TS_INODE
&& (ip
->di_mode
& IFMT
) != IFDIR
&&
171 (ip
->di_mode
& IFMT
) != IFATTRDIR
) ||
174 /* XXX Legitimate error, bad complaint string */
176 panic("%s: %s\n", dirfile
, strerror(errno
));
179 dirp
= rst_initdirfile(dirfile
);
181 perror("initdirfile");
184 /* XXX Legitimate error, bad complaint string */
187 modefile
, strerror(errno
));
190 if (dirlookup(dotname
) == 0) {
191 (void) fprintf(stderr
, gettext(
192 "Root directory is not on tape\n"));
197 itp
= allocinotab(curfile
.ino
, ip
, g_seekpt
);
198 getfile(putdir
, null
);
204 itp
->t_size
= g_seekpt
- itp
->t_seekpt
;
209 * skip over all the directories on the tape
214 while (curfile
.dip
!= NULL
&&
215 ((curfile
.dip
->di_mode
& IFMT
) == IFDIR
||
216 (curfile
.dip
->di_mode
& IFMT
) == IFATTRDIR
)) {
222 * Recursively find names and inumbers of all files in subtree
223 * pname and pass them off to be processed.
226 treescan(char *pname
, ino_t ino
, long (*todo
)())
232 char locname
[MAXCOMPLEXLEN
];
234 itp
= inotablookup(ino
);
237 * Pname is name of a simple file or an unchanged directory.
239 (void) (*todo
)(pname
, ino
, LEAF
);
243 * Pname is a dumped directory name.
245 if ((*todo
)(pname
, ino
, NODE
) == FAIL
)
248 * begin search through the directory
249 * skipping over "." and ".."
251 loclen
= complexcpy(locname
, pname
, MAXCOMPLEXLEN
);
252 locname
[loclen
-1] = '/';
253 rst_seekdir(dirp
, itp
->t_seekpt
, itp
->t_seekpt
);
254 dp
= rst_readdir(dirp
); /* "." */
256 if (dp
!= NULL
&& strcmp(dp
->d_name
, ".") == 0)
257 dp
= rst_readdir(dirp
); /* ".." */
259 (void) fprintf(stderr
,
260 gettext("Warning: `.' missing from directory %s\n"),
262 if (dp
!= NULL
&& strcmp(dp
->d_name
, "..") == 0)
263 dp
= rst_readdir(dirp
); /* first real entry */
265 (void) fprintf(stderr
,
266 gettext("Warning: `..' missing from directory %s\n"),
268 bpt
= rst_telldir(dirp
);
270 * a zero inode signals end of directory
272 while (dp
!= NULL
&& dp
->d_ino
!= 0) {
273 locname
[loclen
] = '\0';
274 if ((loclen
+ dp
->d_namlen
) >= (sizeof (locname
) - 2)) {
275 (void) fprintf(stderr
,
277 "%s%s: ignoring name that exceeds %d char\n"),
278 locname
, dp
->d_name
, MAXCOMPLEXLEN
);
280 /* Always fits by if() condition */
281 (void) strcpy(locname
+ loclen
, dp
->d_name
);
282 /* put a double null on string for lookupname() */
283 locname
[loclen
+dp
->d_namlen
+1] = '\0';
284 treescan(locname
, dp
->d_ino
, todo
);
285 rst_seekdir(dirp
, bpt
, itp
->t_seekpt
);
287 dp
= rst_readdir(dirp
);
288 bpt
= rst_telldir(dirp
);
291 (void) fprintf(stderr
,
292 gettext("corrupted directory: %s.\n"), locname
);
296 * Scan the directory table looking for extended attribute trees.
297 * Recursively find names and inumbers in each tree and pass them
298 * off to be processed. If the always parameter is not set, only
299 * process the attribute tree if the attribute tree parent is to
303 attrscan(int always
, long (*todo
)())
306 struct entry
*ep
, *parent
;
308 char name
[MAXCOMPLEXLEN
];
311 for (itp
= xattrlist
; itp
!= NULL
; itp
= itp
->t_xattr
) {
312 rst_seekdir(dirp
, itp
->t_seekpt
, itp
->t_seekpt
);
313 if ((dp
= rst_readdir(dirp
)) != NULL
&& /* "." */
314 (dp
= rst_readdir(dirp
)) != NULL
&& /* ".." */
315 strcmp(dp
->d_name
, "..") == 0) {
316 if ((parent
= lookupino(dp
->d_ino
)) != NULL
) {
318 (parent
->e_flags
& (NEW
|EXTRACT
)) == 0)
320 len
= complexcpy(name
, myname(parent
),
326 if ((ep
= lookupino(itp
->t_ino
)) == NULL
) {
327 ep
= addentry(name
, itp
->t_ino
,
330 ep
->e_flags
|= XATTRROOT
;
331 treescan(name
, itp
->t_ino
, todo
);
334 (void) fprintf(stderr
,
335 gettext("Warning: orphaned attribute directory\n"));
338 (void) fprintf(stderr
,
339 gettext("Warning: `..' missing from attribute directory\n"));
345 * Search the directory tree rooted at inode ROOTINO
346 * for the path pointed at by n. Note that n must be
347 * modifiable, although it is returned in the same
348 * condition it was given to us in.
358 if (*(cp
= n
) == '/')
362 while (*cp1
!= '/' && *cp1
)
366 ino
= search(ino
, cp
);
380 * search the directory inode ino
381 * looking for entry cp
384 search(ino_t inum
, char *cp
)
390 itp
= inotablookup(inum
);
393 rst_seekdir(dirp
, itp
->t_seekpt
, itp
->t_seekpt
);
396 dp
= rst_readdir(dirp
);
397 if (dp
== NULL
|| dp
->d_ino
== 0)
399 } while (dp
->d_namlen
!= len
|| strncmp(dp
->d_name
, cp
, len
) != 0);
404 * Put the directory entries in the directory file
407 putdir(char *buf
, size_t size
)
409 struct direct cvtbuf
;
411 struct odirect
*eodp
;
416 /*LINTED [buf is char[] in getfile, size % fs_fsize == 0]*/
417 eodp
= (struct odirect
*)&buf
[size
];
418 /*LINTED [buf is char[] in getfile]*/
419 for (odp
= (struct odirect
*)buf
; odp
< eodp
; odp
++)
420 if (odp
->d_ino
!= 0) {
427 /*LINTED [buf is char[] in getfile, loc % 4 == 0]*/
428 dp
= (struct direct
*)(buf
+ loc
);
429 normdirect(byteorder
, dp
);
430 i
= DIRBLKSIZ
- (loc
& (DIRBLKSIZ
- 1));
431 if (dp
->d_reclen
== 0 || (long)dp
->d_reclen
> i
) {
436 if (dp
->d_ino
!= 0) {
444 * These variables are "local" to the following two functions.
446 static char dirbuf
[DIRBLKSIZ
];
447 static int32_t dirloc
= 0;
448 static int32_t prev
= 0;
451 * add a new directory entry to a file.
454 putent(struct direct
*dp
)
456 /* LINTED DIRSIZ will always fit in a ushort_t */
457 dp
->d_reclen
= (ushort_t
)DIRSIZ(dp
);
458 /* LINTED sign extension ok in assert */
459 assert(DIRSIZ(dp
) == (ulong_t
)dp
->d_reclen
);
460 if (dirloc
+ (long)dp
->d_reclen
> DIRBLKSIZ
) {
461 /*LINTED [prev += dp->d_reclen, prev % 4 == 0]*/
462 ((struct direct
*)(dirbuf
+ prev
))->d_reclen
=
464 (void) fwrite(dirbuf
, 1, DIRBLKSIZ
, df
);
466 panic("%s: %s\n", dirfile
, strerror(errno
));
469 bcopy((char *)dp
, dirbuf
+ dirloc
, (size_t)dp
->d_reclen
);
471 dirloc
+= dp
->d_reclen
;
475 * flush out a directory that is finished.
485 /* LINTED prev += dp->d_reclen, prev % 4 == 0 */
486 ((struct direct
*)(dirbuf
+ prev
))->d_reclen
= DIRBLKSIZ
- prev
;
487 (void) fwrite(dirbuf
, (size_t)dirloc
, 1, df
);
489 panic("%s: %s\n", dirfile
, strerror(errno
));
490 g_seekpt
= ftello64(df
);
495 dcvt(struct odirect
*odp
, struct direct
*ndp
)
498 (void) bzero((char *)ndp
, sizeof (*ndp
));
499 ndp
->d_ino
= odp
->d_ino
;
500 /* Note that odp->d_name may not be null-terminated */
501 /* LINTED assertion always true */
502 assert(sizeof (ndp
->d_name
) > sizeof (odp
->d_name
));
503 (void) strncpy(ndp
->d_name
, odp
->d_name
, sizeof (odp
->d_name
));
504 ndp
->d_name
[sizeof (odp
->d_name
)] = '\0';
505 /* LINTED: strlen will fit into d_namlen */
506 ndp
->d_namlen
= strlen(ndp
->d_name
);
508 /* LINTED sign extension ok in assert */
509 assert(DIRSIZ(ndp
) == (ulong_t
)ndp
->d_reclen
);
510 /* LINTED DIRSIZ always fits in ushort_t */
511 ndp
->d_reclen
= (ushort_t
)DIRSIZ(ndp
);
515 * Initialize the directory file
518 rst_initdirfile(char *name
)
523 if ((fd
= open(name
, O_RDONLY
| O_LARGEFILE
)) == -1)
524 return ((RST_DIR
*)0);
525 if ((dp
= (RST_DIR
*)malloc(sizeof (*dp
))) == NULL
) {
527 return ((RST_DIR
*)0);
536 * Simulate the opening of a directory
539 rst_opendir(char *name
)
544 if ((ino
= dirlookup(name
)) > 0 &&
545 (itp
= inotablookup(ino
)) != NULL
) {
546 rst_seekdir(dirp
, itp
->t_seekpt
, itp
->t_seekpt
);
550 return ((RST_DIR
*)0);
554 * Releases the hidden state created by rst_opendir().
555 * Specifically, the dirp it provided to the caller is malloc'd.
558 rst_closedir(RST_DIR
*cdirp
)
560 if ((cdirp
!= NULL
) && (--(cdirp
->dd_refcnt
) < 1))
565 * return a pointer into a directory
568 rst_telldir(RST_DIR
*tdirp
)
570 offset_t pos
= llseek(tdirp
->dd_fd
, (offset_t
)0, SEEK_CUR
);
572 if (pos
== (offset_t
)-1) {
573 perror("Could not determine position in directory file");
577 return ((pos
- tdirp
->dd_size
) + tdirp
->dd_loc
);
581 * Seek to an entry in a directory.
582 * Only values returned by ``rst_telldir'' should be passed to rst_seekdir.
583 * This routine handles many directories in a single file.
584 * It takes the base of the directory in the file, plus
585 * the desired seek offset into it.
588 rst_seekdir(RST_DIR
*sdirp
, offset_t loc
, offset_t base
)
591 if (loc
== rst_telldir(sdirp
))
595 (void) fprintf(stderr
,
596 gettext("bad seek pointer to rst_seekdir %d\n"), loc
);
597 (void) llseek(sdirp
->dd_fd
, base
+ (loc
& ~(DIRBLKSIZ
- 1)), 0);
598 sdirp
->dd_loc
= loc
& (DIRBLKSIZ
- 1);
599 if (sdirp
->dd_loc
!= 0)
600 sdirp
->dd_size
= read(sdirp
->dd_fd
, sdirp
->dd_buf
, DIRBLKSIZ
);
604 * get next entry in a directory.
607 rst_readdir(RST_DIR
*rdirp
)
612 if (rdirp
->dd_loc
== 0) {
613 rdirp
->dd_size
= read(rdirp
->dd_fd
, rdirp
->dd_buf
,
615 if (rdirp
->dd_size
<= 0) {
617 gettext("error reading directory\n"));
618 return ((struct direct
*)0);
621 if (rdirp
->dd_loc
>= rdirp
->dd_size
) {
625 /*LINTED [rvalue will be aligned on int boundary]*/
626 dp
= (struct direct
*)(rdirp
->dd_buf
+ rdirp
->dd_loc
);
627 if (dp
->d_reclen
== 0 ||
628 (long)dp
->d_reclen
> (DIRBLKSIZ
+ 1 - rdirp
->dd_loc
)) {
630 gettext("corrupted directory: bad reclen %d\n"),
632 return ((struct direct
*)0);
634 rdirp
->dd_loc
+= dp
->d_reclen
;
635 if (dp
->d_ino
== 0 && strcmp(dp
->d_name
, "/") != 0)
637 if ((ino_t
)(dp
->d_ino
) >= maxino
) {
639 gettext("corrupted directory: bad inum %lu\n"),
648 * Set the mode, owner, and times for all new or changed directories
659 char *cp
, *metadata
= NULL
;
663 static int complained_chown
= 0;
664 static int complained_chmod
= 0;
667 vprintf(stdout
, gettext("Set directory mode, owner, and times.\n"));
668 /* XXX if modefile[0] == '#', shouldn't we just bail here? */
669 /* XXX why isn't it set already? */
671 smf
= fopen64(modefile
, "r");
674 (void) fprintf(stderr
,
675 gettext("cannot open mode file %s\n"), modefile
);
676 (void) fprintf(stderr
,
677 gettext("directory mode, owner, and times not set\n"));
682 (void) fread((char *)&node
, 1, sizeof (node
), smf
);
685 ep
= lookupino(node
.ino
);
686 if (command
== 'i' || command
== 'x') {
688 skipmetadata(smf
, node
.metasize
);
691 if (ep
->e_flags
& EXISTED
) {
694 "Directories already exist, set modes anyway"))
701 /* LINTED: result fits into short */
703 skipmetadata(smf
, node
.metasize
);
707 if (node
.ino
== ROOTINO
&&
708 reply(gettext("set owner/mode for '.'")) == FAIL
) {
709 skipmetadata(smf
, node
.metasize
);
714 panic(gettext("cannot find directory inode %d\n"),
716 skipmetadata(smf
, node
.metasize
);
720 resolve(myname(ep
), &dfd
, &cp
);
721 if (dfd
!= AT_FDCWD
) {
722 if (fchdir(dfd
) < 0) {
724 (void) fprintf(stderr
,
725 gettext("Can not set attribute context: %s\n"),
731 if (chmod(cp
, node
.mode
) < 0 && !complained_chmod
) {
733 (void) fprintf(stderr
,
734 gettext("Can not set directory permissions: %s\n"),
736 complained_chmod
= 1;
738 if (node
.metasize
!= 0) {
739 if (node
.metasize
> metasize
)
740 metadata
= realloc(metadata
,
741 metasize
= node
.metasize
);
742 if (metadata
== NULL
) {
743 (void) fprintf(stderr
,
744 gettext("Cannot malloc metadata\n"));
747 (void) fread(metadata
, 1, node
.metasize
, smf
);
748 metaproc(cp
, metadata
, node
.metasize
);
753 * Since the ACLs must be set before fixing the ownership,
754 * chown should be called only after metaproc
756 if (chown(cp
, node
.uid
, node
.gid
) < 0 && !complained_chown
) {
758 (void) fprintf(stderr
,
759 gettext("Can not set directory ownership: %s\n"),
761 complained_chown
= 1;
763 utime(cp
, (struct utimbuf
*)node
.timep
);
764 /* LINTED: result fits into short */
766 if (dfd
!= AT_FDCWD
) {
772 panic(gettext("error setting directory modes\n"));
773 if (metadata
!= NULL
)
774 (void) free(metadata
);
779 skipmetadata(FILE *f
, size_t size
)
781 /* XXX should we bail if this doesn't work? */
782 /* LINTED unsigned -> signed conversion ok here */
783 (void) fseeko(f
, (off_t
)size
, SEEK_CUR
);
787 * Generate a literal copy of a directory.
790 genliteraldir(char *name
, ino_t ino
)
798 itp
= inotablookup(ino
);
800 (void) fprintf(stderr
,
801 gettext("Cannot find directory inode %d named %s\n"),
805 if ((ofile
= creat(name
, 0666)) < 0) {
806 (void) fprintf(stderr
, "%s: ", name
);
807 (void) fflush(stderr
);
808 perror(gettext("cannot create file"));
811 rst_seekdir(dirp
, itp
->t_seekpt
, itp
->t_seekpt
);
812 dp
= dup(dirp
->dd_fd
);
814 perror(gettext("dup(2) failed"));
819 for (i
= itp
->t_size
; i
!= 0; i
-= size
) {
820 /* LINTED cast is safe due to comparison */
821 size
= i
< BUFSIZ
? (size_t)i
: BUFSIZ
;
822 /* XXX instead of done(), clean up and return FAIL? */
823 if (read(dp
, buf
, size
) == -1) {
824 (void) fprintf(stderr
, gettext(
825 "read error extracting inode %d, name %s\n"),
826 curfile
.ino
, curfile
.name
);
830 if (write(ofile
, buf
, size
) == -1) {
831 (void) fprintf(stderr
, gettext(
832 "write error extracting inode %d, name %s\n"),
833 curfile
.ino
, curfile
.name
);
844 * Determine the type of an inode
851 itp
= inotablookup(ino
);
858 * Allocate and initialize a directory inode entry.
859 * If requested, save its pertinent mode, owner, and time info.
861 static struct inotab
*
862 allocinotab(ino_t ino
, struct dinode
*dip
, off64_t seekpt
)
866 itp
= (struct inotab
*)calloc(1, sizeof (*itp
));
868 (void) fprintf(stderr
,
869 gettext("no memory for directory table\n"));
872 itp
->t_next
= inotab
[INOHASH(ino
)];
873 inotab
[INOHASH(ino
)] = itp
;
875 itp
->t_seekpt
= seekpt
;
876 if ((dip
->di_mode
& IFMT
) == IFATTRDIR
) {
877 itp
->t_xattr
= xattrlist
;
883 node
.timep
[0] = dip
->di_atime
;
884 node
.timep
[1] = dip
->di_mtime
;
885 node
.mode
= dip
->di_mode
;
887 dip
->di_suid
== UID_LONG
? dip
->di_uid
: (uid_t
)dip
->di_suid
;
889 dip
->di_sgid
== GID_LONG
? dip
->di_gid
: (gid_t
)dip
->di_sgid
;
899 (void) fprintf(stderr
, gettext(
900 "Inconsistency detected: modefile pointer is NULL\n"));
903 metaget(&metadata
, &(node
.metasize
));
904 (void) fwrite((char *)&node
, 1, sizeof (node
), mf
);
905 if (node
.metasize
!= 0)
906 (void) fwrite(metadata
, 1, node
.metasize
, mf
);
908 panic("%s: %s\n", modefile
, strerror(errno
));
912 * Look up an inode in the table of directories
914 static struct inotab
*
915 inotablookup(ino_t ino
)
919 for (itp
= inotab
[INOHASH(ino
)]; itp
!= NULL
; itp
= itp
->t_next
)
920 if (itp
->t_ino
== ino
)
922 return ((struct inotab
*)0);
931 closemt(ALLOW_OFFLINE
); /* don't force offline on exit */
932 if (modefile
[0] != '#')
933 (void) unlink(modefile
);
934 if (dirfile
[0] != '#')
935 (void) unlink(dirfile
);