internal/app/paths: defer extra formatting
All checks were successful
Test / Create distribution (push) Successful in 1m14s
Test / Hakurei (push) Successful in 3m50s
Test / Hpkg (push) Successful in 4m44s
Test / Sandbox (race detector) (push) Successful in 4m51s
Test / Sandbox (push) Successful in 1m37s
Test / Hakurei (race detector) (push) Successful in 3m12s
Test / Flake checks (push) Successful in 1m41s

This reduces payload size for params to shim.

Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
Ophestra 2025-09-30 00:21:26 +09:00
parent 71e70b7b5f
commit e58181a930
Signed by: cat
SSH Key Fingerprint: SHA256:gQ67O0enBZ7UdZypgtspB2FDM1g3GVw8nX0XSdcFw8Q
7 changed files with 199 additions and 19 deletions

View File

@ -231,7 +231,7 @@ func buildCommand(ctx context.Context, msg container.Msg, early *earlyHardeningE
var flagShort bool var flagShort bool
c.NewCommand("ps", "List active instances", func(args []string) error { c.NewCommand("ps", "List active instances", func(args []string) error {
var sc hst.Paths var sc hst.Paths
app.CopyPaths(&sc, new(app.Hsu).MustID()) app.CopyPaths().Copy(&sc, new(app.Hsu).MustID())
printPs(os.Stdout, time.Now().UTC(), state.NewMulti(msg, sc.RunDirPath.String()), flagShort, flagJSON) printPs(os.Stdout, time.Now().UTC(), state.NewMulti(msg, sc.RunDirPath.String()), flagShort, flagJSON)
return errSuccess return errSuccess
}).Flag(&flagShort, "short", command.BoolFlag(false), "Print instance id") }).Flag(&flagShort, "short", command.BoolFlag(false), "Print instance id")

View File

@ -89,7 +89,7 @@ func tryShort(msg container.Msg, name string) (config *hst.Config, entry *state.
msg.Verbose("argument looks like prefix") msg.Verbose("argument looks like prefix")
var sc hst.Paths var sc hst.Paths
app.CopyPaths(&sc, new(app.Hsu).MustID()) app.CopyPaths().Copy(&sc, new(app.Hsu).MustID())
s := state.NewMulti(msg, sc.RunDirPath.String()) s := state.NewMulti(msg, sc.RunDirPath.String())
if entries, err := state.Join(s); err != nil { if entries, err := state.Join(s); err != nil {
log.Printf("cannot join store: %v", err) log.Printf("cannot join store: %v", err)

View File

@ -22,7 +22,7 @@ func printShowSystem(output io.Writer, short, flagJSON bool) {
defer t.MustFlush() defer t.MustFlush()
info := &hst.Info{User: new(app.Hsu).MustID()} info := &hst.Info{User: new(app.Hsu).MustID()}
app.CopyPaths(&info.Paths, info.User) app.CopyPaths().Copy(&info.Paths, info.User)
if flagJSON { if flagJSON {
printJSON(output, short, info) printJSON(output, short, info)

View File

@ -0,0 +1,26 @@
package app
import (
"os"
"os/exec"
"hakurei.app/container"
)
type panicDispatcher struct{}
func (panicDispatcher) new(func(k syscallDispatcher)) { panic("unreachable") }
func (panicDispatcher) getuid() int { panic("unreachable") }
func (panicDispatcher) getgid() int { panic("unreachable") }
func (panicDispatcher) lookupEnv(string) (string, bool) { panic("unreachable") }
func (panicDispatcher) stat(string) (os.FileInfo, error) { panic("unreachable") }
func (panicDispatcher) readdir(string) ([]os.DirEntry, error) { panic("unreachable") }
func (panicDispatcher) tempdir() string { panic("unreachable") }
func (panicDispatcher) evalSymlinks(string) (string, error) { panic("unreachable") }
func (panicDispatcher) lookPath(string) (string, error) { panic("unreachable") }
func (panicDispatcher) lookupGroupId(string) (string, error) { panic("unreachable") }
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) fatalf(string, ...any) { panic("unreachable") }

View File

@ -275,7 +275,7 @@ func (k *outcome) finalise(ctx context.Context, msg container.Msg, config *hst.C
// TODO(ophestra): revert this after params to shim // TODO(ophestra): revert this after params to shim
share := &shareHost{seal: k} share := &shareHost{seal: k}
copyPaths(k.syscallDispatcher, &share.sc, hsu.MustIDMsg(msg)) copyPaths(k.syscallDispatcher).Copy(&share.sc, hsu.MustIDMsg(msg))
msg.Verbosef("process share directory at %q, runtime directory at %q", share.sc.SharePath, share.sc.RunDirPath) msg.Verbosef("process share directory at %q, runtime directory at %q", share.sc.SharePath, share.sc.RunDirPath)
var mapuid, mapgid *stringPair[int] var mapuid, mapgid *stringPair[int]

View File

@ -7,28 +7,53 @@ import (
"hakurei.app/hst" "hakurei.app/hst"
) )
// CopyPaths populates a [hst.Paths] struct. // EnvPaths holds paths copied from the environment and is used to create [hst.Paths].
func CopyPaths(v *hst.Paths, userid int) { copyPaths(direct{}, v, userid) } type EnvPaths struct {
// TempDir is returned by [os.TempDir].
// copyPaths populates a [hst.Paths] struct. TempDir *container.Absolute
func copyPaths(k syscallDispatcher, v *hst.Paths, userid int) { // RuntimePath is copied from $XDG_RUNTIME_DIR.
const xdgRuntimeDir = "XDG_RUNTIME_DIR" RuntimePath *container.Absolute
if tempDir, err := container.NewAbs(k.tempdir()); err != nil {
k.fatalf("invalid TMPDIR: %v", err)
} else {
v.TempDir = tempDir
} }
v.SharePath = v.TempDir.Append("hakurei." + strconv.Itoa(userid)) // Copy expands [EnvPaths] into [hst.Paths].
func (env *EnvPaths) Copy(v *hst.Paths, userid int) {
if env == nil || env.TempDir == nil || v == nil {
panic("attempting to use an invalid EnvPaths")
}
r, _ := k.lookupEnv(xdgRuntimeDir) v.TempDir = env.TempDir
if a, err := container.NewAbs(r); err != nil { v.SharePath = env.TempDir.Append("hakurei." + strconv.Itoa(userid))
if env.RuntimePath == nil {
// fall back to path in share since hakurei has no hard XDG dependency // fall back to path in share since hakurei has no hard XDG dependency
v.RunDirPath = v.SharePath.Append("run") v.RunDirPath = v.SharePath.Append("run")
v.RuntimePath = v.RunDirPath.Append("compat") v.RuntimePath = v.RunDirPath.Append("compat")
} else { } else {
v.RuntimePath = a v.RuntimePath = env.RuntimePath
v.RunDirPath = v.RuntimePath.Append("hakurei") v.RunDirPath = env.RuntimePath.Append("hakurei")
} }
} }
// CopyPaths returns a populated [EnvPaths].
func CopyPaths() *EnvPaths { return copyPaths(direct{}) }
// copyPaths returns a populated [EnvPaths].
func copyPaths(k syscallDispatcher) *EnvPaths {
const xdgRuntimeDir = "XDG_RUNTIME_DIR"
var env EnvPaths
if tempDir, err := container.NewAbs(k.tempdir()); err != nil {
k.fatalf("invalid TMPDIR: %v", err)
panic("unreachable")
} else {
env.TempDir = tempDir
}
r, _ := k.lookupEnv(xdgRuntimeDir)
if a, err := container.NewAbs(r); err == nil {
env.RuntimePath = a
}
return &env
}

129
internal/app/paths_test.go Normal file
View File

@ -0,0 +1,129 @@
package app
import (
"fmt"
"reflect"
"testing"
"hakurei.app/container"
"hakurei.app/container/stub"
"hakurei.app/hst"
)
func TestEnvPaths(t *testing.T) {
testCases := []struct {
name string
env *EnvPaths
want hst.Paths
wantPanic string
}{
{"nil", nil, hst.Paths{}, "attempting to use an invalid EnvPaths"},
{"zero", new(EnvPaths), hst.Paths{}, "attempting to use an invalid EnvPaths"},
{"nil tempdir", &EnvPaths{
RuntimePath: container.AbsFHSTmp,
}, hst.Paths{}, "attempting to use an invalid EnvPaths"},
{"nil runtime", &EnvPaths{
TempDir: container.AbsFHSTmp,
}, hst.Paths{
TempDir: container.AbsFHSTmp,
SharePath: container.AbsFHSTmp.Append("hakurei.3735928559"),
RuntimePath: container.AbsFHSTmp.Append("hakurei.3735928559/run/compat"),
RunDirPath: container.AbsFHSTmp.Append("hakurei.3735928559/run"),
}, ""},
{"full", &EnvPaths{
TempDir: container.AbsFHSTmp,
RuntimePath: container.AbsFHSRunUser.Append("1000"),
}, hst.Paths{
TempDir: container.AbsFHSTmp,
SharePath: container.AbsFHSTmp.Append("hakurei.3735928559"),
RuntimePath: container.AbsFHSRunUser.Append("1000"),
RunDirPath: container.AbsFHSRunUser.Append("1000/hakurei"),
}, ""},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
if tc.wantPanic != "" {
defer func() {
if r := recover(); r != tc.wantPanic {
t.Errorf("Copy: panic = %#v, want %q", r, tc.wantPanic)
}
}()
}
var sc hst.Paths
tc.env.Copy(&sc, 0xdeadbeef)
if !reflect.DeepEqual(&sc, &tc.want) {
t.Errorf("Copy: %#v, want %#v", sc, tc.want)
}
})
}
}
func TestCopyPaths(t *testing.T) {
testCases := []struct {
name string
env map[string]string
tmp string
fatal string
want EnvPaths
}{
{"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)}},
{"invalid XDG_RUNTIME_DIR", map[string]string{"XDG_RUNTIME_DIR": "\x00"}, container.Nonexistent,
"", EnvPaths{TempDir: container.MustAbs(container.Nonexistent)}},
{"full", map[string]string{"XDG_RUNTIME_DIR": "/\x00"}, container.Nonexistent,
"", EnvPaths{TempDir: container.MustAbs(container.Nonexistent), RuntimePath: container.MustAbs("/\x00")}},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
if tc.fatal != "" {
defer stub.HandleExit(t)
}
k := copyPathsDispatcher{t: t, env: tc.env, tmp: tc.tmp, expectsFatal: tc.fatal}
got := copyPaths(k)
if tc.fatal != "" {
t.Fatalf("copyPaths: expected fatal %q", tc.fatal)
}
if !reflect.DeepEqual(got, &tc.want) {
t.Errorf("copyPaths: %#v, want %#v", got, &tc.want)
}
})
}
}
// copyPathsDispatcher implements enough of syscallDispatcher for all copyPaths code paths.
type copyPathsDispatcher struct {
env map[string]string
tmp string
// must be checked at the conclusion of the test
expectsFatal string
t *testing.T
panicDispatcher
}
func (k copyPathsDispatcher) tempdir() string { return k.tmp }
func (k copyPathsDispatcher) lookupEnv(key string) (value string, ok bool) {
value, ok = k.env[key]
return
}
func (k copyPathsDispatcher) fatalf(format string, v ...any) {
if k.expectsFatal == "" {
k.t.Fatalf("unexpected call to fatalf: format = %q, v = %#v", format, v)
}
if got := fmt.Sprintf(format, v...); got != k.expectsFatal {
k.t.Fatalf("fatalf: %q, want %q", got, k.expectsFatal)
}
panic(stub.PanicExit)
}