Initial import
This commit is contained in:
133
README.org
Normal file
133
README.org
Normal file
@@ -0,0 +1,133 @@
|
||||
#+TITLE: OpenCode live mounts
|
||||
#+STARTUP: :indent
|
||||
Bind-mount host folders into a *running* Docker container, add and remove
|
||||
them at will, and re-apply them after a reboot. No daemon, no Docker API
|
||||
tricks — one setuid helper and three shell scripts.
|
||||
|
||||
* Tutorial: get started
|
||||
|
||||
Build and install the helper and scripts:
|
||||
|
||||
#+begin_src bash
|
||||
make
|
||||
make install
|
||||
sudo chown root ~/opencode-workspace/src/mountctl
|
||||
sudo chmod 4755 ~/opencode-workspace/src/mountctl
|
||||
#+end_src
|
||||
|
||||
Give the container a window into the staging area. Add this =volumes=
|
||||
block to your service (replace =source= with the absolute path to your
|
||||
staging directory) and bring it up once:
|
||||
|
||||
#+begin_src yaml
|
||||
services:
|
||||
app:
|
||||
image: ubuntu
|
||||
volumes:
|
||||
- type: bind
|
||||
source: /home/you/opencode-workspace/workspace
|
||||
target: /workspace
|
||||
bind:
|
||||
propagation: rslave
|
||||
entrypoint: "tail -f /dev/null"
|
||||
#+end_src
|
||||
|
||||
#+begin_src bash
|
||||
docker compose up -d
|
||||
#+end_src
|
||||
|
||||
Now mount something:
|
||||
|
||||
#+begin_src bash
|
||||
~/opencode-workspace/mount-opencode /home/you/code/elm
|
||||
#+end_src
|
||||
|
||||
It shows up at =/workspace/elm= inside the running container. That's it.
|
||||
|
||||
* How-to guides
|
||||
|
||||
** Add a mount
|
||||
|
||||
#+begin_src bash
|
||||
~/opencode-workspace/mount-opencode <source> [name]
|
||||
#+end_src
|
||||
|
||||
=name= defaults to =basename <source>=. The mount is recorded in
|
||||
=mounts.list=.
|
||||
|
||||
** Remove a mount
|
||||
|
||||
#+begin_src bash
|
||||
~/opencode-workspace/unmount-opencode <name>
|
||||
#+end_src
|
||||
|
||||
** Recover all mounts after a reboot
|
||||
|
||||
Bring the container back up first (its rslave bind must be in place),
|
||||
then:
|
||||
|
||||
#+begin_src bash
|
||||
~/opencode-workspace/recover-mounts
|
||||
#+end_src
|
||||
|
||||
It re-runs every mount listed in =mounts.list=; safe to run anytime.
|
||||
|
||||
* Explanation
|
||||
|
||||
Docker has no way to add a mount to a running container, and =nsenter=
|
||||
mounts can't see host paths that aren't already bind-mounted in. The
|
||||
=rslave= propagation on the staging directory is the bridge: it is the
|
||||
one path shared between host and container namespaces, and any sub-mount
|
||||
created *on the host* under it propagates into the container. =rslave=
|
||||
is one-way (host → container), so container-side mounts don't leak back
|
||||
onto the host.
|
||||
|
||||
The staging directory must therefore exist before the container starts,
|
||||
and the container must be recreated (a brief restart) if it was started
|
||||
without the bind. This is a Docker limitation, not a tool choice.
|
||||
|
||||
Why setuid root: =mount(2)= requires =CAP_SYS_ADMIN=, which ordinary
|
||||
users don't have. A compiled C helper is the only thing Linux honours
|
||||
setuid on (scripts are ignored). The helper does *only* raw bind mounts
|
||||
and umounts — no shell, no =system()= — and rejects names that could
|
||||
escape the staging root (= /=, leading dot, empty) or that point at an
|
||||
existing non-directory (symlink-escape guard).
|
||||
|
||||
* Reference
|
||||
|
||||
** =mountctl= (the setuid helper)
|
||||
|
||||
#+begin_example
|
||||
mountctl mount <source> <name> # bind-mount source at STAGING_ROOT/name
|
||||
mountctl umount <name> # umount and rmdir the mountpoint
|
||||
#+end_example
|
||||
|
||||
=STAGING_ROOT= is baked in at compile time; default is
|
||||
=$(HOME)/opencode-workspace/workspace=. Override at build time:
|
||||
|
||||
#+begin_src bash
|
||||
make STAGING_ROOT=/some/other/path
|
||||
#+end_src
|
||||
|
||||
** =mounts.list=
|
||||
|
||||
Plain text, one mount per line, tab-separated:
|
||||
|
||||
#+begin_example
|
||||
<source>\t<name>
|
||||
#+end_example
|
||||
|
||||
Managed by the scripts; edit by hand if you must.
|
||||
|
||||
** Makefile targets
|
||||
|
||||
| =make= | compile =src/mountctl= |
|
||||
| =make install= | deploy to =~/opencode-workspace/= and create the symlinks |
|
||||
| =make clean= | remove the binary |
|
||||
|
||||
** Authorization note
|
||||
|
||||
The helper is setuid root (mode 4755) for now. TODO: restrict to a
|
||||
=live-mount= Unix group via =getgroups(2)=. If your home filesystem is
|
||||
mounted =nosuid= the setuid bit is silently ignored — check with
|
||||
=mount | grep 'on /home'=.
|
||||
Reference in New Issue
Block a user