mirror of
https://github.com/open-simh/simtools.git
synced 2026-01-13 23:36:03 +00:00
Fix some OS/8 directory handling routines which did not correctly handle directories with optional extended words. Add missing RL01 initialization support for RT11.
3015 lines
73 KiB
C
3015 lines
73 KiB
C
/*
|
||
* Copyright (C) 2019 John Forecast. All Rights Reserved.
|
||
*
|
||
* Redistribution and use in source and binary forms, with or without
|
||
* modification, are permitted provided that the following conditions are met:
|
||
*
|
||
* 1. Redistributions of source code must retain the above copyright notice,
|
||
* this list of conditions and the following disclaimer.
|
||
*
|
||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||
* this list of conditions and the following disclaimer in the documentation
|
||
* and/or other materials provided with the distribution.
|
||
*
|
||
* 3. The name of the author may not be used to endorse or promote products
|
||
* derived from this software without specific prior written permission.
|
||
*
|
||
* THIS SOFTWARE IS PROVIDED BY JOHN FORECAST "AS IS" AND ANY EXPRESS OR
|
||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
*/
|
||
|
||
/*
|
||
* Support routines for handling OS/8 file systems under fsio
|
||
*/
|
||
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <unistd.h>
|
||
#include <string.h>
|
||
#include <ctype.h>
|
||
#include <time.h>
|
||
#include <sys/stat.h>
|
||
#include <regex.h>
|
||
|
||
#include "fsio.h"
|
||
|
||
static int rk05BlockPresent(struct mountedFS *, uint8_t, unsigned int);
|
||
static int rk05ReadBlock(struct mountedFS *, uint8_t, unsigned int, void *);
|
||
static int rk05WriteBlock(struct mountedFS *, uint8_t, unsigned int, void *);
|
||
static int rx01BlockPresent(struct mountedFS *, uint8_t, unsigned int);
|
||
static int rx01ReadBlock(struct mountedFS *, uint8_t, unsigned int, void *);
|
||
static int rx01WriteBlock(struct mountedFS *, uint8_t, unsigned int, void *);
|
||
static int rx02BlockPresent(struct mountedFS *, uint8_t, unsigned int);
|
||
static int rx02ReadBlock(struct mountedFS *, uint8_t, unsigned int, void *);
|
||
static int rx02WriteBlock(struct mountedFS *, uint8_t, unsigned int, void *);
|
||
|
||
/*
|
||
* Floppy interleave tables:
|
||
*
|
||
* RX01:
|
||
* Each OS/8 logical block maps into 4 sectors (interleave 2)
|
||
*
|
||
* RX02:
|
||
* Each OS/8 logical block maps into 2 sectors (interleave 3)
|
||
*/
|
||
#define RX01INTLV(t, s) ((t * OS8_RX0xNSECT) + s - 1)
|
||
#define RX02INTLV(t, s) ((t * OS8_RX0xNSECT) + s - 1)
|
||
|
||
static uint16_t rx01interleave[] = {
|
||
RX01INTLV(0, 1), RX01INTLV(0, 3), RX01INTLV(0, 5), RX01INTLV(0, 7),
|
||
RX01INTLV(0, 9), RX01INTLV(0, 11), RX01INTLV(0, 13), RX01INTLV(0, 15),
|
||
RX01INTLV(0, 17), RX01INTLV(0, 19), RX01INTLV(0, 21), RX01INTLV(0, 23),
|
||
RX01INTLV(0, 25), RX01INTLV(0, 2), RX01INTLV(0, 4), RX01INTLV(0, 6),
|
||
RX01INTLV(0, 8), RX01INTLV(0, 10), RX01INTLV(0, 12), RX01INTLV(0, 14),
|
||
RX01INTLV(0, 16), RX01INTLV(0, 18), RX01INTLV(0, 20), RX01INTLV(0, 22),
|
||
RX01INTLV(0, 24), RX01INTLV(0, 26), RX01INTLV(1, 1), RX01INTLV(1, 3),
|
||
RX01INTLV(1, 5), RX01INTLV(1, 7), RX01INTLV(1, 9), RX01INTLV(1, 11),
|
||
RX01INTLV(1, 13), RX01INTLV(1, 15), RX01INTLV(1, 17), RX01INTLV(1, 19),
|
||
RX01INTLV(1, 21), RX01INTLV(1, 23), RX01INTLV(1, 25), RX01INTLV(1, 2),
|
||
RX01INTLV(1, 4), RX01INTLV(1, 6), RX01INTLV(1, 8), RX01INTLV(1, 10),
|
||
RX01INTLV(1, 12), RX01INTLV(1, 14), RX01INTLV(1, 16), RX01INTLV(1, 18),
|
||
RX01INTLV(1, 20), RX01INTLV(1, 22), RX01INTLV(1, 24), RX01INTLV(1, 26)
|
||
};
|
||
#define RX01REPEAT ((sizeof(rx01interleave)/sizeof(rx01interleave[0])) / 4)
|
||
|
||
static uint16_t rx02interleave[] = {
|
||
RX02INTLV(0, 1), RX02INTLV(0, 4),
|
||
RX02INTLV(0, 7), RX02INTLV(0, 10),
|
||
RX02INTLV(0, 13), RX02INTLV(0, 16),
|
||
RX02INTLV(0, 19), RX02INTLV(0, 22),
|
||
RX02INTLV(0, 25), RX02INTLV(0, 2),
|
||
RX02INTLV(0, 5), RX02INTLV(0, 8),
|
||
RX02INTLV(0, 11), RX02INTLV(0, 14),
|
||
RX02INTLV(0, 17), RX02INTLV(0, 20),
|
||
RX02INTLV(0, 23), RX02INTLV(0, 26),
|
||
RX02INTLV(0, 3), RX02INTLV(0, 6),
|
||
RX02INTLV(0, 9), RX02INTLV(0, 12),
|
||
RX02INTLV(0, 15), RX02INTLV(0, 18),
|
||
RX02INTLV(0, 21), RX02INTLV(0, 24)
|
||
};
|
||
#define RX02REPEAT ((sizeof(rx02interleave)/sizeof(rx02interleave[0])) / 2)
|
||
|
||
/*
|
||
* Six-bit code => character mapping table
|
||
*/
|
||
static char sixbit[64] = {
|
||
' ', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
|
||
'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
|
||
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
|
||
'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
|
||
' ', '!', '"', '#', '$', '%', '&', '\'',
|
||
'(', ')', '*', '+', ',', '-', '.', '/',
|
||
'0', '1', '2', '3', '4', '5', '6', '7',
|
||
'8','9', ':', ';', '<', '=', '>', '?'
|
||
};
|
||
|
||
/*
|
||
* Month of the year - allow 4-bit index field
|
||
*/
|
||
static char *os8Month[] = {
|
||
"???", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
|
||
"Aug", "Sep", "Oct", "Nov", "Dec", "???", "???", "???"
|
||
};
|
||
|
||
/*
|
||
* Table of "set" commands
|
||
*/
|
||
static char *setCmds[] = {
|
||
"year",
|
||
NULL
|
||
};
|
||
#define OS8SET_YEAR 0
|
||
|
||
static void os8CloseFile(void *);
|
||
|
||
extern int args;
|
||
extern char **words;
|
||
extern int quiet;
|
||
|
||
/*++
|
||
* o s 8 D e v i c e s
|
||
*
|
||
* List of OS/8 disk device types supported by this program.
|
||
*
|
||
--*/
|
||
struct OS8device OS8Devices[] = {
|
||
{ "rk05", 2, 2 * OS8_RK05FS_BLKS,
|
||
0,
|
||
{ OS8_RK05FS_BLKS, OS8_RK05FS_BLKS, 0, 0, 0, 0, 0,0 },
|
||
rk05BlockPresent, rk05ReadBlock, rk05WriteBlock },
|
||
|
||
{ "rx01", 1, (OS8_RX0xSZ * OS8_RX01SS_W) / OS8_BLOCKSIZE,
|
||
OS8_RX0xNSECT * OS8_RX01SS,
|
||
{ ((OS8_RX0xSZ - OS8_RX0xNSECT) * OS8_RX01SS_W) / OS8_BLOCKSIZE,
|
||
0, 0, 0, 0, 0, 0, 0 },
|
||
rx01BlockPresent, rx01ReadBlock, rx01WriteBlock },
|
||
|
||
{ "rx02", 1, (OS8_RX0xSZ * OS8_RX02SS_W) / OS8_BLOCKSIZE,
|
||
OS8_RX0xNSECT * OS8_RX02SS,
|
||
{ ((OS8_RX0xSZ - OS8_RX0xNSECT) * OS8_RX02SS_W) / OS8_BLOCKSIZE,
|
||
0, 0, 0, 0, 0, 0, 0 },
|
||
rx02BlockPresent, rx02ReadBlock, rx02WriteBlock },
|
||
|
||
{ NULL, 0, 0,
|
||
0,
|
||
{ 0, 0, 0, 0, 0, 0, 0, 0 },
|
||
NULL, NULL, NULL }
|
||
};
|
||
|
||
/*++
|
||
* o s 8 W o r d
|
||
*
|
||
* Extract an unsigned 12-bit value.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* value - 16-bit incoming value
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* 16-bit value with the high 4-bit zeroed
|
||
*
|
||
--*/
|
||
static uint16_t os8Word(
|
||
uint16_t value
|
||
)
|
||
{
|
||
return value & 0xFFF;
|
||
}
|
||
|
||
/*++
|
||
* o s 8 S X T
|
||
*
|
||
* Extract a 12-bit value extending the size bit.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* value - 16-bit incoming value
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* Original value with sign extended to 16-bits
|
||
*
|
||
--*/
|
||
static uint16_t os8SXT(
|
||
uint16_t value
|
||
)
|
||
{
|
||
return (value & 0xFFF) | (((value & 0x800) != 0) ? 0xF000 : 0);
|
||
}
|
||
|
||
/*++
|
||
* o s 8 N e g
|
||
*
|
||
* Negate a 12-bit value to a 16-bit value leading to a result in the range
|
||
* 0 - 4095.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* value - 16-bit incoming value
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* Negated value
|
||
*
|
||
--*/
|
||
static uint16_t os8Neg(
|
||
uint16_t value
|
||
)
|
||
{
|
||
uint16_t value16 = os8SXT(value);
|
||
|
||
return (uint16_t)((0 - value16) & 0xFFF);
|
||
}
|
||
|
||
/*++
|
||
* o s 8 V a l u e
|
||
*
|
||
* Truncate a 16-bit value to 12-bits.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* value - 16-bit incoming value
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* The 12-bit truncated value
|
||
*
|
||
--*/
|
||
static uint16_t os8Value(
|
||
uint16_t value
|
||
)
|
||
{
|
||
return value & 0xFFF;
|
||
}
|
||
|
||
/*++
|
||
* o s 8 D a t e
|
||
*
|
||
* Construct a user readable date string given an OS/8 date value. The caller
|
||
* must supply a suitably sized buffer to contain the date string. If the
|
||
* supplied date value is zero, the date string will be all spaces.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* date - OS/8 date value
|
||
* buf - pointer to buffer to receive the file name
|
||
* (Must be at least 12 bytes)
|
||
*
|
||
* Outputs:
|
||
*
|
||
* The buffer will be overwritten with the date string.
|
||
*
|
||
* Returns:
|
||
*
|
||
* Pointer to the NULL terminated file name string
|
||
*
|
||
--*/
|
||
static char *os8Date(
|
||
uint16_t date,
|
||
char *buf
|
||
)
|
||
{
|
||
if (date != 0) {
|
||
sprintf(buf, "%2d-%s-%4d",
|
||
(date >> 3) & 0x1F,
|
||
os8Month[(date >> 8) & 0xF],
|
||
1970 + (date & 0x7));
|
||
} else strcpy(buf, " ");
|
||
|
||
return buf;
|
||
}
|
||
|
||
/*++
|
||
* o s 8 F i l e N a m e
|
||
*
|
||
* Construct a user readable filename string from a directory entry in the
|
||
* mount point specific buffer. The caller must supply a suitably sized
|
||
* buffer to contain the file name string.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* mount - pointer to a mounted file system descriptor
|
||
* off - offset of the directory entry
|
||
* buf - pointer to buffer to receive the file name
|
||
* (Must be at least 10 bytes)
|
||
*
|
||
* Outputs:
|
||
*
|
||
* The buffer will be overwritten with the file name
|
||
*
|
||
* Returns:
|
||
*
|
||
* Pointer to the NULL terminated file name string
|
||
*
|
||
--*/
|
||
static char *os8FileName(
|
||
struct mountedFS *mount,
|
||
uint16_t off,
|
||
char *buf
|
||
)
|
||
{
|
||
struct OS8data *data = &mount->os8data;
|
||
uint16_t fname1 = os8Word(le16toh(data->buf[off + OS8_DI_FNAME1]));
|
||
uint16_t fname2 = os8Word(le16toh(data->buf[off + OS8_DI_FNAME2]));
|
||
uint16_t fname3 = os8Word(le16toh(data->buf[off + OS8_DI_FNAME3]));
|
||
uint16_t ext = os8Word(le16toh(data->buf[off + OS8_DI_EXT]));
|
||
|
||
buf[0] = sixbit[(fname1 >> 6) & 0x3F];
|
||
buf[1] = sixbit[fname1 & 0x3F];
|
||
buf[2] = sixbit[(fname2 >> 6) & 0x3F];
|
||
buf[3] = sixbit[fname2 & 0x3F];
|
||
buf[4] = sixbit[(fname3 >> 6) & 0x3F];
|
||
buf[5] = sixbit[fname3 & 0x3F];
|
||
buf[6] = '.';
|
||
buf[7] = sixbit[(ext >> 6) & 0x3F];
|
||
buf[8] = sixbit[ext & 0x3F];
|
||
buf[9] = '\0';
|
||
|
||
return buf;
|
||
}
|
||
|
||
/*++
|
||
* o s 8 U n i t V a l i d
|
||
*
|
||
* Determine if a provided unit # is valid.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* data - pointer to OS/8 specific data area
|
||
* unit - file system unit number
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* 1 if unit # is valid, 0 otherwise
|
||
*
|
||
--*/
|
||
static int os8UnitValid(
|
||
struct OS8data *data,
|
||
uint8_t unit
|
||
)
|
||
{
|
||
struct OS8device *device;
|
||
|
||
if ((device = data->device) != NULL) {
|
||
if (unit < device->filesys)
|
||
return (data->valid & (1 << unit)) != 0 ? 1 : 0;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/*++
|
||
* r k 0 5 B l o c k P r e s e n t
|
||
*
|
||
* Check if the specified block is present in the container file.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* mount - pointer to a mounted file system descriptor
|
||
* unit - file system unit number
|
||
* block - logical block # in the range 0 - N
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* 1 if block is present in the container file, 0 otherwise
|
||
*
|
||
--*/
|
||
static int rk05BlockPresent(
|
||
struct mountedFS *mount,
|
||
uint8_t unit,
|
||
unsigned int block
|
||
)
|
||
{
|
||
struct OS8data *data = &mount->os8data;
|
||
unsigned int base = unit == 0 ? 0 : OS8_RK05FS_BLKS;
|
||
|
||
return data->blocks > (base + block);
|
||
}
|
||
|
||
/*++
|
||
* r k 0 5 R e a d B l o c k
|
||
*
|
||
* Read a block from an OS/8 file system on an RK05 disk.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* mount - pointer to a mounted file system descriptor
|
||
* unit - file system unit number
|
||
* block - logical block # in the range 0 - N
|
||
* buf - buffer to receive data
|
||
*
|
||
* Outputs:
|
||
*
|
||
* The block will be read into the specified buffer
|
||
*
|
||
* Returns:
|
||
*
|
||
* 1 if successful, 0 otherwise
|
||
*
|
||
--*/
|
||
static int rk05ReadBlock(
|
||
struct mountedFS *mount,
|
||
uint8_t unit,
|
||
unsigned int block,
|
||
void *buf
|
||
)
|
||
{
|
||
int status = 0;
|
||
unsigned int base = unit == 0 ? 0 : OS8_RK05FS_BLKS;
|
||
|
||
if (block >= OS8_RK05FS_BLKS) {
|
||
ERROR("Attempt to read block (%u) outside file system \"%s%o:\"\n",
|
||
block, mount->name, unit);
|
||
return 0;
|
||
}
|
||
|
||
status = FSioReadBlock(mount, base + block, buf);
|
||
|
||
if (status == 0)
|
||
ERROR("I/O error on \"%s%o:\"\n", mount->name, unit);
|
||
|
||
return status;
|
||
}
|
||
|
||
/*++
|
||
* r k 0 5 W r i t e B l o c k
|
||
*
|
||
* Write a block to an OS/8 file system on an RK05 disk.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* mount - pointer to a mounted file system descriptor
|
||
* unit - file system unit number
|
||
* block - logical block # in the range 0 - N
|
||
* buf - buffer containing the data
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* 1 if successful, 0 otherwise
|
||
*
|
||
--*/
|
||
static int rk05WriteBlock(
|
||
struct mountedFS *mount,
|
||
uint8_t unit,
|
||
unsigned int block,
|
||
void *buf
|
||
)
|
||
{
|
||
int status = 0;
|
||
unsigned int base = unit == 0 ? 0 : OS8_RK05FS_BLKS;
|
||
|
||
if (block >= OS8_RK05FS_BLKS) {
|
||
ERROR("Attempt to write block (%u) outside file system \"%s%o:\"\n",
|
||
block, mount->name, unit);
|
||
return 0;
|
||
}
|
||
|
||
status = FSioWriteBlock(mount, base + block, buf);
|
||
|
||
if (status == 0)
|
||
ERROR("I/O error on \"%s%o:\"\n", mount->name, unit);
|
||
|
||
return status;
|
||
}
|
||
|
||
/*++
|
||
* r x 0 1 B l o c k P r e s e n t
|
||
*
|
||
* Check if the specified block is present in the container file.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* mount - pointer to a mounted file system descriptor
|
||
* unit - file system unit number
|
||
* block - logical block # in the range 0 - N
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* 1 if block is present in the container file, 0 otherwise
|
||
*
|
||
--*/
|
||
static int rx01BlockPresent(
|
||
struct mountedFS *mount,
|
||
uint8_t UNUSED(unit),
|
||
unsigned int block
|
||
)
|
||
{
|
||
struct OS8data *data = &mount->os8data;
|
||
|
||
return data->blocks > block;
|
||
}
|
||
|
||
/*++
|
||
* r x 0 1 R e a d B l o c k
|
||
*
|
||
* Read a block from an OS/8 file system on an RX01 disk.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* mount - pointer to a mounted file system descriptor
|
||
* unit - file system unit number
|
||
* block - logical block # in the range 0 - N
|
||
* buf - buffer to receive data
|
||
*
|
||
* Outputs:
|
||
*
|
||
* The block will be read into the specified buffer
|
||
*
|
||
* Returns:
|
||
*
|
||
* 1 if successful, 0 otherwise
|
||
*
|
||
--*/
|
||
static int rx01ReadBlock(
|
||
struct mountedFS *mount,
|
||
uint8_t unit,
|
||
unsigned int block,
|
||
void *buf
|
||
)
|
||
{
|
||
unsigned int base = (block / RX01REPEAT) * RX01REPEAT;
|
||
unsigned int offset = block % RX01REPEAT;
|
||
uint16_t *buffer = buf;
|
||
int i, j, k, status;
|
||
|
||
#ifdef DEBUG
|
||
if ((mount->flags & FS_DEBUG) != 0)
|
||
fprintf(DEBUGout, " >>%s%o: rx01ReadBlock, base %u, offset %u\n",
|
||
mount->name, unit, base, offset);
|
||
#endif
|
||
|
||
/*
|
||
* Convert to sector number
|
||
*/
|
||
base *= OS8_BLOCKSIZE / OS8_RX01SS_W;
|
||
|
||
/*
|
||
* Read 4 sectors into the buffer
|
||
*/
|
||
for (i = 0; i < 4; i++) {
|
||
unsigned int sector = base + rx01interleave[(offset * 4) + i];
|
||
unsigned char temp[OS8_RX01SS];
|
||
|
||
#ifdef DEBUG
|
||
if ((mount->flags & FS_DEBUG) != 0)
|
||
fprintf(DEBUGout, " >> %s%o: (os8) Reading %u, track %u sector %u\n",
|
||
mount->name, unit, sector,
|
||
base + ((rx01interleave[(offset * 4) + i] + 1) / OS8_RX01SS),
|
||
(rx01interleave[(offset * 4) + i] + 1) % OS8_RX01SS);
|
||
#endif
|
||
|
||
if ((status = FSioReadSector(mount, sector, OS8_RX01SS, temp)) == 0) {
|
||
ERROR("I/O error on \"%s%o:\"\n", mount->name, unit);
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* Unpack 64 words from the temporary buffer
|
||
*/
|
||
for (j = 0, k = 0; j < OS8_RX01SS_W; j += 2, k += 3) {
|
||
buffer[j] = ((temp[k] << 4) | ((temp[k + 1] >> 4) & 0xF)) & 0xFFF;
|
||
buffer[j + 1] = (((temp[k + 1] << 8) & 0xF00) | temp[k + 2]) & 0xFFF;
|
||
}
|
||
buffer += OS8_RX01SS_W;
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
/*++
|
||
* r x 0 1 W r i t e B l o c k
|
||
*
|
||
* Write a block to an OS/8 file system on an RX01 disk.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* mount - pointer to a mounted file system descriptor
|
||
* unit - file system unit number
|
||
* block - logical block # in the range 0 - N
|
||
* buf - buffer containing the data
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* 1 if successful, 0 otherwise
|
||
*
|
||
--*/
|
||
static int rx01WriteBlock(
|
||
struct mountedFS *mount,
|
||
uint8_t unit,
|
||
unsigned int block,
|
||
void *buf
|
||
)
|
||
{
|
||
unsigned int base = (block / RX01REPEAT) * RX01REPEAT;
|
||
unsigned int offset = block % RX01REPEAT;
|
||
uint16_t *buffer = buf;
|
||
int i, j, k, status;
|
||
|
||
#ifdef DEBUG
|
||
if ((mount->flags & FS_DEBUG) != 0)
|
||
fprintf(DEBUGout, " >>%s%o: rx01WriteBlock, base %u, offset %u\n",
|
||
mount->name, unit, base, offset);
|
||
#endif
|
||
|
||
/*
|
||
* Convert to sector number
|
||
*/
|
||
base *= OS8_BLOCKSIZE / OS8_RX01SS_W;
|
||
|
||
/*
|
||
* Write 4 sectors from the buffer
|
||
*/
|
||
for (i = 0; i < 4; i++) {
|
||
unsigned int sector = base + rx01interleave[(offset * 4) + i];
|
||
unsigned char temp[OS8_RX01SS];
|
||
|
||
/*
|
||
* Pack 64 words into the temporary buffer
|
||
*/
|
||
for (j = 0, k = 0; j < OS8_RX01SS_W; j += 2, k += 3) {
|
||
temp[k] = (buffer[j] >> 4) & 0xFF;
|
||
temp[k + 1] = ((buffer[j] << 4) & 0xF0) | ((buffer[j + 1] >> 8) & 0xF);
|
||
temp[k + 2] = buffer[j + 1] & 0xFF;
|
||
}
|
||
|
||
#ifdef DEBUG
|
||
if ((mount->flags & FS_DEBUG) != 0)
|
||
fprintf(DEBUGout, " >> %s%o: (os8) Writing %u, track %u sector %u\n",
|
||
mount->name, unit, sector,
|
||
base + ((rx01interleave[(offset * 4) + i] + 1) / OS8_RX01SS),
|
||
(rx01interleave[(offset * 4) + i] + 1) % OS8_RX01SS);
|
||
#endif
|
||
|
||
if ((status = FSioWriteSector(mount, sector, OS8_RX01SS, temp)) == 0) {
|
||
ERROR("I/O error on \"%s%o:\"\n", mount->name, unit);
|
||
return 0;
|
||
}
|
||
buffer += OS8_RX01SS_W;
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
/*++
|
||
* r x 0 2 B l o c k P r e s e n t
|
||
*
|
||
* Check if the specified block is present in the container file.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* mount - pointer to a mounted file system descriptor
|
||
* unit - file system unit number
|
||
* block - logical block # in the range 0 - N
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* 1 if block is present in the container file, 0 otherwise
|
||
*
|
||
--*/
|
||
static int rx02BlockPresent(
|
||
struct mountedFS *mount,
|
||
uint8_t UNUSED(unit),
|
||
unsigned int block
|
||
)
|
||
{
|
||
struct OS8data *data = &mount->os8data;
|
||
|
||
return data->blocks > block;
|
||
}
|
||
|
||
/*++
|
||
* r x 0 2 R e a d B l o c k
|
||
*
|
||
* Read a block from an OS/8 file system on an RX01 disk.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* mount - pointer to a mounted file system descriptor
|
||
* unit - file system unit number
|
||
* block - logical block # in the range 0 - N
|
||
* buf - buffer to receive data
|
||
*
|
||
* Outputs:
|
||
*
|
||
* The block will be read into the specified buffer
|
||
*
|
||
* Returns:
|
||
*
|
||
* 1 if successful, 0 otherwise
|
||
*
|
||
--*/
|
||
static int rx02ReadBlock(
|
||
struct mountedFS *mount,
|
||
uint8_t unit,
|
||
unsigned int block,
|
||
void *buf
|
||
)
|
||
{
|
||
unsigned int base = (block / RX02REPEAT) * RX02REPEAT;
|
||
unsigned int offset = block % RX02REPEAT;
|
||
uint16_t *buffer = buf;
|
||
int i, j, k, status;
|
||
|
||
#ifdef DEBUG
|
||
if ((mount->flags & FS_DEBUG) != 0)
|
||
fprintf(DEBUGout, " >>%s%o: rx02ReadBlock, base %u, offset %u\n",
|
||
mount->name, unit, base, offset);
|
||
#endif
|
||
|
||
/*
|
||
* Convert to sector number
|
||
*/
|
||
base *= OS8_BLOCKSIZE / OS8_RX02SS_W;
|
||
|
||
/*
|
||
* Read 2 sectors into the buffer
|
||
*/
|
||
for (i = 0; i < 2; i++) {
|
||
unsigned int sector = base + rx02interleave[(offset * 2) + i];
|
||
unsigned char temp[OS8_RX02SS];
|
||
|
||
#ifdef DEBUG
|
||
if ((mount->flags & FS_DEBUG) != 0)
|
||
fprintf(DEBUGout, " >> %s%o: (os8) Reading %u, track %u sector %u\n",
|
||
mount->name, unit, sector,
|
||
base + ((rx02interleave[(offset * 2) + i] + 1) / OS8_RX02SS),
|
||
(rx02interleave[(offset * 2) + i] + 1) % OS8_RX02SS);
|
||
#endif
|
||
|
||
if ((status = FSioReadSector(mount, sector, OS8_RX02SS, temp)) == 0) {
|
||
ERROR("I/O error on \"%s%o:\"\n", mount->name, unit);
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* Unpack 128 words from the temporary buffer
|
||
*/
|
||
for (j = 0, k = 0; j < OS8_RX02SS_W; j += 2, k += 3) {
|
||
buffer[j] = ((temp[k] << 4) | ((temp[k + 1] >> 4) & 0xF)) & 0xFFF;
|
||
buffer[j + 1] = (((temp[k + 1] << 8) & 0xF00) | temp[k + 2]) & 0xFFF;
|
||
}
|
||
buffer += OS8_RX02SS_W;
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
/*++
|
||
* r x 0 2 W r i t e B l o c k
|
||
*
|
||
* Write a block to an OS/8 file system on an RX01 disk.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* mount - pointer to a mounted file system descriptor
|
||
* unit - file system unit number
|
||
* block - logical block # in the range 0 - N
|
||
* buf - buffer containing the data
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* 1 if successful, 0 otherwise
|
||
*
|
||
--*/
|
||
static int rx02WriteBlock(
|
||
struct mountedFS *mount,
|
||
uint8_t unit,
|
||
unsigned int block,
|
||
void *buf
|
||
)
|
||
{
|
||
unsigned int base = (block / RX02REPEAT) * RX02REPEAT;
|
||
unsigned int offset = block % RX02REPEAT;
|
||
uint16_t *buffer = buf;
|
||
int i, j, k, status;
|
||
|
||
#ifdef DEBUG
|
||
if ((mount->flags & FS_DEBUG) != 0)
|
||
fprintf(DEBUGout, " >>%s%o: rx01WriteBlock, base %u, offset %u\n",
|
||
mount->name, unit, base, offset);
|
||
#endif
|
||
|
||
/*
|
||
* Convert to sector number
|
||
*/
|
||
base *= OS8_BLOCKSIZE / OS8_RX02SS_W;
|
||
|
||
/*
|
||
* Write 2 sectors from the buffer
|
||
*/
|
||
for (i = 0; i < 2; i++) {
|
||
unsigned int sector = base + rx02interleave[(offset * 2) + i];
|
||
unsigned char temp[OS8_RX02SS];
|
||
|
||
/*
|
||
* Pack 128 words into the temporary buffer
|
||
*/
|
||
for (j = 0, k = 0; j < OS8_RX02SS_W; j += 2, k += 3) {
|
||
temp[k] = (buffer[j] >> 4) & 0xFF;
|
||
temp[k + 1] = ((buffer[j] << 4) & 0xF0) | ((buffer[j + 1] >> 8) & 0xF);
|
||
temp[k + 2] = buffer[j + 1] & 0xFF;
|
||
}
|
||
|
||
#ifdef DEBUG
|
||
if ((mount->flags & FS_DEBUG) != 0)
|
||
fprintf(DEBUGout, " >> %s%o: (os8) Writing %u, track %u sector %u\n",
|
||
mount->name, unit, sector,
|
||
base + ((rx02interleave[(offset * 2) + i] + 1) / OS8_RX02SS),
|
||
(rx02interleave[(offset * 2) + i] + 1) % OS8_RX02SS);
|
||
#endif
|
||
|
||
if ((status = FSioWriteSector(mount, sector, OS8_RX02SS, temp)) == 0) {
|
||
ERROR("I/O error on \"%s%o:\"\n", mount->name, unit);
|
||
return 0;
|
||
}
|
||
buffer += OS8_RX02SS_W;
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
/*++
|
||
* o s 8 B l o c k P r e s e n t
|
||
*
|
||
* Check if a block is present in the container file.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* mount - pointer to a mounted file system descriptor
|
||
* unit - file system unit number
|
||
* block - logical block # in the range 0 - N
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* 1 if the block is present, 0 otherwise
|
||
*
|
||
--*/
|
||
int os8BlockPresent(
|
||
struct mountedFS *mount,
|
||
uint8_t unit,
|
||
unsigned int block
|
||
)
|
||
{
|
||
struct OS8data *data = &mount->os8data;
|
||
|
||
return (*data->device->blockPresent)(mount, unit, block);
|
||
}
|
||
|
||
/*++
|
||
* o s 8 R e a d B l o c k
|
||
*
|
||
* Read a block from an OS/8 file system.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* mount - pointer to a mounted file system descriptor
|
||
* unit - file system unit number
|
||
* block - logical block # in the range 0 - N
|
||
* buf - buffer to receive data, if NULL use the mount
|
||
* point specific buffer
|
||
*
|
||
* Outputs:
|
||
*
|
||
* The block will be read into the specified buffer.
|
||
*
|
||
* Returns:
|
||
*
|
||
* 1 if successful, 0 otherwise
|
||
*
|
||
--*/
|
||
int os8ReadBlock(
|
||
struct mountedFS *mount,
|
||
uint8_t unit,
|
||
unsigned int block,
|
||
void *buf
|
||
)
|
||
{
|
||
struct OS8data *data = &mount->os8data;
|
||
void *buffer = buf == NULL ? data->buf : buf;
|
||
|
||
if ((unit >= data->device->filesys) || ((data->valid & (1 << unit)) == 0)) {
|
||
ERROR("Invalid device \"%s%o:\"\n", mount->name, unit);
|
||
return 0;
|
||
}
|
||
|
||
#ifdef DEBUG
|
||
if ((mount->flags & FS_DEBUG) != 0)
|
||
fprintf(DEBUGout, ">> %s%o: (os8) Reading logical block %u\n",
|
||
mount->name, unit, block);
|
||
#endif
|
||
|
||
return (*data->device->readBlock)(mount, unit, block, buffer);
|
||
}
|
||
|
||
/*++
|
||
* o s 8 W r i t e B l o c k
|
||
*
|
||
* Write a block to an OS/8 file system.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* mount - pointer to a mounted file system descriptor
|
||
* unit - file system unit number
|
||
* block - logical block # in the range 0 - N
|
||
* buf - buffer to receive data, if NULL use the mount
|
||
* point specific buffer
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* 1 if successful, 0 otherwise
|
||
*
|
||
--*/
|
||
int os8WriteBlock(
|
||
struct mountedFS *mount,
|
||
uint8_t unit,
|
||
unsigned int block,
|
||
void *buf
|
||
)
|
||
{
|
||
struct OS8data *data = &mount->os8data;
|
||
void *buffer = buf == NULL ? data->buf : buf;
|
||
|
||
if ((unit >= data->device->filesys) || ((data->valid & (1 << unit)) == 0)) {
|
||
ERROR("Invalid device \"%s%o:\"\n", mount->name, unit);
|
||
return 0;
|
||
}
|
||
|
||
#ifdef DEBUG
|
||
if ((mount->flags & FS_DEBUG) != 0)
|
||
fprintf(DEBUGout, ">> %s%o: (os8) Writing logical block %u\n",
|
||
mount->name, unit, block);
|
||
#endif
|
||
|
||
return (*data->device->writeBlock)(mount, unit, block, buffer);
|
||
}
|
||
|
||
/*++
|
||
* o s 8 C h e c k D i r e c t o r y
|
||
*
|
||
* Check if there is sufficient space available for a new permanent entry
|
||
* in the directory segment currently in the mount specific buffer.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* mount - pointer to a mounted file system descriptor
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* 1 if there is sufficient space available, 0 otherwise
|
||
*
|
||
--*/
|
||
static int os8CheckDirectory(
|
||
struct mountedFS *mount
|
||
)
|
||
{
|
||
struct OS8data *data = &mount->os8data;
|
||
uint16_t i, entrysz, entries, off = OS8_DH_SIZE;
|
||
|
||
entrysz = OS8_DI_SIZE + (-os8SXT(le16toh(data->buf[OS8_DH_EXTRA])));
|
||
entries = os8Neg(le16toh(data->buf[OS8_DH_ENTRIES]));
|
||
|
||
for (i = 0; i < entries; i++)
|
||
if (os8Word(le16toh(data->buf[off + OS8_DI_FNAME1])) != 0)
|
||
off += entrysz;
|
||
else off += OS8_ED_SIZE;
|
||
|
||
return (off + entrysz) < OS8_BLOCKSIZE ? 1 : 0;
|
||
}
|
||
|
||
/*++
|
||
* o s 8 F i n d S p a c e
|
||
*
|
||
* Determine which directory segment has available free space. This routine
|
||
* may be called to find the "best fit" (i.e. smallest free space >= the
|
||
* requested size) or the largest available free space. if successful,
|
||
* the directory segment will be loaded in the mount specific buffer.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* mount - pointer to a mounted file system descriptor
|
||
* unit - file system unit number
|
||
* space - # of blocks of free space required, 0 means find
|
||
* the largest block of free space
|
||
* segment - return directory segment here
|
||
* offset - return directory offset here
|
||
* start - return starting block # here
|
||
*
|
||
* Outputs:
|
||
*
|
||
* The mount specific buffer will be modifed.
|
||
*
|
||
* Returns:
|
||
*
|
||
* 1 if free space was found, 0 otherwise
|
||
*
|
||
--*/
|
||
static int os8FindSpace(
|
||
struct mountedFS *mount,
|
||
uint8_t unit,
|
||
uint16_t space,
|
||
uint16_t *segment,
|
||
uint16_t *offset,
|
||
uint16_t *start
|
||
)
|
||
{
|
||
struct OS8data *data = &mount->os8data;
|
||
int found = 0;
|
||
uint16_t dirseg, diroff, dirstart, size = space == 0 ? 0 : 0xFFFF;
|
||
uint16_t dirblk = OS8_DSSTART;
|
||
|
||
if (os8UnitValid(data, unit)) {
|
||
do {
|
||
uint16_t i, entrysz, entries, extra, startblk, off = OS8_DH_SIZE;
|
||
|
||
if (os8ReadBlock(mount, unit, dirblk, NULL) == 0)
|
||
return 0;
|
||
|
||
entrysz = OS8_DI_SIZE + (-os8SXT(le16toh(data->buf[OS8_DH_EXTRA])));
|
||
entries = os8Neg(le16toh(data->buf[OS8_DH_ENTRIES]));
|
||
extra = os8Neg(le16toh(data->buf[OS8_DH_EXTRA]));
|
||
|
||
startblk = os8Word(le16toh(data->buf[OS8_DH_START]));
|
||
|
||
for (i = 0; i < entries; i++) {
|
||
uint16_t blks;
|
||
|
||
if (os8Word(le16toh(data->buf[off + OS8_DI_FNAME1])) == 0) {
|
||
blks = os8Neg(le16toh(data->buf[off + OS8_ED_LENGTH]));
|
||
|
||
if (space == 0) {
|
||
if (blks > size) {
|
||
size = blks;
|
||
dirseg = dirblk;
|
||
diroff = off;
|
||
dirstart = startblk;
|
||
found = 1;
|
||
}
|
||
} else {
|
||
if (blks >= space)
|
||
if (blks < size) {
|
||
size = blks;
|
||
dirseg = dirblk;
|
||
diroff = off;
|
||
dirstart = startblk;
|
||
found = 1;
|
||
}
|
||
}
|
||
off += OS8_ED_SIZE;
|
||
} else {
|
||
blks = os8Neg(le16toh(data->buf[off + extra + OS8_DI_LENGTH]));
|
||
off += entrysz;
|
||
}
|
||
startblk += blks;
|
||
}
|
||
|
||
dirblk = os8Word(le16toh(data->buf[OS8_DH_NEXT]));
|
||
} while (dirblk != 0);
|
||
|
||
/*
|
||
* If we found a suitable entry, load the directory segment and update
|
||
* return information.
|
||
*/
|
||
if (found) {
|
||
if (os8ReadBlock(mount, unit, dirseg, NULL) == 0)
|
||
return 0;
|
||
|
||
*segment = dirseg;
|
||
*offset = diroff;
|
||
*start = dirstart;
|
||
}
|
||
}
|
||
return found;
|
||
}
|
||
|
||
/*++
|
||
* o s 8 S l i d e D o w n
|
||
*
|
||
* Open up a new permanent directory entry at the specified offset within
|
||
* a directory segment. This routine assumes thatnthe caller has verified
|
||
* that there is sufficient free space at the end of the directory for a new
|
||
* entry. The directory segment is in the mount specific buffer.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* mount - pointer to a mounted file system descriptor
|
||
* off - offset to add the new directory entry
|
||
* entries - # of directory entries
|
||
* entrysz - size of a permanent directory entry
|
||
* remain - return remaining entries here
|
||
*
|
||
* Outputs:
|
||
*
|
||
* The mount specific buffer will be modified.
|
||
*
|
||
* Returns:
|
||
*
|
||
* None
|
||
*
|
||
--*/
|
||
static void os8SlideDown(
|
||
struct mountedFS *mount,
|
||
uint16_t off,
|
||
uint16_t entries,
|
||
uint16_t entrysz,
|
||
uint16_t *remain
|
||
)
|
||
{
|
||
struct OS8data *data = &mount->os8data;
|
||
uint16_t i, j, src = OS8_DH_SIZE, dst = OS8_DH_SIZE;
|
||
uint16_t buf[OS8_BLOCKSIZE];
|
||
|
||
/*
|
||
* Build an updated directory header
|
||
*/
|
||
buf[OS8_DH_ENTRIES] = htole16(os8Value(-(entries + 1)));
|
||
buf[OS8_DH_START] = data->buf[OS8_DH_START];
|
||
buf[OS8_DH_NEXT] = data->buf[OS8_DH_NEXT];
|
||
buf[OS8_DH_FLAGWD] = htole16(os8Value(0));
|
||
buf[OS8_DH_EXTRA] = data->buf[OS8_DH_EXTRA];
|
||
|
||
for (i = 0; i < entries; i++) {
|
||
uint16_t len;
|
||
|
||
if (src == off) {
|
||
dst += entrysz;
|
||
*remain = entries - (i + 1);
|
||
}
|
||
|
||
len =
|
||
os8Word(le16toh(data->buf[src + OS8_DI_FNAME1])) ? entrysz : OS8_ED_SIZE;
|
||
|
||
for (j = 0; j < len; j++)
|
||
buf[dst++] = data->buf[src++];
|
||
}
|
||
|
||
/*
|
||
* Now copy the updated directory segment back to the mount speccific buffer
|
||
*/
|
||
memcpy(data->buf, buf, sizeof(buf));
|
||
}
|
||
|
||
/*++
|
||
* o s 8 S l i d e U p
|
||
*
|
||
* Remove a full or partial directory entry by sliding the remaining entries
|
||
* up. The directory segment is in the mount specific buffer.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* mount - pointer to a mounted file system descriptor
|
||
* dst - offset to entry being removed
|
||
* src - offset to first entry to me moved
|
||
* entrysz - size of a permanent directory entry
|
||
* count - # of entries to "slide up"
|
||
*
|
||
* Outputs:
|
||
*
|
||
* The mount specific buffer may be modified
|
||
*
|
||
* Returns:
|
||
*
|
||
* None
|
||
*
|
||
--*/
|
||
static void os8SlideUp(
|
||
struct mountedFS *mount,
|
||
uint16_t dst,
|
||
uint16_t src,
|
||
uint16_t entrysz,
|
||
uint16_t count
|
||
)
|
||
{
|
||
struct OS8data *data = &mount->os8data;
|
||
uint16_t i, j, len;
|
||
|
||
if (count != 0) {
|
||
for (i = 0; i < count; i++) {
|
||
len = os8Word(le16toh(data->buf[src + OS8_DI_FNAME1])) ? entrysz : OS8_ED_SIZE;
|
||
|
||
for (j = 0; j < len; j++)
|
||
data->buf[dst++] = data->buf[src++];
|
||
}
|
||
}
|
||
}
|
||
|
||
/*++
|
||
* o s 8 S p l i t D i r e c t o r y
|
||
*
|
||
* Split the directory segment currently in the mount specific buffer. The
|
||
* split attempts to optimize for maximal use of each directory segment
|
||
* while using a minimal number of directory segments.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* mount - pointer to a mounted file system descriptor
|
||
* unit - file system unit number
|
||
* segment - directory segment to be split
|
||
*
|
||
* Outputs:
|
||
*
|
||
* The mount specific buffer may be modified.
|
||
*
|
||
* Returns:
|
||
*
|
||
* 1 if directory segment was successfully split, 0 otherwise
|
||
*
|
||
--*/
|
||
static int os8SplitDirectory(
|
||
struct mountedFS *mount,
|
||
uint8_t unit,
|
||
uint16_t segment
|
||
)
|
||
{
|
||
struct OS8data *data = &mount->os8data;
|
||
uint16_t inuse = 0;
|
||
uint16_t dirblk = OS8_DSSTART;
|
||
uint16_t buf[OS8_BLOCKSIZE];
|
||
uint16_t empoff[OS8_BLOCKSIZE], emplen[OS8_BLOCKSIZE];
|
||
uint16_t emptotal = 0, emplast = 0, empcount = 0;
|
||
uint16_t i, j, entries, entrysz, extra, startblk, split = 0;
|
||
uint16_t off, target, dst = OS8_DH_SIZE;
|
||
|
||
/*
|
||
* First we need to find an unused directory segment.
|
||
*/
|
||
do {
|
||
inuse |= (1 << dirblk);
|
||
|
||
if (os8ReadBlock(mount, unit, dirblk, buf) == 0)
|
||
return 0;
|
||
|
||
dirblk = os8Word(le16toh(buf[OS8_DH_NEXT]));
|
||
} while (dirblk != 0);
|
||
|
||
for (i = OS8_DSSTART; i <= OS8_DSLAST; i++) {
|
||
if ((inuse & (1 << i)) == 0) {
|
||
/*
|
||
* Build a new directory segment
|
||
*/
|
||
buf[OS8_DH_ENTRIES] = 0;
|
||
buf[OS8_DH_NEXT] = data->buf[OS8_DH_NEXT];
|
||
buf[OS8_DH_FLAGWD] = htole16(os8Value(0));
|
||
buf[OS8_DH_EXTRA] = data->buf[OS8_DH_EXTRA];
|
||
|
||
entrysz = OS8_DI_SIZE + (-os8SXT(le16toh(data->buf[OS8_DH_EXTRA])));
|
||
entries = os8Neg(le16toh(data->buf[OS8_DH_ENTRIES]));
|
||
extra = os8Neg(le16toh(data->buf[OS8_DH_EXTRA]));
|
||
|
||
startblk = os8Word(le16toh(data->buf[OS8_DH_START]));
|
||
|
||
/*
|
||
* Analyze the current directory segment by finding all the empty
|
||
* descriptors.
|
||
*/
|
||
off = OS8_DH_SIZE;
|
||
|
||
for (j = 0; j < entries; j++) {
|
||
if (os8Word(le16toh(data->buf[off + OS8_DI_FNAME1])) == 0) {
|
||
emplen[empcount] = os8Neg(le16toh(data->buf[off + OS8_ED_LENGTH]));
|
||
emptotal += emplen[empcount];
|
||
empoff[empcount] = off;
|
||
empcount++;
|
||
emplast = j == (entries - 1);
|
||
|
||
off += OS8_ED_SIZE;
|
||
} else off += entrysz;
|
||
}
|
||
|
||
/*
|
||
* We want to avoid "stranding" unused directory entries where there
|
||
* is no free space so we apply the following heuristics:
|
||
*
|
||
* 1. If the last directory entry is "empty" and contains at least
|
||
* 50% of the free space, just move this entry to the newly
|
||
* created directory segment
|
||
*
|
||
* 2. Start the new directory segment at the "empty" entry which would
|
||
* exceed 50% of the free space.
|
||
*/
|
||
target = empoff[empcount - 1];
|
||
|
||
if (!emplast || (emplen[empcount - 1] < (emptotal / 2))) {
|
||
uint16_t empty = 0;
|
||
|
||
for (j = 0; j < empcount; j++)
|
||
if ((empty + emplen[j]) >= (emptotal / 1)) {
|
||
target = empoff[j];
|
||
break;
|
||
} else empty += emplen[j];
|
||
}
|
||
|
||
/*
|
||
* Find the location of the split point and copy all directory entries
|
||
* after this point to the newly created directory segment.
|
||
*/
|
||
off = OS8_DH_SIZE;
|
||
|
||
for (j = 0; j < entries; j++) {
|
||
uint16_t blks, size;
|
||
|
||
if (off == target) {
|
||
split = j + 1;
|
||
buf[OS8_DH_START] = htole16(os8Value(startblk));
|
||
}
|
||
|
||
if (os8Word(le16toh(data->buf[off + OS8_DI_FNAME1])) != 0) {
|
||
blks = os8Neg(le16toh(data->buf[off + extra + OS8_DI_LENGTH]));
|
||
size = OS8_DI_SIZE + extra;
|
||
} else {
|
||
blks = os8Neg(le16toh(data->buf[off + OS8_ED_LENGTH]));
|
||
size = OS8_ED_SIZE;
|
||
}
|
||
|
||
if (split != 0) {
|
||
uint16_t k;
|
||
|
||
for (k = 0; k < size; k++)
|
||
buf[dst++] = data->buf[off + k];
|
||
|
||
buf[OS8_DH_ENTRIES]++;
|
||
}
|
||
off += size;
|
||
startblk += blks;
|
||
}
|
||
|
||
/*
|
||
* Update the new directory segment
|
||
*/
|
||
buf[OS8_DH_ENTRIES] = htole16(os8Value(-buf[OS8_DH_ENTRIES]));
|
||
|
||
if (os8WriteBlock(mount, unit, i, buf) == 0)
|
||
return 0;
|
||
|
||
/*
|
||
* Update the original directory segment
|
||
*/
|
||
data->buf[OS8_DH_ENTRIES] = htole16(os8Value(-(split - 1)));
|
||
data->buf[OS8_DH_NEXT] = htole16(os8Value(i));
|
||
|
||
if (os8WriteBlock(mount, unit, segment, NULL) == 0)
|
||
return 0;
|
||
|
||
/*
|
||
* Directory segment split complete
|
||
*/
|
||
return 1;
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/*++
|
||
* o s 8 M e r g e E m p t y R e g i o n s
|
||
*
|
||
* Scan a directory segment looking for 2 consecutive empty entries which
|
||
* can be merged into a single entry. The directory segment is in the mount
|
||
* specific buffer.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* mount - pointer to a mounted file system descriptor
|
||
*
|
||
* Outputs:
|
||
*
|
||
* The mount specific buffer may be modified
|
||
*
|
||
* Returns:
|
||
*
|
||
* 1 if a merge occured, 0 otherwise
|
||
*
|
||
--*/
|
||
static int os8MergeEmptyRegions(
|
||
struct mountedFS *mount
|
||
)
|
||
{
|
||
struct OS8data *data = &mount->os8data;
|
||
uint16_t i, entrysz, entries, off = OS8_DH_SIZE;
|
||
|
||
entrysz = OS8_DI_SIZE + (-os8SXT(le16toh(data->buf[OS8_DH_EXTRA])));
|
||
entries = os8Neg(le16toh(data->buf[OS8_DH_ENTRIES]));
|
||
|
||
if (entries > 1) {
|
||
for (i = 0; i < (entries - 1); i++) {
|
||
if (os8Word(le16toh(data->buf[off + OS8_DI_FNAME1])) == 0) {
|
||
if (os8Word(le16toh(data->buf[off + OS8_ED_SIZE + OS8_DI_FNAME1])) == 0) {
|
||
data->buf[off + OS8_ED_LENGTH] =
|
||
htole16(os8Value(le16toh(data->buf[off + OS8_ED_LENGTH]) +
|
||
le16toh(data->buf[off + OS8_ED_SIZE + OS8_ED_LENGTH])));
|
||
data->buf[OS8_DH_ENTRIES] = htole16(os8Value(-(entries - 1)));
|
||
|
||
os8SlideUp(mount, off + OS8_ED_SIZE,
|
||
off + (2 * OS8_ED_SIZE), entrysz, entries - 2);
|
||
return 1;
|
||
}
|
||
} else off += entrysz;
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/*++
|
||
* i n f o
|
||
*
|
||
* Display information about the internal structure of a single file system.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* mount - pointer to a mounted file system descriptor
|
||
* unit - file system unit number
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* None
|
||
*
|
||
--*/
|
||
static void info(
|
||
struct mountedFS *mount,
|
||
uint8_t unit
|
||
)
|
||
{
|
||
struct OS8data *data = &mount->os8data;
|
||
unsigned int dirblk = OS8_DSSTART;
|
||
|
||
printf("\n%s%o:\n\n", mount->name, unit);
|
||
|
||
do {
|
||
uint16_t i, entrysz, entries, extra, startblk, off = OS8_DH_SIZE;
|
||
|
||
if (os8ReadBlock(mount, unit, dirblk, NULL) == 0)
|
||
return;
|
||
|
||
entrysz = OS8_DI_SIZE + (-os8SXT(le16toh(data->buf[OS8_DH_EXTRA])));
|
||
entries = os8Neg(le16toh(data->buf[OS8_DH_ENTRIES]));
|
||
extra = os8Neg(le16toh(data->buf[OS8_DH_EXTRA]));
|
||
|
||
startblk = os8Word(le16toh(data->buf[OS8_DH_START]));
|
||
|
||
printf("\nDirectory Segment %d:\n\n", dirblk);
|
||
printf(" File Type Length Date Disk Region\n\n");
|
||
|
||
for (i = 0; i < entries; i++) {
|
||
char temp1[16], temp2[16];
|
||
uint16_t length;
|
||
|
||
if (os8Word(le16toh(data->buf[off + OS8_DI_FNAME1])) != 0) {
|
||
uint16_t dateval = 0;
|
||
|
||
length = os8Neg(le16toh(data->buf[off + extra + OS8_DI_LENGTH]));
|
||
if (extra != 0)
|
||
dateval = os8Word(le16toh(data->buf[off + OS8_DI_DATE]));
|
||
|
||
printf("%s %4hu %s %4d - %4d\n",
|
||
os8FileName(mount, off, temp1), length,
|
||
os8Date(dateval, temp2), startblk, startblk + length - 1);
|
||
|
||
off += entrysz;
|
||
} else {
|
||
length = os8Neg(le16toh(data->buf[off + OS8_ED_LENGTH]));
|
||
|
||
printf("<EMPTY> %4hu %4d - %4d\n",
|
||
os8Neg(le16toh(data->buf[off + OS8_ED_LENGTH])),
|
||
startblk, startblk + length - 1);
|
||
|
||
off += OS8_ED_SIZE;
|
||
}
|
||
startblk += length;
|
||
}
|
||
|
||
dirblk = os8Word(le16toh(data->buf[OS8_DH_NEXT]));
|
||
} while (dirblk != 0);
|
||
}
|
||
|
||
/*++
|
||
* v a l i d a t e
|
||
*
|
||
* Validate the integrity of an OS/8 file system. OS/8 file systems do not
|
||
* include any form of signature on the disk so we have to run a set of
|
||
* heuristics to verify the integrity of the file system.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* mount - pointer to a mounted file system descriptor
|
||
* unit - file system unit number
|
||
* files - return # of permanent files here
|
||
* freeblks - return # of free blocks here
|
||
* addinfo - return # of additional information words here
|
||
*
|
||
* Outputs:
|
||
*
|
||
* The mount point specific buffer will be overwritten.
|
||
*
|
||
* Returns:
|
||
*
|
||
* 1 if file system is valid, 0 otherwise
|
||
*
|
||
--*/
|
||
static int validate(
|
||
struct mountedFS *mount,
|
||
uint8_t unit,
|
||
uint16_t *files,
|
||
uint16_t *freeblks,
|
||
uint16_t *addinfo
|
||
)
|
||
{
|
||
struct OS8data *data = &mount->os8data;
|
||
unsigned int dirblk = OS8_DSSTART;
|
||
uint8_t seen[OS8_DSLAST + 1];
|
||
uint16_t extraVal;
|
||
|
||
memset(seen, 0, sizeof(seen));
|
||
|
||
do {
|
||
uint16_t i, entrysz, entries, flagwd, extra, startblk, off = OS8_DH_SIZE;
|
||
|
||
/*
|
||
* If we've already seen this directory segment, there must be a loop
|
||
*/
|
||
if (seen[dirblk] != 0)
|
||
return 0;
|
||
|
||
if (!os8BlockPresent(mount, unit, dirblk) ||
|
||
!os8ReadBlock(mount, unit, dirblk, NULL))
|
||
return 0;
|
||
|
||
seen[dirblk] = 1;
|
||
|
||
entrysz = OS8_DI_SIZE + (-os8SXT(le16toh(data->buf[OS8_DH_EXTRA])));
|
||
entries = os8Neg(le16toh(data->buf[OS8_DH_ENTRIES]));
|
||
flagwd = os8Word(le16toh(data->buf[OS8_DH_FLAGWD]));
|
||
extra = os8Neg(le16toh(data->buf[OS8_DH_EXTRA]));
|
||
|
||
startblk = os8Word(le16toh(data->buf[OS8_DH_START]));
|
||
|
||
/*
|
||
* The number of additional information words MUST be the same in all
|
||
* directory segments.
|
||
*/
|
||
if (dirblk == OS8_DSSTART) {
|
||
extraVal = extra;
|
||
*addinfo = extra;
|
||
}
|
||
|
||
if (extraVal != extra)
|
||
return 0;
|
||
|
||
/*
|
||
* Up to 077 extra words may be allocated to each directory entry.
|
||
*/
|
||
if ((extra & 07700) != 0)
|
||
return 0;
|
||
|
||
/*
|
||
* The flag word must be either 0 or in the range 01400 - 01777.
|
||
*/
|
||
if ((flagwd != 0) && ((flagwd & 01400) != 01400))
|
||
return 0;
|
||
|
||
/*
|
||
* There must be at least one directory entry (an empty file occupying
|
||
* all the space) and, at maximum, alternating permanent and empty
|
||
* entries (256 / ((N + 5) + 2)) * 2.
|
||
*/
|
||
if ((entries == 0) || (entries > ((256 / (extra + 7))) * 2))
|
||
return 0;
|
||
|
||
/*
|
||
* Scan the directory checking for valid block addresses.
|
||
*/
|
||
for (i = 0; i < entries; i++) {
|
||
uint16_t length;
|
||
|
||
if (os8Word(le16toh(data->buf[off + OS8_DI_FNAME1])) != 0) {
|
||
length = os8Neg(le16toh(data->buf[off + extra + OS8_DI_LENGTH]));
|
||
off += entrysz;
|
||
*files += 1;
|
||
} else {
|
||
length = os8Neg(le16toh(data->buf[off + OS8_ED_LENGTH]));
|
||
off += OS8_ED_SIZE;
|
||
*freeblks += length;
|
||
}
|
||
startblk += length;
|
||
|
||
/*
|
||
* Make sure the block addresses are valid for this disk device
|
||
*/
|
||
if (startblk > data->device->blocks[unit])
|
||
return 0;
|
||
}
|
||
|
||
dirblk = os8Word(le16toh(data->buf[OS8_DH_NEXT]));
|
||
|
||
if ((dirblk != 0) && ((dirblk < OS8_DSSTART) || (dirblk > OS8_DSLAST)))
|
||
return 0;
|
||
|
||
} while (dirblk != 0);
|
||
|
||
return 1;
|
||
}
|
||
|
||
/*++
|
||
* t o S i x b i t
|
||
*
|
||
* Convert an alphanumeric character to sixbit encoding and place it into
|
||
* the appropriate 6-bit field of a 12-bit value. The character will have
|
||
* already been converted to upper-case if needed.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* ch - the character to convert
|
||
* ptr - pointer to the 12-bit location to update
|
||
* which - which 6-bit field to update:
|
||
* 0 - high 6-bit field
|
||
* 1 - low 6-bit field
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* None
|
||
*
|
||
--*/
|
||
static void toSixbit(
|
||
char ch,
|
||
uint16_t *ptr,
|
||
int which
|
||
)
|
||
{
|
||
unsigned long i;
|
||
|
||
for (i = 0; i < sizeof(sixbit); i++)
|
||
if (ch == sixbit[i]) {
|
||
if (which == 0)
|
||
*ptr = (i << 6) | (*ptr & 0x3F);
|
||
else *ptr = (*ptr & 0xFC0) | i;
|
||
return;
|
||
}
|
||
}
|
||
|
||
/*++
|
||
* o s 8 P a r s e F i l e s p e c
|
||
*
|
||
* Parse a character string representing an OS/8 file specification.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* ptr - pointer to the file specification string
|
||
* spec - pointer to the file specification block
|
||
* wildcard - wildcard processing options:
|
||
* 0 (OS8_M_NONE) - wildcards not allowed
|
||
* 1 (OS8_M_ALLOW) - wildcards allowed
|
||
* 2 (OS8_M_NONAME) - wildcards allowed
|
||
* if filename + ext not
|
||
* present default to *.*
|
||
*
|
||
* Outputs:
|
||
*
|
||
* The file specification block will be filled with the file information.
|
||
*
|
||
* Returns:
|
||
*
|
||
* 1 if parse is successful, 0 otherwise
|
||
*
|
||
--*/
|
||
#define P_DONE 0
|
||
#define P_NAME 1
|
||
#define P_EXT 2
|
||
|
||
int os8ParseFilespec(
|
||
char *ptr,
|
||
struct os8FileSpec *spec,
|
||
int wildcard
|
||
)
|
||
{
|
||
int state = P_NAME;
|
||
unsigned int i;
|
||
|
||
memset(spec, 0, sizeof(struct os8FileSpec));
|
||
memset(&spec->fname, ' ', sizeof(spec->fname));
|
||
memset(&spec->fext, ' ', sizeof(spec->fext));
|
||
|
||
if (wildcard == OS8_M_NONAME)
|
||
if (*ptr == '\0') {
|
||
spec->flags = OS8_WC_NAME | OS8_WC_EXT;
|
||
return 1;
|
||
}
|
||
|
||
while (state != P_DONE) {
|
||
i = 0;
|
||
|
||
switch (state) {
|
||
case P_NAME:
|
||
while ((*ptr != '\0') &&
|
||
(*ptr != '.') &&
|
||
(i < sizeof(spec->fname))) {
|
||
char ch = toupper(*ptr++);
|
||
|
||
if (isalnum(ch)) {
|
||
toSixbit(ch, &spec->name[i >> 1], i & 1);
|
||
spec->fname[i++] = ch;
|
||
} else {
|
||
if (ch == '*')
|
||
if (i == 0) {
|
||
spec->flags |= OS8_WC_NAME;
|
||
break;
|
||
}
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
switch (*ptr) {
|
||
case '\0':
|
||
state = P_DONE;
|
||
break;
|
||
|
||
case '.':
|
||
state = P_EXT;
|
||
ptr++;
|
||
break;
|
||
|
||
default:
|
||
return 0;
|
||
}
|
||
break;
|
||
|
||
case P_EXT:
|
||
while ((*ptr != '\0') &&
|
||
(i < sizeof(spec->fext))) {
|
||
char ch = toupper(*ptr++);
|
||
|
||
if (isalnum(ch)) {
|
||
toSixbit(ch, &spec->ext, i);
|
||
spec->fext[i++] = ch;
|
||
} else {
|
||
if (ch == '*')
|
||
if (i == 0) {
|
||
spec->flags |= OS8_WC_EXT;
|
||
break;
|
||
}
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
if (*ptr != '\0')
|
||
return 0;
|
||
|
||
state = P_DONE;
|
||
break;
|
||
}
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
/*++
|
||
* o s 8 C r e a t e F i l e
|
||
*
|
||
* Create a new file within the OS/8 file system.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* mount - pointer to a mounted file system descriptor
|
||
* unit - file system unit number
|
||
* spec - pointer to the file specification block
|
||
* file - pointer to open file descriptor to receive results
|
||
* size - file size in 256-word blocks
|
||
* 0 means use the largest free space region
|
||
*
|
||
* Outputs:
|
||
*
|
||
* The mount point specific buffer area will be modified
|
||
*
|
||
* Returns:
|
||
*
|
||
* 1 if file successfully created, 0 otherwise
|
||
*
|
||
--*/
|
||
int os8CreateFile(
|
||
struct mountedFS *mount,
|
||
uint8_t unit,
|
||
struct os8FileSpec *spec,
|
||
struct os8OpenFile *file,
|
||
uint16_t size
|
||
)
|
||
{
|
||
struct OS8data *data = &mount->os8data;
|
||
uint16_t segment, off, start, entries, extra, entrysz, space, left;
|
||
|
||
retry:
|
||
/*
|
||
* Check if suitable free space is available
|
||
*/
|
||
if (os8FindSpace(mount, unit, size, &segment, &off, &start) == 0) {
|
||
ERROR("Free space not available on \"%s%o:\"\n", mount->name, unit);
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* Check if there is space to create a new directory entry in this
|
||
* directory segment. If not, split the directory segment in 2 and retry
|
||
* from the beginning.
|
||
*/
|
||
if (!os8CheckDirectory(mount)) {
|
||
if (!os8SplitDirectory(mount, unit, segment)) {
|
||
ERROR("No directory space on \"%s%o:\"\n", mount->name, unit);
|
||
return 0;
|
||
}
|
||
goto retry;
|
||
}
|
||
|
||
entries = os8Neg(le16toh(data->buf[OS8_DH_ENTRIES]));
|
||
extra = -os8SXT(le16toh(data->buf[OS8_DH_EXTRA]));
|
||
entrysz = OS8_DI_SIZE + extra;
|
||
|
||
space = size == 0 ? os8Neg(le16toh(data->buf[off + OS8_ED_LENGTH])) : size;
|
||
left = os8Neg(le16toh(data->buf[off + OS8_ED_LENGTH])) - space;
|
||
|
||
/*
|
||
* Remove the requested space from the empty directory entry.
|
||
*/
|
||
data->buf[off + OS8_ED_LENGTH] = htole16(os8Value(-left));
|
||
|
||
os8SlideDown(mount, off, entries, entrysz, &file->remain);
|
||
|
||
/*
|
||
* Build the directory entry
|
||
*/
|
||
data->buf[off + OS8_DI_FNAME1] = htole16(os8Value(spec->name[0]));
|
||
data->buf[off + OS8_DI_FNAME2] = htole16(os8Value(spec->name[1]));
|
||
data->buf[off + OS8_DI_FNAME3] = htole16(os8Value(spec->name[2]));
|
||
data->buf[off + OS8_DI_EXT] = htole16(os8Value(spec->ext));
|
||
|
||
if (extra != 0)
|
||
data->buf[off + OS8_DI_DATE] = htole16(os8Value(data->date));
|
||
|
||
data->buf[off + extra + OS8_DI_LENGTH] = htole16(os8Value(-space));
|
||
|
||
if (os8WriteBlock(mount, unit, segment, NULL) == 0)
|
||
return 0;
|
||
|
||
/*
|
||
* Fill in the open file descriptor
|
||
*/
|
||
file->name[0] = spec->name[0];
|
||
file->name[1] = spec->name[1];
|
||
file->name[2] = spec->name[2];
|
||
file->ext = spec->ext;
|
||
file->creation = data->date;
|
||
file->length = space;
|
||
|
||
file->segment = segment;
|
||
file->offset = off;
|
||
file->entrysz = entrysz;
|
||
file->extra = extra;
|
||
|
||
file->mount = mount;
|
||
file->unit = unit;
|
||
|
||
file->current = 0;
|
||
file->start = start;
|
||
file->last = start + space - 1;
|
||
|
||
file->wordpos = OS8_BLOCKSIZE;
|
||
file->bytepos = OS8_CHECK;
|
||
file->written = 0;
|
||
|
||
return 1;
|
||
}
|
||
|
||
/*++
|
||
* o s 8 L o o k u p F i l e
|
||
*
|
||
* Lookup a specific file within the OS/8 file system. This routine fills in
|
||
* an open file descriptor with information about the file and the directory
|
||
* entry it resides in.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* mount - pointer to a mounted file system descriptor
|
||
* unit - file system unit number
|
||
* spec - pointer to the file specification block
|
||
* file - pointer to open file descriptor to receive results
|
||
*
|
||
* Outputs:
|
||
*
|
||
* The mount point specific buffer will be modified
|
||
*
|
||
* Returns:
|
||
*
|
||
* 1 if file found, 0 otherwise
|
||
*
|
||
--*/
|
||
int os8LookupFile(
|
||
struct mountedFS *mount,
|
||
uint8_t unit,
|
||
struct os8FileSpec *spec,
|
||
struct os8OpenFile *file
|
||
)
|
||
{
|
||
struct OS8data *data = &mount->os8data;
|
||
unsigned int dirblk = OS8_DSSTART;
|
||
|
||
if (os8UnitValid(data, unit)) {
|
||
do {
|
||
uint16_t i, entrysz, entries, extra, startblk, off = OS8_DH_SIZE;
|
||
|
||
if (os8ReadBlock(mount, unit, dirblk, NULL) == 0)
|
||
return 0;
|
||
|
||
entrysz = OS8_DI_SIZE + (-os8SXT(le16toh(data->buf[OS8_DH_EXTRA])));
|
||
entries = os8Neg(le16toh(data->buf[OS8_DH_ENTRIES]));
|
||
extra = os8Neg(le16toh(data->buf[OS8_DH_EXTRA]));
|
||
|
||
startblk = os8Word(le16toh(data->buf[OS8_DH_START]));
|
||
|
||
for (i = 0; i < entries; i++) {
|
||
if (os8Word(le16toh(data->buf[off + OS8_DI_FNAME1])) != 0) {
|
||
if ((os8Word(le16toh(data->buf[off + OS8_DI_FNAME1])) == spec->name[0]) &&
|
||
(os8Word(le16toh(data->buf[off + OS8_DI_FNAME2])) == spec->name[1]) &&
|
||
(os8Word(le16toh(data->buf[off + OS8_DI_FNAME3])) == spec->name[2]) &&
|
||
(os8Word(le16toh(data->buf[off + OS8_DI_EXT])) == spec->ext)) {
|
||
file->name[0] = spec->name[0];
|
||
file->name[1] = spec->name[1];
|
||
file->name[2] = spec->name[2];
|
||
file->ext = spec->ext;
|
||
file->creation =
|
||
extra != 0 ? os8Word(le16toh(data->buf[off + OS8_DI_DATE])) : 0;
|
||
file->length = os8Neg(le16toh(data->buf[off + extra + OS8_DI_LENGTH]));
|
||
|
||
file->segment = dirblk;
|
||
file->offset = off;
|
||
file->entrysz = entrysz;
|
||
file->extra = extra;
|
||
file->remain = entries - (i + 1);
|
||
|
||
file->mount = mount;
|
||
file->unit = unit;
|
||
|
||
file->current = 0;
|
||
if (file->length != 0) {
|
||
file->start = startblk;
|
||
file->last = startblk + file->length - 1;
|
||
} else file->start = file->last = 0;
|
||
|
||
file->wordpos = OS8_BLOCKSIZE;
|
||
file->bytepos = OS8_CHECK;
|
||
file->written = 0;
|
||
|
||
return 1;
|
||
}
|
||
startblk += os8Neg(le16toh(data->buf[off + extra + OS8_DI_LENGTH]));
|
||
off += entrysz;
|
||
} else {
|
||
startblk += os8Neg(le16toh(data->buf[off + OS8_ED_LENGTH]));
|
||
off += OS8_ED_SIZE;
|
||
}
|
||
}
|
||
dirblk = os8Word(le16toh(data->buf[OS8_DH_NEXT]));
|
||
} while (dirblk != 0);
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/*++
|
||
* o s 8 U p d a t e F i l e
|
||
*
|
||
* Update an OS/8 file by writing back the directory entry associated with
|
||
* the file.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* mount - pointer to a mounted file system descriptor
|
||
* unit - file system unit number
|
||
* file - pointer to open file descriptor
|
||
*
|
||
* Outputs:
|
||
*
|
||
* The mount point specific buffer will be modified
|
||
*
|
||
* Returns:
|
||
*
|
||
* None
|
||
*
|
||
--*/
|
||
void os8UpdateFile(
|
||
struct mountedFS *mount,
|
||
uint8_t unit,
|
||
struct os8OpenFile *file
|
||
)
|
||
{
|
||
struct OS8data *data = &mount->os8data;
|
||
uint16_t off = file->offset;
|
||
uint16_t empty = off + file->extra + OS8_DI_SIZE;
|
||
uint16_t length = file->current == 0 ? 0 : file->current - file->start;
|
||
uint16_t left = file->length - length;
|
||
uint16_t space;
|
||
|
||
if (os8ReadBlock(mount, unit, file->segment, NULL) == 0)
|
||
return;
|
||
|
||
/*
|
||
* The directory entry is complete except for the # of blocks in use
|
||
* which we can now update.
|
||
*/
|
||
data->buf[off + file->extra + OS8_DI_LENGTH] = htole16(os8Value(-length));
|
||
|
||
/*
|
||
* Update the remaining space in the following empty file descriptor.
|
||
*/
|
||
space = os8Neg(le16toh(data->buf[empty + OS8_ED_LENGTH])) + left;
|
||
data->buf[empty + OS8_ED_LENGTH] = htole16(os8Value(-space));
|
||
|
||
if (space == 0)
|
||
/*
|
||
* Remove the empty directory entry since it now occupies 0 blocks
|
||
*/
|
||
os8SlideUp(mount, empty, empty + OS8_ED_SIZE, file->entrysz, file->remain);
|
||
|
||
os8WriteBlock(mount, unit, file->segment, NULL);
|
||
}
|
||
|
||
/*++
|
||
* o s 8 R e a d B y t e s
|
||
*
|
||
* Read a sequence of bytes from an open file. Each pair of 12-bit words in
|
||
* the file will be unpacked into 3 bytes.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* file - pointer to an open file descriptor
|
||
* buf - pointer to a buffer to receive the data
|
||
* len - # of bytes of data to read
|
||
*
|
||
* Outputs:
|
||
*
|
||
* The buffer will be overwritten by up to "len" bytes
|
||
*
|
||
* Returns:
|
||
*
|
||
* # of bytes read from the file (may be less than "len"), 0 if EOF
|
||
*
|
||
--*/
|
||
int os8ReadBytes(
|
||
struct os8OpenFile *file,
|
||
char *buf,
|
||
int len
|
||
)
|
||
{
|
||
struct mountedFS *mount = file->mount;
|
||
int count = 0;
|
||
uint16_t temp1, temp2;
|
||
char mask = SWISSET('a') ? 0x7F : 0xFF;
|
||
|
||
while (len) {
|
||
switch (file->bytepos) {
|
||
case OS8_CHECK:
|
||
if (file->wordpos == OS8_BLOCKSIZE) {
|
||
if (file->current == 0) {
|
||
/*
|
||
* For an empty file (does OS/8 support this?)
|
||
* start == current == last == 0
|
||
*/
|
||
if (file->start == 0)
|
||
return 0;
|
||
|
||
file->current = file->start;
|
||
} else {
|
||
if (file->current == file->last)
|
||
return count;
|
||
file->current++;
|
||
}
|
||
if (os8ReadBlock(mount, file->unit, file->current, file->buffer) == 0)
|
||
return 0;
|
||
file->wordpos = 0;
|
||
}
|
||
file->bytepos = OS8_BYTE0;
|
||
/* FALLTHROUGH */
|
||
|
||
case OS8_BYTE0:
|
||
temp1 = os8Word(le16toh(file->buffer[file->wordpos]));
|
||
*buf++ = temp1 & mask;
|
||
file->bytepos = OS8_BYTE1;
|
||
break;
|
||
|
||
case OS8_BYTE1:
|
||
temp1 = os8Word(le16toh(file->buffer[file->wordpos + 1]));
|
||
*buf++ = temp1 & mask;
|
||
file->bytepos = OS8_BYTE2;
|
||
break;
|
||
|
||
case OS8_BYTE2:
|
||
temp1 = os8Word(le16toh(file->buffer[file->wordpos]));
|
||
temp2 = os8Word(le16toh(file->buffer[file->wordpos + 1]));
|
||
*buf++ = (((temp1 & 0xF00) >> 4) | ((temp2 & 0xF00) >> 8)) & mask;
|
||
file->wordpos += 2;
|
||
file->bytepos = OS8_CHECK;
|
||
break;
|
||
}
|
||
len--;
|
||
count++;
|
||
}
|
||
return count;
|
||
}
|
||
|
||
/*++
|
||
* o s 8 W r i t e B y t e s
|
||
*
|
||
* Write a sequence of bytes to an open file. Each sequence of 3 bytes will
|
||
* generate 2 words in the file.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* file - pointer to an open file descriptor
|
||
* buf - pointer to a buffer to receive the data
|
||
* len - # of bytes of data to read
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* # of bytes written to the file (may be less than "len"), 0 if errort
|
||
*
|
||
--*/
|
||
int os8WriteBytes(
|
||
struct os8OpenFile *file,
|
||
char *buf,
|
||
int len
|
||
)
|
||
{
|
||
struct mountedFS *mount = file->mount;
|
||
int count = 0;
|
||
|
||
while (len) {
|
||
switch (file->bytepos) {
|
||
case OS8_CHECK:
|
||
if (file->wordpos == OS8_BLOCKSIZE) {
|
||
if (file->current == file->last)
|
||
return count;
|
||
|
||
if (file->current != 0) {
|
||
if (os8WriteBlock(mount, file->unit, file->current, file->buffer) == 0)
|
||
return 0;
|
||
|
||
file->current++;
|
||
} else file->current = file->start;
|
||
|
||
file->wordpos = 0;
|
||
memset(file->buffer, 0, OS8_BLOCKSIZE * sizeof(uint16_t));
|
||
}
|
||
file->bytepos = OS8_BYTE0;
|
||
/* FALLTHROUGH */
|
||
|
||
case OS8_BYTE0:
|
||
file->buffer[file->wordpos++] = os8Value(*buf++);
|
||
file->bytepos = OS8_BYTE1;
|
||
break;
|
||
|
||
case OS8_BYTE1:
|
||
file->buffer[file->wordpos++] = os8Value(*buf++);
|
||
file->bytepos = OS8_BYTE2;
|
||
break;
|
||
|
||
case OS8_BYTE2:
|
||
file->buffer[file->wordpos - 2] =
|
||
os8Value(file->buffer[file->wordpos - 2] | ((*buf & 0xF0) << 4));
|
||
file->buffer[file->wordpos - 1] =
|
||
os8Value(file->buffer[file->wordpos - 1] | ((*buf++ & 0xF) << 8));
|
||
file->bytepos = OS8_CHECK;
|
||
break;
|
||
}
|
||
len--;
|
||
count++;
|
||
file->written++;
|
||
}
|
||
return count;
|
||
}
|
||
|
||
/*++
|
||
* o s 8 M o u n t
|
||
*
|
||
* Verify that the open container file contains 1 or more OS/8 file systems.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* mount - pointer to a mounted file system descriptor
|
||
* (not in the mounted file system list)
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* 1 if 1 or more valid OS/8 file systems
|
||
* 0 if no valid OS/8 file system present
|
||
* -1 if no valid OS/8 file system present and an error message has
|
||
* already been printed
|
||
*
|
||
--*/
|
||
static int os8Mount(
|
||
struct mountedFS *mount
|
||
)
|
||
{
|
||
struct OS8data *data = &mount->os8data;
|
||
struct stat stat;
|
||
uint8_t valid = 0;
|
||
uint16_t files[8], freeblks[8], extra[8];
|
||
|
||
memset(files, 0, sizeof(files));
|
||
memset(freeblks, 0, sizeof(freeblks));
|
||
memset(extra, 0, sizeof(extra));
|
||
|
||
/*
|
||
* Allow access to all filesystems for validation
|
||
*/
|
||
data->device = NULL;
|
||
data->valid = 0xFF;
|
||
|
||
if (fstat(fileno(mount->container), &stat) == 0) {
|
||
data->blocks = stat.st_size / OS8_BLOCKSIZE;
|
||
|
||
if (SWISSET('t')) {
|
||
struct OS8device *dev = OS8Devices;
|
||
|
||
while (dev->name != NULL) {
|
||
if (strcmp(SWGETVAL('t'), dev->name) == 0) {
|
||
uint8_t i, count = 0;
|
||
|
||
mount->skip = dev->skip;
|
||
data->device = dev;
|
||
|
||
/*
|
||
* Validate all possible file systems.
|
||
*/
|
||
for (i = 0; i < dev->filesys; i++)
|
||
if (validate(mount, i, &files[i], &freeblks[i], &extra[i]) != 0) {
|
||
count++;
|
||
valid |= 1 << i;
|
||
}
|
||
|
||
if (valid != 0) {
|
||
if (!quiet)
|
||
printf("%s: successfully mounted (%d file system%s)\n\n",
|
||
mount->name, count, count == 1 ? "" : "s");
|
||
|
||
for (i = 0; i < dev->filesys; i++)
|
||
if ((valid & 1 << i) != 0) {
|
||
printf("%s%o:\n", mount->name, i);
|
||
printf(" Total Blocks: %4d, Free Blocks: %4d\n"
|
||
" Files: %4d\n"
|
||
" Extra words/directory entry: %d\n",
|
||
dev->blocks[i], freeblks[i], files[i], extra[i]);
|
||
}
|
||
}
|
||
|
||
data->valid = valid;
|
||
return valid != 0 ? 1 : 0;
|
||
}
|
||
dev++;
|
||
}
|
||
fprintf(stderr, "mount: unknown disk type - \"%s\"\n",
|
||
SWGETVAL('t'));
|
||
return -1;
|
||
} else {
|
||
fprintf(stderr, "mount: unable to determine disk type,"
|
||
" use \"-t type\"\n");
|
||
return -1;
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/*++
|
||
* o s 8 U m o u n t
|
||
*
|
||
* Unmount the OS/8 file system(s), releasing any storage allocated.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* mount - pointer to the mounted file system descriptor
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* None
|
||
*
|
||
--*/
|
||
static void os8Umount(
|
||
struct mountedFS *UNUSED(mount)
|
||
)
|
||
{
|
||
}
|
||
|
||
/*++
|
||
* o s 8 S i z e
|
||
*
|
||
* Return the size of an OS/8 container file.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* None
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* Size of the container file in block of default file system size
|
||
*
|
||
--*/
|
||
static size_t os8Size(void)
|
||
{
|
||
if (SWISSET('t')) {
|
||
struct OS8device *dev = OS8Devices;
|
||
|
||
while (dev->name != NULL) {
|
||
if (strcmp(SWGETVAL('t'), dev->name) == 0)
|
||
return dev->diskSize;
|
||
|
||
dev++;
|
||
}
|
||
}
|
||
return OS8Devices[0].diskSize;
|
||
}
|
||
|
||
/*++
|
||
* o s 8 N e w f s
|
||
*
|
||
* Create empty OS/8 file system(s).
|
||
*
|
||
* Inputs:
|
||
*
|
||
* mount - pointer to a mounted file system descriptor
|
||
* (nit in the mounted file system list)
|
||
* size - the sized (in blocks) of the filesystem
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* 1 if the file system(s) were successfully created, 0 otherwise
|
||
*
|
||
--*/
|
||
static int os8Newfs(
|
||
struct mountedFS *mount,
|
||
size_t UNUSED(size)
|
||
)
|
||
{
|
||
struct OS8data *data = &mount->os8data;
|
||
uint16_t extra = 1;
|
||
struct OS8device *dev = OS8Devices;
|
||
uint8_t unit;
|
||
uint16_t i;
|
||
|
||
if (SWISSET('t')) {
|
||
while (dev->name != NULL) {
|
||
if (strcmp(SWGETVAL('t'), dev->name) == 0)
|
||
goto found;
|
||
|
||
dev++;
|
||
}
|
||
dev = OS8Devices;
|
||
}
|
||
found:
|
||
mount->skip = dev->skip;
|
||
data->device = dev;
|
||
|
||
if (SWISSET('e')) {
|
||
char *endptr;
|
||
|
||
extra = strtoul(SWGETVAL('e'), &endptr, 10);
|
||
if ((extra > 63) || (*endptr != '\0'))
|
||
return 0;
|
||
}
|
||
|
||
for (unit = 0; unit < dev->filesys; unit++) {
|
||
data->valid |= (1 << unit);
|
||
|
||
for (i = OS8_DSSTART; i <= OS8_DSLAST; i++) {
|
||
uint16_t buf[OS8_BLOCKSIZE];
|
||
uint16_t entries = i == OS8_DSSTART ? 1 : 0;
|
||
|
||
buf[OS8_DH_ENTRIES] = htole16(os8Value(-entries));
|
||
buf[OS8_DH_START] = htole16(os8Value(OS8_DATA));
|
||
buf[OS8_DH_NEXT] = htole16(os8Value(0));
|
||
buf[OS8_DH_FLAGWD] = htole16(os8Value(0));
|
||
buf[OS8_DH_EXTRA] = htole16(os8Value(-extra));
|
||
|
||
buf[OS8_DH_SIZE + OS8_ED_IND] = htole16(os8Value(0));
|
||
buf[OS8_DH_SIZE + OS8_ED_LENGTH] =
|
||
htole16(os8Value(-(dev->blocks[unit] - OS8_DATA)));
|
||
|
||
if (os8WriteBlock(mount, unit, i, buf) == 0)
|
||
return 0;
|
||
}
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
/*++
|
||
* o s 8 S e t
|
||
*
|
||
* Set mount point specific values.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* mount - pointer to a mounted file system descriptor
|
||
* unit - file system unit number (unused)
|
||
* present - file system unit number present (unused)
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* None
|
||
*
|
||
--*/
|
||
static void os8Set(
|
||
struct mountedFS *mount,
|
||
uint8_t UNUSED(unit),
|
||
uint8_t UNUSED(present)
|
||
)
|
||
{
|
||
struct OS8data *data = &mount->os8data;
|
||
int idx = 0;
|
||
unsigned long year;
|
||
char *endptr;
|
||
|
||
while (setCmds[idx] != NULL) {
|
||
if (strcmp(words[1], setCmds[idx]) == 0) {
|
||
switch (idx) {
|
||
case OS8SET_YEAR:
|
||
if (args == 3) {
|
||
if (strcmp(words[2], "none") == 0) {
|
||
data->date = 0;
|
||
return;
|
||
}
|
||
year = strtoul(words[2], &endptr, 10);
|
||
if ((*endptr == '\0') && (year <= 7)) {
|
||
struct tm tm;
|
||
time_t now = time(NULL);
|
||
|
||
localtime_r(&now, &tm);
|
||
|
||
data->date = (tm.tm_mon + 1) << 8;
|
||
data->date |= (tm.tm_mday + 1) << 3;
|
||
data->date |= year;
|
||
return;
|
||
}
|
||
}
|
||
fprintf(stderr, "os8: Invalid syntax for \"set year\"\n");
|
||
return;
|
||
|
||
default:
|
||
fprintf(stderr, "os8: \"%s\" not implemented\n", words[1]);
|
||
return;
|
||
}
|
||
}
|
||
idx++;
|
||
}
|
||
fprintf(stderr, "os8: Unknown set command \"%s\"\n", words[1]);
|
||
}
|
||
|
||
/*++
|
||
* o s 8 I n f o
|
||
*
|
||
* Display information about the internal structure of the OS/8 file
|
||
* system(s).
|
||
*
|
||
* Inputs:
|
||
*
|
||
* mount - pointer to the mounted file system descriptor
|
||
* unit - file system unit number
|
||
* present - file system unit number present
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* None
|
||
*
|
||
--*/
|
||
static void os8Info(
|
||
struct mountedFS *mount,
|
||
uint8_t unit,
|
||
uint8_t present
|
||
)
|
||
{
|
||
struct OS8data *data = &mount->os8data;
|
||
struct OS8device *device = data->device;
|
||
|
||
if (present) {
|
||
if (os8UnitValid(data, unit))
|
||
info(mount, unit);
|
||
} else {
|
||
uint16_t i;
|
||
|
||
/*
|
||
* Display information about all valid units
|
||
*/
|
||
for (i = 0; i < device->filesys; i++)
|
||
if (os8UnitValid(data, i))
|
||
info(mount, i);
|
||
}
|
||
}
|
||
|
||
/*++
|
||
* o s 8 D i r
|
||
*
|
||
* Produce a full or brief directory listing.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* mount - pointer to the mounted file system descriptor
|
||
* unit - file system unit number
|
||
* fname - pointer to filename string
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* None
|
||
*
|
||
--*/
|
||
static void os8Dir(
|
||
struct mountedFS *mount,
|
||
uint8_t unit,
|
||
char *fname
|
||
)
|
||
{
|
||
struct OS8data *data = &mount->os8data;
|
||
struct os8FileSpec spec;
|
||
|
||
if (os8ParseFilespec(fname, &spec, OS8_M_NONAME) == 0) {
|
||
fprintf(stderr, "dir: syntax error in file spec \"%s\"\n", fname);
|
||
return;
|
||
}
|
||
|
||
if (os8UnitValid(data, unit)) {
|
||
unsigned int dirblk = OS8_DSSTART;
|
||
|
||
do {
|
||
uint16_t i, entrysz, entries, extra, off = OS8_DH_SIZE;
|
||
|
||
if (os8ReadBlock(mount, unit, dirblk, NULL) == 0)
|
||
return;
|
||
|
||
entrysz = OS8_DI_SIZE + (-os8SXT(le16toh(data->buf[OS8_DH_EXTRA])));
|
||
entries = os8Neg(le16toh(data->buf[OS8_DH_ENTRIES]));
|
||
extra = os8Neg(le16toh(data->buf[OS8_DH_EXTRA]));
|
||
|
||
for (i = 0; i < entries; i++) {
|
||
char temp1[16], temp2[16];
|
||
|
||
if (os8Word(le16toh(data->buf[off + OS8_DI_FNAME1])) != 0) {
|
||
if ((spec.flags & OS8_WC_NAME) == 0)
|
||
if ((os8Word(le16toh(data->buf[off + OS8_DI_FNAME1])) != spec.name[0]) ||
|
||
(os8Word(le16toh(data->buf[off + OS8_DI_FNAME2])) != spec.name[1]) ||
|
||
(os8Word(le16toh(data->buf[off + OS8_DI_FNAME3])) != spec.name[2])) {
|
||
off += entrysz;
|
||
continue;
|
||
}
|
||
|
||
if ((spec.flags & OS8_WC_EXT) == 0)
|
||
if (os8Word(le16toh(data->buf[off + OS8_DI_EXT])) != spec.ext) {
|
||
off += entrysz;
|
||
continue;
|
||
}
|
||
|
||
if (SWISSET('f')) {
|
||
uint16_t length, dateval = 0;
|
||
|
||
length = os8Neg(le16toh(data->buf[off + extra + OS8_DI_LENGTH]));
|
||
if (extra)
|
||
dateval = os8Word(le16toh(data->buf[off + OS8_DI_DATE]));
|
||
|
||
printf("%s %4hu %s\n",
|
||
os8FileName(mount, off, temp1), length,
|
||
os8Date(dateval, temp2));
|
||
} else printf("%s\n", os8FileName(mount, off, temp1));
|
||
off += entrysz;
|
||
} else off += OS8_ED_SIZE;
|
||
}
|
||
|
||
dirblk = os8Word(le16toh(data->buf[OS8_DH_NEXT]));
|
||
} while (dirblk != 0);
|
||
}
|
||
}
|
||
|
||
/*++
|
||
* o s 8 O p e n F i l e R
|
||
*
|
||
* Open an OS/8 file for reading.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* mount - pointer to the mounted file system descriptor
|
||
* unit - file system unit number
|
||
* fname - pointer to filename string
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* Pointer to open file descriptor, NULL if open fails
|
||
*
|
||
--*/
|
||
static void *os8OpenFileR(
|
||
struct mountedFS *mount,
|
||
uint8_t unit,
|
||
char *fname
|
||
)
|
||
{
|
||
struct os8OpenFile *file;
|
||
struct os8FileSpec spec;
|
||
|
||
if (os8ParseFilespec(fname, &spec, OS8_M_NONE) == 0) {
|
||
fprintf(stderr, "Failed to parse filename \"%s\"\n", fname);
|
||
return NULL;
|
||
}
|
||
|
||
if ((file = malloc(sizeof(struct os8OpenFile))) != NULL) {
|
||
memset(file, 0, sizeof(struct os8OpenFile));
|
||
|
||
if (os8LookupFile(mount, unit, &spec, file) != 0) {
|
||
/*
|
||
* Allocate local buffer space for the file.
|
||
*/
|
||
if ((file->buffer = malloc(mount->blocksz)) == NULL) {
|
||
free(file);
|
||
return NULL;
|
||
}
|
||
file->mode = M_RD;
|
||
} else {
|
||
free(file);
|
||
return NULL;
|
||
}
|
||
}
|
||
return file;
|
||
}
|
||
|
||
/*++
|
||
* o s 8 O p e n F i l e W
|
||
*
|
||
* Open an OS/8 file for writing.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* mount - pointer to the mounted file system descriptor
|
||
* unit - file system unit number
|
||
* fname - pointer to filename string
|
||
* size - estimated file size (in bytes)
|
||
* 0 means allocate as much space as possible
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* Pointer to open file descriptor, NULL is open fails
|
||
*
|
||
--*/
|
||
void *os8OpenFileW(
|
||
struct mountedFS *mount,
|
||
uint8_t unit,
|
||
char *fname,
|
||
off_t size
|
||
)
|
||
{
|
||
struct os8OpenFile *file;
|
||
struct os8FileSpec spec;
|
||
uint16_t blocks = 0;
|
||
|
||
if (size != 0)
|
||
blocks = (size + ((OS8_BLOCKSIZE * 3) / 2) - 1) / (((OS8_BLOCKSIZE) * 3) / 2);
|
||
|
||
if (os8ParseFilespec(fname, &spec, OS8_M_NONE) == 0) {
|
||
fprintf(stderr, "Failed to parse filename \"%s\"\n", fname);
|
||
return NULL;
|
||
}
|
||
|
||
if ((file = malloc(sizeof(struct os8OpenFile))) != NULL) {
|
||
memset(file, 0, sizeof(struct os8OpenFile));
|
||
|
||
if (os8LookupFile(mount, unit, &spec, file) == 0) {
|
||
/*
|
||
* Allocate local buffer space for the file.\
|
||
*/
|
||
if ((file->buffer = malloc(mount->blocksz)) != NULL) {
|
||
memset(file->buffer, 0, mount->blocksz);
|
||
|
||
if (os8CreateFile(mount, unit, &spec, file, blocks) == 0) {
|
||
ERROR("Failed to create file \"%s\"\n", fname);
|
||
free(file->buffer);
|
||
free(file);
|
||
return NULL;
|
||
} else file->mode = M_WR;
|
||
} else {
|
||
ERROR("Buffer allocation failure for \"%s\"\n", fname);
|
||
free(file);
|
||
return NULL;
|
||
}
|
||
} else {
|
||
ERROR("File \"%s\" already exists\n", fname);\
|
||
free(file);
|
||
return NULL;
|
||
}
|
||
} else ERROR("Memory allocation failure\n");
|
||
return file;
|
||
}
|
||
|
||
/*++
|
||
* o s 8 F i l e S i z e
|
||
*
|
||
* Return an estimate of the size of a currently open file. This routine
|
||
* bases the file size on the number of blocks allocated to the file and
|
||
* may over-report the actual sized of the file.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* filep - pointer to open file descriptor
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* Estimate of the file size
|
||
*
|
||
--*/
|
||
static off_t os8FileSize(
|
||
void *filep
|
||
)
|
||
{
|
||
struct os8OpenFile *file = filep;
|
||
|
||
return (file->length * RT11_BLOCKSIZE * 2) / 3;
|
||
}
|
||
|
||
/*++
|
||
* o s 8 D e l e t e F i l e
|
||
*
|
||
* Delete a file from an O/8 file system.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* filep - pointer to open file descriptor
|
||
* fname - pointer to filename string
|
||
*
|
||
* Outputs:
|
||
*
|
||
* The mount specific buffer will be modified.
|
||
*
|
||
* Returns:
|
||
*
|
||
* None
|
||
*
|
||
--*/
|
||
static void os8DeleteFile(
|
||
void *filep,
|
||
char *UNUSED(fname)
|
||
)
|
||
{
|
||
struct os8OpenFile *file = filep;
|
||
struct mountedFS *mount = file->mount;
|
||
struct OS8data *data = &mount->os8data;
|
||
|
||
if (os8ReadBlock(mount, file->unit, file->segment, NULL) == 0)
|
||
return;
|
||
|
||
data->buf[file->offset + OS8_DI_FNAME1] = 0;
|
||
data->buf[file->offset + OS8_ED_LENGTH] =
|
||
data->buf[file->offset + file->extra + OS8_DI_LENGTH];
|
||
data->buf[OS8_DH_ENTRIES] =
|
||
htole16(os8Value(-(os8Neg(le16toh(data->buf[OS8_DH_ENTRIES])) - 1)));
|
||
|
||
os8SlideUp(mount, file->offset + OS8_ED_SIZE,
|
||
file->offset + file->entrysz, file->entrysz, file->remain);
|
||
|
||
/*
|
||
* Merge any adjacent empty directory entries.
|
||
*/
|
||
if (os8MergeEmptyRegions(mount) != 0)
|
||
os8MergeEmptyRegions(mount);
|
||
|
||
if (os8WriteBlock(mount, file->unit, file->segment, NULL) == 0)
|
||
return;
|
||
|
||
os8CloseFile(file);
|
||
}
|
||
|
||
/*++
|
||
* o s 8 C l o s e F i l e
|
||
*
|
||
* Close an open OS/8 file.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* filep - pointer to open file descriptor
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* None
|
||
*
|
||
--*/
|
||
static void os8CloseFile(
|
||
void *filep
|
||
)
|
||
{
|
||
struct os8OpenFile *file = filep;
|
||
|
||
if (file->mode == M_WR) {
|
||
if (file->current != 0)
|
||
/*
|
||
* Flush the current buffer.
|
||
*/
|
||
os8WriteBlock(file->mount, file->unit, file->current++, file->buffer);
|
||
|
||
os8UpdateFile(file->mount, file->unit, file);
|
||
}
|
||
|
||
if (file->buffer != NULL)
|
||
free(file->buffer);
|
||
free(file);
|
||
}
|
||
|
||
/*++
|
||
* o s 8 R e a d F i l e
|
||
*
|
||
* Read data from an OS/8 file to a supplied buffer
|
||
*
|
||
* Inputs:
|
||
*
|
||
* filep - pointer to open file descriptor
|
||
* buf - pointer to buffer
|
||
* buflen - length of the supplied buffer
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* # of bytes of data read, 0 means EOF or error
|
||
*
|
||
--*/
|
||
static size_t os8ReadFile(
|
||
void *filep,
|
||
void *buf,
|
||
size_t buflen
|
||
)
|
||
{
|
||
struct os8OpenFile *file = filep;
|
||
|
||
return os8ReadBytes(file, buf, buflen);
|
||
}
|
||
|
||
/*++
|
||
* o s 8 W r i t e F i l e
|
||
*
|
||
* Write data to an OS/8 file from a supplied buffer.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* filep - pointer to open file descriptor
|
||
* buf - pointer to buffer
|
||
* buflen - length of the supplied buffer
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* # of bytes of data written, 0 means EOF or error
|
||
*
|
||
--*/
|
||
static size_t os8WriteFile(
|
||
void *filep,
|
||
void *buf,
|
||
size_t buflen
|
||
)
|
||
{
|
||
struct os8OpenFile *file = filep;
|
||
|
||
return os8WriteBytes(file, buf, buflen);
|
||
}
|
||
|
||
/*++
|
||
* o s 8 F S
|
||
*
|
||
* Descriptor for accessing OS/8 file systems.
|
||
*
|
||
--*/
|
||
struct FSdef os8FS = {
|
||
NULL,
|
||
"os8",
|
||
"os8 PDP-8 OS/8 file system (RX01, RX02 or RK05 disks)\n",
|
||
FS_UNITVALID,
|
||
OS8_BLOCKSIZE * sizeof(uint16_t),
|
||
os8Mount,
|
||
os8Umount,
|
||
os8Size,
|
||
os8Newfs,
|
||
os8Set,
|
||
os8Info,
|
||
os8Dir,
|
||
os8OpenFileR,
|
||
os8OpenFileW,
|
||
os8FileSize,
|
||
os8CloseFile,
|
||
os8ReadFile,
|
||
os8WriteFile,
|
||
os8DeleteFile,
|
||
NULL, /* No tape support functions */
|
||
NULL,
|
||
NULL,
|
||
NULL
|
||
};
|