tape.c revision 128175
11558Srgrimes/*
21558Srgrimes * Copyright (c) 1983, 1993
31558Srgrimes *	The Regents of the University of California.  All rights reserved.
41558Srgrimes * (c) UNIX System Laboratories, Inc.
51558Srgrimes * All or some portions of this file are derived from material licensed
61558Srgrimes * to the University of California by American Telephone and Telegraph
71558Srgrimes * Co. or Unix System Laboratories, Inc. and are reproduced herein with
81558Srgrimes * the permission of UNIX System Laboratories, Inc.
91558Srgrimes *
101558Srgrimes * Redistribution and use in source and binary forms, with or without
111558Srgrimes * modification, are permitted provided that the following conditions
121558Srgrimes * are met:
131558Srgrimes * 1. Redistributions of source code must retain the above copyright
141558Srgrimes *    notice, this list of conditions and the following disclaimer.
151558Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
161558Srgrimes *    notice, this list of conditions and the following disclaimer in the
171558Srgrimes *    documentation and/or other materials provided with the distribution.
181558Srgrimes * 4. Neither the name of the University nor the names of its contributors
191558Srgrimes *    may be used to endorse or promote products derived from this software
201558Srgrimes *    without specific prior written permission.
211558Srgrimes *
221558Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
231558Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
241558Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
251558Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
261558Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
271558Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
281558Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
291558Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
301558Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
311558Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
321558Srgrimes * SUCH DAMAGE.
331558Srgrimes */
341558Srgrimes
351558Srgrimes#ifndef lint
3637906Scharnier#if 0
3723685Speterstatic char sccsid[] = "@(#)tape.c	8.9 (Berkeley) 5/1/95";
3837906Scharnier#endif
3937906Scharnierstatic const char rcsid[] =
4050476Speter  "$FreeBSD: head/sbin/restore/tape.c 128175 2004-04-13 02:58:06Z green $";
411558Srgrimes#endif /* not lint */
421558Srgrimes
431558Srgrimes#include <sys/param.h>
441558Srgrimes#include <sys/file.h>
451558Srgrimes#include <sys/mtio.h>
461558Srgrimes#include <sys/stat.h>
4766907Swollman#include <sys/time.h>
481558Srgrimes
491558Srgrimes#include <ufs/ufs/dinode.h>
501558Srgrimes#include <protocols/dumprestore.h>
511558Srgrimes
521558Srgrimes#include <errno.h>
53103949Smike#include <limits.h>
5473986Sobrien#include <paths.h>
551558Srgrimes#include <setjmp.h>
561558Srgrimes#include <stdio.h>
571558Srgrimes#include <stdlib.h>
581558Srgrimes#include <string.h>
5966907Swollman#include <time.h>
601558Srgrimes#include <unistd.h>
611558Srgrimes
621558Srgrimes#include "restore.h"
631558Srgrimes#include "extern.h"
641558Srgrimes
651558Srgrimesstatic long	fssize = MAXBSIZE;
661558Srgrimesstatic int	mt = -1;
671558Srgrimesstatic int	pipein = 0;
68128175Sgreenstatic int	pipecmdin = 0;
69128175Sgreenstatic FILE	*popenfp = NULL;
7021174Sguidostatic char	*magtape;
711558Srgrimesstatic int	blkcnt;
721558Srgrimesstatic int	numtrec;
731558Srgrimesstatic char	*tapebuf;
741558Srgrimesstatic union	u_spcl endoftapemark;
751558Srgrimesstatic long	blksread;		/* blocks read since last header */
7698542Smckusickstatic int64_t	tapeaddr = 0;		/* current TP_BSIZE tape record */
771558Srgrimesstatic long	tapesread;
781558Srgrimesstatic jmp_buf	restart;
791558Srgrimesstatic int	gettingfile = 0;	/* restart has a valid frame */
801558Srgrimesstatic char	*host = NULL;
8198542Smckusickstatic int	readmapflag;
821558Srgrimes
831558Srgrimesstatic int	ofile;
841558Srgrimesstatic char	*map;
851558Srgrimesstatic char	lnkbuf[MAXPATHLEN + 1];
861558Srgrimesstatic int	pathlen;
871558Srgrimes
8898542Smckusickint		Bcvt;		/* Swap Bytes */
891558Srgrimes
901558Srgrimes#define	FLUSHTAPEBUF()	blkcnt = ntrec + 1
911558Srgrimes
9292837Simpstatic void	 accthdr(struct s_spcl *);
9392837Simpstatic int	 checksum(int *);
9492837Simpstatic void	 findinode(struct s_spcl *);
9592837Simpstatic void	 findtapeblksize(void);
9692837Simpstatic int	 gethead(struct s_spcl *);
9792837Simpstatic void	 readtape(char *);
9892837Simpstatic void	 setdumpnum(void);
9992837Simpstatic u_long	 swabl(u_long);
10092837Simpstatic u_char	*swablong(u_char *, int);
10192837Simpstatic u_char	*swabshort(u_char *, int);
10292837Simpstatic void	 terminateinput(void);
10392837Simpstatic void	 xtrfile(char *, long);
10492837Simpstatic void	 xtrlnkfile(char *, long);
10592837Simpstatic void	 xtrlnkskip(char *, long);
10692837Simpstatic void	 xtrmap(char *, long);
10792837Simpstatic void	 xtrmapskip(char *, long);
10892837Simpstatic void	 xtrskip(char *, long);
1091558Srgrimes
1101558Srgrimes/*
1111558Srgrimes * Set up an input source
1121558Srgrimes */
1131558Srgrimesvoid
114128175Sgreensetinput(char *source, int ispipecommand)
1151558Srgrimes{
1161558Srgrimes	FLUSHTAPEBUF();
1171558Srgrimes	if (bflag)
1181558Srgrimes		newtapebuf(ntrec);
1191558Srgrimes	else
1201558Srgrimes		newtapebuf(NTREC > HIGHDENSITYTREC ? NTREC : HIGHDENSITYTREC);
1211558Srgrimes	terminal = stdin;
1221558Srgrimes
123128175Sgreen	if (ispipecommand)
124128175Sgreen		pipecmdin++;
125128175Sgreen	else
1261558Srgrimes#ifdef RRESTORE
12723685Speter	if (strchr(source, ':')) {
1281558Srgrimes		host = source;
12923685Speter		source = strchr(host, ':');
1301558Srgrimes		*source++ = '\0';
1311558Srgrimes		if (rmthost(host) == 0)
1321558Srgrimes			done(1);
1331558Srgrimes	} else
1341558Srgrimes#endif
1351558Srgrimes	if (strcmp(source, "-") == 0) {
1361558Srgrimes		/*
1371558Srgrimes		 * Since input is coming from a pipe we must establish
1381558Srgrimes		 * our own connection to the terminal.
1391558Srgrimes		 */
1401558Srgrimes		terminal = fopen(_PATH_TTY, "r");
1411558Srgrimes		if (terminal == NULL) {
1421558Srgrimes			(void)fprintf(stderr, "cannot open %s: %s\n",
1431558Srgrimes			    _PATH_TTY, strerror(errno));
1441558Srgrimes			terminal = fopen(_PATH_DEVNULL, "r");
1451558Srgrimes			if (terminal == NULL) {
1461558Srgrimes				(void)fprintf(stderr, "cannot open %s: %s\n",
1471558Srgrimes				    _PATH_DEVNULL, strerror(errno));
1481558Srgrimes				done(1);
1491558Srgrimes			}
1501558Srgrimes		}
1511558Srgrimes		pipein++;
1521558Srgrimes	}
1531558Srgrimes	setuid(getuid());	/* no longer need or want root privileges */
15421174Sguido	magtape = strdup(source);
15521174Sguido	if (magtape == NULL) {
15621174Sguido		fprintf(stderr, "Cannot allocate space for magtape buffer\n");
15721174Sguido		done(1);
15821174Sguido	}
1591558Srgrimes}
1601558Srgrimes
1611558Srgrimesvoid
16292837Simpnewtapebuf(long size)
1631558Srgrimes{
16492837Simp	static int tapebufsize = -1;
1651558Srgrimes
1661558Srgrimes	ntrec = size;
1671558Srgrimes	if (size <= tapebufsize)
1681558Srgrimes		return;
1691558Srgrimes	if (tapebuf != NULL)
1701558Srgrimes		free(tapebuf);
1711558Srgrimes	tapebuf = malloc(size * TP_BSIZE);
1721558Srgrimes	if (tapebuf == NULL) {
1731558Srgrimes		fprintf(stderr, "Cannot allocate space for tape buffer\n");
1741558Srgrimes		done(1);
1751558Srgrimes	}
1761558Srgrimes	tapebufsize = size;
1771558Srgrimes}
1781558Srgrimes
1791558Srgrimes/*
1801558Srgrimes * Verify that the tape drive can be accessed and
1811558Srgrimes * that it actually is a dump tape.
1821558Srgrimes */
1831558Srgrimesvoid
18492837Simpsetup(void)
1851558Srgrimes{
1861558Srgrimes	int i, j, *ip;
1871558Srgrimes	struct stat stbuf;
1881558Srgrimes
1891558Srgrimes	vprintf(stdout, "Verify tape and initialize maps\n");
190128175Sgreen	if (pipecmdin) {
191128175Sgreen		if (setenv("RESTORE_VOLUME", "1", 1) == -1) {
192128175Sgreen			fprintf(stderr, "Cannot set $RESTORE_VOLUME: %s\n",
193128175Sgreen			    strerror(errno));
194128175Sgreen			done(1);
195128175Sgreen		}
196128175Sgreen		popenfp = popen(magtape, "r");
197128175Sgreen		mt = popenfp ? fileno(popenfp) : -1;
198128175Sgreen	} else
1991558Srgrimes#ifdef RRESTORE
2001558Srgrimes	if (host)
2011558Srgrimes		mt = rmtopen(magtape, 0);
2021558Srgrimes	else
2031558Srgrimes#endif
2041558Srgrimes	if (pipein)
2051558Srgrimes		mt = 0;
2061558Srgrimes	else
2071558Srgrimes		mt = open(magtape, O_RDONLY, 0);
2081558Srgrimes	if (mt < 0) {
2091558Srgrimes		fprintf(stderr, "%s: %s\n", magtape, strerror(errno));
2101558Srgrimes		done(1);
2111558Srgrimes	}
2121558Srgrimes	volno = 1;
2131558Srgrimes	setdumpnum();
2141558Srgrimes	FLUSHTAPEBUF();
2151558Srgrimes	if (!pipein && !bflag)
2161558Srgrimes		findtapeblksize();
2171558Srgrimes	if (gethead(&spcl) == FAIL) {
21898542Smckusick		fprintf(stderr, "Tape is not a dump tape\n");
21998542Smckusick		done(1);
2201558Srgrimes	}
2211558Srgrimes	if (pipein) {
22298542Smckusick		endoftapemark.s_spcl.c_magic = FS_UFS2_MAGIC;
2231558Srgrimes		endoftapemark.s_spcl.c_type = TS_END;
2241558Srgrimes		ip = (int *)&endoftapemark;
2251558Srgrimes		j = sizeof(union u_spcl) / sizeof(int);
2261558Srgrimes		i = 0;
2271558Srgrimes		do
2281558Srgrimes			i += *ip++;
2291558Srgrimes		while (--j);
2301558Srgrimes		endoftapemark.s_spcl.c_checksum = CHECKSUM - i;
2311558Srgrimes	}
2321558Srgrimes	if (vflag || command == 't')
2331558Srgrimes		printdumpinfo();
23498542Smckusick	dumptime = _time64_to_time(spcl.c_ddate);
23598542Smckusick	dumpdate = _time64_to_time(spcl.c_date);
2361558Srgrimes	if (stat(".", &stbuf) < 0) {
2371558Srgrimes		fprintf(stderr, "cannot stat .: %s\n", strerror(errno));
2381558Srgrimes		done(1);
2391558Srgrimes	}
24034851Sjkh	if (stbuf.st_blksize > 0 && stbuf.st_blksize < TP_BSIZE )
24134851Sjkh		fssize = TP_BSIZE;
24234851Sjkh	if (stbuf.st_blksize >= TP_BSIZE && stbuf.st_blksize <= MAXBSIZE)
2431558Srgrimes		fssize = stbuf.st_blksize;
2441558Srgrimes	if (((fssize - 1) & fssize) != 0) {
24537240Sbde		fprintf(stderr, "bad block size %ld\n", fssize);
2461558Srgrimes		done(1);
2471558Srgrimes	}
2481558Srgrimes	if (spcl.c_volume != 1) {
2491558Srgrimes		fprintf(stderr, "Tape is not volume 1 of the dump\n");
2501558Srgrimes		done(1);
2511558Srgrimes	}
2521558Srgrimes	if (gethead(&spcl) == FAIL) {
25337240Sbde		dprintf(stdout, "header read failed at %ld blocks\n", blksread);
2541558Srgrimes		panic("no header after volume mark!\n");
2551558Srgrimes	}
2561558Srgrimes	findinode(&spcl);
2571558Srgrimes	if (spcl.c_type != TS_CLRI) {
2581558Srgrimes		fprintf(stderr, "Cannot find file removal list\n");
2591558Srgrimes		done(1);
2601558Srgrimes	}
2611558Srgrimes	maxino = (spcl.c_count * TP_BSIZE * NBBY) + 1;
2621558Srgrimes	dprintf(stdout, "maxino = %d\n", maxino);
2631558Srgrimes	map = calloc((unsigned)1, (unsigned)howmany(maxino, NBBY));
2641558Srgrimes	if (map == NULL)
26523685Speter		panic("no memory for active inode map\n");
26623685Speter	usedinomap = map;
2671558Srgrimes	curfile.action = USING;
2681558Srgrimes	getfile(xtrmap, xtrmapskip);
2691558Srgrimes	if (spcl.c_type != TS_BITS) {
2701558Srgrimes		fprintf(stderr, "Cannot find file dump list\n");
2711558Srgrimes		done(1);
2721558Srgrimes	}
2731558Srgrimes	map = calloc((unsigned)1, (unsigned)howmany(maxino, NBBY));
2741558Srgrimes	if (map == (char *)NULL)
2751558Srgrimes		panic("no memory for file dump list\n");
2761558Srgrimes	dumpmap = map;
2771558Srgrimes	curfile.action = USING;
2781558Srgrimes	getfile(xtrmap, xtrmapskip);
27923685Speter	/*
28023685Speter	 * If there may be whiteout entries on the tape, pretend that the
28123685Speter	 * whiteout inode exists, so that the whiteout entries can be
28223685Speter	 * extracted.
28323685Speter	 */
28498542Smckusick	SETINO(WINO, dumpmap);
28590820Siedowse	/* 'r' restores don't call getvol() for tape 1, so mark it as read. */
28690820Siedowse	if (command == 'r')
28790820Siedowse		tapesread = 1;
2881558Srgrimes}
2891558Srgrimes
2901558Srgrimes/*
2911558Srgrimes * Prompt user to load a new dump volume.
2921558Srgrimes * "Nextvol" is the next suggested volume to use.
2931558Srgrimes * This suggested volume is enforced when doing full
29437906Scharnier * or incremental restores, but can be overridden by
2951558Srgrimes * the user when only extracting a subset of the files.
2961558Srgrimes */
2971558Srgrimesvoid
29892837Simpgetvol(long nextvol)
2991558Srgrimes{
30098542Smckusick	int64_t prevtapea;
30198542Smckusick	long i, newvol, savecnt;
3021558Srgrimes	union u_spcl tmpspcl;
3031558Srgrimes#	define tmpbuf tmpspcl.s_spcl
3041558Srgrimes	char buf[TP_BSIZE];
3051558Srgrimes
3061558Srgrimes	if (nextvol == 1) {
3071558Srgrimes		tapesread = 0;
3081558Srgrimes		gettingfile = 0;
3091558Srgrimes	}
31090827Siedowse	prevtapea = tapeaddr;
31190827Siedowse	savecnt = blksread;
3121558Srgrimes	if (pipein) {
31369906Siedowse		if (nextvol != 1) {
3141558Srgrimes			panic("Changing volumes on pipe input?\n");
31569906Siedowse			/* Avoid looping if we couldn't ask the user. */
31669906Siedowse			if (yflag || ferror(terminal) || feof(terminal))
31769906Siedowse				done(1);
31869906Siedowse		}
3191558Srgrimes		if (volno == 1)
3201558Srgrimes			return;
321128175Sgreen		if (pipecmdin) {
322128175Sgreen			closemt();
323128175Sgreen			goto getpipecmdhdr;
324128175Sgreen		}
3251558Srgrimes		goto gethdr;
3261558Srgrimes	}
3271558Srgrimesagain:
3281558Srgrimes	if (pipein)
3291558Srgrimes		done(1); /* pipes do not get a second chance */
33090608Siedowse	if (command == 'R' || command == 'r' || curfile.action != SKIP)
3311558Srgrimes		newvol = nextvol;
33290608Siedowse	else
3331558Srgrimes		newvol = 0;
3341558Srgrimes	while (newvol <= 0) {
3351558Srgrimes		if (tapesread == 0) {
33690820Siedowse			fprintf(stderr, "%s%s%s%s%s%s%s",
3371558Srgrimes			    "You have not read any tapes yet.\n",
33890820Siedowse			    "If you are extracting just a few files,",
33990820Siedowse			    " start with the last volume\n",
34090820Siedowse			    "and work towards the first; restore",
34190820Siedowse			    " can quickly skip tapes that\n",
34290820Siedowse			    "have no further files to extract.",
34390820Siedowse			    " Otherwise, begin with volume 1.\n");
3441558Srgrimes		} else {
3451558Srgrimes			fprintf(stderr, "You have read volumes");
3461558Srgrimes			strcpy(buf, ": ");
34790820Siedowse			for (i = 0; i < 32; i++)
3481558Srgrimes				if (tapesread & (1 << i)) {
34990820Siedowse					fprintf(stderr, "%s%ld", buf, i + 1);
3501558Srgrimes					strcpy(buf, ", ");
3511558Srgrimes				}
3521558Srgrimes			fprintf(stderr, "\n");
3531558Srgrimes		}
3541558Srgrimes		do	{
3551558Srgrimes			fprintf(stderr, "Specify next volume #: ");
3561558Srgrimes			(void) fflush(stderr);
35769906Siedowse			if (fgets(buf, BUFSIZ, terminal) == NULL)
35869906Siedowse				done(1);
35969906Siedowse		} while (buf[0] == '\n');
3601558Srgrimes		newvol = atoi(buf);
3611558Srgrimes		if (newvol <= 0) {
3621558Srgrimes			fprintf(stderr,
3631558Srgrimes			    "Volume numbers are positive numerics\n");
3641558Srgrimes		}
3651558Srgrimes	}
3661558Srgrimes	if (newvol == volno) {
36790820Siedowse		tapesread |= 1 << (volno - 1);
3681558Srgrimes		return;
3691558Srgrimes	}
3701558Srgrimes	closemt();
37137240Sbde	fprintf(stderr, "Mount tape volume %ld\n", newvol);
3721558Srgrimes	fprintf(stderr, "Enter ``none'' if there are no more tapes\n");
3731558Srgrimes	fprintf(stderr, "otherwise enter tape name (default: %s) ", magtape);
3741558Srgrimes	(void) fflush(stderr);
37569906Siedowse	if (fgets(buf, BUFSIZ, terminal) == NULL)
3761558Srgrimes		done(1);
3771558Srgrimes	if (!strcmp(buf, "none\n")) {
3781558Srgrimes		terminateinput();
3791558Srgrimes		return;
3801558Srgrimes	}
3811558Srgrimes	if (buf[0] != '\n') {
3821558Srgrimes		(void) strcpy(magtape, buf);
3831558Srgrimes		magtape[strlen(magtape) - 1] = '\0';
3841558Srgrimes	}
385128175Sgreen	if (pipecmdin) {
386128175Sgreen		char volno[sizeof("2147483647")];
387128175Sgreen
388128175Sgreengetpipecmdhdr:
389128175Sgreen		(void)sprintf(volno, "%d", newvol);
390128175Sgreen		if (setenv("RESTORE_VOLUME", volno, 1) == -1) {
391128175Sgreen			fprintf(stderr, "Cannot set $RESTORE_VOLUME: %s\n",
392128175Sgreen			    strerror(errno));
393128175Sgreen			done(1);
394128175Sgreen		}
395128175Sgreen		popenfp = popen(magtape, "r");
396128175Sgreen		mt = popenfp ? fileno(popenfp) : -1;
397128175Sgreen	} else
3981558Srgrimes#ifdef RRESTORE
3991558Srgrimes	if (host)
4001558Srgrimes		mt = rmtopen(magtape, 0);
4011558Srgrimes	else
4021558Srgrimes#endif
4031558Srgrimes		mt = open(magtape, O_RDONLY, 0);
4041558Srgrimes
4051558Srgrimes	if (mt == -1) {
4061558Srgrimes		fprintf(stderr, "Cannot open %s\n", magtape);
4071558Srgrimes		volno = -1;
4081558Srgrimes		goto again;
4091558Srgrimes	}
4101558Srgrimesgethdr:
4111558Srgrimes	volno = newvol;
4121558Srgrimes	setdumpnum();
4131558Srgrimes	FLUSHTAPEBUF();
4141558Srgrimes	if (gethead(&tmpbuf) == FAIL) {
41537240Sbde		dprintf(stdout, "header read failed at %ld blocks\n", blksread);
4161558Srgrimes		fprintf(stderr, "tape is not dump tape\n");
4171558Srgrimes		volno = 0;
4181558Srgrimes		goto again;
4191558Srgrimes	}
4201558Srgrimes	if (tmpbuf.c_volume != volno) {
42137240Sbde		fprintf(stderr, "Wrong volume (%ld)\n", tmpbuf.c_volume);
4221558Srgrimes		volno = 0;
4231558Srgrimes		goto again;
4241558Srgrimes	}
42598542Smckusick	if (_time64_to_time(tmpbuf.c_date) != dumpdate ||
42698542Smckusick	    _time64_to_time(tmpbuf.c_ddate) != dumptime) {
42798542Smckusick		time_t t = _time64_to_time(tmpbuf.c_date);
42885635Sdillon		fprintf(stderr, "Wrong dump date\n\tgot: %s", ctime(&t));
4291558Srgrimes		fprintf(stderr, "\twanted: %s", ctime(&dumpdate));
4301558Srgrimes		volno = 0;
4311558Srgrimes		goto again;
4321558Srgrimes	}
43390820Siedowse	tapesread |= 1 << (volno - 1);
4341558Srgrimes	blksread = savecnt;
4351558Srgrimes 	/*
4361558Srgrimes 	 * If continuing from the previous volume, skip over any
4371558Srgrimes 	 * blocks read already at the end of the previous volume.
4381558Srgrimes 	 *
4391558Srgrimes 	 * If coming to this volume at random, skip to the beginning
4401558Srgrimes 	 * of the next record.
4411558Srgrimes 	 */
44298542Smckusick	dprintf(stdout, "last rec %qd, tape starts with %qd\n", prevtapea,
44390827Siedowse	    tmpbuf.c_tapea);
44498542Smckusick 	if (tmpbuf.c_type == TS_TAPE) {
44590608Siedowse 		if (curfile.action != USING) {
44690608Siedowse			/*
44790608Siedowse			 * XXX Dump incorrectly sets c_count to 1 in the
44890608Siedowse			 * volume header of the first tape, so ignore
44990608Siedowse			 * c_count when volno == 1.
45090608Siedowse			 */
45190608Siedowse			if (volno != 1)
45290608Siedowse				for (i = tmpbuf.c_count; i > 0; i--)
45390608Siedowse					readtape(buf);
45490827Siedowse 		} else if (tmpbuf.c_tapea <= prevtapea) {
4551558Srgrimes			/*
45690827Siedowse			 * Normally the value of c_tapea in the volume
45790827Siedowse			 * header is the record number of the header itself.
45890827Siedowse			 * However in the volume header following an EOT-
45990827Siedowse			 * terminated tape, it is the record number of the
46090827Siedowse			 * first continuation data block (dump bug?).
46190827Siedowse			 *
46290827Siedowse			 * The next record we want is `prevtapea + 1'.
4631558Srgrimes			 */
46490827Siedowse 			i = prevtapea + 1 - tmpbuf.c_tapea;
46537240Sbde			dprintf(stderr, "Skipping %ld duplicate record%s.\n",
4661558Srgrimes				i, i > 1 ? "s" : "");
4671558Srgrimes 			while (--i >= 0)
4681558Srgrimes 				readtape(buf);
4691558Srgrimes 		}
4701558Srgrimes 	}
47190608Siedowse	if (curfile.action == USING) {
4721558Srgrimes		if (volno == 1)
4731558Srgrimes			panic("active file into volume 1\n");
4741558Srgrimes		return;
4751558Srgrimes	}
4761558Srgrimes	(void) gethead(&spcl);
4771558Srgrimes	findinode(&spcl);
4781558Srgrimes	if (gettingfile) {
4791558Srgrimes		gettingfile = 0;
4801558Srgrimes		longjmp(restart, 1);
4811558Srgrimes	}
4821558Srgrimes}
4831558Srgrimes
4841558Srgrimes/*
4851558Srgrimes * Handle unexpected EOF.
4861558Srgrimes */
4871558Srgrimesstatic void
48892837Simpterminateinput(void)
4891558Srgrimes{
4901558Srgrimes
4911558Srgrimes	if (gettingfile && curfile.action == USING) {
4921558Srgrimes		printf("Warning: %s %s\n",
4931558Srgrimes		    "End-of-input encountered while extracting", curfile.name);
4941558Srgrimes	}
4951558Srgrimes	curfile.name = "<name unknown>";
4961558Srgrimes	curfile.action = UNKNOWN;
49798542Smckusick	curfile.mode = 0;
4981558Srgrimes	curfile.ino = maxino;
4991558Srgrimes	if (gettingfile) {
5001558Srgrimes		gettingfile = 0;
5011558Srgrimes		longjmp(restart, 1);
5021558Srgrimes	}
5031558Srgrimes}
5041558Srgrimes
5051558Srgrimes/*
5061558Srgrimes * handle multiple dumps per tape by skipping forward to the
5071558Srgrimes * appropriate one.
5081558Srgrimes */
5091558Srgrimesstatic void
51092837Simpsetdumpnum(void)
5111558Srgrimes{
5121558Srgrimes	struct mtop tcom;
5131558Srgrimes
5141558Srgrimes	if (dumpnum == 1 || volno != 1)
5151558Srgrimes		return;
5161558Srgrimes	if (pipein) {
5171558Srgrimes		fprintf(stderr, "Cannot have multiple dumps on pipe input\n");
5181558Srgrimes		done(1);
5191558Srgrimes	}
5201558Srgrimes	tcom.mt_op = MTFSF;
5211558Srgrimes	tcom.mt_count = dumpnum - 1;
5221558Srgrimes#ifdef RRESTORE
5231558Srgrimes	if (host)
5241558Srgrimes		rmtioctl(MTFSF, dumpnum - 1);
5258871Srgrimes	else
5261558Srgrimes#endif
527128175Sgreen		if (!pipecmdin && ioctl(mt, MTIOCTOP, (char *)&tcom) < 0)
5281558Srgrimes			fprintf(stderr, "ioctl MTFSF: %s\n", strerror(errno));
5291558Srgrimes}
5301558Srgrimes
5311558Srgrimesvoid
53292837Simpprintdumpinfo(void)
5331558Srgrimes{
53485635Sdillon	time_t t;
53598542Smckusick	t = _time64_to_time(spcl.c_date);
53685635Sdillon	fprintf(stdout, "Dump   date: %s", ctime(&t));
53798542Smckusick	t = _time64_to_time(spcl.c_ddate);
5381558Srgrimes	fprintf(stdout, "Dumped from: %s",
53985635Sdillon	    (spcl.c_ddate == 0) ? "the epoch\n" : ctime(&t));
5401558Srgrimes	if (spcl.c_host[0] == '\0')
5411558Srgrimes		return;
54237240Sbde	fprintf(stderr, "Level %ld dump of %s on %s:%s\n",
5431558Srgrimes		spcl.c_level, spcl.c_filesys, spcl.c_host, spcl.c_dev);
5441558Srgrimes	fprintf(stderr, "Label: %s\n", spcl.c_label);
5451558Srgrimes}
5461558Srgrimes
5471558Srgrimesint
54892837Simpextractfile(char *name)
5491558Srgrimes{
55023685Speter	int flags;
55123685Speter	mode_t mode;
552100207Smckusick	struct timeval mtimep[2], ctimep[2];
5531558Srgrimes	struct entry *ep;
5541558Srgrimes
5551558Srgrimes	curfile.name = name;
5561558Srgrimes	curfile.action = USING;
557100207Smckusick	mtimep[0].tv_sec = curfile.atime_sec;
558100207Smckusick	mtimep[0].tv_usec = curfile.atime_nsec / 1000;
559100207Smckusick	mtimep[1].tv_sec = curfile.mtime_sec;
560100207Smckusick	mtimep[1].tv_usec = curfile.mtime_nsec / 1000;
561100207Smckusick	ctimep[0].tv_sec = curfile.atime_sec;
562100207Smckusick	ctimep[0].tv_usec = curfile.atime_nsec / 1000;
563100207Smckusick	ctimep[1].tv_sec = curfile.birthtime_sec;
564100207Smckusick	ctimep[1].tv_usec = curfile.birthtime_nsec / 1000;
56598542Smckusick	mode = curfile.mode;
56698542Smckusick	flags = curfile.file_flags;
5671558Srgrimes	switch (mode & IFMT) {
5681558Srgrimes
5691558Srgrimes	default:
5701558Srgrimes		fprintf(stderr, "%s: unknown file mode 0%o\n", name, mode);
5711558Srgrimes		skipfile();
5721558Srgrimes		return (FAIL);
5731558Srgrimes
5741558Srgrimes	case IFSOCK:
5751558Srgrimes		vprintf(stdout, "skipped socket %s\n", name);
5761558Srgrimes		skipfile();
5771558Srgrimes		return (GOOD);
5781558Srgrimes
5791558Srgrimes	case IFDIR:
5801558Srgrimes		if (mflag) {
5811558Srgrimes			ep = lookupname(name);
5821558Srgrimes			if (ep == NULL || ep->e_flags & EXTRACT)
5831558Srgrimes				panic("unextracted directory %s\n", name);
5841558Srgrimes			skipfile();
5851558Srgrimes			return (GOOD);
5861558Srgrimes		}
5871558Srgrimes		vprintf(stdout, "extract file %s\n", name);
5881558Srgrimes		return (genliteraldir(name, curfile.ino));
5891558Srgrimes
5901558Srgrimes	case IFLNK:
5911558Srgrimes		lnkbuf[0] = '\0';
5921558Srgrimes		pathlen = 0;
5931558Srgrimes		getfile(xtrlnkfile, xtrlnkskip);
5941558Srgrimes		if (pathlen == 0) {
5951558Srgrimes			vprintf(stdout,
5961558Srgrimes			    "%s: zero length symbolic link (ignored)\n", name);
5971558Srgrimes			return (GOOD);
5981558Srgrimes		}
59996113Siedowse		if (linkit(lnkbuf, name, SYMLINK) == GOOD) {
60098542Smckusick			(void) lchown(name, curfile.uid, curfile.gid);
60196113Siedowse			(void) lchmod(name, mode);
602100207Smckusick			(void) lutimes(name, ctimep);
603100207Smckusick			(void) lutimes(name, mtimep);
60496113Siedowse			return (GOOD);
60595943Siedowse		}
60696113Siedowse		return (FAIL);
6071558Srgrimes
6086305Smartin	case IFIFO:
60923685Speter		vprintf(stdout, "extract fifo %s\n", name);
61023685Speter		if (Nflag) {
61123685Speter			skipfile();
61223685Speter			return (GOOD);
61323685Speter		}
61435852Sjkh		if (uflag && !Nflag)
61535852Sjkh			(void)unlink(name);
6166305Smartin		if (mkfifo(name, mode) < 0) {
61723685Speter			fprintf(stderr, "%s: cannot create fifo: %s\n",
61823685Speter			    name, strerror(errno));
6196305Smartin			skipfile();
6206305Smartin			return (FAIL);
6216305Smartin		}
62298542Smckusick		(void) chown(name, curfile.uid, curfile.gid);
6236305Smartin		(void) chmod(name, mode);
624100207Smckusick		(void) utimes(name, ctimep);
625100207Smckusick		(void) utimes(name, mtimep);
62623685Speter		(void) chflags(name, flags);
6276305Smartin		skipfile();
6286305Smartin		return (GOOD);
6296305Smartin
6301558Srgrimes	case IFCHR:
6311558Srgrimes	case IFBLK:
6321558Srgrimes		vprintf(stdout, "extract special file %s\n", name);
6331558Srgrimes		if (Nflag) {
6341558Srgrimes			skipfile();
6351558Srgrimes			return (GOOD);
6361558Srgrimes		}
63735852Sjkh		if (uflag)
63835852Sjkh			(void)unlink(name);
63998542Smckusick		if (mknod(name, mode, (int)curfile.rdev) < 0) {
6401558Srgrimes			fprintf(stderr, "%s: cannot create special file: %s\n",
6411558Srgrimes			    name, strerror(errno));
6421558Srgrimes			skipfile();
6431558Srgrimes			return (FAIL);
6441558Srgrimes		}
64598542Smckusick		(void) chown(name, curfile.uid, curfile.gid);
6461558Srgrimes		(void) chmod(name, mode);
647100207Smckusick		(void) utimes(name, ctimep);
648100207Smckusick		(void) utimes(name, mtimep);
64923685Speter		(void) chflags(name, flags);
6501558Srgrimes		skipfile();
6511558Srgrimes		return (GOOD);
6521558Srgrimes
6531558Srgrimes	case IFREG:
6541558Srgrimes		vprintf(stdout, "extract file %s\n", name);
6551558Srgrimes		if (Nflag) {
6561558Srgrimes			skipfile();
6571558Srgrimes			return (GOOD);
6581558Srgrimes		}
65935852Sjkh		if (uflag)
66035852Sjkh			(void)unlink(name);
66121149Simp		if ((ofile = open(name, O_WRONLY | O_CREAT | O_TRUNC,
66221149Simp		    0666)) < 0) {
6631558Srgrimes			fprintf(stderr, "%s: cannot create file: %s\n",
6641558Srgrimes			    name, strerror(errno));
6651558Srgrimes			skipfile();
6661558Srgrimes			return (FAIL);
6671558Srgrimes		}
66898542Smckusick		(void) fchown(ofile, curfile.uid, curfile.gid);
6691558Srgrimes		(void) fchmod(ofile, mode);
6701558Srgrimes		getfile(xtrfile, xtrskip);
6711558Srgrimes		(void) close(ofile);
672100207Smckusick		(void) utimes(name, ctimep);
673100207Smckusick		(void) utimes(name, mtimep);
67463283Sdwmalone		(void) chflags(name, flags);
6751558Srgrimes		return (GOOD);
6761558Srgrimes	}
6771558Srgrimes	/* NOTREACHED */
6781558Srgrimes}
6791558Srgrimes
6801558Srgrimes/*
6811558Srgrimes * skip over bit maps on the tape
6821558Srgrimes */
6831558Srgrimesvoid
68492837Simpskipmaps(void)
6851558Srgrimes{
6861558Srgrimes
6871558Srgrimes	while (spcl.c_type == TS_BITS || spcl.c_type == TS_CLRI)
6881558Srgrimes		skipfile();
6891558Srgrimes}
6901558Srgrimes
6911558Srgrimes/*
6921558Srgrimes * skip over a file on the tape
6931558Srgrimes */
6941558Srgrimesvoid
69592837Simpskipfile(void)
6961558Srgrimes{
6971558Srgrimes
6981558Srgrimes	curfile.action = SKIP;
6991558Srgrimes	getfile(xtrnull, xtrnull);
7001558Srgrimes}
7011558Srgrimes
7021558Srgrimes/*
7031558Srgrimes * Extract a file from the tape.
7041558Srgrimes * When an allocated block is found it is passed to the fill function;
7051558Srgrimes * when an unallocated block (hole) is found, a zeroed buffer is passed
7061558Srgrimes * to the skip function.
7071558Srgrimes */
7081558Srgrimesvoid
70992837Simpgetfile(void (*fill)(char *, long), void (*skip)(char *, long))
7101558Srgrimes{
71192806Sobrien	int i;
7121558Srgrimes	int curblk = 0;
71398542Smckusick	quad_t size = spcl.c_size;
7141558Srgrimes	static char clearedbuf[MAXBSIZE];
7151558Srgrimes	char buf[MAXBSIZE / TP_BSIZE][TP_BSIZE];
7161558Srgrimes	char junk[TP_BSIZE];
7171558Srgrimes
7181558Srgrimes	if (spcl.c_type == TS_END)
7191558Srgrimes		panic("ran off end of tape\n");
72098542Smckusick	if (spcl.c_magic != FS_UFS2_MAGIC)
7211558Srgrimes		panic("not at beginning of a file\n");
7221558Srgrimes	if (!gettingfile && setjmp(restart) != 0)
7231558Srgrimes		return;
7241558Srgrimes	gettingfile++;
7251558Srgrimesloop:
7261558Srgrimes	for (i = 0; i < spcl.c_count; i++) {
72737923Simp		if (readmapflag || spcl.c_addr[i]) {
7281558Srgrimes			readtape(&buf[curblk++][0]);
7291558Srgrimes			if (curblk == fssize / TP_BSIZE) {
73023685Speter				(*fill)((char *)buf, (long)(size > TP_BSIZE ?
73123685Speter				     fssize : (curblk - 1) * TP_BSIZE + size));
7321558Srgrimes				curblk = 0;
7331558Srgrimes			}
7341558Srgrimes		} else {
7351558Srgrimes			if (curblk > 0) {
73623685Speter				(*fill)((char *)buf, (long)(size > TP_BSIZE ?
73723685Speter				     curblk * TP_BSIZE :
73823685Speter				     (curblk - 1) * TP_BSIZE + size));
7391558Srgrimes				curblk = 0;
7401558Srgrimes			}
74123685Speter			(*skip)(clearedbuf, (long)(size > TP_BSIZE ?
74223685Speter				TP_BSIZE : size));
7431558Srgrimes		}
7441558Srgrimes		if ((size -= TP_BSIZE) <= 0) {
7451558Srgrimes			for (i++; i < spcl.c_count; i++)
74637923Simp				if (readmapflag || spcl.c_addr[i])
7471558Srgrimes					readtape(junk);
7481558Srgrimes			break;
7491558Srgrimes		}
7501558Srgrimes	}
7511558Srgrimes	if (gethead(&spcl) == GOOD && size > 0) {
7521558Srgrimes		if (spcl.c_type == TS_ADDR)
7531558Srgrimes			goto loop;
7541558Srgrimes		dprintf(stdout,
75537240Sbde			"Missing address (header) block for %s at %ld blocks\n",
7561558Srgrimes			curfile.name, blksread);
7571558Srgrimes	}
7581558Srgrimes	if (curblk > 0)
75923685Speter		(*fill)((char *)buf, (long)((curblk * TP_BSIZE) + size));
7601558Srgrimes	findinode(&spcl);
7611558Srgrimes	gettingfile = 0;
7621558Srgrimes}
7631558Srgrimes
7641558Srgrimes/*
7651558Srgrimes * Write out the next block of a file.
7661558Srgrimes */
7671558Srgrimesstatic void
76892837Simpxtrfile(char *buf, long	size)
7691558Srgrimes{
7701558Srgrimes
7711558Srgrimes	if (Nflag)
7721558Srgrimes		return;
7731558Srgrimes	if (write(ofile, buf, (int) size) == -1) {
7741558Srgrimes		fprintf(stderr,
7751558Srgrimes		    "write error extracting inode %d, name %s\nwrite: %s\n",
7761558Srgrimes			curfile.ino, curfile.name, strerror(errno));
7771558Srgrimes	}
7781558Srgrimes}
7791558Srgrimes
7801558Srgrimes/*
7811558Srgrimes * Skip over a hole in a file.
7821558Srgrimes */
7831558Srgrimes/* ARGSUSED */
7841558Srgrimesstatic void
78592837Simpxtrskip(char *buf, long size)
7861558Srgrimes{
7871558Srgrimes
7881558Srgrimes	if (lseek(ofile, size, SEEK_CUR) == -1) {
7891558Srgrimes		fprintf(stderr,
7901558Srgrimes		    "seek error extracting inode %d, name %s\nlseek: %s\n",
7911558Srgrimes			curfile.ino, curfile.name, strerror(errno));
7921558Srgrimes		done(1);
7931558Srgrimes	}
7941558Srgrimes}
7951558Srgrimes
7961558Srgrimes/*
7971558Srgrimes * Collect the next block of a symbolic link.
7981558Srgrimes */
7991558Srgrimesstatic void
80092837Simpxtrlnkfile(char *buf, long size)
8011558Srgrimes{
8021558Srgrimes
8031558Srgrimes	pathlen += size;
8041558Srgrimes	if (pathlen > MAXPATHLEN) {
8051558Srgrimes		fprintf(stderr, "symbolic link name: %s->%s%s; too long %d\n",
8061558Srgrimes		    curfile.name, lnkbuf, buf, pathlen);
8071558Srgrimes		done(1);
8081558Srgrimes	}
8091558Srgrimes	(void) strcat(lnkbuf, buf);
8101558Srgrimes}
8111558Srgrimes
8121558Srgrimes/*
8131558Srgrimes * Skip over a hole in a symbolic link (should never happen).
8141558Srgrimes */
8151558Srgrimes/* ARGSUSED */
8161558Srgrimesstatic void
81792837Simpxtrlnkskip(char *buf, long size)
8181558Srgrimes{
8191558Srgrimes
8201558Srgrimes	fprintf(stderr, "unallocated block in symbolic link %s\n",
8211558Srgrimes		curfile.name);
8221558Srgrimes	done(1);
8231558Srgrimes}
8241558Srgrimes
8251558Srgrimes/*
8261558Srgrimes * Collect the next block of a bit map.
8271558Srgrimes */
8281558Srgrimesstatic void
82992837Simpxtrmap(char *buf, long size)
8301558Srgrimes{
8311558Srgrimes
83223685Speter	memmove(map, buf, size);
8331558Srgrimes	map += size;
8341558Srgrimes}
8351558Srgrimes
8361558Srgrimes/*
8371558Srgrimes * Skip over a hole in a bit map (should never happen).
8381558Srgrimes */
8391558Srgrimes/* ARGSUSED */
8401558Srgrimesstatic void
84192837Simpxtrmapskip(char *buf, long size)
8421558Srgrimes{
8431558Srgrimes
8441558Srgrimes	panic("hole in map\n");
8451558Srgrimes	map += size;
8461558Srgrimes}
8471558Srgrimes
8481558Srgrimes/*
8491558Srgrimes * Noop, when an extraction function is not needed.
8501558Srgrimes */
8511558Srgrimes/* ARGSUSED */
8521558Srgrimesvoid
85392837Simpxtrnull(char *buf, long size)
8541558Srgrimes{
8551558Srgrimes
8561558Srgrimes	return;
8571558Srgrimes}
8581558Srgrimes
8591558Srgrimes/*
8601558Srgrimes * Read TP_BSIZE blocks from the input.
8611558Srgrimes * Handle read errors, and end of media.
8621558Srgrimes */
8631558Srgrimesstatic void
86492837Simpreadtape(char *buf)
8651558Srgrimes{
8661558Srgrimes	long rd, newvol, i;
8671558Srgrimes	int cnt, seek_failed;
8681558Srgrimes
8691558Srgrimes	if (blkcnt < numtrec) {
87023685Speter		memmove(buf, &tapebuf[(blkcnt++ * TP_BSIZE)], (long)TP_BSIZE);
8711558Srgrimes		blksread++;
87290827Siedowse		tapeaddr++;
8731558Srgrimes		return;
8741558Srgrimes	}
8751558Srgrimes	for (i = 0; i < ntrec; i++)
8761558Srgrimes		((struct s_spcl *)&tapebuf[i * TP_BSIZE])->c_magic = 0;
8771558Srgrimes	if (numtrec == 0)
8781558Srgrimes		numtrec = ntrec;
8791558Srgrimes	cnt = ntrec * TP_BSIZE;
8801558Srgrimes	rd = 0;
8811558Srgrimesgetmore:
8821558Srgrimes#ifdef RRESTORE
8831558Srgrimes	if (host)
8841558Srgrimes		i = rmtread(&tapebuf[rd], cnt);
8851558Srgrimes	else
8861558Srgrimes#endif
8871558Srgrimes		i = read(mt, &tapebuf[rd], cnt);
8881558Srgrimes	/*
8891558Srgrimes	 * Check for mid-tape short read error.
8901558Srgrimes	 * If found, skip rest of buffer and start with the next.
8911558Srgrimes	 */
8921558Srgrimes	if (!pipein && numtrec < ntrec && i > 0) {
8931558Srgrimes		dprintf(stdout, "mid-media short read error.\n");
8941558Srgrimes		numtrec = ntrec;
8951558Srgrimes	}
8961558Srgrimes	/*
8971558Srgrimes	 * Handle partial block read.
8981558Srgrimes	 */
8991558Srgrimes	if (pipein && i == 0 && rd > 0)
9001558Srgrimes		i = rd;
9011558Srgrimes	else if (i > 0 && i != ntrec * TP_BSIZE) {
9021558Srgrimes		if (pipein) {
9031558Srgrimes			rd += i;
9041558Srgrimes			cnt -= i;
9051558Srgrimes			if (cnt > 0)
9061558Srgrimes				goto getmore;
9071558Srgrimes			i = rd;
9081558Srgrimes		} else {
9091558Srgrimes			/*
9101558Srgrimes			 * Short read. Process the blocks read.
9111558Srgrimes			 */
9121558Srgrimes			if (i % TP_BSIZE != 0)
9131558Srgrimes				vprintf(stdout,
91437240Sbde				    "partial block read: %ld should be %ld\n",
9151558Srgrimes				    i, ntrec * TP_BSIZE);
9161558Srgrimes			numtrec = i / TP_BSIZE;
9171558Srgrimes		}
9181558Srgrimes	}
9191558Srgrimes	/*
9201558Srgrimes	 * Handle read error.
9211558Srgrimes	 */
9221558Srgrimes	if (i < 0) {
9231558Srgrimes		fprintf(stderr, "Tape read error while ");
9241558Srgrimes		switch (curfile.action) {
9251558Srgrimes		default:
9261558Srgrimes			fprintf(stderr, "trying to set up tape\n");
9271558Srgrimes			break;
9281558Srgrimes		case UNKNOWN:
9291558Srgrimes			fprintf(stderr, "trying to resynchronize\n");
9301558Srgrimes			break;
9311558Srgrimes		case USING:
9321558Srgrimes			fprintf(stderr, "restoring %s\n", curfile.name);
9331558Srgrimes			break;
9341558Srgrimes		case SKIP:
9351558Srgrimes			fprintf(stderr, "skipping over inode %d\n",
9361558Srgrimes				curfile.ino);
9371558Srgrimes			break;
9381558Srgrimes		}
9391558Srgrimes		if (!yflag && !reply("continue"))
9401558Srgrimes			done(1);
9411558Srgrimes		i = ntrec * TP_BSIZE;
94223685Speter		memset(tapebuf, 0, i);
9431558Srgrimes#ifdef RRESTORE
9441558Srgrimes		if (host)
9451558Srgrimes			seek_failed = (rmtseek(i, 1) < 0);
9461558Srgrimes		else
9471558Srgrimes#endif
9481558Srgrimes			seek_failed = (lseek(mt, i, SEEK_CUR) == (off_t)-1);
9491558Srgrimes
9501558Srgrimes		if (seek_failed) {
9511558Srgrimes			fprintf(stderr,
9521558Srgrimes			    "continuation failed: %s\n", strerror(errno));
9531558Srgrimes			done(1);
9541558Srgrimes		}
9551558Srgrimes	}
9561558Srgrimes	/*
9571558Srgrimes	 * Handle end of tape.
9581558Srgrimes	 */
9591558Srgrimes	if (i == 0) {
9601558Srgrimes		vprintf(stdout, "End-of-tape encountered\n");
9611558Srgrimes		if (!pipein) {
9621558Srgrimes			newvol = volno + 1;
9631558Srgrimes			volno = 0;
9641558Srgrimes			numtrec = 0;
9651558Srgrimes			getvol(newvol);
9661558Srgrimes			readtape(buf);
9671558Srgrimes			return;
9681558Srgrimes		}
9691558Srgrimes		if (rd % TP_BSIZE != 0)
9701558Srgrimes			panic("partial block read: %d should be %d\n",
9711558Srgrimes				rd, ntrec * TP_BSIZE);
9721558Srgrimes		terminateinput();
97323685Speter		memmove(&tapebuf[rd], &endoftapemark, (long)TP_BSIZE);
9741558Srgrimes	}
9751558Srgrimes	blkcnt = 0;
97623685Speter	memmove(buf, &tapebuf[(blkcnt++ * TP_BSIZE)], (long)TP_BSIZE);
9771558Srgrimes	blksread++;
97890827Siedowse	tapeaddr++;
9791558Srgrimes}
9801558Srgrimes
9811558Srgrimesstatic void
98292837Simpfindtapeblksize(void)
9831558Srgrimes{
98492806Sobrien	long i;
9851558Srgrimes
9861558Srgrimes	for (i = 0; i < ntrec; i++)
9871558Srgrimes		((struct s_spcl *)&tapebuf[i * TP_BSIZE])->c_magic = 0;
9881558Srgrimes	blkcnt = 0;
9891558Srgrimes#ifdef RRESTORE
9901558Srgrimes	if (host)
9911558Srgrimes		i = rmtread(tapebuf, ntrec * TP_BSIZE);
9921558Srgrimes	else
9931558Srgrimes#endif
9941558Srgrimes		i = read(mt, tapebuf, ntrec * TP_BSIZE);
9951558Srgrimes
9961558Srgrimes	if (i <= 0) {
9971558Srgrimes		fprintf(stderr, "tape read error: %s\n", strerror(errno));
9981558Srgrimes		done(1);
9991558Srgrimes	}
10001558Srgrimes	if (i % TP_BSIZE != 0) {
100137240Sbde		fprintf(stderr, "Tape block size (%ld) %s (%d)\n",
10021558Srgrimes			i, "is not a multiple of dump block size", TP_BSIZE);
10031558Srgrimes		done(1);
10041558Srgrimes	}
10051558Srgrimes	ntrec = i / TP_BSIZE;
10061558Srgrimes	numtrec = ntrec;
100737240Sbde	vprintf(stdout, "Tape block size is %ld\n", ntrec);
10081558Srgrimes}
10091558Srgrimes
10101558Srgrimesvoid
101192837Simpclosemt(void)
10121558Srgrimes{
10131558Srgrimes
10141558Srgrimes	if (mt < 0)
10151558Srgrimes		return;
1016128175Sgreen	if (pipecmdin) {
1017128175Sgreen		pclose(popenfp);
1018128175Sgreen		popenfp = NULL;
1019128175Sgreen	} else
10201558Srgrimes#ifdef RRESTORE
10211558Srgrimes	if (host)
10221558Srgrimes		rmtclose();
10231558Srgrimes	else
10241558Srgrimes#endif
10251558Srgrimes		(void) close(mt);
10261558Srgrimes}
10271558Srgrimes
10281558Srgrimes/*
10291558Srgrimes * Read the next block from the tape.
10301558Srgrimes * If it is not any valid header, return an error.
10311558Srgrimes */
10321558Srgrimesstatic int
103392837Simpgethead(struct s_spcl *buf)
10341558Srgrimes{
10351558Srgrimes	long i;
10361558Srgrimes
103798542Smckusick	readtape((char *)buf);
103898542Smckusick	if (buf->c_magic != FS_UFS2_MAGIC && buf->c_magic != NFS_MAGIC) {
103998542Smckusick		if (buf->c_magic == OFS_MAGIC) {
104098542Smckusick			fprintf(stderr,
104198542Smckusick			    "Format of dump tape is too old. Must use\n");
104298542Smckusick			fprintf(stderr,
104398542Smckusick			    "a version of restore from before 2002.\n");
104498542Smckusick			return (FAIL);
104598542Smckusick		}
104698542Smckusick		if (swabl(buf->c_magic) != FS_UFS2_MAGIC &&
104798542Smckusick		    buf->c_magic != NFS_MAGIC) {
104898542Smckusick			if (buf->c_magic == OFS_MAGIC) {
104998542Smckusick				fprintf(stderr,
105098542Smckusick				  "Format of dump tape is too old. Must use\n");
105198542Smckusick				fprintf(stderr,
105298542Smckusick				  "a version of restore from before 2002.\n");
10531558Srgrimes			}
10541558Srgrimes			return (FAIL);
105523096Simp		}
105698542Smckusick		if (!Bcvt) {
105798542Smckusick			vprintf(stdout, "Note: Doing Byte swapping\n");
105898542Smckusick			Bcvt = 1;
10591558Srgrimes		}
10601558Srgrimes	}
106198542Smckusick	if (checksum((int *)buf) == FAIL)
106298542Smckusick		return (FAIL);
106398542Smckusick	if (Bcvt) {
106498542Smckusick		swabst((u_char *)"8l4s1q8l2q17l", (u_char *)buf);
106598542Smckusick		swabst((u_char *)"l",(u_char *) &buf->c_level);
106698542Smckusick		swabst((u_char *)"2l4q",(u_char *) &buf->c_flags);
10671558Srgrimes	}
106837923Simp	readmapflag = 0;
10691558Srgrimes
10701558Srgrimes	switch (buf->c_type) {
10711558Srgrimes
10721558Srgrimes	case TS_CLRI:
10731558Srgrimes	case TS_BITS:
10741558Srgrimes		/*
10751558Srgrimes		 * Have to patch up missing information in bit map headers
10761558Srgrimes		 */
10771558Srgrimes		buf->c_inumber = 0;
107898542Smckusick		buf->c_size = buf->c_count * TP_BSIZE;
107937923Simp		if (buf->c_count > TP_NINDIR)
108037923Simp			readmapflag = 1;
108137923Simp		else
108237923Simp			for (i = 0; i < buf->c_count; i++)
108337923Simp				buf->c_addr[i]++;
10841558Srgrimes		break;
10851558Srgrimes
10861558Srgrimes	case TS_TAPE:
10871558Srgrimes	case TS_END:
10881558Srgrimes		buf->c_inumber = 0;
10891558Srgrimes		break;
10901558Srgrimes
10911558Srgrimes	case TS_INODE:
109298542Smckusick		/*
109398542Smckusick		 * For old dump tapes, have to copy up old fields to
109498542Smckusick		 * new locations.
109598542Smckusick		 */
109698542Smckusick		if (buf->c_magic == NFS_MAGIC) {
109798542Smckusick			buf->c_tapea = buf->c_old_tapea;
109898542Smckusick			buf->c_firstrec = buf->c_old_firstrec;
109998542Smckusick			buf->c_date = _time32_to_time(buf->c_old_date);
110098542Smckusick			buf->c_ddate = _time32_to_time(buf->c_old_ddate);
110198542Smckusick			buf->c_atime = _time32_to_time(buf->c_old_atime);
110298542Smckusick			buf->c_mtime = _time32_to_time(buf->c_old_mtime);
110398542Smckusick		}
110498542Smckusick		break;
110598542Smckusick
11061558Srgrimes	case TS_ADDR:
11071558Srgrimes		break;
11081558Srgrimes
11091558Srgrimes	default:
11101558Srgrimes		panic("gethead: unknown inode type %d\n", buf->c_type);
11111558Srgrimes		break;
11121558Srgrimes	}
111398542Smckusick	buf->c_magic = FS_UFS2_MAGIC;
111490827Siedowse	tapeaddr = buf->c_tapea;
11151558Srgrimes	if (dflag)
11161558Srgrimes		accthdr(buf);
11171558Srgrimes	return(GOOD);
11181558Srgrimes}
11191558Srgrimes
11201558Srgrimes/*
11211558Srgrimes * Check that a header is where it belongs and predict the next header
11221558Srgrimes */
11231558Srgrimesstatic void
112492837Simpaccthdr(struct s_spcl *header)
11251558Srgrimes{
11261558Srgrimes	static ino_t previno = 0x7fffffff;
11271558Srgrimes	static int prevtype;
11281558Srgrimes	static long predict;
11291558Srgrimes	long blks, i;
11301558Srgrimes
11311558Srgrimes	if (header->c_type == TS_TAPE) {
113298542Smckusick		fprintf(stderr, "Volume header ");
11331558Srgrimes 		if (header->c_firstrec)
113498542Smckusick 			fprintf(stderr, "begins with record %qd",
11351558Srgrimes 				header->c_firstrec);
11361558Srgrimes 		fprintf(stderr, "\n");
11371558Srgrimes		previno = 0x7fffffff;
11381558Srgrimes		return;
11391558Srgrimes	}
11401558Srgrimes	if (previno == 0x7fffffff)
11411558Srgrimes		goto newcalc;
11421558Srgrimes	switch (prevtype) {
11431558Srgrimes	case TS_BITS:
114423685Speter		fprintf(stderr, "Dumped inodes map header");
11451558Srgrimes		break;
11461558Srgrimes	case TS_CLRI:
114723685Speter		fprintf(stderr, "Used inodes map header");
11481558Srgrimes		break;
11491558Srgrimes	case TS_INODE:
11501558Srgrimes		fprintf(stderr, "File header, ino %d", previno);
11511558Srgrimes		break;
11521558Srgrimes	case TS_ADDR:
11531558Srgrimes		fprintf(stderr, "File continuation header, ino %d", previno);
11541558Srgrimes		break;
11551558Srgrimes	case TS_END:
11561558Srgrimes		fprintf(stderr, "End of tape header");
11571558Srgrimes		break;
11581558Srgrimes	}
11591558Srgrimes	if (predict != blksread - 1)
116037240Sbde		fprintf(stderr, "; predicted %ld blocks, got %ld blocks",
11611558Srgrimes			predict, blksread - 1);
11621558Srgrimes	fprintf(stderr, "\n");
11631558Srgrimesnewcalc:
11641558Srgrimes	blks = 0;
11651558Srgrimes	if (header->c_type != TS_END)
11661558Srgrimes		for (i = 0; i < header->c_count; i++)
116737923Simp			if (readmapflag || header->c_addr[i] != 0)
11681558Srgrimes				blks++;
11691558Srgrimes	predict = blks;
11701558Srgrimes	blksread = 0;
11711558Srgrimes	prevtype = header->c_type;
11721558Srgrimes	previno = header->c_inumber;
11731558Srgrimes}
11741558Srgrimes
11751558Srgrimes/*
11761558Srgrimes * Find an inode header.
117790573Siedowse * Complain if had to skip.
11781558Srgrimes */
11791558Srgrimesstatic void
118092837Simpfindinode(struct s_spcl *header)
11811558Srgrimes{
11821558Srgrimes	static long skipcnt = 0;
11831558Srgrimes	long i;
11841558Srgrimes	char buf[TP_BSIZE];
118590608Siedowse	int htype;
11861558Srgrimes
11871558Srgrimes	curfile.name = "<name unknown>";
11881558Srgrimes	curfile.action = UNKNOWN;
118998542Smckusick	curfile.mode = 0;
11901558Srgrimes	curfile.ino = 0;
11911558Srgrimes	do {
119290608Siedowse		htype = header->c_type;
119390608Siedowse		switch (htype) {
11941558Srgrimes
11951558Srgrimes		case TS_ADDR:
11961558Srgrimes			/*
11971558Srgrimes			 * Skip up to the beginning of the next record
11981558Srgrimes			 */
11991558Srgrimes			for (i = 0; i < header->c_count; i++)
12001558Srgrimes				if (header->c_addr[i])
12011558Srgrimes					readtape(buf);
12021558Srgrimes			while (gethead(header) == FAIL ||
120398542Smckusick			    _time64_to_time(header->c_date) != dumpdate)
12041558Srgrimes				skipcnt++;
12051558Srgrimes			break;
12061558Srgrimes
12071558Srgrimes		case TS_INODE:
120898542Smckusick			curfile.mode = header->c_mode;
120998542Smckusick			curfile.uid = header->c_uid;
121098542Smckusick			curfile.gid = header->c_gid;
121198542Smckusick			curfile.file_flags = header->c_file_flags;
121298542Smckusick			curfile.rdev = header->c_rdev;
121398542Smckusick			curfile.atime_sec = header->c_atime;
121498542Smckusick			curfile.atime_nsec = header->c_atimensec;
121598542Smckusick			curfile.mtime_sec = header->c_mtime;
121698542Smckusick			curfile.mtime_nsec = header->c_mtimensec;
1217100207Smckusick			curfile.birthtime_sec = header->c_birthtime;
1218100207Smckusick			curfile.birthtime_nsec = header->c_birthtimensec;
121998542Smckusick			curfile.size = header->c_size;
12201558Srgrimes			curfile.ino = header->c_inumber;
12211558Srgrimes			break;
12221558Srgrimes
12231558Srgrimes		case TS_END:
122490820Siedowse			/* If we missed some tapes, get another volume. */
122590820Siedowse			if (tapesread & (tapesread + 1)) {
122690820Siedowse				getvol(0);
122790820Siedowse				continue;
122890820Siedowse			}
12291558Srgrimes			curfile.ino = maxino;
12301558Srgrimes			break;
12311558Srgrimes
12321558Srgrimes		case TS_CLRI:
12331558Srgrimes			curfile.name = "<file removal list>";
12341558Srgrimes			break;
12351558Srgrimes
12361558Srgrimes		case TS_BITS:
12371558Srgrimes			curfile.name = "<file dump list>";
12381558Srgrimes			break;
12391558Srgrimes
12401558Srgrimes		case TS_TAPE:
12411558Srgrimes			panic("unexpected tape header\n");
12421558Srgrimes			/* NOTREACHED */
12431558Srgrimes
12441558Srgrimes		default:
12451558Srgrimes			panic("unknown tape header type %d\n", spcl.c_type);
12461558Srgrimes			/* NOTREACHED */
12471558Srgrimes
12481558Srgrimes		}
124990608Siedowse	} while (htype == TS_ADDR);
12501558Srgrimes	if (skipcnt > 0)
125137240Sbde		fprintf(stderr, "resync restore, skipped %ld blocks\n",
125237240Sbde		    skipcnt);
12531558Srgrimes	skipcnt = 0;
12541558Srgrimes}
12551558Srgrimes
12561558Srgrimesstatic int
125792837Simpchecksum(int *buf)
12581558Srgrimes{
125992806Sobrien	int i, j;
12601558Srgrimes
12611558Srgrimes	j = sizeof(union u_spcl) / sizeof(int);
12621558Srgrimes	i = 0;
126398542Smckusick	if (!Bcvt) {
12641558Srgrimes		do
12651558Srgrimes			i += *buf++;
12661558Srgrimes		while (--j);
12671558Srgrimes	} else {
12681558Srgrimes		/* What happens if we want to read restore tapes
12691558Srgrimes			for a 16bit int machine??? */
12708871Srgrimes		do
12711558Srgrimes			i += swabl(*buf++);
12721558Srgrimes		while (--j);
12731558Srgrimes	}
12748871Srgrimes
12751558Srgrimes	if (i != CHECKSUM) {
12761558Srgrimes		fprintf(stderr, "Checksum error %o, inode %d file %s\n", i,
12771558Srgrimes			curfile.ino, curfile.name);
12781558Srgrimes		return(FAIL);
12791558Srgrimes	}
12801558Srgrimes	return(GOOD);
12811558Srgrimes}
12821558Srgrimes
12831558Srgrimes#ifdef RRESTORE
12841558Srgrimes#include <stdarg.h>
12851558Srgrimes
12861558Srgrimesvoid
12871558Srgrimesmsg(const char *fmt, ...)
12881558Srgrimes{
12891558Srgrimes	va_list ap;
12901558Srgrimes	va_start(ap, fmt);
12911558Srgrimes	(void)vfprintf(stderr, fmt, ap);
12921558Srgrimes	va_end(ap);
12931558Srgrimes}
12941558Srgrimes#endif /* RRESTORE */
12951558Srgrimes
12961558Srgrimesstatic u_char *
129792837Simpswabshort(u_char *sp, int n)
12981558Srgrimes{
12991558Srgrimes	char c;
13001558Srgrimes
13011558Srgrimes	while (--n >= 0) {
13021558Srgrimes		c = sp[0]; sp[0] = sp[1]; sp[1] = c;
13031558Srgrimes		sp += 2;
13041558Srgrimes	}
13051558Srgrimes	return (sp);
13061558Srgrimes}
13071558Srgrimes
13081558Srgrimesstatic u_char *
130992837Simpswablong(u_char *sp, int n)
13101558Srgrimes{
13111558Srgrimes	char c;
13121558Srgrimes
13131558Srgrimes	while (--n >= 0) {
13141558Srgrimes		c = sp[0]; sp[0] = sp[3]; sp[3] = c;
13151558Srgrimes		c = sp[2]; sp[2] = sp[1]; sp[1] = c;
13161558Srgrimes		sp += 4;
13171558Srgrimes	}
13181558Srgrimes	return (sp);
13191558Srgrimes}
13201558Srgrimes
132198542Smckusickstatic u_char *
132298542Smckusickswabquad(u_char *sp, int n)
132398542Smckusick{
132498542Smckusick	char c;
132598542Smckusick
132698542Smckusick	while (--n >= 0) {
132798542Smckusick		c = sp[0]; sp[0] = sp[7]; sp[7] = c;
132898542Smckusick		c = sp[1]; sp[1] = sp[6]; sp[6] = c;
132998542Smckusick		c = sp[2]; sp[2] = sp[5]; sp[5] = c;
133098542Smckusick		c = sp[3]; sp[3] = sp[4]; sp[4] = c;
133198542Smckusick		sp += 8;
133298542Smckusick	}
133398542Smckusick	return (sp);
133498542Smckusick}
133598542Smckusick
13361558Srgrimesvoid
133792837Simpswabst(u_char *cp, u_char *sp)
13381558Srgrimes{
13391558Srgrimes	int n = 0;
13401558Srgrimes
13411558Srgrimes	while (*cp) {
13421558Srgrimes		switch (*cp) {
13431558Srgrimes		case '0': case '1': case '2': case '3': case '4':
13441558Srgrimes		case '5': case '6': case '7': case '8': case '9':
13451558Srgrimes			n = (n * 10) + (*cp++ - '0');
13461558Srgrimes			continue;
13478871Srgrimes
13481558Srgrimes		case 's': case 'w': case 'h':
13491558Srgrimes			if (n == 0)
13501558Srgrimes				n = 1;
13511558Srgrimes			sp = swabshort(sp, n);
13521558Srgrimes			break;
13531558Srgrimes
13541558Srgrimes		case 'l':
13551558Srgrimes			if (n == 0)
13561558Srgrimes				n = 1;
13571558Srgrimes			sp = swablong(sp, n);
13581558Srgrimes			break;
13591558Srgrimes
136098542Smckusick		case 'q':
13611558Srgrimes			if (n == 0)
13621558Srgrimes				n = 1;
136398542Smckusick			sp = swabquad(sp, n);
136498542Smckusick			break;
136598542Smckusick
136698542Smckusick		case 'b':
136798542Smckusick			if (n == 0)
136898542Smckusick				n = 1;
13691558Srgrimes			sp += n;
13701558Srgrimes			break;
137198542Smckusick
137298542Smckusick		default:
137398542Smckusick			fprintf(stderr, "Unknown conversion character: %c\n",
137498542Smckusick			    *cp);
137598542Smckusick			done(0);
137698542Smckusick			break;
13771558Srgrimes		}
13781558Srgrimes		cp++;
13791558Srgrimes		n = 0;
13801558Srgrimes	}
13811558Srgrimes}
13821558Srgrimes
13831558Srgrimesstatic u_long
138492837Simpswabl(u_long x)
13851558Srgrimes{
13861558Srgrimes	swabst((u_char *)"l", (u_char *)&x);
13871558Srgrimes	return (x);
13881558Srgrimes}
1389