mirror of
https://github.com/captain-amygdala/pistorm.git
synced 2026-02-24 15:57:39 +00:00
Adapt a314fs to work on PiStorm + A314 emulation
Requires a314fs in L:, a314fs-mountlist in DEVS:DosDrivers and somehow a314fs.py started after the emulator but before mounting the drive. Untested.
This commit is contained in:
817
a314/files_pi/a314fs.py
Normal file
817
a314/files_pi/a314fs.py
Normal file
@@ -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
|
||||
Binary file not shown.
12
a314/software-amiga/a314fs-mountlist
Normal file
12
a314/software-amiga/a314fs-mountlist
Normal file
@@ -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
|
||||
#
|
||||
1305
a314/software-amiga/a314fs_pistorm/a314fs.c
Normal file
1305
a314/software-amiga/a314fs_pistorm/a314fs.c
Normal file
File diff suppressed because it is too large
Load Diff
5
a314/software-amiga/a314fs_pistorm/bcpl_end.asm
Normal file
5
a314/software-amiga/a314fs_pistorm/bcpl_end.asm
Normal file
@@ -0,0 +1,5 @@
|
||||
cnop 0,4
|
||||
dc.l 0
|
||||
dc.l 1
|
||||
dc.l 4
|
||||
dc.l 1
|
||||
8
a314/software-amiga/a314fs_pistorm/bcpl_start.asm
Normal file
8
a314/software-amiga/a314fs_pistorm/bcpl_start.asm
Normal file
@@ -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)
|
||||
5
a314/software-amiga/a314fs_pistorm/build.bat
Normal file
5
a314/software-amiga/a314fs_pistorm/build.bat
Normal file
@@ -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
|
||||
@@ -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'))
|
||||
318
a314/software-amiga/a314fs_pistorm/messages.h
Normal file
318
a314/software-amiga/a314fs_pistorm/messages.h
Normal file
@@ -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)
|
||||
5
a314/software-amiga/a314fs_pistorm/patch_a314fs.py
Normal file
5
a314/software-amiga/a314fs_pistorm/patch_a314fs.py
Normal file
@@ -0,0 +1,5 @@
|
||||
with open('a314fs', 'r+b') as f:
|
||||
f.seek(0x1c)
|
||||
b = f.read(4)
|
||||
f.seek(0x20)
|
||||
f.write(b)
|
||||
Reference in New Issue
Block a user