John Forecast 3927147862 FSIO: Bug fixes and clean up warnings generated by GCC 8.3
Fix some OS/8 directory handling routines which did not correctly handle
directories with optional extended words. Add missing

RL01 initialization support for RT11.
2020-01-29 17:54:48 -08:00

2491 lines
61 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 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 handling DOS/BATCH-11 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 "fsio.h"
extern uint16_t bits[], lowbits[], highbits[];
extern uint8_t zeroes[];
/*
* Table of "set" commands
*/
static char *setCmds[] = {
"uic",
"ufd",
NULL
};
#define DOS11SET_UIC 0
#define DOS11SET_UFD 1
static uint16_t bitmapAllocBlock(struct mountedFS *);
static uint16_t bitmapAllocContiguous(struct mountedFS *, uint16_t);
static int bitmapFlush(struct mountedFS *);
static int bitmapLoad(struct mountedFS *, uint16_t);
static int bitmapReleaseBlock(struct mountedFS *, uint16_t);
static int bitmapScan(struct mountedFS *, uint16_t, uint16_t, uint16_t *);
static int bitmapClrBit(struct mountedFS *, uint16_t);
static int bitmapSetBit(struct mountedFS *, uint16_t);
static int bitmapGetWord(struct mountedFS *, uint16_t, uint16_t *);
int dos11CreateFile(struct mountedFS *, struct dos11FileSpec *, struct dos11OpenFile *, unsigned long);
int dos11LookupFile(struct mountedFS *, struct dos11FileSpec *, struct dos11OpenFile *);
void dos11UpdateFile(struct mountedFS *, struct dos11OpenFile *);
uint16_t dos11XtndDirectory(struct mountedFS *);
static void dos11CloseFile(void *);
int dos11ReadBlock(struct mountedFS *, unsigned int, void *);
int dos11WriteBlock(struct mountedFS *, unsigned int, void *);
extern int args;
extern char **words;
extern int quiet;
/*++
* b i t m a p A l l o c B l o c k
*
* Find an unsed block and allocate it. Ths scan will start at the first
* known location of a free block (or 0 if this is the first scan) and will
* will remember this location to reduce the amount of scanning required
* for subsequent allocations.
*
* Inputs:
*
* mount - pointer to a mounted file system descriptor
*
* Outputs:
*
* If the allocation is successful, the bitmap will be updated in
* memory and the bitmap buffer marked as dirty.
*
* Returns:
*
* Allocated block # if successful, 0 otherwise
*
--*/
static uint16_t bitmapAllocBlock(
struct mountedFS *mount
)
{
struct DOS11data *data = &mount->dos11data;
uint16_t map = data->bmscan / MAP_BLOCKS;
uint16_t offset = data->bmscan % MAP_BLOCKS;
/*
* Iterate across all available bitmaps.
*/
while (map < data->bitmaps) {
uint16_t wrd = offset / 16;
uint16_t bit = offset % 16;
if (bitmapLoad(mount, map) == 0)
return 0;
/*
* Iterate across a single bitmap.
*/
while (wrd < MAP_LEN) {
uint16_t val = le16toh(data->bmbuf[MAP_BMSTART + wrd]);
if (val != 0177777) {
data->bmscan = (map * MAP_BLOCKS) + (wrd * 16);
while (bit < 16) {
if ((val & bits[bit]) == 0) {
val |= bits[bit];
data->bmbuf[MAP_BMSTART + wrd] = htole16(val);
data->bmdirty = 1;
return (map * MAP_BLOCKS) + (wrd * 16) + bit;
}
bit++;
}
}
wrd++;
bit = 0;
}
map++;
offset = 0;
}
return 0;
}
/*++
* b i t m a p A l l o c C o n t i g u o u s
*
* Find a sequence of contiguous bits in the bitmaps and allocate them.
*
* Inputs:
*
* mount - pointer to a mounted file system descriptor
* count - # of contiguous blocks to allocate
*
* Outputs:
*
* If the allocation is successful, the bitmap will be updated in
* memory and possibly on disk. The last referenced bitmap will be
* marked as dirty.
*
* Returns:
*
* First allocated block # if successful, 0 otherwise
*
--*/
static uint16_t bitmapAllocContiguous(
struct mountedFS *mount,
uint16_t count
)
{
struct DOS11data *data = &mount->dos11data;
uint16_t map, offset, scan = data->bmscan;
uint16_t i;
uint8_t first = 1;
/*
* Check that the request is reasonable
*/
if (count >= data->blocks)
return 0;
restart:
/*
* Make sure that there may be sufficient space to satisfy the request.
*/
if (((data->blocks - 1) - scan) < count)
return 0;
map = scan / MAP_BLOCKS;
offset = scan % MAP_BLOCKS;
/*
* Iterate across all available bitmaps.
*/
while (map < data->bitmaps) {
uint16_t wrd = offset / 16;
if (bitmapLoad(mount, map) == 0)
return 0;
/*
* Iterate across a single bitmap.
*/
while (wrd < MAP_LEN) {
uint16_t val = le16toh(data->bmbuf[MAP_BMSTART + wrd]);
if (val != 0177777) {
if (first) {
/*
* Update cache of where to start scanning
*/
data->bmscan = (map * MAP_BLOCKS) + (wrd * 16);
first = 0;
}
if (count <= 16) {
/*
* The required blocks could fit into a single. See if that is the
* case.
*/
uint16_t mask = lowbits[count - 1];
for (i = 0; i < (17 - count); i++)
if ((val & (mask << i)) == 0) {
/*
* We have a fit. Allocate the space.
*/
val |= (mask << i);
data->bmbuf[MAP_BMSTART + wrd] = htole16(val);
data->bmdirty = 1;
return (map * MAP_BLOCKS) + (wrd * 16) + i;
}
}
/*
* Let's see if we can make use of the last blocks described by
* this word.
*/
if ((val & 0100000) == 0) {
uint16_t base = (map * MAP_BLOCKS) + (wrd * 16);
/*
* See how many bits we can use
*/
for (i = 1; i <= 16; i++)
if ((val & highbits[i]) != 0)
break;
if (bitmapScan(mount, count - i, base + 16, &scan) == 0)
goto restart;
base = base + 16 - i;
/*
* Allocate the blocks
*/
for (i = 0; i < count; i++)
if (bitmapSetBit(mount, base + i) == 0)
return 0;
return base;
}
}
wrd++;
}
map++;
offset = 0;
}
return 0;
}
/*++
* b i t m a p F l u s h
*
* If the currently loaded bitmap is dirty, write it back to disk.
*
* Inputs:
*
* mount - pointer to a mounted file system descriptor
*
* Outputs:
*
* None
*
* Returns:
*
* 1 if successful, 0 otherwise
*
--*/
static int bitmapFlush(
struct mountedFS *mount
)
{
struct DOS11data *data = &mount->dos11data;
if (data->bmdirty != 0) {
if (dos11WriteBlock(mount, data->bmblk[data->bmindex], data->bmbuf) == 0)
return 0;
data->bmdirty = 0;
}
return 1;
}
/*++
* b i t m a p L o a d
*
* Load the specified bitmap into the bitmap buffer. If the buffer is dirty,
* write the current contents of the buffer back to disk before loading the
* new bitmap.
*
* Inputs:
*
* mount - pointer to a mounted file system descriptor
* map - logical bitmap # in the range 0 - N
*
* Outputs:
*
* None
*
* Returns:
*
* 1 if successful, 0 otherwise
*
--*/
static int bitmapLoad(
struct mountedFS *mount,
uint16_t map
)
{
struct DOS11data *data = &mount->dos11data;
if (map >= data->bitmaps) {
ERROR("Invalid bitmap # (%d), max is %d\n", map, data->bitmaps);
return 0;
}
/*
* If the map is already loaded, nothing to do.
*/
if (map != data->bmindex) {
if (data->bmdirty != 0) {
if (dos11WriteBlock(mount, data->bmblk[data->bmindex], data->bmbuf) == 0)
return 0;
data->bmdirty = 0;
}
data->bmindex = map;
if (dos11ReadBlock(mount, data->bmblk[map], data->bmbuf) == 0)
return 0;
}
return 1;
}
/*++
* b i t m a p R e l e a s e B l o c k
*
* Release a specified block in the bitmap.
*
* Inputs:
*
* mount - pointer to a mounted file system descriptor
* block - block # to be released
*
* Outputs:
*
* The bitmap will be updated by releasing the block and the bitmap
* will be marked as dirty.
*
* Returns:
*
* 1 if successful, 0 otherwise
*
--*/
static int bitmapReleaseBlock(
struct mountedFS *mount,
uint16_t block
)
{
return bitmapClrBit(mount, block);
}
/*++
* b i t m a p S c a n
*
* Scan forward from a word boundary to check whether a sequence of blocks
* is free.
*
* Inputs:
*
* mount - pointer to a mounted file system descriptor
* count - # of contiguous blocks to allocate
* start - starting block # (always aligned on a 16 block
* boundary)
* restart - return restart point here on failure
*
* Outputs:
*
* None
*
* Returns:
*
* 1 on success, 0 otherwise
*
--*/
static int bitmapScan(
struct mountedFS *mount,
uint16_t count,
uint16_t start,
uint16_t *restart
)
{
uint16_t value;
while (count >= 16) {
if ((bitmapGetWord(mount, start, &value) == 0) ||
(value != 0)) {
*restart = start;
return 0;
}
start += 16;
count -= 16;
}
if (count != 0)
if ((bitmapGetWord(mount, start, &value) == 0) ||
((value & lowbits[count - 1]) != 0)) {
*restart = start;
return 0;
}
return 1;
}
/*++
* b i t m a p C l r B i t
*
* Modify the bitmap by clearing the bit associated with a specific block.
*
* Inputs:
*
* mount - pointer to a mounted file system descriptor
* block - block # whose associated bit is to be cleared
*
* Outputs:
*
* The bitmap will be updated by clearing the bit and the bitmap will
* be marked as dirty.
*
* Returns:
*
* 1 if successful, 0 otherwise
*
--*/
static int bitmapClrBit(
struct mountedFS *mount,
uint16_t block
)
{
struct DOS11data *data = &mount->dos11data;
uint16_t map = block / MAP_BLOCKS;
uint16_t offset = block % MAP_BLOCKS;
uint16_t wrd = offset / 16;
uint16_t bit = offset % 16;
if (bitmapLoad(mount, map) == 0)
return 0;
data->bmbuf[MAP_BMSTART + wrd] =
htole16(le16toh(data->bmbuf[MAP_BMSTART + wrd]) & ~bits[bit]);
data->bmdirty = 1;
return 1;
}
/*++
* b i t m a p S e t B i t
*
* Modify the bitmap by setting the bit associated with a specific block.
*
* Inputs:
*
* mount - pointer to a mounted file system descriptor
* block - block # whose associated bit is to be set
*
* Outputs:
*
* The bitmap will be updated by setting the bit and the bitmap will
* be marked as dirty.
*
* Returns:
*
* 1 if successful, 0 otherwise
*
--*/
static int bitmapSetBit(
struct mountedFS *mount,
uint16_t block
)
{
struct DOS11data *data = &mount->dos11data;
uint16_t map = block / MAP_BLOCKS;
uint16_t offset = block % MAP_BLOCKS;
uint16_t wrd = offset / 16;
uint16_t bit = offset % 16;
if (bitmapLoad(mount, map) == 0)
return 0;
data->bmbuf[MAP_BMSTART + wrd] =
htole16(le16toh(data->bmbuf[MAP_BMSTART + wrd]) | bits[bit]);
data->bmdirty = 1;
return 1;
}
/*++
* b i t m a p G e t W o r d
*
* Get the bitmap word which contains the bit associated with a specified
* block.
*
* Inputs:
*
* mount - pointer to a mounted file system descriptor
* block - block # associated with the bitmap word
* value - bitmap word is returned here
*
* Outputs:
*
* None
*
* Returns:
*
* 1 if successful, 0 otherwise
*
--*/
static int bitmapGetWord(
struct mountedFS *mount,
uint16_t block,
uint16_t *value
)
{
struct DOS11data *data = &mount->dos11data;
uint16_t map = block / MAP_BLOCKS;
uint16_t offset = block % MAP_BLOCKS;
uint16_t wrd = offset / 16;
if (bitmapLoad(mount, map) == 0)
return 0;
*value = le16toh(data->bmbuf[MAP_BMSTART + wrd]);
return 1;
}
/*++
* d o s 1 1 C r e a t e U F D
*
* Create a UFD.
*
* Inputs:
*
* mount - pointer to a mounted file system descriptor
* group - group number
* user - user number
*
* Outputs:
*
* The mount specific buffer will be modified.
*
* Returns:
*
* 1 if UFD alread exists or successfully created, 0 otherwise
*
--*/
static int dos11CreateUFD(
struct mountedFS *mount,
uint8_t group,
uint8_t user
)
{
struct DOS11data *data = &mount->dos11data;
uint16_t mfdblk, mfdsav, uic = (group << 8) | user;
unsigned int i;
if (dos11ReadBlock(mount, MFD1_BLOCK, NULL) == 0)
return 0;
/*
* Search the MFD to see if the UFD already exists
*/
mfdblk = mfdsav = le16toh(data->buf[MFD1_MFD2BLOCK]);
do {
if (dos11ReadBlock(mount, mfdblk, NULL) == 0)
return 0;
for (i = MFD2_HEADER; i < EODIR(mount, MFD2_SIZE); i += MFD2_SIZE) {
if (le16toh(data->buf[i + MFD2_UFDUIC]) != 0)
if (le16toh(data->buf[i + MFD2_UFDUIC] == uic))
return 1;
}
mfdblk = le16toh(data->buf[MFD2_LINK]);
} while (mfdblk != 0);
/*
* The UFD does not currently exist, create a new one without a data
* block assigned. The first file creation operation will allocate a
* data block.
*/
mfdblk = mfdsav;
for (;;) {
if (dos11ReadBlock(mount, mfdblk, NULL) == 0)
return 0;
for (i = MFD2_HEADER; i < EODIR(mount, MFD2_SIZE); i += MFD2_SIZE) {
if (le16toh(data->buf[i + MFD2_UFDUIC]) == 0) {
data->buf[i + MFD2_UFDUIC] = htole16(uic);
data->buf[i + MFD2_UFDSTART] = 0;
data->buf[i + MFD2_UFDSIZE] = htole16(UFD_LEN);
data->buf[i + MFD2_UFDZERO] = 0;
dos11WriteBlock(mount, mfdblk, NULL);
return 1;
}
}
if (le16toh(data->buf[MFD2_LINK]) == 0) {
/*
* We have reached the end of the MFD, append a new block if available.
*/
uint16_t buf[512], newblk;
if ((newblk = bitmapAllocBlock(mount)) == 0) {
ERROR("No space available to extend MFD on \"%s:\"\n", mount->name);
return 0;
}
/*
* The newly created block consists only of unused entries.
*/
memset(buf, 0, sizeof(buf));
if (dos11WriteBlock(mount, newblk, buf) == 0)
return 0;
data->buf[MFD2_LINK] = htole16(newblk);
if (dos11WriteBlock(mount, mfdblk, NULL) == 0)
return 0;
}
mfdblk = le16toh(data->buf[MFD2_LINK]);
}
return 0;
}
/*++
* d o s 1 1 D i s p l a y D i r
*
* Print a directory entry on stdout.
*
* Inputs:
*
* dir - pointer to the UFD entry for the file
* full - if non-zero, print a full directory entry
*
* Outputs:
*
* None
*
* Returns:
*
* None
*
--*/
void dos11DisplayDir(
uint16_t *dir,
int full
)
{
char temp[64], creation[16];
r50Asc(le16toh(dir[UFD_FILENAME]), &temp[0]);
r50Asc(le16toh(dir[UFD_FILENAME + 1]), &temp[3]);
if (le16toh(dir[UFD_EXTENSION]) != 0) {
temp[6] = '.';
r50Asc(le16toh(dir[UFD_EXTENSION]), &temp[7]);
} else {
temp[6] = ' ';
temp[7] = ' ';
temp[8] = ' ';
temp[9] = ' ';
}
if (full) {
sprintf(&temp[10], " %5u%c %s <%03o>",
le16toh(dir[UFD_FILELENGTH]),
(le16toh(dir[UFD_CREATION]) & UFD_TYPE) ? 'C' : ' ',
dos11Date(le16toh(dir[UFD_CREATION]) & UFD_DATE, creation),
le16toh(dir[UFD_LUP]) & UFD_PROT);
} else temp[10] ='\0';
puts(temp);
}
/*++
* d o s 1 1 C r e a t e F i l e
*
* Create a new file within the DOS-11 file system. The new file will be
* marked as "locked" since the resources allocated to the file may be
* in flux. The caller must check that the file does not already exist
* before calling this routine.
*
* Inputs:
*
* mount - pointer to a mounted file system descriptor
* spec - pointer to the file specification block
* file - pointer to open file descriptor to receive results
* contig - # of contiguous blocks
*
* Outputs:
*
* The mount point specific buffer area will be modified.
*
* Returns:
*
* 1 if file successfully created, 0 otherwise
*
--*/
int dos11CreateFile(
struct mountedFS *mount,
struct dos11FileSpec *spec,
struct dos11OpenFile *file,
unsigned long contig
)
{
struct DOS11data *data = &mount->dos11data;
uint16_t mfdblk, ufdblk, ufdblk2;
unsigned int i, j;
struct tm tm;
time_t now = time(NULL);
uint16_t today;
/*
* Compute a suitable year for file creation date. This year will have
* the same calendar as the current year but will be in the 20th century
* so that DOS/BATCH-11 will be able to enter the date successfully (It is
* not Y2K compliant!).
*/
localtime_r(&now, &tm);
tm.tm_year -= 28;
today = ((tm.tm_year - 70) * 1000) + tm.tm_yday + 1;
if (dos11ReadBlock(mount, MFD1_BLOCK, NULL) == 0)
return 0;
/*
* Search the MFD for the specified UIC.
*/
mfdblk = le16toh(data->buf[MFD1_MFD2BLOCK]);
do {
if (dos11ReadBlock(mount, mfdblk, NULL) == 0)
return 0;
for (i = MFD2_HEADER; i < EODIR(mount, MFD2_SIZE); i += MFD2_SIZE) {
uint16_t uic = le16toh(data->buf[i + MFD2_UFDUIC]);
uint16_t entrysz = le16toh(data->buf[i + MFD2_UFDSIZE]);
if (uic != 0) {
if (uic == ((spec->group << 8) | spec->user)) {
ufdblk = le16toh(data->buf[i + MFD2_UFDSTART]);
if (ufdblk == 0) {
if ((ufdblk = dos11XtndDirectory(mount)) == 0) {
ERROR("%s: Unable to create initial directory block\n",
mount->name);
return 0;
}
data->buf[i + MFD2_UFDSTART] = htole16(ufdblk);
if (dos11WriteBlock(mount, mfdblk, NULL) == 0)
return 0;
}
for (;;) {
if (dos11ReadBlock(mount, ufdblk, NULL) == 0)
return 0;
for (j = 1; j < EODIR(mount, entrysz); j += entrysz) {
if ((le16toh(data->buf[j + UFD_FILENAME]) == 0) &&
(le16toh(data->buf[j + UFD_FILENAME + 1]) == 0) &&
(le16toh(data->buf[j + UFD_EXTENSION]) == 0)) {
data->buf[j + UFD_FILENAME] =
file->name[0] = htole16(spec->name[0]);
data->buf[j + UFD_FILENAME + 1] =
file->name[1] = htole16(spec->name[1]);
data->buf[j + UFD_EXTENSION] = file->ext = htole16(spec->ext);
data->buf[j + UFD_CREATION] =
file->creation =
htole16(today | (SWISSET('c') ? UFD_TYPECONTIGUOUS : 0));
data->buf[j + UFD_NEXTFREEBYTE] = file->nfb = 0;
data->buf[j + UFD_FILESTART] = file->start = 0;
data->buf[j + UFD_FILELENGTH] = file->length = 0;
data->buf[j + UFD_LASTBLOCKWRITTEN] = file->last = 0;
data->buf[j + UFD_LUP] = file->lup = htole16(UFD_LOCK + 0233);
file->ufdblk = ufdblk;
file->ufdoffset = j;
file-> mount = mount;
if (SWISSET('c')) {
/*
* Allocate contiguous disk space for the file.
*/
uint16_t base = bitmapAllocContiguous(mount, contig);
if (base == 0) {
ERROR("%s: Unable to allocate contiguous space\n",
mount->name);
return 0;
}
data->buf[j + UFD_FILESTART] =
file->start = htole16(base);
data->buf[j + UFD_FILELENGTH] =
file->length = htole16(contig);
}
/*
* Write the UFD block back to disk. The file will be marked
* as "locked" indicating that it is being written and the
* directory entry may not be accurate.
*/
if (dos11WriteBlock(mount, ufdblk, NULL) == 0)
return 0;
return 1;
}
}
if ((ufdblk = le16toh(data->buf[UFD_LINK])) == 0) {
if ((ufdblk2 = dos11XtndDirectory(mount)) == 0) {
ERROR("%s: Unable to extend UFD\n", mount->name);
return 0;
}
data->buf[UFD_LINK] = htole16(ufdblk2);
if (dos11WriteBlock(mount, ufdblk, NULL) == 0)
return 0;
ufdblk = ufdblk2;
}
}
}
}
}
mfdblk = le16toh(data->buf[MFD2_LINK]);
} while (mfdblk != 0);
return 0;
}
/*++
* d o s 1 1 L o o k u p F i l e
*
* Lookup a specific file within the DOS-11 file system. This routine
* fills in an open file descriptor with information about the file and
* the user directory it resides in.
*
* Inputs:
*
* mount - pointer to a mounted file system descriptor
* spec - pointer to the file specification block
* file - pointer to open file descriptor to receive results
*
* Outputs:
*
* The mount point specific buffer area will be modified.
*
* Returns:
*
* 1 if file found, 0 otherwise
*
--*/
int dos11LookupFile(
struct mountedFS *mount,
struct dos11FileSpec *spec,
struct dos11OpenFile *file
)
{
struct DOS11data *data = &mount->dos11data;
unsigned int mfdblk, ufdblk, i, j;
if (dos11ReadBlock(mount, MFD1_BLOCK, NULL) == 0)
return 0;
/*
* Search the MFD for the specified UIC.
*/
mfdblk = le16toh(data->buf[MFD1_MFD2BLOCK]);
do {
if (dos11ReadBlock(mount, mfdblk, NULL) == 0)
return 0;
for (i = MFD2_HEADER; i < EODIR(mount, MFD2_SIZE); i += MFD2_SIZE) {
uint16_t uic = le16toh(data->buf[i + MFD2_UFDUIC]);
uint16_t entrysz = le16toh(data->buf[i + MFD2_UFDSIZE]);
if (uic != 0) {
if (uic == ((spec->group << 8) | spec->user)) {
ufdblk = le16toh(data->buf[i + MFD2_UFDSTART]);
if (ufdblk != 0) {
do {
if (dos11ReadBlock(mount, ufdblk, NULL) == 0)
return 0;
for (j = UFD_HEADER; j < EODIR(mount, entrysz); j += entrysz) {
if ((le16toh(data->buf[j + UFD_FILENAME]) == 0) &&
(le16toh(data->buf[j + UFD_FILENAME + 1]) == 0) &&
(le16toh(data->buf[j + UFD_EXTENSION]) == 0))
continue;
if ((le16toh(data->buf[j + UFD_FILENAME]) != spec->name[0]) ||
(le16toh(data->buf[j + UFD_FILENAME + 1]) != spec->name[1]) ||
(le16toh(data->buf[j + UFD_EXTENSION]) != spec->ext))
continue;
/*
* Save the directory entry and it's location in the open
* file descriptor.
*/
file->name[0] = data->buf[j + UFD_FILENAME];
file->name[1] = data->buf[j + UFD_FILENAME + 1];
file->ext = data->buf[j + UFD_EXTENSION];
file->creation = data->buf[j + UFD_CREATION];
file->nfb = data->buf[j + UFD_NEXTFREEBYTE];
file->start = data->buf[j + UFD_FILESTART];
file->length = data->buf[j + UFD_FILELENGTH];
file->last = data->buf[j + UFD_LASTBLOCKWRITTEN];
file->lup = data->buf[j + UFD_LUP];
file->ufdblk = ufdblk;
file->ufdoffset = j;
file->mount = mount;
return 1;
}
ufdblk = le16toh(data->buf[UFD_LINK]);
} while (ufdblk != 0);
return 0;
}
}
}
}
mfdblk = le16toh(data->buf[MFD2_LINK]);
} while (mfdblk != 0);
return 0;
}
/*++
* d o s 1 1 U p d a t e F i l e
*
* Update a DOS-11 file by writing back the UFD entry associated with the
* file.
*
* Inputs:
*
* mount - pointer to a mounted file system descriptor
* file - pointer to open file descriptor
*
* Outputs:
*
* The mount point specific buffer area will be modified.
*
* Returns:
*
* None
*
--*/
void dos11UpdateFile(
struct mountedFS *mount,
struct dos11OpenFile *file
)
{
struct DOS11data *data = &mount->dos11data;
if (dos11ReadBlock(mount, file->ufdblk, NULL) == 0)
return;
/*
* Update the directory entry.
*/
data->buf[file->ufdoffset + UFD_FILENAME] = file->name[0];
data->buf[file->ufdoffset + UFD_FILENAME + 1] = file->name[1];
data->buf[file->ufdoffset + UFD_EXTENSION] = file->ext;
data->buf[file->ufdoffset + UFD_CREATION] = file->creation;
data->buf[file->ufdoffset + UFD_NEXTFREEBYTE] = file->nfb;
data->buf[file->ufdoffset + UFD_FILESTART] = file->start;
data->buf[file->ufdoffset + UFD_FILELENGTH] = file->length;
data->buf[file->ufdoffset + UFD_LASTBLOCKWRITTEN] = file->last;
data->buf[file->ufdoffset + UFD_LUP] = file->lup & ~htole16(UFD_LOCK);;
#ifdef DEBUG
if ((mount->flags & FS_DEBUG) != 0) {
if (file->ufdoffset != 1)
dos11DisplayDir(&data->buf[file->ufdoffset - UFD_LEN], 1);
dos11DisplayDir(&data->buf[file->ufdoffset], 1);
}
#endif
dos11WriteBlock(mount, file->ufdblk, NULL);
}
/*++
* d o s 1 1 X t n d D i r e c t o r y
*
* Extend a directory by one block. The new directory block will be zeroed
* which indicates that all directory entries are empty and this is the last
* block in the directory chain.
*
* Inputs:
*
* mount - pointer to a mounted file system descriptor
*
* Outputs:
*
* None
*
* Returns:
*
* Block # allocated and written for the directory, 0 if failure
*
--*/
uint16_t dos11XtndDirectory(
struct mountedFS *mount
)
{
uint16_t buf[512], ufdblk;
memset(buf, 0, sizeof(buf));
if ((ufdblk = bitmapAllocBlock(mount)) != 0) {
if (dos11WriteBlock(mount, ufdblk, buf) == 0)
return 0;
if (bitmapFlush(mount) == 0)
return 0;
}
return ufdblk;
}
/*++
* d o s 1 1 P a r s e F i l e s p e c
*
* Parse a character string representing a DOS-11 file specification.
*
* Inputs:
*
* ptr - pointer to the file specification string
* spec - pointer to the file specification block
* user - default user number
* group - default group number
* wildcard - wildcard processing options:
* 0 (DOS11_M_NONE) - wildcards not allowed
* 1 (DOS11_M_ALLOW) - wildcards allowed
* 2 (DOS11_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
#define P_GROUP 3
#define P_USER 4
int dos11ParseFilespec(
char *ptr,
struct dos11FileSpec *spec,
unsigned char user,
unsigned char group,
int wildcard
)
{
char term[] = { '\0', '.', '[', ',', ']' };
char flags[] =
{ 0, DOS11_WC_NAME, DOS11_WC_EXT, DOS11_WC_GROUP, DOS11_WC_USER };
int state = P_NAME;
unsigned int uic, i;
char filename[6], ext[3];
memset(spec, 0, sizeof(struct dos11FileSpec));
spec->user = user;
spec->group = group;
memset(&filename, ' ', sizeof(filename));
memset(&ext, ' ', sizeof(ext));
if (wildcard == DOS11_M_NONAME)
if ((*ptr == '\0') || (*ptr == '['))
spec->flags = DOS11_WC_NAME | DOS11_WC_EXT;
while (state != P_DONE) {
if (wildcard) {
if (*ptr == '*') {
spec->flags |= flags[state];
ptr++;
if (*ptr == '\0')
state = P_DONE;
else if (*ptr == term[state]) {
if (state == P_USER)
state = P_DONE;
else state++;
} else return 0;
ptr++;
continue;
}
}
i = 0;
switch (state) {
case P_NAME:
while ((*ptr != '\0') &&
(*ptr != '.') &&
(strchr(rad50, toupper(*ptr)) != NULL) &&
(i < sizeof(filename)))
filename[i++] = toupper(*ptr++);
switch (*ptr++) {
case '\0':
state = P_DONE;
break;
case '.':
state = P_EXT;
break;
case '[':
state = P_GROUP;
break;
default:
return 0;
}
break;
case P_EXT:
while ((*ptr != '\0') &&
(strchr(rad50, toupper(*ptr)) != NULL) &&
(i < sizeof(ext)))
ext[i++] = toupper(*ptr++);
switch (*ptr++) {
case '\0':
state = P_DONE;
break;
case '[':
state = P_GROUP;
break;
default:
return 0;
}
break;
case P_GROUP:
uic = 0;
while ((strchr("01234567", *ptr) != NULL) && (i < 3)) {
uic = (uic << 3) | (*ptr++ - '0');
i++;
}
if ((uic == 0) || (uic > 0377))
return 0;
spec->group = uic & 0377;
if (*ptr++ != ',')
return 0;
state = P_USER;
break;
case P_USER:
uic = 0;
while ((strchr("01234567", *ptr) != NULL) && (i < 3)) {
uic = (uic << 3) | (*ptr++ - '0');
i++;
}
if ((uic == 0) || (uic > 0377))
return 0;
spec->user = uic & 0377;
if (*ptr++ != ']')
return 0;
state = P_DONE;
break;
}
}
spec->name[0] = ascR50(&filename[0]);
spec->name[1] = ascR50(&filename[3]);
spec->ext = ascR50(&ext[0]);
return 1;
}
/*++
* d o s 1 1 R e a d B l o c k
*
* Read a block from a DOS-11 file system.
*
* Inputs:
*
* mount - pointer to a mounted file system descriptor
* 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 dos11ReadBlock(
struct mountedFS *mount,
unsigned int block,
void *buf
)
{
struct DOS11data *data = &mount->dos11data;
void *buffer = buf == NULL ? data->buf : buf;
int status;
if (block >= data->blocks) {
ERROR("Attempt to read block (%u) outside file system \"%s\"\n",
block, mount->name);
return 0;
}
#ifdef DEBUG
if ((mount->flags & FS_DEBUG) != 0)
fprintf(DEBUGout, ">> %s: (dos11) Reading logical block %o\n",
mount->name, block);
#endif
status = FSioReadBlock(mount, block, buffer);
if (status == 0)
ERROR("I/O error on \"%s\"\n", mount->name);
return status;
}
/*++
* d o s 1 1 W r i t e B l o c k
*
* Write a block to a DOS-11 file system.
*
* Inputs:
*
* mount - pointer to a mounted file system descriptor
* block - logical block # in the range 0 - N
* buf - buffer to containing data, if NULL use the mount
* point specific buffer
*
* Outputs:
*
* None
*
* Returns:
*
* 1 if successful, 0 otherwise
*
--*/
int dos11WriteBlock(
struct mountedFS *mount,
unsigned int block,
void *buf
)
{
struct DOS11data *data = &mount->dos11data;
void *buffer = buf == NULL ? data->buf : buf;
int status;
if (block >= data->blocks) {
ERROR("Attempt to write block (%u) outside file system \"%s\"\n",
block, mount->name);
return 0;
}
#ifdef DEBUG
if ((mount->flags & FS_DEBUG) != 0)
fprintf(DEBUGout, ">> %s: (dos11) Writing logical block %o\n",
mount->name, block);
#endif
status = FSioWriteBlock(mount, block, buffer);
if (status == 0)
ERROR("I/O error on \"%s\"\n", mount->name);
return status;
}
/*++
* d o s 1 1 R e a d B y t e s
*
* Read a sequence of bytes from an open file. The file may be either
* "Linked" or "Contiguous".
*
* 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 dos11ReadBytes(
struct dos11OpenFile *file,
char *buf,
int len
)
{
int count = 0;
if (file->current == 0) {
file->current = le16toh(file->start);
if (dos11ReadBlock(file->mount, file->current, file->buffer) == 0)
return 0;
file->nab = (le16toh(file->creation) & UFD_TYPE) == 0 ? 2 : 0;
file->eob = file->current == le16toh(file->last) ?
le16toh(file->nfb) : file->mount->blocksz;
}
while (len) {
if (file->nab == file->eob) {
if (file->current == le16toh(file->last))
break;
if ((le16toh(file->creation) & UFD_TYPE) == 0)
file->current = le16toh(*((uint16_t *)(file->buffer)));
else file->current++;
if (dos11ReadBlock(file->mount, file->current, file->buffer) == 0)
return 0;
file->nab = (le16toh(file->creation) & UFD_TYPE) == 0 ? 2 : 0;
file->eob = file->current == le16toh(file->last) ?
le16toh(file->nfb) : file->mount->blocksz;
continue;
}
*buf++ = file->buffer[file->nab++];
len--;
count++;
}
return count;
}
/*++
* d o s 1 1 W r i t e B y t e s
*
* Write a sequence of bytes to an open file. The file may be either
* "Linked" or "Contiguous". Linked files will be automatically extended
* as new data is written.
*
* 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 error
*
--*/
int dos11WriteBytes(
struct dos11OpenFile *file,
char *buf,
int len
)
{
int count = 0;
uint16_t next;
if (file->current == 0) {
file->eob = file->mount->blocksz;
if ((le16toh(file->creation) & UFD_TYPE) == 0) {
/*
* Linked file - allocate initial block
*/
if ((next = bitmapAllocBlock(file->mount)) == 0) {
ERROR("Free disk space exhausted\n");
return 0;
}
file->current = next;
file->start = file->last = htole16(next);
file->length = htole16(1);
*((uint16_t *)(file->buffer)) = 0;
file->nab = 2;
} else {
file->current = le16toh(file->start);
file->last = file->start;
file->nab = 0;
}
}
while (len) {
if (file->nab == file->eob) {
if ((le16toh(file->creation) & UFD_TYPE) == 0) {
/*
* Linked file - extend it by 1 block
*/
if ((next = bitmapAllocBlock(file->mount)) == 0) {
ERROR("Free disk space exhausted extending file\n");
return count;
}
*((uint16_t *)(file->buffer)) = htole16(next);
if (dos11WriteBlock(file->mount, file->current, file->buffer) == 0)
return 0;
file->current = next;
file->last = htole16(next);
file->length = htole16(le16toh(file->length) + 1);
file->nab = 2;
} else {
if (dos11WriteBlock(file->mount, file->current, file->buffer) == 0)
return 0;
if (++file->current == (le16toh(file->last) + le16toh(file->length))) {
ERROR("Contiguous disk space allocation exceeded\n");
return 0;
}
file->nab = 0;
file->last = htole16(file->current);
}
}
file->buffer[file->nab++] = *buf++;
len--;
count++;
}
return count;
}
/*++
* d o s 1 1 M o u n t
*
* Verify that the open container file is a DOS-11 file system. We check that
* we can read all MFD entries and the UFD entries have the correct size
* (9 words). We also verify that we can read all bitmaps and they have the
* correct size (60 words).
*
* Inputs:
*
* mount - pointer to a mounted file system descriptor
* (not in the mounted file system list)
*
* Outputs:
*
* None
*
* Returns:
*
* 1 if a valid DOS-11 file system, 0 otherwise
*
--*/
static int dos11Mount(
struct mountedFS *mount
)
{
struct DOS11data *data = &mount->dos11data;
struct stat stat;
uint16_t mfdblk, mapblk, interleave;
unsigned int i, freeblocks = 0;
if (fstat(fileno(mount->container), &stat) == 0) {
if (stat.st_blocks < DISKSIZE_RK05)
mount->blocksz = BLOCKSIZE_RF11 * 2;
if (stat.st_blocks > DISKSIZE_RK05)
mount->blocksz = BLOCKSIZE_RP03 * 2;
data->blocks = stat.st_size / mount->blocksz;
/*
* Build a list of the bitmap blocks.
*/
if (dos11ReadBlock(mount, MFD1_BLOCK, NULL) == 0)
return 0;
mfdblk = le16toh(data->buf[MFD1_MFD2BLOCK]);
interleave = le16toh(data->buf[MFD1_INTERLEAVE]);
data->bitmaps = 0;
mapblk = le16toh(data->buf[MFD1_BMSTART]);
do {
if (dos11ReadBlock(mount, mapblk, NULL) == 0)
return 0;
if (le16toh(data->buf[MAP_WORDS]) != MAP_LEN) {
ERROR("mount: wrong bitmap size (%d) for bitmap %d\n",
le16toh(data->buf[MAP_WORDS]),
data->bitmaps + 1);
return 0;
}
/*
* Compute the # of free blocks in the bitmap.
*/
for (i = 0; i < MAP_LEN; i++) {
uint16_t bmentry = le16toh(data->buf[i + MAP_BMSTART]);
if (bmentry != 0177777) {
freeblocks += zeroes[(bmentry >> 8) & 0377];
freeblocks += zeroes[bmentry & 0377];
}
}
data->bmblk[data->bitmaps++] = mapblk;
mapblk = le16toh(data->buf[MAP_LINK]);
} while (mapblk != 0);
do {
if (dos11ReadBlock(mount, mfdblk, NULL) == 0)
return 0;
for (i = MFD2_HEADER; i < EODIR(mount, MFD2_SIZE); i += MFD2_SIZE)
if (le16toh(data->buf[i + MFD2_UFDUIC]) != 0)
if (le16toh(data->buf[i + MFD2_UFDSIZE]) != UFD_LEN) {
ERROR("mount: wrong directory size (%d) for [%3o,%3o]\n",
le16toh(data->buf[i + MFD2_UFDSIZE]),
(le16toh(data->buf[i + MFD2_UFDUIC]) >> 8) & 0377,
le16toh(data->buf[i + MFD2_UFDUIC]) & 0377);
return 0;
}
mfdblk = le16toh(data->buf[MFD2_LINK]);
} while (mfdblk != 0);
/*
* Preload bitmap 0
*/
data->bmindex = 0;
data->bmscan = 0;
data->bmdirty = 0;
if (dos11ReadBlock(mount, data->bmblk[0], data->bmbuf) == 0)
return 0;
if (!quiet) {
printf("%s: successfully mounted\n", mount->name);
printf("Total blocks: %d, Free blocks: %d, Interleave: %d\n",
data->blocks, freeblocks, interleave);
}
/*
* Set up default parameters
*/
data->group = 1;
data->user = 1;
return 1;
}
return 0;
}
/*++
* d o s 1 1 U m o u n t
*
* Unmount the DOS-11 file system, releasing any storage allocated.
*
* Inputs:
*
* mount - pointer to the mounted file system descriptor
*
* Outputs:
*
* None
*
* Returns:
*
* None
*
--*/
static void dos11Umount(
struct mountedFS *UNUSED(mount)
)
{
}
/*++
* d o s 1 1 S i z e
*
* Return the size of a DOS-11 container file (RK05).
*
* Inputs:
*
* None
*
* Outputs:
*
* None
*
* Returns:
*
* Default size of the container file in bytes (RK05).
*
--*/
static size_t dos11Size(void)
{
return DISKSIZE_RK05 * BLOCKSIZE_RK11;
}
/*++
* d o s 1 1 N e w f s
*
* Create an empty DOS11 file system.
*
* Inputs:
*
* mount - pointer to a mounted file system descriptor
* (not in the mounted file system list)
* size - the size (in blocks) of the file system
*
* Outputs:
*
* None
*
* Returns:
*
* 1 if the file system was successfully created, 0 otherwise
*
--*/
static int dos11Newfs(
struct mountedFS *mount,
size_t size
)
{
struct DOS11data *data = &mount->dos11data;
int i;
#define MFD2_BLOCK 2
#define MAP_BLOCK 3
memset(data, 0, sizeof(*data));
data->blocks = size / BLOCKSIZE_RK11;
data->bitmaps = 5;
data->bmblk[0] = MAP_BLOCK;
data->bmblk[1] = MAP_BLOCK + 1;
data->bmblk[2] = MAP_BLOCK + 2;
data->bmblk[3] = MAP_BLOCK + 3;
data->bmblk[4] = MAP_BLOCK + 4;
data->bmindex = 0177777;
/*
* Build and write MFD Block #1:
*
* - second MFD block is at block 2
* - 5 bitmaps starting at block (filesys->blocks - 5).
*/
memset(data->buf, 0, mount->blocksz);
data->buf[MFD1_MFD2BLOCK] = htole16(MFD2_BLOCK);
data->buf[MFD1_INTERLEAVE] = htole16(1);
data->buf[MFD1_BMSTART] = htole16(data->bmblk[0]);
data->buf[MFD1_BMSTART + 1] = htole16(data->bmblk[1]);
data->buf[MFD1_BMSTART + 2] = htole16(data->bmblk[2]);
data->buf[MFD1_BMSTART + 3] = htole16(data->bmblk[3]);
data->buf[MFD1_BMSTART + 4] = htole16(data->bmblk[4]);
data->buf[MFD1_BMSTART + 5] = htole16(0);
if (dos11WriteBlock(mount, MFD1_BLOCK, NULL) == 0)
return 0;
/*
* Build and write MFD Block #2:
*
* - no UFDs present
*/
memset(data->buf, 0, mount->blocksz);
if (dos11WriteBlock(mount, MFD2_BLOCK, NULL) == 0)
return 0;
/*
* Build and write 5 bitmap blocks.
*/
memset(data->buf, 0, mount->blocksz);
for (i = 1; i < 6; i++) {
data->buf[MAP_LINK] =
i == 5 ? 0 : htole16(data->bmblk[i]);
data->buf[MAP_MAP] = htole16(i);
data->buf[MAP_WORDS] = htole16(MAP_LEN);
data->buf[MAP_FIRST] = htole16(MAP_BLOCK);
if (dos11WriteBlock(mount, data->bmblk[i - 1], NULL) == 0)
return 0;
}
/*
* Reserve used blocks in the bitmap(s).
*/
if (bitmapSetBit(mount, BOOT_BLOCK) == 0)
return 0;
if (bitmapSetBit(mount, MFD1_BLOCK) == 0)
return 0;
if (bitmapSetBit(mount, MFD2_BLOCK) == 0)
return 0;
for (i = 1; i < 6; i++)
if (bitmapSetBit(mount, MAP_BLOCK + i - 1) == 0)
return 0;
/*
* Reserve all blocks past the end of the disk.
*/
for (i = 4800; i < (5 * MAP_BLOCKS); i++)
if (bitmapSetBit(mount, i) == 0)
return 0;
return bitmapFlush(mount);
}
/*++
* d o s 1 1 S e t
*
* Set mount point specific values.
*
* 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 dos11Set(
struct mountedFS *mount,
uint8_t UNUSED(unit),
uint8_t UNUSED(present)
)
{
struct DOS11data *data = &mount->dos11data;
int idx = 0;
uint8_t user, group;
while (setCmds[idx] != NULL) {
if (strcmp(words[1], setCmds[idx]) == 0) {
switch (idx) {
case DOS11SET_UIC:
if (args == 3) {
if (sscanf(words[2], "[%hho,%hho]", &group, &user) == 2) {
data->group = group;
data->user = user;
} else fprintf(stderr,
"dos11: UIC syntax error \"%s\"\n", words[2]);
} else fprintf(stderr, "dos11: Invalid syntax for \"set uic\"\n");
return;
case DOS11SET_UFD:
if (args == 3) {
if (sscanf(words[2], "[%hho,%hho]", &group, &user) == 2) {
if (dos11CreateUFD(mount, group, user) != 0) {
data->group = group;
data->user = user;
}
} else fprintf(stderr,
"dos11: UIC syntax error \"%s\"\n", words[2]);
} else fprintf(stderr, "dos11: Invalid syntax for \"set ufd\"\n");
return;
default:
fprintf(stderr, "dos11: \"%s\" not implemented\n", words[1]);
return;
}
}
idx++;
}
fprintf(stderr, "dos11: Unknown set command \"%s\"\n", words[1]);
}
/*++
* d o s 1 1 I n f o
*
* Display infomation about the internal structure of the DOS-11 file system.
* This functions generates a display similar to the output provided by the
* LIST operation of DOS/BATCH-11 V09.20C
*
* 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 dos11Info(
struct mountedFS *mount,
uint8_t UNUSED(unit),
uint8_t UNUSED(present)
)
{
struct DOS11data *data = &mount->dos11data;
struct tm tm;
char datetime[32], temp[32];
time_t now = time(NULL);
unsigned int mfd2blk, mfdblk, ufdblk, mapblk, i, j;
uint16_t buf[512];
localtime_r(&now, &tm);
strftime(datetime, sizeof(datetime), "%d-%b-%C at %I:%M:%S", &tm);
printf("* * * * * * Listing of MFD for %s * * * * * * on %s\n",
mount->name, datetime);
printf(" UIC First UFD Block UFD Entry Size\n\n");
if (dos11ReadBlock(mount, MFD1_BLOCK, NULL) == 0)
return;
/*
* Get some useful block addresses
*/
mfd2blk = le16toh(data->buf[MFD1_MFD2BLOCK]);
mapblk = le16toh(data->buf[MFD1_BMSTART]);
/*
* Display the MFD entries
*/
mfdblk = mfd2blk;
do {
if (dos11ReadBlock(mount, mfdblk, NULL) == 0)
return;
for (i = MFD2_HEADER; i < EODIR(mount, MFD2_SIZE); i += MFD2_SIZE)
if (le16toh(data->buf[i + MFD2_UFDUIC]) != 0) {
printf("[%3o,%3o] %5o %5d.\n",
(le16toh(data->buf[i + MFD2_UFDUIC]) >> 8) & 0377,
le16toh(data->buf[i + MFD2_UFDUIC]) & 0377,
le16toh(data->buf[i + MFD2_UFDSTART]),
le16toh(data->buf[i + MFD2_UFDSIZE]));
}
mfdblk = le16toh(data->buf[MFD2_LINK]);
} while (mfdblk != 0);
printf("\n");
/*
* Display the individual UFD entries
*/
mfdblk = mfd2blk;
do {
if (dos11ReadBlock(mount, mfdblk, NULL) == 0)
return;
for (i = MFD2_HEADER; i < EODIR(mount, MFD2_SIZE); i += MFD2_SIZE)
if (le16toh(data->buf[i + MFD2_UFDUIC]) != 0) {
unsigned int entrysz = le16toh(data->buf[i + MFD2_UFDSIZE]);
printf("* * * Listing of [%3o,%3o] User Directory * * *\n\n",
(le16toh(data->buf[i + MFD2_UFDUIC]) >> 8) & 0377,
le16toh(data->buf[i + MFD2_UFDUIC]) & 0377);
printf(" File Ext Date Type Usage Lock Start Length End Prot EBP\n\n");
if ((ufdblk = le16toh(data->buf[i + MFD2_UFDSTART])) != 0) {
do {
if (dos11ReadBlock(mount, ufdblk, buf) == 0)
return;
for (j = UFD_HEADER; j < EODIR(mount, entrysz); j += entrysz) {
/*
* Skip over deleted files
*/
if ((le16toh(buf[j + UFD_FILENAME]) == 0) &&
(le16toh(buf[j + UFD_FILENAME + 1]) == 0) &&
(le16toh(buf[j + UFD_EXTENSION]) == 0))
continue;
r50Asc(le16toh(buf[j + UFD_FILENAME]), &temp[0]);
r50Asc(le16toh(buf[j + UFD_FILENAME + 1]), &temp[3]);
temp[6] = '.';
r50Asc(le16toh(buf[j + UFD_EXTENSION]), &temp[7]);
temp[10] = ' ';
temp[11] = ' ';
dos11Date(le16toh(buf[j + UFD_CREATION]) & UFD_DATE, &temp[12]);
temp[25] = '\0';
printf("%s %c %3o %c %6o %5u. %6o %3o %6o\n",
temp,
(le16toh(buf[j + UFD_CREATION]) & UFD_TYPE) ? 'C' : 'L',
(le16toh(buf[j + UFD_LUP]) & UFD_USAGE) >> 8,
(le16toh(buf[j + UFD_LUP]) & UFD_LOCK) ? '1' : '0',
le16toh(buf[j + UFD_FILESTART]),
le16toh(buf[j + UFD_FILELENGTH]),
le16toh(buf[j + UFD_LASTBLOCKWRITTEN]),
le16toh(buf[j + UFD_LUP]) & UFD_PROT,
le16toh(buf[j + UFD_NEXTFREEBYTE]));
}
ufdblk = le16toh(buf[UFD_LINK]);
} while (ufdblk != 0);
printf("\n");
}
}
mfdblk = le16toh(data->buf[MFD2_LINK]);
} while (mfdblk != 0);
/*
* Display bitmap information
*/
printf("* * * * * * Map Verification * * * * * *\n\n");
do {
if (dos11ReadBlock(mount, mapblk, buf) == 0)
return;
printf("* * * * * * Map Header Information * * * * * *\n");
printf("Link = %6o\n", le16toh(buf[MAP_LINK]));
printf("Map Number = %2u.\n", le16toh(buf[MAP_MAP]));
printf("Words In Map = %3u.\n", le16toh(buf[MAP_WORDS]));
printf("Link To First Map = %6o\n\n", le16toh(buf[MAP_FIRST]));
for (i = 0; i < le16toh(buf[MAP_WORDS]); i++)
printf(" %6o%c",
le16toh(buf[MAP_BMSTART + i]),
((i + 1) % 8) == 0 ? '\n' : ' ');
printf("\n\n");
mapblk = le16toh(buf[MAP_LINK]);
} while (mapblk != 0);
}
/*++
* d o s 1 1 D i r
*
* Produce a full or brief directory listing.
*
* Inputs:
*
* mount - pointer to the mounted file system descriptor
* unit - device unit number (unused)
* fname - pointer to filename string
*
* Outputs:
*
* None
*
* Returns:
*
* None
*
--*/
static void dos11Dir(
struct mountedFS *mount,
uint8_t UNUSED(unit),
char *fname
)
{
struct DOS11data *data = &mount->dos11data;
uint8_t user = data->user;
uint8_t group = data->group;
struct dos11FileSpec spec;
unsigned int mfdblk, ufdblk, i, j;
uint16_t buf[512];
uint8_t found = 0;
if (dos11ParseFilespec(fname, &spec, user, group, DOS11_M_NONAME) == 0) {
fprintf(stderr, "dir: syntax error in file spec \"%s\"\n", fname);
return;
}
if (dos11ReadBlock(mount, MFD1_BLOCK, NULL) == 0)
return;
/*
* Search the MFD for matching UIC entries.
*/
mfdblk = le16toh(data->buf[MFD1_MFD2BLOCK]);
do {
if (dos11ReadBlock(mount, mfdblk, NULL) == 0)
return;
for (i = MFD2_HEADER; i < EODIR(mount, MFD2_SIZE); i += MFD2_SIZE) {
uint16_t uic = le16toh(data->buf[i + MFD2_UFDUIC]);
uint16_t entrysz = le16toh(data->buf[i + MFD2_UFDSIZE]);
if (uic == 0)
continue;
if ((spec.flags & DOS11_WC_GROUP) == 0)
if (spec.group != ((uic >> 8) & 0377))
continue;
if ((spec.flags & DOS11_WC_USER) == 0)
if (spec.user != (uic & 0377))
continue;
found = 1;
if ((ufdblk = le16toh(data->buf[i + MFD2_UFDSTART])) != 0) {
unsigned int header = 0;
do {
if (dos11ReadBlock(mount, ufdblk, buf) == 0)
return;
for (j = UFD_HEADER; j < EODIR(mount, entrysz); j += entrysz) {
if ((le16toh(buf[j + UFD_FILENAME]) == 0) &&
(le16toh(buf[j + UFD_FILENAME + 1]) == 0) &&
(le16toh(buf[j + UFD_EXTENSION]) == 0))
continue;
if ((spec.flags & DOS11_WC_NAME) == 0)
if ((le16toh(buf[j + UFD_FILENAME]) != spec.name[0]) ||
(le16toh(buf[j + UFD_FILENAME + 1]) != spec.name[1]))
continue;
if ((spec.flags & DOS11_WC_EXT) == 0)
if (le16toh(buf[j + UFD_EXTENSION]) != spec.ext)
continue;
if (!header) {
printf("%s:[%u, %u]\n\n",
mount->name, (uic >> 8) & 0377, uic & 0377);
header = 1;
}
dos11DisplayDir(&buf[j], SWISSET('f'));
}
ufdblk = le16toh(buf[UFD_LINK]);
} while (ufdblk != 0);
if (header)
printf("\n");
}
}
mfdblk = le16toh(data->buf[MFD2_LINK]);
} while (mfdblk != 0);
if (!found && ((spec.flags & (DOS11_WC_GROUP | DOS11_WC_USER)) == 0))
ERROR("dir: Directory [%o,%o] not found\n", spec.group, spec.user);
}
/*++
* d o s 1 1 O p e n F i l e R
*
* Open a DOS-11 file for reading.
*
* Inputs:
*
* mount - pointer to the mounted file system descriptor
* unit - device unit number (unused)
* fname - pointer to filename string
*
* Outputs:
*
* None
*
* Returns:
*
* Pointer to open file descriptor, NULL if open fails
*
--*/
static void *dos11OpenFileR(
struct mountedFS *mount,
uint8_t UNUSED(unit),
char *fname
)
{
struct DOS11data *data = &mount->dos11data;
struct dos11OpenFile *file;
struct dos11FileSpec spec;
uint8_t user = data->user;
uint8_t group = data->group;
if (dos11ParseFilespec(fname, &spec, user, group, DOS11_M_NONE) == 0) {
fprintf(stderr, "Failed to parse filename \"%s\"\n", fname);
return NULL;
}
if ((file = malloc(sizeof(struct dos11OpenFile))) != NULL) {
memset(file, 0, sizeof(struct dos11OpenFile));
if (dos11LookupFile(mount, &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;
}
/*++
* d o s 1 1 O p e n F i l e W
*
* Open a DOS-11 file for writing.
*
* Inputs:
*
* mount - pointer to the mounted file system descriptor
* unit - device unit number (unused)
* 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 *dos11OpenFileW(
struct mountedFS *mount,
uint8_t UNUSED(unit),
char *fname,
off_t size
)
{
struct DOS11data *data = &mount->dos11data;
struct dos11OpenFile *file;
struct dos11FileSpec spec;
unsigned long contig = (size + mount->blocksz - 1) / mount->blocksz;
uint8_t user = data->user;
uint8_t group = data->group;
if (SWISSET('c') && (contig == 0)) {
fprintf(stderr,"DOS-11 contiguous files must be at least 1 block\n");
return NULL;
}
if (dos11ParseFilespec(fname, &spec, user, group, DOS11_M_NONE) == 0) {
fprintf(stderr, "Failed to parse filename \"%s\"\n", fname);
return NULL;
}
if ((file = malloc(sizeof(struct dos11OpenFile))) != NULL) {
memset(file, 0, sizeof(struct dos11OpenFile));
if (dos11LookupFile(mount, &spec, file) == 0) {
/*
* Allocate local bufferspace for the file.
*/
if ((file->buffer = malloc(mount->blocksz)) != NULL) {
if (dos11CreateFile(mount, &spec, file, contig) == 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 fprintf(stderr, "Memory allocation failure\n");
return file;
}
/*++
* d o s 1 1 F i l e S i z e
*
* Return an estimate of the size of an open file. This routine bases the
* size on the number of blocks allocated to the file. The size of "Linked"
* files will be over-reported by 2 bytes for each block allocated.
*
* Inputs:
*
* filep - pointer to open file descriptor
*
* Outputs:
*
* None
*
* Returns:
*
* Estimated size of the file
*
--*/
static off_t dos11FileSize(
void *filep
)
{
struct dos11OpenFile *file = filep;
struct mountedFS *mount = file->mount;
return le16toh(file->length) * mount->blocksz;
}
/*++
* d o s 1 1 D e l e t e F i l e
*
* Delete a file which has been opened via dos11LookupFile().
*
* Inputs:
*
* filep - pointer to open file descriptor
* fname - pointer to filename string
*
* Outputs:
*
* The mount point specific buffer will be modified.
*
* Returns:
*
* None
*
--*/
static void dos11DeleteFile(
void *filep,
char *UNUSED(fname)
)
{
struct dos11OpenFile *file = filep;
struct mountedFS *mount = file->mount;
struct DOS11data *data = &mount->dos11data;
uint16_t i, block;
file->name[0] = 0;
file->name[1] = 0;
file->ext = 0;
dos11UpdateFile(mount, file);
/*
* The file has been removed from its UFD, release all disk blocks
* allocated to the file.
*/
block = le16toh(file->start);
if ((le16toh(file->creation) & UFD_TYPE) != 0) {
/*
* Contiguous file
*/
for (i = 0; i < le16toh(file->length); i++)
if (bitmapReleaseBlock(mount, block + i) == 0)
return;
} else {
/*
* Linked file
*/
while (block != 0) {
if (dos11ReadBlock(mount, block, NULL) == 0)
return;
if (bitmapReleaseBlock(mount, block) == 0)
return;
block = le16toh(data->buf[FILE_LINK]);
}
}
/*
* Make sure the bitmaps on disk are up to date.
*/
bitmapFlush(mount);
dos11CloseFile(file);
}
/*++
* d o s 1 1 C l o s e F i l e
*
* Close an open DOS-11 file.
*
* Inputs:
*
* filep - pointer to open file descriptor
*
* Outputs:
*
* None
*
* Returns:
*
* None
*
--*/
static void dos11CloseFile(
void *filep
)
{
struct dos11OpenFile *file = filep;
struct mountedFS *mount = file->mount;
if (file->mode == M_WR) {
uint16_t start = (le16toh(file->creation) & UFD_TYPE) == 0 ? 2 : 0;
/*
* Flush any partial buffer, pending bitmap updates and update the
* directory entry.
*/
if (((le16toh(file->creation) & UFD_TYPE) == 0) ||
(file->current == le16toh(file->last)))
file->nfb = htole16(file->nab);
else file->nfb = htole16(mount->blocksz);
if (file->nab != start)
dos11WriteBlock(mount, file->current, file->buffer);
bitmapFlush(mount);
dos11UpdateFile(mount, file);
}
if (file != NULL) {
if (file->buffer != NULL)
free(file->buffer);
free(file);
}
}
/*++
* d o s 1 1 R e a d F i l e
*
* Read data from a DOS-11 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 dos11ReadFile(
void *filep,
void *buf,
size_t buflen
)
{
struct dos11OpenFile *file = filep;
char *bufr = buf;
if (SWISSET('a')) {
char ch;
size_t count = 0;
/*
* Read a full or partial line from the open file.
*/
while ((buflen != 0) && (dos11ReadBytes(file, &ch, 1) == 1)) {
ch &= 0177;
bufr[count++] = ch;
buflen--;
if (ch == '\n')
break;
}
return count;
}
return dos11ReadBytes(file, bufr, buflen);
}
/*++
* d o s 1 1 W r i t e F i l e
*
* Write data to a DOS-11 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 error
*
--*/
size_t dos11WriteFile(
void *filep,
void *buf,
size_t buflen
)
{
struct dos11OpenFile *file = filep;
char *bufw = buf;
if (SWISSET('a')) {
size_t count = 0;
/*
* Write a line, terminated by <CRLF>, to the open file.
*/
while (buflen != 0) {
char ch;
ch = bufw[count++] & 0177;
buflen--;
if (dos11WriteBytes(file, &ch, 1) == 0)
break;
}
return count;
}
return dos11WriteBytes(file, bufw, buflen);
}
/*++
* d o s 1 1 F S
*
* Descriptor for accessing DOS-11 file systems.
*
--*/
struct FSdef dos11FS = {
NULL,
"dos11",
"dos11 PDP-11 DOS/BATCH-11 file system (RF11, RK05 or RP03 disks)\n",
0,
BLOCKSIZE_RK11 * 2, /* Default for RK05 */
dos11Mount,
dos11Umount,
dos11Size,
dos11Newfs,
dos11Set,
dos11Info,
dos11Dir,
dos11OpenFileR,
dos11OpenFileW,
dos11FileSize,
dos11CloseFile,
dos11ReadFile,
dos11WriteFile,
dos11DeleteFile,
NULL, /* No tape support functions */
NULL,
NULL,
NULL
};