Mount host /private via VirtioFS and rewrite Docker bind paths#249
Mount host /private via VirtioFS and rewrite Docker bind paths#249
Conversation
…aths On macOS, /tmp → /private/tmp and /var → /private/var are symlinks. Docker bind mounts to these paths failed because the guest had no /private mount and its /tmp and /var are isolated tmpfs. Add a VirtioFS share for host /private (macOS-only, gated by is_dir check) so the guest can access /private/tmp, /private/var/folders, etc. Guest /tmp and /var remain isolated tmpfs — no host pollution. In the Docker API proxy, resolve top-level symlinks in bind-mount source paths (HostConfig.Binds and HostConfig.Mounts) before forwarding to guest dockerd. Uses readlink on the first path component rather than a hardcoded prefix list, so it automatically handles any macOS system symlink and is a no-op on Linux.
There was a problem hiding this comment.
Pull request overview
Adds macOS-specific support for Docker bind-mounts that go through top-level system symlinks (e.g. /tmp → /private/tmp) by (1) exposing host /private into the guest via VirtioFS and (2) rewriting Docker container-create bind mount sources to use the resolved /private/... path before proxying to guest dockerd.
Changes:
- Add
/privateVirtioFS share (tag + mountpoint) and mount it in the guest (both PID1 init path and standard agent startup path). - Introduce a Docker-proxy-side path resolver that follows the top-level symlink for bind-mount sources in
HostConfig.BindsandHostConfig.Mounts. - Replace the generic create-container proxy handler with a custom handler that rewrites the request body before forwarding.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| guest/arcbox-agent/src/mount.rs | Mounts the new private VirtioFS share at /private when available. |
| guest/arcbox-agent/src/init.rs | Mounts /private early during PID1 init so it exists before tmpfs mounts. |
| common/arcbox-constants/src/virtiofs.rs | Adds TAG_PRIVATE / MOUNT_PRIVATE constants. |
| app/arcbox-docker/src/lib.rs | Exposes the new host_path module. |
| app/arcbox-docker/src/host_path.rs | Implements top-level symlink resolution + JSON request body rewriting for binds/mounts. |
| app/arcbox-docker/src/handlers/container.rs | Rewrites container-create request body before proxying to guest dockerd. |
| app/arcbox-core/src/machine.rs | Adds host /private to VM shared directories when present. |
- Gate symlink resolution to target_os = "macos" so Linux top-level symlinks (/bin → /usr/bin, etc.) are never rewritten. - Remove Content-Length header after body rewrite so the proxy does not forward a stale value when the rewritten JSON differs in size.
Greptile SummaryThis PR fixes Key changes:
The approach is dynamic (reads the actual symlink on-disk) rather than a hardcoded prefix list, so it naturally handles Confidence Score: 5/5Safe to merge — all prior review concerns addressed, implementation is clean and well-tested, remaining findings are non-blocking P2 style suggestions. Both previously flagged issues (hardcoded string literals, No files require special attention; Important Files Changed
Sequence DiagramsequenceDiagram
participant CLI as Docker CLI (host)
participant Proxy as arcbox-docker proxy
participant HostPath as host_path::rewrite_create_body
participant GuestD as guest dockerd
participant VirtioFS as VirtioFS /private
CLI->>Proxy: POST /containers/create
Proxy->>HostPath: rewrite_create_body(body)
HostPath->>HostPath: resolve("/tmp")
Note over HostPath: read_link("/tmp") → "private/tmp"
HostPath-->>Proxy: Binds rewritten to /private/tmp/foo
Note over Proxy: Strip Content-Length header
Proxy->>GuestD: POST /containers/create (rewritten)
GuestD->>VirtioFS: bind mount /private/tmp/foo
Note over VirtioFS: Backed by host /private/tmp/foo
VirtioFS-->>GuestD: mount OK
GuestD-->>Proxy: 201 Created
Proxy-->>CLI: 201 Created
Reviews (3): Last reviewed commit: "docs(mount): update mount_standard_share..." | Re-trigger Greptile |
Replace hardcoded "private"/"/private" and "users"/"/Users" strings in guest init with TAG_PRIVATE/MOUNT_PRIVATE and TAG_USERS/MOUNT_USERS constants to prevent silent drift from host-side config. Narrow host_path module visibility to pub(crate) — it's an internal implementation detail of the Docker proxy.
Summary
/private(macOS-only) so the guest can access/private/tmp,/private/var/folders, etc. Guest/tmpand/varremain isolated tmpfs — no host pollution.HostConfig.Binds,HostConfig.Mounts) before forwarding to guest dockerd. Usesreadlinkon the first path component rather than a hardcoded prefix list, so it handles any macOS system symlink and is a no-op on Linux.Fixes
docker run -v /tmp/foo:/appand-v /var/folders/...:/appwhich previously silently mounted empty directories.Test plan
cargo clippy --all-targetsandcargo testpassmount | grep virtiofsin guest showsprivate on /private type virtiofs/tmpis still tmpfs (mount | grep /tmpshows tmpfs)docker run --rm -v /tmp/test-arcbox:/mnt alpine ls /mntsees host filesdocker run --rm -v /private/tmp/test-arcbox:/mnt alpine ls /mntsamedocker run --rm -v /Users/...:/mnt alpine ls /mntstill works (no regression)