1/*	$OpenBSD: tape.c,v 1.54 2024/05/09 08:35:40 florian Exp $	*/
2/*	$NetBSD: tape.c,v 1.26 1997/04/15 07:12:25 lukem Exp $	*/
3
4/*
5 * Copyright (c) 1983, 1993
6 *	The Regents of the University of California.  All rights reserved.
7 * (c) UNIX System Laboratories, Inc.
8 * All or some portions of this file are derived from material licensed
9 * to the University of California by American Telephone and Telegraph
10 * Co. or Unix System Laboratories, Inc. and are reproduced herein with
11 * the permission of UNIX System Laboratories, Inc.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 *    notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 *    notice, this list of conditions and the following disclaimer in the
20 *    documentation and/or other materials provided with the distribution.
21 * 3. Neither the name of the University nor the names of its contributors
22 *    may be used to endorse or promote products derived from this software
23 *    without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
36 */
37
38#include <sys/param.h>	/* MAXBSIZE */
39#include <sys/ioctl.h>
40#include <sys/mtio.h>
41#include <sys/stat.h>
42
43#include <ufs/ufs/dinode.h>
44#include <protocols/dumprestore.h>
45
46#include <err.h>
47#include <fcntl.h>
48#include <paths.h>
49#include <setjmp.h>
50#include <stdio.h>
51#include <stdlib.h>
52#include <string.h>
53#include <unistd.h>
54#include <limits.h>
55
56#include "restore.h"
57#include "extern.h"
58
59static long	fssize = MAXBSIZE;
60static int	mt = -1;
61static int	pipein = 0;
62static char	magtape[BUFSIZ];
63static int	blkcnt;
64static int	numtrec;
65static char	*tapebuf;
66static union	u_spcl endoftapemark;
67static long	blksread;		/* blocks read since last header */
68static long	tpblksread = 0;		/* TP_BSIZE blocks read */
69static long	tapesread;
70static jmp_buf	restart;
71static int	gettingfile = 0;	/* restart has a valid frame */
72#ifdef RRESTORE
73static char	*host = NULL;
74#endif
75
76static int	ofile;
77static char	*map;
78static char	lnkbuf[PATH_MAX + 1];
79static size_t	pathlen;
80
81struct context	curfile;	/* describes next file available on the tape */
82union u_spcl 	u_spcl;		/* mapping of variables in a control block */
83int		oldinofmt;	/* old inode format conversion required */
84int		Bcvt;		/* Swap Bytes (for CCI or sun) */
85
86#define	FLUSHTAPEBUF()	blkcnt = ntrec + 1
87
88union u_ospcl {
89	char dummy[TP_BSIZE];
90	struct	s_ospcl {
91		int32_t   c_type;
92		int32_t   c_date;
93		int32_t   c_ddate;
94		int32_t   c_volume;
95		int32_t   c_tapea;
96		u_int16_t c_inumber;
97		int32_t   c_magic;
98		int32_t   c_checksum;
99		struct odinode {
100			unsigned short odi_mode;
101			u_int16_t odi_nlink;
102			u_int16_t odi_uid;
103			u_int16_t odi_gid;
104			int32_t   odi_size;
105			int32_t   odi_rdev;
106			char      odi_addr[36];
107			int32_t   odi_atime;
108			int32_t   odi_mtime;
109			int32_t   odi_ctime;
110		} c_odinode;
111		int32_t c_count;
112		char    c_addr[256];
113	} s_ospcl;
114};
115
116static void	 accthdr(struct s_spcl *);
117static int	 checksum(int *);
118static void	 findinode(struct s_spcl *);
119static void	 findtapeblksize(void);
120static int	 gethead(struct s_spcl *);
121static void	 readtape(char *);
122static void	 setdumpnum(void);
123static void	 swap_header(struct s_spcl *);
124static void	 swap_old_header(struct s_ospcl *);
125static void	 terminateinput(void);
126static void	 xtrlnkfile(char *, size_t);
127static void	 xtrlnkskip(char *, size_t);
128static void	 xtrmap(char *, size_t);
129static void	 xtrmapskip(char *, size_t);
130static void	 xtrskip(char *, size_t);
131
132/*
133 * Set up an input source
134 */
135void
136setinput(char *source)
137{
138	FLUSHTAPEBUF();
139	if (bflag)
140		newtapebuf(ntrec);
141	else
142		/* Max out buffer size, let findtapeblksize() set ntrec. */
143		newtapebuf(MAXBSIZE / TP_BSIZE);
144	terminal = stdin;
145
146#ifdef RRESTORE
147	if (strchr(source, ':')) {
148		host = source;
149		source = strchr(host, ':');
150		*source++ = '\0';
151		if (rmthost(host) == 0)
152			exit(1);
153	} else
154#endif
155	if (strcmp(source, "-") == 0) {
156		/*
157		 * Since input is coming from a pipe we must establish
158		 * our own connection to the terminal.
159		 */
160		terminal = fopen(_PATH_TTY, "r");
161		if (terminal == NULL) {
162			warn("cannot open %s", _PATH_TTY);
163			terminal = fopen(_PATH_DEVNULL, "r");
164			if (terminal == NULL)
165				err(1, "cannot open %s", _PATH_DEVNULL);
166		}
167		pipein++;
168	}
169	(void)strlcpy(magtape, source, sizeof magtape);
170}
171
172void
173newtapebuf(long size)
174{
175	static long tapebufsize = -1;
176
177	ntrec = size;
178	if (size <= tapebufsize)
179		return;
180	free(tapebuf);
181	tapebuf = calloc(size, TP_BSIZE);
182	if (tapebuf == NULL)
183		errx(1, "Cannot allocate space for tape buffer");
184	tapebufsize = size;
185}
186
187/*
188 * Verify that the tape drive can be accessed and
189 * that it actually is a dump tape.
190 */
191void
192setup(void)
193{
194	int i, j, *ip;
195	struct stat stbuf;
196
197	Vprintf(stdout, "Verify tape and initialize maps\n");
198#ifdef RRESTORE
199	if (host)
200		mt = rmtopen(magtape, O_RDONLY);
201	else
202#endif
203	if (pipein)
204		mt = 0;
205	else
206		mt = open(magtape, O_RDONLY);
207	if (mt == -1)
208		err(1, "%s", magtape);
209	volno = 1;
210	setdumpnum();
211	FLUSHTAPEBUF();
212	if (!pipein && !bflag)
213		findtapeblksize();
214	if (gethead(&spcl) == FAIL) {
215		blkcnt--; /* push back this block */
216		blksread--;
217		tpblksread--;
218		cvtflag++;
219		if (gethead(&spcl) == FAIL)
220			errx(1, "Tape is not a dump tape");
221		(void)fputs("Converting to new file system format.\n", stderr);
222	}
223	if (pipein) {
224		endoftapemark.s_spcl.c_magic = cvtflag ? OFS_MAGIC :
225		    FS_UFS2_MAGIC;
226		endoftapemark.s_spcl.c_type = TS_END;
227		ip = (int *)&endoftapemark;
228		j = sizeof(union u_spcl) / sizeof(int);
229		i = 0;
230		do
231			i += *ip++;
232		while (--j);
233		endoftapemark.s_spcl.c_checksum = CHECKSUM - i;
234	}
235	if (vflag || command == 't')
236		printdumpinfo();
237	dumptime = (time_t)spcl.c_ddate;
238	dumpdate = (time_t)spcl.c_date;
239	if (stat(".", &stbuf) == -1)
240		err(1, "cannot stat .");
241	if (stbuf.st_blksize > 0 && stbuf.st_blksize < TP_BSIZE )
242		fssize = TP_BSIZE;
243	if (stbuf.st_blksize >= TP_BSIZE && stbuf.st_blksize <= MAXBSIZE)
244		fssize = stbuf.st_blksize;
245	if (((fssize - 1) & fssize) != 0)
246		errx(1, "bad block size %ld", fssize);
247	if (spcl.c_volume != 1)
248		errx(1, "Tape is not volume 1 of the dump");
249	if (gethead(&spcl) == FAIL) {
250		Dprintf(stdout, "header read failed at %ld blocks\n", blksread);
251		panic("no header after volume mark!\n");
252	}
253	findinode(&spcl);
254	if (spcl.c_type != TS_CLRI)
255		errx(1, "Cannot find file removal list");
256	maxino = (spcl.c_count * TP_BSIZE * NBBY) + 1;
257	Dprintf(stdout, "maxino = %llu\n", (unsigned long long)maxino);
258	map = calloc(1, howmany(maxino, NBBY));
259	if (map == NULL)
260		panic("no memory for active inode map\n");
261	usedinomap = map;
262	curfile.action = USING;
263	getfile(xtrmap, xtrmapskip);
264	if (spcl.c_type != TS_BITS)
265		errx(1, "Cannot find file dump list");
266	map = calloc(1, howmany(maxino, NBBY));
267	if (map == NULL)
268		panic("no memory for file dump list\n");
269	dumpmap = map;
270	curfile.action = USING;
271	getfile(xtrmap, xtrmapskip);
272}
273
274/*
275 * Prompt user to load a new dump volume.
276 * "Nextvol" is the next suggested volume to use.
277 * This suggested volume is enforced when doing full
278 * or incremental restores, but can be overrridden by
279 * the user when only extracting a subset of the files.
280 */
281void
282getvol(long nextvol)
283{
284	long newvol = 0, savecnt = 0, wantnext = 0, i;
285	union u_spcl tmpspcl;
286#	define tmpbuf tmpspcl.s_spcl
287	char buf[TP_BSIZE];
288	const char *errstr;
289
290	if (nextvol == 1) {
291		tapesread = 0;
292		gettingfile = 0;
293	}
294	if (pipein) {
295		if (nextvol != 1)
296			panic("Changing volumes on pipe input?\n");
297		if (volno == 1)
298			return;
299		goto gethdr;
300	}
301	savecnt = blksread;
302again:
303	if (pipein)
304		exit(1); /* pipes do not get a second chance */
305	if (command == 'R' || command == 'r' || curfile.action != SKIP) {
306		newvol = nextvol;
307		wantnext = 1;
308	} else {
309		newvol = 0;
310		wantnext = 0;
311	}
312	while (newvol <= 0) {
313		if (tapesread == 0) {
314			fprintf(stderr, "%s%s%s%s%s",
315			    "You have not read any tapes yet.\n",
316			    "Unless you know which volume your",
317			    " file(s) are on you should start\n",
318			    "with the last volume and work",
319			    " towards the first.\n");
320		} else {
321			fprintf(stderr, "You have read volumes");
322			strlcpy(buf, ": ", sizeof buf);
323			for (i = 1; i < 32; i++)
324				if (tapesread & (1 << i)) {
325					fprintf(stderr, "%s%ld", buf, i);
326					strlcpy(buf, ", ", sizeof buf);
327				}
328			fprintf(stderr, "\n");
329		}
330		do	{
331			fprintf(stderr, "Specify next volume #: ");
332			(void)fflush(stderr);
333			if (fgets(buf, sizeof buf, terminal) == NULL)
334				exit(1);
335			buf[strcspn(buf, "\n")] = '\0';
336
337			newvol = strtonum(buf, 1, INT_MAX, &errstr);
338			if (errstr)
339				fprintf(stderr, "Volume number %s: %s\n", errstr, buf);
340		} while (errstr);
341	}
342	if (newvol == volno) {
343		tapesread |= 1 << volno;
344		return;
345	}
346	closemt();
347	fprintf(stderr, "Mount tape volume %ld\n", newvol);
348	fprintf(stderr, "Enter ``none'' if there are no more tapes\n");
349	fprintf(stderr, "otherwise enter tape name (default: %s) ", magtape);
350	(void)fflush(stderr);
351	if (fgets(buf, sizeof buf, terminal) == NULL || feof(terminal))
352		exit(1);
353	buf[strcspn(buf, "\n")] = '\0';
354	if (strcmp(buf, "none") == 0) {
355		terminateinput();
356		return;
357	}
358	if (buf[0] != '\0')
359		(void)strlcpy(magtape, buf, sizeof magtape);
360
361#ifdef RRESTORE
362	if (host)
363		mt = rmtopen(magtape, O_RDONLY);
364	else
365#endif
366		mt = open(magtape, O_RDONLY);
367
368	if (mt == -1) {
369		fprintf(stderr, "Cannot open %s\n", magtape);
370		volno = -1;
371		goto again;
372	}
373gethdr:
374	volno = newvol;
375	setdumpnum();
376	FLUSHTAPEBUF();
377	if (gethead(&tmpbuf) == FAIL) {
378		Dprintf(stdout, "header read failed at %ld blocks\n", blksread);
379		fprintf(stderr, "tape is not dump tape\n");
380		volno = 0;
381		goto again;
382	}
383	if (tmpbuf.c_volume != volno) {
384		fprintf(stderr, "Wrong volume (%d)\n", tmpbuf.c_volume);
385		volno = 0;
386		goto again;
387	}
388	if (tmpbuf.c_date != dumpdate || tmpbuf.c_ddate != dumptime) {
389		time_t t = (time_t)tmpbuf.c_date;
390		char ct1buf[26], ct2buf[26];
391		char *ct1, *ct2;
392
393		ct1 = ctime_r(&t, ct1buf);
394		ct2 = ctime_r(&dumpdate, ct2buf);
395		if (ct1 && ct2) {
396			fprintf(stderr, "Wrong dump date\n\tgot: %s", ct1);
397			fprintf(stderr, "\twanted: %s", ct2);
398		} else {
399			fprintf(stderr, "Wrong dump date\n\tgot: %lld\n", t);
400			fprintf(stderr, "\twanted: %lld\n", dumpdate);
401		}
402		volno = 0;
403		goto again;
404	}
405	tapesread |= 1 << volno;
406	blksread = savecnt;
407	/*
408	 * If continuing from the previous volume, skip over any
409	 * blocks read already at the end of the previous volume.
410	 *
411	 * If coming to this volume at random, skip to the beginning
412	 * of the next record.
413	 */
414	Dprintf(stdout, "read %ld recs, tape starts with %lld\n",
415		tpblksread, tmpbuf.c_firstrec);
416	if (tmpbuf.c_type == TS_TAPE && (tmpbuf.c_flags & DR_NEWHEADER)) {
417		if (!wantnext) {
418			tpblksread = tmpbuf.c_firstrec;
419			for (i = tmpbuf.c_count; i > 0; i--)
420				readtape(buf);
421		} else if (tmpbuf.c_firstrec > 0 &&
422			   tmpbuf.c_firstrec < tpblksread - 1) {
423			/*
424			 * -1 since we've read the volume header
425			 */
426			i = tpblksread - tmpbuf.c_firstrec - 1;
427			Dprintf(stderr, "Skipping %ld duplicate record%s.\n",
428				i, (i == 1) ? "" : "s");
429			while (--i >= 0)
430				readtape(buf);
431		}
432	}
433	if (curfile.action == USING) {
434		if (volno == 1)
435			panic("active file into volume 1\n");
436		return;
437	}
438	/*
439	 * Skip up to the beginning of the next record
440	 */
441	if (tmpbuf.c_type == TS_TAPE && (tmpbuf.c_flags & DR_NEWHEADER))
442		for (i = tmpbuf.c_count; i > 0; i--)
443			readtape(buf);
444	(void)gethead(&spcl);
445	findinode(&spcl);
446	if (gettingfile) {
447		gettingfile = 0;
448		longjmp(restart, 1);
449	}
450}
451
452/*
453 * Handle unexpected EOF.
454 */
455static void
456terminateinput(void)
457{
458
459	if (gettingfile && curfile.action == USING) {
460		printf("Warning: %s %s\n",
461		    "End-of-input encountered while extracting", curfile.name);
462	}
463	curfile.name = "<name unknown>";
464	curfile.action = UNKNOWN;
465	curfile.mode = 0;
466	curfile.ino = maxino;
467	if (gettingfile) {
468		gettingfile = 0;
469		longjmp(restart, 1);
470	}
471}
472
473/*
474 * handle multiple dumps per tape by skipping forward to the
475 * appropriate one.
476 */
477static void
478setdumpnum(void)
479{
480	struct mtop tcom;
481
482	if (dumpnum == 1 || volno != 1)
483		return;
484	if (pipein)
485		errx(1, "Cannot have multiple dumps on pipe input");
486	tcom.mt_op = MTFSF;
487	tcom.mt_count = dumpnum - 1;
488#ifdef RRESTORE
489	if (host)
490		rmtioctl(MTFSF, dumpnum - 1);
491	else
492#endif
493		if (ioctl(mt, MTIOCTOP, (char *)&tcom) == -1)
494			warn("ioctl MTFSF");
495}
496
497void
498printdumpinfo(void)
499{
500	time_t t;
501	char *ct;
502
503	t = (time_t)spcl.c_date;
504	ct = ctime(&t);
505	if (ct)
506		fprintf(stdout, "Dump   date: %s", ct);
507	else
508		fprintf(stdout, "Dump   date: %lld\n", t);
509	t = (time_t)spcl.c_ddate;
510	ct = ctime(&t);
511	if (ct)
512		fprintf(stdout, "Dumped from: %s",
513		    (spcl.c_ddate == 0) ? "the epoch\n" : ct);
514	else
515		fprintf(stdout, "Dumped from: %lld\n", t);
516	if (spcl.c_host[0] == '\0')
517		return;
518	fprintf(stderr, "Level %d dump of %s on %s:%s\n",
519		spcl.c_level, spcl.c_filesys, spcl.c_host, spcl.c_dev);
520	fprintf(stderr, "Label: %s\n", spcl.c_label);
521}
522
523int
524extractfile(char *name)
525{
526	u_int flags;
527	uid_t uid;
528	gid_t gid;
529	mode_t mode;
530	struct timespec mtimep[2], ctimep[2];
531	struct entry *ep;
532	int setbirth;
533
534	curfile.name = name;
535	curfile.action = USING;
536	mtimep[0].tv_sec = curfile.atime_sec;
537	mtimep[0].tv_nsec = curfile.atime_nsec;
538	mtimep[1].tv_sec = curfile.mtime_sec;
539	mtimep[1].tv_nsec = curfile.mtime_nsec;
540
541	setbirth = curfile.birthtime_sec != 0;
542	if (setbirth) {
543		ctimep[0].tv_sec = curfile.atime_sec;
544		ctimep[0].tv_nsec = curfile.atime_nsec;
545		ctimep[1].tv_sec = curfile.birthtime_sec;
546		ctimep[1].tv_nsec = curfile.birthtime_nsec;
547	}
548	uid = curfile.uid;
549	gid = curfile.gid;
550	mode = curfile.mode;
551	flags = curfile.file_flags;
552	switch (mode & IFMT) {
553
554	default:
555		fprintf(stderr, "%s: unknown file mode 0%o\n", name, mode);
556		skipfile();
557		return (FAIL);
558
559	case IFSOCK:
560		Vprintf(stdout, "skipped socket %s\n", name);
561		skipfile();
562		return (GOOD);
563
564	case IFDIR:
565		if (mflag) {
566			ep = lookupname(name);
567			if (ep == NULL || ep->e_flags & EXTRACT)
568				panic("unextracted directory %s\n", name);
569			skipfile();
570			return (GOOD);
571		}
572		Vprintf(stdout, "extract file %s\n", name);
573		return (genliteraldir(name, curfile.ino));
574
575	case IFLNK: {
576			lnkbuf[0] = '\0';
577			pathlen = 0;
578			getfile(xtrlnkfile, xtrlnkskip);
579			if (pathlen == 0) {
580				Vprintf(stdout,
581				    "%s: zero length symbolic link (ignored)\n",
582				     name);
583				return (GOOD);
584			}
585			if (linkit(lnkbuf, name, SYMLINK) == FAIL)
586				return (FAIL);
587			(void)lchown(name, uid, gid);
588			(void)fchmodat(AT_FDCWD, name, mode,
589			    AT_SYMLINK_NOFOLLOW);
590			return (GOOD);
591		}
592
593	case IFCHR:
594	case IFBLK:
595		Vprintf(stdout, "extract special file %s\n", name);
596		if (Nflag) {
597			skipfile();
598			return (GOOD);
599		}
600		if (mknod(name, mode, (int)curfile.rdev) == -1) {
601			warn("%s: cannot create special file", name);
602			skipfile();
603			return (FAIL);
604		}
605		(void)chown(name, uid, gid);
606		(void)chmod(name, mode);
607		(void)chflags(name, flags);
608		skipfile();
609		if (setbirth)
610			(void)utimensat(AT_FDCWD, name, ctimep, 0);
611		(void)utimensat(AT_FDCWD, name, mtimep, 0);
612		return (GOOD);
613
614	case IFIFO:
615		Vprintf(stdout, "extract fifo %s\n", name);
616		if (Nflag) {
617			skipfile();
618			return (GOOD);
619		}
620		if (mkfifo(name, mode) == -1) {
621			warn("%s: cannot create fifo", name);
622			skipfile();
623			return (FAIL);
624		}
625		(void)chown(name, uid, gid);
626		(void)chmod(name, mode);
627		(void)chflags(name, flags);
628		skipfile();
629		if (setbirth)
630			(void)utimensat(AT_FDCWD, name, ctimep, 0);
631		(void)utimensat(AT_FDCWD, name, mtimep, 0);
632		return (GOOD);
633
634	case IFREG:
635		Vprintf(stdout, "extract file %s\n", name);
636		if (Nflag) {
637			skipfile();
638			return (GOOD);
639		}
640		if ((ofile = open(name, O_WRONLY | O_CREAT | O_TRUNC,
641		    0666)) == -1) {
642			warn("%s: cannot create file", name);
643			skipfile();
644			return (FAIL);
645		}
646		(void)fchown(ofile, curfile.uid, curfile.gid);
647		(void)fchmod(ofile, mode);
648		(void)fchflags(ofile, flags);
649		getfile(xtrfile, xtrskip);
650		(void)close(ofile);
651		if (setbirth)
652			(void)utimensat(AT_FDCWD, name, ctimep, 0);
653		(void)utimensat(AT_FDCWD, name, mtimep, 0);
654		return (GOOD);
655	}
656	/* NOTREACHED */
657}
658
659/*
660 * skip over bit maps on the tape
661 */
662void
663skipmaps(void)
664{
665
666	while (spcl.c_type == TS_BITS || spcl.c_type == TS_CLRI)
667		skipfile();
668}
669
670/*
671 * skip over a file on the tape
672 */
673void
674skipfile(void)
675{
676
677	curfile.action = SKIP;
678	getfile(xtrnull, xtrnull);
679}
680
681/*
682 * Extract a file from the tape.
683 * When an allocated block is found it is passed to the fill function;
684 * when an unallocated block (hole) is found, a zeroed buffer is passed
685 * to the skip function.
686 *
687 * For some block types (TS_BITS, TS_CLRI), the c_addr map is not meaningful
688 * and no blocks should be skipped.
689 */
690void
691getfile(void (*fill)(char *, size_t), void (*skip)(char *, size_t))
692{
693	int i;
694	volatile int curblk = 0;
695	volatile off_t size = spcl.c_size;
696	static char clearedbuf[MAXBSIZE];
697	char buf[MAXBSIZE / TP_BSIZE][TP_BSIZE];
698	char junk[TP_BSIZE];
699	volatile int noskip = (spcl.c_type == TS_BITS || spcl.c_type == TS_CLRI);
700
701	if (spcl.c_type == TS_END)
702		panic("ran off end of tape\n");
703	if (spcl.c_magic != FS_UFS2_MAGIC)
704		panic("not at beginning of a file\n");
705	if (!gettingfile && setjmp(restart) != 0)
706		return;
707	gettingfile++;
708loop:
709	for (i = 0; i < spcl.c_count; i++) {
710		if (noskip || spcl.c_addr[i]) {
711			readtape(&buf[curblk++][0]);
712			if (curblk == fssize / TP_BSIZE) {
713				(*fill)((char *)buf, size > TP_BSIZE ?
714				     fssize :
715				     ((off_t)curblk - 1) * TP_BSIZE + size);
716				curblk = 0;
717			}
718		} else {
719			if (curblk > 0) {
720				(*fill)((char *)buf, size > TP_BSIZE ?
721				     (curblk * TP_BSIZE) :
722				     ((off_t)curblk - 1) * TP_BSIZE + size);
723				curblk = 0;
724			}
725			(*skip)(clearedbuf, size > TP_BSIZE ?
726				TP_BSIZE : size);
727		}
728		if ((size -= TP_BSIZE) <= 0) {
729			for (i++; i < spcl.c_count; i++)
730				if (noskip || spcl.c_addr[i])
731					readtape(junk);
732			break;
733		}
734	}
735	if (gethead(&spcl) == GOOD && size > 0) {
736		if (spcl.c_type == TS_ADDR)
737			goto loop;
738		Dprintf(stdout,
739			"Missing address (header) block for %s at %ld blocks\n",
740			curfile.name, blksread);
741	}
742	if (curblk > 0)
743		(*fill)((char *)buf, ((off_t)curblk * TP_BSIZE) + size);
744	findinode(&spcl);
745	gettingfile = 0;
746}
747
748/*
749 * Write out the next block of a file.
750 */
751void
752xtrfile(char *buf, size_t size)
753{
754
755	if (Nflag)
756		return;
757	if (write(ofile, buf, size) == -1)
758		err(1, "write error extracting inode %llu, name %s\nwrite",
759		    (unsigned long long)curfile.ino, curfile.name);
760}
761
762/*
763 * Skip over a hole in a file.
764 */
765static void
766xtrskip(char *buf, size_t size)
767{
768
769	if (lseek(ofile, (off_t)size, SEEK_CUR) == -1)
770		err(1, "seek error extracting inode %llu, name %s\nlseek",
771		    (unsigned long long)curfile.ino, curfile.name);
772}
773
774/*
775 * Collect the next block of a symbolic link.
776 */
777static void
778xtrlnkfile(char *buf, size_t size)
779{
780
781	pathlen += size;
782	if (pathlen > PATH_MAX)
783		errx(1, "symbolic link name: %s->%s%s; too long %lu",
784		    curfile.name, lnkbuf, buf, (u_long)pathlen);
785	(void)strlcat(lnkbuf, buf, sizeof(lnkbuf));
786}
787
788/*
789 * Skip over a hole in a symbolic link (should never happen).
790 */
791static void
792xtrlnkskip(char *buf, size_t size)
793{
794
795	errx(1, "unallocated block in symbolic link %s", curfile.name);
796}
797
798/*
799 * Collect the next block of a bit map.
800 */
801static void
802xtrmap(char *buf, size_t size)
803{
804
805	memcpy(map, buf, size);
806	map += size;
807}
808
809/*
810 * Skip over a hole in a bit map (should never happen).
811 */
812static void
813xtrmapskip(char *buf, size_t size)
814{
815
816	panic("hole in map\n");
817	map += size;
818}
819
820/*
821 * Noop, when an extraction function is not needed.
822 */
823void
824xtrnull(char *buf, size_t size)
825{
826
827	return;
828}
829
830/*
831 * Read TP_BSIZE blocks from the input.
832 * Handle read errors, and end of media.
833 */
834static void
835readtape(char *buf)
836{
837	long rd, newvol, i;
838	int cnt, seek_failed;
839
840	if (blkcnt < numtrec) {
841		memcpy(buf, &tapebuf[(blkcnt++ * TP_BSIZE)], TP_BSIZE);
842		blksread++;
843		tpblksread++;
844		return;
845	}
846	for (i = 0; i < ntrec; i++)
847		((struct s_spcl *)&tapebuf[i * TP_BSIZE])->c_magic = 0;
848	if (numtrec == 0)
849		numtrec = ntrec;
850	cnt = ntrec * TP_BSIZE;
851	rd = 0;
852getmore:
853#ifdef RRESTORE
854	if (host)
855		i = rmtread(&tapebuf[rd], cnt);
856	else
857#endif
858		i = read(mt, &tapebuf[rd], cnt);
859	/*
860	 * Check for mid-tape short read error.
861	 * If found, skip rest of buffer and start with the next.
862	 */
863	if (!pipein && numtrec < ntrec && i > 0) {
864		Dprintf(stdout, "mid-media short read error.\n");
865		numtrec = ntrec;
866	}
867	/*
868	 * Handle partial block read.
869	 */
870	if (pipein && i == 0 && rd > 0)
871		i = rd;
872	else if (i > 0 && i != ntrec * TP_BSIZE) {
873		if (pipein) {
874			rd += i;
875			cnt -= i;
876			if (cnt > 0)
877				goto getmore;
878			i = rd;
879		} else {
880			/*
881			 * Short read. Process the blocks read.
882			 */
883			if (i % TP_BSIZE != 0)
884				Vprintf(stdout,
885				    "partial block read: %ld should be %ld\n",
886				    i, ntrec * TP_BSIZE);
887			numtrec = i / TP_BSIZE;
888		}
889	}
890	/*
891	 * Handle read error.
892	 */
893	if (i < 0) {
894		fprintf(stderr, "Tape read error while ");
895		switch (curfile.action) {
896		default:
897			fprintf(stderr, "trying to set up tape\n");
898			break;
899		case UNKNOWN:
900			fprintf(stderr, "trying to resynchronize\n");
901			break;
902		case USING:
903			fprintf(stderr, "restoring %s\n", curfile.name);
904			break;
905		case SKIP:
906			fprintf(stderr, "skipping over inode %llu\n",
907			    (unsigned long long)curfile.ino);
908			break;
909		}
910		if (!yflag && !reply("continue"))
911			exit(1);
912		i = ntrec * TP_BSIZE;
913		memset(tapebuf, 0, i);
914#ifdef RRESTORE
915		if (host)
916			seek_failed = (rmtseek(i, 1) < 0);
917		else
918#endif
919			seek_failed = (lseek(mt, i, SEEK_CUR) == (off_t)-1);
920
921		if (seek_failed)
922			err(1, "continuation failed");
923	}
924	/*
925	 * Handle end of tape.
926	 */
927	if (i == 0) {
928		Vprintf(stdout, "End-of-tape encountered\n");
929		if (!pipein) {
930			newvol = volno + 1;
931			volno = 0;
932			numtrec = 0;
933			getvol(newvol);
934			readtape(buf);
935			return;
936		}
937		if (rd % TP_BSIZE != 0)
938			panic("partial block read: %ld should be %ld\n",
939				rd, ntrec * TP_BSIZE);
940		terminateinput();
941		memcpy(&tapebuf[rd], &endoftapemark, TP_BSIZE);
942	}
943	blkcnt = 0;
944	memcpy(buf, &tapebuf[(blkcnt++ * TP_BSIZE)], TP_BSIZE);
945	blksread++;
946	tpblksread++;
947}
948
949static void
950findtapeblksize(void)
951{
952	long i;
953
954	for (i = 0; i < ntrec; i++)
955		((struct s_spcl *)&tapebuf[i * TP_BSIZE])->c_magic = 0;
956	blkcnt = 0;
957#ifdef RRESTORE
958	if (host)
959		i = rmtread(tapebuf, ntrec * TP_BSIZE);
960	else
961#endif
962		i = read(mt, tapebuf, ntrec * TP_BSIZE);
963
964	if (i <= 0)
965		err(1, "tape read error");
966	if (i % TP_BSIZE != 0) {
967		errx(1,
968		    "Tape block size (%ld) is not a multiple of dump block size (%d)",
969		    i, TP_BSIZE);
970	}
971	ntrec = i / TP_BSIZE;
972	numtrec = ntrec;
973	Vprintf(stdout, "Tape block size is %ld\n", ntrec);
974}
975
976void
977closemt(void)
978{
979
980	if (mt < 0)
981		return;
982#ifdef RRESTORE
983	if (host)
984		rmtclose();
985	else
986#endif
987		(void)close(mt);
988}
989
990/*
991 * Read the next block from the tape.
992 * Check to see if it is one of several vintage headers.
993 * If it is an old style header, convert it to a new style header.
994 * If it is not any valid header, return an error.
995 */
996static int
997gethead(struct s_spcl *buf)
998{
999	union u_ospcl u_ospcl;
1000
1001	if (!cvtflag) {
1002		readtape((char *)buf);
1003		if (buf->c_magic != NFS_MAGIC &&
1004		    buf->c_magic != FS_UFS2_MAGIC) {
1005			if (swap32(buf->c_magic) != NFS_MAGIC &&
1006			    swap32(buf->c_magic) != FS_UFS2_MAGIC)
1007				return (FAIL);
1008			if (!Bcvt) {
1009				Vprintf(stdout, "Note: Doing Byte swapping\n");
1010				Bcvt = 1;
1011			}
1012		}
1013		if (checksum((int *)buf) == FAIL)
1014			return (FAIL);
1015		if (Bcvt)
1016			swap_header(buf);
1017		goto good;
1018	}
1019
1020	readtape((char *)(&u_ospcl.s_ospcl));
1021	if (checksum((int *)(&u_ospcl.s_ospcl)) == FAIL)
1022		return (FAIL);
1023	if (u_ospcl.s_ospcl.c_magic != OFS_MAGIC) {
1024		if (swap32(u_ospcl.s_ospcl.c_magic) != OFS_MAGIC)
1025			return (FAIL);
1026		if (!Bcvt) {
1027			fprintf(stdout, "Note: Doing Byte swapping\n");
1028			Bcvt = 1;
1029		}
1030		swap_old_header(&u_ospcl.s_ospcl);
1031	}
1032
1033	memset(buf, 0, TP_BSIZE);
1034	buf->c_type = u_ospcl.s_ospcl.c_type;
1035	buf->c_date = u_ospcl.s_ospcl.c_date;
1036	buf->c_ddate = u_ospcl.s_ospcl.c_ddate;
1037	buf->c_volume = u_ospcl.s_ospcl.c_volume;
1038	buf->c_tapea = u_ospcl.s_ospcl.c_tapea;
1039	buf->c_inumber = u_ospcl.s_ospcl.c_inumber;
1040	buf->c_checksum = u_ospcl.s_ospcl.c_checksum;
1041	buf->c_mode = u_ospcl.s_ospcl.c_odinode.odi_mode;
1042	buf->c_uid = u_ospcl.s_ospcl.c_odinode.odi_uid;
1043	buf->c_gid = u_ospcl.s_ospcl.c_odinode.odi_gid;
1044	buf->c_size = u_ospcl.s_ospcl.c_odinode.odi_size;
1045	buf->c_rdev = u_ospcl.s_ospcl.c_odinode.odi_rdev;
1046	buf->c_atime = u_ospcl.s_ospcl.c_odinode.odi_atime;
1047	buf->c_mtime = u_ospcl.s_ospcl.c_odinode.odi_mtime;
1048	buf->c_count = u_ospcl.s_ospcl.c_count;
1049	memcpy(buf->c_addr, u_ospcl.s_ospcl.c_addr, 256);
1050	buf->c_magic = FS_UFS2_MAGIC;
1051good:
1052	switch (buf->c_type) {
1053
1054	case TS_CLRI:
1055	case TS_BITS:
1056		/*
1057		 * Have to patch up missing information in bit map headers
1058		 */
1059		buf->c_inumber = 0;
1060		buf->c_size = buf->c_count * TP_BSIZE;
1061		break;
1062
1063	case TS_TAPE:
1064		if ((buf->c_flags & DR_NEWINODEFMT) == 0)
1065			oldinofmt = 1;
1066		/* fall through */
1067	case TS_END:
1068		buf->c_inumber = 0;
1069		break;
1070
1071	case TS_INODE:
1072		if (buf->c_magic == NFS_MAGIC) {
1073			buf->c_tapea = buf->c_old_tapea;
1074			buf->c_firstrec = buf->c_old_firstrec;
1075			buf->c_date = buf->c_old_date;
1076			buf->c_ddate = buf->c_old_ddate;
1077			buf->c_atime = buf->c_old_atime;
1078			buf->c_mtime = buf->c_old_mtime;
1079			buf->c_birthtime = 0;
1080			buf->c_birthtimensec = 0;
1081			buf->c_atimensec = buf->c_mtimensec = 0;
1082		}
1083
1084	case TS_ADDR:
1085		break;
1086
1087	default:
1088		panic("gethead: unknown inode type %d\n", buf->c_type);
1089		break;
1090	}
1091
1092	buf->c_magic = FS_UFS2_MAGIC;
1093
1094	/*
1095	 * If we are restoring a filesystem with old format inodes,
1096	 * copy the uid/gid to the new location.
1097	 */
1098	if (oldinofmt) {
1099		buf->c_uid = buf->c_spare1[1];
1100		buf->c_gid = buf->c_spare1[2];
1101	}
1102	if (dflag)
1103		accthdr(buf);
1104	return(GOOD);
1105}
1106
1107/*
1108 * Check that a header is where it belongs and predict the next header
1109 */
1110static void
1111accthdr(struct s_spcl *header)
1112{
1113	static ino_t previno = (ino_t)-1;
1114	static int prevtype;
1115	static long predict;
1116	long blks, i;
1117
1118	if (header->c_type == TS_TAPE) {
1119		fprintf(stderr, "Volume header (%s inode format) ",
1120		    oldinofmt ? "old" : "new");
1121		if (header->c_firstrec)
1122			fprintf(stderr, "begins with record %lld",
1123				(long long)header->c_firstrec);
1124		fprintf(stderr, "\n");
1125		previno = (ino_t)-1;
1126		return;
1127	}
1128	if (previno == (ino_t)-1)
1129		goto newcalc;
1130	switch (prevtype) {
1131	case TS_BITS:
1132		fprintf(stderr, "Dumped inodes map header");
1133		break;
1134	case TS_CLRI:
1135		fprintf(stderr, "Used inodes map header");
1136		break;
1137	case TS_INODE:
1138		fprintf(stderr, "File header, ino %llu",
1139		    (unsigned long long)previno);
1140		break;
1141	case TS_ADDR:
1142		fprintf(stderr, "File continuation header, ino %llu",
1143		    (unsigned long long)previno);
1144		break;
1145	case TS_END:
1146		fprintf(stderr, "End of tape header");
1147		break;
1148	}
1149	if (predict != blksread - 1)
1150		fprintf(stderr, "; predicted %ld blocks, got %ld blocks",
1151			predict, blksread - 1);
1152	fprintf(stderr, "\n");
1153newcalc:
1154	blks = 0;
1155	switch (header->c_type) {
1156
1157	case TS_BITS:
1158	case TS_CLRI:
1159		blks = header->c_count;
1160		break;
1161
1162	case TS_END:
1163		break;
1164
1165	default:
1166		for (i = 0; i < header->c_count; i++)
1167			if (header->c_addr[i] != 0)
1168				blks++;
1169	}
1170	predict = blks;
1171	blksread = 0;
1172	prevtype = header->c_type;
1173	previno = header->c_inumber;
1174}
1175
1176/*
1177 * Find an inode header.
1178 * Complain if had to skip, and complain is set.
1179 */
1180static void
1181findinode(struct s_spcl *header)
1182{
1183	static long skipcnt = 0;
1184	long i;
1185	char buf[TP_BSIZE];
1186
1187	curfile.name = "<name unknown>";
1188	curfile.action = UNKNOWN;
1189	curfile.mode = 0;
1190	curfile.ino = 0;
1191	do {
1192		if (header->c_magic != FS_UFS2_MAGIC) {
1193			skipcnt++;
1194			while (gethead(header) == FAIL ||
1195			    header->c_date != dumpdate)
1196				skipcnt++;
1197		}
1198		switch (header->c_type) {
1199
1200		case TS_ADDR:
1201			/*
1202			 * Skip up to the beginning of the next record
1203			 */
1204			for (i = 0; i < header->c_count; i++)
1205				if (header->c_addr[i])
1206					readtape(buf);
1207			while (gethead(header) == FAIL ||
1208			    header->c_date != dumpdate)
1209				skipcnt++;
1210			break;
1211
1212		case TS_INODE:
1213			curfile.mode = header->c_mode;
1214			curfile.uid = header->c_uid;
1215			curfile.gid = header->c_gid;
1216			curfile.file_flags = header->c_file_flags;
1217			curfile.rdev = header->c_rdev;
1218			curfile.atime_sec = header->c_atime;
1219			curfile.atime_nsec = header->c_atimensec;
1220			curfile.mtime_sec = header->c_mtime;
1221			curfile.mtime_nsec = header->c_mtimensec;
1222			curfile.birthtime_sec = header->c_birthtime;
1223			curfile.birthtime_nsec = header->c_birthtimensec;
1224			curfile.size = header->c_size;
1225			curfile.ino = header->c_inumber;
1226			break;
1227
1228		case TS_END:
1229			curfile.ino = maxino;
1230			break;
1231
1232		case TS_CLRI:
1233			curfile.name = "<file removal list>";
1234			break;
1235
1236		case TS_BITS:
1237			curfile.name = "<file dump list>";
1238			break;
1239
1240		case TS_TAPE:
1241			panic("unexpected tape header\n");
1242			/* NOTREACHED */
1243
1244		default:
1245			panic("unknown tape header type %d\n", spcl.c_type);
1246			/* NOTREACHED */
1247
1248		}
1249	} while (header->c_type == TS_ADDR);
1250	if (skipcnt > 0)
1251		fprintf(stderr, "resync restore, skipped %ld blocks\n", skipcnt);
1252	skipcnt = 0;
1253}
1254
1255static int
1256checksum(int *buf)
1257{
1258	int i, j;
1259
1260	j = sizeof(union u_spcl) / sizeof(int);
1261	i = 0;
1262	if (!Bcvt) {
1263		do
1264			i += *buf++;
1265		while (--j);
1266	} else {
1267		do
1268			i += swap32(*buf++);
1269		while (--j);
1270	}
1271
1272	if (i != CHECKSUM) {
1273		fprintf(stderr, "Checksum error %o, inode %llu file %s\n", i,
1274		    (unsigned long long)curfile.ino, curfile.name);
1275		return(FAIL);
1276	}
1277	return(GOOD);
1278}
1279
1280#ifdef RRESTORE
1281#include <stdarg.h>
1282
1283void
1284msg(const char *fmt, ...)
1285{
1286	va_list ap;
1287
1288	va_start(ap, fmt);
1289	(void)vfprintf(stderr, fmt, ap);
1290	va_end(ap);
1291}
1292#endif /* RRESTORE */
1293
1294static void
1295swap_header(struct s_spcl *s)
1296{
1297	s->c_type = swap32(s->c_type);
1298	s->c_old_date = swap32(s->c_old_date);
1299	s->c_old_ddate = swap32(s->c_old_ddate);
1300	s->c_volume = swap32(s->c_volume);
1301	s->c_old_tapea = swap32(s->c_old_tapea);
1302	s->c_inumber = swap32(s->c_inumber);
1303	s->c_magic = swap32(s->c_magic);
1304	s->c_checksum = swap32(s->c_checksum);
1305
1306	s->c_mode = swap16(s->c_mode);
1307	s->c_size = swap64(s->c_size);
1308	s->c_old_atime = swap32(s->c_old_atime);
1309	s->c_atimensec = swap32(s->c_atimensec);
1310	s->c_old_mtime = swap32(s->c_old_mtime);
1311	s->c_mtimensec = swap32(s->c_mtimensec);
1312	s->c_rdev = swap32(s->c_rdev);
1313	s->c_birthtimensec = swap32(s->c_birthtimensec);
1314	s->c_birthtime = swap64(s->c_birthtime);
1315	s->c_atime = swap64(s->c_atime);
1316	s->c_mtime = swap64(s->c_mtime);
1317	s->c_file_flags = swap32(s->c_file_flags);
1318	s->c_uid = swap32(s->c_uid);
1319	s->c_gid = swap32(s->c_gid);
1320
1321	s->c_count = swap32(s->c_count);
1322	s->c_level = swap32(s->c_level);
1323	s->c_flags = swap32(s->c_flags);
1324	s->c_old_firstrec = swap32(s->c_old_firstrec);
1325
1326	s->c_date = swap64(s->c_date);
1327	s->c_ddate = swap64(s->c_ddate);
1328	s->c_tapea = swap64(s->c_tapea);
1329	s->c_firstrec = swap64(s->c_firstrec);
1330
1331	/*
1332	 * These are ouid and ogid.
1333	 */
1334	s->c_spare1[1] = swap16(s->c_spare1[1]);
1335	s->c_spare1[2] = swap16(s->c_spare1[2]);
1336}
1337
1338static void
1339swap_old_header(struct s_ospcl *os)
1340{
1341	os->c_type = swap32(os->c_type);
1342	os->c_date = swap32(os->c_date);
1343	os->c_ddate = swap32(os->c_ddate);
1344	os->c_volume = swap32(os->c_volume);
1345	os->c_tapea = swap32(os->c_tapea);
1346	os->c_inumber = swap16(os->c_inumber);
1347	os->c_magic = swap32(os->c_magic);
1348	os->c_checksum = swap32(os->c_checksum);
1349
1350	os->c_odinode.odi_mode = swap16(os->c_odinode.odi_mode);
1351	os->c_odinode.odi_nlink = swap16(os->c_odinode.odi_nlink);
1352	os->c_odinode.odi_uid = swap16(os->c_odinode.odi_uid);
1353	os->c_odinode.odi_gid = swap16(os->c_odinode.odi_gid);
1354
1355	os->c_odinode.odi_size = swap32(os->c_odinode.odi_size);
1356	os->c_odinode.odi_rdev = swap32(os->c_odinode.odi_rdev);
1357	os->c_odinode.odi_atime = swap32(os->c_odinode.odi_atime);
1358	os->c_odinode.odi_mtime = swap32(os->c_odinode.odi_mtime);
1359	os->c_odinode.odi_ctime = swap32(os->c_odinode.odi_ctime);
1360
1361	os->c_count = swap32(os->c_count);
1362}
1363