From fadb42ec8daeeb8a576582d274cd4aeb64e535c0 Mon Sep 17 00:00:00 2001 From: Ophestra Date: Tue, 13 Oct 2026 22:58:19 +0900 Subject: internal/system: skip superfluous chmod A successful chmod clobbers acl mask, breaking the fast path. This change also fixes the ACL race. Signed-off-by: Ophestra --- internal/outcome/outcome.go | 8 ++-- internal/outcome/run_test.go | 52 +++++++++++----------- internal/outcome/spcontainer.go | 4 +- internal/outcome/spcontainer_test.go | 16 +++---- internal/outcome/spdbus_test.go | 4 +- internal/outcome/sppipewire_test.go | 2 +- internal/outcome/sppulse_test.go | 24 +++++----- internal/outcome/spruntime.go | 4 +- internal/outcome/spruntime_test.go | 16 +++---- internal/outcome/sptmpdir.go | 4 +- internal/outcome/sptmpdir_test.go | 4 +- internal/outcome/spwayland_test.go | 8 ++-- internal/system/dispatcher_test.go | 13 ++++++ internal/system/mkdir.go | 40 +++++++++++++---- internal/system/mkdir_test.go | 86 +++++++++++++++++++++--------------- internal/system/system_test.go | 40 +++++++++-------- 16 files changed, 190 insertions(+), 135 deletions(-) (limited to 'internal') diff --git a/internal/outcome/outcome.go b/internal/outcome/outcome.go index 0fe0016e..414b421f 100644 --- a/internal/outcome/outcome.go +++ b/internal/outcome/outcome.go @@ -240,8 +240,8 @@ func (state *outcomeStateSys) ensureRuntimeDir() { state.useRuntimeDir = true state.sys. // ensure this dir in case XDG_RUNTIME_DIR is unset - Ensure(state.sc.RuntimePath, 0700).UpdatePermType(system.User, state.sc.RuntimePath, acl.Execute). - Ensure(state.sc.RunDirPath, 0700).UpdatePermType(system.User, state.sc.RunDirPath, acl.Execute) + Ensure(state.sc.RuntimePath, 0700, true).UpdatePermType(system.User, state.sc.RuntimePath, acl.Execute). + Ensure(state.sc.RunDirPath, 0700, true).UpdatePermType(system.User, state.sc.RunDirPath, acl.Execute) } // instance returns the pathname to a process-specific directory within TMPDIR. @@ -252,7 +252,7 @@ func (state *outcomeStateSys) instance() *check.Absolute { return state.sharePath } state.sharePath = state.instancePath() - state.sys.Ephemeral(system.Process, state.sharePath, 0711) + state.sys.Ephemeral(system.Process, state.sharePath, 0711, false) return state.sharePath } @@ -265,7 +265,7 @@ func (state *outcomeStateSys) runtime() *check.Absolute { } state.ensureRuntimeDir() state.runtimeSharePath = state.runtimePath() - state.sys.Ephemeral(system.Process, state.runtimeSharePath, 0700) + state.sys.Ephemeral(system.Process, state.runtimeSharePath, 0700, true) state.sys.UpdatePerm(state.runtimeSharePath, acl.Execute) return state.runtimeSharePath } diff --git a/internal/outcome/run_test.go b/internal/outcome/run_test.go index e4e33dca..a8297768 100644 --- a/internal/outcome/run_test.go +++ b/internal/outcome/run_test.go @@ -42,22 +42,22 @@ func TestOutcomeRun(t *testing.T) { }{ {"template", new(stubNixOS), hst.Template(), checkExpectInstanceId, system.New(panicMsgContext{}, message.New(nil), 10009). // spParamsOp - Ensure(m("/tmp/hakurei.0"), 0711). + Ensure(m("/tmp/hakurei.0"), 0711, false). // spRuntimeOp - Ensure(m("/tmp/hakurei.0/runtime"), 0700). + Ensure(m("/tmp/hakurei.0/runtime"), 0700, true). UpdatePermType(system.User, m("/tmp/hakurei.0/runtime"), acl.Execute). - Ensure(m("/tmp/hakurei.0/runtime/9"), 0700). + Ensure(m("/tmp/hakurei.0/runtime/9"), 0700, true). UpdatePermType(system.User, m("/tmp/hakurei.0/runtime/9"), acl.Read|acl.Write|acl.Execute). // spTmpdirOp - Ensure(m("/tmp/hakurei.0/tmpdir"), 0700). + Ensure(m("/tmp/hakurei.0/tmpdir"), 0700, true). UpdatePermType(system.User, m("/tmp/hakurei.0/tmpdir"), acl.Execute). - Ensure(m("/tmp/hakurei.0/tmpdir/9"), 01700). + Ensure(m("/tmp/hakurei.0/tmpdir/9"), 01700, true). UpdatePermType(system.User, m("/tmp/hakurei.0/tmpdir/9"), acl.Read|acl.Write|acl.Execute). // instance - Ephemeral(system.Process, m("/tmp/hakurei.0/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), 0711). + Ephemeral(system.Process, m("/tmp/hakurei.0/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), 0711, false). // spWaylandOp Wayland( @@ -88,7 +88,7 @@ func TestOutcomeRun(t *testing.T) { UpdatePerm(m("/tmp/hakurei.0/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/system_bus_socket"), acl.Read|acl.Write). // spFilesystemOp - Ensure(m("/var/lib/hakurei/u0"), 0700). + Ensure(m("/var/lib/hakurei/u0"), 0700, true). UpdatePermType(system.User, m("/var/lib/hakurei/u0"), acl.Execute). UpdatePermType(system.User, m("/var/lib/hakurei/u0/org.chromium.Chromium"), acl.Read|acl.Write|acl.Execute), &container.Params{ @@ -201,14 +201,14 @@ func TestOutcomeRun(t *testing.T) { 0xbd, 0x01, 0x78, 0x0e, 0xb9, 0xa6, 0x07, 0xac, }, system.New(t.Context(), msg, 10000). - Ensure(m("/tmp/hakurei.0"), 0711). - Ensure(m("/tmp/hakurei.0/runtime"), 0700). + Ensure(m("/tmp/hakurei.0"), 0711, false). + Ensure(m("/tmp/hakurei.0/runtime"), 0700, true). UpdatePermType(system.User, m("/tmp/hakurei.0/runtime"), acl.Execute). - Ensure(m("/tmp/hakurei.0/runtime/0"), 0700). + Ensure(m("/tmp/hakurei.0/runtime/0"), 0700, true). UpdatePermType(system.User, m("/tmp/hakurei.0/runtime/0"), acl.Read|acl.Write|acl.Execute). - Ensure(m("/tmp/hakurei.0/tmpdir"), 0700). + Ensure(m("/tmp/hakurei.0/tmpdir"), 0700, true). UpdatePermType(system.User, m("/tmp/hakurei.0/tmpdir"), acl.Execute). - Ensure(m("/tmp/hakurei.0/tmpdir/0"), 01700). + Ensure(m("/tmp/hakurei.0/tmpdir/0"), 01700, true). UpdatePermType(system.User, m("/tmp/hakurei.0/tmpdir/0"), acl.Read|acl.Write|acl.Execute), &container.Params{ Dir: m("/home/chronos"), @@ -327,12 +327,12 @@ func TestOutcomeRun(t *testing.T) { 0x82, 0xd4, 0x13, 0x36, 0x9b, 0x64, 0xce, 0x7c, }, system.New(t.Context(), msg, 10009). - Ensure(m("/tmp/hakurei.0"), 0711). - Ensure(m("/tmp/hakurei.0/runtime"), 0700).UpdatePermType(system.User, m("/tmp/hakurei.0/runtime"), acl.Execute). - Ensure(m("/tmp/hakurei.0/runtime/9"), 0700).UpdatePermType(system.User, m("/tmp/hakurei.0/runtime/9"), acl.Read|acl.Write|acl.Execute). - Ensure(m("/tmp/hakurei.0/tmpdir"), 0700).UpdatePermType(system.User, m("/tmp/hakurei.0/tmpdir"), acl.Execute). - Ensure(m("/tmp/hakurei.0/tmpdir/9"), 01700).UpdatePermType(system.User, m("/tmp/hakurei.0/tmpdir/9"), acl.Read|acl.Write|acl.Execute). - Ephemeral(system.Process, m("/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c"), 0711). + Ensure(m("/tmp/hakurei.0"), 0711, false). + Ensure(m("/tmp/hakurei.0/runtime"), 0700, true).UpdatePermType(system.User, m("/tmp/hakurei.0/runtime"), acl.Execute). + Ensure(m("/tmp/hakurei.0/runtime/9"), 0700, true).UpdatePermType(system.User, m("/tmp/hakurei.0/runtime/9"), acl.Read|acl.Write|acl.Execute). + Ensure(m("/tmp/hakurei.0/tmpdir"), 0700, true).UpdatePermType(system.User, m("/tmp/hakurei.0/tmpdir"), acl.Execute). + Ensure(m("/tmp/hakurei.0/tmpdir/9"), 01700, true).UpdatePermType(system.User, m("/tmp/hakurei.0/tmpdir/9"), acl.Read|acl.Write|acl.Execute). + Ephemeral(system.Process, m("/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c"), 0711, false). Wayland(m("/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/wayland"), m("/run/user/1971/wayland-0"), "org.chromium.Chromium", "ebf083d1b175911782d413369b64ce7c"). PipeWire(m("/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/pipewire"), "org.chromium.Chromium", "ebf083d1b175911782d413369b64ce7c"). MustProxyDBus(&hst.BusConfig{ @@ -478,15 +478,15 @@ func TestOutcomeRun(t *testing.T) { 0x4c, 0xf0, 0x73, 0xbd, 0xb4, 0x6e, 0xb5, 0xc1, }, system.New(t.Context(), msg, 10001). - Ensure(m("/tmp/hakurei.0"), 0711). - Ensure(m("/tmp/hakurei.0/runtime"), 0700).UpdatePermType(system.User, m("/tmp/hakurei.0/runtime"), acl.Execute). - Ensure(m("/tmp/hakurei.0/runtime/1"), 0700).UpdatePermType(system.User, m("/tmp/hakurei.0/runtime/1"), acl.Read|acl.Write|acl.Execute). - Ensure(m("/tmp/hakurei.0/tmpdir"), 0700).UpdatePermType(system.User, m("/tmp/hakurei.0/tmpdir"), acl.Execute). - Ensure(m("/tmp/hakurei.0/tmpdir/1"), 01700).UpdatePermType(system.User, m("/tmp/hakurei.0/tmpdir/1"), acl.Read|acl.Write|acl.Execute). - Ensure(m("/run/user/1971"), 0700).UpdatePermType(system.User, m("/run/user/1971"), acl.Execute). // this is ordered as is because the previous Ensure only calls mkdir if XDG_RUNTIME_DIR is unset - Ensure(m("/run/user/1971/hakurei"), 0700).UpdatePermType(system.User, m("/run/user/1971/hakurei"), acl.Execute). + Ensure(m("/tmp/hakurei.0"), 0711, false). + Ensure(m("/tmp/hakurei.0/runtime"), 0700, true).UpdatePermType(system.User, m("/tmp/hakurei.0/runtime"), acl.Execute). + Ensure(m("/tmp/hakurei.0/runtime/1"), 0700, true).UpdatePermType(system.User, m("/tmp/hakurei.0/runtime/1"), acl.Read|acl.Write|acl.Execute). + Ensure(m("/tmp/hakurei.0/tmpdir"), 0700, true).UpdatePermType(system.User, m("/tmp/hakurei.0/tmpdir"), acl.Execute). + Ensure(m("/tmp/hakurei.0/tmpdir/1"), 01700, true).UpdatePermType(system.User, m("/tmp/hakurei.0/tmpdir/1"), acl.Read|acl.Write|acl.Execute). + Ensure(m("/run/user/1971"), 0700, true).UpdatePermType(system.User, m("/run/user/1971"), acl.Execute). // this is ordered as is because the previous Ensure only calls mkdir if XDG_RUNTIME_DIR is unset + Ensure(m("/run/user/1971/hakurei"), 0700, true).UpdatePermType(system.User, m("/run/user/1971/hakurei"), acl.Execute). UpdatePermType(hst.EWayland, m("/run/user/1971/wayland-0"), acl.Read|acl.Write|acl.Execute). - Ephemeral(system.Process, m("/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1"), 0711). + Ephemeral(system.Process, m("/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1"), 0711, false). PipeWire(m("/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1/pipewire"), "org.chromium.Chromium", "8e2c76b066dabe574cf073bdb46eb5c1"). MustProxyDBus(&hst.BusConfig{ Talk: []string{ diff --git a/internal/outcome/spcontainer.go b/internal/outcome/spcontainer.go index 0ddc10ac..4ba3bb2d 100644 --- a/internal/outcome/spcontainer.go +++ b/internal/outcome/spcontainer.go @@ -39,7 +39,7 @@ type spParamsOp struct { func (s *spParamsOp) toSystem(state *outcomeStateSys) error { s.Term, s.TermSet = state.k.lookupEnv("TERM") - state.sys.Ensure(state.sc.SharePath, 0711) + state.sys.Ensure(state.sc.SharePath, 0711, false) return nil } @@ -351,7 +351,7 @@ func flattenExtraPerms(sys *system.I, extraPerms []hst.ExtraPermConfig) { } if p.Ensure { - sys.Ensure(p.Path, 0700) + sys.Ensure(p.Path, 0700, true) } var perms acl.Perm diff --git a/internal/outcome/spcontainer_test.go b/internal/outcome/spcontainer_test.go index d9436cb4..54c276f9 100644 --- a/internal/outcome/spcontainer_test.go +++ b/internal/outcome/spcontainer_test.go @@ -36,7 +36,7 @@ func TestSpParamsOp(t *testing.T) { }, nil, []stub.Call{ call("lookupEnv", stub.ExpectArgs{"TERM"}, "xterm", nil), }, newI(). - Ensure(m(container.Nonexistent+"/tmp/hakurei.0"), 0711), nil, nil, nil, []stub.Call{ + Ensure(m(container.Nonexistent+"/tmp/hakurei.0"), 0711, false), nil, nil, nil, []stub.Call{ // this op configures the container state and does not make calls during toContainer }, nil, nil, &hst.AppError{ Step: "finalise", @@ -57,7 +57,7 @@ func TestSpParamsOp(t *testing.T) { }, nil, []stub.Call{ call("lookupEnv", stub.ExpectArgs{"TERM"}, "xterm", nil), }, newI(). - Ensure(m(container.Nonexistent+"/tmp/hakurei.0"), 0711), nil, nil, nil, []stub.Call{ + Ensure(m(container.Nonexistent+"/tmp/hakurei.0"), 0711, false), nil, nil, nil, []stub.Call{ // this op configures the container state and does not make calls during toContainer }, &container.Params{ Hostname: config.Container.Hostname, @@ -94,7 +94,7 @@ func TestSpParamsOp(t *testing.T) { }, hst.Template, nil, []stub.Call{ call("lookupEnv", stub.ExpectArgs{"TERM"}, "xterm", nil), }, newI(). - Ensure(m(container.Nonexistent+"/tmp/hakurei.0"), 0711), nil, nil, nil, []stub.Call{ + Ensure(m(container.Nonexistent+"/tmp/hakurei.0"), 0711, false), nil, nil, nil, []stub.Call{ // this op configures the container state and does not make calls during toContainer }, &container.Params{ Hostname: config.Container.Hostname, @@ -309,7 +309,7 @@ func TestSpFilesystemOp(t *testing.T) { call("evalSymlinks", stub.ExpectArgs{"/var/lib/hakurei/base/org.nixos/org.chromium.Chromium"}, nePrefix+"/var/lib/hakurei/base/org.nixos/org.chromium.Chromium", nil), call("verbosef", stub.ExpectArgs{"hiding path %q from %q", []any{"/proc/nonexistent/eval/etc/dbus", "/etc/"}}, nil, nil), }, newI(). - Ensure(m("/var/lib/hakurei/u0"), 0700). + Ensure(m("/var/lib/hakurei/u0"), 0700, true). UpdatePermType(system.User, m("/var/lib/hakurei/u0"), acl.Execute). UpdatePermType(system.User, m("/var/lib/hakurei/u0/org.chromium.Chromium"), @@ -342,7 +342,7 @@ func TestSpFilesystemOp(t *testing.T) { call("evalSymlinks", stub.ExpectArgs{"/var/lib/hakurei/base/org.nixos/org.chromium.Chromium"}, nePrefix+"/var/lib/hakurei/base/org.nixos/org.chromium.Chromium", nil), call("verbosef", stub.ExpectArgs{"hiding path %q from %q", []any{"/proc/nonexistent/eval/etc/dbus", "/etc/"}}, nil, nil), }, newI(). - Ensure(m("/var/lib/hakurei/u0"), 0700). + Ensure(m("/var/lib/hakurei/u0"), 0700, true). UpdatePermType(system.User, m("/var/lib/hakurei/u0"), acl.Execute). UpdatePermType(system.User, m("/var/lib/hakurei/u0/org.chromium.Chromium"), @@ -397,7 +397,7 @@ func TestSpFilesystemOp(t *testing.T) { call("evalSymlinks", stub.ExpectArgs{"/var/lib/hakurei/base/org.debian/usr"}, nePrefix+"/var/lib/hakurei/base/org.debian/usr", nil), call("evalSymlinks", stub.ExpectArgs{"/var/lib/hakurei/base/org.debian/var"}, nePrefix+"/var/lib/hakurei/base/org.debian/var", nil), }, newI(). - Ensure(m("/var/lib/hakurei/u0"), 0700). + Ensure(m("/var/lib/hakurei/u0"), 0700, true). UpdatePermType(system.User, m("/var/lib/hakurei/u0"), acl.Execute). UpdatePermType(system.User, m("/var/lib/hakurei/u0/org.chromium.Chromium"), @@ -441,14 +441,14 @@ func TestFlattenExtraPerms(t *testing.T) { want *system.I }{ {"path nil check", append(hst.Template().ExtraPerms, hst.ExtraPermConfig{}), newI(). - Ensure(m("/var/lib/hakurei/u0"), 0700). + Ensure(m("/var/lib/hakurei/u0"), 0700, true). UpdatePermType(system.User, m("/var/lib/hakurei/u0"), acl.Execute). UpdatePermType(system.User, m("/var/lib/hakurei/u0/org.chromium.Chromium"), acl.Read|acl.Write|acl.Execute)}, {"template", hst.Template().ExtraPerms, newI(). - Ensure(m("/var/lib/hakurei/u0"), 0700). + Ensure(m("/var/lib/hakurei/u0"), 0700, true). UpdatePermType(system.User, m("/var/lib/hakurei/u0"), acl.Execute). UpdatePermType(system.User, m("/var/lib/hakurei/u0/org.chromium.Chromium"), diff --git a/internal/outcome/spdbus_test.go b/internal/outcome/spdbus_test.go index 0e4704a8..4720b5c8 100644 --- a/internal/outcome/spdbus_test.go +++ b/internal/outcome/spdbus_test.go @@ -81,7 +81,7 @@ func TestSpDBusOp(t *testing.T) { )}}, nil, nil), }, func() *system.I { sys := system.New(panicMsgContext{}, message.New(nil), checkExpectUid) - sys.Ephemeral(system.Process, m(wantInstancePrefix), 0711) + sys.Ephemeral(system.Process, m(wantInstancePrefix), 0711, false) if err := sys.ProxyDBus( dbus.NewConfig(config.ID, true, true), nil, dbus.ProxyPair{"unix:path=/run/user/1000/bus", wantInstancePrefix + "/bus"}, @@ -163,7 +163,7 @@ func TestSpDBusOp(t *testing.T) { )}}, nil, nil), }, func() *system.I { sys := system.New(panicMsgContext{}, message.New(nil), checkExpectUid) - sys.Ephemeral(system.Process, m(wantInstancePrefix), 0711) + sys.Ephemeral(system.Process, m(wantInstancePrefix), 0711, false) if err := sys.ProxyDBus( config.SessionBus, config.SystemBus, dbus.ProxyPair{"unix:path=/run/user/1000/bus", wantInstancePrefix + "/bus"}, diff --git a/internal/outcome/sppipewire_test.go b/internal/outcome/sppipewire_test.go index 1e3dab6a..804404dc 100644 --- a/internal/outcome/sppipewire_test.go +++ b/internal/outcome/sppipewire_test.go @@ -31,7 +31,7 @@ func TestSpPipeWireOp(t *testing.T) { return c }, nil, []stub.Call{}, newI(). // state.instance - Ephemeral(system.Process, m(wantInstancePrefix), 0711). + Ephemeral(system.Process, m(wantInstancePrefix), 0711, false). // toSystem PipeWire( m(wantInstancePrefix+"/pipewire"), diff --git a/internal/outcome/sppulse_test.go b/internal/outcome/sppulse_test.go index a1ff0d03..a9995562 100644 --- a/internal/outcome/sppulse_test.go +++ b/internal/outcome/sppulse_test.go @@ -143,12 +143,12 @@ func TestSpPulseOp(t *testing.T) { call("open", stub.ExpectArgs{"/proc/nonexistent/cookie"}, &stubOsFile{Reader: bytes.NewReader(sampleCookie)}, nil), }, newI(). // state.ensureRuntimeDir - Ensure(m(wantRuntimePath), 0700). + Ensure(m(wantRuntimePath), 0700, true). UpdatePermType(system.User, m(wantRuntimePath), acl.Execute). - Ensure(m(wantRunDirPath), 0700). + Ensure(m(wantRunDirPath), 0700, true). UpdatePermType(system.User, m(wantRunDirPath), acl.Execute). // state.runtime - Ephemeral(system.Process, m(wantRuntimeSharePath), 0700). + Ephemeral(system.Process, m(wantRuntimeSharePath), 0700, true). UpdatePerm(m(wantRuntimeSharePath), acl.Execute). // toSystem Link(m(wantRuntimePath+"/pulse/native"), m(wantRuntimeSharePath+"/pulse")), sysUsesRuntime(nil), nil, insertsOps(afterSpRuntimeOp(nil)), []stub.Call{ @@ -175,12 +175,12 @@ func TestSpPulseOp(t *testing.T) { call("open", stub.ExpectArgs{"/proc/nonexistent/cookie"}, &stubOsFile{Reader: bytes.NewReader(sampleCookie[:len(sampleCookie)-0xe])}, nil), }, newI(). // state.ensureRuntimeDir - Ensure(m(wantRuntimePath), 0700). + Ensure(m(wantRuntimePath), 0700, true). UpdatePermType(system.User, m(wantRuntimePath), acl.Execute). - Ensure(m(wantRunDirPath), 0700). + Ensure(m(wantRunDirPath), 0700, true). UpdatePermType(system.User, m(wantRunDirPath), acl.Execute). // state.runtime - Ephemeral(system.Process, m(wantRuntimeSharePath), 0700). + Ephemeral(system.Process, m(wantRuntimeSharePath), 0700, true). UpdatePerm(m(wantRuntimeSharePath), acl.Execute). // toSystem Link(m(wantRuntimePath+"/pulse/native"), m(wantRuntimeSharePath+"/pulse")), sysUsesRuntime(nil), nil, insertsOps(afterSpRuntimeOp(nil)), []stub.Call{ @@ -208,12 +208,12 @@ func TestSpPulseOp(t *testing.T) { call("open", stub.ExpectArgs{"/proc/nonexistent/cookie"}, &stubOsFile{Reader: bytes.NewReader(sampleCookie)}, nil), }, newI(). // state.ensureRuntimeDir - Ensure(m(wantRuntimePath), 0700). + Ensure(m(wantRuntimePath), 0700, true). UpdatePermType(system.User, m(wantRuntimePath), acl.Execute). - Ensure(m(wantRunDirPath), 0700). + Ensure(m(wantRunDirPath), 0700, true). UpdatePermType(system.User, m(wantRunDirPath), acl.Execute). // state.runtime - Ephemeral(system.Process, m(wantRuntimeSharePath), 0700). + Ephemeral(system.Process, m(wantRuntimeSharePath), 0700, true). UpdatePerm(m(wantRuntimeSharePath), acl.Execute). // toSystem Link(m(wantRuntimePath+"/pulse/native"), m(wantRuntimeSharePath+"/pulse")), sysUsesRuntime(nil), nil, insertsOps(afterSpRuntimeOp(nil)), []stub.Call{ @@ -238,12 +238,12 @@ func TestSpPulseOp(t *testing.T) { call("verbose", stub.ExpectArgs{[]any{"cannot locate PulseAudio cookie (tried $PULSE_COOKIE, $XDG_CONFIG_HOME/pulse/cookie, $HOME/.pulse-cookie)"}}, nil, nil), }, newI(). // state.ensureRuntimeDir - Ensure(m(wantRuntimePath), 0700). + Ensure(m(wantRuntimePath), 0700, true). UpdatePermType(system.User, m(wantRuntimePath), acl.Execute). - Ensure(m(wantRunDirPath), 0700). + Ensure(m(wantRunDirPath), 0700, true). UpdatePermType(system.User, m(wantRunDirPath), acl.Execute). // state.runtime - Ephemeral(system.Process, m(wantRuntimeSharePath), 0700). + Ephemeral(system.Process, m(wantRuntimeSharePath), 0700, true). UpdatePerm(m(wantRuntimeSharePath), acl.Execute). // toSystem Link(m(wantRuntimePath+"/pulse/native"), m(wantRuntimeSharePath+"/pulse")), sysUsesRuntime(nil), nil, insertsOps(afterSpRuntimeOp(nil)), []stub.Call{ diff --git a/internal/outcome/spruntime.go b/internal/outcome/spruntime.go index 9d36f252..55feac1d 100644 --- a/internal/outcome/spruntime.go +++ b/internal/outcome/spruntime.go @@ -76,9 +76,9 @@ type spRuntimeOp struct { func (s *spRuntimeOp) toSystem(state *outcomeStateSys) error { if state.Container.Flags&hst.FShareRuntime != 0 { runtimeDir, runtimeDirInst := s.commonPaths(state.outcomeState) - state.sys.Ensure(runtimeDir, 0700) + state.sys.Ensure(runtimeDir, 0700, true) state.sys.UpdatePermType(system.User, runtimeDir, acl.Execute) - state.sys.Ensure(runtimeDirInst, 0700) + state.sys.Ensure(runtimeDirInst, 0700, true) state.sys.UpdatePermType(system.User, runtimeDirInst, acl.Read|acl.Write|acl.Execute) } diff --git a/internal/outcome/spruntime_test.go b/internal/outcome/spruntime_test.go index 618fc7ac..20e55d63 100644 --- a/internal/outcome/spruntime_test.go +++ b/internal/outcome/spruntime_test.go @@ -33,9 +33,9 @@ func TestSpRuntimeOp(t *testing.T) { }, nil, []stub.Call{ // this op configures the system state and does not make calls during toSystem }, newI(). - Ensure(m("/proc/nonexistent/tmp/hakurei.0/runtime"), 0700). + Ensure(m("/proc/nonexistent/tmp/hakurei.0/runtime"), 0700, true). UpdatePermType(system.User, m("/proc/nonexistent/tmp/hakurei.0/runtime"), acl.Execute). - Ensure(m("/proc/nonexistent/tmp/hakurei.0/runtime/9"), 0700). + Ensure(m("/proc/nonexistent/tmp/hakurei.0/runtime/9"), 0700, true). UpdatePermType(system.User, m("/proc/nonexistent/tmp/hakurei.0/runtime/9"), acl.Read|acl.Write|acl.Execute), nil, nil, insertsOps(nil), []stub.Call{ // this op configures the container state and does not make calls during toContainer }, &container.Params{ @@ -61,9 +61,9 @@ func TestSpRuntimeOp(t *testing.T) { }, nil, []stub.Call{ // this op configures the system state and does not make calls during toSystem }, newI(). - Ensure(m("/proc/nonexistent/tmp/hakurei.0/runtime"), 0700). + Ensure(m("/proc/nonexistent/tmp/hakurei.0/runtime"), 0700, true). UpdatePermType(system.User, m("/proc/nonexistent/tmp/hakurei.0/runtime"), acl.Execute). - Ensure(m("/proc/nonexistent/tmp/hakurei.0/runtime/9"), 0700). + Ensure(m("/proc/nonexistent/tmp/hakurei.0/runtime/9"), 0700, true). UpdatePermType(system.User, m("/proc/nonexistent/tmp/hakurei.0/runtime/9"), acl.Read|acl.Write|acl.Execute), nil, nil, insertsOps(nil), []stub.Call{ // this op configures the container state and does not make calls during toContainer }, &container.Params{ @@ -89,9 +89,9 @@ func TestSpRuntimeOp(t *testing.T) { }, nil, []stub.Call{ // this op configures the system state and does not make calls during toSystem }, newI(). - Ensure(m("/proc/nonexistent/tmp/hakurei.0/runtime"), 0700). + Ensure(m("/proc/nonexistent/tmp/hakurei.0/runtime"), 0700, true). UpdatePermType(system.User, m("/proc/nonexistent/tmp/hakurei.0/runtime"), acl.Execute). - Ensure(m("/proc/nonexistent/tmp/hakurei.0/runtime/9"), 0700). + Ensure(m("/proc/nonexistent/tmp/hakurei.0/runtime/9"), 0700, true). UpdatePermType(system.User, m("/proc/nonexistent/tmp/hakurei.0/runtime/9"), acl.Read|acl.Write|acl.Execute), nil, nil, insertsOps(nil), []stub.Call{ // this op configures the container state and does not make calls during toContainer }, &container.Params{ @@ -113,9 +113,9 @@ func TestSpRuntimeOp(t *testing.T) { }, hst.Template, nil, []stub.Call{ // this op configures the system state and does not make calls during toSystem }, newI(). - Ensure(m("/proc/nonexistent/tmp/hakurei.0/runtime"), 0700). + Ensure(m("/proc/nonexistent/tmp/hakurei.0/runtime"), 0700, true). UpdatePermType(system.User, m("/proc/nonexistent/tmp/hakurei.0/runtime"), acl.Execute). - Ensure(m("/proc/nonexistent/tmp/hakurei.0/runtime/9"), 0700). + Ensure(m("/proc/nonexistent/tmp/hakurei.0/runtime/9"), 0700, true). UpdatePermType(system.User, m("/proc/nonexistent/tmp/hakurei.0/runtime/9"), acl.Read|acl.Write|acl.Execute), nil, nil, insertsOps(nil), []stub.Call{ // this op configures the container state and does not make calls during toContainer }, &container.Params{ diff --git a/internal/outcome/sptmpdir.go b/internal/outcome/sptmpdir.go index 3acb069f..9a637f8b 100644 --- a/internal/outcome/sptmpdir.go +++ b/internal/outcome/sptmpdir.go @@ -19,9 +19,9 @@ type spTmpdirOp struct{} func (s spTmpdirOp) toSystem(state *outcomeStateSys) error { if state.Container.Flags&hst.FShareTmpdir != 0 { tmpdir, tmpdirInst := s.commonPaths(state.outcomeState) - state.sys.Ensure(tmpdir, 0700) + state.sys.Ensure(tmpdir, 0700, true) state.sys.UpdatePermType(system.User, tmpdir, acl.Execute) - state.sys.Ensure(tmpdirInst, 01700) + state.sys.Ensure(tmpdirInst, 01700, true) state.sys.UpdatePermType(system.User, tmpdirInst, acl.Read|acl.Write|acl.Execute) } return nil diff --git a/internal/outcome/sptmpdir_test.go b/internal/outcome/sptmpdir_test.go index 2073ad81..421d65ab 100644 --- a/internal/outcome/sptmpdir_test.go +++ b/internal/outcome/sptmpdir_test.go @@ -21,9 +21,9 @@ func TestSpTmpdirOp(t *testing.T) { }, hst.Template, nil, []stub.Call{ // this op configures the system state and does not make calls during toSystem }, newI(). - Ensure(m("/proc/nonexistent/tmp/hakurei.0/tmpdir"), 0700). + Ensure(m("/proc/nonexistent/tmp/hakurei.0/tmpdir"), 0700, true). UpdatePermType(system.User, m("/proc/nonexistent/tmp/hakurei.0/tmpdir"), acl.Execute). - Ensure(m("/proc/nonexistent/tmp/hakurei.0/tmpdir/9"), 01700). + Ensure(m("/proc/nonexistent/tmp/hakurei.0/tmpdir/9"), 01700, true). UpdatePermType(system.User, m("/proc/nonexistent/tmp/hakurei.0/tmpdir/9"), acl.Read|acl.Write|acl.Execute), nil, nil, insertsOps(nil), []stub.Call{ // this op configures the container state and does not make calls during toContainer }, &container.Params{ diff --git a/internal/outcome/spwayland_test.go b/internal/outcome/spwayland_test.go index 9bb35512..eea23136 100644 --- a/internal/outcome/spwayland_test.go +++ b/internal/outcome/spwayland_test.go @@ -34,7 +34,7 @@ func TestSpWaylandOp(t *testing.T) { call("lookupEnv", stub.ExpectArgs{"WAYLAND_DISPLAY"}, "wayland-1", nil), }, newI(). // state.instance - Ephemeral(system.Process, m(wantInstancePrefix), 0711). + Ephemeral(system.Process, m(wantInstancePrefix), 0711, false). // toSystem Wayland( m(wantInstancePrefix+"/wayland"), @@ -64,9 +64,9 @@ func TestSpWaylandOp(t *testing.T) { call("verbose", stub.ExpectArgs{[]any{"direct wayland access, PROCEED WITH CAUTION"}}, nil, nil), }, newI(). // state.ensureRuntimeDir - Ensure(m(wantRuntimePath), 0700). + Ensure(m(wantRuntimePath), 0700, true). UpdatePermType(system.User, m(wantRuntimePath), acl.Execute). - Ensure(m(wantRunDirPath), 0700). + Ensure(m(wantRunDirPath), 0700, true). UpdatePermType(system.User, m(wantRunDirPath), acl.Execute). // toSystem UpdatePermType(hst.EWayland, m("/proc/nonexistent/wayland"), acl.Read|acl.Write|acl.Execute), nil, nil, insertsOps(afterSpRuntimeOp(nil)), []stub.Call{ @@ -85,7 +85,7 @@ func TestSpWaylandOp(t *testing.T) { call("verbose", stub.ExpectArgs{[]any{"WAYLAND_DISPLAY is not set, assuming wayland-0"}}, nil, nil), }, newI(). // state.instance - Ephemeral(system.Process, m(wantInstancePrefix), 0711). + Ephemeral(system.Process, m(wantInstancePrefix), 0711, false). // toSystem Wayland( m(wantInstancePrefix+"/wayland"), diff --git a/internal/system/dispatcher_test.go b/internal/system/dispatcher_test.go index 6ffd0dfa..dd42b965 100644 --- a/internal/system/dispatcher_test.go +++ b/internal/system/dispatcher_test.go @@ -2,11 +2,13 @@ package system import ( "io" + "io/fs" "log" "os" "reflect" "slices" "testing" + "time" "unsafe" "hakurei.app/check" @@ -215,6 +217,17 @@ type kstub struct{ *stub.Stub[syscallDispatcher] } func (k *kstub) new(f func(k syscallDispatcher)) { k.Helper(); k.New(f) } +// modeFi implements the Mode method of [os.FileInfo]. +type modeFi os.FileMode + +func (modeFi) Name() string { panic("attempting to call Name") } +func (modeFi) Size() int64 { panic("attempting to call Size") } +func (modeFi) ModTime() time.Time { panic("attempting to call ModTime") } +func (modeFi) Sys() any { panic("attempting to call Sys") } + +func (fi modeFi) Mode() fs.FileMode { return fs.FileMode(fi) } +func (fi modeFi) IsDir() bool { return fs.FileMode(fi).IsDir() } + func (k *kstub) stat(name string) (fi os.FileInfo, err error) { k.Helper() expect := k.Expects("stat") diff --git a/internal/system/mkdir.go b/internal/system/mkdir.go index 81b89d58..ecbfc876 100644 --- a/internal/system/mkdir.go +++ b/internal/system/mkdir.go @@ -4,21 +4,33 @@ import ( "errors" "fmt" "os" + "syscall" "hakurei.app/check" "hakurei.app/hst" ) // Ensure ensures the existence of a directory. -func (sys *I) Ensure(name *check.Absolute, perm os.FileMode) *I { - sys.ops = append(sys.ops, &mkdirOp{User, name.String(), perm, false}) +func (sys *I) Ensure(name *check.Absolute, perm os.FileMode, skipChmod bool) *I { + sys.ops = append(sys.ops, &mkdirOp{ + et: User, + path: name.String(), + perm: perm, + skipChmod: skipChmod, + }) return sys } // Ephemeral ensures the existence of a directory until its [hst.Enablements] is // no longer satisfied. -func (sys *I) Ephemeral(et hst.Enablements, name *check.Absolute, perm os.FileMode) *I { - sys.ops = append(sys.ops, &mkdirOp{et, name.String(), perm, true}) +func (sys *I) Ephemeral(et hst.Enablements, name *check.Absolute, perm os.FileMode, skipChmod bool) *I { + sys.ops = append(sys.ops, &mkdirOp{ + et: et, + path: name.String(), + perm: perm, + ephemeral: true, + skipChmod: skipChmod, + }) return sys } @@ -28,6 +40,7 @@ type mkdirOp struct { path string perm os.FileMode ephemeral bool + skipChmod bool } func (m *mkdirOp) Type() hst.Enablements { return m.et } @@ -35,12 +48,21 @@ func (m *mkdirOp) Type() hst.Enablements { return m.et } func (m *mkdirOp) apply(sys *I) error { sys.msg.Verbose("ensuring directory", m) - if err := sys.mkdir(m.path, m.perm); err != nil { - if !errors.Is(err, os.ErrExist) { - return newOpError("mkdir", err, false) + if fi, err := sys.stat(m.path); err == nil { + if !fi.IsDir() { + return newOpError("mkdir", syscall.ENOTDIR, false) + } + if !m.skipChmod && fi.Mode().Perm() != m.perm { + // directory exists, ensure mode + return newOpError("mkdir", sys.chmod(m.path, m.perm), false) } - // directory exists, ensure mode - return newOpError("mkdir", sys.chmod(m.path, m.perm), false) + return nil + } else if !errors.Is(err, os.ErrNotExist) { + return newOpError("mkdir", err, false) + } + + if err := sys.mkdir(m.path, m.perm); err != nil { + return newOpError("mkdir", err, false) } else { return nil } diff --git a/internal/system/mkdir_test.go b/internal/system/mkdir_test.go index f226a48d..ea79d5b0 100644 --- a/internal/system/mkdir_test.go +++ b/internal/system/mkdir_test.go @@ -2,6 +2,7 @@ package system import ( "os" + "syscall" "testing" "hakurei.app/internal/stub" @@ -11,63 +12,78 @@ func TestMkdirOp(t *testing.T) { t.Parallel() checkOpBehaviour(t, 0, []opBehaviourTestCase{ - {"mkdir", 0xbeef, 0xff, &mkdirOp{User, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, false}, []stub.Call{ - call("verbose", stub.ExpectArgs{[]any{"ensuring directory", &mkdirOp{User, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, false}}}, nil, nil), + {"mkdir", 0xbeef, 0xff, &mkdirOp{User, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, false, false}, []stub.Call{ + call("verbose", stub.ExpectArgs{[]any{"ensuring directory", &mkdirOp{User, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, false, false}}}, nil, nil), + call("stat", stub.ExpectArgs{"/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9"}, nil, os.ErrNotExist), call("mkdir", stub.ExpectArgs{"/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", os.FileMode(0711)}, nil, stub.UniqueError(2)), }, &OpError{Op: "mkdir", Err: stub.UniqueError(2)}, nil, nil}, - {"chmod", 0xbeef, 0xff, &mkdirOp{User, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, false}, []stub.Call{ - call("verbose", stub.ExpectArgs{[]any{"ensuring directory", &mkdirOp{User, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, false}}}, nil, nil), - call("mkdir", stub.ExpectArgs{"/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", os.FileMode(0711)}, nil, os.ErrExist), + {"not directory", 0xbeef, 0xff, &mkdirOp{User, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, false, false}, []stub.Call{ + call("verbose", stub.ExpectArgs{[]any{"ensuring directory", &mkdirOp{User, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, false, false}}}, nil, nil), + call("stat", stub.ExpectArgs{"/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9"}, modeFi(0700), nil), + }, &OpError{Op: "mkdir", Err: syscall.ENOTDIR}, nil, nil}, + + {"chmod", 0xbeef, 0xff, &mkdirOp{User, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, false, false}, []stub.Call{ + call("verbose", stub.ExpectArgs{[]any{"ensuring directory", &mkdirOp{User, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, false, false}}}, nil, nil), + call("stat", stub.ExpectArgs{"/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9"}, modeFi(os.ModeDir|0700), nil), call("chmod", stub.ExpectArgs{"/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", os.FileMode(0711)}, nil, stub.UniqueError(1)), }, &OpError{Op: "mkdir", Err: stub.UniqueError(1)}, nil, nil}, - {"remove", 0xbeef, 0xff, &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, true}, []stub.Call{ - call("verbose", stub.ExpectArgs{[]any{"ensuring directory", &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, true}}}, nil, nil), + {"remove", 0xbeef, 0xff, &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, true, false}, []stub.Call{ + call("verbose", stub.ExpectArgs{[]any{"ensuring directory", &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, true, false}}}, nil, nil), + call("stat", stub.ExpectArgs{"/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9"}, nil, os.ErrNotExist), call("mkdir", stub.ExpectArgs{"/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", os.FileMode(0711)}, nil, nil), }, nil, []stub.Call{ - call("verbose", stub.ExpectArgs{[]any{"destroying ephemeral directory", &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, true}}}, nil, nil), + call("verbose", stub.ExpectArgs{[]any{"destroying ephemeral directory", &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, true, false}}}, nil, nil), call("remove", stub.ExpectArgs{"/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9"}, nil, stub.UniqueError(0)), }, &OpError{Op: "mkdir", Err: stub.UniqueError(0), Revert: true}}, - {"success exist chmod", 0xbeef, 0xff, &mkdirOp{User, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, false}, []stub.Call{ - call("verbose", stub.ExpectArgs{[]any{"ensuring directory", &mkdirOp{User, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, false}}}, nil, nil), - call("mkdir", stub.ExpectArgs{"/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", os.FileMode(0711)}, nil, os.ErrExist), + {"success exist chmod", 0xbeef, 0xff, &mkdirOp{User, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, false, false}, []stub.Call{ + call("verbose", stub.ExpectArgs{[]any{"ensuring directory", &mkdirOp{User, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, false, false}}}, nil, nil), + call("stat", stub.ExpectArgs{"/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9"}, modeFi(os.ModeDir|0700), nil), call("chmod", stub.ExpectArgs{"/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", os.FileMode(0711)}, nil, nil), }, nil, nil, nil}, - {"success ensure", 0xbeef, 0xff, &mkdirOp{User, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, false}, []stub.Call{ - call("verbose", stub.ExpectArgs{[]any{"ensuring directory", &mkdirOp{User, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, false}}}, nil, nil), + {"success exist", 0xbeef, 0xff, &mkdirOp{User, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, false, false}, []stub.Call{ + call("verbose", stub.ExpectArgs{[]any{"ensuring directory", &mkdirOp{User, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, false, false}}}, nil, nil), + call("stat", stub.ExpectArgs{"/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9"}, modeFi(os.ModeDir|0711), nil), + }, nil, nil, nil}, + + {"success ensure", 0xbeef, 0xff, &mkdirOp{User, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, false, false}, []stub.Call{ + call("verbose", stub.ExpectArgs{[]any{"ensuring directory", &mkdirOp{User, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, false, false}}}, nil, nil), + call("stat", stub.ExpectArgs{"/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9"}, nil, os.ErrNotExist), call("mkdir", stub.ExpectArgs{"/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", os.FileMode(0711)}, nil, nil), }, nil, nil, nil}, - {"success skip", 0xbeef, 0xff, &mkdirOp{User, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, true}, []stub.Call{ - call("verbose", stub.ExpectArgs{[]any{"ensuring directory", &mkdirOp{User, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, true}}}, nil, nil), + {"success skip", 0xbeef, 0xff, &mkdirOp{User, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, true, false}, []stub.Call{ + call("verbose", stub.ExpectArgs{[]any{"ensuring directory", &mkdirOp{User, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, true, false}}}, nil, nil), + call("stat", stub.ExpectArgs{"/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9"}, nil, os.ErrNotExist), call("mkdir", stub.ExpectArgs{"/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", os.FileMode(0711)}, nil, nil), }, nil, []stub.Call{ - call("verbose", stub.ExpectArgs{[]any{"skipping ephemeral directory", &mkdirOp{User, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, true}}}, nil, nil), + call("verbose", stub.ExpectArgs{[]any{"skipping ephemeral directory", &mkdirOp{User, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, true, false}}}, nil, nil), }, nil}, - {"success", 0xbeef, 0xff, &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, true}, []stub.Call{ - call("verbose", stub.ExpectArgs{[]any{"ensuring directory", &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, true}}}, nil, nil), + {"success", 0xbeef, 0xff, &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, true, false}, []stub.Call{ + call("verbose", stub.ExpectArgs{[]any{"ensuring directory", &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, true, false}}}, nil, nil), + call("stat", stub.ExpectArgs{"/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9"}, nil, os.ErrNotExist), call("mkdir", stub.ExpectArgs{"/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", os.FileMode(0711)}, nil, nil), }, nil, []stub.Call{ - call("verbose", stub.ExpectArgs{[]any{"destroying ephemeral directory", &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, true}}}, nil, nil), + call("verbose", stub.ExpectArgs{[]any{"destroying ephemeral directory", &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, true, false}}}, nil, nil), call("remove", stub.ExpectArgs{"/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9"}, nil, nil), }, nil}, }) checkOpsBuilder(t, "EnsureEphemeral", []opsBuilderTestCase{ {"ensure", 0xcafe, func(_ *testing.T, sys *I) { - sys.Ensure(m("/tmp/hakurei.0"), 0700) + sys.Ensure(m("/tmp/hakurei.0"), 0700, false) }, []Op{ - &mkdirOp{User, "/tmp/hakurei.0", 0700, false}, + &mkdirOp{User, "/tmp/hakurei.0", 0700, false, false}, }, stub.Expect{}}, {"ephemeral", 0xcafe, func(_ *testing.T, sys *I) { - sys.Ephemeral(Process, m("/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9"), 0711) + sys.Ephemeral(Process, m("/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9"), 0711, false) }, []Op{ - &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, true}, + &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, true, false}, }, stub.Expect{}}, }) @@ -76,37 +92,37 @@ func TestMkdirOp(t *testing.T) { {"zero", new(mkdirOp), new(mkdirOp), true}, {"ephemeral differs", - &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0700, false}, - &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0700, true}, + &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0700, false, false}, + &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0700, true, false}, false}, {"perm differs", - &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0701, true}, - &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0700, true}, + &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0701, true, false}, + &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0700, true, false}, false}, {"path differs", - &mkdirOp{Process, "/tmp/hakurei.1/f2f3bcd492d0266438fa9bf164fe90d9", 0700, true}, - &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0700, true}, + &mkdirOp{Process, "/tmp/hakurei.1/f2f3bcd492d0266438fa9bf164fe90d9", 0700, true, false}, + &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0700, true, false}, false}, {"et differs", - &mkdirOp{User, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0700, true}, - &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0700, true}, + &mkdirOp{User, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0700, true, false}, + &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0700, true, false}, false}, {"equals", - &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0700, true}, - &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0700, true}, + &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0700, true, false}, + &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0700, true, false}, true}, }) checkOpMeta(t, []opMetaTestCase{ - {"ensure", &mkdirOp{User, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0700, false}, + {"ensure", &mkdirOp{User, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0700, false, false}, User, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", `mode: -rwx------ type: ensure path: "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9"`}, - {"ephemeral", &mkdirOp{User, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0700, true}, + {"ephemeral", &mkdirOp{User, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0700, true, false}, User, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", `mode: -rwx------ type: user path: "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9"`}, }) diff --git a/internal/system/system_test.go b/internal/system/system_test.go index b5dec992..e466c171 100644 --- a/internal/system/system_test.go +++ b/internal/system/system_test.go @@ -144,16 +144,16 @@ func TestEqual(t *testing.T) { ChangeHosts("chronos"), New(t.Context(), message.New(nil), 150). ChangeHosts("chronos"). - Ensure(m("/run"), 0755), + Ensure(m("/run"), 0755, false), false}, {"op value mismatch", New(t.Context(), message.New(nil), 150). ChangeHosts("chronos"). - Ensure(m("/run"), 0644), + Ensure(m("/run"), 0644, false), New(t.Context(), message.New(nil), 150). ChangeHosts("chronos"). - Ensure(m("/run"), 0755), + Ensure(m("/run"), 0755, false), false}, {"op type mismatch", @@ -162,16 +162,16 @@ func TestEqual(t *testing.T) { Wayland(m("/proc/nonexistent/dst"), m("/proc/nonexistent/src"), "\x00", "\x00"), New(t.Context(), message.New(nil), 150). ChangeHosts("chronos"). - Ensure(m("/run"), 0755), + Ensure(m("/run"), 0755, false), false}, {"op equals", New(t.Context(), message.New(nil), 150). ChangeHosts("chronos"). - Ensure(m("/run"), 0755), + Ensure(m("/run"), 0755, false), New(t.Context(), message.New(nil), 150). ChangeHosts("chronos"). - Ensure(m("/run"), 0755), + Ensure(m("/run"), 0755, false), true}, } @@ -200,46 +200,49 @@ func TestCommitRevert(t *testing.T) { }{ {"apply xhost partial mkdir", func(sys *I) { sys. - Ephemeral(Process, m("/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9"), 0711). + Ephemeral(Process, m("/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9"), 0711, false). ChangeHosts("chronos") }, 0xff, []stub.Call{ - call("verbose", stub.ExpectArgs{[]any{"ensuring directory", &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, true}}}, nil, nil), + call("verbose", stub.ExpectArgs{[]any{"ensuring directory", &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, true, false}}}, nil, nil), + call("stat", stub.ExpectArgs{"/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9"}, nil, os.ErrNotExist), call("mkdir", stub.ExpectArgs{"/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", os.FileMode(0711)}, nil, nil), call("verbosef", stub.ExpectArgs{"inserting entry %s to X11", []any{xhostOp("chronos")}}, nil, nil), call("xcbChangeHosts", stub.ExpectArgs{xcb.HostMode(xcb.HostModeInsert), xcb.Family(xcb.FamilyServerInterpreted), "localuser\x00chronos"}, nil, stub.UniqueError(2)), call("verbosef", stub.ExpectArgs{"commit faulted after %d ops, rolling back partial commit", []any{1}}, nil, nil), - call("verbose", stub.ExpectArgs{[]any{"destroying ephemeral directory", &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, true}}}, nil, nil), + call("verbose", stub.ExpectArgs{[]any{"destroying ephemeral directory", &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, true, false}}}, nil, nil), call("remove", stub.ExpectArgs{"/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9"}, nil, stub.UniqueError(3)), call("println", stub.ExpectArgs{[]any{"cannot revert mkdir: unique error 3 injected by the test suite"}}, nil, nil), }, &OpError{Op: "xhost", Err: stub.UniqueError(2)}, nil, nil}, {"apply xhost", func(sys *I) { sys. - Ephemeral(Process, m("/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9"), 0711). + Ephemeral(Process, m("/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9"), 0711, false). ChangeHosts("chronos") }, 0xff, []stub.Call{ - call("verbose", stub.ExpectArgs{[]any{"ensuring directory", &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, true}}}, nil, nil), + call("verbose", stub.ExpectArgs{[]any{"ensuring directory", &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, true, false}}}, nil, nil), + call("stat", stub.ExpectArgs{"/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9"}, nil, os.ErrNotExist), call("mkdir", stub.ExpectArgs{"/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", os.FileMode(0711)}, nil, nil), call("verbosef", stub.ExpectArgs{"inserting entry %s to X11", []any{xhostOp("chronos")}}, nil, nil), call("xcbChangeHosts", stub.ExpectArgs{xcb.HostMode(xcb.HostModeInsert), xcb.Family(xcb.FamilyServerInterpreted), "localuser\x00chronos"}, nil, stub.UniqueError(2)), call("verbosef", stub.ExpectArgs{"commit faulted after %d ops, rolling back partial commit", []any{1}}, nil, nil), - call("verbose", stub.ExpectArgs{[]any{"destroying ephemeral directory", &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, true}}}, nil, nil), + call("verbose", stub.ExpectArgs{[]any{"destroying ephemeral directory", &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, true, false}}}, nil, nil), call("remove", stub.ExpectArgs{"/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9"}, nil, nil), }, &OpError{Op: "xhost", Err: stub.UniqueError(2)}, nil, nil}, {"revert multi", func(sys *I) { sys. - Ephemeral(Process, m("/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9"), 0711). + Ephemeral(Process, m("/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9"), 0711, false). ChangeHosts("chronos") }, 0xff, []stub.Call{ - call("verbose", stub.ExpectArgs{[]any{"ensuring directory", &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, true}}}, nil, nil), + call("verbose", stub.ExpectArgs{[]any{"ensuring directory", &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, true, false}}}, nil, nil), + call("stat", stub.ExpectArgs{"/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9"}, nil, os.ErrNotExist), call("mkdir", stub.ExpectArgs{"/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", os.FileMode(0711)}, nil, nil), call("verbosef", stub.ExpectArgs{"inserting entry %s to X11", []any{xhostOp("chronos")}}, nil, nil), call("xcbChangeHosts", stub.ExpectArgs{xcb.HostMode(xcb.HostModeInsert), xcb.Family(xcb.FamilyServerInterpreted), "localuser\x00chronos"}, nil, nil), }, nil, []stub.Call{ call("verbosef", stub.ExpectArgs{"deleting entry %s from X11", []any{xhostOp("chronos")}}, nil, nil), call("xcbChangeHosts", stub.ExpectArgs{xcb.HostMode(xcb.HostModeDelete), xcb.Family(xcb.FamilyServerInterpreted), "localuser\x00chronos"}, nil, stub.UniqueError(1)), - call("verbose", stub.ExpectArgs{[]any{"destroying ephemeral directory", &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, true}}}, nil, nil), + call("verbose", stub.ExpectArgs{[]any{"destroying ephemeral directory", &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, true, false}}}, nil, nil), call("remove", stub.ExpectArgs{"/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9"}, nil, stub.UniqueError(0)), }, errors.Join( &OpError{Op: "xhost", Err: stub.UniqueError(1), Revert: true}, @@ -247,17 +250,18 @@ func TestCommitRevert(t *testing.T) { {"success", func(sys *I) { sys. - Ephemeral(Process, m("/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9"), 0711). + Ephemeral(Process, m("/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9"), 0711, false). ChangeHosts("chronos") }, 0xff, []stub.Call{ - call("verbose", stub.ExpectArgs{[]any{"ensuring directory", &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, true}}}, nil, nil), + call("verbose", stub.ExpectArgs{[]any{"ensuring directory", &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, true, false}}}, nil, nil), + call("stat", stub.ExpectArgs{"/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9"}, nil, os.ErrNotExist), call("mkdir", stub.ExpectArgs{"/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", os.FileMode(0711)}, nil, nil), call("verbosef", stub.ExpectArgs{"inserting entry %s to X11", []any{xhostOp("chronos")}}, nil, nil), call("xcbChangeHosts", stub.ExpectArgs{xcb.HostMode(xcb.HostModeInsert), xcb.Family(xcb.FamilyServerInterpreted), "localuser\x00chronos"}, nil, nil), }, nil, []stub.Call{ call("verbosef", stub.ExpectArgs{"deleting entry %s from X11", []any{xhostOp("chronos")}}, nil, nil), call("xcbChangeHosts", stub.ExpectArgs{xcb.HostMode(xcb.HostModeDelete), xcb.Family(xcb.FamilyServerInterpreted), "localuser\x00chronos"}, nil, nil), - call("verbose", stub.ExpectArgs{[]any{"destroying ephemeral directory", &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, true}}}, nil, nil), + call("verbose", stub.ExpectArgs{[]any{"destroying ephemeral directory", &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, true, false}}}, nil, nil), call("remove", stub.ExpectArgs{"/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9"}, nil, nil), }, nil}, } -- cgit v1.3.1