xref: /original-bsd/sbin/restore/dirs.c (revision a6d8c59f)
1 /*
2  * Copyright (c) 1983 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 static char sccsid[] = "@(#)dirs.c	5.25 (Berkeley) 12/02/92";
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((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 == 0) {
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 == 0) {
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 	char *pathname;
251 {
252 	ino_t ino;
253 	struct direct *dp;
254 	char *name, buffer[MAXPATHLEN];
255 
256 	strcpy(buffer, pathname);
257 	pathname = buffer;
258 	ino = ROOTINO;
259 	while (*pathname == '/')
260 		pathname++;
261 	while ((name = strsep(&pathname, "/")) != NULL && *name != NULL) {
262 		if ((dp = searchdir(ino, name)) == 0)
263 			return (NULL);
264 		ino = dp->d_ino;
265 	}
266 	return (dp);
267 }
268 
269 /*
270  * Lookup the requested name in directory inum.
271  * Return its inode number if found, zero if it does not exist.
272  */
273 static struct direct *
274 searchdir(inum, name)
275 	ino_t	inum;
276 	char	*name;
277 {
278 	register struct direct *dp;
279 	register struct inotab *itp;
280 	int len;
281 
282 	itp = inotablookup(inum);
283 	if (itp == NULL)
284 		return(0);
285 	rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
286 	len = strlen(name);
287 	do {
288 		dp = rst_readdir(dirp);
289 		if (dp == NULL || dp->d_ino == 0)
290 			return (NULL);
291 	} while (dp->d_namlen != len || strncmp(dp->d_name, name, len) != 0);
292 	return (dp);
293 }
294 
295 /*
296  * Put the directory entries in the directory file
297  */
298 static void
299 putdir(buf, size)
300 	char *buf;
301 	long size;
302 {
303 	struct direct cvtbuf;
304 	register struct odirect *odp;
305 	struct odirect *eodp;
306 	register struct direct *dp;
307 	long loc, i;
308 
309 	if (cvtflag) {
310 		eodp = (struct odirect *)&buf[size];
311 		for (odp = (struct odirect *)buf; odp < eodp; odp++)
312 			if (odp->d_ino != 0) {
313 				dcvt(odp, &cvtbuf);
314 				putent(&cvtbuf);
315 			}
316 	} else {
317 		for (loc = 0; loc < size; ) {
318 			dp = (struct direct *)(buf + loc);
319 			if (oldinofmt) {
320 				if (Bcvt) {
321 					swabst((u_char *)"l2s", (u_char *) dp);
322 				}
323 			} else {
324 				if (Bcvt) {
325 					swabst((u_char *)"ls", (u_char *) dp);
326 				}
327 			}
328 			i = DIRBLKSIZ - (loc & (DIRBLKSIZ - 1));
329 			if ((dp->d_reclen & 0x3) != 0 ||
330 			    dp->d_reclen > i ||
331 			    dp->d_reclen < DIRSIZ(0, dp) ||
332 			    dp->d_namlen > NAME_MAX) {
333 				vprintf(stdout, "Mangled directory: ");
334 				if ((dp->d_reclen & 0x3) != 0)
335 					vprintf(stdout,
336 					   "reclen not multiple of 4 ");
337 				if (dp->d_reclen < DIRSIZ(0, dp))
338 					vprintf(stdout,
339 					   "reclen less than DIRSIZ (%d < %d) ",
340 					   dp->d_reclen, DIRSIZ(0, dp));
341 				if (dp->d_namlen > NAME_MAX)
342 					vprintf(stdout,
343 					   "reclen name too big (%d > %d) ",
344 					   dp->d_namlen, NAME_MAX);
345 				vprintf(stdout, "\n");
346 				loc += i;
347 				continue;
348 			}
349 			loc += dp->d_reclen;
350 			if (dp->d_ino != 0) {
351 				putent(dp);
352 			}
353 		}
354 	}
355 }
356 
357 /*
358  * These variables are "local" to the following two functions.
359  */
360 char dirbuf[DIRBLKSIZ];
361 long dirloc = 0;
362 long prev = 0;
363 
364 /*
365  * add a new directory entry to a file.
366  */
367 static void
368 putent(dp)
369 	struct direct *dp;
370 {
371 	dp->d_reclen = DIRSIZ(0, dp);
372 	if (dirloc + dp->d_reclen > DIRBLKSIZ) {
373 		((struct direct *)(dirbuf + prev))->d_reclen =
374 		    DIRBLKSIZ - prev;
375 		(void) fwrite(dirbuf, 1, DIRBLKSIZ, df);
376 		dirloc = 0;
377 	}
378 	bcopy((char *)dp, dirbuf + dirloc, (long)dp->d_reclen);
379 	prev = dirloc;
380 	dirloc += dp->d_reclen;
381 }
382 
383 /*
384  * flush out a directory that is finished.
385  */
386 static void
387 flushent()
388 {
389 	((struct direct *)(dirbuf + prev))->d_reclen = DIRBLKSIZ - prev;
390 	(void) fwrite(dirbuf, (int)dirloc, 1, df);
391 	seekpt = ftell(df);
392 	dirloc = 0;
393 }
394 
395 static void
396 dcvt(odp, ndp)
397 	register struct odirect *odp;
398 	register struct direct *ndp;
399 {
400 
401 	bzero((char *)ndp, (long)(sizeof *ndp));
402 	ndp->d_ino =  odp->d_ino;
403 	ndp->d_type = DT_UNKNOWN;
404 	(void) strncpy(ndp->d_name, odp->d_name, ODIRSIZ);
405 	ndp->d_namlen = strlen(ndp->d_name);
406 	ndp->d_reclen = DIRSIZ(0, ndp);
407 }
408 
409 /*
410  * Seek to an entry in a directory.
411  * Only values returned by rst_telldir should be passed to rst_seekdir.
412  * This routine handles many directories in a single file.
413  * It takes the base of the directory in the file, plus
414  * the desired seek offset into it.
415  */
416 static void
417 rst_seekdir(dirp, loc, base)
418 	register RST_DIR *dirp;
419 	long loc, base;
420 {
421 
422 	if (loc == rst_telldir(dirp))
423 		return;
424 	loc -= base;
425 	if (loc < 0)
426 		fprintf(stderr, "bad seek pointer to rst_seekdir %d\n", loc);
427 	(void) lseek(dirp->dd_fd, base + (loc & ~(DIRBLKSIZ - 1)), SEEK_SET);
428 	dirp->dd_loc = loc & (DIRBLKSIZ - 1);
429 	if (dirp->dd_loc != 0)
430 		dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, DIRBLKSIZ);
431 }
432 
433 /*
434  * get next entry in a directory.
435  */
436 struct direct *
437 rst_readdir(dirp)
438 	register RST_DIR *dirp;
439 {
440 	register struct direct *dp;
441 
442 	for (;;) {
443 		if (dirp->dd_loc == 0) {
444 			dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf,
445 			    DIRBLKSIZ);
446 			if (dirp->dd_size <= 0) {
447 				dprintf(stderr, "error reading directory\n");
448 				return (NULL);
449 			}
450 		}
451 		if (dirp->dd_loc >= dirp->dd_size) {
452 			dirp->dd_loc = 0;
453 			continue;
454 		}
455 		dp = (struct direct *)(dirp->dd_buf + dirp->dd_loc);
456 		if (dp->d_reclen == 0 ||
457 		    dp->d_reclen > DIRBLKSIZ + 1 - dirp->dd_loc) {
458 			dprintf(stderr, "corrupted directory: bad reclen %d\n",
459 				dp->d_reclen);
460 			return (NULL);
461 		}
462 		dirp->dd_loc += dp->d_reclen;
463 		if (dp->d_ino == 0 && strcmp(dp->d_name, "/") != 0)
464 			continue;
465 		if (dp->d_ino >= maxino) {
466 			dprintf(stderr, "corrupted directory: bad inum %d\n",
467 				dp->d_ino);
468 			continue;
469 		}
470 		return (dp);
471 	}
472 }
473 
474 /*
475  * Simulate the opening of a directory
476  */
477 RST_DIR *
478 rst_opendir(name)
479 	char *name;
480 {
481 	struct inotab *itp;
482 	RST_DIR *dirp;
483 	ino_t ino;
484 
485 	if ((ino = dirlookup(name)) > 0 &&
486 	    (itp = inotablookup(ino)) != NULL) {
487 		dirp = opendirfile(dirfile);
488 		rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
489 		return (dirp);
490 	}
491 	return (0);
492 }
493 
494 /*
495  * In our case, there is nothing to do when closing a directory.
496  */
497 void
498 rst_closedir(dirp)
499 	RST_DIR *dirp;
500 {
501 
502 	close(dirp->dd_fd);
503 	free(dirp);
504 	return;
505 }
506 
507 /*
508  * Simulate finding the current offset in the directory.
509  */
510 static long
511 rst_telldir(dirp)
512 	RST_DIR *dirp;
513 {
514 	return ((long)lseek(dirp->dd_fd,
515 	    (off_t)0, SEEK_CUR) - dirp->dd_size + dirp->dd_loc);
516 }
517 
518 /*
519  * Open a directory file.
520  */
521 static RST_DIR *
522 opendirfile(name)
523 	char *name;
524 {
525 	register RST_DIR *dirp;
526 	register int fd;
527 
528 	if ((fd = open(name, O_RDONLY)) == -1)
529 		return (NULL);
530 	if ((dirp = malloc(sizeof(RST_DIR))) == NULL) {
531 		(void)close(fd);
532 		return (NULL);
533 	}
534 	dirp->dd_fd = fd;
535 	dirp->dd_loc = 0;
536 	return (dirp);
537 }
538 
539 /*
540  * Set the mode, owner, and times for all new or changed directories
541  */
542 void
543 setdirmodes(flags)
544 	int flags;
545 {
546 	FILE *mf;
547 	struct modeinfo node;
548 	struct entry *ep;
549 	char *cp;
550 
551 	vprintf(stdout, "Set directory mode, owner, and times.\n");
552 	(void) sprintf(modefile, "%s/rstmode%d", _PATH_TMP, dumpdate);
553 	mf = fopen(modefile, "r");
554 	if (mf == NULL) {
555 		fprintf(stderr, "fopen: %s\n", strerror(errno));
556 		fprintf(stderr, "cannot open mode file %s\n", modefile);
557 		fprintf(stderr, "directory mode, owner, and times not set\n");
558 		return;
559 	}
560 	clearerr(mf);
561 	for (;;) {
562 		(void) fread((char *)&node, 1, sizeof(struct modeinfo), mf);
563 		if (feof(mf))
564 			break;
565 		ep = lookupino(node.ino);
566 		if (command == 'i' || command == 'x') {
567 			if (ep == NULL)
568 				continue;
569 			if ((flags & FORCE) == 0 && ep->e_flags & EXISTED) {
570 				ep->e_flags &= ~NEW;
571 				continue;
572 			}
573 			if (node.ino == ROOTINO &&
574 		   	    reply("set owner/mode for '.'") == FAIL)
575 				continue;
576 		}
577 		if (ep == NULL) {
578 			panic("cannot find directory inode %d\n", node.ino);
579 		} else {
580 			cp = myname(ep);
581 			(void) chown(cp, node.uid, node.gid);
582 			(void) chmod(cp, node.mode);
583 			utimes(cp, node.timep);
584 			ep->e_flags &= ~NEW;
585 		}
586 	}
587 	if (ferror(mf))
588 		panic("error setting directory modes\n");
589 	(void) fclose(mf);
590 }
591 
592 /*
593  * Generate a literal copy of a directory.
594  */
595 int
596 genliteraldir(name, ino)
597 	char *name;
598 	ino_t ino;
599 {
600 	register struct inotab *itp;
601 	int ofile, dp, i, size;
602 	char buf[BUFSIZ];
603 
604 	itp = inotablookup(ino);
605 	if (itp == NULL)
606 		panic("Cannot find directory inode %d named %s\n", ino, name);
607 	if ((ofile = creat(name, 0666)) < 0) {
608 		fprintf(stderr, "%s: ", name);
609 		(void) fflush(stderr);
610 		fprintf(stderr, "cannot create file: %s\n", strerror(errno));
611 		return (FAIL);
612 	}
613 	rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
614 	dp = dup(dirp->dd_fd);
615 	for (i = itp->t_size; i > 0; i -= BUFSIZ) {
616 		size = i < BUFSIZ ? i : BUFSIZ;
617 		if (read(dp, buf, (int) size) == -1) {
618 			fprintf(stderr,
619 				"write error extracting inode %d, name %s\n",
620 				curfile.ino, curfile.name);
621 			fprintf(stderr, "read: %s\n", strerror(errno));
622 			done(1);
623 		}
624 		if (!Nflag && write(ofile, buf, (int) size) == -1) {
625 			fprintf(stderr,
626 				"write error extracting inode %d, name %s\n",
627 				curfile.ino, curfile.name);
628 			fprintf(stderr, "write: %s\n", strerror(errno));
629 			done(1);
630 		}
631 	}
632 	(void) close(dp);
633 	(void) close(ofile);
634 	return (GOOD);
635 }
636 
637 /*
638  * Determine the type of an inode
639  */
640 int
641 inodetype(ino)
642 	ino_t ino;
643 {
644 	struct inotab *itp;
645 
646 	itp = inotablookup(ino);
647 	if (itp == NULL)
648 		return (LEAF);
649 	return (NODE);
650 }
651 
652 /*
653  * Allocate and initialize a directory inode entry.
654  * If requested, save its pertinent mode, owner, and time info.
655  */
656 static struct inotab *
657 allocinotab(ino, dip, seekpt)
658 	ino_t ino;
659 	struct dinode *dip;
660 	long seekpt;
661 {
662 	register struct inotab	*itp;
663 	struct modeinfo node;
664 
665 	itp = calloc(1, sizeof(struct inotab));
666 	if (itp == NULL)
667 		panic("no memory directory table\n");
668 	itp->t_next = inotab[INOHASH(ino)];
669 	inotab[INOHASH(ino)] = itp;
670 	itp->t_ino = ino;
671 	itp->t_seekpt = seekpt;
672 	if (mf == NULL)
673 		return(itp);
674 	node.ino = ino;
675 	node.timep[0].tv_sec = dip->di_atime.ts_sec;
676 	node.timep[0].tv_usec = dip->di_atime.ts_nsec / 1000;
677 	node.timep[1].tv_sec = dip->di_mtime.ts_sec;
678 	node.timep[1].tv_usec = dip->di_mtime.ts_nsec / 1000;
679 	node.mode = dip->di_mode;
680 	node.uid = dip->di_uid;
681 	node.gid = dip->di_gid;
682 	(void) fwrite((char *)&node, 1, sizeof(struct modeinfo), mf);
683 	return(itp);
684 }
685 
686 /*
687  * Look up an inode in the table of directories
688  */
689 static struct inotab *
690 inotablookup(ino)
691 	ino_t	ino;
692 {
693 	register struct inotab *itp;
694 
695 	for (itp = inotab[INOHASH(ino)]; itp != NULL; itp = itp->t_next)
696 		if (itp->t_ino == ino)
697 			return(itp);
698 	return (NULL);
699 }
700 
701 /*
702  * Clean up and exit
703  */
704 void
705 done(exitcode)
706 	int exitcode;
707 {
708 
709 	closemt();
710 	if (modefile[0] != '#')
711 		(void) unlink(modefile);
712 	if (dirfile[0] != '#')
713 		(void) unlink(dirfile);
714 	exit(exitcode);
715 }
716