xref: /freebsd/usr.bin/tcopy/tcopy.cc (revision 6582915c59e8a974d65fafc25c39ec2713a02d28)
14f766afcSPoul-Henning Kamp /*-
24f766afcSPoul-Henning Kamp  * SPDX-License-Identifier: BSD-2-Clause
34f766afcSPoul-Henning Kamp  *
44f766afcSPoul-Henning Kamp  * Copyright (c) 2025 Poul-Henning Kamp, <phk@FreeBSD.org>
54f766afcSPoul-Henning Kamp  * Copyright (c) 1985, 1987, 1993
64f766afcSPoul-Henning Kamp  *	The Regents of the University of California.  All rights reserved.
74f766afcSPoul-Henning Kamp  *
84f766afcSPoul-Henning Kamp  * Redistribution and use in source and binary forms, with or without
94f766afcSPoul-Henning Kamp  * modification, are permitted provided that the following conditions
104f766afcSPoul-Henning Kamp  * are met:
114f766afcSPoul-Henning Kamp  * 1. Redistributions of source code must retain the above copyright
124f766afcSPoul-Henning Kamp  *    notice, this list of conditions and the following disclaimer.
134f766afcSPoul-Henning Kamp  * 2. Redistributions in binary form must reproduce the above copyright
144f766afcSPoul-Henning Kamp  *    notice, this list of conditions and the following disclaimer in the
154f766afcSPoul-Henning Kamp  *    documentation and/or other materials provided with the distribution.
164f766afcSPoul-Henning Kamp  *
174f766afcSPoul-Henning Kamp  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
184f766afcSPoul-Henning Kamp  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
194f766afcSPoul-Henning Kamp  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
204f766afcSPoul-Henning Kamp  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
214f766afcSPoul-Henning Kamp  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
224f766afcSPoul-Henning Kamp  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
234f766afcSPoul-Henning Kamp  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
244f766afcSPoul-Henning Kamp  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
254f766afcSPoul-Henning Kamp  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
264f766afcSPoul-Henning Kamp  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
274f766afcSPoul-Henning Kamp  * SUCH DAMAGE.
284f766afcSPoul-Henning Kamp  */
294f766afcSPoul-Henning Kamp 
304f766afcSPoul-Henning Kamp #include <assert.h>
314f766afcSPoul-Henning Kamp #include <err.h>
324f766afcSPoul-Henning Kamp #include <errno.h>
334f766afcSPoul-Henning Kamp #include <fcntl.h>
344f766afcSPoul-Henning Kamp #include <paths.h>
354f766afcSPoul-Henning Kamp #include <signal.h>
364f766afcSPoul-Henning Kamp #include <stdint.h>
374f766afcSPoul-Henning Kamp #include <stdio.h>
384f766afcSPoul-Henning Kamp #include <stdlib.h>
394f766afcSPoul-Henning Kamp #include <string.h>
404f766afcSPoul-Henning Kamp #include <time.h>
414f766afcSPoul-Henning Kamp #include <sysexits.h>
424f766afcSPoul-Henning Kamp #include <unistd.h>
434f766afcSPoul-Henning Kamp 
444f766afcSPoul-Henning Kamp #include <sys/endian.h>
454f766afcSPoul-Henning Kamp #include <sys/mtio.h>
464f766afcSPoul-Henning Kamp #include <sys/stat.h>
474f766afcSPoul-Henning Kamp #include <sys/sysctl.h>
484f766afcSPoul-Henning Kamp #include <sys/uio.h>
494f766afcSPoul-Henning Kamp 
504f766afcSPoul-Henning Kamp #include <libutil.h>
514f766afcSPoul-Henning Kamp 
524f766afcSPoul-Henning Kamp #define	MAXREC	(1024 * 1024)
534f766afcSPoul-Henning Kamp #define	NOCOUNT	(-2)
544f766afcSPoul-Henning Kamp 
554f766afcSPoul-Henning Kamp enum operation {READ, VERIFY, COPY, COPYVERIFY};
564f766afcSPoul-Henning Kamp 
574f766afcSPoul-Henning Kamp // Stuff the tape_devs need to know about
584f766afcSPoul-Henning Kamp static int filen;
594f766afcSPoul-Henning Kamp static uint64_t	record;
604f766afcSPoul-Henning Kamp 
614f766afcSPoul-Henning Kamp //---------------------------------------------------------------------
624f766afcSPoul-Henning Kamp 
634f766afcSPoul-Henning Kamp class tape_dev {
644f766afcSPoul-Henning Kamp 	size_t	max_read_size;
654f766afcSPoul-Henning Kamp public:
664f766afcSPoul-Henning Kamp 	int fd;
674f766afcSPoul-Henning Kamp 	char *name;
684f766afcSPoul-Henning Kamp 	enum direction {SRC, DST} direction;
694f766afcSPoul-Henning Kamp 
704f766afcSPoul-Henning Kamp 	tape_dev(int file_handle, const char *spec, bool destination);
714f766afcSPoul-Henning Kamp 
724f766afcSPoul-Henning Kamp 	virtual ssize_t read_blk(void *dst, size_t len);
734f766afcSPoul-Henning Kamp 	virtual ssize_t verify_blk(void *dst, size_t len, size_t expected);
744f766afcSPoul-Henning Kamp 	virtual void write_blk(const void *src, size_t len);
754f766afcSPoul-Henning Kamp 	virtual void file_mark(void);
764f766afcSPoul-Henning Kamp 	virtual void rewind(void);
774f766afcSPoul-Henning Kamp };
784f766afcSPoul-Henning Kamp 
tape_dev(int file_handle,const char * spec,bool destination)794f766afcSPoul-Henning Kamp tape_dev::tape_dev(int file_handle, const char *spec, bool destination)
804f766afcSPoul-Henning Kamp {
814f766afcSPoul-Henning Kamp 	assert(file_handle >= 0);
824f766afcSPoul-Henning Kamp 	fd = file_handle;
834f766afcSPoul-Henning Kamp 	name = strdup(spec);
844f766afcSPoul-Henning Kamp 	assert(name != NULL);
854f766afcSPoul-Henning Kamp 	direction = destination ? DST : SRC;
864f766afcSPoul-Henning Kamp 	max_read_size = 0;
874f766afcSPoul-Henning Kamp }
884f766afcSPoul-Henning Kamp 
894f766afcSPoul-Henning Kamp ssize_t
read_blk(void * dst,size_t len)904f766afcSPoul-Henning Kamp tape_dev::read_blk(void *dst, size_t len)
914f766afcSPoul-Henning Kamp {
924f766afcSPoul-Henning Kamp 	ssize_t retval = -1;
934f766afcSPoul-Henning Kamp 
944f766afcSPoul-Henning Kamp 	if (max_read_size == 0) {
954f766afcSPoul-Henning Kamp 		max_read_size = len;
964f766afcSPoul-Henning Kamp 		while (max_read_size > 0) {
974f766afcSPoul-Henning Kamp 			retval = read(fd, dst, max_read_size);
984f766afcSPoul-Henning Kamp 			if (retval >= 0 || (errno != EINVAL && errno != EFBIG))
994f766afcSPoul-Henning Kamp 				break;
1004f766afcSPoul-Henning Kamp 			if (max_read_size < 512)
1014f766afcSPoul-Henning Kamp 				errx(1, "Cannot find a sane max blocksize");
1024f766afcSPoul-Henning Kamp 
1034f766afcSPoul-Henning Kamp 			// Reduce to next lower power of two
1044f766afcSPoul-Henning Kamp 			int i = flsl((long)max_read_size - 1L);
1054f766afcSPoul-Henning Kamp 			max_read_size = 1UL << (i - 1);
1064f766afcSPoul-Henning Kamp 		}
1074f766afcSPoul-Henning Kamp 	} else {
1084f766afcSPoul-Henning Kamp 		retval = read(fd, dst, (size_t)max_read_size);
1094f766afcSPoul-Henning Kamp 	}
1104f766afcSPoul-Henning Kamp 	if (retval < 0) {
1114f766afcSPoul-Henning Kamp 		err(1, "read error, %s, file %d, record %ju",
1124f766afcSPoul-Henning Kamp 		    name, filen, (uintmax_t)record);
1134f766afcSPoul-Henning Kamp 	}
1144f766afcSPoul-Henning Kamp 	return (retval);
1154f766afcSPoul-Henning Kamp }
1164f766afcSPoul-Henning Kamp 
1174f766afcSPoul-Henning Kamp ssize_t
verify_blk(void * dst,size_t len,size_t expected)1184f766afcSPoul-Henning Kamp tape_dev::verify_blk(void *dst, size_t len, size_t expected)
1194f766afcSPoul-Henning Kamp {
1204f766afcSPoul-Henning Kamp 	(void)expected;
1214f766afcSPoul-Henning Kamp 	return read_blk(dst, len);
1224f766afcSPoul-Henning Kamp }
1234f766afcSPoul-Henning Kamp 
1244f766afcSPoul-Henning Kamp void
write_blk(const void * src,size_t len)1254f766afcSPoul-Henning Kamp tape_dev::write_blk(const void *src, size_t len)
1264f766afcSPoul-Henning Kamp {
1274f766afcSPoul-Henning Kamp 	assert(len > 0);
1284f766afcSPoul-Henning Kamp 	ssize_t nwrite = write(fd, src, len);
1294f766afcSPoul-Henning Kamp 	if (nwrite < 0 || (size_t) nwrite != len) {
1304f766afcSPoul-Henning Kamp 		if (nwrite == -1) {
1314f766afcSPoul-Henning Kamp 			warn("write error, file %d, record %ju",
1324f766afcSPoul-Henning Kamp 			    filen, (intmax_t)record);
1334f766afcSPoul-Henning Kamp 		} else {
1344f766afcSPoul-Henning Kamp 			warnx("write error, file %d, record %ju",
1354f766afcSPoul-Henning Kamp 			    filen, (intmax_t)record);
1364f766afcSPoul-Henning Kamp 			warnx("write (%zd) != read (%zd)", nwrite, len);
1374f766afcSPoul-Henning Kamp 		}
1384f766afcSPoul-Henning Kamp 		errx(5, "copy aborted");
1394f766afcSPoul-Henning Kamp 	}
1404f766afcSPoul-Henning Kamp 	return;
1414f766afcSPoul-Henning Kamp }
1424f766afcSPoul-Henning Kamp 
1434f766afcSPoul-Henning Kamp void
file_mark(void)1444f766afcSPoul-Henning Kamp tape_dev::file_mark(void)
1454f766afcSPoul-Henning Kamp {
1464f766afcSPoul-Henning Kamp 	struct mtop op;
1474f766afcSPoul-Henning Kamp 
1484f766afcSPoul-Henning Kamp 	op.mt_op = MTWEOF;
1494f766afcSPoul-Henning Kamp 	op.mt_count = (daddr_t)1;
1504f766afcSPoul-Henning Kamp 	if (ioctl(fd, MTIOCTOP, (char *)&op) < 0)
1514f766afcSPoul-Henning Kamp 		err(6, "tape op (write file mark)");
1524f766afcSPoul-Henning Kamp }
1534f766afcSPoul-Henning Kamp 
1544f766afcSPoul-Henning Kamp void
rewind(void)1554f766afcSPoul-Henning Kamp tape_dev::rewind(void)
1564f766afcSPoul-Henning Kamp {
1574f766afcSPoul-Henning Kamp 	struct mtop op;
1584f766afcSPoul-Henning Kamp 
1594f766afcSPoul-Henning Kamp 	op.mt_op = MTREW;
1604f766afcSPoul-Henning Kamp 	op.mt_count = (daddr_t)1;
1614f766afcSPoul-Henning Kamp 	if (ioctl(fd, MTIOCTOP, (char *)&op) < 0)
1624f766afcSPoul-Henning Kamp 		err(6, "tape op (rewind)");
1634f766afcSPoul-Henning Kamp }
1644f766afcSPoul-Henning Kamp 
1654f766afcSPoul-Henning Kamp //---------------------------------------------------------------------
1664f766afcSPoul-Henning Kamp 
1674f766afcSPoul-Henning Kamp class tap_file: public tape_dev {
1684f766afcSPoul-Henning Kamp public:
tap_file(int file_handle,const char * spec,bool dst)1694f766afcSPoul-Henning Kamp 	tap_file(int file_handle, const char *spec, bool dst) :
1704f766afcSPoul-Henning Kamp 		tape_dev(file_handle, spec, dst) {};
1714f766afcSPoul-Henning Kamp 	ssize_t read_blk(void *dst, size_t len);
1724f766afcSPoul-Henning Kamp 	void write_blk(const void *src, size_t len);
1734f766afcSPoul-Henning Kamp 	void file_mark(void);
1744f766afcSPoul-Henning Kamp 	virtual void rewind(void);
1754f766afcSPoul-Henning Kamp };
1764f766afcSPoul-Henning Kamp 
1774f766afcSPoul-Henning Kamp static
full_read(int fd,void * dst,size_t len)1784f766afcSPoul-Henning Kamp ssize_t full_read(int fd, void *dst, size_t len)
1794f766afcSPoul-Henning Kamp {
1804f766afcSPoul-Henning Kamp 	// Input may be a socket which returns partial reads
1814f766afcSPoul-Henning Kamp 
1824f766afcSPoul-Henning Kamp 	ssize_t retval = read(fd, dst, len);
1834f766afcSPoul-Henning Kamp 	if (retval <= 0 || (size_t)retval == len)
1844f766afcSPoul-Henning Kamp 		return (retval);
1854f766afcSPoul-Henning Kamp 
1864f766afcSPoul-Henning Kamp 	char *ptr = (char *)dst + retval;
1874f766afcSPoul-Henning Kamp 	size_t left = len - (size_t)retval;
1884f766afcSPoul-Henning Kamp 	while (left > 0) {
1894f766afcSPoul-Henning Kamp 		retval = read(fd, ptr, left);
1904f766afcSPoul-Henning Kamp 		if (retval <= 0)
1914f766afcSPoul-Henning Kamp 			return (retval);
1924f766afcSPoul-Henning Kamp 		left -= (size_t)retval;
1934f766afcSPoul-Henning Kamp 		ptr += retval;
1944f766afcSPoul-Henning Kamp 	}
1954f766afcSPoul-Henning Kamp 	return ((ssize_t)len);
1964f766afcSPoul-Henning Kamp }
1974f766afcSPoul-Henning Kamp 
1984f766afcSPoul-Henning Kamp ssize_t
read_blk(void * dst,size_t len)1994f766afcSPoul-Henning Kamp tap_file::read_blk(void *dst, size_t len)
2004f766afcSPoul-Henning Kamp {
2014f766afcSPoul-Henning Kamp 	char lbuf[4];
2024f766afcSPoul-Henning Kamp 
2034f766afcSPoul-Henning Kamp 	ssize_t nread = full_read(fd, lbuf, sizeof lbuf);
2044f766afcSPoul-Henning Kamp 	if (nread == 0)
2054f766afcSPoul-Henning Kamp 		return (0);
2064f766afcSPoul-Henning Kamp 
2074f766afcSPoul-Henning Kamp 	if ((size_t)nread != sizeof lbuf)
2084f766afcSPoul-Henning Kamp 		err(EX_DATAERR, "Corrupt tap-file, read hdr1=%zd", nread);
2094f766afcSPoul-Henning Kamp 
2104f766afcSPoul-Henning Kamp 	uint32_t u = le32dec(lbuf);
2114f766afcSPoul-Henning Kamp 	if (u == 0 || (u >> 24) == 0xff)
2124f766afcSPoul-Henning Kamp 		return(0);
2134f766afcSPoul-Henning Kamp 
2144f766afcSPoul-Henning Kamp 	if (u > len)
2154f766afcSPoul-Henning Kamp 		err(17, "tapfile blocksize too big, 0x%08x", u);
2164f766afcSPoul-Henning Kamp 
2174f766afcSPoul-Henning Kamp 	size_t alen = (u + 1) & ~1;
2184f766afcSPoul-Henning Kamp 	assert (alen <= len);
2194f766afcSPoul-Henning Kamp 
2204f766afcSPoul-Henning Kamp 	ssize_t retval = full_read(fd, dst, alen);
2214f766afcSPoul-Henning Kamp 	if (retval < 0 || (size_t)retval != alen)
2224f766afcSPoul-Henning Kamp 		err(EX_DATAERR, "Corrupt tap-file, read data=%zd", retval);
2234f766afcSPoul-Henning Kamp 
2244f766afcSPoul-Henning Kamp 	nread = full_read(fd, lbuf, sizeof lbuf);
2254f766afcSPoul-Henning Kamp 	if ((size_t)nread != sizeof lbuf)
2264f766afcSPoul-Henning Kamp 		err(EX_DATAERR, "Corrupt tap-file, read hdr2=%zd", nread);
2274f766afcSPoul-Henning Kamp 
2284f766afcSPoul-Henning Kamp 	uint32_t v = le32dec(lbuf);
2294f766afcSPoul-Henning Kamp 	if (u == v)
2304f766afcSPoul-Henning Kamp 		return (u);
2314f766afcSPoul-Henning Kamp 	err(EX_DATAERR,
2324f766afcSPoul-Henning Kamp 	    "Corrupt tap-file, headers differ (0x%08x != 0x%08x)", u, v);
2334f766afcSPoul-Henning Kamp }
2344f766afcSPoul-Henning Kamp 
2354f766afcSPoul-Henning Kamp void
write_blk(const void * src,size_t len)2364f766afcSPoul-Henning Kamp tap_file::write_blk(const void *src, size_t len)
2374f766afcSPoul-Henning Kamp {
2384f766afcSPoul-Henning Kamp 	struct iovec iov[4];
2394f766afcSPoul-Henning Kamp 	uint8_t zero = 0;
2404f766afcSPoul-Henning Kamp 	int niov = 0;
2414f766afcSPoul-Henning Kamp 	size_t expect = 0;
2424f766afcSPoul-Henning Kamp 	char tbuf[4];
2434f766afcSPoul-Henning Kamp 
2444f766afcSPoul-Henning Kamp 	assert((len & ~0xffffffffULL) == 0);
2454f766afcSPoul-Henning Kamp 	le32enc(tbuf, (uint32_t)len);
2464f766afcSPoul-Henning Kamp 
2474f766afcSPoul-Henning Kamp 	iov[niov].iov_base = tbuf;
2484f766afcSPoul-Henning Kamp 	iov[niov].iov_len = sizeof tbuf;
2494f766afcSPoul-Henning Kamp 	expect += iov[niov].iov_len;
2504f766afcSPoul-Henning Kamp 	niov += 1;
2514f766afcSPoul-Henning Kamp 
2524f766afcSPoul-Henning Kamp 	iov[niov].iov_base = (void*)(uintptr_t)src;
2534f766afcSPoul-Henning Kamp 	iov[niov].iov_len = len;
2544f766afcSPoul-Henning Kamp 	expect += iov[niov].iov_len;
2554f766afcSPoul-Henning Kamp 	niov += 1;
2564f766afcSPoul-Henning Kamp 
2574f766afcSPoul-Henning Kamp 	if (len & 1) {
2584f766afcSPoul-Henning Kamp 		iov[niov].iov_base = &zero;
2594f766afcSPoul-Henning Kamp 		iov[niov].iov_len = 1;
2604f766afcSPoul-Henning Kamp 		expect += iov[niov].iov_len;
2614f766afcSPoul-Henning Kamp 		niov += 1;
2624f766afcSPoul-Henning Kamp 	}
2634f766afcSPoul-Henning Kamp 
2644f766afcSPoul-Henning Kamp 	iov[niov].iov_base = tbuf;
2654f766afcSPoul-Henning Kamp 	iov[niov].iov_len = sizeof tbuf;
2664f766afcSPoul-Henning Kamp 	expect += iov[niov].iov_len;
2674f766afcSPoul-Henning Kamp 	niov += 1;
2684f766afcSPoul-Henning Kamp 
2694f766afcSPoul-Henning Kamp 	ssize_t nwrite = writev(fd, iov, niov);
2704f766afcSPoul-Henning Kamp 	if (nwrite < 0 || (size_t)nwrite != expect)
2714f766afcSPoul-Henning Kamp 		errx(17, "write error (%zd != %zd)", nwrite, expect);
2724f766afcSPoul-Henning Kamp }
2734f766afcSPoul-Henning Kamp 
2744f766afcSPoul-Henning Kamp void
file_mark(void)2754f766afcSPoul-Henning Kamp tap_file::file_mark(void)
2764f766afcSPoul-Henning Kamp {
2774f766afcSPoul-Henning Kamp 	char tbuf[4];
2784f766afcSPoul-Henning Kamp 	le32enc(tbuf, 0);
2794f766afcSPoul-Henning Kamp 	ssize_t nwrite = write(fd, tbuf, sizeof tbuf);
2804f766afcSPoul-Henning Kamp 	if ((size_t)nwrite != sizeof tbuf)
2814f766afcSPoul-Henning Kamp 		errx(17, "write error (%zd != %zd)", nwrite, sizeof tbuf);
2824f766afcSPoul-Henning Kamp }
2834f766afcSPoul-Henning Kamp 
2844f766afcSPoul-Henning Kamp void
rewind(void)2854f766afcSPoul-Henning Kamp tap_file::rewind(void)
2864f766afcSPoul-Henning Kamp {
2874f766afcSPoul-Henning Kamp 	off_t where;
2884f766afcSPoul-Henning Kamp 	if (direction == DST) {
2894f766afcSPoul-Henning Kamp 		char tbuf[4];
2904f766afcSPoul-Henning Kamp 		le32enc(tbuf, 0xffffffff);
2914f766afcSPoul-Henning Kamp 		ssize_t nwrite = write(fd, tbuf, sizeof tbuf);
2924f766afcSPoul-Henning Kamp 		if ((size_t)nwrite != sizeof tbuf)
2934f766afcSPoul-Henning Kamp 			errx(17,
2944f766afcSPoul-Henning Kamp 			    "write error (%zd != %zd)", nwrite, sizeof tbuf);
2954f766afcSPoul-Henning Kamp 	}
2964f766afcSPoul-Henning Kamp 	where = lseek(fd, 0L, SEEK_SET);
2974f766afcSPoul-Henning Kamp 	if (where != 0 && errno == ESPIPE)
2984f766afcSPoul-Henning Kamp 		err(EX_USAGE, "Cannot rewind sockets and pipes");
2994f766afcSPoul-Henning Kamp 	if (where != 0)
3004f766afcSPoul-Henning Kamp 		err(17, "lseek(0) failed");
3014f766afcSPoul-Henning Kamp }
3024f766afcSPoul-Henning Kamp 
3034f766afcSPoul-Henning Kamp //---------------------------------------------------------------------
3044f766afcSPoul-Henning Kamp 
3054f766afcSPoul-Henning Kamp class file_set: public tape_dev {
3064f766afcSPoul-Henning Kamp public:
file_set(int file_handle,const char * spec,bool dst)3074f766afcSPoul-Henning Kamp 	file_set(int file_handle, const char *spec, bool dst) :
3084f766afcSPoul-Henning Kamp 		tape_dev(file_handle, spec, dst) {};
3094f766afcSPoul-Henning Kamp 	ssize_t read_blk(void *dst, size_t len);
3104f766afcSPoul-Henning Kamp 	ssize_t verify_blk(void *dst, size_t len, size_t expected);
3114f766afcSPoul-Henning Kamp 	void write_blk(const void *src, size_t len);
3124f766afcSPoul-Henning Kamp 	void file_mark(void);
3134f766afcSPoul-Henning Kamp 	void rewind(void);
3144f766afcSPoul-Henning Kamp 	void open_next(bool increment);
3154f766afcSPoul-Henning Kamp };
3164f766afcSPoul-Henning Kamp 
3174f766afcSPoul-Henning Kamp void
open_next(bool increment)3184f766afcSPoul-Henning Kamp file_set::open_next(bool increment)
3194f766afcSPoul-Henning Kamp {
3204f766afcSPoul-Henning Kamp 	if (fd >= 0) {
3214f766afcSPoul-Henning Kamp 		assert(close(fd) >= 0);
3224f766afcSPoul-Henning Kamp 		fd = -1;
3234f766afcSPoul-Henning Kamp 	}
3244f766afcSPoul-Henning Kamp 	if (increment) {
3254f766afcSPoul-Henning Kamp 		char *p = strchr(name, '\0') - 3;
3264f766afcSPoul-Henning Kamp 		if (++p[2] == '9') {
3274f766afcSPoul-Henning Kamp 			p[2] = '0';
3284f766afcSPoul-Henning Kamp 			if (++p[1] == '9') {
3294f766afcSPoul-Henning Kamp 				p[1] = '0';
3304f766afcSPoul-Henning Kamp 				if (++p[0] == '9') {
3314f766afcSPoul-Henning Kamp 					errx(EX_USAGE,
3324f766afcSPoul-Henning Kamp 					    "file-set sequence overflow");
3334f766afcSPoul-Henning Kamp 				}
3344f766afcSPoul-Henning Kamp 			}
3354f766afcSPoul-Henning Kamp 		}
3364f766afcSPoul-Henning Kamp 	}
3374f766afcSPoul-Henning Kamp 	if (direction == DST) {
3384f766afcSPoul-Henning Kamp 		fd = open(name, O_RDWR|O_CREAT, DEFFILEMODE);
3394f766afcSPoul-Henning Kamp 		if (fd < 0)
3404f766afcSPoul-Henning Kamp 			err(1, "Could not open %s", name);
3414f766afcSPoul-Henning Kamp 	} else {
3424f766afcSPoul-Henning Kamp 		fd = open(name, O_RDONLY, 0);
3434f766afcSPoul-Henning Kamp 	}
3444f766afcSPoul-Henning Kamp }
3454f766afcSPoul-Henning Kamp 
3464f766afcSPoul-Henning Kamp ssize_t
read_blk(void * dst,size_t len)3474f766afcSPoul-Henning Kamp file_set::read_blk(void *dst, size_t len)
3484f766afcSPoul-Henning Kamp {
3494f766afcSPoul-Henning Kamp 	(void)dst;
3504f766afcSPoul-Henning Kamp 	(void)len;
3514f766afcSPoul-Henning Kamp 	errx(EX_SOFTWARE, "That was not supposed to happen");
3524f766afcSPoul-Henning Kamp }
3534f766afcSPoul-Henning Kamp 
3544f766afcSPoul-Henning Kamp ssize_t
verify_blk(void * dst,size_t len,size_t expected)3554f766afcSPoul-Henning Kamp file_set::verify_blk(void *dst, size_t len, size_t expected)
3564f766afcSPoul-Henning Kamp {
3574f766afcSPoul-Henning Kamp 	(void)len;
3584f766afcSPoul-Henning Kamp 	if (fd < 0)
3594f766afcSPoul-Henning Kamp 		open_next(true);
3604f766afcSPoul-Henning Kamp 	if (fd < 0)
3614f766afcSPoul-Henning Kamp 		return (0);
3624f766afcSPoul-Henning Kamp 	ssize_t retval = read(fd, dst, expected);
3634f766afcSPoul-Henning Kamp 	if (retval == 0) {
3644f766afcSPoul-Henning Kamp 		assert(close(fd) >= 0);
3654f766afcSPoul-Henning Kamp 		fd = -1;
3664f766afcSPoul-Henning Kamp 	}
3674f766afcSPoul-Henning Kamp 	return (retval);
3684f766afcSPoul-Henning Kamp }
3694f766afcSPoul-Henning Kamp 
3704f766afcSPoul-Henning Kamp void
write_blk(const void * src,size_t len)3714f766afcSPoul-Henning Kamp file_set::write_blk(const void *src, size_t len)
3724f766afcSPoul-Henning Kamp {
3734f766afcSPoul-Henning Kamp 	if (fd < 0)
3744f766afcSPoul-Henning Kamp 		open_next(true);
3754f766afcSPoul-Henning Kamp 	ssize_t nwrite = write(fd, src, len);
3764f766afcSPoul-Henning Kamp 	if (nwrite < 0 || (size_t)nwrite != len)
3774f766afcSPoul-Henning Kamp 		errx(17, "write error (%zd != %zd)", nwrite, len);
3784f766afcSPoul-Henning Kamp }
3794f766afcSPoul-Henning Kamp 
3804f766afcSPoul-Henning Kamp void
file_mark(void)3814f766afcSPoul-Henning Kamp file_set::file_mark(void)
3824f766afcSPoul-Henning Kamp {
3834f766afcSPoul-Henning Kamp 	if (fd < 0)
3844f766afcSPoul-Henning Kamp 		return;
3854f766afcSPoul-Henning Kamp 
3864f766afcSPoul-Henning Kamp 	off_t where = lseek(fd, 0UL, SEEK_CUR);
3874f766afcSPoul-Henning Kamp 
3884f766afcSPoul-Henning Kamp 	int i = ftruncate(fd, where);
3894f766afcSPoul-Henning Kamp 	if (i < 0)
390aa0538b9SPoul-Henning Kamp 		errx(17, "truncate error, %s to %jd", name, (intmax_t)where);
3914f766afcSPoul-Henning Kamp 	assert(close(fd) >= 0);
3924f766afcSPoul-Henning Kamp 	fd = -1;
3934f766afcSPoul-Henning Kamp }
3944f766afcSPoul-Henning Kamp 
3954f766afcSPoul-Henning Kamp void
rewind(void)3964f766afcSPoul-Henning Kamp file_set::rewind(void)
3974f766afcSPoul-Henning Kamp {
3984f766afcSPoul-Henning Kamp 	char *p = strchr(name, '\0') - 3;
3994f766afcSPoul-Henning Kamp 	p[0] = '0';
4004f766afcSPoul-Henning Kamp 	p[1] = '0';
4014f766afcSPoul-Henning Kamp 	p[2] = '0';
4024f766afcSPoul-Henning Kamp 	open_next(false);
4034f766afcSPoul-Henning Kamp }
4044f766afcSPoul-Henning Kamp 
4054f766afcSPoul-Henning Kamp //---------------------------------------------------------------------
4064f766afcSPoul-Henning Kamp 
4074f766afcSPoul-Henning Kamp class flat_file: public tape_dev {
4084f766afcSPoul-Henning Kamp public:
flat_file(int file_handle,const char * spec,bool dst)4094f766afcSPoul-Henning Kamp 	flat_file(int file_handle, const char *spec, bool dst) :
4104f766afcSPoul-Henning Kamp 		tape_dev(file_handle, spec, dst) {};
4114f766afcSPoul-Henning Kamp 	ssize_t read_blk(void *dst, size_t len);
4124f766afcSPoul-Henning Kamp 	ssize_t verify_blk(void *dst, size_t len, size_t expected);
4134f766afcSPoul-Henning Kamp 	void write_blk(const void *src, size_t len);
4144f766afcSPoul-Henning Kamp 	void file_mark(void);
4154f766afcSPoul-Henning Kamp 	virtual void rewind(void);
4164f766afcSPoul-Henning Kamp };
4174f766afcSPoul-Henning Kamp 
4184f766afcSPoul-Henning Kamp ssize_t
read_blk(void * dst,size_t len)4194f766afcSPoul-Henning Kamp flat_file::read_blk(void *dst, size_t len)
4204f766afcSPoul-Henning Kamp {
4214f766afcSPoul-Henning Kamp 	(void)dst;
4224f766afcSPoul-Henning Kamp 	(void)len;
4234f766afcSPoul-Henning Kamp 	errx(EX_SOFTWARE, "That was not supposed to happen");
4244f766afcSPoul-Henning Kamp }
4254f766afcSPoul-Henning Kamp 
4264f766afcSPoul-Henning Kamp ssize_t
verify_blk(void * dst,size_t len,size_t expected)4274f766afcSPoul-Henning Kamp flat_file::verify_blk(void *dst, size_t len, size_t expected)
4284f766afcSPoul-Henning Kamp {
4294f766afcSPoul-Henning Kamp 	(void)len;
4304f766afcSPoul-Henning Kamp 	return (read(fd, dst, expected));
4314f766afcSPoul-Henning Kamp }
4324f766afcSPoul-Henning Kamp 
4334f766afcSPoul-Henning Kamp void
write_blk(const void * src,size_t len)4344f766afcSPoul-Henning Kamp flat_file::write_blk(const void *src, size_t len)
4354f766afcSPoul-Henning Kamp {
4364f766afcSPoul-Henning Kamp 	ssize_t nwrite = write(fd, src, len);
4374f766afcSPoul-Henning Kamp 	if (nwrite < 0 || (size_t)nwrite != len)
4384f766afcSPoul-Henning Kamp 		errx(17, "write error (%zd != %zd)", nwrite, len);
4394f766afcSPoul-Henning Kamp }
4404f766afcSPoul-Henning Kamp 
4414f766afcSPoul-Henning Kamp void
file_mark(void)4424f766afcSPoul-Henning Kamp flat_file::file_mark(void)
4434f766afcSPoul-Henning Kamp {
4444f766afcSPoul-Henning Kamp 	return;
4454f766afcSPoul-Henning Kamp }
4464f766afcSPoul-Henning Kamp 
4474f766afcSPoul-Henning Kamp void
rewind(void)4484f766afcSPoul-Henning Kamp flat_file::rewind(void)
4494f766afcSPoul-Henning Kamp {
4504f766afcSPoul-Henning Kamp 	errx(EX_SOFTWARE, "That was not supposed to happen");
4514f766afcSPoul-Henning Kamp }
4524f766afcSPoul-Henning Kamp 
4534f766afcSPoul-Henning Kamp //---------------------------------------------------------------------
4544f766afcSPoul-Henning Kamp 
4554f766afcSPoul-Henning Kamp enum e_how {H_INPUT, H_OUTPUT, H_VERIFY};
4564f766afcSPoul-Henning Kamp 
4574f766afcSPoul-Henning Kamp static tape_dev *
open_arg(const char * arg,enum e_how how,int rawfile)4584f766afcSPoul-Henning Kamp open_arg(const char *arg, enum e_how how, int rawfile)
4594f766afcSPoul-Henning Kamp {
4604f766afcSPoul-Henning Kamp 	int fd;
4614f766afcSPoul-Henning Kamp 
4624f766afcSPoul-Henning Kamp 	if (!strcmp(arg, "-") && how == H_OUTPUT)
4634f766afcSPoul-Henning Kamp 		fd = STDOUT_FILENO;
4644f766afcSPoul-Henning Kamp 	else if (!strcmp(arg, "-"))
4654f766afcSPoul-Henning Kamp 		fd = STDIN_FILENO;
4664f766afcSPoul-Henning Kamp 	else if (how == H_OUTPUT)
4674f766afcSPoul-Henning Kamp 		fd = open(arg, O_RDWR|O_CREAT, DEFFILEMODE);
4684f766afcSPoul-Henning Kamp 	else
4694f766afcSPoul-Henning Kamp 		fd = open(arg, O_RDONLY);
4704f766afcSPoul-Henning Kamp 
4714f766afcSPoul-Henning Kamp 	if (fd < 0)
4724f766afcSPoul-Henning Kamp 		err(EX_NOINPUT, "Cannot open %s:", arg);
4734f766afcSPoul-Henning Kamp 
4744f766afcSPoul-Henning Kamp 	struct mtop mt;
4754f766afcSPoul-Henning Kamp 	mt.mt_op = MTNOP;
4764f766afcSPoul-Henning Kamp 	mt.mt_count = 1;
4774f766afcSPoul-Henning Kamp 	int i = ioctl(fd, MTIOCTOP, &mt);
4784f766afcSPoul-Henning Kamp 
4794f766afcSPoul-Henning Kamp 	if (i >= 0)
4804f766afcSPoul-Henning Kamp 		return (new tape_dev(fd, arg, how == H_OUTPUT));
4814f766afcSPoul-Henning Kamp 
4824f766afcSPoul-Henning Kamp 	size_t alen = strlen(arg);
4834f766afcSPoul-Henning Kamp 	if (alen >= 5 && !strcmp(arg + (alen - 4), ".000")) {
4844f766afcSPoul-Henning Kamp 		if (how == H_INPUT)
4854f766afcSPoul-Henning Kamp 			errx(EX_USAGE,
4864f766afcSPoul-Henning Kamp 			    "File-sets files cannot be used as source");
4874f766afcSPoul-Henning Kamp 		return (new file_set(fd, arg, how == H_OUTPUT));
4884f766afcSPoul-Henning Kamp 	}
4894f766afcSPoul-Henning Kamp 
4904f766afcSPoul-Henning Kamp 	if (how != H_INPUT && rawfile)
4914f766afcSPoul-Henning Kamp 		return (new flat_file(fd, arg, how == H_OUTPUT));
4924f766afcSPoul-Henning Kamp 
4934f766afcSPoul-Henning Kamp 	return (new tap_file(fd, arg, how == H_OUTPUT));
4944f766afcSPoul-Henning Kamp }
4954f766afcSPoul-Henning Kamp 
4964f766afcSPoul-Henning Kamp //---------------------------------------------------------------------
4974f766afcSPoul-Henning Kamp 
4984f766afcSPoul-Henning Kamp static tape_dev *input;
4994f766afcSPoul-Henning Kamp static tape_dev *output;
5004f766afcSPoul-Henning Kamp 
5014f766afcSPoul-Henning Kamp static size_t maxblk = MAXREC;
5024f766afcSPoul-Henning Kamp static uint64_t	lastrec, fsize, tsize;
5034f766afcSPoul-Henning Kamp static FILE *msg;
5044f766afcSPoul-Henning Kamp static ssize_t lastnread;
5054f766afcSPoul-Henning Kamp static struct timespec t_start, t_end;
5064f766afcSPoul-Henning Kamp 
5074f766afcSPoul-Henning Kamp static void
report_total(FILE * file)5084f766afcSPoul-Henning Kamp report_total(FILE *file)
5094f766afcSPoul-Henning Kamp {
5104f766afcSPoul-Henning Kamp 	double dur = (t_end.tv_nsec - t_start.tv_nsec) * 1e-9;
5114f766afcSPoul-Henning Kamp 	dur += t_end.tv_sec - t_start.tv_sec;
5124f766afcSPoul-Henning Kamp 	uintmax_t tot = tsize + fsize;
5134f766afcSPoul-Henning Kamp 	fprintf(file, "total length: %ju bytes", tot);
5144f766afcSPoul-Henning Kamp 	fprintf(file, " time: %.0f s", dur);
5154f766afcSPoul-Henning Kamp 	tot /= 1024;
5164f766afcSPoul-Henning Kamp 	fprintf(file, " rate: %.1f kB/s", (double)tot/dur);
5174f766afcSPoul-Henning Kamp 	fprintf(file, "\n");
5184f766afcSPoul-Henning Kamp }
5194f766afcSPoul-Henning Kamp 
5204f766afcSPoul-Henning Kamp static void
sigintr(int signo __unused)5214f766afcSPoul-Henning Kamp sigintr(int signo __unused)
5224f766afcSPoul-Henning Kamp {
5234f766afcSPoul-Henning Kamp 	(void)signo;
5244f766afcSPoul-Henning Kamp 	(void)clock_gettime(CLOCK_MONOTONIC, &t_end);
5254f766afcSPoul-Henning Kamp 	if (record) {
5264f766afcSPoul-Henning Kamp 		if (record - lastrec > 1)
5274f766afcSPoul-Henning Kamp 			fprintf(msg, "records %ju to %ju\n",
5284f766afcSPoul-Henning Kamp 			    (intmax_t)lastrec, (intmax_t)record);
5294f766afcSPoul-Henning Kamp 		else
5304f766afcSPoul-Henning Kamp 			fprintf(msg, "record %ju\n", (intmax_t)lastrec);
5314f766afcSPoul-Henning Kamp 	}
5324f766afcSPoul-Henning Kamp 	fprintf(msg, "interrupt at file %d: record %ju\n",
5334f766afcSPoul-Henning Kamp 	    filen, (uintmax_t)record);
5344f766afcSPoul-Henning Kamp 	report_total(msg);
5354f766afcSPoul-Henning Kamp 	exit(1);
5364f766afcSPoul-Henning Kamp }
5374f766afcSPoul-Henning Kamp 
5384f766afcSPoul-Henning Kamp #ifdef SIGINFO
5394f766afcSPoul-Henning Kamp static volatile sig_atomic_t want_info;
5404f766afcSPoul-Henning Kamp 
5414f766afcSPoul-Henning Kamp static void
siginfo(int signo)5424f766afcSPoul-Henning Kamp siginfo(int signo)
5434f766afcSPoul-Henning Kamp {
5444f766afcSPoul-Henning Kamp 	(void)signo;
5454f766afcSPoul-Henning Kamp 	want_info = 1;
5464f766afcSPoul-Henning Kamp }
5474f766afcSPoul-Henning Kamp 
5484f766afcSPoul-Henning Kamp static void
check_want_info(void)5494f766afcSPoul-Henning Kamp check_want_info(void)
5504f766afcSPoul-Henning Kamp {
5514f766afcSPoul-Henning Kamp 	if (want_info) {
5524f766afcSPoul-Henning Kamp 		(void)clock_gettime(CLOCK_MONOTONIC, &t_end);
5534f766afcSPoul-Henning Kamp 		fprintf(stderr, "tcopy: file %d record %ju ",
5544f766afcSPoul-Henning Kamp 		    filen, (uintmax_t)record);
5554f766afcSPoul-Henning Kamp 		report_total(stderr);
5564f766afcSPoul-Henning Kamp 		want_info = 0;
5574f766afcSPoul-Henning Kamp 	}
5584f766afcSPoul-Henning Kamp }
5594f766afcSPoul-Henning Kamp 
5604f766afcSPoul-Henning Kamp #else /* !SIGINFO */
5614f766afcSPoul-Henning Kamp 
5624f766afcSPoul-Henning Kamp static void
check_want_info(void)5634f766afcSPoul-Henning Kamp check_want_info(void)
5644f766afcSPoul-Henning Kamp {
5654f766afcSPoul-Henning Kamp }
5664f766afcSPoul-Henning Kamp 
5674f766afcSPoul-Henning Kamp #endif
5684f766afcSPoul-Henning Kamp 
5694f766afcSPoul-Henning Kamp static char *
getspace(size_t blk)5704f766afcSPoul-Henning Kamp getspace(size_t blk)
5714f766afcSPoul-Henning Kamp {
5724f766afcSPoul-Henning Kamp 	void *bp;
5734f766afcSPoul-Henning Kamp 
5744f766afcSPoul-Henning Kamp 	assert(blk > 0);
5754f766afcSPoul-Henning Kamp 	if ((bp = malloc(blk)) == NULL)
5764f766afcSPoul-Henning Kamp 		errx(11, "no memory");
5774f766afcSPoul-Henning Kamp 	return ((char *)bp);
5784f766afcSPoul-Henning Kamp }
5794f766afcSPoul-Henning Kamp 
5804f766afcSPoul-Henning Kamp static void
usage(void)5814f766afcSPoul-Henning Kamp usage(void)
5824f766afcSPoul-Henning Kamp {
5834f766afcSPoul-Henning Kamp 	fprintf(stderr, "usage: tcopy [-cvx] [-s maxblk] [src [dest]]\n");
5844f766afcSPoul-Henning Kamp 	exit(1);
5854f766afcSPoul-Henning Kamp }
5864f766afcSPoul-Henning Kamp 
5874f766afcSPoul-Henning Kamp static void
progress(ssize_t nread)5884f766afcSPoul-Henning Kamp progress(ssize_t nread)
5894f766afcSPoul-Henning Kamp {
5904f766afcSPoul-Henning Kamp 	if (nread != lastnread) {
5914f766afcSPoul-Henning Kamp 		if (lastnread != 0 && lastnread != NOCOUNT) {
5924f766afcSPoul-Henning Kamp 			if (lastrec == 0 && nread == 0)
5934f766afcSPoul-Henning Kamp 				fprintf(msg, "%ju records\n",
5944f766afcSPoul-Henning Kamp 				    (uintmax_t)record);
5954f766afcSPoul-Henning Kamp 			else if (record - lastrec > 1)
5964f766afcSPoul-Henning Kamp 				fprintf(msg, "records %ju to %ju\n",
5974f766afcSPoul-Henning Kamp 				    (uintmax_t)lastrec,
5984f766afcSPoul-Henning Kamp 				    (uintmax_t)record);
5994f766afcSPoul-Henning Kamp 			else
6004f766afcSPoul-Henning Kamp 				fprintf(msg, "record %ju\n",
6014f766afcSPoul-Henning Kamp 				    (uintmax_t)lastrec);
6024f766afcSPoul-Henning Kamp 		}
6034f766afcSPoul-Henning Kamp 		if (nread != 0)
6044f766afcSPoul-Henning Kamp 			fprintf(msg,
6054f766afcSPoul-Henning Kamp 			    "file %d: block size %zd: ", filen, nread);
6064f766afcSPoul-Henning Kamp 		(void) fflush(msg);
6074f766afcSPoul-Henning Kamp 		lastrec = record;
6084f766afcSPoul-Henning Kamp 	}
6094f766afcSPoul-Henning Kamp 	if (nread > 0) {
6104f766afcSPoul-Henning Kamp 		fsize += (size_t)nread;
6114f766afcSPoul-Henning Kamp 		record++;
6124f766afcSPoul-Henning Kamp 	} else {
6134f766afcSPoul-Henning Kamp 		if (lastnread <= 0 && lastnread != NOCOUNT) {
6144f766afcSPoul-Henning Kamp 			fprintf(msg, "eot\n");
6154f766afcSPoul-Henning Kamp 			return;
6164f766afcSPoul-Henning Kamp 		}
6174f766afcSPoul-Henning Kamp 		fprintf(msg,
6184f766afcSPoul-Henning Kamp 		    "file %d: eof after %ju records: %ju bytes\n",
6194f766afcSPoul-Henning Kamp 		    filen, (uintmax_t)record, (uintmax_t)fsize);
6204f766afcSPoul-Henning Kamp 		filen++;
6214f766afcSPoul-Henning Kamp 		tsize += fsize;
6224f766afcSPoul-Henning Kamp 		fsize = record = lastrec = 0;
6234f766afcSPoul-Henning Kamp 		lastnread = 0;
6244f766afcSPoul-Henning Kamp 	}
6254f766afcSPoul-Henning Kamp 	lastnread = nread;
6264f766afcSPoul-Henning Kamp }
6274f766afcSPoul-Henning Kamp 
6284f766afcSPoul-Henning Kamp static void
read_or_copy(void)6294f766afcSPoul-Henning Kamp read_or_copy(void)
6304f766afcSPoul-Henning Kamp {
6314f766afcSPoul-Henning Kamp 	int needeof;
6324f766afcSPoul-Henning Kamp 	ssize_t nread, prev_read;
6334f766afcSPoul-Henning Kamp 	char *buff = getspace(maxblk);
6344f766afcSPoul-Henning Kamp 
6354f766afcSPoul-Henning Kamp 	(void)clock_gettime(CLOCK_MONOTONIC, &t_start);
6364f766afcSPoul-Henning Kamp 	needeof = 0;
6374f766afcSPoul-Henning Kamp 	for (prev_read = NOCOUNT;;) {
6384f766afcSPoul-Henning Kamp 		check_want_info();
6394f766afcSPoul-Henning Kamp 		nread = input->read_blk(buff, maxblk);
6404f766afcSPoul-Henning Kamp 		progress(nread);
6414f766afcSPoul-Henning Kamp 		if (nread > 0) {
6424f766afcSPoul-Henning Kamp 			if (output != NULL) {
6434f766afcSPoul-Henning Kamp 				if (needeof) {
6444f766afcSPoul-Henning Kamp 					output->file_mark();
6454f766afcSPoul-Henning Kamp 					needeof = 0;
6464f766afcSPoul-Henning Kamp 				}
6474f766afcSPoul-Henning Kamp 				output->write_blk(buff, (size_t)nread);
6484f766afcSPoul-Henning Kamp 			}
6494f766afcSPoul-Henning Kamp 		} else {
6504f766afcSPoul-Henning Kamp 			if (prev_read <= 0 && prev_read != NOCOUNT) {
6514f766afcSPoul-Henning Kamp 				break;
6524f766afcSPoul-Henning Kamp 			}
6534f766afcSPoul-Henning Kamp 			needeof = 1;
6544f766afcSPoul-Henning Kamp 		}
6554f766afcSPoul-Henning Kamp 		prev_read = nread;
6564f766afcSPoul-Henning Kamp 	}
6574f766afcSPoul-Henning Kamp 	(void)clock_gettime(CLOCK_MONOTONIC, &t_end);
6584f766afcSPoul-Henning Kamp 	report_total(msg);
6594f766afcSPoul-Henning Kamp 	free(buff);
6604f766afcSPoul-Henning Kamp }
6614f766afcSPoul-Henning Kamp 
6624f766afcSPoul-Henning Kamp static void
verify(void)6634f766afcSPoul-Henning Kamp verify(void)
6644f766afcSPoul-Henning Kamp {
6654f766afcSPoul-Henning Kamp 	char *buf1 = getspace(maxblk);
6664f766afcSPoul-Henning Kamp 	char *buf2 = getspace(maxblk);
6674f766afcSPoul-Henning Kamp 	int eot = 0;
6684f766afcSPoul-Henning Kamp 	ssize_t nread1, nread2;
6694f766afcSPoul-Henning Kamp 	filen = 0;
6704f766afcSPoul-Henning Kamp 	tsize = 0;
6714f766afcSPoul-Henning Kamp 
6724f766afcSPoul-Henning Kamp 	assert(output != NULL);
6734f766afcSPoul-Henning Kamp 	(void)clock_gettime(CLOCK_MONOTONIC, &t_start);
6744f766afcSPoul-Henning Kamp 
6754f766afcSPoul-Henning Kamp 	while (1) {
6764f766afcSPoul-Henning Kamp 		check_want_info();
6774f766afcSPoul-Henning Kamp 		nread1 = input->read_blk(buf1, (size_t)maxblk);
6784f766afcSPoul-Henning Kamp 		nread2 = output->verify_blk(buf2, maxblk, (size_t)nread1);
6794f766afcSPoul-Henning Kamp 		progress(nread1);
6804f766afcSPoul-Henning Kamp 		if (nread1 != nread2) {
6814f766afcSPoul-Henning Kamp 			fprintf(msg,
6824f766afcSPoul-Henning Kamp 			    "tcopy: tapes have different block sizes; "
6834f766afcSPoul-Henning Kamp 			    "%zd != %zd.\n", nread1, nread2);
6844f766afcSPoul-Henning Kamp 			exit(1);
6854f766afcSPoul-Henning Kamp 		}
6864f766afcSPoul-Henning Kamp 		if (nread1 > 0 && memcmp(buf1, buf2, (size_t)nread1)) {
6874f766afcSPoul-Henning Kamp 			fprintf(msg, "tcopy: tapes have different data.\n");
6884f766afcSPoul-Henning Kamp 			exit(1);
6894f766afcSPoul-Henning Kamp 		} else if (nread1 > 0) {
6904f766afcSPoul-Henning Kamp 			eot = 0;
6914f766afcSPoul-Henning Kamp 		} else if (eot++) {
6924f766afcSPoul-Henning Kamp 			break;
6934f766afcSPoul-Henning Kamp 		}
6944f766afcSPoul-Henning Kamp 	}
6954f766afcSPoul-Henning Kamp 	(void)clock_gettime(CLOCK_MONOTONIC, &t_end);
6964f766afcSPoul-Henning Kamp 	report_total(msg);
6974f766afcSPoul-Henning Kamp 	fprintf(msg, "tcopy: tapes are identical.\n");
6984f766afcSPoul-Henning Kamp 	fprintf(msg, "rewinding\n");
6994f766afcSPoul-Henning Kamp 	input->rewind();
7004f766afcSPoul-Henning Kamp 	output->rewind();
7014f766afcSPoul-Henning Kamp 
7024f766afcSPoul-Henning Kamp 	free(buf1);
7034f766afcSPoul-Henning Kamp 	free(buf2);
7044f766afcSPoul-Henning Kamp }
7054f766afcSPoul-Henning Kamp 
7064f766afcSPoul-Henning Kamp int
main(int argc,char * argv[])7074f766afcSPoul-Henning Kamp main(int argc, char *argv[])
7084f766afcSPoul-Henning Kamp {
7094f766afcSPoul-Henning Kamp 	enum operation op = READ;
7104f766afcSPoul-Henning Kamp 	int ch;
7114f766afcSPoul-Henning Kamp 	unsigned long maxphys = 0;
7124f766afcSPoul-Henning Kamp 	size_t l_maxphys = sizeof maxphys;
71349f92418SPoul-Henning Kamp 	int64_t tmp;
7144f766afcSPoul-Henning Kamp 	int rawfile = 0;
7154f766afcSPoul-Henning Kamp 
7164f766afcSPoul-Henning Kamp 	setbuf(stderr, NULL);
7174f766afcSPoul-Henning Kamp 
7184f766afcSPoul-Henning Kamp 	if (!sysctlbyname("kern.maxphys", &maxphys, &l_maxphys, NULL, 0UL))
7194f766afcSPoul-Henning Kamp 		maxblk = maxphys;
7204f766afcSPoul-Henning Kamp 
7214f766afcSPoul-Henning Kamp 	msg = stdout;
7224f766afcSPoul-Henning Kamp 	while ((ch = getopt(argc, argv, "cl:rs:vx")) != -1)
7234f766afcSPoul-Henning Kamp 		switch((char)ch) {
7244f766afcSPoul-Henning Kamp 		case 'c':
7254f766afcSPoul-Henning Kamp 			op = COPYVERIFY;
7264f766afcSPoul-Henning Kamp 			break;
7274f766afcSPoul-Henning Kamp 		case 'l':
7284f766afcSPoul-Henning Kamp 			msg = fopen(optarg, "w");
7294f766afcSPoul-Henning Kamp 			if (msg == NULL)
7304f766afcSPoul-Henning Kamp 				errx(EX_CANTCREAT, "Cannot open %s", optarg);
7314f766afcSPoul-Henning Kamp 			setbuf(msg, NULL);
7324f766afcSPoul-Henning Kamp 			break;
7334f766afcSPoul-Henning Kamp 		case 'r':
7344f766afcSPoul-Henning Kamp 			rawfile = 1;
7354f766afcSPoul-Henning Kamp 			break;
7364f766afcSPoul-Henning Kamp 		case 's':
7374f766afcSPoul-Henning Kamp 			if (expand_number(optarg, &tmp)) {
7384f766afcSPoul-Henning Kamp 				warnx("illegal block size");
7394f766afcSPoul-Henning Kamp 				usage();
7404f766afcSPoul-Henning Kamp 			}
741*6582915cSPoul-Henning Kamp 			if (tmp <= 0) {
7424f766afcSPoul-Henning Kamp 				warnx("illegal block size");
7434f766afcSPoul-Henning Kamp 				usage();
7444f766afcSPoul-Henning Kamp 			}
74549f92418SPoul-Henning Kamp 			maxblk = tmp;
7464f766afcSPoul-Henning Kamp 			break;
7474f766afcSPoul-Henning Kamp 		case 'v':
7484f766afcSPoul-Henning Kamp 			op = VERIFY;
7494f766afcSPoul-Henning Kamp 			break;
7504f766afcSPoul-Henning Kamp 		case 'x':
7514f766afcSPoul-Henning Kamp 			if (msg == stdout)
7524f766afcSPoul-Henning Kamp 				msg = stderr;
7534f766afcSPoul-Henning Kamp 			break;
7544f766afcSPoul-Henning Kamp 		case '?':
7554f766afcSPoul-Henning Kamp 		default:
7564f766afcSPoul-Henning Kamp 			usage();
7574f766afcSPoul-Henning Kamp 		}
7584f766afcSPoul-Henning Kamp 	argc -= optind;
7594f766afcSPoul-Henning Kamp 	argv += optind;
7604f766afcSPoul-Henning Kamp 
7614f766afcSPoul-Henning Kamp 	switch(argc) {
7624f766afcSPoul-Henning Kamp 	case 0:
7634f766afcSPoul-Henning Kamp 		if (op != READ)
7644f766afcSPoul-Henning Kamp 			usage();
7654f766afcSPoul-Henning Kamp 		break;
7664f766afcSPoul-Henning Kamp 	case 1:
7674f766afcSPoul-Henning Kamp 		if (op != READ)
7684f766afcSPoul-Henning Kamp 			usage();
7694f766afcSPoul-Henning Kamp 		break;
7704f766afcSPoul-Henning Kamp 	case 2:
7714f766afcSPoul-Henning Kamp 		if (op == READ)
7724f766afcSPoul-Henning Kamp 			op = COPY;
7734f766afcSPoul-Henning Kamp 		if (!strcmp(argv[1], "-")) {
7744f766afcSPoul-Henning Kamp 			if (op == COPYVERIFY)
7754f766afcSPoul-Henning Kamp 				errx(EX_USAGE,
7764f766afcSPoul-Henning Kamp 				    "Cannot copy+verify with '-' destination");
7774f766afcSPoul-Henning Kamp 			if (msg == stdout)
7784f766afcSPoul-Henning Kamp 				msg = stderr;
7794f766afcSPoul-Henning Kamp 		}
7804f766afcSPoul-Henning Kamp 		if (op == VERIFY)
7814f766afcSPoul-Henning Kamp 			output = open_arg(argv[1], H_VERIFY, 0);
7824f766afcSPoul-Henning Kamp 		else
7834f766afcSPoul-Henning Kamp 			output = open_arg(argv[1], H_OUTPUT, rawfile);
7844f766afcSPoul-Henning Kamp 		break;
7854f766afcSPoul-Henning Kamp 	default:
7864f766afcSPoul-Henning Kamp 		usage();
7874f766afcSPoul-Henning Kamp 	}
7884f766afcSPoul-Henning Kamp 
7894f766afcSPoul-Henning Kamp 	if (argc == 0) {
7904f766afcSPoul-Henning Kamp 		input = open_arg(_PATH_DEFTAPE, H_INPUT, 0);
7914f766afcSPoul-Henning Kamp 	} else {
7924f766afcSPoul-Henning Kamp 		input = open_arg(argv[0], H_INPUT, 0);
7934f766afcSPoul-Henning Kamp 	}
7944f766afcSPoul-Henning Kamp 
7954f766afcSPoul-Henning Kamp 	if ((signal(SIGINT, SIG_IGN)) != SIG_IGN)
7964f766afcSPoul-Henning Kamp 		(void) signal(SIGINT, sigintr);
7974f766afcSPoul-Henning Kamp 
7984f766afcSPoul-Henning Kamp #ifdef SIGINFO
7994f766afcSPoul-Henning Kamp 	(void)signal(SIGINFO, siginfo);
8004f766afcSPoul-Henning Kamp #endif
8014f766afcSPoul-Henning Kamp 
8024f766afcSPoul-Henning Kamp 	if (op != VERIFY) {
8034f766afcSPoul-Henning Kamp 		if (op == COPYVERIFY) {
8044f766afcSPoul-Henning Kamp 			assert(output != NULL);
8054f766afcSPoul-Henning Kamp 			fprintf(msg, "rewinding\n");
8064f766afcSPoul-Henning Kamp 			input->rewind();
8074f766afcSPoul-Henning Kamp 			output->rewind();
8084f766afcSPoul-Henning Kamp 		}
8094f766afcSPoul-Henning Kamp 
8104f766afcSPoul-Henning Kamp 		read_or_copy();
8114f766afcSPoul-Henning Kamp 
8124f766afcSPoul-Henning Kamp 		if (op == COPY || op == COPYVERIFY) {
8134f766afcSPoul-Henning Kamp 			assert(output != NULL);
8144f766afcSPoul-Henning Kamp 			output->file_mark();
8154f766afcSPoul-Henning Kamp 			output->file_mark();
8164f766afcSPoul-Henning Kamp 		}
8174f766afcSPoul-Henning Kamp 	}
8184f766afcSPoul-Henning Kamp 
8194f766afcSPoul-Henning Kamp 	if (op == VERIFY || op == COPYVERIFY) {
8204f766afcSPoul-Henning Kamp 
8214f766afcSPoul-Henning Kamp 		if (op == COPYVERIFY) {
8224f766afcSPoul-Henning Kamp 			assert(output != NULL);
8234f766afcSPoul-Henning Kamp 			fprintf(msg, "rewinding\n");
8244f766afcSPoul-Henning Kamp 			input->rewind();
8254f766afcSPoul-Henning Kamp 			output->rewind();
8264f766afcSPoul-Henning Kamp 			input->direction = tape_dev::SRC;
8274f766afcSPoul-Henning Kamp 			output->direction = tape_dev::SRC;
8284f766afcSPoul-Henning Kamp 		}
8294f766afcSPoul-Henning Kamp 
8304f766afcSPoul-Henning Kamp 		verify();
8314f766afcSPoul-Henning Kamp 	}
8324f766afcSPoul-Henning Kamp 
8334f766afcSPoul-Henning Kamp 	if (msg != stderr && msg != stdout)
8344f766afcSPoul-Henning Kamp 		report_total(stderr);
8354f766afcSPoul-Henning Kamp 
8364f766afcSPoul-Henning Kamp 	return(0);
8374f766afcSPoul-Henning Kamp }
838