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