[PR #55] [MERGED] fix(mounts): resolve sibling bind sources inside a container's rootfs (#53) #54

Closed
opened 2026-10-15 12:51:40 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/games-on-whales/LXC2Docker/pull/55
Author: @JBailes
Created: 6/4/2026
Status: ✅ Merged
Merged: 6/4/2026
Merged by: @JBailes

Base: main ← Head: fix/wolf-pulseaudio-sibling-runtimedir


📝 Commits (1)

  • e222273 fix(mounts): resolve sibling bind sources inside a container's rootfs

📊 Changes

3 files changed (+159 additions, -22 deletions)

View changed files

📝 internal/api/containers.go (+92 -8)
📝 internal/api/sibling_mount_test.go (+41 -0)
📝 internal/lxc/manager.go (+26 -14)

📄 Description

Problem

Second half of issue #53. Wolf launches sibling containers (PulseAudio, apps) through the Docker socket and shares its XDG_RUNTIME_DIR (/run/user/wolf) with them by passing it as a bind source. That path has no host bind-mount backing it — Wolf creates it at runtime inside its own rootfs — so translateSiblingBindSource couldn't map it and lxc-start aborted the sibling:

safe_mount: No such file or directory - Failed to mount "/run/user/wolf" onto ".../rootfs/tmp/pulse"
mount_entry: No such file or directory - Failed to mount "/run/user/wolf" on ".../rootfs/tmp/pulse"
lxc_setup: Failed to setup mount entries

The existing translation only rewrote paths that fell under another managed container's bind-mount destination (e.g. /etc/wolf/...). A bare runtime dir that exists only inside Wolf's rootfs had no match.

Fix

Extend translateSiblingBindSource with a second resolution strategy and split it for clarity:

  • translateViaBindDest — the previous destination-mapping logic, factored out unchanged (case 1, preferred: stable host path outside any container).
  • translateViaContainerRootfs — case 2: resolve the path inside the owning container's rootfs.
    • Pass 1: each container's static rootfs path (RootfsPath) — stable across restarts; covers ZFS-subvol and dir-backed lxcpath layouts.
    • Pass 2: the live mount namespace of any running container via /proc/<pid>/root/<path> — resolves the path regardless of backing storage, including dir-backed raw images (the reporter's data:.../vm-1000-disk-0.raw) whose rootfs is only mounted inside the container's namespace while it runs.

Guards are unchanged: we only rewrite when the literal source is missing on the host, so real host binds are never touched.

Also adds Manager.InitPID, a name-scheme-aware init-PID lookup (Proxmox CTs are addressed by VMID, not the Docker ID), and routes KillContainer through it to drop the duplicated inline lookup.

Tests

internal/api/sibling_mount_test.go gains TestTranslateSiblingBindSourceResolvesRuntimeDir, covering the rootfs resolution (and the negative case). The rootfs/PID lookups are indirected through overridable vars so the test doesn't need a privileged LXC manager. go build ./... and go test ./... pass.

Notes

  • Pass 1 is preferred over the /proc fallback because a static path survives a parent restart (a PID would not). For the current GoW deployments where /run is part of the rootfs (no tmpfs over it), Pass 1 is both stable and correct; Pass 2 is the storage-agnostic catch-all.
  • The pre-existing containerPID helper in stats.go has the same VMID blind spot but is out of scope here; InitPID is the corrected variant.

Refs #53

🤖 Generated with Claude Code


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/games-on-whales/LXC2Docker/pull/55 **Author:** [@JBailes](https://github.com/JBailes) **Created:** 6/4/2026 **Status:** ✅ Merged **Merged:** 6/4/2026 **Merged by:** [@JBailes](https://github.com/JBailes) **Base:** `main` ← **Head:** `fix/wolf-pulseaudio-sibling-runtimedir` --- ### 📝 Commits (1) - [`e222273`](https://github.com/games-on-whales/LXC2Docker/commit/e222273d0717655c71467bb3646c23067fd20595) fix(mounts): resolve sibling bind sources inside a container's rootfs ### 📊 Changes **3 files changed** (+159 additions, -22 deletions) <details> <summary>View changed files</summary> 📝 `internal/api/containers.go` (+92 -8) 📝 `internal/api/sibling_mount_test.go` (+41 -0) 📝 `internal/lxc/manager.go` (+26 -14) </details> ### 📄 Description ## Problem Second half of issue #53. Wolf launches sibling containers (PulseAudio, apps) through the Docker socket and shares its `XDG_RUNTIME_DIR` (`/run/user/wolf`) with them by passing it as a bind source. That path has **no host bind-mount backing it** — Wolf creates it at runtime inside its own rootfs — so `translateSiblingBindSource` couldn't map it and `lxc-start` aborted the sibling: ``` safe_mount: No such file or directory - Failed to mount "/run/user/wolf" onto ".../rootfs/tmp/pulse" mount_entry: No such file or directory - Failed to mount "/run/user/wolf" on ".../rootfs/tmp/pulse" lxc_setup: Failed to setup mount entries ``` The existing translation only rewrote paths that fell under another managed container's bind-mount **destination** (e.g. `/etc/wolf/...`). A bare runtime dir that exists only inside Wolf's rootfs had no match. ## Fix Extend `translateSiblingBindSource` with a second resolution strategy and split it for clarity: - **`translateViaBindDest`** — the previous destination-mapping logic, factored out unchanged (case 1, preferred: stable host path outside any container). - **`translateViaContainerRootfs`** — case 2: resolve the path inside the owning container's rootfs. - *Pass 1:* each container's static rootfs path (`RootfsPath`) — stable across restarts; covers ZFS-subvol and dir-backed lxcpath layouts. - *Pass 2:* the live mount namespace of any running container via `/proc/<pid>/root/<path>` — resolves the path regardless of backing storage, including dir-backed raw images (the reporter's `data:.../vm-1000-disk-0.raw`) whose rootfs is only mounted inside the container's namespace while it runs. Guards are unchanged: we only rewrite when the literal source is **missing on the host**, so real host binds are never touched. Also adds **`Manager.InitPID`**, a name-scheme-aware init-PID lookup (Proxmox CTs are addressed by VMID, not the Docker ID), and routes `KillContainer` through it to drop the duplicated inline lookup. ## Tests `internal/api/sibling_mount_test.go` gains `TestTranslateSiblingBindSourceResolvesRuntimeDir`, covering the rootfs resolution (and the negative case). The rootfs/PID lookups are indirected through overridable vars so the test doesn't need a privileged LXC manager. `go build ./...` and `go test ./...` pass. ## Notes - Pass 1 is preferred over the `/proc` fallback because a static path survives a parent restart (a PID would not). For the current GoW deployments where `/run` is part of the rootfs (no tmpfs over it), Pass 1 is both stable and correct; Pass 2 is the storage-agnostic catch-all. - The pre-existing `containerPID` helper in `stats.go` has the same VMID blind spot but is out of scope here; `InitPID` is the corrected variant. Refs #53 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
Sign in to join this conversation.
No labels
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
games-on-whales/LXC2Docker#54
No description provided.