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