tape.c revision 164911
1139823Simp/*
221259Swollman * Copyright (c) 1983, 1993
321259Swollman *	The Regents of the University of California.  All rights reserved.
421259Swollman * (c) UNIX System Laboratories, Inc.
521259Swollman * All or some portions of this file are derived from material licensed
621259Swollman * to the University of California by American Telephone and Telegraph
721259Swollman * Co. or Unix System Laboratories, Inc. and are reproduced herein with
821259Swollman * the permission of UNIX System Laboratories, Inc.
921259Swollman *
1021259Swollman * Redistribution and use in source and binary forms, with or without
1121259Swollman * modification, are permitted provided that the following conditions
1221259Swollman * are met:
1321259Swollman * 1. Redistributions of source code must retain the above copyright
1421259Swollman *    notice, this list of conditions and the following disclaimer.
1521259Swollman * 2. Redistributions in binary form must reproduce the above copyright
1621259Swollman *    notice, this list of conditions and the following disclaimer in the
1721259Swollman *    documentation and/or other materials provided with the distribution.
1821259Swollman * 4. Neither the name of the University nor the names of its contributors
1921259Swollman *    may be used to endorse or promote products derived from this software
2021259Swollman *    without specific prior written permission.
2121259Swollman *
2221259Swollman * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2321259Swollman * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2421259Swollman * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2521259Swollman * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2621259Swollman * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2721259Swollman * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2821259Swollman * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2921259Swollman * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3050477Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3121259Swollman * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3221259Swollman * SUCH DAMAGE.
3321259Swollman */
3421259Swollman
3521259Swollman#ifndef lint
3621259Swollman#if 0
3721259Swollmanstatic char sccsid[] = "@(#)tape.c	8.9 (Berkeley) 5/1/95";
3821259Swollman#endif
3921259Swollman#endif /* not lint */
4021259Swollman
4121259Swollman#include <sys/cdefs.h>
4221259Swollman__FBSDID("$FreeBSD: head/sbin/restore/tape.c 164911 2006-12-05 11:18:51Z dwmalone $");
4321259Swollman
4421259Swollman#include <sys/param.h>
4521259Swollman#include <sys/file.h>
4621259Swollman#include <sys/mtio.h>
4721259Swollman#include <sys/stat.h>
4821259Swollman#include <sys/time.h>
4921259Swollman
5021259Swollman#include <ufs/ufs/dinode.h>
51108533Sschweikh#include <protocols/dumprestore.h>
5221259Swollman
5321259Swollman#include <errno.h>
5421259Swollman#include <limits.h>
5521259Swollman#include <paths.h>
56108533Sschweikh#include <setjmp.h>
5721259Swollman#include <stdint.h>
5821259Swollman#include <stdio.h>
5921259Swollman#include <stdlib.h>
6021259Swollman#include <string.h>
6121259Swollman#include <time.h>
6221259Swollman#include <timeconv.h>
6321259Swollman#include <unistd.h>
6421259Swollman
6521259Swollman#include "restore.h"
6683366Sjulian#include "extern.h"
6721259Swollman
6885074Srustatic long	fssize = MAXBSIZE;
6921259Swollmanstatic int	mt = -1;
7021259Swollmanstatic int	pipein = 0;
71142215Sglebiusstatic int	pipecmdin = 0;
72155051Sglebiusstatic FILE	*popenfp = NULL;
73191148Skmacystatic char	*magtape;
7421259Swollmanstatic int	blkcnt;
7521259Swollmanstatic int	numtrec;
7621259Swollmanstatic char	*tapebuf;
7721259Swollmanstatic union	u_spcl endoftapemark;
7869224Sjlemonstatic long	byteslide = 0;
7969152Sjlemonstatic long	blksread;		/* blocks read since last header */
80126264Smlaierstatic int64_t	tapeaddr = 0;		/* current TP_BSIZE tape record */
81186207Skmacystatic long	tapesread;
8269224Sjlemonstatic jmp_buf	restart;
8374914Sjhbstatic int	gettingfile = 0;	/* restart has a valid frame */
8474914Sjhbstatic char	*host = NULL;
85186199Skmacystatic int	readmapflag;
8683130Sjlemon
87132712Srwatsonstatic int	ofile;
8869152Sjlemonstatic char	*map;
89121816Sbrooksstatic char	lnkbuf[MAXPATHLEN + 1];
90121816Sbrooksstatic int	pathlen;
91130416Smlaier
92130416Smlaierint		Bcvt;		/* Swap Bytes */
9360938Sjakeint		oldinofmt;	/* FreeBSD 1 inode format needs cvt */
9460938Sjake
9560938Sjake#define	FLUSHTAPEBUF()	blkcnt = ntrec + 1
9672084Sphk
97159781Smlaierstatic void	 accthdr(struct s_spcl *);
9821259Swollmanstatic int	 checksum(int *);
9921259Swollmanstatic void	 findinode(struct s_spcl *);
10021259Swollmanstatic void	 findtapeblksize(void);
10121259Swollmanstatic int	 gethead(struct s_spcl *);
10221259Swollmanstatic void	 readtape(char *);
10321259Swollmanstatic void	 setdumpnum(void);
10421259Swollmanstatic u_long	 swabl(u_long);
10521259Swollmanstatic u_char	*swablong(u_char *, int);
10621259Swollmanstatic u_char	*swabshort(u_char *, int);
10721259Swollmanstatic void	 terminateinput(void);
10869152Sjlemonstatic void	 xtrfile(char *, long);
10921259Swollmanstatic void	 xtrlnkfile(char *, long);
11021259Swollmanstatic void	 xtrlnkskip(char *, long);
11121259Swollmanstatic void	 xtrmap(char *, long);
11221259Swollmanstatic void	 xtrmapskip(char *, long);
11321259Swollmanstatic void	 xtrskip(char *, long);
11421259Swollman
11521259Swollman/*
11684380Smjacob * Set up an input source
11721259Swollman */
11821259Swollmanvoid
119147256Sbrookssetinput(char *source, int ispipecommand)
12060938Sjake{
121121816Sbrooks	FLUSHTAPEBUF();
122121816Sbrooks	if (bflag)
123121816Sbrooks		newtapebuf(ntrec);
12421259Swollman	else
125128291Sluigi		newtapebuf(NTREC > HIGHDENSITYTREC ? NTREC : HIGHDENSITYTREC);
126128291Sluigi	terminal = stdin;
127128315Sluigi
128128315Sluigi	if (ispipecommand)
129128315Sluigi		pipecmdin++;
130128315Sluigi	else
131128291Sluigi#ifdef RRESTORE
132128315Sluigi	if (strchr(source, ':')) {
133152315Sru		host = source;
134128291Sluigi		source = strchr(host, ':');
135133741Sjmg		*source++ = '\0';
13683130Sjlemon		if (rmthost(host) == 0)
137142901Sglebius			done(1);
13821259Swollman	} else
13921259Swollman#endif
14021259Swollman	if (strcmp(source, "-") == 0) {
141155051Sglebius		/*
142102052Ssobomax		 * Since input is coming from a pipe we must establish
143162070Sandre		 * our own connection to the terminal.
144162070Sandre		 */
14521259Swollman		terminal = fopen(_PATH_TTY, "r");
14621259Swollman		if (terminal == NULL) {
14721259Swollman			(void)fprintf(stderr, "cannot open %s: %s\n",
14821404Swollman			    _PATH_TTY, strerror(errno));
14921404Swollman			terminal = fopen(_PATH_DEVNULL, "r");
15021259Swollman			if (terminal == NULL) {
15121259Swollman				(void)fprintf(stderr, "cannot open %s: %s\n",
15292725Salfred				    _PATH_DEVNULL, strerror(errno));
153191148Skmacy				done(1);
154106931Ssam			}
155106931Ssam		}
15621259Swollman		pipein++;
15792725Salfred	}
15821259Swollman	setuid(getuid());	/* no longer need or want root privileges */
15992725Salfred	magtape = strdup(source);
16021259Swollman	if (magtape == NULL) {
16192725Salfred		fprintf(stderr, "Cannot allocate space for magtape buffer\n");
16221259Swollman		done(1);
16392725Salfred	}
16421404Swollman}
16592725Salfred
166189230Srwatsonvoid
167189230Srwatsonnewtapebuf(long size)
168189230Srwatson{
169189230Srwatson	static int tapebufsize = -1;
170152315Sru
171174388Skmacy	ntrec = size;
172148265Srwatson	if (size <= tapebufsize)
173130416Smlaier		return;
174123220Simp	if (tapebuf != NULL)
175127828Sluigi		free(tapebuf - TP_BSIZE);
176146986Sthompsa	tapebuf = malloc((size+1) * TP_BSIZE);
177146986Sthompsa	if (tapebuf == NULL) {
178122524Srwatson		fprintf(stderr, "Cannot allocate space for tape buffer\n");
179121161Sume		done(1);
180127828Sluigi	}
181127828Sluigi	tapebuf += TP_BSIZE;
182121161Sume	tapebufsize = size;
183121470Sume}
184186199Skmacy
185145320Sglebius/*
186148640Srwatson * Verify that the tape drive can be accessed and
187186119Sqingli * that it actually is a dump tape.
188152209Sthompsa */
189159781Smlaiervoid
190159781Smlaiersetup(void)
191159781Smlaier{
192168793Sthompsa	int i, j, *ip;
193189230Srwatson	struct stat stbuf;
194189230Srwatson
195189230Srwatson	vprintf(stdout, "Verify tape and initialize maps\n");
196189230Srwatson	if (pipecmdin) {
197189230Srwatson		if (setenv("RESTORE_VOLUME", "1", 1) == -1) {
198189230Srwatson			fprintf(stderr, "Cannot set $RESTORE_VOLUME: %s\n",
199189230Srwatson			    strerror(errno));
200189230Srwatson			done(1);
20121259Swollman		}
20269152Sjlemon		popenfp = popen(magtape, "r");
20392725Salfred		mt = popenfp ? fileno(popenfp) : -1;
20421259Swollman	} else
205128376Sluigi#ifdef RRESTORE
206128376Sluigi	if (host)
207128376Sluigi		mt = rmtopen(magtape, 0);
208128376Sluigi	else
20921259Swollman#endif
21021259Swollman	if (pipein)
21121259Swollman		mt = 0;
21221259Swollman	else
21321259Swollman		mt = open(magtape, O_RDONLY, 0);
21421259Swollman	if (mt < 0) {
215128871Sandre		fprintf(stderr, "%s: %s\n", magtape, strerror(errno));
21621259Swollman		done(1);
21758698Sjlemon	}
21821259Swollman	volno = 1;
21921259Swollman	setdumpnum();
22021259Swollman	FLUSHTAPEBUF();
22121259Swollman	if (!pipein && !bflag)
22221259Swollman		findtapeblksize();
22321259Swollman	if (gethead(&spcl) == FAIL) {
22421259Swollman		fprintf(stderr, "Tape is not a dump tape\n");
22521259Swollman		done(1);
22621259Swollman	}
22721259Swollman	if (pipein) {
22821259Swollman		endoftapemark.s_spcl.c_magic = FS_UFS2_MAGIC;
22921259Swollman		endoftapemark.s_spcl.c_type = TS_END;
230136950Sjmg		ip = (int *)&endoftapemark;
23121259Swollman		j = sizeof(union u_spcl) / sizeof(int);
23253541Sshin		i = 0;
23353541Sshin		do
23453541Sshin			i += *ip++;
235160981Sbrooks		while (--j);
23653541Sshin		endoftapemark.s_spcl.c_checksum = CHECKSUM - i;
23721259Swollman	}
238148640Srwatson	if (vflag || command == 't')
239148640Srwatson		printdumpinfo();
240148640Srwatson	dumptime = _time64_to_time(spcl.c_ddate);
241148640Srwatson	dumpdate = _time64_to_time(spcl.c_date);
242148640Srwatson	if (stat(".", &stbuf) < 0) {
243148640Srwatson		fprintf(stderr, "cannot stat .: %s\n", strerror(errno));
244148640Srwatson		done(1);
245148640Srwatson	}
246148640Srwatson	if (stbuf.st_blksize > 0 && stbuf.st_blksize < TP_BSIZE )
247148640Srwatson		fssize = TP_BSIZE;
24821259Swollman	if (stbuf.st_blksize >= TP_BSIZE && stbuf.st_blksize <= MAXBSIZE)
24921259Swollman		fssize = stbuf.st_blksize;
25021259Swollman	if (((fssize - 1) & fssize) != 0) {
25121259Swollman		fprintf(stderr, "bad block size %ld\n", fssize);
25221259Swollman		done(1);
25372200Sbmilekic	}
25472200Sbmilekic	if (spcl.c_volume != 1) {
255130416Smlaier		fprintf(stderr, "Tape is not volume 1 of the dump\n");
25669152Sjlemon		done(1);
25769152Sjlemon	}
25869152Sjlemon	if (gethead(&spcl) == FAIL) {
25921259Swollman		dprintf(stdout, "header read failed at %ld blocks\n", blksread);
26069152Sjlemon		panic("no header after volume mark!\n");
26169152Sjlemon	}
26269152Sjlemon	findinode(&spcl);
26369152Sjlemon	if (spcl.c_type != TS_CLRI) {
26469152Sjlemon		fprintf(stderr, "Cannot find file removal list\n");
26569152Sjlemon		done(1);
26669152Sjlemon	}
26769152Sjlemon	maxino = (spcl.c_count * TP_BSIZE * NBBY) + 1;
26869152Sjlemon	dprintf(stdout, "maxino = %d\n", maxino);
26969152Sjlemon	map = calloc((unsigned)1, (unsigned)howmany(maxino, NBBY));
27069152Sjlemon	if (map == NULL)
27169152Sjlemon		panic("no memory for active inode map\n");
27269152Sjlemon	usedinomap = map;
27369152Sjlemon	curfile.action = USING;
27469152Sjlemon	getfile(xtrmap, xtrmapskip);
27569152Sjlemon	if (spcl.c_type != TS_BITS) {
27669152Sjlemon		fprintf(stderr, "Cannot find file dump list\n");
27769152Sjlemon		done(1);
27869152Sjlemon	}
27969152Sjlemon	map = calloc((unsigned)1, (unsigned)howmany(maxino, NBBY));
28069152Sjlemon	if (map == (char *)NULL)
28169152Sjlemon		panic("no memory for file dump list\n");
28269152Sjlemon	dumpmap = map;
28369152Sjlemon	curfile.action = USING;
28469152Sjlemon	getfile(xtrmap, xtrmapskip);
28569152Sjlemon	/*
28669152Sjlemon	 * If there may be whiteout entries on the tape, pretend that the
28769152Sjlemon	 * whiteout inode exists, so that the whiteout entries can be
28869152Sjlemon	 * extracted.
28969152Sjlemon	 */
29069152Sjlemon	SETINO(WINO, dumpmap);
29169152Sjlemon	/* 'r' restores don't call getvol() for tape 1, so mark it as read. */
29269152Sjlemon	if (command == 'r')
293136950Sjmg		tapesread = 1;
29469152Sjlemon}
29569152Sjlemon
29669152Sjlemon/*
29769152Sjlemon * Prompt user to load a new dump volume.
29869152Sjlemon * "Nextvol" is the next suggested volume to use.
29969152Sjlemon * This suggested volume is enforced when doing full
30069152Sjlemon * or incremental restores, but can be overridden by
30169152Sjlemon * the user when only extracting a subset of the files.
30269152Sjlemon */
30369152Sjlemonvoid
30469152Sjlemongetvol(long nextvol)
30569152Sjlemon{
306130416Smlaier	int64_t prevtapea;
307130416Smlaier	long i, newvol, savecnt;
308130416Smlaier	union u_spcl tmpspcl;
309130416Smlaier#	define tmpbuf tmpspcl.s_spcl
31069152Sjlemon	char buf[TP_BSIZE];
31169152Sjlemon
31269152Sjlemon	if (nextvol == 1) {
31369152Sjlemon		tapesread = 0;
31469152Sjlemon		gettingfile = 0;
31569152Sjlemon	}
31669152Sjlemon	prevtapea = tapeaddr;
31769152Sjlemon	savecnt = blksread;
31869152Sjlemon	if (pipein) {
319130416Smlaier		if (nextvol != 1) {
320130416Smlaier			panic("Changing volumes on pipe input?\n");
321130416Smlaier			/* Avoid looping if we couldn't ask the user. */
322130416Smlaier			if (yflag || ferror(terminal) || feof(terminal))
323130416Smlaier				done(1);
324130416Smlaier		}
32555205Speter		if (volno == 1)
326126264Smlaier			return;
327126264Smlaier		if (pipecmdin) {
328126264Smlaier			closemt();
329126264Smlaier			goto getpipecmdhdr;
330126264Smlaier		}
331126264Smlaier		goto gethdr;
332126264Smlaier	}
333126264Smlaieragain:
334126264Smlaier	if (pipein)
335126264Smlaier		done(1); /* pipes do not get a second chance */
336159781Smlaier	if (command == 'R' || command == 'r' || curfile.action != SKIP)
337159781Smlaier		newvol = nextvol;
338159781Smlaier	else
339159781Smlaier		newvol = 0;
340159781Smlaier	while (newvol <= 0) {
341159781Smlaier		if (tapesread == 0) {
342159781Smlaier			fprintf(stderr, "%s%s%s%s%s%s%s",
343159781Smlaier			    "You have not read any tapes yet.\n",
344159781Smlaier			    "If you are extracting just a few files,",
345159781Smlaier			    " start with the last volume\n",
346159781Smlaier			    "and work towards the first; restore",
347159781Smlaier			    " can quickly skip tapes that\n",
348159781Smlaier			    "have no further files to extract.",
349159781Smlaier			    " Otherwise, begin with volume 1.\n");
350159781Smlaier		} else {
351159781Smlaier			fprintf(stderr, "You have read volumes");
352159781Smlaier			strcpy(buf, ": ");
353159781Smlaier			for (i = 0; i < 32; i++)
354159781Smlaier				if (tapesread & (1 << i)) {
355159781Smlaier					fprintf(stderr, "%s%ld", buf, i + 1);
356159781Smlaier					strcpy(buf, ", ");
357159781Smlaier				}
358159781Smlaier			fprintf(stderr, "\n");
359159781Smlaier		}
360159781Smlaier		do	{
361159781Smlaier			fprintf(stderr, "Specify next volume #: ");
362159781Smlaier			(void) fflush(stderr);
363159781Smlaier			if (fgets(buf, BUFSIZ, terminal) == NULL)
364159781Smlaier				done(1);
365159781Smlaier		} while (buf[0] == '\n');
366159781Smlaier		newvol = atoi(buf);
367121470Sume		if (newvol <= 0) {
368186199Skmacy			fprintf(stderr,
369121470Sume			    "Volume numbers are positive numerics\n");
370186199Skmacy		}
371186199Skmacy	}
372186199Skmacy	if (newvol == volno) {
373186199Skmacy		tapesread |= 1 << (volno - 1);
374186199Skmacy		return;
375186199Skmacy	}
376186199Skmacy	closemt();
377186199Skmacy	fprintf(stderr, "Mount tape volume %ld\n", newvol);
378186119Sqingli	fprintf(stderr, "Enter ``none'' if there are no more tapes\n");
379186199Skmacy	fprintf(stderr, "otherwise enter tape name (default: %s) ", magtape);
380186199Skmacy	(void) fflush(stderr);
381186199Skmacy	if (fgets(buf, BUFSIZ, terminal) == NULL)
382137065Srwatson		done(1);
383137065Srwatson	if (!strcmp(buf, "none\n")) {
384130416Smlaier		terminateinput();
385130416Smlaier		return;
386130416Smlaier	}
387130416Smlaier	if (buf[0] != '\n') {
38821259Swollman		(void) strcpy(magtape, buf);
389132712Srwatson		magtape[strlen(magtape) - 1] = '\0';
390132712Srwatson	}
391130416Smlaier	if (pipecmdin) {
392130416Smlaier		char volno[sizeof("2147483647")];
393130416Smlaier
394130416Smlaiergetpipecmdhdr:
395130416Smlaier		(void)sprintf(volno, "%d", newvol);
396130416Smlaier		if (setenv("RESTORE_VOLUME", volno, 1) == -1) {
397130416Smlaier			fprintf(stderr, "Cannot set $RESTORE_VOLUME: %s\n",
398130416Smlaier			    strerror(errno));
399130416Smlaier			done(1);
400130416Smlaier		}
401130416Smlaier		popenfp = popen(magtape, "r");
402130416Smlaier		mt = popenfp ? fileno(popenfp) : -1;
403130416Smlaier	} else
404130416Smlaier#ifdef RRESTORE
405130416Smlaier	if (host)
406130416Smlaier		mt = rmtopen(magtape, 0);
407130416Smlaier	else
408130416Smlaier#endif
40921259Swollman		mt = open(magtape, O_RDONLY, 0);
410130416Smlaier
411130416Smlaier	if (mt == -1) {
412130416Smlaier		fprintf(stderr, "Cannot open %s\n", magtape);
413130508Smlaier		volno = -1;
414130416Smlaier		goto again;
415130416Smlaier	}
416130416Smlaiergethdr:
417130416Smlaier	volno = newvol;
418130416Smlaier	setdumpnum();
419130416Smlaier	FLUSHTAPEBUF();
420130416Smlaier	if (gethead(&tmpbuf) == FAIL) {
421130416Smlaier		dprintf(stdout, "header read failed at %ld blocks\n", blksread);
422130416Smlaier		fprintf(stderr, "tape is not dump tape\n");
423130416Smlaier		volno = 0;
424130416Smlaier		goto again;
425130416Smlaier	}
426130416Smlaier	if (tmpbuf.c_volume != volno) {
427130416Smlaier		fprintf(stderr, "Wrong volume (%ld)\n", tmpbuf.c_volume);
428130416Smlaier		volno = 0;
429130416Smlaier		goto again;
430130508Smlaier	}
431130416Smlaier	if (_time64_to_time(tmpbuf.c_date) != dumpdate ||
432130416Smlaier	    _time64_to_time(tmpbuf.c_ddate) != dumptime) {
433130416Smlaier		time_t t = _time64_to_time(tmpbuf.c_date);
434130416Smlaier		fprintf(stderr, "Wrong dump date\n\tgot: %s", ctime(&t));
435130416Smlaier		fprintf(stderr, "\twanted: %s", ctime(&dumpdate));
436130416Smlaier		volno = 0;
437130416Smlaier		goto again;
438130416Smlaier	}
439130416Smlaier	tapesread |= 1 << (volno - 1);
440130416Smlaier	blksread = savecnt;
441130416Smlaier 	/*
442130416Smlaier 	 * If continuing from the previous volume, skip over any
443130416Smlaier 	 * blocks read already at the end of the previous volume.
444130416Smlaier 	 *
445130416Smlaier 	 * If coming to this volume at random, skip to the beginning
446130416Smlaier 	 * of the next record.
447130416Smlaier 	 */
448130416Smlaier	dprintf(stdout, "last rec %qd, tape starts with %qd\n", prevtapea,
449130416Smlaier	    tmpbuf.c_tapea);
450130416Smlaier 	if (tmpbuf.c_type == TS_TAPE) {
451130416Smlaier 		if (curfile.action != USING) {
452130416Smlaier			/*
453130416Smlaier			 * XXX Dump incorrectly sets c_count to 1 in the
454130416Smlaier			 * volume header of the first tape, so ignore
455130416Smlaier			 * c_count when volno == 1.
456130416Smlaier			 */
457130416Smlaier			if (volno != 1)
458130416Smlaier				for (i = tmpbuf.c_count; i > 0; i--)
459130416Smlaier					readtape(buf);
460130416Smlaier 		} else if (tmpbuf.c_tapea <= prevtapea) {
461130416Smlaier			/*
462130416Smlaier			 * Normally the value of c_tapea in the volume
463130416Smlaier			 * header is the record number of the header itself.
464130416Smlaier			 * However in the volume header following an EOT-
465130416Smlaier			 * terminated tape, it is the record number of the
466130416Smlaier			 * first continuation data block (dump bug?).
467130416Smlaier			 *
468130416Smlaier			 * The next record we want is `prevtapea + 1'.
469130416Smlaier			 */
470130416Smlaier 			i = prevtapea + 1 - tmpbuf.c_tapea;
471148886Srwatson			dprintf(stderr, "Skipping %ld duplicate record%s.\n",
472148886Srwatson				i, i > 1 ? "s" : "");
473148886Srwatson 			while (--i >= 0)
474148886Srwatson 				readtape(buf);
475130416Smlaier 		}
476130416Smlaier 	}
477130416Smlaier	if (curfile.action == USING) {
478130416Smlaier		if (volno == 1)
479130416Smlaier			panic("active file into volume 1\n");
480130416Smlaier		return;
481130416Smlaier	}
482130416Smlaier	(void) gethead(&spcl);
483130416Smlaier	findinode(&spcl);
484130416Smlaier	if (gettingfile) {
485130416Smlaier		gettingfile = 0;
486130416Smlaier		longjmp(restart, 1);
487148886Srwatson	}
488132712Srwatson}
489130416Smlaier
490130416Smlaier/*
491130416Smlaier * Handle unexpected EOF.
492130416Smlaier */
493130512Smlaierstatic void
494130416Smlaierterminateinput(void)
495130416Smlaier{
496130416Smlaier
497130416Smlaier	if (gettingfile && curfile.action == USING) {
498130416Smlaier		printf("Warning: %s %s\n",
499130416Smlaier		    "End-of-input encountered while extracting", curfile.name);
500130416Smlaier	}
501130416Smlaier	curfile.name = "<name unknown>";
502130416Smlaier	curfile.action = UNKNOWN;
503130416Smlaier	curfile.mode = 0;
504130416Smlaier	curfile.ino = maxino;
505130416Smlaier	if (gettingfile) {
506130416Smlaier		gettingfile = 0;
507130416Smlaier		longjmp(restart, 1);
508130416Smlaier	}
509130416Smlaier}
510130416Smlaier
511130416Smlaier/*
512130416Smlaier * handle multiple dumps per tape by skipping forward to the
513130416Smlaier * appropriate one.
514130416Smlaier */
515130416Smlaierstatic void
516130416Smlaiersetdumpnum(void)
517130416Smlaier{
518130416Smlaier	struct mtop tcom;
519130416Smlaier
520130416Smlaier	if (dumpnum == 1 || volno != 1)
521130416Smlaier		return;
522130416Smlaier	if (pipein) {
523130416Smlaier		fprintf(stderr, "Cannot have multiple dumps on pipe input\n");
524130416Smlaier		done(1);
525130416Smlaier	}
526132152Smlaier	tcom.mt_op = MTFSF;
527132152Smlaier	tcom.mt_count = dumpnum - 1;
528130416Smlaier#ifdef RRESTORE
529130416Smlaier	if (host)
530130416Smlaier		rmtioctl(MTFSF, dumpnum - 1);
531130416Smlaier	else
532130416Smlaier#endif
533130416Smlaier		if (!pipecmdin && ioctl(mt, MTIOCTOP, (char *)&tcom) < 0)
534130416Smlaier			fprintf(stderr, "ioctl MTFSF: %s\n", strerror(errno));
535130416Smlaier}
536130416Smlaier
537132152Smlaiervoid
538132152Smlaierprintdumpinfo(void)
539132152Smlaier{
540130416Smlaier	time_t t;
541130416Smlaier	t = _time64_to_time(spcl.c_date);
542130416Smlaier	fprintf(stdout, "Dump   date: %s", ctime(&t));
543130416Smlaier	t = _time64_to_time(spcl.c_ddate);
544130416Smlaier	fprintf(stdout, "Dumped from: %s",
545130416Smlaier	    (spcl.c_ddate == 0) ? "the epoch\n" : ctime(&t));
546130416Smlaier	if (spcl.c_host[0] == '\0')
547186207Skmacy		return;
548186213Skmacy	fprintf(stderr, "Level %ld dump of %s on %s:%s\n",
549186213Skmacy		spcl.c_level, spcl.c_filesys, spcl.c_host, spcl.c_dev);
550186213Skmacy	fprintf(stderr, "Label: %s\n", spcl.c_label);
551186213Skmacy}
552186213Skmacy
553186213Skmacyint
554186213Skmacyextractfile(char *name)
555186213Skmacy{
556186213Skmacy	int flags;
557186207Skmacy	uid_t uid;
558186213Skmacy	gid_t gid;
559186207Skmacy	mode_t mode;
560186207Skmacy	struct timeval mtimep[2], ctimep[2];
561186213Skmacy	struct entry *ep;
562186213Skmacy
563186207Skmacy	curfile.name = name;
564191033Skmacy	curfile.action = USING;
565191033Skmacy	mtimep[0].tv_sec = curfile.atime_sec;
566191033Skmacy	mtimep[0].tv_usec = curfile.atime_nsec / 1000;
567191033Skmacy	mtimep[1].tv_sec = curfile.mtime_sec;
568191033Skmacy	mtimep[1].tv_usec = curfile.mtime_nsec / 1000;
569191033Skmacy	ctimep[0].tv_sec = curfile.atime_sec;
570186207Skmacy	ctimep[0].tv_usec = curfile.atime_nsec / 1000;
571186207Skmacy	ctimep[1].tv_sec = curfile.birthtime_sec;
572186213Skmacy	ctimep[1].tv_usec = curfile.birthtime_nsec / 1000;
573186207Skmacy	uid = curfile.uid;
574186213Skmacy	gid = curfile.gid;
575186213Skmacy	mode = curfile.mode;
576186213Skmacy	flags = curfile.file_flags;
577186207Skmacy	switch (mode & IFMT) {
578186207Skmacy
579186207Skmacy	default:
580186207Skmacy		fprintf(stderr, "%s: unknown file mode 0%o\n", name, mode);
581186207Skmacy		skipfile();
582186207Skmacy		return (FAIL);
583186207Skmacy
584186207Skmacy	case IFSOCK:
585186207Skmacy		vprintf(stdout, "skipped socket %s\n", name);
586186207Skmacy		skipfile();
587186207Skmacy		return (GOOD);
588186207Skmacy
589186207Skmacy	case IFDIR:
590191033Skmacy		if (mflag) {
591191033Skmacy			ep = lookupname(name);
592191033Skmacy			if (ep == NULL || ep->e_flags & EXTRACT)
593191033Skmacy				panic("unextracted directory %s\n", name);
594191033Skmacy			skipfile();
595191033Skmacy			return (GOOD);
596191033Skmacy		}
597191033Skmacy		vprintf(stdout, "extract file %s\n", name);
598191033Skmacy		return (genliteraldir(name, curfile.ino));
599191033Skmacy
600191033Skmacy	case IFLNK:
601186207Skmacy		lnkbuf[0] = '\0';
602191033Skmacy		pathlen = 0;
603191033Skmacy		getfile(xtrlnkfile, xtrlnkskip);
604186207Skmacy		if (pathlen == 0) {
605191033Skmacy			vprintf(stdout,
606191033Skmacy			    "%s: zero length symbolic link (ignored)\n", name);
607191033Skmacy			return (GOOD);
608191033Skmacy		}
609191033Skmacy		if (linkit(lnkbuf, name, SYMLINK) == GOOD) {
610191033Skmacy			(void) lchown(name, uid, gid);
611191033Skmacy			(void) lchmod(name, mode);
612191033Skmacy			(void) lutimes(name, ctimep);
613191033Skmacy			(void) lutimes(name, mtimep);
614191033Skmacy			(void) lchflags(name, flags);
61549459Sbrian			return (GOOD);
61649459Sbrian		}
61749459Sbrian		return (FAIL);
61849459Sbrian
61949459Sbrian	case IFIFO:
62049459Sbrian		vprintf(stdout, "extract fifo %s\n", name);
62149459Sbrian		if (Nflag) {
62255205Speter			skipfile();
62321259Swollman			return (GOOD);
62421259Swollman		}
62521259Swollman		if (uflag)
62621259Swollman			(void) unlink(name);
62721259Swollman		if (mkfifo(name, 0600) < 0) {
62821259Swollman			fprintf(stderr, "%s: cannot create fifo: %s\n",
629128291Sluigi			    name, strerror(errno));
630128291Sluigi			skipfile();
631128291Sluigi			return (FAIL);
632128291Sluigi		}
63321259Swollman		skipfile();
63421259Swollman		(void) chown(name, uid, gid);
63521259Swollman		(void) chmod(name, mode);
63621259Swollman		(void) utimes(name, ctimep);
63721259Swollman		(void) utimes(name, mtimep);
63821259Swollman		(void) chflags(name, flags);
63967334Sjoe		return (GOOD);
64021259Swollman
64160938Sjake	case IFCHR:
64221259Swollman	case IFBLK:
64392725Salfred		vprintf(stdout, "extract special file %s\n", name);
64421259Swollman		if (Nflag) {
64547254Spb			skipfile();
64621259Swollman			return (GOOD);
64728845Sjulian		}
64892725Salfred		if (uflag)
649108033Shsu			(void) unlink(name);
65021259Swollman		if (mknod(name, (mode & (IFCHR | IFBLK)) | 0600,
65121259Swollman		    (int)curfile.rdev) < 0) {
65221259Swollman			fprintf(stderr, "%s: cannot create special file: %s\n",
65353541Sshin			    name, strerror(errno));
65453541Sshin			skipfile();
65553541Sshin			return (FAIL);
656108033Shsu		}
657108033Shsu		skipfile();
658108033Shsu		(void) chown(name, uid, gid);
659108033Shsu		(void) chmod(name, mode);
660108033Shsu		(void) utimes(name, ctimep);
661108033Shsu		(void) utimes(name, mtimep);
66221404Swollman		(void) chflags(name, flags);
66352904Sshin		return (GOOD);
66452904Sshin
665108470Sschweikh	case IFREG:
66653541Sshin		vprintf(stdout, "extract file %s\n", name);
66752904Sshin		if (Nflag) {
66852904Sshin			skipfile();
66952904Sshin			return (GOOD);
67052904Sshin		}
67160938Sjake		if (uflag)
67252904Sshin			(void) unlink(name);
67352904Sshin		if ((ofile = open(name, O_WRONLY | O_CREAT | O_TRUNC,
67452904Sshin		    0600)) < 0) {
67552904Sshin			fprintf(stderr, "%s: cannot create file: %s\n",
67652904Sshin			    name, strerror(errno));
67721404Swollman			skipfile();
67821404Swollman			return (FAIL);
67921404Swollman		}
68021404Swollman		getfile(xtrfile, xtrskip);
68172084Sphk		(void) fchown(ofile, uid, gid);
68221434Swollman		(void) fchmod(ofile, mode);
68321434Swollman		(void) futimes(ofile, ctimep);
68421434Swollman		(void) futimes(ofile, mtimep);
68521434Swollman		(void) fchflags(ofile, flags);
68621434Swollman		(void) close(ofile);
687167729Sbms		return (GOOD);
68821404Swollman	}
68921404Swollman	/* NOTREACHED */
69055205Speter}
691108036Shsu
692108036Shsu/*
693108036Shsu * skip over bit maps on the tape
694108036Shsu */
695108036Shsuvoid
696108036Shsuskipmaps(void)
697108036Shsu{
698108036Shsu
699108036Shsu	while (spcl.c_type == TS_BITS || spcl.c_type == TS_CLRI)
700108036Shsu		skipfile();
70146568Speter}
70221259Swollman
703108036Shsu/*
704108036Shsu * skip over a file on the tape
705108036Shsu */
706108036Shsuvoid
707108036Shsuskipfile(void)
708108033Shsu{
709108033Shsu
710186199Skmacy	curfile.action = SKIP;
711108298Shsu	getfile(xtrnull, xtrnull);
712186199Skmacy}
713186199Skmacy
714186199Skmacy/*
715186199Skmacy * Extract a file from the tape.
716186199Skmacy * When an allocated block is found it is passed to the fill function;
717186199Skmacy * when an unallocated block (hole) is found, a zeroed buffer is passed
718108172Shsu * to the skip function.
71983130Sjlemon */
72087914Sjlemonvoid
721130585Sphkgetfile(void (*fill)(char *, long), void (*skip)(char *, long))
72283130Sjlemon{
72383130Sjlemon	int i;
724180042Srwatson	int curblk = 0;
725181892Skmacy	quad_t size = spcl.c_size;
726128315Sluigi	static char clearedbuf[MAXBSIZE];
727128315Sluigi	char buf[MAXBSIZE / TP_BSIZE][TP_BSIZE];
728128315Sluigi	char junk[TP_BSIZE];
729128315Sluigi
730128315Sluigi	if (spcl.c_type == TS_END)
731180042Srwatson		panic("ran off end of tape\n");
732180042Srwatson	if (spcl.c_magic != FS_UFS2_MAGIC)
73383130Sjlemon		panic("not at beginning of a file\n");
734186048Sbz	if (!gettingfile && setjmp(restart) != 0)
73521259Swollman		return;
73671791Speter	gettingfile++;
73721259Swollmanloop:
738186048Sbz	for (i = 0; i < spcl.c_count; i++) {
739186048Sbz		if (!readmapflag && i > TP_NINDIR) {
74021259Swollman			if (Dflag) {
741159781Smlaier				fprintf(stderr, "spcl.c_count = %jd\n",
742159781Smlaier				    (intmax_t)spcl.c_count);
74392725Salfred				break;
74492725Salfred			} else
745147256Sbrooks				panic("spcl.c_count = %jd\n",
74692725Salfred				    (intmax_t)spcl.c_count);
74792725Salfred		}
748167729Sbms		if (readmapflag || spcl.c_addr[i]) {
74992725Salfred			readtape(&buf[curblk++][0]);
750146620Speadar			if (curblk == fssize / TP_BSIZE) {
751177617Ssam				(*fill)((char *)buf, (long)(size > TP_BSIZE ?
75292725Salfred				     fssize : (curblk - 1) * TP_BSIZE + size));
753167732Sbms				curblk = 0;
754167732Sbms			}
755147256Sbrooks		} else {
756147256Sbrooks			if (curblk > 0) {
757121816Sbrooks				(*fill)((char *)buf, (long)(size > TP_BSIZE ?
758138542Ssam				     curblk * TP_BSIZE :
759103900Sbrooks				     (curblk - 1) * TP_BSIZE + size));
760191161Skmacy				curblk = 0;
76192725Salfred			}
76292725Salfred			(*skip)(clearedbuf, (long)(size > TP_BSIZE ?
76392725Salfred				TP_BSIZE : size));
76492725Salfred		}
76592725Salfred		if ((size -= TP_BSIZE) <= 0) {
76692725Salfred			for (i++; i < spcl.c_count; i++) {
76721259Swollman				if (!readmapflag && i > TP_NINDIR) {
768185162Skmacy					if (Dflag) {
769185162Skmacy						fprintf(stderr,
770185162Skmacy						    "spcl.c_count = %jd\n",
77192725Salfred						    (intmax_t)spcl.c_count);
772162068Sandre						break;
77392725Salfred					} else
77492725Salfred						panic("spcl.c_count = %jd\n",
77592725Salfred						    (intmax_t)spcl.c_count);
776178888Sjulian				}
777178888Sjulian				if (readmapflag || spcl.c_addr[i])
77892725Salfred					readtape(junk);
77921259Swollman			}
78092725Salfred			break;
78121434Swollman		}
782147256Sbrooks	}
783147256Sbrooks	if (gethead(&spcl) == GOOD && size > 0) {
784147256Sbrooks		if (spcl.c_type == TS_ADDR)
785147256Sbrooks			goto loop;
786147256Sbrooks		dprintf(stdout,
78784931Sfjoe			"Missing address (header) block for %s at %ld blocks\n",
788152315Sru			curfile.name, blksread);
78984931Sfjoe	}
79087902Sluigi	if (curblk > 0)
791150789Sglebius		(*fill)((char *)buf, (long)((curblk * TP_BSIZE) + size));
79287902Sluigi	findinode(&spcl);
79392725Salfred	gettingfile = 0;
79492725Salfred}
79592725Salfred
79687902Sluigi/*
79787902Sluigi * Write out the next block of a file.
79855205Speter */
79921259Swollmanstatic void
80021259Swollmanxtrfile(char *buf, long	size)
801{
802
803	if (Nflag)
804		return;
805	if (write(ofile, buf, (int) size) == -1) {
806		fprintf(stderr,
807		    "write error extracting inode %d, name %s\nwrite: %s\n",
808			curfile.ino, curfile.name, strerror(errno));
809	}
810}
811
812/*
813 * Skip over a hole in a file.
814 */
815/* ARGSUSED */
816static void
817xtrskip(char *buf, long size)
818{
819
820	if (lseek(ofile, size, SEEK_CUR) == -1) {
821		fprintf(stderr,
822		    "seek error extracting inode %d, name %s\nlseek: %s\n",
823			curfile.ino, curfile.name, strerror(errno));
824		done(1);
825	}
826}
827
828/*
829 * Collect the next block of a symbolic link.
830 */
831static void
832xtrlnkfile(char *buf, long size)
833{
834
835	pathlen += size;
836	if (pathlen > MAXPATHLEN) {
837		fprintf(stderr, "symbolic link name: %s->%s%s; too long %d\n",
838		    curfile.name, lnkbuf, buf, pathlen);
839		done(1);
840	}
841	(void) strcat(lnkbuf, buf);
842}
843
844/*
845 * Skip over a hole in a symbolic link (should never happen).
846 */
847/* ARGSUSED */
848static void
849xtrlnkskip(char *buf, long size)
850{
851
852	fprintf(stderr, "unallocated block in symbolic link %s\n",
853		curfile.name);
854	done(1);
855}
856
857/*
858 * Collect the next block of a bit map.
859 */
860static void
861xtrmap(char *buf, long size)
862{
863
864	memmove(map, buf, size);
865	map += size;
866}
867
868/*
869 * Skip over a hole in a bit map (should never happen).
870 */
871/* ARGSUSED */
872static void
873xtrmapskip(char *buf, long size)
874{
875
876	panic("hole in map\n");
877	map += size;
878}
879
880/*
881 * Noop, when an extraction function is not needed.
882 */
883/* ARGSUSED */
884void
885xtrnull(char *buf, long size)
886{
887
888	return;
889}
890
891/*
892 * Read TP_BSIZE blocks from the input.
893 * Handle read errors, and end of media.
894 */
895static void
896readtape(char *buf)
897{
898	long rd, newvol, i, oldnumtrec;
899	int cnt, seek_failed;
900
901	if (blkcnt + (byteslide > 0) < numtrec) {
902		memmove(buf, &tapebuf[(blkcnt++ * TP_BSIZE) + byteslide], (long)TP_BSIZE);
903		blksread++;
904		tapeaddr++;
905		return;
906	}
907	if (numtrec > 0)
908		memmove(&tapebuf[-TP_BSIZE],
909		    &tapebuf[(numtrec-1) * TP_BSIZE], (long)TP_BSIZE);
910	oldnumtrec = numtrec;
911	for (i = 0; i < ntrec; i++)
912		((struct s_spcl *)&tapebuf[i * TP_BSIZE])->c_magic = 0;
913	if (numtrec == 0)
914		numtrec = ntrec;
915	cnt = ntrec * TP_BSIZE;
916	rd = 0;
917getmore:
918#ifdef RRESTORE
919	if (host)
920		i = rmtread(&tapebuf[rd], cnt);
921	else
922#endif
923		i = read(mt, &tapebuf[rd], cnt);
924	/*
925	 * Check for mid-tape short read error.
926	 * If found, skip rest of buffer and start with the next.
927	 */
928	if (!pipein && numtrec < ntrec && i > 0) {
929		dprintf(stdout, "mid-media short read error.\n");
930		numtrec = ntrec;
931	}
932	/*
933	 * Handle partial block read.
934	 */
935	if (pipein && i == 0 && rd > 0)
936		i = rd;
937	else if (i > 0 && i != ntrec * TP_BSIZE) {
938		if (pipein) {
939			rd += i;
940			cnt -= i;
941			if (cnt > 0)
942				goto getmore;
943			i = rd;
944		} else {
945			/*
946			 * Short read. Process the blocks read.
947			 */
948			if (i % TP_BSIZE != 0)
949				vprintf(stdout,
950				    "partial block read: %ld should be %ld\n",
951				    i, ntrec * TP_BSIZE);
952			numtrec = i / TP_BSIZE;
953		}
954	}
955	/*
956	 * Handle read error.
957	 */
958	if (i < 0) {
959		fprintf(stderr, "Tape read error while ");
960		switch (curfile.action) {
961		default:
962			fprintf(stderr, "trying to set up tape\n");
963			break;
964		case UNKNOWN:
965			fprintf(stderr, "trying to resynchronize\n");
966			break;
967		case USING:
968			fprintf(stderr, "restoring %s\n", curfile.name);
969			break;
970		case SKIP:
971			fprintf(stderr, "skipping over inode %d\n",
972				curfile.ino);
973			break;
974		}
975		if (!yflag && !reply("continue"))
976			done(1);
977		i = ntrec * TP_BSIZE;
978		memset(tapebuf, 0, i);
979#ifdef RRESTORE
980		if (host)
981			seek_failed = (rmtseek(i, 1) < 0);
982		else
983#endif
984			seek_failed = (lseek(mt, i, SEEK_CUR) == (off_t)-1);
985
986		if (seek_failed) {
987			fprintf(stderr,
988			    "continuation failed: %s\n", strerror(errno));
989			done(1);
990		}
991	}
992	/*
993	 * Handle end of tape.
994	 */
995	if (i == 0) {
996		vprintf(stdout, "End-of-tape encountered\n");
997		if (!pipein) {
998			newvol = volno + 1;
999			volno = 0;
1000			numtrec = 0;
1001			getvol(newvol);
1002			readtape(buf);
1003			return;
1004		}
1005		if (rd % TP_BSIZE != 0)
1006			panic("partial block read: %d should be %d\n",
1007				rd, ntrec * TP_BSIZE);
1008		terminateinput();
1009		memmove(&tapebuf[rd], &endoftapemark, (long)TP_BSIZE);
1010	}
1011	if (oldnumtrec == 0)
1012		blkcnt = 0;
1013	else
1014		blkcnt -= oldnumtrec;
1015	memmove(buf,
1016	    &tapebuf[(blkcnt++ * TP_BSIZE) + byteslide], (long)TP_BSIZE);
1017	blksread++;
1018	tapeaddr++;
1019}
1020
1021static void
1022findtapeblksize(void)
1023{
1024	long i;
1025
1026	for (i = 0; i < ntrec; i++)
1027		((struct s_spcl *)&tapebuf[i * TP_BSIZE])->c_magic = 0;
1028	blkcnt = 0;
1029#ifdef RRESTORE
1030	if (host)
1031		i = rmtread(tapebuf, ntrec * TP_BSIZE);
1032	else
1033#endif
1034		i = read(mt, tapebuf, ntrec * TP_BSIZE);
1035
1036	if (i <= 0) {
1037		fprintf(stderr, "tape read error: %s\n", strerror(errno));
1038		done(1);
1039	}
1040	if (i % TP_BSIZE != 0) {
1041		fprintf(stderr, "Tape block size (%ld) %s (%d)\n",
1042			i, "is not a multiple of dump block size", TP_BSIZE);
1043		done(1);
1044	}
1045	ntrec = i / TP_BSIZE;
1046	numtrec = ntrec;
1047	vprintf(stdout, "Tape block size is %ld\n", ntrec);
1048}
1049
1050void
1051closemt(void)
1052{
1053
1054	if (mt < 0)
1055		return;
1056	if (pipecmdin) {
1057		pclose(popenfp);
1058		popenfp = NULL;
1059	} else
1060#ifdef RRESTORE
1061	if (host)
1062		rmtclose();
1063	else
1064#endif
1065		(void) close(mt);
1066}
1067
1068/*
1069 * Read the next block from the tape.
1070 * If it is not any valid header, return an error.
1071 */
1072static int
1073gethead(struct s_spcl *buf)
1074{
1075	long i;
1076
1077	readtape((char *)buf);
1078	if (buf->c_magic != FS_UFS2_MAGIC && buf->c_magic != NFS_MAGIC) {
1079		if (buf->c_magic == OFS_MAGIC) {
1080			fprintf(stderr,
1081			    "Format of dump tape is too old. Must use\n");
1082			fprintf(stderr,
1083			    "a version of restore from before 2002.\n");
1084			return (FAIL);
1085		}
1086		if (swabl(buf->c_magic) != FS_UFS2_MAGIC &&
1087		    buf->c_magic != NFS_MAGIC) {
1088			if (buf->c_magic == OFS_MAGIC) {
1089				fprintf(stderr,
1090				  "Format of dump tape is too old. Must use\n");
1091				fprintf(stderr,
1092				  "a version of restore from before 2002.\n");
1093			}
1094			return (FAIL);
1095		}
1096		if (!Bcvt) {
1097			vprintf(stdout, "Note: Doing Byte swapping\n");
1098			Bcvt = 1;
1099		}
1100	}
1101	if (checksum((int *)buf) == FAIL)
1102		return (FAIL);
1103	if (_time64_to_time(buf->c_date) != dumpdate)
1104		fprintf(stderr, "Header with wrong dumpdate.\n");
1105	if (Bcvt) {
1106		swabst((u_char *)"8l4s1q8l2q17l", (u_char *)buf);
1107		swabst((u_char *)"l",(u_char *) &buf->c_level);
1108		swabst((u_char *)"2l4q",(u_char *) &buf->c_flags);
1109	}
1110	readmapflag = 0;
1111
1112	switch (buf->c_type) {
1113
1114	case TS_CLRI:
1115	case TS_BITS:
1116		/*
1117		 * Have to patch up missing information in bit map headers
1118		 */
1119		buf->c_inumber = 0;
1120		buf->c_size = buf->c_count * TP_BSIZE;
1121		if (buf->c_count > TP_NINDIR)
1122			readmapflag = 1;
1123		else
1124			for (i = 0; i < buf->c_count; i++)
1125				buf->c_addr[i]++;
1126		break;
1127
1128	case TS_TAPE:
1129		if (buf->c_magic == NFS_MAGIC) {
1130			if ((buf->c_flags & NFS_DR_NEWINODEFMT) == 0)
1131				oldinofmt = 1;
1132			buf->c_date = _time32_to_time(buf->c_old_date);
1133			buf->c_ddate = _time32_to_time(buf->c_old_ddate);
1134			buf->c_tapea = buf->c_old_tapea;
1135			buf->c_firstrec = buf->c_old_firstrec;
1136		}
1137	case TS_END:
1138		buf->c_inumber = 0;
1139		break;
1140
1141	case TS_INODE:
1142		/*
1143		 * For old dump tapes, have to copy up old fields to
1144		 * new locations.
1145		 */
1146		if (buf->c_magic == NFS_MAGIC) {
1147			buf->c_tapea = buf->c_old_tapea;
1148			buf->c_firstrec = buf->c_old_firstrec;
1149			buf->c_date = _time32_to_time(buf->c_old_date);
1150			buf->c_ddate = _time32_to_time(buf->c_old_ddate);
1151			buf->c_atime = _time32_to_time(buf->c_old_atime);
1152			buf->c_mtime = _time32_to_time(buf->c_old_mtime);
1153		}
1154		break;
1155
1156	case TS_ADDR:
1157		break;
1158
1159	default:
1160		panic("gethead: unknown inode type %d\n", buf->c_type);
1161		break;
1162	}
1163	/*
1164	 * If we're restoring a filesystem with the old (FreeBSD 1)
1165	 * format inodes, copy the uid/gid to the new location
1166	 */
1167	if (oldinofmt) {
1168		buf->c_uid = buf->c_spare1[1];
1169		buf->c_gid = buf->c_spare1[2];
1170	}
1171	buf->c_magic = FS_UFS2_MAGIC;
1172	tapeaddr = buf->c_tapea;
1173	if (dflag)
1174		accthdr(buf);
1175	return(GOOD);
1176}
1177
1178/*
1179 * Check that a header is where it belongs and predict the next header
1180 */
1181static void
1182accthdr(struct s_spcl *header)
1183{
1184	static ino_t previno = 0x7fffffff;
1185	static int prevtype;
1186	static long predict;
1187	long blks, i;
1188
1189	if (header->c_type == TS_TAPE) {
1190		fprintf(stderr, "Volume header ");
1191 		if (header->c_firstrec)
1192 			fprintf(stderr, "begins with record %qd",
1193 				header->c_firstrec);
1194 		fprintf(stderr, "\n");
1195		previno = 0x7fffffff;
1196		return;
1197	}
1198	if (previno == 0x7fffffff)
1199		goto newcalc;
1200	switch (prevtype) {
1201	case TS_BITS:
1202		fprintf(stderr, "Dumped inodes map header");
1203		break;
1204	case TS_CLRI:
1205		fprintf(stderr, "Used inodes map header");
1206		break;
1207	case TS_INODE:
1208		fprintf(stderr, "File header, ino %d", previno);
1209		break;
1210	case TS_ADDR:
1211		fprintf(stderr, "File continuation header, ino %d", previno);
1212		break;
1213	case TS_END:
1214		fprintf(stderr, "End of tape header");
1215		break;
1216	}
1217	if (predict != blksread - 1)
1218		fprintf(stderr, "; predicted %ld blocks, got %ld blocks",
1219			predict, blksread - 1);
1220	fprintf(stderr, "\n");
1221newcalc:
1222	blks = 0;
1223	if (header->c_type != TS_END)
1224		for (i = 0; i < header->c_count; i++)
1225			if (readmapflag || header->c_addr[i] != 0)
1226				blks++;
1227	predict = blks;
1228	blksread = 0;
1229	prevtype = header->c_type;
1230	previno = header->c_inumber;
1231}
1232
1233/*
1234 * Find an inode header.
1235 * Complain if had to skip.
1236 */
1237static void
1238findinode(struct s_spcl *header)
1239{
1240	static long skipcnt = 0;
1241	long i;
1242	char buf[TP_BSIZE];
1243	int htype;
1244
1245	curfile.name = "<name unknown>";
1246	curfile.action = UNKNOWN;
1247	curfile.mode = 0;
1248	curfile.ino = 0;
1249	do {
1250		htype = header->c_type;
1251		switch (htype) {
1252
1253		case TS_ADDR:
1254			/*
1255			 * Skip up to the beginning of the next record
1256			 */
1257			for (i = 0; i < header->c_count; i++)
1258				if (header->c_addr[i])
1259					readtape(buf);
1260			while (gethead(header) == FAIL ||
1261			    _time64_to_time(header->c_date) != dumpdate) {
1262				skipcnt++;
1263				if (Dflag) {
1264					byteslide++;
1265					if (byteslide < TP_BSIZE) {
1266						blkcnt--;
1267						blksread--;
1268					} else
1269						byteslide = 0;
1270				}
1271			}
1272			break;
1273
1274		case TS_INODE:
1275			curfile.mode = header->c_mode;
1276			curfile.uid = header->c_uid;
1277			curfile.gid = header->c_gid;
1278			curfile.file_flags = header->c_file_flags;
1279			curfile.rdev = header->c_rdev;
1280			curfile.atime_sec = header->c_atime;
1281			curfile.atime_nsec = header->c_atimensec;
1282			curfile.mtime_sec = header->c_mtime;
1283			curfile.mtime_nsec = header->c_mtimensec;
1284			curfile.birthtime_sec = header->c_birthtime;
1285			curfile.birthtime_nsec = header->c_birthtimensec;
1286			curfile.size = header->c_size;
1287			curfile.ino = header->c_inumber;
1288			break;
1289
1290		case TS_END:
1291			/* If we missed some tapes, get another volume. */
1292			if (tapesread & (tapesread + 1)) {
1293				getvol(0);
1294				continue;
1295			}
1296			curfile.ino = maxino;
1297			break;
1298
1299		case TS_CLRI:
1300			curfile.name = "<file removal list>";
1301			break;
1302
1303		case TS_BITS:
1304			curfile.name = "<file dump list>";
1305			break;
1306
1307		case TS_TAPE:
1308			if (Dflag)
1309				fprintf(stderr, "unexpected tape header\n");
1310			else
1311				panic("unexpected tape header\n");
1312
1313		default:
1314			if (Dflag)
1315				fprintf(stderr, "unknown tape header type %d\n",
1316				    spcl.c_type);
1317			else
1318				panic("unknown tape header type %d\n",
1319				    spcl.c_type);
1320			while (gethead(header) == FAIL ||
1321			    _time64_to_time(header->c_date) != dumpdate) {
1322				skipcnt++;
1323				if (Dflag) {
1324					byteslide++;
1325					if (byteslide < TP_BSIZE) {
1326						blkcnt--;
1327						blksread--;
1328					} else
1329						byteslide = 0;
1330				}
1331			}
1332
1333		}
1334	} while (htype == TS_ADDR);
1335	if (skipcnt > 0)
1336		fprintf(stderr, "resync restore, skipped %ld %s\n",
1337		    skipcnt, Dflag ? "bytes" : "blocks");
1338	skipcnt = 0;
1339}
1340
1341static int
1342checksum(int *buf)
1343{
1344	int i, j;
1345
1346	j = sizeof(union u_spcl) / sizeof(int);
1347	i = 0;
1348	if (!Bcvt) {
1349		do
1350			i += *buf++;
1351		while (--j);
1352	} else {
1353		/* What happens if we want to read restore tapes
1354			for a 16bit int machine??? */
1355		do
1356			i += swabl(*buf++);
1357		while (--j);
1358	}
1359
1360	if (i != CHECKSUM) {
1361		fprintf(stderr, "Checksum error %o, inode %d file %s\n", i,
1362			curfile.ino, curfile.name);
1363		return(FAIL);
1364	}
1365	return(GOOD);
1366}
1367
1368#ifdef RRESTORE
1369#include <stdarg.h>
1370
1371void
1372msg(const char *fmt, ...)
1373{
1374	va_list ap;
1375	va_start(ap, fmt);
1376	(void)vfprintf(stderr, fmt, ap);
1377	va_end(ap);
1378}
1379#endif /* RRESTORE */
1380
1381static u_char *
1382swabshort(u_char *sp, int n)
1383{
1384	char c;
1385
1386	while (--n >= 0) {
1387		c = sp[0]; sp[0] = sp[1]; sp[1] = c;
1388		sp += 2;
1389	}
1390	return (sp);
1391}
1392
1393static u_char *
1394swablong(u_char *sp, int n)
1395{
1396	char c;
1397
1398	while (--n >= 0) {
1399		c = sp[0]; sp[0] = sp[3]; sp[3] = c;
1400		c = sp[2]; sp[2] = sp[1]; sp[1] = c;
1401		sp += 4;
1402	}
1403	return (sp);
1404}
1405
1406static u_char *
1407swabquad(u_char *sp, int n)
1408{
1409	char c;
1410
1411	while (--n >= 0) {
1412		c = sp[0]; sp[0] = sp[7]; sp[7] = c;
1413		c = sp[1]; sp[1] = sp[6]; sp[6] = c;
1414		c = sp[2]; sp[2] = sp[5]; sp[5] = c;
1415		c = sp[3]; sp[3] = sp[4]; sp[4] = c;
1416		sp += 8;
1417	}
1418	return (sp);
1419}
1420
1421void
1422swabst(u_char *cp, u_char *sp)
1423{
1424	int n = 0;
1425
1426	while (*cp) {
1427		switch (*cp) {
1428		case '0': case '1': case '2': case '3': case '4':
1429		case '5': case '6': case '7': case '8': case '9':
1430			n = (n * 10) + (*cp++ - '0');
1431			continue;
1432
1433		case 's': case 'w': case 'h':
1434			if (n == 0)
1435				n = 1;
1436			sp = swabshort(sp, n);
1437			break;
1438
1439		case 'l':
1440			if (n == 0)
1441				n = 1;
1442			sp = swablong(sp, n);
1443			break;
1444
1445		case 'q':
1446			if (n == 0)
1447				n = 1;
1448			sp = swabquad(sp, n);
1449			break;
1450
1451		case 'b':
1452			if (n == 0)
1453				n = 1;
1454			sp += n;
1455			break;
1456
1457		default:
1458			fprintf(stderr, "Unknown conversion character: %c\n",
1459			    *cp);
1460			done(0);
1461			break;
1462		}
1463		cp++;
1464		n = 0;
1465	}
1466}
1467
1468static u_long
1469swabl(u_long x)
1470{
1471	swabst((u_char *)"l", (u_char *)&x);
1472	return (x);
1473}
1474