xref: /original-bsd/sbin/restore/main.c (revision a13d7ea1)
1 /* Copyright (c) 1981 Regents of the University of California */
2 
3 char version[] = "@(#)main.c 1.16 03/25/82";
4 
5 /*	Modified to include h option (recursively extract all files within
6  *	a subtree) and m option (recreate the heirarchical structure of
7  *	that subtree and move extracted files to their proper homes).
8  *	8/29/80		by Mike Litzkow
9  *
10  *	Includes the s (skip files) option for use with multiple dumps on
11  *	a single tape.
12  */
13 
14 /* static char *sccsid = "@(#)restor.c	4.3 (Berkeley) 6/3/81"; */
15 
16 #define MAXINO	3000
17 #define BITS	8
18 #define NCACHE	3
19 #define SIZEINC 10
20 
21 #ifndef STANDALONE
22 #include <stdio.h>
23 #include <signal.h>
24 #endif
25 #include "../h/param.h"
26 #include "../h/inode.h"
27 #include "../h/fs.h"
28 #include "../h/buf.h"
29 #include "../h/ndir.h"
30 #include "../h/user.h"
31 #include "../h/dumprestor.h"
32 #include <sys/mtio.h>
33 
34 #define ODIRSIZ 14
35 struct odirect {
36 	u_short	d_ino;
37 	char	d_name[ODIRSIZ];
38 };
39 
40 #define	MWORD(m,i) (m[(unsigned)(i-1)/NBBY])
41 #define	MBIT(i)	(1<<((unsigned)(i-1)%NBBY))
42 #define	BIS(i,w)	(MWORD(w,i) |=  MBIT(i))
43 #define	BIC(i,w)	(MWORD(w,i) &= ~MBIT(i))
44 #define	BIT(i,w)	(MWORD(w,i) & MBIT(i))
45 
46 ino_t	ino, maxi;
47 struct inode *cur_ip;
48 
49 int	eflag = 0, hflag = 0, mflag = 0, cvtdir = 0;
50 
51 extern	long mounted;
52 dev_t	dev = 0;
53 struct fs *fsptr;
54 struct inode parentino;
55 char	tapename[] = "/dev/rmt8";
56 char	*magtape = tapename;
57 int	mt;
58 int	dumpnum = 1;
59 int	volno = 1;
60 int	curblk = 0;
61 int	bct = NTREC+1;
62 char	tbf[NTREC*TP_BSIZE];
63 
64 #ifdef STANDALONE
65 char	mbuf[50];
66 #endif
67 
68 daddr_t	seekpt;
69 FILE	*df;
70 DIR	*dirp;
71 int	ofile;
72 char	dirfile[] = "rstXXXXXX";
73 char	lnkbuf[MAXPATHLEN + 1];
74 int	pathlen;
75 
76 #define INOHASH(val) (val % MAXINO)
77 struct inotab {
78 	struct inotab *t_next;
79 	ino_t	t_ino;
80 	daddr_t	t_seekpt;
81 } *inotab[MAXINO];
82 
83 #define XISDIR	1
84 #define XTRACTD	2
85 #define XINUSE	4
86 #define XLINKED	8
87 struct xtrlist {
88 	struct xtrlist	*x_next;
89 	struct xtrlist	*x_linkedto;
90 	time_t		x_timep[2];
91 	ino_t		x_ino;
92 	char		x_flags;
93 	char 		x_name[1];
94 	/* actually longer */
95 } *xtrlist[MAXINO];
96 int xtrcnt = 0;
97 
98 #ifdef STANDALONE
99 #define msiz 8192
100 char	dumpmap[msiz];
101 char	clrimap[msiz];
102 #else
103 int	msiz;
104 char	*dumpmap;
105 char	*clrimap;
106 #endif
107 
108 char	clearedbuf[MAXBSIZE];
109 
110 extern char *ctime();
111 ino_t search();
112 int dirwrite();
113 char **envp;
114 
115 main(argc, argv, arge)
116 	int argc;
117 	char *argv[];
118 	char **arge;
119 {
120 	register char *cp;
121 	char command;
122 	int (*signal())();
123 	int done();
124 
125 	if (signal(SIGINT, done) == SIG_IGN)
126 		signal(SIGINT, SIG_IGN);
127 	if (signal(SIGTERM, done) == SIG_IGN)
128 		signal(SIGTERM, SIG_IGN);
129 #ifndef STANDALONE
130 	envp = arge;
131 	mktmp(dirfile);
132 	if (argc < 2) {
133 usage:
134 		fprintf(stderr, "Usage: restor x[s|m|h|v] file file..., restor r|R filesys, or restor t\n");
135 		done(1);
136 	}
137 	argv++;
138 	argc -= 2;
139 	for (cp = *argv++; *cp; cp++) {
140 		switch (*cp) {
141 		case '-':
142 			break;
143 		case 'f':
144 			magtape = *argv++;
145 			argc--;
146 			break;
147 		/* s dumpnum (skip to) for multifile dump tapes */
148 		case 's':
149 			dumpnum = atoi(*argv++);
150 			if(dumpnum <= 0) {
151 				fprintf(stderr, "Dump number must be a positive integer\n");
152 				done(1);
153 			}
154 			argc--;
155 			break;
156 		case 'h':
157 			hflag++;
158 			break;
159 		case 'm':
160 			mflag++;
161 			break;
162 		case 'r':
163 		case 'R':
164 		case 't':
165 		case 'x':
166 			command = *cp;
167 			break;
168 		default:
169 			fprintf(stderr, "Bad key character %c\n", *cp);
170 			goto usage;
171 		}
172 	}
173 	if (command == 'x') {
174 		df = fopen(dirfile, "w");
175 		if (df == 0) {
176 			fprintf(stderr, "restor: %s - cannot create directory temporary\n", dirfile);
177 			done(1);
178 		}
179 		mounted++;
180 		xmount(envp);
181 	}
182 	doit(command, argc, argv);
183 	done(0);
184 #else
185 	magtape = "tape";
186 	doit('r', 1, 0);
187 #endif
188 }
189 
190 doit(command, argc, argv)
191 	char	command;
192 	int	argc;
193 	char	*argv[];
194 {
195 	struct mtop tcom;
196 
197 #ifndef STANDALONE
198 	if ((mt = open(magtape, 0)) < 0) {
199 		fprintf(stderr, "%s: cannot open tape\n", magtape);
200 		done(1);
201 	}
202 	if (dumpnum != 1) {
203 		tcom.mt_op = MTFSF;
204 		tcom.mt_count = dumpnum -1;
205 		if (ioctl(mt,MTIOCTOP,&tcom) < 0)
206 			perror("ioctl MTFSF");
207 	}
208 #else
209 	do {
210 		fprintf(stderr, "Tape? ");
211 		gets(mbuf);
212 		mt = open(mbuf, 0);
213 	} while (mt == -1);
214 	magtape = mbuf;
215 #endif
216 	blkclr(clearedbuf, MAXBSIZE);
217 	switch(command) {
218 #ifndef STANDALONE
219 	case 't':
220 		if (readhdr(&spcl) == 0) {
221 			fprintf(stderr, "Tape is not a dump tape\n");
222 			done(1);
223 		}
224 		fprintf(stdout, "Dump   date: %s", ctime(&spcl.c_date));
225 		fprintf(stdout, "Dumped from: %s", ctime(&spcl.c_ddate));
226 		return;
227 	case 'x':
228 		extractfiles(argc, argv);
229 		return;
230 #endif
231 	case 'r':
232 	case 'R':
233 		restorfiles(command, argv);
234 		return;
235 	}
236 }
237 
238 #ifndef STANDALONE
239 extractfiles(argc, argv)
240 	int argc;
241 	char **argv;
242 {
243 	char	*ststore();
244 	register struct xtrlist *xp;
245 	struct xtrlist **xpp;
246 	ino_t	d;
247 	int	xtrfile(), xtrskip(), xtrcvtdir(), xtrcvtskip(),
248 		xtrlnkfile(), xtrlnkskip(), null();
249 	int	mode, uid, gid;
250 	char	name[BUFSIZ + 1];
251 
252 	if (readhdr(&spcl) == 0) {
253 		fprintf(stderr, "Tape is not a dump tape\n");
254 		done(1);
255 	}
256 	if (checkvol(&spcl, 1) == 0) {
257 		fprintf(stderr, "Tape is not volume 1 of the dump\n");
258 	}
259 	fsptr = getfs(dev);
260 	msiz = roundup(howmany(fsptr->fs_ipg * fsptr->fs_ncg, NBBY), TP_BSIZE);
261 	clrimap = (char *)calloc(msiz, sizeof(char));
262 	dumpmap = (char *)calloc(msiz, sizeof(char));
263 	pass1(1);  /* This sets the various maps on the way by */
264 	while (argc--) {
265 		if ((d = psearch(*argv)) == 0 || BIT(d,dumpmap) == 0) {
266 			fprintf(stdout, "%s: not on tape\n", *argv++);
267 			continue;
268 		}
269 		if (mflag)
270 			checkdir(*argv);
271 		if(hflag)
272 			getleaves(d, *argv++);
273 		else
274 			allocxtr(d, *argv++, XINUSE);
275 	}
276 	if (dumpnum > 1) {
277 		/*
278 		 * if this is a multi-dump tape we always start with
279 		 * volume 1, so as to avoid accidentally restoring
280 		 * from a different dump!
281 		 */
282 		resetmt();
283 		dumpnum = 1;
284 		volno = 1;
285 		readhdr(&spcl);
286 		goto rbits;
287 	}
288 newvol:
289 	resetmt();
290 getvol:
291 	fprintf(stderr, "Mount desired tape volume; Specify volume #: ");
292 	if (gets(tbf) == NULL)
293 		return;
294 	volno = atoi(tbf);
295 	if (volno <= 0) {
296 		fprintf(stderr, "Volume numbers are positive numerics\n");
297 		goto getvol;
298 	}
299 	if (readhdr(&spcl) == 0) {
300 		fprintf(stderr, "tape is not dump tape\n");
301 		goto newvol;
302 	}
303 	if (checkvol(&spcl, volno) == 0) {
304 		fprintf(stderr, "Wrong volume (%d)\n", spcl.c_volume);
305 		goto newvol;
306 	}
307 rbits:
308 	while (gethead(&spcl) == 0)
309 		;
310 	if (checktype(&spcl, TS_INODE) == 1) {
311 		fprintf(stderr, "Can't find inode mask!\n");
312 		goto newvol;
313 	}
314 	if (checktype(&spcl, TS_BITS) == 0)
315 		goto rbits;
316 	readbits(dumpmap);
317 	while (xtrcnt > 0) {
318 again:
319 		if (ishead(&spcl) == 0)
320 			while(gethead(&spcl) == 0)
321 				;
322 		if (checktype(&spcl, TS_END) == 1) {
323 			fprintf(stderr, "end of tape\n");
324 			break;
325 		}
326 		if (checktype(&spcl, TS_INODE) == 0) {
327 			gethead(&spcl);
328 			goto again;
329 		}
330 		d = spcl.c_inumber;
331 		for (xp = xtrlist[INOHASH(d)]; xp; xp = xp->x_next) {
332 			if (d != xp->x_ino)
333 				continue;
334 			if (xp->x_flags & XLINKED)
335 				continue;
336 			xp->x_timep[0] = spcl.c_dinode.di_atime;
337 			xp->x_timep[1] = spcl.c_dinode.di_mtime;
338 			mode = spcl.c_dinode.di_mode;
339 			if (mflag)
340 				strcpy(name, xp->x_name);
341 			else
342 				sprintf(name, "%u", xp->x_ino);
343 			switch (mode & IFMT) {
344 			default:
345 				fprintf(stderr, "%s: unknown file type\n", name);
346 				xp->x_flags |= XTRACTD;
347 				xtrcnt--;
348 				goto skipfile;
349 			case IFCHR:
350 			case IFBLK:
351 				fprintf(stdout, "extract special file %s\n", name);
352 				if (xmknod(name, mode, spcl.c_dinode.di_rdev)) {
353 					fprintf(stderr, "%s: cannot create special file\n", name);
354 					xp->x_flags |= XTRACTD;
355 					xtrcnt--;
356 					goto skipfile;
357 				}
358 				getfile(null, null, spcl.c_dinode.di_size);
359 				break;
360 			case IFDIR:
361 				if (mflag) {
362 					fprintf(stdout, "extract directory %s\n", name);
363 					strncat(name, "/.", BUFSIZ);
364 					checkdir(name);
365 					xchown(xp->x_name, spcl.c_dinode.di_uid, spcl.c_dinode.di_gid);
366 					getfile(null, null, spcl.c_dinode.di_size);
367 					break;
368 				}
369 				fprintf(stdout, "extract file %s\n", name);
370 				if ((ofile = xcreat(name, 0666)) < 0) {
371 					fprintf(stderr, "%s: cannot create file\n", name);
372 					xp->x_flags |= XTRACTD;
373 					xtrcnt--;
374 					goto skipfile;
375 				}
376 				xchown(name, spcl.c_dinode.di_uid, spcl.c_dinode.di_gid);
377 				if (cvtdir) {
378 					getfile(xtrcvtdir, xtrcvtskip,
379 					    spcl.c_dinode.di_size);
380 					flushent(xtrfile);
381 				} else
382 					getfile(xtrfile, xtrskip,
383 					    spcl.c_dinode.di_size);
384 				xclose(ofile);
385 				break;
386 			case IFLNK:
387 				fprintf(stdout, "extract symbolic link %s\n", name);
388 				uid = spcl.c_dinode.di_uid;
389 				gid = spcl.c_dinode.di_gid;
390 				lnkbuf[0] = '\0';
391 				pathlen = 0;
392 				getfile(xtrlnkfile, xtrlnkskip, spcl.c_dinode.di_size);
393 				if (xsymlink(lnkbuf, name) < 0) {
394 					fprintf(stderr, "%s: cannot create symbolic link\n", name);
395 					xp->x_flags |= XTRACTD;
396 					xtrcnt--;
397 					goto finished;
398 				}
399 				xchown(name, uid, gid);
400 				break;
401 			case IFREG:
402 				fprintf(stdout, "extract file %s\n", name);
403 				if ((ofile = xcreat(name, 0666)) < 0) {
404 					fprintf(stderr, "%s: cannot create file\n", name);
405 					xp->x_flags |= XTRACTD;
406 					xtrcnt--;
407 					goto skipfile;
408 				}
409 				xchown(name, spcl.c_dinode.di_uid, spcl.c_dinode.di_gid);
410 				getfile(xtrfile, xtrskip, spcl.c_dinode.di_size);
411 				xclose(ofile);
412 				break;
413 			}
414 			xchmod(name, mode);
415 			xutime(name, xp->x_timep);
416 			xp->x_flags |= XTRACTD;
417 			xtrcnt--;
418 			goto finished;
419 		}
420 skipfile:
421 		getfile(null, null, spcl.c_dinode.di_size);
422 finished:
423 		;
424 	}
425 	if (xtrcnt == 0 && !mflag)
426 		return;
427 	for (xpp = xtrlist; xpp < &xtrlist[MAXINO]; xpp++) {
428 		for (xp = *xpp; xp; xp = xp->x_next) {
429 			if (mflag && (xp->x_flags & XISDIR))
430 				xutime(xp->x_name, xp->x_timep);
431 			if (xp->x_flags & XTRACTD)
432 				continue;
433 			if ((xp->x_flags & XLINKED) == 0) {
434 				fprintf(stderr, "cannot find file %s\n",
435 					xp->x_name);
436 				continue;
437 			}
438 			if (!mflag)
439 				continue;
440 			fprintf(stdout, "link %s to %s\n",
441 				xp->x_linkedto->x_name, xp->x_name);
442 			if (xlink(xp->x_linkedto->x_name, xp->x_name) < 0)
443 				fprintf(stderr, "link %s to %s failed\n",
444 					xp->x_linkedto->x_name, xp->x_name);
445 		}
446 	}
447 }
448 #endif
449 
450 restorfiles(command, argv)
451 	char command;
452 	char **argv;
453 {
454 	int null(), rstrfile(), rstrskip(), rstrcvtdir(), rstrcvtskip();
455 	register struct dinode *dp;
456 	register struct inode *ip;
457 	int mode, type;
458 	char mount[BUFSIZ + 1];
459 	char *ptr[2];
460 
461 	mount[0] = '\0';
462 	strcpy(mount, "MOUNT=");
463 #ifndef STANDALONE
464 	strncat(mount, *argv, BUFSIZ);
465 #else
466 	fprintf(stderr, "Disk? ");
467 	gets(&mount[6]);
468 #endif
469 	ptr[0] = mount;
470 	ptr[1] = 0;
471 	mounted++;
472 	xmount(ptr);
473 	iput(u.u_cdir); /* release root inode */
474 	iput(u.u_rdir); /* release root inode */
475 	fsptr = getfs(dev);
476 	parentino.i_fs = fsptr;
477 	parentino.i_dev = dev;
478 	maxi = fsptr->fs_ipg * fsptr->fs_ncg;
479 #ifndef STANDALONE
480 	msiz = roundup(howmany(maxi, NBBY), TP_BSIZE);
481 	clrimap = (char *)calloc(msiz, sizeof(char));
482 	dumpmap = (char *)calloc(msiz, sizeof(char));
483 	if (command == 'R') {
484 		fprintf(stderr, "Enter starting volume number: ");
485 		if (gets(tbf) == EOF) {
486 			volno = 1;
487 			fprintf(stderr, "\n");
488 		}
489 		else
490 			volno = atoi(tbf);
491 	}
492 	else
493 #endif
494 		volno = 1;
495 	fprintf(stderr, "Last chance before scribbling on %s. ",
496 #ifdef STANDALONE
497 							"disk");
498 #else
499 							*argv);
500 #endif
501 	while (getchar() != '\n');
502 	if (readhdr(&spcl) == 0) {
503 		fprintf(stderr, "Missing volume record\n");
504 		done(1);
505 	}
506 	if (checkvol(&spcl, volno) == 0) {
507 		fprintf(stderr, "Tape is not volume %d\n", volno);
508 		done(1);
509 	}
510 	pass1(0);
511 	gethead(&spcl); /* volume header already checked above */
512 	gethead(&spcl);
513 	for (;;) {
514 		if (ishead(&spcl) == 0) {
515 			fprintf(stderr, "Missing header block\n");
516 			while (gethead(&spcl) == 0)
517 				;
518 			eflag++;
519 		}
520 		if (checktype(&spcl, TS_END) == 1) {
521 			fprintf(stderr, "End of tape\n");
522 			close(mt);
523 			return;
524 		}
525 		if (checktype(&spcl, TS_CLRI) == 1) {
526 			readbits(clrimap);
527 			/*
528 			 * if throwing away the root inode, must also
529 			 * discard the predefined lost+found directory.
530 			 */
531 			if (BIT(ROOTINO, clrimap))
532 				BIS(LOSTFOUNDINO + 1, clrimap);
533 			for (ino = ROOTINO; ino <= maxi; ino++)
534 				if (BIT(ino, clrimap) == 0) {
535 					if (!iexist(&parentino, ino))
536 						continue;
537 					ip = iget(dev, fsptr, ino);
538 					if (ip == NULL) {
539 						fprintf(stderr, "can't find inode %u\n", ino);
540 						done(1);
541 					}
542 					ip->i_nlink = 0;
543 					ip->i_flag |= ICHG;
544 					iput(ip);
545 				}
546 			continue;
547 		}
548 		if (checktype(&spcl, TS_BITS) == 1) {
549 			readbits(dumpmap);
550 			continue;
551 		}
552 		if (checktype(&spcl, TS_INODE) == 0) {
553 			fprintf(stderr, "Unknown header type\n");
554 			eflag++;
555 			gethead(&spcl);
556 			continue;
557 		}
558 		ino = spcl.c_inumber;
559 		if (eflag)
560 			fprintf(stderr, "Resynced at inode %u\n", ino);
561 		eflag = 0;
562 		if (ino > maxi) {
563 			fprintf(stderr, "%u: ilist too small\n", ino);
564 			gethead(&spcl);
565 			continue;
566 		}
567 		dp = &spcl.c_dinode;
568 		if (ino < ROOTINO) {
569 			getfile(null, null, dp->di_size);
570 			continue;
571 		}
572 		if (iexist(&parentino, ino)) {
573 			ip = iget(dev, fsptr, ino);
574 			if (ip == NULL) {
575 				fprintf(stderr, "can't find inode %u\n",
576 					ino);
577 				done(1);
578 			}
579 			ip->i_nlink = 0;
580 			ip->i_flag |= ICHG;
581 			iput(ip);
582 		}
583 		ip = ialloc(&parentino, ino, dp->di_mode);
584 		if (ip == NULL || ip->i_number != ino) {
585 			fprintf(stderr, "can't create inode %u\n", ino);
586 			done(1);
587 		}
588 		ip->i_mode = mode = dp->di_mode;
589 		ip->i_nlink = dp->di_nlink;
590 		ip->i_uid = dp->di_uid;
591 		ip->i_gid = dp->di_gid;
592 		ip->i_atime = dp->di_atime;
593 		ip->i_mtime = dp->di_mtime;
594 		ip->i_ctime = dp->di_ctime;
595 		type = ip->i_mode & IFMT;
596 		if (type == IFCHR || type == IFBLK)
597 			ip->i_rdev = dp->di_rdev;
598 		ip->i_size = 0;
599 		cur_ip = ip;
600 		u.u_offset = 0;
601 		u.u_segflg = 1;
602 		if (cvtdir && type == IFDIR) {
603 			getfile(rstrcvtdir, rstrcvtskip, dp->di_size);
604 			flushent(rstrfile);
605 		} else
606 			getfile(rstrfile, rstrskip, dp->di_size);
607 		ip->i_mode = mode;
608 		ip->i_flag &= ~(IUPD|IACC);
609 		ip->i_flag |= ICHG;
610 		iput(ip);
611 	}
612 }
613 
614 /*
615  * Read the tape, bulding up a directory structure for extraction
616  * by name
617  */
618 #ifndef STANDALONE
619 pass1(savedir)
620 	int savedir;
621 {
622 	register int i;
623 	register struct dinode *ip;
624 	struct direct nulldir;
625 	char buf[TP_BSIZE];
626 	int putdir(), null(), dirwrite();
627 
628 	nulldir.d_ino = 1;
629 	nulldir.d_namlen = 1;
630 	strncpy(nulldir.d_name, "/", nulldir.d_namlen);
631 	nulldir.d_reclen = DIRSIZ(&nulldir);
632 	while (gethead(&spcl) == 0) {
633 		fprintf(stderr, "Can't find directory header!\n");
634 	}
635 	for (;;) {
636 		if (checktype(&spcl, TS_BITS) == 1) {
637 			readbits(dumpmap);
638 			continue;
639 		}
640 		if (checktype(&spcl, TS_CLRI) == 1) {
641 			readbits(clrimap);
642 			continue;
643 		}
644 		if (checktype(&spcl, TS_INODE) == 0) {
645 finish:
646 			if (savedir) {
647 				fclose(df);
648 				dirp = opendir(dirfile);
649 				if (dirp == NULL)
650 					perror("opendir");
651 			}
652 			resetmt();
653 			return;
654 		}
655 		ip = &spcl.c_dinode;
656 		i = ip->di_mode & IFMT;
657 		if (i != IFDIR) {
658 			goto finish;
659 		}
660 		if (spcl.c_inumber == ROOTINO) {
661 			readtape(buf);
662 			bct--; /* push back this block */
663 			if (((struct direct *)buf)->d_ino != ROOTINO) {
664 				if (((struct odirect *)buf)->d_ino != ROOTINO) {
665 					fprintf(stderr, "bad root directory\n");
666 					done(1);
667 				}
668 				fprintf(stderr, "converting to new directory format\n");
669 				cvtdir = 1;
670 			}
671 			if (!savedir && !cvtdir) {
672 				/* if no conversion, just return */
673 				goto finish;
674 			}
675 		}
676 		allocinotab(spcl.c_inumber, seekpt);
677 		if (savedir) {
678 			getfile(putdir, null, spcl.c_dinode.di_size);
679 			putent(&nulldir, dirwrite);
680 			flushent(dirwrite);
681 		} else {
682 			getfile(null, null, spcl.c_dinode.di_size);
683 		}
684 	}
685 }
686 #endif
687 
688 /*
689  * Put the directory entries in the directory file
690  */
691 #ifndef STANDALONE
692 putdir(buf, size)
693 	char *buf;
694 	int size;
695 {
696 	struct direct cvtbuf;
697 	register struct odirect *odp;
698 	struct odirect *eodp;
699 	register struct direct *dp;
700 	long loc, i;
701 
702 	if (cvtdir) {
703 		eodp = (struct odirect *)&buf[size];
704 		for (odp = (struct odirect *)buf; odp < eodp; odp++)
705 			if (odp->d_ino != 0) {
706 				dcvt(odp, &cvtbuf);
707 				putent(&cvtbuf, dirwrite);
708 			}
709 	} else {
710 		for (loc = 0; loc < size; ) {
711 			dp = (struct direct *)(buf + loc);
712 			i = DIRBLKSIZ - (loc & (DIRBLKSIZ - 1));
713 			if (dp->d_reclen <= 0 || dp->d_reclen > i) {
714 				loc += i;
715 				continue;
716 			}
717 			loc += dp->d_reclen;
718 			if (dp->d_ino != 0)
719 				putent(dp, dirwrite);
720 		}
721 	}
722 }
723 
724 /*
725  *	Recursively find names and inumbers of all files in subtree
726  *	pname and put them in xtrlist[]
727  */
728 getleaves(ino, pname)
729 	ino_t ino;
730 	char *pname;
731 {
732 	register struct inotab *itp;
733 	int namelen;
734 	long bpt;
735 	register struct direct *dp;
736 	char locname[BUFSIZ + 1];
737 
738 	if (BIT(ino, dumpmap) == 0) {
739 		fprintf(stdout, "%s: not on the tape\n", pname);
740 		return;
741 	}
742 	for (itp = inotab[INOHASH(ino)]; itp; itp = itp->t_next) {
743 		if (itp->t_ino != ino)
744 			continue;
745 		/*
746 		 * pname is a directory name
747 		 */
748 		allocxtr(ino, pname, XISDIR);
749 		/*
750 		 * begin search through the directory
751 		 * skipping over "." and ".."
752 		 */
753 		strncpy(locname, pname, BUFSIZ);
754 		strncat(locname, "/", BUFSIZ);
755 		namelen = strlen(locname);
756 		seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
757 		dp = readdir(dirp);
758 		dp = readdir(dirp);
759 		dp = readdir(dirp);
760 		bpt = telldir(dirp);
761 		/*
762 		 * "/" signals end of directory
763 		 */
764 		while (dp->d_namlen != 1 || dp->d_name[0] != '/') {
765 			locname[namelen] = '\0';
766 			if (namelen + dp->d_namlen >= BUFSIZ) {
767 				fprintf(stderr, "%s%s: name exceedes %d char\n",
768 					locname, dp->d_name, BUFSIZ);
769 				continue;
770 			}
771 			strncat(locname, dp->d_name, dp->d_namlen);
772 			getleaves(dp->d_ino, locname);
773 			seekdir(dirp, bpt, itp->t_seekpt);
774 			dp = readdir(dirp);
775 			bpt = telldir(dirp);
776 		}
777 		return;
778 	}
779 	/*
780 	 * locname is name of a simple file
781 	 */
782 	allocxtr(ino, pname, XINUSE);
783 }
784 
785 /*
786  * Search the directory tree rooted at inode ROOTINO
787  * for the path pointed at by n
788  */
789 psearch(n)
790 	char	*n;
791 {
792 	register char *cp, *cp1;
793 	char c;
794 
795 	ino = ROOTINO;
796 	if (*(cp = n) == '/')
797 		cp++;
798 next:
799 	cp1 = cp + 1;
800 	while (*cp1 != '/' && *cp1)
801 		cp1++;
802 	c = *cp1;
803 	*cp1 = 0;
804 	ino = search(ino, cp);
805 	if (ino == 0) {
806 		*cp1 = c;
807 		return(0);
808 	}
809 	*cp1 = c;
810 	if (c == '/') {
811 		cp = cp1+1;
812 		goto next;
813 	}
814 	return(ino);
815 }
816 
817 /*
818  * search the directory inode ino
819  * looking for entry cp
820  */
821 ino_t
822 search(inum, cp)
823 	ino_t	inum;
824 	char	*cp;
825 {
826 	register struct direct *dp;
827 	register struct inotab *itp;
828 	int len;
829 
830 	for (itp = inotab[INOHASH(inum)]; itp; itp = itp->t_next)
831 		if (itp->t_ino == inum)
832 			goto found;
833 	return(0);
834 found:
835 	seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
836 	len = strlen(cp);
837 	do {
838 		dp = readdir(dirp);
839 		if (dp->d_namlen == 1 && dp->d_name[0] == '/')
840 			return(0);
841 	} while (dp->d_namlen != len || strncmp(dp->d_name, cp, len));
842 	return(dp->d_ino);
843 }
844 #endif
845 
846 /*
847  * Do the file extraction, calling the supplied functions
848  * with the blocks
849  */
850 getfile(f1, f2, size)
851 	int	(*f2)(), (*f1)();
852 	long	size;
853 {
854 	register int i;
855 	char buf[MAXBSIZE / TP_BSIZE][TP_BSIZE];
856 	union u_spcl addrblk;
857 #	define addrblock addrblk.s_spcl
858 
859 	addrblock = spcl;
860 	for (;;) {
861 		for (i = 0; i < addrblock.c_count; i++) {
862 			if (addrblock.c_addr[i]) {
863 				readtape(&buf[curblk++][0]);
864 				if (curblk == BLKING(fsptr) * fsptr->fs_frag) {
865 					(*f1)(buf, size > TP_BSIZE ?
866 					     (long) (BLKING(fsptr) * fsptr->fs_frag * TP_BSIZE) :
867 					     (curblk - 1) * TP_BSIZE + size);
868 					curblk = 0;
869 				}
870 			} else {
871 				if (curblk > 0) {
872 					(*f1)(buf, size > TP_BSIZE ?
873 					     (long) (curblk * TP_BSIZE) :
874 					     (curblk - 1) * TP_BSIZE + size);
875 					curblk = 0;
876 				}
877 				(*f2)(clearedbuf, size > TP_BSIZE ?
878 					(long) TP_BSIZE : size);
879 			}
880 			if ((size -= TP_BSIZE) <= 0) {
881 eloop:
882 				while (gethead(&spcl) == 0)
883 					;
884 				if (checktype(&spcl, TS_ADDR) == 1)
885 					goto eloop;
886 				goto out;
887 			}
888 		}
889 		if (gethead(&addrblock) == 0) {
890 			fprintf(stderr, "Missing address (header) block, ino%u\n", ino);
891 			goto eloop;
892 		}
893 		if (checktype(&addrblock, TS_ADDR) == 0) {
894 			spcl = addrblock;
895 			goto out;
896 		}
897 	}
898 out:
899 	if (curblk > 0) {
900 		(*f1)(buf, (curblk * TP_BSIZE) + size);
901 		curblk = 0;
902 	}
903 }
904 
905 /*
906  * The next routines are called during file extraction to
907  * put the data into the right form and place.
908  */
909 #ifndef STANDALONE
910 xtrfile(buf, size)
911 	char	*buf;
912 	long	size;
913 {
914 	if (xwrite(ofile, buf, (int) size) == -1) {
915 		perror("extract write");
916 		done(1);
917 	}
918 }
919 
920 xtrskip(buf, size)
921 	char *buf;
922 	long size;
923 {
924 	if (xseek(ofile, size, 1) == -1) {
925 		perror("extract seek");
926 		done(1);
927 	}
928 }
929 
930 xtrcvtdir(buf, size)
931 	struct odirect *buf;
932 	long size;
933 {
934 	struct odirect *odp, *edp;
935 	struct direct *dp, cvtbuf;
936 
937 	edp = &buf[size / sizeof(struct odirect)];
938 	for (odp = buf; odp < edp; odp++) {
939 		dcvt(odp, &cvtbuf);
940 		putent(&cvtbuf, xtrfile);
941 	}
942 }
943 
944 xtrcvtskip(buf, size)
945 	char *buf;
946 	long size;
947 {
948 	fprintf(stderr, "unallocated block in directory\n");
949 	xtrskip(buf, size);
950 }
951 
952 xtrlnkfile(buf, size)
953 	char	*buf;
954 	long	size;
955 {
956 	pathlen += size;
957 	if (pathlen > MAXPATHLEN) {
958 		fprintf(stderr, "symbolic link name: %s; too long %d\n",
959 		    buf, size);
960 		done(1);
961 	}
962 	strcat(lnkbuf, buf);
963 }
964 
965 xtrlnkskip(buf, size)
966 	char *buf;
967 	long size;
968 {
969 	fprintf(stderr, "unallocated block in symbolic link\n");
970 	done(1);
971 }
972 #endif
973 
974 rstrfile(buf, size)
975 	char *buf;
976 	long size;
977 {
978 	u.u_base = buf;
979 	u.u_count = size;
980 	writei(cur_ip);
981 	if (u.u_error) {
982 		perror("restor write");
983 		done(1);
984 	}
985 }
986 
987 rstrskip(buf, size)
988 	char *buf;
989 	long size;
990 {
991 	u.u_offset += size;
992 }
993 
994 rstrcvtdir(buf, size)
995 	struct odirect *buf;
996 	long size;
997 {
998 	struct odirect *odp, *edp;
999 	struct direct *dp, cvtbuf;
1000 
1001 	edp = &buf[size / sizeof(struct odirect)];
1002 	for (odp = buf; odp < edp; odp++) {
1003 		dcvt(odp, &cvtbuf);
1004 		putent(&cvtbuf, rstrfile);
1005 	}
1006 }
1007 
1008 rstrcvtskip(buf, size)
1009 	char *buf;
1010 	long size;
1011 {
1012 	fprintf(stderr, "unallocated block in directory\n");
1013 	rstrskip(buf, size);
1014 }
1015 
1016 null() {;}
1017 
1018 /*
1019  * Do the tape i/o, dealing with volume changes
1020  * etc..
1021  */
1022 readtape(b)
1023 	char *b;
1024 {
1025 	register int i;
1026 	struct s_spcl tmpbuf;
1027 
1028 	if (bct >= NTREC) {
1029 		for (i = 0; i < NTREC; i++)
1030 			((struct s_spcl *)&tbf[i*TP_BSIZE])->c_magic = 0;
1031 		bct = 0;
1032 		if ((i = read(mt, tbf, NTREC*TP_BSIZE)) < 0) {
1033 			perror("Tape read error");
1034 			eflag++;
1035 			done(1);
1036 		}
1037 		if (i == 0) {
1038 			bct = NTREC + 1;
1039 			volno++;
1040 loop:
1041 			flsht();
1042 			close(mt);
1043 			fprintf(stderr, "Mount volume %d\n", volno);
1044 			while (getchar() != '\n')
1045 				;
1046 			if ((mt = open(magtape, 0)) == -1) {
1047 				fprintf(stderr, "Cannot open tape!\n");
1048 				goto loop;
1049 			}
1050 			if (readhdr(&tmpbuf) == 0) {
1051 				fprintf(stderr, "Not a dump tape.Try again\n");
1052 				goto loop;
1053 			}
1054 			if (checkvol(&tmpbuf, volno) == 0) {
1055 				fprintf(stderr, "Wrong tape. Try again\n");
1056 				goto loop;
1057 			}
1058 			readtape(b);
1059 			return;
1060 		}
1061 	}
1062 	blkcpy(&tbf[(bct++*TP_BSIZE)], b, TP_BSIZE);
1063 }
1064 
1065 flsht()
1066 {
1067 	bct = NTREC+1;
1068 }
1069 
1070 blkcpy(from, to, size)
1071 	char *from, *to;
1072 	int size;
1073 {
1074 	asm("	movc3	12(ap),*4(ap),*8(ap)");
1075 }
1076 
1077 blkclr(buf, size)
1078 	char *buf;
1079 	int size;
1080 {
1081 	asm("movc5	$0,(r0),$0,8(ap),*4(ap)");
1082 }
1083 
1084 resetmt()
1085 {
1086 	struct mtop tcom;
1087 
1088 	if(dumpnum > 1)
1089 		tcom.mt_op = MTBSF;
1090 	else
1091 		tcom.mt_op = MTREW;
1092 	tcom.mt_count = 1;
1093 	flsht();
1094 	if (ioctl(mt,MTIOCTOP,&tcom) == -1) {
1095 		/* kludge for disk dumps */
1096 		lseek(mt, (long)0, 0);
1097 	}
1098 	if (dumpnum > 1) {
1099 		tcom.mt_op = MTFSF;
1100 		tcom.mt_count = 1;
1101 		ioctl(mt,MTIOCTOP,&tcom);
1102 	}
1103 }
1104 
1105 checkvol(b, t)
1106 	struct s_spcl *b;
1107 	int t;
1108 {
1109 	if (b->c_volume == t)
1110 		return(1);
1111 	return(0);
1112 }
1113 
1114 readhdr(b)
1115 	struct s_spcl *b;
1116 {
1117 	if (gethead(b) == 0)
1118 		return(0);
1119 	if (checktype(b, TS_TAPE) == 0)
1120 		return(0);
1121 	return(1);
1122 }
1123 
1124 /*
1125  * read the tape into buf, then return whether or
1126  * or not it is a header block.
1127  */
1128 gethead(buf)
1129 	struct s_spcl *buf;
1130 {
1131 	readtape((char *)buf);
1132 	if (buf->c_magic != MAGIC || checksum((int *)buf) == 0)
1133 		return(0);
1134 	return(1);
1135 }
1136 
1137 /*
1138  * return whether or not the buffer contains a header block
1139  */
1140 ishead(buf)
1141 	struct s_spcl *buf;
1142 {
1143 	if (buf->c_magic != MAGIC || checksum((int *)buf) == 0)
1144 		return(0);
1145 	return(1);
1146 }
1147 
1148 checktype(b, t)
1149 	struct s_spcl *b;
1150 	int	t;
1151 {
1152 	return(b->c_type == t);
1153 }
1154 
1155 /*
1156  * read a bit mask from the tape into m.
1157  */
1158 readbits(m)
1159 	char	*m;
1160 {
1161 	register int i;
1162 
1163 	i = spcl.c_count;
1164 
1165 	while (i--) {
1166 		readtape((char *) m);
1167 		m += (TP_BSIZE/(NBBY/BITS));
1168 	}
1169 	while (gethead(&spcl) == 0)
1170 		;
1171 }
1172 
1173 checksum(b)
1174 	int *b;
1175 {
1176 	register int i, j;
1177 
1178 	j = sizeof(union u_spcl) / sizeof(int);
1179 	i = 0;
1180 	do
1181 		i += *b++;
1182 	while (--j);
1183 	if (i != CHECKSUM) {
1184 		fprintf(stderr, "Checksum error %o, ino %u\n", i, ino);
1185 		return(0);
1186 	}
1187 	return(1);
1188 }
1189 
1190 /*
1191  *	Check for access into each directory in the pathname of an extracted
1192  *	file and create such a directory if needed in preparation for moving
1193  *	the file to its proper home.
1194  */
1195 checkdir(name)
1196 	register char *name;
1197 {
1198 	register char *cp;
1199 	int i;
1200 
1201 	for (cp = name; *cp; cp++) {
1202 		if (*cp == '/') {
1203 			*cp = '\0';
1204 			if (xaccess(name, 01) < 0) {
1205 				register int pid, rp;
1206 
1207 				xumount();
1208 				mounted = 0;
1209 				if ((pid = fork()) == 0) {
1210 					execl("/bin/xmkdir", "xmkdir", name, 0);
1211 					execl("/usr/bin/xmkdir", "xmkdir", name, 0);
1212 					execl("./xmkdir", "xmkdir", name, 0);
1213 					fprintf(stderr, "xrestor: cannot find xmkdir!\n");
1214 					done(0);
1215 				}
1216 				while ((rp = wait(&i)) >= 0 && rp != pid)
1217 					;
1218 				mounted++;
1219 				xmount(envp);
1220 				fsptr = getfs(dev);
1221 				parentino.i_fs = fsptr;
1222 			}
1223 			*cp = '/';
1224 		}
1225 	}
1226 }
1227 
1228 /*
1229  * These variables are "local" to the following two functions.
1230  */
1231 char dirbuf[DIRBLKSIZ];
1232 long dirloc = 0;
1233 long prev = 0;
1234 
1235 /*
1236  * add a new directory entry to a file.
1237  */
1238 putent(dp, wrtfunc)
1239 	struct direct *dp;
1240 	int (*wrtfunc)();
1241 {
1242 	if (dp->d_ino == 0)
1243 		return;
1244 	for (;;) {
1245 		if (dp->d_reclen < DIRBLKSIZ - dirloc) {
1246 			blkcpy(dp, dirbuf + dirloc, dp->d_reclen);
1247 			prev = dirloc;
1248 			dirloc += dp->d_reclen;
1249 			return;
1250 		}
1251 		((struct direct *)(dirbuf + prev))->d_reclen = DIRBLKSIZ - prev;
1252 		(*wrtfunc)(dirbuf, DIRBLKSIZ);
1253 		dirloc = 0;
1254 	}
1255 }
1256 
1257 /*
1258  * flush out a directory that is finished.
1259  */
1260 flushent(wrtfunc)
1261 	int (*wrtfunc)();
1262 {
1263 	((struct direct *)(dirbuf + prev))->d_reclen = DIRBLKSIZ - prev;
1264 	(*wrtfunc)(dirbuf, dirloc);
1265 	dirloc = 0;
1266 }
1267 
1268 dirwrite(buf, size)
1269 	char *buf;
1270 	int size;
1271 {
1272 	fwrite(buf, 1, size, df);
1273 	seekpt = ftell(df);
1274 }
1275 
1276 dcvt(odp, ndp)
1277 	register struct odirect *odp;
1278 	register struct direct *ndp;
1279 {
1280 	struct inotab *itp;
1281 
1282 	blkclr(ndp, sizeof *ndp);
1283 	ndp->d_ino =  odp->d_ino;
1284 	strncpy(ndp->d_name, odp->d_name, ODIRSIZ);
1285 	ndp->d_namlen = strlen(ndp->d_name);
1286 	ndp->d_reclen = DIRSIZ(ndp);
1287 	/*
1288 	 * this quickly calculates if this inode is a directory.
1289 	 * Currently not maintained.
1290 	 *
1291 	for (itp = inotab[INOHASH(odp->d_ino)]; itp; itp = itp->t_next) {
1292 		if (itp->t_ino != odp->d_ino)
1293 			continue;
1294 		ndp->d_fmt = IFDIR;
1295 		break;
1296 	}
1297 	 */
1298 }
1299 
1300 /*
1301  * Open a directory.
1302  * Modified to allow any random file to be a legal directory.
1303  */
1304 DIR *
1305 opendir(name)
1306 	char *name;
1307 {
1308 	register DIR *dirp;
1309 
1310 	dirp = (DIR *)malloc(sizeof(DIR));
1311 	dirp->dd_fd = open(name, 0);
1312 	if (dirp->dd_fd == -1) {
1313 		free(dirp);
1314 		return NULL;
1315 	}
1316 	dirp->dd_loc = 0;
1317 	return dirp;
1318 }
1319 
1320 /*
1321  * Seek to an entry in a directory.
1322  * Only values returned by ``telldir'' should be passed to seekdir.
1323  * Modified to have many directories based in one file.
1324  */
1325 void
1326 seekdir(dirp, loc, base)
1327 	register DIR *dirp;
1328 	long loc, base;
1329 {
1330 	if (loc == telldir(dirp))
1331 		return;
1332 	loc -= base;
1333 	if (loc < 0)
1334 		fprintf(stderr, "bad seek pointer to seekdir %d\n", loc);
1335 	lseek(dirp->dd_fd, base + (loc & ~(DIRBLKSIZ - 1)), 0);
1336 	dirp->dd_loc = loc & (DIRBLKSIZ - 1);
1337 	if (dirp->dd_loc != 0)
1338 		dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, DIRBLKSIZ);
1339 }
1340 
1341 /*
1342  * tell whether an inode is allocated
1343  * this is drawn from ialloccg in sys/alloc.c
1344  */
1345 iexist(ip, ino)
1346 	struct inode *ip;
1347 	ino_t ino;
1348 {
1349 	register struct fs *fs;
1350 	register struct cg *cgp;
1351 	register struct buf *bp;
1352 	int cg;
1353 
1354 	fs = ip->i_fs;
1355 	if ((unsigned)ino >= fs->fs_ipg*fs->fs_ncg)
1356 		return (0);
1357 	cg = itog(fs, ino);
1358 	bp = bread(ip->i_dev, fsbtodb(fs, cgtod(fs, cg)), fs->fs_bsize);
1359 	if (bp->b_flags & B_ERROR) {
1360 		brelse(bp);
1361 		return(0);
1362 	}
1363 	cgp = bp->b_un.b_cg;
1364 	ino %= fs->fs_ipg;
1365 	if (isclr(cgp->cg_iused, ino)) {
1366 		brelse(bp);
1367 		return(0);
1368 	}
1369 	brelse(bp);
1370 	return (1);
1371 }
1372 
1373 allocinotab(ino, seekpt)
1374 	ino_t ino;
1375 	daddr_t seekpt;
1376 {
1377 	register struct inotab	*itp;
1378 
1379 	itp = (struct inotab *)calloc(1, sizeof(struct inotab));
1380 	itp->t_next = inotab[INOHASH(ino)];
1381 	inotab[INOHASH(ino)] = itp;
1382 	itp->t_ino = ino;
1383 	itp->t_seekpt = seekpt;
1384 }
1385 
1386 allocxtr(ino, name, flags)
1387 	ino_t ino;
1388 	char *name;
1389 	char flags;
1390 {
1391 	register struct xtrlist	*xp, *pxp;
1392 
1393 	xp = (struct xtrlist *)calloc(1, sizeof(struct xtrlist) + strlen(name));
1394 	xp->x_next = xtrlist[INOHASH(ino)];
1395 	xtrlist[INOHASH(ino)] = xp;
1396 	xp->x_ino = ino;
1397 	strcpy(xp->x_name, name);
1398 	xtrcnt++;
1399 	xp->x_flags = flags;
1400 	for (pxp = xp->x_next; pxp; pxp = pxp->x_next)
1401 		if (pxp->x_ino == ino && (pxp->x_flags & XLINKED) == 0) {
1402 			xp->x_flags |= XLINKED;
1403 			xp->x_linkedto = pxp;
1404 			xtrcnt--;
1405 			break;
1406 		}
1407 	if (xp->x_flags & XLINKED)
1408 		fprintf(stdout, "%s: linked to %s\n", xp->x_name, pxp->x_name);
1409 	else if (xp->x_flags & XISDIR)
1410 		fprintf(stdout, "%s: directory inode %u\n", xp->x_name, ino);
1411 	else
1412 		fprintf(stdout, "%s: inode %u\n", xp->x_name, ino);
1413 }
1414 
1415 done(exitcode)
1416 	int exitcode;
1417 {
1418 #ifndef STANDALONE
1419 	unlink(dirfile);
1420 #endif
1421 	if (mounted) {
1422 		xumount();
1423 		mounted = 0;
1424 	}
1425 	exit(exitcode);
1426 }
1427