filesys: mount host directories as AmigaDOS volumes#159
Merged
Conversation
A Copperline services board (0x1448 product 5, 64K Zorro II) carries a tiny guest handler (guest/filesys/, C, built reloc-free with dockerized m68k-amigaos GCC 16) that mounts each [[filesys]] config entry via the DiagArea and pumps every DosPacket to the emulator through an A-line trap; src/filesys.rs implements the ACTION_* semantics on the host side. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ACTION_COPY_DIR/PARENT/SET_PROTECT plus read-only file I/O (FINDINPUT/READ/SEEK/END), enough for list, type, and copy-from. A single trailing slash means the directory itself, not its parent (verified against FFS: "Prefs/" lists Prefs, "Prefs//" its parent). The NDK dos.h/dosextens.h constants and structures move to amigaos::dos as repr(C) structs of big-endian fields (zerocopy), so the definition is the guest layout and one write_bytes serializes it. All hand-assembled bytes leave board_image(): the DiagArea now lives inside the handler ROM (entry.s), like real autoboot board ROMs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Contributor
Author
|
I ran out of Fable credits while testing the .uaem metadata parser. My next goals are:
|
Read the UAE .uaem metadata sidecars (same grammar as the Amiberry fsdb: hsparwed presence flags with the rwed group flipped to the FIB deny convention, centisecond timestamp, optional comment) for protection bits, exact datestamps, and file comments; hide the sidecars from listings. Without a sidecar, a read-only host file denies w, and e stays allowed, both matching the UAE fsdb. Sidecar timestamps convert civil-date-direct, with no timezone round trip. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Flush all per-boot handler state (ports, volumes, locks, open files, lock pool) at expansion init, so a warm reboot starts clean instead of misrouting packets to stale MsgPort addresses. Point dn_Startup at a per-unit FileSysStartupMsg (plus shared DosEnvec and device BSTR) written into the board window, so Early Startup shows 'CLFS hostfs-N' instead of dereferencing a raw integer as a BPTR. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
[[filesys]] entries take bootpri (-128..127, default -128 = never a boot candidate), carried in a per-unit DosEnvec whose de_BootPri the guest handler passes to AddBootNode. The DiagArea's BootPoint is now the standard autoboot code (FindResident dos.library, jsr rt_Init), and DiagPoint returns success so Kickstart keeps the diag copy strap boots through. Booting a real Workbench off a host directory flushed out packet-level gaps, all fixed: names with an "Assign:" prefix must resolve relative to the supplied lock, not the root (broke SetPatch's LIBS: opens); ACTION_FH_FROM_LOCK (broke the ENVARC: copy), ACTION_SAME_LOCK, ACTION_EXAMINE_FH, ACTION_PARENT_FH, and ACTION_FLUSH. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR adds experimental host-filesystem support by introducing a new Copperline “services” Zorro II board and a guest-side handler ROM that forwards AmigaDOS packets to a host-side Rust HLE implementation, enabling [[filesys]] config entries to mount host directories as HOSTFS<n>: volumes.
Changes:
- Introduces a new services autoconfig board type and plumbing to pre-seed its RAM window with the handler ROM + mount/diag data.
- Adds
src/filesys.rs(host-side HLE) plus an AmigaDOS ABI mirror (src/amigaos/dos.rs) and configuration parsing/validation for[[filesys]]. - Adds guest-side handler sources/build instructions and commits the built ROM artifact for normal
cargo buildworkflows.
Reviewed changes
Copilot reviewed 14 out of 17 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/zorro.rs | Adds a services board product ID/spec and a helper to add RAM-backed boards pre-seeded with an image. |
| src/lib.rs | Exposes the new filesys module. |
| src/filesys.rs | Implements host-side AmigaDOS packet semantics for mounted host directories, plus board-window layout helpers and unit tests. |
| src/emulator.rs | Wires config-provided filesys mounts into the machine at build time. |
| src/cpu.rs | Replaces the no-op HLE handler with FilesysHle and adds a mounts setter. |
| src/config.rs | Adds [[filesys]] parsing, defaulting, validation, and services-board insertion into the Zorro chain. |
| src/amigaos/dos.rs | Adds a zerocopy-backed big-endian ABI mirror for DOS constants and structures. |
| src/amigaos.rs | Exposes the new amigaos::dos module. |
| guest/services/README.md | Documents the guest-side handler ROM purpose and rebuild process. |
| guest/services/Makefile | Adds dockerized m68k build pipeline and checks for relocations/data/bss. |
| guest/services/handler.c | Implements the guest packet-pump handler and expansion-init mounting logic. |
| guest/services/entry.s | Defines the entry table and embedded DiagArea used for expansion init / autoboot. |
| guest/services/copperline_board.h | Shares board layout/trap opcode constants between guest and host. |
| Cargo.toml | Adds zerocopy dependency for ABI structs. |
| Cargo.lock | Locks zerocopy dependency. |
| .gitignore | Ensures the committed services ROM artifact is not ignored; ignores guest build intermediates. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
A guest could Lock("HOSTFS:..") and walk out of the mounted directory
onto the host filesystem: AmigaDOS gives dots no special meaning, but
match_component passed them to the host, which does. Found by Copilot
review on PR LinuxJedi#159.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Deleting a file popped "unknown packet" (209); a write-protected FFS disk answers every write-family action with error 214 instead, so do the same until write support lands. id_DiskState now reports ID_WRITE_PROTECTED, so C:Info shows the volumes as Read Only. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A dismount tool sends ACTION_DIE to shut a handler down; stock Assign DISMOUNT only unlinks the DeviceNode silently. Refused with ERROR_OBJECT_IN_USE while locks or files are open (protects the boot volume). On success the host frees the unit state and clears dn_Task, and the guest RemDosEntry-s the volume and exits the process, so the next reference to the device simply restarts the handler and remounts. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The packet pump needs well under 200 bytes; 8K per handler process was just wasted guest RAM. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Contributor
Author
|
@codex review |
Contributor
Author
So multi-mount debug logs say which HOSTFS unit a packet belongs to. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The read length comes straight from the guest packet, so allocating it up front let a bogus multi-GB read force an unbounded host allocation. Read in 64 KiB chunks instead; DOS callers see identical results. Reported by Copilot on PR LinuxJedi#159. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
handle_aline claimed the whole 0xA400-0xA4FF range and returned true for any opcode, swallowing the CPU A-line exception. We only emit 0xA400 and 0xA402; return false for the rest so guest software using its own A-line traps takes the exception as on hardware. Reported by Copilot on PR LinuxJedi#159. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The volume name becomes an AmigaDOS DosList BSTR, so reject up front rather than silently truncating or building a broken volume node. Three separate checks with their own errors: empty, over 30 bytes, and containing ":" "/" or NUL. Adds a rejection test. Reported by Copilot on PR LinuxJedi#159. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
TRAP_DIAG_ENTRY reset the per-boot maps field by field and missed next_file_key, which could (in theory) wrap and alias an open file. Reassign *self from a default, preserving only the configured mounts, so a newly added per-boot field can never be forgotten. Reported by Copilot on PR LinuxJedi#159. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
LinuxJedi
approved these changes
Jul 12, 2026
LinuxJedi
left a comment
Owner
There was a problem hiding this comment.
Fantastic work. I’ll merge it at this point and we can iterate on it in further versions
This was referenced Jul 13, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Host-filesystem support:
[[filesys]]config entries export host directories to the guest as AmigaDOS volumes:Design
A new Copperline services board (manufacturer 0x1448, product 5, 64K Zorro II, RAM-backed) carries everything guest-side, with room for future services (RTG, clipboard, ...) on the same board:
guest/services/-- a tiny handler in C plus an asm entry table, built with the dockerized m68k-amigaos GCC 16 (stefanreinauer/amiga-gcc:gcc-v16.1), compiled-mpcrelso the ROM is relocation-free and runs at whatever base autoconfig assigns (the Makefile fails the build if relocations or data/bss sneak in). The built ROM is committed asassets/services/services_rom.bin(~600 bytes), so a plaincargo buildneeds no cross-toolchain. The DiagArea is embedded in the ROM at +0x40, like real autoboot board ROMs.AddBootNode(ADNF_STARTPROC), so DOS mounts and starts the handlers at boot. Each mount's boot priority comes from a per-unitDosEnvecthe emulator writes into the board window; when a HOSTFS node wins the boot vote, the DiagArea's BootPoint runs the standard autoboot sequence (FindResident("dos.library"),jsr rt_Init) and DOS mounts it as SYS:.WaitPort/GetMsg, one reserved A-line trap per DosPacket (D1 = packet, A1 = port),PutMsgthe reply. All ACTION_* semantics live host-side insrc/filesys.rs(FilesysHle, the CPU's high-level-emulation hook), which reads/writes guest memory directly.AddDosEntrys it (a dedicated trap return code -- only guest code may take the DosList semaphore).id_VolumeNode/fl_Volumepoint at it, which is what makes Workbench icons and C:Info work.src/amigaos/dos.rsmirrors the NDK dos.h/dosextens.h constants and structures asrepr(C)structs of big-endian fields (zerocopy), so the Rust definition is the guest layout and one write serializes it -- no magic offsets at call sites. Guest-visible objects (FileLocks, volume nodes) are allocated from board-window pools, so the host never calls guestAllocMem.Path resolution follows FFS semantics, verified against the real FFS handler (a single trailing slash is the directory itself: "Prefs/" lists Prefs, "Prefs//" its parent; names are case-insensitive; an
Assign:prefix in a packet name resolves relative to the supplied lock, like UAE's get_aino).Status
AddBootNodemounts, handler autostartFileSysStartupMsg/DosEnvec, so Early Startup showsCLFS hostfs-Nbootpriconfig option; BootPoint boots DOS off a HOSTFS volumeACTION_DIEdismount: volume off the DosList, handler process exits; refused withERROR_OBJECT_IN_USEwhile locks or files are open (protects the boot volume).dn_Taskis cleared, so the next access to the device restarts the handler and remounts the volumeDISK_INFO/INFOwith real statvfs sizes, UAE-style block scalingIS_FILESYSTEMLOCATE_OBJECT,FREE_LOCK,COPY_DIR(DupLock),PARENT,SAME_LOCKEXAMINE_OBJECT,EXAMINE_NEXT(dir, list),EXAMINE_FHEXAMINE_ALL(DOS falls back toEXAMINE_NEXT)FINDINPUT,READ,SEEK,END,FH_FROM_LOCK,PARENT_FH,FLUSH(type, copy-from)FINDOUTPUT/FINDUPDATE,WRITE,SET_FILE_SIZE(all write actions refused withERROR_DISK_WRITE_PROTECTED, andid_DiskStatereports Read Only).uaemsidecars (+s/+p/+a; sidecars hidden from listings); host read-only maps tow-deniedSET_PROTECT(accepted and ignored; persist to.uaemonce writes land)CREATE_DIR,DELETE,RENAME,SET_DATE,SET_COMMENTRENAME_DISK, Latin-1 filename mappingTesting
info/dir/list/type/copyall behave.bootpri = 5, and survives warm reboots.dirremounts it, and dismounting the boot volume is refused with error 202.🤖 Generated with Claude Code - hand edited afterwards