dirs.c revision 66907
1251018Sgonzo/*
2251018Sgonzo * Copyright (c) 1983, 1993
3251018Sgonzo *	The Regents of the University of California.  All rights reserved.
4251018Sgonzo * (c) UNIX System Laboratories, Inc.
5251018Sgonzo * All or some portions of this file are derived from material licensed
6251018Sgonzo * to the University of California by American Telephone and Telegraph
7251018Sgonzo * Co. or Unix System Laboratories, Inc. and are reproduced herein with
8251018Sgonzo * the permission of UNIX System Laboratories, Inc.
9251018Sgonzo *
10251018Sgonzo * Redistribution and use in source and binary forms, with or without
11251018Sgonzo * modification, are permitted provided that the following conditions
12251018Sgonzo * are met:
13251018Sgonzo * 1. Redistributions of source code must retain the above copyright
14251018Sgonzo *    notice, this list of conditions and the following disclaimer.
15251018Sgonzo * 2. Redistributions in binary form must reproduce the above copyright
16251018Sgonzo *    notice, this list of conditions and the following disclaimer in the
17251018Sgonzo *    documentation and/or other materials provided with the distribution.
18251018Sgonzo * 3. All advertising materials mentioning features or use of this software
19251018Sgonzo *    must display the following acknowledgement:
20251018Sgonzo *	This product includes software developed by the University of
21251018Sgonzo *	California, Berkeley and its contributors.
22251018Sgonzo * 4. Neither the name of the University nor the names of its contributors
23251018Sgonzo *    may be used to endorse or promote products derived from this software
24251018Sgonzo *    without specific prior written permission.
25251018Sgonzo *
26251018Sgonzo * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27251018Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28251018Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29251018Sgonzo * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30277716Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31251018Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32251018Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33251018Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34251018Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35251018Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36251018Sgonzo * SUCH DAMAGE.
37251018Sgonzo */
38251018Sgonzo
39251018Sgonzo#ifndef lint
40251018Sgonzo#if 0
41251018Sgonzostatic char sccsid[] = "@(#)dirs.c	8.7 (Berkeley) 5/1/95";
42251018Sgonzo#endif
43251018Sgonzostatic const char rcsid[] =
44251018Sgonzo  "$FreeBSD: head/sbin/restore/dirs.c 66907 2000-10-10 01:50:26Z wollman $";
45252282Sgonzo#endif /* not lint */
46252282Sgonzo
47252282Sgonzo#include <sys/param.h>
48251018Sgonzo#include <sys/file.h>
49251018Sgonzo#include <sys/stat.h>
50251018Sgonzo#include <sys/time.h>
51251018Sgonzo
52251018Sgonzo#include <ufs/ufs/dinode.h>
53251018Sgonzo#include <ufs/ufs/dir.h>
54251018Sgonzo#include <protocols/dumprestore.h>
55252282Sgonzo
56277716Sgonzo#include <err.h>
57252282Sgonzo#include <errno.h>
58277716Sgonzo#include <stdio.h>
59277716Sgonzo#include <stdlib.h>
60277716Sgonzo#include <string.h>
61252282Sgonzo#include <unistd.h>
62251018Sgonzo
63251018Sgonzo#include "pathnames.h"
64251018Sgonzo#include "restore.h"
65251018Sgonzo#include "extern.h"
66251018Sgonzo
67251018Sgonzo/*
68277716Sgonzo * Symbol table of directories read from tape.
69277716Sgonzo */
70251018Sgonzo#define HASHSIZE	1000
71251018Sgonzo#define INOHASH(val) (val % HASHSIZE)
72251018Sgonzostruct inotab {
73251018Sgonzo	struct	inotab *t_next;
74251018Sgonzo	ino_t	t_ino;
75251018Sgonzo	int32_t	t_seekpt;
76251018Sgonzo	int32_t	t_size;
77251018Sgonzo};
78251018Sgonzostatic struct inotab *inotab[HASHSIZE];
79251018Sgonzo
80251018Sgonzo/*
81251018Sgonzo * Information retained about directories.
82251018Sgonzo */
83251018Sgonzostruct modeinfo {
84251018Sgonzo	ino_t ino;
85251018Sgonzo	struct timeval timep[2];
86251018Sgonzo	mode_t mode;
87251018Sgonzo	uid_t uid;
88251018Sgonzo	gid_t gid;
89251018Sgonzo	int flags;
90251018Sgonzo};
91251018Sgonzo
92251018Sgonzo/*
93251018Sgonzo * Definitions for library routines operating on directories.
94251018Sgonzo */
95251018Sgonzo#undef DIRBLKSIZ
96251018Sgonzo#define DIRBLKSIZ 1024
97251018Sgonzostruct rstdirdesc {
98251018Sgonzo	int	dd_fd;
99251018Sgonzo	int32_t	dd_loc;
100251018Sgonzo	int32_t	dd_size;
101251018Sgonzo	char	dd_buf[DIRBLKSIZ];
102251018Sgonzo};
103251018Sgonzo
104251018Sgonzo/*
105251018Sgonzo * Global variables for this file.
106251018Sgonzo */
107251018Sgonzostatic long	seekpt;
108251018Sgonzostatic FILE	*df, *mf;
109251018Sgonzostatic RST_DIR	*dirp;
110251018Sgonzostatic char	dirfile[MAXPATHLEN] = "#";	/* No file */
111251018Sgonzostatic char	modefile[MAXPATHLEN] = "#";	/* No file */
112251018Sgonzostatic char	dot[2] = ".";			/* So it can be modified */
113251018Sgonzo
114251018Sgonzo/*
115251018Sgonzo * Format of old style directories.
116251018Sgonzo */
117251018Sgonzo#define ODIRSIZ 14
118251018Sgonzostruct odirect {
119251018Sgonzo	u_short	d_ino;
120251018Sgonzo	char	d_name[ODIRSIZ];
121251018Sgonzo};
122251018Sgonzo
123251018Sgonzostatic struct inotab	*allocinotab __P((ino_t, struct dinode *, long));
124251018Sgonzostatic void		 dcvt __P((struct odirect *, struct direct *));
125251018Sgonzostatic void		 flushent __P((void));
126251018Sgonzostatic struct inotab	*inotablookup __P((ino_t));
127251018Sgonzostatic RST_DIR		*opendirfile __P((const char *));
128251018Sgonzostatic void		 putdir __P((char *, long));
129251018Sgonzostatic void		 putent __P((struct direct *));
130251018Sgonzostatic void		 rst_seekdir __P((RST_DIR *, long, long));
131251018Sgonzostatic long		 rst_telldir __P((RST_DIR *));
132251018Sgonzostatic struct direct	*searchdir __P((ino_t, char *));
133251018Sgonzo
134251018Sgonzo/*
135251018Sgonzo *	Extract directory contents, building up a directory structure
136251018Sgonzo *	on disk for extraction by name.
137251018Sgonzo *	If genmode is requested, save mode, owner, and times for all
138251018Sgonzo *	directories on the tape.
139251018Sgonzo */
140251018Sgonzovoid
141251018Sgonzoextractdirs(genmode)
142251018Sgonzo	int genmode;
143251018Sgonzo{
144251018Sgonzo	register int i;
145251018Sgonzo	register struct dinode *ip;
146251018Sgonzo	struct inotab *itp;
147251018Sgonzo	struct direct nulldir;
148251018Sgonzo	int fd;
149251018Sgonzo
150251018Sgonzo	vprintf(stdout, "Extract directories from tape\n");
151251018Sgonzo	(void) sprintf(dirfile, "%srstdir%d", _PATH_TMP, dumpdate);
152251018Sgonzo	if (command != 'r' && command != 'R') {
153251018Sgonzo		(void *) strcat(dirfile, "-XXXXXX");
154251018Sgonzo		fd = mkstemp(dirfile);
155251018Sgonzo	} else
156251018Sgonzo		fd = open(dirfile, O_RDWR|O_CREAT|O_EXCL, 0666);
157251018Sgonzo	if (fd == -1 || (df = fdopen(fd, "w")) == NULL) {
158277405Sgonzo		if (fd != -1)
159251018Sgonzo			close(fd);
160251018Sgonzo		warn("%s - cannot create directory temporary\nfopen", dirfile);
161251018Sgonzo		done(1);
162251018Sgonzo	}
163251018Sgonzo	if (genmode != 0) {
164251018Sgonzo		(void) sprintf(modefile, "%srstmode%d", _PATH_TMP, dumpdate);
165251018Sgonzo		if (command != 'r' && command != 'R') {
166251018Sgonzo			(void *) strcat(modefile, "-XXXXXX");
167251018Sgonzo			fd = mkstemp(modefile);
168251018Sgonzo		} else
169251018Sgonzo			fd = open(modefile, O_RDWR|O_CREAT|O_EXCL, 0666);
170251018Sgonzo		if (fd == -1 || (mf = fdopen(fd, "w")) == NULL) {
171251018Sgonzo			if (fd != -1)
172251018Sgonzo				close(fd);
173251018Sgonzo			warn("%s - cannot create modefile\nfopen", modefile);
174251018Sgonzo			done(1);
175251018Sgonzo		}
176251018Sgonzo	}
177251018Sgonzo	nulldir.d_ino = 0;
178251018Sgonzo	nulldir.d_type = DT_DIR;
179251018Sgonzo	nulldir.d_namlen = 1;
180251018Sgonzo	(void) strcpy(nulldir.d_name, "/");
181251018Sgonzo	nulldir.d_reclen = DIRSIZ(0, &nulldir);
182251018Sgonzo	for (;;) {
183251018Sgonzo		curfile.name = "<directory file - name unknown>";
184251018Sgonzo		curfile.action = USING;
185251018Sgonzo		ip = curfile.dip;
186277716Sgonzo		if (ip == NULL || (ip->di_mode & IFMT) != IFDIR) {
187251018Sgonzo			(void) fclose(df);
188251018Sgonzo			dirp = opendirfile(dirfile);
189251018Sgonzo			if (dirp == NULL)
190251018Sgonzo				fprintf(stderr, "opendirfile: %s\n",
191251018Sgonzo				    strerror(errno));
192251018Sgonzo			if (mf != NULL)
193251018Sgonzo				(void) fclose(mf);
194251018Sgonzo			i = dirlookup(dot);
195251018Sgonzo			if (i == 0)
196251018Sgonzo				panic("Root directory is not on tape\n");
197251018Sgonzo			return;
198251018Sgonzo		}
199251018Sgonzo		itp = allocinotab(curfile.ino, ip, seekpt);
200251018Sgonzo		getfile(putdir, xtrnull);
201251018Sgonzo		putent(&nulldir);
202251018Sgonzo		flushent();
203251018Sgonzo		itp->t_size = seekpt - itp->t_seekpt;
204251018Sgonzo	}
205251018Sgonzo}
206251018Sgonzo
207251018Sgonzo/*
208251018Sgonzo * skip over all the directories on the tape
209251018Sgonzo */
210251018Sgonzovoid
211251018Sgonzoskipdirs()
212251018Sgonzo{
213251018Sgonzo
214251018Sgonzo	while (curfile.dip && (curfile.dip->di_mode & IFMT) == IFDIR) {
215251018Sgonzo		skipfile();
216251018Sgonzo	}
217251018Sgonzo}
218251018Sgonzo
219251018Sgonzo/*
220251018Sgonzo *	Recursively find names and inumbers of all files in subtree
221251018Sgonzo *	pname and pass them off to be processed.
222251018Sgonzo */
223251018Sgonzovoid
224251018Sgonzotreescan(pname, ino, todo)
225251018Sgonzo	char *pname;
226251018Sgonzo	ino_t ino;
227251018Sgonzo	long (*todo) __P((char *, ino_t, int));
228251018Sgonzo{
229251018Sgonzo	register struct inotab *itp;
230251018Sgonzo	register struct direct *dp;
231251018Sgonzo	int namelen;
232251018Sgonzo	long bpt;
233267171Skevlo	char locname[MAXPATHLEN + 1];
234251018Sgonzo
235251018Sgonzo	itp = inotablookup(ino);
236251018Sgonzo	if (itp == NULL) {
237251018Sgonzo		/*
238251018Sgonzo		 * Pname is name of a simple file or an unchanged directory.
239251018Sgonzo		 */
240251018Sgonzo		(void) (*todo)(pname, ino, LEAF);
241251018Sgonzo		return;
242251018Sgonzo	}
243251018Sgonzo	/*
244251018Sgonzo	 * Pname is a dumped directory name.
245283276Sgonzo	 */
246251018Sgonzo	if ((*todo)(pname, ino, NODE) == FAIL)
247251018Sgonzo		return;
248251018Sgonzo	/*
249251018Sgonzo	 * begin search through the directory
250251018Sgonzo	 * skipping over "." and ".."
251251018Sgonzo	 */
252251018Sgonzo	(void) strncpy(locname, pname, sizeof(locname) - 1);
253251018Sgonzo	locname[sizeof(locname) - 1] = '\0';
254251018Sgonzo	(void) strncat(locname, "/", sizeof(locname) - strlen(locname));
255283276Sgonzo	namelen = strlen(locname);
256251018Sgonzo	rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
257251018Sgonzo	dp = rst_readdir(dirp); /* "." */
258251018Sgonzo	if (dp != NULL && strcmp(dp->d_name, ".") == 0)
259251018Sgonzo		dp = rst_readdir(dirp); /* ".." */
260251018Sgonzo	else
261251018Sgonzo		fprintf(stderr, "Warning: `.' missing from directory %s\n",
262251018Sgonzo			pname);
263251018Sgonzo	if (dp != NULL && strcmp(dp->d_name, "..") == 0)
264251018Sgonzo		dp = rst_readdir(dirp); /* first real entry */
265251018Sgonzo	else
266251018Sgonzo		fprintf(stderr, "Warning: `..' missing from directory %s\n",
267251018Sgonzo			pname);
268251018Sgonzo	bpt = rst_telldir(dirp);
269251018Sgonzo	/*
270251018Sgonzo	 * a zero inode signals end of directory
271283276Sgonzo	 */
272251018Sgonzo	while (dp != NULL) {
273251018Sgonzo		locname[namelen] = '\0';
274283276Sgonzo		if (namelen + dp->d_namlen >= sizeof(locname)) {
275251018Sgonzo			fprintf(stderr, "%s%s: name exceeds %d char\n",
276283503Sgonzo				locname, dp->d_name, sizeof(locname) - 1);
277283276Sgonzo		} else {
278283276Sgonzo			(void) strncat(locname, dp->d_name, (int)dp->d_namlen);
279283276Sgonzo			treescan(locname, dp->d_ino, todo);
280283276Sgonzo			rst_seekdir(dirp, bpt, itp->t_seekpt);
281283276Sgonzo		}
282283276Sgonzo		dp = rst_readdir(dirp);
283283276Sgonzo		bpt = rst_telldir(dirp);
284283276Sgonzo	}
285283276Sgonzo}
286283276Sgonzo
287283276Sgonzo/*
288283276Sgonzo * Lookup a pathname which is always assumed to start from the ROOTINO.
289283276Sgonzo */
290251018Sgonzostruct direct *
291283276Sgonzopathsearch(pathname)
292283276Sgonzo	const char *pathname;
293251018Sgonzo{
294251018Sgonzo	ino_t ino;
295283276Sgonzo	struct direct *dp;
296283276Sgonzo	char *path, *name, buffer[MAXPATHLEN];
297251018Sgonzo
298251018Sgonzo	strcpy(buffer, pathname);
299283276Sgonzo	path = buffer;
300283276Sgonzo	ino = ROOTINO;
301251018Sgonzo	while (*path == '/')
302251018Sgonzo		path++;
303283276Sgonzo	dp = NULL;
304283276Sgonzo	while ((name = strsep(&path, "/")) != NULL && *name != '\0') {
305251018Sgonzo		if ((dp = searchdir(ino, name)) == NULL)
306251018Sgonzo			return (NULL);
307283276Sgonzo		ino = dp->d_ino;
308283276Sgonzo	}
309251018Sgonzo	return (dp);
310251018Sgonzo}
311283276Sgonzo
312283276Sgonzo/*
313251018Sgonzo * Lookup the requested name in directory inum.
314251018Sgonzo * Return its inode number if found, zero if it does not exist.
315283276Sgonzo */
316283276Sgonzostatic struct direct *
317251018Sgonzosearchdir(inum, name)
318251018Sgonzo	ino_t	inum;
319283276Sgonzo	char	*name;
320283276Sgonzo{
321251018Sgonzo	register struct direct *dp;
322251018Sgonzo	register struct inotab *itp;
323283276Sgonzo	int len;
324283276Sgonzo
325251018Sgonzo	itp = inotablookup(inum);
326251018Sgonzo	if (itp == NULL)
327283276Sgonzo		return (NULL);
328283276Sgonzo	rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
329251018Sgonzo	len = strlen(name);
330251018Sgonzo	do {
331283276Sgonzo		dp = rst_readdir(dirp);
332283276Sgonzo		if (dp == NULL)
333251018Sgonzo			return (NULL);
334251018Sgonzo	} while (dp->d_namlen != len || strncmp(dp->d_name, name, len) != 0);
335283276Sgonzo	return (dp);
336283276Sgonzo}
337251018Sgonzo
338251018Sgonzo/*
339283276Sgonzo * Put the directory entries in the directory file
340283276Sgonzo */
341283276Sgonzostatic void
342283276Sgonzoputdir(buf, size)
343283276Sgonzo	char *buf;
344283276Sgonzo	long size;
345283276Sgonzo{
346283276Sgonzo	struct direct cvtbuf;
347283276Sgonzo	register struct odirect *odp;
348283276Sgonzo	struct odirect *eodp;
349283503Sgonzo	register struct direct *dp;
350283276Sgonzo	long loc, i;
351283276Sgonzo
352283276Sgonzo	if (cvtflag) {
353283276Sgonzo		eodp = (struct odirect *)&buf[size];
354283276Sgonzo		for (odp = (struct odirect *)buf; odp < eodp; odp++)
355283276Sgonzo			if (odp->d_ino != 0) {
356283276Sgonzo				dcvt(odp, &cvtbuf);
357251018Sgonzo				putent(&cvtbuf);
358251018Sgonzo			}
359283276Sgonzo	} else {
360283276Sgonzo		for (loc = 0; loc < size; ) {
361251018Sgonzo			dp = (struct direct *)(buf + loc);
362251018Sgonzo			if (Bcvt)
363283276Sgonzo				swabst((u_char *)"ls", (u_char *) dp);
364283276Sgonzo			if (oldinofmt && dp->d_ino != 0) {
365251018Sgonzo#				if BYTE_ORDER == BIG_ENDIAN
366251018Sgonzo					if (Bcvt)
367283276Sgonzo						dp->d_namlen = dp->d_type;
368283276Sgonzo#				else
369251018Sgonzo					if (!Bcvt)
370251018Sgonzo						dp->d_namlen = dp->d_type;
371283276Sgonzo#				endif
372283276Sgonzo				dp->d_type = DT_UNKNOWN;
373251018Sgonzo			}
374251018Sgonzo			i = DIRBLKSIZ - (loc & (DIRBLKSIZ - 1));
375283276Sgonzo			if ((dp->d_reclen & 0x3) != 0 ||
376283276Sgonzo			    dp->d_reclen > i ||
377251018Sgonzo			    dp->d_reclen < DIRSIZ(0, dp) ||
378251018Sgonzo			    dp->d_namlen > NAME_MAX) {
379283276Sgonzo				vprintf(stdout, "Mangled directory: ");
380283276Sgonzo				if ((dp->d_reclen & 0x3) != 0)
381251018Sgonzo					vprintf(stdout,
382251018Sgonzo					   "reclen not multiple of 4 ");
383251018Sgonzo				if (dp->d_reclen < DIRSIZ(0, dp))
384251018Sgonzo					vprintf(stdout,
385251018Sgonzo					   "reclen less than DIRSIZ (%d < %d) ",
386251018Sgonzo					   dp->d_reclen, DIRSIZ(0, dp));
387251018Sgonzo				if (dp->d_namlen > NAME_MAX)
388251018Sgonzo					vprintf(stdout,
389251018Sgonzo					   "reclen name too big (%d > %d) ",
390251018Sgonzo					   dp->d_namlen, NAME_MAX);
391251018Sgonzo				vprintf(stdout, "\n");
392251018Sgonzo				loc += i;
393251018Sgonzo				continue;
394277632Sgonzo			}
395277632Sgonzo			loc += dp->d_reclen;
396251018Sgonzo			if (dp->d_ino != 0) {
397251018Sgonzo				putent(dp);
398251018Sgonzo			}
399251018Sgonzo		}
400251018Sgonzo	}
401251018Sgonzo}
402251018Sgonzo
403251018Sgonzo/*
404251018Sgonzo * These variables are "local" to the following two functions.
405277522Sgonzo */
406251018Sgonzochar dirbuf[DIRBLKSIZ];
407251018Sgonzolong dirloc = 0;
408251018Sgonzolong prev = 0;
409251018Sgonzo
410251018Sgonzo/*
411251018Sgonzo * add a new directory entry to a file.
412251018Sgonzo */
413251018Sgonzostatic void
414251018Sgonzoputent(dp)
415251018Sgonzo	struct direct *dp;
416277522Sgonzo{
417251018Sgonzo	dp->d_reclen = DIRSIZ(0, dp);
418251018Sgonzo	if (dirloc + dp->d_reclen > DIRBLKSIZ) {
419251018Sgonzo		((struct direct *)(dirbuf + prev))->d_reclen =
420251018Sgonzo		    DIRBLKSIZ - prev;
421251018Sgonzo		(void) fwrite(dirbuf, 1, DIRBLKSIZ, df);
422251018Sgonzo		dirloc = 0;
423251018Sgonzo	}
424251018Sgonzo	memmove(dirbuf + dirloc, dp, (long)dp->d_reclen);
425251018Sgonzo	prev = dirloc;
426251018Sgonzo	dirloc += dp->d_reclen;
427251018Sgonzo}
428251018Sgonzo
429251018Sgonzo/*
430251018Sgonzo * flush out a directory that is finished.
431251018Sgonzo */
432251018Sgonzostatic void
433251018Sgonzoflushent()
434251018Sgonzo{
435251018Sgonzo	((struct direct *)(dirbuf + prev))->d_reclen = DIRBLKSIZ - prev;
436251018Sgonzo	(void) fwrite(dirbuf, (int)dirloc, 1, df);
437251018Sgonzo	seekpt = ftell(df);
438277405Sgonzo	dirloc = 0;
439277522Sgonzo}
440277405Sgonzo
441277632Sgonzostatic void
442277632Sgonzodcvt(odp, ndp)
443251018Sgonzo	register struct odirect *odp;
444251018Sgonzo	register struct direct *ndp;
445251018Sgonzo{
446251018Sgonzo
447251018Sgonzo	memset(ndp, 0, (long)(sizeof *ndp));
448277716Sgonzo	ndp->d_ino =  odp->d_ino;
449252282Sgonzo	ndp->d_type = DT_UNKNOWN;
450277716Sgonzo	(void) strncpy(ndp->d_name, odp->d_name, ODIRSIZ);
451252282Sgonzo	ndp->d_namlen = strlen(ndp->d_name);
452261410Sian	ndp->d_reclen = DIRSIZ(0, ndp);
453261410Sian}
454261410Sian
455283276Sgonzo/*
456251018Sgonzo * Seek to an entry in a directory.
457251018Sgonzo * Only values returned by rst_telldir should be passed to rst_seekdir.
458251018Sgonzo * This routine handles many directories in a single file.
459251018Sgonzo * It takes the base of the directory in the file, plus
460277716Sgonzo * the desired seek offset into it.
461252282Sgonzo */
462252282Sgonzostatic void
463252282Sgonzorst_seekdir(dirp, loc, base)
464252282Sgonzo	register RST_DIR *dirp;
465277716Sgonzo	long loc, base;
466252282Sgonzo{
467252282Sgonzo
468251018Sgonzo	if (loc == rst_telldir(dirp))
469251018Sgonzo		return;
470251018Sgonzo	loc -= base;
471251018Sgonzo	if (loc < 0)
472251018Sgonzo		fprintf(stderr, "bad seek pointer to rst_seekdir %ld\n", loc);
473251018Sgonzo	(void) lseek(dirp->dd_fd, base + (loc & ~(DIRBLKSIZ - 1)), SEEK_SET);
474251018Sgonzo	dirp->dd_loc = loc & (DIRBLKSIZ - 1);
475251018Sgonzo	if (dirp->dd_loc != 0)
476251018Sgonzo		dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, DIRBLKSIZ);
477251018Sgonzo}
478251018Sgonzo
479251018Sgonzo/*
480251018Sgonzo * get next entry in a directory.
481251018Sgonzo */
482251018Sgonzostruct direct *
483277313Sgonzorst_readdir(dirp)
484277313Sgonzo	register RST_DIR *dirp;
485277313Sgonzo{
486283276Sgonzo	register struct direct *dp;
487251018Sgonzo
488251018Sgonzo	for (;;) {
489251018Sgonzo		if (dirp->dd_loc == 0) {
490251018Sgonzo			dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf,
491283276Sgonzo			    DIRBLKSIZ);
492283276Sgonzo			if (dirp->dd_size <= 0) {
493283276Sgonzo				dprintf(stderr, "error reading directory\n");
494251018Sgonzo				return (NULL);
495283276Sgonzo			}
496251018Sgonzo		}
497283276Sgonzo		if (dirp->dd_loc >= dirp->dd_size) {
498283276Sgonzo			dirp->dd_loc = 0;
499283276Sgonzo			continue;
500283276Sgonzo		}
501283276Sgonzo		dp = (struct direct *)(dirp->dd_buf + dirp->dd_loc);
502283276Sgonzo		if (dp->d_reclen == 0 ||
503283276Sgonzo		    dp->d_reclen > DIRBLKSIZ + 1 - dirp->dd_loc) {
504283276Sgonzo			dprintf(stderr, "corrupted directory: bad reclen %d\n",
505283276Sgonzo				dp->d_reclen);
506283276Sgonzo			return (NULL);
507283276Sgonzo		}
508283276Sgonzo		dirp->dd_loc += dp->d_reclen;
509283276Sgonzo		if (dp->d_ino == 0 && strcmp(dp->d_name, "/") == 0)
510283276Sgonzo			return (NULL);
511283276Sgonzo		if (dp->d_ino >= maxino) {
512283276Sgonzo			dprintf(stderr, "corrupted directory: bad inum %d\n",
513251018Sgonzo				dp->d_ino);
514251018Sgonzo			continue;
515251018Sgonzo		}
516251018Sgonzo		return (dp);
517251018Sgonzo	}
518251018Sgonzo}
519251018Sgonzo
520251018Sgonzo/*
521251018Sgonzo * Simulate the opening of a directory
522251018Sgonzo */
523251018SgonzoRST_DIR *
524251018Sgonzorst_opendir(name)
525251018Sgonzo	const char *name;
526251018Sgonzo{
527251018Sgonzo	struct inotab *itp;
528251018Sgonzo	RST_DIR *dirp;
529251018Sgonzo	ino_t ino;
530251018Sgonzo
531251018Sgonzo	if ((ino = dirlookup(name)) > 0 &&
532251018Sgonzo	    (itp = inotablookup(ino)) != NULL) {
533251018Sgonzo		dirp = opendirfile(dirfile);
534251018Sgonzo		rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
535251018Sgonzo		return (dirp);
536251018Sgonzo	}
537251018Sgonzo	return (NULL);
538251018Sgonzo}
539251018Sgonzo
540251018Sgonzo/*
541251018Sgonzo * In our case, there is nothing to do when closing a directory.
542251018Sgonzo */
543251018Sgonzovoid
544251018Sgonzorst_closedir(dirp)
545251018Sgonzo	RST_DIR *dirp;
546251018Sgonzo{
547251018Sgonzo
548251018Sgonzo	(void)close(dirp->dd_fd);
549251018Sgonzo	free(dirp);
550251018Sgonzo	return;
551251018Sgonzo}
552251018Sgonzo
553251018Sgonzo/*
554251018Sgonzo * Simulate finding the current offset in the directory.
555251018Sgonzo */
556251018Sgonzostatic long
557251018Sgonzorst_telldir(dirp)
558251018Sgonzo	RST_DIR *dirp;
559251018Sgonzo{
560251018Sgonzo	return ((long)lseek(dirp->dd_fd,
561251018Sgonzo	    (off_t)0, SEEK_CUR) - dirp->dd_size + dirp->dd_loc);
562251018Sgonzo}
563251018Sgonzo
564251018Sgonzo/*
565251018Sgonzo * Open a directory file.
566251018Sgonzo */
567251018Sgonzostatic RST_DIR *
568251018Sgonzoopendirfile(name)
569251018Sgonzo	const char *name;
570252282Sgonzo{
571251018Sgonzo	register RST_DIR *dirp;
572251018Sgonzo	register int fd;
573251018Sgonzo
574251018Sgonzo	if ((fd = open(name, O_RDONLY)) == -1)
575251018Sgonzo		return (NULL);
576251018Sgonzo	if ((dirp = malloc(sizeof(RST_DIR))) == NULL) {
577251018Sgonzo		(void)close(fd);
578251018Sgonzo		return (NULL);
579251018Sgonzo	}
580251018Sgonzo	dirp->dd_fd = fd;
581251018Sgonzo	dirp->dd_loc = 0;
582251018Sgonzo	return (dirp);
583251018Sgonzo}
584251018Sgonzo
585251018Sgonzo/*
586251018Sgonzo * Set the mode, owner, and times for all new or changed directories
587251018Sgonzo */
588251018Sgonzovoid
589251018Sgonzosetdirmodes(flags)
590251018Sgonzo	int flags;
591251018Sgonzo{
592251018Sgonzo	FILE *mf;
593251018Sgonzo	struct modeinfo node;
594251018Sgonzo	struct entry *ep;
595251018Sgonzo	char *cp;
596251018Sgonzo
597251018Sgonzo	vprintf(stdout, "Set directory mode, owner, and times.\n");
598251018Sgonzo	if (command == 'r' || command == 'R')
599251018Sgonzo		(void) sprintf(modefile, "%srstmode%d", _PATH_TMP, dumpdate);
600277313Sgonzo	if (modefile[0] == '#') {
601277313Sgonzo		panic("modefile not defined\n");
602277313Sgonzo		fprintf(stderr, "directory mode, owner, and times not set\n");
603277313Sgonzo		return;
604277313Sgonzo	}
605277313Sgonzo	mf = fopen(modefile, "r");
606277313Sgonzo	if (mf == NULL) {
607277313Sgonzo		fprintf(stderr, "fopen: %s\n", strerror(errno));
608277313Sgonzo		fprintf(stderr, "cannot open mode file %s\n", modefile);
609277313Sgonzo		fprintf(stderr, "directory mode, owner, and times not set\n");
610277313Sgonzo		return;
611251018Sgonzo	}
612277313Sgonzo	clearerr(mf);
613277313Sgonzo	for (;;) {
614251018Sgonzo		(void) fread((char *)&node, 1, sizeof(struct modeinfo), mf);
615277313Sgonzo		if (feof(mf))
616277313Sgonzo			break;
617251018Sgonzo		ep = lookupino(node.ino);
618277313Sgonzo		if (command == 'i' || command == 'x') {
619277313Sgonzo			if (ep == NULL)
620251018Sgonzo				continue;
621251018Sgonzo			if ((flags & FORCE) == 0 && ep->e_flags & EXISTED) {
622277313Sgonzo				ep->e_flags &= ~NEW;
623277313Sgonzo				continue;
624277313Sgonzo			}
625251018Sgonzo			if (node.ino == ROOTINO &&
626251018Sgonzo		   	    reply("set owner/mode for '.'") == FAIL)
627277313Sgonzo				continue;
628251018Sgonzo		}
629277313Sgonzo		if (ep == NULL) {
630251018Sgonzo			panic("cannot find directory inode %d\n", node.ino);
631251018Sgonzo		} else {
632251018Sgonzo			cp = myname(ep);
633277313Sgonzo			(void) chown(cp, node.uid, node.gid);
634251018Sgonzo			(void) chmod(cp, node.mode);
635277313Sgonzo			utimes(cp, node.timep);
636251018Sgonzo			(void) chflags(cp, node.flags);
637251018Sgonzo			ep->e_flags &= ~NEW;
638251018Sgonzo		}
639251018Sgonzo	}
640251018Sgonzo	if (ferror(mf))
641251018Sgonzo		panic("error setting directory modes\n");
642251018Sgonzo	(void) fclose(mf);
643251018Sgonzo}
644251018Sgonzo
645283276Sgonzo/*
646251018Sgonzo * Generate a literal copy of a directory.
647283276Sgonzo */
648251018Sgonzoint
649283276Sgonzogenliteraldir(name, ino)
650251018Sgonzo	char *name;
651251018Sgonzo	ino_t ino;
652251018Sgonzo{
653251018Sgonzo	register struct inotab *itp;
654251018Sgonzo	int ofile, dp, i, size;
655251018Sgonzo	char buf[BUFSIZ];
656251018Sgonzo
657251018Sgonzo	itp = inotablookup(ino);
658251018Sgonzo	if (itp == NULL)
659251018Sgonzo		panic("Cannot find directory inode %d named %s\n", ino, name);
660251018Sgonzo	if ((ofile = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) {
661251018Sgonzo		fprintf(stderr, "%s: ", name);
662251018Sgonzo		(void) fflush(stderr);
663251018Sgonzo		fprintf(stderr, "cannot create file: %s\n", strerror(errno));
664251018Sgonzo		return (FAIL);
665251018Sgonzo	}
666251018Sgonzo	rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
667251018Sgonzo	dp = dup(dirp->dd_fd);
668251018Sgonzo	for (i = itp->t_size; i > 0; i -= BUFSIZ) {
669251018Sgonzo		size = i < BUFSIZ ? i : BUFSIZ;
670251018Sgonzo		if (read(dp, buf, (int) size) == -1) {
671251018Sgonzo			fprintf(stderr,
672251018Sgonzo				"write error extracting inode %d, name %s\n",
673251018Sgonzo				curfile.ino, curfile.name);
674251018Sgonzo			fprintf(stderr, "read: %s\n", strerror(errno));
675251018Sgonzo			done(1);
676251018Sgonzo		}
677251018Sgonzo		if (!Nflag && write(ofile, buf, (int) size) == -1) {
678251018Sgonzo			fprintf(stderr,
679251018Sgonzo				"write error extracting inode %d, name %s\n",
680251018Sgonzo				curfile.ino, curfile.name);
681251018Sgonzo			fprintf(stderr, "write: %s\n", strerror(errno));
682251018Sgonzo			done(1);
683251018Sgonzo		}
684251018Sgonzo	}
685251018Sgonzo	(void) close(dp);
686251018Sgonzo	(void) close(ofile);
687251018Sgonzo	return (GOOD);
688251018Sgonzo}
689251018Sgonzo
690251018Sgonzo/*
691251018Sgonzo * Determine the type of an inode
692251018Sgonzo */
693251018Sgonzoint
694251018Sgonzoinodetype(ino)
695251018Sgonzo	ino_t ino;
696251018Sgonzo{
697251018Sgonzo	struct inotab *itp;
698251018Sgonzo
699251018Sgonzo	itp = inotablookup(ino);
700251018Sgonzo	if (itp == NULL)
701251018Sgonzo		return (LEAF);
702251018Sgonzo	return (NODE);
703251018Sgonzo}
704251018Sgonzo
705251018Sgonzo/*
706251018Sgonzo * Allocate and initialize a directory inode entry.
707251018Sgonzo * If requested, save its pertinent mode, owner, and time info.
708251018Sgonzo */
709251018Sgonzostatic struct inotab *
710251018Sgonzoallocinotab(ino, dip, seekpt)
711251018Sgonzo	ino_t ino;
712251018Sgonzo	struct dinode *dip;
713251018Sgonzo	long seekpt;
714251018Sgonzo{
715251018Sgonzo	register struct inotab	*itp;
716251018Sgonzo	struct modeinfo node;
717251018Sgonzo
718251018Sgonzo	itp = calloc(1, sizeof(struct inotab));
719251018Sgonzo	if (itp == NULL)
720251018Sgonzo		panic("no memory directory table\n");
721251018Sgonzo	itp->t_next = inotab[INOHASH(ino)];
722251018Sgonzo	inotab[INOHASH(ino)] = itp;
723251018Sgonzo	itp->t_ino = ino;
724251018Sgonzo	itp->t_seekpt = seekpt;
725251018Sgonzo	if (mf == NULL)
726251018Sgonzo		return (itp);
727251018Sgonzo	node.ino = ino;
728283276Sgonzo	node.timep[0].tv_sec = dip->di_atime;
729251018Sgonzo	node.timep[0].tv_usec = dip->di_atimensec / 1000;
730251018Sgonzo	node.timep[1].tv_sec = dip->di_mtime;
731251018Sgonzo	node.timep[1].tv_usec = dip->di_mtimensec / 1000;
732277716Sgonzo	node.mode = dip->di_mode;
733277716Sgonzo	node.flags = dip->di_flags;
734277716Sgonzo	node.uid = dip->di_uid;
735277716Sgonzo	node.gid = dip->di_gid;
736277716Sgonzo	(void) fwrite((char *)&node, 1, sizeof(struct modeinfo), mf);
737277716Sgonzo	return (itp);
738277716Sgonzo}
739277716Sgonzo
740277716Sgonzo/*
741277716Sgonzo * Look up an inode in the table of directories
742252282Sgonzo */
743252282Sgonzostatic struct inotab *
744252282Sgonzoinotablookup(ino)
745252282Sgonzo	ino_t	ino;
746252282Sgonzo{
747252282Sgonzo	register struct inotab *itp;
748252282Sgonzo
749252282Sgonzo	for (itp = inotab[INOHASH(ino)]; itp != NULL; itp = itp->t_next)
750251018Sgonzo		if (itp->t_ino == ino)
751277716Sgonzo			return (itp);
752277716Sgonzo	return (NULL);
753277716Sgonzo}
754277716Sgonzo
755277716Sgonzo/*
756277716Sgonzo * Clean up and exit
757277716Sgonzo */
758277716Sgonzovoid
759277716Sgonzodone(exitcode)
760277716Sgonzo	int exitcode;
761277716Sgonzo{
762277716Sgonzo
763251018Sgonzo	closemt();
764251018Sgonzo	if (modefile[0] != '#')
765251018Sgonzo		(void) unlink(modefile);
766251018Sgonzo	if (dirfile[0] != '#')
767251018Sgonzo		(void) unlink(dirfile);
768251018Sgonzo	exit(exitcode);
769251018Sgonzo}
770251018Sgonzo