xref: /illumos-gate/usr/src/cmd/backup/restore/dirs.c (revision 7c478bd9)
1 /*
2  * Copyright (c) 1996-2001 by Sun Microsystems, Inc.
3  * All rights reserved.
4  */
5 
6 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
7 /*	  All Rights Reserved	*/
8 
9 /*
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.
13  */
14 
15 #pragma ident	"%Z%%M%	%I%	%E% SMI"
16 
17 #include "restore.h"
18 #include <byteorder.h>
19 #include <stdlib.h>
20 #include <unistd.h>
21 #include <utime.h>
22 
23 /*
24  * Symbol table of directories read from tape.
25  */
26 #define	HASHSIZE	1000
27 #define	INOHASH(val) (val % HASHSIZE)
28 struct inotab {
29 	struct inotab *t_next;
30 	ino_t	t_ino;
31 	offset_t t_seekpt;
32 	offset_t t_size;
33 	struct inotab *t_xattr;
34 };
35 static struct inotab *inotab[HASHSIZE];
36 static struct inotab *xattrlist = NULL;
37 
38 /*
39  * Information retained about directories.
40  */
41 static struct modeinfo {
42 	ino_t	ino;
43 	time_t	timep[2];
44 	mode_t	mode;
45 	uid_t	uid;
46 	gid_t	gid;
47 	size_t	metasize;
48 } node;
49 
50 /*
51  * Global variables for this file.
52  */
53 static off64_t	g_seekpt;		/* some people have a local seekpt */
54 static FILE	*df, *mf;
55 static char	dirfile[MAXPATHLEN] = "#";	/* No file */
56 static char	modefile[MAXPATHLEN] = "#";	/* No file */
57 
58 static RST_DIR	*dirp;
59 
60 #define	INIT_TEMPFILE(name, type) \
61 	if (name[0] == '#') { \
62 		if (tmpdir == (char *)NULL) /* can't happen; be paranoid */ \
63 			tmpdir = "/tmp"; \
64 		(void) snprintf(name, sizeof (name), \
65 		    "%s/rst" type "%ld.XXXXXX", tmpdir, dumpdate); \
66 		(void) mktemp(name); \
67 	}
68 
69 #define	INIT_DIRFILE()	INIT_TEMPFILE(dirfile, "dir")
70 #define	INIT_MODEFILE()	INIT_TEMPFILE(modefile, "mode")
71 
72 /*
73  * Format of old style directories.
74  */
75 #define	ODIRSIZ 14
76 struct odirect {
77 	ushort_t d_ino;
78 	char	d_name[ODIRSIZ];
79 };
80 
81 #ifdef __STDC__
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);
94 #else
95 static ino_t search();
96 static void putdir();
97 static void putent();
98 static void skipmetadata();
99 static void flushent();
100 static void dcvt();
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();
107 #endif
108 
109 /*
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.
114  */
115 void
116 extractdirs(genmode)
117 	int genmode;
118 {
119 	int ts;
120 	struct dinode *ip;
121 	int saverr;
122 	struct inotab *itp;
123 	struct direct nulldir;
124 	static char dotname[] = "."; /* dirlookup/psearch writes to its arg */
125 
126 	vprintf(stdout, gettext("Extract directories from tape\n"));
127 	INIT_DIRFILE();
128 	if ((df = safe_fopen(dirfile, "w", 0600)) == (FILE *)NULL) {
129 		saverr = errno;
130 		(void) fprintf(stderr,
131 		    gettext("%s: %s - cannot create directory temporary\n"),
132 			progname, dirfile);
133 		errno = saverr;
134 		perror("fopen");
135 		done(1);
136 	}
137 	if (genmode != 0) {
138 		INIT_MODEFILE();
139 		if ((mf = safe_fopen(modefile, "w", 0600)) == (FILE *)NULL) {
140 			saverr = errno;
141 			(void) fprintf(stderr,
142 			    gettext("%s: %s - cannot create modefile \n"),
143 				progname, modefile);
144 			errno = saverr;
145 			perror("fopen");
146 			done(1);
147 		}
148 	}
149 	nulldir.d_ino = 0;
150 	nulldir.d_namlen = 1;
151 	(void) strcpy(nulldir.d_name, "/");
152 	/* LINTED DIRSIZ will always fit into a ushort_t */
153 	nulldir.d_reclen = (ushort_t)DIRSIZ(&nulldir);
154 	/* LINTED sign extension ok in assert */
155 	assert(DIRSIZ(&nulldir) == (ulong_t)nulldir.d_reclen);
156 	for (;;) {
157 		curfile.name = gettext("<directory file - name unknown>");
158 		curfile.action = USING;
159 		ip = curfile.dip;
160 		ts = curfile.ts;
161 		if (ts != TS_END && ts != TS_INODE) {
162 			getfile(null, null);
163 			continue;
164 		}
165 		if (ts == TS_INODE && ip == NULL) {
166 			(void) fprintf(stderr, gettext(
167 "%s: extractdirs: Failed internal consistency check, curfile.dip is NULL\n"),
168 			    progname);
169 			done(1);
170 		}
171 		if ((ts == TS_INODE && (ip->di_mode & IFMT) != IFDIR &&
172 		    (ip->di_mode & IFMT) != IFATTRDIR) ||
173 		    (ts == TS_END)) {
174 			(void) fflush(df);
175 			/* XXX Legitimate error, bad complaint string */
176 			if (ferror(df))
177 				panic("%s: %s\n", dirfile, strerror(errno));
178 			(void) fclose(df);
179 			rst_closedir(dirp);
180 			dirp = rst_initdirfile(dirfile);
181 			if (dirp == NULL)
182 				perror("initdirfile");
183 			if (mf != NULL) {
184 				(void) fflush(mf);
185 				/* XXX Legitimate error, bad complaint string */
186 				if (ferror(mf))
187 					panic("%s: %s\n",
188 					    modefile, strerror(errno));
189 				(void) fclose(mf);
190 			}
191 			if (dirlookup(dotname) == 0) {
192 				(void) fprintf(stderr, gettext(
193 				    "Root directory is not on tape\n"));
194 				done(1);
195 			}
196 			return;
197 		}
198 		itp = allocinotab(curfile.ino, ip, g_seekpt);
199 		getfile(putdir, null);
200 		if (mf != NULL)
201 			nodeflush();
202 
203 		putent(&nulldir);
204 		flushent();
205 		itp->t_size = g_seekpt - itp->t_seekpt;
206 	}
207 }
208 
209 /*
210  * skip over all the directories on the tape
211  */
212 void
213 skipdirs()
214 {
215 	while (curfile.dip != NULL &&
216 		((curfile.dip->di_mode & IFMT) == IFDIR ||
217 		(curfile.dip->di_mode & IFMT) == IFATTRDIR)) {
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)();
231 {
232 	struct inotab *itp;
233 	struct direct *dp;
234 	uint_t loclen;
235 	offset_t bpt;
236 	char locname[MAXCOMPLEXLEN];
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 	loclen = complexcpy(locname, pname, MAXCOMPLEXLEN);
256 	locname[loclen-1] = '/';
257 	rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
258 	dp = rst_readdir(dirp); /* "." */
259 
260 	if (dp != NULL && strcmp(dp->d_name, ".") == 0)
261 		dp = rst_readdir(dirp); /* ".." */
262 	else
263 		(void) fprintf(stderr,
264 		    gettext("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 		(void) fprintf(stderr,
270 		    gettext("Warning: `..' missing from directory %s\n"),
271 			pname);
272 	bpt = rst_telldir(dirp);
273 	/*
274 	 * a zero inode signals end of directory
275 	 */
276 	while (dp != NULL && dp->d_ino != 0) {
277 		locname[loclen] = '\0';
278 		if ((loclen + dp->d_namlen) >= (sizeof (locname) - 2)) {
279 			(void) fprintf(stderr,
280 			    gettext(
281 				"%s%s: ignoring name that exceeds %d char\n"),
282 			    locname, dp->d_name, MAXCOMPLEXLEN);
283 		} else {
284 			/* Always fits by if() condition */
285 			(void) strcpy(locname + loclen, dp->d_name);
286 			/* put a double null on string for lookupname() */
287 			locname[loclen+dp->d_namlen+1] = '\0';
288 			treescan(locname, dp->d_ino, todo);
289 			rst_seekdir(dirp, bpt, itp->t_seekpt);
290 		}
291 		dp = rst_readdir(dirp);
292 		bpt = rst_telldir(dirp);
293 	}
294 	if (dp == NULL)
295 		(void) fprintf(stderr,
296 			gettext("corrupted directory: %s.\n"), locname);
297 }
298 
299 /*
300  * Scan the directory table looking for extended attribute trees.
301  * Recursively find names and inumbers in each tree and pass them
302  * off to be processed.  If the always parameter is not set, only
303  * process the attribute tree if the attribute tree parent is to
304  * be extracted.
305  */
306 void
307 attrscan(always, todo)
308 	int always;
309 	long (*todo)();
310 {
311 	struct inotab *itp;
312 	struct entry *ep, *parent;
313 	struct direct *dp;
314 	char name[MAXCOMPLEXLEN];
315 	int len;
316 
317 	for (itp = xattrlist; itp != NULL; itp = itp->t_xattr) {
318 		rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
319 		if ((dp = rst_readdir(dirp)) != NULL &&	/* "." */
320 		    (dp = rst_readdir(dirp)) != NULL &&	/* ".." */
321 		    strcmp(dp->d_name, "..") == 0) {
322 			if ((parent = lookupino(dp->d_ino)) != NULL) {
323 				if (!always &&
324 				    (parent->e_flags & (NEW|EXTRACT)) == 0)
325 					continue;
326 				len = complexcpy(name, myname(parent),
327 							MAXCOMPLEXLEN - 3);
328 				name[len] = '.';
329 				name[len+1] = '\0';
330 				name[len+2] = '\0';
331 				inattrspace = 1;
332 				if ((ep = lookupino(itp->t_ino)) == NULL) {
333 					ep = addentry(name, itp->t_ino,
334 								NODE|ROOT);
335 				}
336 				ep->e_flags |= XATTRROOT;
337 				treescan(name, itp->t_ino, todo);
338 				inattrspace = 0;
339 			} else {
340 				(void) fprintf(stderr,
341 			gettext("Warning: orphaned attribute directory\n"));
342 			}
343 		} else {
344 			(void) fprintf(stderr,
345 	    gettext("Warning: `..' missing from attribute directory\n"));
346 		}
347 	}
348 }
349 
350 /*
351  * Search the directory tree rooted at inode ROOTINO
352  * for the path pointed at by n.  Note that n must be
353  * modifiable, although it is returned in the same
354  * condition it was given to us in.
355  */
356 ino_t
357 psearch(n)
358 	char	*n;
359 {
360 	char *cp, *cp1;
361 	ino_t ino;
362 	char c;
363 
364 	ino = ROOTINO;
365 	if (*(cp = n) == '/')
366 		cp++;
367 next:
368 	cp1 = cp + 1;
369 	while (*cp1 != '/' && *cp1)
370 		cp1++;
371 	c = *cp1;
372 	*cp1 = 0;
373 	ino = search(ino, cp);
374 	if (ino == 0) {
375 		*cp1 = c;
376 		return (0);
377 	}
378 	*cp1 = c;
379 	if (c == '/') {
380 		cp = cp1+1;
381 		goto next;
382 	}
383 	return (ino);
384 }
385 
386 /*
387  * search the directory inode ino
388  * looking for entry cp
389  */
390 static ino_t
391 search(inum, cp)
392 	ino_t	inum;
393 	char	*cp;
394 {
395 	struct direct *dp;
396 	struct inotab *itp;
397 	uint_t len;
398 
399 	itp = inotablookup(inum);
400 	if (itp == NULL)
401 		return (0);
402 	rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
403 	len = strlen(cp);
404 	do {
405 		dp = rst_readdir(dirp);
406 		if (dp == NULL || dp->d_ino == 0)
407 			return (0);
408 	} while (dp->d_namlen != len || strncmp(dp->d_name, cp, len) != 0);
409 	return (dp->d_ino);
410 }
411 
412 /*
413  * Put the directory entries in the directory file
414  */
415 static void
416 putdir(buf, size)
417 	char *buf;
418 	size_t size;
419 {
420 	struct direct cvtbuf;
421 	struct odirect *odp;
422 	struct odirect *eodp;
423 	struct direct *dp;
424 	size_t loc, i;
425 
426 	if (cvtflag) {
427 		/*LINTED [buf is char[] in getfile, size % fs_fsize == 0]*/
428 		eodp = (struct odirect *)&buf[size];
429 		/*LINTED [buf is char[] in getfile]*/
430 		for (odp = (struct odirect *)buf; odp < eodp; odp++)
431 			if (odp->d_ino != 0) {
432 				dcvt(odp, &cvtbuf);
433 				putent(&cvtbuf);
434 			}
435 	} else {
436 		loc = 0;
437 		while (loc < size) {
438 			/*LINTED [buf is char[] in getfile, loc % 4 == 0]*/
439 			dp = (struct direct *)(buf + loc);
440 			normdirect(byteorder, dp);
441 			i = DIRBLKSIZ - (loc & (DIRBLKSIZ - 1));
442 			if (dp->d_reclen == 0 || (long)dp->d_reclen > i) {
443 				loc += i;
444 				continue;
445 			}
446 			loc += dp->d_reclen;
447 			if (dp->d_ino != 0) {
448 				putent(dp);
449 			}
450 		}
451 	}
452 }
453 
454 /*
455  * These variables are "local" to the following two functions.
456  */
457 static char dirbuf[DIRBLKSIZ];
458 static int32_t dirloc = 0;
459 static int32_t prev = 0;
460 
461 /*
462  * add a new directory entry to a file.
463  */
464 static void
465 putent(dp)
466 	struct direct *dp;
467 {
468 	/* LINTED DIRSIZ will always fit in a ushort_t */
469 	dp->d_reclen = (ushort_t)DIRSIZ(dp);
470 	/* LINTED sign extension ok in assert */
471 	assert(DIRSIZ(dp) == (ulong_t)dp->d_reclen);
472 	if (dirloc + (long)dp->d_reclen > DIRBLKSIZ) {
473 		/*LINTED [prev += dp->d_reclen, prev % 4 == 0]*/
474 		((struct direct *)(dirbuf + prev))->d_reclen =
475 		    DIRBLKSIZ - prev;
476 		(void) fwrite(dirbuf, 1, DIRBLKSIZ, df);
477 		if (ferror(df))
478 			panic("%s: %s\n", dirfile, strerror(errno));
479 		dirloc = 0;
480 	}
481 	bcopy((char *)dp, dirbuf + dirloc, (size_t)dp->d_reclen);
482 	prev = dirloc;
483 	dirloc += dp->d_reclen;
484 }
485 
486 /*
487  * flush out a directory that is finished.
488  */
489 static void
490 #ifdef __STDC__
491 flushent(void)
492 #else
493 flushent()
494 #endif
495 {
496 
497 	/* LINTED prev += dp->d_reclen, prev % 4 == 0 */
498 	((struct direct *)(dirbuf + prev))->d_reclen = DIRBLKSIZ - prev;
499 	(void) fwrite(dirbuf, (size_t)dirloc, 1, df);
500 	if (ferror(df))
501 		panic("%s: %s\n", dirfile, strerror(errno));
502 	g_seekpt = ftello64(df);
503 	dirloc = 0;
504 }
505 
506 static void
507 dcvt(odp, ndp)
508 	struct odirect *odp;
509 	struct direct *ndp;
510 {
511 
512 	(void) bzero((char *)ndp, sizeof (*ndp));
513 	ndp->d_ino =  odp->d_ino;
514 	/* Note that odp->d_name may not be null-terminated */
515 	/* LINTED assertion always true */
516 	assert(sizeof (ndp->d_name) > sizeof (odp->d_name));
517 	(void) strncpy(ndp->d_name, odp->d_name, sizeof (odp->d_name));
518 	ndp->d_name[sizeof (odp->d_name)] = '\0';
519 	/* LINTED: strlen will fit into d_namlen */
520 	ndp->d_namlen = strlen(ndp->d_name);
521 
522 	/* LINTED sign extension ok in assert */
523 	assert(DIRSIZ(ndp) == (ulong_t)ndp->d_reclen);
524 	/* LINTED DIRSIZ always fits in ushort_t */
525 	ndp->d_reclen = (ushort_t)DIRSIZ(ndp);
526 }
527 
528 /*
529  * Initialize the directory file
530  */
531 static RST_DIR *
532 rst_initdirfile(name)
533 	char *name;
534 {
535 	RST_DIR *dp;
536 	int fd;
537 
538 	if ((fd = open(name, O_RDONLY | O_LARGEFILE)) == -1)
539 		return ((RST_DIR *)0);
540 	if ((dp = (RST_DIR *)malloc(sizeof (*dp))) == NULL) {
541 		(void) close(fd);
542 		return ((RST_DIR *)0);
543 	}
544 	dp->dd_fd = fd;
545 	dp->dd_loc = 0;
546 	dp->dd_refcnt = 1;
547 	return (dp);
548 }
549 
550 /*
551  * Simulate the opening of a directory
552  */
553 RST_DIR *
554 rst_opendir(name)
555 	char *name;
556 {
557 	struct inotab *itp;
558 	ino_t ino;
559 
560 	if ((ino = dirlookup(name)) > 0 &&
561 	    (itp = inotablookup(ino)) != NULL) {
562 		rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
563 		dirp->dd_refcnt++;
564 		return (dirp);
565 	}
566 	return ((RST_DIR *)0);
567 }
568 
569 /*
570  * Releases the hidden state created by rst_opendir().
571  * Specifically, the dirp it provided to the caller is malloc'd.
572  */
573 void
574 rst_closedir(cdirp)
575 	RST_DIR *cdirp;
576 {
577 	if ((cdirp != NULL) && (--(cdirp->dd_refcnt) < 1))
578 		free(cdirp);
579 }
580 
581 /*
582  * return a pointer into a directory
583  */
584 static offset_t
585 rst_telldir(tdirp)
586 	RST_DIR *tdirp;
587 {
588 	offset_t pos = llseek(tdirp->dd_fd, (offset_t)0, SEEK_CUR);
589 
590 	if (pos == (offset_t)-1) {
591 		perror("Could not determine position in directory file");
592 		done(1);
593 	}
594 
595 	return ((pos - tdirp->dd_size) + tdirp->dd_loc);
596 }
597 
598 /*
599  * Seek to an entry in a directory.
600  * Only values returned by ``rst_telldir'' should be passed to rst_seekdir.
601  * This routine handles many directories in a single file.
602  * It takes the base of the directory in the file, plus
603  * the desired seek offset into it.
604  */
605 static void
606 rst_seekdir(sdirp, loc, base)
607 	RST_DIR *sdirp;
608 	offset_t loc, base;
609 {
610 
611 	if (loc == rst_telldir(sdirp))
612 		return;
613 	loc -= base;
614 	if (loc < 0)
615 		(void) fprintf(stderr,
616 			gettext("bad seek pointer to rst_seekdir %d\n"), loc);
617 	(void) llseek(sdirp->dd_fd, base + (loc & ~(DIRBLKSIZ - 1)), 0);
618 	sdirp->dd_loc = loc & (DIRBLKSIZ - 1);
619 	if (sdirp->dd_loc != 0)
620 		sdirp->dd_size = read(sdirp->dd_fd, sdirp->dd_buf, DIRBLKSIZ);
621 }
622 
623 /*
624  * get next entry in a directory.
625  */
626 struct direct *
627 rst_readdir(rdirp)
628 	RST_DIR *rdirp;
629 {
630 	struct direct *dp;
631 
632 	for (;;) {
633 		if (rdirp->dd_loc == 0) {
634 			rdirp->dd_size = read(rdirp->dd_fd, rdirp->dd_buf,
635 			    DIRBLKSIZ);
636 			if (rdirp->dd_size <= 0) {
637 				dprintf(stderr,
638 					gettext("error reading directory\n"));
639 				return ((struct direct *)0);
640 			}
641 		}
642 		if (rdirp->dd_loc >= rdirp->dd_size) {
643 			rdirp->dd_loc = 0;
644 			continue;
645 		}
646 		/*LINTED [rvalue will be aligned on int boundary]*/
647 		dp = (struct direct *)(rdirp->dd_buf + rdirp->dd_loc);
648 		if (dp->d_reclen == 0 ||
649 		    (long)dp->d_reclen > (DIRBLKSIZ + 1 - rdirp->dd_loc)) {
650 			dprintf(stderr,
651 			    gettext("corrupted directory: bad reclen %d\n"),
652 				dp->d_reclen);
653 			return ((struct direct *)0);
654 		}
655 		rdirp->dd_loc += dp->d_reclen;
656 		if (dp->d_ino == 0 && strcmp(dp->d_name, "/") != 0)
657 			continue;
658 		if ((ino_t)(dp->d_ino) >= maxino) {
659 			dprintf(stderr,
660 				gettext("corrupted directory: bad inum %lu\n"),
661 				dp->d_ino);
662 			continue;
663 		}
664 		return (dp);
665 	}
666 }
667 
668 /*
669  * Set the mode, owner, and times for all new or changed directories
670  */
671 void
672 #ifdef __STDC__
673 setdirmodes(void)
674 #else
675 setdirmodes()
676 #endif
677 {
678 	FILE *smf;
679 	struct entry *ep;
680 	char *cp, *metadata = NULL;
681 	size_t metasize = 0;
682 	int override = -1;
683 	int saverr;
684 	static int complained_chown = 0;
685 	static int complained_chmod = 0;
686 	int dfd;
687 
688 	vprintf(stdout, gettext("Set directory mode, owner, and times.\n"));
689 	/* XXX if modefile[0] == '#', shouldn't we just bail here? */
690 	/* XXX why isn't it set already? */
691 	INIT_MODEFILE();
692 	smf = fopen64(modefile, "r");
693 	if (smf == NULL) {
694 		perror("fopen");
695 		(void) fprintf(stderr,
696 			gettext("cannot open mode file %s\n"), modefile);
697 		(void) fprintf(stderr,
698 			gettext("directory mode, owner, and times not set\n"));
699 		return;
700 	}
701 	clearerr(smf);
702 	for (;;) {
703 		(void) fread((char *)&node, 1, sizeof (node), smf);
704 		if (feof(smf))
705 			break;
706 		ep = lookupino(node.ino);
707 		if (command == 'i' || command == 'x') {
708 			if (ep == NIL) {
709 				skipmetadata(smf, node.metasize);
710 				continue;
711 			}
712 			if (ep->e_flags & EXISTED) {
713 				if (override < 0) {
714 					if (reply(gettext(
715 				"Directories already exist, set modes anyway"))
716 					    == FAIL)
717 						override = 0;
718 					else
719 						override = 1;
720 				}
721 				if (override == 0) {
722 					/* LINTED: result fits into short */
723 					ep->e_flags &= ~NEW;
724 					skipmetadata(smf, node.metasize);
725 					continue;
726 				}
727 			}
728 			if (node.ino == ROOTINO &&
729 			    reply(gettext("set owner/mode for '.'")) == FAIL) {
730 				skipmetadata(smf, node.metasize);
731 				continue;
732 			}
733 		}
734 		if (ep == NIL) {
735 			panic(gettext("cannot find directory inode %d\n"),
736 				node.ino);
737 			skipmetadata(smf, node.metasize);
738 			continue;
739 		}
740 		cp = myname(ep);
741 		resolve(myname(ep), &dfd, &cp);
742 		if (dfd != AT_FDCWD) {
743 			if (fchdir(dfd) < 0) {
744 				saverr = errno;
745 				(void) fprintf(stderr,
746 			    gettext("Can not set attribute context: %s\n"),
747 					strerror(saverr));
748 				(void) close(dfd);
749 				continue;
750 			}
751 		}
752 		if (chmod(cp, node.mode) < 0 && !complained_chmod) {
753 			saverr = errno;
754 			(void) fprintf(stderr,
755 			gettext("Can not set directory permissions: %s\n"),
756 				strerror(saverr));
757 			complained_chmod = 1;
758 		}
759 		if (node.metasize != 0) {
760 			if (node.metasize > metasize)
761 				metadata = realloc(metadata,
762 				    metasize = node.metasize);
763 			if (metadata == NULL) {
764 				(void) fprintf(stderr,
765 					gettext("Cannot malloc metadata\n"));
766 				done(1);
767 			}
768 			(void) fread(metadata, 1, node.metasize, smf);
769 			metaproc(cp, metadata, node.metasize);
770 		}
771 
772 		/*
773 		 * BUG 4302943
774 		 * Since the ACLs must be set before fixing the ownership,
775 		 * chown should be called only after metaproc
776 		 */
777 		if (chown(cp, node.uid, node.gid) < 0 && !complained_chown) {
778 			saverr = errno;
779 			(void) fprintf(stderr,
780 			    gettext("Can not set directory ownership: %s\n"),
781 			    strerror(saverr));
782 			complained_chown = 1;
783 		}
784 		utime(cp, (struct utimbuf *)node.timep);
785 		/* LINTED: result fits into short */
786 		ep->e_flags &= ~NEW;
787 		if (dfd != AT_FDCWD) {
788 			fchdir(savepwd);
789 			(void) close(dfd);
790 		}
791 	}
792 	if (ferror(smf))
793 		panic(gettext("error setting directory modes\n"));
794 	if (metadata != NULL)
795 		(void) free(metadata);
796 	(void) fclose(smf);
797 }
798 
799 void
800 skipmetadata(f, size)
801 	FILE *f;
802 	size_t size;
803 {
804 	/* XXX should we bail if this doesn't work? */
805 	/* LINTED unsigned -> signed conversion ok here */
806 	(void) fseeko(f, (off_t)size, SEEK_CUR);
807 }
808 
809 /*
810  * Generate a literal copy of a directory.
811  */
812 genliteraldir(name, ino)
813 	char *name;
814 	ino_t ino;
815 {
816 	struct inotab *itp;
817 	int ofile, dp;
818 	off64_t i;
819 	size_t size;
820 	char buf[BUFSIZ];
821 
822 	itp = inotablookup(ino);
823 	if (itp == NULL) {
824 		(void) fprintf(stderr,
825 		    gettext("Cannot find directory inode %d named %s\n"),
826 		    ino, name);
827 		return (FAIL);
828 	}
829 	if ((ofile = creat(name, 0666)) < 0) {
830 		(void) fprintf(stderr, "%s: ", name);
831 		(void) fflush(stderr);
832 		perror(gettext("cannot create file"));
833 		return (FAIL);
834 	}
835 	rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
836 	dp = dup(dirp->dd_fd);
837 	if (dp < 0) {
838 		perror(gettext("dup(2) failed"));
839 		(void) close(ofile);
840 		(void) unlink(name);
841 		return (FAIL);
842 	}
843 	for (i = itp->t_size; i != 0; i -= size) {
844 		/* LINTED cast is safe due to comparison */
845 		size = i < BUFSIZ ? (size_t)i : BUFSIZ;
846 		/* XXX instead of done(), clean up and return FAIL? */
847 		if (read(dp, buf, size) == -1) {
848 			(void) fprintf(stderr, gettext(
849 				"read error extracting inode %d, name %s\n"),
850 				curfile.ino, curfile.name);
851 			perror("read");
852 			done(1);
853 		}
854 		if (write(ofile, buf, size) == -1) {
855 			(void) fprintf(stderr, gettext(
856 				"write error extracting inode %d, name %s\n"),
857 				curfile.ino, curfile.name);
858 			perror("write");
859 			done(1);
860 		}
861 	}
862 	(void) close(dp);
863 	(void) close(ofile);
864 	return (GOOD);
865 }
866 
867 /*
868  * Determine the type of an inode
869  */
870 inodetype(ino)
871 	ino_t ino;
872 {
873 	struct inotab *itp;
874 
875 	itp = inotablookup(ino);
876 	if (itp == NULL)
877 		return (LEAF);
878 	return (NODE);
879 }
880 
881 /*
882  * Allocate and initialize a directory inode entry.
883  * If requested, save its pertinent mode, owner, and time info.
884  */
885 static struct inotab *
886 allocinotab(ino, dip, seekpt)
887 	ino_t ino;
888 	struct dinode *dip;
889 	off64_t seekpt;
890 {
891 	struct inotab	*itp;
892 
893 	itp = (struct inotab *)calloc(1, sizeof (*itp));
894 	if (itp == 0) {
895 		(void) fprintf(stderr,
896 		    gettext("no memory for directory table\n"));
897 		done(1);
898 	}
899 	itp->t_next = inotab[INOHASH(ino)];
900 	inotab[INOHASH(ino)] = itp;
901 	itp->t_ino = ino;
902 	itp->t_seekpt = seekpt;
903 	if ((dip->di_mode & IFMT) == IFATTRDIR) {
904 		itp->t_xattr = xattrlist;
905 		xattrlist = itp;
906 	}
907 	if (mf == NULL)
908 		return (itp);
909 	node.ino = ino;
910 	node.timep[0] = dip->di_atime;
911 	node.timep[1] = dip->di_mtime;
912 	node.mode = dip->di_mode;
913 	node.uid =
914 		dip->di_suid == UID_LONG ? dip->di_uid : (uid_t)dip->di_suid;
915 	node.gid =
916 		dip->di_sgid == GID_LONG ? dip->di_gid : (gid_t)dip->di_sgid;
917 	return (itp);
918 }
919 
920 void
921 nodeflush()
922 {
923 	char *metadata;
924 
925 	if (mf == NULL) {
926 		(void) fprintf(stderr, gettext(
927 		    "Inconsistency detected: modefile pointer is NULL\n"));
928 		done(1);
929 	}
930 	metaget(&metadata, &(node.metasize));
931 	(void) fwrite((char *)&node, 1, sizeof (node), mf);
932 	if (node.metasize != 0)
933 		(void) fwrite(metadata, 1, node.metasize, mf);
934 	if (ferror(mf))
935 		panic("%s: %s\n", modefile, strerror(errno));
936 }
937 
938 /*
939  * Look up an inode in the table of directories
940  */
941 static struct inotab *
942 inotablookup(ino)
943 	ino_t	ino;
944 {
945 	struct inotab *itp;
946 
947 	for (itp = inotab[INOHASH(ino)]; itp != NULL; itp = itp->t_next)
948 		if (itp->t_ino == ino)
949 			return (itp);
950 	return ((struct inotab *)0);
951 }
952 
953 /*
954  * Clean up and exit
955  */
956 void
957 done(exitcode)
958 	int exitcode;
959 {
960 
961 	closemt();
962 	if (modefile[0] != '#')
963 		(void) unlink(modefile);
964 	if (dirfile[0] != '#')
965 		(void) unlink(dirfile);
966 	exit(exitcode);
967 }
968