From cfeb0e42da92f6d22d113c5f3d590325efef1ef5 Mon Sep 17 00:00:00 2001 From: John Forecast Date: Wed, 15 May 2019 09:15:18 -0700 Subject: [PATCH] FDIO: Various improvements --- converters/fsio/Makefile | 4 +- converters/fsio/fsio-rt11.1 | 24 +++- converters/fsio/fsio.1 | 6 +- converters/fsio/fsio.c | 4 +- converters/fsio/fsio.txt | 4 +- converters/fsio/fsioSimh.txt | 4 - converters/fsio/rt11.c | 237 ++++++++++++++++++++++++++++------- converters/fsio/rt11.h | 13 +- 8 files changed, 235 insertions(+), 61 deletions(-) diff --git a/converters/fsio/Makefile b/converters/fsio/Makefile index 8e3671a..f8c6de7 100644 --- a/converters/fsio/Makefile +++ b/converters/fsio/Makefile @@ -1,5 +1,5 @@ -#CFLAGS=-O2 -Wall -Wshadow -Wextra -pedantic -Woverflow -Wstrict-overflow -CFLAGS=-g -Wall -Wshadow -Wextra -pedantic -Woverflow -Wstrict-overflow +CFLAGS=-O2 -Wall -Wshadow -Wextra -pedantic -Woverflow -Wstrict-overflow +#CFLAGS=-g -Wall -Wshadow -Wextra -pedantic -Woverflow -Wstrict-overflow DEFINES=-DDEBUG BIN=/usr/local/bin MAN=/usr/local/man/man1 diff --git a/converters/fsio/fsio-rt11.1 b/converters/fsio/fsio-rt11.1 index 3964ad1..3f521da 100644 --- a/converters/fsio/fsio-rt11.1 +++ b/converters/fsio/fsio-rt11.1 @@ -1,4 +1,4 @@ -.TH FSIO-RT11 1 "December 28,2018" "FFS I/O - RT-11" +.TH FSIO-RT11 1 "May 15,2019" "FFS I/O - RT-11" .SH NAME fsio-rt11 \- Foreign File System I/O - RT-11 .br @@ -6,6 +6,26 @@ fsio-rt11 \- Foreign File System I/O - RT-11 \fBfsio\fP allows access to RT-11 file systems using the file system type "\fIrt11\fP" .br +.SH RT-11 VERSIONS +There were 5 major versions of RT-11 released. Versions 1 and 2 did not use +the home block (block 1) to indicate that the on-disk file structure was RT-11 +format. The remaining 3 versions wrote a signature on the home block +including the file system version in use. In order to access a version 1 or +version 2 container file, the \fI"-f"\fP switch must be used on the +\fI"mount"\fP command to force \fBfsio\fP +to bypass the home block checks. While \fBfsio\fP will use a set of +heuristics to verify the integrity of the RT-11 file structure it is possible +that an invalid file structure will satisfy the checks and cause \fBfsio\fP to +fail. Do not use the \fI"-f"\fP switch other than to mount version 1 or +version 2 file systems. + +.br +The VMS exchange utility may be used to initialize an RT-11 volume but writes +it's own unique signature in the home block. \fBfsio\fP is able to correctly +handle this signature. +.SH LOGICAL DISKS +\fBfsio\fP can be used to access an RT-11 logical disk if the file has been +copied to the host file system. .SH WILDCARD CHARACTERS The wildcard character \fI'%'\fP may be used to match a single character in a filename or type. The wildcard character \fI'*'\fP may be used to match @@ -18,12 +38,14 @@ partition which can be accessed via the DU device (rq in SIMH). If the \fI"-t type"\fP switch is present, a smaller container file will be created depending on the type of device specified: .br +.RS .TP \fIrl02\fP \- RL02 image (10MB, 20480 blocks) .br .TP \fIrx20\fP \- RX20 image (512KB, 1024 blocks) .br +.RE .SH COPY OPERATION When copying in ASCII mode, \fBfsio\fP will normally write ^Z (octal 032) to indicate the end-of-file. Some utilities, such as FLX on RSX-11, cannot diff --git a/converters/fsio/fsio.1 b/converters/fsio/fsio.1 index f9b5fd3..bd30b79 100644 --- a/converters/fsio/fsio.1 +++ b/converters/fsio/fsio.1 @@ -1,4 +1,4 @@ -.TH FSIO 1 "December 25,2018" "Foreign File System I/O" +.TH FSIO 1 "May 15,2019" "Foreign File System I/O" .SH NAME fsio \- Foreign File System I/O .SH SYNOPSIS @@ -76,7 +76,7 @@ The following commands are only accepted by file systems which are on magtape de .br .SH COMMANDS .TP -.B "\fImount\fP [-drx] dev[:] file type" +.B "\fImount\fP [-dfrx] dev[:] file type" Make the container file available to fsio. .br .RS @@ -89,6 +89,8 @@ Make the container file available to fsio. .br .B " Only available if built with DEBUG enabled" .br +.B "\fI\-f\fP \- bypass home block validation (RT-11 only)" +.br .B "\fI\-r\fP \- mount file system read-only" .br .B "\fI\-x\fP \- dosmt will use extended filenames when writing" diff --git a/converters/fsio/fsio.c b/converters/fsio/fsio.c index d04d61b..02302af 100644 --- a/converters/fsio/fsio.c +++ b/converters/fsio/fsio.c @@ -310,9 +310,9 @@ struct command { cmd_t func; /* Command execution function */ } cmdTable[] = { #ifdef DEBUG - { "mount", OPTIONS("drx"), 3, 3, 0, doMount }, + { "mount", OPTIONS("dfrx"), 3, 3, 0, doMount }, #else - { "mount", OPTIONS("rx"), 3, 3, 0, doMount }, + { "mount", OPTIONS("frx"), 3, 3, 0, doMount }, #endif { "umount", NULL, 1, 1, 0, doUmount }, { "newfs", OPTIONS("t:"), 2, 2, 0, doNewfs }, diff --git a/converters/fsio/fsio.txt b/converters/fsio/fsio.txt index 7a2ad8b..983dfaa 100644 --- a/converters/fsio/fsio.txt +++ b/converters/fsio/fsio.txt @@ -87,12 +87,14 @@ Full command syntax: 1. mount - mount [-dr] dev[:] container type + mount [-dfr] dev[:] container type Make the specified container file available to fsio for I/O. -d Generate debug output if fsio is built with the DEBUG symbol defined + -f Force the mount to happen even if we are unable to completely + validate the container file format -r If present, the file system is only available for read access dev[:] User supplied name to be used for accessing files within the diff --git a/converters/fsio/fsioSimh.txt b/converters/fsio/fsioSimh.txt index e7648f8..1fdc5f4 100644 --- a/converters/fsio/fsioSimh.txt +++ b/converters/fsio/fsioSimh.txt @@ -14,10 +14,6 @@ typically RT-11. Note: all of these examples assume that we are running on a Linux or Unix-like operating system (in my case Raspbian on a Raspberry Pi). -WARNING: fsio is alpha-level code. Make sure you make a back-up copy of any -important file system before mounting it read/write. - - Native File System Support -------------------------- diff --git a/converters/fsio/rt11.c b/converters/fsio/rt11.c index c2b8c8e..fefe73f 100644 --- a/converters/fsio/rt11.c +++ b/converters/fsio/rt11.c @@ -474,7 +474,7 @@ int rt11ReadDirSegment( ) { struct RT11data *data = &mount->rt11data; - unsigned int block = RT11_DSSTART + ((segment - 1) * 2); + unsigned int block = data->first[unit] + ((segment - 1) * 2); if (rt11ReadBlock(mount, unit, block, &data->buf[0]) != 0) if (rt11ReadBlock(mount, unit, block + 1, &data->buf[256]) != 0) @@ -510,7 +510,7 @@ int rt11WriteDirSegment( ) { struct RT11data *data = &mount->rt11data; - unsigned int block = RT11_DSSTART + ((segment - 1) * 2); + unsigned int block = data->first[unit] + ((segment - 1) * 2); if (rt11WriteBlock(mount, unit, block, &data->buf[0]) != 0) if (rt11WriteBlock(mount, unit, block + 1, &data->buf[256]) != 0) @@ -1353,10 +1353,10 @@ static void info( } /*++ - * v a l i d a t e + * p a r t i t i o n T y p e * - * Verify that a partition contains a valid RT-11 filesystem. First verify - * that the checksum is correct and then that the ID fields are correct. + * Determine the type of the partition whose home block is in the mount + * point specific buffer. * * Inputs: * @@ -1365,39 +1365,156 @@ static void info( * * Outputs: * - * The mount point specific buffer will be overwritten. + * ... * * Returns: * - * 1 if file system is valid, 0 otherwise + * partition type: + * + * RT11_NOPART - Not a valid RT11 partition + * RT11_SINGLE - 1 partition supported per disk + * RT11_MULTI - Multiple partitions supported per disk * --*/ -static int validate( +static int partitionType( struct mountedFS *mount, uint8_t unit ) { struct RT11data *data = &mount->rt11data; - uint16_t i, checksum = 0; - if (rt11ReadBlock(mount, unit, RT11_HOME, NULL) == 0) - return 0; + if (strncmp((char *)&data->buf[RT11_HB_SYSID], RT11_SYSID, strlen(RT11_SYSID)) == 0) { + uint16_t type = le16toh(data->buf[RT11_HB_SYSVER]); - /* - * Compute the checksum over the Home block. - */ - for (i = 0; i < 255; i++) - checksum += le16toh(data->buf[i]); + if (unit == 0) { + if ((type == RT11_SYSVER_V3A) || (type == RT11_SYSVER_V04)) + return RT11_SINGLE; + } - if (checksum == le16toh(data->buf[255])) { - if ((le16toh(data->buf[RT11_HB_PCS]) == 1) && - (le16toh(data->buf[RT11_HB_FIRST]) == RT11_DSSTART) && - ((strncmp((char *)&data->buf[RT11_HB_SYSID], - RT11_SYSID, strlen(RT11_SYSID))) == 0)) - return 1; + if (type == RT11_SYSVER_V05) + return RT11_MULTI; } - return 0; + if (strncmp((char *)&data->buf[RT11_HB_SYSID], RT11_VMSSYSID, strlen(RT11_VMSSYSID)) == 0) + return RT11_SINGLE; + + return RT11_NOPART; +} + +/*++ + * v a l i d a t e + * + * Verify that a partition contains a valid RT-11 filesystem. First verify + * that the ID fields are correct, then walk the directory structure to + * make sure that we are looking at a valid RT-11 partition. + * + * Inputs: + * + * mount - pointer to a mounted file system descriptor + * unit - partition number + * blocks - return the actual size of the partition here + * first - return first directory segment block # here + * + * Outputs: + * + * The mount point specific buffer will be overwritten. + * + * Returns: + * + * partition type: + * + * RT11_NOPART - Not a valid RT11 partition + * RT11_SINGLE - 1 partition supported per disk + * RT11_MULTI - Multiple partitions supported per disk + * + --*/ +static int validate( + struct mountedFS *mount, + uint8_t unit, + uint16_t *blocks, + uint16_t *first +) +{ + struct RT11data *data = &mount->rt11data; + int type = RT11_SINGLE; + uint16_t entrysz, position, dsseg = 1; + uint16_t seg_count, seg_highest, highest = 0; + uint8_t seen[RT11_DS_MAX + 1]; + + if (rt11ReadBlock(mount, unit, RT11_HOME, NULL) == 0) + return RT11_NOPART; + + if (!SWISSET('f')) { + if ((type = partitionType(mount, unit)) == RT11_NOPART) + return RT11_NOPART; + + *first = le16toh(data->buf[RT11_HB_FIRST]); + } else *first = RT11_DSSTART; + + memset(seen, 0, sizeof(seen)); + + /* + * Now walk the directory to validate it's integrity. + */ + do { + uint16_t off = RT11_DH_SIZE; + + if (rt11ReadDirSegment(mount, unit, dsseg) == 0) + return RT11_NOPART; + + /* + * Make sure we only look at each directory segment once - we want to + * avoid looping up with invalid input. + */ + if (seen[dsseg]++ != 0) + return RT11_NOPART; + + entrysz = RT11_DI_SIZE + (le16toh(data->buf[RT11_DH_EXTRA]) >> 1); + position = le16toh(data->buf[RT11_DH_START]); + + if (dsseg == 1) { + seg_count = le16toh(data->buf[RT11_DH_COUNT]); + seg_highest = le16toh(data->buf[RT11_DH_HIGHEST]); + if ((seg_highest > RT11_DS_MAX) || (seg_highest > seg_count)) + return RT11_NOPART; + } + + if (seg_count != le16toh(data->buf[RT11_DH_COUNT])) + return RT11_NOPART; + + /* + * Loop until we see and end-of-segment marker or there is no room for + * another directory entry. + */ + while (!RT11EOS(le16toh(data->buf[off + RT11_DI_STATUS])) && + ((RT11_DS_SIZE - off) >= entrysz)) { + uint16_t status = le16toh(data->buf[off + RT11_DI_STATUS]); + + if ((status & RT11_E_MPTY) != 0) + break; + + /* + * Within each directory segment the base address should never + * decrease. + */ + if (((position + le16toh(data->buf[off + RT11_DI_LENGTH])) & 0xFFFF) < position) + return RT11_NOPART; + + position += le16toh(data->buf[off + RT11_DI_LENGTH]); + + off += entrysz; + } + if (position > highest) + highest = position; + + dsseg = le16toh(data->buf[RT11_DH_NEXT]); + + if (dsseg > seg_highest) + return RT11_NOPART; + } while (dsseg != 0); + + *blocks = highest; + return type; } /*++ @@ -1570,41 +1687,36 @@ static int rt11Mount( memset(&data->valid, 0, sizeof(data->valid)); if (fstat(fileno(mount->container), &stat) == 0) { - uint16_t i, count, lastsz, validcount; + uint16_t i, count, lastsz, validcount = 0;; data->blocks = stat.st_size / RT11_BLOCKSIZE; count = data->blocks / RT11_MAXPARTSZ; lastsz = data->blocks % RT11_MAXPARTSZ; - validcount = count; + if (lastsz >= RT11_MINPARTSZ) + count++; /* * Check each file system for validity. */ for (i = 0; i < count; i++) { + int type; + /* - * Assume valid + * Assume valid and maximal size */ data->valid[i / 16] |= 1 << (i % 16); data->maxblk[i] = RT11_MAXPARTSZ - 1; - if (validate(mount, i) == 0) { - data->valid[i / 16] &= ~(1 << (i % 16)); - validcount--; - } - } + type = validate(mount, i, &data->maxblk[i], &data->first[i]); - /* - * Check for a small partition to complete the disk - */ - if (lastsz >= RT11_MINPARTSZ) { - data->valid[count / 16] |= 1 << (count % 16); - data->maxblk[count] = lastsz; + if (type != RT11_NOPART) + validcount++; + else data->valid[i / 16] &= ~(1 << (i % 16)); - if (validate(mount, count) == 0) - data->valid[count / 16] &= ~(1 << (count % 16)); - else validcount++; + if (type == RT11_SINGLE) + break; } data->filesystems = validcount; @@ -1642,15 +1754,46 @@ static int rt11Mount( dsseg = le16toh(data->buf[RT11_DH_NEXT]); } while (dsseg != 0); - if (!quiet) - printf("%s%o:\n" - " Total blocks: %5d, Free blocks: %5d\n" + if (!quiet) { + char vers[4], *version = NULL; + + if (rt11ReadBlock(mount, i, RT11_HOME, NULL) == 0) + return 0; + + /* + * Special handling of volumes created by non-RT-11 systenms + * (e.g. VMS Exchange). + */ + if (strncmp((char *)&data->buf[RT11_HB_SYSID], RT11_VMSSYSID, strlen(RT11_VMSSYSID)) == 0) { + r50Asc(le16toh(data->buf[RT11_HB_SYSVER]), vers); + version = vers; + } else { + switch (le16toh(data->buf[RT11_HB_SYSVER])) { + case RT11_SYSVER_V3A: + version = "V3A"; + break; + + case RT11_SYSVER_V04: + version = "V04"; + break; + + case RT11_SYSVER_V05: + version = "V05"; + break; + } + } + + printf("%s%o:\n", mount->name, i); + if (version != NULL) + printf(" Version: %s, System ID: %12s\n", + version, (char *)&data->buf[RT11_HB_SYSID]); + printf(" Total blocks: %5d, Free blocks: %5d\n" " Directory segments: %2d (Highest %d)\n" " Extra bytes/directory entry: %d\n", - mount->name, i, data->maxblk[i] + 1, freeblks, - le16toh(data->buf[RT11_DH_COUNT]), - highest, + data->maxblk[i] + 1, freeblks, + le16toh(data->buf[RT11_DH_COUNT]), highest, le16toh(data->buf[RT11_DH_EXTRA])); + } validcount--; } return 1; @@ -1767,7 +1910,7 @@ static int rt11Newfs( data->buf[RT11_HB_PCS] = htole16(1); data->buf[RT11_HB_FIRST] = htole16(RT11_DSSTART); - data->buf[RT11_HB_SYSVER] = htole16(ascR50(RT11_SYSVER)); + data->buf[RT11_HB_SYSVER] = htole16(RT11_SYSVER_V05); strncpy((char *)&data->buf[RT11_HB_VOLID], RT11_VOLID, strlen(RT11_VOLID)); strncpy((char *)&data->buf[RT11_HB_OWNER], RT11_OWNER, strlen(RT11_OWNER)); strncpy((char *)&data->buf[RT11_HB_SYSID], RT11_SYSID, strlen(RT11_SYSID)); diff --git a/converters/fsio/rt11.h b/converters/fsio/rt11.h index c8c53a8..89528da 100644 --- a/converters/fsio/rt11.h +++ b/converters/fsio/rt11.h @@ -167,10 +167,18 @@ #define RT11_DSSTART 6 /* Start of directory segs */ #define RT11_BLOCKSIZE 512 /* Size of a data block on disk */ -#define RT11_SYSVER "V05" +#define RT11_SYSVER_V3A 36521 +#define RT11_SYSVER_V04 36434 +#define RT11_SYSVER_V05 36435 + +#define RT11_NOPART 0 /* Not a valid partition */ +#define RT11_SINGLE 1 /* Single partition per disk */ +#define RT11_MULTI 2 /* Multiple partitions per disk */ + #define RT11_VOLID "RT11A " #define RT11_OWNER " " #define RT11_SYSID "DECRT11A " +#define RT11_VMSSYSID "DECVMSEXCHNG" /* VMS exchange created volume */ /* * Partition sizes. The last block os a maximum sized partition is unused. @@ -178,7 +186,7 @@ * 1 data block! Is this reasonable? */ #define RT11_MAXPARTSZ 0200000 /* Max partition size */ -#define RT11_MINPARTSZ 0000011 /* Min partition size */ +#define RT11_MINPARTSZ 0000010 /* Min partition size */ #define RT11_RL02SZ 20480 /* Size of an RL02 drive */ #define RT11_RX20SZ 1024 /* Size of an RX20 floppy drive */ @@ -282,6 +290,7 @@ struct RT11data { uint16_t filesystems; /* Max # of filesystems */ uint16_t valid[16]; /* Valid partitions */ uint16_t maxblk[256]; /* Max block address */ + uint16_t first[256]; /* First directory block */ uint16_t buf[512]; /* Disk buffer - enough for a */ /* directory segment */ };