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