tape.c revision 23096
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
401558Srgrimesstatic char sccsid[] = "@(#)tape.c	8.3 (Berkeley) 4/1/94";
411558Srgrimes#endif /* not lint */
421558Srgrimes
431558Srgrimes#include <sys/param.h>
441558Srgrimes#include <sys/file.h>
451558Srgrimes#include <sys/ioctl.h>
461558Srgrimes#include <sys/mtio.h>
471558Srgrimes#include <sys/stat.h>
481558Srgrimes
491558Srgrimes#include <ufs/ufs/dinode.h>
501558Srgrimes#include <protocols/dumprestore.h>
511558Srgrimes
521558Srgrimes#include <errno.h>
531558Srgrimes#include <setjmp.h>
541558Srgrimes#include <stdio.h>
551558Srgrimes#include <stdlib.h>
561558Srgrimes#include <string.h>
571558Srgrimes#include <unistd.h>
581558Srgrimes
591558Srgrimes#include "restore.h"
601558Srgrimes#include "extern.h"
611558Srgrimes#include "pathnames.h"
621558Srgrimes
631558Srgrimesstatic long	fssize = MAXBSIZE;
641558Srgrimesstatic int	mt = -1;
651558Srgrimesstatic int	pipein = 0;
6621174Sguidostatic char	*magtape;
671558Srgrimesstatic int	blkcnt;
681558Srgrimesstatic int	numtrec;
691558Srgrimesstatic char	*tapebuf;
701558Srgrimesstatic union	u_spcl endoftapemark;
711558Srgrimesstatic long	blksread;		/* blocks read since last header */
721558Srgrimesstatic long	tpblksread = 0;		/* TP_BSIZE blocks read */
731558Srgrimesstatic long	tapesread;
741558Srgrimesstatic jmp_buf	restart;
751558Srgrimesstatic int	gettingfile = 0;	/* restart has a valid frame */
761558Srgrimesstatic char	*host = NULL;
771558Srgrimes
781558Srgrimesstatic int	ofile;
791558Srgrimesstatic char	*map;
801558Srgrimesstatic char	lnkbuf[MAXPATHLEN + 1];
811558Srgrimesstatic int	pathlen;
821558Srgrimes
831558Srgrimesint		oldinofmt;	/* old inode format conversion required */
841558Srgrimesint		Bcvt;		/* Swap Bytes (for CCI or sun) */
851558Srgrimesstatic int	Qcvt;		/* Swap quads (for sun) */
861558Srgrimes
871558Srgrimes#define	FLUSHTAPEBUF()	blkcnt = ntrec + 1
881558Srgrimes
891558Srgrimesstatic void	 accthdr __P((struct s_spcl *));
901558Srgrimesstatic int	 checksum __P((int *));
911558Srgrimesstatic void	 findinode __P((struct s_spcl *));
921558Srgrimesstatic void	 findtapeblksize __P((void));
931558Srgrimesstatic int	 gethead __P((struct s_spcl *));
941558Srgrimesstatic void	 readtape __P((char *));
951558Srgrimesstatic void	 setdumpnum __P((void));
961558Srgrimesstatic u_long	 swabl __P((u_long));
971558Srgrimesstatic u_char	*swablong __P((u_char *, int));
981558Srgrimesstatic u_char	*swabshort __P((u_char *, int));
991558Srgrimesstatic void	 terminateinput __P((void));
1001558Srgrimesstatic void	 xtrfile __P((char *, long));
1011558Srgrimesstatic void	 xtrlnkfile __P((char *, long));
1021558Srgrimesstatic void	 xtrlnkskip __P((char *, long));
1031558Srgrimesstatic void	 xtrmap __P((char *, long));
1041558Srgrimesstatic void	 xtrmapskip __P((char *, long));
1051558Srgrimesstatic void	 xtrskip __P((char *, long));
1061558Srgrimes
1071558Srgrimes/*
1081558Srgrimes * Set up an input source
1091558Srgrimes */
1101558Srgrimesvoid
1111558Srgrimessetinput(source)
1121558Srgrimes	char *source;
1131558Srgrimes{
1141558Srgrimes	FLUSHTAPEBUF();
1151558Srgrimes	if (bflag)
1161558Srgrimes		newtapebuf(ntrec);
1171558Srgrimes	else
1181558Srgrimes		newtapebuf(NTREC > HIGHDENSITYTREC ? NTREC : HIGHDENSITYTREC);
1191558Srgrimes	terminal = stdin;
1201558Srgrimes
1211558Srgrimes#ifdef RRESTORE
1221558Srgrimes	if (index(source, ':')) {
1231558Srgrimes		host = source;
1241558Srgrimes		source = index(host, ':');
1251558Srgrimes		*source++ = '\0';
1261558Srgrimes		if (rmthost(host) == 0)
1271558Srgrimes			done(1);
1281558Srgrimes	} else
1291558Srgrimes#endif
1301558Srgrimes	if (strcmp(source, "-") == 0) {
1311558Srgrimes		/*
1321558Srgrimes		 * Since input is coming from a pipe we must establish
1331558Srgrimes		 * our own connection to the terminal.
1341558Srgrimes		 */
1351558Srgrimes		terminal = fopen(_PATH_TTY, "r");
1361558Srgrimes		if (terminal == NULL) {
1371558Srgrimes			(void)fprintf(stderr, "cannot open %s: %s\n",
1381558Srgrimes			    _PATH_TTY, strerror(errno));
1391558Srgrimes			terminal = fopen(_PATH_DEVNULL, "r");
1401558Srgrimes			if (terminal == NULL) {
1411558Srgrimes				(void)fprintf(stderr, "cannot open %s: %s\n",
1421558Srgrimes				    _PATH_DEVNULL, strerror(errno));
1431558Srgrimes				done(1);
1441558Srgrimes			}
1451558Srgrimes		}
1461558Srgrimes		pipein++;
1471558Srgrimes	}
1481558Srgrimes	setuid(getuid());	/* no longer need or want root privileges */
14921174Sguido	magtape = strdup(source);
15021174Sguido	if (magtape == NULL) {
15121174Sguido		fprintf(stderr, "Cannot allocate space for magtape buffer\n");
15221174Sguido		done(1);
15321174Sguido	}
1541558Srgrimes}
1551558Srgrimes
1561558Srgrimesvoid
1571558Srgrimesnewtapebuf(size)
1581558Srgrimes	long size;
1591558Srgrimes{
1601558Srgrimes	static tapebufsize = -1;
1611558Srgrimes
1621558Srgrimes	ntrec = size;
1631558Srgrimes	if (size <= tapebufsize)
1641558Srgrimes		return;
1651558Srgrimes	if (tapebuf != NULL)
1661558Srgrimes		free(tapebuf);
1671558Srgrimes	tapebuf = malloc(size * TP_BSIZE);
1681558Srgrimes	if (tapebuf == NULL) {
1691558Srgrimes		fprintf(stderr, "Cannot allocate space for tape buffer\n");
1701558Srgrimes		done(1);
1711558Srgrimes	}
1721558Srgrimes	tapebufsize = size;
1731558Srgrimes}
1741558Srgrimes
1751558Srgrimes/*
1761558Srgrimes * Verify that the tape drive can be accessed and
1771558Srgrimes * that it actually is a dump tape.
1781558Srgrimes */
1791558Srgrimesvoid
1801558Srgrimessetup()
1811558Srgrimes{
1821558Srgrimes	int i, j, *ip;
1831558Srgrimes	struct stat stbuf;
1841558Srgrimes
1851558Srgrimes	vprintf(stdout, "Verify tape and initialize maps\n");
1861558Srgrimes#ifdef RRESTORE
1871558Srgrimes	if (host)
1881558Srgrimes		mt = rmtopen(magtape, 0);
1891558Srgrimes	else
1901558Srgrimes#endif
1911558Srgrimes	if (pipein)
1921558Srgrimes		mt = 0;
1931558Srgrimes	else
1941558Srgrimes		mt = open(magtape, O_RDONLY, 0);
1951558Srgrimes	if (mt < 0) {
1961558Srgrimes		fprintf(stderr, "%s: %s\n", magtape, strerror(errno));
1971558Srgrimes		done(1);
1981558Srgrimes	}
1991558Srgrimes	volno = 1;
2001558Srgrimes	setdumpnum();
2011558Srgrimes	FLUSHTAPEBUF();
2021558Srgrimes	if (!pipein && !bflag)
2031558Srgrimes		findtapeblksize();
2041558Srgrimes	if (gethead(&spcl) == FAIL) {
2051558Srgrimes		blkcnt--; /* push back this block */
2061558Srgrimes		blksread--;
2071558Srgrimes		tpblksread--;
2081558Srgrimes		cvtflag++;
2091558Srgrimes		if (gethead(&spcl) == FAIL) {
2101558Srgrimes			fprintf(stderr, "Tape is not a dump tape\n");
2111558Srgrimes			done(1);
2121558Srgrimes		}
2131558Srgrimes		fprintf(stderr, "Converting to new file system format.\n");
2141558Srgrimes	}
2151558Srgrimes	if (pipein) {
2161558Srgrimes		endoftapemark.s_spcl.c_magic = cvtflag ? OFS_MAGIC : NFS_MAGIC;
2171558Srgrimes		endoftapemark.s_spcl.c_type = TS_END;
2181558Srgrimes		ip = (int *)&endoftapemark;
2191558Srgrimes		j = sizeof(union u_spcl) / sizeof(int);
2201558Srgrimes		i = 0;
2211558Srgrimes		do
2221558Srgrimes			i += *ip++;
2231558Srgrimes		while (--j);
2241558Srgrimes		endoftapemark.s_spcl.c_checksum = CHECKSUM - i;
2251558Srgrimes	}
2261558Srgrimes	if (vflag || command == 't')
2271558Srgrimes		printdumpinfo();
2281558Srgrimes	dumptime = spcl.c_ddate;
2291558Srgrimes	dumpdate = spcl.c_date;
2301558Srgrimes	if (stat(".", &stbuf) < 0) {
2311558Srgrimes		fprintf(stderr, "cannot stat .: %s\n", strerror(errno));
2321558Srgrimes		done(1);
2331558Srgrimes	}
2341558Srgrimes	if (stbuf.st_blksize > 0 && stbuf.st_blksize <= MAXBSIZE)
2351558Srgrimes		fssize = stbuf.st_blksize;
2361558Srgrimes	if (((fssize - 1) & fssize) != 0) {
2371558Srgrimes		fprintf(stderr, "bad block size %d\n", fssize);
2381558Srgrimes		done(1);
2391558Srgrimes	}
2401558Srgrimes	if (spcl.c_volume != 1) {
2411558Srgrimes		fprintf(stderr, "Tape is not volume 1 of the dump\n");
2421558Srgrimes		done(1);
2431558Srgrimes	}
2441558Srgrimes	if (gethead(&spcl) == FAIL) {
2451558Srgrimes		dprintf(stdout, "header read failed at %d blocks\n", blksread);
2461558Srgrimes		panic("no header after volume mark!\n");
2471558Srgrimes	}
2481558Srgrimes	findinode(&spcl);
2491558Srgrimes	if (spcl.c_type != TS_CLRI) {
2501558Srgrimes		fprintf(stderr, "Cannot find file removal list\n");
2511558Srgrimes		done(1);
2521558Srgrimes	}
2531558Srgrimes	maxino = (spcl.c_count * TP_BSIZE * NBBY) + 1;
2541558Srgrimes	dprintf(stdout, "maxino = %d\n", maxino);
2551558Srgrimes	map = calloc((unsigned)1, (unsigned)howmany(maxino, NBBY));
2561558Srgrimes	if (map == NULL)
2571558Srgrimes		panic("no memory for file removal list\n");
2581558Srgrimes	clrimap = map;
2591558Srgrimes	curfile.action = USING;
2601558Srgrimes	getfile(xtrmap, xtrmapskip);
2611558Srgrimes	if (spcl.c_type != TS_BITS) {
2621558Srgrimes		fprintf(stderr, "Cannot find file dump list\n");
2631558Srgrimes		done(1);
2641558Srgrimes	}
2651558Srgrimes	map = calloc((unsigned)1, (unsigned)howmany(maxino, NBBY));
2661558Srgrimes	if (map == (char *)NULL)
2671558Srgrimes		panic("no memory for file dump list\n");
2681558Srgrimes	dumpmap = map;
2691558Srgrimes	curfile.action = USING;
2701558Srgrimes	getfile(xtrmap, xtrmapskip);
2711558Srgrimes}
2721558Srgrimes
2731558Srgrimes/*
2741558Srgrimes * Prompt user to load a new dump volume.
2751558Srgrimes * "Nextvol" is the next suggested volume to use.
2761558Srgrimes * This suggested volume is enforced when doing full
2771558Srgrimes * or incremental restores, but can be overrridden by
2781558Srgrimes * the user when only extracting a subset of the files.
2791558Srgrimes */
2801558Srgrimesvoid
2811558Srgrimesgetvol(nextvol)
2821558Srgrimes	long nextvol;
2831558Srgrimes{
2841558Srgrimes	long newvol, savecnt, wantnext, i;
2851558Srgrimes	union u_spcl tmpspcl;
2861558Srgrimes#	define tmpbuf tmpspcl.s_spcl
2871558Srgrimes	char buf[TP_BSIZE];
2881558Srgrimes
2891558Srgrimes	if (nextvol == 1) {
2901558Srgrimes		tapesread = 0;
2911558Srgrimes		gettingfile = 0;
2921558Srgrimes	}
2931558Srgrimes	if (pipein) {
2941558Srgrimes		if (nextvol != 1)
2951558Srgrimes			panic("Changing volumes on pipe input?\n");
2961558Srgrimes		if (volno == 1)
2971558Srgrimes			return;
2981558Srgrimes		goto gethdr;
2991558Srgrimes	}
3001558Srgrimes	savecnt = blksread;
3011558Srgrimesagain:
3021558Srgrimes	if (pipein)
3031558Srgrimes		done(1); /* pipes do not get a second chance */
3041558Srgrimes	if (command == 'R' || command == 'r' || curfile.action != SKIP) {
3051558Srgrimes		newvol = nextvol;
3061558Srgrimes		wantnext = 1;
3078871Srgrimes	} else {
3081558Srgrimes		newvol = 0;
3091558Srgrimes		wantnext = 0;
3101558Srgrimes	}
3111558Srgrimes	while (newvol <= 0) {
3121558Srgrimes		if (tapesread == 0) {
3131558Srgrimes			fprintf(stderr, "%s%s%s%s%s",
3141558Srgrimes			    "You have not read any tapes yet.\n",
3151558Srgrimes			    "Unless you know which volume your",
3161558Srgrimes			    " file(s) are on you should start\n",
3171558Srgrimes			    "with the last volume and work",
3181558Srgrimes			    " towards towards the first.\n");
3191558Srgrimes		} else {
3201558Srgrimes			fprintf(stderr, "You have read volumes");
3211558Srgrimes			strcpy(buf, ": ");
3221558Srgrimes			for (i = 1; i < 32; i++)
3231558Srgrimes				if (tapesread & (1 << i)) {
3241558Srgrimes					fprintf(stderr, "%s%d", buf, i);
3251558Srgrimes					strcpy(buf, ", ");
3261558Srgrimes				}
3271558Srgrimes			fprintf(stderr, "\n");
3281558Srgrimes		}
3291558Srgrimes		do	{
3301558Srgrimes			fprintf(stderr, "Specify next volume #: ");
3311558Srgrimes			(void) fflush(stderr);
3321558Srgrimes			(void) fgets(buf, BUFSIZ, terminal);
3331558Srgrimes		} while (!feof(terminal) && buf[0] == '\n');
3341558Srgrimes		if (feof(terminal))
3351558Srgrimes			done(1);
3361558Srgrimes		newvol = atoi(buf);
3371558Srgrimes		if (newvol <= 0) {
3381558Srgrimes			fprintf(stderr,
3391558Srgrimes			    "Volume numbers are positive numerics\n");
3401558Srgrimes		}
3411558Srgrimes	}
3421558Srgrimes	if (newvol == volno) {
3431558Srgrimes		tapesread |= 1 << volno;
3441558Srgrimes		return;
3451558Srgrimes	}
3461558Srgrimes	closemt();
3471558Srgrimes	fprintf(stderr, "Mount tape volume %d\n", newvol);
3481558Srgrimes	fprintf(stderr, "Enter ``none'' if there are no more tapes\n");
3491558Srgrimes	fprintf(stderr, "otherwise enter tape name (default: %s) ", magtape);
3501558Srgrimes	(void) fflush(stderr);
3511558Srgrimes	(void) fgets(buf, BUFSIZ, terminal);
3521558Srgrimes	if (feof(terminal))
3531558Srgrimes		done(1);
3541558Srgrimes	if (!strcmp(buf, "none\n")) {
3551558Srgrimes		terminateinput();
3561558Srgrimes		return;
3571558Srgrimes	}
3581558Srgrimes	if (buf[0] != '\n') {
3591558Srgrimes		(void) strcpy(magtape, buf);
3601558Srgrimes		magtape[strlen(magtape) - 1] = '\0';
3611558Srgrimes	}
3621558Srgrimes#ifdef RRESTORE
3631558Srgrimes	if (host)
3641558Srgrimes		mt = rmtopen(magtape, 0);
3651558Srgrimes	else
3661558Srgrimes#endif
3671558Srgrimes		mt = open(magtape, O_RDONLY, 0);
3681558Srgrimes
3691558Srgrimes	if (mt == -1) {
3701558Srgrimes		fprintf(stderr, "Cannot open %s\n", magtape);
3711558Srgrimes		volno = -1;
3721558Srgrimes		goto again;
3731558Srgrimes	}
3741558Srgrimesgethdr:
3751558Srgrimes	volno = newvol;
3761558Srgrimes	setdumpnum();
3771558Srgrimes	FLUSHTAPEBUF();
3781558Srgrimes	if (gethead(&tmpbuf) == FAIL) {
3791558Srgrimes		dprintf(stdout, "header read failed at %d blocks\n", blksread);
3801558Srgrimes		fprintf(stderr, "tape is not dump tape\n");
3811558Srgrimes		volno = 0;
3821558Srgrimes		goto again;
3831558Srgrimes	}
3841558Srgrimes	if (tmpbuf.c_volume != volno) {
3851558Srgrimes		fprintf(stderr, "Wrong volume (%d)\n", tmpbuf.c_volume);
3861558Srgrimes		volno = 0;
3871558Srgrimes		goto again;
3881558Srgrimes	}
3891558Srgrimes	if (tmpbuf.c_date != dumpdate || tmpbuf.c_ddate != dumptime) {
3901558Srgrimes		fprintf(stderr, "Wrong dump date\n\tgot: %s",
3911558Srgrimes			ctime(&tmpbuf.c_date));
3921558Srgrimes		fprintf(stderr, "\twanted: %s", ctime(&dumpdate));
3931558Srgrimes		volno = 0;
3941558Srgrimes		goto again;
3951558Srgrimes	}
3961558Srgrimes	tapesread |= 1 << volno;
3971558Srgrimes	blksread = savecnt;
3981558Srgrimes 	/*
3991558Srgrimes 	 * If continuing from the previous volume, skip over any
4001558Srgrimes 	 * blocks read already at the end of the previous volume.
4011558Srgrimes 	 *
4021558Srgrimes 	 * If coming to this volume at random, skip to the beginning
4031558Srgrimes 	 * of the next record.
4041558Srgrimes 	 */
4058871Srgrimes	dprintf(stdout, "read %ld recs, tape starts with %ld\n",
4061558Srgrimes		tpblksread, tmpbuf.c_firstrec);
4071558Srgrimes 	if (tmpbuf.c_type == TS_TAPE && (tmpbuf.c_flags & DR_NEWHEADER)) {
4081558Srgrimes 		if (!wantnext) {
4091558Srgrimes 			tpblksread = tmpbuf.c_firstrec;
4101558Srgrimes 			for (i = tmpbuf.c_count; i > 0; i--)
4111558Srgrimes 				readtape(buf);
4121558Srgrimes 		} else if (tmpbuf.c_firstrec > 0 &&
4131558Srgrimes			   tmpbuf.c_firstrec < tpblksread - 1) {
4141558Srgrimes			/*
4151558Srgrimes			 * -1 since we've read the volume header
4161558Srgrimes			 */
4171558Srgrimes 			i = tpblksread - tmpbuf.c_firstrec - 1;
4181558Srgrimes			dprintf(stderr, "Skipping %d duplicate record%s.\n",
4191558Srgrimes				i, i > 1 ? "s" : "");
4201558Srgrimes 			while (--i >= 0)
4211558Srgrimes 				readtape(buf);
4221558Srgrimes 		}
4231558Srgrimes 	}
4241558Srgrimes	if (curfile.action == USING) {
4251558Srgrimes		if (volno == 1)
4261558Srgrimes			panic("active file into volume 1\n");
4271558Srgrimes		return;
4281558Srgrimes	}
4291558Srgrimes	/*
4301558Srgrimes	 * Skip up to the beginning of the next record
4311558Srgrimes	 */
4321558Srgrimes	if (tmpbuf.c_type == TS_TAPE && (tmpbuf.c_flags & DR_NEWHEADER))
4331558Srgrimes		for (i = tmpbuf.c_count; i > 0; i--)
4341558Srgrimes			readtape(buf);
4351558Srgrimes	(void) gethead(&spcl);
4361558Srgrimes	findinode(&spcl);
4371558Srgrimes	if (gettingfile) {
4381558Srgrimes		gettingfile = 0;
4391558Srgrimes		longjmp(restart, 1);
4401558Srgrimes	}
4411558Srgrimes}
4421558Srgrimes
4431558Srgrimes/*
4441558Srgrimes * Handle unexpected EOF.
4451558Srgrimes */
4461558Srgrimesstatic void
4471558Srgrimesterminateinput()
4481558Srgrimes{
4491558Srgrimes
4501558Srgrimes	if (gettingfile && curfile.action == USING) {
4511558Srgrimes		printf("Warning: %s %s\n",
4521558Srgrimes		    "End-of-input encountered while extracting", curfile.name);
4531558Srgrimes	}
4541558Srgrimes	curfile.name = "<name unknown>";
4551558Srgrimes	curfile.action = UNKNOWN;
4561558Srgrimes	curfile.dip = NULL;
4571558Srgrimes	curfile.ino = maxino;
4581558Srgrimes	if (gettingfile) {
4591558Srgrimes		gettingfile = 0;
4601558Srgrimes		longjmp(restart, 1);
4611558Srgrimes	}
4621558Srgrimes}
4631558Srgrimes
4641558Srgrimes/*
4651558Srgrimes * handle multiple dumps per tape by skipping forward to the
4661558Srgrimes * appropriate one.
4671558Srgrimes */
4681558Srgrimesstatic void
4691558Srgrimessetdumpnum()
4701558Srgrimes{
4711558Srgrimes	struct mtop tcom;
4721558Srgrimes
4731558Srgrimes	if (dumpnum == 1 || volno != 1)
4741558Srgrimes		return;
4751558Srgrimes	if (pipein) {
4761558Srgrimes		fprintf(stderr, "Cannot have multiple dumps on pipe input\n");
4771558Srgrimes		done(1);
4781558Srgrimes	}
4791558Srgrimes	tcom.mt_op = MTFSF;
4801558Srgrimes	tcom.mt_count = dumpnum - 1;
4811558Srgrimes#ifdef RRESTORE
4821558Srgrimes	if (host)
4831558Srgrimes		rmtioctl(MTFSF, dumpnum - 1);
4848871Srgrimes	else
4851558Srgrimes#endif
4861558Srgrimes		if (ioctl(mt, (int)MTIOCTOP, (char *)&tcom) < 0)
4871558Srgrimes			fprintf(stderr, "ioctl MTFSF: %s\n", strerror(errno));
4881558Srgrimes}
4891558Srgrimes
4901558Srgrimesvoid
4911558Srgrimesprintdumpinfo()
4921558Srgrimes{
4931558Srgrimes	fprintf(stdout, "Dump   date: %s", ctime(&spcl.c_date));
4941558Srgrimes	fprintf(stdout, "Dumped from: %s",
4951558Srgrimes	    (spcl.c_ddate == 0) ? "the epoch\n" : ctime(&spcl.c_ddate));
4961558Srgrimes	if (spcl.c_host[0] == '\0')
4971558Srgrimes		return;
4981558Srgrimes	fprintf(stderr, "Level %d dump of %s on %s:%s\n",
4991558Srgrimes		spcl.c_level, spcl.c_filesys, spcl.c_host, spcl.c_dev);
5001558Srgrimes	fprintf(stderr, "Label: %s\n", spcl.c_label);
5011558Srgrimes}
5021558Srgrimes
5031558Srgrimesint
5041558Srgrimesextractfile(name)
5051558Srgrimes	char *name;
5061558Srgrimes{
5071558Srgrimes	int mode;
5081558Srgrimes	struct timeval timep[2];
5091558Srgrimes	struct entry *ep;
5101558Srgrimes
5111558Srgrimes	curfile.name = name;
5121558Srgrimes	curfile.action = USING;
51318406Snate	timep[0].tv_sec = curfile.dip->di_atime.tv_sec;
51418406Snate	timep[0].tv_usec = curfile.dip->di_atime.tv_nsec / 1000;
51518406Snate	timep[1].tv_sec = curfile.dip->di_mtime.tv_sec;
51618406Snate	timep[1].tv_usec = curfile.dip->di_mtime.tv_nsec / 1000;
5171558Srgrimes	mode = curfile.dip->di_mode;
5181558Srgrimes	switch (mode & IFMT) {
5191558Srgrimes
5201558Srgrimes	default:
5211558Srgrimes		fprintf(stderr, "%s: unknown file mode 0%o\n", name, mode);
5221558Srgrimes		skipfile();
5231558Srgrimes		return (FAIL);
5241558Srgrimes
5251558Srgrimes	case IFSOCK:
5261558Srgrimes		vprintf(stdout, "skipped socket %s\n", name);
5271558Srgrimes		skipfile();
5281558Srgrimes		return (GOOD);
5291558Srgrimes
5301558Srgrimes	case IFDIR:
5311558Srgrimes		if (mflag) {
5321558Srgrimes			ep = lookupname(name);
5331558Srgrimes			if (ep == NULL || ep->e_flags & EXTRACT)
5341558Srgrimes				panic("unextracted directory %s\n", name);
5351558Srgrimes			skipfile();
5361558Srgrimes			return (GOOD);
5371558Srgrimes		}
5381558Srgrimes		vprintf(stdout, "extract file %s\n", name);
5391558Srgrimes		return (genliteraldir(name, curfile.ino));
5401558Srgrimes
5411558Srgrimes	case IFLNK:
5421558Srgrimes		lnkbuf[0] = '\0';
5431558Srgrimes		pathlen = 0;
5441558Srgrimes		getfile(xtrlnkfile, xtrlnkskip);
5451558Srgrimes		if (pathlen == 0) {
5461558Srgrimes			vprintf(stdout,
5471558Srgrimes			    "%s: zero length symbolic link (ignored)\n", name);
5481558Srgrimes			return (GOOD);
5491558Srgrimes		}
5501558Srgrimes		return (linkit(lnkbuf, name, SYMLINK));
5511558Srgrimes
5526305Smartin	case IFIFO:
5536305Smartin		if (mkfifo(name, mode) < 0) {
5546305Smartin			fprintf(stderr, "%s: cannot create FIFO: %s\n",
5556305Smartin				name, strerror(errno));
5566305Smartin			skipfile();
5576305Smartin			return (FAIL);
5586305Smartin		}
5596305Smartin		(void) chown(name, curfile.dip->di_uid, curfile.dip->di_gid);
5606305Smartin		(void) chmod(name, mode);
5616305Smartin		skipfile();
5626305Smartin		utimes(name, timep);
5636305Smartin		return (GOOD);
5646305Smartin
5651558Srgrimes	case IFCHR:
5661558Srgrimes	case IFBLK:
5671558Srgrimes		vprintf(stdout, "extract special file %s\n", name);
5681558Srgrimes		if (Nflag) {
5691558Srgrimes			skipfile();
5701558Srgrimes			return (GOOD);
5711558Srgrimes		}
5721558Srgrimes		if (mknod(name, mode, (int)curfile.dip->di_rdev) < 0) {
5731558Srgrimes			fprintf(stderr, "%s: cannot create special file: %s\n",
5741558Srgrimes			    name, strerror(errno));
5751558Srgrimes			skipfile();
5761558Srgrimes			return (FAIL);
5771558Srgrimes		}
5781558Srgrimes		(void) chown(name, curfile.dip->di_uid, curfile.dip->di_gid);
5791558Srgrimes		(void) chmod(name, mode);
5801558Srgrimes		skipfile();
5811558Srgrimes		utimes(name, timep);
5821558Srgrimes		return (GOOD);
5831558Srgrimes
5841558Srgrimes	case IFREG:
5851558Srgrimes		vprintf(stdout, "extract file %s\n", name);
5861558Srgrimes		if (Nflag) {
5871558Srgrimes			skipfile();
5881558Srgrimes			return (GOOD);
5891558Srgrimes		}
59021149Simp		if ((ofile = open(name, O_WRONLY | O_CREAT | O_TRUNC,
59121149Simp		    0666)) < 0) {
5921558Srgrimes			fprintf(stderr, "%s: cannot create file: %s\n",
5931558Srgrimes			    name, strerror(errno));
5941558Srgrimes			skipfile();
5951558Srgrimes			return (FAIL);
5961558Srgrimes		}
5971558Srgrimes		(void) fchown(ofile, curfile.dip->di_uid, curfile.dip->di_gid);
5981558Srgrimes		(void) fchmod(ofile, mode);
5991558Srgrimes		getfile(xtrfile, xtrskip);
6001558Srgrimes		(void) close(ofile);
6011558Srgrimes		utimes(name, timep);
6021558Srgrimes		return (GOOD);
6031558Srgrimes	}
6041558Srgrimes	/* NOTREACHED */
6051558Srgrimes}
6061558Srgrimes
6071558Srgrimes/*
6081558Srgrimes * skip over bit maps on the tape
6091558Srgrimes */
6101558Srgrimesvoid
6111558Srgrimesskipmaps()
6121558Srgrimes{
6131558Srgrimes
6141558Srgrimes	while (spcl.c_type == TS_BITS || spcl.c_type == TS_CLRI)
6151558Srgrimes		skipfile();
6161558Srgrimes}
6171558Srgrimes
6181558Srgrimes/*
6191558Srgrimes * skip over a file on the tape
6201558Srgrimes */
6211558Srgrimesvoid
6221558Srgrimesskipfile()
6231558Srgrimes{
6241558Srgrimes
6251558Srgrimes	curfile.action = SKIP;
6261558Srgrimes	getfile(xtrnull, xtrnull);
6271558Srgrimes}
6281558Srgrimes
6291558Srgrimes/*
6301558Srgrimes * Extract a file from the tape.
6311558Srgrimes * When an allocated block is found it is passed to the fill function;
6321558Srgrimes * when an unallocated block (hole) is found, a zeroed buffer is passed
6331558Srgrimes * to the skip function.
6341558Srgrimes */
6351558Srgrimesvoid
6361558Srgrimesgetfile(fill, skip)
6371558Srgrimes	void	(*fill) __P((char *, long));
6381558Srgrimes	void	(*skip) __P((char *, long));
6391558Srgrimes{
6401558Srgrimes	register int i;
6411558Srgrimes	int curblk = 0;
6421558Srgrimes	long size = spcl.c_dinode.di_size;
6431558Srgrimes	static char clearedbuf[MAXBSIZE];
6441558Srgrimes	char buf[MAXBSIZE / TP_BSIZE][TP_BSIZE];
6451558Srgrimes	char junk[TP_BSIZE];
6461558Srgrimes
6471558Srgrimes	if (spcl.c_type == TS_END)
6481558Srgrimes		panic("ran off end of tape\n");
6491558Srgrimes	if (spcl.c_magic != NFS_MAGIC)
6501558Srgrimes		panic("not at beginning of a file\n");
6511558Srgrimes	if (!gettingfile && setjmp(restart) != 0)
6521558Srgrimes		return;
6531558Srgrimes	gettingfile++;
6541558Srgrimesloop:
6551558Srgrimes	for (i = 0; i < spcl.c_count; i++) {
6561558Srgrimes		if (spcl.c_addr[i]) {
6571558Srgrimes			readtape(&buf[curblk++][0]);
6581558Srgrimes			if (curblk == fssize / TP_BSIZE) {
6591558Srgrimes				(*fill)((char *)buf, size > TP_BSIZE ?
6601558Srgrimes				     (long) (fssize) :
6611558Srgrimes				     (curblk - 1) * TP_BSIZE + size);
6621558Srgrimes				curblk = 0;
6631558Srgrimes			}
6641558Srgrimes		} else {
6651558Srgrimes			if (curblk > 0) {
6661558Srgrimes				(*fill)((char *)buf, size > TP_BSIZE ?
6671558Srgrimes				     (long) (curblk * TP_BSIZE) :
6681558Srgrimes				     (curblk - 1) * TP_BSIZE + size);
6691558Srgrimes				curblk = 0;
6701558Srgrimes			}
6711558Srgrimes			(*skip)(clearedbuf, size > TP_BSIZE ?
6721558Srgrimes				(long) TP_BSIZE : size);
6731558Srgrimes		}
6741558Srgrimes		if ((size -= TP_BSIZE) <= 0) {
6751558Srgrimes			for (i++; i < spcl.c_count; i++)
6761558Srgrimes				if (spcl.c_addr[i])
6771558Srgrimes					readtape(junk);
6781558Srgrimes			break;
6791558Srgrimes		}
6801558Srgrimes	}
6811558Srgrimes	if (gethead(&spcl) == GOOD && size > 0) {
6821558Srgrimes		if (spcl.c_type == TS_ADDR)
6831558Srgrimes			goto loop;
6841558Srgrimes		dprintf(stdout,
6851558Srgrimes			"Missing address (header) block for %s at %d blocks\n",
6861558Srgrimes			curfile.name, blksread);
6871558Srgrimes	}
6881558Srgrimes	if (curblk > 0)
6891558Srgrimes		(*fill)((char *)buf, (curblk * TP_BSIZE) + size);
6901558Srgrimes	findinode(&spcl);
6911558Srgrimes	gettingfile = 0;
6921558Srgrimes}
6931558Srgrimes
6941558Srgrimes/*
6951558Srgrimes * Write out the next block of a file.
6961558Srgrimes */
6971558Srgrimesstatic void
6981558Srgrimesxtrfile(buf, size)
6991558Srgrimes	char	*buf;
7001558Srgrimes	long	size;
7011558Srgrimes{
7021558Srgrimes
7031558Srgrimes	if (Nflag)
7041558Srgrimes		return;
7051558Srgrimes	if (write(ofile, buf, (int) size) == -1) {
7061558Srgrimes		fprintf(stderr,
7071558Srgrimes		    "write error extracting inode %d, name %s\nwrite: %s\n",
7081558Srgrimes			curfile.ino, curfile.name, strerror(errno));
7091558Srgrimes		done(1);
7101558Srgrimes	}
7111558Srgrimes}
7121558Srgrimes
7131558Srgrimes/*
7141558Srgrimes * Skip over a hole in a file.
7151558Srgrimes */
7161558Srgrimes/* ARGSUSED */
7171558Srgrimesstatic void
7181558Srgrimesxtrskip(buf, size)
7191558Srgrimes	char *buf;
7201558Srgrimes	long size;
7211558Srgrimes{
7221558Srgrimes
7231558Srgrimes	if (lseek(ofile, size, SEEK_CUR) == -1) {
7241558Srgrimes		fprintf(stderr,
7251558Srgrimes		    "seek error extracting inode %d, name %s\nlseek: %s\n",
7261558Srgrimes			curfile.ino, curfile.name, strerror(errno));
7271558Srgrimes		done(1);
7281558Srgrimes	}
7291558Srgrimes}
7301558Srgrimes
7311558Srgrimes/*
7321558Srgrimes * Collect the next block of a symbolic link.
7331558Srgrimes */
7341558Srgrimesstatic void
7351558Srgrimesxtrlnkfile(buf, size)
7361558Srgrimes	char	*buf;
7371558Srgrimes	long	size;
7381558Srgrimes{
7391558Srgrimes
7401558Srgrimes	pathlen += size;
7411558Srgrimes	if (pathlen > MAXPATHLEN) {
7421558Srgrimes		fprintf(stderr, "symbolic link name: %s->%s%s; too long %d\n",
7431558Srgrimes		    curfile.name, lnkbuf, buf, pathlen);
7441558Srgrimes		done(1);
7451558Srgrimes	}
7461558Srgrimes	(void) strcat(lnkbuf, buf);
7471558Srgrimes}
7481558Srgrimes
7491558Srgrimes/*
7501558Srgrimes * Skip over a hole in a symbolic link (should never happen).
7511558Srgrimes */
7521558Srgrimes/* ARGSUSED */
7531558Srgrimesstatic void
7541558Srgrimesxtrlnkskip(buf, size)
7551558Srgrimes	char *buf;
7561558Srgrimes	long size;
7571558Srgrimes{
7581558Srgrimes
7591558Srgrimes	fprintf(stderr, "unallocated block in symbolic link %s\n",
7601558Srgrimes		curfile.name);
7611558Srgrimes	done(1);
7621558Srgrimes}
7631558Srgrimes
7641558Srgrimes/*
7651558Srgrimes * Collect the next block of a bit map.
7661558Srgrimes */
7671558Srgrimesstatic void
7681558Srgrimesxtrmap(buf, size)
7691558Srgrimes	char	*buf;
7701558Srgrimes	long	size;
7711558Srgrimes{
7721558Srgrimes
7731558Srgrimes	bcopy(buf, map, size);
7741558Srgrimes	map += size;
7751558Srgrimes}
7761558Srgrimes
7771558Srgrimes/*
7781558Srgrimes * Skip over a hole in a bit map (should never happen).
7791558Srgrimes */
7801558Srgrimes/* ARGSUSED */
7811558Srgrimesstatic void
7821558Srgrimesxtrmapskip(buf, size)
7831558Srgrimes	char *buf;
7841558Srgrimes	long size;
7851558Srgrimes{
7861558Srgrimes
7871558Srgrimes	panic("hole in map\n");
7881558Srgrimes	map += size;
7891558Srgrimes}
7901558Srgrimes
7911558Srgrimes/*
7921558Srgrimes * Noop, when an extraction function is not needed.
7931558Srgrimes */
7941558Srgrimes/* ARGSUSED */
7951558Srgrimesvoid
7961558Srgrimesxtrnull(buf, size)
7971558Srgrimes	char *buf;
7981558Srgrimes	long size;
7991558Srgrimes{
8001558Srgrimes
8011558Srgrimes	return;
8021558Srgrimes}
8031558Srgrimes
8041558Srgrimes/*
8051558Srgrimes * Read TP_BSIZE blocks from the input.
8061558Srgrimes * Handle read errors, and end of media.
8071558Srgrimes */
8081558Srgrimesstatic void
8091558Srgrimesreadtape(buf)
8101558Srgrimes	char *buf;
8111558Srgrimes{
8121558Srgrimes	long rd, newvol, i;
8131558Srgrimes	int cnt, seek_failed;
8141558Srgrimes
8151558Srgrimes	if (blkcnt < numtrec) {
8161558Srgrimes		bcopy(&tapebuf[(blkcnt++ * TP_BSIZE)], buf, (long)TP_BSIZE);
8171558Srgrimes		blksread++;
8181558Srgrimes		tpblksread++;
8191558Srgrimes		return;
8201558Srgrimes	}
8211558Srgrimes	for (i = 0; i < ntrec; i++)
8221558Srgrimes		((struct s_spcl *)&tapebuf[i * TP_BSIZE])->c_magic = 0;
8231558Srgrimes	if (numtrec == 0)
8241558Srgrimes		numtrec = ntrec;
8251558Srgrimes	cnt = ntrec * TP_BSIZE;
8261558Srgrimes	rd = 0;
8271558Srgrimesgetmore:
8281558Srgrimes#ifdef RRESTORE
8291558Srgrimes	if (host)
8301558Srgrimes		i = rmtread(&tapebuf[rd], cnt);
8311558Srgrimes	else
8321558Srgrimes#endif
8331558Srgrimes		i = read(mt, &tapebuf[rd], cnt);
8341558Srgrimes	/*
8351558Srgrimes	 * Check for mid-tape short read error.
8361558Srgrimes	 * If found, skip rest of buffer and start with the next.
8371558Srgrimes	 */
8381558Srgrimes	if (!pipein && numtrec < ntrec && i > 0) {
8391558Srgrimes		dprintf(stdout, "mid-media short read error.\n");
8401558Srgrimes		numtrec = ntrec;
8411558Srgrimes	}
8421558Srgrimes	/*
8431558Srgrimes	 * Handle partial block read.
8441558Srgrimes	 */
8451558Srgrimes	if (pipein && i == 0 && rd > 0)
8461558Srgrimes		i = rd;
8471558Srgrimes	else if (i > 0 && i != ntrec * TP_BSIZE) {
8481558Srgrimes		if (pipein) {
8491558Srgrimes			rd += i;
8501558Srgrimes			cnt -= i;
8511558Srgrimes			if (cnt > 0)
8521558Srgrimes				goto getmore;
8531558Srgrimes			i = rd;
8541558Srgrimes		} else {
8551558Srgrimes			/*
8561558Srgrimes			 * Short read. Process the blocks read.
8571558Srgrimes			 */
8581558Srgrimes			if (i % TP_BSIZE != 0)
8591558Srgrimes				vprintf(stdout,
8601558Srgrimes				    "partial block read: %d should be %d\n",
8611558Srgrimes				    i, ntrec * TP_BSIZE);
8621558Srgrimes			numtrec = i / TP_BSIZE;
8631558Srgrimes		}
8641558Srgrimes	}
8651558Srgrimes	/*
8661558Srgrimes	 * Handle read error.
8671558Srgrimes	 */
8681558Srgrimes	if (i < 0) {
8691558Srgrimes		fprintf(stderr, "Tape read error while ");
8701558Srgrimes		switch (curfile.action) {
8711558Srgrimes		default:
8721558Srgrimes			fprintf(stderr, "trying to set up tape\n");
8731558Srgrimes			break;
8741558Srgrimes		case UNKNOWN:
8751558Srgrimes			fprintf(stderr, "trying to resynchronize\n");
8761558Srgrimes			break;
8771558Srgrimes		case USING:
8781558Srgrimes			fprintf(stderr, "restoring %s\n", curfile.name);
8791558Srgrimes			break;
8801558Srgrimes		case SKIP:
8811558Srgrimes			fprintf(stderr, "skipping over inode %d\n",
8821558Srgrimes				curfile.ino);
8831558Srgrimes			break;
8841558Srgrimes		}
8851558Srgrimes		if (!yflag && !reply("continue"))
8861558Srgrimes			done(1);
8871558Srgrimes		i = ntrec * TP_BSIZE;
8881558Srgrimes		bzero(tapebuf, i);
8891558Srgrimes#ifdef RRESTORE
8901558Srgrimes		if (host)
8911558Srgrimes			seek_failed = (rmtseek(i, 1) < 0);
8921558Srgrimes		else
8931558Srgrimes#endif
8941558Srgrimes			seek_failed = (lseek(mt, i, SEEK_CUR) == (off_t)-1);
8951558Srgrimes
8961558Srgrimes		if (seek_failed) {
8971558Srgrimes			fprintf(stderr,
8981558Srgrimes			    "continuation failed: %s\n", strerror(errno));
8991558Srgrimes			done(1);
9001558Srgrimes		}
9011558Srgrimes	}
9021558Srgrimes	/*
9031558Srgrimes	 * Handle end of tape.
9041558Srgrimes	 */
9051558Srgrimes	if (i == 0) {
9061558Srgrimes		vprintf(stdout, "End-of-tape encountered\n");
9071558Srgrimes		if (!pipein) {
9081558Srgrimes			newvol = volno + 1;
9091558Srgrimes			volno = 0;
9101558Srgrimes			numtrec = 0;
9111558Srgrimes			getvol(newvol);
9121558Srgrimes			readtape(buf);
9131558Srgrimes			return;
9141558Srgrimes		}
9151558Srgrimes		if (rd % TP_BSIZE != 0)
9161558Srgrimes			panic("partial block read: %d should be %d\n",
9171558Srgrimes				rd, ntrec * TP_BSIZE);
9181558Srgrimes		terminateinput();
9191558Srgrimes		bcopy((char *)&endoftapemark, &tapebuf[rd], (long)TP_BSIZE);
9201558Srgrimes	}
9211558Srgrimes	blkcnt = 0;
9221558Srgrimes	bcopy(&tapebuf[(blkcnt++ * TP_BSIZE)], buf, (long)TP_BSIZE);
9231558Srgrimes	blksread++;
9241558Srgrimes	tpblksread++;
9251558Srgrimes}
9261558Srgrimes
9271558Srgrimesstatic void
9281558Srgrimesfindtapeblksize()
9291558Srgrimes{
9301558Srgrimes	register long i;
9311558Srgrimes
9321558Srgrimes	for (i = 0; i < ntrec; i++)
9331558Srgrimes		((struct s_spcl *)&tapebuf[i * TP_BSIZE])->c_magic = 0;
9341558Srgrimes	blkcnt = 0;
9351558Srgrimes#ifdef RRESTORE
9361558Srgrimes	if (host)
9371558Srgrimes		i = rmtread(tapebuf, ntrec * TP_BSIZE);
9381558Srgrimes	else
9391558Srgrimes#endif
9401558Srgrimes		i = read(mt, tapebuf, ntrec * TP_BSIZE);
9411558Srgrimes
9421558Srgrimes	if (i <= 0) {
9431558Srgrimes		fprintf(stderr, "tape read error: %s\n", strerror(errno));
9441558Srgrimes		done(1);
9451558Srgrimes	}
9461558Srgrimes	if (i % TP_BSIZE != 0) {
9471558Srgrimes		fprintf(stderr, "Tape block size (%d) %s (%d)\n",
9481558Srgrimes			i, "is not a multiple of dump block size", TP_BSIZE);
9491558Srgrimes		done(1);
9501558Srgrimes	}
9511558Srgrimes	ntrec = i / TP_BSIZE;
9521558Srgrimes	numtrec = ntrec;
9531558Srgrimes	vprintf(stdout, "Tape block size is %d\n", ntrec);
9541558Srgrimes}
9551558Srgrimes
9561558Srgrimesvoid
9571558Srgrimesclosemt()
9581558Srgrimes{
9591558Srgrimes
9601558Srgrimes	if (mt < 0)
9611558Srgrimes		return;
9621558Srgrimes#ifdef RRESTORE
9631558Srgrimes	if (host)
9641558Srgrimes		rmtclose();
9651558Srgrimes	else
9661558Srgrimes#endif
9671558Srgrimes		(void) close(mt);
9681558Srgrimes}
9691558Srgrimes
9701558Srgrimes/*
9711558Srgrimes * Read the next block from the tape.
9721558Srgrimes * Check to see if it is one of several vintage headers.
9731558Srgrimes * If it is an old style header, convert it to a new style header.
9741558Srgrimes * If it is not any valid header, return an error.
9751558Srgrimes */
9761558Srgrimesstatic int
9771558Srgrimesgethead(buf)
9781558Srgrimes	struct s_spcl *buf;
9791558Srgrimes{
9801558Srgrimes	long i;
9811558Srgrimes	union {
9821558Srgrimes		quad_t	qval;
9831558Srgrimes		long	val[2];
9841558Srgrimes	} qcvt;
9851558Srgrimes	union u_ospcl {
9861558Srgrimes		char dummy[TP_BSIZE];
9871558Srgrimes		struct	s_ospcl {
9881558Srgrimes			long	c_type;
9891558Srgrimes			long	c_date;
9901558Srgrimes			long	c_ddate;
9911558Srgrimes			long	c_volume;
9921558Srgrimes			long	c_tapea;
9931558Srgrimes			u_short	c_inumber;
9941558Srgrimes			long	c_magic;
9951558Srgrimes			long	c_checksum;
9961558Srgrimes			struct odinode {
9971558Srgrimes				unsigned short odi_mode;
9981558Srgrimes				u_short	odi_nlink;
9991558Srgrimes				u_short	odi_uid;
10001558Srgrimes				u_short	odi_gid;
10011558Srgrimes				long	odi_size;
10021558Srgrimes				long	odi_rdev;
10031558Srgrimes				char	odi_addr[36];
10041558Srgrimes				long	odi_atime;
10051558Srgrimes				long	odi_mtime;
10061558Srgrimes				long	odi_ctime;
10071558Srgrimes			} c_dinode;
10081558Srgrimes			long	c_count;
10091558Srgrimes			char	c_addr[256];
10101558Srgrimes		} s_ospcl;
10111558Srgrimes	} u_ospcl;
10121558Srgrimes
10131558Srgrimes	if (!cvtflag) {
10141558Srgrimes		readtape((char *)buf);
10151558Srgrimes		if (buf->c_magic != NFS_MAGIC) {
10161558Srgrimes			if (swabl(buf->c_magic) != NFS_MAGIC)
10171558Srgrimes				return (FAIL);
10181558Srgrimes			if (!Bcvt) {
10191558Srgrimes				vprintf(stdout, "Note: Doing Byte swapping\n");
10201558Srgrimes				Bcvt = 1;
10211558Srgrimes			}
10221558Srgrimes		}
10231558Srgrimes		if (checksum((int *)buf) == FAIL)
10241558Srgrimes			return (FAIL);
102523096Simp		if (Bcvt) {
10261558Srgrimes			swabst((u_char *)"8l4s31l", (u_char *)buf);
102723096Simp			swabst((u_char *)"l",(u_char *) &buf->c_level);
102823096Simp			swabst((u_char *)"2l",(u_char *) &buf->c_flags);
102923096Simp		}
10301558Srgrimes		goto good;
10311558Srgrimes	}
10321558Srgrimes	readtape((char *)(&u_ospcl.s_ospcl));
10331558Srgrimes	bzero((char *)buf, (long)TP_BSIZE);
10341558Srgrimes	buf->c_type = u_ospcl.s_ospcl.c_type;
10351558Srgrimes	buf->c_date = u_ospcl.s_ospcl.c_date;
10361558Srgrimes	buf->c_ddate = u_ospcl.s_ospcl.c_ddate;
10371558Srgrimes	buf->c_volume = u_ospcl.s_ospcl.c_volume;
10381558Srgrimes	buf->c_tapea = u_ospcl.s_ospcl.c_tapea;
10391558Srgrimes	buf->c_inumber = u_ospcl.s_ospcl.c_inumber;
10401558Srgrimes	buf->c_checksum = u_ospcl.s_ospcl.c_checksum;
10411558Srgrimes	buf->c_magic = u_ospcl.s_ospcl.c_magic;
10421558Srgrimes	buf->c_dinode.di_mode = u_ospcl.s_ospcl.c_dinode.odi_mode;
10431558Srgrimes	buf->c_dinode.di_nlink = u_ospcl.s_ospcl.c_dinode.odi_nlink;
10441558Srgrimes	buf->c_dinode.di_uid = u_ospcl.s_ospcl.c_dinode.odi_uid;
10451558Srgrimes	buf->c_dinode.di_gid = u_ospcl.s_ospcl.c_dinode.odi_gid;
10461558Srgrimes	buf->c_dinode.di_size = u_ospcl.s_ospcl.c_dinode.odi_size;
10471558Srgrimes	buf->c_dinode.di_rdev = u_ospcl.s_ospcl.c_dinode.odi_rdev;
104818406Snate	buf->c_dinode.di_atime.tv_sec = u_ospcl.s_ospcl.c_dinode.odi_atime;
104918406Snate	buf->c_dinode.di_mtime.tv_sec = u_ospcl.s_ospcl.c_dinode.odi_mtime;
105018406Snate	buf->c_dinode.di_ctime.tv_sec = u_ospcl.s_ospcl.c_dinode.odi_ctime;
10511558Srgrimes	buf->c_count = u_ospcl.s_ospcl.c_count;
10521558Srgrimes	bcopy(u_ospcl.s_ospcl.c_addr, buf->c_addr, (long)256);
10531558Srgrimes	if (u_ospcl.s_ospcl.c_magic != OFS_MAGIC ||
10541558Srgrimes	    checksum((int *)(&u_ospcl.s_ospcl)) == FAIL)
10551558Srgrimes		return(FAIL);
10561558Srgrimes	buf->c_magic = NFS_MAGIC;
10571558Srgrimes
10581558Srgrimesgood:
10591558Srgrimes	if ((buf->c_dinode.di_size == 0 || buf->c_dinode.di_size > 0xfffffff) &&
10601558Srgrimes	    (buf->c_dinode.di_mode & IFMT) == IFDIR && Qcvt == 0) {
10611558Srgrimes		qcvt.qval = buf->c_dinode.di_size;
10621558Srgrimes		if (qcvt.val[0] || qcvt.val[1]) {
10631558Srgrimes			printf("Note: Doing Quad swapping\n");
10641558Srgrimes			Qcvt = 1;
10651558Srgrimes		}
10661558Srgrimes	}
10671558Srgrimes	if (Qcvt) {
10681558Srgrimes		qcvt.qval = buf->c_dinode.di_size;
10691558Srgrimes		i = qcvt.val[1];
10701558Srgrimes		qcvt.val[1] = qcvt.val[0];
10711558Srgrimes		qcvt.val[0] = i;
10721558Srgrimes		buf->c_dinode.di_size = qcvt.qval;
10731558Srgrimes	}
10741558Srgrimes
10751558Srgrimes	switch (buf->c_type) {
10761558Srgrimes
10771558Srgrimes	case TS_CLRI:
10781558Srgrimes	case TS_BITS:
10791558Srgrimes		/*
10801558Srgrimes		 * Have to patch up missing information in bit map headers
10811558Srgrimes		 */
10821558Srgrimes		buf->c_inumber = 0;
10831558Srgrimes		buf->c_dinode.di_size = buf->c_count * TP_BSIZE;
10841558Srgrimes		for (i = 0; i < buf->c_count; i++)
10851558Srgrimes			buf->c_addr[i]++;
10861558Srgrimes		break;
10871558Srgrimes
10881558Srgrimes	case TS_TAPE:
10891558Srgrimes		if ((buf->c_flags & DR_NEWINODEFMT) == 0)
10901558Srgrimes			oldinofmt = 1;
10911558Srgrimes		/* fall through */
10921558Srgrimes	case TS_END:
10931558Srgrimes		buf->c_inumber = 0;
10941558Srgrimes		break;
10951558Srgrimes
10961558Srgrimes	case TS_INODE:
10971558Srgrimes	case TS_ADDR:
10981558Srgrimes		break;
10991558Srgrimes
11001558Srgrimes	default:
11011558Srgrimes		panic("gethead: unknown inode type %d\n", buf->c_type);
11021558Srgrimes		break;
11031558Srgrimes	}
11041558Srgrimes	/*
11058871Srgrimes	 * If we are restoring a filesystem with old format inodes,
11061558Srgrimes	 * copy the uid/gid to the new location.
11071558Srgrimes	 */
11081558Srgrimes	if (oldinofmt) {
11091558Srgrimes		buf->c_dinode.di_uid = buf->c_dinode.di_ouid;
11101558Srgrimes		buf->c_dinode.di_gid = buf->c_dinode.di_ogid;
11111558Srgrimes	}
11121558Srgrimes	if (dflag)
11131558Srgrimes		accthdr(buf);
11141558Srgrimes	return(GOOD);
11151558Srgrimes}
11161558Srgrimes
11171558Srgrimes/*
11181558Srgrimes * Check that a header is where it belongs and predict the next header
11191558Srgrimes */
11201558Srgrimesstatic void
11211558Srgrimesaccthdr(header)
11221558Srgrimes	struct s_spcl *header;
11231558Srgrimes{
11241558Srgrimes	static ino_t previno = 0x7fffffff;
11251558Srgrimes	static int prevtype;
11261558Srgrimes	static long predict;
11271558Srgrimes	long blks, i;
11281558Srgrimes
11291558Srgrimes	if (header->c_type == TS_TAPE) {
11301558Srgrimes		fprintf(stderr, "Volume header (%s inode format) ",
11311558Srgrimes		    oldinofmt ? "old" : "new");
11321558Srgrimes 		if (header->c_firstrec)
11331558Srgrimes 			fprintf(stderr, "begins with record %d",
11341558Srgrimes 				header->c_firstrec);
11351558Srgrimes 		fprintf(stderr, "\n");
11361558Srgrimes		previno = 0x7fffffff;
11371558Srgrimes		return;
11381558Srgrimes	}
11391558Srgrimes	if (previno == 0x7fffffff)
11401558Srgrimes		goto newcalc;
11411558Srgrimes	switch (prevtype) {
11421558Srgrimes	case TS_BITS:
11431558Srgrimes		fprintf(stderr, "Dump mask header");
11441558Srgrimes		break;
11451558Srgrimes	case TS_CLRI:
11461558Srgrimes		fprintf(stderr, "Remove mask header");
11471558Srgrimes		break;
11481558Srgrimes	case TS_INODE:
11491558Srgrimes		fprintf(stderr, "File header, ino %d", previno);
11501558Srgrimes		break;
11511558Srgrimes	case TS_ADDR:
11521558Srgrimes		fprintf(stderr, "File continuation header, ino %d", previno);
11531558Srgrimes		break;
11541558Srgrimes	case TS_END:
11551558Srgrimes		fprintf(stderr, "End of tape header");
11561558Srgrimes		break;
11571558Srgrimes	}
11581558Srgrimes	if (predict != blksread - 1)
11591558Srgrimes		fprintf(stderr, "; predicted %d blocks, got %d blocks",
11601558Srgrimes			predict, blksread - 1);
11611558Srgrimes	fprintf(stderr, "\n");
11621558Srgrimesnewcalc:
11631558Srgrimes	blks = 0;
11641558Srgrimes	if (header->c_type != TS_END)
11651558Srgrimes		for (i = 0; i < header->c_count; i++)
11661558Srgrimes			if (header->c_addr[i] != 0)
11671558Srgrimes				blks++;
11681558Srgrimes	predict = blks;
11691558Srgrimes	blksread = 0;
11701558Srgrimes	prevtype = header->c_type;
11711558Srgrimes	previno = header->c_inumber;
11721558Srgrimes}
11731558Srgrimes
11741558Srgrimes/*
11751558Srgrimes * Find an inode header.
11761558Srgrimes * Complain if had to skip, and complain is set.
11771558Srgrimes */
11781558Srgrimesstatic void
11791558Srgrimesfindinode(header)
11801558Srgrimes	struct s_spcl *header;
11811558Srgrimes{
11821558Srgrimes	static long skipcnt = 0;
11831558Srgrimes	long i;
11841558Srgrimes	char buf[TP_BSIZE];
11851558Srgrimes
11861558Srgrimes	curfile.name = "<name unknown>";
11871558Srgrimes	curfile.action = UNKNOWN;
11881558Srgrimes	curfile.dip = NULL;
11891558Srgrimes	curfile.ino = 0;
11901558Srgrimes	do {
11911558Srgrimes		if (header->c_magic != NFS_MAGIC) {
11921558Srgrimes			skipcnt++;
11931558Srgrimes			while (gethead(header) == FAIL ||
11941558Srgrimes			    header->c_date != dumpdate)
11951558Srgrimes				skipcnt++;
11961558Srgrimes		}
11971558Srgrimes		switch (header->c_type) {
11981558Srgrimes
11991558Srgrimes		case TS_ADDR:
12001558Srgrimes			/*
12011558Srgrimes			 * Skip up to the beginning of the next record
12021558Srgrimes			 */
12031558Srgrimes			for (i = 0; i < header->c_count; i++)
12041558Srgrimes				if (header->c_addr[i])
12051558Srgrimes					readtape(buf);
12061558Srgrimes			while (gethead(header) == FAIL ||
12071558Srgrimes			    header->c_date != dumpdate)
12081558Srgrimes				skipcnt++;
12091558Srgrimes			break;
12101558Srgrimes
12111558Srgrimes		case TS_INODE:
12121558Srgrimes			curfile.dip = &header->c_dinode;
12131558Srgrimes			curfile.ino = header->c_inumber;
12141558Srgrimes			break;
12151558Srgrimes
12161558Srgrimes		case TS_END:
12171558Srgrimes			curfile.ino = maxino;
12181558Srgrimes			break;
12191558Srgrimes
12201558Srgrimes		case TS_CLRI:
12211558Srgrimes			curfile.name = "<file removal list>";
12221558Srgrimes			break;
12231558Srgrimes
12241558Srgrimes		case TS_BITS:
12251558Srgrimes			curfile.name = "<file dump list>";
12261558Srgrimes			break;
12271558Srgrimes
12281558Srgrimes		case TS_TAPE:
12291558Srgrimes			panic("unexpected tape header\n");
12301558Srgrimes			/* NOTREACHED */
12311558Srgrimes
12321558Srgrimes		default:
12331558Srgrimes			panic("unknown tape header type %d\n", spcl.c_type);
12341558Srgrimes			/* NOTREACHED */
12351558Srgrimes
12361558Srgrimes		}
12371558Srgrimes	} while (header->c_type == TS_ADDR);
12381558Srgrimes	if (skipcnt > 0)
12391558Srgrimes		fprintf(stderr, "resync restore, skipped %d blocks\n", skipcnt);
12401558Srgrimes	skipcnt = 0;
12411558Srgrimes}
12421558Srgrimes
12431558Srgrimesstatic int
12441558Srgrimeschecksum(buf)
12451558Srgrimes	register int *buf;
12461558Srgrimes{
12471558Srgrimes	register int i, j;
12481558Srgrimes
12491558Srgrimes	j = sizeof(union u_spcl) / sizeof(int);
12501558Srgrimes	i = 0;
12511558Srgrimes	if(!Bcvt) {
12521558Srgrimes		do
12531558Srgrimes			i += *buf++;
12541558Srgrimes		while (--j);
12551558Srgrimes	} else {
12561558Srgrimes		/* What happens if we want to read restore tapes
12571558Srgrimes			for a 16bit int machine??? */
12588871Srgrimes		do
12591558Srgrimes			i += swabl(*buf++);
12601558Srgrimes		while (--j);
12611558Srgrimes	}
12628871Srgrimes
12631558Srgrimes	if (i != CHECKSUM) {
12641558Srgrimes		fprintf(stderr, "Checksum error %o, inode %d file %s\n", i,
12651558Srgrimes			curfile.ino, curfile.name);
12661558Srgrimes		return(FAIL);
12671558Srgrimes	}
12681558Srgrimes	return(GOOD);
12691558Srgrimes}
12701558Srgrimes
12711558Srgrimes#ifdef RRESTORE
12721558Srgrimes#if __STDC__
12731558Srgrimes#include <stdarg.h>
12741558Srgrimes#else
12751558Srgrimes#include <varargs.h>
12761558Srgrimes#endif
12771558Srgrimes
12781558Srgrimesvoid
12791558Srgrimes#if __STDC__
12801558Srgrimesmsg(const char *fmt, ...)
12811558Srgrimes#else
12821558Srgrimesmsg(fmt, va_alist)
12831558Srgrimes	char *fmt;
12841558Srgrimes	va_dcl
12851558Srgrimes#endif
12861558Srgrimes{
12871558Srgrimes	va_list ap;
12881558Srgrimes#if __STDC__
12891558Srgrimes	va_start(ap, fmt);
12901558Srgrimes#else
12911558Srgrimes	va_start(ap);
12921558Srgrimes#endif
12931558Srgrimes	(void)vfprintf(stderr, fmt, ap);
12941558Srgrimes	va_end(ap);
12951558Srgrimes}
12961558Srgrimes#endif /* RRESTORE */
12971558Srgrimes
12981558Srgrimesstatic u_char *
12991558Srgrimesswabshort(sp, n)
13001558Srgrimes	register u_char *sp;
13011558Srgrimes	register int n;
13021558Srgrimes{
13031558Srgrimes	char c;
13041558Srgrimes
13051558Srgrimes	while (--n >= 0) {
13061558Srgrimes		c = sp[0]; sp[0] = sp[1]; sp[1] = c;
13071558Srgrimes		sp += 2;
13081558Srgrimes	}
13091558Srgrimes	return (sp);
13101558Srgrimes}
13111558Srgrimes
13121558Srgrimesstatic u_char *
13131558Srgrimesswablong(sp, n)
13141558Srgrimes	register u_char *sp;
13151558Srgrimes	register int n;
13161558Srgrimes{
13171558Srgrimes	char c;
13181558Srgrimes
13191558Srgrimes	while (--n >= 0) {
13201558Srgrimes		c = sp[0]; sp[0] = sp[3]; sp[3] = c;
13211558Srgrimes		c = sp[2]; sp[2] = sp[1]; sp[1] = c;
13221558Srgrimes		sp += 4;
13231558Srgrimes	}
13241558Srgrimes	return (sp);
13251558Srgrimes}
13261558Srgrimes
13271558Srgrimesvoid
13281558Srgrimesswabst(cp, sp)
13291558Srgrimes	register u_char *cp, *sp;
13301558Srgrimes{
13311558Srgrimes	int n = 0;
13321558Srgrimes
13331558Srgrimes	while (*cp) {
13341558Srgrimes		switch (*cp) {
13351558Srgrimes		case '0': case '1': case '2': case '3': case '4':
13361558Srgrimes		case '5': case '6': case '7': case '8': case '9':
13371558Srgrimes			n = (n * 10) + (*cp++ - '0');
13381558Srgrimes			continue;
13398871Srgrimes
13401558Srgrimes		case 's': case 'w': case 'h':
13411558Srgrimes			if (n == 0)
13421558Srgrimes				n = 1;
13431558Srgrimes			sp = swabshort(sp, n);
13441558Srgrimes			break;
13451558Srgrimes
13461558Srgrimes		case 'l':
13471558Srgrimes			if (n == 0)
13481558Srgrimes				n = 1;
13491558Srgrimes			sp = swablong(sp, n);
13501558Srgrimes			break;
13511558Srgrimes
13521558Srgrimes		default: /* Any other character, like 'b' counts as byte. */
13531558Srgrimes			if (n == 0)
13541558Srgrimes				n = 1;
13551558Srgrimes			sp += n;
13561558Srgrimes			break;
13571558Srgrimes		}
13581558Srgrimes		cp++;
13591558Srgrimes		n = 0;
13601558Srgrimes	}
13611558Srgrimes}
13621558Srgrimes
13631558Srgrimesstatic u_long
13641558Srgrimesswabl(x)
13651558Srgrimes	u_long x;
13661558Srgrimes{
13671558Srgrimes	swabst((u_char *)"l", (u_char *)&x);
13681558Srgrimes	return (x);
13691558Srgrimes}
1370