container/check: move absolute pathname
All checks were successful
Test / Create distribution (push) Successful in 34s
Test / Hpkg (push) Successful in 4m3s
Test / Sandbox (race detector) (push) Successful in 4m26s
Test / Hakurei (race detector) (push) Successful in 5m19s
Test / Sandbox (push) Successful in 1m28s
Test / Hakurei (push) Successful in 2m16s
Test / Flake checks (push) Successful in 1m37s

This allows use of absolute pathname values without importing container.

Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
2025-10-07 20:06:26 +09:00
parent d23b4dc9e6
commit 0e6c1a5026
72 changed files with 815 additions and 742 deletions

View File

@@ -16,6 +16,7 @@ import (
"hakurei.app/container"
"hakurei.app/container/bits"
"hakurei.app/container/check"
"hakurei.app/hst"
"hakurei.app/internal/app/state"
"hakurei.app/system"
@@ -695,7 +696,7 @@ func (k *stubNixOS) cmdOutput(cmd *exec.Cmd) ([]byte, error) {
func (k *stubNixOS) overflowUid(container.Msg) int { return 65534 }
func (k *stubNixOS) overflowGid(container.Msg) int { return 65534 }
func (k *stubNixOS) mustHsuPath() *container.Absolute { return m("/proc/nonexistent/hsu") }
func (k *stubNixOS) mustHsuPath() *check.Absolute { return m("/proc/nonexistent/hsu") }
func (k *stubNixOS) fatalf(format string, v ...any) { panic(fmt.Sprintf(format, v...)) }
@@ -703,8 +704,8 @@ func (k *stubNixOS) isVerbose() bool { return true }
func (k *stubNixOS) verbose(v ...any) { log.Print(v...) }
func (k *stubNixOS) verbosef(format string, v ...any) { log.Printf(format, v...) }
func m(pathname string) *container.Absolute {
return container.MustAbs(pathname)
func m(pathname string) *check.Absolute {
return check.MustAbs(pathname)
}
func f(c hst.FilesystemConfig) hst.FilesystemConfigJSON {

View File

@@ -10,6 +10,7 @@ import (
"path/filepath"
"hakurei.app/container"
"hakurei.app/container/check"
"hakurei.app/internal"
)
@@ -57,7 +58,7 @@ type syscallDispatcher interface {
overflowGid(msg container.Msg) int
// mustHsuPath provides [internal.MustHsuPath].
mustHsuPath() *container.Absolute
mustHsuPath() *check.Absolute
// fatalf provides [log.Fatalf].
fatalf(format string, v ...any)
@@ -92,6 +93,6 @@ func (direct) cmdOutput(cmd *exec.Cmd) ([]byte, error) { return cmd.Output() }
func (direct) overflowUid(msg container.Msg) int { return container.OverflowUid(msg) }
func (direct) overflowGid(msg container.Msg) int { return container.OverflowGid(msg) }
func (direct) mustHsuPath() *container.Absolute { return internal.MustHsuPath() }
func (direct) mustHsuPath() *check.Absolute { return internal.MustHsuPath() }
func (direct) fatalf(format string, v ...any) { log.Fatalf(format, v...) }

View File

@@ -5,6 +5,7 @@ import (
"os/exec"
"hakurei.app/container"
"hakurei.app/container/check"
)
type panicDispatcher struct{}
@@ -22,5 +23,5 @@ func (panicDispatcher) lookupGroupId(string) (string, error) { panic("unreachab
func (panicDispatcher) cmdOutput(*exec.Cmd) ([]byte, error) { panic("unreachable") }
func (panicDispatcher) overflowUid(container.Msg) int { panic("unreachable") }
func (panicDispatcher) overflowGid(container.Msg) int { panic("unreachable") }
func (panicDispatcher) mustHsuPath() *container.Absolute { panic("unreachable") }
func (panicDispatcher) mustHsuPath() *check.Absolute { panic("unreachable") }
func (panicDispatcher) fatalf(string, ...any) { panic("unreachable") }

View File

@@ -3,16 +3,16 @@ package app
import (
"strconv"
"hakurei.app/container"
"hakurei.app/container/check"
"hakurei.app/hst"
)
// EnvPaths holds paths copied from the environment and is used to create [hst.Paths].
type EnvPaths struct {
// TempDir is returned by [os.TempDir].
TempDir *container.Absolute
TempDir *check.Absolute
// RuntimePath is copied from $XDG_RUNTIME_DIR.
RuntimePath *container.Absolute
RuntimePath *check.Absolute
}
// Copy expands [EnvPaths] into [hst.Paths].
@@ -43,7 +43,7 @@ func copyPaths(k syscallDispatcher) *EnvPaths {
var env EnvPaths
if tempDir, err := container.NewAbs(k.tempdir()); err != nil {
if tempDir, err := check.NewAbs(k.tempdir()); err != nil {
k.fatalf("invalid TMPDIR: %v", err)
panic("unreachable")
} else {
@@ -51,7 +51,7 @@ func copyPaths(k syscallDispatcher) *EnvPaths {
}
r, _ := k.lookupEnv(xdgRuntimeDir)
if a, err := container.NewAbs(r); err == nil {
if a, err := check.NewAbs(r); err == nil {
env.RuntimePath = a
}

View File

@@ -6,6 +6,7 @@ import (
"testing"
"hakurei.app/container"
"hakurei.app/container/check"
"hakurei.app/container/stub"
"hakurei.app/hst"
)
@@ -74,11 +75,11 @@ func TestCopyPaths(t *testing.T) {
{"invalid tempdir", nil, "\x00",
"invalid TMPDIR: path \"\\x00\" is not absolute", EnvPaths{}},
{"empty environment", make(map[string]string), container.Nonexistent,
"", EnvPaths{TempDir: container.MustAbs(container.Nonexistent)}},
"", EnvPaths{TempDir: check.MustAbs(container.Nonexistent)}},
{"invalid XDG_RUNTIME_DIR", map[string]string{"XDG_RUNTIME_DIR": "\x00"}, container.Nonexistent,
"", EnvPaths{TempDir: container.MustAbs(container.Nonexistent)}},
"", EnvPaths{TempDir: check.MustAbs(container.Nonexistent)}},
{"full", map[string]string{"XDG_RUNTIME_DIR": "/\x00"}, container.Nonexistent,
"", EnvPaths{TempDir: container.MustAbs(container.Nonexistent), RuntimePath: container.MustAbs("/\x00")}},
"", EnvPaths{TempDir: check.MustAbs(container.Nonexistent), RuntimePath: check.MustAbs("/\x00")}},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {

View File

@@ -4,6 +4,7 @@ import (
"strconv"
"hakurei.app/container"
"hakurei.app/container/check"
"hakurei.app/hst"
"hakurei.app/internal/app/state"
"hakurei.app/system"
@@ -51,7 +52,7 @@ type outcomeState struct {
*EnvPaths
// Matched paths to cover. Populated by spFilesystemOp.
HidePaths []*container.Absolute
HidePaths []*check.Absolute
// Copied via populateLocal.
k syscallDispatcher
@@ -95,14 +96,14 @@ func (s *outcomeState) populateLocal(k syscallDispatcher, msg container.Msg) err
// instancePath returns a path formatted for outcomeStateSys.instance.
// This method must only be called from outcomeOp.toContainer if
// outcomeOp.toSystem has already called outcomeStateSys.instance.
func (s *outcomeState) instancePath() *container.Absolute {
func (s *outcomeState) instancePath() *check.Absolute {
return s.sc.SharePath.Append(s.id.String())
}
// runtimePath returns a path formatted for outcomeStateSys.runtime.
// This method must only be called from outcomeOp.toContainer if
// outcomeOp.toSystem has already called outcomeStateSys.runtime.
func (s *outcomeState) runtimePath() *container.Absolute {
func (s *outcomeState) runtimePath() *check.Absolute {
return s.sc.RunDirPath.Append(s.id.String())
}
@@ -112,9 +113,9 @@ type outcomeStateSys struct {
// Whether XDG_RUNTIME_DIR is used post hsu.
useRuntimeDir bool
// Process-specific directory in TMPDIR, nil if unused.
sharePath *container.Absolute
sharePath *check.Absolute
// Process-specific directory in XDG_RUNTIME_DIR, nil if unused.
runtimeSharePath *container.Absolute
runtimeSharePath *check.Absolute
sys *system.I
*outcomeState
@@ -134,7 +135,7 @@ func (state *outcomeStateSys) ensureRuntimeDir() {
// instance returns the pathname to a process-specific directory within TMPDIR.
// This directory must only hold entries bound to [system.Process].
func (state *outcomeStateSys) instance() *container.Absolute {
func (state *outcomeStateSys) instance() *check.Absolute {
if state.sharePath != nil {
return state.sharePath
}
@@ -145,7 +146,7 @@ func (state *outcomeStateSys) instance() *container.Absolute {
// runtime returns the pathname to a process-specific directory within XDG_RUNTIME_DIR.
// This directory must only hold entries bound to [system.Process].
func (state *outcomeStateSys) runtime() *container.Absolute {
func (state *outcomeStateSys) runtime() *check.Absolute {
if state.runtimeSharePath != nil {
return state.runtimeSharePath
}
@@ -169,7 +170,7 @@ type outcomeStateParams struct {
// Inner XDG_RUNTIME_DIR default formatting of `/run/user/%d` via mapped uid.
// Populated by spRuntimeOp.
runtimeDir *container.Absolute
runtimeDir *check.Absolute
as hst.ApplyState
*outcomeState

View File

@@ -13,6 +13,7 @@ import (
"time"
"hakurei.app/container"
"hakurei.app/container/check"
"hakurei.app/hst"
"hakurei.app/internal"
"hakurei.app/internal/app/state"
@@ -215,7 +216,7 @@ type finaliseProcess struct {
waitDelay time.Duration
// Copied from the RunDirPath field of [hst.Paths].
runDirPath *container.Absolute
runDirPath *check.Absolute
// Copied from outcomeState.
identity *stringPair[int]

View File

@@ -9,6 +9,7 @@ import (
"hakurei.app/container"
"hakurei.app/container/bits"
"hakurei.app/container/check"
"hakurei.app/container/seccomp"
"hakurei.app/hst"
"hakurei.app/system/dbus"
@@ -183,7 +184,7 @@ func (s spFilesystemOp) toSystem(state *outcomeStateSys, _ *hst.Config) error {
}
}
hidePathSource := make([]*container.Absolute, 0, hidePathSourceCount)
hidePathSource := make([]*check.Absolute, 0, hidePathSourceCount)
// fs append
for _, c := range filesystem {
@@ -234,8 +235,8 @@ func (s spFilesystemOp) toSystem(state *outcomeStateSys, _ *hst.Config) error {
// copy matched paths for shim
for i, ok := range hidePathMatch {
if ok {
if a, err := container.NewAbs(hidePaths[i]); err != nil {
var absoluteError *container.AbsoluteError
if a, err := check.NewAbs(hidePaths[i]); err != nil {
var absoluteError *check.AbsoluteError
if !errors.As(err, &absoluteError) {
return newWithMessageError(absoluteError.Error(), absoluteError)
}

View File

@@ -8,7 +8,7 @@ import (
"os"
"syscall"
"hakurei.app/container"
"hakurei.app/container/check"
"hakurei.app/hst"
)
@@ -45,13 +45,13 @@ func (s *spPulseOp) toSystem(state *outcomeStateSys, _ *hst.Config) error {
state.sys.Link(pulseSocket, state.runtime().Append("pulse"))
// publish current user's pulse cookie for target user
var paCookiePath *container.Absolute
var paCookiePath *check.Absolute
{
const paLocateStep = "locate PulseAudio cookie"
// from environment
if p, ok := state.k.lookupEnv("PULSE_COOKIE"); ok {
if a, err := container.NewAbs(p); err != nil {
if a, err := check.NewAbs(p); err != nil {
return &hst.AppError{Step: paLocateStep, Err: err}
} else {
// this takes precedence, do not verify whether the file is accessible
@@ -62,7 +62,7 @@ func (s *spPulseOp) toSystem(state *outcomeStateSys, _ *hst.Config) error {
// $HOME/.pulse-cookie
if p, ok := state.k.lookupEnv("HOME"); ok {
if a, err := container.NewAbs(p); err != nil {
if a, err := check.NewAbs(p); err != nil {
return &hst.AppError{Step: paLocateStep, Err: err}
} else {
paCookiePath = a.Append(".pulse-cookie")
@@ -83,7 +83,7 @@ func (s *spPulseOp) toSystem(state *outcomeStateSys, _ *hst.Config) error {
// $XDG_CONFIG_HOME/pulse/cookie
if p, ok := state.k.lookupEnv("XDG_CONFIG_HOME"); ok {
if a, err := container.NewAbs(p); err != nil {
if a, err := check.NewAbs(p); err != nil {
return &hst.AppError{Step: paLocateStep, Err: err}
} else {
paCookiePath = a.Append("pulse", "cookie")
@@ -161,7 +161,7 @@ func (s *spPulseOp) toContainer(state *outcomeStateParams) error {
return nil
}
func (s *spPulseOp) commonPaths(state *outcomeState) (pulseRuntimeDir, pulseSocket *container.Absolute) {
func (s *spPulseOp) commonPaths(state *outcomeState) (pulseRuntimeDir, pulseSocket *check.Absolute) {
// PulseAudio runtime directory (usually `/run/user/%d/pulse`)
pulseRuntimeDir = state.sc.RuntimePath.Append("pulse")
// PulseAudio socket (usually `/run/user/%d/pulse/native`)

View File

@@ -2,6 +2,7 @@ package app
import (
"hakurei.app/container"
"hakurei.app/container/check"
"hakurei.app/hst"
"hakurei.app/system"
"hakurei.app/system/acl"
@@ -37,7 +38,7 @@ func (s spRuntimeOp) toContainer(state *outcomeStateParams) error {
return nil
}
func (s spRuntimeOp) commonPaths(state *outcomeState) (runtimeDir, runtimeDirInst *container.Absolute) {
func (s spRuntimeOp) commonPaths(state *outcomeState) (runtimeDir, runtimeDirInst *check.Absolute) {
runtimeDir = state.sc.SharePath.Append("runtime")
runtimeDirInst = runtimeDir.Append(state.identity.String())
return

View File

@@ -2,6 +2,7 @@ package app
import (
"hakurei.app/container"
"hakurei.app/container/check"
"hakurei.app/hst"
"hakurei.app/system"
"hakurei.app/system/acl"
@@ -26,7 +27,7 @@ func (s spTmpdirOp) toContainer(state *outcomeStateParams) error {
return nil
}
func (s spTmpdirOp) commonPaths(state *outcomeState) (tmpdir, tmpdirInst *container.Absolute) {
func (s spTmpdirOp) commonPaths(state *outcomeState) (tmpdir, tmpdirInst *check.Absolute) {
tmpdir = state.sc.SharePath.Append("tmpdir")
tmpdirInst = tmpdir.Append(state.identity.String())
return

View File

@@ -1,7 +1,7 @@
package app
import (
"hakurei.app/container"
"hakurei.app/container/check"
"hakurei.app/hst"
"hakurei.app/system/acl"
"hakurei.app/system/wayland"
@@ -10,16 +10,16 @@ import (
// spWaylandOp exports the Wayland display server to the container.
type spWaylandOp struct {
// Path to host wayland socket. Populated during toSystem if DirectWayland is true.
SocketPath *container.Absolute
SocketPath *check.Absolute
}
func (s *spWaylandOp) toSystem(state *outcomeStateSys, config *hst.Config) error {
// outer wayland socket (usually `/run/user/%d/wayland-%d`)
var socketPath *container.Absolute
var socketPath *check.Absolute
if name, ok := state.k.lookupEnv(wayland.WaylandDisplay); !ok {
state.msg.Verbose(wayland.WaylandDisplay + " is not set, assuming " + wayland.FallbackName)
socketPath = state.sc.RuntimePath.Append(wayland.FallbackName)
} else if a, err := container.NewAbs(name); err != nil {
} else if a, err := check.NewAbs(name); err != nil {
socketPath = state.sc.RuntimePath.Append(name)
} else {
socketPath = a

View File

@@ -8,6 +8,7 @@ import (
"strings"
"hakurei.app/container"
"hakurei.app/container/check"
"hakurei.app/hst"
"hakurei.app/system/acl"
)
@@ -29,13 +30,13 @@ func (s *spX11Op) toSystem(state *outcomeStateSys, _ *hst.Config) error {
// the socket file at `/tmp/.X11-unix/X%d` is typically owned by the priv user
// and not accessible by the target user
var socketPath *container.Absolute
var socketPath *check.Absolute
if len(s.Display) > 1 && s.Display[0] == ':' { // `:%d`
if n, err := strconv.Atoi(s.Display[1:]); err == nil && n >= 0 {
socketPath = absX11SocketDir.Append("X" + strconv.Itoa(n))
}
} else if len(s.Display) > 5 && strings.HasPrefix(s.Display, "unix:") { // `unix:%s`
if a, err := container.NewAbs(s.Display[5:]); err == nil {
if a, err := check.NewAbs(s.Display[5:]); err == nil {
socketPath = a
}
}

View File

@@ -6,11 +6,11 @@ var (
version = compPoison
)
// check validates string value set at compile time.
func check(s string) (string, bool) { return s, s != compPoison && s != "" }
// checkComp validates string value set at compile time.
func checkComp(s string) (string, bool) { return s, s != compPoison && s != "" }
func Version() string {
if v, ok := check(version); ok {
if v, ok := checkComp(version); ok {
return v
}
return "impure"

View File

@@ -3,7 +3,7 @@ package internal
import (
"log"
"hakurei.app/container"
"hakurei.app/container/check"
)
var (
@@ -12,15 +12,15 @@ var (
)
// MustHakureiPath returns the absolute path to hakurei, configured at compile time.
func MustHakureiPath() *container.Absolute { return mustCheckPath(log.Fatal, "hakurei", hmain) }
func MustHakureiPath() *check.Absolute { return mustCheckPath(log.Fatal, "hakurei", hmain) }
// MustHsuPath returns the absolute path to hakurei, configured at compile time.
func MustHsuPath() *container.Absolute { return mustCheckPath(log.Fatal, "hsu", hsu) }
func MustHsuPath() *check.Absolute { return mustCheckPath(log.Fatal, "hsu", hsu) }
// mustCheckPath checks a pathname against compPoison, then [container.NewAbs], calling fatal if either step fails.
func mustCheckPath(fatal func(v ...any), name, pathname string) *container.Absolute {
func mustCheckPath(fatal func(v ...any), name, pathname string) *check.Absolute {
if pathname != compPoison && pathname != "" {
if a, err := container.NewAbs(pathname); err != nil {
if a, err := check.NewAbs(pathname); err != nil {
fatal(err.Error())
return nil // unreachable
} else {

View File

@@ -4,7 +4,7 @@ import (
"reflect"
"testing"
"hakurei.app/container"
"hakurei.app/container/check"
)
func TestMustCheckPath(t *testing.T) {
@@ -35,7 +35,7 @@ func TestMustCheckPath(t *testing.T) {
}
}
if got := mustCheckPath(fatal, "test", tc.pathname); got != nil && !reflect.DeepEqual(got, container.MustAbs(tc.pathname)) {
if got := mustCheckPath(fatal, "test", tc.pathname); got != nil && !reflect.DeepEqual(got, check.MustAbs(tc.pathname)) {
t.Errorf("mustCheckPath: %q", got)
}
})