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:
Ophestra 2025-10-07 20:06:26 +09:00
parent d23b4dc9e6
commit 0e6c1a5026
Signed by: cat
SSH Key Fingerprint: SHA256:gQ67O0enBZ7UdZypgtspB2FDM1g3GVw8nX0XSdcFw8Q
72 changed files with 815 additions and 742 deletions

View File

@ -16,6 +16,7 @@ import (
"hakurei.app/command" "hakurei.app/command"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/check"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/internal" "hakurei.app/internal"
"hakurei.app/internal/app" "hakurei.app/internal/app"
@ -107,7 +108,7 @@ func buildCommand(ctx context.Context, msg container.Msg, early *earlyHardeningE
// paths are identical, resolve inner shell and program path // paths are identical, resolve inner shell and program path
shell := container.AbsFHSRoot.Append("bin", "sh") shell := container.AbsFHSRoot.Append("bin", "sh")
if a, err := container.NewAbs(os.Getenv("SHELL")); err == nil { if a, err := check.NewAbs(os.Getenv("SHELL")); err == nil {
shell = a shell = a
} }
progPath := shell progPath := shell
@ -115,7 +116,7 @@ func buildCommand(ctx context.Context, msg container.Msg, early *earlyHardeningE
if p, err := exec.LookPath(args[0]); err != nil { if p, err := exec.LookPath(args[0]); err != nil {
log.Fatal(errors.Unwrap(err)) log.Fatal(errors.Unwrap(err))
return err return err
} else if progPath, err = container.NewAbs(p); err != nil { } else if progPath, err = check.NewAbs(p); err != nil {
log.Fatal(err.Error()) log.Fatal(err.Error())
return err return err
} }
@ -201,7 +202,7 @@ func buildCommand(ctx context.Context, msg container.Msg, early *earlyHardeningE
passwdOnce.Do(passwdFunc) passwdOnce.Do(passwdFunc)
homeDir = passwd.HomeDir homeDir = passwd.HomeDir
} }
if a, err := container.NewAbs(homeDir); err != nil { if a, err := check.NewAbs(homeDir); err != nil {
log.Fatal(err.Error()) log.Fatal(err.Error())
return err return err
} else { } else {

View File

@ -6,6 +6,7 @@ import (
"os" "os"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/check"
"hakurei.app/hst" "hakurei.app/hst"
) )
@ -54,14 +55,14 @@ type appInfo struct {
// store path to nixGL source // store path to nixGL source
NixGL string `json:"nix_gl,omitempty"` NixGL string `json:"nix_gl,omitempty"`
// store path to activate-and-exec script // store path to activate-and-exec script
Launcher *container.Absolute `json:"launcher"` Launcher *check.Absolute `json:"launcher"`
// store path to /run/current-system // store path to /run/current-system
CurrentSystem *container.Absolute `json:"current_system"` CurrentSystem *check.Absolute `json:"current_system"`
// store path to home-manager activation package // store path to home-manager activation package
ActivationPackage string `json:"activation_package"` ActivationPackage string `json:"activation_package"`
} }
func (app *appInfo) toHst(pathSet *appPathSet, pathname *container.Absolute, argv []string, flagDropShell bool) *hst.Config { func (app *appInfo) toHst(pathSet *appPathSet, pathname *check.Absolute, argv []string, flagDropShell bool) *hst.Config {
config := &hst.Config{ config := &hst.Config{
ID: app.ID, ID: app.ID,

View File

@ -12,6 +12,7 @@ import (
"hakurei.app/command" "hakurei.app/command"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/check"
"hakurei.app/hst" "hakurei.app/hst"
) )
@ -79,11 +80,11 @@ func main() {
Extract package and set up for cleanup. Extract package and set up for cleanup.
*/ */
var workDir *container.Absolute var workDir *check.Absolute
if p, err := os.MkdirTemp("", "hpkg.*"); err != nil { if p, err := os.MkdirTemp("", "hpkg.*"); err != nil {
log.Printf("cannot create temporary directory: %v", err) log.Printf("cannot create temporary directory: %v", err)
return err return err
} else if workDir, err = container.NewAbs(p); err != nil { } else if workDir, err = check.NewAbs(p); err != nil {
log.Printf("invalid temporary directory: %v", err) log.Printf("invalid temporary directory: %v", err)
return err return err
} }

View File

@ -8,18 +8,19 @@ import (
"sync/atomic" "sync/atomic"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/check"
"hakurei.app/hst" "hakurei.app/hst"
) )
const bash = "bash" const bash = "bash"
var ( var (
dataHome *container.Absolute dataHome *check.Absolute
) )
func init() { func init() {
// dataHome // dataHome
if a, err := container.NewAbs(os.Getenv("HAKUREI_DATA_HOME")); err == nil { if a, err := check.NewAbs(os.Getenv("HAKUREI_DATA_HOME")); err == nil {
dataHome = a dataHome = a
} else { } else {
dataHome = container.AbsFHSVarLib.Append("hakurei/" + strconv.Itoa(os.Getuid())) dataHome = container.AbsFHSVarLib.Append("hakurei/" + strconv.Itoa(os.Getuid()))
@ -29,13 +30,13 @@ func init() {
var ( var (
pathBin = container.AbsFHSRoot.Append("bin") pathBin = container.AbsFHSRoot.Append("bin")
pathNix = container.MustAbs("/nix/") pathNix = check.MustAbs("/nix/")
pathNixStore = pathNix.Append("store/") pathNixStore = pathNix.Append("store/")
pathCurrentSystem = container.AbsFHSRun.Append("current-system") pathCurrentSystem = container.AbsFHSRun.Append("current-system")
pathSwBin = pathCurrentSystem.Append("sw/bin/") pathSwBin = pathCurrentSystem.Append("sw/bin/")
pathShell = pathSwBin.Append(bash) pathShell = pathSwBin.Append(bash)
pathData = container.MustAbs("/data") pathData = check.MustAbs("/data")
pathDataData = pathData.Append("data") pathDataData = pathData.Append("data")
) )
@ -64,15 +65,15 @@ func mustRun(msg container.Msg, name string, arg ...string) {
type appPathSet struct { type appPathSet struct {
// ${dataHome}/${id} // ${dataHome}/${id}
baseDir *container.Absolute baseDir *check.Absolute
// ${baseDir}/app // ${baseDir}/app
metaPath *container.Absolute metaPath *check.Absolute
// ${baseDir}/files // ${baseDir}/files
homeDir *container.Absolute homeDir *check.Absolute
// ${baseDir}/cache // ${baseDir}/cache
cacheDir *container.Absolute cacheDir *check.Absolute
// ${baseDir}/cache/nix // ${baseDir}/cache/nix
nixPath *container.Absolute nixPath *check.Absolute
} }
func pathSetByApp(id string) *appPathSet { func pathSetByApp(id string) *appPathSet {

View File

@ -6,6 +6,7 @@ import (
"strings" "strings"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/check"
"hakurei.app/hst" "hakurei.app/hst"
) )
@ -63,7 +64,7 @@ func withNixDaemon(
func withCacheDir( func withCacheDir(
ctx context.Context, ctx context.Context,
msg container.Msg, msg container.Msg,
action string, command []string, workDir *container.Absolute, action string, command []string, workDir *check.Absolute,
app *appInfo, pathSet *appPathSet, dropShell bool, beforeFail func()) { app *appInfo, pathSet *appPathSet, dropShell bool, beforeFail func()) {
mustRunAppDropShell(ctx, msg, &hst.Config{ mustRunAppDropShell(ctx, msg, &hst.Config{
ID: app.ID, ID: app.ID,

View File

@ -3,13 +3,15 @@ package container
import ( import (
"encoding/gob" "encoding/gob"
"fmt" "fmt"
"hakurei.app/container/check"
) )
func init() { gob.Register(new(AutoEtcOp)) } func init() { gob.Register(new(AutoEtcOp)) }
// Etc appends an [Op] that expands host /etc into a toplevel symlink mirror with /etc semantics. // Etc appends an [Op] that expands host /etc into a toplevel symlink mirror with /etc semantics.
// This is not a generic setup op. It is implemented here to reduce ipc overhead. // This is not a generic setup op. It is implemented here to reduce ipc overhead.
func (f *Ops) Etc(host *Absolute, prefix string) *Ops { func (f *Ops) Etc(host *check.Absolute, prefix string) *Ops {
e := &AutoEtcOp{prefix} e := &AutoEtcOp{prefix}
f.Mkdir(AbsFHSEtc, 0755) f.Mkdir(AbsFHSEtc, 0755)
f.Bind(host, e.hostPath(), 0) f.Bind(host, e.hostPath(), 0)
@ -57,8 +59,8 @@ func (e *AutoEtcOp) apply(state *setupState, k syscallDispatcher) error {
return nil return nil
} }
func (e *AutoEtcOp) hostPath() *Absolute { return AbsFHSEtc.Append(e.hostRel()) } func (e *AutoEtcOp) hostPath() *check.Absolute { return AbsFHSEtc.Append(e.hostRel()) }
func (e *AutoEtcOp) hostRel() string { return ".host/" + e.Prefix } func (e *AutoEtcOp) hostRel() string { return ".host/" + e.Prefix }
func (e *AutoEtcOp) Is(op Op) bool { func (e *AutoEtcOp) Is(op Op) bool {
ve, ok := op.(*AutoEtcOp) ve, ok := op.(*AutoEtcOp)

View File

@ -5,6 +5,7 @@ import (
"os" "os"
"testing" "testing"
"hakurei.app/container/check"
"hakurei.app/container/stub" "hakurei.app/container/stub"
) )
@ -256,11 +257,11 @@ func TestAutoEtcOp(t *testing.T) {
}) })
checkOpsBuilder(t, []opsBuilderTestCase{ checkOpsBuilder(t, []opsBuilderTestCase{
{"pd", new(Ops).Etc(MustAbs("/etc/"), "048090b6ed8f9ebb10e275ff5d8c0659"), Ops{ {"pd", new(Ops).Etc(check.MustAbs("/etc/"), "048090b6ed8f9ebb10e275ff5d8c0659"), Ops{
&MkdirOp{Path: MustAbs("/etc/"), Perm: 0755}, &MkdirOp{Path: check.MustAbs("/etc/"), Perm: 0755},
&BindMountOp{ &BindMountOp{
Source: MustAbs("/etc/"), Source: check.MustAbs("/etc/"),
Target: MustAbs("/etc/.host/048090b6ed8f9ebb10e275ff5d8c0659"), Target: check.MustAbs("/etc/.host/048090b6ed8f9ebb10e275ff5d8c0659"),
}, },
&AutoEtcOp{Prefix: "048090b6ed8f9ebb10e275ff5d8c0659"}, &AutoEtcOp{Prefix: "048090b6ed8f9ebb10e275ff5d8c0659"},
}}, }},

View File

@ -3,19 +3,21 @@ package container
import ( import (
"encoding/gob" "encoding/gob"
"fmt" "fmt"
"hakurei.app/container/check"
) )
func init() { gob.Register(new(AutoRootOp)) } func init() { gob.Register(new(AutoRootOp)) }
// Root appends an [Op] that expands a directory into a toplevel bind mount mirror on container root. // Root appends an [Op] that expands a directory into a toplevel bind mount mirror on container root.
// This is not a generic setup op. It is implemented here to reduce ipc overhead. // This is not a generic setup op. It is implemented here to reduce ipc overhead.
func (f *Ops) Root(host *Absolute, flags int) *Ops { func (f *Ops) Root(host *check.Absolute, flags int) *Ops {
*f = append(*f, &AutoRootOp{host, flags, nil}) *f = append(*f, &AutoRootOp{host, flags, nil})
return f return f
} }
type AutoRootOp struct { type AutoRootOp struct {
Host *Absolute Host *check.Absolute
// passed through to bindMount // passed through to bindMount
Flags int Flags int

View File

@ -5,6 +5,7 @@ import (
"os" "os"
"testing" "testing"
"hakurei.app/container/check"
"hakurei.app/container/stub" "hakurei.app/container/stub"
) )
@ -18,14 +19,14 @@ func TestAutoRootOp(t *testing.T) {
checkOpBehaviour(t, []opBehaviourTestCase{ checkOpBehaviour(t, []opBehaviourTestCase{
{"readdir", &Params{ParentPerm: 0750}, &AutoRootOp{ {"readdir", &Params{ParentPerm: 0750}, &AutoRootOp{
Host: MustAbs("/"), Host: check.MustAbs("/"),
Flags: BindWritable, Flags: BindWritable,
}, []stub.Call{ }, []stub.Call{
call("readdir", stub.ExpectArgs{"/"}, stubDir(), stub.UniqueError(2)), call("readdir", stub.ExpectArgs{"/"}, stubDir(), stub.UniqueError(2)),
}, stub.UniqueError(2), nil, nil}, }, stub.UniqueError(2), nil, nil},
{"early", &Params{ParentPerm: 0750}, &AutoRootOp{ {"early", &Params{ParentPerm: 0750}, &AutoRootOp{
Host: MustAbs("/"), Host: check.MustAbs("/"),
Flags: BindWritable, Flags: BindWritable,
}, []stub.Call{ }, []stub.Call{
call("readdir", stub.ExpectArgs{"/"}, stubDir("bin", "dev", "etc", "home", "lib64", call("readdir", stub.ExpectArgs{"/"}, stubDir("bin", "dev", "etc", "home", "lib64",
@ -34,7 +35,7 @@ func TestAutoRootOp(t *testing.T) {
}, stub.UniqueError(1), nil, nil}, }, stub.UniqueError(1), nil, nil},
{"apply", &Params{ParentPerm: 0750}, &AutoRootOp{ {"apply", &Params{ParentPerm: 0750}, &AutoRootOp{
Host: MustAbs("/"), Host: check.MustAbs("/"),
Flags: BindWritable, Flags: BindWritable,
}, []stub.Call{ }, []stub.Call{
call("readdir", stub.ExpectArgs{"/"}, stubDir("bin", "dev", "etc", "home", "lib64", call("readdir", stub.ExpectArgs{"/"}, stubDir("bin", "dev", "etc", "home", "lib64",
@ -55,7 +56,7 @@ func TestAutoRootOp(t *testing.T) {
}, stub.UniqueError(0)}, }, stub.UniqueError(0)},
{"success pd", &Params{ParentPerm: 0750}, &AutoRootOp{ {"success pd", &Params{ParentPerm: 0750}, &AutoRootOp{
Host: MustAbs("/"), Host: check.MustAbs("/"),
Flags: BindWritable, Flags: BindWritable,
}, []stub.Call{ }, []stub.Call{
call("readdir", stub.ExpectArgs{"/"}, stubDir("bin", "dev", "etc", "home", "lib64", call("readdir", stub.ExpectArgs{"/"}, stubDir("bin", "dev", "etc", "home", "lib64",
@ -86,7 +87,7 @@ func TestAutoRootOp(t *testing.T) {
}, nil}, }, nil},
{"success", &Params{ParentPerm: 0750}, &AutoRootOp{ {"success", &Params{ParentPerm: 0750}, &AutoRootOp{
Host: MustAbs("/var/lib/planterette/base/debian:f92c9052"), Host: check.MustAbs("/var/lib/planterette/base/debian:f92c9052"),
}, []stub.Call{ }, []stub.Call{
call("readdir", stub.ExpectArgs{"/var/lib/planterette/base/debian:f92c9052"}, stubDir("bin", "dev", "etc", "home", "lib64", call("readdir", stub.ExpectArgs{"/var/lib/planterette/base/debian:f92c9052"}, stubDir("bin", "dev", "etc", "home", "lib64",
"lost+found", "mnt", "nix", "proc", "root", "run", "srv", "sys", "tmp", "usr", "var"), nil), "lost+found", "mnt", "nix", "proc", "root", "run", "srv", "sys", "tmp", "usr", "var"), nil),
@ -119,13 +120,13 @@ func TestAutoRootOp(t *testing.T) {
checkOpsValid(t, []opValidTestCase{ checkOpsValid(t, []opValidTestCase{
{"nil", (*AutoRootOp)(nil), false}, {"nil", (*AutoRootOp)(nil), false},
{"zero", new(AutoRootOp), false}, {"zero", new(AutoRootOp), false},
{"valid", &AutoRootOp{Host: MustAbs("/")}, true}, {"valid", &AutoRootOp{Host: check.MustAbs("/")}, true},
}) })
checkOpsBuilder(t, []opsBuilderTestCase{ checkOpsBuilder(t, []opsBuilderTestCase{
{"pd", new(Ops).Root(MustAbs("/"), BindWritable), Ops{ {"pd", new(Ops).Root(check.MustAbs("/"), BindWritable), Ops{
&AutoRootOp{ &AutoRootOp{
Host: MustAbs("/"), Host: check.MustAbs("/"),
Flags: BindWritable, Flags: BindWritable,
}, },
}}, }},
@ -135,42 +136,42 @@ func TestAutoRootOp(t *testing.T) {
{"zero", new(AutoRootOp), new(AutoRootOp), false}, {"zero", new(AutoRootOp), new(AutoRootOp), false},
{"internal ne", &AutoRootOp{ {"internal ne", &AutoRootOp{
Host: MustAbs("/"), Host: check.MustAbs("/"),
Flags: BindWritable, Flags: BindWritable,
}, &AutoRootOp{ }, &AutoRootOp{
Host: MustAbs("/"), Host: check.MustAbs("/"),
Flags: BindWritable, Flags: BindWritable,
resolved: []*BindMountOp{new(BindMountOp)}, resolved: []*BindMountOp{new(BindMountOp)},
}, true}, }, true},
{"flags differs", &AutoRootOp{ {"flags differs", &AutoRootOp{
Host: MustAbs("/"), Host: check.MustAbs("/"),
Flags: BindWritable | BindDevice, Flags: BindWritable | BindDevice,
}, &AutoRootOp{ }, &AutoRootOp{
Host: MustAbs("/"), Host: check.MustAbs("/"),
Flags: BindWritable, Flags: BindWritable,
}, false}, }, false},
{"host differs", &AutoRootOp{ {"host differs", &AutoRootOp{
Host: MustAbs("/tmp/"), Host: check.MustAbs("/tmp/"),
Flags: BindWritable, Flags: BindWritable,
}, &AutoRootOp{ }, &AutoRootOp{
Host: MustAbs("/"), Host: check.MustAbs("/"),
Flags: BindWritable, Flags: BindWritable,
}, false}, }, false},
{"equals", &AutoRootOp{ {"equals", &AutoRootOp{
Host: MustAbs("/"), Host: check.MustAbs("/"),
Flags: BindWritable, Flags: BindWritable,
}, &AutoRootOp{ }, &AutoRootOp{
Host: MustAbs("/"), Host: check.MustAbs("/"),
Flags: BindWritable, Flags: BindWritable,
}, true}, }, true},
}) })
checkOpMeta(t, []opMetaTestCase{ checkOpMeta(t, []opMetaTestCase{
{"root", &AutoRootOp{ {"root", &AutoRootOp{
Host: MustAbs("/"), Host: check.MustAbs("/"),
Flags: BindWritable, Flags: BindWritable,
}, "setting up", `auto root "/" flags 0x2`}, }, "setting up", `auto root "/" flags 0x2`},
}) })

View File

@ -1,4 +1,5 @@
package container // Package check provides types yielding values checked to meet a condition.
package check
import ( import (
"encoding/json" "encoding/json"
@ -11,9 +12,7 @@ import (
) )
// AbsoluteError is returned by [NewAbs] and holds the invalid pathname. // AbsoluteError is returned by [NewAbs] and holds the invalid pathname.
type AbsoluteError struct { type AbsoluteError struct{ Pathname string }
Pathname string
}
func (e *AbsoluteError) Error() string { return fmt.Sprintf("path %q is not absolute", e.Pathname) } func (e *AbsoluteError) Error() string { return fmt.Sprintf("path %q is not absolute", e.Pathname) }
func (e *AbsoluteError) Is(target error) bool { func (e *AbsoluteError) Is(target error) bool {
@ -25,15 +24,13 @@ func (e *AbsoluteError) Is(target error) bool {
} }
// Absolute holds a pathname checked to be absolute. // Absolute holds a pathname checked to be absolute.
type Absolute struct { type Absolute struct{ pathname string }
pathname string
}
// isAbs wraps [path.IsAbs] in case additional checks are added in the future. // unsafeAbs returns [check.Absolute] on any string value.
func isAbs(pathname string) bool { return path.IsAbs(pathname) } func unsafeAbs(pathname string) *Absolute { return &Absolute{pathname} }
func (a *Absolute) String() string { func (a *Absolute) String() string {
if a.pathname == zeroString { if a.pathname == "" {
panic("attempted use of zero Absolute") panic("attempted use of zero Absolute")
} }
return a.pathname return a.pathname
@ -44,16 +41,16 @@ func (a *Absolute) Is(v *Absolute) bool {
return true return true
} }
return a != nil && v != nil && return a != nil && v != nil &&
a.pathname != zeroString && v.pathname != zeroString && a.pathname != "" && v.pathname != "" &&
a.pathname == v.pathname a.pathname == v.pathname
} }
// NewAbs checks pathname and returns a new [Absolute] if pathname is absolute. // NewAbs checks pathname and returns a new [Absolute] if pathname is absolute.
func NewAbs(pathname string) (*Absolute, error) { func NewAbs(pathname string) (*Absolute, error) {
if !isAbs(pathname) { if !path.IsAbs(pathname) {
return nil, &AbsoluteError{pathname} return nil, &AbsoluteError{pathname}
} }
return &Absolute{pathname}, nil return unsafeAbs(pathname), nil
} }
// MustAbs calls [NewAbs] and panics on error. // MustAbs calls [NewAbs] and panics on error.
@ -67,16 +64,16 @@ func MustAbs(pathname string) *Absolute {
// Append calls [path.Join] with [Absolute] as the first element. // Append calls [path.Join] with [Absolute] as the first element.
func (a *Absolute) Append(elem ...string) *Absolute { func (a *Absolute) Append(elem ...string) *Absolute {
return &Absolute{path.Join(append([]string{a.String()}, elem...)...)} return unsafeAbs(path.Join(append([]string{a.String()}, elem...)...))
} }
// Dir calls [path.Dir] with [Absolute] as its argument. // Dir calls [path.Dir] with [Absolute] as its argument.
func (a *Absolute) Dir() *Absolute { return &Absolute{path.Dir(a.String())} } func (a *Absolute) Dir() *Absolute { return unsafeAbs(path.Dir(a.String())) }
func (a *Absolute) GobEncode() ([]byte, error) { return []byte(a.String()), nil } func (a *Absolute) GobEncode() ([]byte, error) { return []byte(a.String()), nil }
func (a *Absolute) GobDecode(data []byte) error { func (a *Absolute) GobDecode(data []byte) error {
pathname := string(data) pathname := string(data)
if !isAbs(pathname) { if !path.IsAbs(pathname) {
return &AbsoluteError{pathname} return &AbsoluteError{pathname}
} }
a.pathname = pathname a.pathname = pathname
@ -89,7 +86,7 @@ func (a *Absolute) UnmarshalJSON(data []byte) error {
if err := json.Unmarshal(data, &pathname); err != nil { if err := json.Unmarshal(data, &pathname); err != nil {
return err return err
} }
if !isAbs(pathname) { if !path.IsAbs(pathname) {
return &AbsoluteError{pathname} return &AbsoluteError{pathname}
} }
a.pathname = pathname a.pathname = pathname

View File

@ -1,4 +1,4 @@
package container package check_test
import ( import (
"bytes" "bytes"
@ -9,8 +9,14 @@ import (
"strings" "strings"
"syscall" "syscall"
"testing" "testing"
_ "unsafe"
. "hakurei.app/container/check"
) )
//go:linkname unsafeAbs hakurei.app/container/check.unsafeAbs
func unsafeAbs(_ string) *Absolute
func TestAbsoluteError(t *testing.T) { func TestAbsoluteError(t *testing.T) {
testCases := []struct { testCases := []struct {
name string name string
@ -80,7 +86,7 @@ func TestNewAbs(t *testing.T) {
func TestAbsoluteString(t *testing.T) { func TestAbsoluteString(t *testing.T) {
t.Run("passthrough", func(t *testing.T) { t.Run("passthrough", func(t *testing.T) {
pathname := "/etc" pathname := "/etc"
if got := (&Absolute{pathname}).String(); got != pathname { if got := unsafeAbs(pathname).String(); got != pathname {
t.Errorf("String: %q, want %q", got, pathname) t.Errorf("String: %q, want %q", got, pathname)
} }
}) })

View File

@ -15,6 +15,7 @@ import (
"time" "time"
"hakurei.app/container/bits" "hakurei.app/container/bits"
"hakurei.app/container/check"
"hakurei.app/container/seccomp" "hakurei.app/container/seccomp"
) )
@ -57,11 +58,11 @@ type (
// Params holds container configuration and is safe to serialise. // Params holds container configuration and is safe to serialise.
Params struct { Params struct {
// Working directory in the container. // Working directory in the container.
Dir *Absolute Dir *check.Absolute
// Initial process environment. // Initial process environment.
Env []string Env []string
// Pathname of initial process in the container. // Pathname of initial process in the container.
Path *Absolute Path *check.Absolute
// Initial process argv. // Initial process argv.
Args []string Args []string
// Deliver SIGINT to the initial process on context cancellation. // Deliver SIGINT to the initial process on context cancellation.
@ -407,7 +408,7 @@ func New(ctx context.Context, msg Msg) *Container {
} }
// NewCommand calls [New] and initialises the [Params.Path] and [Params.Args] fields. // NewCommand calls [New] and initialises the [Params.Path] and [Params.Args] fields.
func NewCommand(ctx context.Context, msg Msg, pathname *Absolute, name string, args ...string) *Container { func NewCommand(ctx context.Context, msg Msg, pathname *check.Absolute, name string, args ...string) *Container {
z := New(ctx, msg) z := New(ctx, msg)
z.Path = pathname z.Path = pathname
z.Args = append([]string{name}, args...) z.Args = append([]string{name}, args...)

View File

@ -21,6 +21,7 @@ import (
"hakurei.app/command" "hakurei.app/command"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/bits" "hakurei.app/container/bits"
"hakurei.app/container/check"
"hakurei.app/container/seccomp" "hakurei.app/container/seccomp"
"hakurei.app/container/vfs" "hakurei.app/container/vfs"
"hakurei.app/hst" "hakurei.app/hst"
@ -226,7 +227,7 @@ var containerTestCases = []struct {
{"dev", true, true /* go test output is not a tty */, false, false, {"dev", true, true /* go test output is not a tty */, false, false,
earlyOps(new(container.Ops). earlyOps(new(container.Ops).
Dev(container.MustAbs("/dev"), true), Dev(check.MustAbs("/dev"), true),
), ),
earlyMnt( earlyMnt(
ent("/", "/dev", "ro,nosuid,nodev,relatime", "tmpfs", "devtmpfs", ignore), ent("/", "/dev", "ro,nosuid,nodev,relatime", "tmpfs", "devtmpfs", ignore),
@ -244,7 +245,7 @@ var containerTestCases = []struct {
{"dev no mqueue", true, true /* go test output is not a tty */, false, false, {"dev no mqueue", true, true /* go test output is not a tty */, false, false,
earlyOps(new(container.Ops). earlyOps(new(container.Ops).
Dev(container.MustAbs("/dev"), false), Dev(check.MustAbs("/dev"), false),
), ),
earlyMnt( earlyMnt(
ent("/", "/dev", "ro,nosuid,nodev,relatime", "tmpfs", "devtmpfs", ignore), ent("/", "/dev", "ro,nosuid,nodev,relatime", "tmpfs", "devtmpfs", ignore),
@ -261,13 +262,13 @@ var containerTestCases = []struct {
{"overlay", true, false, false, true, {"overlay", true, false, false, true,
func(t *testing.T) (*container.Ops, context.Context) { func(t *testing.T) (*container.Ops, context.Context) {
tempDir := container.MustAbs(t.TempDir()) tempDir := check.MustAbs(t.TempDir())
lower0, lower1, upper, work := lower0, lower1, upper, work :=
tempDir.Append("lower0"), tempDir.Append("lower0"),
tempDir.Append("lower1"), tempDir.Append("lower1"),
tempDir.Append("upper"), tempDir.Append("upper"),
tempDir.Append("work") tempDir.Append("work")
for _, a := range []*container.Absolute{lower0, lower1, upper, work} { for _, a := range []*check.Absolute{lower0, lower1, upper, work} {
if err := os.Mkdir(a.String(), 0755); err != nil { if err := os.Mkdir(a.String(), 0755); err != nil {
t.Fatalf("Mkdir: error = %v", err) t.Fatalf("Mkdir: error = %v", err)
} }
@ -285,12 +286,12 @@ var containerTestCases = []struct {
return []*vfs.MountInfoEntry{ return []*vfs.MountInfoEntry{
ent("/", hst.Tmp, "rw", "overlay", "overlay", ent("/", hst.Tmp, "rw", "overlay", "overlay",
"rw,lowerdir="+ "rw,lowerdir="+
container.InternalToHostOvlEscape(ctx.Value(testVal("lower0")).(*container.Absolute).String())+":"+ container.InternalToHostOvlEscape(ctx.Value(testVal("lower0")).(*check.Absolute).String())+":"+
container.InternalToHostOvlEscape(ctx.Value(testVal("lower1")).(*container.Absolute).String())+ container.InternalToHostOvlEscape(ctx.Value(testVal("lower1")).(*check.Absolute).String())+
",upperdir="+ ",upperdir="+
container.InternalToHostOvlEscape(ctx.Value(testVal("upper")).(*container.Absolute).String())+ container.InternalToHostOvlEscape(ctx.Value(testVal("upper")).(*check.Absolute).String())+
",workdir="+ ",workdir="+
container.InternalToHostOvlEscape(ctx.Value(testVal("work")).(*container.Absolute).String())+ container.InternalToHostOvlEscape(ctx.Value(testVal("work")).(*check.Absolute).String())+
",redirect_dir=nofollow,uuid=on,userxattr"), ",redirect_dir=nofollow,uuid=on,userxattr"),
} }
}, },
@ -298,11 +299,11 @@ var containerTestCases = []struct {
{"overlay ephemeral", true, false, false, true, {"overlay ephemeral", true, false, false, true,
func(t *testing.T) (*container.Ops, context.Context) { func(t *testing.T) (*container.Ops, context.Context) {
tempDir := container.MustAbs(t.TempDir()) tempDir := check.MustAbs(t.TempDir())
lower0, lower1 := lower0, lower1 :=
tempDir.Append("lower0"), tempDir.Append("lower0"),
tempDir.Append("lower1") tempDir.Append("lower1")
for _, a := range []*container.Absolute{lower0, lower1} { for _, a := range []*check.Absolute{lower0, lower1} {
if err := os.Mkdir(a.String(), 0755); err != nil { if err := os.Mkdir(a.String(), 0755); err != nil {
t.Fatalf("Mkdir: error = %v", err) t.Fatalf("Mkdir: error = %v", err)
} }
@ -322,11 +323,11 @@ var containerTestCases = []struct {
{"overlay readonly", true, false, false, true, {"overlay readonly", true, false, false, true,
func(t *testing.T) (*container.Ops, context.Context) { func(t *testing.T) (*container.Ops, context.Context) {
tempDir := container.MustAbs(t.TempDir()) tempDir := check.MustAbs(t.TempDir())
lower0, lower1 := lower0, lower1 :=
tempDir.Append("lower0"), tempDir.Append("lower0"),
tempDir.Append("lower1") tempDir.Append("lower1")
for _, a := range []*container.Absolute{lower0, lower1} { for _, a := range []*check.Absolute{lower0, lower1} {
if err := os.Mkdir(a.String(), 0755); err != nil { if err := os.Mkdir(a.String(), 0755); err != nil {
t.Fatalf("Mkdir: error = %v", err) t.Fatalf("Mkdir: error = %v", err)
} }
@ -341,8 +342,8 @@ var containerTestCases = []struct {
return []*vfs.MountInfoEntry{ return []*vfs.MountInfoEntry{
ent("/", hst.Tmp, "rw", "overlay", "overlay", ent("/", hst.Tmp, "rw", "overlay", "overlay",
"ro,lowerdir="+ "ro,lowerdir="+
container.InternalToHostOvlEscape(ctx.Value(testVal("lower0")).(*container.Absolute).String())+":"+ container.InternalToHostOvlEscape(ctx.Value(testVal("lower0")).(*check.Absolute).String())+":"+
container.InternalToHostOvlEscape(ctx.Value(testVal("lower1")).(*container.Absolute).String())+ container.InternalToHostOvlEscape(ctx.Value(testVal("lower1")).(*check.Absolute).String())+
",redirect_dir=nofollow,userxattr"), ",redirect_dir=nofollow,userxattr"),
} }
}, },
@ -389,7 +390,7 @@ func TestContainer(t *testing.T) {
ctx, cancel := context.WithTimeout(t.Context(), helperDefaultTimeout) ctx, cancel := context.WithTimeout(t.Context(), helperDefaultTimeout)
defer cancel() defer cancel()
var libPaths []*container.Absolute var libPaths []*check.Absolute
c := helperNewContainerLibPaths(ctx, &libPaths, "container", strconv.Itoa(i)) c := helperNewContainerLibPaths(ctx, &libPaths, "container", strconv.Itoa(i))
c.Uid = tc.uid c.Uid = tc.uid
c.Gid = tc.gid c.Gid = tc.gid
@ -410,11 +411,11 @@ func TestContainer(t *testing.T) {
c.HostNet = tc.net c.HostNet = tc.net
c. c.
Readonly(container.MustAbs(pathReadonly), 0755). Readonly(check.MustAbs(pathReadonly), 0755).
Tmpfs(container.MustAbs("/tmp"), 0, 0755). Tmpfs(check.MustAbs("/tmp"), 0, 0755).
Place(container.MustAbs("/etc/hostname"), []byte(c.Hostname)) Place(check.MustAbs("/etc/hostname"), []byte(c.Hostname))
// needs /proc to check mountinfo // needs /proc to check mountinfo
c.Proc(container.MustAbs("/proc")) c.Proc(check.MustAbs("/proc"))
// mountinfo cannot be resolved directly by helper due to libPaths nondeterminism // mountinfo cannot be resolved directly by helper due to libPaths nondeterminism
mnt := make([]*vfs.MountInfoEntry, 0, 3+len(libPaths)) mnt := make([]*vfs.MountInfoEntry, 0, 3+len(libPaths))
@ -445,10 +446,10 @@ func TestContainer(t *testing.T) {
_, _ = output.WriteTo(os.Stdout) _, _ = output.WriteTo(os.Stdout)
t.Fatalf("cannot serialise expected mount points: %v", err) t.Fatalf("cannot serialise expected mount points: %v", err)
} }
c.Place(container.MustAbs(pathWantMnt), want.Bytes()) c.Place(check.MustAbs(pathWantMnt), want.Bytes())
if tc.ro { if tc.ro {
c.Remount(container.MustAbs("/"), syscall.MS_RDONLY) c.Remount(check.MustAbs("/"), syscall.MS_RDONLY)
} }
if err := c.Start(); err != nil { if err := c.Start(); err != nil {
@ -545,7 +546,7 @@ func testContainerCancel(
func TestContainerString(t *testing.T) { func TestContainerString(t *testing.T) {
msg := container.NewMsg(nil) msg := container.NewMsg(nil)
c := container.NewCommand(t.Context(), msg, container.MustAbs("/run/current-system/sw/bin/ldd"), "ldd", "/usr/bin/env") c := container.NewCommand(t.Context(), msg, check.MustAbs("/run/current-system/sw/bin/ldd"), "ldd", "/usr/bin/env")
c.SeccompFlags |= seccomp.AllowMultiarch c.SeccompFlags |= seccomp.AllowMultiarch
c.SeccompRules = seccomp.Preset( c.SeccompRules = seccomp.Preset(
bits.PresetExt|bits.PresetDenyNS|bits.PresetDenyTTY, bits.PresetExt|bits.PresetDenyNS|bits.PresetDenyTTY,
@ -681,7 +682,7 @@ const (
) )
var ( var (
absHelperInnerPath = container.MustAbs(helperInnerPath) absHelperInnerPath = check.MustAbs(helperInnerPath)
) )
var helperCommands []func(c command.Command) var helperCommands []func(c command.Command)
@ -709,11 +710,11 @@ func TestMain(m *testing.M) {
os.Exit(m.Run()) os.Exit(m.Run())
} }
func helperNewContainerLibPaths(ctx context.Context, libPaths *[]*container.Absolute, args ...string) (c *container.Container) { func helperNewContainerLibPaths(ctx context.Context, libPaths *[]*check.Absolute, args ...string) (c *container.Container) {
msg := container.NewMsg(nil) msg := container.NewMsg(nil)
c = container.NewCommand(ctx, msg, absHelperInnerPath, "helper", args...) c = container.NewCommand(ctx, msg, absHelperInnerPath, "helper", args...)
c.Env = append(c.Env, envDoCheck+"=1") c.Env = append(c.Env, envDoCheck+"=1")
c.Bind(container.MustAbs(os.Args[0]), absHelperInnerPath, 0) c.Bind(check.MustAbs(os.Args[0]), absHelperInnerPath, 0)
// in case test has cgo enabled // in case test has cgo enabled
if entries, err := ldd.Exec(ctx, msg, os.Args[0]); err != nil { if entries, err := ldd.Exec(ctx, msg, os.Args[0]); err != nil {
@ -729,5 +730,5 @@ func helperNewContainerLibPaths(ctx context.Context, libPaths *[]*container.Abso
} }
func helperNewContainer(ctx context.Context, args ...string) (c *container.Container) { func helperNewContainer(ctx context.Context, args ...string) (c *container.Container) {
return helperNewContainerLibPaths(ctx, new([]*container.Absolute), args...) return helperNewContainerLibPaths(ctx, new([]*check.Absolute), args...)
} }

View File

@ -5,6 +5,7 @@ import (
"os" "os"
"syscall" "syscall"
"hakurei.app/container/check"
"hakurei.app/container/vfs" "hakurei.app/container/vfs"
) )
@ -16,7 +17,7 @@ func messageFromError(err error) (string, bool) {
if m, ok := messagePrefixP[os.PathError]("cannot ", err); ok { if m, ok := messagePrefixP[os.PathError]("cannot ", err); ok {
return m, ok return m, ok
} }
if m, ok := messagePrefixP[AbsoluteError]("", err); ok { if m, ok := messagePrefixP[check.AbsoluteError]("", err); ok {
return m, ok return m, ok
} }
if m, ok := messagePrefix[OpRepeatError]("", err); ok { if m, ok := messagePrefix[OpRepeatError]("", err); ok {

View File

@ -8,6 +8,7 @@ import (
"syscall" "syscall"
"testing" "testing"
"hakurei.app/container/check"
"hakurei.app/container/stub" "hakurei.app/container/stub"
"hakurei.app/container/vfs" "hakurei.app/container/vfs"
) )
@ -34,7 +35,7 @@ func TestMessageFromError(t *testing.T) {
Err: stub.UniqueError(0xdeadbeef), Err: stub.UniqueError(0xdeadbeef),
}, "cannot mount /sysroot: unique error 3735928559 injected by the test suite", true}, }, "cannot mount /sysroot: unique error 3735928559 injected by the test suite", true},
{"absolute", &AbsoluteError{"etc/mtab"}, {"absolute", &check.AbsoluteError{Pathname: "etc/mtab"},
`path "etc/mtab" is not absolute`, true}, `path "etc/mtab" is not absolute`, true},
{"repeat", OpRepeatError("autoetc"), {"repeat", OpRepeatError("autoetc"),

View File

@ -7,6 +7,7 @@ import (
"time" "time"
"hakurei.app/container/bits" "hakurei.app/container/bits"
"hakurei.app/container/check"
"hakurei.app/container/seccomp" "hakurei.app/container/seccomp"
"hakurei.app/container/stub" "hakurei.app/container/stub"
) )
@ -58,9 +59,9 @@ func TestInitEntrypoint(t *testing.T) {
call("getpid", stub.ExpectArgs{}, 1, nil), call("getpid", stub.ExpectArgs{}, 1, nil),
call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil), call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil),
call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{ call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{
Dir: MustAbs("/.hakurei"), Dir: check.MustAbs("/.hakurei"),
Env: []string{"DISPLAY=:0"}, Env: []string{"DISPLAY=:0"},
Path: MustAbs("/bin/zsh"), Path: check.MustAbs("/bin/zsh"),
Args: []string{"zsh", "-c", "exec vim"}, Args: []string{"zsh", "-c", "exec vim"},
ForwardCancel: true, ForwardCancel: true,
AdoptWaitDelay: 5 * time.Second, AdoptWaitDelay: 5 * time.Second,
@ -82,9 +83,9 @@ func TestInitEntrypoint(t *testing.T) {
call("getpid", stub.ExpectArgs{}, 1, nil), call("getpid", stub.ExpectArgs{}, 1, nil),
call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil), call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil),
call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{ call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{
Dir: MustAbs("/.hakurei"), Dir: check.MustAbs("/.hakurei"),
Env: []string{"DISPLAY=:0"}, Env: []string{"DISPLAY=:0"},
Path: MustAbs("/bin/zsh"), Path: check.MustAbs("/bin/zsh"),
Args: []string{"zsh", "-c", "exec vim"}, Args: []string{"zsh", "-c", "exec vim"},
ForwardCancel: true, ForwardCancel: true,
AdoptWaitDelay: 5 * time.Second, AdoptWaitDelay: 5 * time.Second,
@ -110,9 +111,9 @@ func TestInitEntrypoint(t *testing.T) {
call("getpid", stub.ExpectArgs{}, 1, nil), call("getpid", stub.ExpectArgs{}, 1, nil),
call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil), call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil),
call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{ call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{
Dir: MustAbs("/.hakurei"), Dir: check.MustAbs("/.hakurei"),
Env: []string{"DISPLAY=:0"}, Env: []string{"DISPLAY=:0"},
Path: MustAbs("/bin/zsh"), Path: check.MustAbs("/bin/zsh"),
Args: []string{"zsh", "-c", "exec vim"}, Args: []string{"zsh", "-c", "exec vim"},
ForwardCancel: true, ForwardCancel: true,
AdoptWaitDelay: 5 * time.Second, AdoptWaitDelay: 5 * time.Second,
@ -139,9 +140,9 @@ func TestInitEntrypoint(t *testing.T) {
call("getpid", stub.ExpectArgs{}, 1, nil), call("getpid", stub.ExpectArgs{}, 1, nil),
call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil), call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil),
call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{ call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{
Dir: MustAbs("/.hakurei"), Dir: check.MustAbs("/.hakurei"),
Env: []string{"DISPLAY=:0"}, Env: []string{"DISPLAY=:0"},
Path: MustAbs("/bin/zsh"), Path: check.MustAbs("/bin/zsh"),
Args: []string{"zsh", "-c", "exec vim"}, Args: []string{"zsh", "-c", "exec vim"},
ForwardCancel: true, ForwardCancel: true,
AdoptWaitDelay: 5 * time.Second, AdoptWaitDelay: 5 * time.Second,
@ -169,9 +170,9 @@ func TestInitEntrypoint(t *testing.T) {
call("getpid", stub.ExpectArgs{}, 1, nil), call("getpid", stub.ExpectArgs{}, 1, nil),
call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil), call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil),
call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{ call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{
Dir: MustAbs("/.hakurei"), Dir: check.MustAbs("/.hakurei"),
Env: []string{"DISPLAY=:0"}, Env: []string{"DISPLAY=:0"},
Path: MustAbs("/bin/zsh"), Path: check.MustAbs("/bin/zsh"),
Args: []string{"zsh", "-c", "exec vim"}, Args: []string{"zsh", "-c", "exec vim"},
ForwardCancel: true, ForwardCancel: true,
AdoptWaitDelay: 5 * time.Second, AdoptWaitDelay: 5 * time.Second,
@ -200,9 +201,9 @@ func TestInitEntrypoint(t *testing.T) {
call("getpid", stub.ExpectArgs{}, 1, nil), call("getpid", stub.ExpectArgs{}, 1, nil),
call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil), call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil),
call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{ call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{
Dir: MustAbs("/.hakurei"), Dir: check.MustAbs("/.hakurei"),
Env: []string{"DISPLAY=:0"}, Env: []string{"DISPLAY=:0"},
Path: MustAbs("/bin/zsh"), Path: check.MustAbs("/bin/zsh"),
Args: []string{"zsh", "-c", "exec vim"}, Args: []string{"zsh", "-c", "exec vim"},
ForwardCancel: true, ForwardCancel: true,
AdoptWaitDelay: 5 * time.Second, AdoptWaitDelay: 5 * time.Second,
@ -232,9 +233,9 @@ func TestInitEntrypoint(t *testing.T) {
call("getpid", stub.ExpectArgs{}, 1, nil), call("getpid", stub.ExpectArgs{}, 1, nil),
call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil), call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil),
call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{ call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{
Dir: MustAbs("/.hakurei"), Dir: check.MustAbs("/.hakurei"),
Env: []string{"DISPLAY=:0"}, Env: []string{"DISPLAY=:0"},
Path: MustAbs("/bin/zsh"), Path: check.MustAbs("/bin/zsh"),
Args: []string{"zsh", "-c", "exec vim"}, Args: []string{"zsh", "-c", "exec vim"},
ForwardCancel: true, ForwardCancel: true,
AdoptWaitDelay: 5 * time.Second, AdoptWaitDelay: 5 * time.Second,
@ -266,9 +267,9 @@ func TestInitEntrypoint(t *testing.T) {
call("getpid", stub.ExpectArgs{}, 1, nil), call("getpid", stub.ExpectArgs{}, 1, nil),
call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil), call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil),
call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{ call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{
Dir: MustAbs("/.hakurei"), Dir: check.MustAbs("/.hakurei"),
Env: []string{"DISPLAY=:0"}, Env: []string{"DISPLAY=:0"},
Path: MustAbs("/bin/zsh"), Path: check.MustAbs("/bin/zsh"),
Args: []string{"zsh", "-c", "exec vim"}, Args: []string{"zsh", "-c", "exec vim"},
ForwardCancel: true, ForwardCancel: true,
AdoptWaitDelay: 5 * time.Second, AdoptWaitDelay: 5 * time.Second,
@ -302,9 +303,9 @@ func TestInitEntrypoint(t *testing.T) {
call("getpid", stub.ExpectArgs{}, 1, nil), call("getpid", stub.ExpectArgs{}, 1, nil),
call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil), call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil),
call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{ call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{
Dir: MustAbs("/.hakurei"), Dir: check.MustAbs("/.hakurei"),
Env: []string{"DISPLAY=:0"}, Env: []string{"DISPLAY=:0"},
Path: MustAbs("/bin/zsh"), Path: check.MustAbs("/bin/zsh"),
Args: []string{"zsh", "-c", "exec vim"}, Args: []string{"zsh", "-c", "exec vim"},
ForwardCancel: true, ForwardCancel: true,
AdoptWaitDelay: 5 * time.Second, AdoptWaitDelay: 5 * time.Second,
@ -340,9 +341,9 @@ func TestInitEntrypoint(t *testing.T) {
call("getpid", stub.ExpectArgs{}, 1, nil), call("getpid", stub.ExpectArgs{}, 1, nil),
call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil), call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil),
call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{ call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{
Dir: MustAbs("/.hakurei"), Dir: check.MustAbs("/.hakurei"),
Env: []string{"DISPLAY=:0"}, Env: []string{"DISPLAY=:0"},
Path: MustAbs("/bin/zsh"), Path: check.MustAbs("/bin/zsh"),
Args: []string{"zsh", "-c", "exec vim"}, Args: []string{"zsh", "-c", "exec vim"},
ForwardCancel: true, ForwardCancel: true,
AdoptWaitDelay: 5 * time.Second, AdoptWaitDelay: 5 * time.Second,
@ -378,16 +379,16 @@ func TestInitEntrypoint(t *testing.T) {
call("getpid", stub.ExpectArgs{}, 1, nil), call("getpid", stub.ExpectArgs{}, 1, nil),
call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil), call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil),
call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{ call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{
Dir: MustAbs("/.hakurei"), Dir: check.MustAbs("/.hakurei"),
Env: []string{"DISPLAY=:0"}, Env: []string{"DISPLAY=:0"},
Path: MustAbs("/bin/zsh"), Path: check.MustAbs("/bin/zsh"),
Args: []string{"zsh", "-c", "exec vim"}, Args: []string{"zsh", "-c", "exec vim"},
ForwardCancel: true, ForwardCancel: true,
AdoptWaitDelay: 5 * time.Second, AdoptWaitDelay: 5 * time.Second,
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(MustAbs("/"), MustAbs("/"), BindDevice).Proc(MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: bits.PresetStrict,
RetainSession: true, RetainSession: true,
@ -417,16 +418,16 @@ func TestInitEntrypoint(t *testing.T) {
call("getpid", stub.ExpectArgs{}, 1, nil), call("getpid", stub.ExpectArgs{}, 1, nil),
call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil), call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil),
call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{ call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{
Dir: MustAbs("/.hakurei"), Dir: check.MustAbs("/.hakurei"),
Env: []string{"DISPLAY=:0"}, Env: []string{"DISPLAY=:0"},
Path: MustAbs("/bin/zsh"), Path: check.MustAbs("/bin/zsh"),
Args: []string{"zsh", "-c", "exec vim"}, Args: []string{"zsh", "-c", "exec vim"},
ForwardCancel: true, ForwardCancel: true,
AdoptWaitDelay: 5 * time.Second, AdoptWaitDelay: 5 * time.Second,
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(MustAbs("/"), MustAbs("/"), BindDevice).Proc(MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: bits.PresetStrict,
RetainSession: true, RetainSession: true,
@ -456,16 +457,16 @@ func TestInitEntrypoint(t *testing.T) {
call("getpid", stub.ExpectArgs{}, 1, nil), call("getpid", stub.ExpectArgs{}, 1, nil),
call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil), call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil),
call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{ call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{
Dir: MustAbs("/.hakurei"), Dir: check.MustAbs("/.hakurei"),
Env: []string{"DISPLAY=:0"}, Env: []string{"DISPLAY=:0"},
Path: MustAbs("/bin/zsh"), Path: check.MustAbs("/bin/zsh"),
Args: []string{"zsh", "-c", "exec vim"}, Args: []string{"zsh", "-c", "exec vim"},
ForwardCancel: true, ForwardCancel: true,
AdoptWaitDelay: 5 * time.Second, AdoptWaitDelay: 5 * time.Second,
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(MustAbs("/"), MustAbs("/"), BindDevice).Proc(MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: bits.PresetStrict,
RetainSession: true, RetainSession: true,
@ -496,16 +497,16 @@ func TestInitEntrypoint(t *testing.T) {
call("getpid", stub.ExpectArgs{}, 1, nil), call("getpid", stub.ExpectArgs{}, 1, nil),
call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil), call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil),
call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{ call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{
Dir: MustAbs("/.hakurei"), Dir: check.MustAbs("/.hakurei"),
Env: []string{"DISPLAY=:0"}, Env: []string{"DISPLAY=:0"},
Path: MustAbs("/bin/zsh"), Path: check.MustAbs("/bin/zsh"),
Args: []string{"zsh", "-c", "exec vim"}, Args: []string{"zsh", "-c", "exec vim"},
ForwardCancel: true, ForwardCancel: true,
AdoptWaitDelay: 5 * time.Second, AdoptWaitDelay: 5 * time.Second,
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(MustAbs("/"), MustAbs("/"), BindDevice).Proc(MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: bits.PresetStrict,
RetainSession: true, RetainSession: true,
@ -537,16 +538,16 @@ func TestInitEntrypoint(t *testing.T) {
call("getpid", stub.ExpectArgs{}, 1, nil), call("getpid", stub.ExpectArgs{}, 1, nil),
call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil), call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil),
call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{ call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{
Dir: MustAbs("/.hakurei"), Dir: check.MustAbs("/.hakurei"),
Env: []string{"DISPLAY=:0"}, Env: []string{"DISPLAY=:0"},
Path: MustAbs("/bin/zsh"), Path: check.MustAbs("/bin/zsh"),
Args: []string{"zsh", "-c", "exec vim"}, Args: []string{"zsh", "-c", "exec vim"},
ForwardCancel: true, ForwardCancel: true,
AdoptWaitDelay: 5 * time.Second, AdoptWaitDelay: 5 * time.Second,
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(MustAbs("/"), MustAbs("/"), BindDevice).Proc(MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: bits.PresetStrict,
RetainSession: true, RetainSession: true,
@ -579,16 +580,16 @@ func TestInitEntrypoint(t *testing.T) {
call("getpid", stub.ExpectArgs{}, 1, nil), call("getpid", stub.ExpectArgs{}, 1, nil),
call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil), call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil),
call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{ call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{
Dir: MustAbs("/.hakurei"), Dir: check.MustAbs("/.hakurei"),
Env: []string{"DISPLAY=:0"}, Env: []string{"DISPLAY=:0"},
Path: MustAbs("/bin/zsh"), Path: check.MustAbs("/bin/zsh"),
Args: []string{"zsh", "-c", "exec vim"}, Args: []string{"zsh", "-c", "exec vim"},
ForwardCancel: true, ForwardCancel: true,
AdoptWaitDelay: 5 * time.Second, AdoptWaitDelay: 5 * time.Second,
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(MustAbs("/"), MustAbs("/"), BindDevice).Proc(MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: bits.PresetStrict,
RetainSession: true, RetainSession: true,
@ -622,16 +623,16 @@ func TestInitEntrypoint(t *testing.T) {
call("getpid", stub.ExpectArgs{}, 1, nil), call("getpid", stub.ExpectArgs{}, 1, nil),
call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil), call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil),
call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{ call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{
Dir: MustAbs("/.hakurei"), Dir: check.MustAbs("/.hakurei"),
Env: []string{"DISPLAY=:0"}, Env: []string{"DISPLAY=:0"},
Path: MustAbs("/bin/zsh"), Path: check.MustAbs("/bin/zsh"),
Args: []string{"zsh", "-c", "exec vim"}, Args: []string{"zsh", "-c", "exec vim"},
ForwardCancel: true, ForwardCancel: true,
AdoptWaitDelay: 5 * time.Second, AdoptWaitDelay: 5 * time.Second,
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(MustAbs("/"), MustAbs("/"), BindDevice).Proc(MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: bits.PresetStrict,
RetainSession: true, RetainSession: true,
@ -666,16 +667,16 @@ func TestInitEntrypoint(t *testing.T) {
call("getpid", stub.ExpectArgs{}, 1, nil), call("getpid", stub.ExpectArgs{}, 1, nil),
call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil), call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil),
call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{ call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{
Dir: MustAbs("/.hakurei"), Dir: check.MustAbs("/.hakurei"),
Env: []string{"DISPLAY=:0"}, Env: []string{"DISPLAY=:0"},
Path: MustAbs("/bin/zsh"), Path: check.MustAbs("/bin/zsh"),
Args: []string{"zsh", "-c", "exec vim"}, Args: []string{"zsh", "-c", "exec vim"},
ForwardCancel: true, ForwardCancel: true,
AdoptWaitDelay: 5 * time.Second, AdoptWaitDelay: 5 * time.Second,
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(MustAbs("/"), MustAbs("/"), BindDevice).Proc(MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: bits.PresetStrict,
RetainSession: true, RetainSession: true,
@ -711,16 +712,16 @@ func TestInitEntrypoint(t *testing.T) {
call("getpid", stub.ExpectArgs{}, 1, nil), call("getpid", stub.ExpectArgs{}, 1, nil),
call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil), call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil),
call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{ call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{
Dir: MustAbs("/.hakurei"), Dir: check.MustAbs("/.hakurei"),
Env: []string{"DISPLAY=:0"}, Env: []string{"DISPLAY=:0"},
Path: MustAbs("/bin/zsh"), Path: check.MustAbs("/bin/zsh"),
Args: []string{"zsh", "-c", "exec vim"}, Args: []string{"zsh", "-c", "exec vim"},
ForwardCancel: true, ForwardCancel: true,
AdoptWaitDelay: 5 * time.Second, AdoptWaitDelay: 5 * time.Second,
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(MustAbs("/"), MustAbs("/"), BindDevice).Proc(MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: bits.PresetStrict,
RetainSession: true, RetainSession: true,
@ -757,16 +758,16 @@ func TestInitEntrypoint(t *testing.T) {
call("getpid", stub.ExpectArgs{}, 1, nil), call("getpid", stub.ExpectArgs{}, 1, nil),
call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil), call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil),
call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{ call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{
Dir: MustAbs("/.hakurei"), Dir: check.MustAbs("/.hakurei"),
Env: []string{"DISPLAY=:0"}, Env: []string{"DISPLAY=:0"},
Path: MustAbs("/bin/zsh"), Path: check.MustAbs("/bin/zsh"),
Args: []string{"zsh", "-c", "exec vim"}, Args: []string{"zsh", "-c", "exec vim"},
ForwardCancel: true, ForwardCancel: true,
AdoptWaitDelay: 5 * time.Second, AdoptWaitDelay: 5 * time.Second,
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(MustAbs("/"), MustAbs("/"), BindDevice).Proc(MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: bits.PresetStrict,
RetainSession: true, RetainSession: true,
@ -798,7 +799,7 @@ func TestInitEntrypoint(t *testing.T) {
call("mkdirAll", stub.ExpectArgs{"/sysroot", os.FileMode(0700)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/sysroot", os.FileMode(0700)}, nil, nil),
call("verbosef", stub.ExpectArgs{"mounting %q flags %#x", []any{"/sysroot", uintptr(0x4001)}}, nil, nil), call("verbosef", stub.ExpectArgs{"mounting %q flags %#x", []any{"/sysroot", uintptr(0x4001)}}, nil, nil),
call("bindMount", stub.ExpectArgs{"/host", "/sysroot", uintptr(0x4001), false}, nil, nil), call("bindMount", stub.ExpectArgs{"/host", "/sysroot", uintptr(0x4001), false}, nil, nil),
call("verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &MountProcOp{Target: MustAbs("/proc/")}}}, nil, nil), call("verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &MountProcOp{Target: check.MustAbs("/proc/")}}}, nil, nil),
call("mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0755)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0755)}, nil, nil),
call("mount", stub.ExpectArgs{"proc", "/sysroot/proc", "proc", uintptr(0xe), ""}, nil, stub.UniqueError(44)), call("mount", stub.ExpectArgs{"proc", "/sysroot/proc", "proc", uintptr(0xe), ""}, nil, stub.UniqueError(44)),
call("fatalf", stub.ExpectArgs{"cannot apply op at index %d: %v", []any{1, stub.UniqueError(44)}}, nil, nil), call("fatalf", stub.ExpectArgs{"cannot apply op at index %d: %v", []any{1, stub.UniqueError(44)}}, nil, nil),
@ -812,16 +813,16 @@ func TestInitEntrypoint(t *testing.T) {
call("getpid", stub.ExpectArgs{}, 1, nil), call("getpid", stub.ExpectArgs{}, 1, nil),
call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil), call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil),
call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{ call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{
Dir: MustAbs("/.hakurei"), Dir: check.MustAbs("/.hakurei"),
Env: []string{"DISPLAY=:0"}, Env: []string{"DISPLAY=:0"},
Path: MustAbs("/bin/zsh"), Path: check.MustAbs("/bin/zsh"),
Args: []string{"zsh", "-c", "exec vim"}, Args: []string{"zsh", "-c", "exec vim"},
ForwardCancel: true, ForwardCancel: true,
AdoptWaitDelay: 5 * time.Second, AdoptWaitDelay: 5 * time.Second,
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(MustAbs("/"), MustAbs("/"), BindDevice).Proc(MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: bits.PresetStrict,
RetainSession: true, RetainSession: true,
@ -853,7 +854,7 @@ func TestInitEntrypoint(t *testing.T) {
call("mkdirAll", stub.ExpectArgs{"/sysroot", os.FileMode(0700)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/sysroot", os.FileMode(0700)}, nil, nil),
call("verbosef", stub.ExpectArgs{"mounting %q flags %#x", []any{"/sysroot", uintptr(0x4001)}}, nil, nil), call("verbosef", stub.ExpectArgs{"mounting %q flags %#x", []any{"/sysroot", uintptr(0x4001)}}, nil, nil),
call("bindMount", stub.ExpectArgs{"/host", "/sysroot", uintptr(0x4001), false}, nil, nil), call("bindMount", stub.ExpectArgs{"/host", "/sysroot", uintptr(0x4001), false}, nil, nil),
call("verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &MountProcOp{Target: MustAbs("/proc/")}}}, nil, nil), call("verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &MountProcOp{Target: check.MustAbs("/proc/")}}}, nil, nil),
call("mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0755)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0755)}, nil, nil),
call("mount", stub.ExpectArgs{"proc", "/sysroot/proc", "proc", uintptr(0xe), ""}, nil, &MountError{"proc", "/sysroot/proc", "proc", uintptr(0xe), "", syscall.ENOTRECOVERABLE}), call("mount", stub.ExpectArgs{"proc", "/sysroot/proc", "proc", uintptr(0xe), ""}, nil, &MountError{"proc", "/sysroot/proc", "proc", uintptr(0xe), "", syscall.ENOTRECOVERABLE}),
call("fatal", stub.ExpectArgs{[]any{"cannot mount proc on /sysroot/proc: state not recoverable"}}, nil, nil), call("fatal", stub.ExpectArgs{[]any{"cannot mount proc on /sysroot/proc: state not recoverable"}}, nil, nil),
@ -867,16 +868,16 @@ func TestInitEntrypoint(t *testing.T) {
call("getpid", stub.ExpectArgs{}, 1, nil), call("getpid", stub.ExpectArgs{}, 1, nil),
call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil), call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil),
call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{ call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{
Dir: MustAbs("/.hakurei"), Dir: check.MustAbs("/.hakurei"),
Env: []string{"DISPLAY=:0"}, Env: []string{"DISPLAY=:0"},
Path: MustAbs("/bin/zsh"), Path: check.MustAbs("/bin/zsh"),
Args: []string{"zsh", "-c", "exec vim"}, Args: []string{"zsh", "-c", "exec vim"},
ForwardCancel: true, ForwardCancel: true,
AdoptWaitDelay: 5 * time.Second, AdoptWaitDelay: 5 * time.Second,
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(MustAbs("/"), MustAbs("/"), BindDevice).Proc(MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: bits.PresetStrict,
RetainSession: true, RetainSession: true,
@ -908,7 +909,7 @@ func TestInitEntrypoint(t *testing.T) {
call("mkdirAll", stub.ExpectArgs{"/sysroot", os.FileMode(0700)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/sysroot", os.FileMode(0700)}, nil, nil),
call("verbosef", stub.ExpectArgs{"mounting %q flags %#x", []any{"/sysroot", uintptr(0x4001)}}, nil, nil), call("verbosef", stub.ExpectArgs{"mounting %q flags %#x", []any{"/sysroot", uintptr(0x4001)}}, nil, nil),
call("bindMount", stub.ExpectArgs{"/host", "/sysroot", uintptr(0x4001), false}, nil, nil), call("bindMount", stub.ExpectArgs{"/host", "/sysroot", uintptr(0x4001), false}, nil, nil),
call("verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &MountProcOp{Target: MustAbs("/proc/")}}}, nil, nil), call("verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &MountProcOp{Target: check.MustAbs("/proc/")}}}, nil, nil),
call("mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0755)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0755)}, nil, nil),
call("mount", stub.ExpectArgs{"proc", "/sysroot/proc", "proc", uintptr(0xe), ""}, nil, nil), call("mount", stub.ExpectArgs{"proc", "/sysroot/proc", "proc", uintptr(0xe), ""}, nil, nil),
/* end apply */ /* end apply */
@ -923,16 +924,16 @@ func TestInitEntrypoint(t *testing.T) {
call("getpid", stub.ExpectArgs{}, 1, nil), call("getpid", stub.ExpectArgs{}, 1, nil),
call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil), call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil),
call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{ call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{
Dir: MustAbs("/.hakurei"), Dir: check.MustAbs("/.hakurei"),
Env: []string{"DISPLAY=:0"}, Env: []string{"DISPLAY=:0"},
Path: MustAbs("/bin/zsh"), Path: check.MustAbs("/bin/zsh"),
Args: []string{"zsh", "-c", "exec vim"}, Args: []string{"zsh", "-c", "exec vim"},
ForwardCancel: true, ForwardCancel: true,
AdoptWaitDelay: 5 * time.Second, AdoptWaitDelay: 5 * time.Second,
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(MustAbs("/"), MustAbs("/"), BindDevice).Proc(MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: bits.PresetStrict,
RetainSession: true, RetainSession: true,
@ -964,7 +965,7 @@ func TestInitEntrypoint(t *testing.T) {
call("mkdirAll", stub.ExpectArgs{"/sysroot", os.FileMode(0700)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/sysroot", os.FileMode(0700)}, nil, nil),
call("verbosef", stub.ExpectArgs{"mounting %q flags %#x", []any{"/sysroot", uintptr(0x4001)}}, nil, nil), call("verbosef", stub.ExpectArgs{"mounting %q flags %#x", []any{"/sysroot", uintptr(0x4001)}}, nil, nil),
call("bindMount", stub.ExpectArgs{"/host", "/sysroot", uintptr(0x4001), false}, nil, nil), call("bindMount", stub.ExpectArgs{"/host", "/sysroot", uintptr(0x4001), false}, nil, nil),
call("verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &MountProcOp{Target: MustAbs("/proc/")}}}, nil, nil), call("verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &MountProcOp{Target: check.MustAbs("/proc/")}}}, nil, nil),
call("mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0755)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0755)}, nil, nil),
call("mount", stub.ExpectArgs{"proc", "/sysroot/proc", "proc", uintptr(0xe), ""}, nil, nil), call("mount", stub.ExpectArgs{"proc", "/sysroot/proc", "proc", uintptr(0xe), ""}, nil, nil),
/* end apply */ /* end apply */
@ -980,16 +981,16 @@ func TestInitEntrypoint(t *testing.T) {
call("getpid", stub.ExpectArgs{}, 1, nil), call("getpid", stub.ExpectArgs{}, 1, nil),
call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil), call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil),
call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{ call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{
Dir: MustAbs("/.hakurei"), Dir: check.MustAbs("/.hakurei"),
Env: []string{"DISPLAY=:0"}, Env: []string{"DISPLAY=:0"},
Path: MustAbs("/bin/zsh"), Path: check.MustAbs("/bin/zsh"),
Args: []string{"zsh", "-c", "exec vim"}, Args: []string{"zsh", "-c", "exec vim"},
ForwardCancel: true, ForwardCancel: true,
AdoptWaitDelay: 5 * time.Second, AdoptWaitDelay: 5 * time.Second,
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(MustAbs("/"), MustAbs("/"), BindDevice).Proc(MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: bits.PresetStrict,
RetainSession: true, RetainSession: true,
@ -1021,7 +1022,7 @@ func TestInitEntrypoint(t *testing.T) {
call("mkdirAll", stub.ExpectArgs{"/sysroot", os.FileMode(0700)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/sysroot", os.FileMode(0700)}, nil, nil),
call("verbosef", stub.ExpectArgs{"mounting %q flags %#x", []any{"/sysroot", uintptr(0x4001)}}, nil, nil), call("verbosef", stub.ExpectArgs{"mounting %q flags %#x", []any{"/sysroot", uintptr(0x4001)}}, nil, nil),
call("bindMount", stub.ExpectArgs{"/host", "/sysroot", uintptr(0x4001), false}, nil, nil), call("bindMount", stub.ExpectArgs{"/host", "/sysroot", uintptr(0x4001), false}, nil, nil),
call("verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &MountProcOp{Target: MustAbs("/proc/")}}}, nil, nil), call("verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &MountProcOp{Target: check.MustAbs("/proc/")}}}, nil, nil),
call("mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0755)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0755)}, nil, nil),
call("mount", stub.ExpectArgs{"proc", "/sysroot/proc", "proc", uintptr(0xe), ""}, nil, nil), call("mount", stub.ExpectArgs{"proc", "/sysroot/proc", "proc", uintptr(0xe), ""}, nil, nil),
/* end apply */ /* end apply */
@ -1039,16 +1040,16 @@ func TestInitEntrypoint(t *testing.T) {
call("getpid", stub.ExpectArgs{}, 1, nil), call("getpid", stub.ExpectArgs{}, 1, nil),
call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil), call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil),
call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{ call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{
Dir: MustAbs("/.hakurei"), Dir: check.MustAbs("/.hakurei"),
Env: []string{"DISPLAY=:0"}, Env: []string{"DISPLAY=:0"},
Path: MustAbs("/bin/zsh"), Path: check.MustAbs("/bin/zsh"),
Args: []string{"zsh", "-c", "exec vim"}, Args: []string{"zsh", "-c", "exec vim"},
ForwardCancel: true, ForwardCancel: true,
AdoptWaitDelay: 5 * time.Second, AdoptWaitDelay: 5 * time.Second,
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(MustAbs("/"), MustAbs("/"), BindDevice).Proc(MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: bits.PresetStrict,
RetainSession: true, RetainSession: true,
@ -1080,7 +1081,7 @@ func TestInitEntrypoint(t *testing.T) {
call("mkdirAll", stub.ExpectArgs{"/sysroot", os.FileMode(0700)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/sysroot", os.FileMode(0700)}, nil, nil),
call("verbosef", stub.ExpectArgs{"mounting %q flags %#x", []any{"/sysroot", uintptr(0x4001)}}, nil, nil), call("verbosef", stub.ExpectArgs{"mounting %q flags %#x", []any{"/sysroot", uintptr(0x4001)}}, nil, nil),
call("bindMount", stub.ExpectArgs{"/host", "/sysroot", uintptr(0x4001), false}, nil, nil), call("bindMount", stub.ExpectArgs{"/host", "/sysroot", uintptr(0x4001), false}, nil, nil),
call("verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &MountProcOp{Target: MustAbs("/proc/")}}}, nil, nil), call("verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &MountProcOp{Target: check.MustAbs("/proc/")}}}, nil, nil),
call("mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0755)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0755)}, nil, nil),
call("mount", stub.ExpectArgs{"proc", "/sysroot/proc", "proc", uintptr(0xe), ""}, nil, nil), call("mount", stub.ExpectArgs{"proc", "/sysroot/proc", "proc", uintptr(0xe), ""}, nil, nil),
/* end apply */ /* end apply */
@ -1099,16 +1100,16 @@ func TestInitEntrypoint(t *testing.T) {
call("getpid", stub.ExpectArgs{}, 1, nil), call("getpid", stub.ExpectArgs{}, 1, nil),
call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil), call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil),
call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{ call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{
Dir: MustAbs("/.hakurei"), Dir: check.MustAbs("/.hakurei"),
Env: []string{"DISPLAY=:0"}, Env: []string{"DISPLAY=:0"},
Path: MustAbs("/bin/zsh"), Path: check.MustAbs("/bin/zsh"),
Args: []string{"zsh", "-c", "exec vim"}, Args: []string{"zsh", "-c", "exec vim"},
ForwardCancel: true, ForwardCancel: true,
AdoptWaitDelay: 5 * time.Second, AdoptWaitDelay: 5 * time.Second,
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(MustAbs("/"), MustAbs("/"), BindDevice).Proc(MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: bits.PresetStrict,
RetainSession: true, RetainSession: true,
@ -1140,7 +1141,7 @@ func TestInitEntrypoint(t *testing.T) {
call("mkdirAll", stub.ExpectArgs{"/sysroot", os.FileMode(0700)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/sysroot", os.FileMode(0700)}, nil, nil),
call("verbosef", stub.ExpectArgs{"mounting %q flags %#x", []any{"/sysroot", uintptr(0x4001)}}, nil, nil), call("verbosef", stub.ExpectArgs{"mounting %q flags %#x", []any{"/sysroot", uintptr(0x4001)}}, nil, nil),
call("bindMount", stub.ExpectArgs{"/host", "/sysroot", uintptr(0x4001), false}, nil, nil), call("bindMount", stub.ExpectArgs{"/host", "/sysroot", uintptr(0x4001), false}, nil, nil),
call("verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &MountProcOp{Target: MustAbs("/proc/")}}}, nil, nil), call("verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &MountProcOp{Target: check.MustAbs("/proc/")}}}, nil, nil),
call("mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0755)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0755)}, nil, nil),
call("mount", stub.ExpectArgs{"proc", "/sysroot/proc", "proc", uintptr(0xe), ""}, nil, nil), call("mount", stub.ExpectArgs{"proc", "/sysroot/proc", "proc", uintptr(0xe), ""}, nil, nil),
/* end apply */ /* end apply */
@ -1160,16 +1161,16 @@ func TestInitEntrypoint(t *testing.T) {
call("getpid", stub.ExpectArgs{}, 1, nil), call("getpid", stub.ExpectArgs{}, 1, nil),
call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil), call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil),
call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{ call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{
Dir: MustAbs("/.hakurei"), Dir: check.MustAbs("/.hakurei"),
Env: []string{"DISPLAY=:0"}, Env: []string{"DISPLAY=:0"},
Path: MustAbs("/bin/zsh"), Path: check.MustAbs("/bin/zsh"),
Args: []string{"zsh", "-c", "exec vim"}, Args: []string{"zsh", "-c", "exec vim"},
ForwardCancel: true, ForwardCancel: true,
AdoptWaitDelay: 5 * time.Second, AdoptWaitDelay: 5 * time.Second,
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(MustAbs("/"), MustAbs("/"), BindDevice).Proc(MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: bits.PresetStrict,
RetainSession: true, RetainSession: true,
@ -1201,7 +1202,7 @@ func TestInitEntrypoint(t *testing.T) {
call("mkdirAll", stub.ExpectArgs{"/sysroot", os.FileMode(0700)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/sysroot", os.FileMode(0700)}, nil, nil),
call("verbosef", stub.ExpectArgs{"mounting %q flags %#x", []any{"/sysroot", uintptr(0x4001)}}, nil, nil), call("verbosef", stub.ExpectArgs{"mounting %q flags %#x", []any{"/sysroot", uintptr(0x4001)}}, nil, nil),
call("bindMount", stub.ExpectArgs{"/host", "/sysroot", uintptr(0x4001), false}, nil, nil), call("bindMount", stub.ExpectArgs{"/host", "/sysroot", uintptr(0x4001), false}, nil, nil),
call("verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &MountProcOp{Target: MustAbs("/proc/")}}}, nil, nil), call("verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &MountProcOp{Target: check.MustAbs("/proc/")}}}, nil, nil),
call("mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0755)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0755)}, nil, nil),
call("mount", stub.ExpectArgs{"proc", "/sysroot/proc", "proc", uintptr(0xe), ""}, nil, nil), call("mount", stub.ExpectArgs{"proc", "/sysroot/proc", "proc", uintptr(0xe), ""}, nil, nil),
/* end apply */ /* end apply */
@ -1222,16 +1223,16 @@ func TestInitEntrypoint(t *testing.T) {
call("getpid", stub.ExpectArgs{}, 1, nil), call("getpid", stub.ExpectArgs{}, 1, nil),
call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil), call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil),
call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{ call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{
Dir: MustAbs("/.hakurei"), Dir: check.MustAbs("/.hakurei"),
Env: []string{"DISPLAY=:0"}, Env: []string{"DISPLAY=:0"},
Path: MustAbs("/bin/zsh"), Path: check.MustAbs("/bin/zsh"),
Args: []string{"zsh", "-c", "exec vim"}, Args: []string{"zsh", "-c", "exec vim"},
ForwardCancel: true, ForwardCancel: true,
AdoptWaitDelay: 5 * time.Second, AdoptWaitDelay: 5 * time.Second,
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(MustAbs("/"), MustAbs("/"), BindDevice).Proc(MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: bits.PresetStrict,
RetainSession: true, RetainSession: true,
@ -1263,7 +1264,7 @@ func TestInitEntrypoint(t *testing.T) {
call("mkdirAll", stub.ExpectArgs{"/sysroot", os.FileMode(0700)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/sysroot", os.FileMode(0700)}, nil, nil),
call("verbosef", stub.ExpectArgs{"mounting %q flags %#x", []any{"/sysroot", uintptr(0x4001)}}, nil, nil), call("verbosef", stub.ExpectArgs{"mounting %q flags %#x", []any{"/sysroot", uintptr(0x4001)}}, nil, nil),
call("bindMount", stub.ExpectArgs{"/host", "/sysroot", uintptr(0x4001), false}, nil, nil), call("bindMount", stub.ExpectArgs{"/host", "/sysroot", uintptr(0x4001), false}, nil, nil),
call("verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &MountProcOp{Target: MustAbs("/proc/")}}}, nil, nil), call("verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &MountProcOp{Target: check.MustAbs("/proc/")}}}, nil, nil),
call("mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0755)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0755)}, nil, nil),
call("mount", stub.ExpectArgs{"proc", "/sysroot/proc", "proc", uintptr(0xe), ""}, nil, nil), call("mount", stub.ExpectArgs{"proc", "/sysroot/proc", "proc", uintptr(0xe), ""}, nil, nil),
/* end apply */ /* end apply */
@ -1285,16 +1286,16 @@ func TestInitEntrypoint(t *testing.T) {
call("getpid", stub.ExpectArgs{}, 1, nil), call("getpid", stub.ExpectArgs{}, 1, nil),
call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil), call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil),
call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{ call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{
Dir: MustAbs("/.hakurei"), Dir: check.MustAbs("/.hakurei"),
Env: []string{"DISPLAY=:0"}, Env: []string{"DISPLAY=:0"},
Path: MustAbs("/bin/zsh"), Path: check.MustAbs("/bin/zsh"),
Args: []string{"zsh", "-c", "exec vim"}, Args: []string{"zsh", "-c", "exec vim"},
ForwardCancel: true, ForwardCancel: true,
AdoptWaitDelay: 5 * time.Second, AdoptWaitDelay: 5 * time.Second,
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(MustAbs("/"), MustAbs("/"), BindDevice).Proc(MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: bits.PresetStrict,
RetainSession: true, RetainSession: true,
@ -1326,7 +1327,7 @@ func TestInitEntrypoint(t *testing.T) {
call("mkdirAll", stub.ExpectArgs{"/sysroot", os.FileMode(0700)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/sysroot", os.FileMode(0700)}, nil, nil),
call("verbosef", stub.ExpectArgs{"mounting %q flags %#x", []any{"/sysroot", uintptr(0x4001)}}, nil, nil), call("verbosef", stub.ExpectArgs{"mounting %q flags %#x", []any{"/sysroot", uintptr(0x4001)}}, nil, nil),
call("bindMount", stub.ExpectArgs{"/host", "/sysroot", uintptr(0x4001), false}, nil, nil), call("bindMount", stub.ExpectArgs{"/host", "/sysroot", uintptr(0x4001), false}, nil, nil),
call("verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &MountProcOp{Target: MustAbs("/proc/")}}}, nil, nil), call("verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &MountProcOp{Target: check.MustAbs("/proc/")}}}, nil, nil),
call("mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0755)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0755)}, nil, nil),
call("mount", stub.ExpectArgs{"proc", "/sysroot/proc", "proc", uintptr(0xe), ""}, nil, nil), call("mount", stub.ExpectArgs{"proc", "/sysroot/proc", "proc", uintptr(0xe), ""}, nil, nil),
/* end apply */ /* end apply */
@ -1349,16 +1350,16 @@ func TestInitEntrypoint(t *testing.T) {
call("getpid", stub.ExpectArgs{}, 1, nil), call("getpid", stub.ExpectArgs{}, 1, nil),
call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil), call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil),
call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{ call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{
Dir: MustAbs("/.hakurei"), Dir: check.MustAbs("/.hakurei"),
Env: []string{"DISPLAY=:0"}, Env: []string{"DISPLAY=:0"},
Path: MustAbs("/bin/zsh"), Path: check.MustAbs("/bin/zsh"),
Args: []string{"zsh", "-c", "exec vim"}, Args: []string{"zsh", "-c", "exec vim"},
ForwardCancel: true, ForwardCancel: true,
AdoptWaitDelay: 5 * time.Second, AdoptWaitDelay: 5 * time.Second,
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(MustAbs("/"), MustAbs("/"), BindDevice).Proc(MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: bits.PresetStrict,
RetainSession: true, RetainSession: true,
@ -1390,7 +1391,7 @@ func TestInitEntrypoint(t *testing.T) {
call("mkdirAll", stub.ExpectArgs{"/sysroot", os.FileMode(0700)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/sysroot", os.FileMode(0700)}, nil, nil),
call("verbosef", stub.ExpectArgs{"mounting %q flags %#x", []any{"/sysroot", uintptr(0x4001)}}, nil, nil), call("verbosef", stub.ExpectArgs{"mounting %q flags %#x", []any{"/sysroot", uintptr(0x4001)}}, nil, nil),
call("bindMount", stub.ExpectArgs{"/host", "/sysroot", uintptr(0x4001), false}, nil, nil), call("bindMount", stub.ExpectArgs{"/host", "/sysroot", uintptr(0x4001), false}, nil, nil),
call("verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &MountProcOp{Target: MustAbs("/proc/")}}}, nil, nil), call("verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &MountProcOp{Target: check.MustAbs("/proc/")}}}, nil, nil),
call("mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0755)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0755)}, nil, nil),
call("mount", stub.ExpectArgs{"proc", "/sysroot/proc", "proc", uintptr(0xe), ""}, nil, nil), call("mount", stub.ExpectArgs{"proc", "/sysroot/proc", "proc", uintptr(0xe), ""}, nil, nil),
/* end apply */ /* end apply */
@ -1414,16 +1415,16 @@ func TestInitEntrypoint(t *testing.T) {
call("getpid", stub.ExpectArgs{}, 1, nil), call("getpid", stub.ExpectArgs{}, 1, nil),
call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil), call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil),
call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{ call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{
Dir: MustAbs("/.hakurei"), Dir: check.MustAbs("/.hakurei"),
Env: []string{"DISPLAY=:0"}, Env: []string{"DISPLAY=:0"},
Path: MustAbs("/bin/zsh"), Path: check.MustAbs("/bin/zsh"),
Args: []string{"zsh", "-c", "exec vim"}, Args: []string{"zsh", "-c", "exec vim"},
ForwardCancel: true, ForwardCancel: true,
AdoptWaitDelay: 5 * time.Second, AdoptWaitDelay: 5 * time.Second,
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(MustAbs("/"), MustAbs("/"), BindDevice).Proc(MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: bits.PresetStrict,
RetainSession: true, RetainSession: true,
@ -1455,7 +1456,7 @@ func TestInitEntrypoint(t *testing.T) {
call("mkdirAll", stub.ExpectArgs{"/sysroot", os.FileMode(0700)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/sysroot", os.FileMode(0700)}, nil, nil),
call("verbosef", stub.ExpectArgs{"mounting %q flags %#x", []any{"/sysroot", uintptr(0x4001)}}, nil, nil), call("verbosef", stub.ExpectArgs{"mounting %q flags %#x", []any{"/sysroot", uintptr(0x4001)}}, nil, nil),
call("bindMount", stub.ExpectArgs{"/host", "/sysroot", uintptr(0x4001), false}, nil, nil), call("bindMount", stub.ExpectArgs{"/host", "/sysroot", uintptr(0x4001), false}, nil, nil),
call("verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &MountProcOp{Target: MustAbs("/proc/")}}}, nil, nil), call("verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &MountProcOp{Target: check.MustAbs("/proc/")}}}, nil, nil),
call("mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0755)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0755)}, nil, nil),
call("mount", stub.ExpectArgs{"proc", "/sysroot/proc", "proc", uintptr(0xe), ""}, nil, nil), call("mount", stub.ExpectArgs{"proc", "/sysroot/proc", "proc", uintptr(0xe), ""}, nil, nil),
/* end apply */ /* end apply */
@ -1480,16 +1481,16 @@ func TestInitEntrypoint(t *testing.T) {
call("getpid", stub.ExpectArgs{}, 1, nil), call("getpid", stub.ExpectArgs{}, 1, nil),
call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil), call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil),
call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{ call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{
Dir: MustAbs("/.hakurei"), Dir: check.MustAbs("/.hakurei"),
Env: []string{"DISPLAY=:0"}, Env: []string{"DISPLAY=:0"},
Path: MustAbs("/bin/zsh"), Path: check.MustAbs("/bin/zsh"),
Args: []string{"zsh", "-c", "exec vim"}, Args: []string{"zsh", "-c", "exec vim"},
ForwardCancel: true, ForwardCancel: true,
AdoptWaitDelay: 5 * time.Second, AdoptWaitDelay: 5 * time.Second,
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(MustAbs("/"), MustAbs("/"), BindDevice).Proc(MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: bits.PresetStrict,
RetainSession: true, RetainSession: true,
@ -1521,7 +1522,7 @@ func TestInitEntrypoint(t *testing.T) {
call("mkdirAll", stub.ExpectArgs{"/sysroot", os.FileMode(0700)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/sysroot", os.FileMode(0700)}, nil, nil),
call("verbosef", stub.ExpectArgs{"mounting %q flags %#x", []any{"/sysroot", uintptr(0x4001)}}, nil, nil), call("verbosef", stub.ExpectArgs{"mounting %q flags %#x", []any{"/sysroot", uintptr(0x4001)}}, nil, nil),
call("bindMount", stub.ExpectArgs{"/host", "/sysroot", uintptr(0x4001), false}, nil, nil), call("bindMount", stub.ExpectArgs{"/host", "/sysroot", uintptr(0x4001), false}, nil, nil),
call("verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &MountProcOp{Target: MustAbs("/proc/")}}}, nil, nil), call("verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &MountProcOp{Target: check.MustAbs("/proc/")}}}, nil, nil),
call("mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0755)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0755)}, nil, nil),
call("mount", stub.ExpectArgs{"proc", "/sysroot/proc", "proc", uintptr(0xe), ""}, nil, nil), call("mount", stub.ExpectArgs{"proc", "/sysroot/proc", "proc", uintptr(0xe), ""}, nil, nil),
/* end apply */ /* end apply */
@ -1554,16 +1555,16 @@ func TestInitEntrypoint(t *testing.T) {
call("getpid", stub.ExpectArgs{}, 1, nil), call("getpid", stub.ExpectArgs{}, 1, nil),
call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil), call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil),
call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{ call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{
Dir: MustAbs("/.hakurei"), Dir: check.MustAbs("/.hakurei"),
Env: []string{"DISPLAY=:0"}, Env: []string{"DISPLAY=:0"},
Path: MustAbs("/bin/zsh"), Path: check.MustAbs("/bin/zsh"),
Args: []string{"zsh", "-c", "exec vim"}, Args: []string{"zsh", "-c", "exec vim"},
ForwardCancel: true, ForwardCancel: true,
AdoptWaitDelay: 5 * time.Second, AdoptWaitDelay: 5 * time.Second,
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(MustAbs("/"), MustAbs("/"), BindDevice).Proc(MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: bits.PresetStrict,
RetainSession: true, RetainSession: true,
@ -1595,7 +1596,7 @@ func TestInitEntrypoint(t *testing.T) {
call("mkdirAll", stub.ExpectArgs{"/sysroot", os.FileMode(0700)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/sysroot", os.FileMode(0700)}, nil, nil),
call("verbosef", stub.ExpectArgs{"mounting %q flags %#x", []any{"/sysroot", uintptr(0x4001)}}, nil, nil), call("verbosef", stub.ExpectArgs{"mounting %q flags %#x", []any{"/sysroot", uintptr(0x4001)}}, nil, nil),
call("bindMount", stub.ExpectArgs{"/host", "/sysroot", uintptr(0x4001), false}, nil, nil), call("bindMount", stub.ExpectArgs{"/host", "/sysroot", uintptr(0x4001), false}, nil, nil),
call("verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &MountProcOp{Target: MustAbs("/proc/")}}}, nil, nil), call("verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &MountProcOp{Target: check.MustAbs("/proc/")}}}, nil, nil),
call("mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0755)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0755)}, nil, nil),
call("mount", stub.ExpectArgs{"proc", "/sysroot/proc", "proc", uintptr(0xe), ""}, nil, nil), call("mount", stub.ExpectArgs{"proc", "/sysroot/proc", "proc", uintptr(0xe), ""}, nil, nil),
/* end apply */ /* end apply */
@ -1661,16 +1662,16 @@ func TestInitEntrypoint(t *testing.T) {
call("getpid", stub.ExpectArgs{}, 1, nil), call("getpid", stub.ExpectArgs{}, 1, nil),
call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil), call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil),
call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{ call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{
Dir: MustAbs("/.hakurei"), Dir: check.MustAbs("/.hakurei"),
Env: []string{"DISPLAY=:0"}, Env: []string{"DISPLAY=:0"},
Path: MustAbs("/bin/zsh"), Path: check.MustAbs("/bin/zsh"),
Args: []string{"zsh", "-c", "exec vim"}, Args: []string{"zsh", "-c", "exec vim"},
ForwardCancel: true, ForwardCancel: true,
AdoptWaitDelay: 5 * time.Second, AdoptWaitDelay: 5 * time.Second,
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(MustAbs("/"), MustAbs("/"), BindDevice).Proc(MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: bits.PresetStrict,
RetainSession: true, RetainSession: true,
@ -1702,7 +1703,7 @@ func TestInitEntrypoint(t *testing.T) {
call("mkdirAll", stub.ExpectArgs{"/sysroot", os.FileMode(0700)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/sysroot", os.FileMode(0700)}, nil, nil),
call("verbosef", stub.ExpectArgs{"mounting %q flags %#x", []any{"/sysroot", uintptr(0x4001)}}, nil, nil), call("verbosef", stub.ExpectArgs{"mounting %q flags %#x", []any{"/sysroot", uintptr(0x4001)}}, nil, nil),
call("bindMount", stub.ExpectArgs{"/host", "/sysroot", uintptr(0x4001), false}, nil, nil), call("bindMount", stub.ExpectArgs{"/host", "/sysroot", uintptr(0x4001), false}, nil, nil),
call("verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &MountProcOp{Target: MustAbs("/proc/")}}}, nil, nil), call("verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &MountProcOp{Target: check.MustAbs("/proc/")}}}, nil, nil),
call("mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0755)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0755)}, nil, nil),
call("mount", stub.ExpectArgs{"proc", "/sysroot/proc", "proc", uintptr(0xe), ""}, nil, nil), call("mount", stub.ExpectArgs{"proc", "/sysroot/proc", "proc", uintptr(0xe), ""}, nil, nil),
/* end apply */ /* end apply */
@ -1769,16 +1770,16 @@ func TestInitEntrypoint(t *testing.T) {
call("getpid", stub.ExpectArgs{}, 1, nil), call("getpid", stub.ExpectArgs{}, 1, nil),
call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil), call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil),
call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{ call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{
Dir: MustAbs("/.hakurei"), Dir: check.MustAbs("/.hakurei"),
Env: []string{"DISPLAY=:0"}, Env: []string{"DISPLAY=:0"},
Path: MustAbs("/bin/zsh"), Path: check.MustAbs("/bin/zsh"),
Args: []string{"zsh", "-c", "exec vim"}, Args: []string{"zsh", "-c", "exec vim"},
ForwardCancel: true, ForwardCancel: true,
AdoptWaitDelay: 5 * time.Second, AdoptWaitDelay: 5 * time.Second,
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(MustAbs("/"), MustAbs("/"), BindDevice).Proc(MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: bits.PresetStrict,
RetainSession: true, RetainSession: true,
@ -1810,7 +1811,7 @@ func TestInitEntrypoint(t *testing.T) {
call("mkdirAll", stub.ExpectArgs{"/sysroot", os.FileMode(0700)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/sysroot", os.FileMode(0700)}, nil, nil),
call("verbosef", stub.ExpectArgs{"mounting %q flags %#x", []any{"/sysroot", uintptr(0x4001)}}, nil, nil), call("verbosef", stub.ExpectArgs{"mounting %q flags %#x", []any{"/sysroot", uintptr(0x4001)}}, nil, nil),
call("bindMount", stub.ExpectArgs{"/host", "/sysroot", uintptr(0x4001), false}, nil, nil), call("bindMount", stub.ExpectArgs{"/host", "/sysroot", uintptr(0x4001), false}, nil, nil),
call("verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &MountProcOp{Target: MustAbs("/proc/")}}}, nil, nil), call("verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &MountProcOp{Target: check.MustAbs("/proc/")}}}, nil, nil),
call("mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0755)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0755)}, nil, nil),
call("mount", stub.ExpectArgs{"proc", "/sysroot/proc", "proc", uintptr(0xe), ""}, nil, nil), call("mount", stub.ExpectArgs{"proc", "/sysroot/proc", "proc", uintptr(0xe), ""}, nil, nil),
/* end apply */ /* end apply */
@ -1880,15 +1881,15 @@ func TestInitEntrypoint(t *testing.T) {
call("setPtracer", stub.ExpectArgs{uintptr(0)}, 0, stub.UniqueError(14)), call("setPtracer", stub.ExpectArgs{uintptr(0)}, 0, stub.UniqueError(14)),
call("verbosef", stub.ExpectArgs{"cannot enable ptrace protection via Yama LSM: %v", []any{stub.UniqueError(14)}}, nil, nil), call("verbosef", stub.ExpectArgs{"cannot enable ptrace protection via Yama LSM: %v", []any{stub.UniqueError(14)}}, nil, nil),
call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{ call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{
Dir: MustAbs("/.hakurei/nonexistent"), Dir: check.MustAbs("/.hakurei/nonexistent"),
Path: MustAbs("/run/current-system/sw/bin/bash"), Path: check.MustAbs("/run/current-system/sw/bin/bash"),
Args: []string{"bash", "-c", "false"}, Args: []string{"bash", "-c", "false"},
ForwardCancel: true, ForwardCancel: true,
AdoptWaitDelay: 5 * time.Second, AdoptWaitDelay: 5 * time.Second,
Uid: 1 << 24, Uid: 1 << 24,
Gid: 1 << 47, Gid: 1 << 47,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(MustAbs("/"), MustAbs("/"), BindDevice).Proc(MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompDisable: true, SeccompDisable: true,
ParentPerm: 0750, ParentPerm: 0750,
@ -1919,7 +1920,7 @@ func TestInitEntrypoint(t *testing.T) {
call("mkdirAll", stub.ExpectArgs{"/sysroot", os.FileMode(0700)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/sysroot", os.FileMode(0700)}, nil, nil),
call("verbosef", stub.ExpectArgs{"mounting %q flags %#x", []any{"/sysroot", uintptr(0x4001)}}, nil, nil), call("verbosef", stub.ExpectArgs{"mounting %q flags %#x", []any{"/sysroot", uintptr(0x4001)}}, nil, nil),
call("bindMount", stub.ExpectArgs{"/host", "/sysroot", uintptr(0x4001), false}, nil, nil), call("bindMount", stub.ExpectArgs{"/host", "/sysroot", uintptr(0x4001), false}, nil, nil),
call("verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &MountProcOp{Target: MustAbs("/proc/")}}}, nil, nil), call("verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &MountProcOp{Target: check.MustAbs("/proc/")}}}, nil, nil),
call("mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0750)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0750)}, nil, nil),
call("mount", stub.ExpectArgs{"proc", "/sysroot/proc", "proc", uintptr(0xe), ""}, nil, nil), call("mount", stub.ExpectArgs{"proc", "/sysroot/proc", "proc", uintptr(0xe), ""}, nil, nil),
/* end apply */ /* end apply */
@ -1980,7 +1981,7 @@ func TestInitEntrypoint(t *testing.T) {
call("newFile", stub.ExpectArgs{uintptr(0x3a), "extra file 0"}, (*os.File)(nil), nil), call("newFile", stub.ExpectArgs{uintptr(0x3a), "extra file 0"}, (*os.File)(nil), nil),
call("newFile", stub.ExpectArgs{uintptr(0x3b), "extra file 1"}, (*os.File)(nil), nil), call("newFile", stub.ExpectArgs{uintptr(0x3b), "extra file 1"}, (*os.File)(nil), nil),
call("umask", stub.ExpectArgs{022}, 0, nil), call("umask", stub.ExpectArgs{022}, 0, nil),
call("verbosef", stub.ExpectArgs{"starting initial program %s", []any{MustAbs("/run/current-system/sw/bin/bash")}}, nil, nil), call("verbosef", stub.ExpectArgs{"starting initial program %s", []any{check.MustAbs("/run/current-system/sw/bin/bash")}}, nil, nil),
call("start", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent"}, nil, stub.UniqueError(12)), call("start", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent"}, nil, stub.UniqueError(12)),
call("fatalf", stub.ExpectArgs{"%v", []any{stub.UniqueError(12)}}, nil, nil), call("fatalf", stub.ExpectArgs{"%v", []any{stub.UniqueError(12)}}, nil, nil),
}, },
@ -1994,15 +1995,15 @@ func TestInitEntrypoint(t *testing.T) {
call("setPtracer", stub.ExpectArgs{uintptr(0)}, 0, stub.UniqueError(11)), call("setPtracer", stub.ExpectArgs{uintptr(0)}, 0, stub.UniqueError(11)),
call("verbosef", stub.ExpectArgs{"cannot enable ptrace protection via Yama LSM: %v", []any{stub.UniqueError(11)}}, nil, nil), call("verbosef", stub.ExpectArgs{"cannot enable ptrace protection via Yama LSM: %v", []any{stub.UniqueError(11)}}, nil, nil),
call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{ call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{
Dir: MustAbs("/.hakurei/nonexistent"), Dir: check.MustAbs("/.hakurei/nonexistent"),
Path: MustAbs("/run/current-system/sw/bin/bash"), Path: check.MustAbs("/run/current-system/sw/bin/bash"),
Args: []string{"bash", "-c", "false"}, Args: []string{"bash", "-c", "false"},
ForwardCancel: true, ForwardCancel: true,
AdoptWaitDelay: 5 * time.Nanosecond, AdoptWaitDelay: 5 * time.Nanosecond,
Uid: 1 << 24, Uid: 1 << 24,
Gid: 1 << 47, Gid: 1 << 47,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(MustAbs("/"), MustAbs("/"), BindDevice).Proc(MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompDisable: true, SeccompDisable: true,
ParentPerm: 0750, ParentPerm: 0750,
@ -2033,7 +2034,7 @@ func TestInitEntrypoint(t *testing.T) {
call("mkdirAll", stub.ExpectArgs{"/sysroot", os.FileMode(0700)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/sysroot", os.FileMode(0700)}, nil, nil),
call("verbosef", stub.ExpectArgs{"mounting %q flags %#x", []any{"/sysroot", uintptr(0x4001)}}, nil, nil), call("verbosef", stub.ExpectArgs{"mounting %q flags %#x", []any{"/sysroot", uintptr(0x4001)}}, nil, nil),
call("bindMount", stub.ExpectArgs{"/host", "/sysroot", uintptr(0x4001), false}, nil, nil), call("bindMount", stub.ExpectArgs{"/host", "/sysroot", uintptr(0x4001), false}, nil, nil),
call("verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &MountProcOp{Target: MustAbs("/proc/")}}}, nil, nil), call("verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &MountProcOp{Target: check.MustAbs("/proc/")}}}, nil, nil),
call("mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0750)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0750)}, nil, nil),
call("mount", stub.ExpectArgs{"proc", "/sysroot/proc", "proc", uintptr(0xe), ""}, nil, nil), call("mount", stub.ExpectArgs{"proc", "/sysroot/proc", "proc", uintptr(0xe), ""}, nil, nil),
/* end apply */ /* end apply */
@ -2058,7 +2059,7 @@ func TestInitEntrypoint(t *testing.T) {
call("newFile", stub.ExpectArgs{uintptr(0x3a), "extra file 0"}, (*os.File)(nil), nil), call("newFile", stub.ExpectArgs{uintptr(0x3a), "extra file 0"}, (*os.File)(nil), nil),
call("newFile", stub.ExpectArgs{uintptr(0x3b), "extra file 1"}, (*os.File)(nil), nil), call("newFile", stub.ExpectArgs{uintptr(0x3b), "extra file 1"}, (*os.File)(nil), nil),
call("umask", stub.ExpectArgs{022}, 0, nil), call("umask", stub.ExpectArgs{022}, 0, nil),
call("verbosef", stub.ExpectArgs{"starting initial program %s", []any{MustAbs("/run/current-system/sw/bin/bash")}}, nil, nil), call("verbosef", stub.ExpectArgs{"starting initial program %s", []any{check.MustAbs("/run/current-system/sw/bin/bash")}}, nil, nil),
call("start", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent"}, &os.Process{Pid: 0xbad}, nil), call("start", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent"}, &os.Process{Pid: 0xbad}, nil),
call("suspend", stub.ExpectArgs{}, true, nil), call("suspend", stub.ExpectArgs{}, true, nil),
call("printf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(10)}}, nil, nil), call("printf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(10)}}, nil, nil),
@ -2094,15 +2095,15 @@ func TestInitEntrypoint(t *testing.T) {
call("setPtracer", stub.ExpectArgs{uintptr(0)}, 0, stub.UniqueError(8)), call("setPtracer", stub.ExpectArgs{uintptr(0)}, 0, stub.UniqueError(8)),
call("verbosef", stub.ExpectArgs{"cannot enable ptrace protection via Yama LSM: %v", []any{stub.UniqueError(8)}}, nil, nil), call("verbosef", stub.ExpectArgs{"cannot enable ptrace protection via Yama LSM: %v", []any{stub.UniqueError(8)}}, nil, nil),
call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{ call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{
Dir: MustAbs("/.hakurei/nonexistent"), Dir: check.MustAbs("/.hakurei/nonexistent"),
Path: MustAbs("/run/current-system/sw/bin/bash"), Path: check.MustAbs("/run/current-system/sw/bin/bash"),
Args: []string{"bash", "-c", "false"}, Args: []string{"bash", "-c", "false"},
ForwardCancel: true, ForwardCancel: true,
AdoptWaitDelay: 5 * time.Nanosecond, AdoptWaitDelay: 5 * time.Nanosecond,
Uid: 1 << 24, Uid: 1 << 24,
Gid: 1 << 47, Gid: 1 << 47,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(MustAbs("/"), MustAbs("/"), BindDevice).Proc(MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompDisable: true, SeccompDisable: true,
ParentPerm: 0750, ParentPerm: 0750,
@ -2133,7 +2134,7 @@ func TestInitEntrypoint(t *testing.T) {
call("mkdirAll", stub.ExpectArgs{"/sysroot", os.FileMode(0700)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/sysroot", os.FileMode(0700)}, nil, nil),
call("verbosef", stub.ExpectArgs{"mounting %q flags %#x", []any{"/sysroot", uintptr(0x4001)}}, nil, nil), call("verbosef", stub.ExpectArgs{"mounting %q flags %#x", []any{"/sysroot", uintptr(0x4001)}}, nil, nil),
call("bindMount", stub.ExpectArgs{"/host", "/sysroot", uintptr(0x4001), false}, nil, nil), call("bindMount", stub.ExpectArgs{"/host", "/sysroot", uintptr(0x4001), false}, nil, nil),
call("verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &MountProcOp{Target: MustAbs("/proc/")}}}, nil, nil), call("verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &MountProcOp{Target: check.MustAbs("/proc/")}}}, nil, nil),
call("mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0750)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0750)}, nil, nil),
call("mount", stub.ExpectArgs{"proc", "/sysroot/proc", "proc", uintptr(0xe), ""}, nil, nil), call("mount", stub.ExpectArgs{"proc", "/sysroot/proc", "proc", uintptr(0xe), ""}, nil, nil),
/* end apply */ /* end apply */
@ -2158,7 +2159,7 @@ func TestInitEntrypoint(t *testing.T) {
call("newFile", stub.ExpectArgs{uintptr(0x3a), "extra file 0"}, (*os.File)(nil), nil), call("newFile", stub.ExpectArgs{uintptr(0x3a), "extra file 0"}, (*os.File)(nil), nil),
call("newFile", stub.ExpectArgs{uintptr(0x3b), "extra file 1"}, (*os.File)(nil), nil), call("newFile", stub.ExpectArgs{uintptr(0x3b), "extra file 1"}, (*os.File)(nil), nil),
call("umask", stub.ExpectArgs{022}, 0, nil), call("umask", stub.ExpectArgs{022}, 0, nil),
call("verbosef", stub.ExpectArgs{"starting initial program %s", []any{MustAbs("/run/current-system/sw/bin/bash")}}, nil, nil), call("verbosef", stub.ExpectArgs{"starting initial program %s", []any{check.MustAbs("/run/current-system/sw/bin/bash")}}, nil, nil),
call("start", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent"}, &os.Process{Pid: 0xbad}, nil), call("start", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent"}, &os.Process{Pid: 0xbad}, nil),
call("suspend", stub.ExpectArgs{}, true, nil), call("suspend", stub.ExpectArgs{}, true, nil),
call("printf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(7)}}, nil, nil), call("printf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(7)}}, nil, nil),
@ -2185,15 +2186,15 @@ func TestInitEntrypoint(t *testing.T) {
call("setPtracer", stub.ExpectArgs{uintptr(0)}, 0, stub.UniqueError(6)), call("setPtracer", stub.ExpectArgs{uintptr(0)}, 0, stub.UniqueError(6)),
call("verbosef", stub.ExpectArgs{"cannot enable ptrace protection via Yama LSM: %v", []any{stub.UniqueError(6)}}, nil, nil), call("verbosef", stub.ExpectArgs{"cannot enable ptrace protection via Yama LSM: %v", []any{stub.UniqueError(6)}}, nil, nil),
call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{ call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{
Dir: MustAbs("/.hakurei/nonexistent"), Dir: check.MustAbs("/.hakurei/nonexistent"),
Path: MustAbs("/run/current-system/sw/bin/bash"), Path: check.MustAbs("/run/current-system/sw/bin/bash"),
Args: []string{"bash", "-c", "false"}, Args: []string{"bash", "-c", "false"},
ForwardCancel: true, ForwardCancel: true,
AdoptWaitDelay: 5 * time.Nanosecond, AdoptWaitDelay: 5 * time.Nanosecond,
Uid: 1 << 24, Uid: 1 << 24,
Gid: 1 << 47, Gid: 1 << 47,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(MustAbs("/"), MustAbs("/"), BindDevice).Proc(MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompDisable: true, SeccompDisable: true,
ParentPerm: 0750, ParentPerm: 0750,
@ -2224,7 +2225,7 @@ func TestInitEntrypoint(t *testing.T) {
call("mkdirAll", stub.ExpectArgs{"/sysroot", os.FileMode(0700)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/sysroot", os.FileMode(0700)}, nil, nil),
call("verbosef", stub.ExpectArgs{"mounting %q flags %#x", []any{"/sysroot", uintptr(0x4001)}}, nil, nil), call("verbosef", stub.ExpectArgs{"mounting %q flags %#x", []any{"/sysroot", uintptr(0x4001)}}, nil, nil),
call("bindMount", stub.ExpectArgs{"/host", "/sysroot", uintptr(0x4001), false}, nil, nil), call("bindMount", stub.ExpectArgs{"/host", "/sysroot", uintptr(0x4001), false}, nil, nil),
call("verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &MountProcOp{Target: MustAbs("/proc/")}}}, nil, nil), call("verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &MountProcOp{Target: check.MustAbs("/proc/")}}}, nil, nil),
call("mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0750)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0750)}, nil, nil),
call("mount", stub.ExpectArgs{"proc", "/sysroot/proc", "proc", uintptr(0xe), ""}, nil, nil), call("mount", stub.ExpectArgs{"proc", "/sysroot/proc", "proc", uintptr(0xe), ""}, nil, nil),
/* end apply */ /* end apply */
@ -2249,7 +2250,7 @@ func TestInitEntrypoint(t *testing.T) {
call("newFile", stub.ExpectArgs{uintptr(0x3a), "extra file 0"}, (*os.File)(nil), nil), call("newFile", stub.ExpectArgs{uintptr(0x3a), "extra file 0"}, (*os.File)(nil), nil),
call("newFile", stub.ExpectArgs{uintptr(0x3b), "extra file 1"}, (*os.File)(nil), nil), call("newFile", stub.ExpectArgs{uintptr(0x3b), "extra file 1"}, (*os.File)(nil), nil),
call("umask", stub.ExpectArgs{022}, 0, nil), call("umask", stub.ExpectArgs{022}, 0, nil),
call("verbosef", stub.ExpectArgs{"starting initial program %s", []any{MustAbs("/run/current-system/sw/bin/bash")}}, nil, nil), call("verbosef", stub.ExpectArgs{"starting initial program %s", []any{check.MustAbs("/run/current-system/sw/bin/bash")}}, nil, nil),
call("start", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent"}, &os.Process{Pid: 0xbad}, nil), call("start", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent"}, &os.Process{Pid: 0xbad}, nil),
call("suspend", stub.ExpectArgs{}, true, nil), call("suspend", stub.ExpectArgs{}, true, nil),
call("printf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(5)}}, nil, nil), call("printf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(5)}}, nil, nil),
@ -2278,15 +2279,15 @@ func TestInitEntrypoint(t *testing.T) {
call("setPtracer", stub.ExpectArgs{uintptr(0)}, 0, stub.UniqueError(4)), call("setPtracer", stub.ExpectArgs{uintptr(0)}, 0, stub.UniqueError(4)),
call("verbosef", stub.ExpectArgs{"cannot enable ptrace protection via Yama LSM: %v", []any{stub.UniqueError(4)}}, nil, nil), call("verbosef", stub.ExpectArgs{"cannot enable ptrace protection via Yama LSM: %v", []any{stub.UniqueError(4)}}, nil, nil),
call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{ call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{
Dir: MustAbs("/.hakurei/nonexistent"), Dir: check.MustAbs("/.hakurei/nonexistent"),
Path: MustAbs("/run/current-system/sw/bin/bash"), Path: check.MustAbs("/run/current-system/sw/bin/bash"),
Args: []string{"bash", "-c", "false"}, Args: []string{"bash", "-c", "false"},
ForwardCancel: true, ForwardCancel: true,
AdoptWaitDelay: 5 * time.Second, AdoptWaitDelay: 5 * time.Second,
Uid: 1 << 24, Uid: 1 << 24,
Gid: 1 << 47, Gid: 1 << 47,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(MustAbs("/"), MustAbs("/"), BindDevice).Proc(MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompDisable: true, SeccompDisable: true,
ParentPerm: 0750, ParentPerm: 0750,
@ -2317,7 +2318,7 @@ func TestInitEntrypoint(t *testing.T) {
call("mkdirAll", stub.ExpectArgs{"/sysroot", os.FileMode(0700)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/sysroot", os.FileMode(0700)}, nil, nil),
call("verbosef", stub.ExpectArgs{"mounting %q flags %#x", []any{"/sysroot", uintptr(0x4001)}}, nil, nil), call("verbosef", stub.ExpectArgs{"mounting %q flags %#x", []any{"/sysroot", uintptr(0x4001)}}, nil, nil),
call("bindMount", stub.ExpectArgs{"/host", "/sysroot", uintptr(0x4001), false}, nil, nil), call("bindMount", stub.ExpectArgs{"/host", "/sysroot", uintptr(0x4001), false}, nil, nil),
call("verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &MountProcOp{Target: MustAbs("/proc/")}}}, nil, nil), call("verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &MountProcOp{Target: check.MustAbs("/proc/")}}}, nil, nil),
call("mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0750)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0750)}, nil, nil),
call("mount", stub.ExpectArgs{"proc", "/sysroot/proc", "proc", uintptr(0xe), ""}, nil, nil), call("mount", stub.ExpectArgs{"proc", "/sysroot/proc", "proc", uintptr(0xe), ""}, nil, nil),
/* end apply */ /* end apply */
@ -2342,7 +2343,7 @@ func TestInitEntrypoint(t *testing.T) {
call("newFile", stub.ExpectArgs{uintptr(0x3a), "extra file 0"}, (*os.File)(nil), nil), call("newFile", stub.ExpectArgs{uintptr(0x3a), "extra file 0"}, (*os.File)(nil), nil),
call("newFile", stub.ExpectArgs{uintptr(0x3b), "extra file 1"}, (*os.File)(nil), nil), call("newFile", stub.ExpectArgs{uintptr(0x3b), "extra file 1"}, (*os.File)(nil), nil),
call("umask", stub.ExpectArgs{022}, 0, nil), call("umask", stub.ExpectArgs{022}, 0, nil),
call("verbosef", stub.ExpectArgs{"starting initial program %s", []any{MustAbs("/run/current-system/sw/bin/bash")}}, nil, nil), call("verbosef", stub.ExpectArgs{"starting initial program %s", []any{check.MustAbs("/run/current-system/sw/bin/bash")}}, nil, nil),
call("start", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent"}, &os.Process{Pid: 0xbad}, nil), call("start", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent"}, &os.Process{Pid: 0xbad}, nil),
call("suspend", stub.ExpectArgs{}, true, nil), call("suspend", stub.ExpectArgs{}, true, nil),
call("printf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(3)}}, nil, nil), call("printf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(3)}}, nil, nil),
@ -2378,15 +2379,15 @@ func TestInitEntrypoint(t *testing.T) {
call("setPtracer", stub.ExpectArgs{uintptr(0)}, 0, stub.UniqueError(2)), call("setPtracer", stub.ExpectArgs{uintptr(0)}, 0, stub.UniqueError(2)),
call("verbosef", stub.ExpectArgs{"cannot enable ptrace protection via Yama LSM: %v", []any{stub.UniqueError(2)}}, nil, nil), call("verbosef", stub.ExpectArgs{"cannot enable ptrace protection via Yama LSM: %v", []any{stub.UniqueError(2)}}, nil, nil),
call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{ call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{
Dir: MustAbs("/.hakurei/nonexistent"), Dir: check.MustAbs("/.hakurei/nonexistent"),
Path: MustAbs("/run/current-system/sw/bin/bash"), Path: check.MustAbs("/run/current-system/sw/bin/bash"),
Args: []string{"bash", "-c", "false"}, Args: []string{"bash", "-c", "false"},
ForwardCancel: true, ForwardCancel: true,
AdoptWaitDelay: 5 * time.Second, AdoptWaitDelay: 5 * time.Second,
Uid: 1 << 24, Uid: 1 << 24,
Gid: 1 << 47, Gid: 1 << 47,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(MustAbs("/"), MustAbs("/"), BindDevice).Proc(MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompDisable: true, SeccompDisable: true,
ParentPerm: 0750, ParentPerm: 0750,
@ -2417,7 +2418,7 @@ func TestInitEntrypoint(t *testing.T) {
call("mkdirAll", stub.ExpectArgs{"/sysroot", os.FileMode(0700)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/sysroot", os.FileMode(0700)}, nil, nil),
call("verbosef", stub.ExpectArgs{"mounting %q flags %#x", []any{"/sysroot", uintptr(0x4001)}}, nil, nil), call("verbosef", stub.ExpectArgs{"mounting %q flags %#x", []any{"/sysroot", uintptr(0x4001)}}, nil, nil),
call("bindMount", stub.ExpectArgs{"/host", "/sysroot", uintptr(0x4001), false}, nil, nil), call("bindMount", stub.ExpectArgs{"/host", "/sysroot", uintptr(0x4001), false}, nil, nil),
call("verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &MountProcOp{Target: MustAbs("/proc/")}}}, nil, nil), call("verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &MountProcOp{Target: check.MustAbs("/proc/")}}}, nil, nil),
call("mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0750)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0750)}, nil, nil),
call("mount", stub.ExpectArgs{"proc", "/sysroot/proc", "proc", uintptr(0xe), ""}, nil, nil), call("mount", stub.ExpectArgs{"proc", "/sysroot/proc", "proc", uintptr(0xe), ""}, nil, nil),
/* end apply */ /* end apply */
@ -2478,7 +2479,7 @@ func TestInitEntrypoint(t *testing.T) {
call("newFile", stub.ExpectArgs{uintptr(0x3a), "extra file 0"}, (*os.File)(nil), nil), call("newFile", stub.ExpectArgs{uintptr(0x3a), "extra file 0"}, (*os.File)(nil), nil),
call("newFile", stub.ExpectArgs{uintptr(0x3b), "extra file 1"}, (*os.File)(nil), nil), call("newFile", stub.ExpectArgs{uintptr(0x3b), "extra file 1"}, (*os.File)(nil), nil),
call("umask", stub.ExpectArgs{022}, 0, nil), call("umask", stub.ExpectArgs{022}, 0, nil),
call("verbosef", stub.ExpectArgs{"starting initial program %s", []any{MustAbs("/run/current-system/sw/bin/bash")}}, nil, nil), call("verbosef", stub.ExpectArgs{"starting initial program %s", []any{check.MustAbs("/run/current-system/sw/bin/bash")}}, nil, nil),
call("start", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent"}, &os.Process{Pid: 0xbad}, nil), call("start", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent"}, &os.Process{Pid: 0xbad}, nil),
call("suspend", stub.ExpectArgs{}, true, nil), call("suspend", stub.ExpectArgs{}, true, nil),
call("printf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(1)}}, nil, nil), call("printf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(1)}}, nil, nil),
@ -2513,16 +2514,16 @@ func TestInitEntrypoint(t *testing.T) {
call("getpid", stub.ExpectArgs{}, 1, nil), call("getpid", stub.ExpectArgs{}, 1, nil),
call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil), call("setPtracer", stub.ExpectArgs{uintptr(0)}, nil, nil),
call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{ call("receive", stub.ExpectArgs{"HAKUREI_SETUP", new(initParams), new(uintptr), &initParams{Params{
Dir: MustAbs("/.hakurei"), Dir: check.MustAbs("/.hakurei"),
Env: []string{"DISPLAY=:0"}, Env: []string{"DISPLAY=:0"},
Path: MustAbs("/bin/zsh"), Path: check.MustAbs("/bin/zsh"),
Args: []string{"zsh", "-c", "exec vim"}, Args: []string{"zsh", "-c", "exec vim"},
ForwardCancel: true, ForwardCancel: true,
AdoptWaitDelay: 5 * time.Second, AdoptWaitDelay: 5 * time.Second,
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(MustAbs("/"), MustAbs("/"), BindDevice).Proc(MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: bits.PresetStrict,
RetainSession: true, RetainSession: true,
@ -2554,7 +2555,7 @@ func TestInitEntrypoint(t *testing.T) {
call("mkdirAll", stub.ExpectArgs{"/sysroot", os.FileMode(0700)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/sysroot", os.FileMode(0700)}, nil, nil),
call("verbosef", stub.ExpectArgs{"mounting %q flags %#x", []any{"/sysroot", uintptr(0x4001)}}, nil, nil), call("verbosef", stub.ExpectArgs{"mounting %q flags %#x", []any{"/sysroot", uintptr(0x4001)}}, nil, nil),
call("bindMount", stub.ExpectArgs{"/host", "/sysroot", uintptr(0x4001), false}, nil, nil), call("bindMount", stub.ExpectArgs{"/host", "/sysroot", uintptr(0x4001), false}, nil, nil),
call("verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &MountProcOp{Target: MustAbs("/proc/")}}}, nil, nil), call("verbosef", stub.ExpectArgs{"%s %s", []any{"mounting", &MountProcOp{Target: check.MustAbs("/proc/")}}}, nil, nil),
call("mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0755)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0755)}, nil, nil),
call("mount", stub.ExpectArgs{"proc", "/sysroot/proc", "proc", uintptr(0xe), ""}, nil, nil), call("mount", stub.ExpectArgs{"proc", "/sysroot/proc", "proc", uintptr(0xe), ""}, nil, nil),
/* end apply */ /* end apply */
@ -2618,7 +2619,7 @@ func TestInitEntrypoint(t *testing.T) {
call("newFile", stub.ExpectArgs{uintptr(11), "extra file 1"}, (*os.File)(nil), nil), call("newFile", stub.ExpectArgs{uintptr(11), "extra file 1"}, (*os.File)(nil), nil),
call("newFile", stub.ExpectArgs{uintptr(12), "extra file 2"}, (*os.File)(nil), nil), call("newFile", stub.ExpectArgs{uintptr(12), "extra file 2"}, (*os.File)(nil), nil),
call("umask", stub.ExpectArgs{022}, 0, nil), call("umask", stub.ExpectArgs{022}, 0, nil),
call("verbosef", stub.ExpectArgs{"starting initial program %s", []any{MustAbs("/bin/zsh")}}, nil, nil), call("verbosef", stub.ExpectArgs{"starting initial program %s", []any{check.MustAbs("/bin/zsh")}}, nil, nil),
call("start", stub.ExpectArgs{"/bin/zsh", []string{"zsh", "-c", "exec vim"}, []string{"DISPLAY=:0"}, "/.hakurei"}, &os.Process{Pid: 0xcafe}, nil), call("start", stub.ExpectArgs{"/bin/zsh", []string{"zsh", "-c", "exec vim"}, []string{"DISPLAY=:0"}, "/.hakurei"}, &os.Process{Pid: 0xcafe}, nil),
call("suspend", stub.ExpectArgs{}, true, nil), call("suspend", stub.ExpectArgs{}, true, nil),
call("printf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(0)}}, nil, nil), call("printf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(0)}}, nil, nil),

View File

@ -5,12 +5,14 @@ import (
"fmt" "fmt"
"os" "os"
"syscall" "syscall"
"hakurei.app/container/check"
) )
func init() { gob.Register(new(BindMountOp)) } func init() { gob.Register(new(BindMountOp)) }
// Bind appends an [Op] that bind mounts host path [BindMountOp.Source] on container path [BindMountOp.Target]. // Bind appends an [Op] that bind mounts host path [BindMountOp.Source] on container path [BindMountOp.Target].
func (f *Ops) Bind(source, target *Absolute, flags int) *Ops { func (f *Ops) Bind(source, target *check.Absolute, flags int) *Ops {
*f = append(*f, &BindMountOp{nil, source, target, flags}) *f = append(*f, &BindMountOp{nil, source, target, flags})
return f return f
} }
@ -18,7 +20,7 @@ func (f *Ops) Bind(source, target *Absolute, flags int) *Ops {
// BindMountOp bind mounts host path Source on container path Target. // BindMountOp bind mounts host path Source on container path Target.
// Note that Flags uses bits declared in this package and should not be set with constants in [syscall]. // Note that Flags uses bits declared in this package and should not be set with constants in [syscall].
type BindMountOp struct { type BindMountOp struct {
sourceFinal, Source, Target *Absolute sourceFinal, Source, Target *check.Absolute
Flags int Flags int
} }
@ -54,7 +56,7 @@ func (b *BindMountOp) early(_ *setupState, k syscallDispatcher) error {
} }
return err return err
} else { } else {
b.sourceFinal, err = NewAbs(pathname) b.sourceFinal, err = check.NewAbs(pathname)
return err return err
} }
} }

View File

@ -6,29 +6,30 @@ import (
"syscall" "syscall"
"testing" "testing"
"hakurei.app/container/check"
"hakurei.app/container/stub" "hakurei.app/container/stub"
) )
func TestBindMountOp(t *testing.T) { func TestBindMountOp(t *testing.T) {
checkOpBehaviour(t, []opBehaviourTestCase{ checkOpBehaviour(t, []opBehaviourTestCase{
{"ENOENT not optional", new(Params), &BindMountOp{ {"ENOENT not optional", new(Params), &BindMountOp{
Source: MustAbs("/bin/"), Source: check.MustAbs("/bin/"),
Target: MustAbs("/bin/"), Target: check.MustAbs("/bin/"),
}, []stub.Call{ }, []stub.Call{
call("evalSymlinks", stub.ExpectArgs{"/bin/"}, "", syscall.ENOENT), call("evalSymlinks", stub.ExpectArgs{"/bin/"}, "", syscall.ENOENT),
}, syscall.ENOENT, nil, nil}, }, syscall.ENOENT, nil, nil},
{"skip optional", new(Params), &BindMountOp{ {"skip optional", new(Params), &BindMountOp{
Source: MustAbs("/bin/"), Source: check.MustAbs("/bin/"),
Target: MustAbs("/bin/"), Target: check.MustAbs("/bin/"),
Flags: BindOptional, Flags: BindOptional,
}, []stub.Call{ }, []stub.Call{
call("evalSymlinks", stub.ExpectArgs{"/bin/"}, "", syscall.ENOENT), call("evalSymlinks", stub.ExpectArgs{"/bin/"}, "", syscall.ENOENT),
}, nil, nil, nil}, }, nil, nil, nil},
{"success optional", new(Params), &BindMountOp{ {"success optional", new(Params), &BindMountOp{
Source: MustAbs("/bin/"), Source: check.MustAbs("/bin/"),
Target: MustAbs("/bin/"), Target: check.MustAbs("/bin/"),
Flags: BindOptional, Flags: BindOptional,
}, []stub.Call{ }, []stub.Call{
call("evalSymlinks", stub.ExpectArgs{"/bin/"}, "/usr/bin", nil), call("evalSymlinks", stub.ExpectArgs{"/bin/"}, "/usr/bin", nil),
@ -40,8 +41,8 @@ func TestBindMountOp(t *testing.T) {
}, nil}, }, nil},
{"ensureFile device", new(Params), &BindMountOp{ {"ensureFile device", new(Params), &BindMountOp{
Source: MustAbs("/dev/null"), Source: check.MustAbs("/dev/null"),
Target: MustAbs("/dev/null"), Target: check.MustAbs("/dev/null"),
Flags: BindWritable | BindDevice, Flags: BindWritable | BindDevice,
}, []stub.Call{ }, []stub.Call{
call("evalSymlinks", stub.ExpectArgs{"/dev/null"}, "/dev/null", nil), call("evalSymlinks", stub.ExpectArgs{"/dev/null"}, "/dev/null", nil),
@ -51,16 +52,16 @@ func TestBindMountOp(t *testing.T) {
}, stub.UniqueError(5)}, }, stub.UniqueError(5)},
{"mkdirAll ensure", new(Params), &BindMountOp{ {"mkdirAll ensure", new(Params), &BindMountOp{
Source: MustAbs("/bin/"), Source: check.MustAbs("/bin/"),
Target: MustAbs("/bin/"), Target: check.MustAbs("/bin/"),
Flags: BindEnsure, Flags: BindEnsure,
}, []stub.Call{ }, []stub.Call{
call("mkdirAll", stub.ExpectArgs{"/bin/", os.FileMode(0700)}, nil, stub.UniqueError(4)), call("mkdirAll", stub.ExpectArgs{"/bin/", os.FileMode(0700)}, nil, stub.UniqueError(4)),
}, stub.UniqueError(4), nil, nil}, }, stub.UniqueError(4), nil, nil},
{"success ensure", new(Params), &BindMountOp{ {"success ensure", new(Params), &BindMountOp{
Source: MustAbs("/bin/"), Source: check.MustAbs("/bin/"),
Target: MustAbs("/usr/bin/"), Target: check.MustAbs("/usr/bin/"),
Flags: BindEnsure, Flags: BindEnsure,
}, []stub.Call{ }, []stub.Call{
call("mkdirAll", stub.ExpectArgs{"/bin/", os.FileMode(0700)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/bin/", os.FileMode(0700)}, nil, nil),
@ -73,8 +74,8 @@ func TestBindMountOp(t *testing.T) {
}, nil}, }, nil},
{"success device ro", new(Params), &BindMountOp{ {"success device ro", new(Params), &BindMountOp{
Source: MustAbs("/dev/null"), Source: check.MustAbs("/dev/null"),
Target: MustAbs("/dev/null"), Target: check.MustAbs("/dev/null"),
Flags: BindDevice, Flags: BindDevice,
}, []stub.Call{ }, []stub.Call{
call("evalSymlinks", stub.ExpectArgs{"/dev/null"}, "/dev/null", nil), call("evalSymlinks", stub.ExpectArgs{"/dev/null"}, "/dev/null", nil),
@ -86,8 +87,8 @@ func TestBindMountOp(t *testing.T) {
}, nil}, }, nil},
{"success device", new(Params), &BindMountOp{ {"success device", new(Params), &BindMountOp{
Source: MustAbs("/dev/null"), Source: check.MustAbs("/dev/null"),
Target: MustAbs("/dev/null"), Target: check.MustAbs("/dev/null"),
Flags: BindWritable | BindDevice, Flags: BindWritable | BindDevice,
}, []stub.Call{ }, []stub.Call{
call("evalSymlinks", stub.ExpectArgs{"/dev/null"}, "/dev/null", nil), call("evalSymlinks", stub.ExpectArgs{"/dev/null"}, "/dev/null", nil),
@ -99,15 +100,15 @@ func TestBindMountOp(t *testing.T) {
}, nil}, }, nil},
{"evalSymlinks", new(Params), &BindMountOp{ {"evalSymlinks", new(Params), &BindMountOp{
Source: MustAbs("/bin/"), Source: check.MustAbs("/bin/"),
Target: MustAbs("/bin/"), Target: check.MustAbs("/bin/"),
}, []stub.Call{ }, []stub.Call{
call("evalSymlinks", stub.ExpectArgs{"/bin/"}, "/usr/bin", stub.UniqueError(3)), call("evalSymlinks", stub.ExpectArgs{"/bin/"}, "/usr/bin", stub.UniqueError(3)),
}, stub.UniqueError(3), nil, nil}, }, stub.UniqueError(3), nil, nil},
{"stat", new(Params), &BindMountOp{ {"stat", new(Params), &BindMountOp{
Source: MustAbs("/bin/"), Source: check.MustAbs("/bin/"),
Target: MustAbs("/bin/"), Target: check.MustAbs("/bin/"),
}, []stub.Call{ }, []stub.Call{
call("evalSymlinks", stub.ExpectArgs{"/bin/"}, "/usr/bin", nil), call("evalSymlinks", stub.ExpectArgs{"/bin/"}, "/usr/bin", nil),
}, nil, []stub.Call{ }, nil, []stub.Call{
@ -115,8 +116,8 @@ func TestBindMountOp(t *testing.T) {
}, stub.UniqueError(2)}, }, stub.UniqueError(2)},
{"mkdirAll", new(Params), &BindMountOp{ {"mkdirAll", new(Params), &BindMountOp{
Source: MustAbs("/bin/"), Source: check.MustAbs("/bin/"),
Target: MustAbs("/bin/"), Target: check.MustAbs("/bin/"),
}, []stub.Call{ }, []stub.Call{
call("evalSymlinks", stub.ExpectArgs{"/bin/"}, "/usr/bin", nil), call("evalSymlinks", stub.ExpectArgs{"/bin/"}, "/usr/bin", nil),
}, nil, []stub.Call{ }, nil, []stub.Call{
@ -125,8 +126,8 @@ func TestBindMountOp(t *testing.T) {
}, stub.UniqueError(1)}, }, stub.UniqueError(1)},
{"bindMount", new(Params), &BindMountOp{ {"bindMount", new(Params), &BindMountOp{
Source: MustAbs("/bin/"), Source: check.MustAbs("/bin/"),
Target: MustAbs("/bin/"), Target: check.MustAbs("/bin/"),
}, []stub.Call{ }, []stub.Call{
call("evalSymlinks", stub.ExpectArgs{"/bin/"}, "/usr/bin", nil), call("evalSymlinks", stub.ExpectArgs{"/bin/"}, "/usr/bin", nil),
}, nil, []stub.Call{ }, nil, []stub.Call{
@ -137,8 +138,8 @@ func TestBindMountOp(t *testing.T) {
}, stub.UniqueError(0)}, }, stub.UniqueError(0)},
{"success eval equals", new(Params), &BindMountOp{ {"success eval equals", new(Params), &BindMountOp{
Source: MustAbs("/bin/"), Source: check.MustAbs("/bin/"),
Target: MustAbs("/bin/"), Target: check.MustAbs("/bin/"),
}, []stub.Call{ }, []stub.Call{
call("evalSymlinks", stub.ExpectArgs{"/bin/"}, "/bin", nil), call("evalSymlinks", stub.ExpectArgs{"/bin/"}, "/bin", nil),
}, nil, []stub.Call{ }, nil, []stub.Call{
@ -149,8 +150,8 @@ func TestBindMountOp(t *testing.T) {
}, nil}, }, nil},
{"success", new(Params), &BindMountOp{ {"success", new(Params), &BindMountOp{
Source: MustAbs("/bin/"), Source: check.MustAbs("/bin/"),
Target: MustAbs("/bin/"), Target: check.MustAbs("/bin/"),
}, []stub.Call{ }, []stub.Call{
call("evalSymlinks", stub.ExpectArgs{"/bin/"}, "/usr/bin", nil), call("evalSymlinks", stub.ExpectArgs{"/bin/"}, "/usr/bin", nil),
}, nil, []stub.Call{ }, nil, []stub.Call{
@ -173,21 +174,21 @@ func TestBindMountOp(t *testing.T) {
checkOpsValid(t, []opValidTestCase{ checkOpsValid(t, []opValidTestCase{
{"nil", (*BindMountOp)(nil), false}, {"nil", (*BindMountOp)(nil), false},
{"zero", new(BindMountOp), false}, {"zero", new(BindMountOp), false},
{"nil source", &BindMountOp{Target: MustAbs("/")}, false}, {"nil source", &BindMountOp{Target: check.MustAbs("/")}, false},
{"nil target", &BindMountOp{Source: MustAbs("/")}, false}, {"nil target", &BindMountOp{Source: check.MustAbs("/")}, false},
{"flag optional ensure", &BindMountOp{Source: MustAbs("/"), Target: MustAbs("/"), Flags: BindOptional | BindEnsure}, false}, {"flag optional ensure", &BindMountOp{Source: check.MustAbs("/"), Target: check.MustAbs("/"), Flags: BindOptional | BindEnsure}, false},
{"valid", &BindMountOp{Source: MustAbs("/"), Target: MustAbs("/")}, true}, {"valid", &BindMountOp{Source: check.MustAbs("/"), Target: check.MustAbs("/")}, true},
}) })
checkOpsBuilder(t, []opsBuilderTestCase{ checkOpsBuilder(t, []opsBuilderTestCase{
{"autoetc", new(Ops).Bind( {"autoetc", new(Ops).Bind(
MustAbs("/etc/"), check.MustAbs("/etc/"),
MustAbs("/etc/.host/048090b6ed8f9ebb10e275ff5d8c0659"), check.MustAbs("/etc/.host/048090b6ed8f9ebb10e275ff5d8c0659"),
0, 0,
), Ops{ ), Ops{
&BindMountOp{ &BindMountOp{
Source: MustAbs("/etc/"), Source: check.MustAbs("/etc/"),
Target: MustAbs("/etc/.host/048090b6ed8f9ebb10e275ff5d8c0659"), Target: check.MustAbs("/etc/.host/048090b6ed8f9ebb10e275ff5d8c0659"),
}, },
}}, }},
}) })
@ -196,45 +197,45 @@ func TestBindMountOp(t *testing.T) {
{"zero", new(BindMountOp), new(BindMountOp), false}, {"zero", new(BindMountOp), new(BindMountOp), false},
{"internal ne", &BindMountOp{ {"internal ne", &BindMountOp{
Source: MustAbs("/etc/"), Source: check.MustAbs("/etc/"),
Target: MustAbs("/etc/.host/048090b6ed8f9ebb10e275ff5d8c0659"), Target: check.MustAbs("/etc/.host/048090b6ed8f9ebb10e275ff5d8c0659"),
}, &BindMountOp{ }, &BindMountOp{
Source: MustAbs("/etc/"), Source: check.MustAbs("/etc/"),
Target: MustAbs("/etc/.host/048090b6ed8f9ebb10e275ff5d8c0659"), Target: check.MustAbs("/etc/.host/048090b6ed8f9ebb10e275ff5d8c0659"),
sourceFinal: MustAbs("/etc/"), sourceFinal: check.MustAbs("/etc/"),
}, true}, }, true},
{"flags differs", &BindMountOp{ {"flags differs", &BindMountOp{
Source: MustAbs("/etc/"), Source: check.MustAbs("/etc/"),
Target: MustAbs("/etc/.host/048090b6ed8f9ebb10e275ff5d8c0659"), Target: check.MustAbs("/etc/.host/048090b6ed8f9ebb10e275ff5d8c0659"),
}, &BindMountOp{ }, &BindMountOp{
Source: MustAbs("/etc/"), Source: check.MustAbs("/etc/"),
Target: MustAbs("/etc/.host/048090b6ed8f9ebb10e275ff5d8c0659"), Target: check.MustAbs("/etc/.host/048090b6ed8f9ebb10e275ff5d8c0659"),
Flags: BindOptional, Flags: BindOptional,
}, false}, }, false},
{"source differs", &BindMountOp{ {"source differs", &BindMountOp{
Source: MustAbs("/.hakurei/etc/"), Source: check.MustAbs("/.hakurei/etc/"),
Target: MustAbs("/etc/.host/048090b6ed8f9ebb10e275ff5d8c0659"), Target: check.MustAbs("/etc/.host/048090b6ed8f9ebb10e275ff5d8c0659"),
}, &BindMountOp{ }, &BindMountOp{
Source: MustAbs("/etc/"), Source: check.MustAbs("/etc/"),
Target: MustAbs("/etc/.host/048090b6ed8f9ebb10e275ff5d8c0659"), Target: check.MustAbs("/etc/.host/048090b6ed8f9ebb10e275ff5d8c0659"),
}, false}, }, false},
{"target differs", &BindMountOp{ {"target differs", &BindMountOp{
Source: MustAbs("/etc/"), Source: check.MustAbs("/etc/"),
Target: MustAbs("/etc/.host/048090b6ed8f9ebb10e275ff5d8c0659"), Target: check.MustAbs("/etc/.host/048090b6ed8f9ebb10e275ff5d8c0659"),
}, &BindMountOp{ }, &BindMountOp{
Source: MustAbs("/etc/"), Source: check.MustAbs("/etc/"),
Target: MustAbs("/etc/"), Target: check.MustAbs("/etc/"),
}, false}, }, false},
{"equals", &BindMountOp{ {"equals", &BindMountOp{
Source: MustAbs("/etc/"), Source: check.MustAbs("/etc/"),
Target: MustAbs("/etc/.host/048090b6ed8f9ebb10e275ff5d8c0659"), Target: check.MustAbs("/etc/.host/048090b6ed8f9ebb10e275ff5d8c0659"),
}, &BindMountOp{ }, &BindMountOp{
Source: MustAbs("/etc/"), Source: check.MustAbs("/etc/"),
Target: MustAbs("/etc/.host/048090b6ed8f9ebb10e275ff5d8c0659"), Target: check.MustAbs("/etc/.host/048090b6ed8f9ebb10e275ff5d8c0659"),
}, true}, }, true},
}) })
@ -242,13 +243,13 @@ func TestBindMountOp(t *testing.T) {
{"invalid", new(BindMountOp), "mounting", "<invalid>"}, {"invalid", new(BindMountOp), "mounting", "<invalid>"},
{"autoetc", &BindMountOp{ {"autoetc", &BindMountOp{
Source: MustAbs("/etc/"), Source: check.MustAbs("/etc/"),
Target: MustAbs("/etc/.host/048090b6ed8f9ebb10e275ff5d8c0659"), Target: check.MustAbs("/etc/.host/048090b6ed8f9ebb10e275ff5d8c0659"),
}, "mounting", `"/etc/" on "/etc/.host/048090b6ed8f9ebb10e275ff5d8c0659" flags 0x0`}, }, "mounting", `"/etc/" on "/etc/.host/048090b6ed8f9ebb10e275ff5d8c0659" flags 0x0`},
{"hostdev", &BindMountOp{ {"hostdev", &BindMountOp{
Source: MustAbs("/dev/"), Source: check.MustAbs("/dev/"),
Target: MustAbs("/dev/"), Target: check.MustAbs("/dev/"),
Flags: BindWritable | BindDevice, Flags: BindWritable | BindDevice,
}, "mounting", `"/dev/" flags 0x6`}, }, "mounting", `"/dev/" flags 0x6`},
}) })

View File

@ -5,19 +5,21 @@ import (
"fmt" "fmt"
"path" "path"
. "syscall" . "syscall"
"hakurei.app/container/check"
) )
func init() { gob.Register(new(MountDevOp)) } func init() { gob.Register(new(MountDevOp)) }
// Dev appends an [Op] that mounts a subset of host /dev. // Dev appends an [Op] that mounts a subset of host /dev.
func (f *Ops) Dev(target *Absolute, mqueue bool) *Ops { func (f *Ops) Dev(target *check.Absolute, mqueue bool) *Ops {
*f = append(*f, &MountDevOp{target, mqueue, false}) *f = append(*f, &MountDevOp{target, mqueue, false})
return f return f
} }
// DevWritable appends an [Op] that mounts a writable subset of host /dev. // DevWritable appends an [Op] that mounts a writable subset of host /dev.
// There is usually no good reason to write to /dev, so this should always be followed by a [RemountOp]. // There is usually no good reason to write to /dev, so this should always be followed by a [RemountOp].
func (f *Ops) DevWritable(target *Absolute, mqueue bool) *Ops { func (f *Ops) DevWritable(target *check.Absolute, mqueue bool) *Ops {
*f = append(*f, &MountDevOp{target, mqueue, true}) *f = append(*f, &MountDevOp{target, mqueue, true})
return f return f
} }
@ -26,7 +28,7 @@ func (f *Ops) DevWritable(target *Absolute, mqueue bool) *Ops {
// If Mqueue is true, a private instance of [FstypeMqueue] is mounted. // If Mqueue is true, a private instance of [FstypeMqueue] is mounted.
// If Write is true, the resulting mount point is left writable. // If Write is true, the resulting mount point is left writable.
type MountDevOp struct { type MountDevOp struct {
Target *Absolute Target *check.Absolute
Mqueue bool Mqueue bool
Write bool Write bool
} }

View File

@ -4,20 +4,21 @@ import (
"os" "os"
"testing" "testing"
"hakurei.app/container/check"
"hakurei.app/container/stub" "hakurei.app/container/stub"
) )
func TestMountDevOp(t *testing.T) { func TestMountDevOp(t *testing.T) {
checkOpBehaviour(t, []opBehaviourTestCase{ checkOpBehaviour(t, []opBehaviourTestCase{
{"mountTmpfs", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{ {"mountTmpfs", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{
Target: MustAbs("/dev/"), Target: check.MustAbs("/dev/"),
Mqueue: true, Mqueue: true,
}, nil, nil, []stub.Call{ }, nil, nil, []stub.Call{
call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, stub.UniqueError(27)), call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, stub.UniqueError(27)),
}, stub.UniqueError(27)}, }, stub.UniqueError(27)},
{"ensureFile null", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{ {"ensureFile null", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{
Target: MustAbs("/dev/"), Target: check.MustAbs("/dev/"),
Mqueue: true, Mqueue: true,
}, nil, nil, []stub.Call{ }, nil, nil, []stub.Call{
call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil), call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil),
@ -25,7 +26,7 @@ func TestMountDevOp(t *testing.T) {
}, stub.UniqueError(26)}, }, stub.UniqueError(26)},
{"bindMount null", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{ {"bindMount null", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{
Target: MustAbs("/dev/"), Target: check.MustAbs("/dev/"),
Mqueue: true, Mqueue: true,
}, nil, nil, []stub.Call{ }, nil, nil, []stub.Call{
call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil), call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil),
@ -34,7 +35,7 @@ func TestMountDevOp(t *testing.T) {
}, stub.UniqueError(25)}, }, stub.UniqueError(25)},
{"ensureFile zero", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{ {"ensureFile zero", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{
Target: MustAbs("/dev/"), Target: check.MustAbs("/dev/"),
Mqueue: true, Mqueue: true,
}, nil, nil, []stub.Call{ }, nil, nil, []stub.Call{
call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil), call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil),
@ -44,7 +45,7 @@ func TestMountDevOp(t *testing.T) {
}, stub.UniqueError(24)}, }, stub.UniqueError(24)},
{"bindMount zero", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{ {"bindMount zero", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{
Target: MustAbs("/dev/"), Target: check.MustAbs("/dev/"),
Mqueue: true, Mqueue: true,
}, nil, nil, []stub.Call{ }, nil, nil, []stub.Call{
call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil), call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil),
@ -55,7 +56,7 @@ func TestMountDevOp(t *testing.T) {
}, stub.UniqueError(23)}, }, stub.UniqueError(23)},
{"ensureFile full", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{ {"ensureFile full", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{
Target: MustAbs("/dev/"), Target: check.MustAbs("/dev/"),
Mqueue: true, Mqueue: true,
}, nil, nil, []stub.Call{ }, nil, nil, []stub.Call{
call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil), call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil),
@ -67,7 +68,7 @@ func TestMountDevOp(t *testing.T) {
}, stub.UniqueError(22)}, }, stub.UniqueError(22)},
{"bindMount full", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{ {"bindMount full", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{
Target: MustAbs("/dev/"), Target: check.MustAbs("/dev/"),
Mqueue: true, Mqueue: true,
}, nil, nil, []stub.Call{ }, nil, nil, []stub.Call{
call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil), call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil),
@ -80,7 +81,7 @@ func TestMountDevOp(t *testing.T) {
}, stub.UniqueError(21)}, }, stub.UniqueError(21)},
{"ensureFile random", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{ {"ensureFile random", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{
Target: MustAbs("/dev/"), Target: check.MustAbs("/dev/"),
Mqueue: true, Mqueue: true,
}, nil, nil, []stub.Call{ }, nil, nil, []stub.Call{
call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil), call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil),
@ -94,7 +95,7 @@ func TestMountDevOp(t *testing.T) {
}, stub.UniqueError(20)}, }, stub.UniqueError(20)},
{"bindMount random", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{ {"bindMount random", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{
Target: MustAbs("/dev/"), Target: check.MustAbs("/dev/"),
Mqueue: true, Mqueue: true,
}, nil, nil, []stub.Call{ }, nil, nil, []stub.Call{
call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil), call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil),
@ -109,7 +110,7 @@ func TestMountDevOp(t *testing.T) {
}, stub.UniqueError(19)}, }, stub.UniqueError(19)},
{"ensureFile urandom", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{ {"ensureFile urandom", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{
Target: MustAbs("/dev/"), Target: check.MustAbs("/dev/"),
Mqueue: true, Mqueue: true,
}, nil, nil, []stub.Call{ }, nil, nil, []stub.Call{
call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil), call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil),
@ -125,7 +126,7 @@ func TestMountDevOp(t *testing.T) {
}, stub.UniqueError(18)}, }, stub.UniqueError(18)},
{"bindMount urandom", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{ {"bindMount urandom", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{
Target: MustAbs("/dev/"), Target: check.MustAbs("/dev/"),
Mqueue: true, Mqueue: true,
}, nil, nil, []stub.Call{ }, nil, nil, []stub.Call{
call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil), call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil),
@ -142,7 +143,7 @@ func TestMountDevOp(t *testing.T) {
}, stub.UniqueError(17)}, }, stub.UniqueError(17)},
{"ensureFile tty", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{ {"ensureFile tty", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{
Target: MustAbs("/dev/"), Target: check.MustAbs("/dev/"),
Mqueue: true, Mqueue: true,
}, nil, nil, []stub.Call{ }, nil, nil, []stub.Call{
call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil), call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil),
@ -160,7 +161,7 @@ func TestMountDevOp(t *testing.T) {
}, stub.UniqueError(16)}, }, stub.UniqueError(16)},
{"bindMount tty", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{ {"bindMount tty", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{
Target: MustAbs("/dev/"), Target: check.MustAbs("/dev/"),
Mqueue: true, Mqueue: true,
}, nil, nil, []stub.Call{ }, nil, nil, []stub.Call{
call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil), call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil),
@ -179,7 +180,7 @@ func TestMountDevOp(t *testing.T) {
}, stub.UniqueError(15)}, }, stub.UniqueError(15)},
{"symlink stdin", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{ {"symlink stdin", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{
Target: MustAbs("/dev/"), Target: check.MustAbs("/dev/"),
Mqueue: true, Mqueue: true,
}, nil, nil, []stub.Call{ }, nil, nil, []stub.Call{
call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil), call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil),
@ -199,7 +200,7 @@ func TestMountDevOp(t *testing.T) {
}, stub.UniqueError(14)}, }, stub.UniqueError(14)},
{"symlink stdout", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{ {"symlink stdout", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{
Target: MustAbs("/dev/"), Target: check.MustAbs("/dev/"),
Mqueue: true, Mqueue: true,
}, nil, nil, []stub.Call{ }, nil, nil, []stub.Call{
call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil), call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil),
@ -220,7 +221,7 @@ func TestMountDevOp(t *testing.T) {
}, stub.UniqueError(13)}, }, stub.UniqueError(13)},
{"symlink stderr", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{ {"symlink stderr", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{
Target: MustAbs("/dev/"), Target: check.MustAbs("/dev/"),
Mqueue: true, Mqueue: true,
}, nil, nil, []stub.Call{ }, nil, nil, []stub.Call{
call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil), call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil),
@ -242,7 +243,7 @@ func TestMountDevOp(t *testing.T) {
}, stub.UniqueError(12)}, }, stub.UniqueError(12)},
{"symlink fd", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{ {"symlink fd", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{
Target: MustAbs("/dev/"), Target: check.MustAbs("/dev/"),
Mqueue: true, Mqueue: true,
}, nil, nil, []stub.Call{ }, nil, nil, []stub.Call{
call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil), call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil),
@ -265,7 +266,7 @@ func TestMountDevOp(t *testing.T) {
}, stub.UniqueError(11)}, }, stub.UniqueError(11)},
{"symlink kcore", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{ {"symlink kcore", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{
Target: MustAbs("/dev/"), Target: check.MustAbs("/dev/"),
Mqueue: true, Mqueue: true,
}, nil, nil, []stub.Call{ }, nil, nil, []stub.Call{
call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil), call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil),
@ -289,7 +290,7 @@ func TestMountDevOp(t *testing.T) {
}, stub.UniqueError(10)}, }, stub.UniqueError(10)},
{"symlink ptmx", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{ {"symlink ptmx", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{
Target: MustAbs("/dev/"), Target: check.MustAbs("/dev/"),
Mqueue: true, Mqueue: true,
}, nil, nil, []stub.Call{ }, nil, nil, []stub.Call{
call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil), call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil),
@ -314,7 +315,7 @@ func TestMountDevOp(t *testing.T) {
}, stub.UniqueError(9)}, }, stub.UniqueError(9)},
{"mkdir shm", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{ {"mkdir shm", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{
Target: MustAbs("/dev/"), Target: check.MustAbs("/dev/"),
Mqueue: true, Mqueue: true,
}, nil, nil, []stub.Call{ }, nil, nil, []stub.Call{
call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil), call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil),
@ -340,7 +341,7 @@ func TestMountDevOp(t *testing.T) {
}, stub.UniqueError(8)}, }, stub.UniqueError(8)},
{"mkdir devpts", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{ {"mkdir devpts", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{
Target: MustAbs("/dev/"), Target: check.MustAbs("/dev/"),
Mqueue: true, Mqueue: true,
}, nil, nil, []stub.Call{ }, nil, nil, []stub.Call{
call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil), call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil),
@ -367,7 +368,7 @@ func TestMountDevOp(t *testing.T) {
}, stub.UniqueError(7)}, }, stub.UniqueError(7)},
{"mount devpts", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{ {"mount devpts", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{
Target: MustAbs("/dev/"), Target: check.MustAbs("/dev/"),
Mqueue: true, Mqueue: true,
}, nil, nil, []stub.Call{ }, nil, nil, []stub.Call{
call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil), call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil),
@ -395,7 +396,7 @@ func TestMountDevOp(t *testing.T) {
}, stub.UniqueError(6)}, }, stub.UniqueError(6)},
{"ensureFile stdout", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{ {"ensureFile stdout", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{
Target: MustAbs("/dev/"), Target: check.MustAbs("/dev/"),
Mqueue: true, Mqueue: true,
}, nil, nil, []stub.Call{ }, nil, nil, []stub.Call{
call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil), call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil),
@ -425,7 +426,7 @@ func TestMountDevOp(t *testing.T) {
}, stub.UniqueError(5)}, }, stub.UniqueError(5)},
{"readlink stdout", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{ {"readlink stdout", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{
Target: MustAbs("/dev/"), Target: check.MustAbs("/dev/"),
Mqueue: true, Mqueue: true,
}, nil, nil, []stub.Call{ }, nil, nil, []stub.Call{
call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil), call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil),
@ -456,7 +457,7 @@ func TestMountDevOp(t *testing.T) {
}, stub.UniqueError(4)}, }, stub.UniqueError(4)},
{"bindMount stdout", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{ {"bindMount stdout", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{
Target: MustAbs("/dev/"), Target: check.MustAbs("/dev/"),
Mqueue: true, Mqueue: true,
}, nil, nil, []stub.Call{ }, nil, nil, []stub.Call{
call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil), call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil),
@ -488,7 +489,7 @@ func TestMountDevOp(t *testing.T) {
}, stub.UniqueError(3)}, }, stub.UniqueError(3)},
{"mkdir mqueue", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{ {"mkdir mqueue", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{
Target: MustAbs("/dev/"), Target: check.MustAbs("/dev/"),
Mqueue: true, Mqueue: true,
}, nil, nil, []stub.Call{ }, nil, nil, []stub.Call{
call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil), call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil),
@ -521,7 +522,7 @@ func TestMountDevOp(t *testing.T) {
}, stub.UniqueError(2)}, }, stub.UniqueError(2)},
{"mount mqueue", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{ {"mount mqueue", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{
Target: MustAbs("/dev/"), Target: check.MustAbs("/dev/"),
Mqueue: true, Mqueue: true,
}, nil, nil, []stub.Call{ }, nil, nil, []stub.Call{
call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil), call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil),
@ -555,7 +556,7 @@ func TestMountDevOp(t *testing.T) {
}, stub.UniqueError(1)}, }, stub.UniqueError(1)},
{"success no session", &Params{ParentPerm: 0755}, &MountDevOp{ {"success no session", &Params{ParentPerm: 0755}, &MountDevOp{
Target: MustAbs("/dev/"), Target: check.MustAbs("/dev/"),
Mqueue: true, Mqueue: true,
Write: true, Write: true,
}, nil, nil, []stub.Call{ }, nil, nil, []stub.Call{
@ -586,7 +587,7 @@ func TestMountDevOp(t *testing.T) {
}, nil}, }, nil},
{"success no tty", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{ {"success no tty", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{
Target: MustAbs("/dev/"), Target: check.MustAbs("/dev/"),
Mqueue: true, Mqueue: true,
Write: true, Write: true,
}, nil, nil, []stub.Call{ }, nil, nil, []stub.Call{
@ -618,7 +619,7 @@ func TestMountDevOp(t *testing.T) {
}, nil}, }, nil},
{"remount", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{ {"remount", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{
Target: MustAbs("/dev/"), Target: check.MustAbs("/dev/"),
}, nil, nil, []stub.Call{ }, nil, nil, []stub.Call{
call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil), call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil),
call("ensureFile", stub.ExpectArgs{"/sysroot/dev/null", os.FileMode(0444), os.FileMode(0750)}, nil, nil), call("ensureFile", stub.ExpectArgs{"/sysroot/dev/null", os.FileMode(0444), os.FileMode(0750)}, nil, nil),
@ -650,7 +651,7 @@ func TestMountDevOp(t *testing.T) {
}, stub.UniqueError(0)}, }, stub.UniqueError(0)},
{"success no mqueue", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{ {"success no mqueue", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{
Target: MustAbs("/dev/"), Target: check.MustAbs("/dev/"),
}, nil, nil, []stub.Call{ }, nil, nil, []stub.Call{
call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil), call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil),
call("ensureFile", stub.ExpectArgs{"/sysroot/dev/null", os.FileMode(0444), os.FileMode(0750)}, nil, nil), call("ensureFile", stub.ExpectArgs{"/sysroot/dev/null", os.FileMode(0444), os.FileMode(0750)}, nil, nil),
@ -683,7 +684,7 @@ func TestMountDevOp(t *testing.T) {
}, nil}, }, nil},
{"success rw", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{ {"success rw", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{
Target: MustAbs("/dev/"), Target: check.MustAbs("/dev/"),
Mqueue: true, Mqueue: true,
Write: true, Write: true,
}, nil, nil, []stub.Call{ }, nil, nil, []stub.Call{
@ -718,7 +719,7 @@ func TestMountDevOp(t *testing.T) {
}, nil}, }, nil},
{"success", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{ {"success", &Params{ParentPerm: 0750, RetainSession: true}, &MountDevOp{
Target: MustAbs("/dev/"), Target: check.MustAbs("/dev/"),
Mqueue: true, Mqueue: true,
}, nil, nil, []stub.Call{ }, nil, nil, []stub.Call{
call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil), call("mountTmpfs", stub.ExpectArgs{"devtmpfs", "/sysroot/dev", uintptr(0x6), 0, os.FileMode(0750)}, nil, nil),
@ -757,20 +758,20 @@ func TestMountDevOp(t *testing.T) {
checkOpsValid(t, []opValidTestCase{ checkOpsValid(t, []opValidTestCase{
{"nil", (*MountDevOp)(nil), false}, {"nil", (*MountDevOp)(nil), false},
{"zero", new(MountDevOp), false}, {"zero", new(MountDevOp), false},
{"valid", &MountDevOp{Target: MustAbs("/dev/")}, true}, {"valid", &MountDevOp{Target: check.MustAbs("/dev/")}, true},
}) })
checkOpsBuilder(t, []opsBuilderTestCase{ checkOpsBuilder(t, []opsBuilderTestCase{
{"dev", new(Ops).Dev(MustAbs("/dev/"), true), Ops{ {"dev", new(Ops).Dev(check.MustAbs("/dev/"), true), Ops{
&MountDevOp{ &MountDevOp{
Target: MustAbs("/dev/"), Target: check.MustAbs("/dev/"),
Mqueue: true, Mqueue: true,
}, },
}}, }},
{"dev writable", new(Ops).DevWritable(MustAbs("/.hakurei/dev/"), false), Ops{ {"dev writable", new(Ops).DevWritable(check.MustAbs("/.hakurei/dev/"), false), Ops{
&MountDevOp{ &MountDevOp{
Target: MustAbs("/.hakurei/dev/"), Target: check.MustAbs("/.hakurei/dev/"),
Write: true, Write: true,
}, },
}}, }},
@ -780,46 +781,46 @@ func TestMountDevOp(t *testing.T) {
{"zero", new(MountDevOp), new(MountDevOp), false}, {"zero", new(MountDevOp), new(MountDevOp), false},
{"write differs", &MountDevOp{ {"write differs", &MountDevOp{
Target: MustAbs("/dev/"), Target: check.MustAbs("/dev/"),
Mqueue: true, Mqueue: true,
}, &MountDevOp{ }, &MountDevOp{
Target: MustAbs("/dev/"), Target: check.MustAbs("/dev/"),
Mqueue: true, Mqueue: true,
Write: true, Write: true,
}, false}, }, false},
{"mqueue differs", &MountDevOp{ {"mqueue differs", &MountDevOp{
Target: MustAbs("/dev/"), Target: check.MustAbs("/dev/"),
}, &MountDevOp{ }, &MountDevOp{
Target: MustAbs("/dev/"), Target: check.MustAbs("/dev/"),
Mqueue: true, Mqueue: true,
}, false}, }, false},
{"target differs", &MountDevOp{ {"target differs", &MountDevOp{
Target: MustAbs("/"), Target: check.MustAbs("/"),
Mqueue: true, Mqueue: true,
}, &MountDevOp{ }, &MountDevOp{
Target: MustAbs("/dev/"), Target: check.MustAbs("/dev/"),
Mqueue: true, Mqueue: true,
}, false}, }, false},
{"equals", &MountDevOp{ {"equals", &MountDevOp{
Target: MustAbs("/dev/"), Target: check.MustAbs("/dev/"),
Mqueue: true, Mqueue: true,
}, &MountDevOp{ }, &MountDevOp{
Target: MustAbs("/dev/"), Target: check.MustAbs("/dev/"),
Mqueue: true, Mqueue: true,
}, true}, }, true},
}) })
checkOpMeta(t, []opMetaTestCase{ checkOpMeta(t, []opMetaTestCase{
{"mqueue", &MountDevOp{ {"mqueue", &MountDevOp{
Target: MustAbs("/dev/"), Target: check.MustAbs("/dev/"),
Mqueue: true, Mqueue: true,
}, "mounting", `dev on "/dev/" with mqueue`}, }, "mounting", `dev on "/dev/" with mqueue`},
{"dev", &MountDevOp{ {"dev", &MountDevOp{
Target: MustAbs("/dev/"), Target: check.MustAbs("/dev/"),
}, "mounting", `dev on "/dev/"`}, }, "mounting", `dev on "/dev/"`},
}) })
} }

View File

@ -4,19 +4,21 @@ import (
"encoding/gob" "encoding/gob"
"fmt" "fmt"
"os" "os"
"hakurei.app/container/check"
) )
func init() { gob.Register(new(MkdirOp)) } func init() { gob.Register(new(MkdirOp)) }
// Mkdir appends an [Op] that creates a directory in the container filesystem. // Mkdir appends an [Op] that creates a directory in the container filesystem.
func (f *Ops) Mkdir(name *Absolute, perm os.FileMode) *Ops { func (f *Ops) Mkdir(name *check.Absolute, perm os.FileMode) *Ops {
*f = append(*f, &MkdirOp{name, perm}) *f = append(*f, &MkdirOp{name, perm})
return f return f
} }
// MkdirOp creates a directory at container Path with permission bits set to Perm. // MkdirOp creates a directory at container Path with permission bits set to Perm.
type MkdirOp struct { type MkdirOp struct {
Path *Absolute Path *check.Absolute
Perm os.FileMode Perm os.FileMode
} }

View File

@ -4,13 +4,14 @@ import (
"os" "os"
"testing" "testing"
"hakurei.app/container/check"
"hakurei.app/container/stub" "hakurei.app/container/stub"
) )
func TestMkdirOp(t *testing.T) { func TestMkdirOp(t *testing.T) {
checkOpBehaviour(t, []opBehaviourTestCase{ checkOpBehaviour(t, []opBehaviourTestCase{
{"success", new(Params), &MkdirOp{ {"success", new(Params), &MkdirOp{
Path: MustAbs("/.hakurei"), Path: check.MustAbs("/.hakurei"),
Perm: 0500, Perm: 0500,
}, nil, nil, []stub.Call{ }, nil, nil, []stub.Call{
call("mkdirAll", stub.ExpectArgs{"/sysroot/.hakurei", os.FileMode(0500)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/sysroot/.hakurei", os.FileMode(0500)}, nil, nil),
@ -20,25 +21,25 @@ func TestMkdirOp(t *testing.T) {
checkOpsValid(t, []opValidTestCase{ checkOpsValid(t, []opValidTestCase{
{"nil", (*MkdirOp)(nil), false}, {"nil", (*MkdirOp)(nil), false},
{"zero", new(MkdirOp), false}, {"zero", new(MkdirOp), false},
{"valid", &MkdirOp{Path: MustAbs("/.hakurei")}, true}, {"valid", &MkdirOp{Path: check.MustAbs("/.hakurei")}, true},
}) })
checkOpsBuilder(t, []opsBuilderTestCase{ checkOpsBuilder(t, []opsBuilderTestCase{
{"etc", new(Ops).Mkdir(MustAbs("/etc/"), 0), Ops{ {"etc", new(Ops).Mkdir(check.MustAbs("/etc/"), 0), Ops{
&MkdirOp{Path: MustAbs("/etc/")}, &MkdirOp{Path: check.MustAbs("/etc/")},
}}, }},
}) })
checkOpIs(t, []opIsTestCase{ checkOpIs(t, []opIsTestCase{
{"zero", new(MkdirOp), new(MkdirOp), false}, {"zero", new(MkdirOp), new(MkdirOp), false},
{"path differs", &MkdirOp{Path: MustAbs("/"), Perm: 0755}, &MkdirOp{Path: MustAbs("/etc/"), Perm: 0755}, false}, {"path differs", &MkdirOp{Path: check.MustAbs("/"), Perm: 0755}, &MkdirOp{Path: check.MustAbs("/etc/"), Perm: 0755}, false},
{"perm differs", &MkdirOp{Path: MustAbs("/")}, &MkdirOp{Path: MustAbs("/"), Perm: 0755}, false}, {"perm differs", &MkdirOp{Path: check.MustAbs("/")}, &MkdirOp{Path: check.MustAbs("/"), Perm: 0755}, false},
{"equals", &MkdirOp{Path: MustAbs("/")}, &MkdirOp{Path: MustAbs("/")}, true}, {"equals", &MkdirOp{Path: check.MustAbs("/")}, &MkdirOp{Path: check.MustAbs("/")}, true},
}) })
checkOpMeta(t, []opMetaTestCase{ checkOpMeta(t, []opMetaTestCase{
{"etc", &MkdirOp{ {"etc", &MkdirOp{
Path: MustAbs("/etc/"), Path: check.MustAbs("/etc/"),
}, "creating", `directory "/etc/" perm ----------`}, }, "creating", `directory "/etc/" perm ----------`},
}) })
} }

View File

@ -5,6 +5,8 @@ import (
"fmt" "fmt"
"slices" "slices"
"strings" "strings"
"hakurei.app/container/check"
) )
const ( const (
@ -52,7 +54,7 @@ func (e *OverlayArgumentError) Error() string {
} }
// Overlay appends an [Op] that mounts the overlay pseudo filesystem on [MountOverlayOp.Target]. // Overlay appends an [Op] that mounts the overlay pseudo filesystem on [MountOverlayOp.Target].
func (f *Ops) Overlay(target, state, work *Absolute, layers ...*Absolute) *Ops { func (f *Ops) Overlay(target, state, work *check.Absolute, layers ...*check.Absolute) *Ops {
*f = append(*f, &MountOverlayOp{ *f = append(*f, &MountOverlayOp{
Target: target, Target: target,
Lower: layers, Lower: layers,
@ -64,21 +66,21 @@ func (f *Ops) Overlay(target, state, work *Absolute, layers ...*Absolute) *Ops {
// OverlayEphemeral appends an [Op] that mounts the overlay pseudo filesystem on [MountOverlayOp.Target] // OverlayEphemeral appends an [Op] that mounts the overlay pseudo filesystem on [MountOverlayOp.Target]
// with an ephemeral upperdir and workdir. // with an ephemeral upperdir and workdir.
func (f *Ops) OverlayEphemeral(target *Absolute, layers ...*Absolute) *Ops { func (f *Ops) OverlayEphemeral(target *check.Absolute, layers ...*check.Absolute) *Ops {
return f.Overlay(target, AbsFHSRoot, nil, layers...) return f.Overlay(target, AbsFHSRoot, nil, layers...)
} }
// OverlayReadonly appends an [Op] that mounts the overlay pseudo filesystem readonly on [MountOverlayOp.Target] // OverlayReadonly appends an [Op] that mounts the overlay pseudo filesystem readonly on [MountOverlayOp.Target]
func (f *Ops) OverlayReadonly(target *Absolute, layers ...*Absolute) *Ops { func (f *Ops) OverlayReadonly(target *check.Absolute, layers ...*check.Absolute) *Ops {
return f.Overlay(target, nil, nil, layers...) return f.Overlay(target, nil, nil, layers...)
} }
// MountOverlayOp mounts [FstypeOverlay] on container path Target. // MountOverlayOp mounts [FstypeOverlay] on container path Target.
type MountOverlayOp struct { type MountOverlayOp struct {
Target *Absolute Target *check.Absolute
// Any filesystem, does not need to be on a writable filesystem. // Any filesystem, does not need to be on a writable filesystem.
Lower []*Absolute Lower []*check.Absolute
// formatted for [OptionOverlayLowerdir], resolved, prefixed and escaped during early // formatted for [OptionOverlayLowerdir], resolved, prefixed and escaped during early
lower []string lower []string
// The upperdir is normally on a writable filesystem. // The upperdir is normally on a writable filesystem.
@ -87,11 +89,11 @@ type MountOverlayOp struct {
// an ephemeral upperdir and workdir will be set up. // an ephemeral upperdir and workdir will be set up.
// //
// If both Work and Upper are nil, upperdir and workdir is omitted and the overlay is mounted readonly. // If both Work and Upper are nil, upperdir and workdir is omitted and the overlay is mounted readonly.
Upper *Absolute Upper *check.Absolute
// formatted for [OptionOverlayUpperdir], resolved, prefixed and escaped during early // formatted for [OptionOverlayUpperdir], resolved, prefixed and escaped during early
upper string upper string
// The workdir needs to be an empty directory on the same filesystem as upperdir. // The workdir needs to be an empty directory on the same filesystem as upperdir.
Work *Absolute Work *check.Absolute
// formatted for [OptionOverlayWorkdir], resolved, prefixed and escaped during early // formatted for [OptionOverlayWorkdir], resolved, prefixed and escaped during early
work string work string
@ -206,7 +208,7 @@ func (o *MountOverlayOp) Is(op Op) bool {
vo, ok := op.(*MountOverlayOp) vo, ok := op.(*MountOverlayOp)
return ok && o.Valid() && vo.Valid() && return ok && o.Valid() && vo.Valid() &&
o.Target.Is(vo.Target) && o.Target.Is(vo.Target) &&
slices.EqualFunc(o.Lower, vo.Lower, func(a *Absolute, v *Absolute) bool { return a.Is(v) }) && slices.EqualFunc(o.Lower, vo.Lower, func(a, v *check.Absolute) bool { return a.Is(v) }) &&
o.Upper.Is(vo.Upper) && o.Work.Is(vo.Work) o.Upper.Is(vo.Upper) && o.Work.Is(vo.Work)
} }
func (*MountOverlayOp) prefix() (string, bool) { return "mounting", true } func (*MountOverlayOp) prefix() (string, bool) { return "mounting", true }

View File

@ -5,6 +5,7 @@ import (
"os" "os"
"testing" "testing"
"hakurei.app/container/check"
"hakurei.app/container/stub" "hakurei.app/container/stub"
) )
@ -38,21 +39,21 @@ func TestMountOverlayOp(t *testing.T) {
checkOpBehaviour(t, []opBehaviourTestCase{ checkOpBehaviour(t, []opBehaviourTestCase{
{"mkdirTemp invalid ephemeral", &Params{ParentPerm: 0705}, &MountOverlayOp{ {"mkdirTemp invalid ephemeral", &Params{ParentPerm: 0705}, &MountOverlayOp{
Target: MustAbs("/"), Target: check.MustAbs("/"),
Lower: []*Absolute{ Lower: []*check.Absolute{
MustAbs("/var/lib/planterette/base/debian:f92c9052"), check.MustAbs("/var/lib/planterette/base/debian:f92c9052"),
MustAbs("/var/lib/planterette/app/org.chromium.Chromium@debian:f92c9052"), check.MustAbs("/var/lib/planterette/app/org.chromium.Chromium@debian:f92c9052"),
}, },
Upper: MustAbs("/proc/"), Upper: check.MustAbs("/proc/"),
}, nil, &OverlayArgumentError{OverlayEphemeralUnexpectedUpper, "/proc/"}, nil, nil}, }, nil, &OverlayArgumentError{OverlayEphemeralUnexpectedUpper, "/proc/"}, nil, nil},
{"mkdirTemp upper ephemeral", &Params{ParentPerm: 0705}, &MountOverlayOp{ {"mkdirTemp upper ephemeral", &Params{ParentPerm: 0705}, &MountOverlayOp{
Target: MustAbs("/"), Target: check.MustAbs("/"),
Lower: []*Absolute{ Lower: []*check.Absolute{
MustAbs("/var/lib/planterette/base/debian:f92c9052"), check.MustAbs("/var/lib/planterette/base/debian:f92c9052"),
MustAbs("/var/lib/planterette/app/org.chromium.Chromium@debian:f92c9052"), check.MustAbs("/var/lib/planterette/app/org.chromium.Chromium@debian:f92c9052"),
}, },
Upper: MustAbs("/"), Upper: check.MustAbs("/"),
}, []stub.Call{ }, []stub.Call{
call("evalSymlinks", stub.ExpectArgs{"/var/lib/planterette/base/debian:f92c9052"}, "/var/lib/planterette/base/debian:f92c9052", nil), call("evalSymlinks", stub.ExpectArgs{"/var/lib/planterette/base/debian:f92c9052"}, "/var/lib/planterette/base/debian:f92c9052", nil),
call("evalSymlinks", stub.ExpectArgs{"/var/lib/planterette/app/org.chromium.Chromium@debian:f92c9052"}, "/var/lib/planterette/app/org.chromium.Chromium@debian:f92c9052", nil), call("evalSymlinks", stub.ExpectArgs{"/var/lib/planterette/app/org.chromium.Chromium@debian:f92c9052"}, "/var/lib/planterette/app/org.chromium.Chromium@debian:f92c9052", nil),
@ -62,12 +63,12 @@ func TestMountOverlayOp(t *testing.T) {
}, stub.UniqueError(6)}, }, stub.UniqueError(6)},
{"mkdirTemp work ephemeral", &Params{ParentPerm: 0705}, &MountOverlayOp{ {"mkdirTemp work ephemeral", &Params{ParentPerm: 0705}, &MountOverlayOp{
Target: MustAbs("/"), Target: check.MustAbs("/"),
Lower: []*Absolute{ Lower: []*check.Absolute{
MustAbs("/var/lib/planterette/base/debian:f92c9052"), check.MustAbs("/var/lib/planterette/base/debian:f92c9052"),
MustAbs("/var/lib/planterette/app/org.chromium.Chromium@debian:f92c9052"), check.MustAbs("/var/lib/planterette/app/org.chromium.Chromium@debian:f92c9052"),
}, },
Upper: MustAbs("/"), Upper: check.MustAbs("/"),
}, []stub.Call{ }, []stub.Call{
call("evalSymlinks", stub.ExpectArgs{"/var/lib/planterette/base/debian:f92c9052"}, "/var/lib/planterette/base/debian:f92c9052", nil), call("evalSymlinks", stub.ExpectArgs{"/var/lib/planterette/base/debian:f92c9052"}, "/var/lib/planterette/base/debian:f92c9052", nil),
call("evalSymlinks", stub.ExpectArgs{"/var/lib/planterette/app/org.chromium.Chromium@debian:f92c9052"}, "/var/lib/planterette/app/org.chromium.Chromium@debian:f92c9052", nil), call("evalSymlinks", stub.ExpectArgs{"/var/lib/planterette/app/org.chromium.Chromium@debian:f92c9052"}, "/var/lib/planterette/app/org.chromium.Chromium@debian:f92c9052", nil),
@ -78,12 +79,12 @@ func TestMountOverlayOp(t *testing.T) {
}, stub.UniqueError(5)}, }, stub.UniqueError(5)},
{"success ephemeral", &Params{ParentPerm: 0705}, &MountOverlayOp{ {"success ephemeral", &Params{ParentPerm: 0705}, &MountOverlayOp{
Target: MustAbs("/"), Target: check.MustAbs("/"),
Lower: []*Absolute{ Lower: []*check.Absolute{
MustAbs("/var/lib/planterette/base/debian:f92c9052"), check.MustAbs("/var/lib/planterette/base/debian:f92c9052"),
MustAbs("/var/lib/planterette/app/org.chromium.Chromium@debian:f92c9052"), check.MustAbs("/var/lib/planterette/app/org.chromium.Chromium@debian:f92c9052"),
}, },
Upper: MustAbs("/"), Upper: check.MustAbs("/"),
}, []stub.Call{ }, []stub.Call{
call("evalSymlinks", stub.ExpectArgs{"/var/lib/planterette/base/debian:f92c9052"}, "/var/lib/planterette/base/debian:f92c9052", nil), call("evalSymlinks", stub.ExpectArgs{"/var/lib/planterette/base/debian:f92c9052"}, "/var/lib/planterette/base/debian:f92c9052", nil),
call("evalSymlinks", stub.ExpectArgs{"/var/lib/planterette/app/org.chromium.Chromium@debian:f92c9052"}, "/var/lib/planterette/app/org.chromium.Chromium@debian:f92c9052", nil), call("evalSymlinks", stub.ExpectArgs{"/var/lib/planterette/app/org.chromium.Chromium@debian:f92c9052"}, "/var/lib/planterette/app/org.chromium.Chromium@debian:f92c9052", nil),
@ -101,9 +102,9 @@ func TestMountOverlayOp(t *testing.T) {
}, nil}, }, nil},
{"short lower ro", &Params{ParentPerm: 0755}, &MountOverlayOp{ {"short lower ro", &Params{ParentPerm: 0755}, &MountOverlayOp{
Target: MustAbs("/nix/store"), Target: check.MustAbs("/nix/store"),
Lower: []*Absolute{ Lower: []*check.Absolute{
MustAbs("/mnt-root/nix/.ro-store"), check.MustAbs("/mnt-root/nix/.ro-store"),
}, },
}, []stub.Call{ }, []stub.Call{
call("evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.ro-store"}, "/mnt-root/nix/.ro-store", nil), call("evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.ro-store"}, "/mnt-root/nix/.ro-store", nil),
@ -112,10 +113,10 @@ func TestMountOverlayOp(t *testing.T) {
}, &OverlayArgumentError{OverlayReadonlyLower, zeroString}}, }, &OverlayArgumentError{OverlayReadonlyLower, zeroString}},
{"success ro noPrefix", &Params{ParentPerm: 0755}, &MountOverlayOp{ {"success ro noPrefix", &Params{ParentPerm: 0755}, &MountOverlayOp{
Target: MustAbs("/nix/store"), Target: check.MustAbs("/nix/store"),
Lower: []*Absolute{ Lower: []*check.Absolute{
MustAbs("/mnt-root/nix/.ro-store"), check.MustAbs("/mnt-root/nix/.ro-store"),
MustAbs("/mnt-root/nix/.ro-store0"), check.MustAbs("/mnt-root/nix/.ro-store0"),
}, },
noPrefix: true, noPrefix: true,
}, []stub.Call{ }, []stub.Call{
@ -131,10 +132,10 @@ func TestMountOverlayOp(t *testing.T) {
}, nil}, }, nil},
{"success ro", &Params{ParentPerm: 0755}, &MountOverlayOp{ {"success ro", &Params{ParentPerm: 0755}, &MountOverlayOp{
Target: MustAbs("/nix/store"), Target: check.MustAbs("/nix/store"),
Lower: []*Absolute{ Lower: []*check.Absolute{
MustAbs("/mnt-root/nix/.ro-store"), check.MustAbs("/mnt-root/nix/.ro-store"),
MustAbs("/mnt-root/nix/.ro-store0"), check.MustAbs("/mnt-root/nix/.ro-store0"),
}, },
}, []stub.Call{ }, []stub.Call{
call("evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.ro-store"}, "/mnt-root/nix/.ro-store", nil), call("evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.ro-store"}, "/mnt-root/nix/.ro-store", nil),
@ -149,9 +150,9 @@ func TestMountOverlayOp(t *testing.T) {
}, nil}, }, nil},
{"nil lower", &Params{ParentPerm: 0700}, &MountOverlayOp{ {"nil lower", &Params{ParentPerm: 0700}, &MountOverlayOp{
Target: MustAbs("/nix/store"), Target: check.MustAbs("/nix/store"),
Upper: MustAbs("/mnt-root/nix/.rw-store/upper"), Upper: check.MustAbs("/mnt-root/nix/.rw-store/upper"),
Work: MustAbs("/mnt-root/nix/.rw-store/work"), Work: check.MustAbs("/mnt-root/nix/.rw-store/work"),
}, []stub.Call{ }, []stub.Call{
call("evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.rw-store/upper"}, "/mnt-root/nix/.rw-store/.upper", nil), call("evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.rw-store/upper"}, "/mnt-root/nix/.rw-store/.upper", nil),
call("evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.rw-store/work"}, "/mnt-root/nix/.rw-store/.work", nil), call("evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.rw-store/work"}, "/mnt-root/nix/.rw-store/.work", nil),
@ -160,29 +161,29 @@ func TestMountOverlayOp(t *testing.T) {
}, &OverlayArgumentError{OverlayEmptyLower, zeroString}}, }, &OverlayArgumentError{OverlayEmptyLower, zeroString}},
{"evalSymlinks upper", &Params{ParentPerm: 0700}, &MountOverlayOp{ {"evalSymlinks upper", &Params{ParentPerm: 0700}, &MountOverlayOp{
Target: MustAbs("/nix/store"), Target: check.MustAbs("/nix/store"),
Lower: []*Absolute{MustAbs("/mnt-root/nix/.ro-store")}, Lower: []*check.Absolute{check.MustAbs("/mnt-root/nix/.ro-store")},
Upper: MustAbs("/mnt-root/nix/.rw-store/upper"), Upper: check.MustAbs("/mnt-root/nix/.rw-store/upper"),
Work: MustAbs("/mnt-root/nix/.rw-store/work"), Work: check.MustAbs("/mnt-root/nix/.rw-store/work"),
}, []stub.Call{ }, []stub.Call{
call("evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.rw-store/upper"}, "/mnt-root/nix/.rw-store/.upper", stub.UniqueError(4)), call("evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.rw-store/upper"}, "/mnt-root/nix/.rw-store/.upper", stub.UniqueError(4)),
}, stub.UniqueError(4), nil, nil}, }, stub.UniqueError(4), nil, nil},
{"evalSymlinks work", &Params{ParentPerm: 0700}, &MountOverlayOp{ {"evalSymlinks work", &Params{ParentPerm: 0700}, &MountOverlayOp{
Target: MustAbs("/nix/store"), Target: check.MustAbs("/nix/store"),
Lower: []*Absolute{MustAbs("/mnt-root/nix/.ro-store")}, Lower: []*check.Absolute{check.MustAbs("/mnt-root/nix/.ro-store")},
Upper: MustAbs("/mnt-root/nix/.rw-store/upper"), Upper: check.MustAbs("/mnt-root/nix/.rw-store/upper"),
Work: MustAbs("/mnt-root/nix/.rw-store/work"), Work: check.MustAbs("/mnt-root/nix/.rw-store/work"),
}, []stub.Call{ }, []stub.Call{
call("evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.rw-store/upper"}, "/mnt-root/nix/.rw-store/.upper", nil), call("evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.rw-store/upper"}, "/mnt-root/nix/.rw-store/.upper", nil),
call("evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.rw-store/work"}, "/mnt-root/nix/.rw-store/.work", stub.UniqueError(3)), call("evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.rw-store/work"}, "/mnt-root/nix/.rw-store/.work", stub.UniqueError(3)),
}, stub.UniqueError(3), nil, nil}, }, stub.UniqueError(3), nil, nil},
{"evalSymlinks lower", &Params{ParentPerm: 0700}, &MountOverlayOp{ {"evalSymlinks lower", &Params{ParentPerm: 0700}, &MountOverlayOp{
Target: MustAbs("/nix/store"), Target: check.MustAbs("/nix/store"),
Lower: []*Absolute{MustAbs("/mnt-root/nix/.ro-store")}, Lower: []*check.Absolute{check.MustAbs("/mnt-root/nix/.ro-store")},
Upper: MustAbs("/mnt-root/nix/.rw-store/upper"), Upper: check.MustAbs("/mnt-root/nix/.rw-store/upper"),
Work: MustAbs("/mnt-root/nix/.rw-store/work"), Work: check.MustAbs("/mnt-root/nix/.rw-store/work"),
}, []stub.Call{ }, []stub.Call{
call("evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.rw-store/upper"}, "/mnt-root/nix/.rw-store/.upper", nil), call("evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.rw-store/upper"}, "/mnt-root/nix/.rw-store/.upper", nil),
call("evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.rw-store/work"}, "/mnt-root/nix/.rw-store/.work", nil), call("evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.rw-store/work"}, "/mnt-root/nix/.rw-store/.work", nil),
@ -190,10 +191,10 @@ func TestMountOverlayOp(t *testing.T) {
}, stub.UniqueError(2), nil, nil}, }, stub.UniqueError(2), nil, nil},
{"mkdirAll", &Params{ParentPerm: 0700}, &MountOverlayOp{ {"mkdirAll", &Params{ParentPerm: 0700}, &MountOverlayOp{
Target: MustAbs("/nix/store"), Target: check.MustAbs("/nix/store"),
Lower: []*Absolute{MustAbs("/mnt-root/nix/.ro-store")}, Lower: []*check.Absolute{check.MustAbs("/mnt-root/nix/.ro-store")},
Upper: MustAbs("/mnt-root/nix/.rw-store/upper"), Upper: check.MustAbs("/mnt-root/nix/.rw-store/upper"),
Work: MustAbs("/mnt-root/nix/.rw-store/work"), Work: check.MustAbs("/mnt-root/nix/.rw-store/work"),
}, []stub.Call{ }, []stub.Call{
call("evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.rw-store/upper"}, "/mnt-root/nix/.rw-store/.upper", nil), call("evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.rw-store/upper"}, "/mnt-root/nix/.rw-store/.upper", nil),
call("evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.rw-store/work"}, "/mnt-root/nix/.rw-store/.work", nil), call("evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.rw-store/work"}, "/mnt-root/nix/.rw-store/.work", nil),
@ -203,10 +204,10 @@ func TestMountOverlayOp(t *testing.T) {
}, stub.UniqueError(1)}, }, stub.UniqueError(1)},
{"mount", &Params{ParentPerm: 0700}, &MountOverlayOp{ {"mount", &Params{ParentPerm: 0700}, &MountOverlayOp{
Target: MustAbs("/nix/store"), Target: check.MustAbs("/nix/store"),
Lower: []*Absolute{MustAbs("/mnt-root/nix/.ro-store")}, Lower: []*check.Absolute{check.MustAbs("/mnt-root/nix/.ro-store")},
Upper: MustAbs("/mnt-root/nix/.rw-store/upper"), Upper: check.MustAbs("/mnt-root/nix/.rw-store/upper"),
Work: MustAbs("/mnt-root/nix/.rw-store/work"), Work: check.MustAbs("/mnt-root/nix/.rw-store/work"),
}, []stub.Call{ }, []stub.Call{
call("evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.rw-store/upper"}, "/mnt-root/nix/.rw-store/.upper", nil), call("evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.rw-store/upper"}, "/mnt-root/nix/.rw-store/.upper", nil),
call("evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.rw-store/work"}, "/mnt-root/nix/.rw-store/.work", nil), call("evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.rw-store/work"}, "/mnt-root/nix/.rw-store/.work", nil),
@ -217,10 +218,10 @@ func TestMountOverlayOp(t *testing.T) {
}, stub.UniqueError(0)}, }, stub.UniqueError(0)},
{"success single layer", &Params{ParentPerm: 0700}, &MountOverlayOp{ {"success single layer", &Params{ParentPerm: 0700}, &MountOverlayOp{
Target: MustAbs("/nix/store"), Target: check.MustAbs("/nix/store"),
Lower: []*Absolute{MustAbs("/mnt-root/nix/.ro-store")}, Lower: []*check.Absolute{check.MustAbs("/mnt-root/nix/.ro-store")},
Upper: MustAbs("/mnt-root/nix/.rw-store/upper"), Upper: check.MustAbs("/mnt-root/nix/.rw-store/upper"),
Work: MustAbs("/mnt-root/nix/.rw-store/work"), Work: check.MustAbs("/mnt-root/nix/.rw-store/work"),
}, []stub.Call{ }, []stub.Call{
call("evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.rw-store/upper"}, "/mnt-root/nix/.rw-store/.upper", nil), call("evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.rw-store/upper"}, "/mnt-root/nix/.rw-store/.upper", nil),
call("evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.rw-store/work"}, "/mnt-root/nix/.rw-store/.work", nil), call("evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.rw-store/work"}, "/mnt-root/nix/.rw-store/.work", nil),
@ -235,16 +236,16 @@ func TestMountOverlayOp(t *testing.T) {
}, nil}, }, nil},
{"success", &Params{ParentPerm: 0700}, &MountOverlayOp{ {"success", &Params{ParentPerm: 0700}, &MountOverlayOp{
Target: MustAbs("/nix/store"), Target: check.MustAbs("/nix/store"),
Lower: []*Absolute{ Lower: []*check.Absolute{
MustAbs("/mnt-root/nix/.ro-store"), check.MustAbs("/mnt-root/nix/.ro-store"),
MustAbs("/mnt-root/nix/.ro-store0"), check.MustAbs("/mnt-root/nix/.ro-store0"),
MustAbs("/mnt-root/nix/.ro-store1"), check.MustAbs("/mnt-root/nix/.ro-store1"),
MustAbs("/mnt-root/nix/.ro-store2"), check.MustAbs("/mnt-root/nix/.ro-store2"),
MustAbs("/mnt-root/nix/.ro-store3"), check.MustAbs("/mnt-root/nix/.ro-store3"),
}, },
Upper: MustAbs("/mnt-root/nix/.rw-store/upper"), Upper: check.MustAbs("/mnt-root/nix/.rw-store/upper"),
Work: MustAbs("/mnt-root/nix/.rw-store/work"), Work: check.MustAbs("/mnt-root/nix/.rw-store/work"),
}, []stub.Call{ }, []stub.Call{
call("evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.rw-store/upper"}, "/mnt-root/nix/.rw-store/.upper", nil), call("evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.rw-store/upper"}, "/mnt-root/nix/.rw-store/.upper", nil),
call("evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.rw-store/work"}, "/mnt-root/nix/.rw-store/.work", nil), call("evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.rw-store/work"}, "/mnt-root/nix/.rw-store/.work", nil),
@ -272,7 +273,7 @@ func TestMountOverlayOp(t *testing.T) {
t.Run("nil Upper non-nil Work not ephemeral", func(t *testing.T) { t.Run("nil Upper non-nil Work not ephemeral", func(t *testing.T) {
wantErr := OpStateError("overlay") wantErr := OpStateError("overlay")
if err := (&MountOverlayOp{ if err := (&MountOverlayOp{
Work: MustAbs("/"), Work: check.MustAbs("/"),
}).early(nil, nil); !errors.Is(err, wantErr) { }).early(nil, nil); !errors.Is(err, wantErr) {
t.Errorf("apply: error = %v, want %v", err, wantErr) t.Errorf("apply: error = %v, want %v", err, wantErr)
} }
@ -282,39 +283,39 @@ func TestMountOverlayOp(t *testing.T) {
checkOpsValid(t, []opValidTestCase{ checkOpsValid(t, []opValidTestCase{
{"nil", (*MountOverlayOp)(nil), false}, {"nil", (*MountOverlayOp)(nil), false},
{"zero", new(MountOverlayOp), false}, {"zero", new(MountOverlayOp), false},
{"nil lower", &MountOverlayOp{Target: MustAbs("/"), Lower: []*Absolute{nil}}, false}, {"nil lower", &MountOverlayOp{Target: check.MustAbs("/"), Lower: []*check.Absolute{nil}}, false},
{"ro", &MountOverlayOp{Target: MustAbs("/"), Lower: []*Absolute{MustAbs("/")}}, true}, {"ro", &MountOverlayOp{Target: check.MustAbs("/"), Lower: []*check.Absolute{check.MustAbs("/")}}, true},
{"ro work", &MountOverlayOp{Target: MustAbs("/"), Work: MustAbs("/tmp/")}, false}, {"ro work", &MountOverlayOp{Target: check.MustAbs("/"), Work: check.MustAbs("/tmp/")}, false},
{"rw", &MountOverlayOp{Target: MustAbs("/"), Lower: []*Absolute{MustAbs("/")}, Upper: MustAbs("/"), Work: MustAbs("/")}, true}, {"rw", &MountOverlayOp{Target: check.MustAbs("/"), Lower: []*check.Absolute{check.MustAbs("/")}, Upper: check.MustAbs("/"), Work: check.MustAbs("/")}, true},
}) })
checkOpsBuilder(t, []opsBuilderTestCase{ checkOpsBuilder(t, []opsBuilderTestCase{
{"full", new(Ops).Overlay( {"full", new(Ops).Overlay(
MustAbs("/nix/store"), check.MustAbs("/nix/store"),
MustAbs("/mnt-root/nix/.rw-store/upper"), check.MustAbs("/mnt-root/nix/.rw-store/upper"),
MustAbs("/mnt-root/nix/.rw-store/work"), check.MustAbs("/mnt-root/nix/.rw-store/work"),
MustAbs("/mnt-root/nix/.ro-store"), check.MustAbs("/mnt-root/nix/.ro-store"),
), Ops{ ), Ops{
&MountOverlayOp{ &MountOverlayOp{
Target: MustAbs("/nix/store"), Target: check.MustAbs("/nix/store"),
Lower: []*Absolute{MustAbs("/mnt-root/nix/.ro-store")}, Lower: []*check.Absolute{check.MustAbs("/mnt-root/nix/.ro-store")},
Upper: MustAbs("/mnt-root/nix/.rw-store/upper"), Upper: check.MustAbs("/mnt-root/nix/.rw-store/upper"),
Work: MustAbs("/mnt-root/nix/.rw-store/work"), Work: check.MustAbs("/mnt-root/nix/.rw-store/work"),
}, },
}}, }},
{"ephemeral", new(Ops).OverlayEphemeral(MustAbs("/nix/store"), MustAbs("/mnt-root/nix/.ro-store")), Ops{ {"ephemeral", new(Ops).OverlayEphemeral(check.MustAbs("/nix/store"), check.MustAbs("/mnt-root/nix/.ro-store")), Ops{
&MountOverlayOp{ &MountOverlayOp{
Target: MustAbs("/nix/store"), Target: check.MustAbs("/nix/store"),
Lower: []*Absolute{MustAbs("/mnt-root/nix/.ro-store")}, Lower: []*check.Absolute{check.MustAbs("/mnt-root/nix/.ro-store")},
Upper: MustAbs("/"), Upper: check.MustAbs("/"),
}, },
}}, }},
{"readonly", new(Ops).OverlayReadonly(MustAbs("/nix/store"), MustAbs("/mnt-root/nix/.ro-store")), Ops{ {"readonly", new(Ops).OverlayReadonly(check.MustAbs("/nix/store"), check.MustAbs("/mnt-root/nix/.ro-store")), Ops{
&MountOverlayOp{ &MountOverlayOp{
Target: MustAbs("/nix/store"), Target: check.MustAbs("/nix/store"),
Lower: []*Absolute{MustAbs("/mnt-root/nix/.ro-store")}, Lower: []*check.Absolute{check.MustAbs("/mnt-root/nix/.ro-store")},
}, },
}}, }},
}) })
@ -323,74 +324,74 @@ func TestMountOverlayOp(t *testing.T) {
{"zero", new(MountOverlayOp), new(MountOverlayOp), false}, {"zero", new(MountOverlayOp), new(MountOverlayOp), false},
{"differs target", &MountOverlayOp{ {"differs target", &MountOverlayOp{
Target: MustAbs("/nix/store/differs"), Target: check.MustAbs("/nix/store/differs"),
Lower: []*Absolute{MustAbs("/mnt-root/nix/.ro-store")}, Lower: []*check.Absolute{check.MustAbs("/mnt-root/nix/.ro-store")},
Upper: MustAbs("/mnt-root/nix/.rw-store/upper"), Upper: check.MustAbs("/mnt-root/nix/.rw-store/upper"),
Work: MustAbs("/mnt-root/nix/.rw-store/work"), Work: check.MustAbs("/mnt-root/nix/.rw-store/work"),
}, &MountOverlayOp{ }, &MountOverlayOp{
Target: MustAbs("/nix/store"), Target: check.MustAbs("/nix/store"),
Lower: []*Absolute{MustAbs("/mnt-root/nix/.ro-store")}, Lower: []*check.Absolute{check.MustAbs("/mnt-root/nix/.ro-store")},
Upper: MustAbs("/mnt-root/nix/.rw-store/upper"), Upper: check.MustAbs("/mnt-root/nix/.rw-store/upper"),
Work: MustAbs("/mnt-root/nix/.rw-store/work")}, false}, Work: check.MustAbs("/mnt-root/nix/.rw-store/work")}, false},
{"differs lower", &MountOverlayOp{ {"differs lower", &MountOverlayOp{
Target: MustAbs("/nix/store"), Target: check.MustAbs("/nix/store"),
Lower: []*Absolute{MustAbs("/mnt-root/nix/.ro-store/differs")}, Lower: []*check.Absolute{check.MustAbs("/mnt-root/nix/.ro-store/differs")},
Upper: MustAbs("/mnt-root/nix/.rw-store/upper"), Upper: check.MustAbs("/mnt-root/nix/.rw-store/upper"),
Work: MustAbs("/mnt-root/nix/.rw-store/work"), Work: check.MustAbs("/mnt-root/nix/.rw-store/work"),
}, &MountOverlayOp{ }, &MountOverlayOp{
Target: MustAbs("/nix/store"), Target: check.MustAbs("/nix/store"),
Lower: []*Absolute{MustAbs("/mnt-root/nix/.ro-store")}, Lower: []*check.Absolute{check.MustAbs("/mnt-root/nix/.ro-store")},
Upper: MustAbs("/mnt-root/nix/.rw-store/upper"), Upper: check.MustAbs("/mnt-root/nix/.rw-store/upper"),
Work: MustAbs("/mnt-root/nix/.rw-store/work")}, false}, Work: check.MustAbs("/mnt-root/nix/.rw-store/work")}, false},
{"differs upper", &MountOverlayOp{ {"differs upper", &MountOverlayOp{
Target: MustAbs("/nix/store"), Target: check.MustAbs("/nix/store"),
Lower: []*Absolute{MustAbs("/mnt-root/nix/.ro-store")}, Lower: []*check.Absolute{check.MustAbs("/mnt-root/nix/.ro-store")},
Upper: MustAbs("/mnt-root/nix/.rw-store/upper/differs"), Upper: check.MustAbs("/mnt-root/nix/.rw-store/upper/differs"),
Work: MustAbs("/mnt-root/nix/.rw-store/work"), Work: check.MustAbs("/mnt-root/nix/.rw-store/work"),
}, &MountOverlayOp{ }, &MountOverlayOp{
Target: MustAbs("/nix/store"), Target: check.MustAbs("/nix/store"),
Lower: []*Absolute{MustAbs("/mnt-root/nix/.ro-store")}, Lower: []*check.Absolute{check.MustAbs("/mnt-root/nix/.ro-store")},
Upper: MustAbs("/mnt-root/nix/.rw-store/upper"), Upper: check.MustAbs("/mnt-root/nix/.rw-store/upper"),
Work: MustAbs("/mnt-root/nix/.rw-store/work")}, false}, Work: check.MustAbs("/mnt-root/nix/.rw-store/work")}, false},
{"differs work", &MountOverlayOp{ {"differs work", &MountOverlayOp{
Target: MustAbs("/nix/store"), Target: check.MustAbs("/nix/store"),
Lower: []*Absolute{MustAbs("/mnt-root/nix/.ro-store")}, Lower: []*check.Absolute{check.MustAbs("/mnt-root/nix/.ro-store")},
Upper: MustAbs("/mnt-root/nix/.rw-store/upper"), Upper: check.MustAbs("/mnt-root/nix/.rw-store/upper"),
Work: MustAbs("/mnt-root/nix/.rw-store/work/differs"), Work: check.MustAbs("/mnt-root/nix/.rw-store/work/differs"),
}, &MountOverlayOp{ }, &MountOverlayOp{
Target: MustAbs("/nix/store"), Target: check.MustAbs("/nix/store"),
Lower: []*Absolute{MustAbs("/mnt-root/nix/.ro-store")}, Lower: []*check.Absolute{check.MustAbs("/mnt-root/nix/.ro-store")},
Upper: MustAbs("/mnt-root/nix/.rw-store/upper"), Upper: check.MustAbs("/mnt-root/nix/.rw-store/upper"),
Work: MustAbs("/mnt-root/nix/.rw-store/work")}, false}, Work: check.MustAbs("/mnt-root/nix/.rw-store/work")}, false},
{"equals ro", &MountOverlayOp{ {"equals ro", &MountOverlayOp{
Target: MustAbs("/nix/store"), Target: check.MustAbs("/nix/store"),
Lower: []*Absolute{MustAbs("/mnt-root/nix/.ro-store")}, Lower: []*check.Absolute{check.MustAbs("/mnt-root/nix/.ro-store")},
}, &MountOverlayOp{ }, &MountOverlayOp{
Target: MustAbs("/nix/store"), Target: check.MustAbs("/nix/store"),
Lower: []*Absolute{MustAbs("/mnt-root/nix/.ro-store")}}, true}, Lower: []*check.Absolute{check.MustAbs("/mnt-root/nix/.ro-store")}}, true},
{"equals", &MountOverlayOp{ {"equals", &MountOverlayOp{
Target: MustAbs("/nix/store"), Target: check.MustAbs("/nix/store"),
Lower: []*Absolute{MustAbs("/mnt-root/nix/.ro-store")}, Lower: []*check.Absolute{check.MustAbs("/mnt-root/nix/.ro-store")},
Upper: MustAbs("/mnt-root/nix/.rw-store/upper"), Upper: check.MustAbs("/mnt-root/nix/.rw-store/upper"),
Work: MustAbs("/mnt-root/nix/.rw-store/work"), Work: check.MustAbs("/mnt-root/nix/.rw-store/work"),
}, &MountOverlayOp{ }, &MountOverlayOp{
Target: MustAbs("/nix/store"), Target: check.MustAbs("/nix/store"),
Lower: []*Absolute{MustAbs("/mnt-root/nix/.ro-store")}, Lower: []*check.Absolute{check.MustAbs("/mnt-root/nix/.ro-store")},
Upper: MustAbs("/mnt-root/nix/.rw-store/upper"), Upper: check.MustAbs("/mnt-root/nix/.rw-store/upper"),
Work: MustAbs("/mnt-root/nix/.rw-store/work")}, true}, Work: check.MustAbs("/mnt-root/nix/.rw-store/work")}, true},
}) })
checkOpMeta(t, []opMetaTestCase{ checkOpMeta(t, []opMetaTestCase{
{"nix", &MountOverlayOp{ {"nix", &MountOverlayOp{
Target: MustAbs("/nix/store"), Target: check.MustAbs("/nix/store"),
Lower: []*Absolute{MustAbs("/mnt-root/nix/.ro-store")}, Lower: []*check.Absolute{check.MustAbs("/mnt-root/nix/.ro-store")},
Upper: MustAbs("/mnt-root/nix/.rw-store/upper"), Upper: check.MustAbs("/mnt-root/nix/.rw-store/upper"),
Work: MustAbs("/mnt-root/nix/.rw-store/work"), Work: check.MustAbs("/mnt-root/nix/.rw-store/work"),
}, "mounting", `overlay on "/nix/store" with 1 layers`}, }, "mounting", `overlay on "/nix/store" with 1 layers`},
}) })
} }

View File

@ -4,6 +4,8 @@ import (
"encoding/gob" "encoding/gob"
"fmt" "fmt"
"syscall" "syscall"
"hakurei.app/container/check"
) )
const ( const (
@ -14,13 +16,13 @@ const (
func init() { gob.Register(new(TmpfileOp)) } func init() { gob.Register(new(TmpfileOp)) }
// Place appends an [Op] that places a file in container path [TmpfileOp.Path] containing [TmpfileOp.Data]. // Place appends an [Op] that places a file in container path [TmpfileOp.Path] containing [TmpfileOp.Data].
func (f *Ops) Place(name *Absolute, data []byte) *Ops { func (f *Ops) Place(name *check.Absolute, data []byte) *Ops {
*f = append(*f, &TmpfileOp{name, data}) *f = append(*f, &TmpfileOp{name, data})
return f return f
} }
// PlaceP is like Place but writes the address of [TmpfileOp.Data] to the pointer dataP points to. // PlaceP is like Place but writes the address of [TmpfileOp.Data] to the pointer dataP points to.
func (f *Ops) PlaceP(name *Absolute, dataP **[]byte) *Ops { func (f *Ops) PlaceP(name *check.Absolute, dataP **[]byte) *Ops {
t := &TmpfileOp{Path: name} t := &TmpfileOp{Path: name}
*dataP = &t.Data *dataP = &t.Data
@ -30,7 +32,7 @@ func (f *Ops) PlaceP(name *Absolute, dataP **[]byte) *Ops {
// TmpfileOp places a file on container Path containing Data. // TmpfileOp places a file on container Path containing Data.
type TmpfileOp struct { type TmpfileOp struct {
Path *Absolute Path *check.Absolute
Data []byte Data []byte
} }

View File

@ -4,13 +4,14 @@ import (
"os" "os"
"testing" "testing"
"hakurei.app/container/check"
"hakurei.app/container/stub" "hakurei.app/container/stub"
) )
func TestTmpfileOp(t *testing.T) { func TestTmpfileOp(t *testing.T) {
const sampleDataString = `chronos:x:65534:65534:Hakurei:/var/empty:/bin/zsh` const sampleDataString = `chronos:x:65534:65534:Hakurei:/var/empty:/bin/zsh`
var ( var (
samplePath = MustAbs("/etc/passwd") samplePath = check.MustAbs("/etc/passwd")
sampleData = []byte(sampleDataString) sampleData = []byte(sampleDataString)
) )
@ -100,7 +101,7 @@ func TestTmpfileOp(t *testing.T) {
{"zero", new(TmpfileOp), new(TmpfileOp), false}, {"zero", new(TmpfileOp), new(TmpfileOp), false},
{"differs path", &TmpfileOp{ {"differs path", &TmpfileOp{
Path: MustAbs("/etc/group"), Path: check.MustAbs("/etc/group"),
Data: sampleData, Data: sampleData,
}, &TmpfileOp{ }, &TmpfileOp{
Path: samplePath, Path: samplePath,

View File

@ -4,18 +4,20 @@ import (
"encoding/gob" "encoding/gob"
"fmt" "fmt"
. "syscall" . "syscall"
"hakurei.app/container/check"
) )
func init() { gob.Register(new(MountProcOp)) } func init() { gob.Register(new(MountProcOp)) }
// Proc appends an [Op] that mounts a private instance of proc. // Proc appends an [Op] that mounts a private instance of proc.
func (f *Ops) Proc(target *Absolute) *Ops { func (f *Ops) Proc(target *check.Absolute) *Ops {
*f = append(*f, &MountProcOp{target}) *f = append(*f, &MountProcOp{target})
return f return f
} }
// MountProcOp mounts a new instance of [FstypeProc] on container path Target. // MountProcOp mounts a new instance of [FstypeProc] on container path Target.
type MountProcOp struct{ Target *Absolute } type MountProcOp struct{ Target *check.Absolute }
func (p *MountProcOp) Valid() bool { return p != nil && p.Target != nil } func (p *MountProcOp) Valid() bool { return p != nil && p.Target != nil }
func (p *MountProcOp) early(*setupState, syscallDispatcher) error { return nil } func (p *MountProcOp) early(*setupState, syscallDispatcher) error { return nil }

View File

@ -4,6 +4,7 @@ import (
"os" "os"
"testing" "testing"
"hakurei.app/container/check"
"hakurei.app/container/stub" "hakurei.app/container/stub"
) )
@ -11,14 +12,14 @@ func TestMountProcOp(t *testing.T) {
checkOpBehaviour(t, []opBehaviourTestCase{ checkOpBehaviour(t, []opBehaviourTestCase{
{"mkdir", &Params{ParentPerm: 0755}, {"mkdir", &Params{ParentPerm: 0755},
&MountProcOp{ &MountProcOp{
Target: MustAbs("/proc/"), Target: check.MustAbs("/proc/"),
}, nil, nil, []stub.Call{ }, nil, nil, []stub.Call{
call("mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0755)}, nil, stub.UniqueError(0)), call("mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0755)}, nil, stub.UniqueError(0)),
}, stub.UniqueError(0)}, }, stub.UniqueError(0)},
{"success", &Params{ParentPerm: 0700}, {"success", &Params{ParentPerm: 0700},
&MountProcOp{ &MountProcOp{
Target: MustAbs("/proc/"), Target: check.MustAbs("/proc/"),
}, nil, nil, []stub.Call{ }, nil, nil, []stub.Call{
call("mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0700)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/sysroot/proc", os.FileMode(0700)}, nil, nil),
call("mount", stub.ExpectArgs{"proc", "/sysroot/proc", "proc", uintptr(0xe), ""}, nil, nil), call("mount", stub.ExpectArgs{"proc", "/sysroot/proc", "proc", uintptr(0xe), ""}, nil, nil),
@ -28,12 +29,12 @@ func TestMountProcOp(t *testing.T) {
checkOpsValid(t, []opValidTestCase{ checkOpsValid(t, []opValidTestCase{
{"nil", (*MountProcOp)(nil), false}, {"nil", (*MountProcOp)(nil), false},
{"zero", new(MountProcOp), false}, {"zero", new(MountProcOp), false},
{"valid", &MountProcOp{Target: MustAbs("/proc/")}, true}, {"valid", &MountProcOp{Target: check.MustAbs("/proc/")}, true},
}) })
checkOpsBuilder(t, []opsBuilderTestCase{ checkOpsBuilder(t, []opsBuilderTestCase{
{"proc", new(Ops).Proc(MustAbs("/proc/")), Ops{ {"proc", new(Ops).Proc(check.MustAbs("/proc/")), Ops{
&MountProcOp{Target: MustAbs("/proc/")}, &MountProcOp{Target: check.MustAbs("/proc/")},
}}, }},
}) })
@ -41,20 +42,20 @@ func TestMountProcOp(t *testing.T) {
{"zero", new(MountProcOp), new(MountProcOp), false}, {"zero", new(MountProcOp), new(MountProcOp), false},
{"target differs", &MountProcOp{ {"target differs", &MountProcOp{
Target: MustAbs("/proc/nonexistent"), Target: check.MustAbs("/proc/nonexistent"),
}, &MountProcOp{ }, &MountProcOp{
Target: MustAbs("/proc/"), Target: check.MustAbs("/proc/"),
}, false}, }, false},
{"equals", &MountProcOp{ {"equals", &MountProcOp{
Target: MustAbs("/proc/"), Target: check.MustAbs("/proc/"),
}, &MountProcOp{ }, &MountProcOp{
Target: MustAbs("/proc/"), Target: check.MustAbs("/proc/"),
}, true}, }, true},
}) })
checkOpMeta(t, []opMetaTestCase{ checkOpMeta(t, []opMetaTestCase{
{"proc", &MountProcOp{Target: MustAbs("/proc/")}, {"proc", &MountProcOp{Target: check.MustAbs("/proc/")},
"mounting", `proc on "/proc/"`}, "mounting", `proc on "/proc/"`},
}) })
} }

View File

@ -3,19 +3,21 @@ package container
import ( import (
"encoding/gob" "encoding/gob"
"fmt" "fmt"
"hakurei.app/container/check"
) )
func init() { gob.Register(new(RemountOp)) } func init() { gob.Register(new(RemountOp)) }
// Remount appends an [Op] that applies [RemountOp.Flags] on container path [RemountOp.Target]. // Remount appends an [Op] that applies [RemountOp.Flags] on container path [RemountOp.Target].
func (f *Ops) Remount(target *Absolute, flags uintptr) *Ops { func (f *Ops) Remount(target *check.Absolute, flags uintptr) *Ops {
*f = append(*f, &RemountOp{target, flags}) *f = append(*f, &RemountOp{target, flags})
return f return f
} }
// RemountOp remounts Target with Flags. // RemountOp remounts Target with Flags.
type RemountOp struct { type RemountOp struct {
Target *Absolute Target *check.Absolute
Flags uintptr Flags uintptr
} }

View File

@ -4,13 +4,14 @@ import (
"syscall" "syscall"
"testing" "testing"
"hakurei.app/container/check"
"hakurei.app/container/stub" "hakurei.app/container/stub"
) )
func TestRemountOp(t *testing.T) { func TestRemountOp(t *testing.T) {
checkOpBehaviour(t, []opBehaviourTestCase{ checkOpBehaviour(t, []opBehaviourTestCase{
{"success", new(Params), &RemountOp{ {"success", new(Params), &RemountOp{
Target: MustAbs("/"), Target: check.MustAbs("/"),
Flags: syscall.MS_RDONLY, Flags: syscall.MS_RDONLY,
}, nil, nil, []stub.Call{ }, nil, nil, []stub.Call{
call("remount", stub.ExpectArgs{"/sysroot", uintptr(1)}, nil, nil), call("remount", stub.ExpectArgs{"/sysroot", uintptr(1)}, nil, nil),
@ -20,13 +21,13 @@ func TestRemountOp(t *testing.T) {
checkOpsValid(t, []opValidTestCase{ checkOpsValid(t, []opValidTestCase{
{"nil", (*RemountOp)(nil), false}, {"nil", (*RemountOp)(nil), false},
{"zero", new(RemountOp), false}, {"zero", new(RemountOp), false},
{"valid", &RemountOp{Target: MustAbs("/"), Flags: syscall.MS_RDONLY}, true}, {"valid", &RemountOp{Target: check.MustAbs("/"), Flags: syscall.MS_RDONLY}, true},
}) })
checkOpsBuilder(t, []opsBuilderTestCase{ checkOpsBuilder(t, []opsBuilderTestCase{
{"root", new(Ops).Remount(MustAbs("/"), syscall.MS_RDONLY), Ops{ {"root", new(Ops).Remount(check.MustAbs("/"), syscall.MS_RDONLY), Ops{
&RemountOp{ &RemountOp{
Target: MustAbs("/"), Target: check.MustAbs("/"),
Flags: syscall.MS_RDONLY, Flags: syscall.MS_RDONLY,
}, },
}}, }},
@ -36,33 +37,33 @@ func TestRemountOp(t *testing.T) {
{"zero", new(RemountOp), new(RemountOp), false}, {"zero", new(RemountOp), new(RemountOp), false},
{"target differs", &RemountOp{ {"target differs", &RemountOp{
Target: MustAbs("/dev/"), Target: check.MustAbs("/dev/"),
Flags: syscall.MS_RDONLY, Flags: syscall.MS_RDONLY,
}, &RemountOp{ }, &RemountOp{
Target: MustAbs("/"), Target: check.MustAbs("/"),
Flags: syscall.MS_RDONLY, Flags: syscall.MS_RDONLY,
}, false}, }, false},
{"flags differs", &RemountOp{ {"flags differs", &RemountOp{
Target: MustAbs("/"), Target: check.MustAbs("/"),
Flags: syscall.MS_RDONLY | syscall.MS_NODEV, Flags: syscall.MS_RDONLY | syscall.MS_NODEV,
}, &RemountOp{ }, &RemountOp{
Target: MustAbs("/"), Target: check.MustAbs("/"),
Flags: syscall.MS_RDONLY, Flags: syscall.MS_RDONLY,
}, false}, }, false},
{"equals", &RemountOp{ {"equals", &RemountOp{
Target: MustAbs("/"), Target: check.MustAbs("/"),
Flags: syscall.MS_RDONLY, Flags: syscall.MS_RDONLY,
}, &RemountOp{ }, &RemountOp{
Target: MustAbs("/"), Target: check.MustAbs("/"),
Flags: syscall.MS_RDONLY, Flags: syscall.MS_RDONLY,
}, true}, }, true},
}) })
checkOpMeta(t, []opMetaTestCase{ checkOpMeta(t, []opMetaTestCase{
{"root", &RemountOp{ {"root", &RemountOp{
Target: MustAbs("/"), Target: check.MustAbs("/"),
Flags: syscall.MS_RDONLY, Flags: syscall.MS_RDONLY,
}, "remounting", `"/" flags 0x1`}, }, "remounting", `"/" flags 0x1`},
}) })

View File

@ -4,19 +4,21 @@ import (
"encoding/gob" "encoding/gob"
"fmt" "fmt"
"path" "path"
"hakurei.app/container/check"
) )
func init() { gob.Register(new(SymlinkOp)) } func init() { gob.Register(new(SymlinkOp)) }
// Link appends an [Op] that creates a symlink in the container filesystem. // Link appends an [Op] that creates a symlink in the container filesystem.
func (f *Ops) Link(target *Absolute, linkName string, dereference bool) *Ops { func (f *Ops) Link(target *check.Absolute, linkName string, dereference bool) *Ops {
*f = append(*f, &SymlinkOp{target, linkName, dereference}) *f = append(*f, &SymlinkOp{target, linkName, dereference})
return f return f
} }
// SymlinkOp optionally dereferences LinkName and creates a symlink at container path Target. // SymlinkOp optionally dereferences LinkName and creates a symlink at container path Target.
type SymlinkOp struct { type SymlinkOp struct {
Target *Absolute Target *check.Absolute
// LinkName is an arbitrary uninterpreted pathname. // LinkName is an arbitrary uninterpreted pathname.
LinkName string LinkName string
@ -28,8 +30,8 @@ func (l *SymlinkOp) Valid() bool { return l != nil && l.Target != nil && l.LinkN
func (l *SymlinkOp) early(_ *setupState, k syscallDispatcher) error { func (l *SymlinkOp) early(_ *setupState, k syscallDispatcher) error {
if l.Dereference { if l.Dereference {
if !isAbs(l.LinkName) { if !path.IsAbs(l.LinkName) {
return &AbsoluteError{l.LinkName} return &check.AbsoluteError{Pathname: l.LinkName}
} }
if name, err := k.readlink(l.LinkName); err != nil { if name, err := k.readlink(l.LinkName); err != nil {
return err return err

View File

@ -4,26 +4,27 @@ import (
"os" "os"
"testing" "testing"
"hakurei.app/container/check"
"hakurei.app/container/stub" "hakurei.app/container/stub"
) )
func TestSymlinkOp(t *testing.T) { func TestSymlinkOp(t *testing.T) {
checkOpBehaviour(t, []opBehaviourTestCase{ checkOpBehaviour(t, []opBehaviourTestCase{
{"mkdir", &Params{ParentPerm: 0700}, &SymlinkOp{ {"mkdir", &Params{ParentPerm: 0700}, &SymlinkOp{
Target: MustAbs("/etc/nixos"), Target: check.MustAbs("/etc/nixos"),
LinkName: "/etc/static/nixos", LinkName: "/etc/static/nixos",
}, nil, nil, []stub.Call{ }, nil, nil, []stub.Call{
call("mkdirAll", stub.ExpectArgs{"/sysroot/etc", os.FileMode(0700)}, nil, stub.UniqueError(1)), call("mkdirAll", stub.ExpectArgs{"/sysroot/etc", os.FileMode(0700)}, nil, stub.UniqueError(1)),
}, stub.UniqueError(1)}, }, stub.UniqueError(1)},
{"abs", &Params{ParentPerm: 0755}, &SymlinkOp{ {"abs", &Params{ParentPerm: 0755}, &SymlinkOp{
Target: MustAbs("/etc/mtab"), Target: check.MustAbs("/etc/mtab"),
LinkName: "etc/mtab", LinkName: "etc/mtab",
Dereference: true, Dereference: true,
}, nil, &AbsoluteError{"etc/mtab"}, nil, nil}, }, nil, &check.AbsoluteError{Pathname: "etc/mtab"}, nil, nil},
{"readlink", &Params{ParentPerm: 0755}, &SymlinkOp{ {"readlink", &Params{ParentPerm: 0755}, &SymlinkOp{
Target: MustAbs("/etc/mtab"), Target: check.MustAbs("/etc/mtab"),
LinkName: "/etc/mtab", LinkName: "/etc/mtab",
Dereference: true, Dereference: true,
}, []stub.Call{ }, []stub.Call{
@ -31,7 +32,7 @@ func TestSymlinkOp(t *testing.T) {
}, stub.UniqueError(0), nil, nil}, }, stub.UniqueError(0), nil, nil},
{"success noderef", &Params{ParentPerm: 0700}, &SymlinkOp{ {"success noderef", &Params{ParentPerm: 0700}, &SymlinkOp{
Target: MustAbs("/etc/nixos"), Target: check.MustAbs("/etc/nixos"),
LinkName: "/etc/static/nixos", LinkName: "/etc/static/nixos",
}, nil, nil, []stub.Call{ }, nil, nil, []stub.Call{
call("mkdirAll", stub.ExpectArgs{"/sysroot/etc", os.FileMode(0700)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/sysroot/etc", os.FileMode(0700)}, nil, nil),
@ -39,7 +40,7 @@ func TestSymlinkOp(t *testing.T) {
}, nil}, }, nil},
{"success", &Params{ParentPerm: 0755}, &SymlinkOp{ {"success", &Params{ParentPerm: 0755}, &SymlinkOp{
Target: MustAbs("/etc/mtab"), Target: check.MustAbs("/etc/mtab"),
LinkName: "/etc/mtab", LinkName: "/etc/mtab",
Dereference: true, Dereference: true,
}, []stub.Call{ }, []stub.Call{
@ -54,18 +55,18 @@ func TestSymlinkOp(t *testing.T) {
{"nil", (*SymlinkOp)(nil), false}, {"nil", (*SymlinkOp)(nil), false},
{"zero", new(SymlinkOp), false}, {"zero", new(SymlinkOp), false},
{"nil target", &SymlinkOp{LinkName: "/run/current-system"}, false}, {"nil target", &SymlinkOp{LinkName: "/run/current-system"}, false},
{"zero linkname", &SymlinkOp{Target: MustAbs("/run/current-system")}, false}, {"zero linkname", &SymlinkOp{Target: check.MustAbs("/run/current-system")}, false},
{"valid", &SymlinkOp{Target: MustAbs("/run/current-system"), LinkName: "/run/current-system", Dereference: true}, true}, {"valid", &SymlinkOp{Target: check.MustAbs("/run/current-system"), LinkName: "/run/current-system", Dereference: true}, true},
}) })
checkOpsBuilder(t, []opsBuilderTestCase{ checkOpsBuilder(t, []opsBuilderTestCase{
{"current-system", new(Ops).Link( {"current-system", new(Ops).Link(
MustAbs("/run/current-system"), check.MustAbs("/run/current-system"),
"/run/current-system", "/run/current-system",
true, true,
), Ops{ ), Ops{
&SymlinkOp{ &SymlinkOp{
Target: MustAbs("/run/current-system"), Target: check.MustAbs("/run/current-system"),
LinkName: "/run/current-system", LinkName: "/run/current-system",
Dereference: true, Dereference: true,
}, },
@ -76,40 +77,40 @@ func TestSymlinkOp(t *testing.T) {
{"zero", new(SymlinkOp), new(SymlinkOp), false}, {"zero", new(SymlinkOp), new(SymlinkOp), false},
{"target differs", &SymlinkOp{ {"target differs", &SymlinkOp{
Target: MustAbs("/run/current-system/differs"), Target: check.MustAbs("/run/current-system/differs"),
LinkName: "/run/current-system", LinkName: "/run/current-system",
Dereference: true, Dereference: true,
}, &SymlinkOp{ }, &SymlinkOp{
Target: MustAbs("/run/current-system"), Target: check.MustAbs("/run/current-system"),
LinkName: "/run/current-system", LinkName: "/run/current-system",
Dereference: true, Dereference: true,
}, false}, }, false},
{"linkname differs", &SymlinkOp{ {"linkname differs", &SymlinkOp{
Target: MustAbs("/run/current-system"), Target: check.MustAbs("/run/current-system"),
LinkName: "/run/current-system/differs", LinkName: "/run/current-system/differs",
Dereference: true, Dereference: true,
}, &SymlinkOp{ }, &SymlinkOp{
Target: MustAbs("/run/current-system"), Target: check.MustAbs("/run/current-system"),
LinkName: "/run/current-system", LinkName: "/run/current-system",
Dereference: true, Dereference: true,
}, false}, }, false},
{"dereference differs", &SymlinkOp{ {"dereference differs", &SymlinkOp{
Target: MustAbs("/run/current-system"), Target: check.MustAbs("/run/current-system"),
LinkName: "/run/current-system", LinkName: "/run/current-system",
}, &SymlinkOp{ }, &SymlinkOp{
Target: MustAbs("/run/current-system"), Target: check.MustAbs("/run/current-system"),
LinkName: "/run/current-system", LinkName: "/run/current-system",
Dereference: true, Dereference: true,
}, false}, }, false},
{"equals", &SymlinkOp{ {"equals", &SymlinkOp{
Target: MustAbs("/run/current-system"), Target: check.MustAbs("/run/current-system"),
LinkName: "/run/current-system", LinkName: "/run/current-system",
Dereference: true, Dereference: true,
}, &SymlinkOp{ }, &SymlinkOp{
Target: MustAbs("/run/current-system"), Target: check.MustAbs("/run/current-system"),
LinkName: "/run/current-system", LinkName: "/run/current-system",
Dereference: true, Dereference: true,
}, true}, }, true},
@ -117,7 +118,7 @@ func TestSymlinkOp(t *testing.T) {
checkOpMeta(t, []opMetaTestCase{ checkOpMeta(t, []opMetaTestCase{
{"current-system", &SymlinkOp{ {"current-system", &SymlinkOp{
Target: MustAbs("/run/current-system"), Target: check.MustAbs("/run/current-system"),
LinkName: "/run/current-system", LinkName: "/run/current-system",
Dereference: true, Dereference: true,
}, "creating", `symlink on "/run/current-system" linkname "/run/current-system"`}, }, "creating", `symlink on "/run/current-system" linkname "/run/current-system"`},

View File

@ -7,6 +7,8 @@ import (
"os" "os"
"strconv" "strconv"
. "syscall" . "syscall"
"hakurei.app/container/check"
) )
func init() { gob.Register(new(MountTmpfsOp)) } func init() { gob.Register(new(MountTmpfsOp)) }
@ -18,13 +20,13 @@ func (e TmpfsSizeError) Error() string {
} }
// Tmpfs appends an [Op] that mounts tmpfs on container path [MountTmpfsOp.Path]. // Tmpfs appends an [Op] that mounts tmpfs on container path [MountTmpfsOp.Path].
func (f *Ops) Tmpfs(target *Absolute, size int, perm os.FileMode) *Ops { func (f *Ops) Tmpfs(target *check.Absolute, size int, perm os.FileMode) *Ops {
*f = append(*f, &MountTmpfsOp{SourceTmpfsEphemeral, target, MS_NOSUID | MS_NODEV, size, perm}) *f = append(*f, &MountTmpfsOp{SourceTmpfsEphemeral, target, MS_NOSUID | MS_NODEV, size, perm})
return f return f
} }
// Readonly appends an [Op] that mounts read-only tmpfs on container path [MountTmpfsOp.Path]. // Readonly appends an [Op] that mounts read-only tmpfs on container path [MountTmpfsOp.Path].
func (f *Ops) Readonly(target *Absolute, perm os.FileMode) *Ops { func (f *Ops) Readonly(target *check.Absolute, perm os.FileMode) *Ops {
*f = append(*f, &MountTmpfsOp{SourceTmpfsReadonly, target, MS_RDONLY | MS_NOSUID | MS_NODEV, 0, perm}) *f = append(*f, &MountTmpfsOp{SourceTmpfsReadonly, target, MS_RDONLY | MS_NOSUID | MS_NODEV, 0, perm})
return f return f
} }
@ -32,7 +34,7 @@ func (f *Ops) Readonly(target *Absolute, perm os.FileMode) *Ops {
// MountTmpfsOp mounts [FstypeTmpfs] on container Path. // MountTmpfsOp mounts [FstypeTmpfs] on container Path.
type MountTmpfsOp struct { type MountTmpfsOp struct {
FSName string FSName string
Path *Absolute Path *check.Absolute
Flags uintptr Flags uintptr
Size int Size int
Perm os.FileMode Perm os.FileMode

View File

@ -5,6 +5,7 @@ import (
"syscall" "syscall"
"testing" "testing"
"hakurei.app/container/check"
"hakurei.app/container/stub" "hakurei.app/container/stub"
) )
@ -24,7 +25,7 @@ func TestMountTmpfsOp(t *testing.T) {
{"success", new(Params), &MountTmpfsOp{ {"success", new(Params), &MountTmpfsOp{
FSName: "ephemeral", FSName: "ephemeral",
Path: MustAbs("/run/user/1000/"), Path: check.MustAbs("/run/user/1000/"),
Size: 1 << 10, Size: 1 << 10,
Perm: 0700, Perm: 0700,
}, nil, nil, []stub.Call{ }, nil, nil, []stub.Call{
@ -42,19 +43,19 @@ func TestMountTmpfsOp(t *testing.T) {
{"nil", (*MountTmpfsOp)(nil), false}, {"nil", (*MountTmpfsOp)(nil), false},
{"zero", new(MountTmpfsOp), false}, {"zero", new(MountTmpfsOp), false},
{"nil path", &MountTmpfsOp{FSName: "tmpfs"}, false}, {"nil path", &MountTmpfsOp{FSName: "tmpfs"}, false},
{"zero fsname", &MountTmpfsOp{Path: MustAbs("/tmp/")}, false}, {"zero fsname", &MountTmpfsOp{Path: check.MustAbs("/tmp/")}, false},
{"valid", &MountTmpfsOp{FSName: "tmpfs", Path: MustAbs("/tmp/")}, true}, {"valid", &MountTmpfsOp{FSName: "tmpfs", Path: check.MustAbs("/tmp/")}, true},
}) })
checkOpsBuilder(t, []opsBuilderTestCase{ checkOpsBuilder(t, []opsBuilderTestCase{
{"runtime", new(Ops).Tmpfs( {"runtime", new(Ops).Tmpfs(
MustAbs("/run/user"), check.MustAbs("/run/user"),
1<<10, 1<<10,
0755, 0755,
), Ops{ ), Ops{
&MountTmpfsOp{ &MountTmpfsOp{
FSName: "ephemeral", FSName: "ephemeral",
Path: MustAbs("/run/user"), Path: check.MustAbs("/run/user"),
Flags: syscall.MS_NOSUID | syscall.MS_NODEV, Flags: syscall.MS_NOSUID | syscall.MS_NODEV,
Size: 1 << 10, Size: 1 << 10,
Perm: 0755, Perm: 0755,
@ -62,12 +63,12 @@ func TestMountTmpfsOp(t *testing.T) {
}}, }},
{"nscd", new(Ops).Readonly( {"nscd", new(Ops).Readonly(
MustAbs("/var/run/nscd"), check.MustAbs("/var/run/nscd"),
0755, 0755,
), Ops{ ), Ops{
&MountTmpfsOp{ &MountTmpfsOp{
FSName: "readonly", FSName: "readonly",
Path: MustAbs("/var/run/nscd"), Path: check.MustAbs("/var/run/nscd"),
Flags: syscall.MS_NOSUID | syscall.MS_NODEV | syscall.MS_RDONLY, Flags: syscall.MS_NOSUID | syscall.MS_NODEV | syscall.MS_RDONLY,
Perm: 0755, Perm: 0755,
}, },
@ -79,13 +80,13 @@ func TestMountTmpfsOp(t *testing.T) {
{"fsname differs", &MountTmpfsOp{ {"fsname differs", &MountTmpfsOp{
FSName: "readonly", FSName: "readonly",
Path: MustAbs("/run/user"), Path: check.MustAbs("/run/user"),
Flags: syscall.MS_NOSUID | syscall.MS_NODEV, Flags: syscall.MS_NOSUID | syscall.MS_NODEV,
Size: 1 << 10, Size: 1 << 10,
Perm: 0755, Perm: 0755,
}, &MountTmpfsOp{ }, &MountTmpfsOp{
FSName: "ephemeral", FSName: "ephemeral",
Path: MustAbs("/run/user"), Path: check.MustAbs("/run/user"),
Flags: syscall.MS_NOSUID | syscall.MS_NODEV, Flags: syscall.MS_NOSUID | syscall.MS_NODEV,
Size: 1 << 10, Size: 1 << 10,
Perm: 0755, Perm: 0755,
@ -93,13 +94,13 @@ func TestMountTmpfsOp(t *testing.T) {
{"path differs", &MountTmpfsOp{ {"path differs", &MountTmpfsOp{
FSName: "ephemeral", FSName: "ephemeral",
Path: MustAbs("/run/user/differs"), Path: check.MustAbs("/run/user/differs"),
Flags: syscall.MS_NOSUID | syscall.MS_NODEV, Flags: syscall.MS_NOSUID | syscall.MS_NODEV,
Size: 1 << 10, Size: 1 << 10,
Perm: 0755, Perm: 0755,
}, &MountTmpfsOp{ }, &MountTmpfsOp{
FSName: "ephemeral", FSName: "ephemeral",
Path: MustAbs("/run/user"), Path: check.MustAbs("/run/user"),
Flags: syscall.MS_NOSUID | syscall.MS_NODEV, Flags: syscall.MS_NOSUID | syscall.MS_NODEV,
Size: 1 << 10, Size: 1 << 10,
Perm: 0755, Perm: 0755,
@ -107,13 +108,13 @@ func TestMountTmpfsOp(t *testing.T) {
{"flags differs", &MountTmpfsOp{ {"flags differs", &MountTmpfsOp{
FSName: "ephemeral", FSName: "ephemeral",
Path: MustAbs("/run/user"), Path: check.MustAbs("/run/user"),
Flags: syscall.MS_NOSUID | syscall.MS_NODEV | syscall.MS_RDONLY, Flags: syscall.MS_NOSUID | syscall.MS_NODEV | syscall.MS_RDONLY,
Size: 1 << 10, Size: 1 << 10,
Perm: 0755, Perm: 0755,
}, &MountTmpfsOp{ }, &MountTmpfsOp{
FSName: "ephemeral", FSName: "ephemeral",
Path: MustAbs("/run/user"), Path: check.MustAbs("/run/user"),
Flags: syscall.MS_NOSUID | syscall.MS_NODEV, Flags: syscall.MS_NOSUID | syscall.MS_NODEV,
Size: 1 << 10, Size: 1 << 10,
Perm: 0755, Perm: 0755,
@ -121,13 +122,13 @@ func TestMountTmpfsOp(t *testing.T) {
{"size differs", &MountTmpfsOp{ {"size differs", &MountTmpfsOp{
FSName: "ephemeral", FSName: "ephemeral",
Path: MustAbs("/run/user"), Path: check.MustAbs("/run/user"),
Flags: syscall.MS_NOSUID | syscall.MS_NODEV, Flags: syscall.MS_NOSUID | syscall.MS_NODEV,
Size: 1, Size: 1,
Perm: 0755, Perm: 0755,
}, &MountTmpfsOp{ }, &MountTmpfsOp{
FSName: "ephemeral", FSName: "ephemeral",
Path: MustAbs("/run/user"), Path: check.MustAbs("/run/user"),
Flags: syscall.MS_NOSUID | syscall.MS_NODEV, Flags: syscall.MS_NOSUID | syscall.MS_NODEV,
Size: 1 << 10, Size: 1 << 10,
Perm: 0755, Perm: 0755,
@ -135,13 +136,13 @@ func TestMountTmpfsOp(t *testing.T) {
{"perm differs", &MountTmpfsOp{ {"perm differs", &MountTmpfsOp{
FSName: "ephemeral", FSName: "ephemeral",
Path: MustAbs("/run/user"), Path: check.MustAbs("/run/user"),
Flags: syscall.MS_NOSUID | syscall.MS_NODEV, Flags: syscall.MS_NOSUID | syscall.MS_NODEV,
Size: 1 << 10, Size: 1 << 10,
Perm: 0700, Perm: 0700,
}, &MountTmpfsOp{ }, &MountTmpfsOp{
FSName: "ephemeral", FSName: "ephemeral",
Path: MustAbs("/run/user"), Path: check.MustAbs("/run/user"),
Flags: syscall.MS_NOSUID | syscall.MS_NODEV, Flags: syscall.MS_NOSUID | syscall.MS_NODEV,
Size: 1 << 10, Size: 1 << 10,
Perm: 0755, Perm: 0755,
@ -149,13 +150,13 @@ func TestMountTmpfsOp(t *testing.T) {
{"equals", &MountTmpfsOp{ {"equals", &MountTmpfsOp{
FSName: "ephemeral", FSName: "ephemeral",
Path: MustAbs("/run/user"), Path: check.MustAbs("/run/user"),
Flags: syscall.MS_NOSUID | syscall.MS_NODEV, Flags: syscall.MS_NOSUID | syscall.MS_NODEV,
Size: 1 << 10, Size: 1 << 10,
Perm: 0755, Perm: 0755,
}, &MountTmpfsOp{ }, &MountTmpfsOp{
FSName: "ephemeral", FSName: "ephemeral",
Path: MustAbs("/run/user"), Path: check.MustAbs("/run/user"),
Flags: syscall.MS_NOSUID | syscall.MS_NODEV, Flags: syscall.MS_NOSUID | syscall.MS_NODEV,
Size: 1 << 10, Size: 1 << 10,
Perm: 0755, Perm: 0755,
@ -165,7 +166,7 @@ func TestMountTmpfsOp(t *testing.T) {
checkOpMeta(t, []opMetaTestCase{ checkOpMeta(t, []opMetaTestCase{
{"runtime", &MountTmpfsOp{ {"runtime", &MountTmpfsOp{
FSName: "ephemeral", FSName: "ephemeral",
Path: MustAbs("/run/user"), Path: check.MustAbs("/run/user"),
Flags: syscall.MS_NOSUID | syscall.MS_NODEV, Flags: syscall.MS_NOSUID | syscall.MS_NODEV,
Size: 1 << 10, Size: 1 << 10,
Perm: 0755, Perm: 0755,

View File

@ -8,12 +8,17 @@ import (
"strconv" "strconv"
"strings" "strings"
"syscall" "syscall"
_ "unsafe"
"hakurei.app/container/check"
"hakurei.app/container/vfs" "hakurei.app/container/vfs"
) )
/* constants in this file bypass abs check, be extremely careful when changing them! */ /* constants in this file bypass abs check, be extremely careful when changing them! */
//go:linkname unsafeAbs hakurei.app/container/check.unsafeAbs
func unsafeAbs(_ string) *check.Absolute
const ( const (
// FHSRoot points to the file system root. // FHSRoot points to the file system root.
FHSRoot = "/" FHSRoot = "/"
@ -52,31 +57,31 @@ const (
var ( var (
// AbsFHSRoot is [FHSRoot] as [Absolute]. // AbsFHSRoot is [FHSRoot] as [Absolute].
AbsFHSRoot = &Absolute{FHSRoot} AbsFHSRoot = unsafeAbs(FHSRoot)
// AbsFHSEtc is [FHSEtc] as [Absolute]. // AbsFHSEtc is [FHSEtc] as [Absolute].
AbsFHSEtc = &Absolute{FHSEtc} AbsFHSEtc = unsafeAbs(FHSEtc)
// AbsFHSTmp is [FHSTmp] as [Absolute]. // AbsFHSTmp is [FHSTmp] as [Absolute].
AbsFHSTmp = &Absolute{FHSTmp} AbsFHSTmp = unsafeAbs(FHSTmp)
// AbsFHSRun is [FHSRun] as [Absolute]. // AbsFHSRun is [FHSRun] as [Absolute].
AbsFHSRun = &Absolute{FHSRun} AbsFHSRun = unsafeAbs(FHSRun)
// AbsFHSRunUser is [FHSRunUser] as [Absolute]. // AbsFHSRunUser is [FHSRunUser] as [Absolute].
AbsFHSRunUser = &Absolute{FHSRunUser} AbsFHSRunUser = unsafeAbs(FHSRunUser)
// AbsFHSUsrBin is [FHSUsrBin] as [Absolute]. // AbsFHSUsrBin is [FHSUsrBin] as [Absolute].
AbsFHSUsrBin = &Absolute{FHSUsrBin} AbsFHSUsrBin = unsafeAbs(FHSUsrBin)
// AbsFHSVar is [FHSVar] as [Absolute]. // AbsFHSVar is [FHSVar] as [Absolute].
AbsFHSVar = &Absolute{FHSVar} AbsFHSVar = unsafeAbs(FHSVar)
// AbsFHSVarLib is [FHSVarLib] as [Absolute]. // AbsFHSVarLib is [FHSVarLib] as [Absolute].
AbsFHSVarLib = &Absolute{FHSVarLib} AbsFHSVarLib = unsafeAbs(FHSVarLib)
// AbsFHSDev is [FHSDev] as [Absolute]. // AbsFHSDev is [FHSDev] as [Absolute].
AbsFHSDev = &Absolute{FHSDev} AbsFHSDev = unsafeAbs(FHSDev)
// AbsFHSProc is [FHSProc] as [Absolute]. // AbsFHSProc is [FHSProc] as [Absolute].
AbsFHSProc = &Absolute{FHSProc} AbsFHSProc = unsafeAbs(FHSProc)
// AbsFHSSys is [FHSSys] as [Absolute]. // AbsFHSSys is [FHSSys] as [Absolute].
AbsFHSSys = &Absolute{FHSSys} AbsFHSSys = unsafeAbs(FHSSys)
) )
const ( const (

View File

@ -10,6 +10,7 @@ import (
"sync" "sync"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/check"
"hakurei.app/helper/proc" "hakurei.app/helper/proc"
) )
@ -17,7 +18,7 @@ import (
func New( func New(
ctx context.Context, ctx context.Context,
msg container.Msg, msg container.Msg,
pathname *container.Absolute, name string, pathname *check.Absolute, name string,
wt io.WriterTo, wt io.WriterTo,
stat bool, stat bool,
argF func(argsFd, statFd int) []string, argF func(argsFd, statFd int) []string,

View File

@ -7,12 +7,13 @@ import (
"testing" "testing"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/check"
"hakurei.app/helper" "hakurei.app/helper"
) )
func TestContainer(t *testing.T) { func TestContainer(t *testing.T) {
t.Run("start invalid container", func(t *testing.T) { t.Run("start invalid container", func(t *testing.T) {
h := helper.New(t.Context(), nil, container.MustAbs(container.Nonexistent), "hakurei", argsWt, false, argF, nil, nil) h := helper.New(t.Context(), nil, check.MustAbs(container.Nonexistent), "hakurei", argsWt, false, argF, nil, nil)
wantErr := "container: starting an invalid container" wantErr := "container: starting an invalid container"
if err := h.Start(); err == nil || err.Error() != wantErr { if err := h.Start(); err == nil || err.Error() != wantErr {
@ -22,7 +23,7 @@ func TestContainer(t *testing.T) {
}) })
t.Run("valid new helper nil check", func(t *testing.T) { t.Run("valid new helper nil check", func(t *testing.T) {
if got := helper.New(t.Context(), nil, container.MustAbs(container.Nonexistent), "hakurei", argsWt, false, argF, nil, nil); got == nil { if got := helper.New(t.Context(), nil, check.MustAbs(container.Nonexistent), "hakurei", argsWt, false, argF, nil, nil); got == nil {
t.Errorf("New(%q, %q) got nil", t.Errorf("New(%q, %q) got nil",
argsWt, "hakurei") argsWt, "hakurei")
return return
@ -31,7 +32,7 @@ func TestContainer(t *testing.T) {
t.Run("implementation compliance", func(t *testing.T) { t.Run("implementation compliance", func(t *testing.T) {
testHelper(t, func(ctx context.Context, setOutput func(stdoutP, stderrP *io.Writer), stat bool) helper.Helper { testHelper(t, func(ctx context.Context, setOutput func(stdoutP, stderrP *io.Writer), stat bool) helper.Helper {
return helper.New(ctx, nil, container.MustAbs(os.Args[0]), "helper", argsWt, stat, argF, func(z *container.Container) { return helper.New(ctx, nil, check.MustAbs(os.Args[0]), "helper", argsWt, stat, argF, func(z *container.Container) {
setOutput(&z.Stdout, &z.Stderr) setOutput(&z.Stdout, &z.Stderr)
z. z.
Bind(container.AbsFHSRoot, container.AbsFHSRoot, 0). Bind(container.AbsFHSRoot, container.AbsFHSRoot, 0).

View File

@ -5,12 +5,12 @@ import (
"strconv" "strconv"
"time" "time"
"hakurei.app/container" "hakurei.app/container/check"
) )
const Tmp = "/.hakurei" const Tmp = "/.hakurei"
var AbsTmp = container.MustAbs(Tmp) var AbsTmp = check.MustAbs(Tmp)
const ( const (
// WaitDelayDefault is used when WaitDelay has its zero value. // WaitDelayDefault is used when WaitDelay has its zero value.
@ -107,12 +107,12 @@ type (
// Defaults to passwd name of target uid or chronos. // Defaults to passwd name of target uid or chronos.
Username string `json:"username,omitempty"` Username string `json:"username,omitempty"`
// Pathname of shell in the container filesystem to use for the emulated user. // Pathname of shell in the container filesystem to use for the emulated user.
Shell *container.Absolute `json:"shell"` Shell *check.Absolute `json:"shell"`
// Directory in the container filesystem to enter and use as the home directory of the emulated user. // Directory in the container filesystem to enter and use as the home directory of the emulated user.
Home *container.Absolute `json:"home"` Home *check.Absolute `json:"home"`
// Pathname to executable file in the container filesystem. // Pathname to executable file in the container filesystem.
Path *container.Absolute `json:"path,omitempty"` Path *check.Absolute `json:"path,omitempty"`
// Final args passed to the initial program. // Final args passed to the initial program.
Args []string `json:"args"` Args []string `json:"args"`
} }
@ -161,11 +161,11 @@ func (config *Config) Validate() error {
// ExtraPermConfig describes an acl update op. // ExtraPermConfig describes an acl update op.
type ExtraPermConfig struct { type ExtraPermConfig struct {
Ensure bool `json:"ensure,omitempty"` Ensure bool `json:"ensure,omitempty"`
Path *container.Absolute `json:"path"` Path *check.Absolute `json:"path"`
Read bool `json:"r,omitempty"` Read bool `json:"r,omitempty"`
Write bool `json:"w,omitempty"` Write bool `json:"w,omitempty"`
Execute bool `json:"x,omitempty"` Execute bool `json:"x,omitempty"`
} }
func (e *ExtraPermConfig) String() string { func (e *ExtraPermConfig) String() string {

View File

@ -7,6 +7,7 @@ import (
"reflect" "reflect"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/check"
) )
// FilesystemConfig is an abstract representation of a mount point. // FilesystemConfig is an abstract representation of a mount point.
@ -14,9 +15,9 @@ type FilesystemConfig interface {
// Valid returns whether the configuration is valid. // Valid returns whether the configuration is valid.
Valid() bool Valid() bool
// Path returns the target path in the container. // Path returns the target path in the container.
Path() *container.Absolute Path() *check.Absolute
// Host returns a slice of all host paths used by this operation. // Host returns a slice of all host paths used by this operation.
Host() []*container.Absolute Host() []*check.Absolute
// Apply appends the [container.Op] implementing this operation. // Apply appends the [container.Op] implementing this operation.
Apply(z *ApplyState) Apply(z *ApplyState)

View File

@ -9,6 +9,7 @@ import (
"testing" "testing"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/check"
"hakurei.app/hst" "hakurei.app/hst"
) )
@ -216,11 +217,11 @@ type stubFS struct {
typeName string typeName string
} }
func (s stubFS) Valid() bool { return false } func (s stubFS) Valid() bool { return false }
func (s stubFS) Path() *container.Absolute { panic("unreachable") } func (s stubFS) Path() *check.Absolute { panic("unreachable") }
func (s stubFS) Host() []*container.Absolute { panic("unreachable") } func (s stubFS) Host() []*check.Absolute { panic("unreachable") }
func (s stubFS) Apply(*hst.ApplyState) { panic("unreachable") } func (s stubFS) Apply(*hst.ApplyState) { panic("unreachable") }
func (s stubFS) String() string { return "<invalid " + s.typeName + ">" } func (s stubFS) String() string { return "<invalid " + s.typeName + ">" }
type sCheck struct { type sCheck struct {
FS hst.FilesystemConfigJSON `json:"fs"` FS hst.FilesystemConfigJSON `json:"fs"`
@ -232,8 +233,8 @@ type fsTestCase struct {
fs hst.FilesystemConfig fs hst.FilesystemConfig
valid bool valid bool
ops container.Ops ops container.Ops
path *container.Absolute path *check.Absolute
host []*container.Absolute host []*check.Absolute
str string str string
} }
@ -287,11 +288,11 @@ func checkFs(t *testing.T, testCases []fsTestCase) {
} }
} }
func m(pathname string) *container.Absolute { return container.MustAbs(pathname) } func m(pathname string) *check.Absolute { return check.MustAbs(pathname) }
func ms(pathnames ...string) []*container.Absolute { func ms(pathnames ...string) []*check.Absolute {
as := make([]*container.Absolute, len(pathnames)) as := make([]*check.Absolute, len(pathnames))
for i, pathname := range pathnames { for i, pathname := range pathnames {
as[i] = container.MustAbs(pathname) as[i] = check.MustAbs(pathname)
} }
return as return as
} }

View File

@ -5,6 +5,7 @@ import (
"strings" "strings"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/check"
) )
func init() { gob.Register(new(FSBind)) } func init() { gob.Register(new(FSBind)) }
@ -15,9 +16,9 @@ const FilesystemBind = "bind"
// FSBind represents a host to container bind mount. // FSBind represents a host to container bind mount.
type FSBind struct { type FSBind struct {
// mount point in container, same as Source if empty // mount point in container, same as Source if empty
Target *container.Absolute `json:"dst,omitempty"` Target *check.Absolute `json:"dst,omitempty"`
// host filesystem path to make available to the container // host filesystem path to make available to the container
Source *container.Absolute `json:"src"` Source *check.Absolute `json:"src"`
// do not mount Target read-only // do not mount Target read-only
Write bool `json:"write,omitempty"` Write bool `json:"write,omitempty"`
// do not disable device files on Target, implies Write // do not disable device files on Target, implies Write
@ -66,7 +67,7 @@ func (b *FSBind) Valid() bool {
return true return true
} }
func (b *FSBind) Path() *container.Absolute { func (b *FSBind) Path() *check.Absolute {
if !b.Valid() { if !b.Valid() {
return nil return nil
} }
@ -76,11 +77,11 @@ func (b *FSBind) Path() *container.Absolute {
return b.Target return b.Target
} }
func (b *FSBind) Host() []*container.Absolute { func (b *FSBind) Host() []*check.Absolute {
if !b.Valid() { if !b.Valid() {
return nil return nil
} }
return []*container.Absolute{b.Source} return []*check.Absolute{b.Source}
} }
func (b *FSBind) Apply(z *ApplyState) { func (b *FSBind) Apply(z *ApplyState) {

View File

@ -5,7 +5,7 @@ import (
"os" "os"
"strings" "strings"
"hakurei.app/container" "hakurei.app/container/check"
) )
func init() { gob.Register(new(FSEphemeral)) } func init() { gob.Register(new(FSEphemeral)) }
@ -16,7 +16,7 @@ const FilesystemEphemeral = "ephemeral"
// FSEphemeral represents an ephemeral container mount point. // FSEphemeral represents an ephemeral container mount point.
type FSEphemeral struct { type FSEphemeral struct {
// mount point in container // mount point in container
Target *container.Absolute `json:"dst,omitempty"` Target *check.Absolute `json:"dst,omitempty"`
// do not mount filesystem read-only // do not mount filesystem read-only
Write bool `json:"write,omitempty"` Write bool `json:"write,omitempty"`
// upper limit on the size of the filesystem // upper limit on the size of the filesystem
@ -27,14 +27,14 @@ type FSEphemeral struct {
func (e *FSEphemeral) Valid() bool { return e != nil && e.Target != nil } func (e *FSEphemeral) Valid() bool { return e != nil && e.Target != nil }
func (e *FSEphemeral) Path() *container.Absolute { func (e *FSEphemeral) Path() *check.Absolute {
if !e.Valid() { if !e.Valid() {
return nil return nil
} }
return e.Target return e.Target
} }
func (e *FSEphemeral) Host() []*container.Absolute { return nil } func (e *FSEphemeral) Host() []*check.Absolute { return nil }
const fsEphemeralDefaultPerm = os.FileMode(0755) const fsEphemeralDefaultPerm = os.FileMode(0755)

View File

@ -4,7 +4,7 @@ import (
"encoding/gob" "encoding/gob"
"path" "path"
"hakurei.app/container" "hakurei.app/container/check"
) )
func init() { gob.Register(new(FSLink)) } func init() { gob.Register(new(FSLink)) }
@ -15,7 +15,7 @@ const FilesystemLink = "link"
// FSLink represents a symlink in the container filesystem. // FSLink represents a symlink in the container filesystem.
type FSLink struct { type FSLink struct {
// link path in container // link path in container
Target *container.Absolute `json:"dst"` Target *check.Absolute `json:"dst"`
// linkname the symlink points to // linkname the symlink points to
Linkname string `json:"linkname"` Linkname string `json:"linkname"`
// whether to dereference linkname before creating the link // whether to dereference linkname before creating the link
@ -29,14 +29,14 @@ func (l *FSLink) Valid() bool {
return !l.Dereference || path.IsAbs(l.Linkname) return !l.Dereference || path.IsAbs(l.Linkname)
} }
func (l *FSLink) Path() *container.Absolute { func (l *FSLink) Path() *check.Absolute {
if !l.Valid() { if !l.Valid() {
return nil return nil
} }
return l.Target return l.Target
} }
func (l *FSLink) Host() []*container.Absolute { return nil } func (l *FSLink) Host() []*check.Absolute { return nil }
func (l *FSLink) Apply(z *ApplyState) { func (l *FSLink) Apply(z *ApplyState) {
if !l.Valid() { if !l.Valid() {

View File

@ -5,6 +5,7 @@ import (
"strings" "strings"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/check"
) )
func init() { gob.Register(new(FSOverlay)) } func init() { gob.Register(new(FSOverlay)) }
@ -15,14 +16,14 @@ const FilesystemOverlay = "overlay"
// FSOverlay represents an overlay mount point. // FSOverlay represents an overlay mount point.
type FSOverlay struct { type FSOverlay struct {
// mount point in container // mount point in container
Target *container.Absolute `json:"dst"` Target *check.Absolute `json:"dst"`
// any filesystem, does not need to be on a writable filesystem, must not be nil // any filesystem, does not need to be on a writable filesystem, must not be nil
Lower []*container.Absolute `json:"lower"` Lower []*check.Absolute `json:"lower"`
// the upperdir is normally on a writable filesystem, leave as nil to mount Lower readonly // the upperdir is normally on a writable filesystem, leave as nil to mount Lower readonly
Upper *container.Absolute `json:"upper,omitempty"` Upper *check.Absolute `json:"upper,omitempty"`
// the workdir needs to be an empty directory on the same filesystem as Upper, must not be nil if Upper is populated // the workdir needs to be an empty directory on the same filesystem as Upper, must not be nil if Upper is populated
Work *container.Absolute `json:"work,omitempty"` Work *check.Absolute `json:"work,omitempty"`
} }
func (o *FSOverlay) Valid() bool { func (o *FSOverlay) Valid() bool {
@ -43,18 +44,18 @@ func (o *FSOverlay) Valid() bool {
} }
} }
func (o *FSOverlay) Path() *container.Absolute { func (o *FSOverlay) Path() *check.Absolute {
if !o.Valid() { if !o.Valid() {
return nil return nil
} }
return o.Target return o.Target
} }
func (o *FSOverlay) Host() []*container.Absolute { func (o *FSOverlay) Host() []*check.Absolute {
if !o.Valid() { if !o.Valid() {
return nil return nil
} }
p := make([]*container.Absolute, 0, 2+len(o.Lower)) p := make([]*check.Absolute, 0, 2+len(o.Lower))
if o.Upper != nil && o.Work != nil { if o.Upper != nil && o.Work != nil {
p = append(p, o.Upper, o.Work) p = append(p, o.Upper, o.Work)
} }

View File

@ -4,13 +4,14 @@ import (
"testing" "testing"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/check"
"hakurei.app/hst" "hakurei.app/hst"
) )
func TestFSOverlay(t *testing.T) { func TestFSOverlay(t *testing.T) {
checkFs(t, []fsTestCase{ checkFs(t, []fsTestCase{
{"nil", (*hst.FSOverlay)(nil), false, nil, nil, nil, "<invalid>"}, {"nil", (*hst.FSOverlay)(nil), false, nil, nil, nil, "<invalid>"},
{"nil lower", &hst.FSOverlay{Target: m("/etc"), Lower: []*container.Absolute{nil}}, false, nil, nil, nil, "<invalid>"}, {"nil lower", &hst.FSOverlay{Target: m("/etc"), Lower: []*check.Absolute{nil}}, false, nil, nil, nil, "<invalid>"},
{"zero lower", &hst.FSOverlay{Target: m("/etc"), Upper: m("/"), Work: m("/")}, false, nil, nil, nil, "<invalid>"}, {"zero lower", &hst.FSOverlay{Target: m("/etc"), Upper: m("/"), Work: m("/")}, false, nil, nil, nil, "<invalid>"},
{"zero lower ro", &hst.FSOverlay{Target: m("/etc")}, false, nil, nil, nil, "<invalid>"}, {"zero lower ro", &hst.FSOverlay{Target: m("/etc")}, false, nil, nil, nil, "<invalid>"},
{"short lower", &hst.FSOverlay{Target: m("/etc"), Lower: ms("/etc")}, false, nil, nil, nil, "<invalid>"}, {"short lower", &hst.FSOverlay{Target: m("/etc"), Lower: ms("/etc")}, false, nil, nil, nil, "<invalid>"},

View File

@ -7,6 +7,7 @@ import (
"os" "os"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/check"
) )
// An AppError is returned while starting an app according to [hst.Config]. // An AppError is returned while starting an app according to [hst.Config].
@ -38,13 +39,13 @@ func (e *AppError) Message() string {
// Paths contains environment-dependent paths used by hakurei. // Paths contains environment-dependent paths used by hakurei.
type Paths struct { type Paths struct {
// temporary directory returned by [os.TempDir] (usually `/tmp`) // temporary directory returned by [os.TempDir] (usually `/tmp`)
TempDir *container.Absolute `json:"temp_dir"` TempDir *check.Absolute `json:"temp_dir"`
// path to shared directory (usually `/tmp/hakurei.%d`, [Info.User]) // path to shared directory (usually `/tmp/hakurei.%d`, [Info.User])
SharePath *container.Absolute `json:"share_path"` SharePath *check.Absolute `json:"share_path"`
// XDG_RUNTIME_DIR value (usually `/run/user/%d`, uid) // XDG_RUNTIME_DIR value (usually `/run/user/%d`, uid)
RuntimePath *container.Absolute `json:"runtime_path"` RuntimePath *check.Absolute `json:"runtime_path"`
// application runtime directory (usually `/run/user/%d/hakurei`) // application runtime directory (usually `/run/user/%d/hakurei`)
RunDirPath *container.Absolute `json:"run_dir_path"` RunDirPath *check.Absolute `json:"run_dir_path"`
} }
type Info struct { type Info struct {
@ -115,22 +116,22 @@ func Template() *Config {
{&FSBind{Target: container.AbsFHSEtc, Source: container.AbsFHSEtc, Special: true}}, {&FSBind{Target: container.AbsFHSEtc, Source: container.AbsFHSEtc, Special: true}},
{&FSEphemeral{Target: container.AbsFHSTmp, Write: true, Perm: 0755}}, {&FSEphemeral{Target: container.AbsFHSTmp, Write: true, Perm: 0755}},
{&FSOverlay{ {&FSOverlay{
Target: container.MustAbs("/nix/store"), Target: check.MustAbs("/nix/store"),
Lower: []*container.Absolute{container.MustAbs("/mnt-root/nix/.ro-store")}, Lower: []*check.Absolute{check.MustAbs("/mnt-root/nix/.ro-store")},
Upper: container.MustAbs("/mnt-root/nix/.rw-store/upper"), Upper: check.MustAbs("/mnt-root/nix/.rw-store/upper"),
Work: container.MustAbs("/mnt-root/nix/.rw-store/work"), Work: check.MustAbs("/mnt-root/nix/.rw-store/work"),
}}, }},
{&FSBind{Source: container.MustAbs("/nix/store")}}, {&FSBind{Source: check.MustAbs("/nix/store")}},
{&FSLink{Target: container.AbsFHSRun.Append("current-system"), Linkname: "/run/current-system", Dereference: true}}, {&FSLink{Target: container.AbsFHSRun.Append("current-system"), Linkname: "/run/current-system", Dereference: true}},
{&FSLink{Target: container.AbsFHSRun.Append("opengl-driver"), Linkname: "/run/opengl-driver", Dereference: true}}, {&FSLink{Target: container.AbsFHSRun.Append("opengl-driver"), Linkname: "/run/opengl-driver", Dereference: true}},
{&FSBind{Source: container.AbsFHSVarLib.Append("hakurei/u0/org.chromium.Chromium"), {&FSBind{Source: container.AbsFHSVarLib.Append("hakurei/u0/org.chromium.Chromium"),
Target: container.MustAbs("/data/data/org.chromium.Chromium"), Write: true, Ensure: true}}, Target: check.MustAbs("/data/data/org.chromium.Chromium"), Write: true, Ensure: true}},
{&FSBind{Source: container.AbsFHSDev.Append("dri"), Device: true, Optional: true}}, {&FSBind{Source: container.AbsFHSDev.Append("dri"), Device: true, Optional: true}},
}, },
Username: "chronos", Username: "chronos",
Shell: container.AbsFHSRun.Append("current-system/sw/bin/zsh"), Shell: container.AbsFHSRun.Append("current-system/sw/bin/zsh"),
Home: container.MustAbs("/data/data/org.chromium.Chromium"), Home: check.MustAbs("/data/data/org.chromium.Chromium"),
Path: container.AbsFHSRun.Append("current-system/sw/bin/chromium"), Path: container.AbsFHSRun.Append("current-system/sw/bin/chromium"),
Args: []string{ Args: []string{

View File

@ -16,6 +16,7 @@ import (
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/bits" "hakurei.app/container/bits"
"hakurei.app/container/check"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/internal/app/state" "hakurei.app/internal/app/state"
"hakurei.app/system" "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) overflowUid(container.Msg) int { return 65534 }
func (k *stubNixOS) overflowGid(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...)) } 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) verbose(v ...any) { log.Print(v...) }
func (k *stubNixOS) verbosef(format string, v ...any) { log.Printf(format, v...) } func (k *stubNixOS) verbosef(format string, v ...any) { log.Printf(format, v...) }
func m(pathname string) *container.Absolute { func m(pathname string) *check.Absolute {
return container.MustAbs(pathname) return check.MustAbs(pathname)
} }
func f(c hst.FilesystemConfig) hst.FilesystemConfigJSON { func f(c hst.FilesystemConfig) hst.FilesystemConfigJSON {

View File

@ -10,6 +10,7 @@ import (
"path/filepath" "path/filepath"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/check"
"hakurei.app/internal" "hakurei.app/internal"
) )
@ -57,7 +58,7 @@ type syscallDispatcher interface {
overflowGid(msg container.Msg) int overflowGid(msg container.Msg) int
// mustHsuPath provides [internal.MustHsuPath]. // mustHsuPath provides [internal.MustHsuPath].
mustHsuPath() *container.Absolute mustHsuPath() *check.Absolute
// fatalf provides [log.Fatalf]. // fatalf provides [log.Fatalf].
fatalf(format string, v ...any) 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) overflowUid(msg container.Msg) int { return container.OverflowUid(msg) }
func (direct) overflowGid(msg container.Msg) int { return container.OverflowGid(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...) } func (direct) fatalf(format string, v ...any) { log.Fatalf(format, v...) }

View File

@ -5,6 +5,7 @@ import (
"os/exec" "os/exec"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/check"
) )
type panicDispatcher struct{} 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) cmdOutput(*exec.Cmd) ([]byte, error) { panic("unreachable") }
func (panicDispatcher) overflowUid(container.Msg) int { panic("unreachable") } func (panicDispatcher) overflowUid(container.Msg) int { panic("unreachable") }
func (panicDispatcher) overflowGid(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") } func (panicDispatcher) fatalf(string, ...any) { panic("unreachable") }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -8,6 +8,7 @@ import (
"strings" "strings"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/check"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/system/acl" "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 // the socket file at `/tmp/.X11-unix/X%d` is typically owned by the priv user
// and not accessible by the target 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 len(s.Display) > 1 && s.Display[0] == ':' { // `:%d`
if n, err := strconv.Atoi(s.Display[1:]); err == nil && n >= 0 { if n, err := strconv.Atoi(s.Display[1:]); err == nil && n >= 0 {
socketPath = absX11SocketDir.Append("X" + strconv.Itoa(n)) socketPath = absX11SocketDir.Append("X" + strconv.Itoa(n))
} }
} else if len(s.Display) > 5 && strings.HasPrefix(s.Display, "unix:") { // `unix:%s` } 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 socketPath = a
} }
} }

View File

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

View File

@ -3,7 +3,7 @@ package internal
import ( import (
"log" "log"
"hakurei.app/container" "hakurei.app/container/check"
) )
var ( var (
@ -12,15 +12,15 @@ var (
) )
// MustHakureiPath returns the absolute path to hakurei, configured at compile time. // 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. // 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. // 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 pathname != compPoison && pathname != "" {
if a, err := container.NewAbs(pathname); err != nil { if a, err := check.NewAbs(pathname); err != nil {
fatal(err.Error()) fatal(err.Error())
return nil // unreachable return nil // unreachable
} else { } else {

View File

@ -4,7 +4,7 @@ import (
"reflect" "reflect"
"testing" "testing"
"hakurei.app/container" "hakurei.app/container/check"
) )
func TestMustCheckPath(t *testing.T) { 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) t.Errorf("mustCheckPath: %q", got)
} }
}) })

View File

@ -10,6 +10,7 @@ import (
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/bits" "hakurei.app/container/bits"
"hakurei.app/container/check"
"hakurei.app/container/seccomp" "hakurei.app/container/seccomp"
) )
@ -27,10 +28,10 @@ func Exec(ctx context.Context, msg container.Msg, p string) ([]*Entry, error) {
c, cancel := context.WithTimeout(ctx, lddTimeout) c, cancel := context.WithTimeout(ctx, lddTimeout)
defer cancel() defer cancel()
var toolPath *container.Absolute var toolPath *check.Absolute
if s, err := exec.LookPath(lddName); err != nil { if s, err := exec.LookPath(lddName); err != nil {
return nil, err return nil, err
} else if toolPath, err = container.NewAbs(s); err != nil { } else if toolPath, err = check.NewAbs(s); err != nil {
return nil, err return nil, err
} }

View File

@ -1,20 +1,20 @@
package ldd package ldd
import ( import (
"hakurei.app/container" "hakurei.app/container/check"
) )
// Path returns a deterministic, deduplicated slice of absolute directory paths in entries. // Path returns a deterministic, deduplicated slice of absolute directory paths in entries.
func Path(entries []*Entry) []*container.Absolute { func Path(entries []*Entry) []*check.Absolute {
p := make([]*container.Absolute, 0, len(entries)*2) p := make([]*check.Absolute, 0, len(entries)*2)
for _, entry := range entries { for _, entry := range entries {
if a, err := container.NewAbs(entry.Path); err == nil { if a, err := check.NewAbs(entry.Path); err == nil {
p = append(p, a.Dir()) p = append(p, a.Dir())
} }
if a, err := container.NewAbs(entry.Name); err == nil { if a, err := check.NewAbs(entry.Name); err == nil {
p = append(p, a.Dir()) p = append(p, a.Dir())
} }
} }
container.SortAbs(p) check.SortAbs(p)
return container.CompactAbs(p) return check.CompactAbs(p)
} }

View File

@ -6,19 +6,19 @@ import (
"os" "os"
"slices" "slices"
"hakurei.app/container" "hakurei.app/container/check"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/system/acl" "hakurei.app/system/acl"
) )
// UpdatePerm calls UpdatePermType with the [Process] criteria. // UpdatePerm calls UpdatePermType with the [Process] criteria.
func (sys *I) UpdatePerm(path *container.Absolute, perms ...acl.Perm) *I { func (sys *I) UpdatePerm(path *check.Absolute, perms ...acl.Perm) *I {
sys.UpdatePermType(Process, path, perms...) sys.UpdatePermType(Process, path, perms...)
return sys return sys
} }
// UpdatePermType maintains [acl.Perms] on a file until its [Enablement] is no longer satisfied. // UpdatePermType maintains [acl.Perms] on a file until its [Enablement] is no longer satisfied.
func (sys *I) UpdatePermType(et hst.Enablement, path *container.Absolute, perms ...acl.Perm) *I { func (sys *I) UpdatePermType(et hst.Enablement, path *check.Absolute, perms ...acl.Perm) *I {
sys.ops = append(sys.ops, &aclUpdateOp{et, path.String(), perms}) sys.ops = append(sys.ops, &aclUpdateOp{et, path.String(), perms})
return sys return sys
} }

View File

@ -12,6 +12,7 @@ import (
"syscall" "syscall"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/check"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/system/dbus" "hakurei.app/system/dbus"
) )
@ -21,7 +22,7 @@ var (
) )
// MustProxyDBus calls ProxyDBus and panics if an error is returned. // MustProxyDBus calls ProxyDBus and panics if an error is returned.
func (sys *I) MustProxyDBus(sessionPath *container.Absolute, session *hst.BusConfig, systemPath *container.Absolute, system *hst.BusConfig) *I { func (sys *I) MustProxyDBus(sessionPath *check.Absolute, session *hst.BusConfig, systemPath *check.Absolute, system *hst.BusConfig) *I {
if err := sys.ProxyDBus(session, system, sessionPath, systemPath); err != nil { if err := sys.ProxyDBus(session, system, sessionPath, systemPath); err != nil {
panic(err.Error()) panic(err.Error())
} else { } else {
@ -31,7 +32,7 @@ func (sys *I) MustProxyDBus(sessionPath *container.Absolute, session *hst.BusCon
// ProxyDBus finalises configuration ahead of time and starts xdg-dbus-proxy via [dbus] and terminates it on revert. // ProxyDBus finalises configuration ahead of time and starts xdg-dbus-proxy via [dbus] and terminates it on revert.
// This [Op] is always [Process] scoped. // This [Op] is always [Process] scoped.
func (sys *I) ProxyDBus(session, system *hst.BusConfig, sessionPath, systemPath *container.Absolute) error { func (sys *I) ProxyDBus(session, system *hst.BusConfig, sessionPath, systemPath *check.Absolute) error {
d := new(dbusProxyOp) d := new(dbusProxyOp)
// session bus is required as otherwise this is effectively a very expensive noop // session bus is required as otherwise this is effectively a very expensive noop

View File

@ -10,6 +10,7 @@ import (
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/bits" "hakurei.app/container/bits"
"hakurei.app/container/check"
"hakurei.app/container/seccomp" "hakurei.app/container/seccomp"
"hakurei.app/helper" "hakurei.app/helper"
"hakurei.app/ldd" "hakurei.app/ldd"
@ -41,18 +42,18 @@ func (p *Proxy) Start() error {
cmd.Env = make([]string, 0) cmd.Env = make([]string, 0)
}, nil) }, nil)
} else { } else {
var toolPath *container.Absolute var toolPath *check.Absolute
if a, err := container.NewAbs(p.name); err != nil { if a, err := check.NewAbs(p.name); err != nil {
if p.name, err = exec.LookPath(p.name); err != nil { if p.name, err = exec.LookPath(p.name); err != nil {
return err return err
} else if toolPath, err = container.NewAbs(p.name); err != nil { } else if toolPath, err = check.NewAbs(p.name); err != nil {
return err return err
} }
} else { } else {
toolPath = a toolPath = a
} }
var libPaths []*container.Absolute var libPaths []*check.Absolute
if entries, err := ldd.Exec(ctx, p.msg, toolPath.String()); err != nil { if entries, err := ldd.Exec(ctx, p.msg, toolPath.String()); err != nil {
return err return err
} else { } else {
@ -76,7 +77,7 @@ func (p *Proxy) Start() error {
} }
// upstream bus directories // upstream bus directories
upstreamPaths := make([]*container.Absolute, 0, 2) upstreamPaths := make([]*check.Absolute, 0, 2)
for _, addr := range [][]AddrEntry{p.final.SessionUpstream, p.final.SystemUpstream} { for _, addr := range [][]AddrEntry{p.final.SessionUpstream, p.final.SystemUpstream} {
for _, ent := range addr { for _, ent := range addr {
if ent.Method != "unix" { if ent.Method != "unix" {
@ -86,7 +87,7 @@ func (p *Proxy) Start() error {
if pair[0] != "path" { if pair[0] != "path" {
continue continue
} }
if a, err := container.NewAbs(pair[1]); err != nil { if a, err := check.NewAbs(pair[1]); err != nil {
continue continue
} else { } else {
upstreamPaths = append(upstreamPaths, a.Dir()) upstreamPaths = append(upstreamPaths, a.Dir())
@ -94,8 +95,8 @@ func (p *Proxy) Start() error {
} }
} }
} }
container.SortAbs(upstreamPaths) check.SortAbs(upstreamPaths)
upstreamPaths = container.CompactAbs(upstreamPaths) upstreamPaths = check.CompactAbs(upstreamPaths)
for _, name := range upstreamPaths { for _, name := range upstreamPaths {
z.Bind(name, name, 0) z.Bind(name, name, 0)
} }
@ -103,15 +104,15 @@ func (p *Proxy) Start() error {
z.HostAbstract = z.HostNet z.HostAbstract = z.HostNet
// parent directories of bind paths // parent directories of bind paths
sockDirPaths := make([]*container.Absolute, 0, 2) sockDirPaths := make([]*check.Absolute, 0, 2)
if a, err := container.NewAbs(p.final.Session[1]); err == nil { if a, err := check.NewAbs(p.final.Session[1]); err == nil {
sockDirPaths = append(sockDirPaths, a.Dir()) sockDirPaths = append(sockDirPaths, a.Dir())
} }
if a, err := container.NewAbs(p.final.System[1]); err == nil { if a, err := check.NewAbs(p.final.System[1]); err == nil {
sockDirPaths = append(sockDirPaths, a.Dir()) sockDirPaths = append(sockDirPaths, a.Dir())
} }
container.SortAbs(sockDirPaths) check.SortAbs(sockDirPaths)
sockDirPaths = container.CompactAbs(sockDirPaths) sockDirPaths = check.CompactAbs(sockDirPaths)
for _, name := range sockDirPaths { for _, name := range sockDirPaths {
z.Bind(name, name, container.BindWritable) z.Bind(name, name, container.BindWritable)
} }

View File

@ -3,17 +3,17 @@ package system
import ( import (
"fmt" "fmt"
"hakurei.app/container" "hakurei.app/container/check"
"hakurei.app/hst" "hakurei.app/hst"
) )
// Link calls LinkFileType with the [Process] criteria. // Link calls LinkFileType with the [Process] criteria.
func (sys *I) Link(oldname, newname *container.Absolute) *I { func (sys *I) Link(oldname, newname *check.Absolute) *I {
return sys.LinkFileType(Process, oldname, newname) return sys.LinkFileType(Process, oldname, newname)
} }
// LinkFileType maintains a hardlink until its [Enablement] is no longer satisfied. // LinkFileType maintains a hardlink until its [Enablement] is no longer satisfied.
func (sys *I) LinkFileType(et hst.Enablement, oldname, newname *container.Absolute) *I { func (sys *I) LinkFileType(et hst.Enablement, oldname, newname *check.Absolute) *I {
sys.ops = append(sys.ops, &hardlinkOp{et, newname.String(), oldname.String()}) sys.ops = append(sys.ops, &hardlinkOp{et, newname.String(), oldname.String()})
return sys return sys
} }

View File

@ -5,18 +5,18 @@ import (
"fmt" "fmt"
"os" "os"
"hakurei.app/container" "hakurei.app/container/check"
"hakurei.app/hst" "hakurei.app/hst"
) )
// Ensure ensures the existence of a directory. // Ensure ensures the existence of a directory.
func (sys *I) Ensure(name *container.Absolute, perm os.FileMode) *I { func (sys *I) Ensure(name *check.Absolute, perm os.FileMode) *I {
sys.ops = append(sys.ops, &mkdirOp{User, name.String(), perm, false}) sys.ops = append(sys.ops, &mkdirOp{User, name.String(), perm, false})
return sys return sys
} }
// Ephemeral ensures the existence of a directory until its [Enablement] is no longer satisfied. // Ephemeral ensures the existence of a directory until its [Enablement] is no longer satisfied.
func (sys *I) Ephemeral(et hst.Enablement, name *container.Absolute, perm os.FileMode) *I { func (sys *I) Ephemeral(et hst.Enablement, name *check.Absolute, perm os.FileMode) *I {
sys.ops = append(sys.ops, &mkdirOp{et, name.String(), perm, true}) sys.ops = append(sys.ops, &mkdirOp{et, name.String(), perm, true})
return sys return sys
} }

View File

@ -9,6 +9,7 @@ import (
"testing" "testing"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/check"
"hakurei.app/container/stub" "hakurei.app/container/stub"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/system/internal/xcb" "hakurei.app/system/internal/xcb"
@ -313,4 +314,4 @@ func TestNop(t *testing.T) {
new(noCopy).Lock() new(noCopy).Lock()
} }
func m(pathname string) *container.Absolute { return container.MustAbs(pathname) } func m(pathname string) *check.Absolute { return check.MustAbs(pathname) }

View File

@ -8,12 +8,12 @@ import (
"os" "os"
"syscall" "syscall"
"hakurei.app/container" "hakurei.app/container/check"
"hakurei.app/hst" "hakurei.app/hst"
) )
// CopyFile reads up to n bytes from src and writes the resulting byte slice to payloadP. // CopyFile reads up to n bytes from src and writes the resulting byte slice to payloadP.
func (sys *I) CopyFile(payloadP *[]byte, src *container.Absolute, cap int, n int64) *I { func (sys *I) CopyFile(payloadP *[]byte, src *check.Absolute, cap int, n int64) *I {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
buf.Grow(cap) buf.Grow(cap)
sys.ops = append(sys.ops, &tmpfileOp{payloadP, src.String(), n, buf}) sys.ops = append(sys.ops, &tmpfileOp{payloadP, src.String(), n, buf})

View File

@ -5,7 +5,7 @@ import (
"fmt" "fmt"
"os" "os"
"hakurei.app/container" "hakurei.app/container/check"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/system/acl" "hakurei.app/system/acl"
"hakurei.app/system/wayland" "hakurei.app/system/wayland"
@ -20,7 +20,7 @@ type waylandConn interface {
// Wayland maintains a wayland socket with security-context-v1 attached via [wayland]. // 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 stops accepting connections once the pipe referred to by sync is closed.
// The socket is pathname only and is destroyed on revert. // The socket is pathname only and is destroyed on revert.
func (sys *I) Wayland(dst, src *container.Absolute, appID, instanceID string) *I { func (sys *I) Wayland(dst, src *check.Absolute, appID, instanceID string) *I {
sys.ops = append(sys.ops, &waylandOp{nil, sys.ops = append(sys.ops, &waylandOp{nil,
dst.String(), src.String(), dst.String(), src.String(),
appID, instanceID, appID, instanceID,