The branch main has been updated by phk: URL: https://cgit.FreeBSD.org/src/commit/?id=4f766afc1ca0595fcabafe4dbc61974bb81bfbfe
commit 4f766afc1ca0595fcabafe4dbc61974bb81bfbfe Author: Poul-Henning Kamp <p...@freebsd.org> AuthorDate: 2025-08-13 07:03:47 +0000 Commit: Poul-Henning Kamp <p...@freebsd.org> CommitDate: 2025-08-13 07:06:45 +0000 tcopy: Refactor and add support for SIMH-TAPFILES See manpage for new features. Preliminary review by: imp --- usr.bin/tcopy/Makefile | 4 +- usr.bin/tcopy/tcopy.1 | 195 +++++++++--- usr.bin/tcopy/tcopy.c | 338 -------------------- usr.bin/tcopy/tcopy.cc | 837 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 994 insertions(+), 380 deletions(-) diff --git a/usr.bin/tcopy/Makefile b/usr.bin/tcopy/Makefile index 73dcd45b2e0f..831de7625db8 100644 --- a/usr.bin/tcopy/Makefile +++ b/usr.bin/tcopy/Makefile @@ -1,4 +1,4 @@ -PROG= tcopy -LIBADD= util +PROG_CXX= tcopy +LIBADD= util .include <bsd.prog.mk> diff --git a/usr.bin/tcopy/tcopy.1 b/usr.bin/tcopy/tcopy.1 index 3f12a807e41e..f74a29ba1173 100644 --- a/usr.bin/tcopy/tcopy.1 +++ b/usr.bin/tcopy/tcopy.1 @@ -30,65 +30,189 @@ .Os .Sh NAME .Nm tcopy -.Nd copy and/or verify mag tapes +.Nd read, write, copy and verify tapes .Sh SYNOPSIS .Nm -.Op Fl cvx +.Op Fl crvx +.Op Fl l Ar logfile .Op Fl s Ar maxblk .Oo Ar src Op Ar dest .Oc .Sh DESCRIPTION The .Nm -utility is designed to copy magnetic tapes. +utility is designed to read, write and copy tapes. +.Pp The only assumption made about the tape layout is that there are two sequential EOF marks at the end. -By default, the -.Nm -utility will print -information about the sizes of records and files found -on the +.Pp +The +.Ar src +argument can be a tape device and defaults to .Pa /dev/sa0 -tape, or on the tape specified by the +or it can be data in SIMH-TAP format. +If +.Ar src +is +.Dq Cm - +the standard input is read. +.Pp +If the +.Ar dest +argument is also specified, a copy of the .Ar src -argument. -If a destination tape is also specified by the +will be made onto the +.Ar dest . +If +.Ar dest +is +.Dq Cm - +standard output will be written to. +.Pp +If .Ar dest -argument, a copy of the source tape will be made. -The blocking on the -destination tape will be identical to that used on the source tape. -Copying -a tape will yield the same program output as if just printing the sizes. +is a tape device, the file and record structure will be the same. +.Pp +If +.Ar dest +is a filename ending in +.Dq Cm .000 +the contents each file on +.Ar src +will be written to sequentially numbered files +.Dq Cm .000 , +.Dq Cm .001 , +.Dq Cm .002 +etc. +Information about record sizes will be lost. +.Pp +If the +.Fl r +flag is specified, only the data will be written, information about +file and record layout is lost. +.Pp +Otherwise the data, file and record structure of +.Ar src +will be written in SIMH-TAP format. +.Pp +The +.Nm +utility will report information about the layout of +.Ar src +like this on standard output: +.Bd -literal -offset indent +file 0: block size 80: 6 records +file 0: eof after 6 records: 480 bytes +file 1: block size 3072: records 0 to 262 +file 1: block size 612: record 262 +file 1: eof after 263 records: 805476 bytes +[…] +eot +total length: 972851280 bytes time: 41 s rate: 22934.8 kB/s +.Ed +.Pp +If +.Ar dest +is +.Dq Cm - +or if +.Fl x +is specified this goes to standard error instead, +and can also be redirected with +.Fl l Ar logfile , +in which case the final total line will also be reported on standard error. +.Pp +If +.Nm +receives a +.Dv SIGINFO +signal, current counts are reported on standard error. .Pp The following options are available: .Bl -tag -width ".Fl s Ar maxblk" .It Fl c -Copy +Rewind both tapes, copy .Ar src to -.Ar dest -and then verify that the two tapes are identical. +.Ar dest , +rewind again and verify that the two tapes are now identical. +.It Fl l Ar logfile +Output all informational messages to +.Ar logfile . +.It Fl r +Write only the contents of all data blocks to the output. +The file and record structure of the input will be lost. .It Fl s Ar maxblk Specify a maximum block size, .Ar maxblk . +The default is +.Va kern.maxphys . .It Fl v -Given the two tapes +Verify that .Ar src and -.Ar dest , -verify that they are identical. +.Ar dest +are identical. +Note that the tapes are not rewound prior to the comparison. .It Fl x Output all informational messages to the standard error instead of the standard output. -This option is useful when +This option is automatic if .Ar dest is given as -.Pa /dev/stdout . +.Dq Cm - . .El +.Sh EXIT STATUS +Unfortunately all over the place, but zero always means succeess. +.Sh EXAMPLES +Verify that the tape in /dev/sa0 can be read and see the layout +of its content: +.Bd -literal -offset indent +tcopy +.Ed +.Pp +Copy a tape using two tape drives: +.Bd -literal -offset indent +tcopy /dev/sa0 /dev/sa1 +.Ed +.Pp +Copy a tape using only a single tape drive, and verify the result: +.Bd -literal -offset indent +tcopy /dev/sa0 /tmp/temp.tapfile +# change tape +tcopy -c /tmp/temp.tapfile /dev/sa0 +.Ed +.Pp +Make a cryptographic hash of both the contents and the layout of the tape in +/dev/sa1: +.Pp +.Bd -literal -offset indent +tcopy /dev/sa1 - | sha256 +.Ed +.Pp +Copy a tape to a tape drive on another machine: +.Bd -literal -offset indent +tcopy /dev/sa0 - | ssh otherhost tcopy - /dev/sa0 +.Ed +.Pp +Extract the tape files into individual files: +.Bd -literal -offset indent +tcopy /dev/sa0 /tmp/_.tape.000 +.Ed +.Pp +Ignore all structure on the tape and feed all data to +.Xr tar 1 : +.Bd -literal -offset indent +tcopy -l /dev/null -r /dev/sa0 - | tar tvf - +.Ed .Sh SEE ALSO .Xr mt 1 , +.Xr sa 4 , .Xr mtio 4 +.Sh STANDARDS +The SIMH-TAP format is documented in the open-simh github repos: +.Pa https://github.com/open-simh/simh/blob/master/doc/simh_magtape.doc .Sh HISTORY The .Nm @@ -99,19 +223,16 @@ command appeared in .It Modern tape drives may return a SCSI "Incorrect Length Indicator (ILI)" for each read with a different block size that what is on the -tape, and that slows things down a lot. +tape, and that slows +.Nm +down a lot. This can be disabled with the .Xr mt 1 command: .Bd -literal -offset indent -$ mt param sili -s 1 +mt param sili -s 1 .Ed .It -Writing an image of a tape to a file does not preserve much more than -the raw data. -Block size(s) and tape EOF marks are lost which would -otherwise be preserved in a tape-to-tape copy. -.It End of data (EOD) is determined by two sequential EOF marks with no data between them. There used to be old systems which typically wrote three EOF's between tape @@ -120,13 +241,7 @@ The .Nm utility will erroneously stop copying early in this case. .It -When using the copy/verify option -.Fl c , -.Nm -does not rewind the tapes prior to start. -A rewind is performed -after writing, prior to the verification stage. -If one does not start -at the beginning-of-tape (BOT) then the comparison -may not be of the intended data. +With +.Fl c +the tape drives are not rewound at the same time, but one after the other. .El diff --git a/usr.bin/tcopy/tcopy.c b/usr.bin/tcopy/tcopy.c deleted file mode 100644 index 39eae4126324..000000000000 --- a/usr.bin/tcopy/tcopy.c +++ /dev/null @@ -1,338 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-3-Clause - * - * Copyright (c) 1985, 1987, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/ioctl.h> -#include <sys/mtio.h> - -#include <err.h> -#include <errno.h> -#include <fcntl.h> -#include <libutil.h> -#include <paths.h> -#include <sys/sysctl.h> -#include <signal.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#define MAXREC (64 * 1024) -#define NOCOUNT (-2) - -static int filen, guesslen, maxblk = MAXREC; -static uint64_t lastrec, record, size, tsize; -static FILE *msg; - -static void *getspace(int); -static void intr(int); -static void usage(void) __dead2; -static void verify(int, int, char *); -static void writeop(int, int); -static void rewind_tape(int); - -int -main(int argc, char *argv[]) -{ - int lastnread, nread, nw, inp, outp; - enum {READ, VERIFY, COPY, COPYVERIFY} op = READ; - sig_t oldsig; - int ch, needeof; - char *buff; - const char *inf; - unsigned long maxphys = 0; - size_t l_maxphys = sizeof maxphys; - uint64_t tmp; - - if (!sysctlbyname("kern.maxphys", &maxphys, &l_maxphys, NULL, 0)) - maxblk = maxphys; - - msg = stdout; - guesslen = 1; - outp = -1; - while ((ch = getopt(argc, argv, "cs:vx")) != -1) - switch((char)ch) { - case 'c': - op = COPYVERIFY; - break; - case 's': - if (expand_number(optarg, &tmp)) { - warnx("illegal block size"); - usage(); - } - maxblk = tmp; - if (maxblk == 0) { - warnx("illegal block size"); - usage(); - } - guesslen = 0; - break; - case 'v': - op = VERIFY; - break; - case 'x': - msg = stderr; - break; - case '?': - default: - usage(); - } - argc -= optind; - argv += optind; - - switch(argc) { - case 0: - if (op != READ) - usage(); - inf = _PATH_DEFTAPE; - break; - case 1: - if (op != READ) - usage(); - inf = argv[0]; - break; - case 2: - if (op == READ) - op = COPY; - inf = argv[0]; - if ((outp = open(argv[1], op == VERIFY ? O_RDONLY : - op == COPY ? O_WRONLY : O_RDWR, DEFFILEMODE)) < 0) - err(3, "%s", argv[1]); - break; - default: - usage(); - } - - if ((inp = open(inf, O_RDONLY, 0)) < 0) - err(1, "%s", inf); - - buff = getspace(maxblk); - - if (op == VERIFY) { - verify(inp, outp, buff); - exit(0); - } - - if ((oldsig = signal(SIGINT, SIG_IGN)) != SIG_IGN) - (void) signal(SIGINT, intr); - - needeof = 0; - for (lastnread = NOCOUNT;;) { - if ((nread = read(inp, buff, maxblk)) == -1) { - while (errno == EINVAL && (maxblk -= 1024)) { - nread = read(inp, buff, maxblk); - if (nread >= 0) - goto r1; - } - err(1, "read error, file %d, record %ju", filen, (intmax_t)record); - } else if (nread != lastnread) { - if (lastnread != 0 && lastnread != NOCOUNT) { - if (lastrec == 0 && nread == 0) - fprintf(msg, "%ju records\n", (intmax_t)record); - else if (record - lastrec > 1) - fprintf(msg, "records %ju to %ju\n", - (intmax_t)lastrec, (intmax_t)record); - else - fprintf(msg, "record %ju\n", (intmax_t)lastrec); - } - if (nread != 0) - fprintf(msg, "file %d: block size %d: ", - filen, nread); - (void) fflush(stdout); - lastrec = record; - } -r1: guesslen = 0; - if (nread > 0) { - if (op == COPY || op == COPYVERIFY) { - if (needeof) { - writeop(outp, MTWEOF); - needeof = 0; - } - nw = write(outp, buff, nread); - if (nw != nread) { - if (nw == -1) { - warn("write error, file %d, record %ju", filen, - (intmax_t)record); - } else { - warnx("write error, file %d, record %ju", filen, - (intmax_t)record); - warnx("write (%d) != read (%d)", nw, nread); - } - errx(5, "copy aborted"); - } - } - size += nread; - record++; - } else { - if (lastnread <= 0 && lastnread != NOCOUNT) { - fprintf(msg, "eot\n"); - break; - } - fprintf(msg, - "file %d: eof after %ju records: %ju bytes\n", - filen, (intmax_t)record, (intmax_t)size); - needeof = 1; - filen++; - tsize += size; - size = record = lastrec = 0; - lastnread = 0; - } - lastnread = nread; - } - fprintf(msg, "total length: %ju bytes\n", (intmax_t)tsize); - (void)signal(SIGINT, oldsig); - if (op == COPY || op == COPYVERIFY) { - writeop(outp, MTWEOF); - writeop(outp, MTWEOF); - if (op == COPYVERIFY) { - rewind_tape(outp); - rewind_tape(inp); - verify(inp, outp, buff); - } - } - exit(0); -} - -static void -verify(int inp, int outp, char *outb) -{ - int eot, inmaxblk, inn, outmaxblk, outn; - char *inb; - - inb = getspace(maxblk); - inmaxblk = outmaxblk = maxblk; - for (eot = 0;; guesslen = 0) { - if ((inn = read(inp, inb, inmaxblk)) == -1) { - if (guesslen) - while (errno == EINVAL && (inmaxblk -= 1024)) { - inn = read(inp, inb, inmaxblk); - if (inn >= 0) - goto r1; - } - warn("read error"); - break; - } -r1: if ((outn = read(outp, outb, outmaxblk)) == -1) { - if (guesslen) - while (errno == EINVAL && (outmaxblk -= 1024)) { - outn = read(outp, outb, outmaxblk); - if (outn >= 0) - goto r2; - } - warn("read error"); - break; - } -r2: if (inn != outn) { - fprintf(msg, - "%s: tapes have different block sizes; %d != %d.\n", - "tcopy", inn, outn); - break; - } - if (!inn) { - if (eot++) { - fprintf(msg, "tcopy: tapes are identical.\n"); - free(inb); - return; - } - } else { - if (bcmp(inb, outb, inn)) { - fprintf(msg, - "tcopy: tapes have different data.\n"); - break; - } - eot = 0; - } - } - exit(1); -} - -static void -intr(int signo __unused) -{ - if (record) { - if (record - lastrec > 1) - fprintf(msg, "records %ju to %ju\n", (intmax_t)lastrec, (intmax_t)record); - else - fprintf(msg, "record %ju\n", (intmax_t)lastrec); - } - fprintf(msg, "interrupt at file %d: record %ju\n", filen, (intmax_t)record); - fprintf(msg, "total length: %ju bytes\n", (uintmax_t)(tsize + size)); - exit(1); -} - -static void * -getspace(int blk) -{ - void *bp; - - if ((bp = malloc((size_t)blk)) == NULL) - errx(11, "no memory"); - return (bp); -} - -static void -writeop(int fd, int type) -{ - struct mtop op; - - op.mt_op = type; - op.mt_count = (daddr_t)1; - if (ioctl(fd, MTIOCTOP, (char *)&op) < 0) - err(6, "tape op"); -} - -static void -usage(void) -{ - fprintf(stderr, "usage: tcopy [-cvx] [-s maxblk] [src [dest]]\n"); - exit(1); -} - -static void -rewind_tape(int fd) -{ - struct stat sp; - - if(fstat(fd, &sp)) - errx(12, "fstat in rewind"); - - /* - * don't want to do tape ioctl on regular files: - */ - if( S_ISREG(sp.st_mode) ) { - if( lseek(fd, 0, SEEK_SET) == -1 ) - errx(13, "lseek"); - } else - /* assume its a tape */ - writeop(fd, MTREW); -} diff --git a/usr.bin/tcopy/tcopy.cc b/usr.bin/tcopy/tcopy.cc new file mode 100644 index 000000000000..640344758402 --- /dev/null +++ b/usr.bin/tcopy/tcopy.cc @@ -0,0 +1,837 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2025 Poul-Henning Kamp, <p...@freebsd.org> + * Copyright (c) 1985, 1987, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <assert.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <paths.h> +#include <signal.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <sysexits.h> +#include <unistd.h> + +#include <sys/endian.h> +#include <sys/mtio.h> +#include <sys/stat.h> +#include <sys/sysctl.h> +#include <sys/uio.h> + +#include <libutil.h> + +#define MAXREC (1024 * 1024) +#define NOCOUNT (-2) + +enum operation {READ, VERIFY, COPY, COPYVERIFY}; + +// Stuff the tape_devs need to know about +static int filen; +static uint64_t record; + +//--------------------------------------------------------------------- + +class tape_dev { + size_t max_read_size; +public: + int fd; + char *name; + enum direction {SRC, DST} direction; + + tape_dev(int file_handle, const char *spec, bool destination); + + virtual ssize_t read_blk(void *dst, size_t len); + virtual ssize_t verify_blk(void *dst, size_t len, size_t expected); + virtual void write_blk(const void *src, size_t len); + virtual void file_mark(void); + virtual void rewind(void); +}; + +tape_dev::tape_dev(int file_handle, const char *spec, bool destination) +{ + assert(file_handle >= 0); + fd = file_handle; + name = strdup(spec); + assert(name != NULL); + direction = destination ? DST : SRC; + max_read_size = 0; +} + +ssize_t +tape_dev::read_blk(void *dst, size_t len) +{ + ssize_t retval = -1; + + if (max_read_size == 0) { + max_read_size = len; + while (max_read_size > 0) { + retval = read(fd, dst, max_read_size); + if (retval >= 0 || (errno != EINVAL && errno != EFBIG)) + break; + if (max_read_size < 512) + errx(1, "Cannot find a sane max blocksize"); + + // Reduce to next lower power of two + int i = flsl((long)max_read_size - 1L); + max_read_size = 1UL << (i - 1); + } + } else { + retval = read(fd, dst, (size_t)max_read_size); + } + if (retval < 0) { + err(1, "read error, %s, file %d, record %ju", + name, filen, (uintmax_t)record); + } + return (retval); +} + +ssize_t +tape_dev::verify_blk(void *dst, size_t len, size_t expected) +{ + (void)expected; + return read_blk(dst, len); +} + +void +tape_dev::write_blk(const void *src, size_t len) +{ + assert(len > 0); + ssize_t nwrite = write(fd, src, len); + if (nwrite < 0 || (size_t) nwrite != len) { + if (nwrite == -1) { + warn("write error, file %d, record %ju", + filen, (intmax_t)record); + } else { + warnx("write error, file %d, record %ju", + filen, (intmax_t)record); + warnx("write (%zd) != read (%zd)", nwrite, len); + } + errx(5, "copy aborted"); + } + return; +} + +void +tape_dev::file_mark(void) +{ + struct mtop op; + + op.mt_op = MTWEOF; + op.mt_count = (daddr_t)1; + if (ioctl(fd, MTIOCTOP, (char *)&op) < 0) + err(6, "tape op (write file mark)"); +} + +void +tape_dev::rewind(void) +{ + struct mtop op; + + op.mt_op = MTREW; + op.mt_count = (daddr_t)1; + if (ioctl(fd, MTIOCTOP, (char *)&op) < 0) + err(6, "tape op (rewind)"); +} + +//--------------------------------------------------------------------- + +class tap_file: public tape_dev { +public: + tap_file(int file_handle, const char *spec, bool dst) : + tape_dev(file_handle, spec, dst) {}; + ssize_t read_blk(void *dst, size_t len); + void write_blk(const void *src, size_t len); + void file_mark(void); + virtual void rewind(void); +}; + +static +ssize_t full_read(int fd, void *dst, size_t len) +{ + // Input may be a socket which returns partial reads + + ssize_t retval = read(fd, dst, len); + if (retval <= 0 || (size_t)retval == len) + return (retval); + + char *ptr = (char *)dst + retval; + size_t left = len - (size_t)retval; + while (left > 0) { + retval = read(fd, ptr, left); + if (retval <= 0) + return (retval); + left -= (size_t)retval; + ptr += retval; + } + return ((ssize_t)len); +} + +ssize_t +tap_file::read_blk(void *dst, size_t len) +{ + char lbuf[4]; + + ssize_t nread = full_read(fd, lbuf, sizeof lbuf); + if (nread == 0) + return (0); + + if ((size_t)nread != sizeof lbuf) + err(EX_DATAERR, "Corrupt tap-file, read hdr1=%zd", nread); + + uint32_t u = le32dec(lbuf); + if (u == 0 || (u >> 24) == 0xff) + return(0); + + if (u > len) + err(17, "tapfile blocksize too big, 0x%08x", u); + + size_t alen = (u + 1) & ~1; + assert (alen <= len); + + ssize_t retval = full_read(fd, dst, alen); + if (retval < 0 || (size_t)retval != alen) + err(EX_DATAERR, "Corrupt tap-file, read data=%zd", retval); + + nread = full_read(fd, lbuf, sizeof lbuf); + if ((size_t)nread != sizeof lbuf) + err(EX_DATAERR, "Corrupt tap-file, read hdr2=%zd", nread); + + uint32_t v = le32dec(lbuf); + if (u == v) + return (u); + err(EX_DATAERR, + "Corrupt tap-file, headers differ (0x%08x != 0x%08x)", u, v); +} + +void +tap_file::write_blk(const void *src, size_t len) +{ + struct iovec iov[4]; + uint8_t zero = 0; + int niov = 0; + size_t expect = 0; + char tbuf[4]; + + assert((len & ~0xffffffffULL) == 0); + le32enc(tbuf, (uint32_t)len); + + iov[niov].iov_base = tbuf; + iov[niov].iov_len = sizeof tbuf; + expect += iov[niov].iov_len; + niov += 1; + + iov[niov].iov_base = (void*)(uintptr_t)src; + iov[niov].iov_len = len; + expect += iov[niov].iov_len; + niov += 1; + + if (len & 1) { + iov[niov].iov_base = &zero; + iov[niov].iov_len = 1; + expect += iov[niov].iov_len; + niov += 1; + } + + iov[niov].iov_base = tbuf; + iov[niov].iov_len = sizeof tbuf; + expect += iov[niov].iov_len; + niov += 1; + + ssize_t nwrite = writev(fd, iov, niov); + if (nwrite < 0 || (size_t)nwrite != expect) + errx(17, "write error (%zd != %zd)", nwrite, expect); +} + +void +tap_file::file_mark(void) +{ + char tbuf[4]; + le32enc(tbuf, 0); + ssize_t nwrite = write(fd, tbuf, sizeof tbuf); + if ((size_t)nwrite != sizeof tbuf) + errx(17, "write error (%zd != %zd)", nwrite, sizeof tbuf); +} + +void +tap_file::rewind(void) +{ + off_t where; + if (direction == DST) { + char tbuf[4]; + le32enc(tbuf, 0xffffffff); + ssize_t nwrite = write(fd, tbuf, sizeof tbuf); + if ((size_t)nwrite != sizeof tbuf) + errx(17, + "write error (%zd != %zd)", nwrite, sizeof tbuf); + } + where = lseek(fd, 0L, SEEK_SET); + if (where != 0 && errno == ESPIPE) + err(EX_USAGE, "Cannot rewind sockets and pipes"); + if (where != 0) + err(17, "lseek(0) failed"); +} + +//--------------------------------------------------------------------- + +class file_set: public tape_dev { +public: + file_set(int file_handle, const char *spec, bool dst) : + tape_dev(file_handle, spec, dst) {}; + ssize_t read_blk(void *dst, size_t len); + ssize_t verify_blk(void *dst, size_t len, size_t expected); + void write_blk(const void *src, size_t len); + void file_mark(void); + void rewind(void); + void open_next(bool increment); +}; + +void +file_set::open_next(bool increment) +{ + if (fd >= 0) { + assert(close(fd) >= 0); + fd = -1; + } + if (increment) { + char *p = strchr(name, '\0') - 3; + if (++p[2] == '9') { + p[2] = '0'; + if (++p[1] == '9') { + p[1] = '0'; + if (++p[0] == '9') { + errx(EX_USAGE, + "file-set sequence overflow"); + } + } + } + } + if (direction == DST) { + fd = open(name, O_RDWR|O_CREAT, DEFFILEMODE); + if (fd < 0) + err(1, "Could not open %s", name); + } else { + fd = open(name, O_RDONLY, 0); + } +} *** 493 LINES SKIPPED ***