hst: optionally disable file placement
Test / Create distribution (push) Successful in 52s
Test / Sandbox (push) Successful in 2m39s
Test / ShareFS (push) Successful in 3m46s
Test / Sandbox (race detector) (push) Successful in 5m28s
Test / Hakurei (race detector) (push) Successful in 6m37s
Test / Hakurei (push) Successful in 2m46s
Test / Flake checks (push) Successful in 1m17s

This works around stubborn package managers.

Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
2026-06-19 23:41:36 +09:00
parent b0b2471c0c
commit cb618093d5
9 changed files with 37 additions and 30 deletions
+2 -1
View File
@@ -103,7 +103,8 @@ func main() {
}}, }},
}, },
Username: "chronos", Username: "chronos",
Flags: hst.FMultiarch | Flags: hst.FNoPlace |
hst.FMultiarch |
hst.FDevel | hst.FDevel |
hst.FUserns | hst.FUserns |
hst.FHostNet | hst.FHostNet |
+5 -2
View File
@@ -64,7 +64,7 @@ func TestPrintShowInstance(t *testing.T) {
Identity: 9 (org.chromium.Chromium) Identity: 9 (org.chromium.Chromium)
Enablements: wayland, dbus, pipewire Enablements: wayland, dbus, pipewire
Groups: video, dialout, plugdev Groups: video, dialout, plugdev
Flags: multiarch, compat, devel, userns, net, abstract, tty, mapuid, device, cover_run, runtime, tmpdir Flags: multiarch, compat, devel, userns, net, abstract, tty, mapuid, noplace, device, cover_run, runtime, tmpdir
Home: /data/data/org.chromium.Chromium Home: /data/data/org.chromium.Chromium
Hostname: localhost Hostname: localhost
Path: /run/current-system/sw/bin/chromium Path: /run/current-system/sw/bin/chromium
@@ -161,7 +161,7 @@ App
Identity: 9 (org.chromium.Chromium) Identity: 9 (org.chromium.Chromium)
Enablements: wayland, dbus, pipewire Enablements: wayland, dbus, pipewire
Groups: video, dialout, plugdev Groups: video, dialout, plugdev
Flags: multiarch, compat, devel, userns, net, abstract, tty, mapuid, device, cover_run, runtime, tmpdir Flags: multiarch, compat, devel, userns, net, abstract, tty, mapuid, noplace, device, cover_run, runtime, tmpdir
Home: /data/data/org.chromium.Chromium Home: /data/data/org.chromium.Chromium
Hostname: localhost Hostname: localhost
Path: /run/current-system/sw/bin/chromium Path: /run/current-system/sw/bin/chromium
@@ -354,6 +354,7 @@ App
"tty": true, "tty": true,
"multiarch": true, "multiarch": true,
"map_real_uid": true, "map_real_uid": true,
"noplace": true,
"device": true, "device": true,
"cover_run": true, "cover_run": true,
"share_runtime": true, "share_runtime": true,
@@ -506,6 +507,7 @@ App
"tty": true, "tty": true,
"multiarch": true, "multiarch": true,
"map_real_uid": true, "map_real_uid": true,
"noplace": true,
"device": true, "device": true,
"cover_run": true, "cover_run": true,
"share_runtime": true, "share_runtime": true,
@@ -705,6 +707,7 @@ func TestPrintPs(t *testing.T) {
"tty": true, "tty": true,
"multiarch": true, "multiarch": true,
"map_real_uid": true, "map_real_uid": true,
"noplace": true,
"device": true, "device": true,
"cover_run": true, "cover_run": true,
"share_runtime": true, "share_runtime": true,
+10
View File
@@ -65,6 +65,8 @@ const (
// Some programs fail to connect to dbus session running as a different uid, // Some programs fail to connect to dbus session running as a different uid,
// this option works around it by mapping priv-side caller uid in container. // this option works around it by mapping priv-side caller uid in container.
FMapRealUID FMapRealUID
// FNoPlace disables placement of /etc/passwd and /etc/group.
FNoPlace
// FDevice mount /dev/ from the init mount namespace as is in the container // FDevice mount /dev/ from the init mount namespace as is in the container
// mount namespace. // mount namespace.
@@ -101,6 +103,8 @@ func (flags Flags) String() string {
return "tty" return "tty"
case FMapRealUID: case FMapRealUID:
return "mapuid" return "mapuid"
case FNoPlace:
return "noplace"
case FDevice: case FDevice:
return "device" return "device"
case FCoverRun: case FCoverRun:
@@ -197,6 +201,8 @@ type containerConfigJSON = struct {
// Corresponds to [FMapRealUID]. // Corresponds to [FMapRealUID].
MapRealUID bool `json:"map_real_uid"` MapRealUID bool `json:"map_real_uid"`
// Corresponds to [FNoPlace].
NoPlace bool `json:"noplace,omitempty"`
// Corresponds to [FDevice]. // Corresponds to [FDevice].
Device bool `json:"device,omitempty"` Device bool `json:"device,omitempty"`
@@ -224,6 +230,7 @@ func (c *ContainerConfig) MarshalJSON() ([]byte, error) {
Tty: c.Flags&FTty != 0, Tty: c.Flags&FTty != 0,
Multiarch: c.Flags&FMultiarch != 0, Multiarch: c.Flags&FMultiarch != 0,
MapRealUID: c.Flags&FMapRealUID != 0, MapRealUID: c.Flags&FMapRealUID != 0,
NoPlace: c.Flags&FNoPlace != 0,
Device: c.Flags&FDevice != 0, Device: c.Flags&FDevice != 0,
CoverRun: c.Flags&FCoverRun != 0, CoverRun: c.Flags&FCoverRun != 0,
ShareRuntime: c.Flags&FShareRuntime != 0, ShareRuntime: c.Flags&FShareRuntime != 0,
@@ -266,6 +273,9 @@ func (c *ContainerConfig) UnmarshalJSON(data []byte) error {
if v.MapRealUID { if v.MapRealUID {
c.Flags |= FMapRealUID c.Flags |= FMapRealUID
} }
if v.NoPlace {
c.Flags |= FNoPlace
}
if v.Device { if v.Device {
c.Flags |= FDevice c.Flags |= FDevice
} }
+3 -3
View File
@@ -21,8 +21,8 @@ func TestFlagsString(t *testing.T) {
}{ }{
{"none", 0, "none"}, {"none", 0, "none"},
{"none high", hst.FAll + 1, "none"}, {"none high", hst.FAll + 1, "none"},
{"all", hst.FAll, "multiarch, compat, devel, userns, net, abstract, tty, mapuid, device, cover_run, runtime, tmpdir"}, {"all", hst.FAll, "multiarch, compat, devel, userns, net, abstract, tty, mapuid, noplace, device, cover_run, runtime, tmpdir"},
{"all high", math.MaxUint, "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, noplace, device, cover_run, runtime, tmpdir"},
} }
for _, tc := range testCases { for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) { 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}, {"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}`}, `{"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}, {"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,"cover_run":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,"noplace":true,"device":true,"cover_run":true,"share_runtime":true,"share_tmpdir":true}`},
} }
for _, tc := range testCases { for _, tc := range testCases {
+1
View File
@@ -244,6 +244,7 @@ func TestTemplate(t *testing.T) {
"tty": true, "tty": true,
"multiarch": true, "multiarch": true,
"map_real_uid": true, "map_real_uid": true,
"noplace": true,
"device": true, "device": true,
"cover_run": true, "cover_run": true,
"share_runtime": true, "share_runtime": true,
+1 -7
View File
@@ -143,10 +143,6 @@ func TestOutcomeRun(t *testing.T) {
// spTmpdirOp // spTmpdirOp
Bind(m("/tmp/hakurei.0/tmpdir/9"), fhs.AbsTmp, std.BindWritable). Bind(m("/tmp/hakurei.0/tmpdir/9"), fhs.AbsTmp, std.BindWritable).
// spAccountOp
Place(m("/etc/passwd"), []byte("chronos:x:1971:100:Hakurei:/data/data/org.chromium.Chromium:/run/current-system/sw/bin/zsh\n")).
Place(m("/etc/group"), []byte("hakurei:x:100:\n")).
// spWaylandOp // spWaylandOp
Bind(m("/tmp/hakurei.0/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/wayland"), m("/run/user/1971/wayland-0"), 0). Bind(m("/tmp/hakurei.0/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/wayland"), m("/run/user/1971/wayland-0"), 0).
@@ -453,7 +449,7 @@ func TestOutcomeRun(t *testing.T) {
Path: m("/nix/store/yqivzpzzn7z5x0lq9hmbzygh45d8rhqd-chromium-start"), Path: m("/nix/store/yqivzpzzn7z5x0lq9hmbzygh45d8rhqd-chromium-start"),
Flags: hst.FUserns | hst.FHostNet | hst.FMapRealUID | hst.FShareRuntime | hst.FShareTmpdir, Flags: hst.FUserns | hst.FHostNet | hst.FMapRealUID | hst.FNoPlace | hst.FShareRuntime | hst.FShareTmpdir,
}, },
SystemBus: &hst.BusConfig{ SystemBus: &hst.BusConfig{
Talk: []string{"org.bluez", "org.freedesktop.Avahi", "org.freedesktop.UPower"}, Talk: []string{"org.bluez", "org.freedesktop.Avahi", "org.freedesktop.UPower"},
@@ -548,8 +544,6 @@ func TestOutcomeRun(t *testing.T) {
Tmpfs(m("/run/user/"), xdgRuntimeDirSize, 0755). Tmpfs(m("/run/user/"), xdgRuntimeDirSize, 0755).
Bind(m("/tmp/hakurei.0/runtime/1"), m("/run/user/1971"), std.BindWritable). Bind(m("/tmp/hakurei.0/runtime/1"), m("/run/user/1971"), std.BindWritable).
Bind(m("/tmp/hakurei.0/tmpdir/1"), m("/tmp/"), std.BindWritable). Bind(m("/tmp/hakurei.0/tmpdir/1"), m("/tmp/"), std.BindWritable).
Place(m("/etc/passwd"), []byte("u0_a1:x:1971:100:Hakurei:/var/lib/persist/module/hakurei/0/1:/run/current-system/sw/bin/zsh\n")).
Place(m("/etc/group"), []byte("hakurei:x:100:\n")).
Bind(m("/run/user/1971/wayland-0"), m("/run/user/1971/wayland-0"), 0). Bind(m("/run/user/1971/wayland-0"), m("/run/user/1971/wayland-0"), 0).
Bind(m("/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1/pipewire"), m("/run/user/1971/pipewire-0"), 0). Bind(m("/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1/pipewire"), m("/run/user/1971/pipewire-0"), 0).
Bind(m("/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1/bus"), m("/run/user/1971/bus"), 0). Bind(m("/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1/bus"), m("/run/user/1971/bus"), 0).
-4
View File
@@ -78,10 +78,6 @@ func TestShimEntrypoint(t *testing.T) {
// spTmpdirOp // spTmpdirOp
Bind(m("/tmp/hakurei.10/tmpdir/9999"), fhs.AbsTmp, std.BindWritable). Bind(m("/tmp/hakurei.10/tmpdir/9999"), fhs.AbsTmp, std.BindWritable).
// spAccountOp
Place(m("/etc/passwd"), []byte("chronos:x:1000:100:Hakurei:/data/data/org.chromium.Chromium:/run/current-system/sw/bin/zsh\n")).
Place(m("/etc/group"), []byte("hakurei:x:100:\n")).
// spWaylandOp // spWaylandOp
Bind(m("/tmp/hakurei.10/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/wayland"), m("/run/user/1000/wayland-0"), 0). Bind(m("/tmp/hakurei.10/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/wayland"), m("/run/user/1000/wayland-0"), 0).
+3
View File
@@ -6,6 +6,7 @@ import (
"syscall" "syscall"
"hakurei.app/fhs" "hakurei.app/fhs"
"hakurei.app/hst"
"hakurei.app/internal/validate" "hakurei.app/internal/validate"
) )
@@ -41,6 +42,7 @@ func (s spAccountOp) toContainer(state *outcomeStateParams) error {
state.env["USER"] = username state.env["USER"] = username
state.env["SHELL"] = state.Container.Shell.String() state.env["SHELL"] = state.Container.Shell.String()
if state.Container.Flags&hst.FNoPlace == 0 {
state.params. state.params.
Place(fhs.AbsEtc.Append("passwd"), Place(fhs.AbsEtc.Append("passwd"),
[]byte(username+":x:"+ []byte(username+":x:"+
@@ -51,6 +53,7 @@ func (s spAccountOp) toContainer(state *outcomeStateParams) error {
state.Container.Shell.String()+"\n")). state.Container.Shell.String()+"\n")).
Place(fhs.AbsEtc.Append("group"), Place(fhs.AbsEtc.Append("group"),
[]byte("hakurei:x:"+state.mapgid.String()+":\n")) []byte("hakurei:x:"+state.mapgid.String()+":\n"))
}
return nil return nil
} }
+2 -3
View File
@@ -38,6 +38,7 @@ func TestSpAccountOp(t *testing.T) {
{"success fallback username", func(bool, bool) outcomeOp { return spAccountOp{} }, func() *hst.Config { {"success fallback username", func(bool, bool) outcomeOp { return spAccountOp{} }, func() *hst.Config {
c := hst.Template() c := hst.Template()
c.Container.Username = "" c.Container.Username = ""
c.Container.Flags = hst.FMapRealUID
return c return c
}, nil, []stub.Call{ }, nil, []stub.Call{
// this op performs basic validation and does not make calls during toSystem // this op performs basic validation and does not make calls during toSystem
@@ -60,9 +61,7 @@ func TestSpAccountOp(t *testing.T) {
// this op configures the container state and does not make calls during toContainer // this op configures the container state and does not make calls during toContainer
}, &container.Params{ }, &container.Params{
Dir: config.Container.Home, Dir: config.Container.Home,
Ops: new(container.Ops). Ops: new(container.Ops),
Place(m("/etc/passwd"), []byte("chronos:x:1000:100:Hakurei:/data/data/org.chromium.Chromium:/run/current-system/sw/bin/zsh\n")).
Place(m("/etc/group"), []byte("hakurei:x:100:\n")),
}, paramsWantEnv(config, map[string]string{ }, paramsWantEnv(config, map[string]string{
"HOME": config.Container.Home.String(), "HOME": config.Container.Home.String(),
"USER": config.Container.Username, "USER": config.Container.Username,