Iris gui net indicator rebased updated#39
Merged
techomancer merged 27 commits intoJun 19, 2026
Merged
Conversation
Add a red/green/grey "NET" dot to the control-column status footer, next
to MIPS, reflecting whether the guest's internal NAT networking is alive:
- grey — machine not running / halted / idle at the PROM
- red — running, but no NAT IP traffic seen this run (a hint the guest
has no IP, or the wrong IP for the configured NAT subnet)
- green — NAT IP traffic has flowed (latched for the run, so it doesn't
flicker back to red during idle lulls)
The NAT engine counts only guest IP frames (NatControl.guest_frames,
bumped in NatEngine::process) — ARP and other link-layer chatter happen
even with no/wrong IP and would light it green misleadingly. The count is
surfaced via Seeq8003::nat_control / Machine::net_guest_frames, sampled by
the GUI status worker into Status.net_frames, and turned into a NetState
by EmulatorHandle. Pure net_state_for() helper is unit-tested.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the floating "Click to capture / Ctrl+Alt+Esc" hint drawn over the framebuffer with a section in the left control column, between the config controls and the status footer (shown only while a machine is up): - not captured: "Mouse/Keyboard Capture Disabled" + a "Capture mouse/keyboard" button - captured: "Mouse/Keyboard Captured" + "To disable: Ctrl+Alt+Esc" Release stays the Ctrl+Alt+Esc hotkey rather than a button: while captured the host pointer is grabbed by the guest, so a panel button couldn't be clicked. Factor the capture-engage path out of input::pump into input::engage_capture so both the framebuffer click and the new button share it. Drop the now-unused fb_rect. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Three issues hit when launching with no persisted prefs (no gui.json): - Settings::load() early-returned Self::default() on a missing/unreadable file, skipping the scale sanitizer — so vm_scale/ui_scale came back as the struct's zero Default (0.0) instead of their real defaults. Always run the sanitizer now. - snap_window_to_fb's clamp(0.05, target) panicked (min > max) when target fell below the floor (e.g. a 0.0 vm_scale). Clamp against 0.05.min(target) so it can't. - VM_SCALE_DEFAULT 1.0 -> 0.75: 1.0 means 'native, clamped to fit', which on a typical laptop clamps to a fractional scale and leaves letterbox slack. 0.75 opens target-bound — sized to the 5:4 picture exactly, so the window is narrower and the footer reads a clean 0.75x. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…dow size The window size was persisted and restored, so the vm_scale 'default' only applied on a true first run and the effective scale drifted across launches (0.56 / 0.75 / 0.83 depending on the saved size). Make the launcher fit run on every launch instead, and drop window_size + fullscreen from GuiSettings — only vm_scale and ui_scale persist now, so the window opens at the configured scale consistently. Old gui.json files still load (unknown keys ignored). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Networking tab (config_ui.rs, new netplan.rs, if-addrs dep): - Split the subnet into a plain network-address control + a mask dropdown (no CIDR field); the two compose into nat_subnet, snapped to a clean network address. - Network dropdown lists the first-available 192.168/172.16/10 network at the selected mask (host interfaces enumerated via if-addrs); mask dropdown offers /8 /12 /16 /22 /24 /25 /26 + Custom (a /bits field beside the base address). - Live derived line (gateway, Indy ec0, usable hosts, broadcast), RFC1918 + host-overlap conflict checks, snap-to-network for off-boundary input, and an "Override Sanity Checks / Use suggested / Cancel" modal. - Add-forward menu (Telnet/FTP/Custom), NFS blurb + live mount command, and the Indy's expected ec0 surfaced in the Check-networking window. - netplan.rs is pure and unit-tested (subnet math, first-free, conflict, classify); all Networking UI strings are ASCII (no tofu glyphs). - Networking edits now mark the config dirty via a new TabOutcome. NAT diagnostics backend (net.rs, machine.rs, seeq8003.rs, z85c30.rs, handle.rs, netfix.rs): - Passively observe the guest's source IP and the in-subnet gateway it ARPs for, surfaced to the GUI's Check-networking window. - netfix.rs: pure, testable logic for diagnosing ec0 vs the expected NAT address and the runtime fix commands. docs/networking-tab-redesign.md: design + phased plan (Phase 0/1 done; FTP ALG and the in-app file bridge are still pending). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-networking dialog Check-networking dialog: - Drop the misleading "IRIS expects ec0 at X" line — IRIS is plug-and-play and adopts whatever gateway the guest ARPs for, so the guest's own subnet is fine. The dialog now explains that and points at the real fix (a default route). - Offer "Set IRIS's NAT subnet to <guest>/24" when the guest is on a different subnet, applied live (no reboot) when running, else saved for next launch. - If the guest's subnet overlaps a host network, refuse to switch and instead tell the user to renumber the guest's ec0 onto IRIS's own subnet (shows the exact ifconfig/route commands via netfix). IRIS never shadows the host's LAN. Live NAT reconfigure (no reboot): - NatControl::request_subnet + a NAT-thread apply path (swap gateway/client/ netmask, flush connection tables). Plumbed via Machine::set_nat_subnet and a new Cmd::SetNatSubnet(cidr) on the GUI worker. Host-conflict guard (backend): - NatControl::set_host_nets / host_conflict; the GUI passes the host's interface networks at Start. Plug-and-play adoption now refuses to move onto a subnet that overlaps a host network (LAN/VPN/Docker), so a guest sharing the host's subnet stays unrouted and the dialog guides the user to renumber it. - Convert the TEMP NET-DIAG eprintln spam to dlog_dev!(LogModule::Net). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Make the FTP port-forward (host 2121 -> guest 21) usable by a host's own FTP
client. On an inbound FTP control connection (server = gateway, guest port 21),
when the guest's ftpd sends a passive "227 Entering Passive Mode
(h1,h2,h3,h4,p1,p2)" reply, the NAT now:
- binds a localhost data listener and rewrites the 227 to advertise it, so the
host client connects to 127.0.0.1:<port> instead of the unreachable guest IP;
- registers a transient forward from that host port to the guest's data port,
reusing the existing port-forward machinery.
The length-changing rewrite is safe because the NAT relays application bytes
between an OS host socket and the userspace guest-side TCP (no seq/ack surgery);
client_seq still advances by the original payload length. ftp_pasv_rewrite is
pure and unit-tested.
Transient data forwards are FIFO-bounded (16) and dropped on reset / live subnet
apply. Passive mode only for now (no active/PORT, no EPSV, no cross-segment 227
reassembly). Unverified against a real IRIX boot.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Decide Phase 3 (in-app file bridge) architecture B (pure NAT-side, no host sockets). FTP reuses suppaftp via a transport-generic fork rather than a hand-rolled client; full spec in docs/suppaftp-emu-fork-prompt.md (FtpConnector trait, default TcpConnector preserves behavior, passive-first, TLS only on the TCP path, in-memory mock-transport test). With a virtual-net connector the bridge needs no PASV/ALG rewrite. Two tracks recorded: the suppaftp-emu fork (separate crate, user-driven) and the IRIS foundation (NatEngine in-process peer seam + VirtualTcpStream + VirtualConnector + dual-pane UI + hand-rolled rcp), which resumes once the crate API exists. Paused after delivering the prompt. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
NAT adoption was gated on guest_frames == 0 (any IP frame), so local or broadcast IP chatter — e.g. a ping to x.x.x.255 — permanently disarmed plug-and-play adoption before the guest's gateway ARP could trigger it, leaving the gateway unanswered. Gate adoption instead on a new `routed` flag, set only when the guest sends a frame to the gateway's MAC (real off-subnet traffic), so local/broadcast traffic no longer disarms it. Re-armed on reset and live subnet-apply. Also surface the most common "no networking" cause: when IRIX has networking turned off (/etc/config/network / chkconfig network on) the guest emits no traffic at all, so the Check-networking window only showed "No guest traffic seen yet". It now suggests `chkconfig network on`. Documented as the first "Common mistake" in rules/irix/networking.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
NFS needs the external unfsd (UNFS3) server, which has no clean native Windows build (Cygwin only) and which macOS bundled / App Store builds can neither ship nor spawn in the sandbox. Gate the NFS section off on Windows and on macOS bundled builds (showing a note pointing at a port forward instead, with a button to clear NFS from an imported config). On macOS source builds, show install guidance: no Homebrew formula exists, so build UNFS3 from source or use MacPorts. Linux is unchanged (apt install unfs3). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Port-forward listeners were bound once at NAT construction and never replaced, so a forward added from the GUI while the machine ran wasn't listening (FTP client got ECONNREFUSED on 127.0.0.1:2121 until a restart). Add a live rebind path mirroring the live-subnet apply: NatControl gains pending_forwards + apply_forwards; the NAT thread rebinds its static listeners on its next loop (factored the bind loop into bind_forwards; rebind_forwards swaps the static set and preserves in-flight FTP-ALG data forwards and established connections). Plumbed via Machine::set_port_forwards and a new Cmd::SetPortForwards. The Networking tab now reports forwards_changed and the app pushes the rebind whenever a forward is added/removed/edited while running (latest-wins coalesces in the NAT thread). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Thorough plan to replace external unfsd with a synchronous, pure-Rust NFSv3/UDP server living inside the NAT: the whole protocol stays in-process (NAT dispatches RPC to the server and injects replies; zero host sockets), the only host I/O is the user's backing folder. Reuses the existing in-NAT portmap, the UDP dispatch hook in handle_udp, and ip_frames_udp (outbound fragmentation already works). Vendors nfsserve's XDR/NFS/MOUNT structs (BSD-3) for a sync rewrite; mirrorfs as the local-dir backend model. Includes phasing (read-only -> read-write -> perf) and 18 flagged open questions (NFSv2-vs-v3 and UDP duplicate-request cache being the riskiest). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Foundation for the in-core, pure-Rust NFS server that replaces external unfsd (plan: docs/nfsv3udp-plan.md). This increment is the version-agnostic backend, fully unit-tested without IRIS/IRIX/the NAT: - NfsBacking: one exported folder + a stable fileid<->relative-path map so the guest's opaque handles resolve back to host paths. - Synthetic/"faked" unix attributes (fixed uid/gid 0, mode heuristic dir 0755 / file 0644 + exec bit on unix) so the export behaves identically on Linux/macOS/Windows. - File ops: lookup, attr, readdir, read, write, create, mkdir, remove, rmdir, rename — all path-contained (rejects .., separators, NUL; one normal component only) so the guest can't escape the export. - 6 unit tests over a temp dir. Decisions folded into the plan: support NFSv2 (IRIX 5.3) + NFSv3 (IRIX 6.x) by dispatching on the RPC version (optional Auto/v2/v3 config), full read-write, inbound IP reassembly, simplest synthetic perms, NFS-only mount instructions. Next increments: XDR + RPC framing, v2/v3 procedure encoders, MOUNT, NAT wiring + inbound reassembly, GUI un-gate. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Transport-agnostic wire layer shared by NFSv2 and v3 (one UDP datagram = one RPC message, no TCP record marking): - Xdr encoder (big-endian, 4-byte aligned: u32/i32/u64/bool/opaque/fixed). - Cur decode cursor (bounds-checked, returns Option; opaque/fixed consume pad). - parse_call: parse the RPC CALL header (xid/prog/vers/proc), skip cred+verf (every host allowed), hand back a cursor on the procedure args. - reply: begin an accepted reply with an AUTH_NULL verifier + accept_stat. - 4 wire unit tests (xdr roundtrip, call parse skips auth, rejects non-call / bad rpcvers, reply header bytes). 10 nfsudp tests total. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
fattr3 encoding (full st_mode like knfsd), fileid-as-handle, post_op_attr, and the read-side NFSv3 procedures over the backend: NULL, GETATTR, LOOKUP, ACCESS (grants all), READ, READDIR/READDIRPLUS (name-sorted, cookie = stable index, byte-budgeted paging + eof), FSINFO (advertises rt/wt sizes), FSSTAT, PATHCONF, plus an nfs3_call dispatcher. 4 new unit tests build real RPC calls and parse the replies (getattr root = dir, lookup+read, readdir lists entries, fsinfo sizes). 14 nfsudp tests total. Write-side procedures (increment 4), NFSv2, MOUNT, and the NAT wiring follow. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…sServer Write-side NFSv3: SETATTR (honors size/truncate, ignores chmod/chown), WRITE, CREATE, MKDIR, REMOVE, RMDIR, RENAME, COMMIT (no-op — writes are synchronous), with wcc_data + sattr3 parsing. NfsServer wraps the backing store, the chosen NfsVersion (Auto/V2/V3), and a duplicate-request cache (UDP retransmits must not re-apply non-idempotent ops) — its handle(datagram) is the entry point the NAT will call. 2 new tests (write-through + DRC dedup of a same-xid retransmit; create+remove via the server). 16 nfsudp tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Parallel NFSv2 (RFC 1094) wire encoding over the same backend: 32-bit fattr, fixed 32-byte file handles, microsecond timevals, v2 procedure numbers, and the v2 procedures (GETATTR/SETATTR/LOOKUP/READ/WRITE/CREATE/MKDIR/REMOVE/RMDIR/ RENAME/READDIR/STATFS). NfsServer.dispatch now routes by RPC version (v3 -> v2 fall-through honoring the Auto/V2/V3 setting), and the duplicate-request cache is version-aware. 2 new tests (v2 getattr/lookup/read/write; V3-only server rejects v2 with PROG_MISMATCH). 18 nfsudp tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
MOUNT program 100005: MNT returns the root file handle (v1 = fhstatus + 32-byte fhandle for NFSv2; v3 = mountstat3 + fhandle3 + AUTH_NULL flavor). NULL, UMNT/ UMNTALL (void), DUMP (empty), EXPORT (single "/" export, anyone). The export path is ignored — single export, allow all. NfsServer.dispatch routes MOUNT_PROG to it. 1 new test (MNT v3 + v1 both hand back the root handle). 19 nfsudp tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The NAT now serves NFS in-process: NatEngine owns an Option<NfsServer> built from the configured export at construction; handle_udp intercepts guest UDP to gateway:2049 (NFS) / :1234 (mountd) and dispatches to NfsServer::handle, injecting the reply via ip_frames_udp (large reads auto-fragment). portmap is already answered in-NAT. Added inbound IP-fragment reassembly to handle_ip (keyed by src/id/proto, 5s eviction) so large NFS writes work. Removed the external unfsd path from the CLI (start_unfsd/UnfsdProc) — it spawned a binary that isn't available cross-platform / in the sandbox, and it cleared cfg.nfs on failure, which would have disabled the in-core server. The whole NFS protocol now stays inside the NAT; the only host I/O is the backing folder. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
NFS is now in-process and cross-platform, so:
- NfsConfig drops unfsd/nfs_host_port/mountd_host_port; keeps shared_dir and adds
a serde NfsVersion (Auto/V2/V3, default Auto), plumbed to NfsServer at start.
- GUI: remove the Windows/macOS-bundled gating and the macOS install hint;
drop the unfsd-binary + host/mountd-port fields; add an NFS-version dropdown;
the live mount hint now uses the export root ("mount <gw>:/ /shared").
- Drop the CLI --unfsd/--nfs-port/--mountd-port flags and the dead NFS branches
in nfs_remap_dst/nfs_unmap_src; drop the unfsd sandbox bookmark.
Builds clean on default + appstore; 19 nfsudp + 28 gui tests pass. The NFS
feature is now code-complete (pure-Rust, zero host sockets, no external binary on
any platform) — pending real-boot validation against IRIX.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A large directory (e.g. `ls` of a real Mac ~/Downloads over NFSv2) failed on the guest with "NFS2 readdir failed ... Can't decode result": the v2 READDIR path ignored the request's `count` and used a fixed ~8 KB budget, which overran the client's count-sized decode buffer AND spilled into multiple IP fragments. Both readdir paths now cap each reply at the client's count/maxcount; v2 additionally caps to a single unfragmented UDP datagram and pages the remainder via the cookie. Regression test: `v2_readdir_pages_within_one_datagram`. Write-up in rules/irix/. Confirmed working on a real IRIX boot. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Compressed CHDs can't be written in place, so writes land in an
uncompressed `.diff.chd` sidecar (MAME's model). This adds the machinery
to fold that diff back into the base so the disk "acts normal", and wires
CHDs into the existing per-disk copy-on-write toggle.
Core (`chd_disk.rs`):
- `flatten_diff(base, diff, progress, cancel)` rebuilds the base from the
merged `reopen_diff` view via `create_from_reader`, preserving the
base's codecs/geometry (compressed stays compressed), into a temp file
+ fsync + atomic rename over the base, then deletes the diff. Any
error/cancel leaves base+diff intact. `pub diff_path_for`.
- `ChdHd::open(path, cow)`: COW on -> always overlay (even an uncompressed
base; base never written in-session) and never auto-fold; COW off ->
in-place (uncompressed) / auto-folding diff (compressed).
- `pending_sync()` (auto-fold) gated on `!cow`; `overlay_paths()`,
`is_cow()`, `diff_dirty()` accessors.
Plumbing: `ScsiDevice::{pending_chd_sync, take_pending_chd_sync}` +
`cow_commit`/`cow_reset`/`is_cow`/`cow_dirty_count` extended to CHDs
(commit = flatten + reopen; reset = delete diff + reopen = rollback) ->
`Wd33c93a::{pending_chd_sync_count, sync_chd_disks}` (rebuild outside the
device lock) -> `Machine` accessors. The monitor `cow status|commit|reset`
is backend-agnostic, so it now drives CHDs too.
Tests: flatten_folds_diff_into_compressed_base,
cancelled_flatten_preserves_base_and_diff,
cow_keeps_changes_separate_and_rolls_back. COW defaults off.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e help, macOS folder grant CHD copy-on-write / sync: - "Synchronizing disks…" modal folds pending CHD diffs back into their bases on a clean exit (close-intercept) and on a safe Stop, with a progress bar (Cmd::SyncDisks, Evt::SyncProgress/SyncDone, status `chd_sync_pending`). - SCSI menu "Copy-on-write changes" section (while stopped): per-disk "Commit changes to disk" / "Discard changes (roll back)" — file-level worker ops (Cmd::CowCommit/CowReset) so they can't corrupt a running guest. Discard asks "Are you sure? You will lose any changes to the disk." Disks-tab checkbox relabelled "Copy-on-write". Other GUI work: - Powered-off overlay: dim the frozen final frame + "⏻ Powered off" once the guest soft-powers-off (new `cpu_stopped` status, distinct from the PROM-idle `cpu_halted`). - Mouse/keyboard capture: debounce focus-loss release (300 ms) so a transient macOS focus flicker no longer drops capture mid-typing. - Fix: picking the NFS shared folder (and other path fields) now marks the config dirty — `path_row` returns whether it changed — so the choice persists (it was silently lost). - Help → "Mount the shared folder in IRIX": exact mount command with the live gateway, version-aware notes, a 2 GiB NFSv2 file-size warning, and a collapsible limitations section. Window auto-sizes (resizable(false)). - macOS App Store: "Grant a disk folder…" (File menu) mints a recursive directory bookmark so the CHD fold (temp + rename beside the base) works under the sandbox and an NFS shared subfolder under it is covered too; NFS section offers "Use <granted>/shared". "📂 Reveal in file manager" on every path field via NSWorkspace (sandbox-safe, no `open` subprocess). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Owner
|
awesome, good riddance of unfs3 hack ;-) |
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.
iris-gui: in-core NFS, networking redesign, CHD copy-on-write, macOS App Store polish
TL;DR
This branch makes
iris-guia self-contained, friendly macOS app — no externalhelpers, file sharing that "just works," and disks that behave normally:
unfsd;pure-Rust, runs entirely inside the NAT, zero host sockets, sandbox-safe.
reconfigure (no reboot), plug-and-play adoption, FTP passive-mode ALG.
accumulate writes in a
.diff.chd; this folds them back into the base so thedisk acts normal, with a per-disk COW toggle for keep-separate / rollback /
commit.
debounce, NFS share help, a config-save fix, folder-grant for the sandbox,
NSWorkspace reveal.
1. In-core NFS server (
src/nfsudp.rs,src/net.rs)Pure-Rust NFSv2 (IRIX 5.3) + NFSv3 (IRIX 6.x) over UDP, plus MOUNT (v1/v3) and
portmap, answered entirely inside the user-mode NAT — no
unfsd, no hostsockets, works on every platform and inside the App Sandbox. Auto/v2/v3 selectable
in the GUI. Inbound IP reassembly for large writes; outbound fragmentation for
large reads.
countand pages within asingle unfragmented UDP datagram — a large
lspreviously failed with"Can't decode result." Regression-tested; confirmed on a real IRIX boot.
2. Networking redesign (
iris-guiNetworking tab,src/net.rs)Network/mask preset dropdowns + derived summary, an Add-forward menu, a NET
traffic-light indicator, and a "Check networking" dialog that explains the
plug-and-play adoption model and can set IRIS's subnet to the guest's live. NAT
gains live subnet apply, host-conflict guard on adoption, live port-forward
rebind, and an FTP passive-mode ALG — all without a guest reboot.
3. CHD copy-on-write + sync (
src/chd_disk.rs,scsi.rs,wd33c93a.rs, GUI)Compressed CHDs can't be written in place, so writes land in an uncompressed
.diff.chd(MAME's model). New machinery folds that back:flatten_diffrebuilds the base from the mergedreopen_diffview, preservingcodecs/geometry (compressed stays compressed), via temp + fsync + atomic
rename, then deletes the diff. Crash/cancel-safe (base+diff intact on error).
and on a safe Stop.
overlayflag, now honored forCHDs): on → always overlay + never auto-fold (keep changes separate); off →
in-place (uncompressed) / auto-fold (compressed). Defaults off.
overlay (CHD
.diff.chdor raw.overlay); Discard confirms first. Themonitor
cow status|commit|resetdrives CHDs too.4. GUI / UX + macOS App Store
the guest soft-powers-off (new precise
cpu_stoppedsignal).mouse/keyboard capture mid-typing.
dirty so the choice persists (was silently lost).
version-aware notes, a 2 GiB NFSv2 file-size warning, and a limitations section.
the CHD fold (and an NFS shared subfolder under it) works under the sandbox;
"📂 Reveal in file manager" via NSWorkspace (no
opensubprocess).Testing
tests — all green. Builds clean for default,
bundled, andappstore.ls(the READDIR fix).end-to-end; the capture debounce feel; and — on a signed App Store build —
the folder-grant fold, NSWorkspace reveal, and
shared-subfolder creation. Thedestructive core (
flatten_diff) is unit-tested (atomic rename, cancel-safe).Not in scope (follow-ups)
In-place incremental flatten for uncompressed bases (avoids a full rebuild); a
Cancel button on the sync/commit modal; GUI surfacing of
cow status.Files changed outside
iris-gui/(core crate)All core (
src/) changes belong to one of the three features below. (No vendoredor unrelated code — the winit/App-Store work from #38 is already in
main.)In-core NFS server
src/nfsudp.rs(new, ~1.9k lines) — the entire server: backing store +fileid↔path map, XDR/RPC framing, NFSv2 (IRIX 5.3) and NFSv3 (IRIX 6.x)procedures, MOUNT v1/v3, portmap, and the duplicate-request cache. Includes the
NFSv2 READDIR count/single-datagram fix.
src/net.rs— dispatches guest portmap/MOUNT/NFS UDP datagrams to thein-core server, with inbound IP reassembly (large writes) and outbound
fragmentation (large reads). (Also carries the networking work below.)
src/config.rs—NfsConfig { shared_dir, version }+ theNfsVersionenum (Auto/v2/v3); removes the old external-
unfsdconfig fields.src/main.rs(standaloneirisCLI) — wires the in-core NFS server intothe CLI's NAT, replacing the external
unfsdlaunch.src/lib.rs—pub mod nfsudp.Networking redesign / NAT diagnostics
src/net.rs— live NAT subnet apply (no reboot), host-conflict guard onplug-and-play adoption, live port-forward rebind, and the FTP passive-mode ALG.
src/seeq8003.rs— exposes guest Ethernet frame counts (drives the GUI'sNET traffic-light) and NAT-diagnostic hooks (SEEQ 8003 = emulated Ethernet).
src/z85c30.rs— SCC serial inject/drain hooks used by the in-app serialconsole / networking diagnostics.
src/machine.rs(networking part) — accessors for NAT state (expectedaddresses, observed guest IP/gateway) and the live subnet / port-forward requests.
CHD copy-on-write + sync
src/chd_disk.rs—flatten_diff(fold a.diff.chdinto its base,preserving codecs; temp + atomic rename), the per-disk COW flag on
ChdHd,overlay/pending accessors, and
pub diff_path_for.src/scsi.rs— extends the COW backend (cow_commit/cow_reset/is_cow/cow_dirty_count+ pending-sync) to CHD devices.src/wd33c93a.rs— passes the COW flag intoChdHd::open, addssync_chd_disks(fold pending diffs outside the device lock), and the monitorcow status|commit|resetcommand now drives CHDs.src/machine.rs(COW part) —pending_chd_sync_count/sync_chd_disksaccessors the GUI worker calls.
src/bin/chd_extract.rs— one-line update for the newChdHd::open(path, cow)signature.
How to review
26 commits on top of
upstream/main. Suggested order:src/nfsudp.rs(NFS) →src/chd_disk.rs+src/scsi.rs+src/wd33c93a.rs(COW core) →iris-gui/src/{main,handle,config_ui}.rs(UI). Design notes live indocs/cow-chd-sync-plan.md,docs/nfsudp-plan.md, anddocs/networking-tab-redesign.md.