]> git.sesse.net Git - pistorm/commitdiff
Adapt a314fs to work on PiStorm + A314 emulation
authorbeeanyew <beeanyew@gmail.com>
Tue, 18 May 2021 11:09:45 +0000 (13:09 +0200)
committerbeeanyew <beeanyew@gmail.com>
Tue, 18 May 2021 11:09:45 +0000 (13:09 +0200)
Requires a314fs in L:, a314fs-mountlist in DEVS:DosDrivers and somehow a314fs.py started after the emulator but before mounting the drive.
Untested.

a314/files_pi/a314fs.py [new file with mode: 0644]
a314/software-amiga/a314fs
a314/software-amiga/a314fs-mountlist [new file with mode: 0644]
a314/software-amiga/a314fs_pistorm/a314fs.c [new file with mode: 0644]
a314/software-amiga/a314fs_pistorm/bcpl_end.asm [new file with mode: 0644]
a314/software-amiga/a314fs_pistorm/bcpl_start.asm [new file with mode: 0644]
a314/software-amiga/a314fs_pistorm/build.bat [new file with mode: 0644]
a314/software-amiga/a314fs_pistorm/comment_out_sections.py [new file with mode: 0644]
a314/software-amiga/a314fs_pistorm/messages.h [new file with mode: 0644]
a314/software-amiga/a314fs_pistorm/patch_a314fs.py [new file with mode: 0644]

diff --git a/a314/files_pi/a314fs.py b/a314/files_pi/a314fs.py
new file mode 100644 (file)
index 0000000..75e0a27
--- /dev/null
@@ -0,0 +1,817 @@
+#!/usr/bin/python3
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2018-2021 Niklas Ekström
+
+import select
+import sys
+import socket
+import time
+import os
+import struct
+import glob
+import logging
+import json
+
+logging.basicConfig(format = '%(levelname)s, %(asctime)s, %(name)s, line %(lineno)d: %(message)s')
+logger = logging.getLogger(__name__)
+logger.setLevel(logging.INFO)
+
+CONFIG_FILE_PATH = './a314fs.conf'
+
+SHARED_DIRECTORY = '/home/pi/a314shared'
+METAFILE_EXTENSION = ':a314'
+
+with open(CONFIG_FILE_PATH, encoding='utf-8') as f:
+    cfg = json.load(f)
+    devs = cfg['devices']
+    dev = devs['PI0']
+    SHARED_DIRECTORY = dev['path']
+
+MSG_REGISTER_REQ        = 1
+MSG_REGISTER_RES        = 2
+MSG_DEREGISTER_REQ      = 3
+MSG_DEREGISTER_RES      = 4
+MSG_READ_MEM_REQ        = 5
+MSG_READ_MEM_RES        = 6
+MSG_WRITE_MEM_REQ       = 7
+MSG_WRITE_MEM_RES       = 8
+MSG_CONNECT             = 9
+MSG_CONNECT_RESPONSE    = 10
+MSG_DATA                = 11
+MSG_EOS                 = 12
+MSG_RESET               = 13
+
+def wait_for_msg():
+    header = b''
+    while len(header) < 9:
+        data = drv.recv(9 - len(header))
+        if not data:
+            logger.error('Connection to a314d was closed, terminating.')
+            exit(-1)
+        header += data
+    (plen, stream_id, ptype) = struct.unpack('=IIB', header)
+    payload = b''
+    while len(payload) < plen:
+        data = drv.recv(plen - len(payload))
+        if not data:
+            logger.error('Connection to a314d was closed, terminating.')
+            exit(-1)
+        payload += data
+    return (stream_id, ptype, payload)
+
+def send_register_req(name):
+    m = struct.pack('=IIB', len(name), 0, MSG_REGISTER_REQ) + name
+    drv.sendall(m)
+
+def send_read_mem_req(address, length):
+    m = struct.pack('=IIBII', 8, 0, MSG_READ_MEM_REQ, address, length)
+    drv.sendall(m)
+
+def read_mem(address, length):
+    send_read_mem_req(address, length)
+    stream_id, ptype, payload = wait_for_msg()
+    if ptype != MSG_READ_MEM_RES:
+        logger.error('Expected MSG_READ_MEM_RES but got %s. Shutting down.', ptype)
+        exit(-1)
+    return payload
+
+def send_write_mem_req(address, data):
+    m = struct.pack('=IIBI', 4 + len(data), 0, MSG_WRITE_MEM_REQ, address) + data
+    drv.sendall(m)
+
+def write_mem(address, data):
+    send_write_mem_req(address, data)
+    stream_id, ptype, payload = wait_for_msg()
+    if ptype != MSG_WRITE_MEM_RES:
+        logger.error('Expected MSG_WRITE_MEM_RES but got %s. Shutting down.', ptype)
+        exit(-1)
+
+def send_connect_response(stream_id, result):
+    m = struct.pack('=IIBB', 1, stream_id, MSG_CONNECT_RESPONSE, result)
+    drv.sendall(m)
+
+def send_data(stream_id, data):
+    m = struct.pack('=IIB', len(data), stream_id, MSG_DATA) + data
+    drv.sendall(m)
+
+def send_eos(stream_id):
+    m = struct.pack('=IIB', 0, stream_id, MSG_EOS)
+    drv.sendall(m)
+
+def send_reset(stream_id):
+    m = struct.pack('=IIB', 0, stream_id, MSG_RESET)
+    drv.sendall(m)
+
+ACTION_NIL              = 0
+ACTION_GET_BLOCK        = 2
+ACTION_SET_MAP          = 4
+ACTION_DIE              = 5
+ACTION_EVENT            = 6
+ACTION_CURRENT_VOLUME   = 7
+ACTION_LOCATE_OBJECT    = 8
+ACTION_RENAME_DISK      = 9
+ACTION_WRITE            = ord('W')
+ACTION_READ             = ord('R')
+ACTION_FREE_LOCK        = 15
+ACTION_DELETE_OBJECT    = 16
+ACTION_RENAME_OBJECT    = 17
+ACTION_MORE_CACHE       = 18
+ACTION_COPY_DIR         = 19
+ACTION_WAIT_CHAR        = 20
+ACTION_SET_PROTECT      = 21
+ACTION_CREATE_DIR       = 22
+ACTION_EXAMINE_OBJECT   = 23
+ACTION_EXAMINE_NEXT     = 24
+ACTION_DISK_INFO        = 25
+ACTION_INFO             = 26
+ACTION_FLUSH            = 27
+ACTION_SET_COMMENT      = 28
+ACTION_PARENT           = 29
+ACTION_TIMER            = 30
+ACTION_INHIBIT          = 31
+ACTION_DISK_TYPE        = 32
+ACTION_DISK_CHANGE      = 33
+ACTION_SET_DATE         = 34
+ACTION_SAME_LOCK        = 40
+ACTION_SCREEN_MODE      = 994
+ACTION_READ_RETURN      = 1001
+ACTION_WRITE_RETURN     = 1002
+ACTION_FINDUPDATE       = 1004
+ACTION_FINDINPUT        = 1005
+ACTION_FINDOUTPUT       = 1006
+ACTION_END              = 1007
+ACTION_SEEK             = 1008
+ACTION_TRUNCATE         = 1022
+ACTION_WRITE_PROTECT    = 1023
+ACTION_EXAMINE_FH       = 1034
+ACTION_UNSUPPORTED      = 65535
+
+ERROR_NO_FREE_STORE             = 103
+ERROR_TASK_TABLE_FULL           = 105
+ERROR_LINE_TOO_LONG             = 120
+ERROR_FILE_NOT_OBJECT           = 121
+ERROR_INVALID_RESIDENT_LIBRARY  = 122
+ERROR_NO_DEFAULT_DIR            = 201
+ERROR_OBJECT_IN_USE             = 202
+ERROR_OBJECT_EXISTS             = 203
+ERROR_DIR_NOT_FOUND             = 204
+ERROR_OBJECT_NOT_FOUND          = 205
+ERROR_BAD_STREAM_NAME           = 206
+ERROR_OBJECT_TOO_LARGE          = 207
+ERROR_ACTION_NOT_KNOWN          = 209
+ERROR_INVALID_COMPONENT_NAME    = 210
+ERROR_INVALID_LOCK              = 211
+ERROR_OBJECT_WRONG_TYPE         = 212
+ERROR_DISK_NOT_VALIDATED        = 213
+ERROR_DISK_WRITE_PROTECTED      = 214
+ERROR_RENAME_ACROSS_DEVICES     = 215
+ERROR_DIRECTORY_NOT_EMPTY       = 216
+ERROR_TOO_MANY_LEVELS           = 217
+ERROR_DEVICE_NOT_MOUNTED        = 218
+ERROR_SEEK_ERROR                = 219
+ERROR_COMMENT_TOO_BIG           = 220
+ERROR_DISK_FULL                 = 221
+ERROR_DELETE_PROTECTED          = 222
+ERROR_WRITE_PROTECTED           = 223
+ERROR_READ_PROTECTED            = 224
+ERROR_NOT_A_DOS_DISK            = 225
+ERROR_NO_DISK                   = 226
+ERROR_NO_MORE_ENTRIES           = 232
+
+SHARED_LOCK         = -2
+EXCLUSIVE_LOCK      = -1
+
+LOCK_DIFFERENT      = -1
+LOCK_SAME           = 0
+LOCK_SAME_VOLUME    = 1
+
+MODE_OLDFILE        = 1005  # Open existing file read/write positioned at beginning of file.
+MODE_NEWFILE        = 1006  # Open freshly created file (delete old file) read/write, exclusive lock.
+MODE_READWRITE      = 1004  # Open old file w/shared lock, creates file if doesn't exist.
+
+OFFSET_BEGINNING    = -1    # Relative to Begining Of File.
+OFFSET_CURRENT      = 0     # Relative to Current file position.
+OFFSET_END          = 1     # relative to End Of File.
+
+ST_ROOT             = 1
+ST_USERDIR          = 2
+ST_SOFTLINK         = 3     # looks like dir, but may point to a file!
+ST_LINKDIR          = 4     # hard link to dir
+ST_FILE             = -3    # must be negative for FIB!
+ST_LINKFILE         = -4    # hard link to file
+ST_PIPEFILE         = -5
+
+current_stream_id = 0
+
+class ObjectLock(object):
+    def __init__(self, key, mode, path):
+        self.key = key
+        self.mode = mode
+        self.path = path
+        self.entry_it = None
+
+locks = {}
+
+next_key = 1
+
+def get_key():
+    global next_key
+    key = next_key
+    next_key = 1 if next_key == 0x7fffffff else (next_key + 1)
+    while key in locks:
+        key += 1
+    return key
+
+def find_path(key, name):
+    i = name.find(':')
+    if i != -1:
+        vol = name[:i].lower()
+        if vol == '' or vol == 'pi0' or vol == 'pidisk':
+            key = 0
+        name = name[i + 1:]
+
+    if key == 0:
+        cp = ()
+    else:
+        cp = locks[key].path
+
+    while name:
+        i = name.find('/')
+        if i == -1:
+            comp = name
+            name = ''
+        else:
+            comp = name[:i]
+            name = name[i + 1:]
+
+        if len(comp) == 0:
+            if len(cp) == 0:
+                return None
+            cp = cp[:-1]
+        else:
+            p = '.' if len(cp) == 0 else '/'.join(cp)
+            entries = os.listdir(p)
+            found = False
+            for e in entries:
+                if comp.lower() == e.lower():
+                    cp = cp + (e,)
+                    found = True
+                    break
+            if not found:
+                if len(name) == 0:
+                    cp = cp + (comp,)
+                else:
+                    return None
+
+    return cp
+
+def read_metadata(path):
+    protection = 0
+    comment = ''
+
+    if not os.path.isfile(path + METAFILE_EXTENSION):
+        return (protection, comment)
+
+    try:
+        f = open(path + METAFILE_EXTENSION, 'r')
+        for line in f:
+            if line[0] == 'p':
+                try:
+                    protection = int(line[1:].strip())
+                except ValueError:
+                    pass
+            elif line[0] == 'c':
+                comment = line[1:].strip()[:79]
+        f.close()
+    except FileNotFoundError:
+        pass
+    return (protection, comment)
+
+def write_metadata(path, protection=None, comment=None):
+    p, c = read_metadata(path)
+
+    if protection == None:
+        protection = p
+    if comment == None:
+        comment = c
+
+    if (p, c) == (protection, comment):
+        return True
+
+    try:
+        f = open(path + METAFILE_EXTENSION, 'w')
+        f.write('p' + str(protection) + '\n')
+        f.write('c' + comment + '\n')
+        f.close()
+    except FileNotFoundError as e:
+        logger.warning('Failed to write metadata for file %s: %s', path, e)
+        return False
+    return True
+
+def process_locate_object(key, mode, name):
+    logger.debug('ACTION_LOCATE_OBJECT, key: %s, mode: %s, name: %s', key, mode, name)
+
+    cp = find_path(key, name)
+
+    if cp is None or not (len(cp) == 0 or os.path.exists('/'.join(cp))):
+        return struct.pack('>HH', 0, ERROR_OBJECT_NOT_FOUND)
+
+    # TODO: Must check if there is already a lock for this path,
+    # and if so, if the locks are compatible.
+
+    key = get_key()
+    locks[key] = ObjectLock(key, mode, cp)
+    return struct.pack('>HHI', 1, 0, key)
+
+def process_free_lock(key):
+    logger.debug('ACTION_FREE_LOCK, key: %s', key)
+    if key in locks:
+        del locks[key]
+    return struct.pack('>HH', 1, 0)
+
+def process_copy_dir(prev_key):
+    logger.debug('ACTION_COPY_DIR, prev_key: %s', prev_key)
+    ol = locks[prev_key]
+    key = get_key()
+    locks[key] = ObjectLock(key, ol.mode, ol.path)
+    return struct.pack('>HHI', 1, 0, key)
+
+def process_parent(prev_key):
+    logger.debug('ACTION_PARENT, prev_key: %s', prev_key)
+    ol = locks[prev_key]
+    if len(ol.path) == 0:
+        key = 0
+    else:
+        key = get_key()
+        locks[key] = ObjectLock(key, SHARED_LOCK, ol.path[:-1])
+    return struct.pack('>HHI', 1, 0, key)
+
+def mtime_to_dmt(mtime):
+    mtime = int(mtime)
+    days = mtime // 86400
+    left = mtime - days * 86400
+    mins = left // 60
+    secs = left - mins * 60
+    ticks = secs * 50
+    days -= 2922 # Days between 1970-01-01 and 1978-01-01
+    days = max(0, days) # If days are before Amiga epoc
+    return (days, mins, ticks)
+
+def process_examine_object(key):
+    logger.debug('ACTION_EXAMINE_OBJECT, key: %s', key)
+    ol = locks[key]
+
+    if len(ol.path) == 0:
+        fn = 'PiDisk'
+        path = '.'
+    else:
+        fn = ol.path[-1]
+        path = '/'.join(ol.path)
+
+    days, mins, ticks = mtime_to_dmt(os.path.getmtime(path))
+    protection, comment = read_metadata(path)
+
+    if os.path.isfile(path):
+        size = os.path.getsize(path)
+        type_ = ST_FILE
+    else:
+        size = 0
+        type_ = ST_USERDIR
+        ol.entry_it = os.scandir(path)
+
+    size = min(size, 2 ** 31 - 1)
+    fn = (chr(len(fn)) + fn).encode('latin-1', 'ignore')
+    comment = (chr(len(comment)) + comment).encode('latin-1', 'ignore')
+    return struct.pack('>HHHhIIIII', 1, 0, 666, type_, size, protection, days, mins, ticks) + fn + comment
+
+def process_examine_next(key, disk_key):
+    logger.debug('ACTION_EXAMINE_NEXT, key: %s, disk_key: %s', key, disk_key)
+    ol = locks[key]
+
+    if len(ol.path) == 0:
+        path = '.'
+    else:
+        path = '/'.join(ol.path)
+
+    if not os.path.isdir(path):
+        return struct.pack('>HH', 0, ERROR_OBJECT_WRONG_TYPE)
+
+    disk_key += 1
+
+    entry = next(ol.entry_it, None)
+    while entry and entry.name.endswith(METAFILE_EXTENSION):
+        entry = next(ol.entry_it, None)
+
+    if not entry:
+        return struct.pack('>HH', 0, ERROR_NO_MORE_ENTRIES)
+
+    fn = entry.name
+    path = ('/'.join(ol.path + (fn,)))
+
+    days, mins, ticks = mtime_to_dmt(entry.stat().st_mtime)
+    protection, comment = read_metadata(path)
+
+    if os.path.isfile(path):
+        size = os.path.getsize(path)
+        type_ = ST_FILE
+    else:
+        size = 0
+        type_ = ST_USERDIR
+
+    size = min(size, 2 ** 31 - 1)
+    fn = (chr(len(fn)) + fn).encode('latin-1', 'ignore')
+    comment = (chr(len(comment)) + comment).encode('latin-1', 'ignore')
+    return struct.pack('>HHHhIIIII', 1, 0, disk_key, type_, size, protection, days, mins, ticks) + fn + comment
+
+def process_examine_fh(arg1):
+    logger.debug('ACTION_EXAMINE_FH, arg1: %s', arg1)
+
+    fn = open_file_handles[arg1].f.name
+    path = os.path.realpath(fn)
+    days, mins, ticks = mtime_to_dmt(os.path.getmtime(path))
+    protection, comment = read_metadata(path)
+
+    if os.path.isfile(path):
+        size = os.path.getsize(path)
+        type_ = ST_FILE
+    else:
+        size = 0
+        type_ = ST_USERDIR
+
+    size = min(size, 2 ** 31 - 1)
+    fn = (chr(len(fn)) + fn).encode('latin-1', 'ignore')
+    comment = (chr(len(comment)) + comment).encode('latin-1', 'ignore')
+    return struct.pack('>HHHhIIIII', 1, 0, 666, type_, size, protection, days, mins, ticks) + fn + comment
+
+
+next_fp = 1
+
+open_file_handles = {}
+
+def get_file_ptr():
+    global next_fp
+    fp = next_fp
+    next_fp = 1 if next_fp == 0x7fffffff else next_fp + 1
+    while fp in open_file_handles:
+        fp += 1
+    return fp
+
+class OpenFileHandle(object):
+    def __init__(self, fp, f, p):
+        self.fp = fp
+        self.f = f
+        self.p = p
+
+def process_findxxx(mode, key, name):
+    if mode == ACTION_FINDINPUT:
+        logger.debug('ACTION_FINDINPUT, key: %s, name: %s', key, name)
+    elif mode == ACTION_FINDOUTPUT:
+        logger.debug('ACTION_FINDOUTPUT, key: %s, name: %s', key, name)
+    elif mode == ACTION_FINDUPDATE:
+        logger.debug('ACTION_FINDUPDATE, key: %s, name: %s', key, name)
+
+    cp = find_path(key, name)
+    if cp is None:
+        return struct.pack('>HH', 0, ERROR_OBJECT_NOT_FOUND)
+
+    path = '/'.join(cp)
+    if len(cp) == 0 or os.path.isdir(path):
+        return struct.pack('>HH', 0, ERROR_OBJECT_WRONG_TYPE)
+
+    # TODO: Must check if there already exists a non-compatible lock for this path.
+
+    # TODO: This must be handled better. Especially error reporting.
+
+    protection, _ = read_metadata(path)
+    try:
+        if mode == MODE_OLDFILE:
+            f = open(path, 'r+b')
+        elif mode == MODE_READWRITE:
+            f = open(path, 'r+b')
+            if protection & 16:
+                protection = protection & 0b11101111
+                write_metadata(path, protection=protection)
+        elif mode == MODE_NEWFILE:
+            if protection & 0x1:
+                return struct.pack('>HH', 0, ERROR_DELETE_PROTECTED)
+            elif protection & 0x4:
+                return struct.pack('>HH', 0, ERROR_WRITE_PROTECTED)
+            f = open(path, 'w+b')
+            if protection & 16:
+                protection = protection & 0b11101111
+                write_metadata(path, protection=protection)
+    except IOError:
+        if mode == MODE_READWRITE:
+            try:
+                f = open(path, 'w+b')
+                if protection & 16:
+                    protection = protection & 0b11101111
+                    write_metadata(path, protection=protection)
+            except IOError:
+                return struct.pack('>HH', 0, ERROR_OBJECT_NOT_FOUND)
+        else:
+            return struct.pack('>HH', 0, ERROR_OBJECT_NOT_FOUND)
+
+    fp = get_file_ptr()
+    ofh = OpenFileHandle(fp, f, protection)
+    open_file_handles[fp] = ofh
+
+    return struct.pack('>HHI', 1, 0, fp)
+
+def process_read(arg1, address, length):
+    logger.debug('ACTION_READ, arg1: %s, address: %s, length: %s', arg1, address, length)
+    protection = open_file_handles[arg1].p
+    if protection & 0x8:
+        return struct.pack('>HH', 0, ERROR_READ_PROTECTED)
+    f = open_file_handles[arg1].f
+    data = f.read(length)
+    if len(data) != 0:
+        write_mem(address, data)
+    return struct.pack('>HHI', 1, 0, len(data))
+
+def process_write(arg1, address, length):
+    logger.debug('ACTION_WRITE, arg1: %s, address: %s, length: %s', arg1, address, length)
+    protection = open_file_handles[arg1].p
+    if protection & 0x4:
+        return struct.pack('>HH', 0, ERROR_WRITE_PROTECTED)
+    data = read_mem(address, length)
+    f = open_file_handles[arg1].f
+    f.seek(0, 1)
+    try:
+        f.write(data)
+    except IOError:
+        return struct.pack('>HH', 0, ERROR_DISK_FULL)
+    return struct.pack('>HHI', 1, 0, length)
+
+def process_seek(arg1, new_pos, mode):
+    logger.debug('ACTION_SEEK, arg1: %s, new_pos: %s, mode: %s', arg1, new_pos, mode)
+
+    f = open_file_handles[arg1].f
+    old_pos = f.tell()
+
+    from_what = 0
+    if mode == OFFSET_CURRENT:
+        from_what = 1
+    elif mode == OFFSET_END:
+        from_what = 2
+
+    f.seek(new_pos, from_what)
+
+    return struct.pack('>HHi', 1, 0, old_pos)
+
+def process_end(arg1):
+    logger.debug('ACTION_END, arg1: %s', arg1)
+
+    if arg1 in open_file_handles:
+        f = open_file_handles.pop(arg1).f
+        f.close()
+
+    return struct.pack('>HH', 1, 0)
+
+def process_delete_object(key, name):
+    logger.debug('ACTION_DELETE_OBJECT, key: %s, name: %s', key, name)
+
+    cp = find_path(key, name)
+    if cp is None or len(cp) == 0:
+        return struct.pack('>HH', 0, ERROR_OBJECT_NOT_FOUND)
+
+    path = '/'.join(cp)
+    is_dir = os.path.isdir(path)
+
+    protection, _ = read_metadata(path)
+    if protection & 0x1:
+        return struct.pack('>HH', 0, ERROR_DELETE_PROTECTED)
+
+    try:
+        if is_dir:
+            os.rmdir(path)
+        else:
+            os.remove(path)
+        if os.path.isfile(path + METAFILE_EXTENSION):
+            os.remove(path + METAFILE_EXTENSION)
+    except:
+        if is_dir:
+            return struct.pack('>HH', 0, ERROR_DIRECTORY_NOT_EMPTY)
+        else:
+            return struct.pack('>HH', 0, ERROR_OBJECT_NOT_FOUND)
+
+    return struct.pack('>HH', 1, 0)
+
+def process_rename_object(key, name, target_dir, new_name):
+    logger.debug('ACTION_RENAME_OBJECT, key: %s, name: %s, target_dir: %s, new_name: %s', key, name, target_dir, new_name)
+
+    cp1 = find_path(key, name)
+    if cp1 is None or len(cp1) == 0:
+        return struct.pack('>HH', 0, ERROR_OBJECT_NOT_FOUND)
+
+    from_path = '/'.join(cp1)
+    if not os.path.exists(from_path):
+        return struct.pack('>HH', 0, ERROR_OBJECT_NOT_FOUND)
+
+    cp2 = find_path(target_dir, new_name)
+    if cp2 is None or len(cp2) == 0:
+        return struct.pack('>HH', 0, ERROR_OBJECT_NOT_FOUND)
+
+    to_path = '/'.join(cp2)
+
+    if from_path == to_path:
+        return struct.pack('>HH', 1, 0)
+
+    if os.path.exists(to_path):
+        return struct.pack('>HH', 0, ERROR_OBJECT_EXISTS)
+
+    try:
+        os.rename(from_path, to_path)
+        if os.path.isfile(from_path + METAFILE_EXTENSION):
+            os.rename(from_path + METAFILE_EXTENSION, to_path + METAFILE_EXTENSION)
+    except:
+        return struct.pack('>HH', 0, ERROR_OBJECT_NOT_FOUND)
+
+    return struct.pack('>HH', 1, 0)
+
+def process_create_dir(key, name):
+    logger.debug('ACTION_CREATE_DIR, key: %s, name: %s', key, name)
+
+    cp = find_path(key, name)
+    if cp is None or len(cp) == 0:
+        return struct.pack('>HH', 0, ERROR_OBJECT_NOT_FOUND)
+
+    try:
+        path = '/'.join(cp)
+        os.makedirs(path)
+    except:
+        return struct.pack('>HH', 0, ERROR_OBJECT_NOT_FOUND)
+
+    key = get_key()
+    locks[key] = ObjectLock(key, SHARED_LOCK, cp)
+    return struct.pack('>HHI', 1, 0, key)
+
+def process_set_protect(key, name, mask):
+    logger.debug('ACTION_SET_PROTECT, key: %s, name: %s, mask: %s', key, name, mask)
+
+    cp = find_path(key, name)
+    if cp is None or len(cp) == 0:
+        return struct.pack('>HH', 0, ERROR_OBJECT_NOT_FOUND)
+
+    path = '/'.join(cp)
+    if write_metadata(path, protection=mask):
+        return struct.pack('>HH', 1, 0)
+    else:
+        return struct.pack('>HH', 0, ERROR_OBJECT_NOT_FOUND)
+
+def process_set_comment(key, name, comment):
+    logger.debug('ACTION_SET_COMMENT, key: %s, name: %s, comment: %s', key, name, comment)
+
+    if len(comment) > 79:
+        return struct.pack('>HH', 0, ERROR_COMMENT_TOO_BIG)
+
+    cp = find_path(key, name)
+    if cp is None or len(cp) == 0:
+        return struct.pack('>HH', 0, ERROR_OBJECT_NOT_FOUND)
+
+    path = '/'.join(cp)
+    if write_metadata(path, comment=comment):
+        return struct.pack('>HH', 1, 0)
+    else:
+        return struct.pack('>HH', 0, ERROR_OBJECT_NOT_FOUND)
+
+def process_same_lock(key1, key2):
+    logger.debug('ACTION_SAME_LOCK, key1: %s key2: %s', key1, key2)
+
+    if not (key1 in locks and key2 in locks):
+        return struct.pack('>HH', 0, LOCK_DIFFERENT)
+    elif locks[key1].path == locks[key2].path:
+        return struct.pack('>HH', 1, LOCK_SAME)
+    else:
+        return struct.pack('>HH', 0, LOCK_SAME_VOLUME)
+
+def process_request(req):
+    #logger.debug('len(req): %s, req: %s', len(req), list(req))
+
+    (rtype,) = struct.unpack('>H', req[:2])
+
+    if rtype == ACTION_LOCATE_OBJECT:
+        key, mode, nlen = struct.unpack('>IHB', req[2:9])
+        name = req[9:9+nlen].decode('latin-1')
+        return process_locate_object(key, mode, name)
+    elif rtype == ACTION_FREE_LOCK:
+        (key,) = struct.unpack('>I', req[2:6])
+        return process_free_lock(key)
+    elif rtype == ACTION_COPY_DIR:
+        (key,) = struct.unpack('>I', req[2:6])
+        return process_copy_dir(key)
+    elif rtype == ACTION_PARENT:
+        (key,) = struct.unpack('>I', req[2:6])
+        return process_parent(key)
+    elif rtype == ACTION_EXAMINE_OBJECT:
+        (key,) = struct.unpack('>I', req[2:6])
+        return process_examine_object(key)
+    elif rtype == ACTION_EXAMINE_NEXT:
+        key, disk_key = struct.unpack('>IH', req[2:8])
+        return process_examine_next(key, disk_key)
+    elif rtype == ACTION_EXAMINE_FH:
+        (arg1,) = struct.unpack('>I', req[2:6])
+        return process_examine_fh(arg1)
+    elif rtype == ACTION_FINDINPUT or rtype == ACTION_FINDOUTPUT or rtype == ACTION_FINDUPDATE:
+        key, nlen = struct.unpack('>IB', req[2:7])
+        name = req[7:7+nlen].decode('latin-1')
+        return process_findxxx(rtype, key, name)
+    elif rtype == ACTION_READ:
+        arg1, address, length = struct.unpack('>III', req[2:14])
+        return process_read(arg1, address, length)
+    elif rtype == ACTION_WRITE:
+        arg1, address, length = struct.unpack('>III', req[2:14])
+        return process_write(arg1, address, length)
+    elif rtype == ACTION_SEEK:
+        arg1, new_pos, mode = struct.unpack('>Iii', req[2:14])
+        return process_seek(arg1, new_pos, mode)
+    elif rtype == ACTION_END:
+        (arg1,) = struct.unpack('>I', req[2:6])
+        return process_end(arg1)
+    elif rtype == ACTION_DELETE_OBJECT:
+        key, nlen = struct.unpack('>IB', req[2:7])
+        name = req[7:7+nlen].decode('latin-1')
+        return process_delete_object(key, name)
+    elif rtype == ACTION_RENAME_OBJECT:
+        key, target_dir, nlen, nnlen = struct.unpack('>IIBB', req[2:12])
+        name = req[12:12+nlen].decode('latin-1')
+        new_name = req[12+nlen:12+nlen+nnlen].decode('latin-1')
+        return process_rename_object(key, name, target_dir, new_name)
+    elif rtype == ACTION_CREATE_DIR:
+        key, nlen = struct.unpack('>IB', req[2:7])
+        name = req[7:7+nlen].decode('latin-1')
+        return process_create_dir(key, name)
+    elif rtype == ACTION_SET_PROTECT:
+        key, mask, nlen = struct.unpack('>IIB', req[2:11])
+        name = req[11:11+nlen].decode('latin-1')
+        return process_set_protect(key, name, mask)
+    elif rtype == ACTION_SET_COMMENT:
+        key, nlen, clen = struct.unpack('>IBB', req[2:8])
+        name = req[8:8+nlen].decode('latin-1')
+        comment = req[8+nlen:8+nlen+clen].decode('latin-1')
+        return process_set_comment(key, name, comment)
+    elif rtype == ACTION_SAME_LOCK:
+        key1, key2 = struct.unpack('>II', req[2:10])
+        return process_same_lock(key1, key2)
+    elif rtype == ACTION_UNSUPPORTED:
+        (dp_Type,) = struct.unpack('>H', req[2:4])
+        logger.warning('Unsupported action %d (Amiga/a314fs)', dp_Type)
+        return struct.pack('>HH', 0, ERROR_ACTION_NOT_KNOWN)
+    else:
+        logger.warning('Unsupported action %d (a314d/a314fs)', rtype)
+        return struct.pack('>HH', 0, ERROR_ACTION_NOT_KNOWN)
+
+done = False
+
+try:
+    idx = sys.argv.index('-ondemand')
+except ValueError:
+    idx = -1
+
+if idx != -1:
+    fd = int(sys.argv[idx + 1])
+    drv = socket.socket(fileno=fd)
+else:
+    drv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    drv.connect(('localhost', 7110))
+
+    send_register_req(b'a314fs')
+    stream_id, ptype, payload = wait_for_msg()
+    if payload[0] != 1:
+        logger.error('Unable to register service a314fs, shutting down')
+        drv.close()
+        done = True
+
+if not done:
+    os.chdir(SHARED_DIRECTORY)
+    logger.info('a314fs is running, shared directory: %s', SHARED_DIRECTORY)
+
+while not done:
+    stream_id, ptype, payload = wait_for_msg()
+
+    if ptype == MSG_CONNECT:
+        if payload == b'a314fs':
+            if current_stream_id is not None:
+                send_reset(current_stream_id)
+            current_stream_id = stream_id
+            send_connect_response(stream_id, 0)
+        else:
+            send_connect_response(stream_id, 3)
+    elif ptype == MSG_DATA:
+        address, length = struct.unpack('>II', payload)
+        #logger.debug('address: %s, length: %s', address, length)
+        req = read_mem(address + 2, length - 2)
+        res = process_request(req)
+        write_mem(address + 2, res)
+        #write_mem(address, b'\xff\xff')
+        send_data(stream_id, b'\xff\xff')
+    elif ptype == MSG_EOS:
+        if stream_id == current_stream_id:
+            logger.debug('Got EOS, stream closed')
+            send_eos(stream_id)
+            current_stream_id = None
+    elif ptype == MSG_RESET:
+        if stream_id == current_stream_id:
+            logger.debug('Got RESET, stream closed')
+            current_stream_id = None
index 1d51c6af32cb913ee145819543e58a831e363335..151aa9d1b07fbdf49b2e4c03fe2c1f9f2b9b580e 100644 (file)
Binary files a/a314/software-amiga/a314fs and b/a314/software-amiga/a314fs differ
diff --git a/a314/software-amiga/a314fs-mountlist b/a314/software-amiga/a314fs-mountlist
new file mode 100644 (file)
index 0000000..4f8e20c
--- /dev/null
@@ -0,0 +1,12 @@
+PI0:    FileSystem      = l:a314fs
+        Device          = a314.device
+        Unit            = 0
+        Surfaces        = 1
+        BlocksPerTrack  = 10
+        Reserved        = 2
+        LowCyl          = 0
+        HighCyl         = 100
+        StackSize       = 1000
+        Mount           = 1
+        DosType         = 0x33313400
+#
diff --git a/a314/software-amiga/a314fs_pistorm/a314fs.c b/a314/software-amiga/a314fs_pistorm/a314fs.c
new file mode 100644 (file)
index 0000000..b07e801
--- /dev/null
@@ -0,0 +1,1305 @@
+/*
+ * Copyright (c) 2018-2021 Niklas Ekström
+ */
+
+#include <exec/types.h>
+#include <exec/ports.h>
+#include <exec/nodes.h>
+#include <exec/libraries.h>
+
+#include <devices/timer.h>
+
+#include <libraries/dos.h>
+#include <libraries/dosextens.h>
+#include <libraries/filehandler.h>
+
+#include <proto/exec.h>
+#include <proto/dos.h>
+
+#include <string.h>
+#include <stdarg.h>
+
+#include "../../a314device/a314.h"
+#include "../../a314device/proto_a314.h"
+
+#include "messages.h"
+
+#define MKBADDRU(x) (((ULONG)x) >> 2)
+
+#define ID_314_DISK (('3' << 24) | ('1' << 16) | ('4' << 8))
+
+#define REQ_RES_BUF_SIZE 256
+#define BUFFER_SIZE 4096
+
+// Not defined if using NDK13
+#ifndef ACTION_EXAMINE_FH
+#define ACTION_EXAMINE_FH 1034
+#endif
+
+#ifndef ACTION_SAME_LOCK
+#define ACTION_SAME_LOCK 40
+#endif
+
+// Grab a reserved action type which seems to be unused
+#define ACTION_UNSUPPORTED 65535
+
+struct ExecBase *SysBase;
+struct DosLibrary *DOSBase;
+struct MsgPort *mp;
+
+struct DeviceList *my_volume;
+char default_volume_name[] = "\006PiDisk";
+
+char device_name[32]; // "\004PI0:"
+
+struct MsgPort *timer_mp;
+struct timerequest *tr;
+
+struct MsgPort *a314_mp;
+struct A314_IORequest *a314_ior;
+
+struct Library *A314Base;
+
+long socket;
+
+// These are allocated in A314 memory.
+char *request_buffer = NULL;
+char *data_buffer = NULL;
+
+void MyNewList(struct List *l)
+{
+    l->lh_Head = (struct Node *)&(l->lh_Tail);
+    l->lh_Tail = NULL;
+    l->lh_TailPred = (struct Node *)&(l->lh_Head);
+}
+
+struct MsgPort *MyCreatePort(char *name, long pri)
+{
+       int sigbit = AllocSignal(-1);
+       if (sigbit == -1)
+               return NULL;
+
+       struct MsgPort *port = (struct MsgPort *)AllocMem(sizeof(struct MsgPort), MEMF_PUBLIC | MEMF_CLEAR);
+       if (!port)
+       {
+               FreeSignal(sigbit);
+               return NULL;
+       }
+
+       port->mp_Node.ln_Name = name;
+       port->mp_Node.ln_Pri = pri;
+       port->mp_Node.ln_Type = NT_MSGPORT;
+       port->mp_Flags = PA_SIGNAL;
+       port->mp_SigBit = sigbit;
+       port->mp_SigTask = FindTask(NULL);
+
+       if (name)
+               AddPort(port);
+       else
+               MyNewList(&(port->mp_MsgList));
+
+       return port;
+}
+
+struct IORequest *MyCreateExtIO(struct MsgPort *ioReplyPort, ULONG size)
+{
+       if (!ioReplyPort)
+               return NULL;
+
+       struct IORequest *ioReq = (struct IORequest *)AllocMem(size, MEMF_PUBLIC | MEMF_CLEAR);
+       if (!ioReq)
+               return NULL;
+
+       ioReq->io_Message.mn_Node.ln_Type = NT_MESSAGE;
+       ioReq->io_Message.mn_Length = size;
+       ioReq->io_Message.mn_ReplyPort = ioReplyPort;
+
+       return ioReq;
+}
+
+#define DEBUG 0
+
+#if !DEBUG
+void dbg_init()
+{
+}
+
+void dbg(const char* fmt, ...)
+{
+}
+#else
+struct FileHandle *dbg_log;
+struct MsgPort *dbg_replyport;
+struct StandardPacket *dbg_sp;
+char dbg_buf[256];
+
+void dbg_init()
+{
+       dbg_log = (struct FileHandle *)BADDR(Open("CON:200/0/440/256/a314fs", MODE_NEWFILE));
+       dbg_replyport = MyCreatePort(NULL, 0);
+       dbg_sp = (struct StandardPacket *)AllocMem(sizeof(struct StandardPacket), MEMF_PUBLIC | MEMF_CLEAR);
+}
+
+void dbg_out(int l)
+{
+       dbg_sp->sp_Msg.mn_Node.ln_Name = (char *)&(dbg_sp->sp_Pkt);
+       dbg_sp->sp_Pkt.dp_Link = &(dbg_sp->sp_Msg);
+       dbg_sp->sp_Pkt.dp_Port = dbg_replyport;
+       dbg_sp->sp_Pkt.dp_Type = ACTION_WRITE;
+       dbg_sp->sp_Pkt.dp_Arg1 = (long)dbg_log->fh_Arg1;
+       dbg_sp->sp_Pkt.dp_Arg2 = (long)dbg_buf;
+       dbg_sp->sp_Pkt.dp_Arg3 = (long)l - 1;
+
+       PutMsg((struct MsgPort *)dbg_log->fh_Type, (struct Message *)dbg_sp);
+       WaitPort(dbg_replyport);
+       GetMsg(dbg_replyport);
+}
+
+void dbg(const char* fmt, ...)
+{
+       char numbuf[16];
+
+       const char *p = fmt;
+       char *q = dbg_buf;
+
+       va_list args;
+       va_start(args, fmt);
+       while (*p != 0)
+       {
+               char c = *p++;
+               if (c == '$')
+               {
+                       c = *p++;
+                       if (c == 'b')
+                       {
+                               UBYTE x = va_arg(args, UBYTE);
+                               *q++ = '$';
+                               for (int i = 0; i < 2; i++)
+                               {
+                                       int ni = (x >> ((1 - i) * 4)) & 0xf;
+                                       *q++ = (ni >= 10) ? ('a' + (ni - 10)) : ('0' + ni);
+                               }
+                       }
+                       else if (c == 'w')
+                       {
+                               UWORD x = va_arg(args, UWORD);
+                               *q++ = '$';
+                               for (int i = 0; i < 4; i++)
+                               {
+                                       int ni = (x >> ((3 - i) * 4)) & 0xf;
+                                       *q++ = (ni >= 10) ? ('a' + (ni - 10)) : ('0' + ni);
+                               }
+                       }
+                       else if (c == 'l')
+                       {
+                               ULONG x = va_arg(args, ULONG);
+                               *q++ = '$';
+                               for (int i = 0; i < 8; i++)
+                               {
+                                       int ni = (x >> ((7 - i) * 4)) & 0xf;
+                                       *q++ = (ni >= 10) ? ('a' + (ni - 10)) : ('0' + ni);
+                               }
+                       }
+                       else if (c == 'S')
+                       {
+                               unsigned char *s = (unsigned char *)va_arg(args, ULONG);
+                               int l = *s++;
+                               for (int i = 0; i < l; i++)
+                                       *q++ = *s++;
+                       }
+                       else if (c == 's')
+                       {
+                               unsigned char *s = (unsigned char *)va_arg(args, ULONG);
+                               while (*s)
+                                       *q++ = *s++;
+                               *q++ = 0;
+                       }
+               }
+               else
+               {
+                       *q++ = c;
+               }
+       }
+       *q++ = 0;
+
+       va_end(args);
+
+       dbg_out(q - dbg_buf);
+}
+#endif
+
+char *DosAllocMem(int len)
+{
+       long *p = (long *)AllocMem(len + 4, MEMF_PUBLIC | MEMF_CLEAR);
+       *p++ = len;
+       return((char *)p);
+}
+
+void DosFreeMem(char *p)
+{
+       long *lp = (long *)p;
+       long len = *--lp;
+       FreeMem((char *)lp, len);
+}
+
+void reply_packet(struct DosPacket *dp)
+{
+       struct MsgPort *reply_port = dp->dp_Port;
+       dp->dp_Port = mp;
+       PutMsg(reply_port, dp->dp_Link);
+}
+
+// Routines for talking to the A314 driver.
+LONG a314_cmd_wait(UWORD command, char *buffer, int length)
+{
+       a314_ior->a314_Request.io_Command = command;
+       a314_ior->a314_Request.io_Error = 0;
+       a314_ior->a314_Socket = socket;
+       a314_ior->a314_Buffer = buffer;
+       a314_ior->a314_Length = length;
+       return DoIO((struct IORequest *)a314_ior);
+}
+
+LONG a314_connect(char *name)
+{
+       struct DateStamp ds;
+       DateStamp(&ds);
+       socket = ds.ds_Tick;
+       return a314_cmd_wait(A314_CONNECT, name, strlen(name));
+}
+
+LONG a314_read(char *buf, int length)
+{
+       return a314_cmd_wait(A314_READ, buf, length);
+}
+
+LONG a314_write(char *buf, int length)
+{
+       return a314_cmd_wait(A314_WRITE, buf, length);
+}
+
+LONG a314_eos()
+{
+       return a314_cmd_wait(A314_EOS, NULL, 0);
+}
+
+LONG a314_reset()
+{
+       return a314_cmd_wait(A314_RESET, NULL, 0);
+}
+
+void create_and_add_volume()
+{
+       my_volume = (struct DeviceList *)DosAllocMem(sizeof(struct DeviceList));
+       my_volume->dl_Name = MKBADDRU(default_volume_name);
+       my_volume->dl_Type = DLT_VOLUME;
+       my_volume->dl_Task = mp;
+       my_volume->dl_DiskType = ID_314_DISK;
+       DateStamp(&my_volume->dl_VolumeDate);
+
+       struct RootNode *root = (struct RootNode *)DOSBase->dl_Root;
+       struct DosInfo *info = (struct DosInfo *)BADDR(root->rn_Info);
+
+       Forbid();
+       my_volume->dl_Next = info->di_DevInfo;
+       info->di_DevInfo = MKBADDRU(my_volume);
+       Permit();
+}
+
+void startup_fs_handler(struct DosPacket *dp)
+{
+       unsigned char *name = (unsigned char *)BADDR(dp->dp_Arg1);
+       struct FileSysStartupMsg *fssm = (struct FileSysStartupMsg *)BADDR(dp->dp_Arg2);
+       struct DeviceNode *node = (struct DeviceNode *)BADDR(dp->dp_Arg3);
+
+       memcpy(device_name, name, *name + 1);
+       device_name[*name + 1] = 0;
+
+       node->dn_Task = mp;
+
+       dp->dp_Res1 = DOSTRUE;
+       dp->dp_Res2 = 0;
+       reply_packet(dp);
+
+       dbg_init();
+
+       dbg("ACTION_STARTUP\n");
+       dbg("  device_name = $S\n", (ULONG)device_name);
+       dbg("  fssm = $l\n", (ULONG)fssm);
+       dbg("  node = $l\n", (ULONG)node);
+
+       timer_mp = MyCreatePort(NULL, 0);
+       tr = (struct timerequest *)MyCreateExtIO(timer_mp, sizeof(struct timerequest));
+       if (OpenDevice(TIMERNAME, UNIT_MICROHZ, (struct IORequest *)tr, 0) != 0)
+       {
+               // If this happens, there's nothing we can do.
+               // For now, assume this does not happen.
+               dbg("Fatal error: unable to open timer.device\n");
+               return;
+       }
+
+       a314_mp = MyCreatePort(NULL, 0);
+       a314_ior = (struct A314_IORequest *)MyCreateExtIO(a314_mp, sizeof(struct A314_IORequest));
+       if (OpenDevice(A314_NAME, 0, (struct IORequest *)a314_ior, 0) != 0)
+       {
+               // If this fails, there's nothing we can do.
+               // For now, assume this does not happen.
+               dbg("Fatal error: unable to open a314.device\n");
+               return;
+       }
+
+       A314Base = &(a314_ior->a314_Request.io_Device->dd_Library);
+
+       if (a314_connect("a314fs") != A314_CONNECT_OK)
+       {
+               dbg("Fatal error: unable to connect to a314fs on rasp\n");
+               // This COULD happen.
+               // If it DOES happen, we just wait for a bit and try again.
+
+               // TODO: Have to use timer.device to set a timer for ~2 seconds and then try connecting again.
+               return;
+       }
+
+       request_buffer = AllocMem(REQ_RES_BUF_SIZE, MEMF_FAST);
+       data_buffer = AllocMem(BUFFER_SIZE, MEMF_FAST);
+
+       // We can assume that we arrive here, and have a stream to the Pi side, to where we can transfer data.
+       create_and_add_volume();
+
+       dbg("Startup successful\n");
+
+       // If we end up having problems with the connections, treat the disc as ejected, and try inserting it again in two seconds.
+       // TODO: This is not currently handled.
+}
+
+void wait_for_response()
+{
+       for (unsigned long i = 0; 1; i++)
+       {
+               if (*request_buffer)
+               {
+                       dbg("--Got response after $l sleeps\n", i);
+                       return;
+               }
+
+               tr->tr_node.io_Command = TR_ADDREQUEST;
+               tr->tr_node.io_Message.mn_ReplyPort = timer_mp;
+               tr->tr_time.tv_secs = 0;
+               tr->tr_time.tv_micro = 1000;
+               DoIO((struct IORequest *)tr);
+       }
+}
+
+void write_req_and_wait_for_res(int len)
+{
+       ULONG buf[2] = {TranslateAddressA314(request_buffer), len};
+       a314_write((char *)&buf[0], 8);
+       //wait_for_response();
+       a314_read((char *)&buf[0], 8);
+}
+
+struct FileLock *create_and_add_file_lock(long key, long mode)
+{
+       struct FileLock *lock = (struct FileLock *)DosAllocMem(sizeof(struct FileLock));
+
+       lock->fl_Key = key;
+       lock->fl_Access = mode;
+       lock->fl_Task = mp;
+       lock->fl_Volume = MKBADDRU(my_volume);
+
+       Forbid();
+       lock->fl_Link = my_volume->dl_Lock;
+       my_volume->dl_Lock = MKBADDRU(lock);
+       Permit();
+
+       return lock;
+}
+
+void action_locate_object(struct DosPacket *dp)
+{
+       struct FileLock *parent = (struct FileLock *)BADDR(dp->dp_Arg1);
+       unsigned char *name = (unsigned char *)BADDR(dp->dp_Arg2);
+       long mode = dp->dp_Arg3;
+
+       dbg("ACTION_LOCATE_OBJECT\n");
+       dbg("  parent lock = $l\n", parent);
+       dbg("  name = $S\n", name);
+       dbg("  mode = $s\n", mode == SHARED_LOCK ? "SHARED_LOCK" : "EXCLUSIVE_LOCK");
+
+       struct LocateObjectRequest *req = (struct LocateObjectRequest *)request_buffer;
+       req->has_response = 0;
+       req->type = dp->dp_Type;
+       req->key = parent == NULL ? 0 : parent->fl_Key;
+       req->mode = mode;
+
+       int nlen = *name;
+       memcpy(req->name, name, nlen + 1);
+
+       write_req_and_wait_for_res(sizeof(struct LocateObjectRequest) + nlen);
+
+       struct LocateObjectResponse *res = (struct LocateObjectResponse *)request_buffer;
+       if (!res->success)
+       {
+               dbg("  Failed, error code $l\n", (LONG)res->error_code);
+               dp->dp_Res1 = DOSFALSE;
+               dp->dp_Res2 = res->error_code;
+       }
+       else
+       {
+               struct FileLock *lock = create_and_add_file_lock(res->key, mode);
+
+               dbg("  Returning lock $l\n", lock);
+               dp->dp_Res1 = MKBADDRU(lock);
+               dp->dp_Res2 = 0;
+       }
+
+       reply_packet(dp);
+}
+
+void action_free_lock(struct DosPacket *dp)
+{
+       ULONG arg1 = dp->dp_Arg1;
+       struct FileLock *lock = (struct FileLock *)BADDR(dp->dp_Arg1);
+
+       dbg("ACTION_FREE_LOCK\n");
+       dbg("  lock = $l\n", lock);
+
+       if (lock != NULL)
+       {
+               struct FreeLockRequest *req = (struct FreeLockRequest *)request_buffer;
+               req->has_response = 0;
+               req->type = dp->dp_Type;
+               req->key = lock->fl_Key;
+
+               write_req_and_wait_for_res(sizeof(struct FreeLockRequest));
+
+               // Ignore the response. Must succeed.
+               //struct FreeLockResponse *res = (struct FreeLockResponse *)request_buffer;
+
+               Forbid();
+               if (my_volume->dl_Lock == arg1)
+                       my_volume->dl_Lock = lock->fl_Link;
+               else
+               {
+                       struct FileLock *prev = (struct FileLock *)BADDR(my_volume->dl_Lock);
+                       while (prev->fl_Link != arg1)
+                               prev = (struct FileLock *)BADDR(prev->fl_Link);
+                       prev->fl_Link = lock->fl_Link;
+               }
+               Permit();
+               DosFreeMem((char *)lock);
+       }
+
+       dp->dp_Res1 = DOSTRUE;
+       dp->dp_Res2 = 0;
+       reply_packet(dp);
+}
+
+void action_copy_dir(struct DosPacket *dp)
+{
+       struct FileLock *parent = (struct FileLock *)BADDR(dp->dp_Arg1);
+
+       dbg("ACTION_COPY_DIR\n");
+       dbg("  lock to duplicate = $l\n", parent);
+
+       if (parent == NULL)
+       {
+               dp->dp_Res1 = 0;
+               dp->dp_Res2 = 0;
+       }
+       else
+       {
+               struct CopyDirRequest *req = (struct CopyDirRequest *)request_buffer;
+               req->has_response = 0;
+               req->type = dp->dp_Type;
+               req->key = parent->fl_Key;
+
+               write_req_and_wait_for_res(sizeof(struct CopyDirRequest));
+
+               struct CopyDirResponse *res = (struct CopyDirResponse *)request_buffer;
+               if (!res->success)
+               {
+                       dbg("  Failed, error code $l\n", (LONG)res->error_code);
+                       dp->dp_Res1 = DOSFALSE;
+                       dp->dp_Res2 = res->error_code;
+               }
+               else
+               {
+                       struct FileLock *lock = create_and_add_file_lock(res->key, parent->fl_Access);
+
+                       dbg("  Returning lock $l\n", lock);
+                       dp->dp_Res1 = MKBADDRU(lock);
+                       dp->dp_Res2 = 0;
+               }
+       }
+
+       reply_packet(dp);
+}
+
+void action_parent(struct DosPacket *dp)
+{
+       struct FileLock *prev_lock = (struct FileLock *)BADDR(dp->dp_Arg1);
+
+       dbg("ACTION_PARENT\n");
+       dbg("  lock = $l\n", prev_lock);
+
+       if (prev_lock == NULL)
+       {
+               dp->dp_Res1 = DOSFALSE;
+               dp->dp_Res2 = ERROR_INVALID_LOCK;
+       }
+       else
+       {
+               struct ParentRequest *req = (struct ParentRequest *)request_buffer;
+               req->has_response = 0;
+               req->type = dp->dp_Type;
+               req->key = prev_lock->fl_Key;
+
+               write_req_and_wait_for_res(sizeof(struct ParentRequest));
+
+               struct ParentResponse *res = (struct ParentResponse *)request_buffer;
+               if (!res->success)
+               {
+                       dbg("  Failed, error code $l\n", (LONG)res->error_code);
+                       dp->dp_Res1 = DOSFALSE;
+                       dp->dp_Res2 = res->error_code;
+               }
+               else if (res->key == 0)
+               {
+                       dp->dp_Res1 = 0;
+                       dp->dp_Res2 = 0;
+               }
+               else
+               {
+                       struct FileLock *lock = create_and_add_file_lock(res->key, SHARED_LOCK);
+
+                       dbg("  Returning lock $l\n", lock);
+                       dp->dp_Res1 = MKBADDRU(lock);
+                       dp->dp_Res2 = 0;
+               }
+       }
+
+       reply_packet(dp);
+}
+
+void action_examine_object(struct DosPacket *dp)
+{
+       struct FileLock *lock = (struct FileLock *)BADDR(dp->dp_Arg1);
+       struct FileInfoBlock *fib = (struct FileInfoBlock *)BADDR(dp->dp_Arg2);
+
+       dbg("ACTION_EXAMINE_OBJECT\n");
+       dbg("  lock = $l\n", lock);
+       dbg("  fib = $l\n", fib);
+
+       struct ExamineObjectRequest *req = (struct ExamineObjectRequest *)request_buffer;
+       req->has_response = 0;
+       req->type = dp->dp_Type;
+       req->key = lock == NULL ? 0 : lock->fl_Key;
+
+       write_req_and_wait_for_res(sizeof(struct ExamineObjectRequest));
+
+       struct ExamineObjectResponse *res = (struct ExamineObjectResponse *)request_buffer;
+       if (!res->success)
+       {
+               dbg("  Failed, error code $l\n", (LONG)res->error_code);
+               dp->dp_Res1 = DOSFALSE;
+               dp->dp_Res2 = res->error_code;
+       }
+       else
+       {
+               int nlen = (unsigned char)(res->data[0]);
+               memcpy(fib->fib_FileName, res->data, nlen + 1);
+               fib->fib_FileName[nlen + 1] = 0;
+
+               int clen = (unsigned char)(res->data[nlen + 1]);
+               memcpy(fib->fib_Comment, &(res->data[nlen + 1]), clen + 1);
+               fib->fib_Comment[clen + 1] = 0;
+
+               fib->fib_DiskKey = res->disk_key;
+               fib->fib_DirEntryType = res->entry_type;
+               fib->fib_EntryType = res->entry_type;
+               fib->fib_Protection = res->protection;
+               fib->fib_Size = res->size;
+               fib->fib_NumBlocks = (res->size + 511) >> 9;
+               fib->fib_Date.ds_Days = res->date[0];
+               fib->fib_Date.ds_Minute = res->date[1];
+               fib->fib_Date.ds_Tick = res->date[2];
+
+               dp->dp_Res1 = DOSTRUE;
+               dp->dp_Res2 = 0;
+       }
+
+       reply_packet(dp);
+}
+
+void action_examine_next(struct DosPacket *dp)
+{
+       struct FileLock *lock = (struct FileLock *)BADDR(dp->dp_Arg1);
+       struct FileInfoBlock *fib = (struct FileInfoBlock *)BADDR(dp->dp_Arg2);
+
+       dbg("ACTION_EXAMINE_NEXT\n");
+       dbg("  lock = $l\n", lock);
+       dbg("  fib = $l\n", fib);
+
+       struct ExamineNextRequest *req = (struct ExamineNextRequest *)request_buffer;
+       req->has_response = 0;
+       req->type = dp->dp_Type;
+       req->key = lock == NULL ? 0 : lock->fl_Key;
+       req->disk_key = fib->fib_DiskKey;
+
+       write_req_and_wait_for_res(sizeof(struct ExamineNextRequest));
+
+       struct ExamineNextResponse *res = (struct ExamineNextResponse *)request_buffer;
+       if (!res->success)
+       {
+               dbg("  Failed, error code $l\n", (LONG)res->error_code);
+               dp->dp_Res1 = DOSFALSE;
+               dp->dp_Res2 = res->error_code;
+       }
+       else
+       {
+               int nlen = (unsigned char)(res->data[0]);
+               memcpy(fib->fib_FileName, res->data, nlen + 1);
+               fib->fib_FileName[nlen + 1] = 0;
+
+               int clen = (unsigned char)(res->data[nlen + 1]);
+               memcpy(fib->fib_Comment, &(res->data[nlen + 1]), clen + 1);
+               fib->fib_Comment[clen + 1] = 0;
+
+               fib->fib_DiskKey = res->disk_key;
+               fib->fib_DirEntryType = res->entry_type;
+               fib->fib_EntryType = res->entry_type;
+               fib->fib_Protection = res->protection;
+               fib->fib_Size = res->size;
+               fib->fib_NumBlocks = (res->size + 511) >> 9;
+               fib->fib_Date.ds_Days = res->date[0];
+               fib->fib_Date.ds_Minute = res->date[1];
+               fib->fib_Date.ds_Tick = res->date[2];
+
+               dp->dp_Res1 = DOSTRUE;
+               dp->dp_Res2 = 0;
+       }
+
+       reply_packet(dp);
+}
+
+void action_examine_fh(struct DosPacket *dp)
+{
+       ULONG arg1 = dp->dp_Arg1;
+       struct FileInfoBlock *fib = (struct FileInfoBlock *)BADDR(dp->dp_Arg2);
+
+       dbg("ACTION_EXAMINE_FH\n");
+       dbg("  arg1 = $l\n", arg1);
+       dbg("  fib = $l\n", fib);
+
+       struct ExamineFhRequest *req = (struct ExamineFhRequest *)request_buffer;
+       req->has_response = 0;
+       req->type = dp->dp_Type;
+       req->arg1 = arg1;
+
+       write_req_and_wait_for_res(sizeof(struct ExamineFhRequest));
+
+       struct ExamineFhResponse *res = (struct ExamineFhResponse *)request_buffer;
+       if (!res->success)
+       {
+               dbg("  Failed, error code $l\n", (LONG)res->error_code);
+               dp->dp_Res1 = DOSFALSE;
+               dp->dp_Res2 = res->error_code;
+       }
+       else
+       {
+               int nlen = (unsigned char)(res->data[0]);
+               memcpy(fib->fib_FileName, res->data, nlen + 1);
+               fib->fib_FileName[nlen + 1] = 0;
+
+               int clen = (unsigned char)(res->data[nlen + 1]);
+               memcpy(fib->fib_Comment, &(res->data[nlen + 1]), clen + 1);
+               fib->fib_Comment[clen + 1] = 0;
+
+               fib->fib_DiskKey = res->disk_key;
+               fib->fib_DirEntryType = res->entry_type;
+               fib->fib_EntryType = res->entry_type;
+               fib->fib_Protection = res->protection;
+               fib->fib_Size = res->size;
+               fib->fib_NumBlocks = (res->size + 511) >> 9;
+               fib->fib_Date.ds_Days = res->date[0];
+               fib->fib_Date.ds_Minute = res->date[1];
+               fib->fib_Date.ds_Tick = res->date[2];
+
+               dp->dp_Res1 = DOSTRUE;
+               dp->dp_Res2 = 0;
+       }
+
+       reply_packet(dp);
+}
+
+void action_findxxx(struct DosPacket *dp)
+{
+       struct FileHandle *fh = (struct FileHandle *)BADDR(dp->dp_Arg1);
+       struct FileLock *lock = (struct FileLock *)BADDR(dp->dp_Arg2);
+       unsigned char *name = (unsigned char *)BADDR(dp->dp_Arg3);
+
+       if (dp->dp_Type == ACTION_FINDUPDATE)
+               dbg("ACTION_FINDUPDATE\n");
+       else if (dp->dp_Type == ACTION_FINDINPUT)
+               dbg("ACTION_FINDINPUT\n");
+       else if (dp->dp_Type == ACTION_FINDOUTPUT)
+               dbg("ACTION_FINDOUTPUT\n");
+
+       dbg("  file handle = $l\n", fh);
+       dbg("  lock = $l\n", lock);
+       dbg("  name = $S\n", name);
+
+       struct FindXxxRequest *req = (struct FindXxxRequest *)request_buffer;
+       req->has_response = 0;
+       req->type = dp->dp_Type;
+       req->key = lock == NULL ? 0 : lock->fl_Key;
+
+       int nlen = *name;
+       memcpy(req->name, name, nlen + 1);
+
+       write_req_and_wait_for_res(sizeof(struct FindXxxRequest) + nlen);
+
+       struct FindXxxResponse *res = (struct FindXxxResponse *)request_buffer;
+       if (!res->success)
+       {
+               dbg("  Failed, error code $l\n", (LONG)res->error_code);
+               dp->dp_Res1 = DOSFALSE;
+               dp->dp_Res2 = res->error_code;
+       }
+       else
+       {
+               fh->fh_Arg1 = res->arg1;
+               fh->fh_Type = mp;
+               fh->fh_Port = DOSFALSE;  // Not an interactive file.
+
+               dp->dp_Res1 = DOSTRUE;
+               dp->dp_Res2 = 0;
+       }
+
+       reply_packet(dp);
+}
+
+void action_read(struct DosPacket *dp)
+{
+       ULONG arg1 = dp->dp_Arg1;
+       UBYTE *dst = (UBYTE *)dp->dp_Arg2;
+       int length = dp->dp_Arg3;
+
+       dbg("ACTION_READ\n");
+       dbg("  arg1 = $l\n", arg1);
+       dbg("  length = $l\n", length);
+
+       if (length == 0)
+       {
+               dp->dp_Res1 = -1;
+               dp->dp_Res2 = ERROR_INVALID_LOCK; // This is not the correct error.
+               reply_packet(dp);
+               return;
+       }
+
+       int total_read = 0;
+       while (length)
+       {
+               int to_read = length;
+               if (to_read > BUFFER_SIZE)
+                       to_read = BUFFER_SIZE;
+
+               struct ReadRequest *req = (struct ReadRequest *)request_buffer;
+               req->has_response = 0;
+               req->type = dp->dp_Type;
+               req->arg1 = arg1;
+               req->address = TranslateAddressA314(data_buffer);
+               req->length = to_read;
+
+               write_req_and_wait_for_res(sizeof(struct ReadRequest));
+
+               struct ReadResponse *res = (struct ReadResponse *)request_buffer;
+               if (!res->success)
+               {
+                       dbg("  Failed, error code $l\n", (LONG)res->error_code);
+                       dp->dp_Res1 = -1;
+                       dp->dp_Res2 = res->error_code;
+                       reply_packet(dp);
+                       return;
+               }
+
+               if (res->actual)
+               {
+                       memcpy(dst, data_buffer, res->actual);
+                       dst += res->actual;
+                       total_read += res->actual;
+                       length -= res->actual;
+               }
+
+               if (res->actual < to_read)
+                       break;
+       }
+
+       dp->dp_Res1 = total_read;
+       dp->dp_Res2 = 0;
+       reply_packet(dp);
+}
+
+void action_write(struct DosPacket *dp)
+{
+       ULONG arg1 = dp->dp_Arg1;
+       UBYTE *src = (UBYTE *)dp->dp_Arg2;
+       int length = dp->dp_Arg3;
+
+       dbg("ACTION_WRITE\n");
+       dbg("  arg1 = $l\n", arg1);
+       dbg("  length = $l\n", length);
+
+       int total_written = 0;
+       while (length)
+       {
+               int to_write = length;
+               if (to_write > BUFFER_SIZE)
+                       to_write = BUFFER_SIZE;
+
+               memcpy(data_buffer, src, to_write);
+
+               struct WriteRequest *req = (struct WriteRequest *)request_buffer;
+               req->has_response = 0;
+               req->type = dp->dp_Type;
+               req->arg1 = arg1;
+               req->address = TranslateAddressA314(data_buffer);
+               req->length = to_write;
+
+               write_req_and_wait_for_res(sizeof(struct WriteRequest));
+
+               struct WriteResponse *res = (struct WriteResponse *)request_buffer;
+               if (!res->success)
+               {
+                       dbg("  Failed, error code $l\n", (LONG)res->error_code);
+                       dp->dp_Res1 = total_written;
+                       dp->dp_Res2 = res->error_code;
+                       reply_packet(dp);
+                       return;
+               }
+
+               if (res->actual)
+               {
+                       src += res->actual;
+                       total_written += res->actual;
+                       length -= res->actual;
+               }
+
+               if (res->actual < to_write)
+                       break;
+       }
+
+       dp->dp_Res1 = total_written;
+       dp->dp_Res2 = 0;
+       reply_packet(dp);
+}
+
+void action_seek(struct DosPacket *dp)
+{
+       ULONG arg1 = dp->dp_Arg1;
+       LONG new_pos = dp->dp_Arg2;
+       LONG mode = dp->dp_Arg3;
+
+       dbg("ACTION_SEEK\n");
+       dbg("  arg1 = $l\n", arg1);
+       dbg("  new_pos = $l\n", new_pos);
+       dbg("  mode = $l\n", mode);
+
+       struct SeekRequest *req = (struct SeekRequest *)request_buffer;
+       req->has_response = 0;
+       req->type = dp->dp_Type;
+       req->arg1 = arg1;
+       req->new_pos = new_pos;
+       req->mode = mode;
+
+       write_req_and_wait_for_res(sizeof(struct SeekRequest));
+
+       struct SeekResponse *res = (struct SeekResponse *)request_buffer;
+       if (!res->success)
+       {
+               dbg("  Failed, error code $l\n", (LONG)res->error_code);
+               dp->dp_Res1 = -1;
+               dp->dp_Res2 = res->error_code;
+       }
+       else
+       {
+               dp->dp_Res1 = res->old_pos;
+               dp->dp_Res2 = 0;
+       }
+
+       reply_packet(dp);
+}
+
+void action_end(struct DosPacket *dp)
+{
+       ULONG arg1 = dp->dp_Arg1;
+
+       dbg("ACTION_END\n");
+       dbg("  arg1 = $l\n", arg1);
+
+       struct EndRequest *req = (struct EndRequest *)request_buffer;
+       req->has_response = 0;
+       req->type = dp->dp_Type;
+       req->arg1 = arg1;
+
+       write_req_and_wait_for_res(sizeof(struct EndRequest));
+
+       //struct EndResponse *res = (struct EndResponse *)request_buffer;
+
+       dp->dp_Res1 = DOSTRUE;
+       dp->dp_Res2 = 0;
+       reply_packet(dp);
+}
+
+void action_delete_object(struct DosPacket *dp)
+{
+       struct FileLock *lock = (struct FileLock *)BADDR(dp->dp_Arg1);
+       unsigned char *name = (unsigned char *)BADDR(dp->dp_Arg2);
+
+       dbg("ACTION_DELETE_OBJECT\n");
+       dbg("  lock = $l\n", lock);
+       dbg("  name = $S\n", name);
+
+       struct DeleteObjectRequest *req = (struct DeleteObjectRequest *)request_buffer;
+       req->has_response = 0;
+       req->type = dp->dp_Type;
+       req->key = lock == NULL ? 0 : lock->fl_Key;
+
+       int nlen = *name;
+       memcpy(req->name, name, nlen + 1);
+
+       write_req_and_wait_for_res(sizeof(struct DeleteObjectRequest) + nlen);
+
+       struct DeleteObjectResponse *res = (struct DeleteObjectResponse *)request_buffer;
+       if (!res->success)
+       {
+               dbg("  Failed, error code $l\n", (LONG)res->error_code);
+               dp->dp_Res1 = DOSFALSE;
+               dp->dp_Res2 = res->error_code;
+       }
+       else
+       {
+               dp->dp_Res1 = DOSTRUE;
+               dp->dp_Res2 = 0;
+       }
+
+       reply_packet(dp);
+}
+
+void action_rename_object(struct DosPacket *dp)
+{
+       struct FileLock *lock = (struct FileLock *)BADDR(dp->dp_Arg1);
+       unsigned char *name = (unsigned char *)BADDR(dp->dp_Arg2);
+       struct FileLock *target_dir = (struct FileLock *)BADDR(dp->dp_Arg3);
+       unsigned char *new_name = (unsigned char *)BADDR(dp->dp_Arg4);
+
+       dbg("ACTION_RENAME_OBJECT\n");
+       dbg("  lock = $l\n", lock);
+       dbg("  name = $S\n", name);
+       dbg("  target directory = $l\n", lock);
+       dbg("  new name = $S\n", new_name);
+
+       struct RenameObjectRequest *req = (struct RenameObjectRequest *)request_buffer;
+       req->has_response = 0;
+       req->type = dp->dp_Type;
+       req->key = lock == NULL ? 0 : lock->fl_Key;
+       req->target_dir = target_dir == NULL ? 0 : target_dir->fl_Key;
+
+       int nlen = *name;
+       int nnlen = *new_name;
+
+       req->name_len = nlen;
+       req->new_name_len = nnlen;
+
+       unsigned char *p = &(req->new_name_len) + 1;
+       memcpy(p, name + 1, nlen);
+       p += nlen;
+       memcpy(p, new_name + 1, nnlen);
+
+       write_req_and_wait_for_res(sizeof(struct RenameObjectRequest) + nlen + nnlen);
+
+       struct RenameObjectResponse *res = (struct RenameObjectResponse *)request_buffer;
+       if (!res->success)
+       {
+               dbg("  Failed, error code $l\n", (LONG)res->error_code);
+               dp->dp_Res1 = DOSFALSE;
+               dp->dp_Res2 = res->error_code;
+       }
+       else
+       {
+               dp->dp_Res1 = DOSTRUE;
+               dp->dp_Res2 = 0;
+       }
+
+       reply_packet(dp);
+}
+
+void action_create_dir(struct DosPacket *dp)
+{
+       struct FileLock *lock = (struct FileLock *)BADDR(dp->dp_Arg1);
+       unsigned char *name = (unsigned char *)BADDR(dp->dp_Arg2);
+
+       dbg("ACTION_CREATE_DIR\n");
+       dbg("  lock = $l\n", lock);
+       dbg("  name = $S\n", name);
+
+       struct CreateDirRequest *req = (struct CreateDirRequest *)request_buffer;
+       req->has_response = 0;
+       req->type = dp->dp_Type;
+       req->key = lock == NULL ? 0 : lock->fl_Key;
+
+       int nlen = *name;
+       memcpy(req->name, name, nlen + 1);
+
+       write_req_and_wait_for_res(sizeof(struct CreateDirRequest) + nlen);
+
+       struct CreateDirResponse *res = (struct CreateDirResponse *)request_buffer;
+       if (!res->success)
+       {
+               dbg("  Failed, error code $l\n", (LONG)res->error_code);
+               dp->dp_Res1 = DOSFALSE;
+               dp->dp_Res2 = res->error_code;
+       }
+       else
+       {
+               struct FileLock *lock = create_and_add_file_lock(res->key, SHARED_LOCK);
+
+               dbg("  Returning lock $l\n", lock);
+               dp->dp_Res1 = MKBADDRU(lock);
+               dp->dp_Res2 = 0;
+       }
+
+       reply_packet(dp);
+}
+
+void action_set_protect(struct DosPacket *dp)
+{
+       struct FileLock *lock = (struct FileLock *)BADDR(dp->dp_Arg2);
+       unsigned char *name = (unsigned char *)BADDR(dp->dp_Arg3);
+       long mask = dp->dp_Arg4;
+
+       dbg("ACTION_SET_PROTECT\n");
+       dbg("  lock = $l\n", lock);
+       dbg("  name = $S\n", name);
+       dbg("  mask = $l\n", mask);
+
+       struct SetProtectRequest *req = (struct SetProtectRequest *)request_buffer;
+       req->has_response = 0;
+       req->type = dp->dp_Type;
+       req->key = lock == NULL ? 0 : lock->fl_Key;
+       req->mask = mask;
+
+       int nlen = *name;
+       memcpy(req->name, name, nlen + 1);
+
+       write_req_and_wait_for_res(sizeof(struct SetProtectRequest) + nlen);
+
+       struct SetProtectResponse *res = (struct SetProtectResponse *)request_buffer;
+       if (!res->success)
+       {
+               dbg("  Failed, error code $l\n", (LONG)res->error_code);
+               dp->dp_Res1 = DOSFALSE;
+               dp->dp_Res2 = res->error_code;
+       }
+       else
+       {
+               dp->dp_Res1 = DOSTRUE;
+               dp->dp_Res2 = 0;
+       }
+
+       reply_packet(dp);
+}
+
+void action_set_comment(struct DosPacket *dp)
+{
+       struct FileLock *lock = (struct FileLock *)BADDR(dp->dp_Arg2);
+       unsigned char *name = (unsigned char *)BADDR(dp->dp_Arg3);
+       unsigned char *comment = (unsigned char *)BADDR(dp->dp_Arg4);
+
+       dbg("ACTION_SET_COMMENT\n");
+       dbg("  lock = $l\n", lock);
+       dbg("  name = $S\n", name);
+       dbg("  comment = $S\n", comment);
+
+       struct SetCommentRequest *req = (struct SetCommentRequest *)request_buffer;
+       req->has_response = 0;
+       req->type = dp->dp_Type;
+       req->key = lock == NULL ? 0 : lock->fl_Key;
+
+       int nlen = *name;
+       int clen = *comment;
+
+       req->name_len = nlen;
+       req->comment_len = clen;
+
+       unsigned char *p = &(req->comment_len) + 1;
+       memcpy(p, name + 1, nlen);
+       p += nlen;
+       memcpy(p, comment + 1, clen);
+
+       write_req_and_wait_for_res(sizeof(struct SetCommentRequest) + nlen + clen);
+
+       struct SetCommentResponse *res = (struct SetCommentResponse *)request_buffer;
+       if (!res->success)
+       {
+               dbg("  Failed, error code $l\n", (LONG)res->error_code);
+               dp->dp_Res1 = DOSFALSE;
+               dp->dp_Res2 = res->error_code;
+       }
+       else
+       {
+               dp->dp_Res1 = DOSTRUE;
+               dp->dp_Res2 = 0;
+       }
+
+       reply_packet(dp);
+}
+
+void action_same_lock(struct DosPacket *dp)
+{
+       struct FileLock *lock1 = (struct FileLock *)BADDR(dp->dp_Arg1);
+       struct FileLock *lock2 = (struct FileLock *)BADDR(dp->dp_Arg2);
+
+       dbg("ACTION_SAME_LOCK\n");
+       dbg("  locks to compare = $l $l\n", lock1, lock2);
+
+       struct SameLockRequest *req = (struct SameLockRequest *)request_buffer;
+       req->has_response = 0;
+       req->type = dp->dp_Type;
+       req->key1 = lock1->fl_Key;
+       req->key2 = lock2->fl_Key;
+
+       write_req_and_wait_for_res(sizeof(struct SameLockRequest));
+
+       struct SameLockResponse *res = (struct SameLockResponse *)request_buffer;
+       dp->dp_Res1 = res->success ? DOSTRUE : DOSFALSE;
+       dp->dp_Res2 = res->error_code;
+
+       reply_packet(dp);
+}
+
+void fill_info_data(struct InfoData *id)
+{
+       memset(id, 0, sizeof(struct InfoData));
+       id->id_DiskState = ID_VALIDATED;
+       id->id_NumBlocks = 512 * 1024;
+       id->id_NumBlocksUsed = 10;
+       id->id_BytesPerBlock = 512;
+       id->id_DiskType = my_volume->dl_DiskType;
+       id->id_VolumeNode = MKBADDRU(my_volume);
+       id->id_InUse = DOSTRUE;
+}
+
+void action_info(struct DosPacket *dp)
+{
+       struct FileLock *lock = (struct FileLock *)BADDR(dp->dp_Arg1);
+       struct InfoData *id = (struct InfoData *)BADDR(dp->dp_Arg2);
+
+       dbg("ACTION_INFO\n");
+       dbg("  lock = $l\n", lock);
+
+       fill_info_data(id);
+
+       dp->dp_Res1 = DOSTRUE;
+       dp->dp_Res2 = 0;
+       reply_packet(dp);
+}
+
+void action_disk_info(struct DosPacket *dp)
+{
+       struct InfoData *id = (struct InfoData *)BADDR(dp->dp_Arg1);
+
+       dbg("ACTION_DISK_INFO\n");
+
+       fill_info_data(id);
+
+       dp->dp_Res1 = DOSTRUE;
+       dp->dp_Res2 = 0;
+       reply_packet(dp);
+}
+
+void action_unsupported(struct DosPacket *dp)
+{
+       dbg("ACTION_UNSUPPORTED\n");
+       dbg("  Unsupported action: $l\n", (ULONG)dp->dp_Type);
+
+       struct UnsupportedRequest *req = (struct UnsupportedRequest *)request_buffer;
+       req->has_response = 0;
+       req->type = ACTION_UNSUPPORTED;
+       req->dp_Type = dp->dp_Type;
+
+       write_req_and_wait_for_res(sizeof(struct UnsupportedRequest));
+
+       struct UnsupportedResponse *res = (struct UnsupportedResponse *)request_buffer;
+       dp->dp_Res1 = res->success ? DOSTRUE : DOSFALSE;
+       dp->dp_Res2 = res->error_code;
+       reply_packet(dp);
+}
+
+void start(__reg("a0") struct DosPacket *startup_packet)
+{
+       SysBase = *(struct ExecBase **)4;
+       DOSBase = (struct DosLibrary *)OpenLibrary(DOSNAME, 0);
+
+       mp = MyCreatePort(NULL, 0);
+
+       startup_fs_handler(startup_packet);
+
+       while (1)
+       {
+               WaitPort(mp);
+               struct StandardPacket *sp = (struct StandardPacket *)GetMsg(mp);
+               struct DosPacket *dp = (struct DosPacket *)(sp->sp_Msg.mn_Node.ln_Name);
+
+               switch (dp->dp_Type)
+               {
+               case ACTION_LOCATE_OBJECT: action_locate_object(dp); break;
+               case ACTION_FREE_LOCK: action_free_lock(dp); break;
+               case ACTION_COPY_DIR: action_copy_dir(dp); break;
+               case ACTION_PARENT: action_parent(dp); break;
+               case ACTION_EXAMINE_OBJECT: action_examine_object(dp); break;
+               case ACTION_EXAMINE_NEXT: action_examine_next(dp); break;
+               case ACTION_EXAMINE_FH: action_examine_fh(dp); break;
+
+               case ACTION_FINDUPDATE: action_findxxx(dp); break;
+               case ACTION_FINDINPUT: action_findxxx(dp); break;
+               case ACTION_FINDOUTPUT: action_findxxx(dp); break;
+               case ACTION_READ: action_read(dp); break;
+               case ACTION_WRITE: action_write(dp); break;
+               case ACTION_SEEK: action_seek(dp); break;
+               case ACTION_END: action_end(dp); break;
+               //case ACTION_TRUNCATE: action_truncate(dp); break;
+
+               case ACTION_DELETE_OBJECT: action_delete_object(dp); break;
+               case ACTION_RENAME_OBJECT: action_rename_object(dp); break;
+               case ACTION_CREATE_DIR: action_create_dir(dp); break;
+
+               case ACTION_SET_PROTECT: action_set_protect(dp); break;
+               case ACTION_SET_COMMENT: action_set_comment(dp); break;
+               //case ACTION_SET_DATE: action_set_date(dp); break;
+
+               case ACTION_SAME_LOCK: action_same_lock(dp); break;
+               case ACTION_DISK_INFO: action_disk_info(dp); break;
+               case ACTION_INFO: action_info(dp); break;
+
+               /*
+               case ACTION_CURRENT_VOLUME: action_current_volume(dp); break;
+               case ACTION_RENAME_DISK: action_rename_disk(dp); break;
+
+               case ACTION_DIE: //action_die(dp); break;
+               case ACTION_MORE_CACHE: //action_more_cache(dp); break;
+               case ACTION_FLUSH: //action_flush(dp); break;
+               case ACTION_INHIBIT: //action_inhibit(dp); break;
+               case ACTION_WRITE_PROTECT: //action_write_protect(dp); break;
+               */
+
+               default: action_unsupported(dp); break;
+               }
+       }
+
+       dbg("Shutting down\n");
+
+       // More cleaning up is necessary if the handler is to exit.
+       CloseLibrary((struct Library *)DOSBase);
+}
diff --git a/a314/software-amiga/a314fs_pistorm/bcpl_end.asm b/a314/software-amiga/a314fs_pistorm/bcpl_end.asm
new file mode 100644 (file)
index 0000000..5459133
--- /dev/null
@@ -0,0 +1,5 @@
+               cnop    0,4
+               dc.l    0
+               dc.l    1
+               dc.l    4
+               dc.l    1
diff --git a/a314/software-amiga/a314fs_pistorm/bcpl_start.asm b/a314/software-amiga/a314fs_pistorm/bcpl_start.asm
new file mode 100644 (file)
index 0000000..748c350
--- /dev/null
@@ -0,0 +1,8 @@
+               dc.l    0
+
+               movem.l a0-a6,-(a7)
+               lsl.l   #2,d1
+               move.l  d1,a0
+               bsr     _start
+               movem.l (a7)+,a0-a6
+               jmp     (a6)
diff --git a/a314/software-amiga/a314fs_pistorm/build.bat b/a314/software-amiga/a314fs_pistorm/build.bat
new file mode 100644 (file)
index 0000000..505cf2b
--- /dev/null
@@ -0,0 +1,5 @@
+vc a314fs.c -S -o a314fs.asm
+python comment_out_sections.py
+vc bcpl_start.asm a314fs.asm bcpl_end.asm -nostdlib -o ../a314fs
+python patch_a314fs.py
+del a314fs.asm
diff --git a/a314/software-amiga/a314fs_pistorm/comment_out_sections.py b/a314/software-amiga/a314fs_pistorm/comment_out_sections.py
new file mode 100644 (file)
index 0000000..b769e11
--- /dev/null
@@ -0,0 +1,5 @@
+with open('a314fs.asm', 'r+b') as f:
+    text = f.read().decode('utf-8')
+    text = text.replace('section', ';section')
+    f.seek(0, 0)
+    f.write(text.encode('utf-8'))
diff --git a/a314/software-amiga/a314fs_pistorm/messages.h b/a314/software-amiga/a314fs_pistorm/messages.h
new file mode 100644 (file)
index 0000000..da0050c
--- /dev/null
@@ -0,0 +1,318 @@
+#pragma pack(push, 1)
+
+struct LocateObjectRequest
+{
+       short has_response;
+       short type;
+       long key;
+       short mode;
+       char name[1];
+};
+
+struct LocateObjectResponse
+{
+       short has_response;
+       short success;
+       short error_code;
+       long key;
+};
+
+struct FreeLockRequest
+{
+       short has_response;
+       short type;
+       long key;
+};
+
+struct FreeLockResponse
+{
+       short has_response;
+       short success;
+       short error_code;
+};
+
+struct CopyDirRequest
+{
+       short has_response;
+       short type;
+       long key;
+};
+
+struct CopyDirResponse
+{
+       short has_response;
+       short success;
+       short error_code;
+       long key;
+};
+
+struct ParentRequest
+{
+       short has_response;
+       short type;
+       long key;
+};
+
+struct ParentResponse
+{
+       short has_response;
+       short success;
+       short error_code;
+       long key;
+};
+
+struct ExamineObjectRequest
+{
+       short has_response;
+       short type;
+       long key;
+};
+
+struct ExamineObjectResponse
+{
+       short has_response;
+       short success;
+       short error_code;
+
+       short disk_key;
+       short entry_type;
+       int size;
+       int protection;
+       int date[3];
+       char data[1];
+};
+
+struct ExamineNextRequest
+{
+       short has_response;
+       short type;
+       long key;
+       short disk_key;
+};
+
+struct ExamineNextResponse
+{
+       short has_response;
+       short success;
+       short error_code;
+
+       short disk_key;
+       short entry_type;
+       int size;
+       int protection;
+       int date[3];
+       char data[1];
+};
+
+struct FindXxxRequest
+{
+       short has_response;
+       short type;
+       long key;
+       char name[1];
+};
+
+struct FindXxxResponse
+{
+       short has_response;
+       short success;
+       short error_code;
+       long arg1;
+};
+
+struct ReadRequest
+{
+       short has_response;
+       short type;
+       long arg1;
+       int address;
+       int length;
+};
+
+struct ReadResponse
+{
+       short has_response;
+       short success;
+       short error_code;
+       int actual;
+};
+
+struct WriteRequest
+{
+       short has_response;
+       short type;
+       long arg1;
+       int address;
+       int length;
+};
+
+struct WriteResponse
+{
+       short has_response;
+       short success;
+       short error_code;
+       int actual;
+};
+
+struct SeekRequest
+{
+       short has_response;
+       short type;
+       long arg1;
+       int new_pos;
+       int mode;
+};
+
+struct SeekResponse
+{
+       short has_response;
+       short success;
+       short error_code;
+       int old_pos;
+};
+
+struct EndRequest
+{
+       short has_response;
+       short type;
+       long arg1;
+};
+
+struct EndResponse
+{
+       short has_response;
+       short success;
+       short error_code;
+};
+
+struct DeleteObjectRequest
+{
+       short has_response;
+       short type;
+       long key;
+       char name[1];
+};
+
+struct DeleteObjectResponse
+{
+       short has_response;
+       short success;
+       short error_code;
+};
+
+struct RenameObjectRequest
+{
+       short has_response;
+       short type;
+       long key;
+       long target_dir;
+       unsigned char name_len;
+       unsigned char new_name_len;
+};
+
+struct RenameObjectResponse
+{
+       short has_response;
+       short success;
+       short error_code;
+};
+
+struct CreateDirRequest
+{
+       short has_response;
+       short type;
+       long key;
+       char name[1];
+};
+
+struct CreateDirResponse
+{
+       short has_response;
+       short success;
+       short error_code;
+       long key;
+};
+
+struct SetProtectRequest
+{
+       short has_response;
+       short type;
+       long key;
+       long mask;
+       char name[1];
+};
+
+struct SetProtectResponse
+{
+       short has_response;
+       short success;
+       short error_code;
+};
+
+struct SetCommentRequest
+{
+       short has_response;
+       short type;
+       long key;
+       unsigned char name_len;
+       unsigned char comment_len;
+};
+
+struct SetCommentResponse
+{
+       short has_response;
+       short success;
+       short error_code;
+};
+
+struct SameLockRequest
+{
+       short has_response;
+       short type;
+       long key1;
+       long key2;
+};
+
+struct SameLockResponse
+{
+       short has_response;
+       short success;
+       short error_code;
+};
+
+struct ExamineFhRequest
+{
+       short has_response;
+       short type;
+       long arg1;
+};
+
+struct ExamineFhResponse
+{
+       short has_response;
+       short success;
+       short error_code;
+
+       short disk_key;
+       short entry_type;
+       int size;
+       int protection;
+       int date[3];
+       char data[1];
+};
+
+struct UnsupportedRequest
+{
+       short has_response;
+       short type;
+       short dp_Type;
+};
+
+struct UnsupportedResponse
+{
+       short has_response;
+       short success;
+       short error_code;
+};
+
+#pragma pack(pop)
diff --git a/a314/software-amiga/a314fs_pistorm/patch_a314fs.py b/a314/software-amiga/a314fs_pistorm/patch_a314fs.py
new file mode 100644 (file)
index 0000000..08d9983
--- /dev/null
@@ -0,0 +1,5 @@
+with open('a314fs', 'r+b') as f:
+    f.seek(0x1c)
+    b = f.read(4)
+    f.seek(0x20)
+    f.write(b)