aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2026-10-13 22:58:19 +0900
committerOphestra <cat@gensokyo.uk>2026-10-13 22:58:19 +0900
commitfadb42ec8daeeb8a576582d274cd4aeb64e535c0 (patch)
tree20db92adde637b3d1bc7a931d0551590e80456cf /internal
parent40e4c9031652731622315e778a9bcace6cea6cb3 (diff)
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 <cat@gensokyo.uk>
Diffstat (limited to 'internal')
-rw-r--r--internal/outcome/outcome.go8
-rw-r--r--internal/outcome/run_test.go52
-rw-r--r--internal/outcome/spcontainer.go4
-rw-r--r--internal/outcome/spcontainer_test.go16
-rw-r--r--internal/outcome/spdbus_test.go4
-rw-r--r--internal/outcome/sppipewire_test.go2
-rw-r--r--internal/outcome/sppulse_test.go24
-rw-r--r--internal/outcome/spruntime.go4
-rw-r--r--internal/outcome/spruntime_test.go16
-rw-r--r--internal/outcome/sptmpdir.go4
-rw-r--r--internal/outcome/sptmpdir_test.go4
-rw-r--r--internal/outcome/spwayland_test.go8
-rw-r--r--internal/system/dispatcher_test.go13
-rw-r--r--internal/system/mkdir.go40
-rw-r--r--internal/system/mkdir_test.go86
-rw-r--r--internal/system/system_test.go40
16 files changed, 190 insertions, 135 deletions
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},
}