From 1da639dfa54e5e8de88eb529a4e4135d01bb5cf2 Mon Sep 17 00:00:00 2001 From: Stefan Reinauer Date: Thu, 21 May 2026 12:46:02 -0700 Subject: [PATCH 01/24] amiga: split OS compatibility layer Start separating Amiga-family OS integration from the packet handler so AmigaOS 4 can grow a native frontend without cloning the OS3 handler. Select an os3 or os4 platform subdirectory from the Makefile and add shared sys_compat declarations. Route allocation, message ports, I/O requests, signals, interrupt setup, library/interface ownership, and utility hook calls through per-OS implementations. Keep -lauto out of the OS4 link. Explicitly open dos.library, obtain IDOS, open utility.library, and obtain its main interface. Move the temporary OS4 main wrapper into the OS4 frontend directory and document the remaining OS4 porting work. --- Makefile | 126 +++++++++++++++----- include/odfs/alloc.h | 10 +- platform/amiga/common/sys_compat.h | 57 +++++++++ platform/amiga/handler.h | 3 + platform/amiga/handler_main.c | 142 +++++++++++----------- platform/amiga/os3/sys_compat.c | 129 ++++++++++++++++++++ platform/amiga/os4/main.c | 13 ++ platform/amiga/os4/sys_compat.c | 185 +++++++++++++++++++++++++++++ 8 files changed, 559 insertions(+), 106 deletions(-) create mode 100644 platform/amiga/common/sys_compat.h create mode 100644 platform/amiga/os3/sys_compat.c create mode 100644 platform/amiga/os4/main.c create mode 100644 platform/amiga/os4/sys_compat.c diff --git a/Makefile b/Makefile index d5bde13..f6cc7e1 100644 --- a/Makefile +++ b/Makefile @@ -5,17 +5,43 @@ # ---- toolchain selection ---- -# Amiga cross-compiler (m68k-amigaos or m68k-aros-gcc) -CC = m68k-amigaos-gcc -STRIP = m68k-amigaos-strip - -# NDK include path (override with: make NDK_PATH=/your/path) -NDK_PATH ?= $(shell realpath $$(dirname $$(which $(CC)))/../m68k-amigaos/ndk-include 2>/dev/null) +# Amiga cross-compiler. +# Override with CC=ppc-amigaos-gcc for an AmigaOS 4 PPC build. +CC ?= m68k-amigaos-gcc # AROS cross-compiler (override: make CC=m68k-aros-gcc AROS=1) # When AROS=1, uses -static instead of -noixemul and defines __AROS__ AROS ?= 0 +# Derive target tools from CC so CC=ppc-amigaos-gcc also selects the +# matching ppc-amigaos-ar/strip/size tools. +AMIGA_CC_TARGET := $(shell $(CC) -dumpmachine 2>/dev/null) +AMIGA_TOOL_PREFIX ?= $(patsubst %-gcc,%,$(notdir $(CC))) +AMIGA_AR ?= $(AMIGA_TOOL_PREFIX)-ar +AMIGA_SIZE ?= $(AMIGA_TOOL_PREFIX)-size +STRIP ?= $(AMIGA_TOOL_PREFIX)-strip + +ifneq ($(filter ppc-amigaos,$(AMIGA_CC_TARGET)),) +AMIGA_TARGET ?= os4 +else ifeq ($(AROS),1) +AMIGA_TARGET ?= aros +else +AMIGA_TARGET ?= os3 +endif + +ifeq ($(AMIGA_TARGET),os4) +AMIGA_OSDIR := os4 +else +AMIGA_OSDIR := os3 +endif + +# NDK include path (override with: make NDK_PATH=/your/path) +ifeq ($(AMIGA_TARGET),os4) +NDK_PATH ?= $(shell realpath $$(dirname $$(which $(CC)))/../ppc-amigaos/SDK/include/include_h 2>/dev/null) +else +NDK_PATH ?= $(shell realpath $$(dirname $$(which $(CC)))/../m68k-amigaos/ndk-include 2>/dev/null) +endif + # Host compiler HOSTCC ?= cc @@ -25,6 +51,11 @@ AMIGA_DATE ?= $(shell date '+%-d.%-m.%Y') ODFS_GIT_VERSION ?= $(shell desc=$$(git describe --tags --match "v*" --dirty --always 2>/dev/null || echo unknown); printf '%s\n' "$$desc" | grep -q '^v' && printf '%s' "$$desc" || printf 'early-0-g%s' "$$desc") INCLUDES = -I include -I backends +AMIGA_PLATFORM_INCLUDES = -I platform/amiga \ + -I platform/amiga/common \ + -I platform/amiga/$(AMIGA_OSDIR) +AMIGA_INCLUDES = $(INCLUDES) $(AMIGA_PLATFORM_INCLUDES) \ + $(if $(NDK_PATH),-I$(NDK_PATH)) # ---- optional 3rdparty submodules ---- @@ -53,7 +84,11 @@ SERIAL_DEBUG ?= 0 PACKET_TRACE ?= 0 # Release size limits (override when intentional growth is approved) +ifeq ($(AMIGA_TARGET),os4) +AMIGA_SIZE_LIMIT ?= 131072 +else AMIGA_SIZE_LIMIT ?= 60000 +endif ROM_SIZE_LIMIT ?= 32768 SIZE_LIMIT_NAME ?= AMIGA_SIZE_LIMIT SIZE_LIMIT_DESC ?= release Amiga handler @@ -85,26 +120,46 @@ FEATURE_DEFS = \ -DODFS_FEATURE_HFSPLUS=$(FEATURE_HFSPLUS) \ -DODFS_FEATURE_CDDA=$(FEATURE_CDDA) -ifeq ($(AROS),1) -CFLAGS = -Os -m68000 -mtune=68020-60 -msoft-float -static -nostartfiles \ - -Wall -Wextra -Werror \ - -Wstrict-prototypes -Wmissing-prototypes \ - -Wno-array-bounds \ - -MMD -MP \ - -DAMIGA -D__AROS__ $(FEATURE_DEFS) -LDFLAGS = -static -LIBS = -lamiga -lgcc +ifeq ($(AMIGA_TARGET),aros) +AMIGA_CPUFLAGS ?= -m68000 -mtune=68020-60 -msoft-float +AMIGA_SYSFLAGS ?= -static +AMIGA_WARNFLAGS = +AMIGA_DEFS = -DAMIGA -D__AROS__ +LDFLAGS = $(AMIGA_SYSFLAGS) +LIBS = -lamiga -lgcc +HANDLER_LDFLAGS = -nostartfiles +HANDLER_LIBS = -nostdlib -Wl,-u,_exit -lgcc -lc -lgcc -lamiga -ramiga-dev +else ifeq ($(AMIGA_TARGET),os4) +AMIGA_CRT ?= newlib +AMIGA_CPUFLAGS ?= -mcpu=powerpc +AMIGA_SYSFLAGS ?= -mcrt=$(AMIGA_CRT) +AMIGA_WARNFLAGS = -Wno-error=deprecated-declarations +AMIGA_DEFS = -DAMIGA -D__USE_INLINE__ -D__USE_BASETYPE__ +LDFLAGS = $(AMIGA_SYSFLAGS) +# Keep OS4 library/interface ownership explicit in os4/sys_compat.c. +# Do not add -lauto to the handler link. +LIBS = -lc -lgcc +HANDLER_LDFLAGS = +HANDLER_LIBS = $(LIBS) else -CFLAGS = -Os -m68000 -mtune=68020-60 -msoft-float -noixemul -nostartfiles \ - -Wall -Wextra -Werror \ - -Wstrict-prototypes -Wmissing-prototypes \ - -Wno-array-bounds \ - -MMD -MP \ - -DAMIGA $(FEATURE_DEFS) -LDFLAGS = -noixemul -LIBS = -lamiga -lgcc +AMIGA_CPUFLAGS ?= -m68000 -mtune=68020-60 -msoft-float +AMIGA_SYSFLAGS ?= -noixemul +AMIGA_WARNFLAGS = +AMIGA_DEFS = -DAMIGA +LDFLAGS = $(AMIGA_SYSFLAGS) +LIBS = -lamiga -lgcc +HANDLER_LDFLAGS = -nostartfiles +HANDLER_LIBS = -nostdlib -Wl,-u,_exit -lgcc -lc -lgcc -lamiga -ramiga-dev endif +CFLAGS = -Os $(AMIGA_CPUFLAGS) $(AMIGA_SYSFLAGS) -nostartfiles \ + -Wall -Wextra -Werror \ + $(AMIGA_WARNFLAGS) \ + -Wstrict-prototypes -Wmissing-prototypes \ + -Wno-array-bounds \ + -MMD -MP \ + $(AMIGA_DEFS) $(FEATURE_DEFS) + # ---- build directories ---- HOST_BUILD = build/host @@ -139,11 +194,20 @@ HOST_SRCS = platform/host/file_media.c # Amiga handler sources AMIGA_SRCS = platform/amiga/handler_main.c \ - platform/amiga/libc_stubs.c \ - platform/amiga/printf_local.c + platform/amiga/printf_local.c \ + platform/amiga/$(AMIGA_OSDIR)/sys_compat.c +ifeq ($(AMIGA_TARGET),os4) +AMIGA_SRCS += platform/amiga/os4/main.c +else +AMIGA_SRCS += platform/amiga/libc_stubs.c +endif # Amiga assembly +ifeq ($(AMIGA_TARGET),os4) +AMIGA_ASM_SRCS = +else AMIGA_ASM_SRCS = platform/amiga/startup.S +endif AMIGA_ASM_OBJS = $(patsubst %.S,$(AMIGA_BUILD)/%.o,$(AMIGA_ASM_SRCS)) HOST_LIB_SRCS = $(CORE_SRCS) $(HOST_SRCS) @@ -258,7 +322,7 @@ rom-test: # Print size breakdown of Amiga library objects size: $(AMIGA_BUILD)/libodfs.a @echo "=== Amiga object sizes ===" - @m68k-amigaos-size $(AMIGA_BUILD)/libodfs.a + @$(AMIGA_SIZE) $(AMIGA_BUILD)/libodfs.a # ---- host library ---- @@ -362,14 +426,14 @@ $(HOST_BUILD)/tools/imgdump: tools/imgdump/imgdump.c $(HOST_BUILD)/libodfs.a $(AMIGA_BUILD)/libodfs.a: $(AMIGA_LIB_OBJS) @mkdir -p $(@D) @echo " AR $@ (amiga)" - @m68k-amigaos-ar rcs $@ $^ + @$(AMIGA_AR) rcs $@ $^ # ---- Amiga object files ---- $(AMIGA_BUILD)/%.o: %.c @mkdir -p $(@D) @echo " CC $<" - @$(CC) $(CPPFLAGS) $(INCLUDES) $(CFLAGS) -c -o $@ $< + @$(CC) $(CPPFLAGS) $(AMIGA_INCLUDES) $(CFLAGS) -c -o $@ $< # ---- Amiga assembly ---- @@ -383,12 +447,12 @@ $(AMIGA_BUILD)/%.o: %.S $(AMIGA_TEST_BUILD)/tests/amiga/%.o: tests/amiga/%.c @mkdir -p $(@D) @echo " CC $<" - @$(CC) $(CPPFLAGS) $(INCLUDES) $(CFLAGS) -c -o $@ $< + @$(CC) $(CPPFLAGS) $(AMIGA_INCLUDES) $(CFLAGS) -c -o $@ $< $(AMIGA_TEST_TOOL): $(AMIGA_TEST_BUILD)/tests/amiga/test_handler.o @mkdir -p $(@D) @echo " LINK $@" - @$(CC) $(LDFLAGS) -o $@ $< -lc -lamiga -lgcc + @$(CC) $(LDFLAGS) -o $@ $< $(LIBS) @echo " STRIP $@" @$(STRIP) $@ @@ -397,7 +461,7 @@ $(AMIGA_TEST_TOOL): $(AMIGA_TEST_BUILD)/tests/amiga/test_handler.o $(HANDLER): $(AMIGA_ASM_OBJS) $(AMIGA_BUILD)/libodfs.a @mkdir -p $(@D) @echo " LINK $@" - @$(CC) $(LDFLAGS) -nostartfiles -o $@ $(AMIGA_ASM_OBJS) -L$(AMIGA_BUILD) -lodfs -nostdlib -Wl,-u,_exit -lgcc -lc -lgcc -lamiga -ramiga-dev + @$(CC) $(LDFLAGS) $(HANDLER_LDFLAGS) -o $@ $(AMIGA_ASM_OBJS) -L$(AMIGA_BUILD) -lodfs $(HANDLER_LIBS) @echo " STRIP $@" @$(STRIP) $@ diff --git a/include/odfs/alloc.h b/include/odfs/alloc.h index e2ee590..d3faca1 100644 --- a/include/odfs/alloc.h +++ b/include/odfs/alloc.h @@ -13,14 +13,13 @@ #if ODFS_PLATFORM_AMIGA -#include -#include +#include "sys_compat.h" static inline void *odfs_malloc(size_t size) { if (size == 0) size = 1; - return AllocVec((ULONG)size, MEMF_PUBLIC); + return odfs_amiga_alloc_vec((ULONG)size, MEMF_PUBLIC); } static inline void *odfs_calloc(size_t count, size_t size) @@ -34,13 +33,12 @@ static inline void *odfs_calloc(size_t count, size_t size) if (total == 0) total = 1; - return AllocVec((ULONG)total, MEMF_PUBLIC | MEMF_CLEAR); + return odfs_amiga_alloc_vec((ULONG)total, MEMF_PUBLIC | MEMF_CLEAR); } static inline void odfs_free(void *ptr) { - if (ptr) - FreeVec(ptr); + odfs_amiga_free_vec(ptr); } #else diff --git a/platform/amiga/common/sys_compat.h b/platform/amiga/common/sys_compat.h new file mode 100644 index 0000000..85eda5e --- /dev/null +++ b/platform/amiga/common/sys_compat.h @@ -0,0 +1,57 @@ +/* + * sys_compat.h - Amiga-family OS integration boundary + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef ODFS_AMIGA_SYS_COMPAT_H +#define ODFS_AMIGA_SYS_COMPAT_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +struct Hook; + +typedef LONG (*odfs_amiga_interrupt_fn)(APTR data); + +extern struct ExecBase *SysBase; +extern struct DosLibrary *DOSBase; + +void odfs_amiga_init_sysbase(void); +struct ExecBase *odfs_amiga_sysbase(void); +struct DosLibrary *odfs_amiga_dosbase(void); + +int odfs_amiga_open_libraries(void); +void odfs_amiga_close_libraries(void); + +void *odfs_amiga_alloc_mem(ULONG size, ULONG flags); +void odfs_amiga_free_mem(void *ptr, ULONG size); +void *odfs_amiga_alloc_vec(ULONG size, ULONG flags); +void odfs_amiga_free_vec(void *ptr); + +struct MsgPort *odfs_amiga_create_msg_port(void); +void odfs_amiga_delete_msg_port(struct MsgPort *port); +struct IORequest *odfs_amiga_create_io_request(struct MsgPort *port, + ULONG size); +void odfs_amiga_delete_io_request(struct IORequest *req); + +LONG odfs_amiga_alloc_signal(LONG num); +void odfs_amiga_free_signal(LONG num); + +void odfs_amiga_init_interrupt(struct Interrupt *intr, + const char *name, + APTR data, + odfs_amiga_interrupt_fn code); + +ULONG odfs_amiga_call_hook_pkt(struct Hook *hook, APTR object, APTR message); + +#endif /* ODFS_AMIGA_SYS_COMPAT_H */ diff --git a/platform/amiga/handler.h b/platform/amiga/handler.h index fd8aa64..7b8e4cc 100644 --- a/platform/amiga/handler.h +++ b/platform/amiga/handler.h @@ -14,6 +14,9 @@ #include #include #include +#ifdef __amigaos4__ +#include +#endif #include "aros_compat.h" #include "odfs/api.h" diff --git a/platform/amiga/handler_main.c b/platform/amiga/handler_main.c index 703ada0..a45b075 100644 --- a/platform/amiga/handler_main.c +++ b/platform/amiga/handler_main.c @@ -9,6 +9,7 @@ */ #include "handler.h" +#include "sys_compat.h" #if ODFS_FEATURE_CDDA #include "cdda/cdda.h" @@ -25,7 +26,6 @@ #include #include -#include #include @@ -42,11 +42,6 @@ static const char version_string[] __attribute__((used)) = "$VER: ODFileSystem " ODFS_GIT_VERSION " (" ODFS_AMIGA_DATE ")"; -/* library bases — set by handler_main() */ -struct ExecBase *SysBase; -struct DosLibrary *DOSBase; -struct Library *UtilityBase; - /* forward declarations */ static void handle_packet(handler_global_t *g, struct DosPacket *pkt); static void return_packet(handler_global_t *g, struct DosPacket *pkt); @@ -107,8 +102,16 @@ static int scsi_is_unsupported_command(const uint8_t *sense) return ((sense[2] & 0x0f) == 0x05 && sense[12] == 0x20); } -static LONG changeint_handler(odfs_changeint_data_t *ci asm("a1")) +static LONG changeint_handler( +#ifdef __amigaos4__ + APTR data +#else + APTR data asm("a1") +#endif +) { + odfs_changeint_data_t *ci = data; + if (ci && ci->task && ci->sigmask) Signal(ci->task, ci->sigmask); return 0; @@ -173,13 +176,13 @@ static void notify_workbench_disk_change(BOOL inserted) struct IOStdReq *req; struct InputEvent event; - port = CreateMsgPort(); + port = odfs_amiga_create_msg_port(); if (!port) return; - req = (struct IOStdReq *)CreateIORequest(port, sizeof(*req)); + req = (struct IOStdReq *)odfs_amiga_create_io_request(port, sizeof(*req)); if (!req) { - DeleteMsgPort(port); + odfs_amiga_delete_msg_port(port); return; } @@ -195,8 +198,8 @@ static void notify_workbench_disk_change(BOOL inserted) CloseDevice((struct IORequest *)req); } - DeleteIORequest((struct IORequest *)req); - DeleteMsgPort(port); + odfs_amiga_delete_io_request((struct IORequest *)req); + odfs_amiga_delete_msg_port(port); } static odfs_err_t amiga_read_sectors(void *ctx, uint32_t lba, @@ -953,7 +956,7 @@ static odfs_entry_t *alloc_entry(odfs_volume_t *volume, { odfs_entry_t *entry; - entry = AllocMem(sizeof(*entry), MEMF_PUBLIC | MEMF_CLEAR); + entry = odfs_amiga_alloc_mem(sizeof(*entry), MEMF_PUBLIC | MEMF_CLEAR); if (!entry) return NULL; @@ -979,7 +982,7 @@ static void release_entry(odfs_entry_t *entry) if (!entry) return; if (--entry->refcount == 0) - FreeMem(entry, sizeof(*entry)); + odfs_amiga_free_mem(entry, sizeof(*entry)); } static odfs_node_t *lock_node(odfs_lock_t *ol) @@ -1043,7 +1046,7 @@ static odfs_volume_t *alloc_volume(handler_global_t *g, struct DeviceList *volno { odfs_volume_t *volume; - volume = AllocMem(sizeof(*volume), MEMF_PUBLIC | MEMF_CLEAR); + volume = odfs_amiga_alloc_mem(sizeof(*volume), MEMF_PUBLIC | MEMF_CLEAR); if (!volume) return NULL; @@ -1199,7 +1202,7 @@ static odfs_err_t read_file_node(handler_global_t *g, static void free_volume(odfs_volume_t *volume) { if (volume) - FreeMem(volume, sizeof(*volume)); + odfs_amiga_free_mem(volume, sizeof(*volume)); } static void drain_all_objects(handler_global_t *g) @@ -1210,14 +1213,14 @@ static void drain_all_objects(handler_global_t *g) odfs_fh_t *fh = (odfs_fh_t *)node; release_volume_object(g, fh->entry->volume); release_entry(fh->entry); - FreeMem(fh, sizeof(*fh)); + odfs_amiga_free_mem(fh, sizeof(*fh)); } while ((node = RemHead((struct List *)&g->locklist)) != NULL) { odfs_lock_t *ol = (odfs_lock_t *)node; release_volume_object(g, ol->entry->volume); release_entry(ol->entry); - FreeMem(ol, sizeof(*ol)); + odfs_amiga_free_mem(ol, sizeof(*ol)); } } @@ -1265,7 +1268,7 @@ static odfs_lock_t *alloc_lock(handler_global_t *g, if (!entry) return NULL; - ol = AllocMem(sizeof(*ol), MEMF_PUBLIC | MEMF_CLEAR); + ol = odfs_amiga_alloc_mem(sizeof(*ol), MEMF_PUBLIC | MEMF_CLEAR); if (!ol) { release_entry(entry); return NULL; @@ -1295,7 +1298,7 @@ static void free_lock(handler_global_t *g, odfs_lock_t *ol) rebuild_volume_locklist(g, ol->entry->volume); release_volume_object(g, ol->entry->volume); release_entry(ol->entry); - FreeMem(ol, sizeof(*ol)); + odfs_amiga_free_mem(ol, sizeof(*ol)); } static odfs_lock_t *dup_lock(handler_global_t *g, odfs_lock_t *src) @@ -1305,7 +1308,7 @@ static odfs_lock_t *dup_lock(handler_global_t *g, odfs_lock_t *src) if (!src) return NULL; - ol = AllocMem(sizeof(*ol), MEMF_PUBLIC | MEMF_CLEAR); + ol = odfs_amiga_alloc_mem(sizeof(*ol), MEMF_PUBLIC | MEMF_CLEAR); if (!ol) return NULL; @@ -1335,7 +1338,7 @@ static odfs_fh_t *alloc_fh(handler_global_t *g, odfs_entry_t *entry, LONG access if (!entry) return NULL; - fh = AllocMem(sizeof(*fh), MEMF_PUBLIC | MEMF_CLEAR); + fh = odfs_amiga_alloc_mem(sizeof(*fh), MEMF_PUBLIC | MEMF_CLEAR); if (!fh) return NULL; @@ -1354,7 +1357,7 @@ static void free_fh(handler_global_t *g, odfs_fh_t *fh) Remove((struct Node *)&fh->node); release_volume_object(g, fh->entry->volume); release_entry(fh->entry); - FreeMem(fh, sizeof(*fh)); + odfs_amiga_free_mem(fh, sizeof(*fh)); } /* ------------------------------------------------------------------ */ @@ -1481,7 +1484,9 @@ static void fill_fib(struct FileInfoBlock *fib, const odfs_node_t *fnode) } fib->fib_DirEntryType = (fnode->kind == ODFS_NODE_DIR) ? ST_USERDIR : ST_FILE; +#ifndef __amigaos4__ fib->fib_EntryType = fib->fib_DirEntryType; +#endif fib->fib_Size = (LONG)fnode->size; fib->fib_NumBlocks = (fnode->size + 511) / 512; @@ -1588,7 +1593,9 @@ static void fill_root_fib(handler_global_t *g, struct FileInfoBlock *fib, fill_fib(fib, fnode); fib->fib_DirEntryType = ST_ROOT; +#ifndef __amigaos4__ fib->fib_EntryType = ST_ROOT; +#endif len = strlen(g->volname); if (len > 30) @@ -2152,12 +2159,12 @@ static int exall_fill_entry(struct ExAllData **cursor, LONG *remaining, p = ((UBYTE *)ed) + exall_fixed_size(data); if (data >= ED_COMMENT) { - ed->ed_Comment = p; + ed->ed_Comment = (STRPTR)p; memcpy(p, comment, comment_len); p += comment_len; } - ed->ed_Name = p; + ed->ed_Name = (STRPTR)p; memcpy(p, name, name_len); if (data >= ED_TYPE) @@ -2220,8 +2227,9 @@ static odfs_err_t exall_cb(const odfs_node_t *entry, void *ctx) return ODFS_ERR_EOF; } - if (UtilityBase && ec->control->eac_MatchFunc && - !CallHookPkt(ec->control->eac_MatchFunc, slot, &ec->data)) { + if (ec->control->eac_MatchFunc && + !odfs_amiga_call_hook_pkt(ec->control->eac_MatchFunc, slot, + &ec->data)) { ec->cursor = cursor_before; ec->remaining = remaining_before; return ODFS_OK; @@ -2826,7 +2834,8 @@ static struct DeviceNode *create_device_node(handler_global_t *g) namelen = 30; alloc_size = sizeof(*devnode) + 32u; - devnode = AllocMem(alloc_size, MEMF_PUBLIC | MEMF_CLEAR); + devnode = odfs_amiga_alloc_mem((ULONG)alloc_size, + MEMF_PUBLIC | MEMF_CLEAR); if (!devnode) return NULL; @@ -2835,7 +2844,9 @@ static struct DeviceNode *create_device_node(handler_global_t *g) memcpy(namebuf + 1, name, (size_t)namelen); devnode->dn_Next = 0; +#ifndef __amigaos4__ devnode->dn_Lock = g->devnode ? g->devnode->dn_Lock : 0; +#endif devnode->dn_Name = MKBADDR(namebuf); sync_device_node(g, devnode); @@ -2846,7 +2857,7 @@ static void destroy_device_node(struct DeviceNode *devnode) { if (!devnode) return; - FreeMem(devnode, sizeof(*devnode) + 32u); + odfs_amiga_free_mem(devnode, sizeof(*devnode) + 32u); } static void publish_device_node(handler_global_t *g) @@ -2958,7 +2969,7 @@ static struct DeviceList *create_volume_node(handler_global_t *g) * metacharacters such as parentheses. */ alloc_size = sizeof(*dl) + 32u; - dl = AllocMem(alloc_size, MEMF_PUBLIC | MEMF_CLEAR); + dl = odfs_amiga_alloc_mem((ULONG)alloc_size, MEMF_PUBLIC | MEMF_CLEAR); if (!dl) return NULL; @@ -2983,7 +2994,7 @@ static void destroy_volume_node(struct DeviceList *volnode) { if (!volnode) return; - FreeMem(volnode, sizeof(*volnode) + 32u); + odfs_amiga_free_mem(volnode, sizeof(*volnode) + 32u); } static void detach_volume_node(struct DeviceList *volnode) @@ -3077,7 +3088,7 @@ static void parse_control_string(handler_global_t *g __attribute__((unused)), return; rdargs->RDA_Flags |= RDAF_NOPROMPT; - rdargs->RDA_Source.CS_Buffer = (UBYTE *)buf; + rdargs->RDA_Source.CS_Buffer = (STRPTR)buf; rdargs->RDA_Source.CS_Length = len + 1; rdargs->RDA_Source.CS_CurChr = 0; @@ -3316,23 +3327,23 @@ static void unmount_volume(handler_global_t *g) static void install_media_change(handler_global_t *g) { - g->chgsigbit = AllocSignal(-1); + g->chgsigbit = odfs_amiga_alloc_signal(-1); if (g->chgsigbit == -1) return; - g->chgport = CreateMsgPort(); + g->chgport = odfs_amiga_create_msg_port(); if (!g->chgport) { - FreeSignal(g->chgsigbit); + odfs_amiga_free_signal(g->chgsigbit); g->chgsigbit = -1; return; } - g->chgreq = (struct IOStdReq *)CreateIORequest(g->chgport, - sizeof(struct IOStdReq)); + g->chgreq = (struct IOStdReq *)odfs_amiga_create_io_request( + g->chgport, sizeof(struct IOStdReq)); if (!g->chgreq) { - DeleteMsgPort(g->chgport); + odfs_amiga_delete_msg_port(g->chgport); g->chgport = NULL; - FreeSignal(g->chgsigbit); + odfs_amiga_free_signal(g->chgsigbit); g->chgsigbit = -1; return; } @@ -3342,13 +3353,10 @@ static void install_media_change(handler_global_t *g) g->chgreq->io_Unit = g->devreq->io_Unit; g->chgreq->io_Command = TD_ADDCHANGEINT; - g->changeint.is_Node.ln_Type = NT_INTERRUPT; - g->changeint.is_Node.ln_Pri = 0; - g->changeint.is_Node.ln_Name = (char *)"odfs-mediachange"; g->changeint_data.task = g->dosport->mp_SigTask; g->changeint_data.sigmask = 1UL << g->chgsigbit; - g->changeint.is_Data = &g->changeint_data; - g->changeint.is_Code = (void (*)(void))(APTR)changeint_handler; + odfs_amiga_init_interrupt(&g->changeint, "odfs-mediachange", + &g->changeint_data, changeint_handler); g->chgreq->io_Data = (APTR)&g->changeint; g->chgreq->io_Length = sizeof(g->changeint); g->chgreq->io_Flags = 0; @@ -3372,15 +3380,15 @@ static void remove_media_change(handler_global_t *g) /* don't CloseDevice — we don't own it */ g->chgreq->io_Device = NULL; g->chgreq->io_Unit = NULL; - DeleteIORequest((struct IORequest *)g->chgreq); + odfs_amiga_delete_io_request((struct IORequest *)g->chgreq); g->chgreq = NULL; } if (g->chgport) { - DeleteMsgPort(g->chgport); + odfs_amiga_delete_msg_port(g->chgport); g->chgport = NULL; } if (g->chgsigbit != -1) { - FreeSignal(g->chgsigbit); + odfs_amiga_free_signal(g->chgsigbit); g->chgsigbit = -1; } @@ -3469,13 +3477,13 @@ void handler_main(void) (void)version_string; /* ensure $VER is not optimized out */ - SysBase = *((struct ExecBase **)4L); + odfs_amiga_init_sysbase(); - g = AllocMem(sizeof(*g), MEMF_PUBLIC | MEMF_CLEAR); + g = odfs_amiga_alloc_mem(sizeof(*g), MEMF_PUBLIC | MEMF_CLEAR); if (!g) return; - g->sysbase = SysBase; + g->sysbase = odfs_amiga_sysbase(); g->locklist.mlh_Head = (struct MinNode *)&g->locklist.mlh_Tail; g->locklist.mlh_Tail = NULL; g->locklist.mlh_TailPred = (struct MinNode *)&g->locklist.mlh_Head; @@ -3529,21 +3537,19 @@ void handler_main(void) "ODFileSystem " ODFS_GIT_VERSION " (" ODFS_AMIGA_DATE ") starting..."); - DOSBase = (struct DosLibrary *)OpenLibrary((CONST_STRPTR)"dos.library", 36); - if (!DOSBase) { + if (!odfs_amiga_open_libraries()) { ODFS_ERROR(&g->log, ODFS_SUB_CORE, "open dos.library failed"); pkt->dp_Res1 = DOSFALSE; pkt->dp_Res2 = ERROR_INVALID_RESIDENT_LIBRARY; return_packet(g, pkt); - FreeMem(g, sizeof(*g)); + odfs_amiga_free_mem(g, sizeof(*g)); return; } - g->dosbase = DOSBase; - UtilityBase = OpenLibrary((CONST_STRPTR)"utility.library", 36); + g->dosbase = odfs_amiga_dosbase(); /* open device */ - g->devport = CreateMsgPort(); + g->devport = odfs_amiga_create_msg_port(); if (!g->devport) { ODFS_ERROR(&g->log, ODFS_SUB_IO, "CreateMsgPort failed for %s unit=%lu", @@ -3554,8 +3560,8 @@ void handler_main(void) goto shutdown; } - g->devreq = (struct IOStdReq *)CreateIORequest(g->devport, - sizeof(struct IOStdReq)); + g->devreq = (struct IOStdReq *)odfs_amiga_create_io_request( + g->devport, sizeof(struct IOStdReq)); if (!g->devreq) { ODFS_ERROR(&g->log, ODFS_SUB_IO, "CreateIORequest failed for %s unit=%lu", @@ -3602,11 +3608,11 @@ void handler_main(void) #define DMA_BUF_SECTORS 8 ULONG memtype = de->de_BufMemType | MEMF_PUBLIC; ULONG raw_size = DMA_BUF_SECTORS * g->sector_size + 15; - g->dma_buf_raw = (uint8_t *)AllocMem(raw_size, memtype); + g->dma_buf_raw = (uint8_t *)odfs_amiga_alloc_mem(raw_size, memtype); if (!g->dma_buf_raw) { /* fallback: try without specific memory type */ - g->dma_buf_raw = (uint8_t *)AllocMem(raw_size, - MEMF_PUBLIC); + g->dma_buf_raw = (uint8_t *)odfs_amiga_alloc_mem(raw_size, + MEMF_PUBLIC); } if (g->dma_buf_raw) { /* 16-byte align */ @@ -3695,21 +3701,19 @@ void handler_main(void) if (g->devreq) { if (g->devreq->io_Device) CloseDevice((struct IORequest *)g->devreq); - DeleteIORequest((struct IORequest *)g->devreq); + odfs_amiga_delete_io_request((struct IORequest *)g->devreq); } if (g->devport) - DeleteMsgPort(g->devport); + odfs_amiga_delete_msg_port(g->devport); /* free DMA bounce buffer */ if (g->dma_buf_raw) - FreeMem(g->dma_buf_raw, DMA_BUF_SECTORS * g->sector_size + 15); + odfs_amiga_free_mem(g->dma_buf_raw, + DMA_BUF_SECTORS * g->sector_size + 15); if (g->devnode) g->devnode->dn_Task = NULL; - if (UtilityBase) - CloseLibrary(UtilityBase); - - CloseLibrary((struct Library *)DOSBase); - FreeMem(g, sizeof(*g)); + odfs_amiga_close_libraries(); + odfs_amiga_free_mem(g, sizeof(*g)); } diff --git a/platform/amiga/os3/sys_compat.c b/platform/amiga/os3/sys_compat.c new file mode 100644 index 0000000..630a262 --- /dev/null +++ b/platform/amiga/os3/sys_compat.c @@ -0,0 +1,129 @@ +/* + * sys_compat.c - AmigaOS 3 / AROS integration helpers + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "sys_compat.h" + +#include +#include +#include + +struct ExecBase *SysBase; +struct DosLibrary *DOSBase; +struct Library *UtilityBase; + +void odfs_amiga_init_sysbase(void) +{ + SysBase = *((struct ExecBase **)4L); +} + +struct ExecBase *odfs_amiga_sysbase(void) +{ + return SysBase; +} + +struct DosLibrary *odfs_amiga_dosbase(void) +{ + return DOSBase; +} + +int odfs_amiga_open_libraries(void) +{ + DOSBase = (struct DosLibrary *)OpenLibrary((CONST_STRPTR)"dos.library", + 36); + if (!DOSBase) + return 0; + + UtilityBase = OpenLibrary((CONST_STRPTR)"utility.library", 36); + return 1; +} + +void odfs_amiga_close_libraries(void) +{ + if (UtilityBase) { + CloseLibrary(UtilityBase); + UtilityBase = NULL; + } + if (DOSBase) { + CloseLibrary((struct Library *)DOSBase); + DOSBase = NULL; + } +} + +void *odfs_amiga_alloc_mem(ULONG size, ULONG flags) +{ + return AllocMem(size, flags); +} + +void odfs_amiga_free_mem(void *ptr, ULONG size) +{ + if (ptr) + FreeMem(ptr, size); +} + +void *odfs_amiga_alloc_vec(ULONG size, ULONG flags) +{ + return AllocVec(size, flags); +} + +void odfs_amiga_free_vec(void *ptr) +{ + if (ptr) + FreeVec(ptr); +} + +struct MsgPort *odfs_amiga_create_msg_port(void) +{ + return CreateMsgPort(); +} + +void odfs_amiga_delete_msg_port(struct MsgPort *port) +{ + if (port) + DeleteMsgPort(port); +} + +struct IORequest *odfs_amiga_create_io_request(struct MsgPort *port, + ULONG size) +{ + return CreateIORequest(port, size); +} + +void odfs_amiga_delete_io_request(struct IORequest *req) +{ + if (req) + DeleteIORequest(req); +} + +LONG odfs_amiga_alloc_signal(LONG num) +{ + return AllocSignal(num); +} + +void odfs_amiga_free_signal(LONG num) +{ + if (num != -1) + FreeSignal(num); +} + +void odfs_amiga_init_interrupt(struct Interrupt *intr, + const char *name, + APTR data, + odfs_amiga_interrupt_fn code) +{ + intr->is_Node.ln_Type = NT_INTERRUPT; + intr->is_Node.ln_Pri = 0; + intr->is_Node.ln_Name = (char *)name; + intr->is_Data = data; + intr->is_Code = (void (*)(void))(APTR)code; +} + +ULONG odfs_amiga_call_hook_pkt(struct Hook *hook, APTR object, APTR message) +{ + if (!UtilityBase) + return 1; + + return CallHookPkt(hook, object, message); +} diff --git a/platform/amiga/os4/main.c b/platform/amiga/os4/main.c new file mode 100644 index 0000000..d13169d --- /dev/null +++ b/platform/amiga/os4/main.c @@ -0,0 +1,13 @@ +/* + * main.c - AmigaOS 4 handler entry wrapper + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "handler.h" + +int main(void) +{ + handler_main(); + return 0; +} diff --git a/platform/amiga/os4/sys_compat.c b/platform/amiga/os4/sys_compat.c new file mode 100644 index 0000000..cc96ab5 --- /dev/null +++ b/platform/amiga/os4/sys_compat.c @@ -0,0 +1,185 @@ +/* + * sys_compat.c - AmigaOS 4 integration helpers + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "sys_compat.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +struct ExecBase *SysBase; +struct DosLibrary *DOSBase; +struct UtilityBase *UtilityBase; + +static struct DOSIFace *dos_iface; +static struct UtilityIFace *utility_iface; + +void odfs_amiga_init_sysbase(void) +{ + SysBase = *((struct ExecBase **)4L); +} + +struct ExecBase *odfs_amiga_sysbase(void) +{ + return SysBase; +} + +struct DosLibrary *odfs_amiga_dosbase(void) +{ + return DOSBase; +} + +int odfs_amiga_open_libraries(void) +{ + DOSBase = (struct DosLibrary *)OpenLibrary((CONST_STRPTR)"dos.library", + 36); + if (!DOSBase) + return 0; + + dos_iface = (struct DOSIFace *)GetInterface((struct Library *)DOSBase, + (CONST_STRPTR)"main", 1, + NULL); + if (!dos_iface) { + CloseLibrary((struct Library *)DOSBase); + DOSBase = NULL; + return 0; + } + IDOS = dos_iface; + + UtilityBase = (struct UtilityBase *)OpenLibrary( + (CONST_STRPTR)"utility.library", 36); + if (UtilityBase) { + utility_iface = (struct UtilityIFace *)GetInterface( + (struct Library *)UtilityBase, (CONST_STRPTR)"main", 1, NULL); + if (!utility_iface) { + CloseLibrary((struct Library *)UtilityBase); + UtilityBase = NULL; + } + } + + return 1; +} + +void odfs_amiga_close_libraries(void) +{ + if (utility_iface) { + DropInterface((struct Interface *)utility_iface); + utility_iface = NULL; + } + if (UtilityBase) { + CloseLibrary((struct Library *)UtilityBase); + UtilityBase = NULL; + } + if (dos_iface) { + if (IDOS == dos_iface) + IDOS = NULL; + DropInterface((struct Interface *)dos_iface); + dos_iface = NULL; + } + if (DOSBase) { + CloseLibrary((struct Library *)DOSBase); + DOSBase = NULL; + } +} + +void *odfs_amiga_alloc_vec(ULONG size, ULONG flags) +{ + if (size == 0) + size = 1; + + if (flags & MEMF_CLEAR) { + return AllocVecTags(size, + AVT_Type, flags & ~MEMF_CLEAR, + AVT_ClearWithValue, 0, + TAG_END); + } + + return AllocVecTags(size, AVT_Type, flags, TAG_END); +} + +void odfs_amiga_free_vec(void *ptr) +{ + if (ptr) + FreeVec(ptr); +} + +void *odfs_amiga_alloc_mem(ULONG size, ULONG flags) +{ + return odfs_amiga_alloc_vec(size, flags); +} + +void odfs_amiga_free_mem(void *ptr, ULONG size) +{ + (void)size; + odfs_amiga_free_vec(ptr); +} + +struct MsgPort *odfs_amiga_create_msg_port(void) +{ + return AllocSysObjectTags(ASOT_PORT, + ASOPORT_AllocSig, TRUE, + ASOPORT_Action, PA_SIGNAL, + ASOPORT_Target, FindTask(NULL), + TAG_END); +} + +void odfs_amiga_delete_msg_port(struct MsgPort *port) +{ + if (port) + FreeSysObject(ASOT_PORT, port); +} + +struct IORequest *odfs_amiga_create_io_request(struct MsgPort *port, + ULONG size) +{ + return AllocSysObjectTags(ASOT_IOREQUEST, + ASOIOR_Size, size, + ASOIOR_ReplyPort, port, + TAG_END); +} + +void odfs_amiga_delete_io_request(struct IORequest *req) +{ + if (req) + FreeSysObject(ASOT_IOREQUEST, req); +} + +LONG odfs_amiga_alloc_signal(LONG num) +{ + return AllocSignal(num); +} + +void odfs_amiga_free_signal(LONG num) +{ + if (num != -1) + FreeSignal(num); +} + +void odfs_amiga_init_interrupt(struct Interrupt *intr, + const char *name, + APTR data, + odfs_amiga_interrupt_fn code) +{ + intr->is_Node.ln_Type = NT_INTERRUPT; + intr->is_Node.ln_Pri = 0; + intr->is_Node.ln_Name = (char *)name; + intr->is_Data = data; + intr->is_Code = (void (*)(void))(APTR)code; +} + +ULONG odfs_amiga_call_hook_pkt(struct Hook *hook, APTR object, APTR message) +{ + if (!utility_iface) + return 1; + + return utility_iface->CallHookPkt(hook, object, message); +} From e24328cfc3ef0786712a326fbadb687ca076c238 Mon Sep 17 00:00:00 2001 From: Stefan Reinauer Date: Thu, 21 May 2026 13:31:07 -0700 Subject: [PATCH 02/24] amiga: hide OS target conditionals Move the remaining AmigaOS 4 source differences out of the shared packet handler. Add per-target compatibility headers and route the interrupt callback ABI, FileInfoBlock entry type, and DeviceNode lock copying through sys_compat helpers. Document the CD0 default, OD0 fallback, and requester policy for unsafe conflicts. Name fallback should be quiet, while duplicate OD0 or same physical Device/Unit conflicts should fail visibly. --- platform/amiga/common/sys_compat.h | 6 ++++++ platform/amiga/handler.h | 4 +--- platform/amiga/handler_main.c | 22 +++++----------------- platform/amiga/os3/amiga_target_compat.h | 10 ++++++++++ platform/amiga/os3/sys_compat.c | 21 ++++++++++++++++++++- platform/amiga/os4/amiga_target_compat.h | 17 +++++++++++++++++ platform/amiga/os4/sys_compat.c | 13 +++++++++++++ 7 files changed, 72 insertions(+), 21 deletions(-) create mode 100644 platform/amiga/os3/amiga_target_compat.h create mode 100644 platform/amiga/os4/amiga_target_compat.h diff --git a/platform/amiga/common/sys_compat.h b/platform/amiga/common/sys_compat.h index 85eda5e..7f91114 100644 --- a/platform/amiga/common/sys_compat.h +++ b/platform/amiga/common/sys_compat.h @@ -16,10 +16,13 @@ #include #include #include +#include #include struct Hook; +struct FileInfoBlock; +struct DeviceNode; typedef LONG (*odfs_amiga_interrupt_fn)(APTR data); @@ -52,6 +55,9 @@ void odfs_amiga_init_interrupt(struct Interrupt *intr, APTR data, odfs_amiga_interrupt_fn code); +void odfs_amiga_set_fib_entry_type(struct FileInfoBlock *fib, LONG type); +void odfs_amiga_copy_device_lock(struct DeviceNode *dst, + const struct DeviceNode *src); ULONG odfs_amiga_call_hook_pkt(struct Hook *hook, APTR object, APTR message); #endif /* ODFS_AMIGA_SYS_COMPAT_H */ diff --git a/platform/amiga/handler.h b/platform/amiga/handler.h index 7b8e4cc..f1a33b1 100644 --- a/platform/amiga/handler.h +++ b/platform/amiga/handler.h @@ -14,10 +14,8 @@ #include #include #include -#ifdef __amigaos4__ -#include -#endif +#include "amiga_target_compat.h" #include "aros_compat.h" #include "odfs/api.h" diff --git a/platform/amiga/handler_main.c b/platform/amiga/handler_main.c index a45b075..7c8e174 100644 --- a/platform/amiga/handler_main.c +++ b/platform/amiga/handler_main.c @@ -102,13 +102,7 @@ static int scsi_is_unsupported_command(const uint8_t *sense) return ((sense[2] & 0x0f) == 0x05 && sense[12] == 0x20); } -static LONG changeint_handler( -#ifdef __amigaos4__ - APTR data -#else - APTR data asm("a1") -#endif -) +static LONG changeint_signal(APTR data) { odfs_changeint_data_t *ci = data; @@ -1484,9 +1478,7 @@ static void fill_fib(struct FileInfoBlock *fib, const odfs_node_t *fnode) } fib->fib_DirEntryType = (fnode->kind == ODFS_NODE_DIR) ? ST_USERDIR : ST_FILE; -#ifndef __amigaos4__ - fib->fib_EntryType = fib->fib_DirEntryType; -#endif + odfs_amiga_set_fib_entry_type(fib, fib->fib_DirEntryType); fib->fib_Size = (LONG)fnode->size; fib->fib_NumBlocks = (fnode->size + 511) / 512; @@ -1593,9 +1585,7 @@ static void fill_root_fib(handler_global_t *g, struct FileInfoBlock *fib, fill_fib(fib, fnode); fib->fib_DirEntryType = ST_ROOT; -#ifndef __amigaos4__ - fib->fib_EntryType = ST_ROOT; -#endif + odfs_amiga_set_fib_entry_type(fib, ST_ROOT); len = strlen(g->volname); if (len > 30) @@ -2844,9 +2834,7 @@ static struct DeviceNode *create_device_node(handler_global_t *g) memcpy(namebuf + 1, name, (size_t)namelen); devnode->dn_Next = 0; -#ifndef __amigaos4__ - devnode->dn_Lock = g->devnode ? g->devnode->dn_Lock : 0; -#endif + odfs_amiga_copy_device_lock(devnode, g->devnode); devnode->dn_Name = MKBADDR(namebuf); sync_device_node(g, devnode); @@ -3356,7 +3344,7 @@ static void install_media_change(handler_global_t *g) g->changeint_data.task = g->dosport->mp_SigTask; g->changeint_data.sigmask = 1UL << g->chgsigbit; odfs_amiga_init_interrupt(&g->changeint, "odfs-mediachange", - &g->changeint_data, changeint_handler); + &g->changeint_data, changeint_signal); g->chgreq->io_Data = (APTR)&g->changeint; g->chgreq->io_Length = sizeof(g->changeint); g->chgreq->io_Flags = 0; diff --git a/platform/amiga/os3/amiga_target_compat.h b/platform/amiga/os3/amiga_target_compat.h new file mode 100644 index 0000000..e4a1634 --- /dev/null +++ b/platform/amiga/os3/amiga_target_compat.h @@ -0,0 +1,10 @@ +/* + * amiga_target_compat.h - AmigaOS 3 / AROS source compatibility + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef ODFS_AMIGA_TARGET_COMPAT_H +#define ODFS_AMIGA_TARGET_COMPAT_H + +#endif /* ODFS_AMIGA_TARGET_COMPAT_H */ diff --git a/platform/amiga/os3/sys_compat.c b/platform/amiga/os3/sys_compat.c index 630a262..c4d7a7d 100644 --- a/platform/amiga/os3/sys_compat.c +++ b/platform/amiga/os3/sys_compat.c @@ -14,6 +14,13 @@ struct ExecBase *SysBase; struct DosLibrary *DOSBase; struct Library *UtilityBase; +static odfs_amiga_interrupt_fn interrupt_code; + +static LONG odfs_amiga_interrupt_entry(APTR data asm("a1")) +{ + return interrupt_code ? interrupt_code(data) : 0; +} + void odfs_amiga_init_sysbase(void) { SysBase = *((struct ExecBase **)4L); @@ -113,11 +120,23 @@ void odfs_amiga_init_interrupt(struct Interrupt *intr, APTR data, odfs_amiga_interrupt_fn code) { + interrupt_code = code; intr->is_Node.ln_Type = NT_INTERRUPT; intr->is_Node.ln_Pri = 0; intr->is_Node.ln_Name = (char *)name; intr->is_Data = data; - intr->is_Code = (void (*)(void))(APTR)code; + intr->is_Code = (void (*)(void))(APTR)odfs_amiga_interrupt_entry; +} + +void odfs_amiga_set_fib_entry_type(struct FileInfoBlock *fib, LONG type) +{ + fib->fib_EntryType = type; +} + +void odfs_amiga_copy_device_lock(struct DeviceNode *dst, + const struct DeviceNode *src) +{ + dst->dn_Lock = src ? src->dn_Lock : 0; } ULONG odfs_amiga_call_hook_pkt(struct Hook *hook, APTR object, APTR message) diff --git a/platform/amiga/os4/amiga_target_compat.h b/platform/amiga/os4/amiga_target_compat.h new file mode 100644 index 0000000..aa053b5 --- /dev/null +++ b/platform/amiga/os4/amiga_target_compat.h @@ -0,0 +1,17 @@ +/* + * amiga_target_compat.h - AmigaOS 4 source compatibility + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef ODFS_AMIGA_TARGET_COMPAT_H +#define ODFS_AMIGA_TARGET_COMPAT_H + +/* + * The current shared packet handler still uses classic names such as + * DeviceList and ACTION_DIE. Keep that compatibility local to the OS4 + * frontend while the native vector-port frontend is being split out. + */ +#include + +#endif /* ODFS_AMIGA_TARGET_COMPAT_H */ diff --git a/platform/amiga/os4/sys_compat.c b/platform/amiga/os4/sys_compat.c index cc96ab5..bb7bdd8 100644 --- a/platform/amiga/os4/sys_compat.c +++ b/platform/amiga/os4/sys_compat.c @@ -176,6 +176,19 @@ void odfs_amiga_init_interrupt(struct Interrupt *intr, intr->is_Code = (void (*)(void))(APTR)code; } +void odfs_amiga_set_fib_entry_type(struct FileInfoBlock *fib, LONG type) +{ + (void)fib; + (void)type; +} + +void odfs_amiga_copy_device_lock(struct DeviceNode *dst, + const struct DeviceNode *src) +{ + (void)dst; + (void)src; +} + ULONG odfs_amiga_call_hook_pkt(struct Hook *hook, APTR object, APTR message) { if (!utility_iface) From 1e72834f65dd11a55961940849b092102052c549 Mon Sep 17 00:00:00 2001 From: Stefan Reinauer Date: Thu, 21 May 2026 13:48:06 -0700 Subject: [PATCH 03/24] amiga: scaffold OS4 vector frontend Teach the OS4 entry wrapper to receive and validate the initial ACTION_STARTUP packet before passing it into the shared handler loop. Add a native FileSystemVectorPort template with explicit unsupported-vector stubs. This gives the next slice a real OS4 frontend to map onto shared read-only operations. --- Makefile | 3 +- platform/amiga/handler.h | 1 + platform/amiga/handler_main.c | 15 +- platform/amiga/os4/main.c | 58 ++- platform/amiga/os4/vector_port.c | 621 +++++++++++++++++++++++++++++++ platform/amiga/os4/vector_port.h | 17 + 6 files changed, 709 insertions(+), 6 deletions(-) create mode 100644 platform/amiga/os4/vector_port.c create mode 100644 platform/amiga/os4/vector_port.h diff --git a/Makefile b/Makefile index f6cc7e1..f14e186 100644 --- a/Makefile +++ b/Makefile @@ -197,7 +197,8 @@ AMIGA_SRCS = platform/amiga/handler_main.c \ platform/amiga/printf_local.c \ platform/amiga/$(AMIGA_OSDIR)/sys_compat.c ifeq ($(AMIGA_TARGET),os4) -AMIGA_SRCS += platform/amiga/os4/main.c +AMIGA_SRCS += platform/amiga/os4/main.c \ + platform/amiga/os4/vector_port.c else AMIGA_SRCS += platform/amiga/libc_stubs.c endif diff --git a/platform/amiga/handler.h b/platform/amiga/handler.h index f1a33b1..7393a53 100644 --- a/platform/amiga/handler.h +++ b/platform/amiga/handler.h @@ -199,5 +199,6 @@ static inline LONG odfs_err_to_dos(odfs_err_t err) /* handler entry point (called from startup.S) */ void handler_main(void); +void handler_main_startup(struct Message *startup_msg); #endif /* ODFS_HANDLER_H */ diff --git a/platform/amiga/handler_main.c b/platform/amiga/handler_main.c index 7c8e174..0361c3c 100644 --- a/platform/amiga/handler_main.c +++ b/platform/amiga/handler_main.c @@ -3452,7 +3452,7 @@ static void handle_media_change(handler_global_t *g) /* handler main entry point */ /* ------------------------------------------------------------------ */ -void handler_main(void) +void handler_main_startup(struct Message *startup_msg) { handler_global_t *g; struct Message *msg; @@ -3491,8 +3491,12 @@ void handler_main(void) } /* wait for startup packet */ - WaitPort(g->dosport); - msg = GetMsg(g->dosport); + if (startup_msg) { + msg = startup_msg; + } else { + WaitPort(g->dosport); + msg = GetMsg(g->dosport); + } pkt = (struct DosPacket *)msg->mn_Node.ln_Name; g->devnode = (struct DeviceNode *)BADDR(pkt->dp_Arg3); @@ -3705,3 +3709,8 @@ void handler_main(void) odfs_amiga_close_libraries(); odfs_amiga_free_mem(g, sizeof(*g)); } + +void handler_main(void) +{ + handler_main_startup(NULL); +} diff --git a/platform/amiga/os4/main.c b/platform/amiga/os4/main.c index d13169d..d87114b 100644 --- a/platform/amiga/os4/main.c +++ b/platform/amiga/os4/main.c @@ -6,8 +6,62 @@ #include "handler.h" +#include +#include + +#include + +static void return_startup_packet(struct DosPacket *pkt, + LONG res1, + LONG res2) +{ + struct MsgPort *replyport; + struct Message *msg; + + if (!pkt || !pkt->dp_Link || !pkt->dp_Port) + return; + + replyport = pkt->dp_Port; + msg = pkt->dp_Link; + + pkt->dp_Res1 = res1; + pkt->dp_Res2 = res2; + msg->mn_Node.ln_Name = (char *)pkt; + msg->mn_Node.ln_Succ = NULL; + msg->mn_Node.ln_Pred = NULL; + PutMsg(replyport, msg); +} + int main(void) { - handler_main(); - return 0; + struct Process *proc; + struct Message *msg; + struct DosPacket *pkt; + + proc = (struct Process *)FindTask(NULL); + if (!proc) + return RETURN_FAIL; + + WaitPort(&proc->pr_MsgPort); + msg = GetMsg(&proc->pr_MsgPort); + if (!msg) { + proc->pr_Result2 = ERROR_OBJECT_WRONG_TYPE; + return RETURN_FAIL; + } + + if (!msg->mn_Node.ln_Name) { + ReplyMsg(msg); + proc->pr_Result2 = ERROR_OBJECT_WRONG_TYPE; + return RETURN_FAIL; + } + + pkt = (struct DosPacket *)msg->mn_Node.ln_Name; + if (pkt->dp_Type != ACTION_STARTUP) { + return_startup_packet(pkt, DOSFALSE, ERROR_ACTION_NOT_KNOWN); + proc->pr_Result2 = ERROR_ACTION_NOT_KNOWN; + return RETURN_FAIL; + } + + handler_main_startup(msg); + return RETURN_OK; } diff --git a/platform/amiga/os4/vector_port.c b/platform/amiga/os4/vector_port.c new file mode 100644 index 0000000..0d5d97b --- /dev/null +++ b/platform/amiga/os4/vector_port.c @@ -0,0 +1,621 @@ +/* + * vector_port.c - AmigaOS 4 filesystem vector-port frontend + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "vector_port.h" + +#include +#include +#include +#include + +#include + +#include + +static void set_unsupported(struct FSVP *vp, int32 *res2) +{ + (void)vp; + + if (res2) + *res2 = ERROR_ACTION_NOT_KNOWN; +} + +static struct Lock *vp_lock(struct FSVP *vp, + int32 *res2, + struct Lock *rel_lock, + CONST_STRPTR obj, + int32 mode) +{ + set_unsupported(vp, res2); + (void)rel_lock; + (void)obj; + (void)mode; + return NULL; +} + +static int32 vp_unlock(struct FSVP *vp, int32 *res2, struct Lock *lock) +{ + set_unsupported(vp, res2); + (void)lock; + return DOSFALSE; +} + +static struct Lock *vp_dup_lock(struct FSVP *vp, + int32 *res2, + struct Lock *lock) +{ + set_unsupported(vp, res2); + (void)lock; + return NULL; +} + +static struct Lock *vp_create_dir(struct FSVP *vp, + int32 *res2, + struct Lock *rel_lock, + CONST_STRPTR obj) +{ + set_unsupported(vp, res2); + (void)rel_lock; + (void)obj; + return NULL; +} + +static struct Lock *vp_parent_dir(struct FSVP *vp, + int32 *res2, + struct Lock *dirlock) +{ + set_unsupported(vp, res2); + (void)dirlock; + return NULL; +} + +static struct Lock *vp_dup_lock_from_fh(struct FSVP *vp, + int32 *res2, + struct FileHandle *filehandle) +{ + set_unsupported(vp, res2); + (void)filehandle; + return NULL; +} + +static int32 vp_open_from_lock(struct FSVP *vp, + int32 *res2, + struct FileHandle *file, + struct Lock *lock) +{ + set_unsupported(vp, res2); + (void)file; + (void)lock; + return DOSFALSE; +} + +static struct Lock *vp_parent_of_fh(struct FSVP *vp, + int32 *res2, + struct FileHandle *file) +{ + set_unsupported(vp, res2); + (void)file; + return NULL; +} + +static int32 vp_open(struct FSVP *vp, + int32 *res2, + struct FileHandle *fh, + struct Lock *rel_dir, + CONST_STRPTR obj, + int32 mode) +{ + set_unsupported(vp, res2); + (void)fh; + (void)rel_dir; + (void)obj; + (void)mode; + return DOSFALSE; +} + +static int32 vp_close(struct FSVP *vp, int32 *res2, struct FileHandle *file) +{ + set_unsupported(vp, res2); + (void)file; + return DOSFALSE; +} + +static int32 vp_delete(struct FSVP *vp, + int32 *res2, + struct Lock *rel_dirlock, + CONST_STRPTR obj) +{ + set_unsupported(vp, res2); + (void)rel_dirlock; + (void)obj; + return DOSFALSE; +} + +static int32 vp_read(struct FSVP *vp, + int32 *res2, + struct FileHandle *file, + STRPTR buffer, + int32 numbytes) +{ + set_unsupported(vp, res2); + (void)file; + (void)buffer; + (void)numbytes; + return 0; +} + +static int32 vp_write(struct FSVP *vp, + int32 *res2, + struct FileHandle *file, + STRPTR buffer, + int32 numbytes) +{ + set_unsupported(vp, res2); + (void)file; + (void)buffer; + (void)numbytes; + return 0; +} + +static int32 vp_flush(struct FSVP *vp, int32 *res2) +{ + set_unsupported(vp, res2); + return DOSFALSE; +} + +static int32 vp_change_file_position(struct FSVP *vp, + int32 *res2, + struct FileHandle *file, + int32 mode, + int64 position) +{ + set_unsupported(vp, res2); + (void)file; + (void)mode; + (void)position; + return DOSFALSE; +} + +static int32 vp_change_file_size(struct FSVP *vp, + int32 *res2, + struct FileHandle *file, + int32 mode, + int64 size) +{ + set_unsupported(vp, res2); + (void)file; + (void)mode; + (void)size; + return DOSFALSE; +} + +static int64 vp_get_file_position(struct FSVP *vp, + int32 *res2, + struct FileHandle *file) +{ + set_unsupported(vp, res2); + (void)file; + return 0; +} + +static int64 vp_get_file_size(struct FSVP *vp, + int32 *res2, + struct FileHandle *file) +{ + set_unsupported(vp, res2); + (void)file; + return 0; +} + +static int32 vp_change_lock_mode(struct FSVP *vp, + int32 *res2, + struct Lock *lock, + int32 new_lock_mode) +{ + set_unsupported(vp, res2); + (void)lock; + (void)new_lock_mode; + return DOSFALSE; +} + +static int32 vp_change_file_mode(struct FSVP *vp, + int32 *res2, + struct FileHandle *fh, + int32 new_lock_mode) +{ + set_unsupported(vp, res2); + (void)fh; + (void)new_lock_mode; + return DOSFALSE; +} + +static int32 vp_set_date(struct FSVP *vp, + int32 *res2, + struct Lock *rel_dirlock, + CONST_STRPTR name, + const struct DateStamp *ds) +{ + set_unsupported(vp, res2); + (void)rel_dirlock; + (void)name; + (void)ds; + return DOSFALSE; +} + +static int32 vp_set_protection(struct FSVP *vp, + int32 *res2, + struct Lock *rel_dirlock, + CONST_STRPTR name, + uint32 mask) +{ + set_unsupported(vp, res2); + (void)rel_dirlock; + (void)name; + (void)mask; + return DOSFALSE; +} + +static int32 vp_set_comment(struct FSVP *vp, + int32 *res2, + struct Lock *rel_dirlock, + CONST_STRPTR name, + CONST_STRPTR comment) +{ + set_unsupported(vp, res2); + (void)rel_dirlock; + (void)name; + (void)comment; + return DOSFALSE; +} + +static int32 vp_set_group(struct FSVP *vp, + int32 *res2, + struct Lock *rel_dirlock, + CONST_STRPTR name, + uint32 group) +{ + set_unsupported(vp, res2); + (void)rel_dirlock; + (void)name; + (void)group; + return DOSFALSE; +} + +static int32 vp_set_user(struct FSVP *vp, + int32 *res2, + struct Lock *rel_dirlock, + CONST_STRPTR name, + uint32 user) +{ + set_unsupported(vp, res2); + (void)rel_dirlock; + (void)name; + (void)user; + return DOSFALSE; +} + +static int32 vp_rename(struct FSVP *vp, + int32 *res2, + struct Lock *src_rel, + CONST_STRPTR src, + struct Lock *dst_rel, + CONST_STRPTR dst) +{ + set_unsupported(vp, res2); + (void)src_rel; + (void)src; + (void)dst_rel; + (void)dst; + return DOSFALSE; +} + +static int32 vp_create_soft_link(struct FSVP *vp, + int32 *res2, + struct Lock *rel_dirlock, + CONST_STRPTR linkname, + CONST_STRPTR dest_obj) +{ + set_unsupported(vp, res2); + (void)rel_dirlock; + (void)linkname; + (void)dest_obj; + return DOSFALSE; +} + +static int32 vp_create_hard_link(struct FSVP *vp, + int32 *res2, + struct Lock *rel_dirlock, + CONST_STRPTR linkname, + struct Lock *dest_obj) +{ + set_unsupported(vp, res2); + (void)rel_dirlock; + (void)linkname; + (void)dest_obj; + return DOSFALSE; +} + +static int32 vp_read_soft_link(struct FSVP *vp, + int32 *res2, + struct Lock *rel_dir, + CONST_STRPTR linkname, + STRPTR buf, + int32 bufsize) +{ + set_unsupported(vp, res2); + (void)rel_dir; + (void)linkname; + (void)buf; + (void)bufsize; + return DOSFALSE; +} + +static int32 vp_same_lock(struct FSVP *vp, + int32 *res2, + struct Lock *lock1, + struct Lock *lock2) +{ + set_unsupported(vp, res2); + (void)lock1; + (void)lock2; + return DOSFALSE; +} + +static int32 vp_same_file(struct FSVP *vp, + int32 *res2, + struct FileHandle *fh1, + struct FileHandle *fh2) +{ + set_unsupported(vp, res2); + (void)fh1; + (void)fh2; + return DOSFALSE; +} + +static int32 vp_filesystem_attr(struct FSVP *vp, + int32 *res2, + struct TagItem *taglist) +{ + set_unsupported(vp, res2); + (void)taglist; + return DOSFALSE; +} + +static int32 vp_volume_info_data(struct FSVP *vp, + int32 *res2, + struct InfoData *info) +{ + set_unsupported(vp, res2); + (void)info; + return DOSFALSE; +} + +static int32 vp_device_info_data(struct FSVP *vp, + int32 *res2, + struct InfoData *info) +{ + set_unsupported(vp, res2); + (void)info; + return DOSFALSE; +} + +static struct ExamineData *vp_examine_obj(struct FSVP *vp, + int32 *res2, + struct Lock *lock, + CONST_STRPTR object) +{ + set_unsupported(vp, res2); + (void)lock; + (void)object; + return NULL; +} + +static struct ExamineData *vp_examine_lock(struct FSVP *vp, + int32 *res2, + struct Lock *lock) +{ + set_unsupported(vp, res2); + (void)lock; + return NULL; +} + +static struct ExamineData *vp_examine_file(struct FSVP *vp, + int32 *res2, + struct FileHandle *file) +{ + set_unsupported(vp, res2); + (void)file; + return NULL; +} + +static int32 vp_examine_dir(struct FSVP *vp, + int32 *res2, + struct PRIVATE_ExamineDirContext *ctx) +{ + set_unsupported(vp, res2); + (void)ctx; + return DOSFALSE; +} + +static int32 vp_inhibit(struct FSVP *vp, int32 *res2, int32 inhibit_state) +{ + set_unsupported(vp, res2); + (void)inhibit_state; + return DOSFALSE; +} + +static int32 vp_write_protect(struct FSVP *vp, + int32 *res2, + int32 wp_state, + uint32 passkey) +{ + set_unsupported(vp, res2); + (void)wp_state; + (void)passkey; + return DOSFALSE; +} + +static int32 vp_format(struct FSVP *vp, + int32 *res2, + CONST_STRPTR new_volname, + uint32 dostype, + uint32 spare) +{ + set_unsupported(vp, res2); + (void)new_volname; + (void)dostype; + (void)spare; + return DOSFALSE; +} + +static int32 vp_serialize(struct FSVP *vp, int32 *res2) +{ + set_unsupported(vp, res2); + return DOSFALSE; +} + +static int32 vp_relabel(struct FSVP *vp, + int32 *res2, + CONST_STRPTR new_volumename) +{ + set_unsupported(vp, res2); + (void)new_volumename; + return DOSFALSE; +} + +static int32 vp_add_notify(struct FSVP *vp, + int32 *res2, + struct NotifyRequest *nr) +{ + set_unsupported(vp, res2); + (void)nr; + return DOSFALSE; +} + +static int32 vp_remove_notify(struct FSVP *vp, + int32 *res2, + struct NotifyRequest *nr) +{ + set_unsupported(vp, res2); + (void)nr; + return DOSFALSE; +} + +static int32 vp_lock_record(struct FSVP *vp, + int32 *res2, + struct FileHandle *file, + int64 offset, + int64 length, + uint32 mode, + uint32 timeout) +{ + set_unsupported(vp, res2); + (void)file; + (void)offset; + (void)length; + (void)mode; + (void)timeout; + return DOSFALSE; +} + +static int32 vp_unlock_record(struct FSVP *vp, + int32 *res2, + struct FileHandle *file, + int64 offset, + int64 length) +{ + set_unsupported(vp, res2); + (void)file; + (void)offset; + (void)length; + return DOSFALSE; +} + +static const struct FileSystemVectors odfs_os4_vectors = { + .StructSize = sizeof(struct FileSystemVectors), + .Version = FS_VECTORPORT_VERSION, + .FSPrivate = NULL, + .Reserved = {0, 0, 0}, + .DOSPrivate = NULL, + .DOSEmulatePacket = NULL, + .FSLock = vp_lock, + .FSUnLock = vp_unlock, + .FSDupLock = vp_dup_lock, + .FSCreateDir = vp_create_dir, + .FSParentDir = vp_parent_dir, + .FSDupLockFromFH = vp_dup_lock_from_fh, + .FSOpenFromLock = vp_open_from_lock, + .FSParentOfFH = vp_parent_of_fh, + .FSOpen = vp_open, + .FSClose = vp_close, + .FSDelete = vp_delete, + .FSRead = vp_read, + .FSWrite = vp_write, + .FSFlush = vp_flush, + .FSChangeFilePosition = vp_change_file_position, + .FSChangeFileSize = vp_change_file_size, + .FSGetFilePosition = vp_get_file_position, + .FSGetFileSize = vp_get_file_size, + .FSChangeLockMode = vp_change_lock_mode, + .FSChangeFileMode = vp_change_file_mode, + .FSSetDate = vp_set_date, + .FSSetProtection = vp_set_protection, + .FSSetComment = vp_set_comment, + .FSSetGroup = vp_set_group, + .FSSetUser = vp_set_user, + .FSRename = vp_rename, + .FSCreateSoftLink = vp_create_soft_link, + .FSCreateHardLink = vp_create_hard_link, + .FSReadSoftLink = vp_read_soft_link, + .FSSameLock = vp_same_lock, + .FSSameFile = vp_same_file, + .FSFileSystemAttr = vp_filesystem_attr, + .FSVolumeInfoData = vp_volume_info_data, + .FSDeviceInfoData = vp_device_info_data, + .FSReserved1 = NULL, + .FSExamineObj = vp_examine_obj, + .FSExamineLock = vp_examine_lock, + .FSExamineFile = vp_examine_file, + .FSExamineDir = vp_examine_dir, + .FSInhibit = vp_inhibit, + .FSWriteProtect = vp_write_protect, + .FSFormat = vp_format, + .FSSerialize = vp_serialize, + .FSRelabel = vp_relabel, + .FSReserved3 = NULL, + .FSAddNotify = vp_add_notify, + .FSRemoveNotify = vp_remove_notify, + .FSLockRecord = vp_lock_record, + .FSUnLockRecord = vp_unlock_record, + .End_Marker = -1 +}; + +const struct FileSystemVectors *odfs_os4_vector_template(void) +{ + return &odfs_os4_vectors; +} + +struct FileSystemVectorPort *odfs_os4_alloc_vector_port(APTR fs_private) +{ + struct FileSystemVectorPort *vp; + + vp = AllocDosObjectTags(DOS_FSVECTORPORT, + ADO_Vectors, + (ULONG)&odfs_os4_vectors, + TAG_END); + if (!vp) + return NULL; + + vp->MP.mp_Node.ln_Type = NT_FILESYSTEM; + vp->FSV.FSPrivate = fs_private; + return vp; +} + +void odfs_os4_free_vector_port(struct FileSystemVectorPort *vp) +{ + if (vp) + FreeDosObject(DOS_FSVECTORPORT, vp); +} diff --git a/platform/amiga/os4/vector_port.h b/platform/amiga/os4/vector_port.h new file mode 100644 index 0000000..2b92cb8 --- /dev/null +++ b/platform/amiga/os4/vector_port.h @@ -0,0 +1,17 @@ +/* + * vector_port.h - AmigaOS 4 filesystem vector-port frontend + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef ODFS_AMIGA_OS4_VECTOR_PORT_H +#define ODFS_AMIGA_OS4_VECTOR_PORT_H + +#include +#include + +const struct FileSystemVectors *odfs_os4_vector_template(void); +struct FileSystemVectorPort *odfs_os4_alloc_vector_port(APTR fs_private); +void odfs_os4_free_vector_port(struct FileSystemVectorPort *vp); + +#endif /* ODFS_AMIGA_OS4_VECTOR_PORT_H */ From 241dcf01ab0034ed4470ca6afa6cb205b847fd72 Mon Sep 17 00:00:00 2001 From: Stefan Reinauer Date: Thu, 21 May 2026 15:23:31 -0700 Subject: [PATCH 04/24] amiga: map OS4 vectors to shared operations Move the first native OS4 vector callbacks onto shared handler operations for locks, file handles, reads, seeks, and info. Keep the packet frontend on the same operations so behavior stays aligned. Add native ExamineData allocation for object, lock, and filehandle examine calls. Leave directory iteration unsupported until the OS4 private context ownership rules are confirmed. --- platform/amiga/handler.h | 76 +- platform/amiga/handler_main.c | 1162 ++++++++++++++++++++---------- platform/amiga/os4/vector_port.c | 330 +++++++-- 3 files changed, 1116 insertions(+), 452 deletions(-) diff --git a/platform/amiga/handler.h b/platform/amiga/handler.h index 7393a53..c45fe6f 100644 --- a/platform/amiga/handler.h +++ b/platform/amiga/handler.h @@ -136,7 +136,7 @@ struct odfs_fh { struct MinNode node; /* for tracking */ odfs_entry_t *entry; /* shared object metadata */ LONG access; /* originating DOS access mode */ - ULONG pos; /* current read position */ + uint64_t pos; /* current read position */ }; /* ---- helper macros ---- */ @@ -150,6 +150,15 @@ struct odfs_fh { #define LOCK_TO_BPTR(ol) \ ((ol) ? MKBADDR(&(ol)->lock) : 0) +/* Convert a direct DOS lock pointer to our odfs_lock_t */ +#define LOCK_FROM_PTR(ptr) \ + ((ptr) ? (odfs_lock_t *)((UBYTE *)(ptr) - \ + offsetof(odfs_lock_t, lock)) : NULL) + +/* Convert odfs_lock_t to a direct DOS lock pointer */ +#define LOCK_TO_PTR(ol) \ + ((ol) ? &(ol)->lock : NULL) + /* BCPL string to C string (AROS-compatible) */ static inline void bstr_to_cstr(BSTR bstr, char *buf, int bufsize) { @@ -197,6 +206,71 @@ static inline LONG odfs_err_to_dos(odfs_err_t err) } } +/* shared operations used by packet and OS4 vector frontends */ +LONG odfs_handler_lock_object(handler_global_t *g, + odfs_lock_t *parent_lock, + const char *path, + LONG access, + odfs_lock_t **out); +LONG odfs_handler_free_lock_object(handler_global_t *g, odfs_lock_t *ol); +LONG odfs_handler_dup_lock_object(handler_global_t *g, + odfs_lock_t *src, + odfs_lock_t **out); +LONG odfs_handler_dup_lock_from_fh(handler_global_t *g, + odfs_fh_t *fh, + odfs_lock_t **out); +LONG odfs_handler_parent_lock_object(handler_global_t *g, + odfs_lock_t *ol, + odfs_lock_t **out); +LONG odfs_handler_parent_fh_object(handler_global_t *g, + odfs_fh_t *fh, + odfs_lock_t **out); +LONG odfs_handler_same_lock_object(handler_global_t *g, + odfs_lock_t *l1, + odfs_lock_t *l2, + LONG *same_result); +LONG odfs_handler_same_file_object(handler_global_t *g, + odfs_fh_t *fh1, + odfs_fh_t *fh2, + LONG *same_result); +LONG odfs_handler_open_object(handler_global_t *g, + odfs_lock_t *dirlock, + const char *path, + LONG mode, + odfs_fh_t **out); +LONG odfs_handler_open_from_lock_object(handler_global_t *g, + odfs_lock_t *ol, + odfs_fh_t **out); +LONG odfs_handler_close_object(handler_global_t *g, odfs_fh_t *fh); +LONG odfs_handler_read_object(handler_global_t *g, + odfs_fh_t *fh, + void *buf, + LONG len, + LONG *actual_out); +LONG odfs_handler_seek_object(handler_global_t *g, + odfs_fh_t *fh, + int64_t offset, + LONG mode, + int64_t *oldpos_out); +LONG odfs_handler_get_file_position(handler_global_t *g, + odfs_fh_t *fh, + int64_t *pos_out); +LONG odfs_handler_get_file_size(handler_global_t *g, + odfs_fh_t *fh, + int64_t *size_out); +LONG odfs_handler_fill_info(handler_global_t *g, + odfs_lock_t *ol, + struct InfoData *info); +LONG odfs_handler_get_lock_node(handler_global_t *g, + odfs_lock_t *ol, + const odfs_node_t **node_out); +LONG odfs_handler_get_fh_node(handler_global_t *g, + odfs_fh_t *fh, + const odfs_node_t **node_out); +ULONG odfs_handler_node_key(const odfs_node_t *node); +ULONG odfs_handler_node_protection(const odfs_node_t *node); +void odfs_handler_node_date(const odfs_node_t *node, struct DateStamp *ds); + /* handler entry point (called from startup.S) */ void handler_main(void); void handler_main_startup(struct Message *startup_msg); diff --git a/platform/amiga/handler_main.c b/platform/amiga/handler_main.c index 0361c3c..2b5b09f 100644 --- a/platform/amiga/handler_main.c +++ b/platform/amiga/handler_main.c @@ -1154,6 +1154,104 @@ static ULONG amiga_node_key(const odfs_node_t *node) return key; } +ULONG odfs_handler_node_key(const odfs_node_t *node) +{ + return amiga_node_key(node); +} + +ULONG odfs_handler_node_protection(const odfs_node_t *node) +{ + ULONG prot = 0; + + if (!node) + return 0; + + if (node->amiga_as.has_protection) { + prot = node->amiga_as.protection[3]; + } else if (node->mode != 0) { + /* MakeCD table 6 default mapping from PX to classic Amiga bits. */ + if ((node->mode & 0200) == 0) + prot |= FIBF_DELETE | FIBF_WRITE; + if ((node->mode & 0100) == 0) + prot |= FIBF_EXECUTE; + if ((node->mode & 0400) == 0) + prot |= FIBF_READ; +#ifdef FIBF_GRP_DELETE + if (node->mode & 0020) + prot |= FIBF_GRP_DELETE; +#endif +#ifdef FIBF_GRP_EXECUTE + if (node->mode & 0010) + prot |= FIBF_GRP_EXECUTE; +#endif +#ifdef FIBF_GRP_WRITE + if (node->mode & 0020) + prot |= FIBF_GRP_WRITE; +#endif +#ifdef FIBF_GRP_READ + if (node->mode & 0040) + prot |= FIBF_GRP_READ; +#endif +#ifdef FIBF_OTR_DELETE + if (node->mode & 0002) + prot |= FIBF_OTR_DELETE; +#endif +#ifdef FIBF_OTR_EXECUTE + if (node->mode & 0001) + prot |= FIBF_OTR_EXECUTE; +#endif +#ifdef FIBF_OTR_WRITE + if (node->mode & 0002) + prot |= FIBF_OTR_WRITE; +#endif +#ifdef FIBF_OTR_READ + if (node->mode & 0004) + prot |= FIBF_OTR_READ; +#endif + } else { + prot = FIBF_WRITE | FIBF_DELETE; + } + + return prot; +} + +void odfs_handler_node_date(const odfs_node_t *node, struct DateStamp *ds) +{ + if (!ds) + return; + + memset(ds, 0, sizeof(*ds)); + if (!node || node->mtime.year < 1978) + return; + + { + LONG days = 0; + int y; + + for (y = 1978; y < node->mtime.year; y++) { + days += 365; + if ((y % 4 == 0 && y % 100 != 0) || y % 400 == 0) + days++; + } + { + static const int mdays[] = {0,31,28,31,30,31,30,31,31,30,31,30,31}; + int m; + for (m = 1; m < node->mtime.month && m <= 12; m++) { + days += mdays[m]; + if (m == 2 && ((node->mtime.year % 4 == 0 && + node->mtime.year % 100 != 0) || + node->mtime.year % 400 == 0)) + days++; + } + } + days += node->mtime.day - 1; + + ds->ds_Days = days; + ds->ds_Minute = node->mtime.hour * 60 + node->mtime.minute; + ds->ds_Tick = node->mtime.second * TICKS_PER_SECOND; + } +} + static odfs_err_t lookup_child_node(handler_global_t *g, const odfs_node_t *dir, const char *name, @@ -1595,203 +1693,171 @@ static void fill_root_fib(handler_global_t *g, struct FileInfoBlock *fib, } /* ------------------------------------------------------------------ */ -/* packet handlers */ +/* shared frontend operations */ /* ------------------------------------------------------------------ */ -static void action_locate_object(handler_global_t *g, struct DosPacket *pkt) +LONG odfs_handler_lock_object(handler_global_t *g, + odfs_lock_t *parent_lock, + const char *path, + LONG access, + odfs_lock_t **out) { - odfs_lock_t *parent_lock = LOCK_FROM_BPTR(pkt->dp_Arg1); - LONG access = pkt->dp_Arg3; - char path[512]; odfs_node_t result, parent_node; odfs_err_t err; const odfs_node_t *start; const odfs_node_t *start_parent; + odfs_lock_t *ol; -#if ODFS_SERIAL_DEBUG && ODFS_PACKET_TRACE - trace_pkt(g, "locate-enter", pkt); -#endif - bstr_to_cstr(pkt->dp_Arg2, path, sizeof(path)); -#if ODFS_SERIAL_DEBUG && ODFS_PACKET_TRACE - ODFS_TRACE(&g->log, ODFS_SUB_DOS, "locate-path path=%s", path); -#endif + if (out) + *out = NULL; + if (!g || !path || !out) + return ERROR_REQUIRED_ARG_MISSING; if (parent_lock) { - LONG err_dos = validate_object_volume(g, parent_lock->entry->volume); - if (err_dos != 0) { - pkt->dp_Res1 = DOSFALSE; - pkt->dp_Res2 = err_dos; - return; - } + LONG err_dos; + + if (!lock_is_active(g, parent_lock)) + return ERROR_INVALID_LOCK; + + err_dos = validate_object_volume(g, parent_lock->entry->volume); + if (err_dos != 0) + return err_dos; start = lock_node(parent_lock); start_parent = lock_parent_node(parent_lock); } else { - if (!g->mounted) { - pkt->dp_Res1 = DOSFALSE; - pkt->dp_Res2 = ERROR_NO_DISK; - return; - } + if (!g->mounted) + return ERROR_NO_DISK; start = &g->mount.root; start_parent = &g->mount.root; } err = resolve_amiga_path(g, start, start_parent, path, &result, &parent_node); - if (err != ODFS_OK) { - pkt->dp_Res1 = DOSFALSE; - pkt->dp_Res2 = odfs_err_to_dos(err); -#if ODFS_SERIAL_DEBUG && ODFS_PACKET_TRACE - trace_pkt(g, "locate-resolve-fail", pkt); -#endif - return; - } + if (err != ODFS_OK) + return odfs_err_to_dos(err); -#if ODFS_SERIAL_DEBUG && ODFS_PACKET_TRACE - trace_node(g, "locate-node", &result); - trace_node(g, "locate-parent", &parent_node); -#endif + if (result.kind == ODFS_NODE_DIR) + access = SHARED_LOCK; - odfs_lock_t *ol = alloc_lock(g, &result, &parent_node, access); - if (!ol) { - pkt->dp_Res1 = DOSFALSE; - pkt->dp_Res2 = ERROR_NO_FREE_STORE; -#if ODFS_SERIAL_DEBUG && ODFS_PACKET_TRACE - trace_pkt(g, "locate-alloc-fail", pkt); -#endif - return; - } + ol = alloc_lock(g, &result, &parent_node, access); + if (!ol) + return ERROR_NO_FREE_STORE; - pkt->dp_Res1 = LOCK_TO_BPTR(ol); -#if ODFS_SERIAL_DEBUG && ODFS_PACKET_TRACE - trace_pkt(g, "locate-exit", pkt); -#endif + *out = ol; + return 0; } -static void action_free_lock(handler_global_t *g, struct DosPacket *pkt) +LONG odfs_handler_free_lock_object(handler_global_t *g, odfs_lock_t *ol) { - odfs_lock_t *ol = LOCK_FROM_BPTR(pkt->dp_Arg1); + if (!ol) + return 0; + if (!lock_is_active(g, ol)) + return ERROR_INVALID_LOCK; + free_lock(g, ol); - pkt->dp_Res1 = DOSTRUE; + return 0; } -static void action_copy_dir(handler_global_t *g, struct DosPacket *pkt) +LONG odfs_handler_dup_lock_object(handler_global_t *g, + odfs_lock_t *src, + odfs_lock_t **out) { - odfs_lock_t *src = LOCK_FROM_BPTR(pkt->dp_Arg1); odfs_lock_t *ol; + if (out) + *out = NULL; + if (!g || !out) + return ERROR_REQUIRED_ARG_MISSING; + if (!src) { - if (!g->mounted) { - pkt->dp_Res1 = DOSFALSE; - pkt->dp_Res2 = ERROR_NO_DISK; - return; - } + if (!g->mounted) + return ERROR_NO_DISK; ol = alloc_lock(g, &g->mount.root, &g->mount.root, SHARED_LOCK); } else { - LONG err_dos = validate_object_volume(g, src->entry->volume); - if (err_dos != 0) { - pkt->dp_Res1 = DOSFALSE; - pkt->dp_Res2 = err_dos; - return; - } + LONG err_dos; + + if (!lock_is_active(g, src)) + return ERROR_INVALID_LOCK; + + err_dos = validate_object_volume(g, src->entry->volume); + if (err_dos != 0) + return err_dos; ol = dup_lock(g, src); } - if (!ol) { - pkt->dp_Res1 = DOSFALSE; - pkt->dp_Res2 = ERROR_NO_FREE_STORE; - return; - } - pkt->dp_Res1 = LOCK_TO_BPTR(ol); + if (!ol) + return ERROR_NO_FREE_STORE; + + *out = ol; + return 0; } -static void action_copy_dir_fh(handler_global_t *g, struct DosPacket *pkt) +LONG odfs_handler_dup_lock_from_fh(handler_global_t *g, + odfs_fh_t *fh, + odfs_lock_t **out) { - odfs_fh_t *fh = (odfs_fh_t *)pkt->dp_Arg1; odfs_lock_t *ol; + if (out) + *out = NULL; + if (!g || !out) + return ERROR_REQUIRED_ARG_MISSING; + if (!fh) { - if (!g->mounted) { - pkt->dp_Res1 = DOSFALSE; - pkt->dp_Res2 = ERROR_NO_DISK; - return; - } + if (!g->mounted) + return ERROR_NO_DISK; ol = alloc_lock(g, &g->mount.root, &g->mount.root, SHARED_LOCK); } else { - if (!fh_is_active(g, fh)) { - pkt->dp_Res1 = DOSFALSE; - pkt->dp_Res2 = ERROR_OBJECT_NOT_FOUND; - return; - } - LONG err_dos = validate_object_volume(g, fh_volume(fh)); - if (err_dos != 0) { - pkt->dp_Res1 = DOSFALSE; - pkt->dp_Res2 = err_dos; - return; - } - /* DupLockFromFH duplicates the file's lock, not just directory FHs. */ + LONG err_dos; + + if (!fh_is_active(g, fh)) + return ERROR_OBJECT_NOT_FOUND; + + err_dos = validate_object_volume(g, fh_volume(fh)); + if (err_dos != 0) + return err_dos; ol = alloc_lock(g, fh_node(fh), fh_parent_node(fh), SHARED_LOCK); } - if (!ol) { - pkt->dp_Res1 = DOSFALSE; - pkt->dp_Res2 = ERROR_NO_FREE_STORE; - return; - } - pkt->dp_Res1 = LOCK_TO_BPTR(ol); + if (!ol) + return ERROR_NO_FREE_STORE; + + *out = ol; + return 0; } -static void action_parent(handler_global_t *g, struct DosPacket *pkt) +LONG odfs_handler_parent_lock_object(handler_global_t *g, + odfs_lock_t *ol, + odfs_lock_t **out) { - odfs_lock_t *ol = LOCK_FROM_BPTR(pkt->dp_Arg1); const odfs_node_t *parent_node; odfs_node_t new_parent; odfs_err_t err; odfs_lock_t *parent; -#if ODFS_SERIAL_DEBUG && ODFS_PACKET_TRACE - ODFS_TRACE(&g->log, ODFS_SUB_DOS, - "parent-enter arg1=%08lx lock=%08lx", - (unsigned long)pkt->dp_Arg1, (unsigned long)ol); -#endif + if (out) + *out = NULL; + if (!g || !out) + return ERROR_REQUIRED_ARG_MISSING; if (!ol) { - if (!g->mounted) { - pkt->dp_Res1 = DOSFALSE; - pkt->dp_Res2 = ERROR_NO_DISK; - return; - } - /* NULL lock = root — root has no parent */ - pkt->dp_Res1 = 0; - return; + if (!g->mounted) + return ERROR_NO_DISK; + return 0; } - if (!lock_is_active(g, ol)) { -#if ODFS_SERIAL_DEBUG && ODFS_PACKET_TRACE - ODFS_TRACE(&g->log, ODFS_SUB_DOS, - "parent-invalid-lock lock=%08lx", - (unsigned long)ol); -#endif - pkt->dp_Res1 = DOSFALSE; - pkt->dp_Res2 = ERROR_INVALID_LOCK; - return; - } + if (!lock_is_active(g, ol)) + return ERROR_INVALID_LOCK; { LONG err_dos = validate_object_volume(g, ol->entry->volume); - if (err_dos != 0) { - pkt->dp_Res1 = DOSFALSE; - pkt->dp_Res2 = err_dos; - return; - } + if (err_dos != 0) + return err_dos; } - /* already at root? */ - if (node_is_mount_root(g, lock_node(ol))) { - pkt->dp_Res1 = 0; - return; - } + if (node_is_mount_root(g, lock_node(ol))) + return 0; - /* Reconstruct the returned lock's immediate parent exactly. */ parent_node = lock_parent_node(ol); if (node_is_mount_root(g, parent_node)) { new_parent = g->mount.root; @@ -1802,70 +1868,49 @@ static void action_parent(handler_global_t *g, struct DosPacket *pkt) } else { err = odfs_resolve_parent_node(&g->mount, parent_node, &new_parent, NULL); - if (err != ODFS_OK) { - pkt->dp_Res1 = DOSFALSE; - pkt->dp_Res2 = odfs_err_to_dos(err); - return; - } + if (err != ODFS_OK) + return odfs_err_to_dos(err); } -#if ODFS_SERIAL_DEBUG && ODFS_PACKET_TRACE - trace_node(g, "parent-node", lock_node(ol)); - trace_node(g, "parent-result", parent_node); - trace_node(g, "parent-parent", &new_parent); -#endif - parent = alloc_lock(g, parent_node, &new_parent, SHARED_LOCK); - if (!parent) { - pkt->dp_Res1 = DOSFALSE; - pkt->dp_Res2 = ERROR_NO_FREE_STORE; - return; - } - pkt->dp_Res1 = LOCK_TO_BPTR(parent); + if (!parent) + return ERROR_NO_FREE_STORE; + + *out = parent; + return 0; } -static void action_parent_fh(handler_global_t *g, struct DosPacket *pkt) +LONG odfs_handler_parent_fh_object(handler_global_t *g, + odfs_fh_t *fh, + odfs_lock_t **out) { - odfs_fh_t *fh = (odfs_fh_t *)pkt->dp_Arg1; const odfs_node_t *parent_node; odfs_node_t new_parent; odfs_err_t err; odfs_lock_t *parent; -#if ODFS_SERIAL_DEBUG && ODFS_PACKET_TRACE - ODFS_TRACE(&g->log, ODFS_SUB_DOS, - "parentfh-enter fh=%08lx", (unsigned long)fh); -#endif + if (out) + *out = NULL; + if (!g || !out) + return ERROR_REQUIRED_ARG_MISSING; if (!fh) { - if (!g->mounted) { - pkt->dp_Res1 = DOSFALSE; - pkt->dp_Res2 = ERROR_NO_DISK; - return; - } - pkt->dp_Res1 = 0; - return; + if (!g->mounted) + return ERROR_NO_DISK; + return 0; } - if (!fh_is_active(g, fh)) { - pkt->dp_Res1 = DOSFALSE; - pkt->dp_Res2 = ERROR_OBJECT_NOT_FOUND; - return; - } + if (!fh_is_active(g, fh)) + return ERROR_OBJECT_NOT_FOUND; { LONG err_dos = validate_object_volume(g, fh_volume(fh)); - if (err_dos != 0) { - pkt->dp_Res1 = DOSFALSE; - pkt->dp_Res2 = err_dos; - return; - } + if (err_dos != 0) + return err_dos; } - if (node_is_mount_root(g, fh_node(fh))) { - pkt->dp_Res1 = 0; - return; - } + if (node_is_mount_root(g, fh_node(fh))) + return 0; parent_node = fh_parent_node(fh); if (node_is_mount_root(g, parent_node)) { @@ -1877,65 +1922,550 @@ static void action_parent_fh(handler_global_t *g, struct DosPacket *pkt) } else { err = odfs_resolve_parent_node(&g->mount, parent_node, &new_parent, NULL); - if (err != ODFS_OK) { - pkt->dp_Res1 = DOSFALSE; - pkt->dp_Res2 = odfs_err_to_dos(err); - return; - } + if (err != ODFS_OK) + return odfs_err_to_dos(err); } -#if ODFS_SERIAL_DEBUG && ODFS_PACKET_TRACE - trace_node(g, "parentfh-node", fh_node(fh)); - trace_node(g, "parentfh-result", parent_node); -#endif - parent = alloc_lock(g, parent_node, &new_parent, SHARED_LOCK); - if (!parent) { - pkt->dp_Res1 = DOSFALSE; - pkt->dp_Res2 = ERROR_NO_FREE_STORE; - return; - } - pkt->dp_Res1 = LOCK_TO_BPTR(parent); + if (!parent) + return ERROR_NO_FREE_STORE; + + *out = parent; + return 0; } -static void action_same_lock(handler_global_t *g, struct DosPacket *pkt) +LONG odfs_handler_same_lock_object(handler_global_t *g, + odfs_lock_t *l1, + odfs_lock_t *l2, + LONG *same_result) { - odfs_lock_t *l1 = LOCK_FROM_BPTR(pkt->dp_Arg1); - odfs_lock_t *l2 = LOCK_FROM_BPTR(pkt->dp_Arg2); odfs_volume_t *v1; odfs_volume_t *v2; int same = 0; - if (!g->mounted && (!pkt->dp_Arg1 || !pkt->dp_Arg2)) { - pkt->dp_Res1 = DOSFALSE; - pkt->dp_Res2 = ERROR_DEVICE_NOT_MOUNTED; - return; - } + if (!g || !same_result) + return ERROR_REQUIRED_ARG_MISSING; - v1 = l1 ? l1->entry->volume : g->current_volume; - v2 = l2 ? l2->entry->volume : g->current_volume; + *same_result = LOCK_DIFFERENT; - pkt->dp_Res1 = DOSFALSE; - pkt->dp_Res2 = LOCK_DIFFERENT; + if (l1 && !lock_is_active(g, l1)) + return ERROR_INVALID_LOCK; + if (l2 && !lock_is_active(g, l2)) + return ERROR_INVALID_LOCK; + + if (!g->mounted && (!l1 || !l2)) + return ERROR_DEVICE_NOT_MOUNTED; + v1 = l1 ? l1->entry->volume : g->current_volume; + v2 = l2 ? l2->entry->volume : g->current_volume; if (v1 != v2) - return; + return 0; - pkt->dp_Res2 = LOCK_SAME_VOLUME; - if (!pkt->dp_Arg1 && !pkt->dp_Arg2) { + *same_result = LOCK_SAME_VOLUME; + if (!l1 && !l2) { same = 1; - } else if (!pkt->dp_Arg1) { + } else if (!l1) { same = node_is_mount_root(g, lock_node(l2)); - } else if (!pkt->dp_Arg2) { + } else if (!l2) { same = node_is_mount_root(g, lock_node(l1)); } else { same = nodes_same(lock_node(l1), lock_node(l2)); } - if (same) { - pkt->dp_Res1 = DOSTRUE; - pkt->dp_Res2 = LOCK_SAME; + if (same) + *same_result = LOCK_SAME; + return 0; +} + +LONG odfs_handler_same_file_object(handler_global_t *g, + odfs_fh_t *fh1, + odfs_fh_t *fh2, + LONG *same_result) +{ + odfs_volume_t *v1; + odfs_volume_t *v2; + int same; + + if (!g || !fh1 || !fh2 || !same_result) + return ERROR_OBJECT_NOT_FOUND; + + *same_result = LOCK_DIFFERENT; + + if (!fh_is_active(g, fh1) || !fh_is_active(g, fh2)) + return ERROR_OBJECT_NOT_FOUND; + + v1 = fh_volume(fh1); + v2 = fh_volume(fh2); + if (v1 != v2) + return 0; + + *same_result = LOCK_SAME_VOLUME; + same = nodes_same(fh_node(fh1), fh_node(fh2)); + if (same) + *same_result = LOCK_SAME; + return 0; +} + +LONG odfs_handler_open_object(handler_global_t *g, + odfs_lock_t *dirlock, + const char *path, + LONG mode, + odfs_fh_t **out) +{ + odfs_node_t result, parent_node; + odfs_err_t err; + const odfs_node_t *start; + const odfs_node_t *start_parent; + odfs_entry_t *entry; + odfs_fh_t *fh; + + if (out) + *out = NULL; + if (!g || !path || !out) + return ERROR_REQUIRED_ARG_MISSING; + + if (mode != MODE_OLDFILE) + return ERROR_DISK_WRITE_PROTECTED; + + if (dirlock) { + LONG err_dos; + + if (!lock_is_active(g, dirlock)) + return ERROR_INVALID_LOCK; + + err_dos = validate_object_volume(g, dirlock->entry->volume); + if (err_dos != 0) + return err_dos; + start = lock_node(dirlock); + start_parent = lock_parent_node(dirlock); + } else { + if (!g->mounted) + return ERROR_NO_DISK; + start = &g->mount.root; + start_parent = &g->mount.root; + } + + err = resolve_amiga_path(g, start, start_parent, path, &result, + &parent_node); + if (err != ODFS_OK) + return odfs_err_to_dos(err); + + if (result.kind == ODFS_NODE_DIR) + return ERROR_OBJECT_WRONG_TYPE; + + entry = alloc_entry(g->current_volume, &result, &parent_node); + if (!entry) + return ERROR_NO_FREE_STORE; + + fh = alloc_fh(g, entry, SHARED_LOCK); + release_entry(entry); + if (!fh) + return ERROR_NO_FREE_STORE; + + *out = fh; + return 0; +} + +LONG odfs_handler_open_from_lock_object(handler_global_t *g, + odfs_lock_t *ol, + odfs_fh_t **out) +{ + odfs_fh_t *fh; + + if (out) + *out = NULL; + if (!g || !ol || !out) + return ERROR_OBJECT_NOT_FOUND; + + if (!lock_is_active(g, ol)) + return ERROR_INVALID_LOCK; + + { + LONG err_dos = validate_object_volume(g, ol->entry->volume); + if (err_dos != 0) + return err_dos; + } + + fh = alloc_fh(g, ol->entry, ol->lock.fl_Access); + if (!fh) + return ERROR_NO_FREE_STORE; + + free_lock(g, ol); + *out = fh; + return 0; +} + +LONG odfs_handler_close_object(handler_global_t *g, odfs_fh_t *fh) +{ + if (!fh) + return 0; + if (!fh_is_active(g, fh)) + return ERROR_OBJECT_NOT_FOUND; + + free_fh(g, fh); + return 0; +} + +LONG odfs_handler_read_object(handler_global_t *g, + odfs_fh_t *fh, + void *buf, + LONG len, + LONG *actual_out) +{ + size_t actual; + odfs_err_t err; + + if (actual_out) + *actual_out = 0; + if (!g || !fh || !buf || !actual_out) + return ERROR_OBJECT_NOT_FOUND; + if (len < 0) + return ERROR_BAD_NUMBER; + if (!fh_is_active(g, fh)) + return ERROR_OBJECT_NOT_FOUND; + + { + LONG err_dos = validate_object_volume(g, fh_volume(fh)); + if (err_dos != 0) + return err_dos; + } + + actual = (size_t)len; + err = read_file_node(g, fh_node(fh), fh->pos, buf, &actual); + if (err != ODFS_OK && actual == 0) + return odfs_err_to_dos(err); + + fh->pos += actual; + *actual_out = (LONG)actual; + return 0; +} + +LONG odfs_handler_seek_object(handler_global_t *g, + odfs_fh_t *fh, + int64_t offset, + LONG mode, + int64_t *oldpos_out) +{ + int64_t oldpos; + int64_t newpos; + uint64_t size; + + if (oldpos_out) + *oldpos_out = -1; + if (!g || !fh || !oldpos_out) + return ERROR_OBJECT_NOT_FOUND; + if (!fh_is_active(g, fh)) + return ERROR_OBJECT_NOT_FOUND; + + { + LONG err_dos = validate_object_volume(g, fh_volume(fh)); + if (err_dos != 0) + return err_dos; + } + + oldpos = (int64_t)fh->pos; + size = fh_node(fh)->size; + + switch (mode) { + case OFFSET_BEGINNING: newpos = offset; break; + case OFFSET_CURRENT: newpos = oldpos + offset; break; + case OFFSET_END: newpos = (int64_t)size + offset; break; + default: + return ERROR_SEEK_ERROR; + } + + if (newpos < 0 || (uint64_t)newpos > size) + return ERROR_SEEK_ERROR; + + fh->pos = (uint64_t)newpos; + *oldpos_out = oldpos; + return 0; +} + +LONG odfs_handler_get_file_position(handler_global_t *g, + odfs_fh_t *fh, + int64_t *pos_out) +{ + if (pos_out) + *pos_out = -1; + if (!g || !fh || !pos_out) + return ERROR_OBJECT_NOT_FOUND; + if (!fh_is_active(g, fh)) + return ERROR_OBJECT_NOT_FOUND; + + { + LONG err_dos = validate_object_volume(g, fh_volume(fh)); + if (err_dos != 0) + return err_dos; + } + + *pos_out = (int64_t)fh->pos; + return 0; +} + +LONG odfs_handler_get_file_size(handler_global_t *g, + odfs_fh_t *fh, + int64_t *size_out) +{ + if (size_out) + *size_out = -1; + if (!g || !fh || !size_out) + return ERROR_OBJECT_NOT_FOUND; + if (!fh_is_active(g, fh)) + return ERROR_OBJECT_NOT_FOUND; + + { + LONG err_dos = validate_object_volume(g, fh_volume(fh)); + if (err_dos != 0) + return err_dos; } + + *size_out = (int64_t)fh_node(fh)->size; + return 0; +} + +LONG odfs_handler_fill_info(handler_global_t *g, + odfs_lock_t *ol, + struct InfoData *info) +{ + if (!g || !info) + return ERROR_REQUIRED_ARG_MISSING; + + if (ol) { + LONG err_dos; + + if (!lock_is_active(g, ol)) + return ERROR_INVALID_LOCK; + + err_dos = validate_object_volume(g, ol->entry->volume); + if (err_dos != 0) + return err_dos; + } + + memset(info, 0, sizeof(*info)); + info->id_NumSoftErrors = 0; + info->id_UnitNumber = g->devunit; + info->id_DiskState = ID_WRITE_PROTECTED; + info->id_NumBlocks = g->mounted ? g->mount.total_blocks : 0; + info->id_NumBlocksUsed = info->id_NumBlocks; + info->id_BytesPerBlock = g->sector_size; + info->id_DiskType = g->mounted ? ID_DOS_DISK : ID_NO_DISK_PRESENT; + info->id_VolumeNode = MKBADDR(volume_node_ptr(g->current_volume)); + info->id_InUse = (g->current_volume && g->current_volume->volnode && + g->current_volume->volnode->dl_LockList) ? + DOSTRUE : DOSFALSE; + return 0; +} + +LONG odfs_handler_get_lock_node(handler_global_t *g, + odfs_lock_t *ol, + const odfs_node_t **node_out) +{ + if (node_out) + *node_out = NULL; + if (!g || !node_out) + return ERROR_REQUIRED_ARG_MISSING; + + if (ol) { + LONG err_dos; + + if (!lock_is_active(g, ol)) + return ERROR_INVALID_LOCK; + + err_dos = validate_object_volume(g, ol->entry->volume); + if (err_dos != 0) + return err_dos; + *node_out = lock_node(ol); + } else { + if (!g->mounted) + return ERROR_NO_DISK; + *node_out = &g->mount.root; + } + + return 0; +} + +LONG odfs_handler_get_fh_node(handler_global_t *g, + odfs_fh_t *fh, + const odfs_node_t **node_out) +{ + if (node_out) + *node_out = NULL; + if (!g || !fh || !node_out) + return ERROR_OBJECT_NOT_FOUND; + + if (!fh_is_active(g, fh)) + return ERROR_OBJECT_NOT_FOUND; + + { + LONG err_dos = validate_object_volume(g, fh_volume(fh)); + if (err_dos != 0) + return err_dos; + } + + *node_out = fh_node(fh); + return 0; +} + +/* ------------------------------------------------------------------ */ +/* packet handlers */ +/* ------------------------------------------------------------------ */ + +static void action_locate_object(handler_global_t *g, struct DosPacket *pkt) +{ + odfs_lock_t *parent_lock = LOCK_FROM_BPTR(pkt->dp_Arg1); + LONG access = pkt->dp_Arg3; + char path[512]; + odfs_lock_t *ol; + LONG err_dos; + +#if ODFS_SERIAL_DEBUG && ODFS_PACKET_TRACE + trace_pkt(g, "locate-enter", pkt); +#endif + bstr_to_cstr(pkt->dp_Arg2, path, sizeof(path)); +#if ODFS_SERIAL_DEBUG && ODFS_PACKET_TRACE + ODFS_TRACE(&g->log, ODFS_SUB_DOS, "locate-path path=%s", path); +#endif + + err_dos = odfs_handler_lock_object(g, parent_lock, path, access, &ol); + if (err_dos != 0) { + pkt->dp_Res1 = DOSFALSE; + pkt->dp_Res2 = err_dos; +#if ODFS_SERIAL_DEBUG && ODFS_PACKET_TRACE + trace_pkt(g, "locate-resolve-fail", pkt); +#endif + return; + } + +#if ODFS_SERIAL_DEBUG && ODFS_PACKET_TRACE + trace_node(g, "locate-node", lock_node(ol)); + trace_node(g, "locate-parent", lock_parent_node(ol)); +#endif + + pkt->dp_Res1 = LOCK_TO_BPTR(ol); +#if ODFS_SERIAL_DEBUG && ODFS_PACKET_TRACE + trace_pkt(g, "locate-exit", pkt); +#endif +} + +static void action_free_lock(handler_global_t *g, struct DosPacket *pkt) +{ + odfs_lock_t *ol = LOCK_FROM_BPTR(pkt->dp_Arg1); + LONG err_dos = odfs_handler_free_lock_object(g, ol); + + if (err_dos != 0) { + pkt->dp_Res1 = DOSFALSE; + pkt->dp_Res2 = err_dos; + return; + } + pkt->dp_Res1 = DOSTRUE; +} + +static void action_copy_dir(handler_global_t *g, struct DosPacket *pkt) +{ + odfs_lock_t *src = LOCK_FROM_BPTR(pkt->dp_Arg1); + odfs_lock_t *ol; + LONG err_dos; + + err_dos = odfs_handler_dup_lock_object(g, src, &ol); + if (err_dos != 0) { + pkt->dp_Res1 = DOSFALSE; + pkt->dp_Res2 = err_dos; + return; + } + pkt->dp_Res1 = LOCK_TO_BPTR(ol); +} + +static void action_copy_dir_fh(handler_global_t *g, struct DosPacket *pkt) +{ + odfs_fh_t *fh = (odfs_fh_t *)pkt->dp_Arg1; + odfs_lock_t *ol; + LONG err_dos; + + err_dos = odfs_handler_dup_lock_from_fh(g, fh, &ol); + if (err_dos != 0) { + pkt->dp_Res1 = DOSFALSE; + pkt->dp_Res2 = err_dos; + return; + } + pkt->dp_Res1 = LOCK_TO_BPTR(ol); +} + +static void action_parent(handler_global_t *g, struct DosPacket *pkt) +{ + odfs_lock_t *ol = LOCK_FROM_BPTR(pkt->dp_Arg1); + odfs_lock_t *parent; + LONG err_dos; + +#if ODFS_SERIAL_DEBUG && ODFS_PACKET_TRACE + ODFS_TRACE(&g->log, ODFS_SUB_DOS, + "parent-enter arg1=%08lx lock=%08lx", + (unsigned long)pkt->dp_Arg1, (unsigned long)ol); +#endif + + err_dos = odfs_handler_parent_lock_object(g, ol, &parent); + if (err_dos != 0) { + pkt->dp_Res1 = DOSFALSE; + pkt->dp_Res2 = err_dos; + return; + } + +#if ODFS_SERIAL_DEBUG && ODFS_PACKET_TRACE + if (ol) + trace_node(g, "parent-node", lock_node(ol)); + if (parent) { + trace_node(g, "parent-result", lock_node(parent)); + trace_node(g, "parent-parent", lock_parent_node(parent)); + } +#endif + + pkt->dp_Res1 = LOCK_TO_BPTR(parent); +} + +static void action_parent_fh(handler_global_t *g, struct DosPacket *pkt) +{ + odfs_fh_t *fh = (odfs_fh_t *)pkt->dp_Arg1; + odfs_lock_t *parent; + LONG err_dos; + +#if ODFS_SERIAL_DEBUG && ODFS_PACKET_TRACE + ODFS_TRACE(&g->log, ODFS_SUB_DOS, + "parentfh-enter fh=%08lx", (unsigned long)fh); +#endif + + err_dos = odfs_handler_parent_fh_object(g, fh, &parent); + if (err_dos != 0) { + pkt->dp_Res1 = DOSFALSE; + pkt->dp_Res2 = err_dos; + return; + } + +#if ODFS_SERIAL_DEBUG && ODFS_PACKET_TRACE + if (fh) + trace_node(g, "parentfh-node", fh_node(fh)); + if (parent) + trace_node(g, "parentfh-result", lock_node(parent)); +#endif + + pkt->dp_Res1 = LOCK_TO_BPTR(parent); +} + +static void action_same_lock(handler_global_t *g, struct DosPacket *pkt) +{ + odfs_lock_t *l1 = LOCK_FROM_BPTR(pkt->dp_Arg1); + odfs_lock_t *l2 = LOCK_FROM_BPTR(pkt->dp_Arg2); + LONG same_result; + LONG err_dos; + + err_dos = odfs_handler_same_lock_object(g, l1, l2, &same_result); + if (err_dos != 0) { + pkt->dp_Res1 = DOSFALSE; + pkt->dp_Res2 = err_dos; + return; + } + + pkt->dp_Res1 = (same_result == LOCK_SAME) ? DOSTRUE : DOSFALSE; + pkt->dp_Res2 = same_result; } /* ---- examine ---- */ @@ -2362,29 +2892,19 @@ static void action_examine_fh(handler_global_t *g, struct DosPacket *pkt) static void action_fh_from_lock(handler_global_t *g, struct DosPacket *pkt) { odfs_lock_t *ol = LOCK_FROM_BPTR(pkt->dp_Arg2); + struct FileHandle *fhandle = (struct FileHandle *)BADDR(pkt->dp_Arg1); + odfs_fh_t *fh; + LONG err_dos; - if (ol) { - LONG err_dos = validate_object_volume(g, ol->entry->volume); - struct FileHandle *fhandle = (struct FileHandle *)BADDR(pkt->dp_Arg1); - odfs_fh_t *fh; - - if (err_dos != 0) { - pkt->dp_Res1 = DOSFALSE; - pkt->dp_Res2 = err_dos; - return; - } - - fh = alloc_fh(g, ol->entry, ol->lock.fl_Access); - if (fh) { - fhandle->fh_Arg1 = (LONG)fh; - free_lock(g, ol); - pkt->dp_Res1 = DOSTRUE; - } else { - pkt->dp_Res2 = ERROR_NO_FREE_STORE; - } - } else { - pkt->dp_Res2 = ERROR_OBJECT_NOT_FOUND; + err_dos = odfs_handler_open_from_lock_object(g, ol, &fh); + if (err_dos != 0) { + pkt->dp_Res1 = DOSFALSE; + pkt->dp_Res2 = err_dos; + return; } + + fhandle->fh_Arg1 = (LONG)fh; + pkt->dp_Res1 = DOSTRUE; } /* ---- file I/O ---- */ @@ -2394,60 +2914,15 @@ static void action_findinput(handler_global_t *g, struct DosPacket *pkt) struct FileHandle *fhandle = (struct FileHandle *)BADDR(pkt->dp_Arg1); odfs_lock_t *dirlock = LOCK_FROM_BPTR(pkt->dp_Arg2); char path[512]; - odfs_node_t result, parent_node; - odfs_err_t err; - const odfs_node_t *start; - const odfs_node_t *start_parent; - - bstr_to_cstr(pkt->dp_Arg3, path, sizeof(path)); - - if (dirlock) { - LONG err_dos = validate_object_volume(g, dirlock->entry->volume); - if (err_dos != 0) { - pkt->dp_Res1 = DOSFALSE; - pkt->dp_Res2 = err_dos; - return; - } - start = lock_node(dirlock); - start_parent = lock_parent_node(dirlock); - } else { - if (!g->mounted) { - pkt->dp_Res1 = DOSFALSE; - pkt->dp_Res2 = ERROR_NO_DISK; - return; - } - start = &g->mount.root; - start_parent = &g->mount.root; - } - - err = resolve_amiga_path(g, start, start_parent, path, &result, - &parent_node); - if (err != ODFS_OK) { - pkt->dp_Res1 = DOSFALSE; - pkt->dp_Res2 = odfs_err_to_dos(err); - return; - } - - if (result.kind == ODFS_NODE_DIR) { - pkt->dp_Res1 = DOSFALSE; - pkt->dp_Res2 = ERROR_OBJECT_WRONG_TYPE; - return; - } - - odfs_entry_t *entry = alloc_entry(g->current_volume, &result, &parent_node); odfs_fh_t *fh; + LONG err_dos; - if (!entry) { - pkt->dp_Res1 = DOSFALSE; - pkt->dp_Res2 = ERROR_NO_FREE_STORE; - return; - } + bstr_to_cstr(pkt->dp_Arg3, path, sizeof(path)); - fh = alloc_fh(g, entry, SHARED_LOCK); - release_entry(entry); - if (!fh) { + err_dos = odfs_handler_open_object(g, dirlock, path, MODE_OLDFILE, &fh); + if (err_dos != 0) { pkt->dp_Res1 = DOSFALSE; - pkt->dp_Res2 = ERROR_NO_FREE_STORE; + pkt->dp_Res2 = err_dos; return; } @@ -2460,90 +2935,49 @@ static void action_read(handler_global_t *g, struct DosPacket *pkt) odfs_fh_t *fh = (odfs_fh_t *)pkt->dp_Arg1; void *buf = (void *)pkt->dp_Arg2; LONG len = pkt->dp_Arg3; + LONG actual; + LONG err_dos; - if (!fh) { + err_dos = odfs_handler_read_object(g, fh, buf, len, &actual); + if (err_dos != 0) { pkt->dp_Res1 = -1; - pkt->dp_Res2 = ERROR_OBJECT_NOT_FOUND; + pkt->dp_Res2 = err_dos; return; } - { - LONG err_dos = validate_object_volume(g, fh_volume(fh)); - if (err_dos != 0) { - pkt->dp_Res1 = -1; - pkt->dp_Res2 = err_dos; - return; - } - } - - size_t actual = (size_t)len; - odfs_err_t err = read_file_node(g, fh_node(fh), fh->pos, buf, &actual); - if (err != ODFS_OK && actual == 0) { - pkt->dp_Res1 = -1; - pkt->dp_Res2 = odfs_err_to_dos(err); - return; - } - - fh->pos += actual; - pkt->dp_Res1 = (LONG)actual; + pkt->dp_Res1 = actual; } -static void action_seek(handler_global_t *g __attribute__((unused)), - struct DosPacket *pkt) +static void action_seek(handler_global_t *g, struct DosPacket *pkt) { odfs_fh_t *fh = (odfs_fh_t *)pkt->dp_Arg1; LONG offset = pkt->dp_Arg2; LONG mode = pkt->dp_Arg3; - LONG oldpos, newpos; - - if (!fh) { - pkt->dp_Res1 = -1; - pkt->dp_Res2 = ERROR_OBJECT_NOT_FOUND; - return; - } - - { - LONG err_dos = validate_object_volume(g, fh_volume(fh)); - if (err_dos != 0) { - pkt->dp_Res1 = -1; - pkt->dp_Res2 = err_dos; - return; - } - } - - oldpos = (LONG)fh->pos; - - switch (mode) { - case OFFSET_BEGINNING: newpos = offset; break; - case OFFSET_CURRENT: newpos = oldpos + offset; break; - case OFFSET_END: newpos = (LONG)fh_node(fh)->size + offset; break; - default: - pkt->dp_Res1 = -1; - pkt->dp_Res2 = ERROR_SEEK_ERROR; - return; - } + int64_t oldpos; + LONG err_dos; - if (newpos < 0 || (ULONG)newpos > fh_node(fh)->size) { + err_dos = odfs_handler_seek_object(g, fh, offset, mode, &oldpos); + if (err_dos != 0) { pkt->dp_Res1 = -1; - pkt->dp_Res2 = ERROR_SEEK_ERROR; + pkt->dp_Res2 = err_dos; return; } - fh->pos = (ULONG)newpos; - pkt->dp_Res1 = oldpos; + pkt->dp_Res1 = (LONG)oldpos; } static void action_end(handler_global_t *g, struct DosPacket *pkt) { odfs_fh_t *fh = (odfs_fh_t *)pkt->dp_Arg1; + LONG err_dos; - if (fh && !fh_is_active(g, fh)) { + err_dos = odfs_handler_close_object(g, fh); + if (err_dos != 0) { pkt->dp_Res1 = DOSFALSE; - pkt->dp_Res2 = ERROR_OBJECT_NOT_FOUND; + pkt->dp_Res2 = err_dos; return; } - free_fh(g, fh); pkt->dp_Res1 = DOSTRUE; } @@ -2552,20 +2986,13 @@ static void action_end(handler_global_t *g, struct DosPacket *pkt) static void action_disk_info(handler_global_t *g, struct DosPacket *pkt) { struct InfoData *info = (struct InfoData *)BADDR(pkt->dp_Arg1); + LONG err_dos = odfs_handler_fill_info(g, NULL, info); - memset(info, 0, sizeof(*info)); - info->id_NumSoftErrors = 0; - info->id_UnitNumber = g->devunit; - info->id_DiskState = ID_WRITE_PROTECTED; - info->id_NumBlocks = g->mounted ? g->mount.total_blocks : 0; - info->id_NumBlocksUsed = info->id_NumBlocks; - info->id_BytesPerBlock = g->sector_size; - info->id_DiskType = g->mounted ? ID_DOS_DISK : ID_NO_DISK_PRESENT; - info->id_VolumeNode = MKBADDR(volume_node_ptr(g->current_volume)); - info->id_InUse = (g->current_volume && g->current_volume->volnode && - g->current_volume->volnode->dl_LockList) ? - DOSTRUE : DOSFALSE; - + if (err_dos != 0) { + pkt->dp_Res1 = DOSFALSE; + pkt->dp_Res2 = err_dos; + return; + } pkt->dp_Res1 = DOSTRUE; } @@ -2573,6 +3000,7 @@ static void action_info(handler_global_t *g, struct DosPacket *pkt) { odfs_lock_t *ol = LOCK_FROM_BPTR(pkt->dp_Arg1); struct InfoData *info = (struct InfoData *)BADDR(pkt->dp_Arg2); + LONG err_dos; if (pkt->dp_Arg1 && !ol) { pkt->dp_Res1 = DOSFALSE; @@ -2580,30 +3008,12 @@ static void action_info(handler_global_t *g, struct DosPacket *pkt) return; } - if (ol) { - LONG err_dos = validate_object_volume(g, ol->entry->volume); - if (err_dos != 0) { - pkt->dp_Res1 = DOSFALSE; - pkt->dp_Res2 = err_dos; - return; - } + err_dos = odfs_handler_fill_info(g, ol, info); + if (err_dos != 0) { + pkt->dp_Res1 = DOSFALSE; + pkt->dp_Res2 = err_dos; + return; } - - /* Single mounted volume: the lock only needs to be valid/current. */ - (void)ol; - memset(info, 0, sizeof(*info)); - info->id_NumSoftErrors = 0; - info->id_UnitNumber = g->devunit; - info->id_DiskState = ID_WRITE_PROTECTED; - info->id_NumBlocks = g->mounted ? g->mount.total_blocks : 0; - info->id_NumBlocksUsed = info->id_NumBlocks; - info->id_BytesPerBlock = g->sector_size; - info->id_DiskType = g->mounted ? ID_DOS_DISK : ID_NO_DISK_PRESENT; - info->id_VolumeNode = MKBADDR(volume_node_ptr(g->current_volume)); - info->id_InUse = (g->current_volume && g->current_volume->volnode && - g->current_volume->volnode->dl_LockList) ? - DOSTRUE : DOSFALSE; - pkt->dp_Res1 = DOSTRUE; } diff --git a/platform/amiga/os4/vector_port.c b/platform/amiga/os4/vector_port.c index 0d5d97b..c37510c 100644 --- a/platform/amiga/os4/vector_port.c +++ b/platform/amiga/os4/vector_port.c @@ -6,6 +6,8 @@ #include "vector_port.h" +#include "handler.h" + #include #include #include @@ -14,6 +16,7 @@ #include #include +#include static void set_unsupported(struct FSVP *vp, int32 *res2) { @@ -23,33 +26,130 @@ static void set_unsupported(struct FSVP *vp, int32 *res2) *res2 = ERROR_ACTION_NOT_KNOWN; } +static handler_global_t *vp_global(struct FSVP *vp) +{ + return vp ? (handler_global_t *)vp->FSV.FSPrivate : NULL; +} + +static void set_dos_error(int32 *res2, LONG err) +{ + if (res2) + *res2 = err; +} + +static int32 return_dos_status(int32 *res2, LONG err) +{ + set_dos_error(res2, err); + return err == 0 ? DOSTRUE : DOSFALSE; +} + +static odfs_lock_t *lock_from_vector(struct Lock *lock) +{ + return (odfs_lock_t *)LOCK_FROM_PTR(lock); +} + +static odfs_fh_t *fh_from_vector(struct FileHandle *fh) +{ + return fh ? (odfs_fh_t *)fh->fh_Arg2 : NULL; +} + +static const char *node_name_for_examine(handler_global_t *g, + const odfs_node_t *node) +{ + if (g && node && odfs_node_matches_identity(node, &g->mount.root)) + return g->volname; + return node ? node->name : ""; +} + +static struct ExamineData *alloc_examine_data(handler_global_t *g, + const odfs_node_t *node) +{ + const char *name; + const char *comment = ""; + size_t name_len; + size_t comment_len; + struct ExamineData *ed; + + if (!node) + return NULL; + + name = node_name_for_examine(g, node); + if (node->amiga_as.has_comment) + comment = node->amiga_as.comment; + + name_len = strlen(name) + 1u; + comment_len = strlen(comment) + 1u; + + ed = AllocDosObjectTags(DOS_EXAMINEDATA, + ADO_ExamineData_NameSize, + (ULONG)name_len, + ADO_ExamineData_CommentSize, + (ULONG)comment_len, + ADO_ExamineData_LinkSize, + 1UL, + TAG_END); + if (!ed) + return NULL; + + ed->Type = (node->kind == ODFS_NODE_DIR) ? + FSO_TYPE_DIRECTORY : FSO_TYPE_FILE; + ed->FileSize = (node->kind == ODFS_NODE_DIR) ? -1LL : (int64)node->size; + odfs_handler_node_date(node, &ed->Date); + ed->RefCount = 0; + ed->ObjectID = odfs_handler_node_key(node); + ed->Protection = odfs_handler_node_protection(node); + ed->OwnerUID = DOS_OWNER_NONE; + ed->OwnerGID = DOS_OWNER_NONE; + ed->FSPrivate = odfs_handler_node_key(node); + + if (ed->Name && ed->NameSize > 0) { + strncpy(ed->Name, name, ed->NameSize - 1); + ed->Name[ed->NameSize - 1] = '\0'; + } + if (ed->Comment && ed->CommentSize > 0) { + strncpy(ed->Comment, comment, ed->CommentSize - 1); + ed->Comment[ed->CommentSize - 1] = '\0'; + } + if (ed->Link && ed->LinkSize > 0) + ed->Link[0] = '\0'; + + return ed; +} + static struct Lock *vp_lock(struct FSVP *vp, int32 *res2, struct Lock *rel_lock, CONST_STRPTR obj, int32 mode) { - set_unsupported(vp, res2); - (void)rel_lock; - (void)obj; - (void)mode; - return NULL; + handler_global_t *g = vp_global(vp); + odfs_lock_t *ol = NULL; + LONG err; + + err = odfs_handler_lock_object(g, lock_from_vector(rel_lock), + obj ? obj : "", mode, &ol); + set_dos_error(res2, err); + return (struct Lock *)LOCK_TO_PTR(ol); } static int32 vp_unlock(struct FSVP *vp, int32 *res2, struct Lock *lock) { - set_unsupported(vp, res2); - (void)lock; - return DOSFALSE; + return return_dos_status(res2, + odfs_handler_free_lock_object( + vp_global(vp), lock_from_vector(lock))); } static struct Lock *vp_dup_lock(struct FSVP *vp, int32 *res2, struct Lock *lock) { - set_unsupported(vp, res2); - (void)lock; - return NULL; + odfs_lock_t *ol = NULL; + LONG err; + + err = odfs_handler_dup_lock_object(vp_global(vp), + lock_from_vector(lock), &ol); + set_dos_error(res2, err); + return (struct Lock *)LOCK_TO_PTR(ol); } static struct Lock *vp_create_dir(struct FSVP *vp, @@ -67,18 +167,27 @@ static struct Lock *vp_parent_dir(struct FSVP *vp, int32 *res2, struct Lock *dirlock) { - set_unsupported(vp, res2); - (void)dirlock; - return NULL; + odfs_lock_t *parent = NULL; + LONG err; + + err = odfs_handler_parent_lock_object(vp_global(vp), + lock_from_vector(dirlock), + &parent); + set_dos_error(res2, err); + return (struct Lock *)LOCK_TO_PTR(parent); } static struct Lock *vp_dup_lock_from_fh(struct FSVP *vp, int32 *res2, struct FileHandle *filehandle) { - set_unsupported(vp, res2); - (void)filehandle; - return NULL; + odfs_lock_t *ol = NULL; + LONG err; + + err = odfs_handler_dup_lock_from_fh(vp_global(vp), + fh_from_vector(filehandle), &ol); + set_dos_error(res2, err); + return (struct Lock *)LOCK_TO_PTR(ol); } static int32 vp_open_from_lock(struct FSVP *vp, @@ -86,19 +195,28 @@ static int32 vp_open_from_lock(struct FSVP *vp, struct FileHandle *file, struct Lock *lock) { - set_unsupported(vp, res2); - (void)file; - (void)lock; - return DOSFALSE; + odfs_fh_t *odfs_fh = NULL; + LONG err; + + err = odfs_handler_open_from_lock_object(vp_global(vp), + lock_from_vector(lock), + &odfs_fh); + if (err == 0 && file) + file->fh_Arg2 = odfs_fh; + return return_dos_status(res2, err); } static struct Lock *vp_parent_of_fh(struct FSVP *vp, int32 *res2, struct FileHandle *file) { - set_unsupported(vp, res2); - (void)file; - return NULL; + odfs_lock_t *parent = NULL; + LONG err; + + err = odfs_handler_parent_fh_object(vp_global(vp), + fh_from_vector(file), &parent); + set_dos_error(res2, err); + return (struct Lock *)LOCK_TO_PTR(parent); } static int32 vp_open(struct FSVP *vp, @@ -108,19 +226,25 @@ static int32 vp_open(struct FSVP *vp, CONST_STRPTR obj, int32 mode) { - set_unsupported(vp, res2); - (void)fh; - (void)rel_dir; - (void)obj; - (void)mode; - return DOSFALSE; + odfs_fh_t *odfs_fh = NULL; + LONG err; + + err = odfs_handler_open_object(vp_global(vp), + lock_from_vector(rel_dir), + obj ? obj : "", mode, &odfs_fh); + if (err == 0 && fh) + fh->fh_Arg2 = odfs_fh; + return return_dos_status(res2, err); } static int32 vp_close(struct FSVP *vp, int32 *res2, struct FileHandle *file) { - set_unsupported(vp, res2); - (void)file; - return DOSFALSE; + LONG err; + + err = odfs_handler_close_object(vp_global(vp), fh_from_vector(file)); + if (err == 0 && file) + file->fh_Arg2 = NULL; + return return_dos_status(res2, err); } static int32 vp_delete(struct FSVP *vp, @@ -140,11 +264,13 @@ static int32 vp_read(struct FSVP *vp, STRPTR buffer, int32 numbytes) { - set_unsupported(vp, res2); - (void)file; - (void)buffer; - (void)numbytes; - return 0; + LONG actual = 0; + LONG err; + + err = odfs_handler_read_object(vp_global(vp), fh_from_vector(file), + buffer, numbytes, &actual); + set_dos_error(res2, err); + return err == 0 ? actual : -1; } static int32 vp_write(struct FSVP *vp, @@ -162,8 +288,9 @@ static int32 vp_write(struct FSVP *vp, static int32 vp_flush(struct FSVP *vp, int32 *res2) { - set_unsupported(vp, res2); - return DOSFALSE; + (void)vp; + set_dos_error(res2, 0); + return DOSTRUE; } static int32 vp_change_file_position(struct FSVP *vp, @@ -172,11 +299,12 @@ static int32 vp_change_file_position(struct FSVP *vp, int32 mode, int64 position) { - set_unsupported(vp, res2); - (void)file; - (void)mode; - (void)position; - return DOSFALSE; + int64_t oldpos; + + return return_dos_status(res2, + odfs_handler_seek_object( + vp_global(vp), fh_from_vector(file), + position, mode, &oldpos)); } static int32 vp_change_file_size(struct FSVP *vp, @@ -196,18 +324,26 @@ static int64 vp_get_file_position(struct FSVP *vp, int32 *res2, struct FileHandle *file) { - set_unsupported(vp, res2); - (void)file; - return 0; + int64_t pos; + LONG err; + + err = odfs_handler_get_file_position(vp_global(vp), + fh_from_vector(file), &pos); + set_dos_error(res2, err); + return err == 0 ? pos : -1; } static int64 vp_get_file_size(struct FSVP *vp, int32 *res2, struct FileHandle *file) { - set_unsupported(vp, res2); - (void)file; - return 0; + int64_t size; + LONG err; + + err = odfs_handler_get_file_size(vp_global(vp), + fh_from_vector(file), &size); + set_dos_error(res2, err); + return err == 0 ? size : -1; } static int32 vp_change_lock_mode(struct FSVP *vp, @@ -358,10 +494,15 @@ static int32 vp_same_lock(struct FSVP *vp, struct Lock *lock1, struct Lock *lock2) { - set_unsupported(vp, res2); - (void)lock1; - (void)lock2; - return DOSFALSE; + LONG same = LOCK_DIFFERENT; + LONG err; + + err = odfs_handler_same_lock_object(vp_global(vp), + lock_from_vector(lock1), + lock_from_vector(lock2), + &same); + set_dos_error(res2, err); + return err == 0 ? same : LOCK_DIFFERENT; } static int32 vp_same_file(struct FSVP *vp, @@ -369,10 +510,15 @@ static int32 vp_same_file(struct FSVP *vp, struct FileHandle *fh1, struct FileHandle *fh2) { - set_unsupported(vp, res2); - (void)fh1; - (void)fh2; - return DOSFALSE; + LONG same = LOCK_DIFFERENT; + LONG err; + + err = odfs_handler_same_file_object(vp_global(vp), + fh_from_vector(fh1), + fh_from_vector(fh2), + &same); + set_dos_error(res2, err); + return err == 0 ? same : LOCK_DIFFERENT; } static int32 vp_filesystem_attr(struct FSVP *vp, @@ -388,18 +534,18 @@ static int32 vp_volume_info_data(struct FSVP *vp, int32 *res2, struct InfoData *info) { - set_unsupported(vp, res2); - (void)info; - return DOSFALSE; + return return_dos_status(res2, + odfs_handler_fill_info(vp_global(vp), NULL, + info)); } static int32 vp_device_info_data(struct FSVP *vp, int32 *res2, struct InfoData *info) { - set_unsupported(vp, res2); - (void)info; - return DOSFALSE; + return return_dos_status(res2, + odfs_handler_fill_info(vp_global(vp), NULL, + info)); } static struct ExamineData *vp_examine_obj(struct FSVP *vp, @@ -407,28 +553,62 @@ static struct ExamineData *vp_examine_obj(struct FSVP *vp, struct Lock *lock, CONST_STRPTR object) { - set_unsupported(vp, res2); - (void)lock; - (void)object; - return NULL; + handler_global_t *g = vp_global(vp); + odfs_lock_t *ol = NULL; + struct ExamineData *ed; + LONG err; + + err = odfs_handler_lock_object(g, lock_from_vector(lock), + object ? object : "", SHARED_LOCK, &ol); + if (err != 0) { + set_dos_error(res2, err); + return NULL; + } + + ed = alloc_examine_data(g, ol->entry ? &ol->entry->fnode : NULL); + (void)odfs_handler_free_lock_object(g, ol); + set_dos_error(res2, ed ? 0 : ERROR_NO_FREE_STORE); + return ed; } static struct ExamineData *vp_examine_lock(struct FSVP *vp, int32 *res2, struct Lock *lock) { - set_unsupported(vp, res2); - (void)lock; - return NULL; + handler_global_t *g = vp_global(vp); + const odfs_node_t *node = NULL; + struct ExamineData *ed; + LONG err; + + err = odfs_handler_get_lock_node(g, lock_from_vector(lock), &node); + if (err != 0) { + set_dos_error(res2, err); + return NULL; + } + + ed = alloc_examine_data(g, node); + set_dos_error(res2, ed ? 0 : ERROR_NO_FREE_STORE); + return ed; } static struct ExamineData *vp_examine_file(struct FSVP *vp, int32 *res2, struct FileHandle *file) { - set_unsupported(vp, res2); - (void)file; - return NULL; + handler_global_t *g = vp_global(vp); + const odfs_node_t *node = NULL; + struct ExamineData *ed; + LONG err; + + err = odfs_handler_get_fh_node(g, fh_from_vector(file), &node); + if (err != 0) { + set_dos_error(res2, err); + return NULL; + } + + ed = alloc_examine_data(g, node); + set_dos_error(res2, ed ? 0 : ERROR_NO_FREE_STORE); + return ed; } static int32 vp_examine_dir(struct FSVP *vp, From f2a0277a810d9c3c33d8bdb5bdd8af8a159aab0a Mon Sep 17 00:00:00 2001 From: Stefan Reinauer Date: Thu, 21 May 2026 15:38:31 -0700 Subject: [PATCH 05/24] amiga: finish read-only OS4 vector stubs Wire native OS4 vector callbacks for inhibit, serialize, and lock or file mode changes into the shared handler operations. Return ERROR_DISK_WRITE_PROTECTED from mutating vectors so DOS sees a deliberate read-only filesystem result instead of missing support. --- platform/amiga/handler.h | 7 +++ platform/amiga/handler_main.c | 77 ++++++++++++++++++++++++++++---- platform/amiga/os4/vector_port.c | 67 +++++++++++++++------------ 3 files changed, 113 insertions(+), 38 deletions(-) diff --git a/platform/amiga/handler.h b/platform/amiga/handler.h index c45fe6f..bb70731 100644 --- a/platform/amiga/handler.h +++ b/platform/amiga/handler.h @@ -252,6 +252,12 @@ LONG odfs_handler_seek_object(handler_global_t *g, int64_t offset, LONG mode, int64_t *oldpos_out); +LONG odfs_handler_change_lock_mode(handler_global_t *g, + odfs_lock_t *ol, + LONG mode); +LONG odfs_handler_change_file_mode(handler_global_t *g, + odfs_fh_t *fh, + LONG mode); LONG odfs_handler_get_file_position(handler_global_t *g, odfs_fh_t *fh, int64_t *pos_out); @@ -270,6 +276,7 @@ LONG odfs_handler_get_fh_node(handler_global_t *g, ULONG odfs_handler_node_key(const odfs_node_t *node); ULONG odfs_handler_node_protection(const odfs_node_t *node); void odfs_handler_node_date(const odfs_node_t *node, struct DateStamp *ds); +LONG odfs_handler_inhibit(handler_global_t *g, LONG state); /* handler entry point (called from startup.S) */ void handler_main(void); diff --git a/platform/amiga/handler_main.c b/platform/amiga/handler_main.c index 2b5b09f..de5c692 100644 --- a/platform/amiga/handler_main.c +++ b/platform/amiga/handler_main.c @@ -1297,6 +1297,9 @@ static void free_volume(odfs_volume_t *volume) odfs_amiga_free_mem(volume, sizeof(*volume)); } +static void mount_volume(handler_global_t *g); +static void unmount_volume(handler_global_t *g); + static void drain_all_objects(handler_global_t *g) { struct Node *node; @@ -2203,6 +2206,49 @@ LONG odfs_handler_get_file_position(handler_global_t *g, return 0; } +LONG odfs_handler_change_lock_mode(handler_global_t *g, + odfs_lock_t *ol, + LONG mode) +{ + if (!g || !ol) + return ERROR_INVALID_LOCK; + if (mode != SHARED_LOCK && mode != EXCLUSIVE_LOCK) + return ERROR_BAD_NUMBER; + if (!lock_is_active(g, ol)) + return ERROR_INVALID_LOCK; + + { + LONG err_dos = validate_object_volume(g, ol->entry->volume); + if (err_dos != 0) + return err_dos; + } + + ol->lock.fl_Access = + (lock_node(ol)->kind == ODFS_NODE_DIR) ? SHARED_LOCK : mode; + return 0; +} + +LONG odfs_handler_change_file_mode(handler_global_t *g, + odfs_fh_t *fh, + LONG mode) +{ + if (!g || !fh) + return ERROR_OBJECT_NOT_FOUND; + if (mode != SHARED_LOCK && mode != EXCLUSIVE_LOCK) + return ERROR_BAD_NUMBER; + if (!fh_is_active(g, fh)) + return ERROR_OBJECT_NOT_FOUND; + + { + LONG err_dos = validate_object_volume(g, fh_volume(fh)); + if (err_dos != 0) + return err_dos; + } + + fh->access = mode; + return 0; +} + LONG odfs_handler_get_file_size(handler_global_t *g, odfs_fh_t *fh, int64_t *size_out) @@ -2307,6 +2353,22 @@ LONG odfs_handler_get_fh_node(handler_global_t *g, return 0; } +LONG odfs_handler_inhibit(handler_global_t *g, LONG state) +{ + if (!g) + return ERROR_REQUIRED_ARG_MISSING; + + if (state != DOSFALSE) { + g->inhibited = 1; + unmount_volume(g); + } else { + g->inhibited = 0; + mount_volume(g); + } + + return 0; +} + /* ------------------------------------------------------------------ */ /* packet handlers */ /* ------------------------------------------------------------------ */ @@ -3034,17 +3096,14 @@ static void action_current_volume(handler_global_t *g, static void action_inhibit(handler_global_t *g, struct DosPacket *pkt) { - LONG state = pkt->dp_Arg1; + LONG err_dos = odfs_handler_inhibit(g, pkt->dp_Arg1); - if (state != DOSFALSE) { - /* inhibit on — unmount, stop I/O */ - g->inhibited = 1; - unmount_volume(g); - } else { - /* inhibit off — try to remount */ - g->inhibited = 0; - mount_volume(g); + if (err_dos != 0) { + pkt->dp_Res1 = DOSFALSE; + pkt->dp_Res2 = err_dos; + return; } + pkt->dp_Res1 = DOSTRUE; } diff --git a/platform/amiga/os4/vector_port.c b/platform/amiga/os4/vector_port.c index c37510c..adbc867 100644 --- a/platform/amiga/os4/vector_port.c +++ b/platform/amiga/os4/vector_port.c @@ -26,6 +26,14 @@ static void set_unsupported(struct FSVP *vp, int32 *res2) *res2 = ERROR_ACTION_NOT_KNOWN; } +static void set_write_protected(struct FSVP *vp, int32 *res2) +{ + (void)vp; + + if (res2) + *res2 = ERROR_DISK_WRITE_PROTECTED; +} + static handler_global_t *vp_global(struct FSVP *vp) { return vp ? (handler_global_t *)vp->FSV.FSPrivate : NULL; @@ -157,7 +165,7 @@ static struct Lock *vp_create_dir(struct FSVP *vp, struct Lock *rel_lock, CONST_STRPTR obj) { - set_unsupported(vp, res2); + set_write_protected(vp, res2); (void)rel_lock; (void)obj; return NULL; @@ -252,7 +260,7 @@ static int32 vp_delete(struct FSVP *vp, struct Lock *rel_dirlock, CONST_STRPTR obj) { - set_unsupported(vp, res2); + set_write_protected(vp, res2); (void)rel_dirlock; (void)obj; return DOSFALSE; @@ -279,11 +287,11 @@ static int32 vp_write(struct FSVP *vp, STRPTR buffer, int32 numbytes) { - set_unsupported(vp, res2); + set_write_protected(vp, res2); (void)file; (void)buffer; (void)numbytes; - return 0; + return -1; } static int32 vp_flush(struct FSVP *vp, int32 *res2) @@ -313,7 +321,7 @@ static int32 vp_change_file_size(struct FSVP *vp, int32 mode, int64 size) { - set_unsupported(vp, res2); + set_write_protected(vp, res2); (void)file; (void)mode; (void)size; @@ -351,10 +359,10 @@ static int32 vp_change_lock_mode(struct FSVP *vp, struct Lock *lock, int32 new_lock_mode) { - set_unsupported(vp, res2); - (void)lock; - (void)new_lock_mode; - return DOSFALSE; + return return_dos_status(res2, + odfs_handler_change_lock_mode( + vp_global(vp), lock_from_vector(lock), + new_lock_mode)); } static int32 vp_change_file_mode(struct FSVP *vp, @@ -362,10 +370,10 @@ static int32 vp_change_file_mode(struct FSVP *vp, struct FileHandle *fh, int32 new_lock_mode) { - set_unsupported(vp, res2); - (void)fh; - (void)new_lock_mode; - return DOSFALSE; + return return_dos_status(res2, + odfs_handler_change_file_mode( + vp_global(vp), fh_from_vector(fh), + new_lock_mode)); } static int32 vp_set_date(struct FSVP *vp, @@ -374,7 +382,7 @@ static int32 vp_set_date(struct FSVP *vp, CONST_STRPTR name, const struct DateStamp *ds) { - set_unsupported(vp, res2); + set_write_protected(vp, res2); (void)rel_dirlock; (void)name; (void)ds; @@ -387,7 +395,7 @@ static int32 vp_set_protection(struct FSVP *vp, CONST_STRPTR name, uint32 mask) { - set_unsupported(vp, res2); + set_write_protected(vp, res2); (void)rel_dirlock; (void)name; (void)mask; @@ -400,7 +408,7 @@ static int32 vp_set_comment(struct FSVP *vp, CONST_STRPTR name, CONST_STRPTR comment) { - set_unsupported(vp, res2); + set_write_protected(vp, res2); (void)rel_dirlock; (void)name; (void)comment; @@ -413,7 +421,7 @@ static int32 vp_set_group(struct FSVP *vp, CONST_STRPTR name, uint32 group) { - set_unsupported(vp, res2); + set_write_protected(vp, res2); (void)rel_dirlock; (void)name; (void)group; @@ -426,7 +434,7 @@ static int32 vp_set_user(struct FSVP *vp, CONST_STRPTR name, uint32 user) { - set_unsupported(vp, res2); + set_write_protected(vp, res2); (void)rel_dirlock; (void)name; (void)user; @@ -440,7 +448,7 @@ static int32 vp_rename(struct FSVP *vp, struct Lock *dst_rel, CONST_STRPTR dst) { - set_unsupported(vp, res2); + set_write_protected(vp, res2); (void)src_rel; (void)src; (void)dst_rel; @@ -454,7 +462,7 @@ static int32 vp_create_soft_link(struct FSVP *vp, CONST_STRPTR linkname, CONST_STRPTR dest_obj) { - set_unsupported(vp, res2); + set_write_protected(vp, res2); (void)rel_dirlock; (void)linkname; (void)dest_obj; @@ -467,7 +475,7 @@ static int32 vp_create_hard_link(struct FSVP *vp, CONST_STRPTR linkname, struct Lock *dest_obj) { - set_unsupported(vp, res2); + set_write_protected(vp, res2); (void)rel_dirlock; (void)linkname; (void)dest_obj; @@ -622,9 +630,9 @@ static int32 vp_examine_dir(struct FSVP *vp, static int32 vp_inhibit(struct FSVP *vp, int32 *res2, int32 inhibit_state) { - set_unsupported(vp, res2); - (void)inhibit_state; - return DOSFALSE; + return return_dos_status(res2, + odfs_handler_inhibit(vp_global(vp), + inhibit_state)); } static int32 vp_write_protect(struct FSVP *vp, @@ -632,7 +640,7 @@ static int32 vp_write_protect(struct FSVP *vp, int32 wp_state, uint32 passkey) { - set_unsupported(vp, res2); + set_write_protected(vp, res2); (void)wp_state; (void)passkey; return DOSFALSE; @@ -644,7 +652,7 @@ static int32 vp_format(struct FSVP *vp, uint32 dostype, uint32 spare) { - set_unsupported(vp, res2); + set_write_protected(vp, res2); (void)new_volname; (void)dostype; (void)spare; @@ -653,15 +661,16 @@ static int32 vp_format(struct FSVP *vp, static int32 vp_serialize(struct FSVP *vp, int32 *res2) { - set_unsupported(vp, res2); - return DOSFALSE; + (void)vp; + set_dos_error(res2, 0); + return DOSTRUE; } static int32 vp_relabel(struct FSVP *vp, int32 *res2, CONST_STRPTR new_volumename) { - set_unsupported(vp, res2); + set_write_protected(vp, res2); (void)new_volumename; return DOSFALSE; } From fff73ce24b5e93a5a964789a69a1e17c8190d4b8 Mon Sep 17 00:00:00 2001 From: Stefan Reinauer Date: Thu, 21 May 2026 15:57:08 -0700 Subject: [PATCH 06/24] amiga: implement OS4 directory examine vector Add a shared handler operation that returns the next directory entry by resume key, matching the existing packet iteration rules. Use it from FSExamineDir to allocate native ExamineData entries, recycle stale context nodes when possible, and append results to the OS4 examine context FreshNodeList. --- platform/amiga/handler.h | 5 ++ platform/amiga/handler_main.c | 84 ++++++++++++++++++++++++++++++++ platform/amiga/os4/vector_port.c | 82 ++++++++++++++++++++++++++----- 3 files changed, 158 insertions(+), 13 deletions(-) diff --git a/platform/amiga/handler.h b/platform/amiga/handler.h index bb70731..a6e3a59 100644 --- a/platform/amiga/handler.h +++ b/platform/amiga/handler.h @@ -273,6 +273,11 @@ LONG odfs_handler_get_lock_node(handler_global_t *g, LONG odfs_handler_get_fh_node(handler_global_t *g, odfs_fh_t *fh, const odfs_node_t **node_out); +LONG odfs_handler_next_dir_entry(handler_global_t *g, + odfs_lock_t *ol, + ULONG previous_key, + odfs_node_t *entry_out, + ULONG *key_out); ULONG odfs_handler_node_key(const odfs_node_t *node); ULONG odfs_handler_node_protection(const odfs_node_t *node); void odfs_handler_node_date(const odfs_node_t *node, struct DateStamp *ds); diff --git a/platform/amiga/handler_main.c b/platform/amiga/handler_main.c index de5c692..bd09f0c 100644 --- a/platform/amiga/handler_main.c +++ b/platform/amiga/handler_main.c @@ -2555,6 +2555,90 @@ static odfs_err_t exnext_cb(const odfs_node_t *entry, void *ctx) return ODFS_ERR_EOF; /* stop after one entry */ } +typedef struct dir_next_ctx { + ULONG previous_key; + int first; + int seen_previous; + int found; + odfs_node_t entry; +} dir_next_ctx_t; + +static odfs_err_t dir_next_cb(const odfs_node_t *entry, void *ctx) +{ + dir_next_ctx_t *dc = ctx; + + if (!dc->first && !dc->seen_previous) { + if (amiga_node_key(entry) == dc->previous_key) + dc->seen_previous = 1; + return ODFS_OK; + } + + dc->entry = *entry; + dc->found = 1; + return ODFS_ERR_EOF; +} + +LONG odfs_handler_next_dir_entry(handler_global_t *g, + odfs_lock_t *ol, + ULONG previous_key, + odfs_node_t *entry_out, + ULONG *key_out) +{ + const odfs_node_t *dir; + ULONG dir_key; + dir_next_ctx_t dc; + uint32_t resume = 0; + + if (!g || !entry_out || !key_out) + return ERROR_REQUIRED_ARG_MISSING; + + dir = ol ? lock_node(ol) : &g->mount.root; + + if (ol) { + LONG err_dos = validate_object_volume(g, ol->entry->volume); + if (err_dos != 0) + return err_dos; + } else if (!g->mounted) { + return ERROR_NO_DISK; + } + + if (dir->kind != ODFS_NODE_DIR) + return ERROR_OBJECT_WRONG_TYPE; + + dir_key = ol ? ol->key : amiga_node_key(dir); + memset(&dc, 0, sizeof(dc)); + dc.previous_key = previous_key; + dc.first = (previous_key == 0 || previous_key == dir_key); + +#if ODFS_FEATURE_CDDA + if (g->has_cdda && !dc.first && + previous_key == amiga_node_key(&g->cdda_root)) + return ERROR_NO_MORE_ENTRIES; + + if (g->has_cdda && dir->backend == ODFS_BACKEND_CDDA) { + (void)cdda_backend_ops.readdir(g->cdda_ctx, &g->mount.cache, + &g->log, dir, dir_next_cb, &dc, + &resume); + } else +#endif + { + (void)odfs_readdir(&g->mount, dir, dir_next_cb, &dc, &resume); + +#if ODFS_FEATURE_CDDA + if (!dc.found && g->has_cdda && node_is_mount_root(g, dir) && + previous_key != amiga_node_key(&g->cdda_root)) + (void)dir_next_cb(&g->cdda_root, &dc); +#endif + } + + if (!dc.found) + return ERROR_NO_MORE_ENTRIES; + + *entry_out = dc.entry; + *key_out = amiga_node_key(&dc.entry); + return 0; +} + static void action_examine_object(handler_global_t *g, struct DosPacket *pkt) { odfs_lock_t *ol = LOCK_FROM_BPTR(pkt->dp_Arg1); diff --git a/platform/amiga/os4/vector_port.c b/platform/amiga/os4/vector_port.c index adbc867..beff972 100644 --- a/platform/amiga/os4/vector_port.c +++ b/platform/amiga/os4/vector_port.c @@ -10,10 +10,12 @@ #include #include +#include #include #include #include +#include #include #include @@ -69,8 +71,34 @@ static const char *node_name_for_examine(handler_global_t *g, return node ? node->name : ""; } -static struct ExamineData *alloc_examine_data(handler_global_t *g, - const odfs_node_t *node) +static struct ExamineData *take_stale_examine_data( + struct PRIVATE_ExamineDirContext *ctx, + size_t name_len, + size_t comment_len) +{ + struct ExamineData *ed; + + if (!ctx) + return NULL; + + while (!IsMinListEmpty(&ctx->StaleNodeList)) { + ed = (struct ExamineData *)RemHead( + (struct List *)&ctx->StaleNodeList); + if (ed->NameSize >= name_len && + ed->CommentSize >= comment_len && + ed->LinkSize >= 1) + return ed; + + FreeDosObject(DOS_EXAMINEDATA, ed); + } + + return NULL; +} + +static struct ExamineData *alloc_examine_data_from_context( + handler_global_t *g, + const odfs_node_t *node, + struct PRIVATE_ExamineDirContext *ctx) { const char *name; const char *comment = ""; @@ -88,17 +116,21 @@ static struct ExamineData *alloc_examine_data(handler_global_t *g, name_len = strlen(name) + 1u; comment_len = strlen(comment) + 1u; - ed = AllocDosObjectTags(DOS_EXAMINEDATA, - ADO_ExamineData_NameSize, - (ULONG)name_len, - ADO_ExamineData_CommentSize, - (ULONG)comment_len, - ADO_ExamineData_LinkSize, - 1UL, - TAG_END); + ed = take_stale_examine_data(ctx, name_len, comment_len); + if (!ed) { + ed = AllocDosObjectTags(DOS_EXAMINEDATA, + ADO_ExamineData_NameSize, + (ULONG)name_len, + ADO_ExamineData_CommentSize, + (ULONG)comment_len, + ADO_ExamineData_LinkSize, + 1UL, + TAG_END); + } if (!ed) return NULL; + ed->EXDinfo = 0; ed->Type = (node->kind == ODFS_NODE_DIR) ? FSO_TYPE_DIRECTORY : FSO_TYPE_FILE; ed->FileSize = (node->kind == ODFS_NODE_DIR) ? -1LL : (int64)node->size; @@ -124,6 +156,12 @@ static struct ExamineData *alloc_examine_data(handler_global_t *g, return ed; } +static struct ExamineData *alloc_examine_data(handler_global_t *g, + const odfs_node_t *node) +{ + return alloc_examine_data_from_context(g, node, NULL); +} + static struct Lock *vp_lock(struct FSVP *vp, int32 *res2, struct Lock *rel_lock, @@ -623,9 +661,27 @@ static int32 vp_examine_dir(struct FSVP *vp, int32 *res2, struct PRIVATE_ExamineDirContext *ctx) { - set_unsupported(vp, res2); - (void)ctx; - return DOSFALSE; + handler_global_t *g = vp_global(vp); + odfs_node_t entry; + ULONG key = 0; + struct ExamineData *ed; + LONG err; + + if (!ctx) + return return_dos_status(res2, ERROR_REQUIRED_ARG_MISSING); + + err = odfs_handler_next_dir_entry(g, lock_from_vector(ctx->ReferenceLock), + ctx->FSPrivate[0], &entry, &key); + if (err != 0) + return return_dos_status(res2, err); + + ed = alloc_examine_data_from_context(g, &entry, ctx); + if (!ed) + return return_dos_status(res2, ERROR_NO_FREE_STORE); + + AddTail((struct List *)&ctx->FreshNodeList, (struct Node *)&ed->EXDnode); + ctx->FSPrivate[0] = key; + return return_dos_status(res2, 0); } static int32 vp_inhibit(struct FSVP *vp, int32 *res2, int32 inhibit_state) From baed6581f35d8bb55819a7180b8fc81f82300430 Mon Sep 17 00:00:00 2001 From: Stefan Reinauer Date: Thu, 11 Jun 2026 21:31:43 -0700 Subject: [PATCH 07/24] amiga: fix default cross-compiler selection in Makefile Make always defines CC, defaulting to "cc", so the conditional assignment "CC ?= m68k-amigaos-gcc" never took effect and a plain "make amiga" tried to build the handler with the host compiler. Assign the m68k cross-compiler only when CC still carries make's built-in default, so command-line and environment overrides such as CC=ppc-amigaos-gcc keep selecting their own toolchain unchanged. --- Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index f14e186..26c5c2e 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,9 @@ # Amiga cross-compiler. # Override with CC=ppc-amigaos-gcc for an AmigaOS 4 PPC build. -CC ?= m68k-amigaos-gcc +ifeq ($(origin CC),default) +CC = m68k-amigaos-gcc +endif # AROS cross-compiler (override: make CC=m68k-aros-gcc AROS=1) # When AROS=1, uses -static instead of -noixemul and defines __AROS__ From 8e7c120d98c7685a775bff0c01fcf5f1402d0f7f Mon Sep 17 00:00:00 2001 From: Stefan Reinauer Date: Thu, 11 Jun 2026 21:32:17 -0700 Subject: [PATCH 08/24] amiga: publish OS4 vector port as the handler port On AmigaOS 4, dos.library only calls a filesystem through its native FileSystemVectors when the handler's port validates as a vector port. The handler replied to ACTION_STARTUP with the plain process message port, so even with the vector callbacks implemented, every DOS call was forced through legacy packet emulation. Allocate the FileSystemVectorPort during startup, attach a dedicated signal to its embedded message port, and verify the object with GetFileSystemVectorPort() before relying on it. Switch the handler port to the vector port before replying to the startup packet, so the device node, volume node, locks, and the packet loop all reference the port DOS validates. If activation fails, the startup packet is rejected and the port and signal are released; on shutdown the process port is restored before the handler exits. Track the owning process port separately from the active handler port in handler_global, and introduce an ODFS_AMIGA_OS4 macro in the per-target compat headers so shared code can test the build target without scattering __amigaos4__ checks. --- platform/amiga/handler.h | 8 +++ platform/amiga/handler_main.c | 91 +++++++++++++++++++++++- platform/amiga/os3/amiga_target_compat.h | 2 + platform/amiga/os4/amiga_target_compat.h | 2 + 4 files changed, 102 insertions(+), 1 deletion(-) diff --git a/platform/amiga/handler.h b/platform/amiga/handler.h index a6e3a59..462afee 100644 --- a/platform/amiga/handler.h +++ b/platform/amiga/handler.h @@ -24,6 +24,9 @@ typedef struct odfs_entry odfs_entry_t; typedef struct odfs_lock odfs_lock_t; typedef struct odfs_fh odfs_fh_t; typedef struct odfs_changeint_data odfs_changeint_data_t; +#if ODFS_AMIGA_OS4 +struct FileSystemVectorPort; +#endif struct odfs_changeint_data { struct Task *task; @@ -33,7 +36,12 @@ struct odfs_changeint_data { /* ---- handler globals ---- */ typedef struct handler_global { + struct MsgPort *process_port; /* owning process message port */ struct MsgPort *dosport; /* DOS message port */ +#if ODFS_AMIGA_OS4 + struct FileSystemVectorPort *vector_port; /* native OS4 vector port */ + LONG vector_sigbit; /* signal bit used by vector port */ +#endif struct DeviceNode *devnode; /* startup packet device node */ struct FileSysStartupMsg *fssm; /* startup packet FSSM */ struct DeviceNode *published_devnode; /* DOS device-list entry */ diff --git a/platform/amiga/handler_main.c b/platform/amiga/handler_main.c index bd09f0c..a56171b 100644 --- a/platform/amiga/handler_main.c +++ b/platform/amiga/handler_main.c @@ -11,6 +11,10 @@ #include "handler.h" #include "sys_compat.h" +#if ODFS_AMIGA_OS4 +#include "vector_port.h" +#endif + #if ODFS_FEATURE_CDDA #include "cdda/cdda.h" #endif @@ -47,6 +51,10 @@ static void handle_packet(handler_global_t *g, struct DosPacket *pkt); static void return_packet(handler_global_t *g, struct DosPacket *pkt); static void publish_device_node(handler_global_t *g); static void unpublish_device_node(handler_global_t *g); +#if ODFS_AMIGA_OS4 +static LONG activate_vector_port(handler_global_t *g); +static void deactivate_vector_port(handler_global_t *g); +#endif static void mount_volume(handler_global_t *g); static void unmount_volume(handler_global_t *g); static void free_volume(odfs_volume_t *volume); @@ -3489,6 +3497,64 @@ static void unpublish_device_node(handler_global_t *g) g->published_devnode_owned = 0; } +#if ODFS_AMIGA_OS4 +static LONG activate_vector_port(handler_global_t *g) +{ + struct FileSystemVectorPort *vp; + LONG sigbit; + + if (!g || !g->process_port) + return ERROR_REQUIRED_ARG_MISSING; + + vp = odfs_os4_alloc_vector_port(g); + if (!vp) + return ERROR_NO_FREE_STORE; + + sigbit = AllocSignal(-1); + if (sigbit < 0) { + odfs_os4_free_vector_port(vp); + return ERROR_NO_FREE_STORE; + } + + vp->MP.mp_Flags = PA_SIGNAL; + vp->MP.mp_SigBit = (UBYTE)sigbit; + vp->MP.mp_SigTask = FindTask(NULL); + + if (GetFileSystemVectorPort(&vp->MP, FS_VECTORPORT_VERSION) != vp) { + FreeSignal(sigbit); + odfs_os4_free_vector_port(vp); + return ERROR_OBJECT_WRONG_TYPE; + } + + g->vector_port = vp; + g->vector_sigbit = sigbit; + g->dosport = &vp->MP; + if (g->devnode) + g->devnode->dn_Task = g->dosport; + + ODFS_INFO(&g->log, ODFS_SUB_CORE, + "OS4 filesystem vector port active"); + return 0; +} + +static void deactivate_vector_port(handler_global_t *g) +{ + if (!g) + return; + + if (g->vector_port) { + odfs_os4_free_vector_port(g->vector_port); + g->vector_port = NULL; + } + if (g->vector_sigbit >= 0) { + FreeSignal((BYTE)g->vector_sigbit); + g->vector_sigbit = -1; + } + if (g->process_port) + g->dosport = g->process_port; +} +#endif + /* ------------------------------------------------------------------ */ /* volume mount / unmount */ /* ------------------------------------------------------------------ */ @@ -4032,6 +4098,9 @@ void handler_main_startup(struct Message *startup_msg) g->fhlist.mlh_Tail = NULL; g->fhlist.mlh_TailPred = (struct MinNode *)&g->fhlist.mlh_Head; g->next_volume_id = 1; +#if ODFS_AMIGA_OS4 + g->vector_sigbit = -1; +#endif g->chgsigbit = -1; g->toc_passthrough = -1; g->last_session_passthrough = -1; @@ -4040,7 +4109,8 @@ void handler_main_startup(struct Message *startup_msg) { struct Process *proc = (struct Process *)FindTask(NULL); - g->dosport = &proc->pr_MsgPort; + g->process_port = &proc->pr_MsgPort; + g->dosport = g->process_port; } /* wait for startup packet */ @@ -4180,6 +4250,21 @@ void handler_main_startup(struct Message *startup_msg) g->media.ops = &amiga_media_ops; g->media.ctx = &amctx; +#if ODFS_AMIGA_OS4 + { + LONG err_dos = activate_vector_port(g); + if (err_dos != 0) { + ODFS_ERROR(&g->log, ODFS_SUB_CORE, + "OS4 filesystem vector port setup failed: %ld", + (long)err_dos); + pkt->dp_Res1 = DOSFALSE; + pkt->dp_Res2 = err_dos; + return_packet(g, pkt); + goto shutdown; + } + } +#endif + /* reply startup packet */ pkt->dp_Res1 = DOSTRUE; pkt->dp_Res2 = 0; @@ -4259,6 +4344,10 @@ void handler_main_startup(struct Message *startup_msg) if (g->devnode) g->devnode->dn_Task = NULL; +#if ODFS_AMIGA_OS4 + deactivate_vector_port(g); +#endif + odfs_amiga_close_libraries(); odfs_amiga_free_mem(g, sizeof(*g)); } diff --git a/platform/amiga/os3/amiga_target_compat.h b/platform/amiga/os3/amiga_target_compat.h index e4a1634..b02e803 100644 --- a/platform/amiga/os3/amiga_target_compat.h +++ b/platform/amiga/os3/amiga_target_compat.h @@ -7,4 +7,6 @@ #ifndef ODFS_AMIGA_TARGET_COMPAT_H #define ODFS_AMIGA_TARGET_COMPAT_H +#define ODFS_AMIGA_OS4 0 + #endif /* ODFS_AMIGA_TARGET_COMPAT_H */ diff --git a/platform/amiga/os4/amiga_target_compat.h b/platform/amiga/os4/amiga_target_compat.h index aa053b5..27be916 100644 --- a/platform/amiga/os4/amiga_target_compat.h +++ b/platform/amiga/os4/amiga_target_compat.h @@ -7,6 +7,8 @@ #ifndef ODFS_AMIGA_TARGET_COMPAT_H #define ODFS_AMIGA_TARGET_COMPAT_H +#define ODFS_AMIGA_OS4 1 + /* * The current shared packet handler still uses classic names such as * DeviceList and ACTION_DIE. Keep that compatibility local to the OS4 From 94ebe050f130a9ceab166e5dc1df5931681954bc Mon Sep 17 00:00:00 2001 From: Stefan Reinauer Date: Thu, 11 Jun 2026 21:33:33 -0700 Subject: [PATCH 09/24] amiga: route OS4 serial debug logging through DebugPrintF The SERIAL_DEBUG log sink wrote bytes to the serial port with inline m68k assembly calling exec's RawPutChar vector, which cannot build for the PPC OS4 target. Keep the raw serial path for OS3 and use exec's DebugPrintF() on OS4, which ends up in the same serial/Sashimi debug stream. This makes the SERIAL_DEBUG=1 and PACKET_TRACE=1 build variants compile for OS4. --- platform/amiga/handler_main.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/platform/amiga/handler_main.c b/platform/amiga/handler_main.c index a56171b..20dafda 100644 --- a/platform/amiga/handler_main.c +++ b/platform/amiga/handler_main.c @@ -859,6 +859,7 @@ static const odfs_media_ops_t amiga_media_ops = { /* ------------------------------------------------------------------ */ #if ODFS_SERIAL_DEBUG +#if !ODFS_AMIGA_OS4 static inline void raw_putchar(char c) { register char _d0 __asm("d0") = c; @@ -876,6 +877,7 @@ static void serial_puts(const char *s) while (*s) raw_putchar(*s++); } +#endif #if ODFS_PACKET_TRACE static void trace_pkt(handler_global_t *g, const char *tag, struct DosPacket *pkt) @@ -929,8 +931,12 @@ static void log_sink(odfs_log_level_t level, odfs_log_subsys_t subsys, (void)level; (void)subsys; (void)ctx; +#if ODFS_AMIGA_OS4 + DebugPrintF("%s\n", msg); +#else serial_puts(msg); raw_putchar('\n'); +#endif } #else static void log_sink(odfs_log_level_t level, odfs_log_subsys_t subsys, From c04e8091bd052f75db9ba47f53808127bf3829cd Mon Sep 17 00:00:00 2001 From: Stefan Reinauer Date: Thu, 11 Jun 2026 21:43:20 -0700 Subject: [PATCH 10/24] amiga: exit the packet loop on OS4 ACTION_SHUTDOWN AmigaOS 4 deprecates ACTION_DIE in favor of ACTION_SHUTDOWN (V51+), which DismountDevice() sends to ask a filesystem to remove its volume node and terminate. The handler only recognized ACTION_DIE, so an OS4 dismount would have been answered with ERROR_ACTION_NOT_KNOWN and the handler process would have stayed alive. Accept ACTION_SHUTDOWN wherever ACTION_DIE is accepted: reply DOSTRUE and leave the packet loop, which already unmounts the volume, drains outstanding locks and filehandles, and unpublishes the device node. The OS4 SDK defines no shutdown member in struct FileSystemVectors, so the packet loop remains the correct delivery path even with the native vector port active. The OS3 NDK does not define the packet number, so the OS3 target compat header supplies it; OS3 DOS never sends it, and accepting it unconditionally keeps the shared loop free of OS conditionals. --- platform/amiga/handler_main.c | 4 +++- platform/amiga/os3/amiga_target_compat.h | 8 ++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/platform/amiga/handler_main.c b/platform/amiga/handler_main.c index 20dafda..b206ed4 100644 --- a/platform/amiga/handler_main.c +++ b/platform/amiga/handler_main.c @@ -3281,6 +3281,7 @@ static void handle_packet(handler_global_t *g, struct DosPacket *pkt) /* ---- shutdown ---- */ case ACTION_DIE: + case ACTION_SHUTDOWN: pkt->dp_Res1 = DOSTRUE; break; @@ -4306,7 +4307,8 @@ void handler_main_startup(struct Message *startup_msg) trace_pkt(g, "dequeue", pkt); #endif - if (pkt->dp_Type == ACTION_DIE) { + if (pkt->dp_Type == ACTION_DIE || + pkt->dp_Type == ACTION_SHUTDOWN) { pkt->dp_Res1 = DOSTRUE; pkt->dp_Res2 = 0; return_packet(g, pkt); diff --git a/platform/amiga/os3/amiga_target_compat.h b/platform/amiga/os3/amiga_target_compat.h index b02e803..d3ab2e7 100644 --- a/platform/amiga/os3/amiga_target_compat.h +++ b/platform/amiga/os3/amiga_target_compat.h @@ -9,4 +9,12 @@ #define ODFS_AMIGA_OS4 0 +/* + * OS4 V51+ shutdown packet. OS3 DOS never sends it, but accepting it + * unconditionally keeps the shared packet loop free of OS conditionals. + */ +#ifndef ACTION_SHUTDOWN +#define ACTION_SHUTDOWN 3000 +#endif + #endif /* ODFS_AMIGA_TARGET_COMPAT_H */ From 35d5444505fc4778cd50bd31f9319821de90639f Mon Sep 17 00:00:00 2001 From: Stefan Reinauer Date: Thu, 11 Jun 2026 22:07:54 -0700 Subject: [PATCH 11/24] amiga: fix OS4 media-change interrupt calling convention The OS4 frontend cast the portable one-argument media-change callback directly into is_Code. On AmigaOS 4 the V50+ conventions documented in the exec autodocs pass is_Data as the third argument: Cause() soft interrupts: void handler(int32 unused, struct ExecBase *sysbase, APTR is_data); interrupt servers (AddIntServer): ULONG handler(struct ExceptionContext *ctx, struct ExecBase *sysbase, APTR userData); With the old cast, the callback's data parameter received zero (or the exception context) instead of the change-interrupt data, so the NULL check in the callback made the handler silently miss every media-change event fired through TD_ADDCHANGEINT. Install a V50-style trampoline that ignores the first two arguments and forwards is_Data to the portable callback. Both documented conventions place is_Data third, so one entry point covers either delivery path. This mirrors the a1 register-binding trampoline the OS3 frontend already uses. --- platform/amiga/os4/sys_compat.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/platform/amiga/os4/sys_compat.c b/platform/amiga/os4/sys_compat.c index bb7bdd8..28dd4ad 100644 --- a/platform/amiga/os4/sys_compat.c +++ b/platform/amiga/os4/sys_compat.c @@ -23,6 +23,24 @@ struct UtilityBase *UtilityBase; static struct DOSIFace *dos_iface; static struct UtilityIFace *utility_iface; +static odfs_amiga_interrupt_fn interrupt_code; + +/* + * V50+ interrupt entry. Soft interrupts fired through Cause() receive + * (0, SysBase, is_Data); interrupt servers receive (context, SysBase, + * userData). Both pass is_Data third, so one entry covers either path. + */ +static void odfs_amiga_interrupt_entry(int32 unused, + struct ExecBase *sysbase, + APTR data) +{ + (void)unused; + (void)sysbase; + + if (interrupt_code) + interrupt_code(data); +} + void odfs_amiga_init_sysbase(void) { SysBase = *((struct ExecBase **)4L); @@ -169,11 +187,12 @@ void odfs_amiga_init_interrupt(struct Interrupt *intr, APTR data, odfs_amiga_interrupt_fn code) { + interrupt_code = code; intr->is_Node.ln_Type = NT_INTERRUPT; intr->is_Node.ln_Pri = 0; intr->is_Node.ln_Name = (char *)name; intr->is_Data = data; - intr->is_Code = (void (*)(void))(APTR)code; + intr->is_Code = (void (*)(void))(APTR)odfs_amiga_interrupt_entry; } void odfs_amiga_set_fib_entry_type(struct FileInfoBlock *fib, LONG type) From 2ee88cac32f22fc8758d684d4cb9ca9f68a86111 Mon Sep 17 00:00:00 2001 From: Stefan Reinauer Date: Thu, 11 Jun 2026 22:24:01 -0700 Subject: [PATCH 12/24] amiga: drop deprecated-declarations suppression for OS4 The OS4 build needed -Wno-error=deprecated-declarations while the shared handler still called AllocMem(), CreateMsgPort(), CreateIORequest() and friends directly. Those calls now go through the per-OS sys_compat wrappers, and the OS4 implementations use AllocVecTags() and AllocSysObjectTags()/FreeSysObject(), so a full rebuild compiles warning-clean without the suppression. Remove it so the build fails again if a deprecated interface member sneaks back in; the SDK marks them with __attribute__((deprecated)), which -Werror turns into a hard error. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 26c5c2e..43f7501 100644 --- a/Makefile +++ b/Makefile @@ -135,7 +135,7 @@ else ifeq ($(AMIGA_TARGET),os4) AMIGA_CRT ?= newlib AMIGA_CPUFLAGS ?= -mcpu=powerpc AMIGA_SYSFLAGS ?= -mcrt=$(AMIGA_CRT) -AMIGA_WARNFLAGS = -Wno-error=deprecated-declarations +AMIGA_WARNFLAGS = AMIGA_DEFS = -DAMIGA -D__USE_INLINE__ -D__USE_BASETYPE__ LDFLAGS = $(AMIGA_SYSFLAGS) # Keep OS4 library/interface ownership explicit in os4/sys_compat.c. From beef6324e13708c4c1a4b3bb2f212fe03e3cfc51 Mon Sep 17 00:00:00 2001 From: Stefan Reinauer Date: Thu, 11 Jun 2026 23:17:16 -0700 Subject: [PATCH 13/24] amiga: allocate DOS list nodes through the sys_compat boundary The handler built its published DeviceNode and the volume DeviceList node by hand: AllocMem of the structure plus a name buffer, then filling every field. On AmigaOS 4 those are compatibility layouts whose real definitions (struct DeviceNode, struct VolumeNode) carry a StructSize field and reserved members that DOS expects the allocator to initialize; the manual path left them zero. Add odfs_amiga_create_dos_entry()/odfs_amiga_delete_dos_entry() to the per-OS compatibility layer. The OS3 implementation keeps the manual allocation, including its deliberate avoidance of MakeDosEntry() so volume names with AmigaDOS metacharacters such as parentheses are not normalized. The OS4 implementation uses AllocDosObject(DOS_DOSLIST, ADO_Type, ADO_Name), which the dos autodoc documents as the supported allocator (MakeDosEntry() itself is a stub over it since V52.16) and which fills dol_StructSize and the V52 fields. AddDosEntry()/RemDosEntry()/AttemptLockDosList() are unchanged; they remain current API on OS4. --- platform/amiga/common/sys_compat.h | 9 ++++++ platform/amiga/handler_main.c | 50 +++--------------------------- platform/amiga/os3/sys_compat.c | 39 +++++++++++++++++++++++ platform/amiga/os4/sys_compat.c | 18 +++++++++++ 4 files changed, 70 insertions(+), 46 deletions(-) diff --git a/platform/amiga/common/sys_compat.h b/platform/amiga/common/sys_compat.h index 7f91114..52d06c8 100644 --- a/platform/amiga/common/sys_compat.h +++ b/platform/amiga/common/sys_compat.h @@ -50,6 +50,15 @@ void odfs_amiga_delete_io_request(struct IORequest *req); LONG odfs_amiga_alloc_signal(LONG num); void odfs_amiga_free_signal(LONG num); +/* + * Allocate a DosList entry (device or volume node) with the given + * dol_Type and a BCPL copy of name, all other fields zeroed. The + * returned node must be released with odfs_amiga_delete_dos_entry() + * after it has been removed from the DOS list. + */ +void *odfs_amiga_create_dos_entry(const char *name, LONG type); +void odfs_amiga_delete_dos_entry(void *node); + void odfs_amiga_init_interrupt(struct Interrupt *intr, const char *name, APTR data, diff --git a/platform/amiga/handler_main.c b/platform/amiga/handler_main.c index b206ed4..76078b4 100644 --- a/platform/amiga/handler_main.c +++ b/platform/amiga/handler_main.c @@ -3377,33 +3377,18 @@ static void sync_device_node(handler_global_t *g, struct DeviceNode *devnode) static struct DeviceNode *create_device_node(handler_global_t *g) { struct DeviceNode *devnode; - UBYTE *namebuf; char name[32]; - int namelen; - size_t alloc_size; device_node_name_from_bstr(g->devnode ? g->devnode->dn_Name : 0, name, sizeof(name)); if (name[0] == '\0') memcpy(name, "ODFS0", 6); - namelen = strlen(name); - if (namelen > 30) - namelen = 30; - - alloc_size = sizeof(*devnode) + 32u; - devnode = odfs_amiga_alloc_mem((ULONG)alloc_size, - MEMF_PUBLIC | MEMF_CLEAR); + devnode = odfs_amiga_create_dos_entry(name, DLT_DEVICE); if (!devnode) return NULL; - namebuf = (UBYTE *)(devnode + 1); - namebuf[0] = (UBYTE)namelen; - memcpy(namebuf + 1, name, (size_t)namelen); - - devnode->dn_Next = 0; odfs_amiga_copy_device_lock(devnode, g->devnode); - devnode->dn_Name = MKBADDR(namebuf); sync_device_node(g, devnode); return devnode; @@ -3411,9 +3396,7 @@ static struct DeviceNode *create_device_node(handler_global_t *g) static void destroy_device_node(struct DeviceNode *devnode) { - if (!devnode) - return; - odfs_amiga_free_mem(devnode, sizeof(*devnode) + 32u); + odfs_amiga_delete_dos_entry(devnode); } static void publish_device_node(handler_global_t *g) @@ -3569,36 +3552,13 @@ static void deactivate_vector_port(handler_global_t *g) static struct DeviceList *create_volume_node(handler_global_t *g) { struct DeviceList *dl; - UBYTE *namebuf; - int namelen; - size_t alloc_size; - - namelen = strlen(g->volname); - if (namelen > 30) - namelen = 30; - /* - * Build the volume DosList entry directly instead of routing the name - * through MakeDosEntry(), which may normalize names containing AmigaDOS - * metacharacters such as parentheses. - */ - alloc_size = sizeof(*dl) + 32u; - dl = odfs_amiga_alloc_mem((ULONG)alloc_size, MEMF_PUBLIC | MEMF_CLEAR); + dl = odfs_amiga_create_dos_entry(g->volname, DLT_VOLUME); if (!dl) return NULL; - namebuf = (UBYTE *)(dl + 1); - namebuf[0] = (UBYTE)namelen; - memcpy(namebuf + 1, g->volname, (size_t)namelen); - - dl->dl_Next = 0; - dl->dl_Type = DLT_VOLUME; dl->dl_Task = g->dosport; - dl->dl_Lock = 0; - dl->dl_LockList = 0; dl->dl_DiskType = ID_DOS_DISK; - dl->dl_unused = 0; - dl->dl_Name = MKBADDR(namebuf); fill_volume_date(g, &dl->dl_VolumeDate); return dl; @@ -3606,9 +3566,7 @@ static struct DeviceList *create_volume_node(handler_global_t *g) static void destroy_volume_node(struct DeviceList *volnode) { - if (!volnode) - return; - odfs_amiga_free_mem(volnode, sizeof(*volnode) + 32u); + odfs_amiga_delete_dos_entry(volnode); } static void detach_volume_node(struct DeviceList *volnode) diff --git a/platform/amiga/os3/sys_compat.c b/platform/amiga/os3/sys_compat.c index c4d7a7d..8216352 100644 --- a/platform/amiga/os3/sys_compat.c +++ b/platform/amiga/os3/sys_compat.c @@ -10,6 +10,8 @@ #include #include +#include + struct ExecBase *SysBase; struct DosLibrary *DOSBase; struct Library *UtilityBase; @@ -115,6 +117,43 @@ void odfs_amiga_free_signal(LONG num) FreeSignal(num); } +void *odfs_amiga_create_dos_entry(const char *name, LONG type) +{ + struct DosList *dl; + UBYTE *namebuf; + size_t namelen; + + if (!name) + return NULL; + + namelen = strlen(name); + if (namelen > 30) + namelen = 30; + + /* + * Build the DosList entry directly instead of routing the name + * through MakeDosEntry(), which may normalize names containing + * AmigaDOS metacharacters such as parentheses. + */ + dl = AllocMem(sizeof(*dl) + 32u, MEMF_PUBLIC | MEMF_CLEAR); + if (!dl) + return NULL; + + namebuf = (UBYTE *)(dl + 1); + namebuf[0] = (UBYTE)namelen; + memcpy(namebuf + 1, name, namelen); + + dl->dol_Type = type; + dl->dol_Name = MKBADDR(namebuf); + return dl; +} + +void odfs_amiga_delete_dos_entry(void *node) +{ + if (node) + FreeMem(node, sizeof(struct DosList) + 32u); +} + void odfs_amiga_init_interrupt(struct Interrupt *intr, const char *name, APTR data, diff --git a/platform/amiga/os4/sys_compat.c b/platform/amiga/os4/sys_compat.c index 28dd4ad..dbe6f9f 100644 --- a/platform/amiga/os4/sys_compat.c +++ b/platform/amiga/os4/sys_compat.c @@ -6,6 +6,7 @@ #include "sys_compat.h" +#include #include #include #include @@ -182,6 +183,23 @@ void odfs_amiga_free_signal(LONG num) FreeSignal(num); } +void *odfs_amiga_create_dos_entry(const char *name, LONG type) +{ + if (!name) + return NULL; + + return AllocDosObjectTags(DOS_DOSLIST, + ADO_Type, (ULONG)type, + ADO_Name, (ULONG)name, + TAG_END); +} + +void odfs_amiga_delete_dos_entry(void *node) +{ + if (node) + FreeDosObject(DOS_DOSLIST, node); +} + void odfs_amiga_init_interrupt(struct Interrupt *intr, const char *name, APTR data, From 3c46446ab487198d05b22ceecbd76b7402dcb3ea Mon Sep 17 00:00:00 2001 From: Stefan Reinauer Date: Thu, 11 Jun 2026 23:32:28 -0700 Subject: [PATCH 14/24] amiga: serialize OS4 vector callbacks and emulate direct packets The OS4 dospackets autodoc states that vector-port functions are invoked from the calling process context, like library calls, and that all access to filesystem state from them must be protected by a semaphore. The vector callbacks previously called the shared handler operations with no locking, racing against each other and against the handler process (packet dispatch and media-change handling) over the lock list, filehandle list, block cache, and the single device IORequest and DMA bounce buffer. Add a SignalSemaphore to the handler globals on OS4. Every vector callback that touches handler state now holds it across the shared operation, and the handler process takes it around media-change handling and shutdown teardown. Pure stub callbacks that only return an error do not take it. Direct legacy packets are now routed through the DOSEmulatePacket() function that AllocDosObject() installs in the vector port, as the autodoc prescribes. The emulator performs the equivalent vector call under the same serialization as native DOS callers, so the handler process no longer mutates filesystem state through a second unsynchronized dispatch path. Packets are validated (non-null, dp_Link == msg) before being trusted, since hand-built packets and private messages can reach the port directly. The shared handle_packet() dispatch remains the OS3/AROS path and the OS4 fallback if the vector port is absent. Shutdown now follows the documented sequence: invalidate the vector port by zeroing FSV.Version so dos.library stops vectoring new callers, tear down DOS-visible state while holding the semaphore so in-flight calls finish first, and reply the shutdown packet only after teardown. This also moves the OS3 ACTION_DIE reply after teardown, which matches common classic handler practice. --- platform/amiga/handler.h | 7 ++ platform/amiga/handler_main.c | 59 ++++++++- platform/amiga/os4/vector_port.c | 198 ++++++++++++++++++++++++------- platform/amiga/os4/vector_port.h | 16 +++ 4 files changed, 231 insertions(+), 49 deletions(-) diff --git a/platform/amiga/handler.h b/platform/amiga/handler.h index 462afee..a4d44bf 100644 --- a/platform/amiga/handler.h +++ b/platform/amiga/handler.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -41,6 +42,12 @@ typedef struct handler_global { #if ODFS_AMIGA_OS4 struct FileSystemVectorPort *vector_port; /* native OS4 vector port */ LONG vector_sigbit; /* signal bit used by vector port */ + /* + * OS4 vector callbacks run in the calling process context, so all + * access to handler state from the vectors and from the handler + * process must hold this semaphore. + */ + struct SignalSemaphore fs_sem; #endif struct DeviceNode *devnode; /* startup packet device node */ struct FileSysStartupMsg *fssm; /* startup packet FSSM */ diff --git a/platform/amiga/handler_main.c b/platform/amiga/handler_main.c index 76078b4..c9cb004 100644 --- a/platform/amiga/handler_main.c +++ b/platform/amiga/handler_main.c @@ -13,6 +13,15 @@ #if ODFS_AMIGA_OS4 #include "vector_port.h" +/* + * OS4 vector callbacks run in the calling process context; the handler + * process must hold the same semaphore while it touches handler state. + */ +#define ODFS_FS_LOCK(g) ObtainSemaphore(&(g)->fs_sem) +#define ODFS_FS_UNLOCK(g) ReleaseSemaphore(&(g)->fs_sem) +#else +#define ODFS_FS_LOCK(g) ((void)0) +#define ODFS_FS_UNLOCK(g) ((void)0) #endif #if ODFS_FEATURE_CDDA @@ -4041,6 +4050,7 @@ void handler_main_startup(struct Message *startup_msg) handler_global_t *g; struct Message *msg; struct DosPacket *pkt; + struct DosPacket *shutdown_pkt = NULL; struct FileSysStartupMsg *fssm; struct DosEnvec *de; ULONG dossig, chgsig, waitmask; @@ -4065,6 +4075,7 @@ void handler_main_startup(struct Message *startup_msg) g->next_volume_id = 1; #if ODFS_AMIGA_OS4 g->vector_sigbit = -1; + InitSemaphore(&g->fs_sem); #endif g->chgsigbit = -1; g->toc_passthrough = -1; @@ -4252,7 +4263,9 @@ void handler_main_startup(struct Message *startup_msg) /* media change */ if ((sigs & chgsig) && !g->inhibited) { + ODFS_FS_LOCK(g); handle_media_change(g); + ODFS_FS_UNLOCK(g); /* re-init media adapter after remount */ amctx.g = g; } @@ -4265,15 +4278,36 @@ void handler_main_startup(struct Message *startup_msg) trace_pkt(g, "dequeue", pkt); #endif +#if ODFS_AMIGA_OS4 + /* hand-built packets and private messages reach this + * port directly; validate before trusting the packet */ + if (!pkt || pkt->dp_Link != msg) { + ReplyMsg(msg); + continue; + } + msg->mn_ReplyPort = pkt->dp_Port; +#endif + if (pkt->dp_Type == ACTION_DIE || pkt->dp_Type == ACTION_SHUTDOWN) { - pkt->dp_Res1 = DOSTRUE; - pkt->dp_Res2 = 0; - return_packet(g, pkt); + shutdown_pkt = pkt; running = 0; break; } +#if ODFS_AMIGA_OS4 + if (g->vector_port) { + /* + * Route direct legacy packets through the DOS packet + * emulator, which performs the equivalent vector-port + * call exactly like a native DOS caller would. + */ + odfs_os4_emulate_packet(g->vector_port, pkt); + return_packet(g, pkt); + continue; + } +#endif + if (!g->mounted && packet_needs_live_mount(pkt)) { pkt->dp_Res1 = DOSFALSE; pkt->dp_Res2 = ERROR_NO_DISK; @@ -4287,11 +4321,28 @@ void handler_main_startup(struct Message *startup_msg) } } - /* ---- shutdown ---- */ + /* + * ---- shutdown ---- + * Invalidate the vector port first so dos.library stops vectoring + * new callers, then tear down DOS-visible state while holding the + * filesystem semaphore so in-flight vector calls finish first. + * The shutdown packet is replied only after the teardown is done. + */ + ODFS_FS_LOCK(g); +#if ODFS_AMIGA_OS4 + odfs_os4_invalidate_vector_port(g->vector_port); +#endif remove_media_change(g); unmount_volume(g); drain_all_objects(g); unpublish_device_node(g); + ODFS_FS_UNLOCK(g); + + if (shutdown_pkt) { + shutdown_pkt->dp_Res1 = DOSTRUE; + shutdown_pkt->dp_Res2 = 0; + return_packet(g, shutdown_pkt); + } shutdown: if (g->devreq) { diff --git a/platform/amiga/os4/vector_port.c b/platform/amiga/os4/vector_port.c index beff972..4eccf8e 100644 --- a/platform/amiga/os4/vector_port.c +++ b/platform/amiga/os4/vector_port.c @@ -41,6 +41,23 @@ static handler_global_t *vp_global(struct FSVP *vp) return vp ? (handler_global_t *)vp->FSV.FSPrivate : NULL; } +/* + * Vector callbacks run in the calling process context, so every + * callback that touches handler state must hold the filesystem + * semaphore around the shared-operation call. + */ +static void fs_lock(handler_global_t *g) +{ + if (g) + ObtainSemaphore(&g->fs_sem); +} + +static void fs_unlock(handler_global_t *g) +{ + if (g) + ReleaseSemaphore(&g->fs_sem); +} + static void set_dos_error(int32 *res2, LONG err) { if (res2) @@ -172,28 +189,36 @@ static struct Lock *vp_lock(struct FSVP *vp, odfs_lock_t *ol = NULL; LONG err; + fs_lock(g); err = odfs_handler_lock_object(g, lock_from_vector(rel_lock), obj ? obj : "", mode, &ol); + fs_unlock(g); set_dos_error(res2, err); return (struct Lock *)LOCK_TO_PTR(ol); } static int32 vp_unlock(struct FSVP *vp, int32 *res2, struct Lock *lock) { - return return_dos_status(res2, - odfs_handler_free_lock_object( - vp_global(vp), lock_from_vector(lock))); + handler_global_t *g = vp_global(vp); + LONG err; + + fs_lock(g); + err = odfs_handler_free_lock_object(g, lock_from_vector(lock)); + fs_unlock(g); + return return_dos_status(res2, err); } static struct Lock *vp_dup_lock(struct FSVP *vp, int32 *res2, struct Lock *lock) { + handler_global_t *g = vp_global(vp); odfs_lock_t *ol = NULL; LONG err; - err = odfs_handler_dup_lock_object(vp_global(vp), - lock_from_vector(lock), &ol); + fs_lock(g); + err = odfs_handler_dup_lock_object(g, lock_from_vector(lock), &ol); + fs_unlock(g); set_dos_error(res2, err); return (struct Lock *)LOCK_TO_PTR(ol); } @@ -213,12 +238,14 @@ static struct Lock *vp_parent_dir(struct FSVP *vp, int32 *res2, struct Lock *dirlock) { + handler_global_t *g = vp_global(vp); odfs_lock_t *parent = NULL; LONG err; - err = odfs_handler_parent_lock_object(vp_global(vp), - lock_from_vector(dirlock), + fs_lock(g); + err = odfs_handler_parent_lock_object(g, lock_from_vector(dirlock), &parent); + fs_unlock(g); set_dos_error(res2, err); return (struct Lock *)LOCK_TO_PTR(parent); } @@ -227,11 +254,13 @@ static struct Lock *vp_dup_lock_from_fh(struct FSVP *vp, int32 *res2, struct FileHandle *filehandle) { + handler_global_t *g = vp_global(vp); odfs_lock_t *ol = NULL; LONG err; - err = odfs_handler_dup_lock_from_fh(vp_global(vp), - fh_from_vector(filehandle), &ol); + fs_lock(g); + err = odfs_handler_dup_lock_from_fh(g, fh_from_vector(filehandle), &ol); + fs_unlock(g); set_dos_error(res2, err); return (struct Lock *)LOCK_TO_PTR(ol); } @@ -241,12 +270,14 @@ static int32 vp_open_from_lock(struct FSVP *vp, struct FileHandle *file, struct Lock *lock) { + handler_global_t *g = vp_global(vp); odfs_fh_t *odfs_fh = NULL; LONG err; - err = odfs_handler_open_from_lock_object(vp_global(vp), - lock_from_vector(lock), + fs_lock(g); + err = odfs_handler_open_from_lock_object(g, lock_from_vector(lock), &odfs_fh); + fs_unlock(g); if (err == 0 && file) file->fh_Arg2 = odfs_fh; return return_dos_status(res2, err); @@ -256,11 +287,13 @@ static struct Lock *vp_parent_of_fh(struct FSVP *vp, int32 *res2, struct FileHandle *file) { + handler_global_t *g = vp_global(vp); odfs_lock_t *parent = NULL; LONG err; - err = odfs_handler_parent_fh_object(vp_global(vp), - fh_from_vector(file), &parent); + fs_lock(g); + err = odfs_handler_parent_fh_object(g, fh_from_vector(file), &parent); + fs_unlock(g); set_dos_error(res2, err); return (struct Lock *)LOCK_TO_PTR(parent); } @@ -272,12 +305,14 @@ static int32 vp_open(struct FSVP *vp, CONST_STRPTR obj, int32 mode) { + handler_global_t *g = vp_global(vp); odfs_fh_t *odfs_fh = NULL; LONG err; - err = odfs_handler_open_object(vp_global(vp), - lock_from_vector(rel_dir), + fs_lock(g); + err = odfs_handler_open_object(g, lock_from_vector(rel_dir), obj ? obj : "", mode, &odfs_fh); + fs_unlock(g); if (err == 0 && fh) fh->fh_Arg2 = odfs_fh; return return_dos_status(res2, err); @@ -285,9 +320,12 @@ static int32 vp_open(struct FSVP *vp, static int32 vp_close(struct FSVP *vp, int32 *res2, struct FileHandle *file) { + handler_global_t *g = vp_global(vp); LONG err; - err = odfs_handler_close_object(vp_global(vp), fh_from_vector(file)); + fs_lock(g); + err = odfs_handler_close_object(g, fh_from_vector(file)); + fs_unlock(g); if (err == 0 && file) file->fh_Arg2 = NULL; return return_dos_status(res2, err); @@ -310,11 +348,14 @@ static int32 vp_read(struct FSVP *vp, STRPTR buffer, int32 numbytes) { + handler_global_t *g = vp_global(vp); LONG actual = 0; LONG err; - err = odfs_handler_read_object(vp_global(vp), fh_from_vector(file), + fs_lock(g); + err = odfs_handler_read_object(g, fh_from_vector(file), buffer, numbytes, &actual); + fs_unlock(g); set_dos_error(res2, err); return err == 0 ? actual : -1; } @@ -345,12 +386,15 @@ static int32 vp_change_file_position(struct FSVP *vp, int32 mode, int64 position) { + handler_global_t *g = vp_global(vp); int64_t oldpos; + LONG err; - return return_dos_status(res2, - odfs_handler_seek_object( - vp_global(vp), fh_from_vector(file), - position, mode, &oldpos)); + fs_lock(g); + err = odfs_handler_seek_object(g, fh_from_vector(file), + position, mode, &oldpos); + fs_unlock(g); + return return_dos_status(res2, err); } static int32 vp_change_file_size(struct FSVP *vp, @@ -370,11 +414,13 @@ static int64 vp_get_file_position(struct FSVP *vp, int32 *res2, struct FileHandle *file) { + handler_global_t *g = vp_global(vp); int64_t pos; LONG err; - err = odfs_handler_get_file_position(vp_global(vp), - fh_from_vector(file), &pos); + fs_lock(g); + err = odfs_handler_get_file_position(g, fh_from_vector(file), &pos); + fs_unlock(g); set_dos_error(res2, err); return err == 0 ? pos : -1; } @@ -383,11 +429,13 @@ static int64 vp_get_file_size(struct FSVP *vp, int32 *res2, struct FileHandle *file) { + handler_global_t *g = vp_global(vp); int64_t size; LONG err; - err = odfs_handler_get_file_size(vp_global(vp), - fh_from_vector(file), &size); + fs_lock(g); + err = odfs_handler_get_file_size(g, fh_from_vector(file), &size); + fs_unlock(g); set_dos_error(res2, err); return err == 0 ? size : -1; } @@ -397,10 +445,14 @@ static int32 vp_change_lock_mode(struct FSVP *vp, struct Lock *lock, int32 new_lock_mode) { - return return_dos_status(res2, - odfs_handler_change_lock_mode( - vp_global(vp), lock_from_vector(lock), - new_lock_mode)); + handler_global_t *g = vp_global(vp); + LONG err; + + fs_lock(g); + err = odfs_handler_change_lock_mode(g, lock_from_vector(lock), + new_lock_mode); + fs_unlock(g); + return return_dos_status(res2, err); } static int32 vp_change_file_mode(struct FSVP *vp, @@ -408,10 +460,14 @@ static int32 vp_change_file_mode(struct FSVP *vp, struct FileHandle *fh, int32 new_lock_mode) { - return return_dos_status(res2, - odfs_handler_change_file_mode( - vp_global(vp), fh_from_vector(fh), - new_lock_mode)); + handler_global_t *g = vp_global(vp); + LONG err; + + fs_lock(g); + err = odfs_handler_change_file_mode(g, fh_from_vector(fh), + new_lock_mode); + fs_unlock(g); + return return_dos_status(res2, err); } static int32 vp_set_date(struct FSVP *vp, @@ -540,13 +596,16 @@ static int32 vp_same_lock(struct FSVP *vp, struct Lock *lock1, struct Lock *lock2) { + handler_global_t *g = vp_global(vp); LONG same = LOCK_DIFFERENT; LONG err; - err = odfs_handler_same_lock_object(vp_global(vp), + fs_lock(g); + err = odfs_handler_same_lock_object(g, lock_from_vector(lock1), lock_from_vector(lock2), &same); + fs_unlock(g); set_dos_error(res2, err); return err == 0 ? same : LOCK_DIFFERENT; } @@ -556,13 +615,16 @@ static int32 vp_same_file(struct FSVP *vp, struct FileHandle *fh1, struct FileHandle *fh2) { + handler_global_t *g = vp_global(vp); LONG same = LOCK_DIFFERENT; LONG err; - err = odfs_handler_same_file_object(vp_global(vp), + fs_lock(g); + err = odfs_handler_same_file_object(g, fh_from_vector(fh1), fh_from_vector(fh2), &same); + fs_unlock(g); set_dos_error(res2, err); return err == 0 ? same : LOCK_DIFFERENT; } @@ -580,18 +642,26 @@ static int32 vp_volume_info_data(struct FSVP *vp, int32 *res2, struct InfoData *info) { - return return_dos_status(res2, - odfs_handler_fill_info(vp_global(vp), NULL, - info)); + handler_global_t *g = vp_global(vp); + LONG err; + + fs_lock(g); + err = odfs_handler_fill_info(g, NULL, info); + fs_unlock(g); + return return_dos_status(res2, err); } static int32 vp_device_info_data(struct FSVP *vp, int32 *res2, struct InfoData *info) { - return return_dos_status(res2, - odfs_handler_fill_info(vp_global(vp), NULL, - info)); + handler_global_t *g = vp_global(vp); + LONG err; + + fs_lock(g); + err = odfs_handler_fill_info(g, NULL, info); + fs_unlock(g); + return return_dos_status(res2, err); } static struct ExamineData *vp_examine_obj(struct FSVP *vp, @@ -604,15 +674,18 @@ static struct ExamineData *vp_examine_obj(struct FSVP *vp, struct ExamineData *ed; LONG err; + fs_lock(g); err = odfs_handler_lock_object(g, lock_from_vector(lock), object ? object : "", SHARED_LOCK, &ol); if (err != 0) { + fs_unlock(g); set_dos_error(res2, err); return NULL; } ed = alloc_examine_data(g, ol->entry ? &ol->entry->fnode : NULL); (void)odfs_handler_free_lock_object(g, ol); + fs_unlock(g); set_dos_error(res2, ed ? 0 : ERROR_NO_FREE_STORE); return ed; } @@ -626,13 +699,16 @@ static struct ExamineData *vp_examine_lock(struct FSVP *vp, struct ExamineData *ed; LONG err; + fs_lock(g); err = odfs_handler_get_lock_node(g, lock_from_vector(lock), &node); if (err != 0) { + fs_unlock(g); set_dos_error(res2, err); return NULL; } ed = alloc_examine_data(g, node); + fs_unlock(g); set_dos_error(res2, ed ? 0 : ERROR_NO_FREE_STORE); return ed; } @@ -646,13 +722,16 @@ static struct ExamineData *vp_examine_file(struct FSVP *vp, struct ExamineData *ed; LONG err; + fs_lock(g); err = odfs_handler_get_fh_node(g, fh_from_vector(file), &node); if (err != 0) { + fs_unlock(g); set_dos_error(res2, err); return NULL; } ed = alloc_examine_data(g, node); + fs_unlock(g); set_dos_error(res2, ed ? 0 : ERROR_NO_FREE_STORE); return ed; } @@ -670,12 +749,16 @@ static int32 vp_examine_dir(struct FSVP *vp, if (!ctx) return return_dos_status(res2, ERROR_REQUIRED_ARG_MISSING); + fs_lock(g); err = odfs_handler_next_dir_entry(g, lock_from_vector(ctx->ReferenceLock), ctx->FSPrivate[0], &entry, &key); - if (err != 0) + if (err != 0) { + fs_unlock(g); return return_dos_status(res2, err); + } ed = alloc_examine_data_from_context(g, &entry, ctx); + fs_unlock(g); if (!ed) return return_dos_status(res2, ERROR_NO_FREE_STORE); @@ -686,9 +769,13 @@ static int32 vp_examine_dir(struct FSVP *vp, static int32 vp_inhibit(struct FSVP *vp, int32 *res2, int32 inhibit_state) { - return return_dos_status(res2, - odfs_handler_inhibit(vp_global(vp), - inhibit_state)); + handler_global_t *g = vp_global(vp); + LONG err; + + fs_lock(g); + err = odfs_handler_inhibit(g, inhibit_state); + fs_unlock(g); + return return_dos_status(res2, err); } static int32 vp_write_protect(struct FSVP *vp, @@ -864,3 +951,24 @@ void odfs_os4_free_vector_port(struct FileSystemVectorPort *vp) if (vp) FreeDosObject(DOS_FSVECTORPORT, vp); } + +void odfs_os4_emulate_packet(struct FileSystemVectorPort *vp, + struct DosPacket *pkt) +{ + if (!pkt) + return; + + if (vp && vp->FSV.DOSEmulatePacket) { + vp->FSV.DOSEmulatePacket(vp, pkt); + return; + } + + pkt->dp_Res1 = DOSFALSE; + pkt->dp_Res2 = ERROR_ACTION_NOT_KNOWN; +} + +void odfs_os4_invalidate_vector_port(struct FileSystemVectorPort *vp) +{ + if (vp) + vp->FSV.Version = 0; +} diff --git a/platform/amiga/os4/vector_port.h b/platform/amiga/os4/vector_port.h index 2b92cb8..33fb66f 100644 --- a/platform/amiga/os4/vector_port.h +++ b/platform/amiga/os4/vector_port.h @@ -10,8 +10,24 @@ #include #include +struct DosPacket; + const struct FileSystemVectors *odfs_os4_vector_template(void); struct FileSystemVectorPort *odfs_os4_alloc_vector_port(APTR fs_private); void odfs_os4_free_vector_port(struct FileSystemVectorPort *vp); +/* + * Route a direct legacy DosPacket through the DOS packet emulator that + * AllocDosObject() installed in the vector port. Results are placed in + * the packet; the caller still replies it. + */ +void odfs_os4_emulate_packet(struct FileSystemVectorPort *vp, + struct DosPacket *pkt); + +/* + * Stop dos.library from vectoring new callers (sets the vector version + * to zero). Must be called before DOS-visible shutdown teardown. + */ +void odfs_os4_invalidate_vector_port(struct FileSystemVectorPort *vp); + #endif /* ODFS_AMIGA_OS4_VECTOR_PORT_H */ From bda9871aff96185b5e5bb15cf7aa82326489d3da Mon Sep 17 00:00:00 2001 From: Stefan Reinauer Date: Sat, 13 Jun 2026 16:34:30 -0700 Subject: [PATCH 15/24] amiga: add freestanding OS4 handler startup The OS4 filesystem handler must receive ACTION_STARTUP as its first process message. Newlib startup consumes that message while probing for a Workbench launch, which can deadlock the boot mount. Link the handler without C runtime startup, provide a small _start entry that initializes the Exec interface, and add the Kickstart Resident plus FileSysEntry registration used when the module loads from Kickstart.zip. Provide the small libc surface the freestanding link still needs and translate legacy allocation flags before passing them to AllocVecTags(). --- Makefile | 19 +++- platform/amiga/os4/freestanding.c | 100 +++++++++++++++++ platform/amiga/os4/main.c | 17 ++- platform/amiga/os4/start.c | 173 ++++++++++++++++++++++++++++++ platform/amiga/os4/sys_compat.c | 26 ++++- 5 files changed, 328 insertions(+), 7 deletions(-) create mode 100644 platform/amiga/os4/freestanding.c create mode 100644 platform/amiga/os4/start.c diff --git a/Makefile b/Makefile index 43f7501..a0d9954 100644 --- a/Makefile +++ b/Makefile @@ -134,15 +134,22 @@ HANDLER_LIBS = -nostdlib -Wl,-u,_exit -lgcc -lc -lgcc -lamiga -ramiga-dev else ifeq ($(AMIGA_TARGET),os4) AMIGA_CRT ?= newlib AMIGA_CPUFLAGS ?= -mcpu=powerpc -AMIGA_SYSFLAGS ?= -mcrt=$(AMIGA_CRT) +# No unwind tables: the freestanding handler has no exception support, +# and the kickstart loader expects plain PT_LOAD program headers only. +AMIGA_SYSFLAGS ?= -mcrt=$(AMIGA_CRT) -fno-asynchronous-unwind-tables AMIGA_WARNFLAGS = AMIGA_DEFS = -DAMIGA -D__USE_INLINE__ -D__USE_BASETYPE__ LDFLAGS = $(AMIGA_SYSFLAGS) # Keep OS4 library/interface ownership explicit in os4/sys_compat.c. # Do not add -lauto to the handler link. LIBS = -lc -lgcc -HANDLER_LDFLAGS = -HANDLER_LIBS = $(LIBS) +# The handler must not run the newlib C runtime startup: it consumes +# the first process message, which is the handler's ACTION_STARTUP +# packet. os4/start.c provides the freestanding entry instead. +# -static keeps gcc from passing --eh-frame-hdr, so the binary carries +# only the plain PT_LOAD program headers the kickstart loader expects. +HANDLER_LDFLAGS = -nostartfiles -static -Wl,-u,_start +HANDLER_LIBS = -nostdlib -lgcc else AMIGA_CPUFLAGS ?= -m68000 -mtune=68020-60 -msoft-float AMIGA_SYSFLAGS ?= -noixemul @@ -200,11 +207,17 @@ AMIGA_SRCS = platform/amiga/handler_main.c \ platform/amiga/$(AMIGA_OSDIR)/sys_compat.c ifeq ($(AMIGA_TARGET),os4) AMIGA_SRCS += platform/amiga/os4/main.c \ + platform/amiga/os4/start.c \ + platform/amiga/os4/freestanding.c \ platform/amiga/os4/vector_port.c else AMIGA_SRCS += platform/amiga/libc_stubs.c endif +# Freestanding libc replacements: stop the compiler from recognizing +# the copy loops and emitting calls to the functions being defined. +$(AMIGA_BUILD)/platform/amiga/os4/freestanding.o: CFLAGS += -fno-builtin + # Amiga assembly ifeq ($(AMIGA_TARGET),os4) AMIGA_ASM_SRCS = diff --git a/platform/amiga/os4/freestanding.c b/platform/amiga/os4/freestanding.c new file mode 100644 index 0000000..317992e --- /dev/null +++ b/platform/amiga/os4/freestanding.c @@ -0,0 +1,100 @@ +/* + * freestanding.c - minimal libc for the AmigaOS 4 handler + * + * SPDX-License-Identifier: BSD-2-Clause + * + * The OS4 newlib static libc.a contains only stubs that call through + * the INewlib interface, which is set up by the C runtime startup the + * handler cannot use (see start.c). Provide the handful of string + * functions the handler and core library reference. + * + * Compiled with -fno-builtin so the compiler cannot turn these loops + * back into calls to themselves. + */ + +#include +#include + +void *memcpy(void *dst, const void *src, size_t n) +{ + unsigned char *d = dst; + const unsigned char *s = src; + + if (((size_t)d & 3u) == 0 && ((size_t)s & 3u) == 0) { + while (n >= 4) { + *(unsigned long *)d = *(const unsigned long *)s; + d += 4; + s += 4; + n -= 4; + } + } + while (n--) + *d++ = *s++; + return dst; +} + +void *memset(void *dst, int c, size_t n) +{ + unsigned char *d = dst; + + while (n--) + *d++ = (unsigned char)c; + return dst; +} + +int memcmp(const void *a, const void *b, size_t n) +{ + const unsigned char *pa = a; + const unsigned char *pb = b; + + while (n--) { + if (*pa != *pb) + return *pa - *pb; + pa++; + pb++; + } + return 0; +} + +size_t strlen(const char *s) +{ + const char *p = s; + + while (*p) + p++; + return (size_t)(p - s); +} + +int strcmp(const char *a, const char *b) +{ + while (*a && *a == *b) { + a++; + b++; + } + return (unsigned char)*a - (unsigned char)*b; +} + +char *strchr(const char *s, int c) +{ + char ch = (char)c; + + for (;; s++) { + if (*s == ch) + return (char *)s; + if (*s == '\0') + return NULL; + } +} + +char *strncpy(char *dst, const char *src, size_t n) +{ + char *d = dst; + + while (n && *src) { + *d++ = *src++; + n--; + } + while (n--) + *d++ = '\0'; + return dst; +} diff --git a/platform/amiga/os4/main.c b/platform/amiga/os4/main.c index d87114b..2d33415 100644 --- a/platform/amiga/os4/main.c +++ b/platform/amiga/os4/main.c @@ -32,7 +32,9 @@ static void return_startup_packet(struct DosPacket *pkt, PutMsg(replyport, msg); } -int main(void) +int odfs_os4_handler_main(void); + +int odfs_os4_handler_main(void) { struct Process *proc; struct Message *msg; @@ -42,8 +44,16 @@ int main(void) if (!proc) return RETURN_FAIL; +#if ODFS_SERIAL_DEBUG + DebugPrintF("[ODFS] handler_main: proc=%p port=%p waiting...\n", + proc, &proc->pr_MsgPort); +#endif WaitPort(&proc->pr_MsgPort); msg = GetMsg(&proc->pr_MsgPort); +#if ODFS_SERIAL_DEBUG + DebugPrintF("[ODFS] handler_main: msg=%p name=%p\n", + msg, msg ? msg->mn_Node.ln_Name : NULL); +#endif if (!msg) { proc->pr_Result2 = ERROR_OBJECT_WRONG_TYPE; return RETURN_FAIL; @@ -56,6 +66,11 @@ int main(void) } pkt = (struct DosPacket *)msg->mn_Node.ln_Name; +#if ODFS_SERIAL_DEBUG + DebugPrintF("[ODFS] handler_main: pkt=%p type=%ld arg1=%lx arg2=%lx " + "arg3=%lx\n", pkt, pkt->dp_Type, pkt->dp_Arg1, + pkt->dp_Arg2, pkt->dp_Arg3); +#endif if (pkt->dp_Type != ACTION_STARTUP) { return_startup_packet(pkt, DOSFALSE, ERROR_ACTION_NOT_KNOWN); proc->pr_Result2 = ERROR_ACTION_NOT_KNOWN; diff --git a/platform/amiga/os4/start.c b/platform/amiga/os4/start.c new file mode 100644 index 0000000..24257e0 --- /dev/null +++ b/platform/amiga/os4/start.c @@ -0,0 +1,173 @@ +/* + * start.c - freestanding AmigaOS 4 handler entry + * + * SPDX-License-Identifier: BSD-2-Clause + * + * A filesystem handler process receives an ACTION_STARTUP DosPacket as + * its first process message. The newlib C runtime consumes the first + * process message while detecting a Workbench start, which deadlocks + * the mount, so the handler must not run any C runtime startup. + * + * The module supports two start paths: + * + * - As a normal disk-based handler, DOS enters the seglist at _start + * with the argument string in r3, its length in r4, and the ExecBase + * pointer in r5. + * + * - As a kickstart module, exec calls the Resident's rt_Init at + * coldstart (with ExecBase as the third argument); the init + * registers a FileSysEntry for DosType 'CD01' in FileSystem.resource + * whose fse_SegList is a fake seglist containing a 68k NOP+JMP gate + * to _start. The 68k gate does not carry the native register + * convention, so rt_Init stores ExecBase for _start to pick up. + * This mirrors the structure of the original CDFileSystem 53.4 + * kickstart module. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "handler.h" +#include "sys_compat.h" + +/* Interface globals normally provided by the C runtime startup. */ +struct ExecIFace *IExec; +struct DOSIFace *IDOS; + +int odfs_os4_handler_main(void); + +int32 _start(STRPTR args, int32 arglen, struct ExecBase *sysbase); + +int32 _start(STRPTR args, int32 arglen, struct ExecBase *sysbase) +{ + (void)args; + (void)arglen; + (void)sysbase; + + /* + * The handler process is entered through the kickstart 68k seglist + * gate (NOP + JMP), so register arguments are undefined here. Use + * the ExecBase rt_Init stored; fall back to the legacy pointer at + * absolute address 4 exactly like the original CDFileSystem module + * (only relevant if the entry ever runs without the resident init). + */ + if (!SysBase) + SysBase = *((struct ExecBase **)4L); + IExec = (struct ExecIFace *)SysBase->MainInterface; + +#if ODFS_SERIAL_DEBUG + DebugPrintF("[ODFS] _start: SysBase=%p IExec=%p\n", SysBase, IExec); +#endif + return odfs_os4_handler_main(); +} + +/* + * Fake seglist for the FileSysEntry. The BPTR points at .next; the + * longword before it conventionally holds the segment size. The + * "code" at BADDR+4 is a 68k NOP and JMP to the native _start; the + * 68k emulator switches to native execution at the jump target. + */ +static const struct { + uint32 size; + uint32 next; + uint16 nop; /* m68k NOP */ + uint16 jmp; /* m68k JMP absolute.l */ + int32 (*target)(STRPTR, int32, struct ExecBase *); + uint32 endmark; +} odfs_ks_seg = { + 0x40, + 0, + 0x4e71, + 0x4ef9, + _start, + 0xffffffff +}; + +static const char odfs_resident_name[] = "CDFileSystem"; +static const char odfs_resident_id[] = + "CDFileSystem 53.4 (ODFileSystem " ODFS_GIT_VERSION ")"; + +static APTR odfs_ks_init(APTR dummy1, APTR dummy2, struct ExecBase *sysbase) +{ + struct FileSysResource *fsr; + struct FileSysEntry *fse; + + (void)dummy1; + (void)dummy2; + + /* exec passes ExecBase as the third argument, like the original. */ + SysBase = sysbase; + IExec = (struct ExecIFace *)SysBase->MainInterface; + +#if ODFS_SERIAL_DEBUG + DebugPrintF("[ODFS] ks_init: SysBase=%p\n", SysBase); +#endif + + fsr = OpenResource((CONST_STRPTR)FSRNAME); +#if ODFS_SERIAL_DEBUG + DebugPrintF("[ODFS] ks_init: fsr=%p\n", fsr); +#endif + if (!fsr) + return NULL; + + /* + * Mirror the original module's plain AllocMem: AllocVecTags is not + * a safe assumption this early in coldstart, and the entry is + * never freed. + */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + fse = AllocMem(sizeof(*fse), MEMF_PUBLIC | MEMF_CLEAR); +#pragma GCC diagnostic pop +#if ODFS_SERIAL_DEBUG + DebugPrintF("[ODFS] ks_init: fse=%p\n", fse); +#endif + if (!fse) + return NULL; + + fse->fse_Node.ln_Name = (char *)odfs_resident_id; + fse->fse_DosType = 0x43443031; /* CD01 */ + fse->fse_Version = (54UL << 16); + fse->fse_PatchFlags = FSEF_SEGLIST | FSEF_GLOBVEC | FSEF_STACKSIZE; + fse->fse_StackSize = 16384; + fse->fse_Priority = 10; + fse->fse_SegList = MKBADDR(&odfs_ks_seg.next); + fse->fse_GlobalVec = -1; + + AddHead(&fsr->fsr_FileSysEntries, &fse->fse_Node); +#if ODFS_SERIAL_DEBUG + DebugPrintF("[ODFS] ks_init: CD01 entry registered, seg=%p\n", + (APTR)fse->fse_SegList); +#endif + return fse; +} + +/* + * Kickstart filesystem modules announce themselves with a Resident + * structure; exec runs rt_Init at coldstart, which registers the + * filesystem. Field values mirror the original CDFileSystem 53.4 + * resident (RTF_NATIVE | RTF_COLDSTART, priority 79). + */ +extern const struct Resident odfs_os4_resident; + +const struct Resident odfs_os4_resident = { + RTC_MATCHWORD, + (struct Resident *)&odfs_os4_resident, + (APTR)(&odfs_os4_resident + 1), + RTF_NATIVE | RTF_COLDSTART, + 53, + NT_UNKNOWN, + 79, + (char *)odfs_resident_name, + (char *)odfs_resident_id, + (APTR)odfs_ks_init +}; diff --git a/platform/amiga/os4/sys_compat.c b/platform/amiga/os4/sys_compat.c index dbe6f9f..245fe46 100644 --- a/platform/amiga/os4/sys_compat.c +++ b/platform/amiga/os4/sys_compat.c @@ -44,7 +44,14 @@ static void odfs_amiga_interrupt_entry(int32 unused, void odfs_amiga_init_sysbase(void) { - SysBase = *((struct ExecBase **)4L); + /* + * _start establishes SysBase before any other handler code runs. + * As a fallback (e.g. if the entry path ever changes), recover it + * from the classic ExecBase pointer at absolute address 4, which + * the kickstart environment maintains. + */ + if (!SysBase) + SysBase = *((struct ExecBase **)4L); } struct ExecBase *odfs_amiga_sysbase(void) @@ -112,17 +119,30 @@ void odfs_amiga_close_libraries(void) void *odfs_amiga_alloc_vec(ULONG size, ULONG flags) { + uint32 type; + if (size == 0) size = 1; + /* + * AVT_Type only accepts MEMF_PRIVATE, MEMF_SHARED, and + * MEMF_EXECUTABLE. Translate the legacy flags the shared handler + * uses (MEMF_PUBLIC, de_BufMemType bits): handler memory is shared + * with DOS and other processes, so legacy MEMF_PUBLIC maps to + * MEMF_SHARED. + */ + type = (flags & MEMF_PRIVATE) ? MEMF_PRIVATE : MEMF_SHARED; + if (flags & MEMF_EXECUTABLE) + type |= MEMF_EXECUTABLE; + if (flags & MEMF_CLEAR) { return AllocVecTags(size, - AVT_Type, flags & ~MEMF_CLEAR, + AVT_Type, type, AVT_ClearWithValue, 0, TAG_END); } - return AllocVecTags(size, AVT_Type, flags, TAG_END); + return AllocVecTags(size, AVT_Type, type, TAG_END); } void odfs_amiga_free_vec(void *ptr) From b62ca8c12e8130e07082478eba72e09804041c88 Mon Sep 17 00:00:00 2001 From: Stefan Reinauer Date: Sat, 13 Jun 2026 16:39:19 -0700 Subject: [PATCH 16/24] amiga: keep media state per handler Diskboot starts one filesystem handler process per CD unit. The media callback context was static, so a later process could replace the device binding used by an earlier mounted volume. Move the media callback context into handler_global and pass that per-process instance to the core media layer. Reinitialize the same context after media-change remounts. --- platform/amiga/handler.h | 13 +++++++++++++ platform/amiga/handler_main.c | 12 +++++------- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/platform/amiga/handler.h b/platform/amiga/handler.h index a4d44bf..0a4aeba 100644 --- a/platform/amiga/handler.h +++ b/platform/amiga/handler.h @@ -34,6 +34,18 @@ struct odfs_changeint_data { ULONG sigmask; }; +/* + * Media-adapter context passed to the odfs_media_ops callbacks. It must + * be per-handler-process: diskboot starts one handler process per CD + * unit, so a shared instance would let a later process clobber the + * device binding of an earlier one. It lives inside handler_global so + * each process owns its own copy. + */ +struct handler_global; +typedef struct amiga_media_ctx { + struct handler_global *g; +} amiga_media_ctx_t; + /* ---- handler globals ---- */ typedef struct handler_global { @@ -49,6 +61,7 @@ typedef struct handler_global { */ struct SignalSemaphore fs_sem; #endif + amiga_media_ctx_t media_ctx; /* per-process media-adapter ctx */ struct DeviceNode *devnode; /* startup packet device node */ struct FileSysStartupMsg *fssm; /* startup packet FSSM */ struct DeviceNode *published_devnode; /* DOS device-list entry */ diff --git a/platform/amiga/handler_main.c b/platform/amiga/handler_main.c index c9cb004..e38a881 100644 --- a/platform/amiga/handler_main.c +++ b/platform/amiga/handler_main.c @@ -107,9 +107,8 @@ static int scsi_is_unsupported_command(const uint8_t *sense); * (x86 AROS) targets. */ -typedef struct amiga_media_ctx { - handler_global_t *g; -} amiga_media_ctx_t; +/* amiga_media_ctx_t is defined in handler.h and embedded per-process + * in handler_global so concurrent handler processes never share it. */ static int scsi_is_unsupported_command(const uint8_t *sense) { @@ -4055,7 +4054,6 @@ void handler_main_startup(struct Message *startup_msg) struct DosEnvec *de; ULONG dossig, chgsig, waitmask; int running = 1; - static amiga_media_ctx_t amctx; (void)version_string; /* ensure $VER is not optimized out */ @@ -4222,9 +4220,9 @@ void handler_main_startup(struct Message *startup_msg) } /* set up media adapter */ - amctx.g = g; + g->media_ctx.g = g; g->media.ops = &amiga_media_ops; - g->media.ctx = &amctx; + g->media.ctx = &g->media_ctx; #if ODFS_AMIGA_OS4 { @@ -4267,7 +4265,7 @@ void handler_main_startup(struct Message *startup_msg) handle_media_change(g); ODFS_FS_UNLOCK(g); /* re-init media adapter after remount */ - amctx.g = g; + g->media_ctx.g = g; } /* DOS packets */ From b80904dcd5a1315f2b4a2908255162a4a0ad1950 Mon Sep 17 00:00:00 2001 From: Stefan Reinauer Date: Sat, 13 Jun 2026 16:43:40 -0700 Subject: [PATCH 17/24] amiga: probe OS4 CD units before mounting Some OS4 device paths report phantom ATAPI units that open but hang when probed through SCSI commands. Mounting those units publishes a dead volume that DOS can keep polling. Answer allocation failures on the startup packet, remove the TEST UNIT READY probe, and use TD_GETGEOMETRY before publishing the volume. Only issue MODE SELECT after geometry proves that a usable drive is present. --- platform/amiga/handler_main.c | 119 +++++++++++++++++++--------------- 1 file changed, 68 insertions(+), 51 deletions(-) diff --git a/platform/amiga/handler_main.c b/platform/amiga/handler_main.c index e38a881..1195bf4 100644 --- a/platform/amiga/handler_main.c +++ b/platform/amiga/handler_main.c @@ -752,44 +752,6 @@ static odfs_err_t amiga_read_cdtext(void *ctx, uint8_t **buf_out, /* SCSI helper commands */ /* ------------------------------------------------------------------ */ -/* - * Issue SCSI Test Unit Ready (0x00). - * Returns 1 if drive is ready, 0 otherwise. - */ -static int scsi_test_unit_ready(handler_global_t *g) -{ - uint8_t cmd[6]; - struct SCSICmd scsi; - LONG io_rc; - - memset(cmd, 0, sizeof(cmd)); - memset(&scsi, 0, sizeof(scsi)); - - cmd[0] = 0x00; /* TEST UNIT READY */ - - scsi.scsi_Data = NULL; - scsi.scsi_Length = 0; - scsi.scsi_CmdLength = 6; - scsi.scsi_Command = cmd; - scsi.scsi_Flags = SCSIF_AUTOSENSE; - - g->devreq->io_Command = HD_SCSICMD; - g->devreq->io_Data = &scsi; - g->devreq->io_Length = sizeof(scsi); - - io_rc = DoIO((struct IORequest *)g->devreq); - if (io_rc != 0 || g->devreq->io_Error != 0 || scsi.scsi_Status != 0) { - ODFS_WARN(&g->log, ODFS_SUB_IO, - "TEST UNIT READY failed io_rc=%ld io_Error=%ld " - "scsi_Status=%lu", - (long)io_rc, (long)g->devreq->io_Error, - (unsigned long)scsi.scsi_Status); - return 0; - } - - return 1; -} - /* * Issue SCSI Mode Select (0x15) to set the block size. * @@ -4060,8 +4022,24 @@ void handler_main_startup(struct Message *startup_msg) odfs_amiga_init_sysbase(); g = odfs_amiga_alloc_mem(sizeof(*g), MEMF_PUBLIC | MEMF_CLEAR); - if (!g) + if (!g) { + /* + * Never exit without answering the startup packet: DOS blocks + * the mounting context (during boot, the whole boot) until the + * packet is replied. + */ + if (startup_msg && startup_msg->mn_Node.ln_Name) { + pkt = (struct DosPacket *)startup_msg->mn_Node.ln_Name; + if (pkt->dp_Port) { + pkt->dp_Res1 = DOSFALSE; + pkt->dp_Res2 = ERROR_NO_FREE_STORE; + startup_msg->mn_Node.ln_Succ = NULL; + startup_msg->mn_Node.ln_Pred = NULL; + PutMsg(pkt->dp_Port, startup_msg); + } + } return; + } g->sysbase = odfs_amiga_sysbase(); g->locklist.mlh_Head = (struct MinNode *)&g->locklist.mlh_Tail; @@ -4136,6 +4114,8 @@ void handler_main_startup(struct Message *startup_msg) return; } g->dosbase = odfs_amiga_dosbase(); + ODFS_INFO(&g->log, ODFS_SUB_CORE, "libraries open, device=%s unit=%lu", + g->devname, (unsigned long)g->devunit); /* open device */ g->devport = odfs_amiga_create_msg_port(); @@ -4161,6 +4141,8 @@ void handler_main_startup(struct Message *startup_msg) goto shutdown; } + ODFS_INFO(&g->log, ODFS_SUB_IO, "opening %s unit %lu", + g->devname, (unsigned long)g->devunit); if (OpenDevice((CONST_STRPTR)g->devname, g->devunit, (struct IORequest *)g->devreq, g->devflags) != 0) { ODFS_ERROR(&g->log, ODFS_SUB_IO, @@ -4173,21 +4155,11 @@ void handler_main_startup(struct Message *startup_msg) return_packet(g, pkt); goto shutdown; } + ODFS_INFO(&g->log, ODFS_SUB_IO, "device open"); g->devnode->dn_Startup = MKBADDR(fssm); g->devnode->dn_Task = g->dosport; - /* - * SCSI drive setup: wait for unit ready and set 2048-byte blocks. - * Mode Select may fail on non-SCSI devices (e.g. IDE with - * trackdisk.device) — this is non-fatal. - */ - scsi_test_unit_ready(g); - if (!scsi_mode_select(g, 2048)) { - /* Mode Select failed — drive probably doesn't support it - * or is already in 2048-byte mode. Not fatal. */ - } - /* * Allocate DMA-safe bounce buffer using de_BufMemType. * 16-byte aligned for 68040 DMA performance (CDVDFS pattern). @@ -4219,7 +4191,52 @@ void handler_main_startup(struct Message *startup_msg) } } - /* set up media adapter */ + /* + * Probe the drive geometry before committing to the mount. + * TD_GETGEOMETRY uses the native ATA path and returns promptly even + * on a not-ready unit (unlike HD_SCSICMD, which can hang). A failure + * here means the unit has no usable device behind it — e.g. the + * empty/phantom second ATAPI channel that QEMU's peg2ide reports + * from a floating bus. Decline the mount in that case rather than + * publishing a dead drive that DOS would route to and poll. + * + * When geometry succeeds but reports a non-2048 block size, switch + * the drive to 2048-byte CD blocks with MODE SELECT (an HD_SCSICMD, + * issued only for a confirmed present drive so it cannot hang on a + * phantom unit). + */ + { + struct DriveGeometry geom; + LONG geo_rc; + + memset(&geom, 0, sizeof(geom)); + g->devreq->io_Command = TD_GETGEOMETRY; + g->devreq->io_Data = &geom; + g->devreq->io_Length = sizeof(geom); + geo_rc = DoIO((struct IORequest *)g->devreq); + ODFS_INFO(&g->log, ODFS_SUB_IO, + "geometry rc=%ld sector=%lu", (long)geo_rc, + (unsigned long)geom.dg_SectorSize); + + if (geo_rc != 0) { + ODFS_WARN(&g->log, ODFS_SUB_IO, + "no usable device on unit %lu (geometry rc=%ld) - " + "declining mount", + (unsigned long)g->devunit, (long)geo_rc); + pkt->dp_Res1 = DOSFALSE; + pkt->dp_Res2 = ERROR_DEVICE_NOT_MOUNTED; + return_packet(g, pkt); + goto shutdown; + } + + if (geom.dg_SectorSize != 0 && geom.dg_SectorSize != 2048) { + ODFS_INFO(&g->log, ODFS_SUB_IO, "mode select..."); + (void)scsi_mode_select(g, 2048); + } + } + ODFS_INFO(&g->log, ODFS_SUB_IO, "scsi setup done"); + + /* set up media adapter (context lives in g, one per process) */ g->media_ctx.g = g; g->media.ops = &amiga_media_ops; g->media.ctx = &g->media_ctx; From da956aa9c53f125af0010f67adca9b6dc467c254 Mon Sep 17 00:00:00 2001 From: Stefan Reinauer Date: Sat, 13 Jun 2026 16:44:26 -0700 Subject: [PATCH 18/24] amiga: allocate OS4 locks through DOS Workbench and native OS4 filesystem vectors expect lock objects with the OS4 DOS lock layout. The classic handler embedded a FileLock in its private lock wrapper, which leaves OS4-only fields uninitialized. Store a DOS-allocated struct Lock in each ODFS lock on OS4, keep the classic embedded FileLock on OS3, and convert through fl_FSPrivate1. Free the DOS lock when the ODFS lock is released or drained. --- platform/amiga/handler.h | 33 ++++++++++++++++ platform/amiga/handler_main.c | 73 ++++++++++++++++++++++++++++------- 2 files changed, 92 insertions(+), 14 deletions(-) diff --git a/platform/amiga/handler.h b/platform/amiga/handler.h index 0a4aeba..dcaaa8b 100644 --- a/platform/amiga/handler.h +++ b/platform/amiga/handler.h @@ -34,6 +34,9 @@ struct odfs_changeint_data { ULONG sigmask; }; +/* Filesystem DOSType (matches the OS4 FileSysEntry). */ +#define ODFS_OS4_CD_DOSTYPE 0x43443031UL /* 'CD01' */ + /* * Media-adapter context passed to the odfs_media_ops callbacks. It must * be per-handler-process: diskboot starts one handler process per CD @@ -144,12 +147,17 @@ struct odfs_entry { struct odfs_lock { struct MinNode node; /* for locklist */ +#if ODFS_AMIGA_OS4 + struct Lock *lock; /* DOS-allocated OS4 lock */ +#else struct FileLock lock; /* DOS lock (MUST be at known offset) */ ULONG dos_private[2]; /* reserve fl_SIZEOF..fl_SIZEOF+7 for DOS */ +#endif odfs_entry_t *entry; /* shared object metadata */ ULONG key; /* unique key */ }; +#if !ODFS_AMIGA_OS4 typedef char odfs_lock_private_offset_must_match[ (offsetof(odfs_lock_t, dos_private) == offsetof(odfs_lock_t, lock) + sizeof(struct FileLock)) ? 1 : -1 @@ -157,6 +165,7 @@ typedef char odfs_lock_private_offset_must_match[ typedef char odfs_lock_private_size_must_match[ (sizeof(((odfs_lock_t *)0)->dos_private) == 8) ? 1 : -1 ]; +#endif /* ---- file handle wrapper ---- */ @@ -169,6 +178,29 @@ struct odfs_fh { /* ---- helper macros ---- */ +#if ODFS_AMIGA_OS4 +#define ODFS_LOCK_DOS(ol) \ + (((ol) != NULL) ? (ol)->lock : NULL) + +/* Convert a direct DOS lock pointer to our odfs_lock_t */ +#define LOCK_FROM_PTR(ptr) \ + ((ptr) ? (odfs_lock_t *)((struct Lock *)(ptr))->fl_FSPrivate1 : NULL) + +/* Convert BPTR lock to our odfs_lock_t */ +#define LOCK_FROM_BPTR(bptr) \ + ((bptr) ? LOCK_FROM_PTR((struct Lock *)BADDR(bptr)) : NULL) + +/* Convert odfs_lock_t to BPTR for DOS */ +#define LOCK_TO_BPTR(ol) \ + (((ol) && (ol)->lock) ? MKBADDR((ol)->lock) : 0) + +/* Convert odfs_lock_t to a direct DOS lock pointer */ +#define LOCK_TO_PTR(ol) \ + (((ol) && (ol)->lock) ? (ol)->lock : NULL) +#else +#define ODFS_LOCK_DOS(ol) \ + (((ol) != NULL) ? &(ol)->lock : NULL) + /* Convert BPTR lock to our odfs_lock_t */ #define LOCK_FROM_BPTR(bptr) \ ((bptr) ? (odfs_lock_t *)((UBYTE *)BADDR(bptr) - \ @@ -186,6 +218,7 @@ struct odfs_fh { /* Convert odfs_lock_t to a direct DOS lock pointer */ #define LOCK_TO_PTR(ol) \ ((ol) ? &(ol)->lock : NULL) +#endif /* BCPL string to C string (AROS-compatible) */ static inline void bstr_to_cstr(BSTR bstr, char *buf, int bufsize) diff --git a/platform/amiga/handler_main.c b/platform/amiga/handler_main.c index 1195bf4..3d80084 100644 --- a/platform/amiga/handler_main.c +++ b/platform/amiga/handler_main.c @@ -1052,12 +1052,12 @@ static void rebuild_volume_locklist(handler_global_t *g, odfs_volume_t *volume) if (!head) head = LOCK_TO_BPTR(ol); if (prev) - prev->lock.fl_Link = LOCK_TO_BPTR(ol); + ODFS_LOCK_DOS(prev)->fl_Link = LOCK_TO_BPTR(ol); prev = ol; } if (prev) - prev->lock.fl_Link = 0; + ODFS_LOCK_DOS(prev)->fl_Link = 0; volume->volnode->dl_LockList = head; Permit(); } @@ -1297,6 +1297,10 @@ static void drain_all_objects(handler_global_t *g) while ((node = RemHead((struct List *)&g->locklist)) != NULL) { odfs_lock_t *ol = (odfs_lock_t *)node; +#if ODFS_AMIGA_OS4 + if (ol->lock) + FreeDosObject(DOS_LOCK, ol->lock); +#endif release_volume_object(g, ol->entry->volume); release_entry(ol->entry); odfs_amiga_free_mem(ol, sizeof(*ol)); @@ -1339,6 +1343,7 @@ static odfs_lock_t *alloc_lock(handler_global_t *g, { odfs_lock_t *ol; odfs_entry_t *entry; + struct FileLock *lock; if (!g->current_volume) return NULL; @@ -1352,16 +1357,33 @@ static odfs_lock_t *alloc_lock(handler_global_t *g, release_entry(entry); return NULL; } +#if ODFS_AMIGA_OS4 + ol->lock = AllocDosObjectTags(DOS_LOCK, + ADO_DOSType, ODFS_OS4_CD_DOSTYPE, + TAG_DONE); + if (!ol->lock) { + odfs_amiga_free_mem(ol, sizeof(*ol)); + release_entry(entry); + return NULL; + } +#endif ol->entry = entry; ol->key = amiga_node_key(fnode); +#if !ODFS_AMIGA_OS4 ol->dos_private[0] = 0; ol->dos_private[1] = 0; +#endif - ol->lock.fl_Link = 0; - ol->lock.fl_Key = ol->key; - ol->lock.fl_Access = access; - ol->lock.fl_Task = g->dosport; - ol->lock.fl_Volume = MKBADDR(volume_node_ptr(entry->volume)); + lock = ODFS_LOCK_DOS(ol); + lock->fl_Link = 0; + lock->fl_Key = ol->key; + lock->fl_Access = access; + lock->fl_Task = g->dosport; + lock->fl_Volume = MKBADDR(volume_node_ptr(entry->volume)); +#if ODFS_AMIGA_OS4 + lock->fl_FSPrivate1 = ol; + lock->fl_FSPrivate2 = entry; +#endif retain_volume_object(entry->volume); AddTail((struct List *)&g->locklist, (struct Node *)&ol->node); @@ -1375,6 +1397,10 @@ static void free_lock(handler_global_t *g, odfs_lock_t *ol) return; Remove((struct Node *)&ol->node); rebuild_volume_locklist(g, ol->entry->volume); +#if ODFS_AMIGA_OS4 + if (ol->lock) + FreeDosObject(DOS_LOCK, ol->lock); +#endif release_volume_object(g, ol->entry->volume); release_entry(ol->entry); odfs_amiga_free_mem(ol, sizeof(*ol)); @@ -1383,6 +1409,7 @@ static void free_lock(handler_global_t *g, odfs_lock_t *ol) static odfs_lock_t *dup_lock(handler_global_t *g, odfs_lock_t *src) { odfs_lock_t *ol; + struct FileLock *lock; if (!src) return NULL; @@ -1391,15 +1418,31 @@ static odfs_lock_t *dup_lock(handler_global_t *g, odfs_lock_t *src) if (!ol) return NULL; +#if ODFS_AMIGA_OS4 + ol->lock = AllocDosObjectTags(DOS_LOCK, + ADO_DOSType, ODFS_OS4_CD_DOSTYPE, + TAG_DONE); + if (!ol->lock) { + odfs_amiga_free_mem(ol, sizeof(*ol)); + return NULL; + } +#endif ol->entry = retain_entry(src->entry); ol->key = src->key; +#if !ODFS_AMIGA_OS4 ol->dos_private[0] = 0; ol->dos_private[1] = 0; - ol->lock.fl_Link = 0; - ol->lock.fl_Key = ol->key; - ol->lock.fl_Access = src->lock.fl_Access; - ol->lock.fl_Task = g->dosport; - ol->lock.fl_Volume = MKBADDR(volume_node_ptr(ol->entry->volume)); +#endif + lock = ODFS_LOCK_DOS(ol); + lock->fl_Link = 0; + lock->fl_Key = ol->key; + lock->fl_Access = ODFS_LOCK_DOS(src)->fl_Access; + lock->fl_Task = g->dosport; + lock->fl_Volume = MKBADDR(volume_node_ptr(ol->entry->volume)); +#if ODFS_AMIGA_OS4 + lock->fl_FSPrivate1 = ol; + lock->fl_FSPrivate2 = ol->entry; +#endif retain_volume_object(ol->entry->volume); AddTail((struct List *)&g->locklist, (struct Node *)&ol->node); rebuild_volume_locklist(g, ol->entry->volume); @@ -2073,7 +2116,7 @@ LONG odfs_handler_open_from_lock_object(handler_global_t *g, return err_dos; } - fh = alloc_fh(g, ol->entry, ol->lock.fl_Access); + fh = alloc_fh(g, ol->entry, ODFS_LOCK_DOS(ol)->fl_Access); if (!fh) return ERROR_NO_FREE_STORE; @@ -2207,7 +2250,7 @@ LONG odfs_handler_change_lock_mode(handler_global_t *g, return err_dos; } - ol->lock.fl_Access = + ODFS_LOCK_DOS(ol)->fl_Access = (lock_node(ol)->kind == ODFS_NODE_DIR) ? SHARED_LOCK : mode; return 0; } @@ -2646,8 +2689,10 @@ static void action_examine_object(handler_global_t *g, struct DosPacket *pkt) fill_root_fib(g, fib, fnode); else fill_fib(fib, fnode); +#if !ODFS_AMIGA_OS4 if (ol) ol->dos_private[1] = (ULONG)-1; +#endif #if ODFS_SERIAL_DEBUG && ODFS_PACKET_TRACE trace_node(g, "examine-node", fnode); ODFS_TRACE(&g->log, ODFS_SUB_DOS, From 7302b60b9dc66507592be0fa16e4ed9669546f92 Mon Sep 17 00:00:00 2001 From: Stefan Reinauer Date: Sat, 13 Jun 2026 16:47:05 -0700 Subject: [PATCH 19/24] amiga: complete OS4 Workbench vector semantics Workbench exercises native filesystem vectors more strictly than shell packet paths. Several callbacks returned classic packet values, left fh_Arg1 empty, or allocated ExamineData outside the directory context that DOS owns. Return native boolean SameLock and SameFile values, preserve fh_Arg1 together with fh_Arg2, implement FileSystemAttr queries, bind ExamineDir data to the DOS context, and reject inactive directory locks before iteration. --- platform/amiga/handler_main.c | 18 +- platform/amiga/os4/vector_port.c | 374 +++++++++++++++++++++++++++---- 2 files changed, 340 insertions(+), 52 deletions(-) diff --git a/platform/amiga/handler_main.c b/platform/amiga/handler_main.c index 3d80084..053a33a 100644 --- a/platform/amiga/handler_main.c +++ b/platform/amiga/handler_main.c @@ -2548,13 +2548,13 @@ static void action_same_lock(handler_global_t *g, struct DosPacket *pkt) err_dos = odfs_handler_same_lock_object(g, l1, l2, &same_result); if (err_dos != 0) { - pkt->dp_Res1 = DOSFALSE; + pkt->dp_Res1 = LOCK_DIFFERENT; pkt->dp_Res2 = err_dos; return; } - pkt->dp_Res1 = (same_result == LOCK_SAME) ? DOSTRUE : DOSFALSE; - pkt->dp_Res2 = same_result; + pkt->dp_Res1 = same_result; + pkt->dp_Res2 = 0; } /* ---- examine ---- */ @@ -2619,14 +2619,20 @@ LONG odfs_handler_next_dir_entry(handler_global_t *g, if (!g || !entry_out || !key_out) return ERROR_REQUIRED_ARG_MISSING; - dir = ol ? lock_node(ol) : &g->mount.root; - if (ol) { - LONG err_dos = validate_object_volume(g, ol->entry->volume); + LONG err_dos; + + if (!lock_is_active(g, ol)) + return ERROR_INVALID_LOCK; + + err_dos = validate_object_volume(g, ol->entry->volume); if (err_dos != 0) return err_dos; + dir = lock_node(ol); } else if (!g->mounted) { return ERROR_NO_DISK; + } else { + dir = &g->mount.root; } if (dir->kind != ODFS_NODE_DIR) diff --git a/platform/amiga/os4/vector_port.c b/platform/amiga/os4/vector_port.c index 4eccf8e..b11b163 100644 --- a/platform/amiga/os4/vector_port.c +++ b/platform/amiga/os4/vector_port.c @@ -20,20 +20,51 @@ #include #include +#ifndef ODFS_GIT_VERSION +#define ODFS_GIT_VERSION "unknown" +#endif + +#define ODFS_OS4_FS_VERSION_NUMBER ((53UL << 16) | 4UL) +#define ODFS_OS4_MAX_FILE_SIZE 0x7fffffffffffffffULL + +static handler_global_t *vp_global(struct FSVP *vp); + +static void set_dos_error(int32 *res2, LONG err) +{ + if (res2) + *res2 = err; +} + +static handler_global_t *vp_require_global(struct FSVP *vp, int32 *res2) +{ + handler_global_t *g = vp_global(vp); + + if (!g) + set_dos_error(res2, ERROR_OBJECT_WRONG_TYPE); + return g; +} + static void set_unsupported(struct FSVP *vp, int32 *res2) { - (void)vp; + handler_global_t *g = vp_require_global(vp, res2); - if (res2) - *res2 = ERROR_ACTION_NOT_KNOWN; + if (!g) + return; + + ODFS_TRACE(&g->log, ODFS_SUB_DOS, + "unsupported vector hit -> ACTION_NOT_KNOWN"); + set_dos_error(res2, ERROR_ACTION_NOT_KNOWN); } static void set_write_protected(struct FSVP *vp, int32 *res2) { - (void)vp; + handler_global_t *g = vp_require_global(vp, res2); - if (res2) - *res2 = ERROR_DISK_WRITE_PROTECTED; + if (!g) + return; + + ODFS_TRACE(&g->log, ODFS_SUB_DOS, "write-protected vector hit"); + set_dos_error(res2, ERROR_DISK_WRITE_PROTECTED); } static handler_global_t *vp_global(struct FSVP *vp) @@ -58,18 +89,73 @@ static void fs_unlock(handler_global_t *g) ReleaseSemaphore(&g->fs_sem); } -static void set_dos_error(int32 *res2, LONG err) -{ - if (res2) - *res2 = err; -} - static int32 return_dos_status(int32 *res2, LONG err) { set_dos_error(res2, err); return err == 0 ? DOSTRUE : DOSFALSE; } +static struct TagItem *next_filesystem_attr_tag(struct TagItem **taglist) +{ + struct TagItem *tag; + + if (!taglist) + return NULL; + + tag = *taglist; + while (tag) { + switch (tag->ti_Tag) { + case TAG_DONE: + *taglist = NULL; + return NULL; + case TAG_IGNORE: + tag++; + break; + case TAG_MORE: + tag = (struct TagItem *)tag->ti_Data; + break; + case TAG_SKIP: + tag += tag->ti_Data + 1; + break; + default: + *taglist = tag + 1; + return tag; + } + } + + *taglist = NULL; + return NULL; +} + +static uint32 filesystem_attr_version_buf_size(struct TagItem *taglist) +{ + struct TagItem *state = taglist; + struct TagItem *tag; + uint32 size = 0; + + while ((tag = next_filesystem_attr_tag(&state)) != NULL) { + if (tag->ti_Tag == FSA_VersionStringR_BufSize) + size = tag->ti_Data; + } + + return size; +} + +static void copy_filesystem_version_string(STRPTR buf, uint32 bufsize) +{ + static const char version[] = "ODFileSystem " ODFS_GIT_VERSION; + uint32 i = 0; + + if (!buf || bufsize == 0) + return; + + while (i + 1 < bufsize && version[i] != '\0') { + buf[i] = version[i]; + i++; + } + buf[i] = '\0'; +} + static odfs_lock_t *lock_from_vector(struct Lock *lock) { return (odfs_lock_t *)LOCK_FROM_PTR(lock); @@ -135,6 +221,14 @@ static struct ExamineData *alloc_examine_data_from_context( ed = take_stale_examine_data(ctx, name_len, comment_len); if (!ed) { + /* + * For the FSExamineDir() path the ExamineData entries must be + * bound to the context's memory pool via ADO_ExamineDir_Context, + * so DOS recycles and frees them through the context. For the + * FSExamineObj() path ctx is NULL, which the tag treats as "not + * specified" — the entry is then standalone and freed by the + * caller with FreeDosObject(). + */ ed = AllocDosObjectTags(DOS_EXAMINEDATA, ADO_ExamineData_NameSize, (ULONG)name_len, @@ -142,6 +236,8 @@ static struct ExamineData *alloc_examine_data_from_context( (ULONG)comment_len, ADO_ExamineData_LinkSize, 1UL, + ADO_ExamineDir_Context, + (ULONG)ctx, TAG_END); } if (!ed) @@ -185,23 +281,33 @@ static struct Lock *vp_lock(struct FSVP *vp, CONST_STRPTR obj, int32 mode) { - handler_global_t *g = vp_global(vp); + handler_global_t *g = vp_require_global(vp, res2); odfs_lock_t *ol = NULL; LONG err; + if (!g) + return NULL; + fs_lock(g); err = odfs_handler_lock_object(g, lock_from_vector(rel_lock), obj ? obj : "", mode, &ol); fs_unlock(g); + ODFS_TRACE(&g->log, ODFS_SUB_DOS, + "FSLock rel=%p obj='%s' mode=%ld -> ol=%p err=%ld", + rel_lock, obj ? (const char *)obj : "", (long)mode, ol, + (long)err); set_dos_error(res2, err); return (struct Lock *)LOCK_TO_PTR(ol); } static int32 vp_unlock(struct FSVP *vp, int32 *res2, struct Lock *lock) { - handler_global_t *g = vp_global(vp); + handler_global_t *g = vp_require_global(vp, res2); LONG err; + if (!g) + return DOSFALSE; + fs_lock(g); err = odfs_handler_free_lock_object(g, lock_from_vector(lock)); fs_unlock(g); @@ -212,13 +318,18 @@ static struct Lock *vp_dup_lock(struct FSVP *vp, int32 *res2, struct Lock *lock) { - handler_global_t *g = vp_global(vp); + handler_global_t *g = vp_require_global(vp, res2); odfs_lock_t *ol = NULL; LONG err; + if (!g) + return NULL; + fs_lock(g); err = odfs_handler_dup_lock_object(g, lock_from_vector(lock), &ol); fs_unlock(g); + ODFS_TRACE(&g->log, ODFS_SUB_DOS, + "FSDupLock lock=%p -> ol=%p err=%ld", lock, ol, (long)err); set_dos_error(res2, err); return (struct Lock *)LOCK_TO_PTR(ol); } @@ -238,10 +349,13 @@ static struct Lock *vp_parent_dir(struct FSVP *vp, int32 *res2, struct Lock *dirlock) { - handler_global_t *g = vp_global(vp); + handler_global_t *g = vp_require_global(vp, res2); odfs_lock_t *parent = NULL; LONG err; + if (!g) + return NULL; + fs_lock(g); err = odfs_handler_parent_lock_object(g, lock_from_vector(dirlock), &parent); @@ -254,10 +368,13 @@ static struct Lock *vp_dup_lock_from_fh(struct FSVP *vp, int32 *res2, struct FileHandle *filehandle) { - handler_global_t *g = vp_global(vp); + handler_global_t *g = vp_require_global(vp, res2); odfs_lock_t *ol = NULL; LONG err; + if (!g) + return NULL; + fs_lock(g); err = odfs_handler_dup_lock_from_fh(g, fh_from_vector(filehandle), &ol); fs_unlock(g); @@ -270,16 +387,21 @@ static int32 vp_open_from_lock(struct FSVP *vp, struct FileHandle *file, struct Lock *lock) { - handler_global_t *g = vp_global(vp); + handler_global_t *g = vp_require_global(vp, res2); odfs_fh_t *odfs_fh = NULL; LONG err; + if (!g) + return DOSFALSE; + fs_lock(g); err = odfs_handler_open_from_lock_object(g, lock_from_vector(lock), &odfs_fh); fs_unlock(g); - if (err == 0 && file) + if (err == 0 && file) { + file->fh_Arg1 = (BPTR)odfs_fh; file->fh_Arg2 = odfs_fh; + } return return_dos_status(res2, err); } @@ -287,10 +409,13 @@ static struct Lock *vp_parent_of_fh(struct FSVP *vp, int32 *res2, struct FileHandle *file) { - handler_global_t *g = vp_global(vp); + handler_global_t *g = vp_require_global(vp, res2); odfs_lock_t *parent = NULL; LONG err; + if (!g) + return NULL; + fs_lock(g); err = odfs_handler_parent_fh_object(g, fh_from_vector(file), &parent); fs_unlock(g); @@ -305,29 +430,43 @@ static int32 vp_open(struct FSVP *vp, CONST_STRPTR obj, int32 mode) { - handler_global_t *g = vp_global(vp); + handler_global_t *g = vp_require_global(vp, res2); odfs_fh_t *odfs_fh = NULL; LONG err; + if (!g) + return DOSFALSE; + fs_lock(g); err = odfs_handler_open_object(g, lock_from_vector(rel_dir), obj ? obj : "", mode, &odfs_fh); fs_unlock(g); - if (err == 0 && fh) + ODFS_TRACE(&g->log, ODFS_SUB_DOS, + "FSOpen rel=%p obj='%s' mode=%ld -> err=%ld", + rel_dir, obj ? (const char *)obj : "", (long)mode, + (long)err); + if (err == 0 && fh) { + fh->fh_Arg1 = (BPTR)odfs_fh; fh->fh_Arg2 = odfs_fh; + } return return_dos_status(res2, err); } static int32 vp_close(struct FSVP *vp, int32 *res2, struct FileHandle *file) { - handler_global_t *g = vp_global(vp); + handler_global_t *g = vp_require_global(vp, res2); LONG err; + if (!g) + return DOSFALSE; + fs_lock(g); err = odfs_handler_close_object(g, fh_from_vector(file)); fs_unlock(g); - if (err == 0 && file) + if (err == 0 && file) { + file->fh_Arg1 = 0; file->fh_Arg2 = NULL; + } return return_dos_status(res2, err); } @@ -348,10 +487,13 @@ static int32 vp_read(struct FSVP *vp, STRPTR buffer, int32 numbytes) { - handler_global_t *g = vp_global(vp); + handler_global_t *g = vp_require_global(vp, res2); LONG actual = 0; LONG err; + if (!g) + return -1; + fs_lock(g); err = odfs_handler_read_object(g, fh_from_vector(file), buffer, numbytes, &actual); @@ -375,7 +517,11 @@ static int32 vp_write(struct FSVP *vp, static int32 vp_flush(struct FSVP *vp, int32 *res2) { - (void)vp; + handler_global_t *g = vp_require_global(vp, res2); + + if (!g) + return DOSFALSE; + set_dos_error(res2, 0); return DOSTRUE; } @@ -386,10 +532,13 @@ static int32 vp_change_file_position(struct FSVP *vp, int32 mode, int64 position) { - handler_global_t *g = vp_global(vp); + handler_global_t *g = vp_require_global(vp, res2); int64_t oldpos; LONG err; + if (!g) + return DOSFALSE; + fs_lock(g); err = odfs_handler_seek_object(g, fh_from_vector(file), position, mode, &oldpos); @@ -414,10 +563,13 @@ static int64 vp_get_file_position(struct FSVP *vp, int32 *res2, struct FileHandle *file) { - handler_global_t *g = vp_global(vp); + handler_global_t *g = vp_require_global(vp, res2); int64_t pos; LONG err; + if (!g) + return -1; + fs_lock(g); err = odfs_handler_get_file_position(g, fh_from_vector(file), &pos); fs_unlock(g); @@ -429,10 +581,13 @@ static int64 vp_get_file_size(struct FSVP *vp, int32 *res2, struct FileHandle *file) { - handler_global_t *g = vp_global(vp); + handler_global_t *g = vp_require_global(vp, res2); int64_t size; LONG err; + if (!g) + return -1; + fs_lock(g); err = odfs_handler_get_file_size(g, fh_from_vector(file), &size); fs_unlock(g); @@ -445,9 +600,12 @@ static int32 vp_change_lock_mode(struct FSVP *vp, struct Lock *lock, int32 new_lock_mode) { - handler_global_t *g = vp_global(vp); + handler_global_t *g = vp_require_global(vp, res2); LONG err; + if (!g) + return DOSFALSE; + fs_lock(g); err = odfs_handler_change_lock_mode(g, lock_from_vector(lock), new_lock_mode); @@ -460,9 +618,12 @@ static int32 vp_change_file_mode(struct FSVP *vp, struct FileHandle *fh, int32 new_lock_mode) { - handler_global_t *g = vp_global(vp); + handler_global_t *g = vp_require_global(vp, res2); LONG err; + if (!g) + return DOSFALSE; + fs_lock(g); err = odfs_handler_change_file_mode(g, fh_from_vector(fh), new_lock_mode); @@ -596,10 +757,13 @@ static int32 vp_same_lock(struct FSVP *vp, struct Lock *lock1, struct Lock *lock2) { - handler_global_t *g = vp_global(vp); + handler_global_t *g = vp_require_global(vp, res2); LONG same = LOCK_DIFFERENT; LONG err; + if (!g) + return DOSFALSE; + fs_lock(g); err = odfs_handler_same_lock_object(g, lock_from_vector(lock1), @@ -607,7 +771,7 @@ static int32 vp_same_lock(struct FSVP *vp, &same); fs_unlock(g); set_dos_error(res2, err); - return err == 0 ? same : LOCK_DIFFERENT; + return (err == 0 && same == LOCK_SAME) ? DOSTRUE : DOSFALSE; } static int32 vp_same_file(struct FSVP *vp, @@ -615,10 +779,13 @@ static int32 vp_same_file(struct FSVP *vp, struct FileHandle *fh1, struct FileHandle *fh2) { - handler_global_t *g = vp_global(vp); + handler_global_t *g = vp_require_global(vp, res2); LONG same = LOCK_DIFFERENT; LONG err; + if (!g) + return DOSFALSE; + fs_lock(g); err = odfs_handler_same_file_object(g, fh_from_vector(fh1), @@ -626,25 +793,105 @@ static int32 vp_same_file(struct FSVP *vp, &same); fs_unlock(g); set_dos_error(res2, err); - return err == 0 ? same : LOCK_DIFFERENT; + return (err == 0 && same == FH_SAME) ? DOSTRUE : DOSFALSE; } static int32 vp_filesystem_attr(struct FSVP *vp, int32 *res2, struct TagItem *taglist) { - set_unsupported(vp, res2); - (void)taglist; - return DOSFALSE; + handler_global_t *g = vp_require_global(vp, res2); + struct TagItem *state = taglist; + struct TagItem *tag; + uint32 version_buf_size; + + if (!g) + return DOSFALSE; + + ODFS_TRACE(&g->log, ODFS_SUB_DOS, "FSFileSystemAttr taglist=%p", + taglist); + + version_buf_size = filesystem_attr_version_buf_size(taglist); + + while ((tag = next_filesystem_attr_tag(&state)) != NULL) { + switch (tag->ti_Tag) { + case FSA_StringNameInput: + case FSA_FileHandleInput: + case FSA_LockInput: + case FSA_MsgPortInput: + case FSA_ShowRequesters: + case FSA_VersionStringR_BufSize: + break; + + case FSA_MaxFileNameLengthR: + if (!tag->ti_Data) + return return_dos_status(res2, ERROR_REQUIRED_ARG_MISSING); + *(uint32 *)tag->ti_Data = MAX_VP_FILENAME; + break; + + case FSA_VersionNumberR: + if (!tag->ti_Data) + return return_dos_status(res2, ERROR_REQUIRED_ARG_MISSING); + *(uint32 *)tag->ti_Data = ODFS_OS4_FS_VERSION_NUMBER; + break; + + case FSA_DOSTypeR: + if (!tag->ti_Data) + return return_dos_status(res2, ERROR_REQUIRED_ARG_MISSING); + *(uint32 *)tag->ti_Data = ODFS_OS4_CD_DOSTYPE; + break; + + case FSA_ActivityFlushTimeoutR: + case FSA_InactivityFlushTimeoutR: + case FSA_MaxRecycledEntriesR: + if (!tag->ti_Data) + return return_dos_status(res2, ERROR_REQUIRED_ARG_MISSING); + *(uint32 *)tag->ti_Data = 0; + break; + + case FSA_HasRecycledEntriesR: + if (!tag->ti_Data) + return return_dos_status(res2, ERROR_REQUIRED_ARG_MISSING); + *(int32 *)tag->ti_Data = DOSFALSE; + break; + + case FSA_VersionStringR: + if (!tag->ti_Data || version_buf_size == 0) + return return_dos_status(res2, ERROR_REQUIRED_ARG_MISSING); + copy_filesystem_version_string((STRPTR)tag->ti_Data, + version_buf_size); + break; + + case FSA_MaxFileSizeR: + if (!tag->ti_Data) + return return_dos_status(res2, ERROR_REQUIRED_ARG_MISSING); + *(uint64_t *)tag->ti_Data = ODFS_OS4_MAX_FILE_SIZE; + break; + + case FSA_MaxFileNameLengthW: + case FSA_ActivityFlushTimeoutW: + case FSA_InactivityFlushTimeoutW: + case FSA_MaxRecycledEntriesW: + return return_dos_status(res2, ERROR_DISK_WRITE_PROTECTED); + + default: + return return_dos_status(res2, ERROR_ACTION_NOT_KNOWN); + } + } + + return return_dos_status(res2, 0); } static int32 vp_volume_info_data(struct FSVP *vp, int32 *res2, struct InfoData *info) { - handler_global_t *g = vp_global(vp); + handler_global_t *g = vp_require_global(vp, res2); LONG err; + if (!g) + return DOSFALSE; + fs_lock(g); err = odfs_handler_fill_info(g, NULL, info); fs_unlock(g); @@ -655,9 +902,12 @@ static int32 vp_device_info_data(struct FSVP *vp, int32 *res2, struct InfoData *info) { - handler_global_t *g = vp_global(vp); + handler_global_t *g = vp_require_global(vp, res2); LONG err; + if (!g) + return DOSFALSE; + fs_lock(g); err = odfs_handler_fill_info(g, NULL, info); fs_unlock(g); @@ -669,14 +919,20 @@ static struct ExamineData *vp_examine_obj(struct FSVP *vp, struct Lock *lock, CONST_STRPTR object) { - handler_global_t *g = vp_global(vp); + handler_global_t *g = vp_require_global(vp, res2); odfs_lock_t *ol = NULL; struct ExamineData *ed; LONG err; + if (!g) + return NULL; + fs_lock(g); err = odfs_handler_lock_object(g, lock_from_vector(lock), object ? object : "", SHARED_LOCK, &ol); + ODFS_TRACE(&g->log, ODFS_SUB_DOS, + "FSExamineObj lock=%p obj='%s' -> err=%ld", + lock, object ? (const char *)object : "", (long)err); if (err != 0) { fs_unlock(g); set_dos_error(res2, err); @@ -694,13 +950,19 @@ static struct ExamineData *vp_examine_lock(struct FSVP *vp, int32 *res2, struct Lock *lock) { - handler_global_t *g = vp_global(vp); + handler_global_t *g = vp_require_global(vp, res2); const odfs_node_t *node = NULL; struct ExamineData *ed; LONG err; + if (!g) + return NULL; + fs_lock(g); err = odfs_handler_get_lock_node(g, lock_from_vector(lock), &node); + ODFS_TRACE(&g->log, ODFS_SUB_DOS, + "FSExamineLock lock=%p -> err=%ld name='%s'", + lock, (long)err, (err == 0 && node) ? node->name : ""); if (err != 0) { fs_unlock(g); set_dos_error(res2, err); @@ -717,11 +979,14 @@ static struct ExamineData *vp_examine_file(struct FSVP *vp, int32 *res2, struct FileHandle *file) { - handler_global_t *g = vp_global(vp); + handler_global_t *g = vp_require_global(vp, res2); const odfs_node_t *node = NULL; struct ExamineData *ed; LONG err; + if (!g) + return NULL; + fs_lock(g); err = odfs_handler_get_fh_node(g, fh_from_vector(file), &node); if (err != 0) { @@ -740,18 +1005,28 @@ static int32 vp_examine_dir(struct FSVP *vp, int32 *res2, struct PRIVATE_ExamineDirContext *ctx) { - handler_global_t *g = vp_global(vp); + handler_global_t *g = vp_require_global(vp, res2); odfs_node_t entry; ULONG key = 0; struct ExamineData *ed; LONG err; + if (!g) + return DOSFALSE; + if (!ctx) return return_dos_status(res2, ERROR_REQUIRED_ARG_MISSING); fs_lock(g); err = odfs_handler_next_dir_entry(g, lock_from_vector(ctx->ReferenceLock), ctx->FSPrivate[0], &entry, &key); + ODFS_TRACE(&g->log, ODFS_SUB_DOS, + "FSExamineDir ctx=%p refLock=%p prevKey=%lx -> " + "err=%ld key=%lx name='%s'", + ctx, ctx->ReferenceLock, + (unsigned long)ctx->FSPrivate[0], + (long)err, (unsigned long)key, + (err == 0) ? entry.name : ""); if (err != 0) { fs_unlock(g); return return_dos_status(res2, err); @@ -769,9 +1044,12 @@ static int32 vp_examine_dir(struct FSVP *vp, static int32 vp_inhibit(struct FSVP *vp, int32 *res2, int32 inhibit_state) { - handler_global_t *g = vp_global(vp); + handler_global_t *g = vp_require_global(vp, res2); LONG err; + if (!g) + return DOSFALSE; + fs_lock(g); err = odfs_handler_inhibit(g, inhibit_state); fs_unlock(g); @@ -804,7 +1082,11 @@ static int32 vp_format(struct FSVP *vp, static int32 vp_serialize(struct FSVP *vp, int32 *res2) { - (void)vp; + handler_global_t *g = vp_require_global(vp, res2); + + if (!g) + return DOSFALSE; + set_dos_error(res2, 0); return DOSTRUE; } From f3448c43e94f49b96b26b98e0fe756f18b48ff97 Mon Sep 17 00:00:00 2001 From: Stefan Reinauer Date: Sat, 13 Jun 2026 17:06:49 -0700 Subject: [PATCH 20/24] amiga: keep OS4 packets on classic dispatcher OS4 still sends legacy packet APIs such as Lock(), Examine(), and ExNext() to the handler port. Routing those packets through DOSEmulatePacket returns vector-era semantics and can reject classic examine actions that callers still use. Leave direct packets on the shared packet dispatcher and serialize that dispatcher with the OS4 filesystem semaphore. Native vector calls and legacy packets now share the same handler state without using the vector packet emulator as an extra dispatch path. --- platform/amiga/handler_main.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/platform/amiga/handler_main.c b/platform/amiga/handler_main.c index 053a33a..8b65364 100644 --- a/platform/amiga/handler_main.c +++ b/platform/amiga/handler_main.c @@ -4361,19 +4361,6 @@ void handler_main_startup(struct Message *startup_msg) break; } -#if ODFS_AMIGA_OS4 - if (g->vector_port) { - /* - * Route direct legacy packets through the DOS packet - * emulator, which performs the equivalent vector-port - * call exactly like a native DOS caller would. - */ - odfs_os4_emulate_packet(g->vector_port, pkt); - return_packet(g, pkt); - continue; - } -#endif - if (!g->mounted && packet_needs_live_mount(pkt)) { pkt->dp_Res1 = DOSFALSE; pkt->dp_Res2 = ERROR_NO_DISK; @@ -4381,7 +4368,20 @@ void handler_main_startup(struct Message *startup_msg) continue; } + /* + * Service DOS packets with the shared packet dispatcher. + * On OS4 this dos.library drives the handler through the + * classic packet protocol (BPTR locks) for the legacy + * Lock()/Examine()/ExNext() APIs, including the + * deprecated examine actions that DOSEmulatePacket + * answers with ERROR_ACTION_NOT_KNOWN. Hold the + * filesystem semaphore so packet servicing in the + * handler process is serialized against native + * vector-port calls made from caller context. + */ + ODFS_FS_LOCK(g); handle_packet(g, pkt); + ODFS_FS_UNLOCK(g); return_packet(g, pkt); } } From c7a46f5f0b7e5b6fcb391634832c8621ea09e335 Mon Sep 17 00:00:00 2001 From: Stefan Reinauer Date: Sat, 13 Jun 2026 17:07:08 -0700 Subject: [PATCH 21/24] amiga: use caller-owned IO requests for OS4 reads Native OS4 vector callbacks run in the caller task, but the handler reused its device IORequest whose reply port belongs to the handler task. If a caller-task DoIO() waited for completion, the device signaled the wrong task and Workbench could hang while opening a drawer. Record the handler task at startup. When media reads run from another task, allocate a temporary reply port and IORequest for that caller while reusing the same device and unit binding. --- platform/amiga/handler.h | 1 + platform/amiga/handler_main.c | 77 +++++++++++++++++++++++++++-------- 2 files changed, 62 insertions(+), 16 deletions(-) diff --git a/platform/amiga/handler.h b/platform/amiga/handler.h index dcaaa8b..7e1d01b 100644 --- a/platform/amiga/handler.h +++ b/platform/amiga/handler.h @@ -55,6 +55,7 @@ typedef struct handler_global { struct MsgPort *process_port; /* owning process message port */ struct MsgPort *dosport; /* DOS message port */ #if ODFS_AMIGA_OS4 + struct Task *handler_task; /* task that owns handler ports */ struct FileSystemVectorPort *vector_port; /* native OS4 vector port */ LONG vector_sigbit; /* signal bit used by vector port */ /* diff --git a/platform/amiga/handler_main.c b/platform/amiga/handler_main.c index 8b65364..7b58a43 100644 --- a/platform/amiga/handler_main.c +++ b/platform/amiga/handler_main.c @@ -217,9 +217,41 @@ static odfs_err_t amiga_read_sectors(void *ctx, uint32_t lba, { amiga_media_ctx_t *am = ctx; handler_global_t *g = am->g; + struct IOStdReq *req = g->devreq; +#if ODFS_AMIGA_OS4 + struct MsgPort *tmp_port = NULL; + struct IOStdReq *tmp_req = NULL; +#endif uint32_t total_bytes = count * g->sector_size; uint8_t *out = buf; uint32_t done = 0; + odfs_err_t ret = ODFS_OK; + +#if ODFS_AMIGA_OS4 + /* + * Native vector callbacks run in the caller's task, but g->devreq + * replies to the handler task's port. If a caller-task DoIO() has to + * wait for completion, the device signals the wrong task and the + * caller blocks forever. Use a request with a reply port owned by the + * current task for vector-context media I/O. + */ + if (FindTask(NULL) != g->handler_task) { + tmp_port = odfs_amiga_create_msg_port(); + if (!tmp_port) + return ODFS_ERR_NOMEM; + + tmp_req = (struct IOStdReq *)odfs_amiga_create_io_request( + tmp_port, sizeof(*tmp_req)); + if (!tmp_req) { + odfs_amiga_delete_msg_port(tmp_port); + return ODFS_ERR_NOMEM; + } + + tmp_req->io_Device = g->devreq->io_Device; + tmp_req->io_Unit = g->devreq->io_Unit; + req = tmp_req; + } +#endif /* * Read through the DMA-safe bounce buffer, one chunk at a time. @@ -245,36 +277,46 @@ static odfs_err_t amiga_read_sectors(void *ctx, uint32_t lba, byte_offset_lo = cur_lba * g->sector_size; } - g->devreq->io_Offset = byte_offset_lo; - g->devreq->io_Actual = byte_offset_hi; - g->devreq->io_Length = chunk; - g->devreq->io_Data = g->dma_buf; + req->io_Offset = byte_offset_lo; + req->io_Actual = byte_offset_hi; + req->io_Length = chunk; + req->io_Data = g->dma_buf; if (byte_offset_hi != 0) - g->devreq->io_Command = TD_READ64; + req->io_Command = TD_READ64; else - g->devreq->io_Command = CMD_READ; + req->io_Command = CMD_READ; - if (DoIO((struct IORequest *)g->devreq) != 0 || - g->devreq->io_Error != 0 || - g->devreq->io_Actual != chunk) { + if (DoIO((struct IORequest *)req) != 0 || + req->io_Error != 0 || + req->io_Actual != chunk) { ODFS_ERROR(&g->log, ODFS_SUB_IO, - "sector read failed lba=%lu count=%lu " - "chunk=%lu io_Error=%ld actual=%lu cmd=%lu", + "sector read failed unit=%lu lba=%lu count=%lu " + "chunk=%lu off=%lu io_Error=%ld actual=%lu cmd=%lu", + (unsigned long)g->devunit, (unsigned long)cur_lba, (unsigned long)count, (unsigned long)chunk, - (long)g->devreq->io_Error, - (unsigned long)g->devreq->io_Actual, - (unsigned long)g->devreq->io_Command); - return ODFS_ERR_IO; + (unsigned long)req->io_Offset, + (long)req->io_Error, + (unsigned long)req->io_Actual, + (unsigned long)req->io_Command); + ret = ODFS_ERR_IO; + goto out; } memcpy(out + done, g->dma_buf, chunk); done += chunk; } - return ODFS_OK; +out: +#if ODFS_AMIGA_OS4 + if (tmp_req) + odfs_amiga_delete_io_request((struct IORequest *)tmp_req); + if (tmp_port) + odfs_amiga_delete_msg_port(tmp_port); +#endif + return ret; } static uint32_t amiga_sector_size(void *ctx) @@ -4112,6 +4154,9 @@ void handler_main_startup(struct Message *startup_msg) { struct Process *proc = (struct Process *)FindTask(NULL); +#if ODFS_AMIGA_OS4 + g->handler_task = (struct Task *)proc; +#endif g->process_port = &proc->pr_MsgPort; g->dosport = g->process_port; } From f5951d3c83e608212afd4fa0656343d818006f57 Mon Sep 17 00:00:00 2001 From: Stefan Reinauer Date: Sun, 14 Jun 2026 08:08:38 -0700 Subject: [PATCH 22/24] docs: document AmigaOS 3 and 4 builds Add explicit AmigaOS 3 and AmigaOS 4 build instructions to the README. Describe compiler target selection, separate build directories, explicit tool overrides, and per-target size limits. --- README.md | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 90 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index eb0033e..f0071ae 100644 --- a/README.md +++ b/README.md @@ -135,21 +135,104 @@ Real-world `AS` source images used during development: The automated real-image golden test downloads only the smaller `Arabian Nights` archive on demand, verifies the Archive.org MD5 before reuse, extracts track 1 to a plain 2048-byte data image, and skips cleanly if download or extraction tooling is unavailable. If a prepared data-track image already exists locally, set `ODFS_REAL_AS_IMAGE=/path/to/arabian_nights.iso` to reuse it without redownloading. The larger `Benefactor` image is kept as a manual reference input. -## Sample Mountlist +## Building for AmigaOS + +The Amiga handler is built with the `amiga` make target. The Makefile selects +the Amiga frontend from the compiler target: + +- `m68k-amigaos-gcc` builds the AmigaOS 3/classic handler +- `ppc-amigaos-gcc` builds the native AmigaOS 4 handler -Build the handler with: +### AmigaOS 3 + +With an m68k AmigaOS cross-compiler in `PATH`, run: ```sh make amiga ``` -This builds the release handler with serial logging disabled. For a test build -with serial output enabled, use `make amiga-test`. +This uses `m68k-amigaos-gcc` by default and writes: + +```text +build/amiga/ODFileSystem +``` + +If the compiler is not in `PATH`, pass it explicitly: + +```sh +make amiga CC=/path/to/m68k-amigaos-gcc +``` + +If the matching `ar`, `size`, and `strip` tools are also outside `PATH`, pass +those too: + +```sh +make amiga \ + CC=/path/to/m68k-amigaos-gcc \ + AMIGA_AR=/path/to/m68k-amigaos-ar \ + AMIGA_SIZE=/path/to/m68k-amigaos-size \ + STRIP=/path/to/m68k-amigaos-strip +``` + +### AmigaOS 4 + +With the AmigaOS 4 PPC toolchain in `PATH`, run: + +```sh +make amiga CC=ppc-amigaos-gcc +``` + +This selects `AMIGA_TARGET=os4` automatically and builds the native OS4 +filesystem handler. To keep OS3 and OS4 outputs side by side, use a separate +build directory: + +```sh +make amiga \ + CC=ppc-amigaos-gcc \ + AMIGA_BUILD=build/amiga-os4 +``` + +The handler is then written to: + +```text +build/amiga-os4/ODFileSystem +``` + +If the OS4 toolchain is installed outside `PATH`, pass the full tool paths: + +```sh +make amiga \ + CC=/opt/amiga-ppc/bin/ppc-amigaos-gcc \ + AMIGA_AR=/opt/amiga-ppc/bin/ppc-amigaos-ar \ + AMIGA_SIZE=/opt/amiga-ppc/bin/ppc-amigaos-size \ + STRIP=/opt/amiga-ppc/bin/ppc-amigaos-strip \ + AMIGA_BUILD=build/amiga-os4 +``` + +The Makefile normally derives the NDK include path from the selected compiler. +If your SDK is elsewhere, add `NDK_PATH=/path/to/include_h`. + +### Debug and Size Limits + +Release builds have serial logging disabled. For a test build with serial +output enabled, use `make amiga-test` with the same toolchain selection: + +```sh +make amiga-test +make amiga-test CC=ppc-amigaos-gcc AMIGA_BUILD=build/amiga-os4-test +``` + +Release builds enforce a default size limit of `60000` bytes for OS3 and +`131072` bytes for OS4. If intentional growth needs a higher ceiling, override +it with `AMIGA_SIZE_LIMIT=`. During local bring-up, the limit can be +disabled with `ENFORCE_SIZE_LIMITS=0`. -Release builds enforce a default size limit of `60000` bytes. If intentional -growth needs a higher ceiling, override it with `AMIGA_SIZE_LIMIT=`. +## Sample Mountlist -Then copy `build/amiga/ODFileSystem` to `L:ODFileSystem`. +For the mountlist examples below, copy the built handler to +`L:ODFileSystem`. With the default build directory that is +`build/amiga/ODFileSystem`; if you set `AMIGA_BUILD`, use the corresponding +`ODFileSystem` output from that directory. For Workbench-style installation, copy: From 62459a80f048f55021f5a303501a3a66b8767b7c Mon Sep 17 00:00:00 2001 From: Stefan Reinauer Date: Sun, 14 Jun 2026 08:09:25 -0700 Subject: [PATCH 23/24] ci: build AmigaOS 4 handler artifacts Add a PPC CI job that runs in the amigappc-gcc container. Put /opt/amigappc/bin on PATH, verify ppc-amigaos-gcc, and build both release and serial-debug handler variants. --- .github/workflows/ci-build.yml | 41 ++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index 4e4d696..05f60ef 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -54,3 +54,44 @@ jobs: build/amiga-rom-test/ODFileSystem build/host/tools/ if-no-files-found: error + + build-amigaos4: + runs-on: ubuntu-latest + container: stefanreinauer/amigappc-gcc + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Mark git directory as safe + run: git config --global --add safe.directory '*' + + - name: Add AmigaOS 4 toolchain to PATH + run: echo /opt/amigappc/bin >> "$GITHUB_PATH" + + - name: Check AmigaOS 4 compiler + run: | + command -v ppc-amigaos-gcc + ppc-amigaos-gcc -dumpmachine + + - name: Build AmigaOS 4 handler + run: | + make amiga \ + CC=ppc-amigaos-gcc \ + AMIGA_BUILD=build/amigaos4 + + - name: Build AmigaOS 4 test handler + run: | + make amiga-test \ + CC=ppc-amigaos-gcc \ + AMIGA_TEST_BUILD=build/amigaos4-test + + - name: Upload AmigaOS 4 CI artifacts + uses: actions/upload-artifact@v4 + with: + name: odfilesystem-amigaos4-build + path: | + build/amigaos4/ODFileSystem + build/amigaos4-test/ODFileSystem + if-no-files-found: error From c99d42897f21d6daa2a5abea283432902f4d834d Mon Sep 17 00:00:00 2001 From: Stefan Reinauer Date: Sun, 14 Jun 2026 08:09:41 -0700 Subject: [PATCH 24/24] release: publish AmigaOS 4 handler artifacts Split the draft release workflow into OS3 and OS4 builders so each target uses its matching container and toolchain. Download both artifact sets before creating the draft release, and attach the PPC release and serial-debug handlers alongside the OS3 files. --- .github/workflows/release.yml | 99 ++++++++++++++++++++++++++++++----- 1 file changed, 85 insertions(+), 14 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8c14384..d7486ab 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,8 +19,8 @@ env: RELEASE_REF: ${{ github.event_name == 'workflow_dispatch' && format('refs/tags/{0}', github.event.inputs.tag) || github.ref }} jobs: - build: - name: Build release artifacts + build-amigaos3: + name: Build AmigaOS 3 release artifacts runs-on: ubuntu-latest container: stefanreinauer/amiga-gcc:gcc-v13.3 @@ -44,7 +44,7 @@ jobs: - name: Mark git directory as safe run: git config --global --add safe.directory '*' - - name: Build release artifacts + - name: Build AmigaOS 3 release artifacts env: ODFS_GIT_VERSION: ${{ env.RELEASE_TAG }} run: | @@ -64,10 +64,10 @@ jobs: cp build/amiga-rom-test/ODFileSystem dist/ODFileSystem-rom-test cp build/amiga-test/ODFileSystem.adf dist/ODFileSystem.adf - - name: Upload release artifacts + - name: Upload AmigaOS 3 release artifacts uses: actions/upload-artifact@v4 with: - name: release-artifacts + name: amigaos3-release-artifacts path: | dist/ODFileSystem dist/ODFileSystem-test @@ -76,17 +76,86 @@ jobs: dist/ODFileSystem.adf if-no-files-found: error + build-amigaos4: + name: Build AmigaOS 4 release artifacts + runs-on: ubuntu-latest + container: stefanreinauer/amigappc-gcc + + steps: + - name: Validate release tag + run: | + case "${RELEASE_TAG}" in + v*) ;; + *) + echo "Release tag must start with v: ${RELEASE_TAG}" >&2 + exit 1 + ;; + esac + + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: ${{ env.RELEASE_REF }} + + - name: Mark git directory as safe + run: git config --global --add safe.directory '*' + + - name: Add AmigaOS 4 toolchain to PATH + run: echo /opt/amigappc/bin >> "$GITHUB_PATH" + + - name: Check AmigaOS 4 compiler + run: | + command -v ppc-amigaos-gcc + ppc-amigaos-gcc -dumpmachine + + - name: Build AmigaOS 4 release artifacts + env: + ODFS_GIT_VERSION: ${{ env.RELEASE_TAG }} + run: | + make amiga \ + CC=ppc-amigaos-gcc \ + AMIGA_BUILD=build/amigaos4 + make amiga-test \ + CC=ppc-amigaos-gcc \ + AMIGA_TEST_BUILD=build/amigaos4-test + + - name: Collect AmigaOS 4 release files + run: | + set -eu + mkdir -p dist + cp build/amigaos4/ODFileSystem dist/ODFileSystem-amigaos4 + cp build/amigaos4-test/ODFileSystem \ + dist/ODFileSystem-amigaos4-test + + - name: Upload AmigaOS 4 release artifacts + uses: actions/upload-artifact@v4 + with: + name: amigaos4-release-artifacts + path: | + dist/ODFileSystem-amigaos4 + dist/ODFileSystem-amigaos4-test + if-no-files-found: error + release: name: Create GitHub draft release runs-on: ubuntu-latest - needs: build + needs: + - build-amigaos3 + - build-amigaos4 steps: - - name: Download build artifacts + - name: Download AmigaOS 3 build artifacts + uses: actions/download-artifact@v4 + with: + name: amigaos3-release-artifacts + path: artifacts/amigaos3 + + - name: Download AmigaOS 4 build artifacts uses: actions/download-artifact@v4 with: - name: release-artifacts - path: artifacts + name: amigaos4-release-artifacts + path: artifacts/amigaos4 - name: Create draft release uses: softprops/action-gh-release@v2 @@ -96,8 +165,10 @@ jobs: draft: true generate_release_notes: true files: | - artifacts/ODFileSystem - artifacts/ODFileSystem-test - artifacts/ODFileSystem-rom - artifacts/ODFileSystem-rom-test - artifacts/ODFileSystem.adf + artifacts/amigaos3/ODFileSystem + artifacts/amigaos3/ODFileSystem-test + artifacts/amigaos3/ODFileSystem-rom + artifacts/amigaos3/ODFileSystem-rom-test + artifacts/amigaos3/ODFileSystem.adf + artifacts/amigaos4/ODFileSystem-amigaos4 + artifacts/amigaos4/ODFileSystem-amigaos4-test