1331722Seadler/*
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);
107298901Spfgstatic void	 xtrattr(char *, size_t);
108167011Smckusickstatic void	 set_extattr_link(char *, void *, int);
109167011Smckusickstatic void	 set_extattr_fd(int, char *, void *, int);
110298901Spfgstatic void	 skiphole(void (*)(char *, size_t), size_t *);
11192837Simpstatic int	 gethead(struct s_spcl *);
11292837Simpstatic void	 readtape(char *);
11392837Simpstatic void	 setdumpnum(void);
11492837Simpstatic u_long	 swabl(u_long);
11592837Simpstatic u_char	*swablong(u_char *, int);
11692837Simpstatic u_char	*swabshort(u_char *, int);
11792837Simpstatic void	 terminateinput(void);
118298901Spfgstatic void	 xtrfile(char *, size_t);
119298901Spfgstatic void	 xtrlnkfile(char *, size_t);
120298901Spfgstatic void	 xtrlnkskip(char *, size_t);
121298901Spfgstatic void	 xtrmap(char *, size_t);
122298901Spfgstatic void	 xtrmapskip(char *, size_t);
123298901Spfgstatic void	 xtrskip(char *, size_t);
1241558Srgrimes
1251558Srgrimes/*
1261558Srgrimes * Set up an input source
1271558Srgrimes */
1281558Srgrimesvoid
129128175Sgreensetinput(char *source, int ispipecommand)
1301558Srgrimes{
1311558Srgrimes	FLUSHTAPEBUF();
1321558Srgrimes	if (bflag)
1331558Srgrimes		newtapebuf(ntrec);
1341558Srgrimes	else
135298394Saraujo		newtapebuf(MAX(NTREC, HIGHDENSITYTREC));
1361558Srgrimes	terminal = stdin;
1371558Srgrimes
138128175Sgreen	if (ispipecommand)
139128175Sgreen		pipecmdin++;
140128175Sgreen	else
1411558Srgrimes#ifdef RRESTORE
14223685Speter	if (strchr(source, ':')) {
1431558Srgrimes		host = source;
14423685Speter		source = strchr(host, ':');
1451558Srgrimes		*source++ = '\0';
1461558Srgrimes		if (rmthost(host) == 0)
1471558Srgrimes			done(1);
1481558Srgrimes	} else
1491558Srgrimes#endif
1501558Srgrimes	if (strcmp(source, "-") == 0) {
1511558Srgrimes		/*
1521558Srgrimes		 * Since input is coming from a pipe we must establish
1531558Srgrimes		 * our own connection to the terminal.
1541558Srgrimes		 */
1551558Srgrimes		terminal = fopen(_PATH_TTY, "r");
1561558Srgrimes		if (terminal == NULL) {
1571558Srgrimes			(void)fprintf(stderr, "cannot open %s: %s\n",
1581558Srgrimes			    _PATH_TTY, strerror(errno));
1591558Srgrimes			terminal = fopen(_PATH_DEVNULL, "r");
1601558Srgrimes			if (terminal == NULL) {
1611558Srgrimes				(void)fprintf(stderr, "cannot open %s: %s\n",
1621558Srgrimes				    _PATH_DEVNULL, strerror(errno));
1631558Srgrimes				done(1);
1641558Srgrimes			}
1651558Srgrimes		}
1661558Srgrimes		pipein++;
1671558Srgrimes	}
168241848Seadler	/* no longer need or want root privileges */
169241848Seadler	if (setuid(getuid()) != 0) {
170241848Seadler		fprintf(stderr, "setuid failed\n");
171241848Seadler		done(1);
172241848Seadler	}
17321174Sguido	magtape = strdup(source);
17421174Sguido	if (magtape == NULL) {
17521174Sguido		fprintf(stderr, "Cannot allocate space for magtape buffer\n");
17621174Sguido		done(1);
17721174Sguido	}
1781558Srgrimes}
1791558Srgrimes
1801558Srgrimesvoid
18192837Simpnewtapebuf(long size)
1821558Srgrimes{
18392837Simp	static int tapebufsize = -1;
1841558Srgrimes
1851558Srgrimes	ntrec = size;
1861558Srgrimes	if (size <= tapebufsize)
1871558Srgrimes		return;
1881558Srgrimes	if (tapebuf != NULL)
189164911Sdwmalone		free(tapebuf - TP_BSIZE);
190164911Sdwmalone	tapebuf = malloc((size+1) * TP_BSIZE);
1911558Srgrimes	if (tapebuf == NULL) {
1921558Srgrimes		fprintf(stderr, "Cannot allocate space for tape buffer\n");
1931558Srgrimes		done(1);
1941558Srgrimes	}
195164911Sdwmalone	tapebuf += TP_BSIZE;
1961558Srgrimes	tapebufsize = size;
1971558Srgrimes}
1981558Srgrimes
1991558Srgrimes/*
2001558Srgrimes * Verify that the tape drive can be accessed and
2011558Srgrimes * that it actually is a dump tape.
2021558Srgrimes */
2031558Srgrimesvoid
20492837Simpsetup(void)
2051558Srgrimes{
2061558Srgrimes	int i, j, *ip;
2071558Srgrimes	struct stat stbuf;
2081558Srgrimes
2091558Srgrimes	vprintf(stdout, "Verify tape and initialize maps\n");
210128175Sgreen	if (pipecmdin) {
211128175Sgreen		if (setenv("RESTORE_VOLUME", "1", 1) == -1) {
212128175Sgreen			fprintf(stderr, "Cannot set $RESTORE_VOLUME: %s\n",
213128175Sgreen			    strerror(errno));
214128175Sgreen			done(1);
215128175Sgreen		}
216128175Sgreen		popenfp = popen(magtape, "r");
217128175Sgreen		mt = popenfp ? fileno(popenfp) : -1;
218128175Sgreen	} else
2191558Srgrimes#ifdef RRESTORE
2201558Srgrimes	if (host)
2211558Srgrimes		mt = rmtopen(magtape, 0);
2221558Srgrimes	else
2231558Srgrimes#endif
2241558Srgrimes	if (pipein)
2251558Srgrimes		mt = 0;
2261558Srgrimes	else
2271558Srgrimes		mt = open(magtape, O_RDONLY, 0);
2281558Srgrimes	if (mt < 0) {
2291558Srgrimes		fprintf(stderr, "%s: %s\n", magtape, strerror(errno));
2301558Srgrimes		done(1);
2311558Srgrimes	}
2321558Srgrimes	volno = 1;
2331558Srgrimes	setdumpnum();
2341558Srgrimes	FLUSHTAPEBUF();
235203816Sjh	if (!pipein && !pipecmdin && !bflag)
2361558Srgrimes		findtapeblksize();
2371558Srgrimes	if (gethead(&spcl) == FAIL) {
23898542Smckusick		fprintf(stderr, "Tape is not a dump tape\n");
23998542Smckusick		done(1);
2401558Srgrimes	}
2411558Srgrimes	if (pipein) {
24298542Smckusick		endoftapemark.s_spcl.c_magic = FS_UFS2_MAGIC;
2431558Srgrimes		endoftapemark.s_spcl.c_type = TS_END;
2441558Srgrimes		ip = (int *)&endoftapemark;
2451558Srgrimes		j = sizeof(union u_spcl) / sizeof(int);
2461558Srgrimes		i = 0;
2471558Srgrimes		do
2481558Srgrimes			i += *ip++;
2491558Srgrimes		while (--j);
2501558Srgrimes		endoftapemark.s_spcl.c_checksum = CHECKSUM - i;
2511558Srgrimes	}
2521558Srgrimes	if (vflag || command == 't')
2531558Srgrimes		printdumpinfo();
25498542Smckusick	dumptime = _time64_to_time(spcl.c_ddate);
25598542Smckusick	dumpdate = _time64_to_time(spcl.c_date);
2561558Srgrimes	if (stat(".", &stbuf) < 0) {
2571558Srgrimes		fprintf(stderr, "cannot stat .: %s\n", strerror(errno));
2581558Srgrimes		done(1);
2591558Srgrimes	}
26034851Sjkh	if (stbuf.st_blksize > 0 && stbuf.st_blksize < TP_BSIZE )
26134851Sjkh		fssize = TP_BSIZE;
26234851Sjkh	if (stbuf.st_blksize >= TP_BSIZE && stbuf.st_blksize <= MAXBSIZE)
2631558Srgrimes		fssize = stbuf.st_blksize;
264269303Smckusick	if (((TP_BSIZE - 1) & stbuf.st_blksize) != 0) {
265269303Smckusick		fprintf(stderr, "Warning: filesystem with non-multiple-of-%d "
266269303Smckusick		    "blocksize (%d);\n", TP_BSIZE, stbuf.st_blksize);
267269303Smckusick		fssize = roundup(fssize, TP_BSIZE);
268269303Smckusick		fprintf(stderr, "\twriting using blocksize %ld\n", fssize);
2691558Srgrimes	}
2701558Srgrimes	if (spcl.c_volume != 1) {
2711558Srgrimes		fprintf(stderr, "Tape is not volume 1 of the dump\n");
2721558Srgrimes		done(1);
2731558Srgrimes	}
2741558Srgrimes	if (gethead(&spcl) == FAIL) {
27537240Sbde		dprintf(stdout, "header read failed at %ld blocks\n", blksread);
2761558Srgrimes		panic("no header after volume mark!\n");
2771558Srgrimes	}
2781558Srgrimes	findinode(&spcl);
2791558Srgrimes	if (spcl.c_type != TS_CLRI) {
2801558Srgrimes		fprintf(stderr, "Cannot find file removal list\n");
2811558Srgrimes		done(1);
2821558Srgrimes	}
2831558Srgrimes	maxino = (spcl.c_count * TP_BSIZE * NBBY) + 1;
284241013Smdf	dprintf(stdout, "maxino = %ju\n", (uintmax_t)maxino);
2851558Srgrimes	map = calloc((unsigned)1, (unsigned)howmany(maxino, NBBY));
2861558Srgrimes	if (map == NULL)
28723685Speter		panic("no memory for active inode map\n");
28823685Speter	usedinomap = map;
2891558Srgrimes	curfile.action = USING;
290167011Smckusick	getfile(xtrmap, xtrmapskip, xtrmapskip);
2911558Srgrimes	if (spcl.c_type != TS_BITS) {
2921558Srgrimes		fprintf(stderr, "Cannot find file dump list\n");
2931558Srgrimes		done(1);
2941558Srgrimes	}
2951558Srgrimes	map = calloc((unsigned)1, (unsigned)howmany(maxino, NBBY));
2961558Srgrimes	if (map == (char *)NULL)
2971558Srgrimes		panic("no memory for file dump list\n");
2981558Srgrimes	dumpmap = map;
2991558Srgrimes	curfile.action = USING;
300167011Smckusick	getfile(xtrmap, xtrmapskip, xtrmapskip);
30123685Speter	/*
30223685Speter	 * If there may be whiteout entries on the tape, pretend that the
30323685Speter	 * whiteout inode exists, so that the whiteout entries can be
30423685Speter	 * extracted.
30523685Speter	 */
30698542Smckusick	SETINO(WINO, dumpmap);
30790820Siedowse	/* 'r' restores don't call getvol() for tape 1, so mark it as read. */
30890820Siedowse	if (command == 'r')
30990820Siedowse		tapesread = 1;
3101558Srgrimes}
3111558Srgrimes
3121558Srgrimes/*
3131558Srgrimes * Prompt user to load a new dump volume.
3141558Srgrimes * "Nextvol" is the next suggested volume to use.
3151558Srgrimes * This suggested volume is enforced when doing full
31637906Scharnier * or incremental restores, but can be overridden by
3171558Srgrimes * the user when only extracting a subset of the files.
3181558Srgrimes */
3191558Srgrimesvoid
32092837Simpgetvol(long nextvol)
3211558Srgrimes{
32298542Smckusick	int64_t prevtapea;
32398542Smckusick	long i, newvol, savecnt;
3241558Srgrimes	union u_spcl tmpspcl;
3251558Srgrimes#	define tmpbuf tmpspcl.s_spcl
3261558Srgrimes	char buf[TP_BSIZE];
3271558Srgrimes
3281558Srgrimes	if (nextvol == 1) {
3291558Srgrimes		tapesread = 0;
3301558Srgrimes		gettingfile = 0;
3311558Srgrimes	}
33290827Siedowse	prevtapea = tapeaddr;
33390827Siedowse	savecnt = blksread;
3341558Srgrimes	if (pipein) {
33569906Siedowse		if (nextvol != 1) {
3361558Srgrimes			panic("Changing volumes on pipe input?\n");
33769906Siedowse			/* Avoid looping if we couldn't ask the user. */
33869906Siedowse			if (yflag || ferror(terminal) || feof(terminal))
33969906Siedowse				done(1);
34069906Siedowse		}
3411558Srgrimes		if (volno == 1)
3421558Srgrimes			return;
343297441Spfg		newvol = 0;
3441558Srgrimes		goto gethdr;
3451558Srgrimes	}
3461558Srgrimesagain:
3471558Srgrimes	if (pipein)
3481558Srgrimes		done(1); /* pipes do not get a second chance */
34990608Siedowse	if (command == 'R' || command == 'r' || curfile.action != SKIP)
3501558Srgrimes		newvol = nextvol;
35190608Siedowse	else
3521558Srgrimes		newvol = 0;
3531558Srgrimes	while (newvol <= 0) {
3541558Srgrimes		if (tapesread == 0) {
35590820Siedowse			fprintf(stderr, "%s%s%s%s%s%s%s",
3561558Srgrimes			    "You have not read any tapes yet.\n",
35790820Siedowse			    "If you are extracting just a few files,",
35890820Siedowse			    " start with the last volume\n",
35990820Siedowse			    "and work towards the first; restore",
36090820Siedowse			    " can quickly skip tapes that\n",
36190820Siedowse			    "have no further files to extract.",
36290820Siedowse			    " Otherwise, begin with volume 1.\n");
3631558Srgrimes		} else {
3641558Srgrimes			fprintf(stderr, "You have read volumes");
3651558Srgrimes			strcpy(buf, ": ");
36690820Siedowse			for (i = 0; i < 32; i++)
3671558Srgrimes				if (tapesread & (1 << i)) {
36890820Siedowse					fprintf(stderr, "%s%ld", buf, i + 1);
3691558Srgrimes					strcpy(buf, ", ");
3701558Srgrimes				}
3711558Srgrimes			fprintf(stderr, "\n");
3721558Srgrimes		}
3731558Srgrimes		do	{
3741558Srgrimes			fprintf(stderr, "Specify next volume #: ");
3751558Srgrimes			(void) fflush(stderr);
37669906Siedowse			if (fgets(buf, BUFSIZ, terminal) == NULL)
37769906Siedowse				done(1);
37869906Siedowse		} while (buf[0] == '\n');
3791558Srgrimes		newvol = atoi(buf);
3801558Srgrimes		if (newvol <= 0) {
3811558Srgrimes			fprintf(stderr,
3821558Srgrimes			    "Volume numbers are positive numerics\n");
3831558Srgrimes		}
3841558Srgrimes	}
3851558Srgrimes	if (newvol == volno) {
38690820Siedowse		tapesread |= 1 << (volno - 1);
3871558Srgrimes		return;
3881558Srgrimes	}
3891558Srgrimes	closemt();
39037240Sbde	fprintf(stderr, "Mount tape volume %ld\n", newvol);
3911558Srgrimes	fprintf(stderr, "Enter ``none'' if there are no more tapes\n");
3921558Srgrimes	fprintf(stderr, "otherwise enter tape name (default: %s) ", magtape);
3931558Srgrimes	(void) fflush(stderr);
39469906Siedowse	if (fgets(buf, BUFSIZ, terminal) == NULL)
3951558Srgrimes		done(1);
3961558Srgrimes	if (!strcmp(buf, "none\n")) {
3971558Srgrimes		terminateinput();
3981558Srgrimes		return;
3991558Srgrimes	}
4001558Srgrimes	if (buf[0] != '\n') {
4011558Srgrimes		(void) strcpy(magtape, buf);
4021558Srgrimes		magtape[strlen(magtape) - 1] = '\0';
4031558Srgrimes	}
404128175Sgreen	if (pipecmdin) {
405128175Sgreen		char volno[sizeof("2147483647")];
406128175Sgreen
407203155Sjh		(void)sprintf(volno, "%ld", newvol);
408128175Sgreen		if (setenv("RESTORE_VOLUME", volno, 1) == -1) {
409128175Sgreen			fprintf(stderr, "Cannot set $RESTORE_VOLUME: %s\n",
410128175Sgreen			    strerror(errno));
411128175Sgreen			done(1);
412128175Sgreen		}
413128175Sgreen		popenfp = popen(magtape, "r");
414128175Sgreen		mt = popenfp ? fileno(popenfp) : -1;
415128175Sgreen	} else
4161558Srgrimes#ifdef RRESTORE
4171558Srgrimes	if (host)
4181558Srgrimes		mt = rmtopen(magtape, 0);
4191558Srgrimes	else
4201558Srgrimes#endif
4211558Srgrimes		mt = open(magtape, O_RDONLY, 0);
4221558Srgrimes
4231558Srgrimes	if (mt == -1) {
4241558Srgrimes		fprintf(stderr, "Cannot open %s\n", magtape);
4251558Srgrimes		volno = -1;
4261558Srgrimes		goto again;
4271558Srgrimes	}
4281558Srgrimesgethdr:
4291558Srgrimes	volno = newvol;
4301558Srgrimes	setdumpnum();
4311558Srgrimes	FLUSHTAPEBUF();
4321558Srgrimes	if (gethead(&tmpbuf) == FAIL) {
43337240Sbde		dprintf(stdout, "header read failed at %ld blocks\n", blksread);
4341558Srgrimes		fprintf(stderr, "tape is not dump tape\n");
4351558Srgrimes		volno = 0;
4361558Srgrimes		goto again;
4371558Srgrimes	}
4381558Srgrimes	if (tmpbuf.c_volume != volno) {
439203155Sjh		fprintf(stderr, "Wrong volume (%jd)\n",
440203155Sjh		    (intmax_t)tmpbuf.c_volume);
4411558Srgrimes		volno = 0;
4421558Srgrimes		goto again;
4431558Srgrimes	}
44498542Smckusick	if (_time64_to_time(tmpbuf.c_date) != dumpdate ||
44598542Smckusick	    _time64_to_time(tmpbuf.c_ddate) != dumptime) {
44698542Smckusick		time_t t = _time64_to_time(tmpbuf.c_date);
44785635Sdillon		fprintf(stderr, "Wrong dump date\n\tgot: %s", ctime(&t));
4481558Srgrimes		fprintf(stderr, "\twanted: %s", ctime(&dumpdate));
4491558Srgrimes		volno = 0;
4501558Srgrimes		goto again;
4511558Srgrimes	}
45290820Siedowse	tapesread |= 1 << (volno - 1);
4531558Srgrimes	blksread = savecnt;
4541558Srgrimes 	/*
4551558Srgrimes 	 * If continuing from the previous volume, skip over any
4561558Srgrimes 	 * blocks read already at the end of the previous volume.
4571558Srgrimes 	 *
4581558Srgrimes 	 * If coming to this volume at random, skip to the beginning
4591558Srgrimes 	 * of the next record.
4601558Srgrimes 	 */
461203155Sjh	dprintf(stdout, "last rec %jd, tape starts with %jd\n",
462203155Sjh	    (intmax_t)prevtapea, (intmax_t)tmpbuf.c_tapea);
46398542Smckusick 	if (tmpbuf.c_type == TS_TAPE) {
46490608Siedowse 		if (curfile.action != USING) {
46590608Siedowse			/*
46690608Siedowse			 * XXX Dump incorrectly sets c_count to 1 in the
46790608Siedowse			 * volume header of the first tape, so ignore
46890608Siedowse			 * c_count when volno == 1.
46990608Siedowse			 */
47090608Siedowse			if (volno != 1)
47190608Siedowse				for (i = tmpbuf.c_count; i > 0; i--)
47290608Siedowse					readtape(buf);
47390827Siedowse 		} else if (tmpbuf.c_tapea <= prevtapea) {
4741558Srgrimes			/*
47590827Siedowse			 * Normally the value of c_tapea in the volume
47690827Siedowse			 * header is the record number of the header itself.
47790827Siedowse			 * However in the volume header following an EOT-
47890827Siedowse			 * terminated tape, it is the record number of the
47990827Siedowse			 * first continuation data block (dump bug?).
48090827Siedowse			 *
48190827Siedowse			 * The next record we want is `prevtapea + 1'.
4821558Srgrimes			 */
48390827Siedowse 			i = prevtapea + 1 - tmpbuf.c_tapea;
48437240Sbde			dprintf(stderr, "Skipping %ld duplicate record%s.\n",
4851558Srgrimes				i, i > 1 ? "s" : "");
4861558Srgrimes 			while (--i >= 0)
4871558Srgrimes 				readtape(buf);
4881558Srgrimes 		}
4891558Srgrimes 	}
49090608Siedowse	if (curfile.action == USING) {
4911558Srgrimes		if (volno == 1)
4921558Srgrimes			panic("active file into volume 1\n");
4931558Srgrimes		return;
4941558Srgrimes	}
4951558Srgrimes	(void) gethead(&spcl);
4961558Srgrimes	findinode(&spcl);
4971558Srgrimes	if (gettingfile) {
4981558Srgrimes		gettingfile = 0;
4991558Srgrimes		longjmp(restart, 1);
5001558Srgrimes	}
5011558Srgrimes}
5021558Srgrimes
5031558Srgrimes/*
5041558Srgrimes * Handle unexpected EOF.
5051558Srgrimes */
5061558Srgrimesstatic void
50792837Simpterminateinput(void)
5081558Srgrimes{
5091558Srgrimes
5101558Srgrimes	if (gettingfile && curfile.action == USING) {
5111558Srgrimes		printf("Warning: %s %s\n",
5121558Srgrimes		    "End-of-input encountered while extracting", curfile.name);
5131558Srgrimes	}
5141558Srgrimes	curfile.name = "<name unknown>";
5151558Srgrimes	curfile.action = UNKNOWN;
51698542Smckusick	curfile.mode = 0;
5171558Srgrimes	curfile.ino = maxino;
5181558Srgrimes	if (gettingfile) {
5191558Srgrimes		gettingfile = 0;
5201558Srgrimes		longjmp(restart, 1);
5211558Srgrimes	}
5221558Srgrimes}
5231558Srgrimes
5241558Srgrimes/*
5251558Srgrimes * handle multiple dumps per tape by skipping forward to the
5261558Srgrimes * appropriate one.
5271558Srgrimes */
5281558Srgrimesstatic void
52992837Simpsetdumpnum(void)
5301558Srgrimes{
5311558Srgrimes	struct mtop tcom;
5321558Srgrimes
5331558Srgrimes	if (dumpnum == 1 || volno != 1)
5341558Srgrimes		return;
5351558Srgrimes	if (pipein) {
5361558Srgrimes		fprintf(stderr, "Cannot have multiple dumps on pipe input\n");
5371558Srgrimes		done(1);
5381558Srgrimes	}
5391558Srgrimes	tcom.mt_op = MTFSF;
5401558Srgrimes	tcom.mt_count = dumpnum - 1;
5411558Srgrimes#ifdef RRESTORE
5421558Srgrimes	if (host)
5431558Srgrimes		rmtioctl(MTFSF, dumpnum - 1);
5448871Srgrimes	else
5451558Srgrimes#endif
546128175Sgreen		if (!pipecmdin && ioctl(mt, MTIOCTOP, (char *)&tcom) < 0)
5471558Srgrimes			fprintf(stderr, "ioctl MTFSF: %s\n", strerror(errno));
5481558Srgrimes}
5491558Srgrimes
5501558Srgrimesvoid
55192837Simpprintdumpinfo(void)
5521558Srgrimes{
55385635Sdillon	time_t t;
55498542Smckusick	t = _time64_to_time(spcl.c_date);
55585635Sdillon	fprintf(stdout, "Dump   date: %s", ctime(&t));
55698542Smckusick	t = _time64_to_time(spcl.c_ddate);
5571558Srgrimes	fprintf(stdout, "Dumped from: %s",
55885635Sdillon	    (spcl.c_ddate == 0) ? "the epoch\n" : ctime(&t));
5591558Srgrimes	if (spcl.c_host[0] == '\0')
5601558Srgrimes		return;
561203155Sjh	fprintf(stderr, "Level %jd dump of %s on %s:%s\n",
562203155Sjh	    (intmax_t)spcl.c_level, spcl.c_filesys, spcl.c_host, spcl.c_dev);
5631558Srgrimes	fprintf(stderr, "Label: %s\n", spcl.c_label);
5641558Srgrimes}
5651558Srgrimes
5661558Srgrimesint
56792837Simpextractfile(char *name)
5681558Srgrimes{
569298901Spfg	u_int flags;
570161598Smaxim	uid_t uid;
571161598Smaxim	gid_t gid;
57223685Speter	mode_t mode;
573167011Smckusick	int extsize;
574279986Sjilles	struct timespec mtimep[2], ctimep[2];
5751558Srgrimes	struct entry *ep;
576167011Smckusick	char *buf;
5771558Srgrimes
5781558Srgrimes	curfile.name = name;
5791558Srgrimes	curfile.action = USING;
580100207Smckusick	mtimep[0].tv_sec = curfile.atime_sec;
581279986Sjilles	mtimep[0].tv_nsec = curfile.atime_nsec;
582100207Smckusick	mtimep[1].tv_sec = curfile.mtime_sec;
583279986Sjilles	mtimep[1].tv_nsec = curfile.mtime_nsec;
584100207Smckusick	ctimep[0].tv_sec = curfile.atime_sec;
585279986Sjilles	ctimep[0].tv_nsec = curfile.atime_nsec;
586100207Smckusick	ctimep[1].tv_sec = curfile.birthtime_sec;
587279986Sjilles	ctimep[1].tv_nsec = curfile.birthtime_nsec;
588167011Smckusick	extsize = curfile.extsize;
589178125Smckusick	uid = getuid();
590178125Smckusick	if (uid == 0)
591178125Smckusick		uid = curfile.uid;
592161598Smaxim	gid = curfile.gid;
59398542Smckusick	mode = curfile.mode;
59498542Smckusick	flags = curfile.file_flags;
5951558Srgrimes	switch (mode & IFMT) {
5961558Srgrimes
5971558Srgrimes	default:
5981558Srgrimes		fprintf(stderr, "%s: unknown file mode 0%o\n", name, mode);
5991558Srgrimes		skipfile();
6001558Srgrimes		return (FAIL);
6011558Srgrimes
6021558Srgrimes	case IFSOCK:
6031558Srgrimes		vprintf(stdout, "skipped socket %s\n", name);
6041558Srgrimes		skipfile();
6051558Srgrimes		return (GOOD);
6061558Srgrimes
6071558Srgrimes	case IFDIR:
6081558Srgrimes		if (mflag) {
6091558Srgrimes			ep = lookupname(name);
6101558Srgrimes			if (ep == NULL || ep->e_flags & EXTRACT)
6111558Srgrimes				panic("unextracted directory %s\n", name);
6121558Srgrimes			skipfile();
6131558Srgrimes			return (GOOD);
6141558Srgrimes		}
6151558Srgrimes		vprintf(stdout, "extract file %s\n", name);
6161558Srgrimes		return (genliteraldir(name, curfile.ino));
6171558Srgrimes
6181558Srgrimes	case IFLNK:
6191558Srgrimes		lnkbuf[0] = '\0';
6201558Srgrimes		pathlen = 0;
621167011Smckusick		buf = setupextattr(extsize);
622167011Smckusick		getfile(xtrlnkfile, xtrattr, xtrlnkskip);
6231558Srgrimes		if (pathlen == 0) {
6241558Srgrimes			vprintf(stdout,
6251558Srgrimes			    "%s: zero length symbolic link (ignored)\n", name);
6261558Srgrimes			return (GOOD);
6271558Srgrimes		}
62896113Siedowse		if (linkit(lnkbuf, name, SYMLINK) == GOOD) {
629167011Smckusick			if (extsize > 0)
630167011Smckusick				set_extattr_link(name, buf, extsize);
631161598Smaxim			(void) lchown(name, uid, gid);
63296113Siedowse			(void) lchmod(name, mode);
633279986Sjilles			(void) utimensat(AT_FDCWD, name, ctimep,
634279986Sjilles			    AT_SYMLINK_NOFOLLOW);
635279986Sjilles			(void) utimensat(AT_FDCWD, name, mtimep,
636279986Sjilles			    AT_SYMLINK_NOFOLLOW);
637161598Smaxim			(void) lchflags(name, flags);
63896113Siedowse			return (GOOD);
63995943Siedowse		}
64096113Siedowse		return (FAIL);
6411558Srgrimes
6426305Smartin	case IFIFO:
64323685Speter		vprintf(stdout, "extract fifo %s\n", name);
64423685Speter		if (Nflag) {
64523685Speter			skipfile();
64623685Speter			return (GOOD);
64723685Speter		}
648161598Smaxim		if (uflag)
649161598Smaxim			(void) unlink(name);
650161598Smaxim		if (mkfifo(name, 0600) < 0) {
65123685Speter			fprintf(stderr, "%s: cannot create fifo: %s\n",
65223685Speter			    name, strerror(errno));
6536305Smartin			skipfile();
6546305Smartin			return (FAIL);
6556305Smartin		}
656167011Smckusick		if (extsize == 0) {
657167011Smckusick			skipfile();
658167011Smckusick		} else {
659167011Smckusick			buf = setupextattr(extsize);
660167011Smckusick			getfile(xtrnull, xtrattr, xtrnull);
661167011Smckusick			set_extattr_file(name, buf, extsize);
662167011Smckusick		}
663161598Smaxim		(void) chown(name, uid, gid);
6646305Smartin		(void) chmod(name, mode);
665279986Sjilles		(void) utimensat(AT_FDCWD, name, ctimep, 0);
666279986Sjilles		(void) utimensat(AT_FDCWD, name, mtimep, 0);
66723685Speter		(void) chflags(name, flags);
6686305Smartin		return (GOOD);
6696305Smartin
6701558Srgrimes	case IFCHR:
6711558Srgrimes	case IFBLK:
6721558Srgrimes		vprintf(stdout, "extract special file %s\n", name);
6731558Srgrimes		if (Nflag) {
6741558Srgrimes			skipfile();
6751558Srgrimes			return (GOOD);
6761558Srgrimes		}
67735852Sjkh		if (uflag)
678161598Smaxim			(void) unlink(name);
679161598Smaxim		if (mknod(name, (mode & (IFCHR | IFBLK)) | 0600,
680161605Smaxim		    (int)curfile.rdev) < 0) {
6811558Srgrimes			fprintf(stderr, "%s: cannot create special file: %s\n",
6821558Srgrimes			    name, strerror(errno));
6831558Srgrimes			skipfile();
6841558Srgrimes			return (FAIL);
6851558Srgrimes		}
686167011Smckusick		if (extsize == 0) {
687167011Smckusick			skipfile();
688167011Smckusick		} else {
689167011Smckusick			buf = setupextattr(extsize);
690167011Smckusick			getfile(xtrnull, xtrattr, xtrnull);
691167011Smckusick			set_extattr_file(name, buf, extsize);
692167011Smckusick		}
693161598Smaxim		(void) chown(name, uid, gid);
6941558Srgrimes		(void) chmod(name, mode);
695279986Sjilles		(void) utimensat(AT_FDCWD, name, ctimep, 0);
696279986Sjilles		(void) utimensat(AT_FDCWD, name, mtimep, 0);
69723685Speter		(void) chflags(name, flags);
6981558Srgrimes		return (GOOD);
6991558Srgrimes
7001558Srgrimes	case IFREG:
7011558Srgrimes		vprintf(stdout, "extract file %s\n", name);
7021558Srgrimes		if (Nflag) {
7031558Srgrimes			skipfile();
7041558Srgrimes			return (GOOD);
7051558Srgrimes		}
70635852Sjkh		if (uflag)
707161598Smaxim			(void) unlink(name);
70821149Simp		if ((ofile = open(name, O_WRONLY | O_CREAT | O_TRUNC,
709161598Smaxim		    0600)) < 0) {
7101558Srgrimes			fprintf(stderr, "%s: cannot create file: %s\n",
7111558Srgrimes			    name, strerror(errno));
7121558Srgrimes			skipfile();
7131558Srgrimes			return (FAIL);
7141558Srgrimes		}
715167011Smckusick		buf = setupextattr(extsize);
716167011Smckusick		getfile(xtrfile, xtrattr, xtrskip);
717167011Smckusick		if (extsize > 0)
718167011Smckusick			set_extattr_fd(ofile, name, buf, extsize);
719161598Smaxim		(void) fchown(ofile, uid, gid);
7201558Srgrimes		(void) fchmod(ofile, mode);
721279986Sjilles		(void) futimens(ofile, ctimep);
722279986Sjilles		(void) futimens(ofile, mtimep);
723161598Smaxim		(void) fchflags(ofile, flags);
7241558Srgrimes		(void) close(ofile);
7251558Srgrimes		return (GOOD);
7261558Srgrimes	}
7271558Srgrimes	/* NOTREACHED */
7281558Srgrimes}
7291558Srgrimes
7301558Srgrimes/*
731167011Smckusick * Set attributes for a file.
732167011Smckusick */
733167011Smckusickvoid
734167011Smckusickset_extattr_file(char *name, void *buf, int size)
735167011Smckusick{
736167011Smckusick	struct extattr *eap, *eaend;
737167011Smckusick
738167011Smckusick	vprintf(stdout, "Set attributes for %s:", name);
739167011Smckusick	eaend = buf + size;
740167011Smckusick	for (eap = buf; eap < eaend; eap = EXTATTR_NEXT(eap)) {
741167011Smckusick		/*
742167011Smckusick		 * Make sure this entry is complete.
743167011Smckusick		 */
744167011Smckusick		if (EXTATTR_NEXT(eap) > eaend || eap->ea_length <= 0) {
745167011Smckusick			dprintf(stdout, "\n\t%scorrupted",
746167011Smckusick				eap == buf ? "" : "remainder ");
747167011Smckusick			break;
748167011Smckusick		}
749167011Smckusick		if (eap->ea_namespace == EXTATTR_NAMESPACE_EMPTY)
750167011Smckusick			continue;
751167011Smckusick		vprintf(stdout, "\n\t%s, (%d bytes), %*s",
752167011Smckusick			namespace_names[eap->ea_namespace], eap->ea_length,
753167011Smckusick			eap->ea_namelength, eap->ea_name);
754167011Smckusick		/*
755167011Smckusick		 * First we try the general attribute setting interface.
756167011Smckusick		 * However, some attributes can only be set by root or
757167011Smckusick		 * by using special interfaces (for example, ACLs).
758167011Smckusick		 */
759167011Smckusick		if (extattr_set_file(name, eap->ea_namespace, eap->ea_name,
760167011Smckusick		    EXTATTR_CONTENT(eap), EXTATTR_CONTENT_SIZE(eap)) != -1) {
761167011Smckusick			dprintf(stdout, " (set using extattr_set_file)");
762167011Smckusick			continue;
763167011Smckusick		}
764167011Smckusick		/*
765167011Smckusick		 * If the general interface refuses to set the attribute,
766167011Smckusick		 * then we try all the specialized interfaces that we
767167011Smckusick		 * know about.
768167011Smckusick		 */
769167011Smckusick		if (eap->ea_namespace == EXTATTR_NAMESPACE_SYSTEM &&
770167011Smckusick		    !strcmp(eap->ea_name, POSIX1E_ACL_ACCESS_EXTATTR_NAME)) {
771167011Smckusick			if (acl_set_file(name, ACL_TYPE_ACCESS,
772167011Smckusick			    EXTATTR_CONTENT(eap)) != -1) {
773167011Smckusick				dprintf(stdout, " (set using acl_set_file)");
774167011Smckusick				continue;
775167011Smckusick			}
776167011Smckusick		}
777167011Smckusick		if (eap->ea_namespace == EXTATTR_NAMESPACE_SYSTEM &&
778167011Smckusick		    !strcmp(eap->ea_name, POSIX1E_ACL_DEFAULT_EXTATTR_NAME)) {
779167011Smckusick			if (acl_set_file(name, ACL_TYPE_DEFAULT,
780167011Smckusick			    EXTATTR_CONTENT(eap)) != -1) {
781167011Smckusick				dprintf(stdout, " (set using acl_set_file)");
782167011Smckusick				continue;
783167011Smckusick			}
784167011Smckusick		}
785167011Smckusick		vprintf(stdout, " (unable to set)");
786167011Smckusick	}
787167011Smckusick	vprintf(stdout, "\n");
788167011Smckusick}
789167011Smckusick
790167011Smckusick/*
791167011Smckusick * Set attributes for a symbolic link.
792167011Smckusick */
793167011Smckusickstatic void
794167011Smckusickset_extattr_link(char *name, void *buf, int size)
795167011Smckusick{
796167011Smckusick	struct extattr *eap, *eaend;
797167011Smckusick
798167011Smckusick	vprintf(stdout, "Set attributes for %s:", name);
799167011Smckusick	eaend = buf + size;
800167011Smckusick	for (eap = buf; eap < eaend; eap = EXTATTR_NEXT(eap)) {
801167011Smckusick		/*
802167011Smckusick		 * Make sure this entry is complete.
803167011Smckusick		 */
804167011Smckusick		if (EXTATTR_NEXT(eap) > eaend || eap->ea_length <= 0) {
805167011Smckusick			dprintf(stdout, "\n\t%scorrupted",
806167011Smckusick				eap == buf ? "" : "remainder ");
807167011Smckusick			break;
808167011Smckusick		}
809167011Smckusick		if (eap->ea_namespace == EXTATTR_NAMESPACE_EMPTY)
810167011Smckusick			continue;
811167011Smckusick		vprintf(stdout, "\n\t%s, (%d bytes), %*s",
812167011Smckusick			namespace_names[eap->ea_namespace], eap->ea_length,
813167011Smckusick			eap->ea_namelength, eap->ea_name);
814167011Smckusick		/*
815167011Smckusick		 * First we try the general attribute setting interface.
816167011Smckusick		 * However, some attributes can only be set by root or
817167011Smckusick		 * by using special interfaces (for example, ACLs).
818167011Smckusick		 */
819167011Smckusick		if (extattr_set_link(name, eap->ea_namespace, eap->ea_name,
820167011Smckusick		    EXTATTR_CONTENT(eap), EXTATTR_CONTENT_SIZE(eap)) != -1) {
821167011Smckusick			dprintf(stdout, " (set using extattr_set_link)");
822167011Smckusick			continue;
823167011Smckusick		}
824167011Smckusick		/*
825167011Smckusick		 * If the general interface refuses to set the attribute,
826167011Smckusick		 * then we try all the specialized interfaces that we
827167011Smckusick		 * know about.
828167011Smckusick		 */
829167011Smckusick		if (eap->ea_namespace == EXTATTR_NAMESPACE_SYSTEM &&
830167011Smckusick		    !strcmp(eap->ea_name, POSIX1E_ACL_ACCESS_EXTATTR_NAME)) {
831167011Smckusick			if (acl_set_link_np(name, ACL_TYPE_ACCESS,
832167011Smckusick			    EXTATTR_CONTENT(eap)) != -1) {
833167011Smckusick				dprintf(stdout, " (set using acl_set_link_np)");
834167011Smckusick				continue;
835167011Smckusick			}
836167011Smckusick		}
837167011Smckusick		if (eap->ea_namespace == EXTATTR_NAMESPACE_SYSTEM &&
838167011Smckusick		    !strcmp(eap->ea_name, POSIX1E_ACL_DEFAULT_EXTATTR_NAME)) {
839167011Smckusick			if (acl_set_link_np(name, ACL_TYPE_DEFAULT,
840167011Smckusick			    EXTATTR_CONTENT(eap)) != -1) {
841167011Smckusick				dprintf(stdout, " (set using acl_set_link_np)");
842167011Smckusick				continue;
843167011Smckusick			}
844167011Smckusick		}
845167011Smckusick		vprintf(stdout, " (unable to set)");
846167011Smckusick	}
847167011Smckusick	vprintf(stdout, "\n");
848167011Smckusick}
849167011Smckusick
850167011Smckusick/*
851167011Smckusick * Set attributes on a file descriptor.
852167011Smckusick */
853167011Smckusickstatic void
854167011Smckusickset_extattr_fd(int fd, char *name, void *buf, int size)
855167011Smckusick{
856167011Smckusick	struct extattr *eap, *eaend;
857167011Smckusick
858167011Smckusick	vprintf(stdout, "Set attributes for %s:", name);
859167011Smckusick	eaend = buf + size;
860167011Smckusick	for (eap = buf; eap < eaend; eap = EXTATTR_NEXT(eap)) {
861167011Smckusick		/*
862167011Smckusick		 * Make sure this entry is complete.
863167011Smckusick		 */
864167011Smckusick		if (EXTATTR_NEXT(eap) > eaend || eap->ea_length <= 0) {
865167011Smckusick			dprintf(stdout, "\n\t%scorrupted",
866167011Smckusick				eap == buf ? "" : "remainder ");
867167011Smckusick			break;
868167011Smckusick		}
869167011Smckusick		if (eap->ea_namespace == EXTATTR_NAMESPACE_EMPTY)
870167011Smckusick			continue;
871167011Smckusick		vprintf(stdout, "\n\t%s, (%d bytes), %*s",
872167011Smckusick			namespace_names[eap->ea_namespace], eap->ea_length,
873167011Smckusick			eap->ea_namelength, eap->ea_name);
874167011Smckusick		/*
875167011Smckusick		 * First we try the general attribute setting interface.
876167011Smckusick		 * However, some attributes can only be set by root or
877167011Smckusick		 * by using special interfaces (for example, ACLs).
878167011Smckusick		 */
879167011Smckusick		if (extattr_set_fd(fd, eap->ea_namespace, eap->ea_name,
880167011Smckusick		    EXTATTR_CONTENT(eap), EXTATTR_CONTENT_SIZE(eap)) != -1) {
881167011Smckusick			dprintf(stdout, " (set using extattr_set_fd)");
882167011Smckusick			continue;
883167011Smckusick		}
884167011Smckusick		/*
885167011Smckusick		 * If the general interface refuses to set the attribute,
886167011Smckusick		 * then we try all the specialized interfaces that we
887167011Smckusick		 * know about.
888167011Smckusick		 */
889167011Smckusick		if (eap->ea_namespace == EXTATTR_NAMESPACE_SYSTEM &&
890167011Smckusick		    !strcmp(eap->ea_name, POSIX1E_ACL_ACCESS_EXTATTR_NAME)) {
891167011Smckusick			if (acl_set_fd(fd, EXTATTR_CONTENT(eap)) != -1) {
892167011Smckusick				dprintf(stdout, " (set using acl_set_fd)");
893167011Smckusick				continue;
894167011Smckusick			}
895167011Smckusick		}
896167011Smckusick		if (eap->ea_namespace == EXTATTR_NAMESPACE_SYSTEM &&
897167011Smckusick		    !strcmp(eap->ea_name, POSIX1E_ACL_DEFAULT_EXTATTR_NAME)) {
898167011Smckusick			if (acl_set_file(name, ACL_TYPE_DEFAULT,
899167011Smckusick			    EXTATTR_CONTENT(eap)) != -1) {
900167011Smckusick				dprintf(stdout, " (set using acl_set_file)");
901167011Smckusick				continue;
902167011Smckusick			}
903167011Smckusick		}
904167011Smckusick		vprintf(stdout, " (unable to set)");
905167011Smckusick	}
906167011Smckusick	vprintf(stdout, "\n");
907167011Smckusick}
908167011Smckusick
909167011Smckusick/*
9101558Srgrimes * skip over bit maps on the tape
9111558Srgrimes */
9121558Srgrimesvoid
91392837Simpskipmaps(void)
9141558Srgrimes{
9151558Srgrimes
9161558Srgrimes	while (spcl.c_type == TS_BITS || spcl.c_type == TS_CLRI)
9171558Srgrimes		skipfile();
9181558Srgrimes}
9191558Srgrimes
9201558Srgrimes/*
9211558Srgrimes * skip over a file on the tape
9221558Srgrimes */
9231558Srgrimesvoid
92492837Simpskipfile(void)
9251558Srgrimes{
9261558Srgrimes
9271558Srgrimes	curfile.action = SKIP;
928167011Smckusick	getfile(xtrnull, xtrnull, xtrnull);
9291558Srgrimes}
9301558Srgrimes
9311558Srgrimes/*
932290900Sjmg * Skip a hole in an output file
933290900Sjmg */
934290900Sjmgstatic void
935298901Spfgskiphole(void (*skip)(char *, size_t), size_t *seekpos)
936290900Sjmg{
937290900Sjmg	char buf[MAXBSIZE];
938290900Sjmg
939290900Sjmg	if (*seekpos > 0) {
940290900Sjmg		(*skip)(buf, *seekpos);
941290900Sjmg		*seekpos = 0;
942290900Sjmg	}
943290900Sjmg}
944290900Sjmg
945290900Sjmg/*
9461558Srgrimes * Extract a file from the tape.
9471558Srgrimes * When an allocated block is found it is passed to the fill function;
9481558Srgrimes * when an unallocated block (hole) is found, a zeroed buffer is passed
9491558Srgrimes * to the skip function.
9501558Srgrimes */
9511558Srgrimesvoid
952298901Spfggetfile(void (*datafill)(char *, size_t), void (*attrfill)(char *, size_t),
953298901Spfg	void (*skip)(char *, size_t))
9541558Srgrimes{
95592806Sobrien	int i;
956298901Spfg	volatile off_t size;
957298901Spfg	size_t seekpos;
958167011Smckusick	int curblk, attrsize;
959298901Spfg	void (*fillit)(char *, size_t);
9601558Srgrimes	char buf[MAXBSIZE / TP_BSIZE][TP_BSIZE];
9611558Srgrimes	char junk[TP_BSIZE];
9621558Srgrimes
963167011Smckusick	curblk = 0;
964167011Smckusick	size = spcl.c_size;
965290900Sjmg	seekpos = 0;
966167011Smckusick	attrsize = spcl.c_extsize;
9671558Srgrimes	if (spcl.c_type == TS_END)
9681558Srgrimes		panic("ran off end of tape\n");
96998542Smckusick	if (spcl.c_magic != FS_UFS2_MAGIC)
9701558Srgrimes		panic("not at beginning of a file\n");
9711558Srgrimes	if (!gettingfile && setjmp(restart) != 0)
9721558Srgrimes		return;
9731558Srgrimes	gettingfile++;
974167011Smckusick	fillit = datafill;
975167011Smckusick	if (size == 0 && attrsize > 0) {
976167011Smckusick		fillit = attrfill;
977167011Smckusick		size = attrsize;
978167011Smckusick		attrsize = 0;
979167011Smckusick	}
9801558Srgrimesloop:
9811558Srgrimes	for (i = 0; i < spcl.c_count; i++) {
982164911Sdwmalone		if (!readmapflag && i > TP_NINDIR) {
983164911Sdwmalone			if (Dflag) {
984164911Sdwmalone				fprintf(stderr, "spcl.c_count = %jd\n",
985164911Sdwmalone				    (intmax_t)spcl.c_count);
986164911Sdwmalone				break;
987164911Sdwmalone			} else
988164911Sdwmalone				panic("spcl.c_count = %jd\n",
989164911Sdwmalone				    (intmax_t)spcl.c_count);
990164911Sdwmalone		}
99137923Simp		if (readmapflag || spcl.c_addr[i]) {
9921558Srgrimes			readtape(&buf[curblk++][0]);
9931558Srgrimes			if (curblk == fssize / TP_BSIZE) {
994290900Sjmg				skiphole(skip, &seekpos);
995167011Smckusick				(*fillit)((char *)buf, (long)(size > TP_BSIZE ?
99623685Speter				     fssize : (curblk - 1) * TP_BSIZE + size));
9971558Srgrimes				curblk = 0;
9981558Srgrimes			}
9991558Srgrimes		} else {
10001558Srgrimes			if (curblk > 0) {
1001290900Sjmg				skiphole(skip, &seekpos);
1002167011Smckusick				(*fillit)((char *)buf, (long)(size > TP_BSIZE ?
100323685Speter				     curblk * TP_BSIZE :
100423685Speter				     (curblk - 1) * TP_BSIZE + size));
10051558Srgrimes				curblk = 0;
10061558Srgrimes			}
1007290900Sjmg			/*
1008290900Sjmg			 * We have a block of a hole. Don't skip it
1009290900Sjmg			 * now, because there may be next adjacent
1010290900Sjmg			 * block of the hole in the file. Postpone the
1011290900Sjmg			 * seek until next file write.
1012290900Sjmg			 */
1013298394Saraujo			seekpos += (long)MIN(TP_BSIZE, size);
10141558Srgrimes		}
10151558Srgrimes		if ((size -= TP_BSIZE) <= 0) {
1016167011Smckusick			if (size > -TP_BSIZE && curblk > 0) {
1017290900Sjmg				skiphole(skip, &seekpos);
1018167011Smckusick				(*fillit)((char *)buf,
1019167011Smckusick					(long)((curblk * TP_BSIZE) + size));
1020167011Smckusick				curblk = 0;
1021167011Smckusick			}
1022167011Smckusick			if (attrsize > 0) {
1023167011Smckusick				fillit = attrfill;
1024167011Smckusick				size = attrsize;
1025167011Smckusick				attrsize = 0;
1026167011Smckusick				continue;
1027167011Smckusick			}
1028167011Smckusick			if (spcl.c_count - i > 1)
1029167011Smckusick				dprintf(stdout, "skipping %d junk block(s)\n",
1030167011Smckusick					spcl.c_count - i - 1);
1031164911Sdwmalone			for (i++; i < spcl.c_count; i++) {
1032164911Sdwmalone				if (!readmapflag && i > TP_NINDIR) {
1033164911Sdwmalone					if (Dflag) {
1034164911Sdwmalone						fprintf(stderr,
1035164911Sdwmalone						    "spcl.c_count = %jd\n",
1036164911Sdwmalone						    (intmax_t)spcl.c_count);
1037164911Sdwmalone						break;
1038164911Sdwmalone					} else
1039164911Sdwmalone						panic("spcl.c_count = %jd\n",
1040164911Sdwmalone						    (intmax_t)spcl.c_count);
1041164911Sdwmalone				}
104237923Simp				if (readmapflag || spcl.c_addr[i])
10431558Srgrimes					readtape(junk);
1044164911Sdwmalone			}
10451558Srgrimes			break;
10461558Srgrimes		}
10471558Srgrimes	}
10481558Srgrimes	if (gethead(&spcl) == GOOD && size > 0) {
10491558Srgrimes		if (spcl.c_type == TS_ADDR)
10501558Srgrimes			goto loop;
10511558Srgrimes		dprintf(stdout,
105237240Sbde			"Missing address (header) block for %s at %ld blocks\n",
10531558Srgrimes			curfile.name, blksread);
10541558Srgrimes	}
10551558Srgrimes	if (curblk > 0)
1056167011Smckusick		panic("getfile: lost data\n");
10571558Srgrimes	findinode(&spcl);
10581558Srgrimes	gettingfile = 0;
10591558Srgrimes}
10601558Srgrimes
10611558Srgrimes/*
1062167011Smckusick * These variables are shared between the next two functions.
1063167011Smckusick */
1064167011Smckusickstatic int extbufsize = 0;
1065167011Smckusickstatic char *extbuf;
1066167011Smckusickstatic int extloc;
1067167011Smckusick
1068167011Smckusick/*
1069167011Smckusick * Allocate a buffer into which to extract extended attributes.
1070167011Smckusick */
1071167011Smckusickstatic char *
1072167011Smckusicksetupextattr(int extsize)
1073167011Smckusick{
1074167011Smckusick
1075167011Smckusick	extloc = 0;
1076167011Smckusick	if (extsize <= extbufsize)
1077167011Smckusick		return (extbuf);
1078167011Smckusick	if (extbufsize > 0)
1079167011Smckusick		free(extbuf);
1080167011Smckusick	if ((extbuf = malloc(extsize)) != NULL) {
1081167011Smckusick		extbufsize = extsize;
1082167011Smckusick		return (extbuf);
1083167011Smckusick	}
1084167011Smckusick	extbufsize = 0;
1085167011Smckusick	extbuf = NULL;
1086241013Smdf	fprintf(stderr, "Cannot extract %d bytes %s for inode %ju, name %s\n",
1087241013Smdf	    extsize, "of extended attributes", (uintmax_t)curfile.ino,
1088241013Smdf	    curfile.name);
1089167011Smckusick	return (NULL);
1090167011Smckusick}
1091167011Smckusick
1092167011Smckusick/*
1093167011Smckusick * Extract the next block of extended attributes.
1094167011Smckusick */
1095167011Smckusickstatic void
1096298901Spfgxtrattr(char *buf, size_t size)
1097167011Smckusick{
1098167011Smckusick
1099167011Smckusick	if (extloc + size > extbufsize)
1100167011Smckusick		panic("overrun attribute buffer\n");
1101167011Smckusick	memmove(&extbuf[extloc], buf, size);
1102167011Smckusick	extloc += size;
1103167011Smckusick}
1104167011Smckusick
1105167011Smckusick/*
11061558Srgrimes * Write out the next block of a file.
11071558Srgrimes */
11081558Srgrimesstatic void
1109298901Spfgxtrfile(char *buf, size_t size)
11101558Srgrimes{
11111558Srgrimes
11121558Srgrimes	if (Nflag)
11131558Srgrimes		return;
11141558Srgrimes	if (write(ofile, buf, (int) size) == -1) {
11151558Srgrimes		fprintf(stderr,
1116241013Smdf		    "write error extracting inode %ju, name %s\nwrite: %s\n",
1117241013Smdf		    (uintmax_t)curfile.ino, curfile.name, strerror(errno));
11181558Srgrimes	}
11191558Srgrimes}
11201558Srgrimes
11211558Srgrimes/*
11221558Srgrimes * Skip over a hole in a file.
11231558Srgrimes */
11241558Srgrimes/* ARGSUSED */
11251558Srgrimesstatic void
1126298901Spfgxtrskip(char *buf, size_t size)
11271558Srgrimes{
11281558Srgrimes
11291558Srgrimes	if (lseek(ofile, size, SEEK_CUR) == -1) {
11301558Srgrimes		fprintf(stderr,
1131241013Smdf		    "seek error extracting inode %ju, name %s\nlseek: %s\n",
1132241013Smdf		    (uintmax_t)curfile.ino, curfile.name, strerror(errno));
11331558Srgrimes		done(1);
11341558Srgrimes	}
11351558Srgrimes}
11361558Srgrimes
11371558Srgrimes/*
11381558Srgrimes * Collect the next block of a symbolic link.
11391558Srgrimes */
11401558Srgrimesstatic void
1141298901Spfgxtrlnkfile(char *buf, size_t size)
11421558Srgrimes{
11431558Srgrimes
11441558Srgrimes	pathlen += size;
11451558Srgrimes	if (pathlen > MAXPATHLEN) {
11461558Srgrimes		fprintf(stderr, "symbolic link name: %s->%s%s; too long %d\n",
11471558Srgrimes		    curfile.name, lnkbuf, buf, pathlen);
11481558Srgrimes		done(1);
11491558Srgrimes	}
11501558Srgrimes	(void) strcat(lnkbuf, buf);
11511558Srgrimes}
11521558Srgrimes
11531558Srgrimes/*
11541558Srgrimes * Skip over a hole in a symbolic link (should never happen).
11551558Srgrimes */
11561558Srgrimes/* ARGSUSED */
11571558Srgrimesstatic void
1158298901Spfgxtrlnkskip(char *buf, size_t size)
11591558Srgrimes{
11601558Srgrimes
11611558Srgrimes	fprintf(stderr, "unallocated block in symbolic link %s\n",
11621558Srgrimes		curfile.name);
11631558Srgrimes	done(1);
11641558Srgrimes}
11651558Srgrimes
11661558Srgrimes/*
11671558Srgrimes * Collect the next block of a bit map.
11681558Srgrimes */
11691558Srgrimesstatic void
1170298901Spfgxtrmap(char *buf, size_t size)
11711558Srgrimes{
11721558Srgrimes
117323685Speter	memmove(map, buf, size);
11741558Srgrimes	map += size;
11751558Srgrimes}
11761558Srgrimes
11771558Srgrimes/*
11781558Srgrimes * Skip over a hole in a bit map (should never happen).
11791558Srgrimes */
11801558Srgrimes/* ARGSUSED */
11811558Srgrimesstatic void
1182298901Spfgxtrmapskip(char *buf, size_t size)
11831558Srgrimes{
11841558Srgrimes
11851558Srgrimes	panic("hole in map\n");
11861558Srgrimes	map += size;
11871558Srgrimes}
11881558Srgrimes
11891558Srgrimes/*
11901558Srgrimes * Noop, when an extraction function is not needed.
11911558Srgrimes */
11921558Srgrimes/* ARGSUSED */
11931558Srgrimesvoid
1194298901Spfgxtrnull(char *buf, size_t size)
11951558Srgrimes{
11961558Srgrimes
11971558Srgrimes	return;
11981558Srgrimes}
11991558Srgrimes
12001558Srgrimes/*
12011558Srgrimes * Read TP_BSIZE blocks from the input.
12021558Srgrimes * Handle read errors, and end of media.
12031558Srgrimes */
12041558Srgrimesstatic void
120592837Simpreadtape(char *buf)
12061558Srgrimes{
1207164911Sdwmalone	long rd, newvol, i, oldnumtrec;
12081558Srgrimes	int cnt, seek_failed;
12091558Srgrimes
1210164911Sdwmalone	if (blkcnt + (byteslide > 0) < numtrec) {
1211164911Sdwmalone		memmove(buf, &tapebuf[(blkcnt++ * TP_BSIZE) + byteslide], (long)TP_BSIZE);
12121558Srgrimes		blksread++;
121390827Siedowse		tapeaddr++;
12141558Srgrimes		return;
12151558Srgrimes	}
1216164911Sdwmalone	if (numtrec > 0)
1217164911Sdwmalone		memmove(&tapebuf[-TP_BSIZE],
1218164911Sdwmalone		    &tapebuf[(numtrec-1) * TP_BSIZE], (long)TP_BSIZE);
1219164911Sdwmalone	oldnumtrec = numtrec;
12201558Srgrimes	for (i = 0; i < ntrec; i++)
12211558Srgrimes		((struct s_spcl *)&tapebuf[i * TP_BSIZE])->c_magic = 0;
12221558Srgrimes	if (numtrec == 0)
12231558Srgrimes		numtrec = ntrec;
12241558Srgrimes	cnt = ntrec * TP_BSIZE;
12251558Srgrimes	rd = 0;
12261558Srgrimesgetmore:
12271558Srgrimes#ifdef RRESTORE
12281558Srgrimes	if (host)
12291558Srgrimes		i = rmtread(&tapebuf[rd], cnt);
12301558Srgrimes	else
12311558Srgrimes#endif
12321558Srgrimes		i = read(mt, &tapebuf[rd], cnt);
12331558Srgrimes	/*
12341558Srgrimes	 * Check for mid-tape short read error.
12351558Srgrimes	 * If found, skip rest of buffer and start with the next.
12361558Srgrimes	 */
1237203157Sjh	if (!pipein && !pipecmdin && numtrec < ntrec && i > 0) {
12381558Srgrimes		dprintf(stdout, "mid-media short read error.\n");
12391558Srgrimes		numtrec = ntrec;
12401558Srgrimes	}
12411558Srgrimes	/*
12421558Srgrimes	 * Handle partial block read.
12431558Srgrimes	 */
1244203157Sjh	if ((pipein || pipecmdin) && i == 0 && rd > 0)
12451558Srgrimes		i = rd;
12461558Srgrimes	else if (i > 0 && i != ntrec * TP_BSIZE) {
1247203157Sjh		if (pipein || pipecmdin) {
12481558Srgrimes			rd += i;
12491558Srgrimes			cnt -= i;
12501558Srgrimes			if (cnt > 0)
12511558Srgrimes				goto getmore;
12521558Srgrimes			i = rd;
12531558Srgrimes		} else {
12541558Srgrimes			/*
12551558Srgrimes			 * Short read. Process the blocks read.
12561558Srgrimes			 */
12571558Srgrimes			if (i % TP_BSIZE != 0)
12581558Srgrimes				vprintf(stdout,
125937240Sbde				    "partial block read: %ld should be %ld\n",
12601558Srgrimes				    i, ntrec * TP_BSIZE);
12611558Srgrimes			numtrec = i / TP_BSIZE;
12621558Srgrimes		}
12631558Srgrimes	}
12641558Srgrimes	/*
12651558Srgrimes	 * Handle read error.
12661558Srgrimes	 */
12671558Srgrimes	if (i < 0) {
12681558Srgrimes		fprintf(stderr, "Tape read error while ");
12691558Srgrimes		switch (curfile.action) {
12701558Srgrimes		default:
12711558Srgrimes			fprintf(stderr, "trying to set up tape\n");
12721558Srgrimes			break;
12731558Srgrimes		case UNKNOWN:
12741558Srgrimes			fprintf(stderr, "trying to resynchronize\n");
12751558Srgrimes			break;
12761558Srgrimes		case USING:
12771558Srgrimes			fprintf(stderr, "restoring %s\n", curfile.name);
12781558Srgrimes			break;
12791558Srgrimes		case SKIP:
1280241013Smdf			fprintf(stderr, "skipping over inode %ju\n",
1281241013Smdf			    (uintmax_t)curfile.ino);
12821558Srgrimes			break;
12831558Srgrimes		}
12841558Srgrimes		if (!yflag && !reply("continue"))
12851558Srgrimes			done(1);
12861558Srgrimes		i = ntrec * TP_BSIZE;
128723685Speter		memset(tapebuf, 0, i);
12881558Srgrimes#ifdef RRESTORE
12891558Srgrimes		if (host)
12901558Srgrimes			seek_failed = (rmtseek(i, 1) < 0);
12911558Srgrimes		else
12921558Srgrimes#endif
12931558Srgrimes			seek_failed = (lseek(mt, i, SEEK_CUR) == (off_t)-1);
12941558Srgrimes
12951558Srgrimes		if (seek_failed) {
12961558Srgrimes			fprintf(stderr,
12971558Srgrimes			    "continuation failed: %s\n", strerror(errno));
12981558Srgrimes			done(1);
12991558Srgrimes		}
13001558Srgrimes	}
13011558Srgrimes	/*
13021558Srgrimes	 * Handle end of tape.
13031558Srgrimes	 */
13041558Srgrimes	if (i == 0) {
13051558Srgrimes		vprintf(stdout, "End-of-tape encountered\n");
13061558Srgrimes		if (!pipein) {
13071558Srgrimes			newvol = volno + 1;
13081558Srgrimes			volno = 0;
13091558Srgrimes			numtrec = 0;
13101558Srgrimes			getvol(newvol);
13111558Srgrimes			readtape(buf);
13121558Srgrimes			return;
13131558Srgrimes		}
13141558Srgrimes		if (rd % TP_BSIZE != 0)
1315203155Sjh			panic("partial block read: %ld should be %ld\n",
13161558Srgrimes				rd, ntrec * TP_BSIZE);
13171558Srgrimes		terminateinput();
131823685Speter		memmove(&tapebuf[rd], &endoftapemark, (long)TP_BSIZE);
13191558Srgrimes	}
1320164911Sdwmalone	if (oldnumtrec == 0)
1321164911Sdwmalone		blkcnt = 0;
1322164911Sdwmalone	else
1323164911Sdwmalone		blkcnt -= oldnumtrec;
1324164911Sdwmalone	memmove(buf,
1325164911Sdwmalone	    &tapebuf[(blkcnt++ * TP_BSIZE) + byteslide], (long)TP_BSIZE);
13261558Srgrimes	blksread++;
132790827Siedowse	tapeaddr++;
13281558Srgrimes}
13291558Srgrimes
13301558Srgrimesstatic void
133192837Simpfindtapeblksize(void)
13321558Srgrimes{
133392806Sobrien	long i;
13341558Srgrimes
13351558Srgrimes	for (i = 0; i < ntrec; i++)
13361558Srgrimes		((struct s_spcl *)&tapebuf[i * TP_BSIZE])->c_magic = 0;
13371558Srgrimes	blkcnt = 0;
13381558Srgrimes#ifdef RRESTORE
13391558Srgrimes	if (host)
13401558Srgrimes		i = rmtread(tapebuf, ntrec * TP_BSIZE);
13411558Srgrimes	else
13421558Srgrimes#endif
13431558Srgrimes		i = read(mt, tapebuf, ntrec * TP_BSIZE);
13441558Srgrimes
13451558Srgrimes	if (i <= 0) {
13461558Srgrimes		fprintf(stderr, "tape read error: %s\n", strerror(errno));
13471558Srgrimes		done(1);
13481558Srgrimes	}
13491558Srgrimes	if (i % TP_BSIZE != 0) {
135037240Sbde		fprintf(stderr, "Tape block size (%ld) %s (%d)\n",
13511558Srgrimes			i, "is not a multiple of dump block size", TP_BSIZE);
13521558Srgrimes		done(1);
13531558Srgrimes	}
13541558Srgrimes	ntrec = i / TP_BSIZE;
13551558Srgrimes	numtrec = ntrec;
135637240Sbde	vprintf(stdout, "Tape block size is %ld\n", ntrec);
13571558Srgrimes}
13581558Srgrimes
13591558Srgrimesvoid
136092837Simpclosemt(void)
13611558Srgrimes{
13621558Srgrimes
13631558Srgrimes	if (mt < 0)
13641558Srgrimes		return;
1365128175Sgreen	if (pipecmdin) {
1366128175Sgreen		pclose(popenfp);
1367128175Sgreen		popenfp = NULL;
1368128175Sgreen	} else
13691558Srgrimes#ifdef RRESTORE
13701558Srgrimes	if (host)
13711558Srgrimes		rmtclose();
13721558Srgrimes	else
13731558Srgrimes#endif
13741558Srgrimes		(void) close(mt);
13751558Srgrimes}
13761558Srgrimes
13771558Srgrimes/*
13781558Srgrimes * Read the next block from the tape.
13791558Srgrimes * If it is not any valid header, return an error.
13801558Srgrimes */
13811558Srgrimesstatic int
138292837Simpgethead(struct s_spcl *buf)
13831558Srgrimes{
13841558Srgrimes	long i;
13851558Srgrimes
138698542Smckusick	readtape((char *)buf);
138798542Smckusick	if (buf->c_magic != FS_UFS2_MAGIC && buf->c_magic != NFS_MAGIC) {
138898542Smckusick		if (buf->c_magic == OFS_MAGIC) {
138998542Smckusick			fprintf(stderr,
139098542Smckusick			    "Format of dump tape is too old. Must use\n");
139198542Smckusick			fprintf(stderr,
139298542Smckusick			    "a version of restore from before 2002.\n");
139398542Smckusick			return (FAIL);
139498542Smckusick		}
139598542Smckusick		if (swabl(buf->c_magic) != FS_UFS2_MAGIC &&
139698542Smckusick		    buf->c_magic != NFS_MAGIC) {
139798542Smckusick			if (buf->c_magic == OFS_MAGIC) {
139898542Smckusick				fprintf(stderr,
139998542Smckusick				  "Format of dump tape is too old. Must use\n");
140098542Smckusick				fprintf(stderr,
140198542Smckusick				  "a version of restore from before 2002.\n");
14021558Srgrimes			}
14031558Srgrimes			return (FAIL);
140423096Simp		}
140598542Smckusick		if (!Bcvt) {
140698542Smckusick			vprintf(stdout, "Note: Doing Byte swapping\n");
140798542Smckusick			Bcvt = 1;
14081558Srgrimes		}
14091558Srgrimes	}
141098542Smckusick	if (checksum((int *)buf) == FAIL)
141198542Smckusick		return (FAIL);
141298542Smckusick	if (Bcvt) {
141398542Smckusick		swabst((u_char *)"8l4s1q8l2q17l", (u_char *)buf);
141498542Smckusick		swabst((u_char *)"l",(u_char *) &buf->c_level);
141598542Smckusick		swabst((u_char *)"2l4q",(u_char *) &buf->c_flags);
14161558Srgrimes	}
141737923Simp	readmapflag = 0;
14181558Srgrimes
14191558Srgrimes	switch (buf->c_type) {
14201558Srgrimes
14211558Srgrimes	case TS_CLRI:
14221558Srgrimes	case TS_BITS:
14231558Srgrimes		/*
14241558Srgrimes		 * Have to patch up missing information in bit map headers
14251558Srgrimes		 */
142698542Smckusick		buf->c_size = buf->c_count * TP_BSIZE;
142737923Simp		if (buf->c_count > TP_NINDIR)
142837923Simp			readmapflag = 1;
142937923Simp		else
143037923Simp			for (i = 0; i < buf->c_count; i++)
143137923Simp				buf->c_addr[i]++;
1432179218Smckusick		/* FALL THROUGH */
14331558Srgrimes
14341558Srgrimes	case TS_TAPE:
1435179218Smckusick		if (buf->c_magic == NFS_MAGIC &&
1436179218Smckusick		    (buf->c_flags & NFS_DR_NEWINODEFMT) == 0)
1437179218Smckusick			oldinofmt = 1;
1438179218Smckusick		/* FALL THROUGH */
1439179218Smckusick
14401558Srgrimes	case TS_END:
14411558Srgrimes		buf->c_inumber = 0;
1442179218Smckusick		/* FALL THROUGH */
14431558Srgrimes
1444179218Smckusick	case TS_ADDR:
14451558Srgrimes	case TS_INODE:
144698542Smckusick		/*
144798542Smckusick		 * For old dump tapes, have to copy up old fields to
144898542Smckusick		 * new locations.
144998542Smckusick		 */
145098542Smckusick		if (buf->c_magic == NFS_MAGIC) {
145198542Smckusick			buf->c_tapea = buf->c_old_tapea;
145298542Smckusick			buf->c_firstrec = buf->c_old_firstrec;
145398542Smckusick			buf->c_date = _time32_to_time(buf->c_old_date);
145498542Smckusick			buf->c_ddate = _time32_to_time(buf->c_old_ddate);
145598542Smckusick			buf->c_atime = _time32_to_time(buf->c_old_atime);
145698542Smckusick			buf->c_mtime = _time32_to_time(buf->c_old_mtime);
1457179219Smckusick			buf->c_birthtime = 0;
1458179219Smckusick			buf->c_birthtimensec = 0;
1459179219Smckusick			buf->c_extsize = 0;
146098542Smckusick		}
146198542Smckusick		break;
146298542Smckusick
14631558Srgrimes	default:
14641558Srgrimes		panic("gethead: unknown inode type %d\n", buf->c_type);
14651558Srgrimes		break;
14661558Srgrimes	}
1467179218Smckusick	if (dumpdate != 0 && _time64_to_time(buf->c_date) != dumpdate)
1468179218Smckusick		fprintf(stderr, "Header with wrong dumpdate.\n");
1469144099Simp	/*
1470144099Simp	 * If we're restoring a filesystem with the old (FreeBSD 1)
1471144099Simp	 * format inodes, copy the uid/gid to the new location
1472144099Simp	 */
1473144099Simp	if (oldinofmt) {
1474144099Simp		buf->c_uid = buf->c_spare1[1];
1475144099Simp		buf->c_gid = buf->c_spare1[2];
1476144099Simp	}
147798542Smckusick	buf->c_magic = FS_UFS2_MAGIC;
147890827Siedowse	tapeaddr = buf->c_tapea;
14791558Srgrimes	if (dflag)
14801558Srgrimes		accthdr(buf);
14811558Srgrimes	return(GOOD);
14821558Srgrimes}
14831558Srgrimes
14841558Srgrimes/*
14851558Srgrimes * Check that a header is where it belongs and predict the next header
14861558Srgrimes */
14871558Srgrimesstatic void
148892837Simpaccthdr(struct s_spcl *header)
14891558Srgrimes{
14901558Srgrimes	static ino_t previno = 0x7fffffff;
14911558Srgrimes	static int prevtype;
14921558Srgrimes	static long predict;
14931558Srgrimes	long blks, i;
14941558Srgrimes
14951558Srgrimes	if (header->c_type == TS_TAPE) {
149698542Smckusick		fprintf(stderr, "Volume header ");
14971558Srgrimes 		if (header->c_firstrec)
1498203155Sjh 			fprintf(stderr, "begins with record %jd",
1499203155Sjh			    (intmax_t)header->c_firstrec);
15001558Srgrimes 		fprintf(stderr, "\n");
15011558Srgrimes		previno = 0x7fffffff;
15021558Srgrimes		return;
15031558Srgrimes	}
15041558Srgrimes	if (previno == 0x7fffffff)
15051558Srgrimes		goto newcalc;
15061558Srgrimes	switch (prevtype) {
15071558Srgrimes	case TS_BITS:
150823685Speter		fprintf(stderr, "Dumped inodes map header");
15091558Srgrimes		break;
15101558Srgrimes	case TS_CLRI:
151123685Speter		fprintf(stderr, "Used inodes map header");
15121558Srgrimes		break;
15131558Srgrimes	case TS_INODE:
1514241013Smdf		fprintf(stderr, "File header, ino %ju", (uintmax_t)previno);
15151558Srgrimes		break;
15161558Srgrimes	case TS_ADDR:
1517241013Smdf		fprintf(stderr, "File continuation header, ino %ju",
1518241013Smdf		    (uintmax_t)previno);
15191558Srgrimes		break;
15201558Srgrimes	case TS_END:
15211558Srgrimes		fprintf(stderr, "End of tape header");
15221558Srgrimes		break;
15231558Srgrimes	}
15241558Srgrimes	if (predict != blksread - 1)
152537240Sbde		fprintf(stderr, "; predicted %ld blocks, got %ld blocks",
15261558Srgrimes			predict, blksread - 1);
15271558Srgrimes	fprintf(stderr, "\n");
15281558Srgrimesnewcalc:
15291558Srgrimes	blks = 0;
15301558Srgrimes	if (header->c_type != TS_END)
15311558Srgrimes		for (i = 0; i < header->c_count; i++)
153237923Simp			if (readmapflag || header->c_addr[i] != 0)
15331558Srgrimes				blks++;
15341558Srgrimes	predict = blks;
15351558Srgrimes	blksread = 0;
15361558Srgrimes	prevtype = header->c_type;
15371558Srgrimes	previno = header->c_inumber;
15381558Srgrimes}
15391558Srgrimes
15401558Srgrimes/*
15411558Srgrimes * Find an inode header.
154290573Siedowse * Complain if had to skip.
15431558Srgrimes */
15441558Srgrimesstatic void
154592837Simpfindinode(struct s_spcl *header)
15461558Srgrimes{
15471558Srgrimes	static long skipcnt = 0;
15481558Srgrimes	long i;
15491558Srgrimes	char buf[TP_BSIZE];
155090608Siedowse	int htype;
15511558Srgrimes
15521558Srgrimes	curfile.name = "<name unknown>";
15531558Srgrimes	curfile.action = UNKNOWN;
155498542Smckusick	curfile.mode = 0;
15551558Srgrimes	curfile.ino = 0;
15561558Srgrimes	do {
155790608Siedowse		htype = header->c_type;
155890608Siedowse		switch (htype) {
15591558Srgrimes
15601558Srgrimes		case TS_ADDR:
15611558Srgrimes			/*
15621558Srgrimes			 * Skip up to the beginning of the next record
15631558Srgrimes			 */
15641558Srgrimes			for (i = 0; i < header->c_count; i++)
15651558Srgrimes				if (header->c_addr[i])
15661558Srgrimes					readtape(buf);
15671558Srgrimes			while (gethead(header) == FAIL ||
1568164911Sdwmalone			    _time64_to_time(header->c_date) != dumpdate) {
15691558Srgrimes				skipcnt++;
1570164911Sdwmalone				if (Dflag) {
1571164911Sdwmalone					byteslide++;
1572164911Sdwmalone					if (byteslide < TP_BSIZE) {
1573164911Sdwmalone						blkcnt--;
1574164911Sdwmalone						blksread--;
1575164911Sdwmalone					} else
1576164911Sdwmalone						byteslide = 0;
1577164911Sdwmalone				}
1578164911Sdwmalone			}
15791558Srgrimes			break;
15801558Srgrimes
15811558Srgrimes		case TS_INODE:
158298542Smckusick			curfile.mode = header->c_mode;
158398542Smckusick			curfile.uid = header->c_uid;
158498542Smckusick			curfile.gid = header->c_gid;
158598542Smckusick			curfile.file_flags = header->c_file_flags;
158698542Smckusick			curfile.rdev = header->c_rdev;
158798542Smckusick			curfile.atime_sec = header->c_atime;
158898542Smckusick			curfile.atime_nsec = header->c_atimensec;
158998542Smckusick			curfile.mtime_sec = header->c_mtime;
159098542Smckusick			curfile.mtime_nsec = header->c_mtimensec;
1591100207Smckusick			curfile.birthtime_sec = header->c_birthtime;
1592100207Smckusick			curfile.birthtime_nsec = header->c_birthtimensec;
1593167011Smckusick			curfile.extsize = header->c_extsize;
159498542Smckusick			curfile.size = header->c_size;
15951558Srgrimes			curfile.ino = header->c_inumber;
15961558Srgrimes			break;
15971558Srgrimes
15981558Srgrimes		case TS_END:
159990820Siedowse			/* If we missed some tapes, get another volume. */
160090820Siedowse			if (tapesread & (tapesread + 1)) {
160190820Siedowse				getvol(0);
160290820Siedowse				continue;
160390820Siedowse			}
16041558Srgrimes			curfile.ino = maxino;
16051558Srgrimes			break;
16061558Srgrimes
16071558Srgrimes		case TS_CLRI:
16081558Srgrimes			curfile.name = "<file removal list>";
16091558Srgrimes			break;
16101558Srgrimes
16111558Srgrimes		case TS_BITS:
16121558Srgrimes			curfile.name = "<file dump list>";
16131558Srgrimes			break;
16141558Srgrimes
16151558Srgrimes		case TS_TAPE:
1616164911Sdwmalone			if (Dflag)
1617164911Sdwmalone				fprintf(stderr, "unexpected tape header\n");
1618164911Sdwmalone			else
1619164911Sdwmalone				panic("unexpected tape header\n");
16201558Srgrimes
16211558Srgrimes		default:
1622164911Sdwmalone			if (Dflag)
1623164911Sdwmalone				fprintf(stderr, "unknown tape header type %d\n",
1624164911Sdwmalone				    spcl.c_type);
1625164911Sdwmalone			else
1626164911Sdwmalone				panic("unknown tape header type %d\n",
1627164911Sdwmalone				    spcl.c_type);
1628164911Sdwmalone			while (gethead(header) == FAIL ||
1629164911Sdwmalone			    _time64_to_time(header->c_date) != dumpdate) {
1630164911Sdwmalone				skipcnt++;
1631164911Sdwmalone				if (Dflag) {
1632164911Sdwmalone					byteslide++;
1633164911Sdwmalone					if (byteslide < TP_BSIZE) {
1634164911Sdwmalone						blkcnt--;
1635164911Sdwmalone						blksread--;
1636164911Sdwmalone					} else
1637164911Sdwmalone						byteslide = 0;
1638164911Sdwmalone				}
1639164911Sdwmalone			}
16401558Srgrimes
16411558Srgrimes		}
164290608Siedowse	} while (htype == TS_ADDR);
16431558Srgrimes	if (skipcnt > 0)
1644164911Sdwmalone		fprintf(stderr, "resync restore, skipped %ld %s\n",
1645164911Sdwmalone		    skipcnt, Dflag ? "bytes" : "blocks");
16461558Srgrimes	skipcnt = 0;
16471558Srgrimes}
16481558Srgrimes
16491558Srgrimesstatic int
165092837Simpchecksum(int *buf)
16511558Srgrimes{
165292806Sobrien	int i, j;
16531558Srgrimes
16541558Srgrimes	j = sizeof(union u_spcl) / sizeof(int);
16551558Srgrimes	i = 0;
165698542Smckusick	if (!Bcvt) {
16571558Srgrimes		do
16581558Srgrimes			i += *buf++;
16591558Srgrimes		while (--j);
16601558Srgrimes	} else {
16611558Srgrimes		/* What happens if we want to read restore tapes
16621558Srgrimes			for a 16bit int machine??? */
16638871Srgrimes		do
16641558Srgrimes			i += swabl(*buf++);
16651558Srgrimes		while (--j);
16661558Srgrimes	}
16678871Srgrimes
16681558Srgrimes	if (i != CHECKSUM) {
1669241013Smdf		fprintf(stderr, "Checksum error %o, inode %ju file %s\n", i,
1670241013Smdf		    (uintmax_t)curfile.ino, curfile.name);
16711558Srgrimes		return(FAIL);
16721558Srgrimes	}
16731558Srgrimes	return(GOOD);
16741558Srgrimes}
16751558Srgrimes
16761558Srgrimes#ifdef RRESTORE
16771558Srgrimes#include <stdarg.h>
16781558Srgrimes
16791558Srgrimesvoid
16801558Srgrimesmsg(const char *fmt, ...)
16811558Srgrimes{
16821558Srgrimes	va_list ap;
16831558Srgrimes	va_start(ap, fmt);
16841558Srgrimes	(void)vfprintf(stderr, fmt, ap);
16851558Srgrimes	va_end(ap);
16861558Srgrimes}
16871558Srgrimes#endif /* RRESTORE */
16881558Srgrimes
16891558Srgrimesstatic u_char *
169092837Simpswabshort(u_char *sp, int n)
16911558Srgrimes{
16921558Srgrimes	char c;
16931558Srgrimes
16941558Srgrimes	while (--n >= 0) {
16951558Srgrimes		c = sp[0]; sp[0] = sp[1]; sp[1] = c;
16961558Srgrimes		sp += 2;
16971558Srgrimes	}
16981558Srgrimes	return (sp);
16991558Srgrimes}
17001558Srgrimes
17011558Srgrimesstatic u_char *
170292837Simpswablong(u_char *sp, int n)
17031558Srgrimes{
17041558Srgrimes	char c;
17051558Srgrimes
17061558Srgrimes	while (--n >= 0) {
17071558Srgrimes		c = sp[0]; sp[0] = sp[3]; sp[3] = c;
17081558Srgrimes		c = sp[2]; sp[2] = sp[1]; sp[1] = c;
17091558Srgrimes		sp += 4;
17101558Srgrimes	}
17111558Srgrimes	return (sp);
17121558Srgrimes}
17131558Srgrimes
171498542Smckusickstatic u_char *
171598542Smckusickswabquad(u_char *sp, int n)
171698542Smckusick{
171798542Smckusick	char c;
171898542Smckusick
171998542Smckusick	while (--n >= 0) {
172098542Smckusick		c = sp[0]; sp[0] = sp[7]; sp[7] = c;
172198542Smckusick		c = sp[1]; sp[1] = sp[6]; sp[6] = c;
172298542Smckusick		c = sp[2]; sp[2] = sp[5]; sp[5] = c;
172398542Smckusick		c = sp[3]; sp[3] = sp[4]; sp[4] = c;
172498542Smckusick		sp += 8;
172598542Smckusick	}
172698542Smckusick	return (sp);
172798542Smckusick}
172898542Smckusick
17291558Srgrimesvoid
173092837Simpswabst(u_char *cp, u_char *sp)
17311558Srgrimes{
17321558Srgrimes	int n = 0;
17331558Srgrimes
17341558Srgrimes	while (*cp) {
17351558Srgrimes		switch (*cp) {
17361558Srgrimes		case '0': case '1': case '2': case '3': case '4':
17371558Srgrimes		case '5': case '6': case '7': case '8': case '9':
17381558Srgrimes			n = (n * 10) + (*cp++ - '0');
17391558Srgrimes			continue;
17408871Srgrimes
17411558Srgrimes		case 's': case 'w': case 'h':
17421558Srgrimes			if (n == 0)
17431558Srgrimes				n = 1;
17441558Srgrimes			sp = swabshort(sp, n);
17451558Srgrimes			break;
17461558Srgrimes
17471558Srgrimes		case 'l':
17481558Srgrimes			if (n == 0)
17491558Srgrimes				n = 1;
17501558Srgrimes			sp = swablong(sp, n);
17511558Srgrimes			break;
17521558Srgrimes
175398542Smckusick		case 'q':
17541558Srgrimes			if (n == 0)
17551558Srgrimes				n = 1;
175698542Smckusick			sp = swabquad(sp, n);
175798542Smckusick			break;
175898542Smckusick
175998542Smckusick		case 'b':
176098542Smckusick			if (n == 0)
176198542Smckusick				n = 1;
17621558Srgrimes			sp += n;
17631558Srgrimes			break;
176498542Smckusick
176598542Smckusick		default:
176698542Smckusick			fprintf(stderr, "Unknown conversion character: %c\n",
176798542Smckusick			    *cp);
176898542Smckusick			done(0);
176998542Smckusick			break;
17701558Srgrimes		}
17711558Srgrimes		cp++;
17721558Srgrimes		n = 0;
17731558Srgrimes	}
17741558Srgrimes}
17751558Srgrimes
17761558Srgrimesstatic u_long
177792837Simpswabl(u_long x)
17781558Srgrimes{
17791558Srgrimes	swabst((u_char *)"l", (u_char *)&x);
17801558Srgrimes	return (x);
17811558Srgrimes}
1782