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