aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2026-06-17 02:59:48 +0900
committerOphestra <cat@gensokyo.uk>2026-06-17 02:59:48 +0900
commit08112f0b906947bbaf82e450b19c5e0ea8b3fb4d (patch)
treedc14cb72cd6d77cf91fdd73fef19dea9356edded
parente1a1e1e3996b4a7669de6b3c70fb0750d1841311 (diff)
hst: optionally cover /run/ early
This works around awkward root permissions. Signed-off-by: Ophestra <cat@gensokyo.uk>
-rw-r--r--cmd/app/app.go2
-rw-r--r--cmd/app/app_test.go2
-rw-r--r--cmd/hakurei/print_test.go7
-rw-r--r--hst/container.go10
-rw-r--r--hst/container_test.go6
-rw-r--r--hst/hst_test.go1
-rw-r--r--internal/outcome/run_test.go1
-rw-r--r--internal/outcome/shim_test.go1
-rw-r--r--internal/outcome/spruntime.go3
-rw-r--r--internal/outcome/spruntime_test.go4
10 files changed, 31 insertions, 6 deletions
diff --git a/cmd/app/app.go b/cmd/app/app.go
index fa895896..b5e5ee92 100644
--- a/cmd/app/app.go
+++ b/cmd/app/app.go
@@ -83,6 +83,8 @@ func parse(id string, base *check.Absolute, r io.Reader) (*hst.Config, error) {
Home: home,
Path: shell,
Args: []string{"zsh", "-c"},
+
+ Flags: hst.FCoverRun,
},
}
diff --git a/cmd/app/app_test.go b/cmd/app/app_test.go
index 7c8c9ede..7a83bcc8 100644
--- a/cmd/app/app_test.go
+++ b/cmd/app/app_test.go
@@ -122,7 +122,7 @@ talk com.canonical.Unity
"exec Discord --ozone-platform-hint=wayland",
},
- Flags: hst.FUserns | hst.FHostNet | hst.FMapRealUID |
+ Flags: hst.FCoverRun | hst.FUserns | hst.FHostNet | hst.FMapRealUID |
hst.FShareRuntime | hst.FShareTmpdir,
},
}, nil},
diff --git a/cmd/hakurei/print_test.go b/cmd/hakurei/print_test.go
index f947fced..71eb8caa 100644
--- a/cmd/hakurei/print_test.go
+++ b/cmd/hakurei/print_test.go
@@ -64,7 +64,7 @@ func TestPrintShowInstance(t *testing.T) {
Identity: 9 (org.chromium.Chromium)
Enablements: wayland, dbus, pipewire
Groups: video, dialout, plugdev
- Flags: multiarch, compat, devel, userns, net, abstract, tty, mapuid, device, runtime, tmpdir
+ Flags: multiarch, compat, devel, userns, net, abstract, tty, mapuid, device, cover_run, runtime, tmpdir
Home: /data/data/org.chromium.Chromium
Hostname: localhost
Path: /run/current-system/sw/bin/chromium
@@ -161,7 +161,7 @@ App
Identity: 9 (org.chromium.Chromium)
Enablements: wayland, dbus, pipewire
Groups: video, dialout, plugdev
- Flags: multiarch, compat, devel, userns, net, abstract, tty, mapuid, device, runtime, tmpdir
+ Flags: multiarch, compat, devel, userns, net, abstract, tty, mapuid, device, cover_run, runtime, tmpdir
Home: /data/data/org.chromium.Chromium
Hostname: localhost
Path: /run/current-system/sw/bin/chromium
@@ -355,6 +355,7 @@ App
"multiarch": true,
"map_real_uid": true,
"device": true,
+ "cover_run": true,
"share_runtime": true,
"share_tmpdir": true
},
@@ -506,6 +507,7 @@ App
"multiarch": true,
"map_real_uid": true,
"device": true,
+ "cover_run": true,
"share_runtime": true,
"share_tmpdir": true
}
@@ -704,6 +706,7 @@ func TestPrintPs(t *testing.T) {
"multiarch": true,
"map_real_uid": true,
"device": true,
+ "cover_run": true,
"share_runtime": true,
"share_tmpdir": true
},
diff --git a/hst/container.go b/hst/container.go
index f985f393..a34efc32 100644
--- a/hst/container.go
+++ b/hst/container.go
@@ -69,6 +69,8 @@ const (
// FDevice mount /dev/ from the init mount namespace as is in the container
// mount namespace.
FDevice
+ // FCoverRun covers /run/ in the container mount namespace early.
+ FCoverRun
// FShareRuntime shares XDG_RUNTIME_DIR between containers under the same identity.
FShareRuntime
@@ -101,6 +103,8 @@ func (flags Flags) String() string {
return "mapuid"
case FDevice:
return "device"
+ case FCoverRun:
+ return "cover_run"
case FShareRuntime:
return "runtime"
case FShareTmpdir:
@@ -196,6 +200,8 @@ type containerConfigJSON = struct {
// Corresponds to [FDevice].
Device bool `json:"device,omitempty"`
+ // Corresponds to [FCoverRun].
+ CoverRun bool `json:"cover_run,omitempty"`
// Corresponds to [FShareRuntime].
ShareRuntime bool `json:"share_runtime,omitempty"`
@@ -219,6 +225,7 @@ func (c *ContainerConfig) MarshalJSON() ([]byte, error) {
Multiarch: c.Flags&FMultiarch != 0,
MapRealUID: c.Flags&FMapRealUID != 0,
Device: c.Flags&FDevice != 0,
+ CoverRun: c.Flags&FCoverRun != 0,
ShareRuntime: c.Flags&FShareRuntime != 0,
ShareTmpdir: c.Flags&FShareTmpdir != 0,
})
@@ -262,6 +269,9 @@ func (c *ContainerConfig) UnmarshalJSON(data []byte) error {
if v.Device {
c.Flags |= FDevice
}
+ if v.CoverRun {
+ c.Flags |= FCoverRun
+ }
if v.ShareRuntime {
c.Flags |= FShareRuntime
}
diff --git a/hst/container_test.go b/hst/container_test.go
index df84827e..309c0a63 100644
--- a/hst/container_test.go
+++ b/hst/container_test.go
@@ -21,8 +21,8 @@ func TestFlagsString(t *testing.T) {
}{
{"none", 0, "none"},
{"none high", hst.FAll + 1, "none"},
- {"all", hst.FAll, "multiarch, compat, devel, userns, net, abstract, tty, mapuid, device, runtime, tmpdir"},
- {"all high", math.MaxUint, "multiarch, compat, devel, userns, net, abstract, tty, mapuid, device, runtime, tmpdir"},
+ {"all", hst.FAll, "multiarch, compat, devel, userns, net, abstract, tty, mapuid, device, cover_run, runtime, tmpdir"},
+ {"all high", math.MaxUint, "multiarch, compat, devel, userns, net, abstract, tty, mapuid, device, cover_run, runtime, tmpdir"},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
@@ -53,7 +53,7 @@ func TestContainerConfig(t *testing.T) {
{"hostnet hostabstract mapuid", &hst.ContainerConfig{Flags: hst.FHostNet | hst.FHostAbstract | hst.FMapRealUID},
`{"env":null,"filesystem":null,"shell":null,"home":null,"args":null,"host_net":true,"host_abstract":true,"map_real_uid":true}`},
{"all", &hst.ContainerConfig{Flags: hst.FAll},
- `{"env":null,"filesystem":null,"shell":null,"home":null,"args":null,"seccomp_compat":true,"devel":true,"userns":true,"host_net":true,"host_abstract":true,"tty":true,"multiarch":true,"map_real_uid":true,"device":true,"share_runtime":true,"share_tmpdir":true}`},
+ `{"env":null,"filesystem":null,"shell":null,"home":null,"args":null,"seccomp_compat":true,"devel":true,"userns":true,"host_net":true,"host_abstract":true,"tty":true,"multiarch":true,"map_real_uid":true,"device":true,"cover_run":true,"share_runtime":true,"share_tmpdir":true}`},
}
for _, tc := range testCases {
diff --git a/hst/hst_test.go b/hst/hst_test.go
index 54a81284..c5e2cb12 100644
--- a/hst/hst_test.go
+++ b/hst/hst_test.go
@@ -245,6 +245,7 @@ func TestTemplate(t *testing.T) {
"multiarch": true,
"map_real_uid": true,
"device": true,
+ "cover_run": true,
"share_runtime": true,
"share_tmpdir": true
}
diff --git a/internal/outcome/run_test.go b/internal/outcome/run_test.go
index 77738641..faa322ec 100644
--- a/internal/outcome/run_test.go
+++ b/internal/outcome/run_test.go
@@ -136,6 +136,7 @@ func TestOutcomeRun(t *testing.T) {
Tmpfs(fhs.AbsDevShm, 0, 01777).
// spRuntimeOp
+ Tmpfs(fhs.AbsRun, xdgRuntimeDirSize, 0755).
Tmpfs(fhs.AbsRunUser, xdgRuntimeDirSize, 0755).
Bind(m("/tmp/hakurei.0/runtime/9"), m("/run/user/1971"), std.BindWritable).
diff --git a/internal/outcome/shim_test.go b/internal/outcome/shim_test.go
index 6d9a9f3d..2dc7a4cd 100644
--- a/internal/outcome/shim_test.go
+++ b/internal/outcome/shim_test.go
@@ -71,6 +71,7 @@ func TestShimEntrypoint(t *testing.T) {
Tmpfs(fhs.AbsDevShm, 0, 01777).
// spRuntimeOp
+ Tmpfs(fhs.AbsRun, xdgRuntimeDirSize, 0755).
Tmpfs(fhs.AbsRunUser, xdgRuntimeDirSize, 0755).
Bind(m("/tmp/hakurei.10/runtime/9999"), m("/run/user/1000"), std.BindWritable).
diff --git a/internal/outcome/spruntime.go b/internal/outcome/spruntime.go
index 15974a44..c08bfe81 100644
--- a/internal/outcome/spruntime.go
+++ b/internal/outcome/spruntime.go
@@ -113,6 +113,9 @@ func (s *spRuntimeOp) toContainer(state *outcomeStateParams) error {
}
+ if state.Container.Flags&hst.FCoverRun != 0 {
+ state.params.Tmpfs(fhs.AbsRun, xdgRuntimeDirSize, 0755)
+ }
state.params.Tmpfs(fhs.AbsRunUser, xdgRuntimeDirSize, 0755)
if state.Container.Flags&hst.FShareRuntime != 0 {
_, runtimeDirInst := s.commonPaths(state.outcomeState)
diff --git a/internal/outcome/spruntime_test.go b/internal/outcome/spruntime_test.go
index e083f7c3..25f2684c 100644
--- a/internal/outcome/spruntime_test.go
+++ b/internal/outcome/spruntime_test.go
@@ -40,6 +40,7 @@ func TestSpRuntimeOp(t *testing.T) {
// this op configures the container state and does not make calls during toContainer
}, &container.Params{
Ops: new(container.Ops).
+ Tmpfs(fhs.AbsRun, xdgRuntimeDirSize, 0755).
Tmpfs(fhs.AbsRunUser, xdgRuntimeDirSize, 0755).
Bind(m("/proc/nonexistent/tmp/hakurei.0/runtime/9"), m("/run/user/1000"), std.BindWritable),
}, paramsWantEnv(config, map[string]string{
@@ -67,6 +68,7 @@ func TestSpRuntimeOp(t *testing.T) {
// this op configures the container state and does not make calls during toContainer
}, &container.Params{
Ops: new(container.Ops).
+ Tmpfs(fhs.AbsRun, xdgRuntimeDirSize, 0755).
Tmpfs(fhs.AbsRunUser, xdgRuntimeDirSize, 0755).
Bind(m("/proc/nonexistent/tmp/hakurei.0/runtime/9"), m("/run/user/1000"), std.BindWritable),
}, paramsWantEnv(config, map[string]string{
@@ -94,6 +96,7 @@ func TestSpRuntimeOp(t *testing.T) {
// this op configures the container state and does not make calls during toContainer
}, &container.Params{
Ops: new(container.Ops).
+ Tmpfs(fhs.AbsRun, xdgRuntimeDirSize, 0755).
Tmpfs(fhs.AbsRunUser, xdgRuntimeDirSize, 0755).
Bind(m("/proc/nonexistent/tmp/hakurei.0/runtime/9"), m("/run/user/1000"), std.BindWritable),
}, paramsWantEnv(config, map[string]string{
@@ -117,6 +120,7 @@ func TestSpRuntimeOp(t *testing.T) {
// this op configures the container state and does not make calls during toContainer
}, &container.Params{
Ops: new(container.Ops).
+ Tmpfs(fhs.AbsRun, xdgRuntimeDirSize, 0755).
Tmpfs(fhs.AbsRunUser, xdgRuntimeDirSize, 0755).
Bind(m("/proc/nonexistent/tmp/hakurei.0/runtime/9"), m("/run/user/1000"), std.BindWritable),
}, paramsWantEnv(config, map[string]string{