tape.c revision 21149
172213Sasmodai/*
272213Sasmodai * Copyright (c) 1983, 1993
372213Sasmodai *	The Regents of the University of California.  All rights reserved.
472213Sasmodai * (c) UNIX System Laboratories, Inc.
572213Sasmodai * All or some portions of this file are derived from material licensed
672213Sasmodai * to the University of California by American Telephone and Telegraph
772213Sasmodai * Co. or Unix System Laboratories, Inc. and are reproduced herein with
872213Sasmodai * the permission of UNIX System Laboratories, Inc.
972213Sasmodai *
1072213Sasmodai * Redistribution and use in source and binary forms, with or without
1172213Sasmodai * modification, are permitted provided that the following conditions
1272213Sasmodai * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 *    must display the following acknowledgement:
20 *	This product includes software developed by the University of
21 *	California, Berkeley and its contributors.
22 * 4. Neither the name of the University nor the names of its contributors
23 *    may be used to endorse or promote products derived from this software
24 *    without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
37 */
38
39#ifndef lint
40static char sccsid[] = "@(#)tape.c	8.3 (Berkeley) 4/1/94";
41#endif /* not lint */
42
43#include <sys/param.h>
44#include <sys/file.h>
45#include <sys/ioctl.h>
46#include <sys/mtio.h>
47#include <sys/stat.h>
48
49#include <ufs/ufs/dinode.h>
50#include <protocols/dumprestore.h>
51
52#include <errno.h>
53#include <setjmp.h>
54#include <stdio.h>
55#include <stdlib.h>
56#include <string.h>
57#include <unistd.h>
58
59#include "restore.h"
60#include "extern.h"
61#include "pathnames.h"
62
63static long	fssize = MAXBSIZE;
64static int	mt = -1;
65static int	pipein = 0;
66static char	magtape[BUFSIZ];
67static int	blkcnt;
68static int	numtrec;
69static char	*tapebuf;
70static union	u_spcl endoftapemark;
71static long	blksread;		/* blocks read since last header */
72static long	tpblksread = 0;		/* TP_BSIZE blocks read */
73static long	tapesread;
74static jmp_buf	restart;
75static int	gettingfile = 0;	/* restart has a valid frame */
76static char	*host = NULL;
77
78static int	ofile;
79static char	*map;
80static char	lnkbuf[MAXPATHLEN + 1];
81static int	pathlen;
82
83int		oldinofmt;	/* old inode format conversion required */
84int		Bcvt;		/* Swap Bytes (for CCI or sun) */
85static int	Qcvt;		/* Swap quads (for sun) */
86
87#define	FLUSHTAPEBUF()	blkcnt = ntrec + 1
88
89static void	 accthdr __P((struct s_spcl *));
90static int	 checksum __P((int *));
91static void	 findinode __P((struct s_spcl *));
92static void	 findtapeblksize __P((void));
93static int	 gethead __P((struct s_spcl *));
94static void	 readtape __P((char *));
95static void	 setdumpnum __P((void));
96static u_long	 swabl __P((u_long));
97static u_char	*swablong __P((u_char *, int));
98static u_char	*swabshort __P((u_char *, int));
99static void	 terminateinput __P((void));
100static void	 xtrfile __P((char *, long));
101static void	 xtrlnkfile __P((char *, long));
102static void	 xtrlnkskip __P((char *, long));
103static void	 xtrmap __P((char *, long));
104static void	 xtrmapskip __P((char *, long));
105static void	 xtrskip __P((char *, long));
106
107/*
108 * Set up an input source
109 */
110void
111setinput(source)
112	char *source;
113{
114	FLUSHTAPEBUF();
115	if (bflag)
116		newtapebuf(ntrec);
117	else
118		newtapebuf(NTREC > HIGHDENSITYTREC ? NTREC : HIGHDENSITYTREC);
119	terminal = stdin;
120
121#ifdef RRESTORE
122	if (index(source, ':')) {
123		host = source;
124		source = index(host, ':');
125		*source++ = '\0';
126		if (rmthost(host) == 0)
127			done(1);
128	} else
129#endif
130	if (strcmp(source, "-") == 0) {
131		/*
132		 * Since input is coming from a pipe we must establish
133		 * our own connection to the terminal.
134		 */
135		terminal = fopen(_PATH_TTY, "r");
136		if (terminal == NULL) {
137			(void)fprintf(stderr, "cannot open %s: %s\n",
138			    _PATH_TTY, strerror(errno));
139			terminal = fopen(_PATH_DEVNULL, "r");
140			if (terminal == NULL) {
141				(void)fprintf(stderr, "cannot open %s: %s\n",
142				    _PATH_DEVNULL, strerror(errno));
143				done(1);
144			}
145		}
146		pipein++;
147	}
148	setuid(getuid());	/* no longer need or want root privileges */
149	(void) strcpy(magtape, source);
150}
151
152void
153newtapebuf(size)
154	long size;
155{
156	static tapebufsize = -1;
157
158	ntrec = size;
159	if (size <= tapebufsize)
160		return;
161	if (tapebuf != NULL)
162		free(tapebuf);
163	tapebuf = malloc(size * TP_BSIZE);
164	if (tapebuf == NULL) {
165		fprintf(stderr, "Cannot allocate space for tape buffer\n");
166		done(1);
167	}
168	tapebufsize = size;
169}
170
171/*
172 * Verify that the tape drive can be accessed and
173 * that it actually is a dump tape.
174 */
175void
176setup()
177{
178	int i, j, *ip;
179	struct stat stbuf;
180
181	vprintf(stdout, "Verify tape and initialize maps\n");
182#ifdef RRESTORE
183	if (host)
184		mt = rmtopen(magtape, 0);
185	else
186#endif
187	if (pipein)
188		mt = 0;
189	else
190		mt = open(magtape, O_RDONLY, 0);
191	if (mt < 0) {
192		fprintf(stderr, "%s: %s\n", magtape, strerror(errno));
193		done(1);
194	}
195	volno = 1;
196	setdumpnum();
197	FLUSHTAPEBUF();
198	if (!pipein && !bflag)
199		findtapeblksize();
200	if (gethead(&spcl) == FAIL) {
201		blkcnt--; /* push back this block */
202		blksread--;
203		tpblksread--;
204		cvtflag++;
205		if (gethead(&spcl) == FAIL) {
206			fprintf(stderr, "Tape is not a dump tape\n");
207			done(1);
208		}
209		fprintf(stderr, "Converting to new file system format.\n");
210	}
211	if (pipein) {
212		endoftapemark.s_spcl.c_magic = cvtflag ? OFS_MAGIC : NFS_MAGIC;
213		endoftapemark.s_spcl.c_type = TS_END;
214		ip = (int *)&endoftapemark;
215		j = sizeof(union u_spcl) / sizeof(int);
216		i = 0;
217		do
218			i += *ip++;
219		while (--j);
220		endoftapemark.s_spcl.c_checksum = CHECKSUM - i;
221	}
222	if (vflag || command == 't')
223		printdumpinfo();
224	dumptime = spcl.c_ddate;
225	dumpdate = spcl.c_date;
226	if (stat(".", &stbuf) < 0) {
227		fprintf(stderr, "cannot stat .: %s\n", strerror(errno));
228		done(1);
229	}
230	if (stbuf.st_blksize > 0 && stbuf.st_blksize <= MAXBSIZE)
231		fssize = stbuf.st_blksize;
232	if (((fssize - 1) & fssize) != 0) {
233		fprintf(stderr, "bad block size %d\n", fssize);
234		done(1);
235	}
236	if (spcl.c_volume != 1) {
237		fprintf(stderr, "Tape is not volume 1 of the dump\n");
238		done(1);
239	}
240	if (gethead(&spcl) == FAIL) {
241		dprintf(stdout, "header read failed at %d blocks\n", blksread);
242		panic("no header after volume mark!\n");
243	}
244	findinode(&spcl);
245	if (spcl.c_type != TS_CLRI) {
246		fprintf(stderr, "Cannot find file removal list\n");
247		done(1);
248	}
249	maxino = (spcl.c_count * TP_BSIZE * NBBY) + 1;
250	dprintf(stdout, "maxino = %d\n", maxino);
251	map = calloc((unsigned)1, (unsigned)howmany(maxino, NBBY));
252	if (map == NULL)
253		panic("no memory for file removal list\n");
254	clrimap = map;
255	curfile.action = USING;
256	getfile(xtrmap, xtrmapskip);
257	if (spcl.c_type != TS_BITS) {
258		fprintf(stderr, "Cannot find file dump list\n");
259		done(1);
260	}
261	map = calloc((unsigned)1, (unsigned)howmany(maxino, NBBY));
262	if (map == (char *)NULL)
263		panic("no memory for file dump list\n");
264	dumpmap = map;
265	curfile.action = USING;
266	getfile(xtrmap, xtrmapskip);
267}
268
269/*
270 * Prompt user to load a new dump volume.
271 * "Nextvol" is the next suggested volume to use.
272 * This suggested volume is enforced when doing full
273 * or incremental restores, but can be overrridden by
274 * the user when only extracting a subset of the files.
275 */
276void
277getvol(nextvol)
278	long nextvol;
279{
280	long newvol, savecnt, wantnext, i;
281	union u_spcl tmpspcl;
282#	define tmpbuf tmpspcl.s_spcl
283	char buf[TP_BSIZE];
284
285	if (nextvol == 1) {
286		tapesread = 0;
287		gettingfile = 0;
288	}
289	if (pipein) {
290		if (nextvol != 1)
291			panic("Changing volumes on pipe input?\n");
292		if (volno == 1)
293			return;
294		goto gethdr;
295	}
296	savecnt = blksread;
297again:
298	if (pipein)
299		done(1); /* pipes do not get a second chance */
300	if (command == 'R' || command == 'r' || curfile.action != SKIP) {
301		newvol = nextvol;
302		wantnext = 1;
303	} else {
304		newvol = 0;
305		wantnext = 0;
306	}
307	while (newvol <= 0) {
308		if (tapesread == 0) {
309			fprintf(stderr, "%s%s%s%s%s",
310			    "You have not read any tapes yet.\n",
311			    "Unless you know which volume your",
312			    " file(s) are on you should start\n",
313			    "with the last volume and work",
314			    " towards towards the first.\n");
315		} else {
316			fprintf(stderr, "You have read volumes");
317			strcpy(buf, ": ");
318			for (i = 1; i < 32; i++)
319				if (tapesread & (1 << i)) {
320					fprintf(stderr, "%s%d", buf, i);
321					strcpy(buf, ", ");
322				}
323			fprintf(stderr, "\n");
324		}
325		do	{
326			fprintf(stderr, "Specify next volume #: ");
327			(void) fflush(stderr);
328			(void) fgets(buf, BUFSIZ, terminal);
329		} while (!feof(terminal) && buf[0] == '\n');
330		if (feof(terminal))
331			done(1);
332		newvol = atoi(buf);
333		if (newvol <= 0) {
334			fprintf(stderr,
335			    "Volume numbers are positive numerics\n");
336		}
337	}
338	if (newvol == volno) {
339		tapesread |= 1 << volno;
340		return;
341	}
342	closemt();
343	fprintf(stderr, "Mount tape volume %d\n", newvol);
344	fprintf(stderr, "Enter ``none'' if there are no more tapes\n");
345	fprintf(stderr, "otherwise enter tape name (default: %s) ", magtape);
346	(void) fflush(stderr);
347	(void) fgets(buf, BUFSIZ, terminal);
348	if (feof(terminal))
349		done(1);
350	if (!strcmp(buf, "none\n")) {
351		terminateinput();
352		return;
353	}
354	if (buf[0] != '\n') {
355		(void) strcpy(magtape, buf);
356		magtape[strlen(magtape) - 1] = '\0';
357	}
358#ifdef RRESTORE
359	if (host)
360		mt = rmtopen(magtape, 0);
361	else
362#endif
363		mt = open(magtape, O_RDONLY, 0);
364
365	if (mt == -1) {
366		fprintf(stderr, "Cannot open %s\n", magtape);
367		volno = -1;
368		goto again;
369	}
370gethdr:
371	volno = newvol;
372	setdumpnum();
373	FLUSHTAPEBUF();
374	if (gethead(&tmpbuf) == FAIL) {
375		dprintf(stdout, "header read failed at %d blocks\n", blksread);
376		fprintf(stderr, "tape is not dump tape\n");
377		volno = 0;
378		goto again;
379	}
380	if (tmpbuf.c_volume != volno) {
381		fprintf(stderr, "Wrong volume (%d)\n", tmpbuf.c_volume);
382		volno = 0;
383		goto again;
384	}
385	if (tmpbuf.c_date != dumpdate || tmpbuf.c_ddate != dumptime) {
386		fprintf(stderr, "Wrong dump date\n\tgot: %s",
387			ctime(&tmpbuf.c_date));
388		fprintf(stderr, "\twanted: %s", ctime(&dumpdate));
389		volno = 0;
390		goto again;
391	}
392	tapesread |= 1 << volno;
393	blksread = savecnt;
394 	/*
395 	 * If continuing from the previous volume, skip over any
396 	 * blocks read already at the end of the previous volume.
397 	 *
398 	 * If coming to this volume at random, skip to the beginning
399 	 * of the next record.
400 	 */
401	dprintf(stdout, "read %ld recs, tape starts with %ld\n",
402		tpblksread, tmpbuf.c_firstrec);
403 	if (tmpbuf.c_type == TS_TAPE && (tmpbuf.c_flags & DR_NEWHEADER)) {
404 		if (!wantnext) {
405 			tpblksread = tmpbuf.c_firstrec;
406 			for (i = tmpbuf.c_count; i > 0; i--)
407 				readtape(buf);
408 		} else if (tmpbuf.c_firstrec > 0 &&
409			   tmpbuf.c_firstrec < tpblksread - 1) {
410			/*
411			 * -1 since we've read the volume header
412			 */
413 			i = tpblksread - tmpbuf.c_firstrec - 1;
414			dprintf(stderr, "Skipping %d duplicate record%s.\n",
415				i, i > 1 ? "s" : "");
416 			while (--i >= 0)
417 				readtape(buf);
418 		}
419 	}
420	if (curfile.action == USING) {
421		if (volno == 1)
422			panic("active file into volume 1\n");
423		return;
424	}
425	/*
426	 * Skip up to the beginning of the next record
427	 */
428	if (tmpbuf.c_type == TS_TAPE && (tmpbuf.c_flags & DR_NEWHEADER))
429		for (i = tmpbuf.c_count; i > 0; i--)
430			readtape(buf);
431	(void) gethead(&spcl);
432	findinode(&spcl);
433	if (gettingfile) {
434		gettingfile = 0;
435		longjmp(restart, 1);
436	}
437}
438
439/*
440 * Handle unexpected EOF.
441 */
442static void
443terminateinput()
444{
445
446	if (gettingfile && curfile.action == USING) {
447		printf("Warning: %s %s\n",
448		    "End-of-input encountered while extracting", curfile.name);
449	}
450	curfile.name = "<name unknown>";
451	curfile.action = UNKNOWN;
452	curfile.dip = NULL;
453	curfile.ino = maxino;
454	if (gettingfile) {
455		gettingfile = 0;
456		longjmp(restart, 1);
457	}
458}
459
460/*
461 * handle multiple dumps per tape by skipping forward to the
462 * appropriate one.
463 */
464static void
465setdumpnum()
466{
467	struct mtop tcom;
468
469	if (dumpnum == 1 || volno != 1)
470		return;
471	if (pipein) {
472		fprintf(stderr, "Cannot have multiple dumps on pipe input\n");
473		done(1);
474	}
475	tcom.mt_op = MTFSF;
476	tcom.mt_count = dumpnum - 1;
477#ifdef RRESTORE
478	if (host)
479		rmtioctl(MTFSF, dumpnum - 1);
480	else
481#endif
482		if (ioctl(mt, (int)MTIOCTOP, (char *)&tcom) < 0)
483			fprintf(stderr, "ioctl MTFSF: %s\n", strerror(errno));
484}
485
486void
487printdumpinfo()
488{
489	fprintf(stdout, "Dump   date: %s", ctime(&spcl.c_date));
490	fprintf(stdout, "Dumped from: %s",
491	    (spcl.c_ddate == 0) ? "the epoch\n" : ctime(&spcl.c_ddate));
492	if (spcl.c_host[0] == '\0')
493		return;
494	fprintf(stderr, "Level %d dump of %s on %s:%s\n",
495		spcl.c_level, spcl.c_filesys, spcl.c_host, spcl.c_dev);
496	fprintf(stderr, "Label: %s\n", spcl.c_label);
497}
498
499int
500extractfile(name)
501	char *name;
502{
503	int mode;
504	struct timeval timep[2];
505	struct entry *ep;
506
507	curfile.name = name;
508	curfile.action = USING;
509	timep[0].tv_sec = curfile.dip->di_atime.tv_sec;
510	timep[0].tv_usec = curfile.dip->di_atime.tv_nsec / 1000;
511	timep[1].tv_sec = curfile.dip->di_mtime.tv_sec;
512	timep[1].tv_usec = curfile.dip->di_mtime.tv_nsec / 1000;
513	mode = curfile.dip->di_mode;
514	switch (mode & IFMT) {
515
516	default:
517		fprintf(stderr, "%s: unknown file mode 0%o\n", name, mode);
518		skipfile();
519		return (FAIL);
520
521	case IFSOCK:
522		vprintf(stdout, "skipped socket %s\n", name);
523		skipfile();
524		return (GOOD);
525
526	case IFDIR:
527		if (mflag) {
528			ep = lookupname(name);
529			if (ep == NULL || ep->e_flags & EXTRACT)
530				panic("unextracted directory %s\n", name);
531			skipfile();
532			return (GOOD);
533		}
534		vprintf(stdout, "extract file %s\n", name);
535		return (genliteraldir(name, curfile.ino));
536
537	case IFLNK:
538		lnkbuf[0] = '\0';
539		pathlen = 0;
540		getfile(xtrlnkfile, xtrlnkskip);
541		if (pathlen == 0) {
542			vprintf(stdout,
543			    "%s: zero length symbolic link (ignored)\n", name);
544			return (GOOD);
545		}
546		return (linkit(lnkbuf, name, SYMLINK));
547
548	case IFIFO:
549		if (mkfifo(name, mode) < 0) {
550			fprintf(stderr, "%s: cannot create FIFO: %s\n",
551				name, strerror(errno));
552			skipfile();
553			return (FAIL);
554		}
555		(void) chown(name, curfile.dip->di_uid, curfile.dip->di_gid);
556		(void) chmod(name, mode);
557		skipfile();
558		utimes(name, timep);
559		return (GOOD);
560
561	case IFCHR:
562	case IFBLK:
563		vprintf(stdout, "extract special file %s\n", name);
564		if (Nflag) {
565			skipfile();
566			return (GOOD);
567		}
568		if (mknod(name, mode, (int)curfile.dip->di_rdev) < 0) {
569			fprintf(stderr, "%s: cannot create special file: %s\n",
570			    name, strerror(errno));
571			skipfile();
572			return (FAIL);
573		}
574		(void) chown(name, curfile.dip->di_uid, curfile.dip->di_gid);
575		(void) chmod(name, mode);
576		skipfile();
577		utimes(name, timep);
578		return (GOOD);
579
580	case IFREG:
581		vprintf(stdout, "extract file %s\n", name);
582		if (Nflag) {
583			skipfile();
584			return (GOOD);
585		}
586		if ((ofile = open(name, O_WRONLY | O_CREAT | O_TRUNC,
587		    0666)) < 0) {
588			fprintf(stderr, "%s: cannot create file: %s\n",
589			    name, strerror(errno));
590			skipfile();
591			return (FAIL);
592		}
593		(void) fchown(ofile, curfile.dip->di_uid, curfile.dip->di_gid);
594		(void) fchmod(ofile, mode);
595		getfile(xtrfile, xtrskip);
596		(void) close(ofile);
597		utimes(name, timep);
598		return (GOOD);
599	}
600	/* NOTREACHED */
601}
602
603/*
604 * skip over bit maps on the tape
605 */
606void
607skipmaps()
608{
609
610	while (spcl.c_type == TS_BITS || spcl.c_type == TS_CLRI)
611		skipfile();
612}
613
614/*
615 * skip over a file on the tape
616 */
617void
618skipfile()
619{
620
621	curfile.action = SKIP;
622	getfile(xtrnull, xtrnull);
623}
624
625/*
626 * Extract a file from the tape.
627 * When an allocated block is found it is passed to the fill function;
628 * when an unallocated block (hole) is found, a zeroed buffer is passed
629 * to the skip function.
630 */
631void
632getfile(fill, skip)
633	void	(*fill) __P((char *, long));
634	void	(*skip) __P((char *, long));
635{
636	register int i;
637	int curblk = 0;
638	long size = spcl.c_dinode.di_size;
639	static char clearedbuf[MAXBSIZE];
640	char buf[MAXBSIZE / TP_BSIZE][TP_BSIZE];
641	char junk[TP_BSIZE];
642
643	if (spcl.c_type == TS_END)
644		panic("ran off end of tape\n");
645	if (spcl.c_magic != NFS_MAGIC)
646		panic("not at beginning of a file\n");
647	if (!gettingfile && setjmp(restart) != 0)
648		return;
649	gettingfile++;
650loop:
651	for (i = 0; i < spcl.c_count; i++) {
652		if (spcl.c_addr[i]) {
653			readtape(&buf[curblk++][0]);
654			if (curblk == fssize / TP_BSIZE) {
655				(*fill)((char *)buf, size > TP_BSIZE ?
656				     (long) (fssize) :
657				     (curblk - 1) * TP_BSIZE + size);
658				curblk = 0;
659			}
660		} else {
661			if (curblk > 0) {
662				(*fill)((char *)buf, size > TP_BSIZE ?
663				     (long) (curblk * TP_BSIZE) :
664				     (curblk - 1) * TP_BSIZE + size);
665				curblk = 0;
666			}
667			(*skip)(clearedbuf, size > TP_BSIZE ?
668				(long) TP_BSIZE : size);
669		}
670		if ((size -= TP_BSIZE) <= 0) {
671			for (i++; i < spcl.c_count; i++)
672				if (spcl.c_addr[i])
673					readtape(junk);
674			break;
675		}
676	}
677	if (gethead(&spcl) == GOOD && size > 0) {
678		if (spcl.c_type == TS_ADDR)
679			goto loop;
680		dprintf(stdout,
681			"Missing address (header) block for %s at %d blocks\n",
682			curfile.name, blksread);
683	}
684	if (curblk > 0)
685		(*fill)((char *)buf, (curblk * TP_BSIZE) + size);
686	findinode(&spcl);
687	gettingfile = 0;
688}
689
690/*
691 * Write out the next block of a file.
692 */
693static void
694xtrfile(buf, size)
695	char	*buf;
696	long	size;
697{
698
699	if (Nflag)
700		return;
701	if (write(ofile, buf, (int) size) == -1) {
702		fprintf(stderr,
703		    "write error extracting inode %d, name %s\nwrite: %s\n",
704			curfile.ino, curfile.name, strerror(errno));
705		done(1);
706	}
707}
708
709/*
710 * Skip over a hole in a file.
711 */
712/* ARGSUSED */
713static void
714xtrskip(buf, size)
715	char *buf;
716	long size;
717{
718
719	if (lseek(ofile, size, SEEK_CUR) == -1) {
720		fprintf(stderr,
721		    "seek error extracting inode %d, name %s\nlseek: %s\n",
722			curfile.ino, curfile.name, strerror(errno));
723		done(1);
724	}
725}
726
727/*
728 * Collect the next block of a symbolic link.
729 */
730static void
731xtrlnkfile(buf, size)
732	char	*buf;
733	long	size;
734{
735
736	pathlen += size;
737	if (pathlen > MAXPATHLEN) {
738		fprintf(stderr, "symbolic link name: %s->%s%s; too long %d\n",
739		    curfile.name, lnkbuf, buf, pathlen);
740		done(1);
741	}
742	(void) strcat(lnkbuf, buf);
743}
744
745/*
746 * Skip over a hole in a symbolic link (should never happen).
747 */
748/* ARGSUSED */
749static void
750xtrlnkskip(buf, size)
751	char *buf;
752	long size;
753{
754
755	fprintf(stderr, "unallocated block in symbolic link %s\n",
756		curfile.name);
757	done(1);
758}
759
760/*
761 * Collect the next block of a bit map.
762 */
763static void
764xtrmap(buf, size)
765	char	*buf;
766	long	size;
767{
768
769	bcopy(buf, map, size);
770	map += size;
771}
772
773/*
774 * Skip over a hole in a bit map (should never happen).
775 */
776/* ARGSUSED */
777static void
778xtrmapskip(buf, size)
779	char *buf;
780	long size;
781{
782
783	panic("hole in map\n");
784	map += size;
785}
786
787/*
788 * Noop, when an extraction function is not needed.
789 */
790/* ARGSUSED */
791void
792xtrnull(buf, size)
793	char *buf;
794	long size;
795{
796
797	return;
798}
799
800/*
801 * Read TP_BSIZE blocks from the input.
802 * Handle read errors, and end of media.
803 */
804static void
805readtape(buf)
806	char *buf;
807{
808	long rd, newvol, i;
809	int cnt, seek_failed;
810
811	if (blkcnt < numtrec) {
812		bcopy(&tapebuf[(blkcnt++ * TP_BSIZE)], buf, (long)TP_BSIZE);
813		blksread++;
814		tpblksread++;
815		return;
816	}
817	for (i = 0; i < ntrec; i++)
818		((struct s_spcl *)&tapebuf[i * TP_BSIZE])->c_magic = 0;
819	if (numtrec == 0)
820		numtrec = ntrec;
821	cnt = ntrec * TP_BSIZE;
822	rd = 0;
823getmore:
824#ifdef RRESTORE
825	if (host)
826		i = rmtread(&tapebuf[rd], cnt);
827	else
828#endif
829		i = read(mt, &tapebuf[rd], cnt);
830	/*
831	 * Check for mid-tape short read error.
832	 * If found, skip rest of buffer and start with the next.
833	 */
834	if (!pipein && numtrec < ntrec && i > 0) {
835		dprintf(stdout, "mid-media short read error.\n");
836		numtrec = ntrec;
837	}
838	/*
839	 * Handle partial block read.
840	 */
841	if (pipein && i == 0 && rd > 0)
842		i = rd;
843	else if (i > 0 && i != ntrec * TP_BSIZE) {
844		if (pipein) {
845			rd += i;
846			cnt -= i;
847			if (cnt > 0)
848				goto getmore;
849			i = rd;
850		} else {
851			/*
852			 * Short read. Process the blocks read.
853			 */
854			if (i % TP_BSIZE != 0)
855				vprintf(stdout,
856				    "partial block read: %d should be %d\n",
857				    i, ntrec * TP_BSIZE);
858			numtrec = i / TP_BSIZE;
859		}
860	}
861	/*
862	 * Handle read error.
863	 */
864	if (i < 0) {
865		fprintf(stderr, "Tape read error while ");
866		switch (curfile.action) {
867		default:
868			fprintf(stderr, "trying to set up tape\n");
869			break;
870		case UNKNOWN:
871			fprintf(stderr, "trying to resynchronize\n");
872			break;
873		case USING:
874			fprintf(stderr, "restoring %s\n", curfile.name);
875			break;
876		case SKIP:
877			fprintf(stderr, "skipping over inode %d\n",
878				curfile.ino);
879			break;
880		}
881		if (!yflag && !reply("continue"))
882			done(1);
883		i = ntrec * TP_BSIZE;
884		bzero(tapebuf, i);
885#ifdef RRESTORE
886		if (host)
887			seek_failed = (rmtseek(i, 1) < 0);
888		else
889#endif
890			seek_failed = (lseek(mt, i, SEEK_CUR) == (off_t)-1);
891
892		if (seek_failed) {
893			fprintf(stderr,
894			    "continuation failed: %s\n", strerror(errno));
895			done(1);
896		}
897	}
898	/*
899	 * Handle end of tape.
900	 */
901	if (i == 0) {
902		vprintf(stdout, "End-of-tape encountered\n");
903		if (!pipein) {
904			newvol = volno + 1;
905			volno = 0;
906			numtrec = 0;
907			getvol(newvol);
908			readtape(buf);
909			return;
910		}
911		if (rd % TP_BSIZE != 0)
912			panic("partial block read: %d should be %d\n",
913				rd, ntrec * TP_BSIZE);
914		terminateinput();
915		bcopy((char *)&endoftapemark, &tapebuf[rd], (long)TP_BSIZE);
916	}
917	blkcnt = 0;
918	bcopy(&tapebuf[(blkcnt++ * TP_BSIZE)], buf, (long)TP_BSIZE);
919	blksread++;
920	tpblksread++;
921}
922
923static void
924findtapeblksize()
925{
926	register long i;
927
928	for (i = 0; i < ntrec; i++)
929		((struct s_spcl *)&tapebuf[i * TP_BSIZE])->c_magic = 0;
930	blkcnt = 0;
931#ifdef RRESTORE
932	if (host)
933		i = rmtread(tapebuf, ntrec * TP_BSIZE);
934	else
935#endif
936		i = read(mt, tapebuf, ntrec * TP_BSIZE);
937
938	if (i <= 0) {
939		fprintf(stderr, "tape read error: %s\n", strerror(errno));
940		done(1);
941	}
942	if (i % TP_BSIZE != 0) {
943		fprintf(stderr, "Tape block size (%d) %s (%d)\n",
944			i, "is not a multiple of dump block size", TP_BSIZE);
945		done(1);
946	}
947	ntrec = i / TP_BSIZE;
948	numtrec = ntrec;
949	vprintf(stdout, "Tape block size is %d\n", ntrec);
950}
951
952void
953closemt()
954{
955
956	if (mt < 0)
957		return;
958#ifdef RRESTORE
959	if (host)
960		rmtclose();
961	else
962#endif
963		(void) close(mt);
964}
965
966/*
967 * Read the next block from the tape.
968 * Check to see if it is one of several vintage headers.
969 * If it is an old style header, convert it to a new style header.
970 * If it is not any valid header, return an error.
971 */
972static int
973gethead(buf)
974	struct s_spcl *buf;
975{
976	long i;
977	union {
978		quad_t	qval;
979		long	val[2];
980	} qcvt;
981	union u_ospcl {
982		char dummy[TP_BSIZE];
983		struct	s_ospcl {
984			long	c_type;
985			long	c_date;
986			long	c_ddate;
987			long	c_volume;
988			long	c_tapea;
989			u_short	c_inumber;
990			long	c_magic;
991			long	c_checksum;
992			struct odinode {
993				unsigned short odi_mode;
994				u_short	odi_nlink;
995				u_short	odi_uid;
996				u_short	odi_gid;
997				long	odi_size;
998				long	odi_rdev;
999				char	odi_addr[36];
1000				long	odi_atime;
1001				long	odi_mtime;
1002				long	odi_ctime;
1003			} c_dinode;
1004			long	c_count;
1005			char	c_addr[256];
1006		} s_ospcl;
1007	} u_ospcl;
1008
1009	if (!cvtflag) {
1010		readtape((char *)buf);
1011		if (buf->c_magic != NFS_MAGIC) {
1012			if (swabl(buf->c_magic) != NFS_MAGIC)
1013				return (FAIL);
1014			if (!Bcvt) {
1015				vprintf(stdout, "Note: Doing Byte swapping\n");
1016				Bcvt = 1;
1017			}
1018		}
1019		if (checksum((int *)buf) == FAIL)
1020			return (FAIL);
1021		if (Bcvt)
1022			swabst((u_char *)"8l4s31l", (u_char *)buf);
1023		goto good;
1024	}
1025	readtape((char *)(&u_ospcl.s_ospcl));
1026	bzero((char *)buf, (long)TP_BSIZE);
1027	buf->c_type = u_ospcl.s_ospcl.c_type;
1028	buf->c_date = u_ospcl.s_ospcl.c_date;
1029	buf->c_ddate = u_ospcl.s_ospcl.c_ddate;
1030	buf->c_volume = u_ospcl.s_ospcl.c_volume;
1031	buf->c_tapea = u_ospcl.s_ospcl.c_tapea;
1032	buf->c_inumber = u_ospcl.s_ospcl.c_inumber;
1033	buf->c_checksum = u_ospcl.s_ospcl.c_checksum;
1034	buf->c_magic = u_ospcl.s_ospcl.c_magic;
1035	buf->c_dinode.di_mode = u_ospcl.s_ospcl.c_dinode.odi_mode;
1036	buf->c_dinode.di_nlink = u_ospcl.s_ospcl.c_dinode.odi_nlink;
1037	buf->c_dinode.di_uid = u_ospcl.s_ospcl.c_dinode.odi_uid;
1038	buf->c_dinode.di_gid = u_ospcl.s_ospcl.c_dinode.odi_gid;
1039	buf->c_dinode.di_size = u_ospcl.s_ospcl.c_dinode.odi_size;
1040	buf->c_dinode.di_rdev = u_ospcl.s_ospcl.c_dinode.odi_rdev;
1041	buf->c_dinode.di_atime.tv_sec = u_ospcl.s_ospcl.c_dinode.odi_atime;
1042	buf->c_dinode.di_mtime.tv_sec = u_ospcl.s_ospcl.c_dinode.odi_mtime;
1043	buf->c_dinode.di_ctime.tv_sec = u_ospcl.s_ospcl.c_dinode.odi_ctime;
1044	buf->c_count = u_ospcl.s_ospcl.c_count;
1045	bcopy(u_ospcl.s_ospcl.c_addr, buf->c_addr, (long)256);
1046	if (u_ospcl.s_ospcl.c_magic != OFS_MAGIC ||
1047	    checksum((int *)(&u_ospcl.s_ospcl)) == FAIL)
1048		return(FAIL);
1049	buf->c_magic = NFS_MAGIC;
1050
1051good:
1052	if ((buf->c_dinode.di_size == 0 || buf->c_dinode.di_size > 0xfffffff) &&
1053	    (buf->c_dinode.di_mode & IFMT) == IFDIR && Qcvt == 0) {
1054		qcvt.qval = buf->c_dinode.di_size;
1055		if (qcvt.val[0] || qcvt.val[1]) {
1056			printf("Note: Doing Quad swapping\n");
1057			Qcvt = 1;
1058		}
1059	}
1060	if (Qcvt) {
1061		qcvt.qval = buf->c_dinode.di_size;
1062		i = qcvt.val[1];
1063		qcvt.val[1] = qcvt.val[0];
1064		qcvt.val[0] = i;
1065		buf->c_dinode.di_size = qcvt.qval;
1066	}
1067
1068	switch (buf->c_type) {
1069
1070	case TS_CLRI:
1071	case TS_BITS:
1072		/*
1073		 * Have to patch up missing information in bit map headers
1074		 */
1075		buf->c_inumber = 0;
1076		buf->c_dinode.di_size = buf->c_count * TP_BSIZE;
1077		for (i = 0; i < buf->c_count; i++)
1078			buf->c_addr[i]++;
1079		break;
1080
1081	case TS_TAPE:
1082		if ((buf->c_flags & DR_NEWINODEFMT) == 0)
1083			oldinofmt = 1;
1084		/* fall through */
1085	case TS_END:
1086		buf->c_inumber = 0;
1087		break;
1088
1089	case TS_INODE:
1090	case TS_ADDR:
1091		break;
1092
1093	default:
1094		panic("gethead: unknown inode type %d\n", buf->c_type);
1095		break;
1096	}
1097	/*
1098	 * If we are restoring a filesystem with old format inodes,
1099	 * copy the uid/gid to the new location.
1100	 */
1101	if (oldinofmt) {
1102		buf->c_dinode.di_uid = buf->c_dinode.di_ouid;
1103		buf->c_dinode.di_gid = buf->c_dinode.di_ogid;
1104	}
1105	if (dflag)
1106		accthdr(buf);
1107	return(GOOD);
1108}
1109
1110/*
1111 * Check that a header is where it belongs and predict the next header
1112 */
1113static void
1114accthdr(header)
1115	struct s_spcl *header;
1116{
1117	static ino_t previno = 0x7fffffff;
1118	static int prevtype;
1119	static long predict;
1120	long blks, i;
1121
1122	if (header->c_type == TS_TAPE) {
1123		fprintf(stderr, "Volume header (%s inode format) ",
1124		    oldinofmt ? "old" : "new");
1125 		if (header->c_firstrec)
1126 			fprintf(stderr, "begins with record %d",
1127 				header->c_firstrec);
1128 		fprintf(stderr, "\n");
1129		previno = 0x7fffffff;
1130		return;
1131	}
1132	if (previno == 0x7fffffff)
1133		goto newcalc;
1134	switch (prevtype) {
1135	case TS_BITS:
1136		fprintf(stderr, "Dump mask header");
1137		break;
1138	case TS_CLRI:
1139		fprintf(stderr, "Remove mask header");
1140		break;
1141	case TS_INODE:
1142		fprintf(stderr, "File header, ino %d", previno);
1143		break;
1144	case TS_ADDR:
1145		fprintf(stderr, "File continuation header, ino %d", previno);
1146		break;
1147	case TS_END:
1148		fprintf(stderr, "End of tape header");
1149		break;
1150	}
1151	if (predict != blksread - 1)
1152		fprintf(stderr, "; predicted %d blocks, got %d blocks",
1153			predict, blksread - 1);
1154	fprintf(stderr, "\n");
1155newcalc:
1156	blks = 0;
1157	if (header->c_type != TS_END)
1158		for (i = 0; i < header->c_count; i++)
1159			if (header->c_addr[i] != 0)
1160				blks++;
1161	predict = blks;
1162	blksread = 0;
1163	prevtype = header->c_type;
1164	previno = header->c_inumber;
1165}
1166
1167/*
1168 * Find an inode header.
1169 * Complain if had to skip, and complain is set.
1170 */
1171static void
1172findinode(header)
1173	struct s_spcl *header;
1174{
1175	static long skipcnt = 0;
1176	long i;
1177	char buf[TP_BSIZE];
1178
1179	curfile.name = "<name unknown>";
1180	curfile.action = UNKNOWN;
1181	curfile.dip = NULL;
1182	curfile.ino = 0;
1183	do {
1184		if (header->c_magic != NFS_MAGIC) {
1185			skipcnt++;
1186			while (gethead(header) == FAIL ||
1187			    header->c_date != dumpdate)
1188				skipcnt++;
1189		}
1190		switch (header->c_type) {
1191
1192		case TS_ADDR:
1193			/*
1194			 * Skip up to the beginning of the next record
1195			 */
1196			for (i = 0; i < header->c_count; i++)
1197				if (header->c_addr[i])
1198					readtape(buf);
1199			while (gethead(header) == FAIL ||
1200			    header->c_date != dumpdate)
1201				skipcnt++;
1202			break;
1203
1204		case TS_INODE:
1205			curfile.dip = &header->c_dinode;
1206			curfile.ino = header->c_inumber;
1207			break;
1208
1209		case TS_END:
1210			curfile.ino = maxino;
1211			break;
1212
1213		case TS_CLRI:
1214			curfile.name = "<file removal list>";
1215			break;
1216
1217		case TS_BITS:
1218			curfile.name = "<file dump list>";
1219			break;
1220
1221		case TS_TAPE:
1222			panic("unexpected tape header\n");
1223			/* NOTREACHED */
1224
1225		default:
1226			panic("unknown tape header type %d\n", spcl.c_type);
1227			/* NOTREACHED */
1228
1229		}
1230	} while (header->c_type == TS_ADDR);
1231	if (skipcnt > 0)
1232		fprintf(stderr, "resync restore, skipped %d blocks\n", skipcnt);
1233	skipcnt = 0;
1234}
1235
1236static int
1237checksum(buf)
1238	register int *buf;
1239{
1240	register int i, j;
1241
1242	j = sizeof(union u_spcl) / sizeof(int);
1243	i = 0;
1244	if(!Bcvt) {
1245		do
1246			i += *buf++;
1247		while (--j);
1248	} else {
1249		/* What happens if we want to read restore tapes
1250			for a 16bit int machine??? */
1251		do
1252			i += swabl(*buf++);
1253		while (--j);
1254	}
1255
1256	if (i != CHECKSUM) {
1257		fprintf(stderr, "Checksum error %o, inode %d file %s\n", i,
1258			curfile.ino, curfile.name);
1259		return(FAIL);
1260	}
1261	return(GOOD);
1262}
1263
1264#ifdef RRESTORE
1265#if __STDC__
1266#include <stdarg.h>
1267#else
1268#include <varargs.h>
1269#endif
1270
1271void
1272#if __STDC__
1273msg(const char *fmt, ...)
1274#else
1275msg(fmt, va_alist)
1276	char *fmt;
1277	va_dcl
1278#endif
1279{
1280	va_list ap;
1281#if __STDC__
1282	va_start(ap, fmt);
1283#else
1284	va_start(ap);
1285#endif
1286	(void)vfprintf(stderr, fmt, ap);
1287	va_end(ap);
1288}
1289#endif /* RRESTORE */
1290
1291static u_char *
1292swabshort(sp, n)
1293	register u_char *sp;
1294	register int n;
1295{
1296	char c;
1297
1298	while (--n >= 0) {
1299		c = sp[0]; sp[0] = sp[1]; sp[1] = c;
1300		sp += 2;
1301	}
1302	return (sp);
1303}
1304
1305static u_char *
1306swablong(sp, n)
1307	register u_char *sp;
1308	register int n;
1309{
1310	char c;
1311
1312	while (--n >= 0) {
1313		c = sp[0]; sp[0] = sp[3]; sp[3] = c;
1314		c = sp[2]; sp[2] = sp[1]; sp[1] = c;
1315		sp += 4;
1316	}
1317	return (sp);
1318}
1319
1320void
1321swabst(cp, sp)
1322	register u_char *cp, *sp;
1323{
1324	int n = 0;
1325
1326	while (*cp) {
1327		switch (*cp) {
1328		case '0': case '1': case '2': case '3': case '4':
1329		case '5': case '6': case '7': case '8': case '9':
1330			n = (n * 10) + (*cp++ - '0');
1331			continue;
1332
1333		case 's': case 'w': case 'h':
1334			if (n == 0)
1335				n = 1;
1336			sp = swabshort(sp, n);
1337			break;
1338
1339		case 'l':
1340			if (n == 0)
1341				n = 1;
1342			sp = swablong(sp, n);
1343			break;
1344
1345		default: /* Any other character, like 'b' counts as byte. */
1346			if (n == 0)
1347				n = 1;
1348			sp += n;
1349			break;
1350		}
1351		cp++;
1352		n = 0;
1353	}
1354}
1355
1356static u_long
1357swabl(x)
1358	u_long x;
1359{
1360	swabst((u_char *)"l", (u_char *)&x);
1361	return (x);
1362}
1363