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 ***

Reply via email to