xref: /freebsd/usr.bin/tcopy/tcopy.cc (revision 4f766afc1ca0595fcabafe4dbc61974bb81bfbfe)
1*4f766afcSPoul-Henning Kamp /*-
2*4f766afcSPoul-Henning Kamp  * SPDX-License-Identifier: BSD-2-Clause
3*4f766afcSPoul-Henning Kamp  *
4*4f766afcSPoul-Henning Kamp  * Copyright (c) 2025 Poul-Henning Kamp, <phk@FreeBSD.org>
5*4f766afcSPoul-Henning Kamp  * Copyright (c) 1985, 1987, 1993
6*4f766afcSPoul-Henning Kamp  *	The Regents of the University of California.  All rights reserved.
7*4f766afcSPoul-Henning Kamp  *
8*4f766afcSPoul-Henning Kamp  * Redistribution and use in source and binary forms, with or without
9*4f766afcSPoul-Henning Kamp  * modification, are permitted provided that the following conditions
10*4f766afcSPoul-Henning Kamp  * are met:
11*4f766afcSPoul-Henning Kamp  * 1. Redistributions of source code must retain the above copyright
12*4f766afcSPoul-Henning Kamp  *    notice, this list of conditions and the following disclaimer.
13*4f766afcSPoul-Henning Kamp  * 2. Redistributions in binary form must reproduce the above copyright
14*4f766afcSPoul-Henning Kamp  *    notice, this list of conditions and the following disclaimer in the
15*4f766afcSPoul-Henning Kamp  *    documentation and/or other materials provided with the distribution.
16*4f766afcSPoul-Henning Kamp  *
17*4f766afcSPoul-Henning Kamp  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18*4f766afcSPoul-Henning Kamp  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19*4f766afcSPoul-Henning Kamp  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20*4f766afcSPoul-Henning Kamp  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21*4f766afcSPoul-Henning Kamp  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22*4f766afcSPoul-Henning Kamp  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23*4f766afcSPoul-Henning Kamp  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24*4f766afcSPoul-Henning Kamp  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25*4f766afcSPoul-Henning Kamp  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26*4f766afcSPoul-Henning Kamp  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27*4f766afcSPoul-Henning Kamp  * SUCH DAMAGE.
28*4f766afcSPoul-Henning Kamp  */
29*4f766afcSPoul-Henning Kamp 
30*4f766afcSPoul-Henning Kamp #include <assert.h>
31*4f766afcSPoul-Henning Kamp #include <err.h>
32*4f766afcSPoul-Henning Kamp #include <errno.h>
33*4f766afcSPoul-Henning Kamp #include <fcntl.h>
34*4f766afcSPoul-Henning Kamp #include <paths.h>
35*4f766afcSPoul-Henning Kamp #include <signal.h>
36*4f766afcSPoul-Henning Kamp #include <stdint.h>
37*4f766afcSPoul-Henning Kamp #include <stdio.h>
38*4f766afcSPoul-Henning Kamp #include <stdlib.h>
39*4f766afcSPoul-Henning Kamp #include <string.h>
40*4f766afcSPoul-Henning Kamp #include <time.h>
41*4f766afcSPoul-Henning Kamp #include <sysexits.h>
42*4f766afcSPoul-Henning Kamp #include <unistd.h>
43*4f766afcSPoul-Henning Kamp 
44*4f766afcSPoul-Henning Kamp #include <sys/endian.h>
45*4f766afcSPoul-Henning Kamp #include <sys/mtio.h>
46*4f766afcSPoul-Henning Kamp #include <sys/stat.h>
47*4f766afcSPoul-Henning Kamp #include <sys/sysctl.h>
48*4f766afcSPoul-Henning Kamp #include <sys/uio.h>
49*4f766afcSPoul-Henning Kamp 
50*4f766afcSPoul-Henning Kamp #include <libutil.h>
51*4f766afcSPoul-Henning Kamp 
52*4f766afcSPoul-Henning Kamp #define	MAXREC	(1024 * 1024)
53*4f766afcSPoul-Henning Kamp #define	NOCOUNT	(-2)
54*4f766afcSPoul-Henning Kamp 
55*4f766afcSPoul-Henning Kamp enum operation {READ, VERIFY, COPY, COPYVERIFY};
56*4f766afcSPoul-Henning Kamp 
57*4f766afcSPoul-Henning Kamp // Stuff the tape_devs need to know about
58*4f766afcSPoul-Henning Kamp static int filen;
59*4f766afcSPoul-Henning Kamp static uint64_t	record;
60*4f766afcSPoul-Henning Kamp 
61*4f766afcSPoul-Henning Kamp //---------------------------------------------------------------------
62*4f766afcSPoul-Henning Kamp 
63*4f766afcSPoul-Henning Kamp class tape_dev {
64*4f766afcSPoul-Henning Kamp 	size_t	max_read_size;
65*4f766afcSPoul-Henning Kamp public:
66*4f766afcSPoul-Henning Kamp 	int fd;
67*4f766afcSPoul-Henning Kamp 	char *name;
68*4f766afcSPoul-Henning Kamp 	enum direction {SRC, DST} direction;
69*4f766afcSPoul-Henning Kamp 
70*4f766afcSPoul-Henning Kamp 	tape_dev(int file_handle, const char *spec, bool destination);
71*4f766afcSPoul-Henning Kamp 
72*4f766afcSPoul-Henning Kamp 	virtual ssize_t read_blk(void *dst, size_t len);
73*4f766afcSPoul-Henning Kamp 	virtual ssize_t verify_blk(void *dst, size_t len, size_t expected);
74*4f766afcSPoul-Henning Kamp 	virtual void write_blk(const void *src, size_t len);
75*4f766afcSPoul-Henning Kamp 	virtual void file_mark(void);
76*4f766afcSPoul-Henning Kamp 	virtual void rewind(void);
77*4f766afcSPoul-Henning Kamp };
78*4f766afcSPoul-Henning Kamp 
79*4f766afcSPoul-Henning Kamp tape_dev::tape_dev(int file_handle, const char *spec, bool destination)
80*4f766afcSPoul-Henning Kamp {
81*4f766afcSPoul-Henning Kamp 	assert(file_handle >= 0);
82*4f766afcSPoul-Henning Kamp 	fd = file_handle;
83*4f766afcSPoul-Henning Kamp 	name = strdup(spec);
84*4f766afcSPoul-Henning Kamp 	assert(name != NULL);
85*4f766afcSPoul-Henning Kamp 	direction = destination ? DST : SRC;
86*4f766afcSPoul-Henning Kamp 	max_read_size = 0;
87*4f766afcSPoul-Henning Kamp }
88*4f766afcSPoul-Henning Kamp 
89*4f766afcSPoul-Henning Kamp ssize_t
90*4f766afcSPoul-Henning Kamp tape_dev::read_blk(void *dst, size_t len)
91*4f766afcSPoul-Henning Kamp {
92*4f766afcSPoul-Henning Kamp 	ssize_t retval = -1;
93*4f766afcSPoul-Henning Kamp 
94*4f766afcSPoul-Henning Kamp 	if (max_read_size == 0) {
95*4f766afcSPoul-Henning Kamp 		max_read_size = len;
96*4f766afcSPoul-Henning Kamp 		while (max_read_size > 0) {
97*4f766afcSPoul-Henning Kamp 			retval = read(fd, dst, max_read_size);
98*4f766afcSPoul-Henning Kamp 			if (retval >= 0 || (errno != EINVAL && errno != EFBIG))
99*4f766afcSPoul-Henning Kamp 				break;
100*4f766afcSPoul-Henning Kamp 			if (max_read_size < 512)
101*4f766afcSPoul-Henning Kamp 				errx(1, "Cannot find a sane max blocksize");
102*4f766afcSPoul-Henning Kamp 
103*4f766afcSPoul-Henning Kamp 			// Reduce to next lower power of two
104*4f766afcSPoul-Henning Kamp 			int i = flsl((long)max_read_size - 1L);
105*4f766afcSPoul-Henning Kamp 			max_read_size = 1UL << (i - 1);
106*4f766afcSPoul-Henning Kamp 		}
107*4f766afcSPoul-Henning Kamp 	} else {
108*4f766afcSPoul-Henning Kamp 		retval = read(fd, dst, (size_t)max_read_size);
109*4f766afcSPoul-Henning Kamp 	}
110*4f766afcSPoul-Henning Kamp 	if (retval < 0) {
111*4f766afcSPoul-Henning Kamp 		err(1, "read error, %s, file %d, record %ju",
112*4f766afcSPoul-Henning Kamp 		    name, filen, (uintmax_t)record);
113*4f766afcSPoul-Henning Kamp 	}
114*4f766afcSPoul-Henning Kamp 	return (retval);
115*4f766afcSPoul-Henning Kamp }
116*4f766afcSPoul-Henning Kamp 
117*4f766afcSPoul-Henning Kamp ssize_t
118*4f766afcSPoul-Henning Kamp tape_dev::verify_blk(void *dst, size_t len, size_t expected)
119*4f766afcSPoul-Henning Kamp {
120*4f766afcSPoul-Henning Kamp 	(void)expected;
121*4f766afcSPoul-Henning Kamp 	return read_blk(dst, len);
122*4f766afcSPoul-Henning Kamp }
123*4f766afcSPoul-Henning Kamp 
124*4f766afcSPoul-Henning Kamp void
125*4f766afcSPoul-Henning Kamp tape_dev::write_blk(const void *src, size_t len)
126*4f766afcSPoul-Henning Kamp {
127*4f766afcSPoul-Henning Kamp 	assert(len > 0);
128*4f766afcSPoul-Henning Kamp 	ssize_t nwrite = write(fd, src, len);
129*4f766afcSPoul-Henning Kamp 	if (nwrite < 0 || (size_t) nwrite != len) {
130*4f766afcSPoul-Henning Kamp 		if (nwrite == -1) {
131*4f766afcSPoul-Henning Kamp 			warn("write error, file %d, record %ju",
132*4f766afcSPoul-Henning Kamp 			    filen, (intmax_t)record);
133*4f766afcSPoul-Henning Kamp 		} else {
134*4f766afcSPoul-Henning Kamp 			warnx("write error, file %d, record %ju",
135*4f766afcSPoul-Henning Kamp 			    filen, (intmax_t)record);
136*4f766afcSPoul-Henning Kamp 			warnx("write (%zd) != read (%zd)", nwrite, len);
137*4f766afcSPoul-Henning Kamp 		}
138*4f766afcSPoul-Henning Kamp 		errx(5, "copy aborted");
139*4f766afcSPoul-Henning Kamp 	}
140*4f766afcSPoul-Henning Kamp 	return;
141*4f766afcSPoul-Henning Kamp }
142*4f766afcSPoul-Henning Kamp 
143*4f766afcSPoul-Henning Kamp void
144*4f766afcSPoul-Henning Kamp tape_dev::file_mark(void)
145*4f766afcSPoul-Henning Kamp {
146*4f766afcSPoul-Henning Kamp 	struct mtop op;
147*4f766afcSPoul-Henning Kamp 
148*4f766afcSPoul-Henning Kamp 	op.mt_op = MTWEOF;
149*4f766afcSPoul-Henning Kamp 	op.mt_count = (daddr_t)1;
150*4f766afcSPoul-Henning Kamp 	if (ioctl(fd, MTIOCTOP, (char *)&op) < 0)
151*4f766afcSPoul-Henning Kamp 		err(6, "tape op (write file mark)");
152*4f766afcSPoul-Henning Kamp }
153*4f766afcSPoul-Henning Kamp 
154*4f766afcSPoul-Henning Kamp void
155*4f766afcSPoul-Henning Kamp tape_dev::rewind(void)
156*4f766afcSPoul-Henning Kamp {
157*4f766afcSPoul-Henning Kamp 	struct mtop op;
158*4f766afcSPoul-Henning Kamp 
159*4f766afcSPoul-Henning Kamp 	op.mt_op = MTREW;
160*4f766afcSPoul-Henning Kamp 	op.mt_count = (daddr_t)1;
161*4f766afcSPoul-Henning Kamp 	if (ioctl(fd, MTIOCTOP, (char *)&op) < 0)
162*4f766afcSPoul-Henning Kamp 		err(6, "tape op (rewind)");
163*4f766afcSPoul-Henning Kamp }
164*4f766afcSPoul-Henning Kamp 
165*4f766afcSPoul-Henning Kamp //---------------------------------------------------------------------
166*4f766afcSPoul-Henning Kamp 
167*4f766afcSPoul-Henning Kamp class tap_file: public tape_dev {
168*4f766afcSPoul-Henning Kamp public:
169*4f766afcSPoul-Henning Kamp 	tap_file(int file_handle, const char *spec, bool dst) :
170*4f766afcSPoul-Henning Kamp 		tape_dev(file_handle, spec, dst) {};
171*4f766afcSPoul-Henning Kamp 	ssize_t read_blk(void *dst, size_t len);
172*4f766afcSPoul-Henning Kamp 	void write_blk(const void *src, size_t len);
173*4f766afcSPoul-Henning Kamp 	void file_mark(void);
174*4f766afcSPoul-Henning Kamp 	virtual void rewind(void);
175*4f766afcSPoul-Henning Kamp };
176*4f766afcSPoul-Henning Kamp 
177*4f766afcSPoul-Henning Kamp static
178*4f766afcSPoul-Henning Kamp ssize_t full_read(int fd, void *dst, size_t len)
179*4f766afcSPoul-Henning Kamp {
180*4f766afcSPoul-Henning Kamp 	// Input may be a socket which returns partial reads
181*4f766afcSPoul-Henning Kamp 
182*4f766afcSPoul-Henning Kamp 	ssize_t retval = read(fd, dst, len);
183*4f766afcSPoul-Henning Kamp 	if (retval <= 0 || (size_t)retval == len)
184*4f766afcSPoul-Henning Kamp 		return (retval);
185*4f766afcSPoul-Henning Kamp 
186*4f766afcSPoul-Henning Kamp 	char *ptr = (char *)dst + retval;
187*4f766afcSPoul-Henning Kamp 	size_t left = len - (size_t)retval;
188*4f766afcSPoul-Henning Kamp 	while (left > 0) {
189*4f766afcSPoul-Henning Kamp 		retval = read(fd, ptr, left);
190*4f766afcSPoul-Henning Kamp 		if (retval <= 0)
191*4f766afcSPoul-Henning Kamp 			return (retval);
192*4f766afcSPoul-Henning Kamp 		left -= (size_t)retval;
193*4f766afcSPoul-Henning Kamp 		ptr += retval;
194*4f766afcSPoul-Henning Kamp 	}
195*4f766afcSPoul-Henning Kamp 	return ((ssize_t)len);
196*4f766afcSPoul-Henning Kamp }
197*4f766afcSPoul-Henning Kamp 
198*4f766afcSPoul-Henning Kamp ssize_t
199*4f766afcSPoul-Henning Kamp tap_file::read_blk(void *dst, size_t len)
200*4f766afcSPoul-Henning Kamp {
201*4f766afcSPoul-Henning Kamp 	char lbuf[4];
202*4f766afcSPoul-Henning Kamp 
203*4f766afcSPoul-Henning Kamp 	ssize_t nread = full_read(fd, lbuf, sizeof lbuf);
204*4f766afcSPoul-Henning Kamp 	if (nread == 0)
205*4f766afcSPoul-Henning Kamp 		return (0);
206*4f766afcSPoul-Henning Kamp 
207*4f766afcSPoul-Henning Kamp 	if ((size_t)nread != sizeof lbuf)
208*4f766afcSPoul-Henning Kamp 		err(EX_DATAERR, "Corrupt tap-file, read hdr1=%zd", nread);
209*4f766afcSPoul-Henning Kamp 
210*4f766afcSPoul-Henning Kamp 	uint32_t u = le32dec(lbuf);
211*4f766afcSPoul-Henning Kamp 	if (u == 0 || (u >> 24) == 0xff)
212*4f766afcSPoul-Henning Kamp 		return(0);
213*4f766afcSPoul-Henning Kamp 
214*4f766afcSPoul-Henning Kamp 	if (u > len)
215*4f766afcSPoul-Henning Kamp 		err(17, "tapfile blocksize too big, 0x%08x", u);
216*4f766afcSPoul-Henning Kamp 
217*4f766afcSPoul-Henning Kamp 	size_t alen = (u + 1) & ~1;
218*4f766afcSPoul-Henning Kamp 	assert (alen <= len);
219*4f766afcSPoul-Henning Kamp 
220*4f766afcSPoul-Henning Kamp 	ssize_t retval = full_read(fd, dst, alen);
221*4f766afcSPoul-Henning Kamp 	if (retval < 0 || (size_t)retval != alen)
222*4f766afcSPoul-Henning Kamp 		err(EX_DATAERR, "Corrupt tap-file, read data=%zd", retval);
223*4f766afcSPoul-Henning Kamp 
224*4f766afcSPoul-Henning Kamp 	nread = full_read(fd, lbuf, sizeof lbuf);
225*4f766afcSPoul-Henning Kamp 	if ((size_t)nread != sizeof lbuf)
226*4f766afcSPoul-Henning Kamp 		err(EX_DATAERR, "Corrupt tap-file, read hdr2=%zd", nread);
227*4f766afcSPoul-Henning Kamp 
228*4f766afcSPoul-Henning Kamp 	uint32_t v = le32dec(lbuf);
229*4f766afcSPoul-Henning Kamp 	if (u == v)
230*4f766afcSPoul-Henning Kamp 		return (u);
231*4f766afcSPoul-Henning Kamp 	err(EX_DATAERR,
232*4f766afcSPoul-Henning Kamp 	    "Corrupt tap-file, headers differ (0x%08x != 0x%08x)", u, v);
233*4f766afcSPoul-Henning Kamp }
234*4f766afcSPoul-Henning Kamp 
235*4f766afcSPoul-Henning Kamp void
236*4f766afcSPoul-Henning Kamp tap_file::write_blk(const void *src, size_t len)
237*4f766afcSPoul-Henning Kamp {
238*4f766afcSPoul-Henning Kamp 	struct iovec iov[4];
239*4f766afcSPoul-Henning Kamp 	uint8_t zero = 0;
240*4f766afcSPoul-Henning Kamp 	int niov = 0;
241*4f766afcSPoul-Henning Kamp 	size_t expect = 0;
242*4f766afcSPoul-Henning Kamp 	char tbuf[4];
243*4f766afcSPoul-Henning Kamp 
244*4f766afcSPoul-Henning Kamp 	assert((len & ~0xffffffffULL) == 0);
245*4f766afcSPoul-Henning Kamp 	le32enc(tbuf, (uint32_t)len);
246*4f766afcSPoul-Henning Kamp 
247*4f766afcSPoul-Henning Kamp 	iov[niov].iov_base = tbuf;
248*4f766afcSPoul-Henning Kamp 	iov[niov].iov_len = sizeof tbuf;
249*4f766afcSPoul-Henning Kamp 	expect += iov[niov].iov_len;
250*4f766afcSPoul-Henning Kamp 	niov += 1;
251*4f766afcSPoul-Henning Kamp 
252*4f766afcSPoul-Henning Kamp 	iov[niov].iov_base = (void*)(uintptr_t)src;
253*4f766afcSPoul-Henning Kamp 	iov[niov].iov_len = len;
254*4f766afcSPoul-Henning Kamp 	expect += iov[niov].iov_len;
255*4f766afcSPoul-Henning Kamp 	niov += 1;
256*4f766afcSPoul-Henning Kamp 
257*4f766afcSPoul-Henning Kamp 	if (len & 1) {
258*4f766afcSPoul-Henning Kamp 		iov[niov].iov_base = &zero;
259*4f766afcSPoul-Henning Kamp 		iov[niov].iov_len = 1;
260*4f766afcSPoul-Henning Kamp 		expect += iov[niov].iov_len;
261*4f766afcSPoul-Henning Kamp 		niov += 1;
262*4f766afcSPoul-Henning Kamp 	}
263*4f766afcSPoul-Henning Kamp 
264*4f766afcSPoul-Henning Kamp 	iov[niov].iov_base = tbuf;
265*4f766afcSPoul-Henning Kamp 	iov[niov].iov_len = sizeof tbuf;
266*4f766afcSPoul-Henning Kamp 	expect += iov[niov].iov_len;
267*4f766afcSPoul-Henning Kamp 	niov += 1;
268*4f766afcSPoul-Henning Kamp 
269*4f766afcSPoul-Henning Kamp 	ssize_t nwrite = writev(fd, iov, niov);
270*4f766afcSPoul-Henning Kamp 	if (nwrite < 0 || (size_t)nwrite != expect)
271*4f766afcSPoul-Henning Kamp 		errx(17, "write error (%zd != %zd)", nwrite, expect);
272*4f766afcSPoul-Henning Kamp }
273*4f766afcSPoul-Henning Kamp 
274*4f766afcSPoul-Henning Kamp void
275*4f766afcSPoul-Henning Kamp tap_file::file_mark(void)
276*4f766afcSPoul-Henning Kamp {
277*4f766afcSPoul-Henning Kamp 	char tbuf[4];
278*4f766afcSPoul-Henning Kamp 	le32enc(tbuf, 0);
279*4f766afcSPoul-Henning Kamp 	ssize_t nwrite = write(fd, tbuf, sizeof tbuf);
280*4f766afcSPoul-Henning Kamp 	if ((size_t)nwrite != sizeof tbuf)
281*4f766afcSPoul-Henning Kamp 		errx(17, "write error (%zd != %zd)", nwrite, sizeof tbuf);
282*4f766afcSPoul-Henning Kamp }
283*4f766afcSPoul-Henning Kamp 
284*4f766afcSPoul-Henning Kamp void
285*4f766afcSPoul-Henning Kamp tap_file::rewind(void)
286*4f766afcSPoul-Henning Kamp {
287*4f766afcSPoul-Henning Kamp 	off_t where;
288*4f766afcSPoul-Henning Kamp 	if (direction == DST) {
289*4f766afcSPoul-Henning Kamp 		char tbuf[4];
290*4f766afcSPoul-Henning Kamp 		le32enc(tbuf, 0xffffffff);
291*4f766afcSPoul-Henning Kamp 		ssize_t nwrite = write(fd, tbuf, sizeof tbuf);
292*4f766afcSPoul-Henning Kamp 		if ((size_t)nwrite != sizeof tbuf)
293*4f766afcSPoul-Henning Kamp 			errx(17,
294*4f766afcSPoul-Henning Kamp 			    "write error (%zd != %zd)", nwrite, sizeof tbuf);
295*4f766afcSPoul-Henning Kamp 	}
296*4f766afcSPoul-Henning Kamp 	where = lseek(fd, 0L, SEEK_SET);
297*4f766afcSPoul-Henning Kamp 	if (where != 0 && errno == ESPIPE)
298*4f766afcSPoul-Henning Kamp 		err(EX_USAGE, "Cannot rewind sockets and pipes");
299*4f766afcSPoul-Henning Kamp 	if (where != 0)
300*4f766afcSPoul-Henning Kamp 		err(17, "lseek(0) failed");
301*4f766afcSPoul-Henning Kamp }
302*4f766afcSPoul-Henning Kamp 
303*4f766afcSPoul-Henning Kamp //---------------------------------------------------------------------
304*4f766afcSPoul-Henning Kamp 
305*4f766afcSPoul-Henning Kamp class file_set: public tape_dev {
306*4f766afcSPoul-Henning Kamp public:
307*4f766afcSPoul-Henning Kamp 	file_set(int file_handle, const char *spec, bool dst) :
308*4f766afcSPoul-Henning Kamp 		tape_dev(file_handle, spec, dst) {};
309*4f766afcSPoul-Henning Kamp 	ssize_t read_blk(void *dst, size_t len);
310*4f766afcSPoul-Henning Kamp 	ssize_t verify_blk(void *dst, size_t len, size_t expected);
311*4f766afcSPoul-Henning Kamp 	void write_blk(const void *src, size_t len);
312*4f766afcSPoul-Henning Kamp 	void file_mark(void);
313*4f766afcSPoul-Henning Kamp 	void rewind(void);
314*4f766afcSPoul-Henning Kamp 	void open_next(bool increment);
315*4f766afcSPoul-Henning Kamp };
316*4f766afcSPoul-Henning Kamp 
317*4f766afcSPoul-Henning Kamp void
318*4f766afcSPoul-Henning Kamp file_set::open_next(bool increment)
319*4f766afcSPoul-Henning Kamp {
320*4f766afcSPoul-Henning Kamp 	if (fd >= 0) {
321*4f766afcSPoul-Henning Kamp 		assert(close(fd) >= 0);
322*4f766afcSPoul-Henning Kamp 		fd = -1;
323*4f766afcSPoul-Henning Kamp 	}
324*4f766afcSPoul-Henning Kamp 	if (increment) {
325*4f766afcSPoul-Henning Kamp 		char *p = strchr(name, '\0') - 3;
326*4f766afcSPoul-Henning Kamp 		if (++p[2] == '9') {
327*4f766afcSPoul-Henning Kamp 			p[2] = '0';
328*4f766afcSPoul-Henning Kamp 			if (++p[1] == '9') {
329*4f766afcSPoul-Henning Kamp 				p[1] = '0';
330*4f766afcSPoul-Henning Kamp 				if (++p[0] == '9') {
331*4f766afcSPoul-Henning Kamp 					errx(EX_USAGE,
332*4f766afcSPoul-Henning Kamp 					    "file-set sequence overflow");
333*4f766afcSPoul-Henning Kamp 				}
334*4f766afcSPoul-Henning Kamp 			}
335*4f766afcSPoul-Henning Kamp 		}
336*4f766afcSPoul-Henning Kamp 	}
337*4f766afcSPoul-Henning Kamp 	if (direction == DST) {
338*4f766afcSPoul-Henning Kamp 		fd = open(name, O_RDWR|O_CREAT, DEFFILEMODE);
339*4f766afcSPoul-Henning Kamp 		if (fd < 0)
340*4f766afcSPoul-Henning Kamp 			err(1, "Could not open %s", name);
341*4f766afcSPoul-Henning Kamp 	} else {
342*4f766afcSPoul-Henning Kamp 		fd = open(name, O_RDONLY, 0);
343*4f766afcSPoul-Henning Kamp 	}
344*4f766afcSPoul-Henning Kamp }
345*4f766afcSPoul-Henning Kamp 
346*4f766afcSPoul-Henning Kamp ssize_t
347*4f766afcSPoul-Henning Kamp file_set::read_blk(void *dst, size_t len)
348*4f766afcSPoul-Henning Kamp {
349*4f766afcSPoul-Henning Kamp 	(void)dst;
350*4f766afcSPoul-Henning Kamp 	(void)len;
351*4f766afcSPoul-Henning Kamp 	errx(EX_SOFTWARE, "That was not supposed to happen");
352*4f766afcSPoul-Henning Kamp }
353*4f766afcSPoul-Henning Kamp 
354*4f766afcSPoul-Henning Kamp ssize_t
355*4f766afcSPoul-Henning Kamp file_set::verify_blk(void *dst, size_t len, size_t expected)
356*4f766afcSPoul-Henning Kamp {
357*4f766afcSPoul-Henning Kamp 	(void)len;
358*4f766afcSPoul-Henning Kamp 	if (fd < 0)
359*4f766afcSPoul-Henning Kamp 		open_next(true);
360*4f766afcSPoul-Henning Kamp 	if (fd < 0)
361*4f766afcSPoul-Henning Kamp 		return (0);
362*4f766afcSPoul-Henning Kamp 	ssize_t retval = read(fd, dst, expected);
363*4f766afcSPoul-Henning Kamp 	if (retval == 0) {
364*4f766afcSPoul-Henning Kamp 		assert(close(fd) >= 0);
365*4f766afcSPoul-Henning Kamp 		fd = -1;
366*4f766afcSPoul-Henning Kamp 	}
367*4f766afcSPoul-Henning Kamp 	return (retval);
368*4f766afcSPoul-Henning Kamp }
369*4f766afcSPoul-Henning Kamp 
370*4f766afcSPoul-Henning Kamp void
371*4f766afcSPoul-Henning Kamp file_set::write_blk(const void *src, size_t len)
372*4f766afcSPoul-Henning Kamp {
373*4f766afcSPoul-Henning Kamp 	if (fd < 0)
374*4f766afcSPoul-Henning Kamp 		open_next(true);
375*4f766afcSPoul-Henning Kamp 	ssize_t nwrite = write(fd, src, len);
376*4f766afcSPoul-Henning Kamp 	if (nwrite < 0 || (size_t)nwrite != len)
377*4f766afcSPoul-Henning Kamp 		errx(17, "write error (%zd != %zd)", nwrite, len);
378*4f766afcSPoul-Henning Kamp }
379*4f766afcSPoul-Henning Kamp 
380*4f766afcSPoul-Henning Kamp void
381*4f766afcSPoul-Henning Kamp file_set::file_mark(void)
382*4f766afcSPoul-Henning Kamp {
383*4f766afcSPoul-Henning Kamp 	if (fd < 0)
384*4f766afcSPoul-Henning Kamp 		return;
385*4f766afcSPoul-Henning Kamp 
386*4f766afcSPoul-Henning Kamp 	off_t where = lseek(fd, 0UL, SEEK_CUR);
387*4f766afcSPoul-Henning Kamp 
388*4f766afcSPoul-Henning Kamp 	int i = ftruncate(fd, where);
389*4f766afcSPoul-Henning Kamp 	if (i < 0)
390*4f766afcSPoul-Henning Kamp 		errx(17, "truncate error, %s to %zd", name, where);
391*4f766afcSPoul-Henning Kamp 	assert(close(fd) >= 0);
392*4f766afcSPoul-Henning Kamp 	fd = -1;
393*4f766afcSPoul-Henning Kamp }
394*4f766afcSPoul-Henning Kamp 
395*4f766afcSPoul-Henning Kamp void
396*4f766afcSPoul-Henning Kamp file_set::rewind(void)
397*4f766afcSPoul-Henning Kamp {
398*4f766afcSPoul-Henning Kamp 	char *p = strchr(name, '\0') - 3;
399*4f766afcSPoul-Henning Kamp 	p[0] = '0';
400*4f766afcSPoul-Henning Kamp 	p[1] = '0';
401*4f766afcSPoul-Henning Kamp 	p[2] = '0';
402*4f766afcSPoul-Henning Kamp 	open_next(false);
403*4f766afcSPoul-Henning Kamp }
404*4f766afcSPoul-Henning Kamp 
405*4f766afcSPoul-Henning Kamp //---------------------------------------------------------------------
406*4f766afcSPoul-Henning Kamp 
407*4f766afcSPoul-Henning Kamp class flat_file: public tape_dev {
408*4f766afcSPoul-Henning Kamp public:
409*4f766afcSPoul-Henning Kamp 	flat_file(int file_handle, const char *spec, bool dst) :
410*4f766afcSPoul-Henning Kamp 		tape_dev(file_handle, spec, dst) {};
411*4f766afcSPoul-Henning Kamp 	ssize_t read_blk(void *dst, size_t len);
412*4f766afcSPoul-Henning Kamp 	ssize_t verify_blk(void *dst, size_t len, size_t expected);
413*4f766afcSPoul-Henning Kamp 	void write_blk(const void *src, size_t len);
414*4f766afcSPoul-Henning Kamp 	void file_mark(void);
415*4f766afcSPoul-Henning Kamp 	virtual void rewind(void);
416*4f766afcSPoul-Henning Kamp };
417*4f766afcSPoul-Henning Kamp 
418*4f766afcSPoul-Henning Kamp ssize_t
419*4f766afcSPoul-Henning Kamp flat_file::read_blk(void *dst, size_t len)
420*4f766afcSPoul-Henning Kamp {
421*4f766afcSPoul-Henning Kamp 	(void)dst;
422*4f766afcSPoul-Henning Kamp 	(void)len;
423*4f766afcSPoul-Henning Kamp 	errx(EX_SOFTWARE, "That was not supposed to happen");
424*4f766afcSPoul-Henning Kamp }
425*4f766afcSPoul-Henning Kamp 
426*4f766afcSPoul-Henning Kamp ssize_t
427*4f766afcSPoul-Henning Kamp flat_file::verify_blk(void *dst, size_t len, size_t expected)
428*4f766afcSPoul-Henning Kamp {
429*4f766afcSPoul-Henning Kamp 	(void)len;
430*4f766afcSPoul-Henning Kamp 	return (read(fd, dst, expected));
431*4f766afcSPoul-Henning Kamp }
432*4f766afcSPoul-Henning Kamp 
433*4f766afcSPoul-Henning Kamp void
434*4f766afcSPoul-Henning Kamp flat_file::write_blk(const void *src, size_t len)
435*4f766afcSPoul-Henning Kamp {
436*4f766afcSPoul-Henning Kamp 	ssize_t nwrite = write(fd, src, len);
437*4f766afcSPoul-Henning Kamp 	if (nwrite < 0 || (size_t)nwrite != len)
438*4f766afcSPoul-Henning Kamp 		errx(17, "write error (%zd != %zd)", nwrite, len);
439*4f766afcSPoul-Henning Kamp }
440*4f766afcSPoul-Henning Kamp 
441*4f766afcSPoul-Henning Kamp void
442*4f766afcSPoul-Henning Kamp flat_file::file_mark(void)
443*4f766afcSPoul-Henning Kamp {
444*4f766afcSPoul-Henning Kamp 	return;
445*4f766afcSPoul-Henning Kamp }
446*4f766afcSPoul-Henning Kamp 
447*4f766afcSPoul-Henning Kamp void
448*4f766afcSPoul-Henning Kamp flat_file::rewind(void)
449*4f766afcSPoul-Henning Kamp {
450*4f766afcSPoul-Henning Kamp 	errx(EX_SOFTWARE, "That was not supposed to happen");
451*4f766afcSPoul-Henning Kamp }
452*4f766afcSPoul-Henning Kamp 
453*4f766afcSPoul-Henning Kamp //---------------------------------------------------------------------
454*4f766afcSPoul-Henning Kamp 
455*4f766afcSPoul-Henning Kamp enum e_how {H_INPUT, H_OUTPUT, H_VERIFY};
456*4f766afcSPoul-Henning Kamp 
457*4f766afcSPoul-Henning Kamp static tape_dev *
458*4f766afcSPoul-Henning Kamp open_arg(const char *arg, enum e_how how, int rawfile)
459*4f766afcSPoul-Henning Kamp {
460*4f766afcSPoul-Henning Kamp 	int fd;
461*4f766afcSPoul-Henning Kamp 
462*4f766afcSPoul-Henning Kamp 	if (!strcmp(arg, "-") && how == H_OUTPUT)
463*4f766afcSPoul-Henning Kamp 		fd = STDOUT_FILENO;
464*4f766afcSPoul-Henning Kamp 	else if (!strcmp(arg, "-"))
465*4f766afcSPoul-Henning Kamp 		fd = STDIN_FILENO;
466*4f766afcSPoul-Henning Kamp 	else if (how == H_OUTPUT)
467*4f766afcSPoul-Henning Kamp 		fd = open(arg, O_RDWR|O_CREAT, DEFFILEMODE);
468*4f766afcSPoul-Henning Kamp 	else
469*4f766afcSPoul-Henning Kamp 		fd = open(arg, O_RDONLY);
470*4f766afcSPoul-Henning Kamp 
471*4f766afcSPoul-Henning Kamp 	if (fd < 0)
472*4f766afcSPoul-Henning Kamp 		err(EX_NOINPUT, "Cannot open %s:", arg);
473*4f766afcSPoul-Henning Kamp 
474*4f766afcSPoul-Henning Kamp 	struct mtop mt;
475*4f766afcSPoul-Henning Kamp 	mt.mt_op = MTNOP;
476*4f766afcSPoul-Henning Kamp 	mt.mt_count = 1;
477*4f766afcSPoul-Henning Kamp 	int i = ioctl(fd, MTIOCTOP, &mt);
478*4f766afcSPoul-Henning Kamp 
479*4f766afcSPoul-Henning Kamp 	if (i >= 0)
480*4f766afcSPoul-Henning Kamp 		return (new tape_dev(fd, arg, how == H_OUTPUT));
481*4f766afcSPoul-Henning Kamp 
482*4f766afcSPoul-Henning Kamp 	size_t alen = strlen(arg);
483*4f766afcSPoul-Henning Kamp 	if (alen >= 5 && !strcmp(arg + (alen - 4), ".000")) {
484*4f766afcSPoul-Henning Kamp 		if (how == H_INPUT)
485*4f766afcSPoul-Henning Kamp 			errx(EX_USAGE,
486*4f766afcSPoul-Henning Kamp 			    "File-sets files cannot be used as source");
487*4f766afcSPoul-Henning Kamp 		return (new file_set(fd, arg, how == H_OUTPUT));
488*4f766afcSPoul-Henning Kamp 	}
489*4f766afcSPoul-Henning Kamp 
490*4f766afcSPoul-Henning Kamp 	if (how != H_INPUT && rawfile)
491*4f766afcSPoul-Henning Kamp 		return (new flat_file(fd, arg, how == H_OUTPUT));
492*4f766afcSPoul-Henning Kamp 
493*4f766afcSPoul-Henning Kamp 	return (new tap_file(fd, arg, how == H_OUTPUT));
494*4f766afcSPoul-Henning Kamp }
495*4f766afcSPoul-Henning Kamp 
496*4f766afcSPoul-Henning Kamp //---------------------------------------------------------------------
497*4f766afcSPoul-Henning Kamp 
498*4f766afcSPoul-Henning Kamp static tape_dev *input;
499*4f766afcSPoul-Henning Kamp static tape_dev *output;
500*4f766afcSPoul-Henning Kamp 
501*4f766afcSPoul-Henning Kamp static size_t maxblk = MAXREC;
502*4f766afcSPoul-Henning Kamp static uint64_t	lastrec, fsize, tsize;
503*4f766afcSPoul-Henning Kamp static FILE *msg;
504*4f766afcSPoul-Henning Kamp static ssize_t lastnread;
505*4f766afcSPoul-Henning Kamp static struct timespec t_start, t_end;
506*4f766afcSPoul-Henning Kamp 
507*4f766afcSPoul-Henning Kamp static void
508*4f766afcSPoul-Henning Kamp report_total(FILE *file)
509*4f766afcSPoul-Henning Kamp {
510*4f766afcSPoul-Henning Kamp 	double dur = (t_end.tv_nsec - t_start.tv_nsec) * 1e-9;
511*4f766afcSPoul-Henning Kamp 	dur += t_end.tv_sec - t_start.tv_sec;
512*4f766afcSPoul-Henning Kamp 	uintmax_t tot = tsize + fsize;
513*4f766afcSPoul-Henning Kamp 	fprintf(file, "total length: %ju bytes", tot);
514*4f766afcSPoul-Henning Kamp 	fprintf(file, " time: %.0f s", dur);
515*4f766afcSPoul-Henning Kamp 	tot /= 1024;
516*4f766afcSPoul-Henning Kamp 	fprintf(file, " rate: %.1f kB/s", (double)tot/dur);
517*4f766afcSPoul-Henning Kamp 	fprintf(file, "\n");
518*4f766afcSPoul-Henning Kamp }
519*4f766afcSPoul-Henning Kamp 
520*4f766afcSPoul-Henning Kamp static void
521*4f766afcSPoul-Henning Kamp sigintr(int signo __unused)
522*4f766afcSPoul-Henning Kamp {
523*4f766afcSPoul-Henning Kamp 	(void)signo;
524*4f766afcSPoul-Henning Kamp 	(void)clock_gettime(CLOCK_MONOTONIC, &t_end);
525*4f766afcSPoul-Henning Kamp 	if (record) {
526*4f766afcSPoul-Henning Kamp 		if (record - lastrec > 1)
527*4f766afcSPoul-Henning Kamp 			fprintf(msg, "records %ju to %ju\n",
528*4f766afcSPoul-Henning Kamp 			    (intmax_t)lastrec, (intmax_t)record);
529*4f766afcSPoul-Henning Kamp 		else
530*4f766afcSPoul-Henning Kamp 			fprintf(msg, "record %ju\n", (intmax_t)lastrec);
531*4f766afcSPoul-Henning Kamp 	}
532*4f766afcSPoul-Henning Kamp 	fprintf(msg, "interrupt at file %d: record %ju\n",
533*4f766afcSPoul-Henning Kamp 	    filen, (uintmax_t)record);
534*4f766afcSPoul-Henning Kamp 	report_total(msg);
535*4f766afcSPoul-Henning Kamp 	exit(1);
536*4f766afcSPoul-Henning Kamp }
537*4f766afcSPoul-Henning Kamp 
538*4f766afcSPoul-Henning Kamp #ifdef SIGINFO
539*4f766afcSPoul-Henning Kamp static volatile sig_atomic_t want_info;
540*4f766afcSPoul-Henning Kamp 
541*4f766afcSPoul-Henning Kamp static void
542*4f766afcSPoul-Henning Kamp siginfo(int signo)
543*4f766afcSPoul-Henning Kamp {
544*4f766afcSPoul-Henning Kamp 	(void)signo;
545*4f766afcSPoul-Henning Kamp 	want_info = 1;
546*4f766afcSPoul-Henning Kamp }
547*4f766afcSPoul-Henning Kamp 
548*4f766afcSPoul-Henning Kamp static void
549*4f766afcSPoul-Henning Kamp check_want_info(void)
550*4f766afcSPoul-Henning Kamp {
551*4f766afcSPoul-Henning Kamp 	if (want_info) {
552*4f766afcSPoul-Henning Kamp 		(void)clock_gettime(CLOCK_MONOTONIC, &t_end);
553*4f766afcSPoul-Henning Kamp 		fprintf(stderr, "tcopy: file %d record %ju ",
554*4f766afcSPoul-Henning Kamp 		    filen, (uintmax_t)record);
555*4f766afcSPoul-Henning Kamp 		report_total(stderr);
556*4f766afcSPoul-Henning Kamp 		want_info = 0;
557*4f766afcSPoul-Henning Kamp 	}
558*4f766afcSPoul-Henning Kamp }
559*4f766afcSPoul-Henning Kamp 
560*4f766afcSPoul-Henning Kamp #else /* !SIGINFO */
561*4f766afcSPoul-Henning Kamp 
562*4f766afcSPoul-Henning Kamp static void
563*4f766afcSPoul-Henning Kamp check_want_info(void)
564*4f766afcSPoul-Henning Kamp {
565*4f766afcSPoul-Henning Kamp }
566*4f766afcSPoul-Henning Kamp 
567*4f766afcSPoul-Henning Kamp #endif
568*4f766afcSPoul-Henning Kamp 
569*4f766afcSPoul-Henning Kamp static char *
570*4f766afcSPoul-Henning Kamp getspace(size_t blk)
571*4f766afcSPoul-Henning Kamp {
572*4f766afcSPoul-Henning Kamp 	void *bp;
573*4f766afcSPoul-Henning Kamp 
574*4f766afcSPoul-Henning Kamp 	assert(blk > 0);
575*4f766afcSPoul-Henning Kamp 	if ((bp = malloc(blk)) == NULL)
576*4f766afcSPoul-Henning Kamp 		errx(11, "no memory");
577*4f766afcSPoul-Henning Kamp 	return ((char *)bp);
578*4f766afcSPoul-Henning Kamp }
579*4f766afcSPoul-Henning Kamp 
580*4f766afcSPoul-Henning Kamp static void
581*4f766afcSPoul-Henning Kamp usage(void)
582*4f766afcSPoul-Henning Kamp {
583*4f766afcSPoul-Henning Kamp 	fprintf(stderr, "usage: tcopy [-cvx] [-s maxblk] [src [dest]]\n");
584*4f766afcSPoul-Henning Kamp 	exit(1);
585*4f766afcSPoul-Henning Kamp }
586*4f766afcSPoul-Henning Kamp 
587*4f766afcSPoul-Henning Kamp static void
588*4f766afcSPoul-Henning Kamp progress(ssize_t nread)
589*4f766afcSPoul-Henning Kamp {
590*4f766afcSPoul-Henning Kamp 	if (nread != lastnread) {
591*4f766afcSPoul-Henning Kamp 		if (lastnread != 0 && lastnread != NOCOUNT) {
592*4f766afcSPoul-Henning Kamp 			if (lastrec == 0 && nread == 0)
593*4f766afcSPoul-Henning Kamp 				fprintf(msg, "%ju records\n",
594*4f766afcSPoul-Henning Kamp 				    (uintmax_t)record);
595*4f766afcSPoul-Henning Kamp 			else if (record - lastrec > 1)
596*4f766afcSPoul-Henning Kamp 				fprintf(msg, "records %ju to %ju\n",
597*4f766afcSPoul-Henning Kamp 				    (uintmax_t)lastrec,
598*4f766afcSPoul-Henning Kamp 				    (uintmax_t)record);
599*4f766afcSPoul-Henning Kamp 			else
600*4f766afcSPoul-Henning Kamp 				fprintf(msg, "record %ju\n",
601*4f766afcSPoul-Henning Kamp 				    (uintmax_t)lastrec);
602*4f766afcSPoul-Henning Kamp 		}
603*4f766afcSPoul-Henning Kamp 		if (nread != 0)
604*4f766afcSPoul-Henning Kamp 			fprintf(msg,
605*4f766afcSPoul-Henning Kamp 			    "file %d: block size %zd: ", filen, nread);
606*4f766afcSPoul-Henning Kamp 		(void) fflush(msg);
607*4f766afcSPoul-Henning Kamp 		lastrec = record;
608*4f766afcSPoul-Henning Kamp 	}
609*4f766afcSPoul-Henning Kamp 	if (nread > 0) {
610*4f766afcSPoul-Henning Kamp 		fsize += (size_t)nread;
611*4f766afcSPoul-Henning Kamp 		record++;
612*4f766afcSPoul-Henning Kamp 	} else {
613*4f766afcSPoul-Henning Kamp 		if (lastnread <= 0 && lastnread != NOCOUNT) {
614*4f766afcSPoul-Henning Kamp 			fprintf(msg, "eot\n");
615*4f766afcSPoul-Henning Kamp 			return;
616*4f766afcSPoul-Henning Kamp 		}
617*4f766afcSPoul-Henning Kamp 		fprintf(msg,
618*4f766afcSPoul-Henning Kamp 		    "file %d: eof after %ju records: %ju bytes\n",
619*4f766afcSPoul-Henning Kamp 		    filen, (uintmax_t)record, (uintmax_t)fsize);
620*4f766afcSPoul-Henning Kamp 		filen++;
621*4f766afcSPoul-Henning Kamp 		tsize += fsize;
622*4f766afcSPoul-Henning Kamp 		fsize = record = lastrec = 0;
623*4f766afcSPoul-Henning Kamp 		lastnread = 0;
624*4f766afcSPoul-Henning Kamp 	}
625*4f766afcSPoul-Henning Kamp 	lastnread = nread;
626*4f766afcSPoul-Henning Kamp }
627*4f766afcSPoul-Henning Kamp 
628*4f766afcSPoul-Henning Kamp static void
629*4f766afcSPoul-Henning Kamp read_or_copy(void)
630*4f766afcSPoul-Henning Kamp {
631*4f766afcSPoul-Henning Kamp 	int needeof;
632*4f766afcSPoul-Henning Kamp 	ssize_t nread, prev_read;
633*4f766afcSPoul-Henning Kamp 	char *buff = getspace(maxblk);
634*4f766afcSPoul-Henning Kamp 
635*4f766afcSPoul-Henning Kamp 	(void)clock_gettime(CLOCK_MONOTONIC, &t_start);
636*4f766afcSPoul-Henning Kamp 	needeof = 0;
637*4f766afcSPoul-Henning Kamp 	for (prev_read = NOCOUNT;;) {
638*4f766afcSPoul-Henning Kamp 		check_want_info();
639*4f766afcSPoul-Henning Kamp 		nread = input->read_blk(buff, maxblk);
640*4f766afcSPoul-Henning Kamp 		progress(nread);
641*4f766afcSPoul-Henning Kamp 		if (nread > 0) {
642*4f766afcSPoul-Henning Kamp 			if (output != NULL) {
643*4f766afcSPoul-Henning Kamp 				if (needeof) {
644*4f766afcSPoul-Henning Kamp 					output->file_mark();
645*4f766afcSPoul-Henning Kamp 					needeof = 0;
646*4f766afcSPoul-Henning Kamp 				}
647*4f766afcSPoul-Henning Kamp 				output->write_blk(buff, (size_t)nread);
648*4f766afcSPoul-Henning Kamp 			}
649*4f766afcSPoul-Henning Kamp 		} else {
650*4f766afcSPoul-Henning Kamp 			if (prev_read <= 0 && prev_read != NOCOUNT) {
651*4f766afcSPoul-Henning Kamp 				break;
652*4f766afcSPoul-Henning Kamp 			}
653*4f766afcSPoul-Henning Kamp 			needeof = 1;
654*4f766afcSPoul-Henning Kamp 		}
655*4f766afcSPoul-Henning Kamp 		prev_read = nread;
656*4f766afcSPoul-Henning Kamp 	}
657*4f766afcSPoul-Henning Kamp 	(void)clock_gettime(CLOCK_MONOTONIC, &t_end);
658*4f766afcSPoul-Henning Kamp 	report_total(msg);
659*4f766afcSPoul-Henning Kamp 	free(buff);
660*4f766afcSPoul-Henning Kamp }
661*4f766afcSPoul-Henning Kamp 
662*4f766afcSPoul-Henning Kamp static void
663*4f766afcSPoul-Henning Kamp verify(void)
664*4f766afcSPoul-Henning Kamp {
665*4f766afcSPoul-Henning Kamp 	char *buf1 = getspace(maxblk);
666*4f766afcSPoul-Henning Kamp 	char *buf2 = getspace(maxblk);
667*4f766afcSPoul-Henning Kamp 	int eot = 0;
668*4f766afcSPoul-Henning Kamp 	ssize_t nread1, nread2;
669*4f766afcSPoul-Henning Kamp 	filen = 0;
670*4f766afcSPoul-Henning Kamp 	tsize = 0;
671*4f766afcSPoul-Henning Kamp 
672*4f766afcSPoul-Henning Kamp 	assert(output != NULL);
673*4f766afcSPoul-Henning Kamp 	(void)clock_gettime(CLOCK_MONOTONIC, &t_start);
674*4f766afcSPoul-Henning Kamp 
675*4f766afcSPoul-Henning Kamp 	while (1) {
676*4f766afcSPoul-Henning Kamp 		check_want_info();
677*4f766afcSPoul-Henning Kamp 		nread1 = input->read_blk(buf1, (size_t)maxblk);
678*4f766afcSPoul-Henning Kamp 		nread2 = output->verify_blk(buf2, maxblk, (size_t)nread1);
679*4f766afcSPoul-Henning Kamp 		progress(nread1);
680*4f766afcSPoul-Henning Kamp 		if (nread1 != nread2) {
681*4f766afcSPoul-Henning Kamp 			fprintf(msg,
682*4f766afcSPoul-Henning Kamp 			    "tcopy: tapes have different block sizes; "
683*4f766afcSPoul-Henning Kamp 			    "%zd != %zd.\n", nread1, nread2);
684*4f766afcSPoul-Henning Kamp 			exit(1);
685*4f766afcSPoul-Henning Kamp 		}
686*4f766afcSPoul-Henning Kamp 		if (nread1 > 0 && memcmp(buf1, buf2, (size_t)nread1)) {
687*4f766afcSPoul-Henning Kamp 			fprintf(msg, "tcopy: tapes have different data.\n");
688*4f766afcSPoul-Henning Kamp 			exit(1);
689*4f766afcSPoul-Henning Kamp 		} else if (nread1 > 0) {
690*4f766afcSPoul-Henning Kamp 			eot = 0;
691*4f766afcSPoul-Henning Kamp 		} else if (eot++) {
692*4f766afcSPoul-Henning Kamp 			break;
693*4f766afcSPoul-Henning Kamp 		}
694*4f766afcSPoul-Henning Kamp 	}
695*4f766afcSPoul-Henning Kamp 	(void)clock_gettime(CLOCK_MONOTONIC, &t_end);
696*4f766afcSPoul-Henning Kamp 	report_total(msg);
697*4f766afcSPoul-Henning Kamp 	fprintf(msg, "tcopy: tapes are identical.\n");
698*4f766afcSPoul-Henning Kamp 	fprintf(msg, "rewinding\n");
699*4f766afcSPoul-Henning Kamp 	input->rewind();
700*4f766afcSPoul-Henning Kamp 	output->rewind();
701*4f766afcSPoul-Henning Kamp 
702*4f766afcSPoul-Henning Kamp 	free(buf1);
703*4f766afcSPoul-Henning Kamp 	free(buf2);
704*4f766afcSPoul-Henning Kamp }
705*4f766afcSPoul-Henning Kamp 
706*4f766afcSPoul-Henning Kamp int
707*4f766afcSPoul-Henning Kamp main(int argc, char *argv[])
708*4f766afcSPoul-Henning Kamp {
709*4f766afcSPoul-Henning Kamp 	enum operation op = READ;
710*4f766afcSPoul-Henning Kamp 	int ch;
711*4f766afcSPoul-Henning Kamp 	unsigned long maxphys = 0;
712*4f766afcSPoul-Henning Kamp 	size_t l_maxphys = sizeof maxphys;
713*4f766afcSPoul-Henning Kamp 	uint64_t tmp;
714*4f766afcSPoul-Henning Kamp 	int rawfile = 0;
715*4f766afcSPoul-Henning Kamp 
716*4f766afcSPoul-Henning Kamp 	setbuf(stderr, NULL);
717*4f766afcSPoul-Henning Kamp 
718*4f766afcSPoul-Henning Kamp 	if (!sysctlbyname("kern.maxphys", &maxphys, &l_maxphys, NULL, 0UL))
719*4f766afcSPoul-Henning Kamp 		maxblk = maxphys;
720*4f766afcSPoul-Henning Kamp 
721*4f766afcSPoul-Henning Kamp 	msg = stdout;
722*4f766afcSPoul-Henning Kamp 	while ((ch = getopt(argc, argv, "cl:rs:vx")) != -1)
723*4f766afcSPoul-Henning Kamp 		switch((char)ch) {
724*4f766afcSPoul-Henning Kamp 		case 'c':
725*4f766afcSPoul-Henning Kamp 			op = COPYVERIFY;
726*4f766afcSPoul-Henning Kamp 			break;
727*4f766afcSPoul-Henning Kamp 		case 'l':
728*4f766afcSPoul-Henning Kamp 			msg = fopen(optarg, "w");
729*4f766afcSPoul-Henning Kamp 			if (msg == NULL)
730*4f766afcSPoul-Henning Kamp 				errx(EX_CANTCREAT, "Cannot open %s", optarg);
731*4f766afcSPoul-Henning Kamp 			setbuf(msg, NULL);
732*4f766afcSPoul-Henning Kamp 			break;
733*4f766afcSPoul-Henning Kamp 		case 'r':
734*4f766afcSPoul-Henning Kamp 			rawfile = 1;
735*4f766afcSPoul-Henning Kamp 			break;
736*4f766afcSPoul-Henning Kamp 		case 's':
737*4f766afcSPoul-Henning Kamp 			if (expand_number(optarg, &tmp)) {
738*4f766afcSPoul-Henning Kamp 				warnx("illegal block size");
739*4f766afcSPoul-Henning Kamp 				usage();
740*4f766afcSPoul-Henning Kamp 			}
741*4f766afcSPoul-Henning Kamp 			maxblk = tmp;
742*4f766afcSPoul-Henning Kamp 			if (maxblk == 0) {
743*4f766afcSPoul-Henning Kamp 				warnx("illegal block size");
744*4f766afcSPoul-Henning Kamp 				usage();
745*4f766afcSPoul-Henning Kamp 			}
746*4f766afcSPoul-Henning Kamp 			break;
747*4f766afcSPoul-Henning Kamp 		case 'v':
748*4f766afcSPoul-Henning Kamp 			op = VERIFY;
749*4f766afcSPoul-Henning Kamp 			break;
750*4f766afcSPoul-Henning Kamp 		case 'x':
751*4f766afcSPoul-Henning Kamp 			if (msg == stdout)
752*4f766afcSPoul-Henning Kamp 				msg = stderr;
753*4f766afcSPoul-Henning Kamp 			break;
754*4f766afcSPoul-Henning Kamp 		case '?':
755*4f766afcSPoul-Henning Kamp 		default:
756*4f766afcSPoul-Henning Kamp 			usage();
757*4f766afcSPoul-Henning Kamp 		}
758*4f766afcSPoul-Henning Kamp 	argc -= optind;
759*4f766afcSPoul-Henning Kamp 	argv += optind;
760*4f766afcSPoul-Henning Kamp 
761*4f766afcSPoul-Henning Kamp 	switch(argc) {
762*4f766afcSPoul-Henning Kamp 	case 0:
763*4f766afcSPoul-Henning Kamp 		if (op != READ)
764*4f766afcSPoul-Henning Kamp 			usage();
765*4f766afcSPoul-Henning Kamp 		break;
766*4f766afcSPoul-Henning Kamp 	case 1:
767*4f766afcSPoul-Henning Kamp 		if (op != READ)
768*4f766afcSPoul-Henning Kamp 			usage();
769*4f766afcSPoul-Henning Kamp 		break;
770*4f766afcSPoul-Henning Kamp 	case 2:
771*4f766afcSPoul-Henning Kamp 		if (op == READ)
772*4f766afcSPoul-Henning Kamp 			op = COPY;
773*4f766afcSPoul-Henning Kamp 		if (!strcmp(argv[1], "-")) {
774*4f766afcSPoul-Henning Kamp 			if (op == COPYVERIFY)
775*4f766afcSPoul-Henning Kamp 				errx(EX_USAGE,
776*4f766afcSPoul-Henning Kamp 				    "Cannot copy+verify with '-' destination");
777*4f766afcSPoul-Henning Kamp 			if (msg == stdout)
778*4f766afcSPoul-Henning Kamp 				msg = stderr;
779*4f766afcSPoul-Henning Kamp 		}
780*4f766afcSPoul-Henning Kamp 		if (op == VERIFY)
781*4f766afcSPoul-Henning Kamp 			output = open_arg(argv[1], H_VERIFY, 0);
782*4f766afcSPoul-Henning Kamp 		else
783*4f766afcSPoul-Henning Kamp 			output = open_arg(argv[1], H_OUTPUT, rawfile);
784*4f766afcSPoul-Henning Kamp 		break;
785*4f766afcSPoul-Henning Kamp 	default:
786*4f766afcSPoul-Henning Kamp 		usage();
787*4f766afcSPoul-Henning Kamp 	}
788*4f766afcSPoul-Henning Kamp 
789*4f766afcSPoul-Henning Kamp 	if (argc == 0) {
790*4f766afcSPoul-Henning Kamp 		input = open_arg(_PATH_DEFTAPE, H_INPUT, 0);
791*4f766afcSPoul-Henning Kamp 	} else {
792*4f766afcSPoul-Henning Kamp 		input = open_arg(argv[0], H_INPUT, 0);
793*4f766afcSPoul-Henning Kamp 	}
794*4f766afcSPoul-Henning Kamp 
795*4f766afcSPoul-Henning Kamp 	if ((signal(SIGINT, SIG_IGN)) != SIG_IGN)
796*4f766afcSPoul-Henning Kamp 		(void) signal(SIGINT, sigintr);
797*4f766afcSPoul-Henning Kamp 
798*4f766afcSPoul-Henning Kamp #ifdef SIGINFO
799*4f766afcSPoul-Henning Kamp 	(void)signal(SIGINFO, siginfo);
800*4f766afcSPoul-Henning Kamp #endif
801*4f766afcSPoul-Henning Kamp 
802*4f766afcSPoul-Henning Kamp 	if (op != VERIFY) {
803*4f766afcSPoul-Henning Kamp 		if (op == COPYVERIFY) {
804*4f766afcSPoul-Henning Kamp 			assert(output != NULL);
805*4f766afcSPoul-Henning Kamp 			fprintf(msg, "rewinding\n");
806*4f766afcSPoul-Henning Kamp 			input->rewind();
807*4f766afcSPoul-Henning Kamp 			output->rewind();
808*4f766afcSPoul-Henning Kamp 		}
809*4f766afcSPoul-Henning Kamp 
810*4f766afcSPoul-Henning Kamp 		read_or_copy();
811*4f766afcSPoul-Henning Kamp 
812*4f766afcSPoul-Henning Kamp 		if (op == COPY || op == COPYVERIFY) {
813*4f766afcSPoul-Henning Kamp 			assert(output != NULL);
814*4f766afcSPoul-Henning Kamp 			output->file_mark();
815*4f766afcSPoul-Henning Kamp 			output->file_mark();
816*4f766afcSPoul-Henning Kamp 		}
817*4f766afcSPoul-Henning Kamp 	}
818*4f766afcSPoul-Henning Kamp 
819*4f766afcSPoul-Henning Kamp 	if (op == VERIFY || op == COPYVERIFY) {
820*4f766afcSPoul-Henning Kamp 
821*4f766afcSPoul-Henning Kamp 		if (op == COPYVERIFY) {
822*4f766afcSPoul-Henning Kamp 			assert(output != NULL);
823*4f766afcSPoul-Henning Kamp 			fprintf(msg, "rewinding\n");
824*4f766afcSPoul-Henning Kamp 			input->rewind();
825*4f766afcSPoul-Henning Kamp 			output->rewind();
826*4f766afcSPoul-Henning Kamp 			input->direction = tape_dev::SRC;
827*4f766afcSPoul-Henning Kamp 			output->direction = tape_dev::SRC;
828*4f766afcSPoul-Henning Kamp 		}
829*4f766afcSPoul-Henning Kamp 
830*4f766afcSPoul-Henning Kamp 		verify();
831*4f766afcSPoul-Henning Kamp 	}
832*4f766afcSPoul-Henning Kamp 
833*4f766afcSPoul-Henning Kamp 	if (msg != stderr && msg != stdout)
834*4f766afcSPoul-Henning Kamp 		report_total(stderr);
835*4f766afcSPoul-Henning Kamp 
836*4f766afcSPoul-Henning Kamp 	return(0);
837*4f766afcSPoul-Henning Kamp }
838