11558Srgrimes/*
21558Srgrimes * Copyright (c) 1983, 1993
31558Srgrimes *	The Regents of the University of California.  All rights reserved.
41558Srgrimes * (c) UNIX System Laboratories, Inc.
51558Srgrimes * All or some portions of this file are derived from material licensed
61558Srgrimes * to the University of California by American Telephone and Telegraph
71558Srgrimes * Co. or Unix System Laboratories, Inc. and are reproduced herein with
81558Srgrimes * the permission of UNIX System Laboratories, Inc.
91558Srgrimes *
101558Srgrimes * Redistribution and use in source and binary forms, with or without
111558Srgrimes * modification, are permitted provided that the following conditions
121558Srgrimes * are met:
131558Srgrimes * 1. Redistributions of source code must retain the above copyright
141558Srgrimes *    notice, this list of conditions and the following disclaimer.
151558Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
161558Srgrimes *    notice, this list of conditions and the following disclaimer in the
171558Srgrimes *    documentation and/or other materials provided with the distribution.
181558Srgrimes * 4. Neither the name of the University nor the names of its contributors
191558Srgrimes *    may be used to endorse or promote products derived from this software
201558Srgrimes *    without specific prior written permission.
211558Srgrimes *
221558Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
231558Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
241558Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
251558Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
261558Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
271558Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
281558Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
291558Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
301558Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
311558Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
321558Srgrimes * SUCH DAMAGE.
331558Srgrimes */
341558Srgrimes
351558Srgrimes#ifndef lint
3637906Scharnier#if 0
3723685Speterstatic char sccsid[] = "@(#)tape.c	8.9 (Berkeley) 5/1/95";
3837906Scharnier#endif
391558Srgrimes#endif /* not lint */
401558Srgrimes
41146754Scharnier#include <sys/cdefs.h>
42146754Scharnier__FBSDID("$FreeBSD$");
43146754Scharnier
441558Srgrimes#include <sys/param.h>
451558Srgrimes#include <sys/file.h>
461558Srgrimes#include <sys/mtio.h>
471558Srgrimes#include <sys/stat.h>
4866907Swollman#include <sys/time.h>
49167011Smckusick#include <sys/extattr.h>
50167011Smckusick#include <sys/acl.h>
511558Srgrimes
52167259Smckusick#include <ufs/ufs/extattr.h>
531558Srgrimes#include <ufs/ufs/dinode.h>
541558Srgrimes#include <protocols/dumprestore.h>
551558Srgrimes
561558Srgrimes#include <errno.h>
57103949Smike#include <limits.h>
5873986Sobrien#include <paths.h>
591558Srgrimes#include <setjmp.h>
60164911Sdwmalone#include <stdint.h>
611558Srgrimes#include <stdio.h>
621558Srgrimes#include <stdlib.h>
631558Srgrimes#include <string.h>
6466907Swollman#include <time.h>
65129665Sstefanf#include <timeconv.h>
661558Srgrimes#include <unistd.h>
671558Srgrimes
681558Srgrimes#include "restore.h"
691558Srgrimes#include "extern.h"
701558Srgrimes
711558Srgrimesstatic long	fssize = MAXBSIZE;
721558Srgrimesstatic int	mt = -1;
731558Srgrimesstatic int	pipein = 0;
74128175Sgreenstatic int	pipecmdin = 0;
75128175Sgreenstatic FILE	*popenfp = NULL;
7621174Sguidostatic char	*magtape;
771558Srgrimesstatic int	blkcnt;
781558Srgrimesstatic int	numtrec;
791558Srgrimesstatic char	*tapebuf;
801558Srgrimesstatic union	u_spcl endoftapemark;
81164911Sdwmalonestatic long	byteslide = 0;
821558Srgrimesstatic long	blksread;		/* blocks read since last header */
8398542Smckusickstatic int64_t	tapeaddr = 0;		/* current TP_BSIZE tape record */
841558Srgrimesstatic long	tapesread;
851558Srgrimesstatic jmp_buf	restart;
861558Srgrimesstatic int	gettingfile = 0;	/* restart has a valid frame */
871558Srgrimesstatic char	*host = NULL;
8898542Smckusickstatic int	readmapflag;
891558Srgrimes
901558Srgrimesstatic int	ofile;
911558Srgrimesstatic char	*map;
921558Srgrimesstatic char	lnkbuf[MAXPATHLEN + 1];
931558Srgrimesstatic int	pathlen;
941558Srgrimes
9598542Smckusickint		Bcvt;		/* Swap Bytes */
96144099Simpint		oldinofmt;	/* FreeBSD 1 inode format needs cvt */
971558Srgrimes
981558Srgrimes#define	FLUSHTAPEBUF()	blkcnt = ntrec + 1
991558Srgrimes
100167011Smckusickchar *namespace_names[] = EXTATTR_NAMESPACE_NAMES;
101167011Smckusick
10292837Simpstatic void	 accthdr(struct s_spcl *);
10392837Simpstatic int	 checksum(int *);
10492837Simpstatic void	 findinode(struct s_spcl *);
10592837Simpstatic void	 findtapeblksize(void);
106167011Smckusickstatic char	*setupextattr(int);
107167011Smckusickstatic void	 xtrattr(char *, long);
108167011Smckusickstatic void	 set_extattr_link(char *, void *, int);
109167011Smckusickstatic void	 set_extattr_fd(int, char *, void *, int);
11092837Simpstatic int	 gethead(struct s_spcl *);
11192837Simpstatic void	 readtape(char *);
11292837Simpstatic void	 setdumpnum(void);
11392837Simpstatic u_long	 swabl(u_long);
11492837Simpstatic u_char	*swablong(u_char *, int);
11592837Simpstatic u_char	*swabshort(u_char *, int);
11692837Simpstatic void	 terminateinput(void);
11792837Simpstatic void	 xtrfile(char *, long);
11892837Simpstatic void	 xtrlnkfile(char *, long);
11992837Simpstatic void	 xtrlnkskip(char *, long);
12092837Simpstatic void	 xtrmap(char *, long);
12192837Simpstatic void	 xtrmapskip(char *, long);
12292837Simpstatic void	 xtrskip(char *, long);
1231558Srgrimes
1241558Srgrimes/*
1251558Srgrimes * Set up an input source
1261558Srgrimes */
1271558Srgrimesvoid
128128175Sgreensetinput(char *source, int ispipecommand)
1291558Srgrimes{
1301558Srgrimes	FLUSHTAPEBUF();
1311558Srgrimes	if (bflag)
1321558Srgrimes		newtapebuf(ntrec);
1331558Srgrimes	else
1341558Srgrimes		newtapebuf(NTREC > HIGHDENSITYTREC ? NTREC : HIGHDENSITYTREC);
1351558Srgrimes	terminal = stdin;
1361558Srgrimes
137128175Sgreen	if (ispipecommand)
138128175Sgreen		pipecmdin++;
139128175Sgreen	else
1401558Srgrimes#ifdef RRESTORE
14123685Speter	if (strchr(source, ':')) {
1421558Srgrimes		host = source;
14323685Speter		source = strchr(host, ':');
1441558Srgrimes		*source++ = '\0';
1451558Srgrimes		if (rmthost(host) == 0)
1461558Srgrimes			done(1);
1471558Srgrimes	} else
1481558Srgrimes#endif
1491558Srgrimes	if (strcmp(source, "-") == 0) {
1501558Srgrimes		/*
1511558Srgrimes		 * Since input is coming from a pipe we must establish
1521558Srgrimes		 * our own connection to the terminal.
1531558Srgrimes		 */
1541558Srgrimes		terminal = fopen(_PATH_TTY, "r");
1551558Srgrimes		if (terminal == NULL) {
1561558Srgrimes			(void)fprintf(stderr, "cannot open %s: %s\n",
1571558Srgrimes			    _PATH_TTY, strerror(errno));
1581558Srgrimes			terminal = fopen(_PATH_DEVNULL, "r");
1591558Srgrimes			if (terminal == NULL) {
1601558Srgrimes				(void)fprintf(stderr, "cannot open %s: %s\n",
1611558Srgrimes				    _PATH_DEVNULL, strerror(errno));
1621558Srgrimes				done(1);
1631558Srgrimes			}
1641558Srgrimes		}
1651558Srgrimes		pipein++;
1661558Srgrimes	}
167241848Seadler	/* no longer need or want root privileges */
168241848Seadler	if (setuid(getuid()) != 0) {
169241848Seadler		fprintf(stderr, "setuid failed\n");
170241848Seadler		done(1);
171241848Seadler	}
17221174Sguido	magtape = strdup(source);
17321174Sguido	if (magtape == NULL) {
17421174Sguido		fprintf(stderr, "Cannot allocate space for magtape buffer\n");
17521174Sguido		done(1);
17621174Sguido	}
1771558Srgrimes}
1781558Srgrimes
1791558Srgrimesvoid
18092837Simpnewtapebuf(long size)
1811558Srgrimes{
18292837Simp	static int tapebufsize = -1;
1831558Srgrimes
1841558Srgrimes	ntrec = size;
1851558Srgrimes	if (size <= tapebufsize)
1861558Srgrimes		return;
1871558Srgrimes	if (tapebuf != NULL)
188164911Sdwmalone		free(tapebuf - TP_BSIZE);
189164911Sdwmalone	tapebuf = malloc((size+1) * TP_BSIZE);
1901558Srgrimes	if (tapebuf == NULL) {
1911558Srgrimes		fprintf(stderr, "Cannot allocate space for tape buffer\n");
1921558Srgrimes		done(1);
1931558Srgrimes	}
194164911Sdwmalone	tapebuf += TP_BSIZE;
1951558Srgrimes	tapebufsize = size;
1961558Srgrimes}
1971558Srgrimes
1981558Srgrimes/*
1991558Srgrimes * Verify that the tape drive can be accessed and
2001558Srgrimes * that it actually is a dump tape.
2011558Srgrimes */
2021558Srgrimesvoid
20392837Simpsetup(void)
2041558Srgrimes{
2051558Srgrimes	int i, j, *ip;
2061558Srgrimes	struct stat stbuf;
2071558Srgrimes
2081558Srgrimes	vprintf(stdout, "Verify tape and initialize maps\n");
209128175Sgreen	if (pipecmdin) {
210128175Sgreen		if (setenv("RESTORE_VOLUME", "1", 1) == -1) {
211128175Sgreen			fprintf(stderr, "Cannot set $RESTORE_VOLUME: %s\n",
212128175Sgreen			    strerror(errno));
213128175Sgreen			done(1);
214128175Sgreen		}
215128175Sgreen		popenfp = popen(magtape, "r");
216128175Sgreen		mt = popenfp ? fileno(popenfp) : -1;
217128175Sgreen	} else
2181558Srgrimes#ifdef RRESTORE
2191558Srgrimes	if (host)
2201558Srgrimes		mt = rmtopen(magtape, 0);
2211558Srgrimes	else
2221558Srgrimes#endif
2231558Srgrimes	if (pipein)
2241558Srgrimes		mt = 0;
2251558Srgrimes	else
2261558Srgrimes		mt = open(magtape, O_RDONLY, 0);
2271558Srgrimes	if (mt < 0) {
2281558Srgrimes		fprintf(stderr, "%s: %s\n", magtape, strerror(errno));
2291558Srgrimes		done(1);
2301558Srgrimes	}
2311558Srgrimes	volno = 1;
2321558Srgrimes	setdumpnum();
2331558Srgrimes	FLUSHTAPEBUF();
234203816Sjh	if (!pipein && !pipecmdin && !bflag)
2351558Srgrimes		findtapeblksize();
2361558Srgrimes	if (gethead(&spcl) == FAIL) {
23798542Smckusick		fprintf(stderr, "Tape is not a dump tape\n");
23898542Smckusick		done(1);
2391558Srgrimes	}
2401558Srgrimes	if (pipein) {
24198542Smckusick		endoftapemark.s_spcl.c_magic = FS_UFS2_MAGIC;
2421558Srgrimes		endoftapemark.s_spcl.c_type = TS_END;
2431558Srgrimes		ip = (int *)&endoftapemark;
2441558Srgrimes		j = sizeof(union u_spcl) / sizeof(int);
2451558Srgrimes		i = 0;
2461558Srgrimes		do
2471558Srgrimes			i += *ip++;
2481558Srgrimes		while (--j);
2491558Srgrimes		endoftapemark.s_spcl.c_checksum = CHECKSUM - i;
2501558Srgrimes	}
2511558Srgrimes	if (vflag || command == 't')
2521558Srgrimes		printdumpinfo();
25398542Smckusick	dumptime = _time64_to_time(spcl.c_ddate);
25498542Smckusick	dumpdate = _time64_to_time(spcl.c_date);
2551558Srgrimes	if (stat(".", &stbuf) < 0) {
2561558Srgrimes		fprintf(stderr, "cannot stat .: %s\n", strerror(errno));
2571558Srgrimes		done(1);
2581558Srgrimes	}
25934851Sjkh	if (stbuf.st_blksize > 0 && stbuf.st_blksize < TP_BSIZE )
26034851Sjkh		fssize = TP_BSIZE;
26134851Sjkh	if (stbuf.st_blksize >= TP_BSIZE && stbuf.st_blksize <= MAXBSIZE)
2621558Srgrimes		fssize = stbuf.st_blksize;
263269651Smckusick	if (((TP_BSIZE - 1) & stbuf.st_blksize) != 0) {
264269651Smckusick		fprintf(stderr, "Warning: filesystem with non-multiple-of-%d "
265269651Smckusick		    "blocksize (%d);\n", TP_BSIZE, stbuf.st_blksize);
266269651Smckusick		fssize = roundup(fssize, TP_BSIZE);
267269651Smckusick		fprintf(stderr, "\twriting using blocksize %ld\n", fssize);
2681558Srgrimes	}
2691558Srgrimes	if (spcl.c_volume != 1) {
2701558Srgrimes		fprintf(stderr, "Tape is not volume 1 of the dump\n");
2711558Srgrimes		done(1);
2721558Srgrimes	}
2731558Srgrimes	if (gethead(&spcl) == FAIL) {
27437240Sbde		dprintf(stdout, "header read failed at %ld blocks\n", blksread);
2751558Srgrimes		panic("no header after volume mark!\n");
2761558Srgrimes	}
2771558Srgrimes	findinode(&spcl);
2781558Srgrimes	if (spcl.c_type != TS_CLRI) {
2791558Srgrimes		fprintf(stderr, "Cannot find file removal list\n");
2801558Srgrimes		done(1);
2811558Srgrimes	}
2821558Srgrimes	maxino = (spcl.c_count * TP_BSIZE * NBBY) + 1;
283241013Smdf	dprintf(stdout, "maxino = %ju\n", (uintmax_t)maxino);
2841558Srgrimes	map = calloc((unsigned)1, (unsigned)howmany(maxino, NBBY));
2851558Srgrimes	if (map == NULL)
28623685Speter		panic("no memory for active inode map\n");
28723685Speter	usedinomap = map;
2881558Srgrimes	curfile.action = USING;
289167011Smckusick	getfile(xtrmap, xtrmapskip, xtrmapskip);
2901558Srgrimes	if (spcl.c_type != TS_BITS) {
2911558Srgrimes		fprintf(stderr, "Cannot find file dump list\n");
2921558Srgrimes		done(1);
2931558Srgrimes	}
2941558Srgrimes	map = calloc((unsigned)1, (unsigned)howmany(maxino, NBBY));
2951558Srgrimes	if (map == (char *)NULL)
2961558Srgrimes		panic("no memory for file dump list\n");
2971558Srgrimes	dumpmap = map;
2981558Srgrimes	curfile.action = USING;
299167011Smckusick	getfile(xtrmap, xtrmapskip, xtrmapskip);
30023685Speter	/*
30123685Speter	 * If there may be whiteout entries on the tape, pretend that the
30223685Speter	 * whiteout inode exists, so that the whiteout entries can be
30323685Speter	 * extracted.
30423685Speter	 */
30598542Smckusick	SETINO(WINO, dumpmap);
30690820Siedowse	/* 'r' restores don't call getvol() for tape 1, so mark it as read. */
30790820Siedowse	if (command == 'r')
30890820Siedowse		tapesread = 1;
3091558Srgrimes}
3101558Srgrimes
3111558Srgrimes/*
3121558Srgrimes * Prompt user to load a new dump volume.
3131558Srgrimes * "Nextvol" is the next suggested volume to use.
3141558Srgrimes * This suggested volume is enforced when doing full
31537906Scharnier * or incremental restores, but can be overridden by
3161558Srgrimes * the user when only extracting a subset of the files.
3171558Srgrimes */
3181558Srgrimesvoid
31992837Simpgetvol(long nextvol)
3201558Srgrimes{
32198542Smckusick	int64_t prevtapea;
32298542Smckusick	long i, newvol, savecnt;
3231558Srgrimes	union u_spcl tmpspcl;
3241558Srgrimes#	define tmpbuf tmpspcl.s_spcl
3251558Srgrimes	char buf[TP_BSIZE];
3261558Srgrimes
3271558Srgrimes	if (nextvol == 1) {
3281558Srgrimes		tapesread = 0;
3291558Srgrimes		gettingfile = 0;
3301558Srgrimes	}
33190827Siedowse	prevtapea = tapeaddr;
33290827Siedowse	savecnt = blksread;
3331558Srgrimes	if (pipein) {
33469906Siedowse		if (nextvol != 1) {
3351558Srgrimes			panic("Changing volumes on pipe input?\n");
33669906Siedowse			/* Avoid looping if we couldn't ask the user. */
33769906Siedowse			if (yflag || ferror(terminal) || feof(terminal))
33869906Siedowse				done(1);
33969906Siedowse		}
3401558Srgrimes		if (volno == 1)
3411558Srgrimes			return;
3421558Srgrimes		goto gethdr;
3431558Srgrimes	}
3441558Srgrimesagain:
3451558Srgrimes	if (pipein)
3461558Srgrimes		done(1); /* pipes do not get a second chance */
34790608Siedowse	if (command == 'R' || command == 'r' || curfile.action != SKIP)
3481558Srgrimes		newvol = nextvol;
34990608Siedowse	else
3501558Srgrimes		newvol = 0;
3511558Srgrimes	while (newvol <= 0) {
3521558Srgrimes		if (tapesread == 0) {
35390820Siedowse			fprintf(stderr, "%s%s%s%s%s%s%s",
3541558Srgrimes			    "You have not read any tapes yet.\n",
35590820Siedowse			    "If you are extracting just a few files,",
35690820Siedowse			    " start with the last volume\n",
35790820Siedowse			    "and work towards the first; restore",
35890820Siedowse			    " can quickly skip tapes that\n",
35990820Siedowse			    "have no further files to extract.",
36090820Siedowse			    " Otherwise, begin with volume 1.\n");
3611558Srgrimes		} else {
3621558Srgrimes			fprintf(stderr, "You have read volumes");
3631558Srgrimes			strcpy(buf, ": ");
36490820Siedowse			for (i = 0; i < 32; i++)
3651558Srgrimes				if (tapesread & (1 << i)) {
36690820Siedowse					fprintf(stderr, "%s%ld", buf, i + 1);
3671558Srgrimes					strcpy(buf, ", ");
3681558Srgrimes				}
3691558Srgrimes			fprintf(stderr, "\n");
3701558Srgrimes		}
3711558Srgrimes		do	{
3721558Srgrimes			fprintf(stderr, "Specify next volume #: ");
3731558Srgrimes			(void) fflush(stderr);
37469906Siedowse			if (fgets(buf, BUFSIZ, terminal) == NULL)
37569906Siedowse				done(1);
37669906Siedowse		} while (buf[0] == '\n');
3771558Srgrimes		newvol = atoi(buf);
3781558Srgrimes		if (newvol <= 0) {
3791558Srgrimes			fprintf(stderr,
3801558Srgrimes			    "Volume numbers are positive numerics\n");
3811558Srgrimes		}
3821558Srgrimes	}
3831558Srgrimes	if (newvol == volno) {
38490820Siedowse		tapesread |= 1 << (volno - 1);
3851558Srgrimes		return;
3861558Srgrimes	}
3871558Srgrimes	closemt();
38837240Sbde	fprintf(stderr, "Mount tape volume %ld\n", newvol);
3891558Srgrimes	fprintf(stderr, "Enter ``none'' if there are no more tapes\n");
3901558Srgrimes	fprintf(stderr, "otherwise enter tape name (default: %s) ", magtape);
3911558Srgrimes	(void) fflush(stderr);
39269906Siedowse	if (fgets(buf, BUFSIZ, terminal) == NULL)
3931558Srgrimes		done(1);
3941558Srgrimes	if (!strcmp(buf, "none\n")) {
3951558Srgrimes		terminateinput();
3961558Srgrimes		return;
3971558Srgrimes	}
3981558Srgrimes	if (buf[0] != '\n') {
3991558Srgrimes		(void) strcpy(magtape, buf);
4001558Srgrimes		magtape[strlen(magtape) - 1] = '\0';
4011558Srgrimes	}
402128175Sgreen	if (pipecmdin) {
403128175Sgreen		char volno[sizeof("2147483647")];
404128175Sgreen
405203155Sjh		(void)sprintf(volno, "%ld", newvol);
406128175Sgreen		if (setenv("RESTORE_VOLUME", volno, 1) == -1) {
407128175Sgreen			fprintf(stderr, "Cannot set $RESTORE_VOLUME: %s\n",
408128175Sgreen			    strerror(errno));
409128175Sgreen			done(1);
410128175Sgreen		}
411128175Sgreen		popenfp = popen(magtape, "r");
412128175Sgreen		mt = popenfp ? fileno(popenfp) : -1;
413128175Sgreen	} else
4141558Srgrimes#ifdef RRESTORE
4151558Srgrimes	if (host)
4161558Srgrimes		mt = rmtopen(magtape, 0);
4171558Srgrimes	else
4181558Srgrimes#endif
4191558Srgrimes		mt = open(magtape, O_RDONLY, 0);
4201558Srgrimes
4211558Srgrimes	if (mt == -1) {
4221558Srgrimes		fprintf(stderr, "Cannot open %s\n", magtape);
4231558Srgrimes		volno = -1;
4241558Srgrimes		goto again;
4251558Srgrimes	}
4261558Srgrimesgethdr:
4271558Srgrimes	volno = newvol;
4281558Srgrimes	setdumpnum();
4291558Srgrimes	FLUSHTAPEBUF();
4301558Srgrimes	if (gethead(&tmpbuf) == FAIL) {
43137240Sbde		dprintf(stdout, "header read failed at %ld blocks\n", blksread);
4321558Srgrimes		fprintf(stderr, "tape is not dump tape\n");
4331558Srgrimes		volno = 0;
4341558Srgrimes		goto again;
4351558Srgrimes	}
4361558Srgrimes	if (tmpbuf.c_volume != volno) {
437203155Sjh		fprintf(stderr, "Wrong volume (%jd)\n",
438203155Sjh		    (intmax_t)tmpbuf.c_volume);
4391558Srgrimes		volno = 0;
4401558Srgrimes		goto again;
4411558Srgrimes	}
44298542Smckusick	if (_time64_to_time(tmpbuf.c_date) != dumpdate ||
44398542Smckusick	    _time64_to_time(tmpbuf.c_ddate) != dumptime) {
44498542Smckusick		time_t t = _time64_to_time(tmpbuf.c_date);
44585635Sdillon		fprintf(stderr, "Wrong dump date\n\tgot: %s", ctime(&t));
4461558Srgrimes		fprintf(stderr, "\twanted: %s", ctime(&dumpdate));
4471558Srgrimes		volno = 0;
4481558Srgrimes		goto again;
4491558Srgrimes	}
45090820Siedowse	tapesread |= 1 << (volno - 1);
4511558Srgrimes	blksread = savecnt;
4521558Srgrimes 	/*
4531558Srgrimes 	 * If continuing from the previous volume, skip over any
4541558Srgrimes 	 * blocks read already at the end of the previous volume.
4551558Srgrimes 	 *
4561558Srgrimes 	 * If coming to this volume at random, skip to the beginning
4571558Srgrimes 	 * of the next record.
4581558Srgrimes 	 */
459203155Sjh	dprintf(stdout, "last rec %jd, tape starts with %jd\n",
460203155Sjh	    (intmax_t)prevtapea, (intmax_t)tmpbuf.c_tapea);
46198542Smckusick 	if (tmpbuf.c_type == TS_TAPE) {
46290608Siedowse 		if (curfile.action != USING) {
46390608Siedowse			/*
46490608Siedowse			 * XXX Dump incorrectly sets c_count to 1 in the
46590608Siedowse			 * volume header of the first tape, so ignore
46690608Siedowse			 * c_count when volno == 1.
46790608Siedowse			 */
46890608Siedowse			if (volno != 1)
46990608Siedowse				for (i = tmpbuf.c_count; i > 0; i--)
47090608Siedowse					readtape(buf);
47190827Siedowse 		} else if (tmpbuf.c_tapea <= prevtapea) {
4721558Srgrimes			/*
47390827Siedowse			 * Normally the value of c_tapea in the volume
47490827Siedowse			 * header is the record number of the header itself.
47590827Siedowse			 * However in the volume header following an EOT-
47690827Siedowse			 * terminated tape, it is the record number of the
47790827Siedowse			 * first continuation data block (dump bug?).
47890827Siedowse			 *
47990827Siedowse			 * The next record we want is `prevtapea + 1'.
4801558Srgrimes			 */
48190827Siedowse 			i = prevtapea + 1 - tmpbuf.c_tapea;
48237240Sbde			dprintf(stderr, "Skipping %ld duplicate record%s.\n",
4831558Srgrimes				i, i > 1 ? "s" : "");
4841558Srgrimes 			while (--i >= 0)
4851558Srgrimes 				readtape(buf);
4861558Srgrimes 		}
4871558Srgrimes 	}
48890608Siedowse	if (curfile.action == USING) {
4891558Srgrimes		if (volno == 1)
4901558Srgrimes			panic("active file into volume 1\n");
4911558Srgrimes		return;
4921558Srgrimes	}
4931558Srgrimes	(void) gethead(&spcl);
4941558Srgrimes	findinode(&spcl);
4951558Srgrimes	if (gettingfile) {
4961558Srgrimes		gettingfile = 0;
4971558Srgrimes		longjmp(restart, 1);
4981558Srgrimes	}
4991558Srgrimes}
5001558Srgrimes
5011558Srgrimes/*
5021558Srgrimes * Handle unexpected EOF.
5031558Srgrimes */
5041558Srgrimesstatic void
50592837Simpterminateinput(void)
5061558Srgrimes{
5071558Srgrimes
5081558Srgrimes	if (gettingfile && curfile.action == USING) {
5091558Srgrimes		printf("Warning: %s %s\n",
5101558Srgrimes		    "End-of-input encountered while extracting", curfile.name);
5111558Srgrimes	}
5121558Srgrimes	curfile.name = "<name unknown>";
5131558Srgrimes	curfile.action = UNKNOWN;
51498542Smckusick	curfile.mode = 0;
5151558Srgrimes	curfile.ino = maxino;
5161558Srgrimes	if (gettingfile) {
5171558Srgrimes		gettingfile = 0;
5181558Srgrimes		longjmp(restart, 1);
5191558Srgrimes	}
5201558Srgrimes}
5211558Srgrimes
5221558Srgrimes/*
5231558Srgrimes * handle multiple dumps per tape by skipping forward to the
5241558Srgrimes * appropriate one.
5251558Srgrimes */
5261558Srgrimesstatic void
52792837Simpsetdumpnum(void)
5281558Srgrimes{
5291558Srgrimes	struct mtop tcom;
5301558Srgrimes
5311558Srgrimes	if (dumpnum == 1 || volno != 1)
5321558Srgrimes		return;
5331558Srgrimes	if (pipein) {
5341558Srgrimes		fprintf(stderr, "Cannot have multiple dumps on pipe input\n");
5351558Srgrimes		done(1);
5361558Srgrimes	}
5371558Srgrimes	tcom.mt_op = MTFSF;
5381558Srgrimes	tcom.mt_count = dumpnum - 1;
5391558Srgrimes#ifdef RRESTORE
5401558Srgrimes	if (host)
5411558Srgrimes		rmtioctl(MTFSF, dumpnum - 1);
5428871Srgrimes	else
5431558Srgrimes#endif
544128175Sgreen		if (!pipecmdin && ioctl(mt, MTIOCTOP, (char *)&tcom) < 0)
5451558Srgrimes			fprintf(stderr, "ioctl MTFSF: %s\n", strerror(errno));
5461558Srgrimes}
5471558Srgrimes
5481558Srgrimesvoid
54992837Simpprintdumpinfo(void)
5501558Srgrimes{
55185635Sdillon	time_t t;
55298542Smckusick	t = _time64_to_time(spcl.c_date);
55385635Sdillon	fprintf(stdout, "Dump   date: %s", ctime(&t));
55498542Smckusick	t = _time64_to_time(spcl.c_ddate);
5551558Srgrimes	fprintf(stdout, "Dumped from: %s",
55685635Sdillon	    (spcl.c_ddate == 0) ? "the epoch\n" : ctime(&t));
5571558Srgrimes	if (spcl.c_host[0] == '\0')
5581558Srgrimes		return;
559203155Sjh	fprintf(stderr, "Level %jd dump of %s on %s:%s\n",
560203155Sjh	    (intmax_t)spcl.c_level, spcl.c_filesys, spcl.c_host, spcl.c_dev);
5611558Srgrimes	fprintf(stderr, "Label: %s\n", spcl.c_label);
5621558Srgrimes}
5631558Srgrimes
5641558Srgrimesint
56592837Simpextractfile(char *name)
5661558Srgrimes{
56723685Speter	int flags;
568161598Smaxim	uid_t uid;
569161598Smaxim	gid_t gid;
57023685Speter	mode_t mode;
571167011Smckusick	int extsize;
572100207Smckusick	struct timeval mtimep[2], ctimep[2];
5731558Srgrimes	struct entry *ep;
574167011Smckusick	char *buf;
5751558Srgrimes
5761558Srgrimes	curfile.name = name;
5771558Srgrimes	curfile.action = USING;
578100207Smckusick	mtimep[0].tv_sec = curfile.atime_sec;
579100207Smckusick	mtimep[0].tv_usec = curfile.atime_nsec / 1000;
580100207Smckusick	mtimep[1].tv_sec = curfile.mtime_sec;
581100207Smckusick	mtimep[1].tv_usec = curfile.mtime_nsec / 1000;
582100207Smckusick	ctimep[0].tv_sec = curfile.atime_sec;
583100207Smckusick	ctimep[0].tv_usec = curfile.atime_nsec / 1000;
584100207Smckusick	ctimep[1].tv_sec = curfile.birthtime_sec;
585100207Smckusick	ctimep[1].tv_usec = curfile.birthtime_nsec / 1000;
586167011Smckusick	extsize = curfile.extsize;
587178125Smckusick	uid = getuid();
588178125Smckusick	if (uid == 0)
589178125Smckusick		uid = curfile.uid;
590161598Smaxim	gid = curfile.gid;
59198542Smckusick	mode = curfile.mode;
59298542Smckusick	flags = curfile.file_flags;
5931558Srgrimes	switch (mode & IFMT) {
5941558Srgrimes
5951558Srgrimes	default:
5961558Srgrimes		fprintf(stderr, "%s: unknown file mode 0%o\n", name, mode);
5971558Srgrimes		skipfile();
5981558Srgrimes		return (FAIL);
5991558Srgrimes
6001558Srgrimes	case IFSOCK:
6011558Srgrimes		vprintf(stdout, "skipped socket %s\n", name);
6021558Srgrimes		skipfile();
6031558Srgrimes		return (GOOD);
6041558Srgrimes
6051558Srgrimes	case IFDIR:
6061558Srgrimes		if (mflag) {
6071558Srgrimes			ep = lookupname(name);
6081558Srgrimes			if (ep == NULL || ep->e_flags & EXTRACT)
6091558Srgrimes				panic("unextracted directory %s\n", name);
6101558Srgrimes			skipfile();
6111558Srgrimes			return (GOOD);
6121558Srgrimes		}
6131558Srgrimes		vprintf(stdout, "extract file %s\n", name);
6141558Srgrimes		return (genliteraldir(name, curfile.ino));
6151558Srgrimes
6161558Srgrimes	case IFLNK:
6171558Srgrimes		lnkbuf[0] = '\0';
6181558Srgrimes		pathlen = 0;
619167011Smckusick		buf = setupextattr(extsize);
620167011Smckusick		getfile(xtrlnkfile, xtrattr, xtrlnkskip);
6211558Srgrimes		if (pathlen == 0) {
6221558Srgrimes			vprintf(stdout,
6231558Srgrimes			    "%s: zero length symbolic link (ignored)\n", name);
6241558Srgrimes			return (GOOD);
6251558Srgrimes		}
62696113Siedowse		if (linkit(lnkbuf, name, SYMLINK) == GOOD) {
627167011Smckusick			if (extsize > 0)
628167011Smckusick				set_extattr_link(name, buf, extsize);
629161598Smaxim			(void) lchown(name, uid, gid);
63096113Siedowse			(void) lchmod(name, mode);
631100207Smckusick			(void) lutimes(name, ctimep);
632100207Smckusick			(void) lutimes(name, mtimep);
633161598Smaxim			(void) lchflags(name, flags);
63496113Siedowse			return (GOOD);
63595943Siedowse		}
63696113Siedowse		return (FAIL);
6371558Srgrimes
6386305Smartin	case IFIFO:
63923685Speter		vprintf(stdout, "extract fifo %s\n", name);
64023685Speter		if (Nflag) {
64123685Speter			skipfile();
64223685Speter			return (GOOD);
64323685Speter		}
644161598Smaxim		if (uflag)
645161598Smaxim			(void) unlink(name);
646161598Smaxim		if (mkfifo(name, 0600) < 0) {
64723685Speter			fprintf(stderr, "%s: cannot create fifo: %s\n",
64823685Speter			    name, strerror(errno));
6496305Smartin			skipfile();
6506305Smartin			return (FAIL);
6516305Smartin		}
652167011Smckusick		if (extsize == 0) {
653167011Smckusick			skipfile();
654167011Smckusick		} else {
655167011Smckusick			buf = setupextattr(extsize);
656167011Smckusick			getfile(xtrnull, xtrattr, xtrnull);
657167011Smckusick			set_extattr_file(name, buf, extsize);
658167011Smckusick		}
659161598Smaxim		(void) chown(name, uid, gid);
6606305Smartin		(void) chmod(name, mode);
661100207Smckusick		(void) utimes(name, ctimep);
662100207Smckusick		(void) utimes(name, mtimep);
66323685Speter		(void) chflags(name, flags);
6646305Smartin		return (GOOD);
6656305Smartin
6661558Srgrimes	case IFCHR:
6671558Srgrimes	case IFBLK:
6681558Srgrimes		vprintf(stdout, "extract special file %s\n", name);
6691558Srgrimes		if (Nflag) {
6701558Srgrimes			skipfile();
6711558Srgrimes			return (GOOD);
6721558Srgrimes		}
67335852Sjkh		if (uflag)
674161598Smaxim			(void) unlink(name);
675161598Smaxim		if (mknod(name, (mode & (IFCHR | IFBLK)) | 0600,
676161605Smaxim		    (int)curfile.rdev) < 0) {
6771558Srgrimes			fprintf(stderr, "%s: cannot create special file: %s\n",
6781558Srgrimes			    name, strerror(errno));
6791558Srgrimes			skipfile();
6801558Srgrimes			return (FAIL);
6811558Srgrimes		}
682167011Smckusick		if (extsize == 0) {
683167011Smckusick			skipfile();
684167011Smckusick		} else {
685167011Smckusick			buf = setupextattr(extsize);
686167011Smckusick			getfile(xtrnull, xtrattr, xtrnull);
687167011Smckusick			set_extattr_file(name, buf, extsize);
688167011Smckusick		}
689161598Smaxim		(void) chown(name, uid, gid);
6901558Srgrimes		(void) chmod(name, mode);
691100207Smckusick		(void) utimes(name, ctimep);
692100207Smckusick		(void) utimes(name, mtimep);
69323685Speter		(void) chflags(name, flags);
6941558Srgrimes		return (GOOD);
6951558Srgrimes
6961558Srgrimes	case IFREG:
6971558Srgrimes		vprintf(stdout, "extract file %s\n", name);
6981558Srgrimes		if (Nflag) {
6991558Srgrimes			skipfile();
7001558Srgrimes			return (GOOD);
7011558Srgrimes		}
70235852Sjkh		if (uflag)
703161598Smaxim			(void) unlink(name);
70421149Simp		if ((ofile = open(name, O_WRONLY | O_CREAT | O_TRUNC,
705161598Smaxim		    0600)) < 0) {
7061558Srgrimes			fprintf(stderr, "%s: cannot create file: %s\n",
7071558Srgrimes			    name, strerror(errno));
7081558Srgrimes			skipfile();
7091558Srgrimes			return (FAIL);
7101558Srgrimes		}
711167011Smckusick		buf = setupextattr(extsize);
712167011Smckusick		getfile(xtrfile, xtrattr, xtrskip);
713167011Smckusick		if (extsize > 0)
714167011Smckusick			set_extattr_fd(ofile, name, buf, extsize);
715161598Smaxim		(void) fchown(ofile, uid, gid);
7161558Srgrimes		(void) fchmod(ofile, mode);
717161598Smaxim		(void) futimes(ofile, ctimep);
718161598Smaxim		(void) futimes(ofile, mtimep);
719161598Smaxim		(void) fchflags(ofile, flags);
7201558Srgrimes		(void) close(ofile);
7211558Srgrimes		return (GOOD);
7221558Srgrimes	}
7231558Srgrimes	/* NOTREACHED */
7241558Srgrimes}
7251558Srgrimes
7261558Srgrimes/*
727167011Smckusick * Set attributes for a file.
728167011Smckusick */
729167011Smckusickvoid
730167011Smckusickset_extattr_file(char *name, void *buf, int size)
731167011Smckusick{
732167011Smckusick	struct extattr *eap, *eaend;
733167011Smckusick
734167011Smckusick	vprintf(stdout, "Set attributes for %s:", name);
735167011Smckusick	eaend = buf + size;
736167011Smckusick	for (eap = buf; eap < eaend; eap = EXTATTR_NEXT(eap)) {
737167011Smckusick		/*
738167011Smckusick		 * Make sure this entry is complete.
739167011Smckusick		 */
740167011Smckusick		if (EXTATTR_NEXT(eap) > eaend || eap->ea_length <= 0) {
741167011Smckusick			dprintf(stdout, "\n\t%scorrupted",
742167011Smckusick				eap == buf ? "" : "remainder ");
743167011Smckusick			break;
744167011Smckusick		}
745167011Smckusick		if (eap->ea_namespace == EXTATTR_NAMESPACE_EMPTY)
746167011Smckusick			continue;
747167011Smckusick		vprintf(stdout, "\n\t%s, (%d bytes), %*s",
748167011Smckusick			namespace_names[eap->ea_namespace], eap->ea_length,
749167011Smckusick			eap->ea_namelength, eap->ea_name);
750167011Smckusick		/*
751167011Smckusick		 * First we try the general attribute setting interface.
752167011Smckusick		 * However, some attributes can only be set by root or
753167011Smckusick		 * by using special interfaces (for example, ACLs).
754167011Smckusick		 */
755167011Smckusick		if (extattr_set_file(name, eap->ea_namespace, eap->ea_name,
756167011Smckusick		    EXTATTR_CONTENT(eap), EXTATTR_CONTENT_SIZE(eap)) != -1) {
757167011Smckusick			dprintf(stdout, " (set using extattr_set_file)");
758167011Smckusick			continue;
759167011Smckusick		}
760167011Smckusick		/*
761167011Smckusick		 * If the general interface refuses to set the attribute,
762167011Smckusick		 * then we try all the specialized interfaces that we
763167011Smckusick		 * know about.
764167011Smckusick		 */
765167011Smckusick		if (eap->ea_namespace == EXTATTR_NAMESPACE_SYSTEM &&
766167011Smckusick		    !strcmp(eap->ea_name, POSIX1E_ACL_ACCESS_EXTATTR_NAME)) {
767167011Smckusick			if (acl_set_file(name, ACL_TYPE_ACCESS,
768167011Smckusick			    EXTATTR_CONTENT(eap)) != -1) {
769167011Smckusick				dprintf(stdout, " (set using acl_set_file)");
770167011Smckusick				continue;
771167011Smckusick			}
772167011Smckusick		}
773167011Smckusick		if (eap->ea_namespace == EXTATTR_NAMESPACE_SYSTEM &&
774167011Smckusick		    !strcmp(eap->ea_name, POSIX1E_ACL_DEFAULT_EXTATTR_NAME)) {
775167011Smckusick			if (acl_set_file(name, ACL_TYPE_DEFAULT,
776167011Smckusick			    EXTATTR_CONTENT(eap)) != -1) {
777167011Smckusick				dprintf(stdout, " (set using acl_set_file)");
778167011Smckusick				continue;
779167011Smckusick			}
780167011Smckusick		}
781167011Smckusick		vprintf(stdout, " (unable to set)");
782167011Smckusick	}
783167011Smckusick	vprintf(stdout, "\n");
784167011Smckusick}
785167011Smckusick
786167011Smckusick/*
787167011Smckusick * Set attributes for a symbolic link.
788167011Smckusick */
789167011Smckusickstatic void
790167011Smckusickset_extattr_link(char *name, void *buf, int size)
791167011Smckusick{
792167011Smckusick	struct extattr *eap, *eaend;
793167011Smckusick
794167011Smckusick	vprintf(stdout, "Set attributes for %s:", name);
795167011Smckusick	eaend = buf + size;
796167011Smckusick	for (eap = buf; eap < eaend; eap = EXTATTR_NEXT(eap)) {
797167011Smckusick		/*
798167011Smckusick		 * Make sure this entry is complete.
799167011Smckusick		 */
800167011Smckusick		if (EXTATTR_NEXT(eap) > eaend || eap->ea_length <= 0) {
801167011Smckusick			dprintf(stdout, "\n\t%scorrupted",
802167011Smckusick				eap == buf ? "" : "remainder ");
803167011Smckusick			break;
804167011Smckusick		}
805167011Smckusick		if (eap->ea_namespace == EXTATTR_NAMESPACE_EMPTY)
806167011Smckusick			continue;
807167011Smckusick		vprintf(stdout, "\n\t%s, (%d bytes), %*s",
808167011Smckusick			namespace_names[eap->ea_namespace], eap->ea_length,
809167011Smckusick			eap->ea_namelength, eap->ea_name);
810167011Smckusick		/*
811167011Smckusick		 * First we try the general attribute setting interface.
812167011Smckusick		 * However, some attributes can only be set by root or
813167011Smckusick		 * by using special interfaces (for example, ACLs).
814167011Smckusick		 */
815167011Smckusick		if (extattr_set_link(name, eap->ea_namespace, eap->ea_name,
816167011Smckusick		    EXTATTR_CONTENT(eap), EXTATTR_CONTENT_SIZE(eap)) != -1) {
817167011Smckusick			dprintf(stdout, " (set using extattr_set_link)");
818167011Smckusick			continue;
819167011Smckusick		}
820167011Smckusick		/*
821167011Smckusick		 * If the general interface refuses to set the attribute,
822167011Smckusick		 * then we try all the specialized interfaces that we
823167011Smckusick		 * know about.
824167011Smckusick		 */
825167011Smckusick		if (eap->ea_namespace == EXTATTR_NAMESPACE_SYSTEM &&
826167011Smckusick		    !strcmp(eap->ea_name, POSIX1E_ACL_ACCESS_EXTATTR_NAME)) {
827167011Smckusick			if (acl_set_link_np(name, ACL_TYPE_ACCESS,
828167011Smckusick			    EXTATTR_CONTENT(eap)) != -1) {
829167011Smckusick				dprintf(stdout, " (set using acl_set_link_np)");
830167011Smckusick				continue;
831167011Smckusick			}
832167011Smckusick		}
833167011Smckusick		if (eap->ea_namespace == EXTATTR_NAMESPACE_SYSTEM &&
834167011Smckusick		    !strcmp(eap->ea_name, POSIX1E_ACL_DEFAULT_EXTATTR_NAME)) {
835167011Smckusick			if (acl_set_link_np(name, ACL_TYPE_DEFAULT,
836167011Smckusick			    EXTATTR_CONTENT(eap)) != -1) {
837167011Smckusick				dprintf(stdout, " (set using acl_set_link_np)");
838167011Smckusick				continue;
839167011Smckusick			}
840167011Smckusick		}
841167011Smckusick		vprintf(stdout, " (unable to set)");
842167011Smckusick	}
843167011Smckusick	vprintf(stdout, "\n");
844167011Smckusick}
845167011Smckusick
846167011Smckusick/*
847167011Smckusick * Set attributes on a file descriptor.
848167011Smckusick */
849167011Smckusickstatic void
850167011Smckusickset_extattr_fd(int fd, char *name, void *buf, int size)
851167011Smckusick{
852167011Smckusick	struct extattr *eap, *eaend;
853167011Smckusick
854167011Smckusick	vprintf(stdout, "Set attributes for %s:", name);
855167011Smckusick	eaend = buf + size;
856167011Smckusick	for (eap = buf; eap < eaend; eap = EXTATTR_NEXT(eap)) {
857167011Smckusick		/*
858167011Smckusick		 * Make sure this entry is complete.
859167011Smckusick		 */
860167011Smckusick		if (EXTATTR_NEXT(eap) > eaend || eap->ea_length <= 0) {
861167011Smckusick			dprintf(stdout, "\n\t%scorrupted",
862167011Smckusick				eap == buf ? "" : "remainder ");
863167011Smckusick			break;
864167011Smckusick		}
865167011Smckusick		if (eap->ea_namespace == EXTATTR_NAMESPACE_EMPTY)
866167011Smckusick			continue;
867167011Smckusick		vprintf(stdout, "\n\t%s, (%d bytes), %*s",
868167011Smckusick			namespace_names[eap->ea_namespace], eap->ea_length,
869167011Smckusick			eap->ea_namelength, eap->ea_name);
870167011Smckusick		/*
871167011Smckusick		 * First we try the general attribute setting interface.
872167011Smckusick		 * However, some attributes can only be set by root or
873167011Smckusick		 * by using special interfaces (for example, ACLs).
874167011Smckusick		 */
875167011Smckusick		if (extattr_set_fd(fd, eap->ea_namespace, eap->ea_name,
876167011Smckusick		    EXTATTR_CONTENT(eap), EXTATTR_CONTENT_SIZE(eap)) != -1) {
877167011Smckusick			dprintf(stdout, " (set using extattr_set_fd)");
878167011Smckusick			continue;
879167011Smckusick		}
880167011Smckusick		/*
881167011Smckusick		 * If the general interface refuses to set the attribute,
882167011Smckusick		 * then we try all the specialized interfaces that we
883167011Smckusick		 * know about.
884167011Smckusick		 */
885167011Smckusick		if (eap->ea_namespace == EXTATTR_NAMESPACE_SYSTEM &&
886167011Smckusick		    !strcmp(eap->ea_name, POSIX1E_ACL_ACCESS_EXTATTR_NAME)) {
887167011Smckusick			if (acl_set_fd(fd, EXTATTR_CONTENT(eap)) != -1) {
888167011Smckusick				dprintf(stdout, " (set using acl_set_fd)");
889167011Smckusick				continue;
890167011Smckusick			}
891167011Smckusick		}
892167011Smckusick		if (eap->ea_namespace == EXTATTR_NAMESPACE_SYSTEM &&
893167011Smckusick		    !strcmp(eap->ea_name, POSIX1E_ACL_DEFAULT_EXTATTR_NAME)) {
894167011Smckusick			if (acl_set_file(name, ACL_TYPE_DEFAULT,
895167011Smckusick			    EXTATTR_CONTENT(eap)) != -1) {
896167011Smckusick				dprintf(stdout, " (set using acl_set_file)");
897167011Smckusick				continue;
898167011Smckusick			}
899167011Smckusick		}
900167011Smckusick		vprintf(stdout, " (unable to set)");
901167011Smckusick	}
902167011Smckusick	vprintf(stdout, "\n");
903167011Smckusick}
904167011Smckusick
905167011Smckusick/*
9061558Srgrimes * skip over bit maps on the tape
9071558Srgrimes */
9081558Srgrimesvoid
90992837Simpskipmaps(void)
9101558Srgrimes{
9111558Srgrimes
9121558Srgrimes	while (spcl.c_type == TS_BITS || spcl.c_type == TS_CLRI)
9131558Srgrimes		skipfile();
9141558Srgrimes}
9151558Srgrimes
9161558Srgrimes/*
9171558Srgrimes * skip over a file on the tape
9181558Srgrimes */
9191558Srgrimesvoid
92092837Simpskipfile(void)
9211558Srgrimes{
9221558Srgrimes
9231558Srgrimes	curfile.action = SKIP;
924167011Smckusick	getfile(xtrnull, xtrnull, xtrnull);
9251558Srgrimes}
9261558Srgrimes
9271558Srgrimes/*
9281558Srgrimes * Extract a file from the tape.
9291558Srgrimes * When an allocated block is found it is passed to the fill function;
9301558Srgrimes * when an unallocated block (hole) is found, a zeroed buffer is passed
9311558Srgrimes * to the skip function.
9321558Srgrimes */
9331558Srgrimesvoid
934167011Smckusickgetfile(void (*datafill)(char *, long), void (*attrfill)(char *, long),
935167011Smckusick	void (*skip)(char *, long))
9361558Srgrimes{
93792806Sobrien	int i;
938167011Smckusick	off_t size;
939167011Smckusick	int curblk, attrsize;
940167011Smckusick	void (*fillit)(char *, long);
9411558Srgrimes	static char clearedbuf[MAXBSIZE];
9421558Srgrimes	char buf[MAXBSIZE / TP_BSIZE][TP_BSIZE];
9431558Srgrimes	char junk[TP_BSIZE];
9441558Srgrimes
945167011Smckusick	curblk = 0;
946167011Smckusick	size = spcl.c_size;
947167011Smckusick	attrsize = spcl.c_extsize;
9481558Srgrimes	if (spcl.c_type == TS_END)
9491558Srgrimes		panic("ran off end of tape\n");
95098542Smckusick	if (spcl.c_magic != FS_UFS2_MAGIC)
9511558Srgrimes		panic("not at beginning of a file\n");
9521558Srgrimes	if (!gettingfile && setjmp(restart) != 0)
9531558Srgrimes		return;
9541558Srgrimes	gettingfile++;
955167011Smckusick	fillit = datafill;
956167011Smckusick	if (size == 0 && attrsize > 0) {
957167011Smckusick		fillit = attrfill;
958167011Smckusick		size = attrsize;
959167011Smckusick		attrsize = 0;
960167011Smckusick	}
9611558Srgrimesloop:
9621558Srgrimes	for (i = 0; i < spcl.c_count; i++) {
963164911Sdwmalone		if (!readmapflag && i > TP_NINDIR) {
964164911Sdwmalone			if (Dflag) {
965164911Sdwmalone				fprintf(stderr, "spcl.c_count = %jd\n",
966164911Sdwmalone				    (intmax_t)spcl.c_count);
967164911Sdwmalone				break;
968164911Sdwmalone			} else
969164911Sdwmalone				panic("spcl.c_count = %jd\n",
970164911Sdwmalone				    (intmax_t)spcl.c_count);
971164911Sdwmalone		}
97237923Simp		if (readmapflag || spcl.c_addr[i]) {
9731558Srgrimes			readtape(&buf[curblk++][0]);
9741558Srgrimes			if (curblk == fssize / TP_BSIZE) {
975167011Smckusick				(*fillit)((char *)buf, (long)(size > TP_BSIZE ?
97623685Speter				     fssize : (curblk - 1) * TP_BSIZE + size));
9771558Srgrimes				curblk = 0;
9781558Srgrimes			}
9791558Srgrimes		} else {
9801558Srgrimes			if (curblk > 0) {
981167011Smckusick				(*fillit)((char *)buf, (long)(size > TP_BSIZE ?
98223685Speter				     curblk * TP_BSIZE :
98323685Speter				     (curblk - 1) * TP_BSIZE + size));
9841558Srgrimes				curblk = 0;
9851558Srgrimes			}
98623685Speter			(*skip)(clearedbuf, (long)(size > TP_BSIZE ?
98723685Speter				TP_BSIZE : size));
9881558Srgrimes		}
9891558Srgrimes		if ((size -= TP_BSIZE) <= 0) {
990167011Smckusick			if (size > -TP_BSIZE && curblk > 0) {
991167011Smckusick				(*fillit)((char *)buf,
992167011Smckusick					(long)((curblk * TP_BSIZE) + size));
993167011Smckusick				curblk = 0;
994167011Smckusick			}
995167011Smckusick			if (attrsize > 0) {
996167011Smckusick				fillit = attrfill;
997167011Smckusick				size = attrsize;
998167011Smckusick				attrsize = 0;
999167011Smckusick				continue;
1000167011Smckusick			}
1001167011Smckusick			if (spcl.c_count - i > 1)
1002167011Smckusick				dprintf(stdout, "skipping %d junk block(s)\n",
1003167011Smckusick					spcl.c_count - i - 1);
1004164911Sdwmalone			for (i++; i < spcl.c_count; i++) {
1005164911Sdwmalone				if (!readmapflag && i > TP_NINDIR) {
1006164911Sdwmalone					if (Dflag) {
1007164911Sdwmalone						fprintf(stderr,
1008164911Sdwmalone						    "spcl.c_count = %jd\n",
1009164911Sdwmalone						    (intmax_t)spcl.c_count);
1010164911Sdwmalone						break;
1011164911Sdwmalone					} else
1012164911Sdwmalone						panic("spcl.c_count = %jd\n",
1013164911Sdwmalone						    (intmax_t)spcl.c_count);
1014164911Sdwmalone				}
101537923Simp				if (readmapflag || spcl.c_addr[i])
10161558Srgrimes					readtape(junk);
1017164911Sdwmalone			}
10181558Srgrimes			break;
10191558Srgrimes		}
10201558Srgrimes	}
10211558Srgrimes	if (gethead(&spcl) == GOOD && size > 0) {
10221558Srgrimes		if (spcl.c_type == TS_ADDR)
10231558Srgrimes			goto loop;
10241558Srgrimes		dprintf(stdout,
102537240Sbde			"Missing address (header) block for %s at %ld blocks\n",
10261558Srgrimes			curfile.name, blksread);
10271558Srgrimes	}
10281558Srgrimes	if (curblk > 0)
1029167011Smckusick		panic("getfile: lost data\n");
10301558Srgrimes	findinode(&spcl);
10311558Srgrimes	gettingfile = 0;
10321558Srgrimes}
10331558Srgrimes
10341558Srgrimes/*
1035167011Smckusick * These variables are shared between the next two functions.
1036167011Smckusick */
1037167011Smckusickstatic int extbufsize = 0;
1038167011Smckusickstatic char *extbuf;
1039167011Smckusickstatic int extloc;
1040167011Smckusick
1041167011Smckusick/*
1042167011Smckusick * Allocate a buffer into which to extract extended attributes.
1043167011Smckusick */
1044167011Smckusickstatic char *
1045167011Smckusicksetupextattr(int extsize)
1046167011Smckusick{
1047167011Smckusick
1048167011Smckusick	extloc = 0;
1049167011Smckusick	if (extsize <= extbufsize)
1050167011Smckusick		return (extbuf);
1051167011Smckusick	if (extbufsize > 0)
1052167011Smckusick		free(extbuf);
1053167011Smckusick	if ((extbuf = malloc(extsize)) != NULL) {
1054167011Smckusick		extbufsize = extsize;
1055167011Smckusick		return (extbuf);
1056167011Smckusick	}
1057167011Smckusick	extbufsize = 0;
1058167011Smckusick	extbuf = NULL;
1059241013Smdf	fprintf(stderr, "Cannot extract %d bytes %s for inode %ju, name %s\n",
1060241013Smdf	    extsize, "of extended attributes", (uintmax_t)curfile.ino,
1061241013Smdf	    curfile.name);
1062167011Smckusick	return (NULL);
1063167011Smckusick}
1064167011Smckusick
1065167011Smckusick/*
1066167011Smckusick * Extract the next block of extended attributes.
1067167011Smckusick */
1068167011Smckusickstatic void
1069167011Smckusickxtrattr(char *buf, long size)
1070167011Smckusick{
1071167011Smckusick
1072167011Smckusick	if (extloc + size > extbufsize)
1073167011Smckusick		panic("overrun attribute buffer\n");
1074167011Smckusick	memmove(&extbuf[extloc], buf, size);
1075167011Smckusick	extloc += size;
1076167011Smckusick}
1077167011Smckusick
1078167011Smckusick/*
10791558Srgrimes * Write out the next block of a file.
10801558Srgrimes */
10811558Srgrimesstatic void
108292837Simpxtrfile(char *buf, long	size)
10831558Srgrimes{
10841558Srgrimes
10851558Srgrimes	if (Nflag)
10861558Srgrimes		return;
10871558Srgrimes	if (write(ofile, buf, (int) size) == -1) {
10881558Srgrimes		fprintf(stderr,
1089241013Smdf		    "write error extracting inode %ju, name %s\nwrite: %s\n",
1090241013Smdf		    (uintmax_t)curfile.ino, curfile.name, strerror(errno));
10911558Srgrimes	}
10921558Srgrimes}
10931558Srgrimes
10941558Srgrimes/*
10951558Srgrimes * Skip over a hole in a file.
10961558Srgrimes */
10971558Srgrimes/* ARGSUSED */
10981558Srgrimesstatic void
109992837Simpxtrskip(char *buf, long size)
11001558Srgrimes{
11011558Srgrimes
11021558Srgrimes	if (lseek(ofile, size, SEEK_CUR) == -1) {
11031558Srgrimes		fprintf(stderr,
1104241013Smdf		    "seek error extracting inode %ju, name %s\nlseek: %s\n",
1105241013Smdf		    (uintmax_t)curfile.ino, curfile.name, strerror(errno));
11061558Srgrimes		done(1);
11071558Srgrimes	}
11081558Srgrimes}
11091558Srgrimes
11101558Srgrimes/*
11111558Srgrimes * Collect the next block of a symbolic link.
11121558Srgrimes */
11131558Srgrimesstatic void
111492837Simpxtrlnkfile(char *buf, long size)
11151558Srgrimes{
11161558Srgrimes
11171558Srgrimes	pathlen += size;
11181558Srgrimes	if (pathlen > MAXPATHLEN) {
11191558Srgrimes		fprintf(stderr, "symbolic link name: %s->%s%s; too long %d\n",
11201558Srgrimes		    curfile.name, lnkbuf, buf, pathlen);
11211558Srgrimes		done(1);
11221558Srgrimes	}
11231558Srgrimes	(void) strcat(lnkbuf, buf);
11241558Srgrimes}
11251558Srgrimes
11261558Srgrimes/*
11271558Srgrimes * Skip over a hole in a symbolic link (should never happen).
11281558Srgrimes */
11291558Srgrimes/* ARGSUSED */
11301558Srgrimesstatic void
113192837Simpxtrlnkskip(char *buf, long size)
11321558Srgrimes{
11331558Srgrimes
11341558Srgrimes	fprintf(stderr, "unallocated block in symbolic link %s\n",
11351558Srgrimes		curfile.name);
11361558Srgrimes	done(1);
11371558Srgrimes}
11381558Srgrimes
11391558Srgrimes/*
11401558Srgrimes * Collect the next block of a bit map.
11411558Srgrimes */
11421558Srgrimesstatic void
114392837Simpxtrmap(char *buf, long size)
11441558Srgrimes{
11451558Srgrimes
114623685Speter	memmove(map, buf, size);
11471558Srgrimes	map += size;
11481558Srgrimes}
11491558Srgrimes
11501558Srgrimes/*
11511558Srgrimes * Skip over a hole in a bit map (should never happen).
11521558Srgrimes */
11531558Srgrimes/* ARGSUSED */
11541558Srgrimesstatic void
115592837Simpxtrmapskip(char *buf, long size)
11561558Srgrimes{
11571558Srgrimes
11581558Srgrimes	panic("hole in map\n");
11591558Srgrimes	map += size;
11601558Srgrimes}
11611558Srgrimes
11621558Srgrimes/*
11631558Srgrimes * Noop, when an extraction function is not needed.
11641558Srgrimes */
11651558Srgrimes/* ARGSUSED */
11661558Srgrimesvoid
116792837Simpxtrnull(char *buf, long size)
11681558Srgrimes{
11691558Srgrimes
11701558Srgrimes	return;
11711558Srgrimes}
11721558Srgrimes
11731558Srgrimes/*
11741558Srgrimes * Read TP_BSIZE blocks from the input.
11751558Srgrimes * Handle read errors, and end of media.
11761558Srgrimes */
11771558Srgrimesstatic void
117892837Simpreadtape(char *buf)
11791558Srgrimes{
1180164911Sdwmalone	long rd, newvol, i, oldnumtrec;
11811558Srgrimes	int cnt, seek_failed;
11821558Srgrimes
1183164911Sdwmalone	if (blkcnt + (byteslide > 0) < numtrec) {
1184164911Sdwmalone		memmove(buf, &tapebuf[(blkcnt++ * TP_BSIZE) + byteslide], (long)TP_BSIZE);
11851558Srgrimes		blksread++;
118690827Siedowse		tapeaddr++;
11871558Srgrimes		return;
11881558Srgrimes	}
1189164911Sdwmalone	if (numtrec > 0)
1190164911Sdwmalone		memmove(&tapebuf[-TP_BSIZE],
1191164911Sdwmalone		    &tapebuf[(numtrec-1) * TP_BSIZE], (long)TP_BSIZE);
1192164911Sdwmalone	oldnumtrec = numtrec;
11931558Srgrimes	for (i = 0; i < ntrec; i++)
11941558Srgrimes		((struct s_spcl *)&tapebuf[i * TP_BSIZE])->c_magic = 0;
11951558Srgrimes	if (numtrec == 0)
11961558Srgrimes		numtrec = ntrec;
11971558Srgrimes	cnt = ntrec * TP_BSIZE;
11981558Srgrimes	rd = 0;
11991558Srgrimesgetmore:
12001558Srgrimes#ifdef RRESTORE
12011558Srgrimes	if (host)
12021558Srgrimes		i = rmtread(&tapebuf[rd], cnt);
12031558Srgrimes	else
12041558Srgrimes#endif
12051558Srgrimes		i = read(mt, &tapebuf[rd], cnt);
12061558Srgrimes	/*
12071558Srgrimes	 * Check for mid-tape short read error.
12081558Srgrimes	 * If found, skip rest of buffer and start with the next.
12091558Srgrimes	 */
1210203157Sjh	if (!pipein && !pipecmdin && numtrec < ntrec && i > 0) {
12111558Srgrimes		dprintf(stdout, "mid-media short read error.\n");
12121558Srgrimes		numtrec = ntrec;
12131558Srgrimes	}
12141558Srgrimes	/*
12151558Srgrimes	 * Handle partial block read.
12161558Srgrimes	 */
1217203157Sjh	if ((pipein || pipecmdin) && i == 0 && rd > 0)
12181558Srgrimes		i = rd;
12191558Srgrimes	else if (i > 0 && i != ntrec * TP_BSIZE) {
1220203157Sjh		if (pipein || pipecmdin) {
12211558Srgrimes			rd += i;
12221558Srgrimes			cnt -= i;
12231558Srgrimes			if (cnt > 0)
12241558Srgrimes				goto getmore;
12251558Srgrimes			i = rd;
12261558Srgrimes		} else {
12271558Srgrimes			/*
12281558Srgrimes			 * Short read. Process the blocks read.
12291558Srgrimes			 */
12301558Srgrimes			if (i % TP_BSIZE != 0)
12311558Srgrimes				vprintf(stdout,
123237240Sbde				    "partial block read: %ld should be %ld\n",
12331558Srgrimes				    i, ntrec * TP_BSIZE);
12341558Srgrimes			numtrec = i / TP_BSIZE;
12351558Srgrimes		}
12361558Srgrimes	}
12371558Srgrimes	/*
12381558Srgrimes	 * Handle read error.
12391558Srgrimes	 */
12401558Srgrimes	if (i < 0) {
12411558Srgrimes		fprintf(stderr, "Tape read error while ");
12421558Srgrimes		switch (curfile.action) {
12431558Srgrimes		default:
12441558Srgrimes			fprintf(stderr, "trying to set up tape\n");
12451558Srgrimes			break;
12461558Srgrimes		case UNKNOWN:
12471558Srgrimes			fprintf(stderr, "trying to resynchronize\n");
12481558Srgrimes			break;
12491558Srgrimes		case USING:
12501558Srgrimes			fprintf(stderr, "restoring %s\n", curfile.name);
12511558Srgrimes			break;
12521558Srgrimes		case SKIP:
1253241013Smdf			fprintf(stderr, "skipping over inode %ju\n",
1254241013Smdf			    (uintmax_t)curfile.ino);
12551558Srgrimes			break;
12561558Srgrimes		}
12571558Srgrimes		if (!yflag && !reply("continue"))
12581558Srgrimes			done(1);
12591558Srgrimes		i = ntrec * TP_BSIZE;
126023685Speter		memset(tapebuf, 0, i);
12611558Srgrimes#ifdef RRESTORE
12621558Srgrimes		if (host)
12631558Srgrimes			seek_failed = (rmtseek(i, 1) < 0);
12641558Srgrimes		else
12651558Srgrimes#endif
12661558Srgrimes			seek_failed = (lseek(mt, i, SEEK_CUR) == (off_t)-1);
12671558Srgrimes
12681558Srgrimes		if (seek_failed) {
12691558Srgrimes			fprintf(stderr,
12701558Srgrimes			    "continuation failed: %s\n", strerror(errno));
12711558Srgrimes			done(1);
12721558Srgrimes		}
12731558Srgrimes	}
12741558Srgrimes	/*
12751558Srgrimes	 * Handle end of tape.
12761558Srgrimes	 */
12771558Srgrimes	if (i == 0) {
12781558Srgrimes		vprintf(stdout, "End-of-tape encountered\n");
12791558Srgrimes		if (!pipein) {
12801558Srgrimes			newvol = volno + 1;
12811558Srgrimes			volno = 0;
12821558Srgrimes			numtrec = 0;
12831558Srgrimes			getvol(newvol);
12841558Srgrimes			readtape(buf);
12851558Srgrimes			return;
12861558Srgrimes		}
12871558Srgrimes		if (rd % TP_BSIZE != 0)
1288203155Sjh			panic("partial block read: %ld should be %ld\n",
12891558Srgrimes				rd, ntrec * TP_BSIZE);
12901558Srgrimes		terminateinput();
129123685Speter		memmove(&tapebuf[rd], &endoftapemark, (long)TP_BSIZE);
12921558Srgrimes	}
1293164911Sdwmalone	if (oldnumtrec == 0)
1294164911Sdwmalone		blkcnt = 0;
1295164911Sdwmalone	else
1296164911Sdwmalone		blkcnt -= oldnumtrec;
1297164911Sdwmalone	memmove(buf,
1298164911Sdwmalone	    &tapebuf[(blkcnt++ * TP_BSIZE) + byteslide], (long)TP_BSIZE);
12991558Srgrimes	blksread++;
130090827Siedowse	tapeaddr++;
13011558Srgrimes}
13021558Srgrimes
13031558Srgrimesstatic void
130492837Simpfindtapeblksize(void)
13051558Srgrimes{
130692806Sobrien	long i;
13071558Srgrimes
13081558Srgrimes	for (i = 0; i < ntrec; i++)
13091558Srgrimes		((struct s_spcl *)&tapebuf[i * TP_BSIZE])->c_magic = 0;
13101558Srgrimes	blkcnt = 0;
13111558Srgrimes#ifdef RRESTORE
13121558Srgrimes	if (host)
13131558Srgrimes		i = rmtread(tapebuf, ntrec * TP_BSIZE);
13141558Srgrimes	else
13151558Srgrimes#endif
13161558Srgrimes		i = read(mt, tapebuf, ntrec * TP_BSIZE);
13171558Srgrimes
13181558Srgrimes	if (i <= 0) {
13191558Srgrimes		fprintf(stderr, "tape read error: %s\n", strerror(errno));
13201558Srgrimes		done(1);
13211558Srgrimes	}
13221558Srgrimes	if (i % TP_BSIZE != 0) {
132337240Sbde		fprintf(stderr, "Tape block size (%ld) %s (%d)\n",
13241558Srgrimes			i, "is not a multiple of dump block size", TP_BSIZE);
13251558Srgrimes		done(1);
13261558Srgrimes	}
13271558Srgrimes	ntrec = i / TP_BSIZE;
13281558Srgrimes	numtrec = ntrec;
132937240Sbde	vprintf(stdout, "Tape block size is %ld\n", ntrec);
13301558Srgrimes}
13311558Srgrimes
13321558Srgrimesvoid
133392837Simpclosemt(void)
13341558Srgrimes{
13351558Srgrimes
13361558Srgrimes	if (mt < 0)
13371558Srgrimes		return;
1338128175Sgreen	if (pipecmdin) {
1339128175Sgreen		pclose(popenfp);
1340128175Sgreen		popenfp = NULL;
1341128175Sgreen	} else
13421558Srgrimes#ifdef RRESTORE
13431558Srgrimes	if (host)
13441558Srgrimes		rmtclose();
13451558Srgrimes	else
13461558Srgrimes#endif
13471558Srgrimes		(void) close(mt);
13481558Srgrimes}
13491558Srgrimes
13501558Srgrimes/*
13511558Srgrimes * Read the next block from the tape.
13521558Srgrimes * If it is not any valid header, return an error.
13531558Srgrimes */
13541558Srgrimesstatic int
135592837Simpgethead(struct s_spcl *buf)
13561558Srgrimes{
13571558Srgrimes	long i;
13581558Srgrimes
135998542Smckusick	readtape((char *)buf);
136098542Smckusick	if (buf->c_magic != FS_UFS2_MAGIC && buf->c_magic != NFS_MAGIC) {
136198542Smckusick		if (buf->c_magic == OFS_MAGIC) {
136298542Smckusick			fprintf(stderr,
136398542Smckusick			    "Format of dump tape is too old. Must use\n");
136498542Smckusick			fprintf(stderr,
136598542Smckusick			    "a version of restore from before 2002.\n");
136698542Smckusick			return (FAIL);
136798542Smckusick		}
136898542Smckusick		if (swabl(buf->c_magic) != FS_UFS2_MAGIC &&
136998542Smckusick		    buf->c_magic != NFS_MAGIC) {
137098542Smckusick			if (buf->c_magic == OFS_MAGIC) {
137198542Smckusick				fprintf(stderr,
137298542Smckusick				  "Format of dump tape is too old. Must use\n");
137398542Smckusick				fprintf(stderr,
137498542Smckusick				  "a version of restore from before 2002.\n");
13751558Srgrimes			}
13761558Srgrimes			return (FAIL);
137723096Simp		}
137898542Smckusick		if (!Bcvt) {
137998542Smckusick			vprintf(stdout, "Note: Doing Byte swapping\n");
138098542Smckusick			Bcvt = 1;
13811558Srgrimes		}
13821558Srgrimes	}
138398542Smckusick	if (checksum((int *)buf) == FAIL)
138498542Smckusick		return (FAIL);
138598542Smckusick	if (Bcvt) {
138698542Smckusick		swabst((u_char *)"8l4s1q8l2q17l", (u_char *)buf);
138798542Smckusick		swabst((u_char *)"l",(u_char *) &buf->c_level);
138898542Smckusick		swabst((u_char *)"2l4q",(u_char *) &buf->c_flags);
13891558Srgrimes	}
139037923Simp	readmapflag = 0;
13911558Srgrimes
13921558Srgrimes	switch (buf->c_type) {
13931558Srgrimes
13941558Srgrimes	case TS_CLRI:
13951558Srgrimes	case TS_BITS:
13961558Srgrimes		/*
13971558Srgrimes		 * Have to patch up missing information in bit map headers
13981558Srgrimes		 */
139998542Smckusick		buf->c_size = buf->c_count * TP_BSIZE;
140037923Simp		if (buf->c_count > TP_NINDIR)
140137923Simp			readmapflag = 1;
140237923Simp		else
140337923Simp			for (i = 0; i < buf->c_count; i++)
140437923Simp				buf->c_addr[i]++;
1405179218Smckusick		/* FALL THROUGH */
14061558Srgrimes
14071558Srgrimes	case TS_TAPE:
1408179218Smckusick		if (buf->c_magic == NFS_MAGIC &&
1409179218Smckusick		    (buf->c_flags & NFS_DR_NEWINODEFMT) == 0)
1410179218Smckusick			oldinofmt = 1;
1411179218Smckusick		/* FALL THROUGH */
1412179218Smckusick
14131558Srgrimes	case TS_END:
14141558Srgrimes		buf->c_inumber = 0;
1415179218Smckusick		/* FALL THROUGH */
14161558Srgrimes
1417179218Smckusick	case TS_ADDR:
14181558Srgrimes	case TS_INODE:
141998542Smckusick		/*
142098542Smckusick		 * For old dump tapes, have to copy up old fields to
142198542Smckusick		 * new locations.
142298542Smckusick		 */
142398542Smckusick		if (buf->c_magic == NFS_MAGIC) {
142498542Smckusick			buf->c_tapea = buf->c_old_tapea;
142598542Smckusick			buf->c_firstrec = buf->c_old_firstrec;
142698542Smckusick			buf->c_date = _time32_to_time(buf->c_old_date);
142798542Smckusick			buf->c_ddate = _time32_to_time(buf->c_old_ddate);
142898542Smckusick			buf->c_atime = _time32_to_time(buf->c_old_atime);
142998542Smckusick			buf->c_mtime = _time32_to_time(buf->c_old_mtime);
1430179219Smckusick			buf->c_birthtime = 0;
1431179219Smckusick			buf->c_birthtimensec = 0;
1432179219Smckusick			buf->c_extsize = 0;
143398542Smckusick		}
143498542Smckusick		break;
143598542Smckusick
14361558Srgrimes	default:
14371558Srgrimes		panic("gethead: unknown inode type %d\n", buf->c_type);
14381558Srgrimes		break;
14391558Srgrimes	}
1440179218Smckusick	if (dumpdate != 0 && _time64_to_time(buf->c_date) != dumpdate)
1441179218Smckusick		fprintf(stderr, "Header with wrong dumpdate.\n");
1442144099Simp	/*
1443144099Simp	 * If we're restoring a filesystem with the old (FreeBSD 1)
1444144099Simp	 * format inodes, copy the uid/gid to the new location
1445144099Simp	 */
1446144099Simp	if (oldinofmt) {
1447144099Simp		buf->c_uid = buf->c_spare1[1];
1448144099Simp		buf->c_gid = buf->c_spare1[2];
1449144099Simp	}
145098542Smckusick	buf->c_magic = FS_UFS2_MAGIC;
145190827Siedowse	tapeaddr = buf->c_tapea;
14521558Srgrimes	if (dflag)
14531558Srgrimes		accthdr(buf);
14541558Srgrimes	return(GOOD);
14551558Srgrimes}
14561558Srgrimes
14571558Srgrimes/*
14581558Srgrimes * Check that a header is where it belongs and predict the next header
14591558Srgrimes */
14601558Srgrimesstatic void
146192837Simpaccthdr(struct s_spcl *header)
14621558Srgrimes{
14631558Srgrimes	static ino_t previno = 0x7fffffff;
14641558Srgrimes	static int prevtype;
14651558Srgrimes	static long predict;
14661558Srgrimes	long blks, i;
14671558Srgrimes
14681558Srgrimes	if (header->c_type == TS_TAPE) {
146998542Smckusick		fprintf(stderr, "Volume header ");
14701558Srgrimes 		if (header->c_firstrec)
1471203155Sjh 			fprintf(stderr, "begins with record %jd",
1472203155Sjh			    (intmax_t)header->c_firstrec);
14731558Srgrimes 		fprintf(stderr, "\n");
14741558Srgrimes		previno = 0x7fffffff;
14751558Srgrimes		return;
14761558Srgrimes	}
14771558Srgrimes	if (previno == 0x7fffffff)
14781558Srgrimes		goto newcalc;
14791558Srgrimes	switch (prevtype) {
14801558Srgrimes	case TS_BITS:
148123685Speter		fprintf(stderr, "Dumped inodes map header");
14821558Srgrimes		break;
14831558Srgrimes	case TS_CLRI:
148423685Speter		fprintf(stderr, "Used inodes map header");
14851558Srgrimes		break;
14861558Srgrimes	case TS_INODE:
1487241013Smdf		fprintf(stderr, "File header, ino %ju", (uintmax_t)previno);
14881558Srgrimes		break;
14891558Srgrimes	case TS_ADDR:
1490241013Smdf		fprintf(stderr, "File continuation header, ino %ju",
1491241013Smdf		    (uintmax_t)previno);
14921558Srgrimes		break;
14931558Srgrimes	case TS_END:
14941558Srgrimes		fprintf(stderr, "End of tape header");
14951558Srgrimes		break;
14961558Srgrimes	}
14971558Srgrimes	if (predict != blksread - 1)
149837240Sbde		fprintf(stderr, "; predicted %ld blocks, got %ld blocks",
14991558Srgrimes			predict, blksread - 1);
15001558Srgrimes	fprintf(stderr, "\n");
15011558Srgrimesnewcalc:
15021558Srgrimes	blks = 0;
15031558Srgrimes	if (header->c_type != TS_END)
15041558Srgrimes		for (i = 0; i < header->c_count; i++)
150537923Simp			if (readmapflag || header->c_addr[i] != 0)
15061558Srgrimes				blks++;
15071558Srgrimes	predict = blks;
15081558Srgrimes	blksread = 0;
15091558Srgrimes	prevtype = header->c_type;
15101558Srgrimes	previno = header->c_inumber;
15111558Srgrimes}
15121558Srgrimes
15131558Srgrimes/*
15141558Srgrimes * Find an inode header.
151590573Siedowse * Complain if had to skip.
15161558Srgrimes */
15171558Srgrimesstatic void
151892837Simpfindinode(struct s_spcl *header)
15191558Srgrimes{
15201558Srgrimes	static long skipcnt = 0;
15211558Srgrimes	long i;
15221558Srgrimes	char buf[TP_BSIZE];
152390608Siedowse	int htype;
15241558Srgrimes
15251558Srgrimes	curfile.name = "<name unknown>";
15261558Srgrimes	curfile.action = UNKNOWN;
152798542Smckusick	curfile.mode = 0;
15281558Srgrimes	curfile.ino = 0;
15291558Srgrimes	do {
153090608Siedowse		htype = header->c_type;
153190608Siedowse		switch (htype) {
15321558Srgrimes
15331558Srgrimes		case TS_ADDR:
15341558Srgrimes			/*
15351558Srgrimes			 * Skip up to the beginning of the next record
15361558Srgrimes			 */
15371558Srgrimes			for (i = 0; i < header->c_count; i++)
15381558Srgrimes				if (header->c_addr[i])
15391558Srgrimes					readtape(buf);
15401558Srgrimes			while (gethead(header) == FAIL ||
1541164911Sdwmalone			    _time64_to_time(header->c_date) != dumpdate) {
15421558Srgrimes				skipcnt++;
1543164911Sdwmalone				if (Dflag) {
1544164911Sdwmalone					byteslide++;
1545164911Sdwmalone					if (byteslide < TP_BSIZE) {
1546164911Sdwmalone						blkcnt--;
1547164911Sdwmalone						blksread--;
1548164911Sdwmalone					} else
1549164911Sdwmalone						byteslide = 0;
1550164911Sdwmalone				}
1551164911Sdwmalone			}
15521558Srgrimes			break;
15531558Srgrimes
15541558Srgrimes		case TS_INODE:
155598542Smckusick			curfile.mode = header->c_mode;
155698542Smckusick			curfile.uid = header->c_uid;
155798542Smckusick			curfile.gid = header->c_gid;
155898542Smckusick			curfile.file_flags = header->c_file_flags;
155998542Smckusick			curfile.rdev = header->c_rdev;
156098542Smckusick			curfile.atime_sec = header->c_atime;
156198542Smckusick			curfile.atime_nsec = header->c_atimensec;
156298542Smckusick			curfile.mtime_sec = header->c_mtime;
156398542Smckusick			curfile.mtime_nsec = header->c_mtimensec;
1564100207Smckusick			curfile.birthtime_sec = header->c_birthtime;
1565100207Smckusick			curfile.birthtime_nsec = header->c_birthtimensec;
1566167011Smckusick			curfile.extsize = header->c_extsize;
156798542Smckusick			curfile.size = header->c_size;
15681558Srgrimes			curfile.ino = header->c_inumber;
15691558Srgrimes			break;
15701558Srgrimes
15711558Srgrimes		case TS_END:
157290820Siedowse			/* If we missed some tapes, get another volume. */
157390820Siedowse			if (tapesread & (tapesread + 1)) {
157490820Siedowse				getvol(0);
157590820Siedowse				continue;
157690820Siedowse			}
15771558Srgrimes			curfile.ino = maxino;
15781558Srgrimes			break;
15791558Srgrimes
15801558Srgrimes		case TS_CLRI:
15811558Srgrimes			curfile.name = "<file removal list>";
15821558Srgrimes			break;
15831558Srgrimes
15841558Srgrimes		case TS_BITS:
15851558Srgrimes			curfile.name = "<file dump list>";
15861558Srgrimes			break;
15871558Srgrimes
15881558Srgrimes		case TS_TAPE:
1589164911Sdwmalone			if (Dflag)
1590164911Sdwmalone				fprintf(stderr, "unexpected tape header\n");
1591164911Sdwmalone			else
1592164911Sdwmalone				panic("unexpected tape header\n");
15931558Srgrimes
15941558Srgrimes		default:
1595164911Sdwmalone			if (Dflag)
1596164911Sdwmalone				fprintf(stderr, "unknown tape header type %d\n",
1597164911Sdwmalone				    spcl.c_type);
1598164911Sdwmalone			else
1599164911Sdwmalone				panic("unknown tape header type %d\n",
1600164911Sdwmalone				    spcl.c_type);
1601164911Sdwmalone			while (gethead(header) == FAIL ||
1602164911Sdwmalone			    _time64_to_time(header->c_date) != dumpdate) {
1603164911Sdwmalone				skipcnt++;
1604164911Sdwmalone				if (Dflag) {
1605164911Sdwmalone					byteslide++;
1606164911Sdwmalone					if (byteslide < TP_BSIZE) {
1607164911Sdwmalone						blkcnt--;
1608164911Sdwmalone						blksread--;
1609164911Sdwmalone					} else
1610164911Sdwmalone						byteslide = 0;
1611164911Sdwmalone				}
1612164911Sdwmalone			}
16131558Srgrimes
16141558Srgrimes		}
161590608Siedowse	} while (htype == TS_ADDR);
16161558Srgrimes	if (skipcnt > 0)
1617164911Sdwmalone		fprintf(stderr, "resync restore, skipped %ld %s\n",
1618164911Sdwmalone		    skipcnt, Dflag ? "bytes" : "blocks");
16191558Srgrimes	skipcnt = 0;
16201558Srgrimes}
16211558Srgrimes
16221558Srgrimesstatic int
162392837Simpchecksum(int *buf)
16241558Srgrimes{
162592806Sobrien	int i, j;
16261558Srgrimes
16271558Srgrimes	j = sizeof(union u_spcl) / sizeof(int);
16281558Srgrimes	i = 0;
162998542Smckusick	if (!Bcvt) {
16301558Srgrimes		do
16311558Srgrimes			i += *buf++;
16321558Srgrimes		while (--j);
16331558Srgrimes	} else {
16341558Srgrimes		/* What happens if we want to read restore tapes
16351558Srgrimes			for a 16bit int machine??? */
16368871Srgrimes		do
16371558Srgrimes			i += swabl(*buf++);
16381558Srgrimes		while (--j);
16391558Srgrimes	}
16408871Srgrimes
16411558Srgrimes	if (i != CHECKSUM) {
1642241013Smdf		fprintf(stderr, "Checksum error %o, inode %ju file %s\n", i,
1643241013Smdf		    (uintmax_t)curfile.ino, curfile.name);
16441558Srgrimes		return(FAIL);
16451558Srgrimes	}
16461558Srgrimes	return(GOOD);
16471558Srgrimes}
16481558Srgrimes
16491558Srgrimes#ifdef RRESTORE
16501558Srgrimes#include <stdarg.h>
16511558Srgrimes
16521558Srgrimesvoid
16531558Srgrimesmsg(const char *fmt, ...)
16541558Srgrimes{
16551558Srgrimes	va_list ap;
16561558Srgrimes	va_start(ap, fmt);
16571558Srgrimes	(void)vfprintf(stderr, fmt, ap);
16581558Srgrimes	va_end(ap);
16591558Srgrimes}
16601558Srgrimes#endif /* RRESTORE */
16611558Srgrimes
16621558Srgrimesstatic u_char *
166392837Simpswabshort(u_char *sp, int n)
16641558Srgrimes{
16651558Srgrimes	char c;
16661558Srgrimes
16671558Srgrimes	while (--n >= 0) {
16681558Srgrimes		c = sp[0]; sp[0] = sp[1]; sp[1] = c;
16691558Srgrimes		sp += 2;
16701558Srgrimes	}
16711558Srgrimes	return (sp);
16721558Srgrimes}
16731558Srgrimes
16741558Srgrimesstatic u_char *
167592837Simpswablong(u_char *sp, int n)
16761558Srgrimes{
16771558Srgrimes	char c;
16781558Srgrimes
16791558Srgrimes	while (--n >= 0) {
16801558Srgrimes		c = sp[0]; sp[0] = sp[3]; sp[3] = c;
16811558Srgrimes		c = sp[2]; sp[2] = sp[1]; sp[1] = c;
16821558Srgrimes		sp += 4;
16831558Srgrimes	}
16841558Srgrimes	return (sp);
16851558Srgrimes}
16861558Srgrimes
168798542Smckusickstatic u_char *
168898542Smckusickswabquad(u_char *sp, int n)
168998542Smckusick{
169098542Smckusick	char c;
169198542Smckusick
169298542Smckusick	while (--n >= 0) {
169398542Smckusick		c = sp[0]; sp[0] = sp[7]; sp[7] = c;
169498542Smckusick		c = sp[1]; sp[1] = sp[6]; sp[6] = c;
169598542Smckusick		c = sp[2]; sp[2] = sp[5]; sp[5] = c;
169698542Smckusick		c = sp[3]; sp[3] = sp[4]; sp[4] = c;
169798542Smckusick		sp += 8;
169898542Smckusick	}
169998542Smckusick	return (sp);
170098542Smckusick}
170198542Smckusick
17021558Srgrimesvoid
170392837Simpswabst(u_char *cp, u_char *sp)
17041558Srgrimes{
17051558Srgrimes	int n = 0;
17061558Srgrimes
17071558Srgrimes	while (*cp) {
17081558Srgrimes		switch (*cp) {
17091558Srgrimes		case '0': case '1': case '2': case '3': case '4':
17101558Srgrimes		case '5': case '6': case '7': case '8': case '9':
17111558Srgrimes			n = (n * 10) + (*cp++ - '0');
17121558Srgrimes			continue;
17138871Srgrimes
17141558Srgrimes		case 's': case 'w': case 'h':
17151558Srgrimes			if (n == 0)
17161558Srgrimes				n = 1;
17171558Srgrimes			sp = swabshort(sp, n);
17181558Srgrimes			break;
17191558Srgrimes
17201558Srgrimes		case 'l':
17211558Srgrimes			if (n == 0)
17221558Srgrimes				n = 1;
17231558Srgrimes			sp = swablong(sp, n);
17241558Srgrimes			break;
17251558Srgrimes
172698542Smckusick		case 'q':
17271558Srgrimes			if (n == 0)
17281558Srgrimes				n = 1;
172998542Smckusick			sp = swabquad(sp, n);
173098542Smckusick			break;
173198542Smckusick
173298542Smckusick		case 'b':
173398542Smckusick			if (n == 0)
173498542Smckusick				n = 1;
17351558Srgrimes			sp += n;
17361558Srgrimes			break;
173798542Smckusick
173898542Smckusick		default:
173998542Smckusick			fprintf(stderr, "Unknown conversion character: %c\n",
174098542Smckusick			    *cp);
174198542Smckusick			done(0);
174298542Smckusick			break;
17431558Srgrimes		}
17441558Srgrimes		cp++;
17451558Srgrimes		n = 0;
17461558Srgrimes	}
17471558Srgrimes}
17481558Srgrimes
17491558Srgrimesstatic u_long
175092837Simpswabl(u_long x)
17511558Srgrimes{
17521558Srgrimes	swabst((u_char *)"l", (u_char *)&x);
17531558Srgrimes	return (x);
17541558Srgrimes}
1755