xref: /original-bsd/sbin/restore/tape.c (revision 6472dbc6)
1 /*
2  * Copyright (c) 1983 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17 
18 #ifndef lint
19 static char sccsid[] = "@(#)tape.c	5.18 (Berkeley) 05/30/90";
20 #endif /* not lint */
21 
22 #include "restore.h"
23 #include <protocols/dumprestore.h>
24 #include <sys/ioctl.h>
25 #include <sys/mtio.h>
26 #include <sys/file.h>
27 #include <setjmp.h>
28 #include <sys/stat.h>
29 #include "pathnames.h"
30 
31 static long	fssize = MAXBSIZE;
32 static int	mt = -1;
33 static int	pipein = 0;
34 static char	magtape[BUFSIZ];
35 static int	bct;
36 static char	*tbf;
37 static union	u_spcl endoftapemark;
38 static long	blksread;
39 static long	tapesread;
40 static jmp_buf	restart;
41 static int	gettingfile = 0;	/* restart has a valid frame */
42 
43 static int	ofile;
44 static char	*map;
45 static char	lnkbuf[MAXPATHLEN + 1];
46 static int	pathlen;
47 
48 int		Bcvt;		/* Swap Bytes (for CCI or sun) */
49 static int	Qcvt;		/* Swap quads (for sun) */
50 /*
51  * Set up an input source
52  */
53 setinput(source)
54 	char *source;
55 {
56 	extern int errno;
57 #ifdef RRESTORE
58 	char *host, *tape;
59 #endif RRESTORE
60 	char *strerror();
61 
62 	flsht();
63 	if (bflag)
64 		newtapebuf(ntrec);
65 	else
66 		newtapebuf(NTREC > HIGHDENSITYTREC ? NTREC : HIGHDENSITYTREC);
67 	terminal = stdin;
68 #ifdef RRESTORE
69 	host = source;
70 	tape = index(host, ':');
71 	if (tape == 0) {
72 nohost:
73 		msg("need keyletter ``f'' and device ``host:tape''\n");
74 		done(1);
75 	}
76 	*tape++ = '\0';
77 	(void) strcpy(magtape, tape);
78 	if (rmthost(host) == 0)
79 		done(1);
80 	setuid(getuid());	/* no longer need or want root privileges */
81 #else
82 	if (strcmp(source, "-") == 0) {
83 		/*
84 		 * Since input is coming from a pipe we must establish
85 		 * our own connection to the terminal.
86 		 */
87 		terminal = fopen(_PATH_TTY, "r");
88 		if (terminal == NULL) {
89 			(void)fprintf(stderr, "Cannot open %s: %s\n",
90 			    _PATH_TTY, strerror(errno));
91 			terminal = fopen(_PATH_DEVNULL, "r");
92 			if (terminal == NULL) {
93 			    (void)fprintf(stderr, "Cannot open %s: %s\n",
94 				_PATH_DEVNULL, strerror(errno));
95 				done(1);
96 			}
97 		}
98 		pipein++;
99 	}
100 	(void) strcpy(magtape, source);
101 #endif RRESTORE
102 }
103 
104 newtapebuf(size)
105 	long size;
106 {
107 	static tbfsize = -1;
108 
109 	ntrec = size;
110 	if (size <= tbfsize)
111 		return;
112 	if (tbf != NULL)
113 		free(tbf);
114 	tbf = (char *)malloc(size * TP_BSIZE);
115 	if (tbf == NULL) {
116 		fprintf(stderr, "Cannot allocate space for tape buffer\n");
117 		done(1);
118 	}
119 	tbfsize = size;
120 }
121 
122 /*
123  * Verify that the tape drive can be accessed and
124  * that it actually is a dump tape.
125  */
126 setup()
127 {
128 	int i, j, *ip;
129 	struct stat stbuf;
130 	extern int xtrmap(), xtrmapskip();
131 
132 	vprintf(stdout, "Verify tape and initialize maps\n");
133 #ifdef RRESTORE
134 	if ((mt = rmtopen(magtape, 0)) < 0)
135 #else
136 	if (pipein)
137 		mt = 0;
138 	else if ((mt = open(magtape, 0)) < 0)
139 #endif
140 	{
141 		perror(magtape);
142 		done(1);
143 	}
144 	volno = 1;
145 	setdumpnum();
146 	flsht();
147 	if (!pipein && !bflag)
148 		findtapeblksize();
149 	if (gethead(&spcl) == FAIL) {
150 		bct--; /* push back this block */
151 		cvtflag++;
152 		if (gethead(&spcl) == FAIL) {
153 			fprintf(stderr, "Tape is not a dump tape\n");
154 			done(1);
155 		}
156 		fprintf(stderr, "Converting to new file system format.\n");
157 	}
158 	if (pipein) {
159 		endoftapemark.s_spcl.c_magic = cvtflag ? OFS_MAGIC : NFS_MAGIC;
160 		endoftapemark.s_spcl.c_type = TS_END;
161 		ip = (int *)&endoftapemark;
162 		j = sizeof(union u_spcl) / sizeof(int);
163 		i = 0;
164 		do
165 			i += *ip++;
166 		while (--j);
167 		endoftapemark.s_spcl.c_checksum = CHECKSUM - i;
168 	}
169 	if (vflag || command == 't')
170 		printdumpinfo();
171 	dumptime = spcl.c_ddate;
172 	dumpdate = spcl.c_date;
173 	if (stat(".", &stbuf) < 0) {
174 		perror("cannot stat .");
175 		done(1);
176 	}
177 	if (stbuf.st_blksize > 0 && stbuf.st_blksize <= MAXBSIZE)
178 		fssize = stbuf.st_blksize;
179 	if (((fssize - 1) & fssize) != 0) {
180 		fprintf(stderr, "bad block size %d\n", fssize);
181 		done(1);
182 	}
183 	if (checkvol(&spcl, (long)1) == FAIL) {
184 		fprintf(stderr, "Tape is not volume 1 of the dump\n");
185 		done(1);
186 	}
187 	if (readhdr(&spcl) == FAIL)
188 		panic("no header after volume mark!\n");
189 	findinode(&spcl);
190 	if (checktype(&spcl, TS_CLRI) == FAIL) {
191 		fprintf(stderr, "Cannot find file removal list\n");
192 		done(1);
193 	}
194 	maxino = (spcl.c_count * TP_BSIZE * NBBY) + 1;
195 	dprintf(stdout, "maxino = %d\n", maxino);
196 	map = calloc((unsigned)1, (unsigned)howmany(maxino, NBBY));
197 	if (map == (char *)NIL)
198 		panic("no memory for file removal list\n");
199 	clrimap = map;
200 	curfile.action = USING;
201 	getfile(xtrmap, xtrmapskip);
202 	if (checktype(&spcl, TS_BITS) == FAIL) {
203 		fprintf(stderr, "Cannot find file dump list\n");
204 		done(1);
205 	}
206 	map = calloc((unsigned)1, (unsigned)howmany(maxino, NBBY));
207 	if (map == (char *)NULL)
208 		panic("no memory for file dump list\n");
209 	dumpmap = map;
210 	curfile.action = USING;
211 	getfile(xtrmap, xtrmapskip);
212 }
213 
214 /*
215  * Prompt user to load a new dump volume.
216  * "Nextvol" is the next suggested volume to use.
217  * This suggested volume is enforced when doing full
218  * or incremental restores, but can be overrridden by
219  * the user when only extracting a subset of the files.
220  */
221 getvol(nextvol)
222 	long nextvol;
223 {
224 	long newvol;
225 	long savecnt, i;
226 	union u_spcl tmpspcl;
227 #	define tmpbuf tmpspcl.s_spcl
228 	char buf[TP_BSIZE];
229 	extern char *ctime();
230 
231 	if (nextvol == 1) {
232 		tapesread = 0;
233 		gettingfile = 0;
234 	}
235 	if (pipein) {
236 		if (nextvol != 1)
237 			panic("Changing volumes on pipe input?\n");
238 		if (volno == 1)
239 			return;
240 		goto gethdr;
241 	}
242 	savecnt = blksread;
243 again:
244 	if (pipein)
245 		done(1); /* pipes do not get a second chance */
246 	if (command == 'R' || command == 'r' || curfile.action != SKIP)
247 		newvol = nextvol;
248 	else
249 		newvol = 0;
250 	while (newvol <= 0) {
251 		if (tapesread == 0) {
252 			fprintf(stderr, "%s%s%s%s%s",
253 			    "You have not read any tapes yet.\n",
254 			    "Unless you know which volume your",
255 			    " file(s) are on you should start\n",
256 			    "with the last volume and work",
257 			    " towards towards the first.\n");
258 		} else {
259 			fprintf(stderr, "You have read volumes");
260 			strcpy(tbf, ": ");
261 			for (i = 1; i < 32; i++)
262 				if (tapesread & (1 << i)) {
263 					fprintf(stderr, "%s%d", tbf, i);
264 					strcpy(tbf, ", ");
265 				}
266 			fprintf(stderr, "\n");
267 		}
268 		do	{
269 			fprintf(stderr, "Specify next volume #: ");
270 			(void) fflush(stderr);
271 			(void) fgets(tbf, BUFSIZ, terminal);
272 		} while (!feof(terminal) && tbf[0] == '\n');
273 		if (feof(terminal))
274 			done(1);
275 		newvol = atoi(tbf);
276 		if (newvol <= 0) {
277 			fprintf(stderr,
278 			    "Volume numbers are positive numerics\n");
279 		}
280 	}
281 	if (newvol == volno) {
282 		tapesread |= 1 << volno;
283 		return;
284 	}
285 	closemt();
286 	fprintf(stderr, "Mount tape volume %d\n", newvol);
287 	fprintf(stderr, "Enter ``none'' if there are no more tapes\n");
288 	fprintf(stderr, "otherwise enter tape name (default: %s) ", magtape);
289 	(void) fflush(stderr);
290 	(void) fgets(tbf, BUFSIZ, terminal);
291 	if (feof(terminal))
292 		done(1);
293 	if (!strcmp(tbf, "none\n")) {
294 		curfile.name = "<name unknown>";
295 		curfile.action = UNKNOWN;
296 		curfile.dip = (struct dinode *)NIL;
297 		curfile.ino = maxino;
298 		if (gettingfile) {
299 			gettingfile = 0;
300 			longjmp(restart, 1);
301 		}
302 	}
303 	if (tbf[0] != '\n') {
304 		(void) strcpy(magtape, tbf);
305 		magtape[strlen(magtape) - 1] = '\0';
306 	}
307 #ifdef RRESTORE
308 	if ((mt = rmtopen(magtape, 0)) == -1)
309 #else
310 	if ((mt = open(magtape, 0)) == -1)
311 #endif
312 	{
313 		fprintf(stderr, "Cannot open %s\n", magtape);
314 		volno = -1;
315 		goto again;
316 	}
317 gethdr:
318 	volno = newvol;
319 	setdumpnum();
320 	flsht();
321 	if (readhdr(&tmpbuf) == FAIL) {
322 		fprintf(stderr, "tape is not dump tape\n");
323 		volno = 0;
324 		goto again;
325 	}
326 	if (checkvol(&tmpbuf, volno) == FAIL) {
327 		fprintf(stderr, "Wrong volume (%d)\n", tmpbuf.c_volume);
328 		volno = 0;
329 		goto again;
330 	}
331 	if (tmpbuf.c_date != dumpdate || tmpbuf.c_ddate != dumptime) {
332 		fprintf(stderr, "Wrong dump date\n\tgot: %s",
333 			ctime(&tmpbuf.c_date));
334 		fprintf(stderr, "\twanted: %s", ctime(&dumpdate));
335 		volno = 0;
336 		goto again;
337 	}
338 	tapesread |= 1 << volno;
339 	blksread = savecnt;
340 	if (curfile.action == USING) {
341 		if (volno == 1)
342 			panic("active file into volume 1\n");
343 		return;
344 	}
345 	/*
346 	 * Skip up to the beginning of the next record
347 	 */
348 	if (tmpbuf.c_type == TS_TAPE && (tmpbuf.c_flags & DR_NEWHEADER))
349 		for (i = tmpbuf.c_count; i > 0; i--)
350 			readtape(buf);
351 	(void) gethead(&spcl);
352 	findinode(&spcl);
353 	if (gettingfile) {
354 		gettingfile = 0;
355 		longjmp(restart, 1);
356 	}
357 }
358 
359 /*
360  * handle multiple dumps per tape by skipping forward to the
361  * appropriate one.
362  */
363 setdumpnum()
364 {
365 	struct mtop tcom;
366 
367 	if (dumpnum == 1 || volno != 1)
368 		return;
369 	if (pipein) {
370 		fprintf(stderr, "Cannot have multiple dumps on pipe input\n");
371 		done(1);
372 	}
373 	tcom.mt_op = MTFSF;
374 	tcom.mt_count = dumpnum - 1;
375 #ifdef RRESTORE
376 	rmtioctl(MTFSF, dumpnum - 1);
377 #else
378 	if (ioctl(mt, (int)MTIOCTOP, (char *)&tcom) < 0)
379 		perror("ioctl MTFSF");
380 #endif
381 }
382 
383 printdumpinfo()
384 {
385 	extern char *ctime();
386 
387 	fprintf(stdout, "Dump   date: %s", ctime(&spcl.c_date));
388 	fprintf(stdout, "Dumped from: %s",
389 	    (spcl.c_ddate == 0) ? "the epoch\n" : ctime(&spcl.c_ddate));
390 	if (spcl.c_host[0] == '\0')
391 		return;
392 	fprintf(stderr, "Level %d dump of %s on %s:%s\n",
393 		spcl.c_level, spcl.c_filesys, spcl.c_host, spcl.c_dev);
394 	fprintf(stderr, "Label: %s\n", spcl.c_label);
395 }
396 
397 extractfile(name)
398 	char *name;
399 {
400 	int mode;
401 	struct timeval timep[2];
402 	struct entry *ep;
403 	extern int xtrlnkfile(), xtrlnkskip();
404 	extern int xtrfile(), xtrskip();
405 
406 	curfile.name = name;
407 	curfile.action = USING;
408 	timep[0].tv_sec = curfile.dip->di_atime;
409 	timep[0].tv_usec = 0;
410 	timep[1].tv_sec = curfile.dip->di_mtime;
411 	timep[1].tv_usec = 0;
412 	mode = curfile.dip->di_mode;
413 	switch (mode & IFMT) {
414 
415 	default:
416 		fprintf(stderr, "%s: unknown file mode 0%o\n", name, mode);
417 		skipfile();
418 		return (FAIL);
419 
420 	case IFSOCK:
421 		vprintf(stdout, "skipped socket %s\n", name);
422 		skipfile();
423 		return (GOOD);
424 
425 	case IFDIR:
426 		if (mflag) {
427 			ep = lookupname(name);
428 			if (ep == NIL || ep->e_flags & EXTRACT)
429 				panic("unextracted directory %s\n", name);
430 			skipfile();
431 			return (GOOD);
432 		}
433 		vprintf(stdout, "extract file %s\n", name);
434 		return (genliteraldir(name, curfile.ino));
435 
436 	case IFLNK:
437 		lnkbuf[0] = '\0';
438 		pathlen = 0;
439 		getfile(xtrlnkfile, xtrlnkskip);
440 		if (pathlen == 0) {
441 			vprintf(stdout,
442 			    "%s: zero length symbolic link (ignored)\n", name);
443 			return (GOOD);
444 		}
445 		return (linkit(lnkbuf, name, SYMLINK));
446 
447 	case IFCHR:
448 	case IFBLK:
449 		vprintf(stdout, "extract special file %s\n", name);
450 		if (Nflag) {
451 			skipfile();
452 			return (GOOD);
453 		}
454 		if (mknod(name, mode, (int)curfile.dip->di_rdev) < 0) {
455 			fprintf(stderr, "%s: ", name);
456 			(void) fflush(stderr);
457 			perror("cannot create special file");
458 			skipfile();
459 			return (FAIL);
460 		}
461 		(void) chown(name, curfile.dip->di_uid, curfile.dip->di_gid);
462 		(void) chmod(name, mode);
463 		skipfile();
464 		utimes(name, timep);
465 		return (GOOD);
466 
467 	case IFREG:
468 		vprintf(stdout, "extract file %s\n", name);
469 		if (Nflag) {
470 			skipfile();
471 			return (GOOD);
472 		}
473 		if ((ofile = creat(name, 0666)) < 0) {
474 			fprintf(stderr, "%s: ", name);
475 			(void) fflush(stderr);
476 			perror("cannot create file");
477 			skipfile();
478 			return (FAIL);
479 		}
480 		(void) fchown(ofile, curfile.dip->di_uid, curfile.dip->di_gid);
481 		(void) fchmod(ofile, mode);
482 		getfile(xtrfile, xtrskip);
483 		(void) close(ofile);
484 		utimes(name, timep);
485 		return (GOOD);
486 	}
487 	/* NOTREACHED */
488 }
489 
490 /*
491  * skip over bit maps on the tape
492  */
493 skipmaps()
494 {
495 
496 	while (checktype(&spcl, TS_CLRI) == GOOD ||
497 	       checktype(&spcl, TS_BITS) == GOOD)
498 		skipfile();
499 }
500 
501 /*
502  * skip over a file on the tape
503  */
504 skipfile()
505 {
506 	extern int null();
507 
508 	curfile.action = SKIP;
509 	getfile(null, null);
510 }
511 
512 /*
513  * Do the file extraction, calling the supplied functions
514  * with the blocks
515  */
516 getfile(f1, f2)
517 	int	(*f2)(), (*f1)();
518 {
519 	register int i;
520 	int curblk = 0;
521 	off_t size = spcl.c_dinode.di_size;
522 	static char clearedbuf[MAXBSIZE];
523 	char buf[MAXBSIZE / TP_BSIZE][TP_BSIZE];
524 	char junk[TP_BSIZE];
525 
526 	if (checktype(&spcl, TS_END) == GOOD)
527 		panic("ran off end of tape\n");
528 	if (ishead(&spcl) == FAIL)
529 		panic("not at beginning of a file\n");
530 	if (!gettingfile && setjmp(restart) != 0)
531 		return;
532 	gettingfile++;
533 loop:
534 	for (i = 0; i < spcl.c_count; i++) {
535 		if (spcl.c_addr[i]) {
536 			readtape(&buf[curblk++][0]);
537 			if (curblk == fssize / TP_BSIZE) {
538 				(*f1)(buf, size > TP_BSIZE ?
539 				     (long) (fssize) :
540 				     (curblk - 1) * TP_BSIZE + size);
541 				curblk = 0;
542 			}
543 		} else {
544 			if (curblk > 0) {
545 				(*f1)(buf, size > TP_BSIZE ?
546 				     (long) (curblk * TP_BSIZE) :
547 				     (curblk - 1) * TP_BSIZE + size);
548 				curblk = 0;
549 			}
550 			(*f2)(clearedbuf, size > TP_BSIZE ?
551 				(long) TP_BSIZE : size);
552 		}
553 		if ((size -= TP_BSIZE) <= 0) {
554 			for (i++; i < spcl.c_count; i++)
555 				if (spcl.c_addr[i])
556 					readtape(junk);
557 			break;
558 		}
559 	}
560 	if (readhdr(&spcl) == GOOD && size > 0) {
561 		if (checktype(&spcl, TS_ADDR) == GOOD)
562 			goto loop;
563 		dprintf(stdout, "Missing address (header) block for %s\n",
564 			curfile.name);
565 	}
566 	if (curblk > 0)
567 		(*f1)(buf, (curblk * TP_BSIZE) + size);
568 	findinode(&spcl);
569 	gettingfile = 0;
570 }
571 
572 /*
573  * The next routines are called during file extraction to
574  * put the data into the right form and place.
575  */
576 xtrfile(buf, size)
577 	char	*buf;
578 	long	size;
579 {
580 
581 	if (Nflag)
582 		return;
583 	if (write(ofile, buf, (int) size) == -1) {
584 		fprintf(stderr, "write error extracting inode %d, name %s\n",
585 			curfile.ino, curfile.name);
586 		perror("write");
587 		done(1);
588 	}
589 }
590 
591 xtrskip(buf, size)
592 	char *buf;
593 	long size;
594 {
595 
596 #ifdef lint
597 	buf = buf;
598 #endif
599 	if (lseek(ofile, size, 1) == (long)-1) {
600 		fprintf(stderr, "seek error extracting inode %d, name %s\n",
601 			curfile.ino, curfile.name);
602 		perror("lseek");
603 		done(1);
604 	}
605 }
606 
607 xtrlnkfile(buf, size)
608 	char	*buf;
609 	long	size;
610 {
611 
612 	pathlen += size;
613 	if (pathlen > MAXPATHLEN) {
614 		fprintf(stderr, "symbolic link name: %s->%s%s; too long %d\n",
615 		    curfile.name, lnkbuf, buf, pathlen);
616 		done(1);
617 	}
618 	(void) strcat(lnkbuf, buf);
619 }
620 
621 xtrlnkskip(buf, size)
622 	char *buf;
623 	long size;
624 {
625 
626 #ifdef lint
627 	buf = buf, size = size;
628 #endif
629 	fprintf(stderr, "unallocated block in symbolic link %s\n",
630 		curfile.name);
631 	done(1);
632 }
633 
634 xtrmap(buf, size)
635 	char	*buf;
636 	long	size;
637 {
638 
639 	bcopy(buf, map, size);
640 	map += size;
641 }
642 
643 xtrmapskip(buf, size)
644 	char *buf;
645 	long size;
646 {
647 
648 #ifdef lint
649 	buf = buf;
650 #endif
651 	panic("hole in map\n");
652 	map += size;
653 }
654 
655 null() {;}
656 
657 /*
658  * Do the tape i/o, dealing with volume changes
659  * etc..
660  */
661 readtape(b)
662 	char *b;
663 {
664 	register long i;
665 	long rd, newvol;
666 	int cnt;
667 
668 	if (bct < ntrec) {
669 		bcopy(&tbf[(bct++*TP_BSIZE)], b, (long)TP_BSIZE);
670 		blksread++;
671 		return;
672 	}
673 	for (i = 0; i < ntrec; i++)
674 		((struct s_spcl *)&tbf[i*TP_BSIZE])->c_magic = 0;
675 	bct = 0;
676 	cnt = ntrec*TP_BSIZE;
677 	rd = 0;
678 getmore:
679 #ifdef RRESTORE
680 	i = rmtread(&tbf[rd], cnt);
681 #else
682 	i = read(mt, &tbf[rd], cnt);
683 #endif
684 	if (i > 0 && i != ntrec*TP_BSIZE) {
685 		if (pipein) {
686 			rd += i;
687 			cnt -= i;
688 			if (cnt > 0)
689 				goto getmore;
690 			i = rd;
691 		} else {
692 			if (i % TP_BSIZE != 0)
693 				panic("partial block read: %d should be %d\n",
694 					i, ntrec * TP_BSIZE);
695 			bcopy((char *)&endoftapemark, &tbf[i],
696 				(long)TP_BSIZE);
697 		}
698 	}
699 	if (i < 0) {
700 		fprintf(stderr, "Tape read error while ");
701 		switch (curfile.action) {
702 		default:
703 			fprintf(stderr, "trying to set up tape\n");
704 			break;
705 		case UNKNOWN:
706 			fprintf(stderr, "trying to resynchronize\n");
707 			break;
708 		case USING:
709 			fprintf(stderr, "restoring %s\n", curfile.name);
710 			break;
711 		case SKIP:
712 			fprintf(stderr, "skipping over inode %d\n",
713 				curfile.ino);
714 			break;
715 		}
716 		if (!yflag && !reply("continue"))
717 			done(1);
718 		i = ntrec*TP_BSIZE;
719 		bzero(tbf, i);
720 #ifdef RRESTORE
721 		if (rmtseek(i, 1) < 0)
722 #else
723 		if (lseek(mt, i, 1) == (long)-1)
724 #endif
725 		{
726 			perror("continuation failed");
727 			done(1);
728 		}
729 	}
730 	if (i == 0) {
731 		if (!pipein) {
732 			newvol = volno + 1;
733 			volno = 0;
734 			getvol(newvol);
735 			readtape(b);
736 			return;
737 		}
738 		if (rd % TP_BSIZE != 0)
739 			panic("partial block read: %d should be %d\n",
740 				rd, ntrec * TP_BSIZE);
741 		bcopy((char *)&endoftapemark, &tbf[rd], (long)TP_BSIZE);
742 	}
743 	bcopy(&tbf[(bct++*TP_BSIZE)], b, (long)TP_BSIZE);
744 	blksread++;
745 }
746 
747 findtapeblksize()
748 {
749 	register long i;
750 
751 	for (i = 0; i < ntrec; i++)
752 		((struct s_spcl *)&tbf[i * TP_BSIZE])->c_magic = 0;
753 	bct = 0;
754 #ifdef RRESTORE
755 	i = rmtread(tbf, ntrec * TP_BSIZE);
756 #else
757 	i = read(mt, tbf, ntrec * TP_BSIZE);
758 #endif
759 	if (i <= 0) {
760 		perror("Tape read error");
761 		done(1);
762 	}
763 	if (i % TP_BSIZE != 0) {
764 		fprintf(stderr, "Tape block size (%d) %s (%d)\n",
765 			i, "is not a multiple of dump block size", TP_BSIZE);
766 		done(1);
767 	}
768 	ntrec = i / TP_BSIZE;
769 	vprintf(stdout, "Tape block size is %d\n", ntrec);
770 }
771 
772 flsht()
773 {
774 
775 	bct = ntrec+1;
776 }
777 
778 closemt()
779 {
780 	if (mt < 0)
781 		return;
782 #ifdef RRESTORE
783 	rmtclose();
784 #else
785 	(void) close(mt);
786 #endif
787 }
788 
789 checkvol(b, t)
790 	struct s_spcl *b;
791 	long t;
792 {
793 
794 	if (b->c_volume != t)
795 		return(FAIL);
796 	return(GOOD);
797 }
798 
799 readhdr(b)
800 	struct s_spcl *b;
801 {
802 
803 	if (gethead(b) == FAIL) {
804 		dprintf(stdout, "readhdr fails at %d blocks\n", blksread);
805 		return(FAIL);
806 	}
807 	return(GOOD);
808 }
809 
810 /*
811  * read the tape into buf, then return whether or
812  * or not it is a header block.
813  */
814 gethead(buf)
815 	struct s_spcl *buf;
816 {
817 	long i;
818 	u_long *j;
819 	union u_ospcl {
820 		char dummy[TP_BSIZE];
821 		struct	s_ospcl {
822 			long	c_type;
823 			long	c_date;
824 			long	c_ddate;
825 			long	c_volume;
826 			long	c_tapea;
827 			u_short	c_inumber;
828 			long	c_magic;
829 			long	c_checksum;
830 			struct odinode {
831 				unsigned short odi_mode;
832 				u_short	odi_nlink;
833 				u_short	odi_uid;
834 				u_short	odi_gid;
835 				long	odi_size;
836 				long	odi_rdev;
837 				char	odi_addr[36];
838 				long	odi_atime;
839 				long	odi_mtime;
840 				long	odi_ctime;
841 			} c_dinode;
842 			long	c_count;
843 			char	c_addr[256];
844 		} s_ospcl;
845 	} u_ospcl;
846 
847 	if (!cvtflag) {
848 		readtape((char *)buf);
849 		if (buf->c_magic != NFS_MAGIC) {
850 			if (swabl(buf->c_magic) != NFS_MAGIC)
851 				return (FAIL);
852 			if (!Bcvt) {
853 				vprintf(stdout, "Note: Doing Byte swapping\n");
854 				Bcvt = 1;
855 			}
856 		}
857 		if (checksum((int *)buf) == FAIL)
858 			return (FAIL);
859 		if (Bcvt)
860 			swabst("8l4s31l", (char *)buf);
861 		goto good;
862 	}
863 	readtape((char *)(&u_ospcl.s_ospcl));
864 	bzero((char *)buf, (long)TP_BSIZE);
865 	buf->c_type = u_ospcl.s_ospcl.c_type;
866 	buf->c_date = u_ospcl.s_ospcl.c_date;
867 	buf->c_ddate = u_ospcl.s_ospcl.c_ddate;
868 	buf->c_volume = u_ospcl.s_ospcl.c_volume;
869 	buf->c_tapea = u_ospcl.s_ospcl.c_tapea;
870 	buf->c_inumber = u_ospcl.s_ospcl.c_inumber;
871 	buf->c_checksum = u_ospcl.s_ospcl.c_checksum;
872 	buf->c_magic = u_ospcl.s_ospcl.c_magic;
873 	buf->c_dinode.di_mode = u_ospcl.s_ospcl.c_dinode.odi_mode;
874 	buf->c_dinode.di_nlink = u_ospcl.s_ospcl.c_dinode.odi_nlink;
875 	buf->c_dinode.di_uid = u_ospcl.s_ospcl.c_dinode.odi_uid;
876 	buf->c_dinode.di_gid = u_ospcl.s_ospcl.c_dinode.odi_gid;
877 	buf->c_dinode.di_size = u_ospcl.s_ospcl.c_dinode.odi_size;
878 	buf->c_dinode.di_rdev = u_ospcl.s_ospcl.c_dinode.odi_rdev;
879 	buf->c_dinode.di_atime = u_ospcl.s_ospcl.c_dinode.odi_atime;
880 	buf->c_dinode.di_mtime = u_ospcl.s_ospcl.c_dinode.odi_mtime;
881 	buf->c_dinode.di_ctime = u_ospcl.s_ospcl.c_dinode.odi_ctime;
882 	buf->c_count = u_ospcl.s_ospcl.c_count;
883 	bcopy(u_ospcl.s_ospcl.c_addr, buf->c_addr, (long)256);
884 	if (u_ospcl.s_ospcl.c_magic != OFS_MAGIC ||
885 	    checksum((int *)(&u_ospcl.s_ospcl)) == FAIL)
886 		return(FAIL);
887 	buf->c_magic = NFS_MAGIC;
888 
889 good:
890 	j = buf->c_dinode.di_qsize.val;
891 	i = j[1];
892 	if (buf->c_dinode.di_size == 0 &&
893 	    (buf->c_dinode.di_mode & IFMT) == IFDIR && Qcvt==0) {
894 		if (*j || i) {
895 			printf("Note: Doing Quad swapping\n");
896 			Qcvt = 1;
897 		}
898 	}
899 	if (Qcvt) {
900 		j[1] = *j; *j = i;
901 	}
902 	switch (buf->c_type) {
903 
904 	case TS_CLRI:
905 	case TS_BITS:
906 		/*
907 		 * Have to patch up missing information in bit map headers
908 		 */
909 		buf->c_inumber = 0;
910 		buf->c_dinode.di_size = buf->c_count * TP_BSIZE;
911 		for (i = 0; i < buf->c_count; i++)
912 			buf->c_addr[i]++;
913 		break;
914 
915 	case TS_TAPE:
916 	case TS_END:
917 		buf->c_inumber = 0;
918 		break;
919 
920 	case TS_INODE:
921 	case TS_ADDR:
922 		break;
923 
924 	default:
925 		panic("gethead: unknown inode type %d\n", buf->c_type);
926 		break;
927 	}
928 	if (dflag)
929 		accthdr(buf);
930 	return(GOOD);
931 }
932 
933 /*
934  * Check that a header is where it belongs and predict the next header
935  */
936 accthdr(header)
937 	struct s_spcl *header;
938 {
939 	static ino_t previno = 0x7fffffff;
940 	static int prevtype;
941 	static long predict;
942 	long blks, i;
943 
944 	if (header->c_type == TS_TAPE) {
945 		fprintf(stderr, "Volume header\n");
946 		previno = 0x7fffffff;
947 		return;
948 	}
949 	if (previno == 0x7fffffff)
950 		goto newcalc;
951 	switch (prevtype) {
952 	case TS_BITS:
953 		fprintf(stderr, "Dump mask header");
954 		break;
955 	case TS_CLRI:
956 		fprintf(stderr, "Remove mask header");
957 		break;
958 	case TS_INODE:
959 		fprintf(stderr, "File header, ino %d", previno);
960 		break;
961 	case TS_ADDR:
962 		fprintf(stderr, "File continuation header, ino %d", previno);
963 		break;
964 	case TS_END:
965 		fprintf(stderr, "End of tape header");
966 		break;
967 	}
968 	if (predict != blksread - 1)
969 		fprintf(stderr, "; predicted %d blocks, got %d blocks",
970 			predict, blksread - 1);
971 	fprintf(stderr, "\n");
972 newcalc:
973 	blks = 0;
974 	if (header->c_type != TS_END)
975 		for (i = 0; i < header->c_count; i++)
976 			if (header->c_addr[i] != 0)
977 				blks++;
978 	predict = blks;
979 	blksread = 0;
980 	prevtype = header->c_type;
981 	previno = header->c_inumber;
982 }
983 
984 /*
985  * Find an inode header.
986  * Complain if had to skip, and complain is set.
987  */
988 findinode(header)
989 	struct s_spcl *header;
990 {
991 	static long skipcnt = 0;
992 	long i;
993 	char buf[TP_BSIZE];
994 
995 	curfile.name = "<name unknown>";
996 	curfile.action = UNKNOWN;
997 	curfile.dip = (struct dinode *)NIL;
998 	curfile.ino = 0;
999 	if (ishead(header) == FAIL) {
1000 		skipcnt++;
1001 		while (gethead(header) == FAIL || header->c_date != dumpdate)
1002 			skipcnt++;
1003 	}
1004 	for (;;) {
1005 		if (checktype(header, TS_ADDR) == GOOD) {
1006 			/*
1007 			 * Skip up to the beginning of the next record
1008 			 */
1009 			for (i = 0; i < header->c_count; i++)
1010 				if (header->c_addr[i])
1011 					readtape(buf);
1012 			(void) gethead(header);
1013 			continue;
1014 		}
1015 		if (checktype(header, TS_INODE) == GOOD) {
1016 			curfile.dip = &header->c_dinode;
1017 			curfile.ino = header->c_inumber;
1018 			break;
1019 		}
1020 		if (checktype(header, TS_END) == GOOD) {
1021 			curfile.ino = maxino;
1022 			break;
1023 		}
1024 		if (checktype(header, TS_CLRI) == GOOD) {
1025 			curfile.name = "<file removal list>";
1026 			break;
1027 		}
1028 		if (checktype(header, TS_BITS) == GOOD) {
1029 			curfile.name = "<file dump list>";
1030 			break;
1031 		}
1032 		while (gethead(header) == FAIL)
1033 			skipcnt++;
1034 	}
1035 	if (skipcnt > 0)
1036 		fprintf(stderr, "resync restore, skipped %d blocks\n", skipcnt);
1037 	skipcnt = 0;
1038 }
1039 
1040 /*
1041  * return whether or not the buffer contains a header block
1042  */
1043 ishead(buf)
1044 	struct s_spcl *buf;
1045 {
1046 
1047 	if (buf->c_magic != NFS_MAGIC)
1048 		return(FAIL);
1049 	return(GOOD);
1050 }
1051 
1052 checktype(b, t)
1053 	struct s_spcl *b;
1054 	int	t;
1055 {
1056 
1057 	if (b->c_type != t)
1058 		return(FAIL);
1059 	return(GOOD);
1060 }
1061 
1062 checksum(b)
1063 	register int *b;
1064 {
1065 	register int i, j;
1066 
1067 	j = sizeof(union u_spcl) / sizeof(int);
1068 	i = 0;
1069 	if(!Bcvt) {
1070 		do
1071 			i += *b++;
1072 		while (--j);
1073 	} else {
1074 		/* What happens if we want to read restore tapes
1075 			for a 16bit int machine??? */
1076 		do
1077 			i += swabl(*b++);
1078 		while (--j);
1079 	}
1080 
1081 	if (i != CHECKSUM) {
1082 		fprintf(stderr, "Checksum error %o, inode %d file %s\n", i,
1083 			curfile.ino, curfile.name);
1084 		return(FAIL);
1085 	}
1086 	return(GOOD);
1087 }
1088 
1089 #ifdef RRESTORE
1090 /* VARARGS1 */
1091 msg(cp, a1, a2, a3)
1092 	char *cp;
1093 {
1094 
1095 	fprintf(stderr, cp, a1, a2, a3);
1096 }
1097 #endif RRESTORE
1098 
1099 swabst(cp, sp)
1100 register char *cp, *sp;
1101 {
1102 	int n = 0;
1103 	char c;
1104 	while(*cp) {
1105 		switch (*cp) {
1106 		case '0': case '1': case '2': case '3': case '4':
1107 		case '5': case '6': case '7': case '8': case '9':
1108 			n = (n * 10) + (*cp++ - '0');
1109 			continue;
1110 
1111 		case 's': case 'w': case 'h':
1112 			c = sp[0]; sp[0] = sp[1]; sp[1] = c;
1113 			sp++;
1114 			break;
1115 
1116 		case 'l':
1117 			c = sp[0]; sp[0] = sp[3]; sp[3] = c;
1118 			c = sp[2]; sp[2] = sp[1]; sp[1] = c;
1119 			sp += 3;
1120 		}
1121 		sp++; /* Any other character, like 'b' counts as byte. */
1122 		if (n <= 1) {
1123 			n = 0; cp++;
1124 		} else
1125 			n--;
1126 	}
1127 }
1128 swabl(x) { unsigned long l = x; swabst("l", (char *)&l); return l; }
1129