diff --git a/converters/fsio/Changes b/converters/fsio/Changes new file mode 100644 index 0000000..338a1f9 --- /dev/null +++ b/converters/fsio/Changes @@ -0,0 +1,35 @@ +Changes Since Alpha Release: + +1. mount: Fix memory leak when file system verification fails + +2. dos11: Fix hang when parsing wildcard programmer number + +3. Migrate to using GNU readline library for command line editing etc + +4. local: Fix buffering so that local "type" command works + +5. Add DOS-11 magtape support using file system type "dosmt" + +6. newfs: Remove -a switch. Replace with "-t type" switch to allow more + flexibility. The equivalent of "-a" is now "-t rl02". + +12-Jun-19 + +- Added support for sector interleave on RX01/RX02 drives + + - Now supports creating RX01-sized container files + - Now works with RTFLX on TOPS-10 with RX02-sized container files + (documentation updated) + +25-Jun-19 + +- Added support for OS/8 file systems on RX01, RX02 and RK05 drives using + file system type "os8" + +- Added support for creating RT-11 file systems with additional space + allocated to each directory entry + +9-Jan-20 + +- Merged change from tvrusso to fix compilation on FreeBSD + diff --git a/converters/fsio/Makefile b/converters/fsio/Makefile index f8c6de7..9fa9330 100644 --- a/converters/fsio/Makefile +++ b/converters/fsio/Makefile @@ -7,13 +7,14 @@ INSTALL=install CC=gcc EXECUTABLE=fsio -SOURCES=fsio.c declib.c tape.c dos11.c rt11.c dosmt.c local.c -INCLUDES=fsio.h declib.h tape.h dos11.h rt11.h dosmt.h +SOURCES=fsio.c declib.c tape.c dos11.c rt11.c dosmt.c local.c os8.c +INCLUDES=fsio.h declib.h tape.h dos11.h rt11.h dosmt.h os8.h LIBS=-lreadline MANPAGE=fsio.1 MANPAGE_DOS=fsio-dos11.1 MANPAGE_RT=fsio-rt11.1 MANPAGE_DOSMT=fsio-dosmt.1 +MANPAGE_OS8=fsio-os8.1 ARCHIVE=fsio.tgz RELEASEFILES=$(BIN)/$(EXECUTABLE) @@ -21,6 +22,7 @@ RELEASEFILES+=$(MAN)/$(MANPAGE) RELEASEFILES+=$(MAN)/$(MANPAGE_DOS) RELEASEFILES+=$(MAN)/$(MANPAGE_RT) RELEASEFILES+=$(MAN)/$(MANPAGE_DOSMT) +RELEASEFILES+=$(MAN)/$(MANPAGE_OS8) RELEASEFILES+=./fsio.txt ./fsioSimh.txt $(EXECUTABLE): $(SOURCES) $(INCLUDES) Makefile @@ -38,6 +40,7 @@ install: $(EXECUTABLE) $(MANPAGE) $(MANPAGE_DOS) $(MANPAGE_RT) $(INSTALL) -p -m u=r,g=r,o=r $(MANPAGE_DOS) $(MAN) $(INSTALL) -p -m u=r,g=r,o=r $(MANPAGE_RT) $(MAN) $(INSTALL) -p -m u=r,g=r,o=r $(MANPAGE_DOSMT) $(MAN) + $(INSTALL) -p -m u=r,g=r,o=r $(MANPAGE_OS8) $(MAN) uninstall: rm -f $(BIN)/$(EXECUTABLE) @@ -45,6 +48,7 @@ uninstall: rm -f $(MAN)/$(MANPAGE_DOS) rm -f $(MAN)/$(MANPAGE_RT) rm -f $(MAN)/$(MANPAGE_DOSMT) + rm -f $(MAN)/$(MANPAGE_OS8) # This assumes that fsio has been "installed" on the current system archive: $(RELEASEFILES) diff --git a/converters/fsio/dos11.c b/converters/fsio/dos11.c index c68f390..60581e3 100644 --- a/converters/fsio/dos11.c +++ b/converters/fsio/dos11.c @@ -1666,12 +1666,12 @@ static void dos11Umount( * * Returns: * - * Size of the container file in blocks of default file system size + * Default size of the container file in bytes (RK05). * --*/ static size_t dos11Size(void) { - return DISKSIZE_RK05; + return DISKSIZE_RK05 * BLOCKSIZE_RK11; } /*++ @@ -1707,7 +1707,7 @@ static int dos11Newfs( memset(data, 0, sizeof(*data)); - data->blocks = size; + data->blocks = size / BLOCKSIZE_RK11; data->bitmaps = 5; data->bmblk[0] = MAP_BLOCK; data->bmblk[1] = MAP_BLOCK + 1; diff --git a/converters/fsio/fsio-dos11.1 b/converters/fsio/fsio-dos11.1 index dcc3e7a..b4665d1 100644 --- a/converters/fsio/fsio-dos11.1 +++ b/converters/fsio/fsio-dos11.1 @@ -1,4 +1,4 @@ -.TH FSIO-DOS11 1 "December 28,2018" "FFS I/O - DOS-11" +.TH FSIO-DOS11 1 "Jun 21,2019" "FFS I/O - DOS-11" .SH NAME fsio-dos11 \- Foreign File System I/O - DOS-11 .br @@ -45,6 +45,7 @@ Creates an empty UFD and sets default UIC for file access. .BR fsio (1), .BR fsio-rt11 (1) .BR fsio-dosmt (1) +.BR fsio-os8 (1) .SH AUTHOR John Forecast, .br diff --git a/converters/fsio/fsio-dosmt.1 b/converters/fsio/fsio-dosmt.1 index cca7c12..cf263e3 100644 --- a/converters/fsio/fsio-dosmt.1 +++ b/converters/fsio/fsio-dosmt.1 @@ -1,4 +1,4 @@ -.TH FSIO-DOSMT 1 "January 28,2019" "FFS I/O - DOS-11 magtape" +.TH FSIO-DOSMT 1 "Jun 25,2019" "FFS I/O - DOS-11 magtape" .SH NAME fsio-dosmt \- Foreign File System I/O - DOS-11 magtape .br @@ -102,9 +102,10 @@ the end of any specific file (see info command for more details on how to determine the current tape position). .br .SH SEE ALSO -.BR fsio (1), +.BR fsio (1) .BR fsio-dos11 (1) .BR fsio-rt11 (1) +.BR fsio-os8 (1) .SH AUTHOR John Forecast, .br diff --git a/converters/fsio/fsio-os8.1 b/converters/fsio/fsio-os8.1 new file mode 100644 index 0000000..724886b --- /dev/null +++ b/converters/fsio/fsio-os8.1 @@ -0,0 +1,80 @@ +.TH FSIO-OS8 1 "Sep 218,2019" "FFS I/O - OS/8" +.SH NAME +fsio-os8 \- Foreign File System I/O - OS/8 +.br +.SH DESCRIPTION +\fBfsio\fP allows access to OS/8 file systems using the file system type +"\fIos8\fP" +.br +.SH OS/8 PHYSICAL DISKS +OS/8 uses a file system block size of 256 words and OS/8 devices are limited +to 4095 blocks. Blocks 0 through 6 are reserved so the largest possible file +is 4088 blocks long (1046528 words). Larger devices, for example the RK05, +place multiple file systems on each physical device. + +.br +OS/8 does not write any type of signature on the device and each device type +has it's own partitioning scheme so the \fImount\fP command must use the +"\fI-f type\fP switch so that \fBfsio\fP can determine the file system +layout. \fBfsio\fP uses a set of heuristics to verify the integrity of +the OS/8 file system(s) but it is quite possible for a random disk to pass +these tests and later crash \fBfsio\fP. +.SH MOUNT OPERATION +\fImount\fP requires the "\fI-f type\fP" switch so that it can determine the +type of the underlying disk (See NEWFS OPERATION below for details). +.SH NEWFS OPERATION +\fInewfs\fP creates an RK05 disk image with 2 file systems. If the +"\fI-f type\fP" switch is present a different container file will be created +depending on the type of the device specified: +.br +.RS +.TP +\fIrk05\fP \- RK05 image (2 file systems, 3248 blocks each) +.br +.TP +\fIrx01\fP \- RX01 image (single file system, 494 blocks) +.br +.TP +\fIrx02\fP \- RX02 image (single file system, 988 blocks) +.br +.RE + +Note that in order to use RX02 media the OS/8 system must have the device +extensions kit installed. +.br + +The "\fI-e extra\fP" switch, where "extra" is a number in the range 0 - 63, +may be used to create file systems with "extra" additional information words +available to each directory entry. By default, \fInewfs\fP will create file +systems with 1 additional information word which will be used to hold the +file creation date. If you do not want any additional information words +use "\fI-e 0\fP". +.br +.SH SET OPERATION +The following \fIset\fP commands are supported: +.br +.TP +.B "\fIyear\fP n" +.br + +All subsequent OS/8 file creations will include a creation date (if the file +system was created with at least 1 additional information word). The date used +will be the month and day of the month for today with the year "n" (where "n" +is in the range 0 - 7). +.br +.TP +.B "\fIyear\fP none" +.br + +All subsequent OS/8 file creations will include a zero creation date (if the +file system was created with at least 1 additional information word). This is +the default state after mount. +.br +.SH SEE ALSO +.BR fsio (1) +.BR fsio-dos11 (1) +.BR fsio-dosmt (1) +.BR fsio-rt11 (1) +.SH AUTHOR +John Forecast, +.br diff --git a/converters/fsio/fsio-rt11.1 b/converters/fsio/fsio-rt11.1 index 3f521da..923d9c7 100644 --- a/converters/fsio/fsio-rt11.1 +++ b/converters/fsio/fsio-rt11.1 @@ -1,4 +1,4 @@ -.TH FSIO-RT11 1 "May 15,2019" "FFS I/O - RT-11" +.TH FSIO-RT11 1 "Jun 25,2019" "FFS I/O - RT-11" .SH NAME fsio-rt11 \- Foreign File System I/O - RT-11 .br @@ -23,6 +23,15 @@ version 2 file systems. 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. +.br +.SH RT-11 PHYSICAL DISKS +RT-11 uses a file system block size of 512 bytes and most disk type either +have a block size of 512 bytes or use 2 contiguous blocks or 256 bytes each. +Floppy diskettes, RX01 and RX02 drives, use small sectors size and for +performance reason, adjacent logical sectors are interleaved within the same +track. When mounting a floppy diskette, the \fI"-f type"\fP must be used so +that \fIfsio\fP can determine the interleaving used. In this case "type" +should be either "rx01" or "rx02". .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. @@ -43,9 +52,18 @@ depending on the type of device specified: \fIrl02\fP \- RL02 image (10MB, 20480 blocks) .br .TP -\fIrx20\fP \- RX20 image (512KB, 1024 blocks) +\fIrx01\fP \- RX01 floopy image (256KB, 2002 sectors of 128 bytes) +.br +.TP +\fIrx02\fP \- RX02 floopy image (512KB, 2002 sectors of 256 bytes) .br .RE + +The "\fI-e extra\fP" switch, where "extra" is a number in the range 0 - 63, +may be used to create file systems with "extra" additional bytes available to +each directory entry. \fIfsio\fP will silently round up the value to make it +an even number required by RT-11. By default, \fInewfs\fP will create file +systems with no additional bytes for each directory entry. .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 @@ -56,9 +74,10 @@ NULLs up to the next block boundary if this occurs. .SH SET OPERATION No \fIset\fP commands are currently supported. .SH SEE ALSO -.BR fsio (1), +.BR fsio (1) .BR fsio-dos11 (1) .BR fsio-dosmt (1) +.BR fsio-os8 (1) .SH AUTHOR John Forecast, .br diff --git a/converters/fsio/fsio.1 b/converters/fsio/fsio.1 index bd30b79..fb664d1 100644 --- a/converters/fsio/fsio.1 +++ b/converters/fsio/fsio.1 @@ -1,4 +1,4 @@ -.TH FSIO 1 "May 15,2019" "Foreign File System I/O" +.TH FSIO 1 "Sep 17,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 [-dfrx] dev[:] file type" +.B "\fImount\fP [-dfrx] [-t type] dev[:] file type" Make the container file available to fsio. .br .RS @@ -93,6 +93,8 @@ Make the container file available to fsio. .br .B "\fI\-r\fP \- mount file system read-only" .br +.B "\fI\-t type\fP \- specify optional disk type" +.br .B "\fI\-x\fP \- dosmt will use extended filenames when writing" .br .B "\fIdev[:]\fP \- user supplied name for the mount" @@ -113,13 +115,15 @@ Remove knowledge of the container file from fsio. .RE .RE .TP -.B "\fInewfs\fP [-t type] file type" +.B "\fInewfs\fP [-t type] [-e count] file type" Create an new container with an empty file system. .br .RS .RS .B "\fI\-t type\fP \- use alternate, file-system dependent size" .br +.B "\fI\-e n\fP \- Specify extra space for directory entries (RT11, OS/8)" +.br .B "\fIfile\fP \- name of the container file" .br .B "\fItype\fP \- type of container file system" @@ -327,10 +331,13 @@ This function depends on the value of blks: .br .B "\fIdosmt\fP \- container file in DOS-11 magtape format" .br +.B "\fIos8\fP \- OS/8 on RX01, RX01 or RK05" +.br .SH SEE ALSO .BR fsio-dos11 (1), .BR fsio-rt11 (1) .BR fsio-dosmt (1) +.BR fsio-os8 (1) .SH AUTHOR John Forecast, .br diff --git a/converters/fsio/fsio.c b/converters/fsio/fsio.c index 02302af..116a5c1 100644 --- a/converters/fsio/fsio.c +++ b/converters/fsio/fsio.c @@ -32,7 +32,7 @@ */ /* - * Foreign file system interface (12/26/2018) + * Foreign file system interface (9/18/2019) * * Each foreign file system is described by a struct FSdef which is linked * into a master list of supported file systems in FSioInit(). Each entry @@ -84,7 +84,9 @@ * This routine is called when processing a "mount" command. The container * file is open, and this routine should verify that it contains a valid * file system. This routine may print out information about the file - * system. Return 1 if the file system is valid, 0 otherwise. + * system. Return 1 if the file system is valid, 0 if the file system is + * invalid and -1 if the file system is invalid and an erroir message + * has already been printed. * * void (*umount)(struct mountedFS *mount); * @@ -95,9 +97,8 @@ * * size_t (*size)(void) * - * Return the number of "blocksz" size blocks used for the container file. - * The command line switch (-t type) may be used to determine the - * required size. + * Return the number of byte used for the container file. The command + * line switch (-t type) may be used to override the default size. * * int (*newfs)(struct mountedFS *mount, size_t size); * @@ -151,9 +152,9 @@ * * off_t (*fileSize)(void *filep); * - * Returns the size of a file currently open for reading. If it is - * not possible to determine the actual file size, return 0. In all - * other cases, the returned value may over-estimate, but not + * Returns the size, in bytes, of a file currently open for reading. + * If it is not possible to determine the actual file size, return 0. + * In all other cases, the returned value may over-estimate, but not * under-estimate the size of the file. For example, linked files * on DOS-11 will overestimate the size by 2 bytes/block. * @@ -310,12 +311,12 @@ struct command { cmd_t func; /* Command execution function */ } cmdTable[] = { #ifdef DEBUG - { "mount", OPTIONS("dfrx"), 3, 3, 0, doMount }, + { "mount", OPTIONS("dfrt:x"), 3, 3, 0, doMount }, #else - { "mount", OPTIONS("frx"), 3, 3, 0, doMount }, + { "mount", OPTIONS("frt:x"), 3, 3, 0, doMount }, #endif { "umount", NULL, 1, 1, 0, doUmount }, - { "newfs", OPTIONS("t:"), 2, 2, 0, doNewfs }, + { "newfs", OPTIONS("e:t:"), 2, 2, 0, doNewfs }, { "set", NULL, 2, MAX_CMDLEN, 0, doSet }, { "info", NULL, 1, 1, 0, doInfo }, { "dir", OPTIONS("fn"), 1, 1, 0, doDir }, @@ -364,6 +365,7 @@ extern struct FSdef localFS; extern struct FSdef dos11FS; extern struct FSdef rt11FS; extern struct FSdef dosmtFS; +extern struct FSdef os8FS; #define MAX(a, b) (((a) > (b)) ? (a) : (b)) #define MIN(a, b) (((a) < (b)) ? (a) : (b)) @@ -418,6 +420,9 @@ static void FSioInit(void) dosmtFS.next = fileSystems; fileSystems = &dosmtFS; + + os8FS.next = fileSystems; + fileSystems = &os8FS; } /*++ @@ -746,6 +751,7 @@ static void doMount(void) { struct FSdef *filesys; FILE *container; + int status; char *mode = SWISSET('r') ? "r" : "r+"; if (checkDev(words[0]) != 0) { @@ -761,6 +767,8 @@ static void doMount(void) struct mountedFS *mount; if ((mount = malloc(sizeof(struct mountedFS))) != NULL) { + memset(mount, 0, sizeof(struct mountedFS)); + strcpy(mount->name, words[0]); mount->filesys = filesys; mount->blocksz = filesys->blocksz; @@ -781,19 +789,21 @@ static void doMount(void) } #endif mount->container = container; + mount->skip = 0; /* * Verify that the container holds a valid file system */ - if ((*filesys->mount)(mount) != 0) { + if ((status = (*filesys->mount)(mount)) > 0) { mount->next = mounts; mounts = mount; return; } free(mount); - fprintf(stderr, - "mount: \"%s\" does not contain a valid file system\n", - words[1]); + if (status == 0) + fprintf(stderr, + "mount: \"%s\" does not contain a valid file system\n", + words[1]); } else fprintf(stderr, "mount: out of memory\n"); fclose(container); } else fprintf(stderr, "mount: failed to open \"%s\"\n", words[1]); @@ -892,7 +902,9 @@ static void doNewfs(void) size = (*filesys->size)(); if ((mount.container = fopen(words[0], "w+")) != NULL) { - off_t offset = filesys->blocksz * (size - 1); + off_t offset = size - 1; + + mount.skip = 0; /* * If an empty file is valid, we are all done @@ -900,8 +912,8 @@ static void doNewfs(void) if ((filesys->flags & FS_EMPTYFILE) != 0) return; - if ((fseeko(mount.container, offset, SEEK_SET) == 0) && - (fwrite(&ch, 1, filesys->blocksz, mount.container) == filesys->blocksz)) { + if ((fseeko(mount.container, offset + mount.skip, SEEK_SET) == 0) && + (fwrite(&ch, 1, 1, mount.container) == 1)) { mount.filesys = filesys; mount.blocksz = filesys->blocksz; @@ -1263,8 +1275,8 @@ static void doType(void) * Close the file */ (*mount->filesys->closeFile)(file); - } - } else fprintf(stderr, "dump: \"%s\" no such file\n", fname); + } else fprintf(stderr, "type: \"%s\" no such file\n", fname); + } } /*++ @@ -1415,10 +1427,12 @@ static void doHelp(void) "A common command format is used:\n\n" " verb [switches] arg1 arg2 ...\n\n" "The following commands are supported:\n\n" - " mount [-r] dev[:] container fstype\n\n" + " mount [-r] [-t type] dev[:] container fstype\n\n" "The file system container file is made available to fsio (via the dev\n" "specifier). fstype specifies the type of the container file system.\n" - "If -r is specified, the file system will be read-only.\n\n" + "If -r is specified, the file system will be read-only.\n" + "In some cases (e.g. OS/8) fsio is unable to determine the type of the\n" + "underlying disk so it must be specified using \"-t type\"\n\n" " umount dev[:]\n\n" "Remove all knowledge of the container file from fsio.\n\n" " newfs [-t type] container fstype\n\n" @@ -1942,7 +1956,7 @@ int FSioReadBlock( { off_t offset = block * mount->blocksz; - if (fseeko(mount->container, offset, SEEK_SET) == 0) + if (fseeko(mount->container, offset + mount->skip, SEEK_SET) == 0) return fread(buf, mount->blocksz, 1, mount->container); return 0; @@ -1976,8 +1990,87 @@ int FSioWriteBlock( { off_t offset = block * mount->blocksz; - if (fseeko(mount->container, offset, SEEK_SET) == 0) + if (fseeko(mount->container, offset + mount->skip, SEEK_SET) == 0) return fwrite(buf, mount->blocksz, 1, mount->container); return 0; } + +/*++ + * F S i o R e a d S e c t o r + * + * Read a sector of a specified size from the container. The caller is + * responsible for making sure that the buffer has sufficient space for + * the sector. This routine is used when file system blocks are constructed + * from a number of sectors which are interleaved on the physical disk (e.g. + * RX02 floppies). + * + * Inputs: + * + * mount - pointer to a mounted file system descriptor + * sector - logical sector # in the range 0 - N + * size - size of each sector (in bytes) + * buf - pointer to the buffer to receive the data + * + * Outputs: + * + * The buffer will be overwritten by the contents of the sector from + * the container file system + * + * Returns: + * + * 1 if read was successful, 0 otherwise + * + --*/ +int FSioReadSector( + struct mountedFS *mount, + unsigned int sector, + unsigned int size, + void *buf +) +{ + off_t offset = sector * size; + + if (fseeko(mount->container, offset + mount->skip, SEEK_SET) == 0) + return fread(buf, size, 1, mount->container); + + return 0; +} + +/*++ + * F S i o W r i t e S e c t o r + * + * Write a sector of a specified size to the container. This routine is used + * when file system blocks are constructed from a number of sectors which are + * interleaved on the physical disk (e.g. RX02 floppies). + * + * Inputs: + * + * mount - pointer to a mounted file system descriptor + * sector - logical sector # in the range 0 - N + * size - size of each sector (in bytes) + * buf - pointer to the buffer to receive the data + * + * Outputs: + * + * None + * + * Returns: + * + * 1 if read was successful, 0 otherwise + * + --*/ +int FSioWriteSector( + struct mountedFS *mount, + unsigned int sector, + unsigned int size, + void *buf +) +{ + off_t offset = sector * size; + + if (fseeko(mount->container, offset + mount->skip, SEEK_SET) == 0) + return fwrite(buf, size, 1, mount->container); + + return 0; +} diff --git a/converters/fsio/fsio.h b/converters/fsio/fsio.h index 11c566f..02c86d5 100644 --- a/converters/fsio/fsio.h +++ b/converters/fsio/fsio.h @@ -40,6 +40,7 @@ enum openMode { M_RD, M_WR }; #include "dos11.h" #include "rt11.h" #include "dosmt.h" +#include "os8.h" /* * All of the supported file systems are natively little endian so we only @@ -150,17 +151,22 @@ struct mountedFS { /* Bits after 0x0080 reserved for */ /* file system use */ FILE *container; /* Container file access */ + off_t skip; /* Data to skip in container file */ union { struct DOS11data _dos11; struct RT11data _rt11; struct DOSMTdata _dosmt; + struct OS8data _os8; } FSdata; #define dos11data FSdata._dos11 #define rt11data FSdata._rt11 #define dosmtdata FSdata._dosmt +#define os8data FSdata._os8 }; extern int FSioReadBlock(struct mountedFS *, unsigned int, void *); extern int FSioWriteBlock(struct mountedFS *, unsigned int, void *); +extern int FSioReadSector(struct mountedFS *, unsigned int, unsigned int, void *); +extern int FSioWriteSector(struct mountedFS *, unsigned int, unsigned int, void *); #endif diff --git a/converters/fsio/fsio.txt b/converters/fsio/fsio.txt index 983dfaa..f924c96 100644 --- a/converters/fsio/fsio.txt +++ b/converters/fsio/fsio.txt @@ -87,7 +87,7 @@ Full command syntax: 1. mount - mount [-dfr] dev[:] container type + mount [-dfr] [-t type] dev[:] container type Make the specified container file available to fsio for I/O. @@ -97,6 +97,10 @@ Full command syntax: validate the container file format -r If present, the file system is only available for read access + -t type Specify the type of the container file. This is only required + for OS/8 file systems where type should be one of "rx01", + "rx02" or "rk05". + dev[:] User supplied name to be used for accessing files within the container file. Files within the container are named by using the syntax dev:filespec where filespec uses the native syntax @@ -133,6 +137,7 @@ Full command syntax: dos11 RK05 image (2.5MB, 4800 blocks) rt11 MSCP image (32MB, 65535 blocks) dosmt An empty file suitable for use with any magtape controller + os8 An OS/8 file system -t type Use a different size for the container file. The size used will be file system dependent. @@ -142,6 +147,12 @@ Full command syntax: rl02 RL02 image (10MB, 20480 blocks) rx20 RX20 image (512KB, 1024 blocks) + For os8, the following disk types are valid: + + rk05 RK05 image (3248 blocks) + rx01 RX01 image (2002 sectors of 128 bytes each) + rx02 RX02 imgae (2002 sectors of 256 bytes each) + container The name of the file to create type The type of the file system to create in the container diff --git a/converters/fsio/fsioSimh.txt b/converters/fsio/fsioSimh.txt index 1fdc5f4..cc76d33 100644 --- a/converters/fsio/fsioSimh.txt +++ b/converters/fsio/fsioSimh.txt @@ -274,14 +274,44 @@ before you have any network running. TOPS-10 ------- +The RTFLX utility is available on TOPS-10 reading/writing files on an RX20 +drive (combination of RX02 drive and controller) attached to a DECsystem-2020 +(KS10). In order to access the RX20, a new system generation must be +performed. There is a help file on Tops-10 for RTFLX but no other +documentation. -Use the RTFLX utility to copy the file over to the TOPS-10 file system. When -creating the SIMH file using the "newfs" command use the "-t rx20" switch to -create a floppy image. The RX20 is a double density floppy and is only -available on KS10 systems. + . + Simulation stopped, PC: 000001 (SOJG 6,1) + sim> att ry0 xfer.dsk + RY: buffering file in memory + sim> c -There is a help file on Tops-10 for RTFLX but no other documentation. The -default TOPS-10 monitor does not include support for the RX20 so a new -system generation will be required. + .assign rxa0: rt: + RXA000 assigned + .r rtflx + + + RTFLX>dir + + FILE TXT 1 11-Jun-87 + + Total of 1 block in 1 file + 973 blocks free on RT: + + RTFLX>copy rt:file.txt + FILE.TXT=RT:FILE.TXT + + RTFLX>exit + + .dir *.txt + + + FILE TXT 1 <057> 11-Jun-87 DSKB: [1,2] + + .deassign rxa0: + . + +If you use the RTFLX "ZERO" command to initialize an RT-11 file system it +will create an RT-11 V2 format file system and you will need to use the "-f" +switch to force fsio to see the file system. -I do not currently have a functioning TOPS-10 system to test this on. diff --git a/converters/fsio/os8.c b/converters/fsio/os8.c new file mode 100644 index 0000000..bebcf33 --- /dev/null +++ b/converters/fsio/os8.c @@ -0,0 +1,3015 @@ +/* + * Copyright (C) 2019 John Forecast. 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN FORECAST "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 AUTHOR 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. + */ + +/* + * Support routines for handling OS/8 file systems under fsio + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fsio.h" + +static int rk05BlockPresent(struct mountedFS *, uint8_t, unsigned int); +static int rk05ReadBlock(struct mountedFS *, uint8_t, unsigned int, void *); +static int rk05WriteBlock(struct mountedFS *, uint8_t, unsigned int, void *); +static int rx01BlockPresent(struct mountedFS *, uint8_t, unsigned int); +static int rx01ReadBlock(struct mountedFS *, uint8_t, unsigned int, void *); +static int rx01WriteBlock(struct mountedFS *, uint8_t, unsigned int, void *); +static int rx02BlockPresent(struct mountedFS *, uint8_t, unsigned int); +static int rx02ReadBlock(struct mountedFS *, uint8_t, unsigned int, void *); +static int rx02WriteBlock(struct mountedFS *, uint8_t, unsigned int, void *); + +/* + * Floppy interleave tables: + * + * RX01: + * Each OS/8 logical block maps into 4 sectors (interleave 2) + * + * RX02: + * Each OS/8 logical block maps into 2 sectors (interleave 3) + */ +#define RX01INTLV(t, s) ((t * OS8_RX0xNSECT) + s - 1) +#define RX02INTLV(t, s) ((t * OS8_RX0xNSECT) + s - 1) + +static uint16_t rx01interleave[] = { + RX01INTLV(0, 1), RX01INTLV(0, 3), RX01INTLV(0, 5), RX01INTLV(0, 7), + RX01INTLV(0, 9), RX01INTLV(0, 11), RX01INTLV(0, 13), RX01INTLV(0, 15), + RX01INTLV(0, 17), RX01INTLV(0, 19), RX01INTLV(0, 21), RX01INTLV(0, 23), + RX01INTLV(0, 25), RX01INTLV(0, 2), RX01INTLV(0, 4), RX01INTLV(0, 6), + RX01INTLV(0, 8), RX01INTLV(0, 10), RX01INTLV(0, 12), RX01INTLV(0, 14), + RX01INTLV(0, 16), RX01INTLV(0, 18), RX01INTLV(0, 20), RX01INTLV(0, 22), + RX01INTLV(0, 24), RX01INTLV(0, 26), RX01INTLV(1, 1), RX01INTLV(1, 3), + RX01INTLV(1, 5), RX01INTLV(1, 7), RX01INTLV(1, 9), RX01INTLV(1, 11), + RX01INTLV(1, 13), RX01INTLV(1, 15), RX01INTLV(1, 17), RX01INTLV(1, 19), + RX01INTLV(1, 21), RX01INTLV(1, 23), RX01INTLV(1, 25), RX01INTLV(1, 2), + RX01INTLV(1, 4), RX01INTLV(1, 6), RX01INTLV(1, 8), RX01INTLV(1, 10), + RX01INTLV(1, 12), RX01INTLV(1, 14), RX01INTLV(1, 16), RX01INTLV(1, 18), + RX01INTLV(1, 20), RX01INTLV(1, 22), RX01INTLV(1, 24), RX01INTLV(1, 26) +}; +#define RX01REPEAT ((sizeof(rx01interleave)/sizeof(rx01interleave[0])) / 4) + +static uint16_t rx02interleave[] = { + RX02INTLV(0, 1), RX02INTLV(0, 4), + RX02INTLV(0, 7), RX02INTLV(0, 10), + RX02INTLV(0, 13), RX02INTLV(0, 16), + RX02INTLV(0, 19), RX02INTLV(0, 22), + RX02INTLV(0, 25), RX02INTLV(0, 2), + RX02INTLV(0, 5), RX02INTLV(0, 8), + RX02INTLV(0, 11), RX02INTLV(0, 14), + RX02INTLV(0, 17), RX02INTLV(0, 20), + RX02INTLV(0, 23), RX02INTLV(0, 26), + RX02INTLV(0, 3), RX02INTLV(0, 6), + RX02INTLV(0, 9), RX02INTLV(0, 12), + RX02INTLV(0, 15), RX02INTLV(0, 18), + RX02INTLV(0, 21), RX02INTLV(0, 24) +}; +#define RX02REPEAT ((sizeof(rx02interleave)/sizeof(rx02interleave[0])) / 2) + +/* + * Six-bit code => character mapping table + */ +static char sixbit[64] = { + ' ', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', + 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', + ' ', '!', '"', '#', '$', '%', '&', '\'', + '(', ')', '*', '+', ',', '-', '.', '/', + '0', '1', '2', '3', '4', '5', '6', '7', + '8','9', ':', ';', '<', '=', '>', '?' +}; + +/* + * Month of the year - allow 4-bit index field + */ +static char *os8Month[] = { + "???", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", + "Aug", "Sep", "Oct", "Nov", "Dec", "???", "???", "???" +}; + +/* + * Table of "set" commands + */ +static char *setCmds[] = { + "year", + NULL +}; +#define OS8SET_YEAR 0 + +static void os8CloseFile(void *); + +extern int args; +extern char **words; +extern int quiet; + +/*++ + * o s 8 D e v i c e s + * + * List of OS/8 disk device types supported by this program. + * + --*/ +struct OS8device OS8Devices[] = { + { "rk05", 2, 2 * OS8_RK05FS_BLKS, + 0, + { OS8_RK05FS_BLKS, OS8_RK05FS_BLKS, 0, 0, 0, 0, 0,0 }, + rk05BlockPresent, rk05ReadBlock, rk05WriteBlock }, + + { "rx01", 1, (OS8_RX0xSZ * OS8_RX01SS_W) / OS8_BLOCKSIZE, + OS8_RX0xNSECT * OS8_RX01SS, + { ((OS8_RX0xSZ - OS8_RX0xNSECT) * OS8_RX01SS_W) / OS8_BLOCKSIZE, + 0, 0, 0, 0, 0, 0, 0 }, + rx01BlockPresent, rx01ReadBlock, rx01WriteBlock }, + + { "rx02", 1, (OS8_RX0xSZ * OS8_RX02SS_W) / OS8_BLOCKSIZE, + OS8_RX0xNSECT * OS8_RX02SS, + { ((OS8_RX0xSZ - OS8_RX0xNSECT) * OS8_RX02SS_W) / OS8_BLOCKSIZE, + 0, 0, 0, 0, 0, 0, 0 }, + rx02BlockPresent, rx02ReadBlock, rx02WriteBlock }, + + { NULL, 0, 0, + 0, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + NULL, NULL, NULL } +}; + +/*++ + * o s 8 W o r d + * + * Extract an unsigned 12-bit value. + * + * Inputs: + * + * value - 16-bit incoming value + * + * Outputs: + * + * None + * + * Returns: + * + * 16-bit value with the high 4-bit zeroed + * + --*/ +static uint16_t os8Word( + uint16_t value +) +{ + return value & 0xFFF; +} + +/*++ + * o s 8 S X T + * + * Extract a 12-bit value extending the size bit. + * + * Inputs: + * + * value - 16-bit incoming value + * + * Outputs: + * + * None + * + * Returns: + * + * Original value with sign extended to 16-bits + * + --*/ +static uint16_t os8SXT( + uint16_t value +) +{ + return (value & 0xFFF) | (((value & 0x800) != 0) ? 0xF000 : 0); +} + +/*++ + * o s 8 N e g + * + * Negate a 12-bit value to a 16-bit value leading to a result in the range + * 0 - 4095. + * + * Inputs: + * + * value - 16-bit incoming value + * + * Outputs: + * + * None + * + * Returns: + * + * Negated value + * + --*/ +static uint16_t os8Neg( + uint16_t value +) +{ + uint16_t value16 = os8SXT(value); + + return (uint16_t)((0 - value16) & 0xFFF); +} + +/*++ + * o s 8 V a l u e + * + * Truncate a 16-bit value to 12-bits. + * + * Inputs: + * + * value - 16-bit incoming value + * + * Outputs: + * + * None + * + * Returns: + * + * The 12-bit truncated value + * + --*/ +static uint16_t os8Value( + uint16_t value +) +{ + return value & 0xFFF; +} + +/*++ + * o s 8 D a t e + * + * Construct a user readable date string given an OS/8 date value. The caller + * must supply a suitably sized buffer to contain the date string. If the + * supplied date value is zero, the date string will be all spaces. + * + * Inputs: + * + * date - OS/8 date value + * buf - pointer to buffer to receive the file name + * (Must be at least 12 bytes) + * + * Outputs: + * + * The buffer will be overwritten with the date string. + * + * Returns: + * + * Pointer to the NULL terminated file name string + * + --*/ +static char *os8Date( + uint16_t date, + char *buf +) +{ + if (date != 0) { + sprintf(buf, "%2d-%s-%4d", + (date >> 3) & 0x1F, + os8Month[(date >> 8) & 0xF], + 1970 + (date & 0x7)); + } else strcpy(buf, " "); + + return buf; +} + +/*++ + * o s 8 F i l e N a m e + * + * Construct a user readable filename string from a directory entry in the + * mount point specific buffer. The caller must supply a suitably sized + * buffer to contain the file name string. + * + * Inputs: + * + * mount - pointer to a mounted file system descriptor + * off - offset of the directory entry + * buf - pointer to buffer to receive the file name + * (Must be at least 10 bytes) + * + * Outputs: + * + * The buffer will be overwritten with the file name + * + * Returns: + * + * Pointer to the NULL terminated file name string + * + --*/ +static char *os8FileName( + struct mountedFS *mount, + uint16_t off, + char *buf +) +{ + struct OS8data *data = &mount->os8data; + uint16_t fname1 = os8Word(le16toh(data->buf[off + OS8_DI_FNAME1])); + uint16_t fname2 = os8Word(le16toh(data->buf[off + OS8_DI_FNAME2])); + uint16_t fname3 = os8Word(le16toh(data->buf[off + OS8_DI_FNAME3])); + uint16_t ext = os8Word(le16toh(data->buf[off + OS8_DI_EXT])); + + buf[0] = sixbit[(fname1 >> 6) & 0x3F]; + buf[1] = sixbit[fname1 & 0x3F]; + buf[2] = sixbit[(fname2 >> 6) & 0x3F]; + buf[3] = sixbit[fname2 & 0x3F]; + buf[4] = sixbit[(fname3 >> 6) & 0x3F]; + buf[5] = sixbit[fname3 & 0x3F]; + buf[6] = '.'; + buf[7] = sixbit[(ext >> 6) & 0x3F]; + buf[8] = sixbit[ext & 0x3F]; + buf[9] = '\0'; + + return buf; +} + +/*++ + * o s 8 U n i t V a l i d + * + * Determine if a provided unit # is valid. + * + * Inputs: + * + * data - pointer to OS/8 specific data area + * unit - file system unit number + * + * Outputs: + * + * None + * + * Returns: + * + * 1 if unit # is valid, 0 otherwise + * + --*/ +static int os8UnitValid( + struct OS8data *data, + uint8_t unit +) +{ + struct OS8device *device; + + if ((device = data->device) != NULL) { + if (unit < device->filesys) + return (data->valid & (1 << unit)) != 0 ? 1 : 0; + } + return 0; +} + +/*++ + * r k 0 5 B l o c k P r e s e n t + * + * Check if the specified block is present in the container file. + * + * Inputs: + * + * mount - pointer to a mounted file system descriptor + * unit - file system unit number + * block - logical block # in the range 0 - N + * + * Outputs: + * + * None + * + * Returns: + * + * 1 if block is present in the container file, 0 otherwise + * + --*/ +static int rk05BlockPresent( + struct mountedFS *mount, + uint8_t unit, + unsigned int block +) +{ + struct OS8data *data = &mount->os8data; + unsigned int base = unit == 0 ? 0 : OS8_RK05FS_BLKS; + + return data->blocks > (base + block); +} + +/*++ + * r k 0 5 R e a d B l o c k + * + * Read a block from an OS/8 file system on an RK05 disk. + * + * Inputs: + * + * mount - pointer to a mounted file system descriptor + * unit - file system unit number + * block - logical block # in the range 0 - N + * buf - buffer to receive data + * + * Outputs: + * + * The block will be read into the specified buffer + * + * Returns: + * + * 1 if successful, 0 otherwise + * + --*/ +static int rk05ReadBlock( + struct mountedFS *mount, + uint8_t unit, + unsigned int block, + void *buf +) +{ + int status = 0; + unsigned int base = unit == 0 ? 0 : OS8_RK05FS_BLKS; + + if (block >= OS8_RK05FS_BLKS) { + ERROR("Attempt to read block (%u) outside file system \"%s%o:\"\n", + block, mount->name, unit); + return 0; + } + + status = FSioReadBlock(mount, base + block, buf); + + if (status == 0) + ERROR("I/O error on \"%s%o:\"\n", mount->name, unit); + + return status; +} + +/*++ + * r k 0 5 W r i t e B l o c k + * + * Write a block to an OS/8 file system on an RK05 disk. + * + * Inputs: + * + * mount - pointer to a mounted file system descriptor + * unit - file system unit number + * block - logical block # in the range 0 - N + * buf - buffer containing the data + * + * Outputs: + * + * None + * + * Returns: + * + * 1 if successful, 0 otherwise + * + --*/ +static int rk05WriteBlock( + struct mountedFS *mount, + uint8_t unit, + unsigned int block, + void *buf +) +{ + int status = 0; + unsigned int base = unit == 0 ? 0 : OS8_RK05FS_BLKS; + + if (block >= OS8_RK05FS_BLKS) { + ERROR("Attempt to write block (%u) outside file system \"%s%o:\"\n", + block, mount->name, unit); + return 0; + } + + status = FSioWriteBlock(mount, base + block, buf); + + if (status == 0) + ERROR("I/O error on \"%s%o:\"\n", mount->name, unit); + + return status; +} + +/*++ + * r x 0 1 B l o c k P r e s e n t + * + * Check if the specified block is present in the container file. + * + * Inputs: + * + * mount - pointer to a mounted file system descriptor + * unit - file system unit number + * block - logical block # in the range 0 - N + * + * Outputs: + * + * None + * + * Returns: + * + * 1 if block is present in the container file, 0 otherwise + * + --*/ +static int rx01BlockPresent( + struct mountedFS *mount, + uint8_t UNUSED(unit), + unsigned int block +) +{ + struct OS8data *data = &mount->os8data; + + return data->blocks > block; +} + +/*++ + * r x 0 1 R e a d B l o c k + * + * Read a block from an OS/8 file system on an RX01 disk. + * + * Inputs: + * + * mount - pointer to a mounted file system descriptor + * unit - file system unit number + * block - logical block # in the range 0 - N + * buf - buffer to receive data + * + * Outputs: + * + * The block will be read into the specified buffer + * + * Returns: + * + * 1 if successful, 0 otherwise + * + --*/ +static int rx01ReadBlock( + struct mountedFS *mount, + uint8_t unit, + unsigned int block, + void *buf +) +{ + unsigned int base = (block / RX01REPEAT) * RX01REPEAT; + unsigned int offset = block % RX01REPEAT; + uint16_t *buffer = buf; + int i, j, k, status; + +#ifdef DEBUG + if ((mount->flags & FS_DEBUG) != 0) + fprintf(DEBUGout, " >>%s%o: rx01ReadBlock, base %u, offset %u\n", + mount->name, unit, base, offset); +#endif + + /* + * Convert to sector number + */ + base *= OS8_BLOCKSIZE / OS8_RX01SS_W; + + /* + * Read 4 sectors into the buffer + */ + for (i = 0; i < 4; i++) { + unsigned int sector = base + rx01interleave[(offset * 4) + i]; + unsigned char temp[OS8_RX01SS]; + +#ifdef DEBUG + if ((mount->flags & FS_DEBUG) != 0) + fprintf(DEBUGout, " >> %s%o: (os8) Reading %u, track %u sector %u\n", + mount->name, unit, sector, + base + ((rx01interleave[(offset * 4) + i] + 1) / OS8_RX01SS), + (rx01interleave[(offset * 4) + i] + 1) % OS8_RX01SS); +#endif + + if ((status = FSioReadSector(mount, sector, OS8_RX01SS, temp)) == 0) { + ERROR("I/O error on \"%s%o:\"\n", mount->name, unit); + return 0; + } + + /* + * Unpack 64 words from the temporary buffer + */ + for (j = 0, k = 0; j < OS8_RX01SS_W; j += 2, k += 3) { + buffer[j] = ((temp[k] << 4) | ((temp[k + 1] >> 4) & 0xF)) & 0xFFF; + buffer[j + 1] = (((temp[k + 1] << 8) & 0xF00) | temp[k + 2]) & 0xFFF; + } + buffer += OS8_RX01SS_W; + } + return 1; +} + +/*++ + * r x 0 1 W r i t e B l o c k + * + * Write a block to an OS/8 file system on an RX01 disk. + * + * Inputs: + * + * mount - pointer to a mounted file system descriptor + * unit - file system unit number + * block - logical block # in the range 0 - N + * buf - buffer containing the data + * + * Outputs: + * + * None + * + * Returns: + * + * 1 if successful, 0 otherwise + * + --*/ +static int rx01WriteBlock( + struct mountedFS *mount, + uint8_t unit, + unsigned int block, + void *buf +) +{ + unsigned int base = (block / RX01REPEAT) * RX01REPEAT; + unsigned int offset = block % RX01REPEAT; + uint16_t *buffer = buf; + int i, j, k, status; + +#ifdef DEBUG + if ((mount->flags & FS_DEBUG) != 0) + fprintf(DEBUGout, " >>%s%o: rx01WriteBlock, base %u, offset %u\n", + mount->name, unit, base, offset); +#endif + + /* + * Convert to sector number + */ + base *= OS8_BLOCKSIZE / OS8_RX01SS_W; + + /* + * Write 4 sectors from the buffer + */ + for (i = 0; i < 4; i++) { + unsigned int sector = base + rx01interleave[(offset * 4) + i]; + unsigned char temp[OS8_RX01SS]; + + /* + * Pack 64 words into the temporary buffer + */ + for (j = 0, k = 0; j < OS8_RX01SS_W; j += 2, k += 3) { + temp[k] = (buffer[j] >> 4) & 0xFF; + temp[k + 1] = ((buffer[j] << 4) & 0xF0) | ((buffer[j + 1] >> 8) & 0xF); + temp[k + 2] = buffer[j + 1] & 0xFF; + } + +#ifdef DEBUG + if ((mount->flags & FS_DEBUG) != 0) + fprintf(DEBUGout, " >> %s%o: (os8) Writing %u, track %u sector %u\n", + mount->name, unit, sector, + base + ((rx01interleave[(offset * 4) + i] + 1) / OS8_RX01SS), + (rx01interleave[(offset * 4) + i] + 1) % OS8_RX01SS); +#endif + + if ((status = FSioWriteSector(mount, sector, OS8_RX01SS, temp)) == 0) { + ERROR("I/O error on \"%s%o:\"\n", mount->name, unit); + return 0; + } + buffer += OS8_RX01SS_W; + } + return 1; +} + +/*++ + * r x 0 2 B l o c k P r e s e n t + * + * Check if the specified block is present in the container file. + * + * Inputs: + * + * mount - pointer to a mounted file system descriptor + * unit - file system unit number + * block - logical block # in the range 0 - N + * + * Outputs: + * + * None + * + * Returns: + * + * 1 if block is present in the container file, 0 otherwise + * + --*/ +static int rx02BlockPresent( + struct mountedFS *mount, + uint8_t UNUSED(unit), + unsigned int block +) +{ + struct OS8data *data = &mount->os8data; + + return data->blocks > block; +} + +/*++ + * r x 0 2 R e a d B l o c k + * + * Read a block from an OS/8 file system on an RX01 disk. + * + * Inputs: + * + * mount - pointer to a mounted file system descriptor + * unit - file system unit number + * block - logical block # in the range 0 - N + * buf - buffer to receive data + * + * Outputs: + * + * The block will be read into the specified buffer + * + * Returns: + * + * 1 if successful, 0 otherwise + * + --*/ +static int rx02ReadBlock( + struct mountedFS *mount, + uint8_t unit, + unsigned int block, + void *buf +) +{ + unsigned int base = (block / RX02REPEAT) * RX02REPEAT; + unsigned int offset = block % RX02REPEAT; + uint16_t *buffer = buf; + int i, j, k, status; + +#ifdef DEBUG + if ((mount->flags & FS_DEBUG) != 0) + fprintf(DEBUGout, " >>%s%o: rx02ReadBlock, base %u, offset %u\n", + mount->name, unit, base, offset); +#endif + + /* + * Convert to sector number + */ + base *= OS8_BLOCKSIZE / OS8_RX02SS_W; + + /* + * Read 2 sectors into the buffer + */ + for (i = 0; i < 2; i++) { + unsigned int sector = base + rx02interleave[(offset * 2) + i]; + unsigned char temp[OS8_RX02SS]; + +#ifdef DEBUG + if ((mount->flags & FS_DEBUG) != 0) + fprintf(DEBUGout, " >> %s%o: (os8) Reading %u, track %u sector %u\n", + mount->name, unit, sector, + base + ((rx02interleave[(offset * 2) + i] + 1) / OS8_RX02SS), + (rx02interleave[(offset * 2) + i] + 1) % OS8_RX02SS); +#endif + + if ((status = FSioReadSector(mount, sector, OS8_RX02SS, temp)) == 0) { + ERROR("I/O error on \"%s%o:\"\n", mount->name, unit); + return 0; + } + + /* + * Unpack 128 words from the temporary buffer + */ + for (j = 0, k = 0; j < OS8_RX02SS_W; j += 2, k += 3) { + buffer[j] = ((temp[k] << 4) | ((temp[k + 1] >> 4) & 0xF)) & 0xFFF; + buffer[j + 1] = (((temp[k + 1] << 8) & 0xF00) | temp[k + 2]) & 0xFFF; + } + buffer += OS8_RX02SS_W; + } + return 1; +} + +/*++ + * r x 0 2 W r i t e B l o c k + * + * Write a block to an OS/8 file system on an RX01 disk. + * + * Inputs: + * + * mount - pointer to a mounted file system descriptor + * unit - file system unit number + * block - logical block # in the range 0 - N + * buf - buffer containing the data + * + * Outputs: + * + * None + * + * Returns: + * + * 1 if successful, 0 otherwise + * + --*/ +static int rx02WriteBlock( + struct mountedFS *mount, + uint8_t unit, + unsigned int block, + void *buf +) +{ + unsigned int base = (block / RX02REPEAT) * RX02REPEAT; + unsigned int offset = block % RX02REPEAT; + uint16_t *buffer = buf; + int i, j, k, status; + +#ifdef DEBUG + if ((mount->flags & FS_DEBUG) != 0) + fprintf(DEBUGout, " >>%s%o: rx01WriteBlock, base %u, offset %u\n", + mount->name, unit, base, offset); +#endif + + /* + * Convert to sector number + */ + base *= OS8_BLOCKSIZE / OS8_RX02SS_W; + + /* + * Write 2 sectors from the buffer + */ + for (i = 0; i < 2; i++) { + unsigned int sector = base + rx02interleave[(offset * 2) + i]; + unsigned char temp[OS8_RX02SS]; + + /* + * Pack 128 words into the temporary buffer + */ + for (j = 0, k = 0; j < OS8_RX02SS_W; j += 2, k += 3) { + temp[k] = (buffer[j] >> 4) & 0xFF; + temp[k + 1] = ((buffer[j] << 4) & 0xF0) | ((buffer[j + 1] >> 8) & 0xF); + temp[k + 2] = buffer[j + 1] & 0xFF; + } + +#ifdef DEBUG + if ((mount->flags & FS_DEBUG) != 0) + fprintf(DEBUGout, " >> %s%o: (os8) Writing %u, track %u sector %u\n", + mount->name, unit, sector, + base + ((rx02interleave[(offset * 2) + i] + 1) / OS8_RX02SS), + (rx02interleave[(offset * 2) + i] + 1) % OS8_RX02SS); +#endif + + if ((status = FSioWriteSector(mount, sector, OS8_RX02SS, temp)) == 0) { + ERROR("I/O error on \"%s%o:\"\n", mount->name, unit); + return 0; + } + buffer += OS8_RX02SS_W; + } + return 1; +} + +/*++ + * o s 8 B l o c k P r e s e n t + * + * Check if a block is present in the container file. + * + * Inputs: + * + * mount - pointer to a mounted file system descriptor + * unit - file system unit number + * block - logical block # in the range 0 - N + * + * Outputs: + * + * None + * + * Returns: + * + * 1 if the block is present, 0 otherwise + * + --*/ +int os8BlockPresent( + struct mountedFS *mount, + uint8_t unit, + unsigned int block +) +{ + struct OS8data *data = &mount->os8data; + + return (*data->device->blockPresent)(mount, unit, block); +} + +/*++ + * o s 8 R e a d B l o c k + * + * Read a block from an OS/8 file system. + * + * Inputs: + * + * mount - pointer to a mounted file system descriptor + * unit - file system unit number + * block - logical block # in the range 0 - N + * buf - buffer to receive data, if NULL use the mount + * point specific buffer + * + * Outputs: + * + * The block will be read into the specified buffer. + * + * Returns: + * + * 1 if successful, 0 otherwise + * + --*/ +int os8ReadBlock( + struct mountedFS *mount, + uint8_t unit, + unsigned int block, + void *buf +) +{ + struct OS8data *data = &mount->os8data; + void *buffer = buf == NULL ? data->buf : buf; + + if ((unit >= data->device->filesys) || ((data->valid & (1 << unit)) == 0)) { + ERROR("Invalid device \"%s%o:\"\n", mount->name, unit); + return 0; + } + +#ifdef DEBUG + if ((mount->flags & FS_DEBUG) != 0) + fprintf(DEBUGout, ">> %s%o: (os8) Reading logical block %u\n", + mount->name, unit, block); +#endif + + return (*data->device->readBlock)(mount, unit, block, buffer); +} + +/*++ + * o s 8 W r i t e B l o c k + * + * Write a block to an OS/8 file system. + * + * Inputs: + * + * mount - pointer to a mounted file system descriptor + * unit - file system unit number + * block - logical block # in the range 0 - N + * buf - buffer to receive data, if NULL use the mount + * point specific buffer + * + * Outputs: + * + * None + * + * Returns: + * + * 1 if successful, 0 otherwise + * + --*/ +int os8WriteBlock( + struct mountedFS *mount, + uint8_t unit, + unsigned int block, + void *buf +) +{ + struct OS8data *data = &mount->os8data; + void *buffer = buf == NULL ? data->buf : buf; + + if ((unit >= data->device->filesys) || ((data->valid & (1 << unit)) == 0)) { + ERROR("Invalid device \"%s%o:\"\n", mount->name, unit); + return 0; + } + +#ifdef DEBUG + if ((mount->flags & FS_DEBUG) != 0) + fprintf(DEBUGout, ">> %s%o: (os8) Writing logical block %u\n", + mount->name, unit, block); +#endif + + return (*data->device->writeBlock)(mount, unit, block, buffer); +} + +/*++ + * o s 8 C h e c k D i r e c t o r y + * + * Check if there is sufficient space available for a new permanent entry + * in the directory segment currently in the mount specific buffer. + * + * Inputs: + * + * mount - pointer to a mounted file system descriptor + * + * Outputs: + * + * None + * + * Returns: + * + * 1 if there is sufficient space available, 0 otherwise + * + --*/ +static int os8CheckDirectory( + struct mountedFS *mount +) +{ + struct OS8data *data = &mount->os8data; + uint16_t i, entrysz, entries, extra, off = OS8_DH_SIZE; + + entrysz = OS8_DI_SIZE + (-os8SXT(le16toh(data->buf[OS8_DH_EXTRA]))); + entries = os8Neg(le16toh(data->buf[OS8_DH_ENTRIES])); + extra = os8Neg(le16toh(data->buf[OS8_DH_EXTRA])); + + for (i = 0; i < entries; i++) + if (os8Word(le16toh(data->buf[off + OS8_DI_FNAME1])) != 0) + off += entrysz; + else off += OS8_ED_SIZE; + + return (off + entrysz) < OS8_BLOCKSIZE ? 1 : 0; +} + +/*++ + * o s 8 F i n d S p a c e + * + * Determine which directory segment has available free space. This routine + * may be called to find the "best fit" (i.e. smallest free space >= the + * requested size) or the largest available free space. if successful, + * the directory segment will be loaded in the mount specific buffer. + * + * Inputs: + * + * mount - pointer to a mounted file system descriptor + * unit - file system unit number + * space - # of blocks of free space required, 0 means find + * the largest block of free space + * segment - return directory segment here + * offset - return directory offset here + * start - return starting block # here + * + * Outputs: + * + * The mount specific buffer will be modifed. + * + * Returns: + * + * 1 if free space was found, 0 otherwise + * + --*/ +static int os8FindSpace( + struct mountedFS *mount, + uint8_t unit, + uint16_t space, + uint16_t *segment, + uint16_t *offset, + uint16_t *start +) +{ + struct OS8data *data = &mount->os8data; + int found = 0; + uint16_t dirseg, diroff, dirstart, size = space == 0 ? 0 : 0xFFFF; + uint16_t dirblk = OS8_DSSTART; + + if (os8UnitValid(data, unit)) { + do { + uint16_t i, entrysz, entries, extra, startblk, off = OS8_DH_SIZE; + + if (os8ReadBlock(mount, unit, dirblk, NULL) == 0) + return 0; + + entrysz = OS8_DI_SIZE + (-os8SXT(le16toh(data->buf[OS8_DH_EXTRA]))); + entries = os8Neg(le16toh(data->buf[OS8_DH_ENTRIES])); + extra = os8Neg(le16toh(data->buf[OS8_DH_EXTRA])); + + startblk = os8Word(le16toh(data->buf[OS8_DH_START])); + + for (i = 0; i < entries; i++) { + uint16_t blks; + + if (os8Word(le16toh(data->buf[off + OS8_DI_FNAME1])) == 0) { + blks = os8Neg(le16toh(data->buf[off + OS8_ED_LENGTH])); + + if (space == 0) { + if (blks > size) { + size = blks; + dirseg = dirblk; + diroff = off; + dirstart = startblk; + found = 1; + } + } else { + if (blks >= space) + if (blks < size) { + size = blks; + dirseg = dirblk; + diroff = off; + dirstart = startblk; + found = 1; + } + } + off += OS8_ED_SIZE; + } else { + blks = os8Neg(le16toh(data->buf[off + extra + OS8_DI_LENGTH])); + off += entrysz; + } + startblk += blks; + } + + dirblk = os8Word(le16toh(data->buf[OS8_DH_NEXT])); + } while (dirblk != 0); + + /* + * If we found a suitable entry, load the directory segment and update + * return information. + */ + if (found) { + if (os8ReadBlock(mount, unit, dirseg, NULL) == 0) + return 0; + + *segment = dirseg; + *offset = diroff; + *start = dirstart; + } + } + return found; +} + +/*++ + * o s 8 S l i d e D o w n + * + * Open up a new permanent directory entry at the specified offset within + * a directory segment. This routine assumes thatnthe caller has verified + * that there is sufficient free space at the end of the directory for a new + * entry. The directory segment is in the mount specific buffer. + * + * Inputs: + * + * mount - pointer to a mounted file system descriptor + * off - offset to add the new directory entry + * entries - # of directory entries + * entrysz - size of a permanent directory entry + * remain - return remaining entries here + * + * Outputs: + * + * The mount specific buffer will be modified. + * + * Returns: + * + * None + * + --*/ +static void os8SlideDown( + struct mountedFS *mount, + uint16_t off, + uint16_t entries, + uint16_t entrysz, + uint16_t *remain +) +{ + struct OS8data *data = &mount->os8data; + uint16_t i, j, src = OS8_DH_SIZE, dst = OS8_DH_SIZE; + uint16_t buf[OS8_BLOCKSIZE]; + + /* + * Build an updated directory header + */ + buf[OS8_DH_ENTRIES] = htole16(os8Value(-(entries + 1))); + buf[OS8_DH_START] = data->buf[OS8_DH_START]; + buf[OS8_DH_NEXT] = data->buf[OS8_DH_NEXT]; + buf[OS8_DH_FLAGWD] = htole16(os8Value(0)); + buf[OS8_DH_EXTRA] = data->buf[OS8_DH_EXTRA]; + + for (i = 0; i < entries; i++) { + uint16_t len; + + if (src == off) { + dst += entrysz; + *remain = entries - (i + 1); + } + + len = + os8Word(le16toh(data->buf[src + OS8_DI_FNAME1])) ? entrysz : OS8_ED_SIZE; + + for (j = 0; j < len; j++) + buf[dst++] = data->buf[src++]; + } + + /* + * Now copy the updated directory segment back to the mount speccific buffer + */ + memcpy(data->buf, buf, sizeof(buf)); +} + +/*++ + * o s 8 S l i d e U p + * + * Remove a full or partial directory entry by sliding the remaining entries + * up. The directory segment is in the mount specific buffer. + * + * Inputs: + * + * mount - pointer to a mounted file system descriptor + * dst - offset to entry being removed + * src - offset to first entry to me moved + * entrysz - size of a permanent directory entry + * count - # of entries to "slide up" + * + * Outputs: + * + * The mount specific buffer may be modified + * + * Returns: + * + * None + * + --*/ +static void os8SlideUp( + struct mountedFS *mount, + uint16_t dst, + uint16_t src, + uint16_t entrysz, + uint16_t count +) +{ + struct OS8data *data = &mount->os8data; + uint16_t i, j, len; + + if (count != 0) { + for (i = 0; i < count; i++) { + len = os8Word(le16toh(data->buf[src + OS8_DI_FNAME1])) ? entrysz : OS8_ED_SIZE; + + for (j = 0; j < len; j++) + data->buf[dst++] = data->buf[src++]; + } + } +} + +/*++ + * o s 8 S p l i t D i r e c t o r y + * + * Split the directory segment currently in the mount specific buffer. The + * split attempts to optimize for maximal use of each directory segment + * while using a minimal number of directory segments. + * + * Inputs: + * + * mount - pointer to a mounted file system descriptor + * unit - file system unit number + * segment - directory segment to be split + * + * Outputs: + * + * The mount specific buffer may be modified. + * + * Returns: + * + * 1 if directory segment was successfully split, 0 otherwise + * + --*/ +static int os8SplitDirectory( + struct mountedFS *mount, + uint8_t unit, + uint16_t segment +) +{ + struct OS8data *data = &mount->os8data; + uint16_t inuse = 0; + uint16_t dirblk = OS8_DSSTART; + uint16_t buf[OS8_BLOCKSIZE]; + uint16_t empoff[OS8_BLOCKSIZE], emplen[OS8_BLOCKSIZE]; + uint16_t emptotal = 0, emplast = 0, empcount = 0; + uint16_t i, j, entries, entrysz, extra, startblk, split = 0; + uint16_t off, target, dst = OS8_DH_SIZE; + + /* + * First we need to find an unused directory segment. + */ + do { + inuse |= (1 << dirblk); + + if (os8ReadBlock(mount, unit, dirblk, buf) == 0) + return 0; + + dirblk = os8Word(le16toh(buf[OS8_DH_NEXT])); + } while (dirblk != 0); + + for (i = OS8_DSSTART; i <= OS8_DSLAST; i++) { + if ((inuse & (1 << i)) == 0) { + /* + * Build a new directory segment + */ + buf[OS8_DH_ENTRIES] = 0; + buf[OS8_DH_NEXT] = data->buf[OS8_DH_NEXT]; + buf[OS8_DH_FLAGWD] = htole16(os8Value(0)); + buf[OS8_DH_EXTRA] = data->buf[OS8_DH_EXTRA]; + + entrysz = OS8_DI_SIZE + (-os8SXT(le16toh(data->buf[OS8_DH_EXTRA]))); + entries = os8Neg(le16toh(data->buf[OS8_DH_ENTRIES])); + extra = os8Neg(le16toh(data->buf[OS8_DH_EXTRA])); + + startblk = os8Word(le16toh(data->buf[OS8_DH_START])); + + /* + * Analyze the current directory segment by finding all the empty + * descriptors. + */ + off = OS8_DH_SIZE; + + for (j = 0; j < entries; j++) { + if (os8Word(le16toh(data->buf[off + OS8_DI_FNAME1])) == 0) { + emplen[empcount] = os8Neg(le16toh(data->buf[off + OS8_ED_LENGTH])); + emptotal += emplen[empcount]; + empoff[empcount] = off; + empcount++; + emplast = j == (entries - 1); + + off += OS8_ED_SIZE; + } else off += entrysz; + } + + /* + * We want to avoid "stranding" unused directory entries where there + * is no free space so we apply the following heuristics: + * + * 1. If the last directory entry is "empty" and contains at least + * 50% of the free space, just move this entry to the newly + * created directory segment + * + * 2. Start the new directory segment at the "empty" entry which would + * exceed 50% of the free space. + */ + target = empoff[empcount - 1]; + + if (!emplast || (emplen[empcount - 1] < (emptotal / 2))) { + uint16_t empty = 0; + + for (j = 0; j < empcount; j++) + if ((empty + emplen[j]) >= (emptotal / 1)) { + target = empoff[j]; + break; + } else empty += emplen[j]; + } + + /* + * Find the location of the split point and copy all directory entries + * after this point to the newly created directory segment. + */ + off = OS8_DH_SIZE; + + for (j = 0; j < entries; j++) { + uint16_t blks, size; + + if (off == target) { + split = j + 1; + buf[OS8_DH_START] = htole16(os8Value(startblk)); + } + + if (os8Word(le16toh(data->buf[off + OS8_DI_FNAME1])) != 0) { + blks = os8Neg(le16toh(data->buf[off + extra + OS8_DI_LENGTH])); + size = OS8_DI_SIZE + extra; + } else { + blks = os8Neg(le16toh(data->buf[off + OS8_ED_LENGTH])); + size = OS8_ED_SIZE; + } + + if (split != 0) { + uint16_t k; + + for (k = 0; k < size; k++) + buf[dst++] = data->buf[off + k]; + + buf[OS8_DH_ENTRIES]++; + } + off += size; + startblk += blks; + } + + /* + * Update the new directory segment + */ + buf[OS8_DH_ENTRIES] = htole16(os8Value(-buf[OS8_DH_ENTRIES])); + + if (os8WriteBlock(mount, unit, i, buf) == 0) + return 0; + + /* + * Update the original directory segment + */ + data->buf[OS8_DH_ENTRIES] = htole16(os8Value(-(split - 1))); + data->buf[OS8_DH_NEXT] = htole16(os8Value(i)); + + if (os8WriteBlock(mount, unit, segment, NULL) == 0) + return 0; + + /* + * Directory segment split complete + */ + return 1; + } + } + return 0; +} + +/*++ + * o s 8 M e r g e E m p t y R e g i o n s + * + * Scan a directory segment looking for 2 consecutive empty entries which + * can be merged into a single entry. The directory segment is in the mount + * specific buffer. + * + * Inputs: + * + * mount - pointer to a mounted file system descriptor + * + * Outputs: + * + * The mount specific buffer may be modified + * + * Returns: + * + * 1 if a merge occured, 0 otherwise + * + --*/ +static int os8MergeEmptyRegions( + struct mountedFS *mount +) +{ + struct OS8data *data = &mount->os8data; + uint16_t i, entrysz, entries, off = OS8_DH_SIZE; + + entrysz = OS8_DI_SIZE + (-os8SXT(le16toh(data->buf[OS8_DH_EXTRA]))); + entries = os8Neg(le16toh(data->buf[OS8_DH_ENTRIES])); + + if (entries > 1) { + for (i = 0; i < (entries - 1); i++) { + if (os8Word(le16toh(data->buf[off + OS8_DI_FNAME1])) == 0) { + if (os8Word(le16toh(data->buf[off + OS8_ED_SIZE + OS8_DI_FNAME1])) == 0) { + data->buf[off + OS8_ED_LENGTH] = + htole16(os8Value(le16toh(data->buf[off + OS8_ED_LENGTH]) + + le16toh(data->buf[off + OS8_ED_SIZE + OS8_ED_LENGTH]))); + data->buf[OS8_DH_ENTRIES] = htole16(os8Value(-(entries - 1))); + + os8SlideUp(mount, off + OS8_ED_SIZE, + off + (2 * OS8_ED_SIZE), entrysz, entries - 2); + return 1; + } + } else off += entrysz; + } + } + return 0; +} + +/*++ + * i n f o + * + * Display information about the internal structure of a single file system. + * + * Inputs: + * + * mount - pointer to a mounted file system descriptor + * unit - file system unit number + * + * Outputs: + * + * None + * + * Returns: + * + * None + * + --*/ +static void info( + struct mountedFS *mount, + uint8_t unit +) +{ + struct OS8data *data = &mount->os8data; + unsigned int dirblk = OS8_DSSTART; + + printf("\n%s%o:\n\n", mount->name, unit); + + do { + uint16_t i, entrysz, entries, extra, startblk, off = OS8_DH_SIZE; + + if (os8ReadBlock(mount, unit, dirblk, NULL) == 0) + return; + + entrysz = OS8_DI_SIZE + (-os8SXT(le16toh(data->buf[OS8_DH_EXTRA]))); + entries = os8Neg(le16toh(data->buf[OS8_DH_ENTRIES])); + extra = os8Neg(le16toh(data->buf[OS8_DH_EXTRA])); + + startblk = os8Word(le16toh(data->buf[OS8_DH_START])); + + printf("\nDirectory Segment %d:\n\n", dirblk); + printf(" File Type Length Date Disk Region\n\n"); + + for (i = 0; i < entries; i++) { + char temp1[16], temp2[16]; + uint16_t length; + + if (os8Word(le16toh(data->buf[off + OS8_DI_FNAME1])) != 0) { + uint16_t dateval = 0; + + length = os8Neg(le16toh(data->buf[off + extra + OS8_DI_LENGTH])); + if (extra != 0) + dateval = os8Word(le16toh(data->buf[off + OS8_DI_DATE])); + + printf("%s %4hu %s %4d - %4d\n", + os8FileName(mount, off, temp1), length, + os8Date(dateval, temp2), startblk, startblk + length - 1); + + off += entrysz; + } else { + length = os8Neg(le16toh(data->buf[off + OS8_ED_LENGTH])); + + printf(" %4hu %4d - %4d\n", + os8Neg(le16toh(data->buf[off + OS8_ED_LENGTH])), + startblk, startblk + length - 1); + + off += OS8_ED_SIZE; + } + startblk += length; + } + + dirblk = os8Word(le16toh(data->buf[OS8_DH_NEXT])); + } while (dirblk != 0); +} + +/*++ + * v a l i d a t e + * + * Validate the integrity of an OS/8 file system. OS/8 file systems do not + * include any form of signature on the disk so we have to run a set of + * heuristics to verify the integrity of the file system. + * + * Inputs: + * + * mount - pointer to a mounted file system descriptor + * unit - file system unit number + * files - return # of permanent files here + * freeblks - return # of free blocks here + * addinfo - return # of additional information words here + * + * Outputs: + * + * The mount point specific buffer will be overwritten. + * + * Returns: + * + * 1 if file system is valid, 0 otherwise + * + --*/ +static int validate( + struct mountedFS *mount, + uint8_t unit, + uint16_t *files, + uint16_t *freeblks, + uint16_t *addinfo +) +{ + struct OS8data *data = &mount->os8data; + unsigned int dirblk = OS8_DSSTART; + uint8_t seen[OS8_DSLAST + 1]; + uint16_t extraVal; + + memset(seen, 0, sizeof(seen)); + + do { + uint16_t i, entrysz, entries, flagwd, extra, startblk, off = OS8_DH_SIZE; + + /* + * If we've already seen this directory segment, there must be a loop + */ + if (seen[dirblk] != 0) + return 0; + + if (!os8BlockPresent(mount, unit, dirblk) || + !os8ReadBlock(mount, unit, dirblk, NULL)) + return 0; + + seen[dirblk] = 1; + + entrysz = OS8_DI_SIZE + (-os8SXT(le16toh(data->buf[OS8_DH_EXTRA]))); + entries = os8Neg(le16toh(data->buf[OS8_DH_ENTRIES])); + flagwd = os8Word(le16toh(data->buf[OS8_DH_FLAGWD])); + extra = os8Neg(le16toh(data->buf[OS8_DH_EXTRA])); + + startblk = os8Word(le16toh(data->buf[OS8_DH_START])); + + /* + * The number of additional information words MUST be the same in all + * directory segments. + */ + if (dirblk == OS8_DSSTART) { + extraVal = extra; + *addinfo = extra; + } + + if (extraVal != extra) + return 0; + + /* + * Up to 077 extra words may be allocated to each directory entry. + */ + if ((extra & 07700) != 0) + return 0; + + /* + * The flag word must be either 0 or in the range 01400 - 01777. + */ + if ((flagwd != 0) && ((flagwd & 01400) != 01400)) + return 0; + + /* + * There must be at least one directory entry (an empty file occupying + * all the space) and, at maximum, alternating permanent and empty + * entries (256 / ((N + 5) + 2)) * 2. + */ + if ((entries == 0) || (entries > ((256 / (extra + 7))) * 2)) + return 0; + + /* + * Scan the directory checking for valid block addresses. + */ + for (i = 0; i < entries; i++) { + uint16_t length; + + if (os8Word(le16toh(data->buf[off + OS8_DI_FNAME1])) != 0) { + length = os8Neg(le16toh(data->buf[off + extra + OS8_DI_LENGTH])); + off += entrysz; + *files += 1; + } else { + length = os8Neg(le16toh(data->buf[off + OS8_ED_LENGTH])); + off += OS8_ED_SIZE; + *freeblks += length; + } + startblk += length; + + /* + * Make sure the block addresses are valid for this disk device + */ + if (startblk > data->device->blocks[unit]) + return 0; + } + + dirblk = os8Word(le16toh(data->buf[OS8_DH_NEXT])); + + if ((dirblk != 0) && ((dirblk < OS8_DSSTART) || (dirblk > OS8_DSLAST))) + return 0; + + } while (dirblk != 0); + + return 1; +} + +/*++ + * t o S i x b i t + * + * Convert an alphanumeric character to sixbit encoding and place it into + * the appropriate 6-bit field of a 12-bit value. The character will have + * already been converted to upper-case if needed. + * + * Inputs: + * + * ch - the character to convert + * ptr - pointer to the 12-bit location to update + * which - which 6-bit field to update: + * 0 - high 6-bit field + * 1 - low 6-bit field + * + * Outputs: + * + * None + * + * Returns: + * + * None + * + --*/ +static void toSixbit( + char ch, + uint16_t *ptr, + int which +) +{ + unsigned long i; + + for (i = 0; i < sizeof(sixbit); i++) + if (ch == sixbit[i]) { + if (which == 0) + *ptr = (i << 6) | (*ptr & 0x3F); + else *ptr = (*ptr & 0xFC0) | i; + return; + } +} + +/*++ + * o s 8 P a r s e F i l e s p e c + * + * Parse a character string representing an OS/8 file specification. + * + * Inputs: + * + * ptr - pointer to the file specification string + * spec - pointer to the file specification block + * wildcard - wildcard processing options: + * 0 (OS8_M_NONE) - wildcards not allowed + * 1 (OS8_M_ALLOW) - wildcards allowed + * 2 (OS8_M_NONAME) - wildcards allowed + * if filename + ext not + * present default to *.* + * + * Outputs: + * + * The file specification block will be filled with the file information. + * + * Returns: + * + * 1 if parse is successful, 0 otherwise + * + --*/ +#define P_DONE 0 +#define P_NAME 1 +#define P_EXT 2 + +int os8ParseFilespec( + char *ptr, + struct os8FileSpec *spec, + int wildcard +) +{ + int state = P_NAME; + unsigned int i; + + memset(spec, 0, sizeof(struct os8FileSpec)); + memset(&spec->fname, ' ', sizeof(spec->fname)); + memset(&spec->fext, ' ', sizeof(spec->fext)); + + if (wildcard == OS8_M_NONAME) + if (*ptr == '\0') { + spec->flags = OS8_WC_NAME | OS8_WC_EXT; + return 1; + } + + while (state != P_DONE) { + i = 0; + + switch (state) { + case P_NAME: + while ((*ptr != '\0') && + (*ptr != '.') && + (i < sizeof(spec->fname))) { + char ch = toupper(*ptr++); + + if (isalnum(ch)) { + toSixbit(ch, &spec->name[i >> 1], i & 1); + spec->fname[i++] = ch; + } else { + if (ch == '*') + if (i == 0) { + spec->flags |= OS8_WC_NAME; + break; + } + return 0; + } + } + + switch (*ptr) { + case '\0': + state = P_DONE; + break; + + case '.': + state = P_EXT; + ptr++; + break; + + default: + return 0; + } + break; + + case P_EXT: + while ((*ptr != '\0') && + (i < sizeof(spec->fext))) { + char ch = toupper(*ptr++); + + if (isalnum(ch)) { + toSixbit(ch, &spec->ext, i); + spec->fext[i++] = ch; + } else { + if (ch == '*') + if (i == 0) { + spec->flags |= OS8_WC_EXT; + break; + } + return 0; + } + } + + if (*ptr != '\0') + return 0; + + state = P_DONE; + break; + } + } + return 1; +} + +/*++ + * o s 8 C r e a t e F i l e + * + * Create a new file within the OS/8 file system. + * + * Inputs: + * + * mount - pointer to a mounted file system descriptor + * unit - file system unit number + * spec - pointer to the file specification block + * file - pointer to open file descriptor to receive results + * size - file size in 256-word blocks + * 0 means use the largest free space region + * + * Outputs: + * + * The mount point specific buffer area will be modified + * + * Returns: + * + * 1 if file successfully created, 0 otherwise + * + --*/ +int os8CreateFile( + struct mountedFS *mount, + uint8_t unit, + struct os8FileSpec *spec, + struct os8OpenFile *file, + uint16_t size +) +{ + struct OS8data *data = &mount->os8data; + uint16_t segment, off, start, entries, extra, entrysz, space, left; + + retry: + /* + * Check if suitable free space is available + */ + if (os8FindSpace(mount, unit, size, &segment, &off, &start) == 0) { + ERROR("Free space not available on \"%s%o:\"\n", mount->name, unit); + return 0; + } + + /* + * Check if there is space to create a new directory entry in this + * directory segment. If not, split the directory segment in 2 and retry + * from the beginning. + */ + if (!os8CheckDirectory(mount)) { + if (!os8SplitDirectory(mount, unit, segment)) { + ERROR("No directory space on \"%s%o:\"\n", mount->name, unit); + return 0; + } + goto retry; + } + + entries = os8Neg(le16toh(data->buf[OS8_DH_ENTRIES])); + extra = -os8SXT(le16toh(data->buf[OS8_DH_EXTRA])); + entrysz = OS8_DI_SIZE + extra; + + space = size == 0 ? os8Neg(le16toh(data->buf[off + OS8_ED_LENGTH])) : size; + left = os8Neg(le16toh(data->buf[off + OS8_ED_LENGTH])) - space; + + /* + * Remove the requested space from the empty directory entry. + */ + data->buf[off + OS8_ED_LENGTH] = htole16(os8Value(-left)); + + os8SlideDown(mount, off, entries, entrysz, &file->remain); + + /* + * Build the directory entry + */ + data->buf[off + OS8_DI_FNAME1] = htole16(os8Value(spec->name[0])); + data->buf[off + OS8_DI_FNAME2] = htole16(os8Value(spec->name[1])); + data->buf[off + OS8_DI_FNAME3] = htole16(os8Value(spec->name[2])); + data->buf[off + OS8_DI_EXT] = htole16(os8Value(spec->ext)); + + if (extra != 0) + data->buf[off + OS8_DI_DATE] = htole16(os8Value(data->date)); + + data->buf[off + extra + OS8_DI_LENGTH] = htole16(os8Value(-space)); + + if (os8WriteBlock(mount, unit, segment, NULL) == 0) + return 0; + + /* + * Fill in the open file descriptor + */ + file->name[0] = spec->name[0]; + file->name[1] = spec->name[1]; + file->name[2] = spec->name[2]; + file->ext = spec->ext; + file->creation = data->date; + file->length = space; + + file->segment = segment; + file->offset = off; + file->entrysz = entrysz; + file->extra = extra; + + file->mount = mount; + file->unit = unit; + + file->current = 0; + file->start = start; + file->last = start + space - 1; + + file->wordpos = OS8_BLOCKSIZE; + file->bytepos = OS8_CHECK; + file->written = 0; + + return 1; +} + +/*++ + * o s 8 L o o k u p F i l e + * + * Lookup a specific file within the OS/8 file system. This routine fills in + * an open file descriptor with information about the file and the directory + * entry it resides in. + * + * Inputs: + * + * mount - pointer to a mounted file system descriptor + * unit - file system unit number + * spec - pointer to the file specification block + * file - pointer to open file descriptor to receive results + * + * Outputs: + * + * The mount point specific buffer will be modified + * + * Returns: + * + * 1 if file found, 0 otherwise + * + --*/ +int os8LookupFile( + struct mountedFS *mount, + uint8_t unit, + struct os8FileSpec *spec, + struct os8OpenFile *file +) +{ + struct OS8data *data = &mount->os8data; + unsigned int dirblk = OS8_DSSTART; + + if (os8UnitValid(data, unit)) { + do { + uint16_t i, entrysz, entries, extra, startblk, off = OS8_DH_SIZE; + + if (os8ReadBlock(mount, unit, dirblk, NULL) == 0) + return 0; + + entrysz = OS8_DI_SIZE + (-os8SXT(le16toh(data->buf[OS8_DH_EXTRA]))); + entries = os8Neg(le16toh(data->buf[OS8_DH_ENTRIES])); + extra = os8Neg(le16toh(data->buf[OS8_DH_EXTRA])); + + startblk = os8Word(le16toh(data->buf[OS8_DH_START])); + + for (i = 0; i < entries; i++) { + if (os8Word(le16toh(data->buf[off + OS8_DI_FNAME1])) != 0) { + if ((os8Word(le16toh(data->buf[off + OS8_DI_FNAME1])) == spec->name[0]) && + (os8Word(le16toh(data->buf[off + OS8_DI_FNAME2])) == spec->name[1]) && + (os8Word(le16toh(data->buf[off + OS8_DI_FNAME3])) == spec->name[2]) && + (os8Word(le16toh(data->buf[off + OS8_DI_EXT])) == spec->ext)) { + file->name[0] = spec->name[0]; + file->name[1] = spec->name[1]; + file->name[2] = spec->name[2]; + file->ext = spec->ext; + file->creation = + extra != 0 ? os8Word(le16toh(data->buf[off + OS8_DI_DATE])) : 0; + file->length = os8Neg(le16toh(data->buf[off + extra + OS8_DI_LENGTH])); + + file->segment = dirblk; + file->offset = off; + file->entrysz = entrysz; + file->extra = extra; + file->remain = entries - (i + 1); + + file->mount = mount; + file->unit = unit; + + file->current = 0; + if (file->length != 0) { + file->start = startblk; + file->last = startblk + file->length - 1; + } else file->start = file->last = 0; + + file->wordpos = OS8_BLOCKSIZE; + file->bytepos = OS8_CHECK; + file->written = 0; + + return 1; + } + startblk += os8Neg(le16toh(data->buf[off + extra + OS8_DI_LENGTH])); + off += entrysz; + } else { + startblk += os8Neg(le16toh(data->buf[off + OS8_ED_LENGTH])); + off += OS8_ED_SIZE; + } + } + dirblk = os8Word(le16toh(data->buf[OS8_DH_NEXT])); + } while (dirblk != 0); + } + return 0; +} + +/*++ + * o s 8 U p d a t e F i l e + * + * Update an OS/8 file by writing back the directory entry associated with + * the file. + * + * Inputs: + * + * mount - pointer to a mounted file system descriptor + * unit - file system unit number + * file - pointer to open file descriptor + * + * Outputs: + * + * The mount point specific buffer will be modified + * + * Returns: + * + * None + * + --*/ +void os8UpdateFile( + struct mountedFS *mount, + uint8_t unit, + struct os8OpenFile *file +) +{ + struct OS8data *data = &mount->os8data; + uint16_t off = file->offset; + uint16_t empty = off + file->extra + OS8_DI_SIZE; + uint16_t length = file->current == 0 ? 0 : file->current - file->start; + uint16_t left = file->length - length; + uint16_t space; + + if (os8ReadBlock(mount, unit, file->segment, NULL) == 0) + return; + + /* + * The directory entry is complete except for the # of blocks in use + * which we can now update. + */ + data->buf[off + file->extra + OS8_DI_LENGTH] = htole16(os8Value(-length)); + + /* + * Update the remaining space in the following empty file descriptor. + */ + space = os8Neg(le16toh(data->buf[empty + OS8_ED_LENGTH])) + left; + data->buf[empty + OS8_ED_LENGTH] = htole16(os8Value(-space)); + + if (space == 0) + /* + * Remove the empty directory entry since it now occupies 0 blocks + */ + os8SlideUp(mount, empty, empty + OS8_ED_SIZE, file->entrysz, file->remain); + + os8WriteBlock(mount, unit, file->segment, NULL); +} + +/*++ + * o s 8 R e a d B y t e s + * + * Read a sequence of bytes from an open file. Each pair of 12-bit words in + * the file will be unpacked into 3 bytes. + * + * Inputs: + * + * file - pointer to an open file descriptor + * buf - pointer to a buffer to receive the data + * len - # of bytes of data to read + * + * Outputs: + * + * The buffer will be overwritten by up to "len" bytes + * + * Returns: + * + * # of bytes read from the file (may be less than "len"), 0 if EOF + * + --*/ +int os8ReadBytes( + struct os8OpenFile *file, + char *buf, + int len +) +{ + struct mountedFS *mount = file->mount; + int count = 0; + uint16_t temp1, temp2; + char mask = SWISSET('a') ? 0x7F : 0xFF; + + while (len) { + switch (file->bytepos) { + case OS8_CHECK: + if (file->wordpos == OS8_BLOCKSIZE) { + if (file->current == 0) { + /* + * For an empty file (does OS/8 support this?) + * start == current == last == 0 + */ + if (file->start == 0) + return 0; + + file->current = file->start; + } else { + if (file->current == file->last) + return count; + file->current++; + } + if (os8ReadBlock(mount, file->unit, file->current, file->buffer) == 0) + return 0; + file->wordpos = 0; + } + file->bytepos = OS8_BYTE0; + /* FALLTHROUGH */ + + case OS8_BYTE0: + temp1 = os8Word(le16toh(file->buffer[file->wordpos])); + *buf++ = temp1 & mask; + file->bytepos = OS8_BYTE1; + break; + + case OS8_BYTE1: + temp1 = os8Word(le16toh(file->buffer[file->wordpos + 1])); + *buf++ = temp1 & mask; + file->bytepos = OS8_BYTE2; + break; + + case OS8_BYTE2: + temp1 = os8Word(le16toh(file->buffer[file->wordpos])); + temp2 = os8Word(le16toh(file->buffer[file->wordpos + 1])); + *buf++ = (((temp1 & 0xF00) >> 4) | ((temp2 & 0xF00) >> 8)) & mask; + file->wordpos += 2; + file->bytepos = OS8_CHECK; + break; + } + len--; + count++; + } + return count; +} + +/*++ + * o s 8 W r i t e B y t e s + * + * Write a sequence of bytes to an open file. Each sequence of 3 bytes will + * generate 2 words in the file. + * + * Inputs: + * + * file - pointer to an open file descriptor + * buf - pointer to a buffer to receive the data + * len - # of bytes of data to read + * + * Outputs: + * + * None + * + * Returns: + * + * # of bytes written to the file (may be less than "len"), 0 if errort + * + --*/ +int os8WriteBytes( + struct os8OpenFile *file, + char *buf, + int len +) +{ + struct mountedFS *mount = file->mount; + int count = 0; + + while (len) { + switch (file->bytepos) { + case OS8_CHECK: + if (file->wordpos == OS8_BLOCKSIZE) { + if (file->current == file->last) + return count; + + if (file->current != 0) { + if (os8WriteBlock(mount, file->unit, file->current, file->buffer) == 0) + return 0; + + file->current++; + } else file->current = file->start; + + file->wordpos = 0; + memset(file->buffer, 0, OS8_BLOCKSIZE * sizeof(uint16_t)); + } + file->bytepos = OS8_BYTE0; + /* FALLTHROUGH */ + + case OS8_BYTE0: + file->buffer[file->wordpos++] = os8Value(*buf++); + file->bytepos = OS8_BYTE1; + break; + + case OS8_BYTE1: + file->buffer[file->wordpos++] = os8Value(*buf++); + file->bytepos = OS8_BYTE2; + break; + + case OS8_BYTE2: + file->buffer[file->wordpos - 2] = + os8Value(file->buffer[file->wordpos - 2] | ((*buf & 0xF0) << 4)); + file->buffer[file->wordpos - 1] = + os8Value(file->buffer[file->wordpos - 1] | ((*buf++ & 0xF) << 8)); + file->bytepos = OS8_CHECK; + break; + } + len--; + count++; + file->written++; + } + return count; +} + +/*++ + * o s 8 M o u n t + * + * Verify that the open container file contains 1 or more OS/8 file systems. + * + * Inputs: + * + * mount - pointer to a mounted file system descriptor + * (not in the mounted file system list) + * + * Outputs: + * + * None + * + * Returns: + * + * 1 if 1 or more valid OS/8 file systems + * 0 if no valid OS/8 file system present + * -1 if no valid OS/8 file system present and an error message has + * already been printed + * + --*/ +static int os8Mount( + struct mountedFS *mount +) +{ + struct OS8data *data = &mount->os8data; + struct stat stat; + uint8_t valid = 0; + uint16_t files[8], freeblks[8], extra[8]; + + memset(files, 0, sizeof(files)); + memset(freeblks, 0, sizeof(freeblks)); + memset(extra, 0, sizeof(extra)); + + /* + * Allow access to all filesystems for validation + */ + data->device = NULL; + data->valid = 0xFF; + + if (fstat(fileno(mount->container), &stat) == 0) { + data->blocks = stat.st_size / OS8_BLOCKSIZE; + + if (SWISSET('t')) { + struct OS8device *dev = OS8Devices; + + while (dev->name != NULL) { + if (strcmp(SWGETVAL('t'), dev->name) == 0) { + uint8_t i, count = 0; + + mount->skip = dev->skip; + data->device = dev; + + /* + * Validate all possible file systems. + */ + for (i = 0; i < dev->filesys; i++) + if (validate(mount, i, &files[i], &freeblks[i], &extra[i]) != 0) { + count++; + valid |= 1 << i; + } + + if (valid != 0) { + if (!quiet) + printf("%s: successfully mounted (%d file system%s)\n\n", + mount->name, count, count == 1 ? "" : "s"); + + for (i = 0; i < dev->filesys; i++) + if ((valid & 1 << i) != 0) { + printf("%s%o:\n", mount->name, i); + printf(" Total Blocks: %4d, Free Blocks: %4d\n" + " Files: %4d\n" + " Extra words/directory entry: %d\n", + dev->blocks[i], freeblks[i], files[i], extra[i]); + } + } + + data->valid = valid; + return valid != 0 ? 1 : 0; + } + dev++; + } + fprintf(stderr, "mount: unknown disk type - \"%s\"\n", + SWGETVAL('t')); + return -1; + } else { + fprintf(stderr, "mount: unable to determine disk type," + " use \"-t type\"\n"); + return -1; + } + } + return 0; +} + +/*++ + * o s 8 U m o u n t + * + * Unmount the OS/8 file system(s), releasing any storage allocated. + * + * Inputs: + * + * mount - pointer to the mounted file system descriptor + * + * Outputs: + * + * None + * + * Returns: + * + * None + * + --*/ +static void os8Umount( + struct mountedFS *UNUSED(mount) +) +{ +} + +/*++ + * o s 8 S i z e + * + * Return the size of an OS/8 container file. + * + * Inputs: + * + * None + * + * Outputs: + * + * None + * + * Returns: + * + * Size of the container file in block of default file system size + * + --*/ +static size_t os8Size(void) +{ + if (SWISSET('t')) { + struct OS8device *dev = OS8Devices; + + while (dev->name != NULL) { + if (strcmp(SWGETVAL('t'), dev->name) == 0) + return dev->diskSize; + + dev++; + } + } + return OS8Devices[0].diskSize; +} + +/*++ + * o s 8 N e w f s + * + * Create empty OS/8 file system(s). + * + * Inputs: + * + * mount - pointer to a mounted file system descriptor + * (nit in the mounted file system list) + * size - the sized (in blocks) of the filesystem + * + * Outputs: + * + * None + * + * Returns: + * + * 1 if the file system(s) were successfully created, 0 otherwise + * + --*/ +static int os8Newfs( + struct mountedFS *mount, + size_t UNUSED(size) +) +{ + struct OS8data *data = &mount->os8data; + uint16_t extra = 1; + struct OS8device *dev = OS8Devices; + uint8_t unit; + uint16_t i; + + if (SWISSET('t')) { + while (dev->name != NULL) { + if (strcmp(SWGETVAL('t'), dev->name) == 0) + goto found; + + dev++; + } + dev = OS8Devices; + } + found: + mount->skip = dev->skip; + data->device = dev; + + if (SWISSET('e')) { + char *endptr; + + extra = strtoul(SWGETVAL('e'), &endptr, 10); + if ((extra > 63) || (*endptr != '\0')) + return 0; + } + + for (unit = 0; unit < dev->filesys; unit++) { + data->valid |= (1 << unit); + + for (i = OS8_DSSTART; i <= OS8_DSLAST; i++) { + uint16_t buf[OS8_BLOCKSIZE]; + uint16_t entries = i == OS8_DSSTART ? 1 : 0; + + buf[OS8_DH_ENTRIES] = htole16(os8Value(-entries)); + buf[OS8_DH_START] = htole16(os8Value(OS8_DATA)); + buf[OS8_DH_NEXT] = htole16(os8Value(0)); + buf[OS8_DH_FLAGWD] = htole16(os8Value(0)); + buf[OS8_DH_EXTRA] = htole16(os8Value(-extra)); + + buf[OS8_DH_SIZE + OS8_ED_IND] = htole16(os8Value(0)); + buf[OS8_DH_SIZE + OS8_ED_LENGTH] = + htole16(os8Value(-(dev->blocks[unit] - OS8_DATA))); + + if (os8WriteBlock(mount, unit, i, buf) == 0) + return 0; + } + } + return 1; +} + +/*++ + * o s 8 S e t + * + * Set mount point specific values. + * + * Inputs: + * + * mount - pointer to a mounted file system descriptor + * unit - file system unit number (unused) + * present - file system unit number present (unused) + * + * Outputs: + * + * None + * + * Returns: + * + * None + * + --*/ +static void os8Set( + struct mountedFS *mount, + uint8_t UNUSED(unit), + uint8_t UNUSED(present) +) +{ + struct OS8data *data = &mount->os8data; + int idx = 0; + unsigned long year; + char *endptr; + + while (setCmds[idx] != NULL) { + if (strcmp(words[1], setCmds[idx]) == 0) { + switch (idx) { + case OS8SET_YEAR: + if (args == 3) { + if (strcmp(words[2], "none") == 0) { + data->date = 0; + return; + } + year = strtoul(words[2], &endptr, 10); + if ((*endptr == '\0') && (year <= 7)) { + struct tm tm; + time_t now = time(NULL); + + localtime_r(&now, &tm); + + data->date = (tm.tm_mon + 1) << 8; + data->date |= (tm.tm_mday + 1) << 3; + data->date |= year; + return; + } + } + fprintf(stderr, "os8: Invalid syntax for \"set year\"\n"); + return; + + default: + fprintf(stderr, "os8: \"%s\" not implemented\n", words[1]); + return; + } + } + idx++; + } + fprintf(stderr, "os8: Unknown set command \"%s\"\n", words[1]); +} + +/*++ + * o s 8 I n f o + * + * Display information about the internal structure of the OS/8 file + * system(s). + * + * Inputs: + * + * mount - pointer to the mounted file system descriptor + * unit - file system unit number + * present - file system unit number present + * + * Outputs: + * + * None + * + * Returns: + * + * None + * + --*/ +static void os8Info( + struct mountedFS *mount, + uint8_t unit, + uint8_t present +) +{ + struct OS8data *data = &mount->os8data; + struct OS8device *device = data->device; + + if (present) { + if (os8UnitValid(data, unit)) + info(mount, unit); + } else { + uint16_t i; + + /* + * Display information about all valid units + */ + for (i = 0; i < device->filesys; i++) + if (os8UnitValid(data, i)) + info(mount, i); + } +} + +/*++ + * o s 8 D i r + * + * Produce a full or brief directory listing. + * + * Inputs: + * + * mount - pointer to the mounted file system descriptor + * unit - file system unit number + * fname - pointer to filename string + * + * Outputs: + * + * None + * + * Returns: + * + * None + * + --*/ +static void os8Dir( + struct mountedFS *mount, + uint8_t unit, + char *fname +) +{ + struct OS8data *data = &mount->os8data; + struct os8FileSpec spec; + + if (os8ParseFilespec(fname, &spec, OS8_M_NONAME) == 0) { + fprintf(stderr, "dir: syntax error in file spec \"%s\"\n", fname); + return; + } + + if (os8UnitValid(data, unit)) { + unsigned int dirblk = OS8_DSSTART; + + do { + uint16_t i, entrysz, entries, extra, off = OS8_DH_SIZE; + + if (os8ReadBlock(mount, unit, dirblk, NULL) == 0) + return; + + entrysz = OS8_DI_SIZE + (-os8SXT(le16toh(data->buf[OS8_DH_EXTRA]))); + entries = os8Neg(le16toh(data->buf[OS8_DH_ENTRIES])); + extra = os8Neg(le16toh(data->buf[OS8_DH_EXTRA])); + + for (i = 0; i < entries; i++) { + char temp1[16], temp2[16]; + + if (os8Word(le16toh(data->buf[off + OS8_DI_FNAME1])) != 0) { + if ((spec.flags & OS8_WC_NAME) == 0) + if ((os8Word(le16toh(data->buf[off + OS8_DI_FNAME1])) != spec.name[0]) || + (os8Word(le16toh(data->buf[off + OS8_DI_FNAME2])) != spec.name[1]) || + (os8Word(le16toh(data->buf[off + OS8_DI_FNAME3])) != spec.name[2])) { + off += entrysz; + continue; + } + + if ((spec.flags & OS8_WC_EXT) == 0) + if (os8Word(le16toh(data->buf[off + OS8_DI_EXT])) != spec.ext) { + off += entrysz; + continue; + } + + if (SWISSET('f')) { + uint16_t length, dateval = 0; + + length = os8Neg(le16toh(data->buf[off + extra + OS8_DI_LENGTH])); + if (extra) + dateval = os8Word(le16toh(data->buf[off + OS8_DI_DATE])); + + printf("%s %4hu %s\n", + os8FileName(mount, off, temp1), length, + os8Date(dateval, temp2)); + } else printf("%s\n", os8FileName(mount, off, temp1)); + off += entrysz; + } else off += OS8_ED_SIZE; + } + + dirblk = os8Word(le16toh(data->buf[OS8_DH_NEXT])); + } while (dirblk != 0); + } +} + +/*++ + * o s 8 O p e n F i l e R + * + * Open an OS/8 file for reading. + * + * Inputs: + * + * mount - pointer to the mounted file system descriptor + * unit - file system unit number + * fname - pointer to filename string + * + * Outputs: + * + * None + * + * Returns: + * + * Pointer to open file descriptor, NULL if open fails + * + --*/ +static void *os8OpenFileR( + struct mountedFS *mount, + uint8_t unit, + char *fname +) +{ + struct os8OpenFile *file; + struct os8FileSpec spec; + + if (os8ParseFilespec(fname, &spec, OS8_M_NONE) == 0) { + fprintf(stderr, "Failed to parse filename \"%s\"\n", fname); + return NULL; + } + + if ((file = malloc(sizeof(struct os8OpenFile))) != NULL) { + memset(file, 0, sizeof(struct os8OpenFile)); + + if (os8LookupFile(mount, unit, &spec, file) != 0) { + /* + * Allocate local buffer space for the file. + */ + if ((file->buffer = malloc(mount->blocksz)) == NULL) { + free(file); + return NULL; + } + file->mode = M_RD; + } else { + free(file); + return NULL; + } + } + return file; +} + +/*++ + * o s 8 O p e n F i l e W + * + * Open an OS/8 file for writing. + * + * Inputs: + * + * mount - pointer to the mounted file system descriptor + * unit - file system unit number + * fname - pointer to filename string + * size - estimated file size (in bytes) + * 0 means allocate as much space as possible + * + * Outputs: + * + * None + * + * Returns: + * + * Pointer to open file descriptor, NULL is open fails + * + --*/ +void *os8OpenFileW( + struct mountedFS *mount, + uint8_t unit, + char *fname, + off_t size +) +{ + struct os8OpenFile *file; + struct os8FileSpec spec; + uint16_t blocks = 0; + + if (size != 0) + blocks = (size + ((OS8_BLOCKSIZE * 3) / 2) - 1) / (((OS8_BLOCKSIZE) * 3) / 2); + + if (os8ParseFilespec(fname, &spec, OS8_M_NONE) == 0) { + fprintf(stderr, "Failed to parse filename \"%s\"\n", fname); + return NULL; + } + + if ((file = malloc(sizeof(struct os8OpenFile))) != NULL) { + memset(file, 0, sizeof(struct os8OpenFile)); + + if (os8LookupFile(mount, unit, &spec, file) == 0) { + /* + * Allocate local buffer space for the file.\ + */ + if ((file->buffer = malloc(mount->blocksz)) != NULL) { + memset(file->buffer, 0, mount->blocksz); + + if (os8CreateFile(mount, unit, &spec, file, blocks) == 0) { + ERROR("Failed to create file \"%s\"\n", fname); + free(file->buffer); + free(file); + return NULL; + } else file->mode = M_WR; + } else { + ERROR("Buffer allocation failure for \"%s\"\n", fname); + free(file); + return NULL; + } + } else { + ERROR("File \"%s\" already exists\n", fname);\ + free(file); + return NULL; + } + } else ERROR("Memory allocation failure\n"); + return file; +} + +/*++ + * o s 8 F i l e S i z e + * + * Return an estimate of the size of a currently open file. This routine + * bases the file size on the number of blocks allocated to the file and + * may over-report the actual sized of the file. + * + * Inputs: + * + * filep - pointer to open file descriptor + * + * Outputs: + * + * None + * + * Returns: + * + * Estimate of the file size + * + --*/ +static off_t os8FileSize( + void *filep +) +{ + struct os8OpenFile *file = filep; + + return (file->length * RT11_BLOCKSIZE * 2) / 3; +} + +/*++ + * o s 8 D e l e t e F i l e + * + * Delete a file from an O/8 file system. + * + * Inputs: + * + * filep - pointer to open file descriptor + * fname - pointer to filename string + * + * Outputs: + * + * The mount specific buffer will be modified. + * + * Returns: + * + * None + * + --*/ +static void os8DeleteFile( + void *filep, + char *UNUSED(fname) +) +{ + struct os8OpenFile *file = filep; + struct mountedFS *mount = file->mount; + struct OS8data *data = &mount->os8data; + + if (os8ReadBlock(mount, file->unit, file->segment, NULL) == 0) + return; + + data->buf[file->offset + OS8_DI_FNAME1] = 0; + data->buf[file->offset + OS8_ED_LENGTH] = + data->buf[file->offset + file->extra + OS8_DI_LENGTH]; + data->buf[OS8_DH_ENTRIES] = + htole16(os8Value(-(os8Neg(le16toh(data->buf[OS8_DH_ENTRIES])) - 1))); + + os8SlideUp(mount, file->offset + OS8_ED_SIZE, + file->offset + file->entrysz, file->entrysz, file->remain); + + /* + * Merge any adjacent empty directory entries. + */ + if (os8MergeEmptyRegions(mount) != 0) + os8MergeEmptyRegions(mount); + + if (os8WriteBlock(mount, file->unit, file->segment, NULL) == 0) + return; + + os8CloseFile(file); +} + +/*++ + * o s 8 C l o s e F i l e + * + * Close an open OS/8 file. + * + * Inputs: + * + * filep - pointer to open file descriptor + * + * Outputs: + * + * None + * + * Returns: + * + * None + * + --*/ +static void os8CloseFile( + void *filep +) +{ + struct os8OpenFile *file = filep; + + if (file->mode == M_WR) { + if (file->current != 0) + /* + * Flush the current buffer. + */ + os8WriteBlock(file->mount, file->unit, file->current++, file->buffer); + + os8UpdateFile(file->mount, file->unit, file); + } + + if (file->buffer != NULL) + free(file->buffer); + free(file); +} + +/*++ + * o s 8 R e a d F i l e + * + * Read data from an OS/8 file to a supplied buffer + * + * Inputs: + * + * filep - pointer to open file descriptor + * buf - pointer to buffer + * buflen - length of the supplied buffer + * + * Outputs: + * + * None + * + * Returns: + * + * # of bytes of data read, 0 means EOF or error + * + --*/ +static size_t os8ReadFile( + void *filep, + void *buf, + size_t buflen +) +{ + struct os8OpenFile *file = filep; + + return os8ReadBytes(file, buf, buflen); +} + +/*++ + * o s 8 W r i t e F i l e + * + * Write data to an OS/8 file from a supplied buffer. + * + * Inputs: + * + * filep - pointer to open file descriptor + * buf - pointer to buffer + * buflen - length of the supplied buffer + * + * Outputs: + * + * None + * + * Returns: + * + * # of bytes of data written, 0 means EOF or error + * + --*/ +static size_t os8WriteFile( + void *filep, + void *buf, + size_t buflen +) +{ + struct os8OpenFile *file = filep; + + return os8WriteBytes(file, buf, buflen); +} + +/*++ + * o s 8 F S + * + * Descriptor for accessing OS/8 file systems. + * + --*/ +struct FSdef os8FS = { + NULL, + "os8", + "os8 PDP-8 OS/8 file system\n", + FS_UNITVALID, + OS8_BLOCKSIZE * sizeof(uint16_t), + os8Mount, + os8Umount, + os8Size, + os8Newfs, + os8Set, + os8Info, + os8Dir, + os8OpenFileR, + os8OpenFileW, + os8FileSize, + os8CloseFile, + os8ReadFile, + os8WriteFile, + os8DeleteFile, + NULL, /* No tape support functions */ + NULL, + NULL, + NULL +}; diff --git a/converters/fsio/os8.h b/converters/fsio/os8.h new file mode 100644 index 0000000..4346503 --- /dev/null +++ b/converters/fsio/os8.h @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2019 John Forecast. 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN FORECAST "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 AUTHOR 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. + */ + +#ifndef __OS8_H__ +#define __OS8_H__ + +/* + * General disk layout (each block is 256 12-bit words). + * + * Block + * +---+---+---+---+---+---+---+---+---+---+---+---+ + * 0 | Reserved | + * +---+---+---+---+---+---+---+---+---+---+---+---+ + * 1-6 | Directory segments | + * +---+---+---+---+---+---+---+---+---+---+---+---+ + * 7-12 | Keyboard Monitor | ** + * +---+---+---+---+---+---+---+---+---+---+---+---+ + * 13-15 | User Service Routine | ** + * +---+---+---+---+---+---+---+---+---+---+---+---+ + * 16-25 | Device Handlers | ** + * +---+---+---+---+---+---+---+---+---+---+---+---+ + * 26 | Enter Processor For USR | ** + * +---+---+---+---+---+---+---+---+---+---+---+---+ + * 27-50 | System Scratch Blocks | ** + * +---+---+---+---+---+---+---+---+---+---+---+---+ + * 51-53 | Command Decoder | ** + * +---+---+---+---+---+---+---+---+---+---+---+---+ + * 54-55 | SAVE And DATE Overlays | ** + * +---+---+---+---+---+---+---+---+---+---+---+---+ + * 56 | Monitor Error Routine | ** + * +---+---+---+---+---+---+---+---+---+---+---+---+ + * 57 | CHAIN Processor For USR | ** + * +---+---+---+---+---+---+---+---+---+---+---+---+ + * 60-63 | System ODT | ** + * +---+---+---+---+---+---+---+---+---+---+---+---+ + * 64 | Reserved For System Expansion | ** + * +---+---+---+---+---+---+---+---+---+---+---+---+ + * 65 | CCL Reminiscences | ** + * +---+---+---+---+---+---+---+---+---+---+---+---+ + * 66 | 12K TD8E Resident Code | ** + * +---+---+---+---+---+---+---+---+---+---+---+---+ + * 67 | CCL Overlay | ** + * +---+---+---+---+---+---+---+---+---+---+---+---+ + * Files | ... | + * | ... | + * | ... | + * | ... | + * +---+---+---+---+---+---+---+---+---+---+---+---+ + * End Of Volume + * + * Blocks marked "**" are only reserved on System Devices, on non-System + * Devices, file storage starts at block 7. + * + * Directory Segment Header: + * + * Word + * +---+---+---+---+---+---+---+---+---+---+---+---+ + * 0 | Minus # of entries in this segment | + * +---+---+---+---+---+---+---+---+---+---+---+---+ + * 1 | Starting block # of first file in this segment| + * +---+---+---+---+---+---+---+---+---+---+---+---+ + * 2 | Link to next directory segment | + * +---+---+---+---+---+---+---+---+---+---+---+---+ + * 3 | Flag Word | + * +---+---+---+---+---+---+---+---+---+---+---+---+ + * 4 | Minus # of additional information words | + * +---+---+---+---+---+---+---+---+---+---+---+---+ + * + * Directory Entry: + * + * Word + * +---+---+---+---+---+---+---+---+---+---+---+---+ + * 0 | Sixbit ASCII File Name (chars 1 - 2) | + * +---+---+---+---+---+---+---+---+---+---+---+---+ + * 1 | Sixbit ASCII File Name (chars 3 - 4) | + * +---+---+---+---+---+---+---+---+---+---+---+---+ + * 2 | Sixbit ASCII File Name (chars 5 - 6) | + * +---+---+---+---+---+---+---+---+---+---+---+---+ + * 3 | Sixbit ASCII File Extension (chars 1 - 2) | + * +---+---+---+---+---+---+---+---+---+---+---+---+ + * | N additional information words | + * | ... | + * | ... | + * +---+---+---+---+---+---+---+---+---+---+---+---+ + * N + 4 | Minus file length in blocks | + * +---+---+---+---+---+---+---+---+---+---+---+---+ + * + * If there are additional informations words in the directory, the file + * creation time is stored in word 4 (zero means not available): + * + * +---+---+---+---+---+---+---+---+---+---+---+---+ + * | | | | + * +---+---+---+---+---+---+---+---+---+---+---+---+ + * | | | | | | + * | | | | +-------+ + * | | +---------------+ | + * +-----------+ | Year - 1970 + * | Day of Month (1 - 31) + * Month (1 - 12) + */ + +#define OS8_BLOCKSIZE 256 /* Size of a block on disk (words) */ + +#define OS8_DSSTART 1 /* Start of directory segments */ +#define OS8_DSLAST 6 /* Last directory segment */ +#define OS8_DATA 7 /* Start of data (non-system device) */ + +#define OS8_DH_ENTRIES 0 /* -(# of entries in segment) */ +#define OS8_DH_START 1 /* Block # for segment start */ +#define OS8_DH_NEXT 2 /* Next directory segment */ +#define OS8_DH_FLAGWD 3 /* Flag word */ +#define OS8_DH_EXTRA 4 /* -(# of additional info. words) */ +#define OS8_DH_SIZE 5 /* Size of directory header */ + +#define OS8_DI_FNAME1 0 /* File name (chars 1 - 2) */ +#define OS8_DI_FNAME2 1 /* File name (chars 3 - 4) */ +#define OS8_DI_FNAME3 2 /* File name (chars 5 - 6) */ +#define OS8_DI_EXT 3 /* File extension (1 - 2 chars) */ + /* Additional words go here */ +#define OS8_DI_DATE 4 /* Optional creation date */ +#define OS8_DI_LENGTH 4 /* -(File length in blocks) */ +#define OS8_DI_SIZE 5 /* Default entry size */ + +#define OS8_ED_IND 0 /* Empty directory entry */ +#define OS8_ED_LENGTH 1 /* -(Empty file length in blocks) */ +#define OS8_ED_SIZE 2 /* Entry size */ + +/* + * Device specific definitions + */ +#define OS8_RK05FS_BLKS 3248 /* Blocks in an RK05 file system */ + +#define OS8_RX01SS 128 /* Byte sector size of RX01 floppy */ +#define OS8_RX02SS 256 /* Byte sector size of RX02 floppy */ +#define OS8_RX01SS_W 64 /* Word sector size of RX01 floppy */ +#define OS8_RX02SS_W 128 /* Word sector size of RX02 floppy */ +#define OS8_RX0xNSECT 26 /* Sectors/track on RX01/RX02 */ +#define OS8_RX0xSZ 2002 /* Sectors on an RX01/RX02 */ + +/* + * Structure to describe a filename. Asterisks may be used as wild card + * characters for the 2 components of a filename; namd and extension + */ +struct os8FileSpec { + uint8_t flags; /* Wild card indicators */ + uint16_t name[3]; /* File name (sixbit) */ + uint16_t ext; /* File extension (sixbit) */ + char fname[6]; /* File name (ASCII) */ + char fext[2]; /* File extension (ASCII) */ +}; +#define OS8_WC_NAME 001 /* Wild card in name */ +#define OS8_WC_EXT 002 /* Wild card in extension */ + +#define OS8_M_NONE 0000 /* Wild cards not allowed */ +#define OS8_M_ALLOW 0001 /* Wild cards allowed */ +#define OS8_M_NONAME 0002 /* Wild cards allowed */ + /* If no filename + extension */ + /* present, default to *.* */ +/* + * OS/8 files pack 3 bytes into a pair of 12-bit words. The following states + * are used to pack/unpack bytes. + */ +#define OS8_BYTE0 0 /* Pack/unpack byte 0 */ +#define OS8_BYTE1 1 /* Pack/unpack byte 1 */ +#define OS8_BYTE2 2 /* Pack/unpack byte 2 */ +#define OS8_CHECK 3 /* Check for end of block/EOF */ + +/* + * Structure to define an open file. This is an OS/8 directory entry along + * with sufficient information to be able to write the directory entry back + * to disk. + */ +struct os8OpenFile { + uint16_t name[3]; /* File name */ + uint16_t ext; /* File extension */ + uint16_t creation; /* Creation date */ + uint16_t length; /* File length (blocks) */ + /* End of directory entry */ + uint8_t segment; /* Directory segment */ + uint16_t offset; /* Directory offset */ + uint16_t entrysz; /* Size of directory entries */ + uint16_t extra; /* Extra words in directory entries */ + uint16_t remain; /* Remaining directory entries */ + /* Start of read/write info */ + enum openMode mode; /* Open mode (read/write) */ + struct mountedFS *mount; /* Mounted file system descriptor */ + uint8_t unit; /* File system # */ + uint16_t *buffer; /* Private buffer for file I/O */ + uint16_t start; /* Starting block # */ + uint16_t current; /* Current block # */ + uint16_t last; /* Last block # */ + uint16_t wordpos; /* Current word offset */ + uint8_t bytepos; /* Current byte position */ + off_t written; /* # of bytes written to the file */ +}; + +/* + * Device descriptor + */ +struct OS8device { + char *name; /* Device name */ + uint8_t filesys; /* # of file systems on device */ + size_t diskSize; /* # of blocks on device */ + off_t skip; /* Reserved space at start of disk */ + uint16_t blocks[8]; /* File system sizes */ + int (*blockPresent)(struct mountedFS *, uint8_t, unsigned int); + int (*readBlock)(struct mountedFS *, uint8_t, unsigned int, void *); + int (*writeBlock)(struct mountedFS *, uint8_t, unsigned int, void *); +}; + +/* + * OS/8 specific data area + */ +struct OS8data { + unsigned int blocks; /* Size of container */ + struct OS8device *device; /* Device type */ + uint8_t devices; /* # of "devices" present */ + uint8_t valid; /* Bitmap of valid devices */ + uint16_t date; /* Date for creating files */ + uint16_t buf[OS8_BLOCKSIZE]; /* Disk buffer */ +}; + +#endif diff --git a/converters/fsio/rt11.c b/converters/fsio/rt11.c index fefe73f..fb105e9 100644 --- a/converters/fsio/rt11.c +++ b/converters/fsio/rt11.c @@ -45,8 +45,9 @@ static struct DiskSize { char *name; /* Disk name */ size_t size; /* Disk size */ } rt11DiskSize[] = { - { "rl02", RT11_RL02SZ }, - { "rx20", RT11_RX20SZ }, + { "rl02", RT11_RL02SZ * RT11_BLOCKSIZE }, + { "rx01", RT11_RX0xSZ * RT11_RX01SS }, + { "rx02", RT11_RX0xSZ * RT11_RX02SS }, { NULL, 0 } }; @@ -337,6 +338,39 @@ int rt11MatchRegex( return 0; } +/*++ + * M a p L o g T o P h y s + * + * Map a logical sector address to a physical sector address for an RX01/RX02 + * drive. + * + * Inputs: + * + * sectno - logical sector number + * + * Outputs: + * + * None + * + * Returns: + * + * Physical sector number + * + --*/ +unsigned int MapLogToPhys( + unsigned int sectno +) +{ + unsigned int i, track, sector; + + track = sectno / RT11_RX0xNSECT; + i = (sectno % RT11_RX0xNSECT) << 1; + if (i >= RT11_RX0xNSECT) + i++; + sector = (i + (6 * track)) % RT11_RX0xNSECT; + return sector + (track * RT11_RX0xNSECT); +} + /*++ * r t 1 1 R e a d B l o c k * @@ -367,10 +401,10 @@ int rt11ReadBlock( ) { struct RT11data *data = &mount->rt11data; - void *buffer = buf == NULL ? data->buf : buf; + char *buffer = buf == NULL ? data->buf : buf; int status = 0; - if (PARTITIONVALID(data, unit)) { + if (RT11_PARTITIONVALID(data, unit)) { if (block > data->maxblk[unit]) { ERROR("Attempt to read block (%u) outside file system \"%s%o:\"\n", block, mount->name, unit); @@ -383,7 +417,25 @@ int rt11ReadBlock( mount->name, unit, block); #endif - status = FSioReadBlock(mount, (unit << 16) | block, buffer); + if (data->sectorsz != 0) { + /* + * The sectors making up the disk are sector-interleaved. The algorithm + * below is for RX01/RX02 interleave. Note that all such devices are + * small enough that there can only be a single file system present. + */ + unsigned int count = RT11_BLOCKSIZE / data->sectorsz; + unsigned int sectno = block * count; + + do { + unsigned int sector = MapLogToPhys(sectno); + + status = FSioReadSector(mount, sector, data->sectorsz, buffer); + + count--; + buffer += data->sectorsz; + sectno++; + } while ((count != 0) && (status != 0)); + } else status = FSioReadBlock(mount, (unit << 16) | block, buffer); if (status == 0) ERROR("I/O error on \"%s%o:\"\n", mount->name, unit); @@ -422,10 +474,10 @@ int rt11WriteBlock( ) { struct RT11data *data = &mount->rt11data; - void *buffer = buf == NULL ? data->buf : buf; + char *buffer = buf == NULL ? data->buf : buf; int status = 0; - if (PARTITIONVALID(data, unit)) { + if (RT11_PARTITIONVALID(data, unit)) { if (block > data->maxblk[unit]) { ERROR("Attempt to write block (%u) outside file system \"%s%o:\"\n", block, mount->name, unit); @@ -438,7 +490,25 @@ int rt11WriteBlock( mount->name, unit, block); #endif - status = FSioWriteBlock(mount, (unit << 16) | block, buffer); + if (data->sectorsz != 0) { + /* + * The sectors making up the disk are sector-interleaved. The algorithm + * below is for RX01/RX02 interleave. Note that all such devices are + * small enough that there can only be a single file system present. + */ + unsigned int count = RT11_BLOCKSIZE / data->sectorsz; + unsigned int sectno = block * count; + + do { + unsigned int sector = MapLogToPhys(sectno); + + status = FSioWriteSector(mount, sector, data->sectorsz, buffer); + + count--; + buffer += data->sectorsz; + sectno++; + } while ((count != 0) && (status != 0)); + } else status = FSioWriteBlock(mount, (unit << 16) | block, buffer); if (status == 0) ERROR("I/O error on \"%s%o:\"\n", mount->name, unit); @@ -450,7 +520,8 @@ int rt11WriteBlock( /*++ * r t 1 1 R e a d D i r S e g m e n t * - * Read a directory segment (2 disk blocks) into the mount specific buffer. + * Read a directory segment (2 file system blocks) into the mount specific + * buffer. * * Inputs: * @@ -460,7 +531,7 @@ int rt11WriteBlock( * * Outputs: * - * The directory segment will be read into the mount specific buffer. + * The directory segment will be read into the mount specific buffer. * * Returns: * @@ -680,7 +751,7 @@ static int rt11BestFit( * r t 1 1 M e r g e E m p t y R e g i o n s * * Following a delete operation, check if we can merge empty regions - * together anc compact the directory segment. In the worst case there + * together and compact the directory segment. In the worst case there * can be 3 directory entries involved; , , . If * is deleted we need to collapse all three entries to one. * The directory segment is currently in the mount point specific buffer. @@ -1131,7 +1202,7 @@ int rt11LookupFile( { struct RT11data *data = &mount->rt11data; - if (PARTITIONVALID(data, unit)) { + if (RT11_PARTITIONVALID(data, unit)) { uint16_t entrysz, position, dsseg = 1; do { @@ -1684,6 +1755,26 @@ static int rt11Mount( struct RT11data *data = &mount->rt11data; struct stat stat; + data->sectorsz = 0; + + /* + * Check for device type override. + */ + if (SWISSET('t')) { + if (strcmp("rx01", SWGETVAL('t')) == 0) { + mount->skip = RT11_RX0xNSECT * RT11_RX01SS; + data->sectorsz = RT11_RX01SS; + } + if (strcmp("rx02", SWGETVAL('t')) == 0) { + mount->skip = RT11_RX0xNSECT * RT11_RX02SS; + data->sectorsz = RT11_RX02SS; + } + + if (data->sectorsz == 0) + fprintf(stderr, + "mount: Ignoring unknown disk type \"%s\"\n", SWGETVAL('t')); + } + memset(&data->valid, 0, sizeof(data->valid)); if (fstat(fileno(mount->container), &stat) == 0) { @@ -1727,7 +1818,7 @@ static int rt11Mount( mount->name, validcount, validcount == 1 ? "" : "s"); for (i = 0; (i < 256) && (validcount != 0); i++) - if (PARTITIONVALID(data, i)) { + if (RT11_PARTITIONVALID(data, i)) { uint16_t entrysz, freeblks = 0, dsseg = 1; uint16_t highest = 0; @@ -1756,6 +1847,10 @@ static int rt11Mount( if (!quiet) { char vers[4], *version = NULL; + uint16_t cnt, extra; + + cnt = le16toh(data->buf[RT11_DH_COUNT]); + extra = le16toh(data->buf[RT11_DH_EXTRA]); if (rt11ReadBlock(mount, i, RT11_HOME, NULL) == 0) return 0; @@ -1788,11 +1883,11 @@ static int rt11Mount( 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" + " Directory segments: %2d (Highest in use: %d)\n" " Extra bytes/directory entry: %d\n", - data->maxblk[i] + 1, freeblks, - le16toh(data->buf[RT11_DH_COUNT]), highest, - le16toh(data->buf[RT11_DH_EXTRA])); + data->maxblk[i] + 1, freeblks, cnt, highest, extra); + if (data->sectorsz != 0) + printf(" Sector size: %d\n", data->sectorsz); } validcount--; } @@ -1829,7 +1924,7 @@ static void rt11Umount( /*++ * r t 1 1 S i z e * - * Return the size of and RT-11 container file. + * Return the size of an RT-11 container file. * * Inputs: * @@ -1846,7 +1941,7 @@ static void rt11Umount( --*/ static size_t rt11Size(void) { - size_t size = RT11_MAXPARTSZ - 1; + size_t size = (RT11_MAXPARTSZ - 1) * RT11_BLOCKSIZE; if (SWISSET('t')) { int i = 0; @@ -1859,7 +1954,7 @@ static size_t rt11Size(void) } i++; } - if (size == (RT11_MAXPARTSZ - 1)) + if (size == ((RT11_MAXPARTSZ - 1) * RT11_BLOCKSIZE)) fprintf(stderr, "newfs: Invalid device type \"%s\", using default\n", type); } @@ -1893,7 +1988,43 @@ static int rt11Newfs( { struct RT11data *data = &mount->rt11data; int i; - uint16_t checksum = 0; + uint16_t checksum = 0, extra = 0;; + + /* + * Check for device type override. + */ + if (SWISSET('t')) { + if (strcmp("rx01", SWGETVAL('t')) == 0) { + mount->skip = RT11_RX0xNSECT * RT11_RX01SS; + data->sectorsz = RT11_RX01SS; + } + if (strcmp("rx02", SWGETVAL('t')) == 0) { + mount->skip = RT11_RX0xNSECT * RT11_RX02SS; + data->sectorsz = RT11_RX02SS; + } + } + + /* + * Check for extra bytes on each directory entry, rounded up to nearest + * even value. + */ + if (SWISSET('e')) { + char *endptr; + + extra = strtoul(SWGETVAL('e'), &endptr, 10); + extra = (extra + 1) & ~1; + + if ((extra > 63) || (*endptr != '\0')) { + fprintf(stderr, "newfs: bad -e switch value \"%s\" - ignored\n", + SWGETVAL('e')); + extra = 0; + } + } + + /* + * Remove possible first track + */ + size = ((size * RT11_BLOCKSIZE) - mount->skip) / RT11_BLOCKSIZE; /* * Mark partition 0 as valid @@ -1901,6 +2032,7 @@ static int rt11Newfs( memset(data->valid, 0, sizeof(data->valid)); data->valid[0] = 1; data->maxblk[0] = size; + data->first[0] = RT11_DSSTART; data->filesystems = 1; /* @@ -1931,6 +2063,7 @@ static int rt11Newfs( data->buf[RT11_DH_COUNT] = htole16(RT11_DS_MAX); data->buf[RT11_DH_HIGHEST] = htole16(1); + data->buf[RT11_DH_EXTRA] = htole16(extra); data->buf[RT11_DH_START] = htole16(RT11_DSSTART + (2 * RT11_DS_MAX)); if (i == 1) { @@ -1977,7 +2110,7 @@ static void rt11Info( struct RT11data *data = &mount->rt11data; if (present) { - if (PARTITIONVALID(data, unit)) + if (RT11_PARTITIONVALID(data, unit)) info(mount, unit); } else { uint16_t i, count = data->filesystems; @@ -1986,7 +2119,7 @@ static void rt11Info( * Display information about all valid partitions */ for (i = 0; (i < 256) && (count != 0); i++) - if (PARTITIONVALID(data, i)) { + if (RT11_PARTITIONVALID(data, i)) { info(mount, i); count--; } @@ -2027,7 +2160,7 @@ static void rt11Dir( return; } - if (PARTITIONVALID(data, unit)) { + if (RT11_PARTITIONVALID(data, unit)) { uint16_t entrysz, dsseg = 1; regex_t reg; @@ -2404,7 +2537,7 @@ static size_t rt11ReadFile( * # of bytes of data written, 0 means EOF or error * --*/ -size_t rt11WriteFile( +static size_t rt11WriteFile( void *filep, void *buf, size_t buflen diff --git a/converters/fsio/rt11.h b/converters/fsio/rt11.h index 89528da..6159576 100644 --- a/converters/fsio/rt11.h +++ b/converters/fsio/rt11.h @@ -166,6 +166,9 @@ #define RT11_HOME 1 /* Home block is always 1 */ #define RT11_DSSTART 6 /* Start of directory segs */ #define RT11_BLOCKSIZE 512 /* Size of a data block on disk */ +#define RT11_RX01SS 128 /* Sector size of RX01 floppy */ +#define RT11_RX02SS 256 /* Sector size of RX02 floppy */ +#define RT11_RX0xNSECT 26 /* Sectors/track on RX01/RX02 */ #define RT11_SYSVER_V3A 36521 #define RT11_SYSVER_V04 36434 @@ -181,15 +184,15 @@ #define RT11_VMSSYSID "DECVMSEXCHNG" /* VMS exchange created volume */ /* - * Partition sizes. The last block os a maximum sized partition is unused. - * The minimum size is based on a file system having 1 directory segment and - * 1 data block! Is this reasonable? + * Partition sizes. The last block of a maximum sized partition (32MB) is + * unused. The minimum size is based on a file system having 1 directory + * segment and 1 data block! Is this reasonable? */ #define RT11_MAXPARTSZ 0200000 /* Max 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 */ +#define RT11_RL02SZ 20480 /* Blocks on an RL02 drive */ +#define RT11_RX0xSZ 2002 /* Sectors on an RX01/RX02 */ #define RT11_HB_BBLOCK 0000 /* Bad block replacement tbl */ #define RT11_HB_RESTORE 0102 /* INIT/RESTORE data area */ @@ -287,6 +290,8 @@ struct rt11OpenFile { */ struct RT11data { unsigned int blocks; /* Size of container */ + unsigned int sectorsz; /* Interleave sector size */ + /* 0 if no interleave */ uint16_t filesystems; /* Max # of filesystems */ uint16_t valid[16]; /* Valid partitions */ uint16_t maxblk[256]; /* Max block address */ @@ -294,6 +299,6 @@ struct RT11data { uint16_t buf[512]; /* Disk buffer - enough for a */ /* directory segment */ }; -#define PARTITIONVALID(d, u) ((d->valid[u / 16] & (1 << (u % 16))) != 0) +#define RT11_PARTITIONVALID(d, u) ((d->valid[u / 16] & (1 << (u % 16))) != 0) #endif diff --git a/extracters/ods2/Makefile b/extracters/ods2/Makefile new file mode 100644 index 0000000..f5622ce --- /dev/null +++ b/extracters/ods2/Makefile @@ -0,0 +1,20 @@ +# all of these can be over-ridden on the "make" command line if they don't suit your environment. +TOOL=ods2 +CFLAGS=-O2 -Wall -Wshadow -Wextra -pedantic -Woverflow -Wstrict-overflow +BIN=/usr/local/bin +INSTALL=install +CC=gcc + +$(TOOL): $(TOOL).c access.c cache.c device.c direct.c phynt.c rms.c vmstime.c + $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $(TOOL) $(TOOL).c access.c cache.c device.c direct.c phynt.c rms.c vmstime.c $(LDLIBS) + +.PHONY: clean install uninstall + +clean: + rm -f $(TOOL) + +install: $(TOOL) + $(INSTALL) -p -m u=rx,g=rx,o=rx $(TOOL) $(BIN) + +uninstall: + rm -f $(BIN)/$(TOOL)