summaryrefslogtreecommitdiff
path: root/9ns/README.md
diff options
context:
space:
mode:
Diffstat (limited to '9ns/README.md')
-rw-r--r--9ns/README.md61
1 files changed, 44 insertions, 17 deletions
diff --git a/9ns/README.md b/9ns/README.md
index 04edffb..7d6babe 100644
--- a/9ns/README.md
+++ b/9ns/README.md
@@ -4,15 +4,20 @@ Mount a 9P2000 file tree into a fresh mount namespace and run a program in it,
as a plain user, without touching the host's mount table.
```sh
-9ns --unix /run/user/1000/acme -- fish # a shell that sees the tree at /mnt/9p
-9ns --tcp 127.0.0.1:564 -- claude # an agent that sees it too
-9ns --spawn '9proc-demo --stdio' -- bash # start the server yourself, talk over a socketpair
+9ns --unix /run/user/1000/acme -- fish # a shell that sees the tree at /mnt/9p/acme
+9ns --tcp 127.0.0.1:564 -- claude # an agent that sees it at /mnt/9p/tcp-127.0.0.1-564
+9ns --spawn '9proc-demo --stdio' -- bash # start the server yourself; /mnt/9p/9proc-demo
+9ns --unix /tmp/9debug.sock --name dbg -- bash # pick the name: /mnt/9p/dbg
```
Inside, the tree is ordinary files: `ls`, `cat`, `echo x > ctl`, editors,
-`find`, `rsync`, whatever. `$NINE_MOUNT` tells programs where it is
-(default `/mnt/9p`). When the program exits, 9ns exits with its status
-and the namespace, mount and connection disappear.
+`find`, `rsync`, whatever. Every mount lives under `/mnt/9p/<name>`, where
+the name comes from `--name` or, by default, from the transport (the socket's
+basename, `tcp-IP-PORT`, the spawned command's basename, `fdN`); `--mount
+PATH` puts it anywhere else. `$NINE_MOUNT` tells programs where it is. When
+the program exits, 9ns exits with its status and the namespace, mount and
+connection disappear. Nesting works: `9ns --name a -- 9ns --name b -- fish`
+gives a shell that sees both `/mnt/9p/a` and `/mnt/9p/b`.
## How it works
@@ -24,7 +29,7 @@ of the kernel FUSE protocol needed, straight from `linux/fuse.h`.
```
program (fish/bash/claude) 9ns (parent) 9P server
in a new user+mount namespace │
- /mnt/9p ── FUSE ──▶ kernel ────▶│ fuse.zig ─▶ bridge.zig ─▶ nine.zig ──▶ unix / tcp / socketpair
+ /mnt/9p/<name> ─FUSE─▶ kernel ─▶│ fuse.zig ─▶ bridge.zig ─▶ nine.zig ──▶ unix / tcp / socketpair
│ (framing) (translation) (cloud9 Client)
```
@@ -43,10 +48,15 @@ Files are opened with `FOPEN_DIRECT_IO`, so synthetic files that report length
travels inside the 9P open mode (`OTRUNC`) rather than as a separate
truncate. Repeated lookups of the same qid map to the same inode.
-If `/mnt/9p` does not exist and cannot be created (the normal case), 9ns
-mounts a tmpfs over `/mnt` *inside the namespace only* and bind-mounts every
-existing entry of `/mnt` back into it, so nothing is hidden. Pass `--mount DIR`
-to use any other directory.
+The mountpoint `/mnt/9p/<name>` normally does not exist and cannot be
+created by a plain user. 9ns then walks down the path to the deepest existing
+directory (`/mnt` on a host without `/mnt/9p`, `/mnt/9p` if the host has one),
+mounts a tmpfs over it *inside the namespace only*, bind-mounts every existing
+entry of that directory back into the tmpfs so nothing is hidden, and creates
+the missing components inside. Inside a 9ns namespace `/mnt/9p` is already
+that writable tmpfs directory, so a nested 9ns just adds its own name next to
+the outer mount (the same name mounts over it). Pass `--mount DIR` to use any
+other directory.
## Building and testing
@@ -87,7 +97,9 @@ Transport (exactly one):
--spawn CMD run CMD via /bin/sh -c with a socketpair on its stdin/stdout
Options:
- --mount PATH mountpoint inside the new namespace (default /mnt/9p)
+ --name NAME mount name: the tree appears at /mnt/9p/NAME (one path
+ component; default derived from the transport, see below)
+ --mount PATH mountpoint inside the new namespace (overrides --name)
--uname NAME 9P user name (default $USER)
--aname NAME 9P tree to attach (default "")
--msize BYTES maximum 9P message size to request (default 131072, max 16 MiB)
@@ -101,6 +113,17 @@ PROGRAM defaults to `$SHELL`. Exit status is the program's (`128+signal` if it
was killed); 125 means 9ns itself failed (usage, connect, namespace,
mount); 126/127 are exec failures as usual.
+The default name comes from the transport: `--unix PATH` → the basename of
+PATH without a trailing `.sock`, `.9p` or `.socket` (`/tmp/9debug.sock` →
+`9debug`); `--tcp IP:PORT` → `tcp-IP-PORT` with every `:` turned into `-`
+(`[::1]:564` → `tcp---1-564`); `--spawn CMD` → the basename of CMD's first
+word (`/x/9proc-demo --stdio` → `9proc-demo`); `--fd N` → `fdN`. A name must
+be a single path component (not empty, no `/`, not `.` or `..`); an invalid
+`--name` is a usage error, an unusable derived name falls back to `9p`. With
+`--mount PATH` the name is simply PATH's last component. The program gets
+`NINE_MOUNT=<mountpoint>` (replacing any inherited value, so a nested 9ns
+overwrites it).
+
## 9proc-demo: a demo 9P server
`9proc-demo` is a single-binary 9P2000 server whose file tree is the binary
@@ -131,14 +154,18 @@ zig-out/bin/9ns --unix /tmp/intro.sock -- sh -c '
echo "fib 20" > $NINE_MOUNT/runtime/ctl; cat $NINE_MOUNT/runtime/ctl'
```
-The same command with `claude -p "explore /mnt/9p ..."` as the program gives an
-agent a live, file-shaped view into a running process; that is the intended
-use.
+The same command with `claude -p "explore /mnt/9p/intro ..."` as the program
+gives an agent a live, file-shaped view into a running process; that is the
+intended use.
## Limitations
-* One 9P request is in flight at a time; a server read that blocks (event
- files) stalls the mount until it returns.
+* One 9P request is in flight at a time, so a server read that blocks (event
+ files, consoles) stalls the mount while it is outstanding. The blocked
+ request can be interrupted, though: killing or Ctrl-C-ing the reader makes
+ the kernel send `FUSE_INTERRUPT`, which 9ns forwards as 9P `Tflush`;
+ servers that honour it release the reader with `EINTR` at once, servers
+ that ignore it keep the mount stalled until they answer.
* Base 9P2000 only: no symlinks, ownership, xattrs or locks. Every file is
reported as owned by the invoking user. Cross-directory rename is `EXDEV`.
* No PID namespace and no `/proc` remount. `--tcp` takes IP literals only