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