tape.c revision 96113
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 * 3. All advertising materials mentioning features or use of this software
191558Srgrimes *    must display the following acknowledgement:
201558Srgrimes *	This product includes software developed by the University of
211558Srgrimes *	California, Berkeley and its contributors.
221558Srgrimes * 4. Neither the name of the University nor the names of its contributors
231558Srgrimes *    may be used to endorse or promote products derived from this software
241558Srgrimes *    without specific prior written permission.
251558Srgrimes *
261558Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
271558Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
281558Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
291558Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
301558Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
311558Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
321558Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
331558Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
341558Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
351558Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
361558Srgrimes * SUCH DAMAGE.
371558Srgrimes */
381558Srgrimes
391558Srgrimes#ifndef lint
4037906Scharnier#if 0
4123685Speterstatic char sccsid[] = "@(#)tape.c	8.9 (Berkeley) 5/1/95";
4237906Scharnier#endif
4337906Scharnierstatic const char rcsid[] =
4450476Speter  "$FreeBSD: head/sbin/restore/tape.c 96113 2002-05-06 15:15:51Z iedowse $";
451558Srgrimes#endif /* not lint */
461558Srgrimes
471558Srgrimes#include <sys/param.h>
481558Srgrimes#include <sys/file.h>
491558Srgrimes#include <sys/mtio.h>
501558Srgrimes#include <sys/stat.h>
5166907Swollman#include <sys/time.h>
521558Srgrimes
531558Srgrimes#include <ufs/ufs/dinode.h>
541558Srgrimes#include <protocols/dumprestore.h>
551558Srgrimes
561558Srgrimes#include <errno.h>
5773986Sobrien#include <paths.h>
581558Srgrimes#include <setjmp.h>
591558Srgrimes#include <stdio.h>
601558Srgrimes#include <stdlib.h>
611558Srgrimes#include <string.h>
6266907Swollman#include <time.h>
631558Srgrimes#include <unistd.h>
641558Srgrimes
651558Srgrimes#include "restore.h"
661558Srgrimes#include "extern.h"
671558Srgrimes
681558Srgrimesstatic long	fssize = MAXBSIZE;
691558Srgrimesstatic int	mt = -1;
701558Srgrimesstatic int	pipein = 0;
7121174Sguidostatic char	*magtape;
721558Srgrimesstatic int	blkcnt;
731558Srgrimesstatic int	numtrec;
741558Srgrimesstatic char	*tapebuf;
751558Srgrimesstatic union	u_spcl endoftapemark;
761558Srgrimesstatic long	blksread;		/* blocks read since last header */
7790827Siedowsestatic long	tapeaddr = 0;		/* current TP_BSIZE tape record */
781558Srgrimesstatic long	tapesread;
791558Srgrimesstatic jmp_buf	restart;
801558Srgrimesstatic int	gettingfile = 0;	/* restart has a valid frame */
811558Srgrimesstatic char	*host = NULL;
821558Srgrimes
831558Srgrimesstatic int	ofile;
841558Srgrimesstatic char	*map;
851558Srgrimesstatic char	lnkbuf[MAXPATHLEN + 1];
861558Srgrimesstatic int	pathlen;
871558Srgrimes
881558Srgrimesint		oldinofmt;	/* old inode format conversion required */
891558Srgrimesint		Bcvt;		/* Swap Bytes (for CCI or sun) */
901558Srgrimesstatic int	Qcvt;		/* Swap quads (for sun) */
911558Srgrimes
921558Srgrimes#define	FLUSHTAPEBUF()	blkcnt = ntrec + 1
931558Srgrimes
9492837Simpstatic void	 accthdr(struct s_spcl *);
9592837Simpstatic int	 checksum(int *);
9692837Simpstatic void	 findinode(struct s_spcl *);
9792837Simpstatic void	 findtapeblksize(void);
9892837Simpstatic int	 gethead(struct s_spcl *);
9992837Simpstatic void	 readtape(char *);
10092837Simpstatic void	 setdumpnum(void);
10192837Simpstatic u_long	 swabl(u_long);
10292837Simpstatic u_char	*swablong(u_char *, int);
10392837Simpstatic u_char	*swabshort(u_char *, int);
10492837Simpstatic void	 terminateinput(void);
10592837Simpstatic void	 xtrfile(char *, long);
10692837Simpstatic void	 xtrlnkfile(char *, long);
10792837Simpstatic void	 xtrlnkskip(char *, long);
10892837Simpstatic void	 xtrmap(char *, long);
10992837Simpstatic void	 xtrmapskip(char *, long);
11092837Simpstatic void	 xtrskip(char *, long);
1111558Srgrimes
11237923Simpstatic int readmapflag;
11337923Simp
1141558Srgrimes/*
1151558Srgrimes * Set up an input source
1161558Srgrimes */
1171558Srgrimesvoid
11892837Simpsetinput(char *source)
1191558Srgrimes{
1201558Srgrimes	FLUSHTAPEBUF();
1211558Srgrimes	if (bflag)
1221558Srgrimes		newtapebuf(ntrec);
1231558Srgrimes	else
1241558Srgrimes		newtapebuf(NTREC > HIGHDENSITYTREC ? NTREC : HIGHDENSITYTREC);
1251558Srgrimes	terminal = stdin;
1261558Srgrimes
1271558Srgrimes#ifdef RRESTORE
12823685Speter	if (strchr(source, ':')) {
1291558Srgrimes		host = source;
13023685Speter		source = strchr(host, ':');
1311558Srgrimes		*source++ = '\0';
1321558Srgrimes		if (rmthost(host) == 0)
1331558Srgrimes			done(1);
1341558Srgrimes	} else
1351558Srgrimes#endif
1361558Srgrimes	if (strcmp(source, "-") == 0) {
1371558Srgrimes		/*
1381558Srgrimes		 * Since input is coming from a pipe we must establish
1391558Srgrimes		 * our own connection to the terminal.
1401558Srgrimes		 */
1411558Srgrimes		terminal = fopen(_PATH_TTY, "r");
1421558Srgrimes		if (terminal == NULL) {
1431558Srgrimes			(void)fprintf(stderr, "cannot open %s: %s\n",
1441558Srgrimes			    _PATH_TTY, strerror(errno));
1451558Srgrimes			terminal = fopen(_PATH_DEVNULL, "r");
1461558Srgrimes			if (terminal == NULL) {
1471558Srgrimes				(void)fprintf(stderr, "cannot open %s: %s\n",
1481558Srgrimes				    _PATH_DEVNULL, strerror(errno));
1491558Srgrimes				done(1);
1501558Srgrimes			}
1511558Srgrimes		}
1521558Srgrimes		pipein++;
1531558Srgrimes	}
1541558Srgrimes	setuid(getuid());	/* no longer need or want root privileges */
15521174Sguido	magtape = strdup(source);
15621174Sguido	if (magtape == NULL) {
15721174Sguido		fprintf(stderr, "Cannot allocate space for magtape buffer\n");
15821174Sguido		done(1);
15921174Sguido	}
1601558Srgrimes}
1611558Srgrimes
1621558Srgrimesvoid
16392837Simpnewtapebuf(long size)
1641558Srgrimes{
16592837Simp	static int tapebufsize = -1;
1661558Srgrimes
1671558Srgrimes	ntrec = size;
1681558Srgrimes	if (size <= tapebufsize)
1691558Srgrimes		return;
1701558Srgrimes	if (tapebuf != NULL)
1711558Srgrimes		free(tapebuf);
1721558Srgrimes	tapebuf = malloc(size * TP_BSIZE);
1731558Srgrimes	if (tapebuf == NULL) {
1741558Srgrimes		fprintf(stderr, "Cannot allocate space for tape buffer\n");
1751558Srgrimes		done(1);
1761558Srgrimes	}
1771558Srgrimes	tapebufsize = size;
1781558Srgrimes}
1791558Srgrimes
1801558Srgrimes/*
1811558Srgrimes * Verify that the tape drive can be accessed and
1821558Srgrimes * that it actually is a dump tape.
1831558Srgrimes */
1841558Srgrimesvoid
18592837Simpsetup(void)
1861558Srgrimes{
1871558Srgrimes	int i, j, *ip;
1881558Srgrimes	struct stat stbuf;
1891558Srgrimes
1901558Srgrimes	vprintf(stdout, "Verify tape and initialize maps\n");
1911558Srgrimes#ifdef RRESTORE
1921558Srgrimes	if (host)
1931558Srgrimes		mt = rmtopen(magtape, 0);
1941558Srgrimes	else
1951558Srgrimes#endif
1961558Srgrimes	if (pipein)
1971558Srgrimes		mt = 0;
1981558Srgrimes	else
1991558Srgrimes		mt = open(magtape, O_RDONLY, 0);
2001558Srgrimes	if (mt < 0) {
2011558Srgrimes		fprintf(stderr, "%s: %s\n", magtape, strerror(errno));
2021558Srgrimes		done(1);
2031558Srgrimes	}
2041558Srgrimes	volno = 1;
2051558Srgrimes	setdumpnum();
2061558Srgrimes	FLUSHTAPEBUF();
2071558Srgrimes	if (!pipein && !bflag)
2081558Srgrimes		findtapeblksize();
2091558Srgrimes	if (gethead(&spcl) == FAIL) {
2101558Srgrimes		blkcnt--; /* push back this block */
2111558Srgrimes		blksread--;
2121558Srgrimes		cvtflag++;
2131558Srgrimes		if (gethead(&spcl) == FAIL) {
2141558Srgrimes			fprintf(stderr, "Tape is not a dump tape\n");
2151558Srgrimes			done(1);
2161558Srgrimes		}
2171558Srgrimes		fprintf(stderr, "Converting to new file system format.\n");
2181558Srgrimes	}
2191558Srgrimes	if (pipein) {
2201558Srgrimes		endoftapemark.s_spcl.c_magic = cvtflag ? OFS_MAGIC : NFS_MAGIC;
2211558Srgrimes		endoftapemark.s_spcl.c_type = TS_END;
2221558Srgrimes		ip = (int *)&endoftapemark;
2231558Srgrimes		j = sizeof(union u_spcl) / sizeof(int);
2241558Srgrimes		i = 0;
2251558Srgrimes		do
2261558Srgrimes			i += *ip++;
2271558Srgrimes		while (--j);
2281558Srgrimes		endoftapemark.s_spcl.c_checksum = CHECKSUM - i;
2291558Srgrimes	}
2301558Srgrimes	if (vflag || command == 't')
2311558Srgrimes		printdumpinfo();
23289572Sdillon	dumptime = _time32_to_time(spcl.c_ddate);
23389572Sdillon	dumpdate = _time32_to_time(spcl.c_date);
2341558Srgrimes	if (stat(".", &stbuf) < 0) {
2351558Srgrimes		fprintf(stderr, "cannot stat .: %s\n", strerror(errno));
2361558Srgrimes		done(1);
2371558Srgrimes	}
23834851Sjkh	if (stbuf.st_blksize > 0 && stbuf.st_blksize < TP_BSIZE )
23934851Sjkh		fssize = TP_BSIZE;
24034851Sjkh	if (stbuf.st_blksize >= TP_BSIZE && stbuf.st_blksize <= MAXBSIZE)
2411558Srgrimes		fssize = stbuf.st_blksize;
2421558Srgrimes	if (((fssize - 1) & fssize) != 0) {
24337240Sbde		fprintf(stderr, "bad block size %ld\n", fssize);
2441558Srgrimes		done(1);
2451558Srgrimes	}
2461558Srgrimes	if (spcl.c_volume != 1) {
2471558Srgrimes		fprintf(stderr, "Tape is not volume 1 of the dump\n");
2481558Srgrimes		done(1);
2491558Srgrimes	}
2501558Srgrimes	if (gethead(&spcl) == FAIL) {
25137240Sbde		dprintf(stdout, "header read failed at %ld blocks\n", blksread);
2521558Srgrimes		panic("no header after volume mark!\n");
2531558Srgrimes	}
2541558Srgrimes	findinode(&spcl);
2551558Srgrimes	if (spcl.c_type != TS_CLRI) {
2561558Srgrimes		fprintf(stderr, "Cannot find file removal list\n");
2571558Srgrimes		done(1);
2581558Srgrimes	}
2591558Srgrimes	maxino = (spcl.c_count * TP_BSIZE * NBBY) + 1;
2601558Srgrimes	dprintf(stdout, "maxino = %d\n", maxino);
2611558Srgrimes	map = calloc((unsigned)1, (unsigned)howmany(maxino, NBBY));
2621558Srgrimes	if (map == NULL)
26323685Speter		panic("no memory for active inode map\n");
26423685Speter	usedinomap = map;
2651558Srgrimes	curfile.action = USING;
2661558Srgrimes	getfile(xtrmap, xtrmapskip);
2671558Srgrimes	if (spcl.c_type != TS_BITS) {
2681558Srgrimes		fprintf(stderr, "Cannot find file dump list\n");
2691558Srgrimes		done(1);
2701558Srgrimes	}
2711558Srgrimes	map = calloc((unsigned)1, (unsigned)howmany(maxino, NBBY));
2721558Srgrimes	if (map == (char *)NULL)
2731558Srgrimes		panic("no memory for file dump list\n");
2741558Srgrimes	dumpmap = map;
2751558Srgrimes	curfile.action = USING;
2761558Srgrimes	getfile(xtrmap, xtrmapskip);
27723685Speter	/*
27823685Speter	 * If there may be whiteout entries on the tape, pretend that the
27923685Speter	 * whiteout inode exists, so that the whiteout entries can be
28023685Speter	 * extracted.
28123685Speter	 */
28223685Speter	if (oldinofmt == 0)
28323685Speter		SETINO(WINO, dumpmap);
28490820Siedowse	/* 'r' restores don't call getvol() for tape 1, so mark it as read. */
28590820Siedowse	if (command == 'r')
28690820Siedowse		tapesread = 1;
2871558Srgrimes}
2881558Srgrimes
2891558Srgrimes/*
2901558Srgrimes * Prompt user to load a new dump volume.
2911558Srgrimes * "Nextvol" is the next suggested volume to use.
2921558Srgrimes * This suggested volume is enforced when doing full
29337906Scharnier * or incremental restores, but can be overridden by
2941558Srgrimes * the user when only extracting a subset of the files.
2951558Srgrimes */
2961558Srgrimesvoid
29792837Simpgetvol(long nextvol)
2981558Srgrimes{
29990827Siedowse	long newvol, prevtapea, savecnt, i;
3001558Srgrimes	union u_spcl tmpspcl;
3011558Srgrimes#	define tmpbuf tmpspcl.s_spcl
3021558Srgrimes	char buf[TP_BSIZE];
3031558Srgrimes
3041558Srgrimes	if (nextvol == 1) {
3051558Srgrimes		tapesread = 0;
3061558Srgrimes		gettingfile = 0;
3071558Srgrimes	}
30890827Siedowse	prevtapea = tapeaddr;
30990827Siedowse	savecnt = blksread;
3101558Srgrimes	if (pipein) {
31169906Siedowse		if (nextvol != 1) {
3121558Srgrimes			panic("Changing volumes on pipe input?\n");
31369906Siedowse			/* Avoid looping if we couldn't ask the user. */
31469906Siedowse			if (yflag || ferror(terminal) || feof(terminal))
31569906Siedowse				done(1);
31669906Siedowse		}
3171558Srgrimes		if (volno == 1)
3181558Srgrimes			return;
3191558Srgrimes		goto gethdr;
3201558Srgrimes	}
3211558Srgrimesagain:
3221558Srgrimes	if (pipein)
3231558Srgrimes		done(1); /* pipes do not get a second chance */
32490608Siedowse	if (command == 'R' || command == 'r' || curfile.action != SKIP)
3251558Srgrimes		newvol = nextvol;
32690608Siedowse	else
3271558Srgrimes		newvol = 0;
3281558Srgrimes	while (newvol <= 0) {
3291558Srgrimes		if (tapesread == 0) {
33090820Siedowse			fprintf(stderr, "%s%s%s%s%s%s%s",
3311558Srgrimes			    "You have not read any tapes yet.\n",
33290820Siedowse			    "If you are extracting just a few files,",
33390820Siedowse			    " start with the last volume\n",
33490820Siedowse			    "and work towards the first; restore",
33590820Siedowse			    " can quickly skip tapes that\n",
33690820Siedowse			    "have no further files to extract.",
33790820Siedowse			    " Otherwise, begin with volume 1.\n");
3381558Srgrimes		} else {
3391558Srgrimes			fprintf(stderr, "You have read volumes");
3401558Srgrimes			strcpy(buf, ": ");
34190820Siedowse			for (i = 0; i < 32; i++)
3421558Srgrimes				if (tapesread & (1 << i)) {
34390820Siedowse					fprintf(stderr, "%s%ld", buf, i + 1);
3441558Srgrimes					strcpy(buf, ", ");
3451558Srgrimes				}
3461558Srgrimes			fprintf(stderr, "\n");
3471558Srgrimes		}
3481558Srgrimes		do	{
3491558Srgrimes			fprintf(stderr, "Specify next volume #: ");
3501558Srgrimes			(void) fflush(stderr);
35169906Siedowse			if (fgets(buf, BUFSIZ, terminal) == NULL)
35269906Siedowse				done(1);
35369906Siedowse		} while (buf[0] == '\n');
3541558Srgrimes		newvol = atoi(buf);
3551558Srgrimes		if (newvol <= 0) {
3561558Srgrimes			fprintf(stderr,
3571558Srgrimes			    "Volume numbers are positive numerics\n");
3581558Srgrimes		}
3591558Srgrimes	}
3601558Srgrimes	if (newvol == volno) {
36190820Siedowse		tapesread |= 1 << (volno - 1);
3621558Srgrimes		return;
3631558Srgrimes	}
3641558Srgrimes	closemt();
36537240Sbde	fprintf(stderr, "Mount tape volume %ld\n", newvol);
3661558Srgrimes	fprintf(stderr, "Enter ``none'' if there are no more tapes\n");
3671558Srgrimes	fprintf(stderr, "otherwise enter tape name (default: %s) ", magtape);
3681558Srgrimes	(void) fflush(stderr);
36969906Siedowse	if (fgets(buf, BUFSIZ, terminal) == NULL)
3701558Srgrimes		done(1);
3711558Srgrimes	if (!strcmp(buf, "none\n")) {
3721558Srgrimes		terminateinput();
3731558Srgrimes		return;
3741558Srgrimes	}
3751558Srgrimes	if (buf[0] != '\n') {
3761558Srgrimes		(void) strcpy(magtape, buf);
3771558Srgrimes		magtape[strlen(magtape) - 1] = '\0';
3781558Srgrimes	}
3791558Srgrimes#ifdef RRESTORE
3801558Srgrimes	if (host)
3811558Srgrimes		mt = rmtopen(magtape, 0);
3821558Srgrimes	else
3831558Srgrimes#endif
3841558Srgrimes		mt = open(magtape, O_RDONLY, 0);
3851558Srgrimes
3861558Srgrimes	if (mt == -1) {
3871558Srgrimes		fprintf(stderr, "Cannot open %s\n", magtape);
3881558Srgrimes		volno = -1;
3891558Srgrimes		goto again;
3901558Srgrimes	}
3911558Srgrimesgethdr:
3921558Srgrimes	volno = newvol;
3931558Srgrimes	setdumpnum();
3941558Srgrimes	FLUSHTAPEBUF();
3951558Srgrimes	if (gethead(&tmpbuf) == FAIL) {
39637240Sbde		dprintf(stdout, "header read failed at %ld blocks\n", blksread);
3971558Srgrimes		fprintf(stderr, "tape is not dump tape\n");
3981558Srgrimes		volno = 0;
3991558Srgrimes		goto again;
4001558Srgrimes	}
4011558Srgrimes	if (tmpbuf.c_volume != volno) {
40237240Sbde		fprintf(stderr, "Wrong volume (%ld)\n", tmpbuf.c_volume);
4031558Srgrimes		volno = 0;
4041558Srgrimes		goto again;
4051558Srgrimes	}
40689572Sdillon	if (_time32_to_time(tmpbuf.c_date) != dumpdate ||
40789572Sdillon	    _time32_to_time(tmpbuf.c_ddate) != dumptime) {
40889572Sdillon		time_t t = _time32_to_time(tmpbuf.c_date);
40985635Sdillon		fprintf(stderr, "Wrong dump date\n\tgot: %s", ctime(&t));
4101558Srgrimes		fprintf(stderr, "\twanted: %s", ctime(&dumpdate));
4111558Srgrimes		volno = 0;
4121558Srgrimes		goto again;
4131558Srgrimes	}
41490820Siedowse	tapesread |= 1 << (volno - 1);
4151558Srgrimes	blksread = savecnt;
4161558Srgrimes 	/*
4171558Srgrimes 	 * If continuing from the previous volume, skip over any
4181558Srgrimes 	 * blocks read already at the end of the previous volume.
4191558Srgrimes 	 *
4201558Srgrimes 	 * If coming to this volume at random, skip to the beginning
4211558Srgrimes 	 * of the next record.
4221558Srgrimes 	 */
42390827Siedowse	dprintf(stdout, "last rec %ld, tape starts with %ld\n", prevtapea,
42490827Siedowse	    tmpbuf.c_tapea);
4251558Srgrimes 	if (tmpbuf.c_type == TS_TAPE && (tmpbuf.c_flags & DR_NEWHEADER)) {
42690608Siedowse 		if (curfile.action != USING) {
42790608Siedowse			/*
42890608Siedowse			 * XXX Dump incorrectly sets c_count to 1 in the
42990608Siedowse			 * volume header of the first tape, so ignore
43090608Siedowse			 * c_count when volno == 1.
43190608Siedowse			 */
43290608Siedowse			if (volno != 1)
43390608Siedowse				for (i = tmpbuf.c_count; i > 0; i--)
43490608Siedowse					readtape(buf);
43590827Siedowse 		} else if (tmpbuf.c_tapea <= prevtapea) {
4361558Srgrimes			/*
43790827Siedowse			 * Normally the value of c_tapea in the volume
43890827Siedowse			 * header is the record number of the header itself.
43990827Siedowse			 * However in the volume header following an EOT-
44090827Siedowse			 * terminated tape, it is the record number of the
44190827Siedowse			 * first continuation data block (dump bug?).
44290827Siedowse			 *
44390827Siedowse			 * The next record we want is `prevtapea + 1'.
4441558Srgrimes			 */
44590827Siedowse 			i = prevtapea + 1 - tmpbuf.c_tapea;
44637240Sbde			dprintf(stderr, "Skipping %ld duplicate record%s.\n",
4471558Srgrimes				i, i > 1 ? "s" : "");
4481558Srgrimes 			while (--i >= 0)
4491558Srgrimes 				readtape(buf);
4501558Srgrimes 		}
4511558Srgrimes 	}
45290608Siedowse	if (curfile.action == USING) {
4531558Srgrimes		if (volno == 1)
4541558Srgrimes			panic("active file into volume 1\n");
4551558Srgrimes		return;
4561558Srgrimes	}
4571558Srgrimes	(void) gethead(&spcl);
4581558Srgrimes	findinode(&spcl);
4591558Srgrimes	if (gettingfile) {
4601558Srgrimes		gettingfile = 0;
4611558Srgrimes		longjmp(restart, 1);
4621558Srgrimes	}
4631558Srgrimes}
4641558Srgrimes
4651558Srgrimes/*
4661558Srgrimes * Handle unexpected EOF.
4671558Srgrimes */
4681558Srgrimesstatic void
46992837Simpterminateinput(void)
4701558Srgrimes{
4711558Srgrimes
4721558Srgrimes	if (gettingfile && curfile.action == USING) {
4731558Srgrimes		printf("Warning: %s %s\n",
4741558Srgrimes		    "End-of-input encountered while extracting", curfile.name);
4751558Srgrimes	}
4761558Srgrimes	curfile.name = "<name unknown>";
4771558Srgrimes	curfile.action = UNKNOWN;
4781558Srgrimes	curfile.dip = NULL;
4791558Srgrimes	curfile.ino = maxino;
4801558Srgrimes	if (gettingfile) {
4811558Srgrimes		gettingfile = 0;
4821558Srgrimes		longjmp(restart, 1);
4831558Srgrimes	}
4841558Srgrimes}
4851558Srgrimes
4861558Srgrimes/*
4871558Srgrimes * handle multiple dumps per tape by skipping forward to the
4881558Srgrimes * appropriate one.
4891558Srgrimes */
4901558Srgrimesstatic void
49192837Simpsetdumpnum(void)
4921558Srgrimes{
4931558Srgrimes	struct mtop tcom;
4941558Srgrimes
4951558Srgrimes	if (dumpnum == 1 || volno != 1)
4961558Srgrimes		return;
4971558Srgrimes	if (pipein) {
4981558Srgrimes		fprintf(stderr, "Cannot have multiple dumps on pipe input\n");
4991558Srgrimes		done(1);
5001558Srgrimes	}
5011558Srgrimes	tcom.mt_op = MTFSF;
5021558Srgrimes	tcom.mt_count = dumpnum - 1;
5031558Srgrimes#ifdef RRESTORE
5041558Srgrimes	if (host)
5051558Srgrimes		rmtioctl(MTFSF, dumpnum - 1);
5068871Srgrimes	else
5071558Srgrimes#endif
50865786Smjacob		if (ioctl(mt, MTIOCTOP, (char *)&tcom) < 0)
5091558Srgrimes			fprintf(stderr, "ioctl MTFSF: %s\n", strerror(errno));
5101558Srgrimes}
5111558Srgrimes
5121558Srgrimesvoid
51392837Simpprintdumpinfo(void)
5141558Srgrimes{
51585635Sdillon	time_t t;
51689572Sdillon	t = _time32_to_time(spcl.c_date);
51785635Sdillon	fprintf(stdout, "Dump   date: %s", ctime(&t));
51889572Sdillon	t = _time32_to_time(spcl.c_ddate);
5191558Srgrimes	fprintf(stdout, "Dumped from: %s",
52085635Sdillon	    (spcl.c_ddate == 0) ? "the epoch\n" : ctime(&t));
5211558Srgrimes	if (spcl.c_host[0] == '\0')
5221558Srgrimes		return;
52337240Sbde	fprintf(stderr, "Level %ld dump of %s on %s:%s\n",
5241558Srgrimes		spcl.c_level, spcl.c_filesys, spcl.c_host, spcl.c_dev);
5251558Srgrimes	fprintf(stderr, "Label: %s\n", spcl.c_label);
5261558Srgrimes}
5271558Srgrimes
5281558Srgrimesint
52992837Simpextractfile(char *name)
5301558Srgrimes{
53123685Speter	int flags;
53296113Siedowse	uid_t uid;
53396113Siedowse	gid_t gid;
53423685Speter	mode_t mode;
5351558Srgrimes	struct timeval timep[2];
5361558Srgrimes	struct entry *ep;
5371558Srgrimes
5381558Srgrimes	curfile.name = name;
5391558Srgrimes	curfile.action = USING;
54023685Speter	timep[0].tv_sec = curfile.dip->di_atime;
54123685Speter	timep[0].tv_usec = curfile.dip->di_atimensec / 1000;
54223685Speter	timep[1].tv_sec = curfile.dip->di_mtime;
54323685Speter	timep[1].tv_usec = curfile.dip->di_mtimensec / 1000;
54496113Siedowse	uid = curfile.dip->di_uid;
54596113Siedowse	gid = curfile.dip->di_gid;
5461558Srgrimes	mode = curfile.dip->di_mode;
54723685Speter	flags = curfile.dip->di_flags;
5481558Srgrimes	switch (mode & IFMT) {
5491558Srgrimes
5501558Srgrimes	default:
5511558Srgrimes		fprintf(stderr, "%s: unknown file mode 0%o\n", name, mode);
5521558Srgrimes		skipfile();
5531558Srgrimes		return (FAIL);
5541558Srgrimes
5551558Srgrimes	case IFSOCK:
5561558Srgrimes		vprintf(stdout, "skipped socket %s\n", name);
5571558Srgrimes		skipfile();
5581558Srgrimes		return (GOOD);
5591558Srgrimes
5601558Srgrimes	case IFDIR:
5611558Srgrimes		if (mflag) {
5621558Srgrimes			ep = lookupname(name);
5631558Srgrimes			if (ep == NULL || ep->e_flags & EXTRACT)
5641558Srgrimes				panic("unextracted directory %s\n", name);
5651558Srgrimes			skipfile();
5661558Srgrimes			return (GOOD);
5671558Srgrimes		}
5681558Srgrimes		vprintf(stdout, "extract file %s\n", name);
5691558Srgrimes		return (genliteraldir(name, curfile.ino));
5701558Srgrimes
5711558Srgrimes	case IFLNK:
5721558Srgrimes		lnkbuf[0] = '\0';
5731558Srgrimes		pathlen = 0;
5741558Srgrimes		getfile(xtrlnkfile, xtrlnkskip);
5751558Srgrimes		if (pathlen == 0) {
5761558Srgrimes			vprintf(stdout,
5771558Srgrimes			    "%s: zero length symbolic link (ignored)\n", name);
5781558Srgrimes			return (GOOD);
5791558Srgrimes		}
58096113Siedowse		if (linkit(lnkbuf, name, SYMLINK) == GOOD) {
58196113Siedowse			(void) lchown(name, uid, gid);
58296113Siedowse			(void) lchmod(name, mode);
58396113Siedowse			(void) lutimes(name, timep);
58496113Siedowse			return (GOOD);
58595943Siedowse		}
58696113Siedowse		return (FAIL);
5871558Srgrimes
5886305Smartin	case IFIFO:
58923685Speter		vprintf(stdout, "extract fifo %s\n", name);
59023685Speter		if (Nflag) {
59123685Speter			skipfile();
59223685Speter			return (GOOD);
59323685Speter		}
59435852Sjkh		if (uflag && !Nflag)
59535852Sjkh			(void)unlink(name);
5966305Smartin		if (mkfifo(name, mode) < 0) {
59723685Speter			fprintf(stderr, "%s: cannot create fifo: %s\n",
59823685Speter			    name, strerror(errno));
5996305Smartin			skipfile();
6006305Smartin			return (FAIL);
6016305Smartin		}
60296113Siedowse		(void) chown(name, uid, gid);
6036305Smartin		(void) chmod(name, mode);
60496113Siedowse		(void) utimes(name, timep);
60523685Speter		(void) chflags(name, flags);
6066305Smartin		skipfile();
6076305Smartin		return (GOOD);
6086305Smartin
6091558Srgrimes	case IFCHR:
6101558Srgrimes	case IFBLK:
6111558Srgrimes		vprintf(stdout, "extract special file %s\n", name);
6121558Srgrimes		if (Nflag) {
6131558Srgrimes			skipfile();
6141558Srgrimes			return (GOOD);
6151558Srgrimes		}
61635852Sjkh		if (uflag)
61735852Sjkh			(void)unlink(name);
6181558Srgrimes		if (mknod(name, mode, (int)curfile.dip->di_rdev) < 0) {
6191558Srgrimes			fprintf(stderr, "%s: cannot create special file: %s\n",
6201558Srgrimes			    name, strerror(errno));
6211558Srgrimes			skipfile();
6221558Srgrimes			return (FAIL);
6231558Srgrimes		}
62496113Siedowse		(void) chown(name, uid, gid);
6251558Srgrimes		(void) chmod(name, mode);
62696113Siedowse		(void) utimes(name, timep);
62723685Speter		(void) chflags(name, flags);
6281558Srgrimes		skipfile();
6291558Srgrimes		return (GOOD);
6301558Srgrimes
6311558Srgrimes	case IFREG:
6321558Srgrimes		vprintf(stdout, "extract file %s\n", name);
6331558Srgrimes		if (Nflag) {
6341558Srgrimes			skipfile();
6351558Srgrimes			return (GOOD);
6361558Srgrimes		}
63735852Sjkh		if (uflag)
63835852Sjkh			(void)unlink(name);
63921149Simp		if ((ofile = open(name, O_WRONLY | O_CREAT | O_TRUNC,
64021149Simp		    0666)) < 0) {
6411558Srgrimes			fprintf(stderr, "%s: cannot create file: %s\n",
6421558Srgrimes			    name, strerror(errno));
6431558Srgrimes			skipfile();
6441558Srgrimes			return (FAIL);
6451558Srgrimes		}
64696113Siedowse		(void) fchown(ofile, uid, gid);
6471558Srgrimes		(void) fchmod(ofile, mode);
6481558Srgrimes		getfile(xtrfile, xtrskip);
6491558Srgrimes		(void) close(ofile);
6501558Srgrimes		utimes(name, timep);
65163283Sdwmalone		(void) chflags(name, flags);
6521558Srgrimes		return (GOOD);
6531558Srgrimes	}
6541558Srgrimes	/* NOTREACHED */
6551558Srgrimes}
6561558Srgrimes
6571558Srgrimes/*
6581558Srgrimes * skip over bit maps on the tape
6591558Srgrimes */
6601558Srgrimesvoid
66192837Simpskipmaps(void)
6621558Srgrimes{
6631558Srgrimes
6641558Srgrimes	while (spcl.c_type == TS_BITS || spcl.c_type == TS_CLRI)
6651558Srgrimes		skipfile();
6661558Srgrimes}
6671558Srgrimes
6681558Srgrimes/*
6691558Srgrimes * skip over a file on the tape
6701558Srgrimes */
6711558Srgrimesvoid
67292837Simpskipfile(void)
6731558Srgrimes{
6741558Srgrimes
6751558Srgrimes	curfile.action = SKIP;
6761558Srgrimes	getfile(xtrnull, xtrnull);
6771558Srgrimes}
6781558Srgrimes
6791558Srgrimes/*
6801558Srgrimes * Extract a file from the tape.
6811558Srgrimes * When an allocated block is found it is passed to the fill function;
6821558Srgrimes * when an unallocated block (hole) is found, a zeroed buffer is passed
6831558Srgrimes * to the skip function.
6841558Srgrimes */
6851558Srgrimesvoid
68692837Simpgetfile(void (*fill)(char *, long), void (*skip)(char *, long))
6871558Srgrimes{
68892806Sobrien	int i;
6891558Srgrimes	int curblk = 0;
69023685Speter	quad_t size = spcl.c_dinode.di_size;
6911558Srgrimes	static char clearedbuf[MAXBSIZE];
6921558Srgrimes	char buf[MAXBSIZE / TP_BSIZE][TP_BSIZE];
6931558Srgrimes	char junk[TP_BSIZE];
6941558Srgrimes
6951558Srgrimes	if (spcl.c_type == TS_END)
6961558Srgrimes		panic("ran off end of tape\n");
6971558Srgrimes	if (spcl.c_magic != NFS_MAGIC)
6981558Srgrimes		panic("not at beginning of a file\n");
6991558Srgrimes	if (!gettingfile && setjmp(restart) != 0)
7001558Srgrimes		return;
7011558Srgrimes	gettingfile++;
7021558Srgrimesloop:
7031558Srgrimes	for (i = 0; i < spcl.c_count; i++) {
70437923Simp		if (readmapflag || spcl.c_addr[i]) {
7051558Srgrimes			readtape(&buf[curblk++][0]);
7061558Srgrimes			if (curblk == fssize / TP_BSIZE) {
70723685Speter				(*fill)((char *)buf, (long)(size > TP_BSIZE ?
70823685Speter				     fssize : (curblk - 1) * TP_BSIZE + size));
7091558Srgrimes				curblk = 0;
7101558Srgrimes			}
7111558Srgrimes		} else {
7121558Srgrimes			if (curblk > 0) {
71323685Speter				(*fill)((char *)buf, (long)(size > TP_BSIZE ?
71423685Speter				     curblk * TP_BSIZE :
71523685Speter				     (curblk - 1) * TP_BSIZE + size));
7161558Srgrimes				curblk = 0;
7171558Srgrimes			}
71823685Speter			(*skip)(clearedbuf, (long)(size > TP_BSIZE ?
71923685Speter				TP_BSIZE : size));
7201558Srgrimes		}
7211558Srgrimes		if ((size -= TP_BSIZE) <= 0) {
7221558Srgrimes			for (i++; i < spcl.c_count; i++)
72337923Simp				if (readmapflag || spcl.c_addr[i])
7241558Srgrimes					readtape(junk);
7251558Srgrimes			break;
7261558Srgrimes		}
7271558Srgrimes	}
7281558Srgrimes	if (gethead(&spcl) == GOOD && size > 0) {
7291558Srgrimes		if (spcl.c_type == TS_ADDR)
7301558Srgrimes			goto loop;
7311558Srgrimes		dprintf(stdout,
73237240Sbde			"Missing address (header) block for %s at %ld blocks\n",
7331558Srgrimes			curfile.name, blksread);
7341558Srgrimes	}
7351558Srgrimes	if (curblk > 0)
73623685Speter		(*fill)((char *)buf, (long)((curblk * TP_BSIZE) + size));
7371558Srgrimes	findinode(&spcl);
7381558Srgrimes	gettingfile = 0;
7391558Srgrimes}
7401558Srgrimes
7411558Srgrimes/*
7421558Srgrimes * Write out the next block of a file.
7431558Srgrimes */
7441558Srgrimesstatic void
74592837Simpxtrfile(char *buf, long	size)
7461558Srgrimes{
7471558Srgrimes
7481558Srgrimes	if (Nflag)
7491558Srgrimes		return;
7501558Srgrimes	if (write(ofile, buf, (int) size) == -1) {
7511558Srgrimes		fprintf(stderr,
7521558Srgrimes		    "write error extracting inode %d, name %s\nwrite: %s\n",
7531558Srgrimes			curfile.ino, curfile.name, strerror(errno));
7541558Srgrimes	}
7551558Srgrimes}
7561558Srgrimes
7571558Srgrimes/*
7581558Srgrimes * Skip over a hole in a file.
7591558Srgrimes */
7601558Srgrimes/* ARGSUSED */
7611558Srgrimesstatic void
76292837Simpxtrskip(char *buf, long size)
7631558Srgrimes{
7641558Srgrimes
7651558Srgrimes	if (lseek(ofile, size, SEEK_CUR) == -1) {
7661558Srgrimes		fprintf(stderr,
7671558Srgrimes		    "seek error extracting inode %d, name %s\nlseek: %s\n",
7681558Srgrimes			curfile.ino, curfile.name, strerror(errno));
7691558Srgrimes		done(1);
7701558Srgrimes	}
7711558Srgrimes}
7721558Srgrimes
7731558Srgrimes/*
7741558Srgrimes * Collect the next block of a symbolic link.
7751558Srgrimes */
7761558Srgrimesstatic void
77792837Simpxtrlnkfile(char *buf, long size)
7781558Srgrimes{
7791558Srgrimes
7801558Srgrimes	pathlen += size;
7811558Srgrimes	if (pathlen > MAXPATHLEN) {
7821558Srgrimes		fprintf(stderr, "symbolic link name: %s->%s%s; too long %d\n",
7831558Srgrimes		    curfile.name, lnkbuf, buf, pathlen);
7841558Srgrimes		done(1);
7851558Srgrimes	}
7861558Srgrimes	(void) strcat(lnkbuf, buf);
7871558Srgrimes}
7881558Srgrimes
7891558Srgrimes/*
7901558Srgrimes * Skip over a hole in a symbolic link (should never happen).
7911558Srgrimes */
7921558Srgrimes/* ARGSUSED */
7931558Srgrimesstatic void
79492837Simpxtrlnkskip(char *buf, long size)
7951558Srgrimes{
7961558Srgrimes
7971558Srgrimes	fprintf(stderr, "unallocated block in symbolic link %s\n",
7981558Srgrimes		curfile.name);
7991558Srgrimes	done(1);
8001558Srgrimes}
8011558Srgrimes
8021558Srgrimes/*
8031558Srgrimes * Collect the next block of a bit map.
8041558Srgrimes */
8051558Srgrimesstatic void
80692837Simpxtrmap(char *buf, long size)
8071558Srgrimes{
8081558Srgrimes
80923685Speter	memmove(map, buf, size);
8101558Srgrimes	map += size;
8111558Srgrimes}
8121558Srgrimes
8131558Srgrimes/*
8141558Srgrimes * Skip over a hole in a bit map (should never happen).
8151558Srgrimes */
8161558Srgrimes/* ARGSUSED */
8171558Srgrimesstatic void
81892837Simpxtrmapskip(char *buf, long size)
8191558Srgrimes{
8201558Srgrimes
8211558Srgrimes	panic("hole in map\n");
8221558Srgrimes	map += size;
8231558Srgrimes}
8241558Srgrimes
8251558Srgrimes/*
8261558Srgrimes * Noop, when an extraction function is not needed.
8271558Srgrimes */
8281558Srgrimes/* ARGSUSED */
8291558Srgrimesvoid
83092837Simpxtrnull(char *buf, long size)
8311558Srgrimes{
8321558Srgrimes
8331558Srgrimes	return;
8341558Srgrimes}
8351558Srgrimes
8361558Srgrimes/*
8371558Srgrimes * Read TP_BSIZE blocks from the input.
8381558Srgrimes * Handle read errors, and end of media.
8391558Srgrimes */
8401558Srgrimesstatic void
84192837Simpreadtape(char *buf)
8421558Srgrimes{
8431558Srgrimes	long rd, newvol, i;
8441558Srgrimes	int cnt, seek_failed;
8451558Srgrimes
8461558Srgrimes	if (blkcnt < numtrec) {
84723685Speter		memmove(buf, &tapebuf[(blkcnt++ * TP_BSIZE)], (long)TP_BSIZE);
8481558Srgrimes		blksread++;
84990827Siedowse		tapeaddr++;
8501558Srgrimes		return;
8511558Srgrimes	}
8521558Srgrimes	for (i = 0; i < ntrec; i++)
8531558Srgrimes		((struct s_spcl *)&tapebuf[i * TP_BSIZE])->c_magic = 0;
8541558Srgrimes	if (numtrec == 0)
8551558Srgrimes		numtrec = ntrec;
8561558Srgrimes	cnt = ntrec * TP_BSIZE;
8571558Srgrimes	rd = 0;
8581558Srgrimesgetmore:
8591558Srgrimes#ifdef RRESTORE
8601558Srgrimes	if (host)
8611558Srgrimes		i = rmtread(&tapebuf[rd], cnt);
8621558Srgrimes	else
8631558Srgrimes#endif
8641558Srgrimes		i = read(mt, &tapebuf[rd], cnt);
8651558Srgrimes	/*
8661558Srgrimes	 * Check for mid-tape short read error.
8671558Srgrimes	 * If found, skip rest of buffer and start with the next.
8681558Srgrimes	 */
8691558Srgrimes	if (!pipein && numtrec < ntrec && i > 0) {
8701558Srgrimes		dprintf(stdout, "mid-media short read error.\n");
8711558Srgrimes		numtrec = ntrec;
8721558Srgrimes	}
8731558Srgrimes	/*
8741558Srgrimes	 * Handle partial block read.
8751558Srgrimes	 */
8761558Srgrimes	if (pipein && i == 0 && rd > 0)
8771558Srgrimes		i = rd;
8781558Srgrimes	else if (i > 0 && i != ntrec * TP_BSIZE) {
8791558Srgrimes		if (pipein) {
8801558Srgrimes			rd += i;
8811558Srgrimes			cnt -= i;
8821558Srgrimes			if (cnt > 0)
8831558Srgrimes				goto getmore;
8841558Srgrimes			i = rd;
8851558Srgrimes		} else {
8861558Srgrimes			/*
8871558Srgrimes			 * Short read. Process the blocks read.
8881558Srgrimes			 */
8891558Srgrimes			if (i % TP_BSIZE != 0)
8901558Srgrimes				vprintf(stdout,
89137240Sbde				    "partial block read: %ld should be %ld\n",
8921558Srgrimes				    i, ntrec * TP_BSIZE);
8931558Srgrimes			numtrec = i / TP_BSIZE;
8941558Srgrimes		}
8951558Srgrimes	}
8961558Srgrimes	/*
8971558Srgrimes	 * Handle read error.
8981558Srgrimes	 */
8991558Srgrimes	if (i < 0) {
9001558Srgrimes		fprintf(stderr, "Tape read error while ");
9011558Srgrimes		switch (curfile.action) {
9021558Srgrimes		default:
9031558Srgrimes			fprintf(stderr, "trying to set up tape\n");
9041558Srgrimes			break;
9051558Srgrimes		case UNKNOWN:
9061558Srgrimes			fprintf(stderr, "trying to resynchronize\n");
9071558Srgrimes			break;
9081558Srgrimes		case USING:
9091558Srgrimes			fprintf(stderr, "restoring %s\n", curfile.name);
9101558Srgrimes			break;
9111558Srgrimes		case SKIP:
9121558Srgrimes			fprintf(stderr, "skipping over inode %d\n",
9131558Srgrimes				curfile.ino);
9141558Srgrimes			break;
9151558Srgrimes		}
9161558Srgrimes		if (!yflag && !reply("continue"))
9171558Srgrimes			done(1);
9181558Srgrimes		i = ntrec * TP_BSIZE;
91923685Speter		memset(tapebuf, 0, i);
9201558Srgrimes#ifdef RRESTORE
9211558Srgrimes		if (host)
9221558Srgrimes			seek_failed = (rmtseek(i, 1) < 0);
9231558Srgrimes		else
9241558Srgrimes#endif
9251558Srgrimes			seek_failed = (lseek(mt, i, SEEK_CUR) == (off_t)-1);
9261558Srgrimes
9271558Srgrimes		if (seek_failed) {
9281558Srgrimes			fprintf(stderr,
9291558Srgrimes			    "continuation failed: %s\n", strerror(errno));
9301558Srgrimes			done(1);
9311558Srgrimes		}
9321558Srgrimes	}
9331558Srgrimes	/*
9341558Srgrimes	 * Handle end of tape.
9351558Srgrimes	 */
9361558Srgrimes	if (i == 0) {
9371558Srgrimes		vprintf(stdout, "End-of-tape encountered\n");
9381558Srgrimes		if (!pipein) {
9391558Srgrimes			newvol = volno + 1;
9401558Srgrimes			volno = 0;
9411558Srgrimes			numtrec = 0;
9421558Srgrimes			getvol(newvol);
9431558Srgrimes			readtape(buf);
9441558Srgrimes			return;
9451558Srgrimes		}
9461558Srgrimes		if (rd % TP_BSIZE != 0)
9471558Srgrimes			panic("partial block read: %d should be %d\n",
9481558Srgrimes				rd, ntrec * TP_BSIZE);
9491558Srgrimes		terminateinput();
95023685Speter		memmove(&tapebuf[rd], &endoftapemark, (long)TP_BSIZE);
9511558Srgrimes	}
9521558Srgrimes	blkcnt = 0;
95323685Speter	memmove(buf, &tapebuf[(blkcnt++ * TP_BSIZE)], (long)TP_BSIZE);
9541558Srgrimes	blksread++;
95590827Siedowse	tapeaddr++;
9561558Srgrimes}
9571558Srgrimes
9581558Srgrimesstatic void
95992837Simpfindtapeblksize(void)
9601558Srgrimes{
96192806Sobrien	long i;
9621558Srgrimes
9631558Srgrimes	for (i = 0; i < ntrec; i++)
9641558Srgrimes		((struct s_spcl *)&tapebuf[i * TP_BSIZE])->c_magic = 0;
9651558Srgrimes	blkcnt = 0;
9661558Srgrimes#ifdef RRESTORE
9671558Srgrimes	if (host)
9681558Srgrimes		i = rmtread(tapebuf, ntrec * TP_BSIZE);
9691558Srgrimes	else
9701558Srgrimes#endif
9711558Srgrimes		i = read(mt, tapebuf, ntrec * TP_BSIZE);
9721558Srgrimes
9731558Srgrimes	if (i <= 0) {
9741558Srgrimes		fprintf(stderr, "tape read error: %s\n", strerror(errno));
9751558Srgrimes		done(1);
9761558Srgrimes	}
9771558Srgrimes	if (i % TP_BSIZE != 0) {
97837240Sbde		fprintf(stderr, "Tape block size (%ld) %s (%d)\n",
9791558Srgrimes			i, "is not a multiple of dump block size", TP_BSIZE);
9801558Srgrimes		done(1);
9811558Srgrimes	}
9821558Srgrimes	ntrec = i / TP_BSIZE;
9831558Srgrimes	numtrec = ntrec;
98437240Sbde	vprintf(stdout, "Tape block size is %ld\n", ntrec);
9851558Srgrimes}
9861558Srgrimes
9871558Srgrimesvoid
98892837Simpclosemt(void)
9891558Srgrimes{
9901558Srgrimes
9911558Srgrimes	if (mt < 0)
9921558Srgrimes		return;
9931558Srgrimes#ifdef RRESTORE
9941558Srgrimes	if (host)
9951558Srgrimes		rmtclose();
9961558Srgrimes	else
9971558Srgrimes#endif
9981558Srgrimes		(void) close(mt);
9991558Srgrimes}
10001558Srgrimes
10011558Srgrimes/*
10021558Srgrimes * Read the next block from the tape.
10031558Srgrimes * Check to see if it is one of several vintage headers.
10041558Srgrimes * If it is an old style header, convert it to a new style header.
10051558Srgrimes * If it is not any valid header, return an error.
10061558Srgrimes */
10071558Srgrimesstatic int
100892837Simpgethead(struct s_spcl *buf)
10091558Srgrimes{
10101558Srgrimes	long i;
10111558Srgrimes	union {
10121558Srgrimes		quad_t	qval;
101340668Sdima		int32_t	val[2];
10141558Srgrimes	} qcvt;
10151558Srgrimes	union u_ospcl {
10161558Srgrimes		char dummy[TP_BSIZE];
10171558Srgrimes		struct	s_ospcl {
101840668Sdima			int32_t	c_type;
101940668Sdima			int32_t	c_date;
102040668Sdima			int32_t	c_ddate;
102140668Sdima			int32_t	c_volume;
102240668Sdima			int32_t	c_tapea;
10231558Srgrimes			u_short	c_inumber;
102440668Sdima			int32_t	c_magic;
102540668Sdima			int32_t	c_checksum;
10261558Srgrimes			struct odinode {
10271558Srgrimes				unsigned short odi_mode;
10281558Srgrimes				u_short	odi_nlink;
10291558Srgrimes				u_short	odi_uid;
10301558Srgrimes				u_short	odi_gid;
103140668Sdima				int32_t	odi_size;
103240668Sdima				int32_t	odi_rdev;
10331558Srgrimes				char	odi_addr[36];
103440668Sdima				int32_t	odi_atime;
103540668Sdima				int32_t	odi_mtime;
103640668Sdima				int32_t	odi_ctime;
10371558Srgrimes			} c_dinode;
103840668Sdima			int32_t	c_count;
10391558Srgrimes			char	c_addr[256];
10401558Srgrimes		} s_ospcl;
10411558Srgrimes	} u_ospcl;
10421558Srgrimes
10431558Srgrimes	if (!cvtflag) {
10441558Srgrimes		readtape((char *)buf);
10451558Srgrimes		if (buf->c_magic != NFS_MAGIC) {
10461558Srgrimes			if (swabl(buf->c_magic) != NFS_MAGIC)
10471558Srgrimes				return (FAIL);
10481558Srgrimes			if (!Bcvt) {
10491558Srgrimes				vprintf(stdout, "Note: Doing Byte swapping\n");
10501558Srgrimes				Bcvt = 1;
10511558Srgrimes			}
10521558Srgrimes		}
10531558Srgrimes		if (checksum((int *)buf) == FAIL)
10541558Srgrimes			return (FAIL);
105523096Simp		if (Bcvt) {
10561558Srgrimes			swabst((u_char *)"8l4s31l", (u_char *)buf);
105723096Simp			swabst((u_char *)"l",(u_char *) &buf->c_level);
105823096Simp			swabst((u_char *)"2l",(u_char *) &buf->c_flags);
105923096Simp		}
10601558Srgrimes		goto good;
10611558Srgrimes	}
10621558Srgrimes	readtape((char *)(&u_ospcl.s_ospcl));
106323685Speter	memset(buf, 0, (long)TP_BSIZE);
10641558Srgrimes	buf->c_type = u_ospcl.s_ospcl.c_type;
10651558Srgrimes	buf->c_date = u_ospcl.s_ospcl.c_date;
10661558Srgrimes	buf->c_ddate = u_ospcl.s_ospcl.c_ddate;
10671558Srgrimes	buf->c_volume = u_ospcl.s_ospcl.c_volume;
10681558Srgrimes	buf->c_tapea = u_ospcl.s_ospcl.c_tapea;
10691558Srgrimes	buf->c_inumber = u_ospcl.s_ospcl.c_inumber;
10701558Srgrimes	buf->c_checksum = u_ospcl.s_ospcl.c_checksum;
10711558Srgrimes	buf->c_magic = u_ospcl.s_ospcl.c_magic;
10721558Srgrimes	buf->c_dinode.di_mode = u_ospcl.s_ospcl.c_dinode.odi_mode;
10731558Srgrimes	buf->c_dinode.di_nlink = u_ospcl.s_ospcl.c_dinode.odi_nlink;
10741558Srgrimes	buf->c_dinode.di_uid = u_ospcl.s_ospcl.c_dinode.odi_uid;
10751558Srgrimes	buf->c_dinode.di_gid = u_ospcl.s_ospcl.c_dinode.odi_gid;
10761558Srgrimes	buf->c_dinode.di_size = u_ospcl.s_ospcl.c_dinode.odi_size;
10771558Srgrimes	buf->c_dinode.di_rdev = u_ospcl.s_ospcl.c_dinode.odi_rdev;
107823685Speter	buf->c_dinode.di_atime = u_ospcl.s_ospcl.c_dinode.odi_atime;
107923685Speter	buf->c_dinode.di_mtime = u_ospcl.s_ospcl.c_dinode.odi_mtime;
108023685Speter	buf->c_dinode.di_ctime = u_ospcl.s_ospcl.c_dinode.odi_ctime;
10811558Srgrimes	buf->c_count = u_ospcl.s_ospcl.c_count;
108223685Speter	memmove(buf->c_addr, u_ospcl.s_ospcl.c_addr, (long)256);
10831558Srgrimes	if (u_ospcl.s_ospcl.c_magic != OFS_MAGIC ||
10841558Srgrimes	    checksum((int *)(&u_ospcl.s_ospcl)) == FAIL)
10851558Srgrimes		return(FAIL);
10861558Srgrimes	buf->c_magic = NFS_MAGIC;
10871558Srgrimes
10881558Srgrimesgood:
10891558Srgrimes	if ((buf->c_dinode.di_size == 0 || buf->c_dinode.di_size > 0xfffffff) &&
10901558Srgrimes	    (buf->c_dinode.di_mode & IFMT) == IFDIR && Qcvt == 0) {
10911558Srgrimes		qcvt.qval = buf->c_dinode.di_size;
10921558Srgrimes		if (qcvt.val[0] || qcvt.val[1]) {
10931558Srgrimes			printf("Note: Doing Quad swapping\n");
10941558Srgrimes			Qcvt = 1;
10951558Srgrimes		}
10961558Srgrimes	}
10971558Srgrimes	if (Qcvt) {
10981558Srgrimes		qcvt.qval = buf->c_dinode.di_size;
10991558Srgrimes		i = qcvt.val[1];
11001558Srgrimes		qcvt.val[1] = qcvt.val[0];
11011558Srgrimes		qcvt.val[0] = i;
11021558Srgrimes		buf->c_dinode.di_size = qcvt.qval;
11031558Srgrimes	}
110437923Simp	readmapflag = 0;
11051558Srgrimes
11061558Srgrimes	switch (buf->c_type) {
11071558Srgrimes
11081558Srgrimes	case TS_CLRI:
11091558Srgrimes	case TS_BITS:
11101558Srgrimes		/*
11111558Srgrimes		 * Have to patch up missing information in bit map headers
11121558Srgrimes		 */
11131558Srgrimes		buf->c_inumber = 0;
11141558Srgrimes		buf->c_dinode.di_size = buf->c_count * TP_BSIZE;
111537923Simp		if (buf->c_count > TP_NINDIR)
111637923Simp			readmapflag = 1;
111737923Simp		else
111837923Simp			for (i = 0; i < buf->c_count; i++)
111937923Simp				buf->c_addr[i]++;
11201558Srgrimes		break;
11211558Srgrimes
11221558Srgrimes	case TS_TAPE:
11231558Srgrimes		if ((buf->c_flags & DR_NEWINODEFMT) == 0)
11241558Srgrimes			oldinofmt = 1;
11251558Srgrimes		/* fall through */
11261558Srgrimes	case TS_END:
11271558Srgrimes		buf->c_inumber = 0;
11281558Srgrimes		break;
11291558Srgrimes
11301558Srgrimes	case TS_INODE:
11311558Srgrimes	case TS_ADDR:
11321558Srgrimes		break;
11331558Srgrimes
11341558Srgrimes	default:
11351558Srgrimes		panic("gethead: unknown inode type %d\n", buf->c_type);
11361558Srgrimes		break;
11371558Srgrimes	}
11381558Srgrimes	/*
11398871Srgrimes	 * If we are restoring a filesystem with old format inodes,
11401558Srgrimes	 * copy the uid/gid to the new location.
11411558Srgrimes	 */
11421558Srgrimes	if (oldinofmt) {
11431558Srgrimes		buf->c_dinode.di_uid = buf->c_dinode.di_ouid;
11441558Srgrimes		buf->c_dinode.di_gid = buf->c_dinode.di_ogid;
11451558Srgrimes	}
114690827Siedowse	tapeaddr = buf->c_tapea;
11471558Srgrimes	if (dflag)
11481558Srgrimes		accthdr(buf);
11491558Srgrimes	return(GOOD);
11501558Srgrimes}
11511558Srgrimes
11521558Srgrimes/*
11531558Srgrimes * Check that a header is where it belongs and predict the next header
11541558Srgrimes */
11551558Srgrimesstatic void
115692837Simpaccthdr(struct s_spcl *header)
11571558Srgrimes{
11581558Srgrimes	static ino_t previno = 0x7fffffff;
11591558Srgrimes	static int prevtype;
11601558Srgrimes	static long predict;
11611558Srgrimes	long blks, i;
11621558Srgrimes
11631558Srgrimes	if (header->c_type == TS_TAPE) {
11641558Srgrimes		fprintf(stderr, "Volume header (%s inode format) ",
11651558Srgrimes		    oldinofmt ? "old" : "new");
11661558Srgrimes 		if (header->c_firstrec)
116737240Sbde 			fprintf(stderr, "begins with record %ld",
11681558Srgrimes 				header->c_firstrec);
11691558Srgrimes 		fprintf(stderr, "\n");
11701558Srgrimes		previno = 0x7fffffff;
11711558Srgrimes		return;
11721558Srgrimes	}
11731558Srgrimes	if (previno == 0x7fffffff)
11741558Srgrimes		goto newcalc;
11751558Srgrimes	switch (prevtype) {
11761558Srgrimes	case TS_BITS:
117723685Speter		fprintf(stderr, "Dumped inodes map header");
11781558Srgrimes		break;
11791558Srgrimes	case TS_CLRI:
118023685Speter		fprintf(stderr, "Used inodes map header");
11811558Srgrimes		break;
11821558Srgrimes	case TS_INODE:
11831558Srgrimes		fprintf(stderr, "File header, ino %d", previno);
11841558Srgrimes		break;
11851558Srgrimes	case TS_ADDR:
11861558Srgrimes		fprintf(stderr, "File continuation header, ino %d", previno);
11871558Srgrimes		break;
11881558Srgrimes	case TS_END:
11891558Srgrimes		fprintf(stderr, "End of tape header");
11901558Srgrimes		break;
11911558Srgrimes	}
11921558Srgrimes	if (predict != blksread - 1)
119337240Sbde		fprintf(stderr, "; predicted %ld blocks, got %ld blocks",
11941558Srgrimes			predict, blksread - 1);
11951558Srgrimes	fprintf(stderr, "\n");
11961558Srgrimesnewcalc:
11971558Srgrimes	blks = 0;
11981558Srgrimes	if (header->c_type != TS_END)
11991558Srgrimes		for (i = 0; i < header->c_count; i++)
120037923Simp			if (readmapflag || header->c_addr[i] != 0)
12011558Srgrimes				blks++;
12021558Srgrimes	predict = blks;
12031558Srgrimes	blksread = 0;
12041558Srgrimes	prevtype = header->c_type;
12051558Srgrimes	previno = header->c_inumber;
12061558Srgrimes}
12071558Srgrimes
12081558Srgrimes/*
12091558Srgrimes * Find an inode header.
121090573Siedowse * Complain if had to skip.
12111558Srgrimes */
12121558Srgrimesstatic void
121392837Simpfindinode(struct s_spcl *header)
12141558Srgrimes{
12151558Srgrimes	static long skipcnt = 0;
12161558Srgrimes	long i;
12171558Srgrimes	char buf[TP_BSIZE];
121890608Siedowse	int htype;
12191558Srgrimes
12201558Srgrimes	curfile.name = "<name unknown>";
12211558Srgrimes	curfile.action = UNKNOWN;
12221558Srgrimes	curfile.dip = NULL;
12231558Srgrimes	curfile.ino = 0;
12241558Srgrimes	do {
12251558Srgrimes		if (header->c_magic != NFS_MAGIC) {
12261558Srgrimes			skipcnt++;
12271558Srgrimes			while (gethead(header) == FAIL ||
122889572Sdillon			    _time32_to_time(header->c_date) != dumpdate)
12291558Srgrimes				skipcnt++;
12301558Srgrimes		}
123190608Siedowse		htype = header->c_type;
123290608Siedowse		switch (htype) {
12331558Srgrimes
12341558Srgrimes		case TS_ADDR:
12351558Srgrimes			/*
12361558Srgrimes			 * Skip up to the beginning of the next record
12371558Srgrimes			 */
12381558Srgrimes			for (i = 0; i < header->c_count; i++)
12391558Srgrimes				if (header->c_addr[i])
12401558Srgrimes					readtape(buf);
12411558Srgrimes			while (gethead(header) == FAIL ||
124289572Sdillon			    _time32_to_time(header->c_date) != dumpdate)
12431558Srgrimes				skipcnt++;
12441558Srgrimes			break;
12451558Srgrimes
12461558Srgrimes		case TS_INODE:
12471558Srgrimes			curfile.dip = &header->c_dinode;
12481558Srgrimes			curfile.ino = header->c_inumber;
12491558Srgrimes			break;
12501558Srgrimes
12511558Srgrimes		case TS_END:
125290820Siedowse			/* If we missed some tapes, get another volume. */
125390820Siedowse			if (tapesread & (tapesread + 1)) {
125490820Siedowse				getvol(0);
125590820Siedowse				continue;
125690820Siedowse			}
12571558Srgrimes			curfile.ino = maxino;
12581558Srgrimes			break;
12591558Srgrimes
12601558Srgrimes		case TS_CLRI:
12611558Srgrimes			curfile.name = "<file removal list>";
12621558Srgrimes			break;
12631558Srgrimes
12641558Srgrimes		case TS_BITS:
12651558Srgrimes			curfile.name = "<file dump list>";
12661558Srgrimes			break;
12671558Srgrimes
12681558Srgrimes		case TS_TAPE:
12691558Srgrimes			panic("unexpected tape header\n");
12701558Srgrimes			/* NOTREACHED */
12711558Srgrimes
12721558Srgrimes		default:
12731558Srgrimes			panic("unknown tape header type %d\n", spcl.c_type);
12741558Srgrimes			/* NOTREACHED */
12751558Srgrimes
12761558Srgrimes		}
127790608Siedowse	} while (htype == TS_ADDR);
12781558Srgrimes	if (skipcnt > 0)
127937240Sbde		fprintf(stderr, "resync restore, skipped %ld blocks\n",
128037240Sbde		    skipcnt);
12811558Srgrimes	skipcnt = 0;
12821558Srgrimes}
12831558Srgrimes
12841558Srgrimesstatic int
128592837Simpchecksum(int *buf)
12861558Srgrimes{
128792806Sobrien	int i, j;
12881558Srgrimes
12891558Srgrimes	j = sizeof(union u_spcl) / sizeof(int);
12901558Srgrimes	i = 0;
12911558Srgrimes	if(!Bcvt) {
12921558Srgrimes		do
12931558Srgrimes			i += *buf++;
12941558Srgrimes		while (--j);
12951558Srgrimes	} else {
12961558Srgrimes		/* What happens if we want to read restore tapes
12971558Srgrimes			for a 16bit int machine??? */
12988871Srgrimes		do
12991558Srgrimes			i += swabl(*buf++);
13001558Srgrimes		while (--j);
13011558Srgrimes	}
13028871Srgrimes
13031558Srgrimes	if (i != CHECKSUM) {
13041558Srgrimes		fprintf(stderr, "Checksum error %o, inode %d file %s\n", i,
13051558Srgrimes			curfile.ino, curfile.name);
13061558Srgrimes		return(FAIL);
13071558Srgrimes	}
13081558Srgrimes	return(GOOD);
13091558Srgrimes}
13101558Srgrimes
13111558Srgrimes#ifdef RRESTORE
13121558Srgrimes#include <stdarg.h>
13131558Srgrimes
13141558Srgrimesvoid
13151558Srgrimesmsg(const char *fmt, ...)
13161558Srgrimes{
13171558Srgrimes	va_list ap;
13181558Srgrimes	va_start(ap, fmt);
13191558Srgrimes	(void)vfprintf(stderr, fmt, ap);
13201558Srgrimes	va_end(ap);
13211558Srgrimes}
13221558Srgrimes#endif /* RRESTORE */
13231558Srgrimes
13241558Srgrimesstatic u_char *
132592837Simpswabshort(u_char *sp, int n)
13261558Srgrimes{
13271558Srgrimes	char c;
13281558Srgrimes
13291558Srgrimes	while (--n >= 0) {
13301558Srgrimes		c = sp[0]; sp[0] = sp[1]; sp[1] = c;
13311558Srgrimes		sp += 2;
13321558Srgrimes	}
13331558Srgrimes	return (sp);
13341558Srgrimes}
13351558Srgrimes
13361558Srgrimesstatic u_char *
133792837Simpswablong(u_char *sp, int n)
13381558Srgrimes{
13391558Srgrimes	char c;
13401558Srgrimes
13411558Srgrimes	while (--n >= 0) {
13421558Srgrimes		c = sp[0]; sp[0] = sp[3]; sp[3] = c;
13431558Srgrimes		c = sp[2]; sp[2] = sp[1]; sp[1] = c;
13441558Srgrimes		sp += 4;
13451558Srgrimes	}
13461558Srgrimes	return (sp);
13471558Srgrimes}
13481558Srgrimes
13491558Srgrimesvoid
135092837Simpswabst(u_char *cp, u_char *sp)
13511558Srgrimes{
13521558Srgrimes	int n = 0;
13531558Srgrimes
13541558Srgrimes	while (*cp) {
13551558Srgrimes		switch (*cp) {
13561558Srgrimes		case '0': case '1': case '2': case '3': case '4':
13571558Srgrimes		case '5': case '6': case '7': case '8': case '9':
13581558Srgrimes			n = (n * 10) + (*cp++ - '0');
13591558Srgrimes			continue;
13608871Srgrimes
13611558Srgrimes		case 's': case 'w': case 'h':
13621558Srgrimes			if (n == 0)
13631558Srgrimes				n = 1;
13641558Srgrimes			sp = swabshort(sp, n);
13651558Srgrimes			break;
13661558Srgrimes
13671558Srgrimes		case 'l':
13681558Srgrimes			if (n == 0)
13691558Srgrimes				n = 1;
13701558Srgrimes			sp = swablong(sp, n);
13711558Srgrimes			break;
13721558Srgrimes
13731558Srgrimes		default: /* Any other character, like 'b' counts as byte. */
13741558Srgrimes			if (n == 0)
13751558Srgrimes				n = 1;
13761558Srgrimes			sp += n;
13771558Srgrimes			break;
13781558Srgrimes		}
13791558Srgrimes		cp++;
13801558Srgrimes		n = 0;
13811558Srgrimes	}
13821558Srgrimes}
13831558Srgrimes
13841558Srgrimesstatic u_long
138592837Simpswabl(u_long x)
13861558Srgrimes{
13871558Srgrimes	swabst((u_char *)"l", (u_char *)&x);
13881558Srgrimes	return (x);
13891558Srgrimes}
1390