tape.c revision 146754
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
391558Srgrimes#endif /* not lint */
401558Srgrimes
41146754Scharnier#include <sys/cdefs.h>
42146754Scharnier__FBSDID("$FreeBSD: head/sbin/restore/tape.c 146754 2005-05-29 15:57:00Z charnier $");
43146754Scharnier
441558Srgrimes#include <sys/param.h>
451558Srgrimes#include <sys/file.h>
461558Srgrimes#include <sys/mtio.h>
471558Srgrimes#include <sys/stat.h>
4866907Swollman#include <sys/time.h>
491558Srgrimes
501558Srgrimes#include <ufs/ufs/dinode.h>
511558Srgrimes#include <protocols/dumprestore.h>
521558Srgrimes
531558Srgrimes#include <errno.h>
54103949Smike#include <limits.h>
5573986Sobrien#include <paths.h>
561558Srgrimes#include <setjmp.h>
571558Srgrimes#include <stdio.h>
581558Srgrimes#include <stdlib.h>
591558Srgrimes#include <string.h>
6066907Swollman#include <time.h>
61129665Sstefanf#include <timeconv.h>
621558Srgrimes#include <unistd.h>
631558Srgrimes
641558Srgrimes#include "restore.h"
651558Srgrimes#include "extern.h"
661558Srgrimes
671558Srgrimesstatic long	fssize = MAXBSIZE;
681558Srgrimesstatic int	mt = -1;
691558Srgrimesstatic int	pipein = 0;
70128175Sgreenstatic int	pipecmdin = 0;
71128175Sgreenstatic FILE	*popenfp = NULL;
7221174Sguidostatic char	*magtape;
731558Srgrimesstatic int	blkcnt;
741558Srgrimesstatic int	numtrec;
751558Srgrimesstatic char	*tapebuf;
761558Srgrimesstatic union	u_spcl endoftapemark;
771558Srgrimesstatic long	blksread;		/* blocks read since last header */
7898542Smckusickstatic int64_t	tapeaddr = 0;		/* current TP_BSIZE tape record */
791558Srgrimesstatic long	tapesread;
801558Srgrimesstatic jmp_buf	restart;
811558Srgrimesstatic int	gettingfile = 0;	/* restart has a valid frame */
821558Srgrimesstatic char	*host = NULL;
8398542Smckusickstatic int	readmapflag;
841558Srgrimes
851558Srgrimesstatic int	ofile;
861558Srgrimesstatic char	*map;
871558Srgrimesstatic char	lnkbuf[MAXPATHLEN + 1];
881558Srgrimesstatic int	pathlen;
891558Srgrimes
9098542Smckusickint		Bcvt;		/* Swap Bytes */
91144099Simpint		oldinofmt;	/* FreeBSD 1 inode format needs cvt */
921558Srgrimes
931558Srgrimes#define	FLUSHTAPEBUF()	blkcnt = ntrec + 1
941558Srgrimes
9592837Simpstatic void	 accthdr(struct s_spcl *);
9692837Simpstatic int	 checksum(int *);
9792837Simpstatic void	 findinode(struct s_spcl *);
9892837Simpstatic void	 findtapeblksize(void);
9992837Simpstatic int	 gethead(struct s_spcl *);
10092837Simpstatic void	 readtape(char *);
10192837Simpstatic void	 setdumpnum(void);
10292837Simpstatic u_long	 swabl(u_long);
10392837Simpstatic u_char	*swablong(u_char *, int);
10492837Simpstatic u_char	*swabshort(u_char *, int);
10592837Simpstatic void	 terminateinput(void);
10692837Simpstatic void	 xtrfile(char *, long);
10792837Simpstatic void	 xtrlnkfile(char *, long);
10892837Simpstatic void	 xtrlnkskip(char *, long);
10992837Simpstatic void	 xtrmap(char *, long);
11092837Simpstatic void	 xtrmapskip(char *, long);
11192837Simpstatic void	 xtrskip(char *, long);
1121558Srgrimes
1131558Srgrimes/*
1141558Srgrimes * Set up an input source
1151558Srgrimes */
1161558Srgrimesvoid
117128175Sgreensetinput(char *source, int ispipecommand)
1181558Srgrimes{
1191558Srgrimes	FLUSHTAPEBUF();
1201558Srgrimes	if (bflag)
1211558Srgrimes		newtapebuf(ntrec);
1221558Srgrimes	else
1231558Srgrimes		newtapebuf(NTREC > HIGHDENSITYTREC ? NTREC : HIGHDENSITYTREC);
1241558Srgrimes	terminal = stdin;
1251558Srgrimes
126128175Sgreen	if (ispipecommand)
127128175Sgreen		pipecmdin++;
128128175Sgreen	else
1291558Srgrimes#ifdef RRESTORE
13023685Speter	if (strchr(source, ':')) {
1311558Srgrimes		host = source;
13223685Speter		source = strchr(host, ':');
1331558Srgrimes		*source++ = '\0';
1341558Srgrimes		if (rmthost(host) == 0)
1351558Srgrimes			done(1);
1361558Srgrimes	} else
1371558Srgrimes#endif
1381558Srgrimes	if (strcmp(source, "-") == 0) {
1391558Srgrimes		/*
1401558Srgrimes		 * Since input is coming from a pipe we must establish
1411558Srgrimes		 * our own connection to the terminal.
1421558Srgrimes		 */
1431558Srgrimes		terminal = fopen(_PATH_TTY, "r");
1441558Srgrimes		if (terminal == NULL) {
1451558Srgrimes			(void)fprintf(stderr, "cannot open %s: %s\n",
1461558Srgrimes			    _PATH_TTY, strerror(errno));
1471558Srgrimes			terminal = fopen(_PATH_DEVNULL, "r");
1481558Srgrimes			if (terminal == NULL) {
1491558Srgrimes				(void)fprintf(stderr, "cannot open %s: %s\n",
1501558Srgrimes				    _PATH_DEVNULL, strerror(errno));
1511558Srgrimes				done(1);
1521558Srgrimes			}
1531558Srgrimes		}
1541558Srgrimes		pipein++;
1551558Srgrimes	}
1561558Srgrimes	setuid(getuid());	/* no longer need or want root privileges */
15721174Sguido	magtape = strdup(source);
15821174Sguido	if (magtape == NULL) {
15921174Sguido		fprintf(stderr, "Cannot allocate space for magtape buffer\n");
16021174Sguido		done(1);
16121174Sguido	}
1621558Srgrimes}
1631558Srgrimes
1641558Srgrimesvoid
16592837Simpnewtapebuf(long size)
1661558Srgrimes{
16792837Simp	static int tapebufsize = -1;
1681558Srgrimes
1691558Srgrimes	ntrec = size;
1701558Srgrimes	if (size <= tapebufsize)
1711558Srgrimes		return;
1721558Srgrimes	if (tapebuf != NULL)
1731558Srgrimes		free(tapebuf);
1741558Srgrimes	tapebuf = malloc(size * TP_BSIZE);
1751558Srgrimes	if (tapebuf == NULL) {
1761558Srgrimes		fprintf(stderr, "Cannot allocate space for tape buffer\n");
1771558Srgrimes		done(1);
1781558Srgrimes	}
1791558Srgrimes	tapebufsize = size;
1801558Srgrimes}
1811558Srgrimes
1821558Srgrimes/*
1831558Srgrimes * Verify that the tape drive can be accessed and
1841558Srgrimes * that it actually is a dump tape.
1851558Srgrimes */
1861558Srgrimesvoid
18792837Simpsetup(void)
1881558Srgrimes{
1891558Srgrimes	int i, j, *ip;
1901558Srgrimes	struct stat stbuf;
1911558Srgrimes
1921558Srgrimes	vprintf(stdout, "Verify tape and initialize maps\n");
193128175Sgreen	if (pipecmdin) {
194128175Sgreen		if (setenv("RESTORE_VOLUME", "1", 1) == -1) {
195128175Sgreen			fprintf(stderr, "Cannot set $RESTORE_VOLUME: %s\n",
196128175Sgreen			    strerror(errno));
197128175Sgreen			done(1);
198128175Sgreen		}
199128175Sgreen		popenfp = popen(magtape, "r");
200128175Sgreen		mt = popenfp ? fileno(popenfp) : -1;
201128175Sgreen	} else
2021558Srgrimes#ifdef RRESTORE
2031558Srgrimes	if (host)
2041558Srgrimes		mt = rmtopen(magtape, 0);
2051558Srgrimes	else
2061558Srgrimes#endif
2071558Srgrimes	if (pipein)
2081558Srgrimes		mt = 0;
2091558Srgrimes	else
2101558Srgrimes		mt = open(magtape, O_RDONLY, 0);
2111558Srgrimes	if (mt < 0) {
2121558Srgrimes		fprintf(stderr, "%s: %s\n", magtape, strerror(errno));
2131558Srgrimes		done(1);
2141558Srgrimes	}
2151558Srgrimes	volno = 1;
2161558Srgrimes	setdumpnum();
2171558Srgrimes	FLUSHTAPEBUF();
2181558Srgrimes	if (!pipein && !bflag)
2191558Srgrimes		findtapeblksize();
2201558Srgrimes	if (gethead(&spcl) == FAIL) {
22198542Smckusick		fprintf(stderr, "Tape is not a dump tape\n");
22298542Smckusick		done(1);
2231558Srgrimes	}
2241558Srgrimes	if (pipein) {
22598542Smckusick		endoftapemark.s_spcl.c_magic = FS_UFS2_MAGIC;
2261558Srgrimes		endoftapemark.s_spcl.c_type = TS_END;
2271558Srgrimes		ip = (int *)&endoftapemark;
2281558Srgrimes		j = sizeof(union u_spcl) / sizeof(int);
2291558Srgrimes		i = 0;
2301558Srgrimes		do
2311558Srgrimes			i += *ip++;
2321558Srgrimes		while (--j);
2331558Srgrimes		endoftapemark.s_spcl.c_checksum = CHECKSUM - i;
2341558Srgrimes	}
2351558Srgrimes	if (vflag || command == 't')
2361558Srgrimes		printdumpinfo();
23798542Smckusick	dumptime = _time64_to_time(spcl.c_ddate);
23898542Smckusick	dumpdate = _time64_to_time(spcl.c_date);
2391558Srgrimes	if (stat(".", &stbuf) < 0) {
2401558Srgrimes		fprintf(stderr, "cannot stat .: %s\n", strerror(errno));
2411558Srgrimes		done(1);
2421558Srgrimes	}
24334851Sjkh	if (stbuf.st_blksize > 0 && stbuf.st_blksize < TP_BSIZE )
24434851Sjkh		fssize = TP_BSIZE;
24534851Sjkh	if (stbuf.st_blksize >= TP_BSIZE && stbuf.st_blksize <= MAXBSIZE)
2461558Srgrimes		fssize = stbuf.st_blksize;
2471558Srgrimes	if (((fssize - 1) & fssize) != 0) {
24837240Sbde		fprintf(stderr, "bad block size %ld\n", fssize);
2491558Srgrimes		done(1);
2501558Srgrimes	}
2511558Srgrimes	if (spcl.c_volume != 1) {
2521558Srgrimes		fprintf(stderr, "Tape is not volume 1 of the dump\n");
2531558Srgrimes		done(1);
2541558Srgrimes	}
2551558Srgrimes	if (gethead(&spcl) == FAIL) {
25637240Sbde		dprintf(stdout, "header read failed at %ld blocks\n", blksread);
2571558Srgrimes		panic("no header after volume mark!\n");
2581558Srgrimes	}
2591558Srgrimes	findinode(&spcl);
2601558Srgrimes	if (spcl.c_type != TS_CLRI) {
2611558Srgrimes		fprintf(stderr, "Cannot find file removal list\n");
2621558Srgrimes		done(1);
2631558Srgrimes	}
2641558Srgrimes	maxino = (spcl.c_count * TP_BSIZE * NBBY) + 1;
2651558Srgrimes	dprintf(stdout, "maxino = %d\n", maxino);
2661558Srgrimes	map = calloc((unsigned)1, (unsigned)howmany(maxino, NBBY));
2671558Srgrimes	if (map == NULL)
26823685Speter		panic("no memory for active inode map\n");
26923685Speter	usedinomap = map;
2701558Srgrimes	curfile.action = USING;
2711558Srgrimes	getfile(xtrmap, xtrmapskip);
2721558Srgrimes	if (spcl.c_type != TS_BITS) {
2731558Srgrimes		fprintf(stderr, "Cannot find file dump list\n");
2741558Srgrimes		done(1);
2751558Srgrimes	}
2761558Srgrimes	map = calloc((unsigned)1, (unsigned)howmany(maxino, NBBY));
2771558Srgrimes	if (map == (char *)NULL)
2781558Srgrimes		panic("no memory for file dump list\n");
2791558Srgrimes	dumpmap = map;
2801558Srgrimes	curfile.action = USING;
2811558Srgrimes	getfile(xtrmap, xtrmapskip);
28223685Speter	/*
28323685Speter	 * If there may be whiteout entries on the tape, pretend that the
28423685Speter	 * whiteout inode exists, so that the whiteout entries can be
28523685Speter	 * extracted.
28623685Speter	 */
28798542Smckusick	SETINO(WINO, dumpmap);
28890820Siedowse	/* 'r' restores don't call getvol() for tape 1, so mark it as read. */
28990820Siedowse	if (command == 'r')
29090820Siedowse		tapesread = 1;
2911558Srgrimes}
2921558Srgrimes
2931558Srgrimes/*
2941558Srgrimes * Prompt user to load a new dump volume.
2951558Srgrimes * "Nextvol" is the next suggested volume to use.
2961558Srgrimes * This suggested volume is enforced when doing full
29737906Scharnier * or incremental restores, but can be overridden by
2981558Srgrimes * the user when only extracting a subset of the files.
2991558Srgrimes */
3001558Srgrimesvoid
30192837Simpgetvol(long nextvol)
3021558Srgrimes{
30398542Smckusick	int64_t prevtapea;
30498542Smckusick	long i, newvol, savecnt;
3051558Srgrimes	union u_spcl tmpspcl;
3061558Srgrimes#	define tmpbuf tmpspcl.s_spcl
3071558Srgrimes	char buf[TP_BSIZE];
3081558Srgrimes
3091558Srgrimes	if (nextvol == 1) {
3101558Srgrimes		tapesread = 0;
3111558Srgrimes		gettingfile = 0;
3121558Srgrimes	}
31390827Siedowse	prevtapea = tapeaddr;
31490827Siedowse	savecnt = blksread;
3151558Srgrimes	if (pipein) {
31669906Siedowse		if (nextvol != 1) {
3171558Srgrimes			panic("Changing volumes on pipe input?\n");
31869906Siedowse			/* Avoid looping if we couldn't ask the user. */
31969906Siedowse			if (yflag || ferror(terminal) || feof(terminal))
32069906Siedowse				done(1);
32169906Siedowse		}
3221558Srgrimes		if (volno == 1)
3231558Srgrimes			return;
324128175Sgreen		if (pipecmdin) {
325128175Sgreen			closemt();
326128175Sgreen			goto getpipecmdhdr;
327128175Sgreen		}
3281558Srgrimes		goto gethdr;
3291558Srgrimes	}
3301558Srgrimesagain:
3311558Srgrimes	if (pipein)
3321558Srgrimes		done(1); /* pipes do not get a second chance */
33390608Siedowse	if (command == 'R' || command == 'r' || curfile.action != SKIP)
3341558Srgrimes		newvol = nextvol;
33590608Siedowse	else
3361558Srgrimes		newvol = 0;
3371558Srgrimes	while (newvol <= 0) {
3381558Srgrimes		if (tapesread == 0) {
33990820Siedowse			fprintf(stderr, "%s%s%s%s%s%s%s",
3401558Srgrimes			    "You have not read any tapes yet.\n",
34190820Siedowse			    "If you are extracting just a few files,",
34290820Siedowse			    " start with the last volume\n",
34390820Siedowse			    "and work towards the first; restore",
34490820Siedowse			    " can quickly skip tapes that\n",
34590820Siedowse			    "have no further files to extract.",
34690820Siedowse			    " Otherwise, begin with volume 1.\n");
3471558Srgrimes		} else {
3481558Srgrimes			fprintf(stderr, "You have read volumes");
3491558Srgrimes			strcpy(buf, ": ");
35090820Siedowse			for (i = 0; i < 32; i++)
3511558Srgrimes				if (tapesread & (1 << i)) {
35290820Siedowse					fprintf(stderr, "%s%ld", buf, i + 1);
3531558Srgrimes					strcpy(buf, ", ");
3541558Srgrimes				}
3551558Srgrimes			fprintf(stderr, "\n");
3561558Srgrimes		}
3571558Srgrimes		do	{
3581558Srgrimes			fprintf(stderr, "Specify next volume #: ");
3591558Srgrimes			(void) fflush(stderr);
36069906Siedowse			if (fgets(buf, BUFSIZ, terminal) == NULL)
36169906Siedowse				done(1);
36269906Siedowse		} while (buf[0] == '\n');
3631558Srgrimes		newvol = atoi(buf);
3641558Srgrimes		if (newvol <= 0) {
3651558Srgrimes			fprintf(stderr,
3661558Srgrimes			    "Volume numbers are positive numerics\n");
3671558Srgrimes		}
3681558Srgrimes	}
3691558Srgrimes	if (newvol == volno) {
37090820Siedowse		tapesread |= 1 << (volno - 1);
3711558Srgrimes		return;
3721558Srgrimes	}
3731558Srgrimes	closemt();
37437240Sbde	fprintf(stderr, "Mount tape volume %ld\n", newvol);
3751558Srgrimes	fprintf(stderr, "Enter ``none'' if there are no more tapes\n");
3761558Srgrimes	fprintf(stderr, "otherwise enter tape name (default: %s) ", magtape);
3771558Srgrimes	(void) fflush(stderr);
37869906Siedowse	if (fgets(buf, BUFSIZ, terminal) == NULL)
3791558Srgrimes		done(1);
3801558Srgrimes	if (!strcmp(buf, "none\n")) {
3811558Srgrimes		terminateinput();
3821558Srgrimes		return;
3831558Srgrimes	}
3841558Srgrimes	if (buf[0] != '\n') {
3851558Srgrimes		(void) strcpy(magtape, buf);
3861558Srgrimes		magtape[strlen(magtape) - 1] = '\0';
3871558Srgrimes	}
388128175Sgreen	if (pipecmdin) {
389128175Sgreen		char volno[sizeof("2147483647")];
390128175Sgreen
391128175Sgreengetpipecmdhdr:
392128175Sgreen		(void)sprintf(volno, "%d", newvol);
393128175Sgreen		if (setenv("RESTORE_VOLUME", volno, 1) == -1) {
394128175Sgreen			fprintf(stderr, "Cannot set $RESTORE_VOLUME: %s\n",
395128175Sgreen			    strerror(errno));
396128175Sgreen			done(1);
397128175Sgreen		}
398128175Sgreen		popenfp = popen(magtape, "r");
399128175Sgreen		mt = popenfp ? fileno(popenfp) : -1;
400128175Sgreen	} else
4011558Srgrimes#ifdef RRESTORE
4021558Srgrimes	if (host)
4031558Srgrimes		mt = rmtopen(magtape, 0);
4041558Srgrimes	else
4051558Srgrimes#endif
4061558Srgrimes		mt = open(magtape, O_RDONLY, 0);
4071558Srgrimes
4081558Srgrimes	if (mt == -1) {
4091558Srgrimes		fprintf(stderr, "Cannot open %s\n", magtape);
4101558Srgrimes		volno = -1;
4111558Srgrimes		goto again;
4121558Srgrimes	}
4131558Srgrimesgethdr:
4141558Srgrimes	volno = newvol;
4151558Srgrimes	setdumpnum();
4161558Srgrimes	FLUSHTAPEBUF();
4171558Srgrimes	if (gethead(&tmpbuf) == FAIL) {
41837240Sbde		dprintf(stdout, "header read failed at %ld blocks\n", blksread);
4191558Srgrimes		fprintf(stderr, "tape is not dump tape\n");
4201558Srgrimes		volno = 0;
4211558Srgrimes		goto again;
4221558Srgrimes	}
4231558Srgrimes	if (tmpbuf.c_volume != volno) {
42437240Sbde		fprintf(stderr, "Wrong volume (%ld)\n", tmpbuf.c_volume);
4251558Srgrimes		volno = 0;
4261558Srgrimes		goto again;
4271558Srgrimes	}
42898542Smckusick	if (_time64_to_time(tmpbuf.c_date) != dumpdate ||
42998542Smckusick	    _time64_to_time(tmpbuf.c_ddate) != dumptime) {
43098542Smckusick		time_t t = _time64_to_time(tmpbuf.c_date);
43185635Sdillon		fprintf(stderr, "Wrong dump date\n\tgot: %s", ctime(&t));
4321558Srgrimes		fprintf(stderr, "\twanted: %s", ctime(&dumpdate));
4331558Srgrimes		volno = 0;
4341558Srgrimes		goto again;
4351558Srgrimes	}
43690820Siedowse	tapesread |= 1 << (volno - 1);
4371558Srgrimes	blksread = savecnt;
4381558Srgrimes 	/*
4391558Srgrimes 	 * If continuing from the previous volume, skip over any
4401558Srgrimes 	 * blocks read already at the end of the previous volume.
4411558Srgrimes 	 *
4421558Srgrimes 	 * If coming to this volume at random, skip to the beginning
4431558Srgrimes 	 * of the next record.
4441558Srgrimes 	 */
44598542Smckusick	dprintf(stdout, "last rec %qd, tape starts with %qd\n", prevtapea,
44690827Siedowse	    tmpbuf.c_tapea);
44798542Smckusick 	if (tmpbuf.c_type == TS_TAPE) {
44890608Siedowse 		if (curfile.action != USING) {
44990608Siedowse			/*
45090608Siedowse			 * XXX Dump incorrectly sets c_count to 1 in the
45190608Siedowse			 * volume header of the first tape, so ignore
45290608Siedowse			 * c_count when volno == 1.
45390608Siedowse			 */
45490608Siedowse			if (volno != 1)
45590608Siedowse				for (i = tmpbuf.c_count; i > 0; i--)
45690608Siedowse					readtape(buf);
45790827Siedowse 		} else if (tmpbuf.c_tapea <= prevtapea) {
4581558Srgrimes			/*
45990827Siedowse			 * Normally the value of c_tapea in the volume
46090827Siedowse			 * header is the record number of the header itself.
46190827Siedowse			 * However in the volume header following an EOT-
46290827Siedowse			 * terminated tape, it is the record number of the
46390827Siedowse			 * first continuation data block (dump bug?).
46490827Siedowse			 *
46590827Siedowse			 * The next record we want is `prevtapea + 1'.
4661558Srgrimes			 */
46790827Siedowse 			i = prevtapea + 1 - tmpbuf.c_tapea;
46837240Sbde			dprintf(stderr, "Skipping %ld duplicate record%s.\n",
4691558Srgrimes				i, i > 1 ? "s" : "");
4701558Srgrimes 			while (--i >= 0)
4711558Srgrimes 				readtape(buf);
4721558Srgrimes 		}
4731558Srgrimes 	}
47490608Siedowse	if (curfile.action == USING) {
4751558Srgrimes		if (volno == 1)
4761558Srgrimes			panic("active file into volume 1\n");
4771558Srgrimes		return;
4781558Srgrimes	}
4791558Srgrimes	(void) gethead(&spcl);
4801558Srgrimes	findinode(&spcl);
4811558Srgrimes	if (gettingfile) {
4821558Srgrimes		gettingfile = 0;
4831558Srgrimes		longjmp(restart, 1);
4841558Srgrimes	}
4851558Srgrimes}
4861558Srgrimes
4871558Srgrimes/*
4881558Srgrimes * Handle unexpected EOF.
4891558Srgrimes */
4901558Srgrimesstatic void
49192837Simpterminateinput(void)
4921558Srgrimes{
4931558Srgrimes
4941558Srgrimes	if (gettingfile && curfile.action == USING) {
4951558Srgrimes		printf("Warning: %s %s\n",
4961558Srgrimes		    "End-of-input encountered while extracting", curfile.name);
4971558Srgrimes	}
4981558Srgrimes	curfile.name = "<name unknown>";
4991558Srgrimes	curfile.action = UNKNOWN;
50098542Smckusick	curfile.mode = 0;
5011558Srgrimes	curfile.ino = maxino;
5021558Srgrimes	if (gettingfile) {
5031558Srgrimes		gettingfile = 0;
5041558Srgrimes		longjmp(restart, 1);
5051558Srgrimes	}
5061558Srgrimes}
5071558Srgrimes
5081558Srgrimes/*
5091558Srgrimes * handle multiple dumps per tape by skipping forward to the
5101558Srgrimes * appropriate one.
5111558Srgrimes */
5121558Srgrimesstatic void
51392837Simpsetdumpnum(void)
5141558Srgrimes{
5151558Srgrimes	struct mtop tcom;
5161558Srgrimes
5171558Srgrimes	if (dumpnum == 1 || volno != 1)
5181558Srgrimes		return;
5191558Srgrimes	if (pipein) {
5201558Srgrimes		fprintf(stderr, "Cannot have multiple dumps on pipe input\n");
5211558Srgrimes		done(1);
5221558Srgrimes	}
5231558Srgrimes	tcom.mt_op = MTFSF;
5241558Srgrimes	tcom.mt_count = dumpnum - 1;
5251558Srgrimes#ifdef RRESTORE
5261558Srgrimes	if (host)
5271558Srgrimes		rmtioctl(MTFSF, dumpnum - 1);
5288871Srgrimes	else
5291558Srgrimes#endif
530128175Sgreen		if (!pipecmdin && ioctl(mt, MTIOCTOP, (char *)&tcom) < 0)
5311558Srgrimes			fprintf(stderr, "ioctl MTFSF: %s\n", strerror(errno));
5321558Srgrimes}
5331558Srgrimes
5341558Srgrimesvoid
53592837Simpprintdumpinfo(void)
5361558Srgrimes{
53785635Sdillon	time_t t;
53898542Smckusick	t = _time64_to_time(spcl.c_date);
53985635Sdillon	fprintf(stdout, "Dump   date: %s", ctime(&t));
54098542Smckusick	t = _time64_to_time(spcl.c_ddate);
5411558Srgrimes	fprintf(stdout, "Dumped from: %s",
54285635Sdillon	    (spcl.c_ddate == 0) ? "the epoch\n" : ctime(&t));
5431558Srgrimes	if (spcl.c_host[0] == '\0')
5441558Srgrimes		return;
54537240Sbde	fprintf(stderr, "Level %ld dump of %s on %s:%s\n",
5461558Srgrimes		spcl.c_level, spcl.c_filesys, spcl.c_host, spcl.c_dev);
5471558Srgrimes	fprintf(stderr, "Label: %s\n", spcl.c_label);
5481558Srgrimes}
5491558Srgrimes
5501558Srgrimesint
55192837Simpextractfile(char *name)
5521558Srgrimes{
55323685Speter	int flags;
55423685Speter	mode_t mode;
555100207Smckusick	struct timeval mtimep[2], ctimep[2];
5561558Srgrimes	struct entry *ep;
5571558Srgrimes
5581558Srgrimes	curfile.name = name;
5591558Srgrimes	curfile.action = USING;
560100207Smckusick	mtimep[0].tv_sec = curfile.atime_sec;
561100207Smckusick	mtimep[0].tv_usec = curfile.atime_nsec / 1000;
562100207Smckusick	mtimep[1].tv_sec = curfile.mtime_sec;
563100207Smckusick	mtimep[1].tv_usec = curfile.mtime_nsec / 1000;
564100207Smckusick	ctimep[0].tv_sec = curfile.atime_sec;
565100207Smckusick	ctimep[0].tv_usec = curfile.atime_nsec / 1000;
566100207Smckusick	ctimep[1].tv_sec = curfile.birthtime_sec;
567100207Smckusick	ctimep[1].tv_usec = curfile.birthtime_nsec / 1000;
56898542Smckusick	mode = curfile.mode;
56998542Smckusick	flags = curfile.file_flags;
5701558Srgrimes	switch (mode & IFMT) {
5711558Srgrimes
5721558Srgrimes	default:
5731558Srgrimes		fprintf(stderr, "%s: unknown file mode 0%o\n", name, mode);
5741558Srgrimes		skipfile();
5751558Srgrimes		return (FAIL);
5761558Srgrimes
5771558Srgrimes	case IFSOCK:
5781558Srgrimes		vprintf(stdout, "skipped socket %s\n", name);
5791558Srgrimes		skipfile();
5801558Srgrimes		return (GOOD);
5811558Srgrimes
5821558Srgrimes	case IFDIR:
5831558Srgrimes		if (mflag) {
5841558Srgrimes			ep = lookupname(name);
5851558Srgrimes			if (ep == NULL || ep->e_flags & EXTRACT)
5861558Srgrimes				panic("unextracted directory %s\n", name);
5871558Srgrimes			skipfile();
5881558Srgrimes			return (GOOD);
5891558Srgrimes		}
5901558Srgrimes		vprintf(stdout, "extract file %s\n", name);
5911558Srgrimes		return (genliteraldir(name, curfile.ino));
5921558Srgrimes
5931558Srgrimes	case IFLNK:
5941558Srgrimes		lnkbuf[0] = '\0';
5951558Srgrimes		pathlen = 0;
5961558Srgrimes		getfile(xtrlnkfile, xtrlnkskip);
5971558Srgrimes		if (pathlen == 0) {
5981558Srgrimes			vprintf(stdout,
5991558Srgrimes			    "%s: zero length symbolic link (ignored)\n", name);
6001558Srgrimes			return (GOOD);
6011558Srgrimes		}
60296113Siedowse		if (linkit(lnkbuf, name, SYMLINK) == GOOD) {
60398542Smckusick			(void) lchown(name, curfile.uid, curfile.gid);
60496113Siedowse			(void) lchmod(name, mode);
605100207Smckusick			(void) lutimes(name, ctimep);
606100207Smckusick			(void) lutimes(name, mtimep);
60796113Siedowse			return (GOOD);
60895943Siedowse		}
60996113Siedowse		return (FAIL);
6101558Srgrimes
6116305Smartin	case IFIFO:
61223685Speter		vprintf(stdout, "extract fifo %s\n", name);
61323685Speter		if (Nflag) {
61423685Speter			skipfile();
61523685Speter			return (GOOD);
61623685Speter		}
61735852Sjkh		if (uflag && !Nflag)
61835852Sjkh			(void)unlink(name);
6196305Smartin		if (mkfifo(name, mode) < 0) {
62023685Speter			fprintf(stderr, "%s: cannot create fifo: %s\n",
62123685Speter			    name, strerror(errno));
6226305Smartin			skipfile();
6236305Smartin			return (FAIL);
6246305Smartin		}
62598542Smckusick		(void) chown(name, curfile.uid, curfile.gid);
6266305Smartin		(void) chmod(name, mode);
627100207Smckusick		(void) utimes(name, ctimep);
628100207Smckusick		(void) utimes(name, mtimep);
62923685Speter		(void) chflags(name, flags);
6306305Smartin		skipfile();
6316305Smartin		return (GOOD);
6326305Smartin
6331558Srgrimes	case IFCHR:
6341558Srgrimes	case IFBLK:
6351558Srgrimes		vprintf(stdout, "extract special file %s\n", name);
6361558Srgrimes		if (Nflag) {
6371558Srgrimes			skipfile();
6381558Srgrimes			return (GOOD);
6391558Srgrimes		}
64035852Sjkh		if (uflag)
64135852Sjkh			(void)unlink(name);
64298542Smckusick		if (mknod(name, mode, (int)curfile.rdev) < 0) {
6431558Srgrimes			fprintf(stderr, "%s: cannot create special file: %s\n",
6441558Srgrimes			    name, strerror(errno));
6451558Srgrimes			skipfile();
6461558Srgrimes			return (FAIL);
6471558Srgrimes		}
64898542Smckusick		(void) chown(name, curfile.uid, curfile.gid);
6491558Srgrimes		(void) chmod(name, mode);
650100207Smckusick		(void) utimes(name, ctimep);
651100207Smckusick		(void) utimes(name, mtimep);
65223685Speter		(void) chflags(name, flags);
6531558Srgrimes		skipfile();
6541558Srgrimes		return (GOOD);
6551558Srgrimes
6561558Srgrimes	case IFREG:
6571558Srgrimes		vprintf(stdout, "extract file %s\n", name);
6581558Srgrimes		if (Nflag) {
6591558Srgrimes			skipfile();
6601558Srgrimes			return (GOOD);
6611558Srgrimes		}
66235852Sjkh		if (uflag)
66335852Sjkh			(void)unlink(name);
66421149Simp		if ((ofile = open(name, O_WRONLY | O_CREAT | O_TRUNC,
66521149Simp		    0666)) < 0) {
6661558Srgrimes			fprintf(stderr, "%s: cannot create file: %s\n",
6671558Srgrimes			    name, strerror(errno));
6681558Srgrimes			skipfile();
6691558Srgrimes			return (FAIL);
6701558Srgrimes		}
67198542Smckusick		(void) fchown(ofile, curfile.uid, curfile.gid);
6721558Srgrimes		(void) fchmod(ofile, mode);
6731558Srgrimes		getfile(xtrfile, xtrskip);
6741558Srgrimes		(void) close(ofile);
675100207Smckusick		(void) utimes(name, ctimep);
676100207Smckusick		(void) utimes(name, mtimep);
67763283Sdwmalone		(void) chflags(name, flags);
6781558Srgrimes		return (GOOD);
6791558Srgrimes	}
6801558Srgrimes	/* NOTREACHED */
6811558Srgrimes}
6821558Srgrimes
6831558Srgrimes/*
6841558Srgrimes * skip over bit maps on the tape
6851558Srgrimes */
6861558Srgrimesvoid
68792837Simpskipmaps(void)
6881558Srgrimes{
6891558Srgrimes
6901558Srgrimes	while (spcl.c_type == TS_BITS || spcl.c_type == TS_CLRI)
6911558Srgrimes		skipfile();
6921558Srgrimes}
6931558Srgrimes
6941558Srgrimes/*
6951558Srgrimes * skip over a file on the tape
6961558Srgrimes */
6971558Srgrimesvoid
69892837Simpskipfile(void)
6991558Srgrimes{
7001558Srgrimes
7011558Srgrimes	curfile.action = SKIP;
7021558Srgrimes	getfile(xtrnull, xtrnull);
7031558Srgrimes}
7041558Srgrimes
7051558Srgrimes/*
7061558Srgrimes * Extract a file from the tape.
7071558Srgrimes * When an allocated block is found it is passed to the fill function;
7081558Srgrimes * when an unallocated block (hole) is found, a zeroed buffer is passed
7091558Srgrimes * to the skip function.
7101558Srgrimes */
7111558Srgrimesvoid
71292837Simpgetfile(void (*fill)(char *, long), void (*skip)(char *, long))
7131558Srgrimes{
71492806Sobrien	int i;
7151558Srgrimes	int curblk = 0;
71698542Smckusick	quad_t size = spcl.c_size;
7171558Srgrimes	static char clearedbuf[MAXBSIZE];
7181558Srgrimes	char buf[MAXBSIZE / TP_BSIZE][TP_BSIZE];
7191558Srgrimes	char junk[TP_BSIZE];
7201558Srgrimes
7211558Srgrimes	if (spcl.c_type == TS_END)
7221558Srgrimes		panic("ran off end of tape\n");
72398542Smckusick	if (spcl.c_magic != FS_UFS2_MAGIC)
7241558Srgrimes		panic("not at beginning of a file\n");
7251558Srgrimes	if (!gettingfile && setjmp(restart) != 0)
7261558Srgrimes		return;
7271558Srgrimes	gettingfile++;
7281558Srgrimesloop:
7291558Srgrimes	for (i = 0; i < spcl.c_count; i++) {
73037923Simp		if (readmapflag || spcl.c_addr[i]) {
7311558Srgrimes			readtape(&buf[curblk++][0]);
7321558Srgrimes			if (curblk == fssize / TP_BSIZE) {
73323685Speter				(*fill)((char *)buf, (long)(size > TP_BSIZE ?
73423685Speter				     fssize : (curblk - 1) * TP_BSIZE + size));
7351558Srgrimes				curblk = 0;
7361558Srgrimes			}
7371558Srgrimes		} else {
7381558Srgrimes			if (curblk > 0) {
73923685Speter				(*fill)((char *)buf, (long)(size > TP_BSIZE ?
74023685Speter				     curblk * TP_BSIZE :
74123685Speter				     (curblk - 1) * TP_BSIZE + size));
7421558Srgrimes				curblk = 0;
7431558Srgrimes			}
74423685Speter			(*skip)(clearedbuf, (long)(size > TP_BSIZE ?
74523685Speter				TP_BSIZE : size));
7461558Srgrimes		}
7471558Srgrimes		if ((size -= TP_BSIZE) <= 0) {
7481558Srgrimes			for (i++; i < spcl.c_count; i++)
74937923Simp				if (readmapflag || spcl.c_addr[i])
7501558Srgrimes					readtape(junk);
7511558Srgrimes			break;
7521558Srgrimes		}
7531558Srgrimes	}
7541558Srgrimes	if (gethead(&spcl) == GOOD && size > 0) {
7551558Srgrimes		if (spcl.c_type == TS_ADDR)
7561558Srgrimes			goto loop;
7571558Srgrimes		dprintf(stdout,
75837240Sbde			"Missing address (header) block for %s at %ld blocks\n",
7591558Srgrimes			curfile.name, blksread);
7601558Srgrimes	}
7611558Srgrimes	if (curblk > 0)
76223685Speter		(*fill)((char *)buf, (long)((curblk * TP_BSIZE) + size));
7631558Srgrimes	findinode(&spcl);
7641558Srgrimes	gettingfile = 0;
7651558Srgrimes}
7661558Srgrimes
7671558Srgrimes/*
7681558Srgrimes * Write out the next block of a file.
7691558Srgrimes */
7701558Srgrimesstatic void
77192837Simpxtrfile(char *buf, long	size)
7721558Srgrimes{
7731558Srgrimes
7741558Srgrimes	if (Nflag)
7751558Srgrimes		return;
7761558Srgrimes	if (write(ofile, buf, (int) size) == -1) {
7771558Srgrimes		fprintf(stderr,
7781558Srgrimes		    "write error extracting inode %d, name %s\nwrite: %s\n",
7791558Srgrimes			curfile.ino, curfile.name, strerror(errno));
7801558Srgrimes	}
7811558Srgrimes}
7821558Srgrimes
7831558Srgrimes/*
7841558Srgrimes * Skip over a hole in a file.
7851558Srgrimes */
7861558Srgrimes/* ARGSUSED */
7871558Srgrimesstatic void
78892837Simpxtrskip(char *buf, long size)
7891558Srgrimes{
7901558Srgrimes
7911558Srgrimes	if (lseek(ofile, size, SEEK_CUR) == -1) {
7921558Srgrimes		fprintf(stderr,
7931558Srgrimes		    "seek error extracting inode %d, name %s\nlseek: %s\n",
7941558Srgrimes			curfile.ino, curfile.name, strerror(errno));
7951558Srgrimes		done(1);
7961558Srgrimes	}
7971558Srgrimes}
7981558Srgrimes
7991558Srgrimes/*
8001558Srgrimes * Collect the next block of a symbolic link.
8011558Srgrimes */
8021558Srgrimesstatic void
80392837Simpxtrlnkfile(char *buf, long size)
8041558Srgrimes{
8051558Srgrimes
8061558Srgrimes	pathlen += size;
8071558Srgrimes	if (pathlen > MAXPATHLEN) {
8081558Srgrimes		fprintf(stderr, "symbolic link name: %s->%s%s; too long %d\n",
8091558Srgrimes		    curfile.name, lnkbuf, buf, pathlen);
8101558Srgrimes		done(1);
8111558Srgrimes	}
8121558Srgrimes	(void) strcat(lnkbuf, buf);
8131558Srgrimes}
8141558Srgrimes
8151558Srgrimes/*
8161558Srgrimes * Skip over a hole in a symbolic link (should never happen).
8171558Srgrimes */
8181558Srgrimes/* ARGSUSED */
8191558Srgrimesstatic void
82092837Simpxtrlnkskip(char *buf, long size)
8211558Srgrimes{
8221558Srgrimes
8231558Srgrimes	fprintf(stderr, "unallocated block in symbolic link %s\n",
8241558Srgrimes		curfile.name);
8251558Srgrimes	done(1);
8261558Srgrimes}
8271558Srgrimes
8281558Srgrimes/*
8291558Srgrimes * Collect the next block of a bit map.
8301558Srgrimes */
8311558Srgrimesstatic void
83292837Simpxtrmap(char *buf, long size)
8331558Srgrimes{
8341558Srgrimes
83523685Speter	memmove(map, buf, size);
8361558Srgrimes	map += size;
8371558Srgrimes}
8381558Srgrimes
8391558Srgrimes/*
8401558Srgrimes * Skip over a hole in a bit map (should never happen).
8411558Srgrimes */
8421558Srgrimes/* ARGSUSED */
8431558Srgrimesstatic void
84492837Simpxtrmapskip(char *buf, long size)
8451558Srgrimes{
8461558Srgrimes
8471558Srgrimes	panic("hole in map\n");
8481558Srgrimes	map += size;
8491558Srgrimes}
8501558Srgrimes
8511558Srgrimes/*
8521558Srgrimes * Noop, when an extraction function is not needed.
8531558Srgrimes */
8541558Srgrimes/* ARGSUSED */
8551558Srgrimesvoid
85692837Simpxtrnull(char *buf, long size)
8571558Srgrimes{
8581558Srgrimes
8591558Srgrimes	return;
8601558Srgrimes}
8611558Srgrimes
8621558Srgrimes/*
8631558Srgrimes * Read TP_BSIZE blocks from the input.
8641558Srgrimes * Handle read errors, and end of media.
8651558Srgrimes */
8661558Srgrimesstatic void
86792837Simpreadtape(char *buf)
8681558Srgrimes{
8691558Srgrimes	long rd, newvol, i;
8701558Srgrimes	int cnt, seek_failed;
8711558Srgrimes
8721558Srgrimes	if (blkcnt < numtrec) {
87323685Speter		memmove(buf, &tapebuf[(blkcnt++ * TP_BSIZE)], (long)TP_BSIZE);
8741558Srgrimes		blksread++;
87590827Siedowse		tapeaddr++;
8761558Srgrimes		return;
8771558Srgrimes	}
8781558Srgrimes	for (i = 0; i < ntrec; i++)
8791558Srgrimes		((struct s_spcl *)&tapebuf[i * TP_BSIZE])->c_magic = 0;
8801558Srgrimes	if (numtrec == 0)
8811558Srgrimes		numtrec = ntrec;
8821558Srgrimes	cnt = ntrec * TP_BSIZE;
8831558Srgrimes	rd = 0;
8841558Srgrimesgetmore:
8851558Srgrimes#ifdef RRESTORE
8861558Srgrimes	if (host)
8871558Srgrimes		i = rmtread(&tapebuf[rd], cnt);
8881558Srgrimes	else
8891558Srgrimes#endif
8901558Srgrimes		i = read(mt, &tapebuf[rd], cnt);
8911558Srgrimes	/*
8921558Srgrimes	 * Check for mid-tape short read error.
8931558Srgrimes	 * If found, skip rest of buffer and start with the next.
8941558Srgrimes	 */
8951558Srgrimes	if (!pipein && numtrec < ntrec && i > 0) {
8961558Srgrimes		dprintf(stdout, "mid-media short read error.\n");
8971558Srgrimes		numtrec = ntrec;
8981558Srgrimes	}
8991558Srgrimes	/*
9001558Srgrimes	 * Handle partial block read.
9011558Srgrimes	 */
9021558Srgrimes	if (pipein && i == 0 && rd > 0)
9031558Srgrimes		i = rd;
9041558Srgrimes	else if (i > 0 && i != ntrec * TP_BSIZE) {
9051558Srgrimes		if (pipein) {
9061558Srgrimes			rd += i;
9071558Srgrimes			cnt -= i;
9081558Srgrimes			if (cnt > 0)
9091558Srgrimes				goto getmore;
9101558Srgrimes			i = rd;
9111558Srgrimes		} else {
9121558Srgrimes			/*
9131558Srgrimes			 * Short read. Process the blocks read.
9141558Srgrimes			 */
9151558Srgrimes			if (i % TP_BSIZE != 0)
9161558Srgrimes				vprintf(stdout,
91737240Sbde				    "partial block read: %ld should be %ld\n",
9181558Srgrimes				    i, ntrec * TP_BSIZE);
9191558Srgrimes			numtrec = i / TP_BSIZE;
9201558Srgrimes		}
9211558Srgrimes	}
9221558Srgrimes	/*
9231558Srgrimes	 * Handle read error.
9241558Srgrimes	 */
9251558Srgrimes	if (i < 0) {
9261558Srgrimes		fprintf(stderr, "Tape read error while ");
9271558Srgrimes		switch (curfile.action) {
9281558Srgrimes		default:
9291558Srgrimes			fprintf(stderr, "trying to set up tape\n");
9301558Srgrimes			break;
9311558Srgrimes		case UNKNOWN:
9321558Srgrimes			fprintf(stderr, "trying to resynchronize\n");
9331558Srgrimes			break;
9341558Srgrimes		case USING:
9351558Srgrimes			fprintf(stderr, "restoring %s\n", curfile.name);
9361558Srgrimes			break;
9371558Srgrimes		case SKIP:
9381558Srgrimes			fprintf(stderr, "skipping over inode %d\n",
9391558Srgrimes				curfile.ino);
9401558Srgrimes			break;
9411558Srgrimes		}
9421558Srgrimes		if (!yflag && !reply("continue"))
9431558Srgrimes			done(1);
9441558Srgrimes		i = ntrec * TP_BSIZE;
94523685Speter		memset(tapebuf, 0, i);
9461558Srgrimes#ifdef RRESTORE
9471558Srgrimes		if (host)
9481558Srgrimes			seek_failed = (rmtseek(i, 1) < 0);
9491558Srgrimes		else
9501558Srgrimes#endif
9511558Srgrimes			seek_failed = (lseek(mt, i, SEEK_CUR) == (off_t)-1);
9521558Srgrimes
9531558Srgrimes		if (seek_failed) {
9541558Srgrimes			fprintf(stderr,
9551558Srgrimes			    "continuation failed: %s\n", strerror(errno));
9561558Srgrimes			done(1);
9571558Srgrimes		}
9581558Srgrimes	}
9591558Srgrimes	/*
9601558Srgrimes	 * Handle end of tape.
9611558Srgrimes	 */
9621558Srgrimes	if (i == 0) {
9631558Srgrimes		vprintf(stdout, "End-of-tape encountered\n");
9641558Srgrimes		if (!pipein) {
9651558Srgrimes			newvol = volno + 1;
9661558Srgrimes			volno = 0;
9671558Srgrimes			numtrec = 0;
9681558Srgrimes			getvol(newvol);
9691558Srgrimes			readtape(buf);
9701558Srgrimes			return;
9711558Srgrimes		}
9721558Srgrimes		if (rd % TP_BSIZE != 0)
9731558Srgrimes			panic("partial block read: %d should be %d\n",
9741558Srgrimes				rd, ntrec * TP_BSIZE);
9751558Srgrimes		terminateinput();
97623685Speter		memmove(&tapebuf[rd], &endoftapemark, (long)TP_BSIZE);
9771558Srgrimes	}
9781558Srgrimes	blkcnt = 0;
97923685Speter	memmove(buf, &tapebuf[(blkcnt++ * TP_BSIZE)], (long)TP_BSIZE);
9801558Srgrimes	blksread++;
98190827Siedowse	tapeaddr++;
9821558Srgrimes}
9831558Srgrimes
9841558Srgrimesstatic void
98592837Simpfindtapeblksize(void)
9861558Srgrimes{
98792806Sobrien	long i;
9881558Srgrimes
9891558Srgrimes	for (i = 0; i < ntrec; i++)
9901558Srgrimes		((struct s_spcl *)&tapebuf[i * TP_BSIZE])->c_magic = 0;
9911558Srgrimes	blkcnt = 0;
9921558Srgrimes#ifdef RRESTORE
9931558Srgrimes	if (host)
9941558Srgrimes		i = rmtread(tapebuf, ntrec * TP_BSIZE);
9951558Srgrimes	else
9961558Srgrimes#endif
9971558Srgrimes		i = read(mt, tapebuf, ntrec * TP_BSIZE);
9981558Srgrimes
9991558Srgrimes	if (i <= 0) {
10001558Srgrimes		fprintf(stderr, "tape read error: %s\n", strerror(errno));
10011558Srgrimes		done(1);
10021558Srgrimes	}
10031558Srgrimes	if (i % TP_BSIZE != 0) {
100437240Sbde		fprintf(stderr, "Tape block size (%ld) %s (%d)\n",
10051558Srgrimes			i, "is not a multiple of dump block size", TP_BSIZE);
10061558Srgrimes		done(1);
10071558Srgrimes	}
10081558Srgrimes	ntrec = i / TP_BSIZE;
10091558Srgrimes	numtrec = ntrec;
101037240Sbde	vprintf(stdout, "Tape block size is %ld\n", ntrec);
10111558Srgrimes}
10121558Srgrimes
10131558Srgrimesvoid
101492837Simpclosemt(void)
10151558Srgrimes{
10161558Srgrimes
10171558Srgrimes	if (mt < 0)
10181558Srgrimes		return;
1019128175Sgreen	if (pipecmdin) {
1020128175Sgreen		pclose(popenfp);
1021128175Sgreen		popenfp = NULL;
1022128175Sgreen	} else
10231558Srgrimes#ifdef RRESTORE
10241558Srgrimes	if (host)
10251558Srgrimes		rmtclose();
10261558Srgrimes	else
10271558Srgrimes#endif
10281558Srgrimes		(void) close(mt);
10291558Srgrimes}
10301558Srgrimes
10311558Srgrimes/*
10321558Srgrimes * Read the next block from the tape.
10331558Srgrimes * If it is not any valid header, return an error.
10341558Srgrimes */
10351558Srgrimesstatic int
103692837Simpgethead(struct s_spcl *buf)
10371558Srgrimes{
10381558Srgrimes	long i;
10391558Srgrimes
104098542Smckusick	readtape((char *)buf);
104198542Smckusick	if (buf->c_magic != FS_UFS2_MAGIC && buf->c_magic != NFS_MAGIC) {
104298542Smckusick		if (buf->c_magic == OFS_MAGIC) {
104398542Smckusick			fprintf(stderr,
104498542Smckusick			    "Format of dump tape is too old. Must use\n");
104598542Smckusick			fprintf(stderr,
104698542Smckusick			    "a version of restore from before 2002.\n");
104798542Smckusick			return (FAIL);
104898542Smckusick		}
104998542Smckusick		if (swabl(buf->c_magic) != FS_UFS2_MAGIC &&
105098542Smckusick		    buf->c_magic != NFS_MAGIC) {
105198542Smckusick			if (buf->c_magic == OFS_MAGIC) {
105298542Smckusick				fprintf(stderr,
105398542Smckusick				  "Format of dump tape is too old. Must use\n");
105498542Smckusick				fprintf(stderr,
105598542Smckusick				  "a version of restore from before 2002.\n");
10561558Srgrimes			}
10571558Srgrimes			return (FAIL);
105823096Simp		}
105998542Smckusick		if (!Bcvt) {
106098542Smckusick			vprintf(stdout, "Note: Doing Byte swapping\n");
106198542Smckusick			Bcvt = 1;
10621558Srgrimes		}
10631558Srgrimes	}
106498542Smckusick	if (checksum((int *)buf) == FAIL)
106598542Smckusick		return (FAIL);
106698542Smckusick	if (Bcvt) {
106798542Smckusick		swabst((u_char *)"8l4s1q8l2q17l", (u_char *)buf);
106898542Smckusick		swabst((u_char *)"l",(u_char *) &buf->c_level);
106998542Smckusick		swabst((u_char *)"2l4q",(u_char *) &buf->c_flags);
10701558Srgrimes	}
107137923Simp	readmapflag = 0;
10721558Srgrimes
10731558Srgrimes	switch (buf->c_type) {
10741558Srgrimes
10751558Srgrimes	case TS_CLRI:
10761558Srgrimes	case TS_BITS:
10771558Srgrimes		/*
10781558Srgrimes		 * Have to patch up missing information in bit map headers
10791558Srgrimes		 */
10801558Srgrimes		buf->c_inumber = 0;
108198542Smckusick		buf->c_size = buf->c_count * TP_BSIZE;
108237923Simp		if (buf->c_count > TP_NINDIR)
108337923Simp			readmapflag = 1;
108437923Simp		else
108537923Simp			for (i = 0; i < buf->c_count; i++)
108637923Simp				buf->c_addr[i]++;
10871558Srgrimes		break;
10881558Srgrimes
10891558Srgrimes	case TS_TAPE:
1090143819Simp		if (buf->c_magic == NFS_MAGIC) {
1091144099Simp			if ((buf->c_flags & NFS_DR_NEWINODEFMT) == 0)
1092144099Simp				oldinofmt = 1;
1093143819Simp			buf->c_date = _time32_to_time(buf->c_old_date);
1094143819Simp			buf->c_ddate = _time32_to_time(buf->c_old_ddate);
1095144093Simp			buf->c_tapea = buf->c_old_tapea;
1096144093Simp			buf->c_firstrec = buf->c_old_firstrec;
1097143819Simp		}
10981558Srgrimes	case TS_END:
10991558Srgrimes		buf->c_inumber = 0;
11001558Srgrimes		break;
11011558Srgrimes
11021558Srgrimes	case TS_INODE:
110398542Smckusick		/*
110498542Smckusick		 * For old dump tapes, have to copy up old fields to
110598542Smckusick		 * new locations.
110698542Smckusick		 */
110798542Smckusick		if (buf->c_magic == NFS_MAGIC) {
110898542Smckusick			buf->c_tapea = buf->c_old_tapea;
110998542Smckusick			buf->c_firstrec = buf->c_old_firstrec;
111098542Smckusick			buf->c_date = _time32_to_time(buf->c_old_date);
111198542Smckusick			buf->c_ddate = _time32_to_time(buf->c_old_ddate);
111298542Smckusick			buf->c_atime = _time32_to_time(buf->c_old_atime);
111398542Smckusick			buf->c_mtime = _time32_to_time(buf->c_old_mtime);
111498542Smckusick		}
111598542Smckusick		break;
111698542Smckusick
11171558Srgrimes	case TS_ADDR:
11181558Srgrimes		break;
11191558Srgrimes
11201558Srgrimes	default:
11211558Srgrimes		panic("gethead: unknown inode type %d\n", buf->c_type);
11221558Srgrimes		break;
11231558Srgrimes	}
1124144099Simp	/*
1125144099Simp	 * If we're restoring a filesystem with the old (FreeBSD 1)
1126144099Simp	 * format inodes, copy the uid/gid to the new location
1127144099Simp	 */
1128144099Simp	if (oldinofmt) {
1129144099Simp		buf->c_uid = buf->c_spare1[1];
1130144099Simp		buf->c_gid = buf->c_spare1[2];
1131144099Simp	}
113298542Smckusick	buf->c_magic = FS_UFS2_MAGIC;
113390827Siedowse	tapeaddr = buf->c_tapea;
11341558Srgrimes	if (dflag)
11351558Srgrimes		accthdr(buf);
11361558Srgrimes	return(GOOD);
11371558Srgrimes}
11381558Srgrimes
11391558Srgrimes/*
11401558Srgrimes * Check that a header is where it belongs and predict the next header
11411558Srgrimes */
11421558Srgrimesstatic void
114392837Simpaccthdr(struct s_spcl *header)
11441558Srgrimes{
11451558Srgrimes	static ino_t previno = 0x7fffffff;
11461558Srgrimes	static int prevtype;
11471558Srgrimes	static long predict;
11481558Srgrimes	long blks, i;
11491558Srgrimes
11501558Srgrimes	if (header->c_type == TS_TAPE) {
115198542Smckusick		fprintf(stderr, "Volume header ");
11521558Srgrimes 		if (header->c_firstrec)
115398542Smckusick 			fprintf(stderr, "begins with record %qd",
11541558Srgrimes 				header->c_firstrec);
11551558Srgrimes 		fprintf(stderr, "\n");
11561558Srgrimes		previno = 0x7fffffff;
11571558Srgrimes		return;
11581558Srgrimes	}
11591558Srgrimes	if (previno == 0x7fffffff)
11601558Srgrimes		goto newcalc;
11611558Srgrimes	switch (prevtype) {
11621558Srgrimes	case TS_BITS:
116323685Speter		fprintf(stderr, "Dumped inodes map header");
11641558Srgrimes		break;
11651558Srgrimes	case TS_CLRI:
116623685Speter		fprintf(stderr, "Used inodes map header");
11671558Srgrimes		break;
11681558Srgrimes	case TS_INODE:
11691558Srgrimes		fprintf(stderr, "File header, ino %d", previno);
11701558Srgrimes		break;
11711558Srgrimes	case TS_ADDR:
11721558Srgrimes		fprintf(stderr, "File continuation header, ino %d", previno);
11731558Srgrimes		break;
11741558Srgrimes	case TS_END:
11751558Srgrimes		fprintf(stderr, "End of tape header");
11761558Srgrimes		break;
11771558Srgrimes	}
11781558Srgrimes	if (predict != blksread - 1)
117937240Sbde		fprintf(stderr, "; predicted %ld blocks, got %ld blocks",
11801558Srgrimes			predict, blksread - 1);
11811558Srgrimes	fprintf(stderr, "\n");
11821558Srgrimesnewcalc:
11831558Srgrimes	blks = 0;
11841558Srgrimes	if (header->c_type != TS_END)
11851558Srgrimes		for (i = 0; i < header->c_count; i++)
118637923Simp			if (readmapflag || header->c_addr[i] != 0)
11871558Srgrimes				blks++;
11881558Srgrimes	predict = blks;
11891558Srgrimes	blksread = 0;
11901558Srgrimes	prevtype = header->c_type;
11911558Srgrimes	previno = header->c_inumber;
11921558Srgrimes}
11931558Srgrimes
11941558Srgrimes/*
11951558Srgrimes * Find an inode header.
119690573Siedowse * Complain if had to skip.
11971558Srgrimes */
11981558Srgrimesstatic void
119992837Simpfindinode(struct s_spcl *header)
12001558Srgrimes{
12011558Srgrimes	static long skipcnt = 0;
12021558Srgrimes	long i;
12031558Srgrimes	char buf[TP_BSIZE];
120490608Siedowse	int htype;
12051558Srgrimes
12061558Srgrimes	curfile.name = "<name unknown>";
12071558Srgrimes	curfile.action = UNKNOWN;
120898542Smckusick	curfile.mode = 0;
12091558Srgrimes	curfile.ino = 0;
12101558Srgrimes	do {
121190608Siedowse		htype = header->c_type;
121290608Siedowse		switch (htype) {
12131558Srgrimes
12141558Srgrimes		case TS_ADDR:
12151558Srgrimes			/*
12161558Srgrimes			 * Skip up to the beginning of the next record
12171558Srgrimes			 */
12181558Srgrimes			for (i = 0; i < header->c_count; i++)
12191558Srgrimes				if (header->c_addr[i])
12201558Srgrimes					readtape(buf);
12211558Srgrimes			while (gethead(header) == FAIL ||
122298542Smckusick			    _time64_to_time(header->c_date) != dumpdate)
12231558Srgrimes				skipcnt++;
12241558Srgrimes			break;
12251558Srgrimes
12261558Srgrimes		case TS_INODE:
122798542Smckusick			curfile.mode = header->c_mode;
122898542Smckusick			curfile.uid = header->c_uid;
122998542Smckusick			curfile.gid = header->c_gid;
123098542Smckusick			curfile.file_flags = header->c_file_flags;
123198542Smckusick			curfile.rdev = header->c_rdev;
123298542Smckusick			curfile.atime_sec = header->c_atime;
123398542Smckusick			curfile.atime_nsec = header->c_atimensec;
123498542Smckusick			curfile.mtime_sec = header->c_mtime;
123598542Smckusick			curfile.mtime_nsec = header->c_mtimensec;
1236100207Smckusick			curfile.birthtime_sec = header->c_birthtime;
1237100207Smckusick			curfile.birthtime_nsec = header->c_birthtimensec;
123898542Smckusick			curfile.size = header->c_size;
12391558Srgrimes			curfile.ino = header->c_inumber;
12401558Srgrimes			break;
12411558Srgrimes
12421558Srgrimes		case TS_END:
124390820Siedowse			/* If we missed some tapes, get another volume. */
124490820Siedowse			if (tapesread & (tapesread + 1)) {
124590820Siedowse				getvol(0);
124690820Siedowse				continue;
124790820Siedowse			}
12481558Srgrimes			curfile.ino = maxino;
12491558Srgrimes			break;
12501558Srgrimes
12511558Srgrimes		case TS_CLRI:
12521558Srgrimes			curfile.name = "<file removal list>";
12531558Srgrimes			break;
12541558Srgrimes
12551558Srgrimes		case TS_BITS:
12561558Srgrimes			curfile.name = "<file dump list>";
12571558Srgrimes			break;
12581558Srgrimes
12591558Srgrimes		case TS_TAPE:
12601558Srgrimes			panic("unexpected tape header\n");
12611558Srgrimes			/* NOTREACHED */
12621558Srgrimes
12631558Srgrimes		default:
12641558Srgrimes			panic("unknown tape header type %d\n", spcl.c_type);
12651558Srgrimes			/* NOTREACHED */
12661558Srgrimes
12671558Srgrimes		}
126890608Siedowse	} while (htype == TS_ADDR);
12691558Srgrimes	if (skipcnt > 0)
127037240Sbde		fprintf(stderr, "resync restore, skipped %ld blocks\n",
127137240Sbde		    skipcnt);
12721558Srgrimes	skipcnt = 0;
12731558Srgrimes}
12741558Srgrimes
12751558Srgrimesstatic int
127692837Simpchecksum(int *buf)
12771558Srgrimes{
127892806Sobrien	int i, j;
12791558Srgrimes
12801558Srgrimes	j = sizeof(union u_spcl) / sizeof(int);
12811558Srgrimes	i = 0;
128298542Smckusick	if (!Bcvt) {
12831558Srgrimes		do
12841558Srgrimes			i += *buf++;
12851558Srgrimes		while (--j);
12861558Srgrimes	} else {
12871558Srgrimes		/* What happens if we want to read restore tapes
12881558Srgrimes			for a 16bit int machine??? */
12898871Srgrimes		do
12901558Srgrimes			i += swabl(*buf++);
12911558Srgrimes		while (--j);
12921558Srgrimes	}
12938871Srgrimes
12941558Srgrimes	if (i != CHECKSUM) {
12951558Srgrimes		fprintf(stderr, "Checksum error %o, inode %d file %s\n", i,
12961558Srgrimes			curfile.ino, curfile.name);
12971558Srgrimes		return(FAIL);
12981558Srgrimes	}
12991558Srgrimes	return(GOOD);
13001558Srgrimes}
13011558Srgrimes
13021558Srgrimes#ifdef RRESTORE
13031558Srgrimes#include <stdarg.h>
13041558Srgrimes
13051558Srgrimesvoid
13061558Srgrimesmsg(const char *fmt, ...)
13071558Srgrimes{
13081558Srgrimes	va_list ap;
13091558Srgrimes	va_start(ap, fmt);
13101558Srgrimes	(void)vfprintf(stderr, fmt, ap);
13111558Srgrimes	va_end(ap);
13121558Srgrimes}
13131558Srgrimes#endif /* RRESTORE */
13141558Srgrimes
13151558Srgrimesstatic u_char *
131692837Simpswabshort(u_char *sp, int n)
13171558Srgrimes{
13181558Srgrimes	char c;
13191558Srgrimes
13201558Srgrimes	while (--n >= 0) {
13211558Srgrimes		c = sp[0]; sp[0] = sp[1]; sp[1] = c;
13221558Srgrimes		sp += 2;
13231558Srgrimes	}
13241558Srgrimes	return (sp);
13251558Srgrimes}
13261558Srgrimes
13271558Srgrimesstatic u_char *
132892837Simpswablong(u_char *sp, int n)
13291558Srgrimes{
13301558Srgrimes	char c;
13311558Srgrimes
13321558Srgrimes	while (--n >= 0) {
13331558Srgrimes		c = sp[0]; sp[0] = sp[3]; sp[3] = c;
13341558Srgrimes		c = sp[2]; sp[2] = sp[1]; sp[1] = c;
13351558Srgrimes		sp += 4;
13361558Srgrimes	}
13371558Srgrimes	return (sp);
13381558Srgrimes}
13391558Srgrimes
134098542Smckusickstatic u_char *
134198542Smckusickswabquad(u_char *sp, int n)
134298542Smckusick{
134398542Smckusick	char c;
134498542Smckusick
134598542Smckusick	while (--n >= 0) {
134698542Smckusick		c = sp[0]; sp[0] = sp[7]; sp[7] = c;
134798542Smckusick		c = sp[1]; sp[1] = sp[6]; sp[6] = c;
134898542Smckusick		c = sp[2]; sp[2] = sp[5]; sp[5] = c;
134998542Smckusick		c = sp[3]; sp[3] = sp[4]; sp[4] = c;
135098542Smckusick		sp += 8;
135198542Smckusick	}
135298542Smckusick	return (sp);
135398542Smckusick}
135498542Smckusick
13551558Srgrimesvoid
135692837Simpswabst(u_char *cp, u_char *sp)
13571558Srgrimes{
13581558Srgrimes	int n = 0;
13591558Srgrimes
13601558Srgrimes	while (*cp) {
13611558Srgrimes		switch (*cp) {
13621558Srgrimes		case '0': case '1': case '2': case '3': case '4':
13631558Srgrimes		case '5': case '6': case '7': case '8': case '9':
13641558Srgrimes			n = (n * 10) + (*cp++ - '0');
13651558Srgrimes			continue;
13668871Srgrimes
13671558Srgrimes		case 's': case 'w': case 'h':
13681558Srgrimes			if (n == 0)
13691558Srgrimes				n = 1;
13701558Srgrimes			sp = swabshort(sp, n);
13711558Srgrimes			break;
13721558Srgrimes
13731558Srgrimes		case 'l':
13741558Srgrimes			if (n == 0)
13751558Srgrimes				n = 1;
13761558Srgrimes			sp = swablong(sp, n);
13771558Srgrimes			break;
13781558Srgrimes
137998542Smckusick		case 'q':
13801558Srgrimes			if (n == 0)
13811558Srgrimes				n = 1;
138298542Smckusick			sp = swabquad(sp, n);
138398542Smckusick			break;
138498542Smckusick
138598542Smckusick		case 'b':
138698542Smckusick			if (n == 0)
138798542Smckusick				n = 1;
13881558Srgrimes			sp += n;
13891558Srgrimes			break;
139098542Smckusick
139198542Smckusick		default:
139298542Smckusick			fprintf(stderr, "Unknown conversion character: %c\n",
139398542Smckusick			    *cp);
139498542Smckusick			done(0);
139598542Smckusick			break;
13961558Srgrimes		}
13971558Srgrimes		cp++;
13981558Srgrimes		n = 0;
13991558Srgrimes	}
14001558Srgrimes}
14011558Srgrimes
14021558Srgrimesstatic u_long
140392837Simpswabl(u_long x)
14041558Srgrimes{
14051558Srgrimes	swabst((u_char *)"l", (u_char *)&x);
14061558Srgrimes	return (x);
14071558Srgrimes}
1408