From c1990b3e6e196ad41379aa432bf3ccca8a65d9f0 Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Sun, 20 Sep 2026 00:36:50 -0300 Subject: Flatten the 9P control tree and move it out of fs.zig The served tree loses the self/ level: /index /ctl /new /log /screen /listeners /pane//... /os, with /src only in -Dembed-sources=true builds (default off, on for esp32p4). ctl speaks the editor's own language with two lowercase verbs, look TEXT and exec TEXT, plus acme's addr verbs; the new/ factory directory becomes one clone file; cons is gone (exec Msg); name and sel are files; stats report real lengths, modes and mtimes; /log streams pane new/del/rename/save events. The tree code lives in src/ninep/ (tree, pane, ctl, addr, pty, events, screen, sources); fs.zig keeps host access, mounts, resolution and find/grep. Same engine and transports. README (fs-help.txt) and docs rewritten; tests updated and extended. Co-Authored-By: Claude Fable 5.1 --- test/v9fs.py | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) (limited to 'test/v9fs.py') diff --git a/test/v9fs.py b/test/v9fs.py index 7dc88195..16916d0b 100644 --- a/test/v9fs.py +++ b/test/v9fs.py @@ -42,25 +42,26 @@ def worker(mountpoint, socket, uid, gid, original_namespace): assert len(mounted) == 1 and ' - 9p ' in mounted[0], mounted assert not any(field.startswith(('shared:', 'master:')) for field in mounted[0].split()[6:]) - assert set(os.listdir(mountpoint)) == {'os', 'self'} - tree = mountpoint / 'self' - assert {'index', 'pane', 'new', 'screen'} <= set(os.listdir(tree)) + tree = mountpoint + assert {'os', 'index', 'pane', 'new', 'ctl', 'log', 'screen', 'README'} <= set(os.listdir(tree)) + assert 'self' not in os.listdir(tree) # A direct connection provides independent evidence for VFS reads/writes. with Client(socket) as client: - assert (tree / 'index').read_bytes() == client.read('/self/index') + assert (tree / 'index').read_bytes() == client.read('/index') assert (tree / 'pane/1/body').read_bytes() == b'initial\n' - before = client.read('/self/index') - subprocess.run(['ls', '-l', str(tree / 'new')], check=True, capture_output=True, timeout=5) - assert {'README', 'ctl', 'body'} <= set(os.listdir(tree / 'new')) - assert (tree / 'new/README').read_bytes() == (tree / 'README').read_bytes() - assert client.read('/self/index') == before, 'browsing created panes' - serial = int((tree / 'new/ctl').read_bytes().split()[0]) - another = int((tree / 'new/ctl').read_bytes().split()[0]) + before = client.read('/index') + subprocess.run(['ls', '-l', str(tree), str(tree / 'pane' / '1')], check=True, capture_output=True, timeout=5) + subprocess.run(['find', str(tree / 'pane'), str(tree / 'new'), '-ls'], check=True, capture_output=True, timeout=5) + assert (tree / 'README').read_bytes() == client.read('/README') + assert (tree / 'new').stat().st_size == 0 + assert client.read('/index') == before, 'browsing created panes' + serial = int((tree / 'new').read_bytes().split()[0]) + another = int((tree / 'new').read_bytes().split()[0]) assert serial != another, 'cached factory reused a pane' - client.write(f'/self/pane/{another}/ctl', b'delete\n') + client.write(f'/pane/{another}/ctl', b'exec Del\n') pane = tree / 'pane' / str(serial) - wire = f'/self/pane/{serial}' + wire = f'/pane/{serial}' assert str(serial) in os.listdir(tree / 'pane') assert serial in [int(row.split()[0]) for row in (tree / 'index').read_bytes().splitlines()] @@ -76,7 +77,7 @@ def worker(mountpoint, socket, uid, gid, original_namespace): assert client.read(wire + '/body') == b'v9fs update\nappended\n' # Exercise an actual shell redirection, including its O_TRUNC open. - subprocess.run(['/bin/sh', '-c', 'printf "name kernel-probe\\n" > "$1/ctl"', + subprocess.run(['/bin/sh', '-c', 'printf "kernel-probe\\n" > "$1/name"', 'v9fs-probe', str(pane)], check=True, timeout=5) tag = client.read(wire + '/tag') assert tag.split(maxsplit=1)[0] == str(socket.parent / 'kernel-probe').encode(), tag @@ -100,7 +101,7 @@ def worker(mountpoint, socket, uid, gid, original_namespace): # Reading OS files through the exported tree does not recurse through # the mount: the core still lives in the supervisor's namespace. assert (mountpoint / 'os' / str(socket.parent).lstrip('/') / 'kernel.txt').read_bytes() == b'initial\n' - write_existing(pane / 'ctl', b'delete\n') + write_existing(pane / 'ctl', b'exec Del\n') assert serial not in [int(row.split()[0]) for row in (tree / 'index').read_bytes().splitlines()] print('v9fs: namespace isolation, privilege drop, inherited mount, directory refresh, ' @@ -154,7 +155,7 @@ def run(binary, helper): print(run_helper(command), end='') assert os.readlink('/proc/self/ns/mnt') == namespace assert list(target.iterdir()) == [], 'mount escaped its private namespace' - assert client.read('/self/pane/1/body') == b'initial\n', 'core stopped serving after probe exit' + assert client.read('/pane/1/body') == b'initial\n', 'core stopped serving after probe exit' print('v9fs: supervisor namespace unchanged and session cleanup passed') -- cgit v1.3