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