FSIO: Disk and Tape container file utility

This commit is contained in:
John Forecast 2019-05-01 06:24:33 -07:00 committed by Mark Pizzolato
parent 18202f1b8d
commit 5117bd4d7c
20 changed files with 11727 additions and 0 deletions

51
converters/fsio/Makefile Normal file
View File

@ -0,0 +1,51 @@
#CFLAGS=-O2 -Wall -Wshadow -Wextra -pedantic -Woverflow -Wstrict-overflow
CFLAGS=-g -Wall -Wshadow -Wextra -pedantic -Woverflow -Wstrict-overflow
DEFINES=-DDEBUG
BIN=/usr/local/bin
MAN=/usr/local/man/man1
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
LIBS=-lreadline
MANPAGE=fsio.1
MANPAGE_DOS=fsio-dos11.1
MANPAGE_RT=fsio-rt11.1
MANPAGE_DOSMT=fsio-dosmt.1
ARCHIVE=fsio.tgz
RELEASEFILES=$(BIN)/$(EXECUTABLE)
RELEASEFILES+=$(MAN)/$(MANPAGE)
RELEASEFILES+=$(MAN)/$(MANPAGE_DOS)
RELEASEFILES+=$(MAN)/$(MANPAGE_RT)
RELEASEFILES+=$(MAN)/$(MANPAGE_DOSMT)
RELEASEFILES+=./fsio.txt ./fsioSimh.txt
$(EXECUTABLE): $(SOURCES) $(INCLUDES) Makefile
$(CC) $(CFLAGS) $(DEFINES) -o $(EXECUTABLE) $(SOURCES) $(LIBS)
.phony: clean install uninstall
clean:
rm -f $(EXECUTABLE)
install: $(EXECUTABLE) $(MANPAGE) $(MANPAGE_DOS) $(MANPAGE_RT)
$(INSTALL) -p -m u=rx,g=rx,o=rx $(EXECUTABLE) $(BIN)
mkdir -p $(MAN)
$(INSTALL) -p -m u=r,g=r,o=r $(MANPAGE) $(MAN)
$(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)
uninstall:
rm -f $(BIN)/$(EXECUTABLE)
rm -f $(MAN)/$(MANPAGE)
rm -f $(MAN)/$(MANPAGE_DOS)
rm -f $(MAN)/$(MANPAGE_RT)
rm -f $(MAN)/$(MANPAGE_DOSMT)
# This assumes that fsio has been "installed" on the current system
archive: $(RELEASEFILES)
tar czvPf $(ARCHIVE) $(RELEASEFILES)

242
converters/fsio/declib.c Normal file
View File

@ -0,0 +1,242 @@
/*
* Copyright (C) 2018 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.
*/
/*
* Routines/data which are common to multiple DEC file systems.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <sys/stat.h>
#include "fsio.h"
char rad50[] = " ABCDEFGHIJKLMNOPQRSTUVWXYZ$.%0123456789";
char *month[12] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
/*
* Table of days/month for both normal and leap years.
*/
unsigned short mnth[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
unsigned short mnthl[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
/*
* ASCII character set
*/
char *Ascii[128] = {
"NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
" BS", " HT", " LF", " VT", " FF", " CR", " SO", " SI",
"DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
"CAN", " EM", "SUB", "ESC", " FS", " GS", " RS", " US",
" ", " ! ", " \" ", " # ", " $ ", " % ", " & ", " \' ",
" ( ", " ) ", " * ", " + ", " , ", " - ", " . ", " / ",
" 0 ", " 1 ", " 2 ", " 3 ", " 4 ", " 5 ", " 6 ", " 7 ",
" 8 ", " 9 ", " : ", " ; ", " < ", " = ", " > ", " ? ",
" @ ", " 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 ", " [ ", " \\ ", " ] ", " ^ ", " _ ",
" ` ", " 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 ", " { ", " | ", " } ", " ~ ", "DEL"
};
/*++
* r 5 0 A s c
*
* Convert a 16-bit rad50 value into 3 ASCII characters.
*
* Inputs:
*
* value - rad50 value to be converted
* outbuf - pointer to 3 character buffer to receive the
* converted output
*
* Outputs:
*
* None
*
* Returns:
*
* None
*
--*/
void r50Asc(
uint16_t value,
char *outbuf
)
{
outbuf[2] = rad50[value % 050];
value /= 050;
outbuf[1] = rad50[value % 050];
outbuf[0] = rad50[value / 050];
}
/*++
* r 5 0 A s c N o S p a c e
*
* Convert a 16-bit rad50 value into up to 3 ASCII characters. Spaces are
* dropped from the conversion.
*
* Inputs:
*
* value - rad50 value to be converted
* outbuf - pointer to 3 character buffer to receive the
* converted output
*
* Outputs:
*
* None
*
* Returns:
*
* None
*
--*/
void r50AscNoSpace(
uint16_t value,
char *outbuf
)
{
uint16_t value2 = value / 050;
/*
* The rad50 representation of ' ' is zero.
*/
if ((value2 / 050) != 0)
*outbuf++ = rad50[value2 / 050];
if ((value2 % 050) != 0)
*outbuf++ = rad50[value2 % 050];
if ((value % 050) != 0)
*outbuf++ = rad50[value % 050];
}
/*++
* a s c R 5 0
*
* Convert 3 ASCII characters into a single 16-bit rad50 value. If an input
* character is not in the rad50 character set it is quietly converted to '%'.
*
* Inputs:
*
* inbuf - pointer to the buffer with the 3 characters
*
* Outputs:
*
* None
*
* Returns:
*
* rad50 value for the 3 characters
*
--*/
uint16_t ascR50(
char *inbuf
)
{
uint16_t value;
char *ptr;
if ((ptr = strchr(rad50, toupper(*inbuf++))) == NULL)
ptr = strchr(rad50, '%');
value = (ptr - rad50) * 03100;
if ((ptr = strchr(rad50, toupper(*inbuf++))) == NULL)
ptr = strchr(rad50, '%');
value += (ptr - rad50) * 050;
if ((ptr = strchr(rad50, toupper(*inbuf++))) == NULL)
ptr = strchr(rad50, '%');
value += ptr - rad50;
return value;
}
/*++
* d o s 1 1 D a t e
*
* Convert a DOS/BATCH-11 date value into an ASCII string.
*
* Inputs:
*
* value - DOS/BATCH-11 date value
* buf - buffer to receive the string (requires 12 bytes)
*
* Outputs:
*
* None
*
* Returns:
*
* Pointer to the date string
*
--*/
char *dos11Date(
uint16_t value,
char *buf
)
{
unsigned short year, doyr, leapyr;
unsigned short *table;
/*
* The DOS/BATCH-11 date format is documented as having 3 reserved bit.
* Version 9.20C (and later?) makes use of these bits to allow dates up
* to 2035. Unfortunately, the date input routine only allows up to 1999!
*/
year = 1970 + value / 1000;
doyr = value % 1000;
leapyr = ((year % 4) == 0) && (year != 2000);
table = leapyr ? mnthl : mnth;
/*
* Check for valid day of year.
*/
if (doyr < (leapyr ? 366 : 365)) {
int i = 0;
while (doyr > table[i])
doyr -= table[i++];
sprintf(buf, "%2d-%s-%4d", doyr, month[i], year);
} else strcpy(buf, "xx-yyy-xxxx");
return buf;
}

40
converters/fsio/declib.h Normal file
View File

@ -0,0 +1,40 @@
/*
* Copyright (C) 2018 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 __DECLIB_H__
#define __DECLIB_H__
extern char rad50[];
extern char *month[];
extern char *Ascii[];
extern void r50Asc(uint16_t, char *);
extern void r50AscNoSpace(uint16_t, char *);
extern uint16_t ascR50(char *);
extern char *dos11Date(uint16_t, char *);
#endif

2523
converters/fsio/dos11.c Normal file

File diff suppressed because it is too large Load Diff

292
converters/fsio/dos11.h Normal file
View File

@ -0,0 +1,292 @@
/*
* Copyright (C) 2018 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 __DOS11_H__
#define __DOS11_H__
/*
* General disk layout:
*
* Block
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* 0 | Reserved for Bootstrap |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* 1 | MFD Block #1 |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* 2 | UFD Block #1 |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* ... | |
* | User linked files |
* | & |
* | other UFD blocks |
* | |
* |-- --|
* | User contiguous files |
* | |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* X-n | MFD Block #2 |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* X-n-1 | Bitmap Block #1 |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* ... | |
* | |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* X | Bitmap Block #n |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
*
* MFD Block 1:
*
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | Block # of MFD Block 2 |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | Interleave factor |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | Bitmap start block # |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | Bitmap #1 block # |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | Bitmap #2 block # |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | |
* | ... |
* | |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | Bitmap #N block # |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | 0 |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | |
*
* MFD Block 2 - N:
*
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | Link to next MFD block or 0 |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | Group code | User code |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | UFD start block # |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | # of words in UFD entry |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | 0 |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | Repeat above 4 words |
* | for each UFD |
* | |
*
* UFD Block:
*
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | Link to next UFD block or 0 |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | File |
* +- -+
* | name |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | Extension |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* |Typ| Rsvd | Creation Date |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | Next free byte |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | Start block # |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | Length (in # of blocks) |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | Last block written |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* |LCK| Usage count | Protection code |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
*
* Bitmap Block:
*
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | Link to next bitmap block |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | Map # |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | # of words of map |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | Link to first bitmap block |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | Map for blocks 0 - 17 |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | Map for blocks 20 - 37 |
* | ... |
* | |
*/
/*
* The MFD and UFD directories can be treated as an array of 16-bit values.
* The following offsets describe each structure.
*/
#define BOOT_BLOCK 0 /* Reserved for bootstrap */
#define MFD1_BLOCK 1 /* MFD always at block 1*/
#define MFD1_MFD2BLOCK 0
#define MFD1_INTERLEAVE 1
#define MFD1_BMSTART 2
#define MFD1_BM1 3
#define MFD2_LINK 0
#define MFD2_HEADER 1
#define MFD2_UFDUIC 0
#define MFD2_UFDSTART 1
#define MFD2_UFDSIZE 2
#define MFD2_UFDZERO 3
#define MFD2_SIZE 4
#define UFD_LINK 0
#define UFD_HEADER 1
#define UFD_FILENAME 0
#define UFD_EXTENSION 2
#define UFD_CREATION 3
#define UFD_TYPE 0100000
#define UFD_TYPELINKED 0000000
#define UFD_TYPECONTIGUOUS 0100000
#define UFD_DATE 0077777
#define UFD_NEXTFREEBYTE 4
#define UFD_FILESTART 5
#define UFD_FILELENGTH 6
#define UFD_LASTBLOCKWRITTEN 7
#define UFD_LUP 8
#define UFD_LOCK 0100000
#define UFD_USAGE 0077400
#define UFD_PROT 0000377
#define UFD_LEN 9 /* 9 word in each entry */
#define MAP_LINK 0
#define MAP_MAP 1
#define MAP_WORDS 2
#define MAP_FIRST 3
#define MAP_BMSTART 4
#define MAP_LEN 60 /* 60 words in each entry */
#define MAP_BLOCKS (MAP_LEN * 16)
#define FILE_LINK 0
/*
* Block sizes for each supported disk drive
*/
#define BLOCKSIZE_RC11 64
#define BLOCKSIZE_RF11 64
#define BLOCKSIZE_RK11 256
#define BLOCKSIZE_RP03 512
/*
* Max # of UIC's (UFDs) for each supported disk drive
*/
#define UICCOUNT_RC11 15
#define UICCOUNT_RF11 15
#define UICCOUNT_RK11 63
#define UICCOUNT_RP03 127
/*
* Compute end of directory block, given the size of each directory entry.
* This macro is good for both MFDs and UFDs.
*/
#define EODIR(m, sz) ((m->blocksz / 2) - (sz - 1))
/*
* The logical block size will depend on the type of the disks. Disks smaller
* than a RK05 (RS11/RS64) will use 64 word blocks while disks larger than
* a RK05 (RP03) will use 512 word blocks. The default will be for an 256
* word blocks for a RK05.
*/
#define DISKSIZE_RK05 4800
/*
* Structure to describe a filename and associated UIC. Asterisks may be used
* as wild card characters for the 4 components of a filename; name, extension,
* group number and user number.
*/
struct dos11FileSpec {
uint8_t flags; /* Wild card indicators */
uint16_t name[2]; /* File name */
uint16_t ext; /* Extension */
unsigned char user; /* User number */
unsigned char group; /* Group number */
};
#define DOS11_WC_NAME 0001 /* Wild card name */
#define DOS11_WC_EXT 0002 /* Wild card extension */
#define DOS11_WC_GROUP 0004 /* Wild card group number */
#define DOS11_WC_USER 0010 /* Wild card user number */
#define DOS11_M_NONE 0000 /* Wild cards not allowed */
#define DOS11_M_ALLOW 0001 /* Wild cards allowed */
#define DOS11_M_NONAME 0002 /* Wild cards allowed */
/* If no filename + extension */
/* present, default to *.* */
/*
* Structure to define an open file. This is a DOS-11 UFD entry along with
* sufficient information to be able to write the directory entry back
* to disk.
*/
struct dos11OpenFile {
uint16_t name[2]; /* File name */
uint16_t ext; /* Extension */
uint16_t creation; /* Type + creation date */
uint16_t nfb; /* Next free byte */
uint16_t start; /* Start block # */
uint16_t length; /* Length in blocks */
uint16_t last; /* Last block written */
uint16_t lup; /* Lock, usage + protection */
/* End of directory entry */
uint16_t ufdblk; /* UFD block # */
uint16_t ufdoffset; /* Offset within UFD */
/* Start of read/write info */
enum openMode mode; /* Open mode (read/write) */
struct mountedFS *mount; /* Mounted file system descriptor */
char *buffer; /* Private buffer for file I/O */
uint16_t current; /* Current block */
uint16_t nab; /* Next available byte */
uint16_t eob; /* End of buffer */
};
/*
* DOS-11 specific data area. Some fields are sized for the worst case -
* RP03 disk pack with 65535 blocks of 1024 bytes each.
*/
struct DOS11data {
unsigned int blocks; /* # of blocks in file system */
uint16_t bitmaps; /* # of bitmaps */
uint16_t bmblk[128]; /* Bitmap block addresses */
uint16_t bmindex; /* Current bitmap in buffer */
uint16_t bmscan; /* Start bitmap scans here */
uint8_t bmdirty; /* 0 => clean, 1 => dirty */
uint16_t bmbuf[512]; /* Buffer for bitmap access */
uint16_t buf[512]; /* Disk buffer */
/*
* Settable parameters
*/
uint8_t group; /* Default group # */
uint8_t user; /* Default user # */
};
#endif

1321
converters/fsio/dosmt.c Normal file

File diff suppressed because it is too large Load Diff

101
converters/fsio/dosmt.h Normal file
View File

@ -0,0 +1,101 @@
/*
* Copyright (C) 2018 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 __DOSMT_H__
#define __DOSMT_H__
#include "tape.h"
/*
* DOS-11 magtapes contain data records of 512 bytes each.
*/
#define DOSMTRCLNT 512
/*
* DOS/BATCH-11 magtape file header.
*/
struct dosmthdr {
uint16_t fname[2]; /* first 6 chars of filename (RAD50) */
uint16_t ext; /* 3 char file extension (RAD50) */
uint8_t prog; /* programmer # (octal) */
uint8_t proj; /* project # (octal) */
uint16_t prot; /* protection code (octal) */
uint16_t date; /* (year-1970) * 1000 + day of year */
uint16_t fname3; /* optional, char 7 - 9 of name */
};
/*
* Structure to describe a filename and associated UIC. Asterisks may be used
* as wild card characters for the 4 components of a filename; name, extension,
* group number and user number.
*/
struct dosmtFileSpec {
uint8_t flags; /* Wild card indicators */
uint16_t name[3]; /* File name */
uint16_t ext; /* Extension */
unsigned char proj; /* Project number */
unsigned char prog; /* Programmer number */
};
#define DOSMT_WC_NAME 0001 /* Wild card name */
#define DOSMT_WC_EXT 0002 /* Wild card extension */
#define DOSMT_WC_PROJ 0004 /* Wild card project number */
#define DOSMT_WC_PROG 0010 /* Wild card programmer number */
#define DOSMT_M_NONE 0000 /* Wild cards not allowed */
#define DOSMT_M_ALLOW 0001 /* Wild cards allowed */
#define DOSMT_M_NONAME 0002 /* Wild cards allowed */
/* If no filename + extension */
/* present, default to *.*[*,*] */
/*
* Structure to define an open file.
*/
struct dosmtOpenFile {
enum openMode mode; /* Open mode (read/write) */
struct mountedFS *mount; /* Mounted file system descriptor */
char buf[DOSMTRCLNT];/* Private buffer for file I/O */
uint16_t nextb; /* Next byte to use */
uint8_t tm; /* Tape mark has been read */
uint8_t error; /* Error has been detected */
};
/*
* DOS-11 magtape specific data area.
*/
struct DOSMTdata {
uint8_t buf[DOSMTRCLNT];
off_t eot; /* Logical end-of-tape */
/*
* Settable parameters
*/
uint8_t proj; /* project # */
uint8_t prog; /* programmer # */
uint8_t prot; /* protection code */
#define FS_DOSMTEXT 0x0100 /* Write extended file headers */
};
#endif

View File

@ -0,0 +1,50 @@
.TH FSIO-DOS11 1 "December 28,2018" "FFS I/O - DOS-11"
.SH NAME
fsio-dos11 \- Foreign File System I/O - DOS-11
.br
.SH DESCRIPTION
\fBfsio\fP allows access to DOS-11 file systems using the file system type
"\fIdos11\fP"
.br
.SH WILDCARD CHARACTERS
The wildcard character \fI'*'\fP may be used to match filename, extension,
group number and user number. Wildcard characters are only valid with the
\fIdir\fP command.
.br
.SH NEWFS OPERATION
\fInewfs\fP creates a blank RK05 image (2.5MB, 4800 blocks) with no UFD
entries.
.SH SET OPERATION
The following \fIset\fP commands are supported:
.br
.TP
.B "\fIuic\fP [group,user]"
Sets the default UIC for file access (was [1,1] after mount).
.br
.RS
.RS
.B "\fIgroup\fP \- Group number (octal 1 - 377)"
.br
.B "\fIuser\fP \- User number (octal 1 - 377)"
.br
.RE
.RE
.TP
.B "\fIufd\fP [group,user]"
Creates an empty UFD and sets default UIC for file access.
.br
.RS
.RS
.B "\fIgroup\fP \- Group number (octal 1 - 377)"
.br
.B "\fIuser\fP \- User number (octal 1 - 377)"
.br
.RE
.RE
.SH SEE ALSO
.BR fsio (1),
.BR fsio-rt11 (1)
.BR fsio-dosmt (1)
.SH AUTHOR
John Forecast, <john@forecast.name>
.br

View File

@ -0,0 +1,110 @@
.TH FSIO-DOSMT 1 "January 28,2019" "FFS I/O - DOS-11 magtape"
.SH NAME
fsio-dosmt \- Foreign File System I/O - DOS-11 magtape
.br
.SH DESCRIPTION
\fBfsio\fP allows access to DOS-11 magtapes using the file system type
"\fIdosmt\fP"
.br
.SH WILDCARD CHARACTERS
The wildcard character \fI'*'\fP may be used to match filename, extension,
group number and user number. Wildcard characters may be used with the
\fIdir\fP command to limit the number of files being displayed. They may also
be used with commands to read files; \fIdump\fP, \fItype\fP and \fIcopy\fP
(only the source file for \fIcopy\fP), in which case the next file which
matches the wildcard character(s) will be used for input. As a consequence
of this, the empty filename may be used to select the next file on the tape,
so:
.RS
fsio> type mt:
.RE
will type the next file on the tape.
.br
.SH MOUNT OPERATION
DOS/BATCH-11 defined magtape headers to be 14 bytes long with 2 bytes unused.
Some tapes use these unused bytes to add an additional 3 characters to the
filename (e.g. ABCDEFGHI.EXT rather than ABCDEF.EXT). By default, fsio will
ignore these unused bytes. If the \fI-x\fP switch is used on the \fImount\fP
command, fsio will make use of the extra 3 characters on file lookup,
directory listing and file creation.
.SH NEWFS OPERATION
\fInewfs\fP creates an empty (zero length) file.
.SH SET OPERATION
The following \fIset\fP commands are supported:
.br
.TP
.B "\fIuic\fP [proj,prog]"
Sets the default UIC for file access (was [1,1] after mount).
.br
.RS
.RS
.B "\fIproj\fP \- Project number (octal 1 - 377)"
.br
.B "\fIprog\fP \- Programmer number (octal 1 - 377)"
.br
.RE
.RE
.TP
.B "\fIprot\fP <code>"
Set the default protection code when writing new files to magtape.
.br
.RS
.RS
.B "\fIprot\fP \- Protection code (octal 0 - 377)"
.br
.RE
.RE
.SH INFO OPERATION
\fIinfo\fP displays where the tape is currently positioned. If the tape is
not positioned at the end and it is not an empty tape, the name of the next
file after the current position will be displayed.
.SH NOTES
DOS-11 magtapes differ from disk-based file systems in a number of ways:
.br
.RS
1. Filenames are not unique on a magtape.
.br
2. The tape has to be positioned before writing a new file to
avoid over-writing an existing file.
.br
.RE
Assuming a magtape is mounted on mt: and has 5 files on it:
.br
The sequence:
.br
.RS
fsio> rewind mt:
.br
fsio> copy -a a.b mt:c.d
.br
.RE
will result in the tape containing a single file (c.d), while the sequence:
.br
.RS
fsio> eom mt:
.br
fsio> copy -a a.b mt:c.d
.br
.RE
will result in the tape containing 6 files (the original 5 followed by c.d).
.br
By using skipf/skipr commands it is possible to position the tape just past
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-dos11 (1)
.BR fsio-rt11 (1)
.SH AUTHOR
John Forecast, <john@forecast.name>
.br

View File

@ -0,0 +1,42 @@
.TH FSIO-RT11 1 "December 28,2018" "FFS I/O - RT-11"
.SH NAME
fsio-rt11 \- Foreign File System I/O - RT-11
.br
.SH DESCRIPTION
\fBfsio\fP allows access to RT-11 file systems using the file system type
"\fIrt11\fP"
.br
.SH WILDCARD CHARACTERS
The wildcard character \fI'%'\fP may be used to match a single character in a
filename or type. The wildcard character \fI'*'\fP may be used to match
zero or more characters in a filename or type. Wildcard characters are only
valid with the \fIdir\fP command.
.br
.SH NEWFS OPERATION
\fInewfs\fP creates a blank MSCP image (32MB, 65535 blocks) with a single
partition which can be accessed via the DU device (rq in SIMH). If the
\fI"-t type"\fP switch is present, a smaller container file will be created
depending on the type of device specified:
.br
.TP
\fIrl02\fP \- RL02 image (10MB, 20480 blocks)
.br
.TP
\fIrx20\fP \- RX20 image (512KB, 1024 blocks)
.br
.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
handle ^Z in the middle of a formatted ASCII file. The \fI"-p"\fP switch
may be used on a copy operation to skip writing the ^Z and pad the file with
NULLs up to the next block boundary if this occurs.
.br
.SH SET OPERATION
No \fIset\fP commands are currently supported.
.SH SEE ALSO
.BR fsio (1),
.BR fsio-dos11 (1)
.BR fsio-dosmt (1)
.SH AUTHOR
John Forecast, <john@forecast.name>
.br

334
converters/fsio/fsio.1 Normal file
View File

@ -0,0 +1,334 @@
.TH FSIO 1 "December 25,2018" "Foreign File System I/O"
.SH NAME
fsio \- Foreign File System I/O
.SH SYNOPSIS
.B fsio
[
.B \-qv
]
[
.I cmdfile
]
.br
.SH DESCRIPTION
\fBfsio\fP is a utility for manipulating foreign file systems within container
files used by various emulators such as
.B SIMH.
If cmdfile is given, commands will be read and executed from the command file
otherwise it will prompt the user for commands with \fBfsio> \fP
.br
.TP
\fB-q\fP - Be quiet, do not output unsolicited text during processing
.TP
\fB-v\fP - Echo each command as it is read from a command file
.br
.TP
Each command occupies one line and has a common format:
.br
.RS
.TP
verb [switches] args ...
.RE
.TP
The following verbs are supported:
.br
.B "\fImount\fP \- make a container file available to fsio"
.br
.B "\fIumount\fP \- remove knowledge of a container file from fsio"
.br
.B "\fInewfs\fP \- create and new container and empty file system"
.br
.B "\fIset\fP \- set parameters on a mounted file system"
.br
.B "\fIinfo\fP \- display information about the container file system"
.br
.B "\fIdir\fP \- list a directory"
.br
.B "\fIdump\fP \- dump a file in hex or octal"
.br
.B "\fIcopy\fP \- copy a single file"
.br
.B "\fItype\fP \- type a file on the terminal"
.br
.B "\fIdelete\fP \- delete a file"
.br
.B "\fIstatus\fP \- display currently mounted file systems"
.br
.B "\fIdo\fP \- echo and execute commands from a file"
.br
.B "\fIhelp\fP \- display help on using fsio"
.br
.B "\fIexit\fP \- terminate fsio (quit is an alias for exit)"
.br
.TP
The following commands are only accepted by file systems which are on magtape devices:
.br
.B "\fIrewind\fP \- position the tape to the start of the data stream"
.br
.B "\fIeom\fP \- position the tape to the end of the data stream"
.br
.B "\fIskipf\fP \- position the tape by skipping forward over files"
.br
.B "\fIskipr\fP \- position the tape by skipping backward over files"
.br
.SH COMMANDS
.TP
.B "\fImount\fP [-drx] dev[:] file type"
Make the container file available to fsio.
.br
.RS
.RS
.B "\fI\-d\fP \- generate debug output on stdout"
.br
.B " Use environment variable \fIFSioDebugLog\fP to"
.br
.B " override stdout"
.br
.B " Only available if built with DEBUG enabled"
.br
.B "\fI\-r\fP \- mount file system read-only"
.br
.B "\fI\-x\fP \- dosmt will use extended filenames when writing"
.br
.B "\fIdev[:]\fP \- user supplied name for the mount"
.br
.B "\fIfile\fP \- name of the container file"
.br
.B "\fItype\fP \- type of container file system"
.br
.RE
.RE
.TP
.B "\fIumount\fP dev[:]"
Remove knowledge of the container file from fsio.
.br
.RS
.RS
.B "\fIdev[:]\fP \- name supplied on a previous mount"
.RE
.RE
.TP
.B "\fInewfs\fP [-t type] 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 "\fIfile\fP \- name of the container file"
.br
.B "\fItype\fP \- type of container file system"
.br
.RE
.RE
.TP
.B "\fIset\fP dev: args ..."
Set parameters on a mounted file system.
.br
.RS
.RS
.B "\fIdev:\fP \- name supplied on a previous mount"
.br
.B "\fIargs ...\fP\- arguments are passed on to the file system"
.br
.RE
.RE
.TP
.B "\fIinfo\fP dev:"
Display information about the file system(s) within the container file.
.br
.RS
.RS
.B "\fIdev:\fP \- name supplied on a previous mount"
.RE
.RE
.TP
.B "\fIdir\fP [-fn] dev:dirspec"
List the contents of a specific directory.
.br
.RS
.RS
.B "\fI\-f\fP \- display a full (vs. brief) directory"
.br
.B "\fI\-n\fP \- don't rewind tape before listing directory"
.br
.B "\fIdev:\fP \- name supplied on a previous mount"
.br
.B "\fIdirspec\fP \- filespec to display, may include wildcards"
.br
.RE
.RE
.TP
.B "\fIdump\fP [-bcdnwx] dev:filespec"
Dump the contents of the file in octal, hex or characters.
.br
.RS
.RS
.B "\fI\-b\fP \- dump byte (8-bits) at a time"
.br
.B "\fI\-c\fP \- dump in character format"
.br
.B "\fI\-d\fP \- dump double-word (32-bits) at a time"
.br
.B "\fI\-w\fP \- dump word (16-bits) at a time"
.br
.B "\fI\-x\fP \- dump in hex format (default is octal)"
.br
.B "\fI\-n\fP \- don't rewind magtape before looking for file"
.br
.B "\fIdev:\fP \- name supplied on a previous mount"
.br
.B "\fIdirspec\fP \- filespec to dump"
.br
.RE
.RE
.TP
.B "\fIcopy\fP [-anpc blks] dev1:srcfile dev2:dstfile"
Copy a file.
.br
.RS
.RS
.B "\fI\-a\fP \- copy in ASCII mode (translates line endings)"
.br
.B "\fI\-p\fP \- pad the file with NULLs"
.br
.B "\fI\-n\fP \- don't rewind magtape before looking for input file"
.br
.B "\fI\-c blks\fP \- make contiguous file of specified size"
.br
.B "\fIdev1:\fP \- name supplied on a previous mount"
.br
.B "\fIsrcfile\fP \- source file to copy"
.br
.B "\fIdev2:\fP \- name supplied on a previous mount"
.br
.B "\fIdstfile\fP \- destination file to copy"
.br
.RE
.RE
.TP
.B "\fItype\fP [-n] dev:filespec"
Types the contents of the file on stdout.
.br
.RS
.RS
.B "\fI\-n\fP \- don't rewind magtape before looking for file"
.br
.B "\fIdev:\fP \- name supplied on a previous mount"
.br
.B "\fIfilespec\fP\- filespec to type"
.br
.RE
.RE
.TP
.B "\fIdelete\fP dev:filespec"
Deletes the specified file.
.br
.RS
.RS
.B "\fIdev:\fP \- name supplied on a previous mount"
.br
.B "\fIfilespec\fP\- filespec to delete"
.br
.RE
.RE
.TP
.B "\fIstatus\fP"
Displays the currently mounted file system(s).
.br
.TP
.B "\fIdo\fP [-q] cmdFile"
Echo and execute commands from a file.
.br
.RS
.RS
.B "\fI\-q\fP \- don't echo commands as they are read"
.br
.B "\fIcmdFile\fP \- file containing fsio commands"
.br
.RE
.RE
.TP
.B "\fIhelp\fP"
Displays help text on stdout.
.br
.TP
.B "\fIexit\fP"
Causes fsio to exit (the quit command has the same effect).
.br
.TP
.B "\fIrewind\fP dev:"
Positions the device to the start of the tape.
.br
.RS
.RS
.B "\fIdev:\fP \- name supplied on a previous mount"
.br
.RE
.RE
.TP
.B "\fIeom\fP dev:"
Positions the device to the end of the tape.
.br
.RS
.RS
.B "\fIdev:\fP \- name supplied on a previous mount"
.br
.RE
.RE
.TP
.B "\fIskipf\fP dev: n"
Positions the device by skipping forward over files.
.br
.RS
.RS
.B "\fIdev:\fP \- name supplied on a previous mount"
.br
.B "\fIn\fP \- # of files to skip (must be > 0)"
.br
.RE
.RE
.TP
.B "\fIskipr\fP dev: n"
Positions the device by skipping backward over files.
.br
.RS
.RS
.B "\fIdev:\fP \- name supplied on a previous mount"
.br
.B "\fIn\fP \- # of files to skip (must be > 0)"
.br
.RE
.RE
.SH NOTES
If the "\fIdev:\fP" prefix is not present on a file specification, a file in
the host file system is used. It is also possible to use the "\fIlocal:\fP"
prefix to reference local files.
.br
The -c switch on the copy command is used to determine the number of
contiguous blocks allocated to the destination file before starting the copy.
This function depends on the value of blks:
.br
.B "\fI0\fP \- Use size of source file, if 0 then default to 1 block"
.br
.B "\fI!=0\fP \- Use larger of blks and size of the source file"
.br
.SH SUPPORTED FILESYSTEMS
.B "\fIdos11\fP \- DOS/BATCH-11 on RF11, RK05 or RP03"
.br
.B "\fIrt11\fP \- RT-11 including large drives with multiple partitions"
.br
.B "\fIdosmt\fP \- container file in DOS-11 magtape format"
.br
.SH SEE ALSO
.BR fsio-dos11 (1),
.BR fsio-rt11 (1)
.BR fsio-dosmt (1)
.SH AUTHOR
John Forecast, <john@forecast.name>
.br

1983
converters/fsio/fsio.c Normal file

File diff suppressed because it is too large Load Diff

166
converters/fsio/fsio.h Normal file
View File

@ -0,0 +1,166 @@
/*
* Copyright (C) 2018 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 __FSIO_H__
#define __FSIO_H__
#include <stdio.h>
#include <stdint.h>
#include "declib.h"
/*
* Mode for open files
*/
enum openMode { M_RD, M_WR };
#include "dos11.h"
#include "rt11.h"
#include "dosmt.h"
/*
* All of the supported file systems are natively little endian so we only
* need a subset of the endian support macros/routines.
*/
#if defined(__APPLE__)
#include <libkern/OSByteOrder.h>
#define htole16(x) OSSwapHostToLittleInt16(x)
#define le16toh(x) OSSwapLittleToHostInt16(x)
#define htole32(x) OSSwapHostToLittleInt32(x)
#define le32toh(x) OSSwapLittleToHostInt32(x)
#elif defined(__linux__)
#include <endian.h>
#elif defined(__FreeBSD__) || defined(__NetBSD__)
#include <sys/endian.h>
#define le16toh(x) letoh16(x)
#define le32toh(x) letoh32(x)
#elif defined(__OpenBSD__)
#include <sys/endian.h>
#endif
#ifdef __GNUC__
#define UNUSED(x) UNUSED_ ## x __attribute__((__unused__))
#else
#define UNUSED(x) UNUSED_ ## x
#endif
#define MAX_DEVLEN 16
#if defined(__linux__)
#define OPTIONS(s) "+" s
#else
#define OPTIONS(s) s
#endif
extern uint32_t swPresent;
extern char *swValue[];
#define SWISSET(c) ((swPresent & (1 << (c - 'a'))) != 0)
#define SWSET(c) swPresent |= (1 << (c - 'a'))
#define SWGETVAL(c) (swValue[c - 'a'])
#define SWSETVAL(c, v) swValue[c - 'a'] = v
#ifdef DEBUG
extern FILE *DEBUGout;
#define ERROR(...) { \
fprintf(stderr, __VA_ARGS__); \
if ((DEBUGout != NULL) && (DEBUGout != stdout)) \
fprintf(DEBUGout, __VA_ARGS__); \
}
#else
#define ERROR(...) fprintf(stderr, __VA_ARGS__)
#endif
struct mountedFS;
/*
* File system definition
*/
struct FSdef {
struct FSdef *next; /* Pointer to next file system */
char *fstype; /* File system type name */
char *descr; /* File system description */
uint16_t flags; /* Flags */
size_t blocksz; /* Default block size */
int (*mount)(struct mountedFS *);
void (*umount)(struct mountedFS *);
size_t (*size)(void);
int (*newfs)(struct mountedFS *, size_t);
void (*set)(struct mountedFS *, uint8_t, uint8_t);
void (*info)(struct mountedFS *, uint8_t, uint8_t);
void (*dir)(struct mountedFS *, uint8_t, char *);
void *(*openFileR)(struct mountedFS *, uint8_t, char *);
void *(*openFileW)(struct mountedFS *, uint8_t, char *, off_t);
off_t (*fileSize)(void *);
void (*closeFile)(void *);
size_t (*readFile)(void *, void *, size_t);
size_t (*writeFile)(void *, void *, size_t);
void (*deleteFile)(void *, char *);
/*
* The following functions are only supported by magtape file systems.
*/
void (*rewind)(struct mountedFS *);
void (*eom)(struct mountedFS *);
void (*skipforw)(struct mountedFS *, unsigned long);
void (*skiprev)(struct mountedFS *, unsigned long);
};
#define FS_UNITVALID 0x0001 /* Unit # valid in device name */
#define FS_TAPE 0x0002 /* File system for magtapes */
#define FS_EMPTYFILE 0x0004 /* Empty file is OK */
#define FS_1OPENFILE 0x0008 /* Only support a single open file */
/*
* Mounted file system descriptor
*/
struct mountedFS {
struct mountedFS *next; /* Pointer to next mounted file sys */
char name[MAX_DEVLEN + 1];
struct FSdef *filesys; /* File system */
size_t blocksz; /* Active block size */
uint16_t flags;
#define FS_READONLY 0x0001 /* Mounted read-only */
#define FS_DEBUG 0x0002 /* Debug output */
/* Bits after 0x0080 reserved for */
/* file system use */
FILE *container; /* Container file access */
union {
struct DOS11data _dos11;
struct RT11data _rt11;
struct DOSMTdata _dosmt;
} FSdata;
#define dos11data FSdata._dos11
#define rt11data FSdata._rt11
#define dosmtdata FSdata._dosmt
};
extern int FSioReadBlock(struct mountedFS *, unsigned int, void *);
extern int FSioWriteBlock(struct mountedFS *, unsigned int, void *);
#endif

319
converters/fsio/fsio.txt Normal file
View File

@ -0,0 +1,319 @@
fsio is a utility for manipulating files within file system container files
supported by by various emulators such as SIMH. fsio is designed for Unix and
Unix-like operating systems.
fsio is executed by the command:
fsio [-qv] [cmdfile]
If cmdfile is present, fsio will read commands from the command file and
echoing each command to stdout if -v . present. If the -q switch is present,
no unsolicited output will be generated by fsio. If the command file is not
present fsio will prompt for input:
fsio>
Commands to fsio have the following general syntax:
verb [switches] args...
The following verbs are supported:
mount - makes a container file available to fsio and specifies the file
system it contains.
umount - removes knowledge of a container file from fsio
newfs - create a new container with an empty file system
set - set file system parameters
info - display information about the file system
dir - list a directory
dump - Dump the contents of a file in a human readable format
copy - copies a single file
type - types the contents of a file on the terminal
delete - deletes a file
status - displays the currently mounted file systems
do - execute commands from a file
help - display help on using fsio
exit - terminate fsio
The following commands are only accepted by file systems which are on magtape
devices:
rewind - position the tape to the start of the data stream
eom - position the tape to the end of the data stream
skipf - position the tape by skipping forward over a number of files
skipr - position the tape by skipping backwards over a number of files
fsio will accept shorter versions of each verb as long as they are unique
within the command set, so c, co and cop are all synonyms for copy.
The generic form of the "dev" argument is "DDnn:" where the ":" is optional
in some commands. DD is a sequence of upper and lower case letters and nn is
an octal number in the range 0 - 377. The length of "DDnn" must not exceed 16
characters. In most cases the number field should not be present and will be
flagged as an error if it is. Some container file formats, e.g. RT-11, allow
multiple file systems within a single container and the number is used to
specify which file system to use. For these types of containers the following
rules apply:
- For "mount" and "umount" commands always use "DD:"
- For file access commands (e.g. "dir", "type" etc) use "DDnn:" ("DD:"
is treated the same as "DD0:")
- The "info" command use the presence/absence of the number to control
what information is display. If "DD:" is used, it will display
information abount all file systems within the container. If "DDnn:"
is used, it will only display information about the specific file
system.
Full command syntax:
1. mount
mount [-dr] dev[:] container type
Make the specified container file available to fsio for I/O.
-d Generate debug output if fsio is built with the DEBUG symbol
defined
-r If present, the file system is only available for read access
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
of the container file system. Files within the host file
system can be named directly without any prefix. If such a
file includes ':' in its filename, the reserved prefix local:
may be used to provide access. If you want to disallow default
access to the local filesystem, creating an environment
variable with the name "FSioForceLocal" (the value does not
matter) will require use of the "local:" prefix.
container The name of the file containing the file system
type The type of the file system in the container
2. umount
umount dev[:]
Removes all knowledge of the container file from fsio
dev[:] The name of a previously mounted file system
3. newfs
newfs [-t type] container type
Create a new container file with an empty file system. The container
will be a fixed size (depending on file system type) and may not exist
prior to issuing this command. This command does not support all features
of container files and will build a fixed size container known to work
on the target O/S:
dos11 RK05 image (2.5MB, 4800 blocks)
rt11 MSCP image (32MB, 65535 blocks)
dosmt An empty file suitable for use with any magtape controller
-t type Use a different size for the container file. The size used
will be file system dependent.
For rt11, the following disk types are valid:
rl02 RL02 image (10MB, 20480 blocks)
rx20 RX20 image (512KB, 1024 blocks)
container The name of the file to create
type The type of the file system to create in the container
4. set
set dev: args ...
Set parameters on a mounted file system. The arguments are file system
dependent.
dev: The name of a previously mounted file system
5. info
info dev:
Display internal information about the file system within the container
file
dev: The name of a previously mounted file system
6. dir
dir [-fn] dev:dirspec
List the contents of a specific directory.
-f If present, display full directory information.
-n If present, don't rewind the magtape before listing
dev: The name of a previosly mounted file system
dirspec Specification of the directory to list using the dev: syntax.
7. dump
dump [-bcdnwx] dev:src
Dump the contents of a specified file in some human readable format (e.g
hex, octal etc). If no switches are present, the output will be in octal
word format. If multiple size switches are set, the first in alphabetical
order will take precedence.
-b Output byte at a time
-d Output double word (4 bytes) at a type
-c Output ASCII characters
-w Output word (2 bytes) at a time
-x Output the data in hex
-n Don't rewind the magtape before looking for the input file
dev: The name of a previosly mounted file system
src The name of the source file (e.g. dp:input.dat)
8. copy
copy [-anpc blocks] dev1:src dev2:dest
Copy a file. The copy may be between file systems or within a single
filesystem including the host filesystem.
-a Copy in ASCII mode. This performs any necessary translation
of end-of-line characters.
-n Don't rewind the magtape before looking for the input file
-p Pad the file with NULLs. This is target file system
dependent.
-c blocks If the file system for the destination file supports
contiguous files, "blocks" is the number of file system
contiguous files to be allocated before starting to write
the file. This function depends on the value of "blocks":
0 - Use the size of the source file for the allocation,
if that is 0 (e.g. input from a keyboard) use 1.
!= 0 - Use the larger of "blocks" and the size of the
source file.
dev1: The name of a previosly mounted file system
src The name of the source file (e.g. dk:source.file)
dev2: The name of a previously mounted file system
dest The name of the desination file (e.g. dm:dest.file)
Note that wildcard naming is not supported and the source and destination
file names must be fully specified.
9. type
type [-n] dev:src
Copies the specified file to the terminal. This is equivalent to
copy -a dev:src /dev/tty
-n Don't rewind the magtape before looking for the input file
dev: The name of a previously mounted file system
src The name of the source file (e.g. dp:input.txt)
10. delete
delete dev:file
Deletes the specified file from the file system.
dev: The name of a previously mounted file system
file The name of the file to be deleted
11. status
status
Displays the currently mounted file systems.
12. do
do [-q] cmdFile
Executes commands from the specified file
-q By default, command lines are echoed. Use -q to not echo.
13. help
help
Display help text on the terminal.
14. exit
exit
Terminates the fsio application.
15. rewind
rewind dev:
Positions a magtape device to the start of the tape.
dev: The name of a previously mounted file system
16. eom
eom dev:
Positions a magtape device past the end of the last file already on the
tape. A subsequent copy to the device will append a new file to the tape.
dev: The name of a previously mounted file system
17. skipf
skipf dev: n
Changes the current position of the magtape device by skipping forwards
over a number of files. If end of tape is reached before the file count is
complete, the skip operation will terminate early.
dev: The name of a previously mounted file system
n Number of files to skip (must be > 0)
18. skipr
skipr dev: n
Changes the current position of the magtape device by skipping backwards
over a number of files. If beginning of tape is reached before the file
count is complete, the skip operation will terminate early.
dev: The name of a previously mounted file system
n Number of files to skip (must be > 0)

View File

@ -0,0 +1,291 @@
How to use fsio to transfer files in/out of SIMH emulators
----------------------------------------------------------
The examples below assume a basic knowlegde of fsio (see the man pages - fsio.1
fsio-dos11.1 and fsio-rt11.1). The following examples assume that we want to
move an ASCII formatted file "file.txt" into the target operating system. If
fsio understands the target file system (DOS-11 and RT-11 right now), we can
copy the file directly into the SIMH container file used for booting the
operating system. For other target file systems we need to transfer into a
file system that the target operating system understands, even if it is not
the native file system for that operation system. In the DEC world, this is
typically RT-11.
Note: all of these examples assume that we are running on a Linux or Unix-like
operating system (in my case Raspbian on a Raspberry Pi).
WARNING: fsio is alpha-level code. Make sure you make a back-up copy of any
important file system before mounting it read/write.
Native File System Support
--------------------------
DOS-11
------
For DOS-11, fsio understands the native disk format so we can transfer
directly into the bootable SIMH disk ("system.dsk" in the examples).
pi@host:~ $ fsio
fsio> mount dk: system.dsk dos11
dk: successfully mounted
Total blocks: 4800, Free blocks: 3591, Interleave: 1
fsio> copy -a file.txt dk:file.txt
fsio> quit
pi@host:~ $
This will copy file.txt into the default UFD ([1,1]) on the target file system.
The copy will be in ASCII mode meaning that line endings will be translated
into the character pair <cr><lf> which DOS/BATCH-11 requires. If you wish to
transfer the file into another (existing) UFD (e.g. [200,200]) you can use
either of the following 2 options:
fsio> copy -a file.txt dk:file.txt[200,200]
or
fsio> set dk: uic [200,200]
fsio> copy -a file.txt dk:file.txt
If the destination UFD does not already exist on the target file system use
the following to create the UFD:
fsio> set dk: ufd [200,200]
fsio> copy -a file.txt dk:file.txt
RT-11
-----
For RT-11, fsio understands the native disk format so we can transfer
directly into the bootable SIMH disk ("rt11sys.dsk" in the examples). Note
that RT-11 disk images can hold multiple partitions, each having a maximum
size of 32MB. Partitions are named by appending an octal number to the device
name - xx0: will be the first partition, xx1: the second etc. fsio will only
allow access to partitions which have been initialized with an RT-11 file
system. The example below shows a disk with 3 partitions but only the first
and last have been initialized.
pi@host:~ $ fsio
fsio> mount dk: rt11sys.dsk rt11
dk: successfully mounted (2 partitions)
dk0:
Total blocks: 65536, Free blocks: 24279
Directory segments: 31 (Highest 4)
Extra bytes/directory entry: 0
dk2:
Total blocks: 65536, Free blocks: 65422
Directory segments: 31 (Highest 1)
Extra bytes/directory entry: 0
fsio> copy -a file.txt dk:file.txt
fsio> quit
pi@host:~ $
This will copy file.txt into the target file system. The copy will be in
ASCII mode meaning that line endings will be translated into the character
pair <cr><lf> which RT-11 requires. In addition, unless the file ends
exactly on a block (512 bytes) boundary, a ^Z (octal 32) will be appended
to the file indicating end-of-file.
Foreign File System Support
---------------------------
For other target operating systems we will use an RT-11 format RL02 disk as an
intermediary for performing the transfer:
pi@host:~ $ fsio
fsio> newfs -a xfer.dsk rt11
fsio> mount dk: xfer.dsk rt11
fsio> copy -a file.txt dk:file.txt
fsio> quit
pi@host:~ $
This will create a maximum sized, single partition RT-11 file system in a
container file called "xfer.dsk" which may be copied to the target system
running SIMH.
Once on the target system, the commands to copy the file into the emulated
operating system will depend on that operating system and, possible, its age.
RSX-11M/RSX-11M+
----------------
Use the FLX utility to copy the file over to the Files-11 file system:
>set /uic=[1,2]
>^E
Simulation stopped, PC: 017460 (BR 17426)
sim> att rq2 xfer.dsk
sim> c
>all du2:
>mou du2:/for
>ins $flx
>flx [200,200]/fa=du2:file.txt/rt
>pip [200,200]file.txt/fu
Directory DU0:[200,200]
2019-01-10 18:10
FILE.TXT;1 (6400,15) 1./1. 2019-01-10 18:09
[1,2] [RWED,RWED,RWED,R]
Total of 1./1. blocks in 1. file
>dmo du2:
18:10:28 *** DU2: -- Dismount complete
DMO -- TT0: dismounted from DU2: *** Final dismount initiated ***
>dea du2:
>^E
Simulation stopped, PC: 017460 (BR 17426)
sim> det rq2
sim> c
>
Note that RSX FLX cannot handle formatted ASCII input files with an embedded
^Z character indicating end-of-file. Use the "-p" switch during the copy to
pad the file with NULLs.
IAS
---
The following example assumes that you are logged into a non-console
terminal. You will still need to break in on the cosole with ^E and attach
the RT-11 transfer disk as in all the other examples.
PDS> ALLOC DL0:
PDS> MOU/FOR DL0:
MOUNT-**Volume Information**
Device =DL0
Class =Foreign
UIC =[201,201]
Access =[RWED,RWED,RWED,RWED]
Charac =[FOR,ATCH,DCF]
PDS> DIR DL0:/RT
DIRECTORY DL0:
31-JAN-99
FILE .TXT 1. 12-87
< UNUSED > 20411.
20411. FREE BLOCKS
TOTAL OF 1. BLOCKS IN 1. FILES
PDS> COPY DL:FILE.TXT/RT *.*
PDS> DIR
Directory DU0:[201,201]
31-JAN-99 15:56
FILE.TXT;1 1. 31-JAN-99 15:56
Total of 1./1. blocks in 1. file
PDS> DISM DL0:
DMO -- DL00: ** DISMOUNT COMPLETE **
Note that in order to use the RL02 drive from timesharing, the following
changes will need to be made to the IAS startup scripts of the PiDP-11
distribution:
1. [1,1]STARTUP.CMD
Add the following lines at the start of the file:
INS [11,1]DL
LOA DL
2. [1,1]IASSTART.CMD
Add the following lines after the one containing "DU0/S":
DL0
DL1
After making these changes, reboot the system and users will be able to access
the 2 RL02 drives.
RSTS/E
------
For this example I have used the "-a" on the fsio "newfs" command to create an
empty RL02 disk image. Use the "-p" switch on the fsio "copy" command to pad
the file with NULLs, otherwise the ^Z will be copied into the RSTS/E file. Use
the FIT utility to copy the file over to the RSTS/E file system:
$ ^E
Simulation stopped, PC: 065640 (BR 65630)
sim> att rl0 xfer.dsk
sim> c
$ run auxlib$:fit
FIT V10.1-A RSTS V10.1-L RSTS/E V10.1
FIT>sy:=dl0:file.txt/rt11
FIT>^Z
$ dir file.txt/fu
Name .Typ Size Prot Access Date Time Clu RTS Pos Op/rr SY:[1,2]
FILE .TXT 1 < 60> 12-Jan-19 12-Jan-87 04:23 PM 8 RT11 214 0/0
Total of 1 block in 1 file in SY:[1,2]
$ ^E
Simulation stopped, PC: 065640 (BR 65630)
sim> det rl0
sim> c
$
VMS
---
Use the exchange utility to copy the file over to the VMS file system:
$^E
Simulation stopped, PC: 80B90B8D (BBC #3,26C(R3),80B90BE1)
sim> att rq1 xfer.dsk
sim> c
$ mou/for dua1:
%MOUNT-I-MOUNTED, mounted on _ROUTER$DUA1:
$ exchange copy dua1:file.txt/volume=rt11/record=stream file.txt
%EXCHANGE-S-MOUNTED, the RT-11 volume _ROUTER$DUA1: has been mounted
$ dism dua1:
$^E
Simulation stopped, PC: 80B90936 (ASHL #1,R3,R0)
sim> det rq1
sim> c
$ dir file.txt
Directory SYS$SYSROOT:[SYSMGR]
FILE.TXT;1
Total of 1 file.
$
This can be especially useful for getting the Hobbyist licences into VMS
before you have any network running.
TOPS-10
-------
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.
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.
I do not currently have a functioning TOPS-10 system to test this on.

377
converters/fsio/local.c Normal file
View File

@ -0,0 +1,377 @@
/*
* Copyright (C) 2018 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 local file access under fsio.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <sys/stat.h>
#include "fsio.h"
/*++
* l o c a l I n f o
*
* Display information about the local file system. This is not supported
* by the "local" device.
*
* Inputs:
*
* mount - pointer to the mounted file system descriptor
* unit - device unit number (unused)
* present - device unit number present (unused)
*
* Outputs:
*
* None
*
* Returns:
*
* None
*
--*/
static void localInfo(
struct mountedFS *UNUSED(mount),
uint8_t UNUSED(unit),
uint8_t UNUSED(present)
)
{
fprintf(stderr, "info: The \"local:\" device does not support this command\n");
}
/*++
* l o c a l D i r
*
* Produce a full or brief directory listing.
*
* Inputs:
*
* mount - pointer to the mounted file system descriptor
* unit - device unit number
* fname - pointer to filename string
*
* Outputs:
*
* None
*
* Returns:
*
* None
*
--*/
static void localDir(
struct mountedFS *UNUSED(mount),
uint8_t UNUSED(unit),
char *fname
)
{
char cmd[64];
sprintf(cmd, "/bin/ls %s%s\n",
SWISSET('f') ? "-l " : "", fname);
system(cmd);
}
/*++
* l o c a l O p e n F i l e R
*
* Open a local file for reading.
*
* Inputs:
*
* mount - pointer to the mounted file system descriptor
* unit - device unit number
* fname - pointer to filename string
*
* Outputs:
*
* None
*
* Returns:
*
* Pointer to open file descriptor, NULL if open fails
*
--*/
static void *localOpenFileR(
struct mountedFS *UNUSED(mount),
uint8_t UNUSED(unit),
char *fname
)
{
return fopen(fname, "r");
}
/*++
* l o c a l O p e n F i l e W
*
* Open a local file for writing.
*
* Inputs:
*
* mount - pointer to the mounted file system descriptor
* unit - device unit number
* fname - pointer to filename string
* size - estimated file size (in bytes)
*
* Outputs:
*
* None
*
* Returns:
*
* Pointer to open file descriptor, NULL if open fails
*
--*/
static void *localOpenFileW(
struct mountedFS *UNUSED(mount),
uint8_t UNUSED(unit),
char *fname,
off_t UNUSED(size)
)
{
return fopen(fname, "w");
}
/*++
* l o c a l F i l e S i z e
*
* Return the size of a currently open file. If the file is open in ASCII
* mode we do not know how many lines are present in the file and so cannot
* calculate how much additional space will be needed for a possible <LF>
* to <CRLF> translation. In this case we return 0.
*
* Inputs:
*
* filep - pointer to open file descriptor
*
* Outputs:
*
* None
*
* Returns:
*
* Current size of the file, 0 on error
*
--*/
static off_t localFileSize(
void *filep
)
{
FILE *file = filep;
struct stat stat;
if (!SWISSET('a'))
if (fstat(fileno(file), &stat) == 0)
return stat.st_size;
return 0;
}
/*++
* l o c a l D e l e t e F i l e
*
* Delete a local file.
*
* Inputs:
*
* file - pointer to open file descriptor
* fname - pointer to filename string
*
* Outputs:
*
* None
*
* Returns:
*
* None
*
--*/
static void localDeleteFile(
void *file,
char *fname
)
{
fclose(file);
if (unlink(fname) != 0)
fprintf(stderr, "delete: failed to delete \"%s\"\n", fname);
}
/*++
* l o c a l C l o s e F i l e
*
* Close an open local file.
*
* Inputs:
*
* file - pointer to open file descriptor
*
* Outputs:
*
* None
*
* Returns:
*
* None
*
--*/
static void localCloseFile(
void *file
)
{
fclose(file);
}
/*++
* l o c a l R e a d F i l e
*
* Read from a local file into a supplied buffer. If ASCII mode is active,
* each read will return at most 1 line of data and any terminating <LF> will
* be translated into <CRLF>.
*
* Inputs:
*
* file - 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 localReadFile(
void *file,
void *buf,
size_t buflen
)
{
if (SWISSET('a')) {
char *bufr = buf;
int readlen;
if (fgets(bufr, buflen - 1, file) == NULL)
return 0;
/*
* Translate terminating \n into \r\n unless it is already there
*/
if ((readlen = strlen(bufr)) != 0) {
if (bufr[readlen - 1] == '\n') {
if ((readlen == 1) || (bufr[readlen - 2] != '\r'))
strcpy(&bufr[readlen - 1], "\r\n");
}
}
return strlen(bufr);
}
return fread(buf, sizeof(char), buflen, file);
}
/*++
* l o c a l W r i t e F i l e
*
* Write to a local file from a supplied buffer. If ASCII mode is active,
* if the buffer is terminated with <CRLF> translate it into <LF>.
*
* Inputs:
*
* file - 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 error
*
--*/
static size_t localWriteFile(
void *file,
void *buf,
size_t buflen
)
{
if (SWISSET('a')) {
char *bufw = buf;
if (buflen >= 2)
if ((bufw[buflen - 2] == '\r') && (bufw[buflen - 1] == '\n')) {
bufw[buflen - 2] = '\n';
buflen--;
}
}
return fwrite(buf, sizeof(char), buflen, file);
}
/*++
* l o c a l F S
*
* Descriptor for accessing local files. Note that none of command routines
* are present since this device never appears to be mounted.
--*/
struct FSdef localFS = {
NULL,
"local",
"local Local file access\n",
0,
0,
NULL,
NULL,
NULL,
NULL,
NULL,
localInfo,
localDir,
localOpenFileR,
localOpenFileW,
localFileSize,
localCloseFile,
localReadFile,
localWriteFile,
localDeleteFile,
NULL, /* No tape support functions */
NULL,
NULL,
NULL
};
/*
* Statically allocated mounted file system for local file access
*/
struct mountedFS localMount;

2306
converters/fsio/rt11.c Normal file

File diff suppressed because it is too large Load Diff

290
converters/fsio/rt11.h Normal file
View File

@ -0,0 +1,290 @@
/*
* Copyright (C) 2018 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 __RT11_H__
#define __RT11_H__
/*
* General disk layout:
*
* Block
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* 0 | Reserved |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* 1 | Home Block (Reserved) |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* 2 | Reserved |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* 3 | Reserved |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* 4 | Reserved |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* 5 | Reserved |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* 6 | Directory Segment 1 |
* 7 | |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* 10 | Directory Segment 2 |
* 11 | |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | ... |
* | ... |
* | ... |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* x | Directory Segment n |
* x+1 | |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* Files | ... |
* | ... |
* | ... |
* | ... |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* End of Volume
*
* Home Block:
*
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* 0000 | Bad block replacement table |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* 0202 | Unused |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* 0204 | INITIALIZE/RESTORE data area |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* 0252 | BUP Information area |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* 0274 | Unused |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* 0700 | Reserved for Digital - 000000 |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* 0702 | Reserved for Digital - 000000 |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* 0704 | Unused |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* 0722 | Pack cluster size - 000001 |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* 0724 | Block # of first directory segment - 000006 |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* 0726 | System version - Radix-50 "V3A"? |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* 0730 | Volume identification - "RT11A " |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* 0744 | Owner name - " " |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* 0760 | System identification - "DECRT11A " |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* 0776 | Checksum |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
*
* Directory Segment Header:
*
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | Total # of directory segments (1 - 31) |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | Next logical directory segment # |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | Highest directory segment in use |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | Extra bytes per directory entry |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | Block # for start of this segment |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
*
* Directory Entry:
*
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | Status Word |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | Radix-50 File Name (chars 1 - 3) |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | Radix-50 File Name (chars 4 - 6) |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | Radix-50 File Type (1 - 3 chars) |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | Job # | Channel # |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | Creation Date |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | Optional Extra Words |
* | ... |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
*
* Status Word:
*
* 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | | | | | | | | | | | | | | | | |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | | | | | | |
* | | | | | | - Prefix block indicator
* | | | | | - Tentative file
* | | | | - Empty area
* | | | - Permanent file
* | | - End of segment marker
* | - Protected from .WRITE requests
* - Protected permanent file
*
* Date Word:
*
* 15 14 13 10 9 5 4 0
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | | | | |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | | | | | | | |
* +---+ +-----------+ +---------------+ +---------------+
* | | | |
* | | | Year - 1972
* | | | - 32 x Age
* | | - Day (1 - 31)
* | - Month (1 - 12)
* - Age (0 - 3)
*/
#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_SYSVER "V05"
#define RT11_VOLID "RT11A "
#define RT11_OWNER " "
#define RT11_SYSID "DECRT11A "
/*
* 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?
*/
#define RT11_MAXPARTSZ 0200000 /* Max partition size */
#define RT11_MINPARTSZ 0000011 /* Min partition size */
#define RT11_RL02SZ 20480 /* Size of an RL02 drive */
#define RT11_RX20SZ 1024 /* Size of an RX20 floppy drive */
#define RT11_HB_BBLOCK 0000 /* Bad block replacement tbl */
#define RT11_HB_RESTORE 0102 /* INIT/RESTORE data area */
#define RT11_HB_BUP 0125 /* BUP info area */
#define RT11_HB_PCS 0351 /* Pack cluster size */
#define RT11_HB_FIRST 0352 /* First directory segment */
#define RT11_HB_SYSVER 0353 /* System version */
#define RT11_HB_VOLID 0354 /* Volume identification */
#define RT11_HB_OWNER 0362 /* Owner name */
#define RT11_HB_SYSID 0370 /* System identification */
#define RT11_HB_CHKSUM 0377 /* Checksum */
#define RT11_DH_COUNT 0000 /* # of directory segments */
#define RT11_DH_NEXT 0001 /* Next logical segment # */
#define RT11_DH_HIGHEST 0002 /* Highest segment # in use */
#define RT11_DH_EXTRA 0003 /* Extra bytes/dir. entry */
#define RT11_DH_START 0004 /* Block # for segment start */
#define RT11_DH_SIZE 0005 /* Size of header */
#define RT11_DS_SIZE 512 /* Directory segment size */
#define RT11_DS_DISPACE (RT11_DS_SIZE - RT11_DH_SIZE)
#define RT11_DS_MAX 31 /* Max # of directory segments */
#define RT11_DI_STATUS 0000 /* Status word */
#define RT11_DI_FNAME1 0001 /* File name (chars 1 - 3) */
#define RT11_DI_FNAME2 0002 /* File name (chars 4 - 6) */
#define RT11_DI_FTYPE 0003 /* File type (1 - 3 chars) */
#define RT11_DI_LENGTH 0004 /* Total file length */
#define RT11_DI_JOB_CHN 0005 /* Channel # */
#define RT11_DI_CREATE 0006 /* Date of creation */
#define RT11_DI_SIZE 0007 /* Default entry size */
#define RT11_E_PRE 000020 /* Prefix block indicator */
#define RT11_E_TENT 000400 /* Tentative file */
#define RT11_E_MPTY 001000 /* Empty area */
#define RT11_E_PERM 002000 /* Permanent file */
#define RT11_E_EOS 004000 /* End of segment marker */
#define RT11_E_READ 040000 /* Protected from .WRITE */
#define RT11_E_PROT 100000 /* Protected permanent file */
#define RT11EOS(v) (((v) & RT11_E_EOS) != 0)
#define RT11_DW_YEAR 0000037 /* Year */
#define RT11_DW_DAY 0001740 /* Day */
#define RT11_DW_MONTH 0036000 /* Month */
#define RT11_DW_AGE 0140000 /* Age */
/*
* Structure to describe a filename. Asterisks may be used as wild card
* characters for the 2 components of a filename; name and type.
*/
struct rt11FileSpec {
uint8_t flags; /* Wild card indicators */
uint16_t name[2]; /* File name (RAD50) */
uint16_t type; /* File type (RAD50) */
char fname[6]; /* File name (ASCII) */
char ftype[3]; /* File type (ASCII) */
};
#define RT11_WC_NAME 0001 /* Wild card in name */
#define RT11_WC_TYPE 0002 /* Wild card in type */
#define RT11_M_NONE 0000 /* Wild cards not allowed */
#define RT11_M_ALLOW 0001 /* Wild cards allowed */
#define RT11_M_NONAME 0002 /* Wild cards allowed */
/* If no filename + extension */
/* present, default to *.* */
/*
* Structure to define an open file. This is an RT-11 directory entry along
* with sufficient information to be able to write the directory entry back
* to disk. Some of the directory information can be created on-the-fly so
* will not be stored here.
*/
struct rt11OpenFile {
uint16_t status; /* File status */
uint16_t name[2]; /* File name */
uint16_t type; /* File type */
uint16_t length; /* Blocks written */
uint16_t creation; /* Creation date */
/* End of directory entry */
uint8_t segment; /* Directory segment # */
uint16_t offset; /* Directory offset */
/* Start of read/write info */
enum openMode mode; /* Open mode (read/write) */
struct mountedFS *mount; /* Mounted file system descriptor */
uint8_t unit; /* Partition number */
char *buffer; /* Private buffer for file I/O */
uint16_t current; /* Current working block # */
uint16_t last; /* Last usable block */
uint16_t count; /* Bytes used in current block */
uint16_t start; /* Starting block # */
};
/*
* RT-11 specific data area.
*/
struct RT11data {
unsigned int blocks; /* Size of container */
uint16_t filesystems; /* Max # of filesystems */
uint16_t valid[16]; /* Valid partitions */
uint16_t maxblk[256]; /* Max block address */
uint16_t buf[512]; /* Disk buffer - enough for a */
/* directory segment */
};
#define PARTITIONVALID(d, u) ((d->valid[u / 16] & (1 << (u % 16))) != 0)
#endif

816
converters/fsio/tape.c Normal file
View File

@ -0,0 +1,816 @@
/*
* Copyright (C) 2018 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 reading/writing SIMH tape container files.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <sys/stat.h>
#include "fsio.h"
/*++
* t a p e V e r i f y
*
* Verify that the container format is valid, leaving the tape positioned
* at beginning-of-tape.
*
* Inputs:
*
* container - pointer open container file
* eot - return end-of-tape info here, NUL if not needed
*
* Outputs:
*
* None
*
* Returns:
*
* 1 if format is valid, 0 otherwise
*
--*/
int tapeVerify(
FILE *container,
off_t *eot
)
{
int errorCount = 0, tmSeen = 0;
uint32_t meta, header, bc;
off_t position;
struct stat stat;
/*
* Determine the size of the file.
*/
fstat(fileno(container), &stat);
for (;;) {
position = ftello(container);
/*
* If we are positioned at the end-of-file, there is a tape mark or
* end-of-media marker missing. Treat it as though one is present.
*/
if (position == stat.st_size)
break;
if (fread(&meta, sizeof(meta), 1, container) != 1)
return 0;
bc = le32toh(meta);
switch (bc) {
case ST_TM:
if (++tmSeen <= 1)
break;
/* Treat second TM in a row as end of medium */
/* FALLTHROUGH */
case ST_EOM:
if (fseeko(container, -sizeof(meta), SEEK_CUR) != 0)
return 0;
if (errorCount)
printf("mount: Tape contains error records\n");
goto done;
case ST_GAP:
break;
default:
/*
* Record descriptor
*/
tmSeen = 0;
header = bc;
if ((bc & ST_ERROR) != 0)
errorCount++;
if ((bc & ST_MBZ) != 0)
return 0;
bc = RECLEN(bc & ST_LENGTH);
/*
* Check if we are seeking ouside of the file. If so, this is not
* a .tap container file.
*/
if ((position + bc + (2 * sizeof(meta))) > (unsigned long long)stat.st_size)
return 0;
if (fseeko(container, bc, SEEK_CUR) != 0)
return 0;
if (fread(&meta, sizeof(meta), 1, container) != 1)
return 0;
bc = le32toh(meta);
if (header != bc)
return 0;
}
}
done:
if (eot != NULL)
*eot = ftello(container);
/*
* Position at beginning-of-tape.
*/
if (fseeko(container, 0, SEEK_SET) == 0)
return 1;
return 0;
}
/*++
* t a p e G e t P o s i t i o n
*
* Get the current position of the tape.
*
* Inputs:
*
* container - pointer open container file
*
* Outputs:
*
* None
*
* Returns:
*
* Current position of the tape
*
--*/
off_t tapeGetPosition(
FILE *container
)
{
return ftello(container);
}
/*++
* t a p e S e t P o s i t i o n
*
* Position the tape to a position previously obtained by tapeGetPosition().
*
* Inputs:
*
* container - pointer open container file
* pos - requested position
*
* Outputs:
*
* None
*
* Returns:
*
* 1 if successfully positioned, 0 if error
*
--*/
int tapeSetPosition(
FILE *container,
off_t pos
)
{
return fseeko(container, pos, SEEK_SET) == 0 ? 1 : 0;
}
/*++
* t a p e S k i p R e c o r d F
*
* Skip over the next record in the forward direction.
*
* Inputs:
*
* container - pointer open container file
*
* Outputs:
*
* None
*
* Returns:
*
* ST_FAIL - error accessing container file
* ST_EOM - end of media detected
* ST_TM - tape mark detected
* Other - record length (including error flag)
*
--*/
uint32_t tapeSkipRecordF(
FILE *container
)
{
return tapeReadRecordLength(container);
}
/*++
* t a p e S k i p R e c o r d R
*
* Skip over the next record in the reverse direction.
*
* Inputs:
*
* container - pointer open container file
*
* Outputs:
*
* None
*
* Returns:
*
* ST_FAIL - error accessing container file
* ST_EOM - end of media detected
* ST_TM - tape mark detected
* Other - record length (including error flag)
*
--*/
uint32_t tapeSkipRecordR(
FILE *container
)
{
return tapeReadRecordLengthReverse(container);
}
/*++
* t a p e P e e k R e c o r d L e n g t h
*
* Get the length of the next record on the tape without actually reading
* the data or changing the current position of the tape.
*
* Inputs:
*
* container - pointer open container file
*
* Outputs:
*
* None
*
* Returns:
*
* ST_FAIL - error accessing the container file
* ST_EOM - end of media detected
* ST_TM - tape mark detected
* Other - record length (including error flag)
*
--*/
uint32_t tapePeekRecordLength(
FILE *container
)
{
off_t pos = ftello(container);
uint32_t meta;
if ((fread(&meta, sizeof(meta), 1, container) != 1) ||
(fseeko(container, pos, SEEK_SET) != 0))
return ST_FAIL;
return le32toh(meta);
}
/*++
* t a p e R e a d R e c o r d
*
* Read the next record from the tape into the specified buffer. If the
* buffer is smaller than the record, the entire record will be consumed,
* losing data.
*
* Inputs:
*
* container - pointer open container file
* buf - pointer to the buffer to receive the data
* len - length of the buffer
*
* Outputs:
*
* None
*
* Returns:
*
* ST_FAIL - error accessing the container file
* ST_EOM - end of media detected
* ST_TM - tape mark detected
* Other - record length (including error flag)
* if the buffer is smaller than the record, the
* length returned will be that of the buffer
*
--*/
uint32_t tapeReadRecord(
FILE *container,
void *buf,
int len
)
{
off_t pos = ftello(container);
uint32_t meta, bc, erflag, length;
if (fread(&meta, sizeof(meta), 1, container) != 1)
return ST_FAIL;
bc = le32toh(meta);
switch (bc) {
case ST_EOM:
case ST_TM:
return bc;
default:
erflag = bc & ST_ERROR;
bc &= ST_LENGTH;
length = (uint32_t)len;
if (bc < length)
length = bc;
if (fread(buf, sizeof(uint8_t), length, container) != length)
return ST_FAIL;
/*
* Now position the file after this record.
*/
pos += RECLEN(bc) + (2 * sizeof(meta));
if (fseeko(container, pos, SEEK_SET) != 0)
return ST_FAIL;
return erflag | length;
}
return ST_FAIL;
}
/*++
* t a p e R e a d R e c o r d L e n g t h
*
* Get the length of the next record on the tape without actually reading
* the data. The tape will be positioned at the start of the next record.
*
* Inputs:
*
* container - pointer open container file
*
* Outputs:
*
* None
*
* Returns:
*
* ST_FAIL - error accessing container file
* ST_EOM - end of media detected
* ST_TM - tape mark detected
* Other - record length (including error flag)
*
--*/
uint32_t tapeReadRecordLength(
FILE *container
)
{
off_t pos = ftello(container);
uint32_t meta, bc, erflag;
if (fread(&meta, sizeof(meta), 1, container) != 1)
return ST_FAIL;
bc = le32toh(meta);
switch (bc) {
case ST_EOM:
case ST_TM:
return bc;
default:
erflag = bc & ST_ERROR;
bc &= ST_LENGTH;
/*
* Now position the file after this record.
*/
pos += RECLEN(bc) + (2 * sizeof(meta));
if (fseeko(container, pos, SEEK_SET) != 0)
return ST_FAIL;
return erflag | bc;
}
return ST_FAIL;
}
/*++
* t a p e R e a d R e c o r d L e n g t h R e v e r s e
*
* Get the length of the previous record on the tape without actually reading
* the data. The tape will be positioned at the start of the previous record.
*
* Inputs:
*
* container - pointer open container file
*
* Outputs:
*
* None
*
* Returns:
*
* ST_FAIL - error accessing container file
* ST_EOM - end of media detected
* ST_TM - tape mark detected
* Other - record length (including error flag)
*
--*/
uint32_t tapeReadRecordLengthReverse(
FILE *container
)
{
uint32_t meta, bc, erflag;
off_t delta;
if ((fseeko(container, -sizeof(meta), SEEK_CUR) != 0) ||
(fread(&meta, sizeof(meta), 1, container) != 1))
return ST_FAIL;
bc = le32toh(meta);
switch (bc) {
case ST_EOM:
case ST_TM:
if (fseeko(container, -sizeof(meta), SEEK_CUR) != 0)
return ST_FAIL;
return bc;
default:
erflag = bc & ST_ERROR;
bc &= ST_LENGTH;
/*
* Now position the file before this record.
*/
delta = RECLEN(bc) + (2 * sizeof(meta));
if (fseeko(container, -delta, SEEK_CUR) != 0)
return ST_FAIL;
return erflag | bc;
}
return ST_FAIL;
}
/*++
* t a p e W r i t e R e c o r d
*
* Write a record to the tape at it's current position leaving the tape
* positioned after the newly written record.
*
* Inputs:
*
* container - pointer open container file
* buf - pointer to the record to be written
* len - length of the record
*
* Outputs:
*
* None
*
* Returns:
*
* 1 if record was successfully written, 0 otherwise
*
--*/
int tapeWriteRecord(
FILE *container,
void *buf,
int len
)
{
uint32_t meta = htole16(len);
int datalen = (len + 1) & ~1;
if ((fwrite(&meta, sizeof(meta), 1, container) != 1) ||
(fwrite(buf, datalen, 1, container) != 1) ||
(fwrite(&meta, sizeof(meta), 1, container) != 1))
return 0;
return 1;
}
/*++
* t a p e W r i t e E O M
*
* Write an end-of-media record to the tape at it's current position and,
* optionally, backup the tape to before the newly written record.
*
* Inputs:
*
* container - pointer open container file
* backup - if 1, position the tape before the new record
*
* Outputs:
*
* None
*
* Returns:
*
* 1 if EOM record successfully written, 0 otherwise
*
--*/
int tapeWriteEOM(
FILE *container,
int backup
)
{
uint32_t eom = htole32(ST_EOM);
if (fwrite(&eom, sizeof(eom), 1, container) != 1)
return 0;
if (backup)
if (fseeko(container, -sizeof(eom), SEEK_CUR) != 0)
return 0;
return 1;
}
/*++
* t a p e W r i t e T M
*
* Write a tape mark record to the tape at it's current position.
*
* Inputs:
*
* container - pointer open container file
*
* Outputs:
*
* None
*
* Returns:
*
* 1 if EOM record successfully written, 0 otherwise
*
--*/
int tapeWriteTM(
FILE *container
)
{
uint32_t tm = htole32(ST_TM);
if (fwrite(&tm, sizeof(tm), 1, container) != 1)
return 0;
return 1;
}
/*++
* t a p e E O M
*
* Position the tape to the end of media so that a subsequent write will
* append a file to the tape.
*
* Inputs:
*
* container - pointer open container file
* eot - pointer to end-of-tape position, NULL if
* not available
*
* Outputs:
*
* None
*
* Returns:
*
* 1 if tape successfully positioned, 0 otherwise
*
--*/
int tapeEOM(
FILE *container,
off_t *eot
)
{
int whence = eot == NULL ? SEEK_END : SEEK_SET;
off_t pos = eot == NULL ? 0 : *eot;
uint32_t bc1, bc2;
/*
* Move to the end of the tape and then look backwards to see how it is
* terminated.
*/
if (fseeko(container, pos, whence) == 0) {
if (ftello(container) == 0) {
/*
* Empty file, we are correctly positioned.
*/
return 1;
}
if ((bc1 = tapeReadRecordLengthReverse(container)) == ST_FAIL)
return 0;
if ((bc1 == ST_EOM) || (bc1 == ST_TM)) {
if (ftello(container) == 0) {
/*
* Only ST_EOM or ST_TM present, we are correctly positioned.
*/
return 1;
}
if ((bc2 = tapeReadRecordLengthReverse(container)) == ST_FAIL)
return 0;
if (bc2 == ST_TM) {
if (ftello(container) == 0) {
/*
* Only ST_TM followed by ST_TM or ST_EOM, we are correctly
* positioned
*/
return 1;
}
/*
* ST_TM followed by ST_TM or ST_EOM with at least one data block
* present, skip over the initial ST_TM.
*/
if (fseeko(container, sizeof(uint32_t), SEEK_CUR) == 0)
return 1;
} else {
/*
* Only a single ST_TM at the end of the container file. This
* indicates that there is a missing ST_TM or ST_EOM. Position
* the tape at the logical end-of-tape so that any subsequent file
* write will fix the problem.
*/
if (fseeko(container, pos, whence) == 0)
return 1;
}
}
}
return 0;
}
/*++
* t a p e R e w i n d
*
* Rewind the tape.
*
* Inputs:
*
* container - pointer open container file
*
* Outputs:
*
* None
*
* Returns:
*
* None
*
--*/
void tapeRewind(
FILE *container
)
{
fseeko(container, 0, SEEK_SET);
}
/*++
* t a p e S k i p F o r w a r d
*
* Skip forward over a number of files. If end-of-media is reached, the skip
* operation will terminate early.
*
* Inputs:
*
* container - pointer open container file
* count - # of files to skip
*
* Outputs:
*
* None
*
* Returns:
*
* 1 if skip was successful, 0 otherwise
*
--*/
int tapeSkipForward(
FILE *container,
unsigned long count
)
{
unsigned long i;
uint32_t bc;
for (i = 0; i < count; i++) {
/*
* Peek at the next record. If it's a tape mark or end-of-media there
* are not more files to skip.
*/
switch (tapePeekRecordLength(container)) {
case ST_FAIL:
return 0;
case ST_TM:
case ST_EOM:
return 1;
}
/*
* Skip forward over 1 file.
*/
do {
switch (bc = tapeReadRecordLength(container)) {
case ST_FAIL:
return 0;
case ST_EOM:
if (fseeko(container, -sizeof(uint32_t), SEEK_CUR) != 0)
return 0;
break;
}
} while ((bc != ST_TM) && (bc != ST_EOM));
}
return 1;
}
/*++
* t a p e S k i p R e v e r s e
*
* Skip backwards over a number of files. If beginning-of-tape is reached,
* the skip operation will terminate early.
*
* Inputs:
*
* container - pointer open container file
* count - # of files to skip
*
* Outputs:
*
* None
*
* Returns:
*
* 1 if skip was successful, 0 otherwise
*
--*/
int tapeSkipReverse(
FILE *container,
unsigned long count
)
{
unsigned long i;
uint32_t bc;
for (i = 0; i < count; i++) {
/*
* If we are at beginning-of-tape, there are no more files to skip.
*/
if (ftello(container) == 0)
return 1;
/*
* If we are not at the beginning of tape, the previous record should
* be a tape mark.
*/
if (tapeReadRecordLengthReverse(container) != ST_TM)
return 0;
/*
* Now skip over the remainder of the file.
*/
do {
if ((bc = tapeReadRecordLengthReverse(container)) == ST_FAIL)
return 0;
} while ((bc != ST_TM) && (ftello(container) != 0));
/*
* Skip over the tape mark since it marks the end of the previous file.
*/
if (bc == ST_TM) {
if (fseeko(container, sizeof(uint32_t), SEEK_CUR) != 0)
return 0;
}
}
return 1;
}

73
converters/fsio/tape.h Normal file
View File

@ -0,0 +1,73 @@
/*
* Copyright (C) 2018 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 __TAPE_H__
#define __TAPE_H__
/*
* Metadata markers
*/
#define ST_EOM 0xFFFFFFFF /* end of medium */
#define ST_GAP 0xFFFFFFFE /* erase gap */
#define ST_TM 0x00000000 /* tape mark */
/*
* Special code for reporting errors. If actually read from a container
* file, this would be considered an error.
*/
#define ST_FAIL 0xFEFEFEFE
/*
* Record length field layout
*/
#define ST_ERROR 0x80000000 /* record contains an error */
#define ST_MBZ 0x7F000000 /* must be zero */
#define ST_LENGTH 0x00FFFFFF /* record length */
/*
* Data in the .tap container file is rounded up to an even number of bytes
*/
#define RECLEN(c) (((c) + 1) & ~1)
extern int tapeVerify(FILE *, off_t *);
extern off_t tapeGetPosition(FILE *);
extern int tapeSetPosition(FILE *, off_t);
extern uint32_t tapeSkipRecordF(FILE *);
extern uint32_t tapeSkipRecordR(FILE *);
extern uint32_t tapePeekRecordLength(FILE *);
extern uint32_t tapeReadRecord(FILE *, void *, int);
extern uint32_t tapeReadRecordLength(FILE *);
extern uint32_t tapeReadRecordLengthReverse(FILE *);
extern int tapeWriteRecord(FILE *, void *, int);
extern int tapeWriteEOM(FILE *, int);
extern int tapeWriteTM(FILE *);
extern int tapeEOM(FILE *, off_t *);
extern void tapeRewind(FILE *);
extern int tapeSkipForward(FILE *, unsigned long);
extern int tapeSkipReverse(FILE *, unsigned long);
#endif