Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F142697404
D51442.id.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
15 KB
Referenced Files
None
Subscribers
None
D51442.id.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D51442: Refactor tcopy(1) and teach it about SIMH-TAPFILE format
Attached
Detach File
Event Timeline
Log In to Comment