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