Page MenuHomeFreeBSD

D51442.id.diff
No OneTemporary

D51442.id.diff

diff --git a/usr.bin/tcopy/tcopy.1 b/usr.bin/tcopy/tcopy.1
--- a/usr.bin/tcopy/tcopy.1
+++ b/usr.bin/tcopy/tcopy.1
@@ -69,6 +69,9 @@
to
.Ar dest
and then verify that the two tapes are identical.
+Both tapes will be rewound first.
+.It Fl t
+Treat non device files as SIMH-TAPFILE data streams.
.It Fl s Ar maxblk
Specify a maximum block size,
.Ar maxblk .
@@ -86,9 +89,38 @@
is given as
.Pa /dev/stdout .
.El
+.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 -t /dev/sa0 /tmp/temp.tapfile
+(change the tape)
+$ tcopy -c -t /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 -t /dev/sa1 - | sha256
+.Ed
.Sh SEE ALSO
.Xr mt 1 ,
+.Xr sa 4 ,
.Xr mtio 4
+.Sh STANDARDS
+The SIMH-TAPFILE format is documented in the SIMH github repos:
+.Pa https://github.com/simh/simh/blob/master/doc/simh_magtape.doc
.Sh HISTORY
The
.Nm
@@ -107,10 +139,16 @@
$ 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.
+Copying tape to a file, pipe or socket, without
+.Fl t
+only copies the raw data, all information about block size(s)
+and tape EOF marks is lost.
+.It
+With
+.Fl t
+files, pipes, sockets etc, will be treated as a
+SIMH-TAPFILE data stream, which retain the information
+about block size(s) and tape EOF marks.
.It
End of data (EOD) is determined by two sequential EOF marks
with no data between them.
@@ -119,14 +157,4 @@
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.
.El
diff --git a/usr.bin/tcopy/tcopy.c b/usr.bin/tcopy/tcopy.c
--- a/usr.bin/tcopy/tcopy.c
+++ b/usr.bin/tcopy/tcopy.c
@@ -33,7 +33,9 @@
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/mtio.h>
+#include <sys/endian.h>
+#include <assert.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
@@ -50,37 +52,289 @@
#define MAXREC (64 * 1024)
#define NOCOUNT (-2)
-static int filen, guesslen, maxblk = MAXREC;
+struct tapedev {
+ int fd;
+ const char *filename;
+ int is_reg;
+ ssize_t read_size;
+};
+
+static int filen, tapfile;
+static ssize_t maxblk = MAXREC;
static uint64_t lastrec, record, size, tsize;
static FILE *msg;
+static struct tapedev in_dev = {-1, NULL, 0, -1};
+static struct tapedev out_dev = {-1, NULL, 0, -1};
+static ssize_t lastnread;
+
+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(ssize_t blk)
+{
+ void *bp;
+
+ assert(blk > 0);
+ if ((bp = malloc((size_t)blk)) == NULL)
+ errx(11, "no memory");
+ return (bp);
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: tcopy [-cvx] [-s maxblk] [src [dest]]\n");
+ exit(1);
+}
+
+static void
+tape_dev_open(struct tapedev *td, const char *filename, int mode, int diag)
+{
+ td->fd = open(filename, mode, DEFFILEMODE);
+ if (td->fd < 0)
+ err(diag, "%s", filename);
+ td->filename = filename;
+
+ struct stat sp;
+
+ if(fstat(td->fd, &sp))
+ errx(12, "fstat on %s", filename);
+ td->is_reg = !S_ISCHR(sp.st_mode);
+}
+
+static ssize_t
+tape_dev_read(struct tapedev *td, void *buffer)
+{
+ ssize_t nread;
+
+ if (!tapfile || !td->is_reg) {
+ if (td->read_size < 0) {
+ td->read_size = maxblk;
+ while (td->read_size > 0) {
+ nread = read(td->fd, buffer, td->read_size);
+ if (nread >= 0 || errno != EINVAL)
+ break;
+ if (td->read_size > (128<<10))
+ td->read_size >>= 1;
+ else
+ td->read_size -= 1024;
+ }
+ } else {
+ nread = read(td->fd, buffer, td->read_size);
+ }
+ if (nread < 0)
+ err(1, "read error, %s, file %d, record %ju",
+ td->filename, filen, (uintmax_t)record);
+ return (nread);
+ }
+
+ char tbuf[4];
+
+ nread = read(td->fd, tbuf, 4);
+ if (nread < 0)
+ err(1, "read error, %s, file %d, record %ju",
+ td->filename, filen, (uintmax_t)record);
+ if (nread == 0)
+ return(nread);
+
+ uint32_t u = le32dec(tbuf);
+ if (u == 0 || (u >> 24) == 0xff)
+ return(0);
+
+ if (u > maxblk)
+ err(17, "tapfile blocksize too big, 0x%08x", u);
+
+ size_t alen = (u + 1) & ~1;
+
+ nread = read(td->fd, buffer, alen);
+ if (nread < 0)
+ err(1, "read error, %s, file %d, record %ju",
+ td->filename, filen, (uintmax_t)record);
+
+ nread = read(td->fd, tbuf, 4);
+ if (nread < 0)
+ err(1, "read error, %s, file %d, record %ju",
+ td->filename, filen, (uintmax_t)record);
+ if (nread == 0)
+ err(17, "short tapfile, %s, file %d, record %ju",
+ td->filename, filen, (uintmax_t)record);
+
+ uint32_t v = le32dec(tbuf);
+ if (u != v)
+ err(17, "bad tapfile, %s, file %d, record %ju",
+ td->filename, filen, (uintmax_t)record);
+ return (u);
+}
+
+static void
+tape_dev_write(const struct tapedev *td, char *buffer, ssize_t len)
+{
+ ssize_t nwrite;
+
+ assert(len > 0);
+ if (!tapfile || !td->is_reg) {
+ nwrite = write(td->fd, buffer, len);
+ if (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;
+ }
+
+ char tbuf[4];
+ le32enc(tbuf, len);
+ nwrite = write(td->fd, tbuf, 4);
+ if (nwrite != 4)
+ errx(17, "write error");
+ if (len & 1)
+ buffer[len] = 0x00;
+ size_t alen = (len + 1) & ~1;
+ nwrite = write(td->fd, buffer, alen);
+ if (nwrite < 0 || nwrite != (ssize_t)alen)
+ errx(17, "write error");
+ nwrite = write(td->fd, tbuf, 4);
+ if (nwrite != 4)
+ errx(17, "write error");
+}
+
+static void
+tape_dev_op(const struct tapedev *td, int what)
+{
+ if (td->is_reg) {
+ if (what == MTREW) {
+ if(lseek(td->fd, 0, SEEK_SET) == -1)
+ errx(13, "lseek");
+ } else if (what == MTWEOF && tapfile) {
+ char tbuf[4];
+ ssize_t nwrite;
+ le32enc(tbuf, 0);
+ nwrite = write(td->fd, tbuf, 4);
+ if (nwrite != 4)
+ errx(17, "write error");
+ }
+ return;
+ }
+
+ struct mtop op;
+
+ op.mt_op = what;
+ op.mt_count = (daddr_t)1;
+ if (ioctl(td->fd, MTIOCTOP, (char *)&op) < 0)
+ err(6, "tape op");
+}
+
+static void
+progress(ssize_t nread)
+{
+ if (nread != lastnread) {
+ if (lastnread != 0 && lastnread != NOCOUNT) {
+ if (lastrec == 0 && nread == 0)
+ fprintf(msg, "%ju records\n",
+ (uintmax_t)record);
+ else if (record - lastrec > 1)
+ fprintf(msg, "records %ju to %ju\n",
+ (uintmax_t)lastrec,
+ (uintmax_t)record);
+ else
+ fprintf(msg, "record %ju\n",
+ (uintmax_t)lastrec);
+ }
+ if (nread != 0)
+ fprintf(msg,
+ "file %d: block size %zd: ", filen, nread);
+ (void) fflush(msg);
+ lastrec = record;
+ }
+ if (nread > 0) {
+ size += nread;
+ record++;
+ } else {
+ if (lastnread <= 0 && lastnread != NOCOUNT) {
+ fprintf(msg, "eot\n");
+ return;
+ }
+ fprintf(msg,
+ "file %d: eof after %ju records: %ju bytes\n",
+ filen, (uintmax_t)record, (uintmax_t)size);
+ filen++;
+ tsize += size;
+ size = record = lastrec = 0;
+ lastnread = 0;
+ }
+ lastnread = nread;
+}
+
+static void
+verify(struct tapedev *in1, struct tapedev *in2)
+{
+ int eot = 0;
+ ssize_t nread1, nread2;
+
+ char *inb1 = getspace(maxblk);
+ char *inb2 = getspace(maxblk);
+ while (1) {
+ nread1 = tape_dev_read(in1, inb1);
+ nread2 = tape_dev_read(in2, inb2);
+ progress(nread1);
+ if (nread1 != nread2) {
+ fprintf(msg,
+ "tcopy: tapes have different block sizes; "
+ "%zd != %zd.\n", nread1, nread2);
+ exit(1);
+ }
+ if (nread1 > 0 && memcmp(inb1, inb2, nread1)) {
+ fprintf(msg,
+ "tcopy: tapes have different data.\n");
+ exit(1);
+ } else if (nread1 > 0) {
+ eot = 0;
+ } else if (eot++) {
+ fprintf(msg, "tcopy: tapes are identical.\n");
+ free(inb1);
+ free(inb2);
+ return;
+ }
+ }
+}
-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;
+ ssize_t nread, prev_read;
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)
+ while ((ch = getopt(argc, argv, "cs:tvx")) != -1)
switch((char)ch) {
case 'c':
op = COPYVERIFY;
@@ -95,7 +349,9 @@
warnx("illegal block size");
usage();
}
- guesslen = 0;
+ break;
+ case 't':
+ tapfile = 1;
break;
case 'v':
op = VERIFY;
@@ -110,229 +366,95 @@
argc -= optind;
argv += optind;
+
+ int mode;
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)
+ switch (op) {
+ case VERIFY:
+ mode = O_RDONLY;
+ break;
+ case 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]);
+ /* FALL_THROUGH */
+ case COPY:
+ mode = O_WRONLY|O_CREAT;
+ break;
+ default:
+ mode = O_RDWR|O_CREAT;
+ break;
+ }
+ if (!strcmp(argv[1], "-")) {
+ tape_dev_open(&out_dev, "/dev/stdout", mode, 3);
+ msg = stderr;
+ } else {
+ tape_dev_open(&out_dev, argv[1], mode, 3);
+ }
break;
default:
usage();
}
- if ((inp = open(inf, O_RDONLY, 0)) < 0)
- err(1, "%s", inf);
+ if (argc == 0) {
+ tape_dev_open(&in_dev, _PATH_DEFTAPE, O_RDONLY, 1);
+ } else if (!strcmp(argv[0], "-")) {
+ tape_dev_open(&out_dev, "/dev/stdin", mode, 3);
+ } else {
+ tape_dev_open(&in_dev, argv[0], O_RDONLY, 1);
+ }
- buff = getspace(maxblk);
+ char *buff = getspace(maxblk);
- if (op == VERIFY) {
- verify(inp, outp, buff);
+ if (op == VERIFY && out_dev.fd > 0) {
+ verify(&in_dev, &out_dev);
exit(0);
}
if ((oldsig = signal(SIGINT, SIG_IGN)) != SIG_IGN)
(void) signal(SIGINT, intr);
+ if (op == COPYVERIFY) {
+ tape_dev_op(&out_dev, MTREW);
+ tape_dev_op(&in_dev, MTREW);
+ }
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;
+ for (prev_read = NOCOUNT;;) {
+ nread = tape_dev_read(&in_dev, buff);
+ progress(nread);
if (nread > 0) {
if (op == COPY || op == COPYVERIFY) {
if (needeof) {
- writeop(outp, MTWEOF);
+ tape_dev_op(&out_dev, 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");
- }
+ tape_dev_write(&out_dev, buff, nread);
}
- size += nread;
- record++;
} else {
- if (lastnread <= 0 && lastnread != NOCOUNT) {
- fprintf(msg, "eot\n");
+ if (prev_read <= 0 && prev_read != NOCOUNT) {
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;
+ prev_read = 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);
- }
+ tape_dev_op(&out_dev, MTWEOF);
+ tape_dev_op(&out_dev, MTWEOF);
}
- 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;
- }
+ if (op == COPYVERIFY) {
+ tape_dev_op(&out_dev, MTREW);
+ tape_dev_op(&in_dev, MTREW);
+ verify(&in_dev, &out_dev);
}
- 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);
+ (void)signal(SIGINT, oldsig);
+ exit(0);
}

File Metadata

Mime Type
text/plain
Expires
Fri, Jan 23, 9:00 AM (14 h, 21 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
27881687
Default Alt Text
D51442.id.diff (15 KB)

Event Timeline