From d16da6da8c4880d90dc35fa9006806f7c0395d7f Mon Sep 17 00:00:00 2001 From: Ophestra Date: Fri, 3 Oct 2025 02:26:14 +0900 Subject: [PATCH] system: enforce absolute paths This is less error-prone, and is quite easy to integrate considering internal/app has already migrated to container.Absolute. Closes #11. Signed-off-by: Ophestra --- internal/app/app_test.go | 81 ++++++++++++++++++++-------------------- internal/app/finalise.go | 52 +++++++++++++------------- system/acl.go | 7 ++-- system/acl_test.go | 16 ++++---- system/dbus.go | 6 +-- system/dbus_test.go | 14 +++---- system/link.go | 9 +++-- system/link_test.go | 4 +- system/mkdir.go | 9 +++-- system/mkdir_test.go | 4 +- system/system_test.go | 24 ++++++------ system/tmpfiles.go | 5 ++- system/tmpfiles_test.go | 2 +- system/wayland.go | 5 ++- system/wayland_test.go | 4 +- 15 files changed, 125 insertions(+), 117 deletions(-) diff --git a/internal/app/app_test.go b/internal/app/app_test.go index 585f95b..5c8e9e7 100644 --- a/internal/app/app_test.go +++ b/internal/app/app_test.go @@ -1,7 +1,6 @@ package app import ( - "context" "encoding/json" "io/fs" "os" @@ -37,12 +36,12 @@ func TestApp(t *testing.T) { 0xbd, 0x01, 0x78, 0x0e, 0xb9, 0xa6, 0x07, 0xac, }, - system.New(context.TODO(), container.NewMsg(nil), 1000000). - Ensure("/tmp/hakurei.0", 0711). - Ensure("/tmp/hakurei.0/runtime", 0700).UpdatePermType(system.User, "/tmp/hakurei.0/runtime", acl.Execute). - Ensure("/tmp/hakurei.0/runtime/0", 0700).UpdatePermType(system.User, "/tmp/hakurei.0/runtime/0", acl.Read, acl.Write, acl.Execute). - Ensure("/tmp/hakurei.0/tmpdir", 0700).UpdatePermType(system.User, "/tmp/hakurei.0/tmpdir", acl.Execute). - Ensure("/tmp/hakurei.0/tmpdir/0", 01700).UpdatePermType(system.User, "/tmp/hakurei.0/tmpdir/0", acl.Read, acl.Write, acl.Execute), + system.New(t.Context(), container.NewMsg(nil), 1000000). + 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/0"), 0700).UpdatePermType(system.User, m("/tmp/hakurei.0/runtime/0"), 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/0"), 01700).UpdatePermType(system.User, m("/tmp/hakurei.0/tmpdir/0"), acl.Read, acl.Write, acl.Execute), &container.Params{ Dir: m("/home/chronos"), Path: m("/run/current-system/sw/bin/zsh"), @@ -129,20 +128,20 @@ func TestApp(t *testing.T) { 0x82, 0xd4, 0x13, 0x36, 0x9b, 0x64, 0xce, 0x7c, }, - system.New(context.TODO(), container.NewMsg(nil), 1000009). - Ensure("/tmp/hakurei.0", 0711). - Ensure("/tmp/hakurei.0/runtime", 0700).UpdatePermType(system.User, "/tmp/hakurei.0/runtime", acl.Execute). - Ensure("/tmp/hakurei.0/runtime/9", 0700).UpdatePermType(system.User, "/tmp/hakurei.0/runtime/9", acl.Read, acl.Write, acl.Execute). - Ensure("/tmp/hakurei.0/tmpdir", 0700).UpdatePermType(system.User, "/tmp/hakurei.0/tmpdir", acl.Execute). - Ensure("/tmp/hakurei.0/tmpdir/9", 01700).UpdatePermType(system.User, "/tmp/hakurei.0/tmpdir/9", acl.Read, acl.Write, acl.Execute). - Ephemeral(system.Process, "/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c", 0711). - Wayland(new(*os.File), "/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/wayland", "/run/user/1971/wayland-0", "org.chromium.Chromium", "ebf083d1b175911782d413369b64ce7c"). - Ensure("/run/user/1971/hakurei", 0700).UpdatePermType(system.User, "/run/user/1971/hakurei", acl.Execute). - Ensure("/run/user/1971", 0700).UpdatePermType(system.User, "/run/user/1971", acl.Execute). // this is ordered as is because the previous Ensure only calls mkdir if XDG_RUNTIME_DIR is unset - Ephemeral(system.Process, "/run/user/1971/hakurei/ebf083d1b175911782d413369b64ce7c", 0700).UpdatePermType(system.Process, "/run/user/1971/hakurei/ebf083d1b175911782d413369b64ce7c", acl.Execute). - Link("/run/user/1971/pulse/native", "/run/user/1971/hakurei/ebf083d1b175911782d413369b64ce7c/pulse"). - CopyFile(new([]byte), "/home/ophestra/xdg/config/pulse/cookie", 256, 256). - MustProxyDBus("/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/bus", &dbus.Config{ + system.New(t.Context(), container.NewMsg(nil), 1000009). + 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). + Wayland(new(*os.File), m("/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/wayland"), m("/run/user/1971/wayland-0"), "org.chromium.Chromium", "ebf083d1b175911782d413369b64ce7c"). + Ensure(m("/run/user/1971/hakurei"), 0700).UpdatePermType(system.User, m("/run/user/1971/hakurei"), 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 + Ephemeral(system.Process, m("/run/user/1971/hakurei/ebf083d1b175911782d413369b64ce7c"), 0700).UpdatePermType(system.Process, m("/run/user/1971/hakurei/ebf083d1b175911782d413369b64ce7c"), acl.Execute). + Link(m("/run/user/1971/pulse/native"), m("/run/user/1971/hakurei/ebf083d1b175911782d413369b64ce7c/pulse")). + CopyFile(new([]byte), m("/home/ophestra/xdg/config/pulse/cookie"), 256, 256). + MustProxyDBus(m("/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/bus"), &dbus.Config{ Talk: []string{ "org.freedesktop.Notifications", "org.freedesktop.FileManager1", @@ -164,7 +163,7 @@ func TestApp(t *testing.T) { "org.freedesktop.portal.*": "@/org/freedesktop/portal/*", }, Filter: true, - }, "/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/system_bus_socket", &dbus.Config{ + }, m("/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/system_bus_socket"), &dbus.Config{ Talk: []string{ "org.bluez", "org.freedesktop.Avahi", @@ -172,8 +171,8 @@ func TestApp(t *testing.T) { }, Filter: true, }). - UpdatePerm("/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/bus", acl.Read, acl.Write). - UpdatePerm("/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/system_bus_socket", acl.Read, acl.Write), + UpdatePerm(m("/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/bus"), acl.Read, acl.Write). + UpdatePerm(m("/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/system_bus_socket"), acl.Read, acl.Write), &container.Params{ Dir: m("/home/chronos"), Path: m("/run/current-system/sw/bin/zsh"), @@ -280,20 +279,20 @@ func TestApp(t *testing.T) { 0x4c, 0xf0, 0x73, 0xbd, 0xb4, 0x6e, 0xb5, 0xc1, }, - system.New(context.TODO(), container.NewMsg(nil), 1000001). - Ensure("/tmp/hakurei.0", 0711). - Ensure("/tmp/hakurei.0/runtime", 0700).UpdatePermType(system.User, "/tmp/hakurei.0/runtime", acl.Execute). - Ensure("/tmp/hakurei.0/runtime/1", 0700).UpdatePermType(system.User, "/tmp/hakurei.0/runtime/1", acl.Read, acl.Write, acl.Execute). - Ensure("/tmp/hakurei.0/tmpdir", 0700).UpdatePermType(system.User, "/tmp/hakurei.0/tmpdir", acl.Execute). - Ensure("/tmp/hakurei.0/tmpdir/1", 01700).UpdatePermType(system.User, "/tmp/hakurei.0/tmpdir/1", acl.Read, acl.Write, acl.Execute). - Ensure("/run/user/1971/hakurei", 0700).UpdatePermType(system.User, "/run/user/1971/hakurei", acl.Execute). - Ensure("/run/user/1971", 0700).UpdatePermType(system.User, "/run/user/1971", acl.Execute). // this is ordered as is because the previous Ensure only calls mkdir if XDG_RUNTIME_DIR is unset - UpdatePermType(hst.EWayland, "/run/user/1971/wayland-0", acl.Read, acl.Write, acl.Execute). - Ephemeral(system.Process, "/run/user/1971/hakurei/8e2c76b066dabe574cf073bdb46eb5c1", 0700).UpdatePermType(system.Process, "/run/user/1971/hakurei/8e2c76b066dabe574cf073bdb46eb5c1", acl.Execute). - Link("/run/user/1971/pulse/native", "/run/user/1971/hakurei/8e2c76b066dabe574cf073bdb46eb5c1/pulse"). - CopyFile(nil, "/home/ophestra/xdg/config/pulse/cookie", 256, 256). - Ephemeral(system.Process, "/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1", 0711). - MustProxyDBus("/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1/bus", &dbus.Config{ + system.New(t.Context(), container.NewMsg(nil), 1000001). + 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/hakurei"), 0700).UpdatePermType(system.User, m("/run/user/1971/hakurei"), 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 + UpdatePermType(hst.EWayland, m("/run/user/1971/wayland-0"), acl.Read, acl.Write, acl.Execute). + Ephemeral(system.Process, m("/run/user/1971/hakurei/8e2c76b066dabe574cf073bdb46eb5c1"), 0700).UpdatePermType(system.Process, m("/run/user/1971/hakurei/8e2c76b066dabe574cf073bdb46eb5c1"), acl.Execute). + Link(m("/run/user/1971/pulse/native"), m("/run/user/1971/hakurei/8e2c76b066dabe574cf073bdb46eb5c1/pulse")). + CopyFile(nil, m("/home/ophestra/xdg/config/pulse/cookie"), 256, 256). + Ephemeral(system.Process, m("/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1"), 0711). + MustProxyDBus(m("/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1/bus"), &dbus.Config{ Talk: []string{ "org.freedesktop.FileManager1", "org.freedesktop.Notifications", "org.freedesktop.ScreenSaver", "org.freedesktop.secrets", @@ -306,7 +305,7 @@ func TestApp(t *testing.T) { }, Call: map[string]string{}, Broadcast: map[string]string{}, Filter: true, - }, "/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1/system_bus_socket", &dbus.Config{ + }, m("/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1/system_bus_socket"), &dbus.Config{ Talk: []string{ "org.bluez", "org.freedesktop.Avahi", @@ -314,8 +313,8 @@ func TestApp(t *testing.T) { }, Filter: true, }). - UpdatePerm("/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1/bus", acl.Read, acl.Write). - UpdatePerm("/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1/system_bus_socket", acl.Read, acl.Write), + UpdatePerm(m("/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1/bus"), acl.Read, acl.Write). + UpdatePerm(m("/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1/system_bus_socket"), acl.Read, acl.Write), &container.Params{ Uid: 1971, Gid: 100, diff --git a/internal/app/finalise.go b/internal/app/finalise.go index fbe4bb3..a33bcc9 100644 --- a/internal/app/finalise.go +++ b/internal/app/finalise.go @@ -76,10 +76,10 @@ func (share *shareHost) ensureRuntimeDir() { return } share.useRuntimeDir = true - share.seal.sys.Ensure(share.sc.RunDirPath.String(), 0700) - share.seal.sys.UpdatePermType(system.User, share.sc.RunDirPath.String(), acl.Execute) - share.seal.sys.Ensure(share.sc.RuntimePath.String(), 0700) // ensure this dir in case XDG_RUNTIME_DIR is unset - share.seal.sys.UpdatePermType(system.User, share.sc.RuntimePath.String(), acl.Execute) + share.seal.sys.Ensure(share.sc.RunDirPath, 0700) + share.seal.sys.UpdatePermType(system.User, share.sc.RunDirPath, acl.Execute) + share.seal.sys.Ensure(share.sc.RuntimePath, 0700) // ensure this dir in case XDG_RUNTIME_DIR is unset + share.seal.sys.UpdatePermType(system.User, share.sc.RuntimePath, acl.Execute) } // instance returns a process-specific share path within tmpdir @@ -88,7 +88,7 @@ func (share *shareHost) instance() *container.Absolute { return share.sharePath } share.sharePath = share.sc.SharePath.Append(share.seal.id.String()) - share.seal.sys.Ephemeral(system.Process, share.sharePath.String(), 0711) + share.seal.sys.Ephemeral(system.Process, share.sharePath, 0711) return share.sharePath } @@ -99,8 +99,8 @@ func (share *shareHost) runtime() *container.Absolute { } share.ensureRuntimeDir() share.runtimeSharePath = share.sc.RunDirPath.Append(share.seal.id.String()) - share.seal.sys.Ephemeral(system.Process, share.runtimeSharePath.String(), 0700) - share.seal.sys.UpdatePerm(share.runtimeSharePath.String(), acl.Execute) + share.seal.sys.Ephemeral(system.Process, share.runtimeSharePath, 0700) + share.seal.sys.UpdatePerm(share.runtimeSharePath, acl.Execute) return share.runtimeSharePath } @@ -308,26 +308,26 @@ func (k *outcome) finalise(ctx context.Context, msg container.Msg, config *hst.C k.runDirPath = share.sc.RunDirPath k.sys = system.New(k.ctx, msg, k.user.uid.unwrap()) - k.sys.Ensure(share.sc.SharePath.String(), 0711) + k.sys.Ensure(share.sc.SharePath, 0711) { runtimeDir := share.sc.SharePath.Append("runtime") - k.sys.Ensure(runtimeDir.String(), 0700) - k.sys.UpdatePermType(system.User, runtimeDir.String(), acl.Execute) + k.sys.Ensure(runtimeDir, 0700) + k.sys.UpdatePermType(system.User, runtimeDir, acl.Execute) runtimeDirInst := runtimeDir.Append(k.user.identity.String()) - k.sys.Ensure(runtimeDirInst.String(), 0700) - k.sys.UpdatePermType(system.User, runtimeDirInst.String(), acl.Read, acl.Write, acl.Execute) + k.sys.Ensure(runtimeDirInst, 0700) + k.sys.UpdatePermType(system.User, runtimeDirInst, acl.Read, acl.Write, acl.Execute) k.container.Tmpfs(container.AbsFHSRunUser, 1<<12, 0755) k.container.Bind(runtimeDirInst, innerRuntimeDir, container.BindWritable) } { tmpdir := share.sc.SharePath.Append("tmpdir") - k.sys.Ensure(tmpdir.String(), 0700) - k.sys.UpdatePermType(system.User, tmpdir.String(), acl.Execute) + k.sys.Ensure(tmpdir, 0700) + k.sys.UpdatePermType(system.User, tmpdir, acl.Execute) tmpdirInst := tmpdir.Append(k.user.identity.String()) - k.sys.Ensure(tmpdirInst.String(), 01700) - k.sys.UpdatePermType(system.User, tmpdirInst.String(), acl.Read, acl.Write, acl.Execute) + k.sys.Ensure(tmpdirInst, 01700) + k.sys.UpdatePermType(system.User, tmpdirInst, acl.Read, acl.Write, acl.Execute) // mount inner /tmp from share so it shares persistence and storage behaviour of host /tmp k.container.Bind(tmpdirInst, container.AbsFHSTmp, container.BindWritable) } @@ -376,13 +376,13 @@ func (k *outcome) finalise(ctx context.Context, msg container.Msg, config *hst.C } // downstream socket paths outerPath := share.instance().Append("wayland") - k.sys.Wayland(&k.sync, outerPath.String(), socketPath.String(), appID, k.id.String()) + k.sys.Wayland(&k.sync, outerPath, socketPath, appID, k.id.String()) k.container.Bind(outerPath, innerPath, 0) } else { // bind mount wayland socket (insecure) msg.Verbose("direct wayland access, PROCEED WITH CAUTION") share.ensureRuntimeDir() k.container.Bind(socketPath, innerPath, 0) - k.sys.UpdatePermType(hst.EWayland, socketPath.String(), acl.Read, acl.Write, acl.Execute) + k.sys.UpdatePermType(hst.EWayland, socketPath, acl.Read, acl.Write, acl.Execute) } } @@ -410,7 +410,7 @@ func (k *outcome) finalise(ctx context.Context, msg container.Msg, config *hst.C return &hst.AppError{Step: fmt.Sprintf("access X11 socket %q", socketPath), Err: err} } } else { - k.sys.UpdatePermType(hst.EX11, socketPath.String(), acl.Read, acl.Write, acl.Execute) + k.sys.UpdatePermType(hst.EX11, socketPath, acl.Read, acl.Write, acl.Execute) if !config.Container.HostAbstract { d = "unix:" + socketPath.String() } @@ -450,7 +450,7 @@ func (k *outcome) finalise(ctx context.Context, msg container.Msg, config *hst.C // hard link pulse socket into target-executable share innerPulseRuntimeDir := share.runtime().Append("pulse") innerPulseSocket := innerRuntimeDir.Append("pulse", "native") - k.sys.Link(pulseSocket.String(), innerPulseRuntimeDir.String()) + k.sys.Link(pulseSocket, innerPulseRuntimeDir) k.container.Bind(innerPulseRuntimeDir, innerPulseSocket, 0) k.env[pulseServer] = "unix:" + innerPulseSocket.String() @@ -518,7 +518,7 @@ func (k *outcome) finalise(ctx context.Context, msg container.Msg, config *hst.C k.env[pulseCookie] = innerDst.String() var payload *[]byte k.container.PlaceP(innerDst, &payload) - k.sys.CopyFile(payload, paCookiePath.String(), 256, 256) + k.sys.CopyFile(payload, paCookiePath, 256, 256) } else { msg.Verbose("cannot locate PulseAudio cookie (tried " + "$PULSE_COOKIE, " + @@ -539,7 +539,7 @@ func (k *outcome) finalise(ctx context.Context, msg container.Msg, config *hst.C // configure dbus proxy if f, err := k.sys.ProxyDBus( config.SessionBus, config.SystemBus, - sessionPath.String(), systemPath.String(), + sessionPath, systemPath, ); err != nil { return err } else { @@ -550,12 +550,12 @@ func (k *outcome) finalise(ctx context.Context, msg container.Msg, config *hst.C sessionInner := innerRuntimeDir.Append("bus") k.env[dbusSessionBusAddress] = "unix:path=" + sessionInner.String() k.container.Bind(sessionPath, sessionInner, 0) - k.sys.UpdatePerm(sessionPath.String(), acl.Read, acl.Write) + k.sys.UpdatePerm(sessionPath, acl.Read, acl.Write) if config.SystemBus != nil { systemInner := container.AbsFHSRun.Append("dbus/system_bus_socket") k.env[dbusSystemBusAddress] = "unix:path=" + systemInner.String() k.container.Bind(systemPath, systemInner, 0) - k.sys.UpdatePerm(systemPath.String(), acl.Read, acl.Write) + k.sys.UpdatePerm(systemPath, acl.Read, acl.Write) } } @@ -569,7 +569,7 @@ func (k *outcome) finalise(ctx context.Context, msg container.Msg, config *hst.C } if p.Ensure { - k.sys.Ensure(p.Path.String(), 0700) + k.sys.Ensure(p.Path, 0700) } perms := make(acl.Perms, 0, 3) @@ -582,7 +582,7 @@ func (k *outcome) finalise(ctx context.Context, msg container.Msg, config *hst.C if p.Execute { perms = append(perms, acl.Execute) } - k.sys.UpdatePermType(system.User, p.Path.String(), perms...) + k.sys.UpdatePermType(system.User, p.Path, perms...) } // flatten and sort env for deterministic behaviour diff --git a/system/acl.go b/system/acl.go index eb0fe8a..e2e0082 100644 --- a/system/acl.go +++ b/system/acl.go @@ -6,19 +6,20 @@ import ( "os" "slices" + "hakurei.app/container" "hakurei.app/hst" "hakurei.app/system/acl" ) // UpdatePerm calls UpdatePermType with the [Process] criteria. -func (sys *I) UpdatePerm(path string, perms ...acl.Perm) *I { +func (sys *I) UpdatePerm(path *container.Absolute, perms ...acl.Perm) *I { sys.UpdatePermType(Process, path, perms...) return sys } // UpdatePermType maintains [acl.Perms] on a file until its [Enablement] is no longer satisfied. -func (sys *I) UpdatePermType(et hst.Enablement, path string, perms ...acl.Perm) *I { - sys.ops = append(sys.ops, &aclUpdateOp{et, path, perms}) +func (sys *I) UpdatePermType(et hst.Enablement, path *container.Absolute, perms ...acl.Perm) *I { + sys.ops = append(sys.ops, &aclUpdateOp{et, path.String(), perms}) return sys } diff --git a/system/acl_test.go b/system/acl_test.go index b288bd4..992271a 100644 --- a/system/acl_test.go +++ b/system/acl_test.go @@ -60,42 +60,42 @@ func TestACLUpdateOp(t *testing.T) { 0xdeadbeef, func(_ *testing.T, sys *I) { sys. - UpdatePerm("/run/user/1971/hakurei", acl.Execute). - UpdatePerm("/tmp/hakurei.0/tmpdir/150", acl.Read, acl.Write, acl.Execute) + UpdatePerm(m("/run/user/1971/hakurei"), acl.Execute). + UpdatePerm(m("/tmp/hakurei.0/tmpdir/150"), acl.Read, acl.Write, acl.Execute) }, []Op{ &aclUpdateOp{Process, "/run/user/1971/hakurei", []acl.Perm{acl.Execute}}, &aclUpdateOp{Process, "/tmp/hakurei.0/tmpdir/150", []acl.Perm{acl.Read, acl.Write, acl.Execute}}, }, stub.Expect{}}, {"tmpdirp", 0xdeadbeef, func(_ *testing.T, sys *I) { - sys.UpdatePermType(User, "/tmp/hakurei.0/tmpdir", acl.Execute) + sys.UpdatePermType(User, m("/tmp/hakurei.0/tmpdir"), acl.Execute) }, []Op{ &aclUpdateOp{User, "/tmp/hakurei.0/tmpdir", []acl.Perm{acl.Execute}}, }, stub.Expect{}}, {"tmpdir", 0xdeadbeef, func(_ *testing.T, sys *I) { - sys.UpdatePermType(User, "/tmp/hakurei.0/tmpdir/150", acl.Read, acl.Write, acl.Execute) + sys.UpdatePermType(User, m("/tmp/hakurei.0/tmpdir/150"), acl.Read, acl.Write, acl.Execute) }, []Op{ &aclUpdateOp{User, "/tmp/hakurei.0/tmpdir/150", []acl.Perm{acl.Read, acl.Write, acl.Execute}}, }, stub.Expect{}}, {"share", 0xdeadbeef, func(_ *testing.T, sys *I) { - sys.UpdatePermType(Process, "/run/user/1971/hakurei/fcb8a12f7c482d183ade8288c3de78b5", acl.Execute) + sys.UpdatePermType(Process, m("/run/user/1971/hakurei/fcb8a12f7c482d183ade8288c3de78b5"), acl.Execute) }, []Op{ &aclUpdateOp{Process, "/run/user/1971/hakurei/fcb8a12f7c482d183ade8288c3de78b5", []acl.Perm{acl.Execute}}, }, stub.Expect{}}, {"passwd", 0xdeadbeef, func(_ *testing.T, sys *I) { sys. - UpdatePermType(Process, "/tmp/hakurei.0/fcb8a12f7c482d183ade8288c3de78b5/passwd", acl.Read). - UpdatePermType(Process, "/tmp/hakurei.0/fcb8a12f7c482d183ade8288c3de78b5/group", acl.Read) + UpdatePermType(Process, m("/tmp/hakurei.0/fcb8a12f7c482d183ade8288c3de78b5/passwd"), acl.Read). + UpdatePermType(Process, m("/tmp/hakurei.0/fcb8a12f7c482d183ade8288c3de78b5/group"), acl.Read) }, []Op{ &aclUpdateOp{Process, "/tmp/hakurei.0/fcb8a12f7c482d183ade8288c3de78b5/passwd", []acl.Perm{acl.Read}}, &aclUpdateOp{Process, "/tmp/hakurei.0/fcb8a12f7c482d183ade8288c3de78b5/group", []acl.Perm{acl.Read}}, }, stub.Expect{}}, {"wayland", 0xdeadbeef, func(_ *testing.T, sys *I) { - sys.UpdatePermType(hst.EWayland, "/run/user/1971/wayland-0", acl.Read, acl.Write, acl.Execute) + sys.UpdatePermType(hst.EWayland, m("/run/user/1971/wayland-0"), acl.Read, acl.Write, acl.Execute) }, []Op{ &aclUpdateOp{hst.EWayland, "/run/user/1971/wayland-0", []acl.Perm{acl.Read, acl.Write, acl.Execute}}, }, stub.Expect{}}, diff --git a/system/dbus.go b/system/dbus.go index 2f2ac35..9e1b92d 100644 --- a/system/dbus.go +++ b/system/dbus.go @@ -21,7 +21,7 @@ var ( ) // MustProxyDBus calls ProxyDBus and panics if an error is returned. -func (sys *I) MustProxyDBus(sessionPath string, session *dbus.Config, systemPath string, system *dbus.Config) *I { +func (sys *I) MustProxyDBus(sessionPath *container.Absolute, session *dbus.Config, systemPath *container.Absolute, system *dbus.Config) *I { if _, err := sys.ProxyDBus(session, system, sessionPath, systemPath); err != nil { panic(err.Error()) } else { @@ -31,7 +31,7 @@ func (sys *I) MustProxyDBus(sessionPath string, session *dbus.Config, systemPath // ProxyDBus finalises configuration ahead of time and starts xdg-dbus-proxy via [dbus] and terminates it on revert. // This [Op] is always [Process] scoped. -func (sys *I) ProxyDBus(session, system *dbus.Config, sessionPath, systemPath string) (func(), error) { +func (sys *I) ProxyDBus(session, system *dbus.Config, sessionPath, systemPath *container.Absolute) (func(), error) { d := new(dbusProxyOp) // session bus is required as otherwise this is effectively a very expensive noop @@ -45,7 +45,7 @@ func (sys *I) ProxyDBus(session, system *dbus.Config, sessionPath, systemPath st var sessionBus, systemBus dbus.ProxyPair sessionBus[0], systemBus[0] = sys.dbusAddress() - sessionBus[1], systemBus[1] = sessionPath, systemPath + sessionBus[1], systemBus[1] = sessionPath.String(), systemPath.String() d.out = &linePrefixWriter{println: log.Println, prefix: "(dbus) ", buf: new(strings.Builder)} if final, err := sys.dbusFinalise(sessionBus, systemBus, session, system); err != nil { if errors.Is(err, syscall.EINVAL) { diff --git a/system/dbus_test.go b/system/dbus_test.go index 1ae217b..f20f984 100644 --- a/system/dbus_test.go +++ b/system/dbus_test.go @@ -82,7 +82,7 @@ func TestDBusProxyOp(t *testing.T) { Op: "dbus", Err: ErrDBusConfig, Msg: "attempted to create message bus proxy args without session bus config", } - if f, err := sys.ProxyDBus(nil, new(dbus.Config), "", ""); !reflect.DeepEqual(err, wantErr) { + if f, err := sys.ProxyDBus(nil, new(dbus.Config), nil, nil); !reflect.DeepEqual(err, wantErr) { t.Errorf("ProxyDBus: error = %v, want %v", err, wantErr) } else if f != nil { t.Errorf("ProxyDBus: f = %p", f) @@ -98,10 +98,10 @@ func TestDBusProxyOp(t *testing.T) { }() sys.MustProxyDBus( - "/tmp/hakurei.0/99dd71ee2146369514e0d10783368f8f/bus", &dbus.Config{ + m("/tmp/hakurei.0/99dd71ee2146369514e0d10783368f8f/bus"), &dbus.Config{ // use impossible value here as an implicit assert that it goes through the stub Talk: []string{"session\x00"}, Filter: true, - }, "/tmp/hakurei.0/99dd71ee2146369514e0d10783368f8f/system_bus_socket", &dbus.Config{ + }, m("/tmp/hakurei.0/99dd71ee2146369514e0d10783368f8f/system_bus_socket"), &dbus.Config{ // use impossible value here as an implicit assert that it goes through the stub Talk: []string{"system\x00"}, Filter: true, }) @@ -128,8 +128,8 @@ func TestDBusProxyOp(t *testing.T) { // use impossible value here as an implicit assert that it goes through the stub Talk: []string{"system\x00"}, Filter: true, }, - "/tmp/hakurei.0/99dd71ee2146369514e0d10783368f8f/bus", - "/tmp/hakurei.0/99dd71ee2146369514e0d10783368f8f/system_bus_socket"); !reflect.DeepEqual(err, wantErr) { + m("/tmp/hakurei.0/99dd71ee2146369514e0d10783368f8f/bus"), + m("/tmp/hakurei.0/99dd71ee2146369514e0d10783368f8f/system_bus_socket")); !reflect.DeepEqual(err, wantErr) { t.Errorf("ProxyDBus: error = %v", err) } else if f != nil { t.Errorf("ProxyDBus: f = %p", f) @@ -146,10 +146,10 @@ func TestDBusProxyOp(t *testing.T) { {"full", 0xcafebabe, func(_ *testing.T, sys *I) { sys.MustProxyDBus( - "/tmp/hakurei.0/99dd71ee2146369514e0d10783368f8f/bus", &dbus.Config{ + m("/tmp/hakurei.0/99dd71ee2146369514e0d10783368f8f/bus"), &dbus.Config{ // use impossible value here as an implicit assert that it goes through the stub Talk: []string{"session\x00"}, Filter: true, - }, "/tmp/hakurei.0/99dd71ee2146369514e0d10783368f8f/system_bus_socket", &dbus.Config{ + }, m("/tmp/hakurei.0/99dd71ee2146369514e0d10783368f8f/system_bus_socket"), &dbus.Config{ // use impossible value here as an implicit assert that it goes through the stub Talk: []string{"system\x00"}, Filter: true, }) diff --git a/system/link.go b/system/link.go index 50a0534..dcd742c 100644 --- a/system/link.go +++ b/system/link.go @@ -3,15 +3,18 @@ package system import ( "fmt" + "hakurei.app/container" "hakurei.app/hst" ) // Link calls LinkFileType with the [Process] criteria. -func (sys *I) Link(oldname, newname string) *I { return sys.LinkFileType(Process, oldname, newname) } +func (sys *I) Link(oldname, newname *container.Absolute) *I { + return sys.LinkFileType(Process, oldname, newname) +} // LinkFileType maintains a hardlink until its [Enablement] is no longer satisfied. -func (sys *I) LinkFileType(et hst.Enablement, oldname, newname string) *I { - sys.ops = append(sys.ops, &hardlinkOp{et, newname, oldname}) +func (sys *I) LinkFileType(et hst.Enablement, oldname, newname *container.Absolute) *I { + sys.ops = append(sys.ops, &hardlinkOp{et, newname.String(), oldname.String()}) return sys } diff --git a/system/link_test.go b/system/link_test.go index 02a59c0..2085596 100644 --- a/system/link_test.go +++ b/system/link_test.go @@ -40,13 +40,13 @@ func TestHardlinkOp(t *testing.T) { checkOpsBuilder(t, "LinkFileType", []opsBuilderTestCase{ {"type", 0xcafebabe, func(_ *testing.T, sys *I) { - sys.LinkFileType(User, "/run/user/1000/pulse/native", "/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse") + sys.LinkFileType(User, m("/run/user/1000/pulse/native"), m("/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse")) }, []Op{ &hardlinkOp{User, "/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse", "/run/user/1000/pulse/native"}, }, stub.Expect{}}, {"link", 0xcafebabe, func(_ *testing.T, sys *I) { - sys.Link("/run/user/1000/pulse/native", "/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse") + sys.Link(m("/run/user/1000/pulse/native"), m("/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse")) }, []Op{ &hardlinkOp{Process, "/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse", "/run/user/1000/pulse/native"}, }, stub.Expect{}}, diff --git a/system/mkdir.go b/system/mkdir.go index 23c4210..fc6a830 100644 --- a/system/mkdir.go +++ b/system/mkdir.go @@ -5,18 +5,19 @@ import ( "fmt" "os" + "hakurei.app/container" "hakurei.app/hst" ) // Ensure ensures the existence of a directory. -func (sys *I) Ensure(name string, perm os.FileMode) *I { - sys.ops = append(sys.ops, &mkdirOp{User, name, perm, false}) +func (sys *I) Ensure(name *container.Absolute, perm os.FileMode) *I { + sys.ops = append(sys.ops, &mkdirOp{User, name.String(), perm, false}) return sys } // Ephemeral ensures the existence of a directory until its [Enablement] is no longer satisfied. -func (sys *I) Ephemeral(et hst.Enablement, name string, perm os.FileMode) *I { - sys.ops = append(sys.ops, &mkdirOp{et, name, perm, true}) +func (sys *I) Ephemeral(et hst.Enablement, name *container.Absolute, perm os.FileMode) *I { + sys.ops = append(sys.ops, &mkdirOp{et, name.String(), perm, true}) return sys } diff --git a/system/mkdir_test.go b/system/mkdir_test.go index dd7104b..7d79075 100644 --- a/system/mkdir_test.go +++ b/system/mkdir_test.go @@ -57,13 +57,13 @@ func TestMkdirOp(t *testing.T) { checkOpsBuilder(t, "EnsureEphemeral", []opsBuilderTestCase{ {"ensure", 0xcafebabe, func(_ *testing.T, sys *I) { - sys.Ensure("/tmp/hakurei.0", 0700) + sys.Ensure(m("/tmp/hakurei.0"), 0700) }, []Op{ &mkdirOp{User, "/tmp/hakurei.0", 0700, false}, }, stub.Expect{}}, {"ephemeral", 0xcafebabe, func(_ *testing.T, sys *I) { - sys.Ephemeral(Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711) + sys.Ephemeral(Process, m("/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9"), 0711) }, []Op{ &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, true}, }, stub.Expect{}}, diff --git a/system/system_test.go b/system/system_test.go index 0f5e2d6..91d8a87 100644 --- a/system/system_test.go +++ b/system/system_test.go @@ -133,34 +133,34 @@ func TestEqual(t *testing.T) { ChangeHosts("chronos"), New(t.Context(), container.NewMsg(nil), 150). ChangeHosts("chronos"). - Ensure("/run", 0755), + Ensure(m("/run"), 0755), false}, {"op value mismatch", New(t.Context(), container.NewMsg(nil), 150). ChangeHosts("chronos"). - Ensure("/run", 0644), + Ensure(m("/run"), 0644), New(t.Context(), container.NewMsg(nil), 150). ChangeHosts("chronos"). - Ensure("/run", 0755), + Ensure(m("/run"), 0755), false}, {"op type mismatch", New(t.Context(), container.NewMsg(nil), 150). ChangeHosts("chronos"). - CopyFile(new([]byte), "/home/ophestra/xdg/config/pulse/cookie", 0, 256), + CopyFile(new([]byte), m("/home/ophestra/xdg/config/pulse/cookie"), 0, 256), New(t.Context(), container.NewMsg(nil), 150). ChangeHosts("chronos"). - Ensure("/run", 0755), + Ensure(m("/run"), 0755), false}, {"op equals", New(t.Context(), container.NewMsg(nil), 150). ChangeHosts("chronos"). - Ensure("/run", 0755), + Ensure(m("/run"), 0755), New(t.Context(), container.NewMsg(nil), 150). ChangeHosts("chronos"). - Ensure("/run", 0755), + Ensure(m("/run"), 0755), true}, } @@ -187,7 +187,7 @@ func TestCommitRevert(t *testing.T) { }{ {"apply xhost partial mkdir", func(sys *I) { sys. - Ephemeral(Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711). + Ephemeral(Process, m("/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9"), 0711). ChangeHosts("chronos") }, 0xff, []stub.Call{ call("verbose", stub.ExpectArgs{[]any{"ensuring directory", &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, true}}}, nil, nil), @@ -202,7 +202,7 @@ func TestCommitRevert(t *testing.T) { {"apply xhost", func(sys *I) { sys. - Ephemeral(Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711). + Ephemeral(Process, m("/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9"), 0711). ChangeHosts("chronos") }, 0xff, []stub.Call{ call("verbose", stub.ExpectArgs{[]any{"ensuring directory", &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, true}}}, nil, nil), @@ -216,7 +216,7 @@ func TestCommitRevert(t *testing.T) { {"revert multi", func(sys *I) { sys. - Ephemeral(Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711). + Ephemeral(Process, m("/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9"), 0711). ChangeHosts("chronos") }, 0xff, []stub.Call{ call("verbose", stub.ExpectArgs{[]any{"ensuring directory", &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, true}}}, nil, nil), @@ -234,7 +234,7 @@ func TestCommitRevert(t *testing.T) { {"success", func(sys *I) { sys. - Ephemeral(Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711). + Ephemeral(Process, m("/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9"), 0711). ChangeHosts("chronos") }, 0xff, []stub.Call{ call("verbose", stub.ExpectArgs{[]any{"ensuring directory", &mkdirOp{Process, "/tmp/hakurei.0/f2f3bcd492d0266438fa9bf164fe90d9", 0711, true}}}, nil, nil), @@ -312,3 +312,5 @@ func TestNop(t *testing.T) { new(noCopy).Unlock() new(noCopy).Lock() } + +func m(pathname string) *container.Absolute { return container.MustAbs(pathname) } diff --git a/system/tmpfiles.go b/system/tmpfiles.go index 434a3ae..0fe6117 100644 --- a/system/tmpfiles.go +++ b/system/tmpfiles.go @@ -8,14 +8,15 @@ import ( "os" "syscall" + "hakurei.app/container" "hakurei.app/hst" ) // CopyFile reads up to n bytes from src and writes the resulting byte slice to payloadP. -func (sys *I) CopyFile(payloadP *[]byte, src string, cap int, n int64) *I { +func (sys *I) CopyFile(payloadP *[]byte, src *container.Absolute, cap int, n int64) *I { buf := new(bytes.Buffer) buf.Grow(cap) - sys.ops = append(sys.ops, &tmpfileOp{payloadP, src, n, buf}) + sys.ops = append(sys.ops, &tmpfileOp{payloadP, src.String(), n, buf}) return sys } diff --git a/system/tmpfiles_test.go b/system/tmpfiles_test.go index 5ee5e0d..aa7a7e9 100644 --- a/system/tmpfiles_test.go +++ b/system/tmpfiles_test.go @@ -98,7 +98,7 @@ func TestTmpfileOp(t *testing.T) { checkOpsBuilder(t, "CopyFile", []opsBuilderTestCase{ {"pulse", 0xcafebabe, func(_ *testing.T, sys *I) { - sys.CopyFile(new([]byte), "/home/ophestra/xdg/config/pulse/cookie", 1<<8, 1<<8) + sys.CopyFile(new([]byte), m("/home/ophestra/xdg/config/pulse/cookie"), 1<<8, 1<<8) }, []Op{&tmpfileOp{ new([]byte), "/home/ophestra/xdg/config/pulse/cookie", 1 << 8, func() *bytes.Buffer { buf := new(bytes.Buffer); buf.Grow(1 << 8); return buf }(), diff --git a/system/wayland.go b/system/wayland.go index 77cd61f..7ebb211 100644 --- a/system/wayland.go +++ b/system/wayland.go @@ -5,6 +5,7 @@ import ( "fmt" "os" + "hakurei.app/container" "hakurei.app/hst" "hakurei.app/system/acl" "hakurei.app/system/wayland" @@ -19,8 +20,8 @@ type waylandConn interface { // Wayland maintains a wayland socket with security-context-v1 attached via [wayland]. // The socket stops accepting connections once the pipe referred to by sync is closed. // The socket is pathname only and is destroyed on revert. -func (sys *I) Wayland(syncFd **os.File, dst, src, appID, instanceID string) *I { - sys.ops = append(sys.ops, &waylandOp{syncFd, dst, src, appID, instanceID, new(wayland.Conn)}) +func (sys *I) Wayland(syncFd **os.File, dst, src *container.Absolute, appID, instanceID string) *I { + sys.ops = append(sys.ops, &waylandOp{syncFd, dst.String(), src.String(), appID, instanceID, new(wayland.Conn)}) return sys } diff --git a/system/wayland_test.go b/system/wayland_test.go index 3ae6247..9e06cfa 100644 --- a/system/wayland_test.go +++ b/system/wayland_test.go @@ -218,8 +218,8 @@ func TestWaylandOp(t *testing.T) { {"chromium", 0xcafe, func(_ *testing.T, sys *I) { sys.Wayland( new(*os.File), - "/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland", - "/run/user/1971/wayland-0", + m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/wayland"), + m("/run/user/1971/wayland-0"), "org.chromium.Chromium", "ebf083d1b175911782d413369b64ce7c", )