container/init: op interface valid method
All checks were successful
Test / Create distribution (push) Successful in 34s
Test / Sandbox (push) Successful in 2m10s
Test / Hakurei (push) Successful in 3m12s
Test / Hpkg (push) Successful in 3m58s
Test / Sandbox (race detector) (push) Successful in 4m19s
Test / Hakurei (race detector) (push) Successful in 4m57s
Test / Flake checks (push) Successful in 1m25s
All checks were successful
Test / Create distribution (push) Successful in 34s
Test / Sandbox (push) Successful in 2m10s
Test / Hakurei (push) Successful in 3m12s
Test / Hpkg (push) Successful in 3m58s
Test / Sandbox (race detector) (push) Successful in 4m19s
Test / Hakurei (race detector) (push) Successful in 4m57s
Test / Flake checks (push) Successful in 1m25s
Check ops early and eliminate duplicate checks. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
parent
a1482ecdd0
commit
5d8a2199b6
@ -21,6 +21,7 @@ func (f *Ops) Etc(host *Absolute, prefix string) *Ops {
|
||||
|
||||
type AutoEtcOp struct{ Prefix string }
|
||||
|
||||
func (e *AutoEtcOp) Valid() bool { return e != nil }
|
||||
func (e *AutoEtcOp) early(*setupState) error { return nil }
|
||||
func (e *AutoEtcOp) apply(state *setupState) error {
|
||||
if state.nonrepeatable&nrAutoEtc != 0 {
|
||||
@ -66,7 +67,7 @@ func (e *AutoEtcOp) hostRel() string { return ".host/" + e.Prefix }
|
||||
|
||||
func (e *AutoEtcOp) Is(op Op) bool {
|
||||
ve, ok := op.(*AutoEtcOp)
|
||||
return ok && ((e == nil && ve == nil) || (e != nil && ve != nil && *e == *ve))
|
||||
return ok && e.Valid() && ve.Valid() && *e == *ve
|
||||
}
|
||||
func (*AutoEtcOp) prefix() string { return "setting up" }
|
||||
func (e *AutoEtcOp) String() string { return fmt.Sprintf("auto etc %s", e.Prefix) }
|
||||
|
@ -3,6 +3,12 @@ package container
|
||||
import "testing"
|
||||
|
||||
func TestAutoEtcOp(t *testing.T) {
|
||||
checkOpsValid(t, []opValidTestCase{
|
||||
{"nil", (*AutoEtcOp)(nil), false},
|
||||
{"zero", new(AutoEtcOp), true},
|
||||
{"populated", &AutoEtcOp{Prefix: ":3"}, true},
|
||||
})
|
||||
|
||||
checkOpsBuilder(t, []opsBuilderTestCase{
|
||||
{"pd", new(Ops).Etc(MustAbs("/etc/"), "048090b6ed8f9ebb10e275ff5d8c0659"), Ops{
|
||||
&MkdirOp{Path: MustAbs("/etc/"), Perm: 0755},
|
||||
|
@ -28,11 +28,9 @@ type AutoRootOp struct {
|
||||
resolved []Op
|
||||
}
|
||||
|
||||
func (r *AutoRootOp) early(state *setupState) error {
|
||||
if r.Host == nil {
|
||||
return syscall.EBADE
|
||||
}
|
||||
func (r *AutoRootOp) Valid() bool { return r != nil && r.Host != nil }
|
||||
|
||||
func (r *AutoRootOp) early(state *setupState) error {
|
||||
if d, err := os.ReadDir(r.Host.String()); err != nil {
|
||||
return wrapErrSelf(err)
|
||||
} else {
|
||||
@ -72,9 +70,10 @@ func (r *AutoRootOp) apply(state *setupState) error {
|
||||
|
||||
func (r *AutoRootOp) Is(op Op) bool {
|
||||
vr, ok := op.(*AutoRootOp)
|
||||
return ok && ((r == nil && vr == nil) || (r != nil && vr != nil &&
|
||||
r.Host != nil && vr.Host != nil && r.Host.Is(vr.Host) &&
|
||||
r.Prefix == vr.Prefix && r.Flags == vr.Flags))
|
||||
return ok && r.Valid() && vr.Valid() &&
|
||||
r.Host.Is(vr.Host) &&
|
||||
r.Prefix == vr.Prefix &&
|
||||
r.Flags == vr.Flags
|
||||
}
|
||||
func (*AutoRootOp) prefix() string { return "setting up" }
|
||||
func (r *AutoRootOp) String() string {
|
||||
|
@ -3,6 +3,12 @@ package container
|
||||
import "testing"
|
||||
|
||||
func TestAutoRootOp(t *testing.T) {
|
||||
checkOpsValid(t, []opValidTestCase{
|
||||
{"nil", (*AutoRootOp)(nil), false},
|
||||
{"zero", new(AutoRootOp), false},
|
||||
{"valid", &AutoRootOp{Host: MustAbs("/")}, true},
|
||||
})
|
||||
|
||||
checkOpsBuilder(t, []opsBuilderTestCase{
|
||||
{"pd", new(Ops).Root(MustAbs("/"), "048090b6ed8f9ebb10e275ff5d8c0659", BindWritable), Ops{
|
||||
&AutoRootOp{
|
||||
|
@ -52,6 +52,7 @@ type (
|
||||
|
||||
prefix() string
|
||||
Is(op Op) bool
|
||||
Valid() bool
|
||||
fmt.Stringer
|
||||
}
|
||||
|
||||
@ -167,8 +168,8 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) {
|
||||
via library functions after pivot_root, and implementations are expected to avoid changing
|
||||
the state of the mount namespace */
|
||||
for i, op := range *params.Ops {
|
||||
if op == nil {
|
||||
log.Fatalf("invalid op %d", i)
|
||||
if op == nil || !op.Valid() {
|
||||
log.Fatalf("invalid op at index %d", i)
|
||||
}
|
||||
|
||||
if err := op.early(state); err != nil {
|
||||
|
@ -24,6 +24,8 @@ type BindMountOp struct {
|
||||
Flags int
|
||||
}
|
||||
|
||||
func (b *BindMountOp) Valid() bool { return b != nil && b.Source != nil && b.Target != nil }
|
||||
|
||||
const (
|
||||
// BindOptional skips nonexistent host paths.
|
||||
BindOptional = 1 << iota
|
||||
@ -34,10 +36,6 @@ const (
|
||||
)
|
||||
|
||||
func (b *BindMountOp) early(*setupState) error {
|
||||
if b.Source == nil || b.Target == nil {
|
||||
return EBADE
|
||||
}
|
||||
|
||||
if pathname, err := filepath.EvalSymlinks(b.Source.String()); err != nil {
|
||||
if os.IsNotExist(err) && b.Flags&BindOptional != 0 {
|
||||
// leave sourceFinal as nil
|
||||
@ -87,10 +85,10 @@ func (b *BindMountOp) apply(*setupState) error {
|
||||
|
||||
func (b *BindMountOp) Is(op Op) bool {
|
||||
vb, ok := op.(*BindMountOp)
|
||||
return ok && ((b == nil && vb == nil) || (b != nil && vb != nil &&
|
||||
b.Source != nil && vb.Source != nil && b.Source.Is(vb.Source) &&
|
||||
b.Target != nil && vb.Target != nil && b.Target.Is(vb.Target) &&
|
||||
b.Flags == vb.Flags))
|
||||
return ok && b.Valid() && vb.Valid() &&
|
||||
b.Source.Is(vb.Source) &&
|
||||
b.Target.Is(vb.Target) &&
|
||||
b.Flags == vb.Flags
|
||||
}
|
||||
func (*BindMountOp) prefix() string { return "mounting" }
|
||||
func (b *BindMountOp) String() string {
|
||||
|
@ -3,6 +3,14 @@ package container
|
||||
import "testing"
|
||||
|
||||
func TestBindMountOp(t *testing.T) {
|
||||
checkOpsValid(t, []opValidTestCase{
|
||||
{"nil", (*BindMountOp)(nil), false},
|
||||
{"zero", new(BindMountOp), false},
|
||||
{"nil source", &BindMountOp{Target: MustAbs("/")}, false},
|
||||
{"nil target", &BindMountOp{Source: MustAbs("/")}, false},
|
||||
{"valid", &BindMountOp{Source: MustAbs("/"), Target: MustAbs("/")}, true},
|
||||
})
|
||||
|
||||
checkOpsBuilder(t, []opsBuilderTestCase{
|
||||
{"autoetc", new(Ops).Bind(
|
||||
MustAbs("/etc/"),
|
||||
|
@ -33,11 +33,9 @@ type MountDevOp struct {
|
||||
Write bool
|
||||
}
|
||||
|
||||
func (d *MountDevOp) Valid() bool { return d != nil && d.Target != nil }
|
||||
func (d *MountDevOp) early(*setupState) error { return nil }
|
||||
func (d *MountDevOp) apply(state *setupState) error {
|
||||
if d.Target == nil {
|
||||
return EBADE
|
||||
}
|
||||
target := toSysroot(d.Target.String())
|
||||
|
||||
if err := mountTmpfs(SourceTmpfsDevtmpfs, target, MS_NOSUID|MS_NODEV, 0, state.ParentPerm); err != nil {
|
||||
@ -128,9 +126,10 @@ func (d *MountDevOp) apply(state *setupState) error {
|
||||
|
||||
func (d *MountDevOp) Is(op Op) bool {
|
||||
vd, ok := op.(*MountDevOp)
|
||||
return ok && ((d == nil && vd == nil) || (d != nil && vd != nil &&
|
||||
d.Target != nil && vd.Target != nil && d.Target.Is(vd.Target) &&
|
||||
d.Mqueue == vd.Mqueue && d.Write == vd.Write))
|
||||
return ok && d.Valid() && vd.Valid() &&
|
||||
d.Target.Is(vd.Target) &&
|
||||
d.Mqueue == vd.Mqueue &&
|
||||
d.Write == vd.Write
|
||||
}
|
||||
func (*MountDevOp) prefix() string { return "mounting" }
|
||||
func (d *MountDevOp) String() string {
|
||||
|
@ -3,6 +3,12 @@ package container
|
||||
import "testing"
|
||||
|
||||
func TestMountDevOp(t *testing.T) {
|
||||
checkOpsValid(t, []opValidTestCase{
|
||||
{"nil", (*MountDevOp)(nil), false},
|
||||
{"zero", new(MountDevOp), false},
|
||||
{"valid", &MountDevOp{Target: MustAbs("/dev/")}, true},
|
||||
})
|
||||
|
||||
checkOpsBuilder(t, []opsBuilderTestCase{
|
||||
{"dev", new(Ops).Dev(MustAbs("/dev/"), true), Ops{
|
||||
&MountDevOp{
|
||||
|
@ -4,7 +4,6 @@ import (
|
||||
"encoding/gob"
|
||||
"fmt"
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func init() { gob.Register(new(MkdirOp)) }
|
||||
@ -21,19 +20,17 @@ type MkdirOp struct {
|
||||
Perm os.FileMode
|
||||
}
|
||||
|
||||
func (m *MkdirOp) Valid() bool { return m != nil && m.Path != nil }
|
||||
func (m *MkdirOp) early(*setupState) error { return nil }
|
||||
func (m *MkdirOp) apply(*setupState) error {
|
||||
if m.Path == nil {
|
||||
return syscall.EBADE
|
||||
}
|
||||
return wrapErrSelf(os.MkdirAll(toSysroot(m.Path.String()), m.Perm))
|
||||
}
|
||||
|
||||
func (m *MkdirOp) Is(op Op) bool {
|
||||
vm, ok := op.(*MkdirOp)
|
||||
return ok && ((m == nil && vm == nil) || (m != nil && vm != nil &&
|
||||
m.Path != nil && vm.Path != nil && m.Path.Is(vm.Path) &&
|
||||
m.Perm == vm.Perm))
|
||||
return ok && m.Valid() && vm.Valid() &&
|
||||
m.Path.Is(vm.Path) &&
|
||||
m.Perm == vm.Perm
|
||||
}
|
||||
func (*MkdirOp) prefix() string { return "creating" }
|
||||
func (m *MkdirOp) String() string { return fmt.Sprintf("directory %q perm %s", m.Path, m.Perm) }
|
||||
|
@ -3,6 +3,12 @@ package container
|
||||
import "testing"
|
||||
|
||||
func TestMkdirOp(t *testing.T) {
|
||||
checkOpsValid(t, []opValidTestCase{
|
||||
{"nil", (*MkdirOp)(nil), false},
|
||||
{"zero", new(MkdirOp), false},
|
||||
{"valid", &MkdirOp{Path: MustAbs("/.hakurei")}, true},
|
||||
})
|
||||
|
||||
checkOpsBuilder(t, []opsBuilderTestCase{
|
||||
{"etc", new(Ops).Mkdir(MustAbs("/etc/"), 0), Ops{
|
||||
&MkdirOp{Path: MustAbs("/etc/")},
|
||||
|
@ -53,10 +53,10 @@ type MountOverlayOp struct {
|
||||
lower []string
|
||||
// The upperdir is normally on a writable filesystem.
|
||||
//
|
||||
// If Work is nil and Upper holds the special value [FHSRoot],
|
||||
// If Work is nil and Upper holds the special value [AbsFHSRoot],
|
||||
// an ephemeral upperdir and workdir will be set up.
|
||||
//
|
||||
// If both Work and Upper are empty strings, 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
|
||||
// formatted for [OptionOverlayUpperdir], resolved, prefixed and escaped during early
|
||||
upper string
|
||||
@ -68,6 +68,19 @@ type MountOverlayOp struct {
|
||||
ephemeral bool
|
||||
}
|
||||
|
||||
func (o *MountOverlayOp) Valid() bool {
|
||||
if o == nil {
|
||||
return false
|
||||
}
|
||||
if o.Work != nil && o.Upper == nil {
|
||||
return false
|
||||
}
|
||||
if slices.Contains(o.Lower, nil) {
|
||||
return false
|
||||
}
|
||||
return o.Target != nil
|
||||
}
|
||||
|
||||
func (o *MountOverlayOp) early(*setupState) error {
|
||||
if o.Work == nil && o.Upper != nil {
|
||||
switch o.Upper.String() {
|
||||
@ -104,11 +117,7 @@ func (o *MountOverlayOp) early(*setupState) error {
|
||||
}
|
||||
|
||||
o.lower = make([]string, len(o.Lower))
|
||||
for i, a := range o.Lower {
|
||||
if a == nil {
|
||||
return EBADE
|
||||
}
|
||||
|
||||
for i, a := range o.Lower { // nil checked in Valid
|
||||
if v, err := filepath.EvalSymlinks(a.String()); err != nil {
|
||||
return wrapErrSelf(err)
|
||||
} else {
|
||||
@ -119,9 +128,6 @@ func (o *MountOverlayOp) early(*setupState) error {
|
||||
}
|
||||
|
||||
func (o *MountOverlayOp) apply(state *setupState) error {
|
||||
if o.Target == nil {
|
||||
return EBADE
|
||||
}
|
||||
target := toSysroot(o.Target.String())
|
||||
if err := os.MkdirAll(target, state.ParentPerm); err != nil {
|
||||
return wrapErrSelf(err)
|
||||
@ -163,10 +169,10 @@ func (o *MountOverlayOp) apply(state *setupState) error {
|
||||
|
||||
func (o *MountOverlayOp) Is(op Op) bool {
|
||||
vo, ok := op.(*MountOverlayOp)
|
||||
return ok && ((o == nil && vo == nil) || (o != nil && vo != nil &&
|
||||
o.Target != nil && vo.Target != nil && o.Target.Is(vo.Target) &&
|
||||
return ok && o.Valid() && vo.Valid() &&
|
||||
o.Target.Is(vo.Target) &&
|
||||
slices.EqualFunc(o.Lower, vo.Lower, func(a *Absolute, v *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 { return "mounting" }
|
||||
func (o *MountOverlayOp) String() string {
|
||||
|
@ -3,6 +3,15 @@ package container
|
||||
import "testing"
|
||||
|
||||
func TestMountOverlayOp(t *testing.T) {
|
||||
checkOpsValid(t, []opValidTestCase{
|
||||
{"nil", (*MountOverlayOp)(nil), false},
|
||||
{"zero", new(MountOverlayOp), false},
|
||||
{"nil lower", &MountOverlayOp{Target: MustAbs("/"), Lower: []*Absolute{nil}}, false},
|
||||
{"ro", &MountOverlayOp{Target: MustAbs("/"), Lower: []*Absolute{MustAbs("/")}}, true},
|
||||
{"ro work", &MountOverlayOp{Target: MustAbs("/"), Work: MustAbs("/tmp/")}, false},
|
||||
{"rw", &MountOverlayOp{Target: MustAbs("/"), Lower: []*Absolute{MustAbs("/")}, Upper: MustAbs("/"), Work: MustAbs("/")}, true},
|
||||
})
|
||||
|
||||
checkOpsBuilder(t, []opsBuilderTestCase{
|
||||
{"full", new(Ops).Overlay(
|
||||
MustAbs("/nix/store"),
|
||||
|
@ -35,12 +35,9 @@ type TmpfileOp struct {
|
||||
Data []byte
|
||||
}
|
||||
|
||||
func (t *TmpfileOp) Valid() bool { return t != nil && t.Path != nil }
|
||||
func (t *TmpfileOp) early(*setupState) error { return nil }
|
||||
func (t *TmpfileOp) apply(state *setupState) error {
|
||||
if t.Path == nil {
|
||||
return EBADE
|
||||
}
|
||||
|
||||
var tmpPath string
|
||||
if f, err := os.CreateTemp(FHSRoot, intermediatePatternTmpfile); err != nil {
|
||||
return wrapErrSelf(err)
|
||||
@ -72,7 +69,8 @@ func (t *TmpfileOp) apply(state *setupState) error {
|
||||
|
||||
func (t *TmpfileOp) Is(op Op) bool {
|
||||
vt, ok := op.(*TmpfileOp)
|
||||
return ok && t.Path != nil && vt.Path != nil && t.Path.Is(vt.Path) &&
|
||||
return ok && t.Valid() && vt.Valid() &&
|
||||
t.Path.Is(vt.Path) &&
|
||||
string(t.Data) == string(vt.Data)
|
||||
}
|
||||
func (*TmpfileOp) prefix() string { return "placing" }
|
||||
|
@ -3,6 +3,12 @@ package container
|
||||
import "testing"
|
||||
|
||||
func TestTmpfileOp(t *testing.T) {
|
||||
checkOpsValid(t, []opValidTestCase{
|
||||
{"nil", (*TmpfileOp)(nil), false},
|
||||
{"zero", new(TmpfileOp), false},
|
||||
{"valid", &TmpfileOp{Path: MustAbs("/etc/passwd")}, true},
|
||||
})
|
||||
|
||||
checkOpsBuilder(t, []opsBuilderTestCase{
|
||||
{"noref", new(Ops).Place(MustAbs("/etc/passwd"), []byte(`chronos:x:65534:65534:Hakurei:/var/empty:/bin/zsh`)), Ops{
|
||||
&TmpfileOp{
|
||||
|
@ -16,15 +16,11 @@ func (f *Ops) Proc(target *Absolute) *Ops {
|
||||
}
|
||||
|
||||
// MountProcOp mounts a new instance of [FstypeProc] on container path Target.
|
||||
type MountProcOp struct {
|
||||
Target *Absolute
|
||||
}
|
||||
type MountProcOp struct{ Target *Absolute }
|
||||
|
||||
func (p *MountProcOp) Valid() bool { return p != nil && p.Target != nil }
|
||||
func (p *MountProcOp) early(*setupState) error { return nil }
|
||||
func (p *MountProcOp) apply(state *setupState) error {
|
||||
if p.Target == nil {
|
||||
return EBADE
|
||||
}
|
||||
target := toSysroot(p.Target.String())
|
||||
if err := os.MkdirAll(target, state.ParentPerm); err != nil {
|
||||
return wrapErrSelf(err)
|
||||
@ -35,8 +31,8 @@ func (p *MountProcOp) apply(state *setupState) error {
|
||||
|
||||
func (p *MountProcOp) Is(op Op) bool {
|
||||
vp, ok := op.(*MountProcOp)
|
||||
return ok && ((p == nil && vp == nil) ||
|
||||
(p.Target != nil && vp.Target != nil && p.Target.Is(vp.Target)))
|
||||
return ok && p.Valid() && vp.Valid() &&
|
||||
p.Target.Is(vp.Target)
|
||||
}
|
||||
func (*MountProcOp) prefix() string { return "mounting" }
|
||||
func (p *MountProcOp) String() string { return fmt.Sprintf("proc on %q", p.Target) }
|
||||
|
@ -3,6 +3,12 @@ package container
|
||||
import "testing"
|
||||
|
||||
func TestMountProcOp(t *testing.T) {
|
||||
checkOpsValid(t, []opValidTestCase{
|
||||
{"nil", (*MountProcOp)(nil), false},
|
||||
{"zero", new(MountProcOp), false},
|
||||
{"valid", &MountProcOp{Target: MustAbs("/proc/")}, true},
|
||||
})
|
||||
|
||||
checkOpsBuilder(t, []opsBuilderTestCase{
|
||||
{"proc", new(Ops).Proc(MustAbs("/proc/")), Ops{
|
||||
&MountProcOp{Target: MustAbs("/proc/")},
|
||||
|
@ -3,7 +3,6 @@ package container
|
||||
import (
|
||||
"encoding/gob"
|
||||
"fmt"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func init() { gob.Register(new(RemountOp)) }
|
||||
@ -20,20 +19,18 @@ type RemountOp struct {
|
||||
Flags uintptr
|
||||
}
|
||||
|
||||
func (r *RemountOp) Valid() bool { return r != nil && r.Target != nil }
|
||||
func (*RemountOp) early(*setupState) error { return nil }
|
||||
func (r *RemountOp) apply(*setupState) error {
|
||||
if r.Target == nil {
|
||||
return syscall.EBADE
|
||||
}
|
||||
return wrapErrSuffix(hostProc.remount(toSysroot(r.Target.String()), r.Flags),
|
||||
fmt.Sprintf("cannot remount %q:", r.Target))
|
||||
}
|
||||
|
||||
func (r *RemountOp) Is(op Op) bool {
|
||||
vr, ok := op.(*RemountOp)
|
||||
return ok && ((r == nil && vr == nil) ||
|
||||
(r.Target != nil && vr.Target != nil && r.Target.Is(vr.Target)) &&
|
||||
r.Flags == vr.Flags)
|
||||
return ok && r.Valid() && vr.Valid() &&
|
||||
r.Target.Is(vr.Target) &&
|
||||
r.Flags == vr.Flags
|
||||
}
|
||||
func (*RemountOp) prefix() string { return "remounting" }
|
||||
func (r *RemountOp) String() string { return fmt.Sprintf("%q flags %#x", r.Target, r.Flags) }
|
||||
|
@ -6,6 +6,12 @@ import (
|
||||
)
|
||||
|
||||
func TestRemountOp(t *testing.T) {
|
||||
checkOpsValid(t, []opValidTestCase{
|
||||
{"nil", (*RemountOp)(nil), false},
|
||||
{"zero", new(RemountOp), false},
|
||||
{"valid", &RemountOp{Target: MustAbs("/"), Flags: syscall.MS_RDONLY}, true},
|
||||
})
|
||||
|
||||
checkOpsBuilder(t, []opsBuilderTestCase{
|
||||
{"root", new(Ops).Remount(MustAbs("/"), syscall.MS_RDONLY), Ops{
|
||||
&RemountOp{
|
||||
|
@ -26,6 +26,8 @@ type SymlinkOp struct {
|
||||
Dereference bool
|
||||
}
|
||||
|
||||
func (l *SymlinkOp) Valid() bool { return l != nil && l.Target != nil && l.LinkName != zeroString }
|
||||
|
||||
func (l *SymlinkOp) early(*setupState) error {
|
||||
if l.Dereference {
|
||||
if !isAbs(l.LinkName) {
|
||||
@ -41,9 +43,6 @@ func (l *SymlinkOp) early(*setupState) error {
|
||||
}
|
||||
|
||||
func (l *SymlinkOp) apply(state *setupState) error {
|
||||
if l.Target == nil {
|
||||
return syscall.EBADE
|
||||
}
|
||||
target := toSysroot(l.Target.String())
|
||||
if err := os.MkdirAll(path.Dir(target), state.ParentPerm); err != nil {
|
||||
return wrapErrSelf(err)
|
||||
@ -56,9 +55,10 @@ func (l *SymlinkOp) apply(state *setupState) error {
|
||||
|
||||
func (l *SymlinkOp) Is(op Op) bool {
|
||||
vl, ok := op.(*SymlinkOp)
|
||||
return ok && ((l == nil && vl == nil) ||
|
||||
(l.Target != nil && vl.Target != nil && l.Target.Is(vl.Target)) &&
|
||||
l.LinkName == vl.LinkName && l.Dereference == vl.Dereference)
|
||||
return ok && l.Valid() && vl.Valid() &&
|
||||
l.Target.Is(vl.Target) &&
|
||||
l.LinkName == vl.LinkName &&
|
||||
l.Dereference == vl.Dereference
|
||||
}
|
||||
func (*SymlinkOp) prefix() string { return "creating" }
|
||||
func (l *SymlinkOp) String() string {
|
||||
|
@ -3,6 +3,14 @@ package container
|
||||
import "testing"
|
||||
|
||||
func TestSymlinkOp(t *testing.T) {
|
||||
checkOpsValid(t, []opValidTestCase{
|
||||
{"nil", (*SymlinkOp)(nil), false},
|
||||
{"zero", new(SymlinkOp), false},
|
||||
{"nil target", &SymlinkOp{LinkName: "/run/current-system"}, false},
|
||||
{"zero linkname", &SymlinkOp{Target: MustAbs("/run/current-system")}, false},
|
||||
{"valid", &SymlinkOp{Target: MustAbs("/run/current-system"), LinkName: "/run/current-system", Dereference: true}, true},
|
||||
})
|
||||
|
||||
checkOpsBuilder(t, []opsBuilderTestCase{
|
||||
{"current-system", new(Ops).Link(
|
||||
MustAbs("/run/current-system"),
|
||||
|
@ -31,11 +31,9 @@ type MountTmpfsOp struct {
|
||||
Perm os.FileMode
|
||||
}
|
||||
|
||||
func (t *MountTmpfsOp) Valid() bool { return t != nil && t.Path != nil && t.FSName != zeroString }
|
||||
func (t *MountTmpfsOp) early(*setupState) error { return nil }
|
||||
func (t *MountTmpfsOp) apply(*setupState) error {
|
||||
if t.Path == nil {
|
||||
return EBADE
|
||||
}
|
||||
if t.Size < 0 || t.Size > math.MaxUint>>1 {
|
||||
return msg.WrapErr(EBADE, fmt.Sprintf("size %d out of bounds", t.Size))
|
||||
}
|
||||
@ -44,9 +42,12 @@ func (t *MountTmpfsOp) apply(*setupState) error {
|
||||
|
||||
func (t *MountTmpfsOp) Is(op Op) bool {
|
||||
vt, ok := op.(*MountTmpfsOp)
|
||||
return ok && ((t == nil && vt == nil) ||
|
||||
(t.Path != nil && vt.Path != nil && t.Path.Is(vt.Path)) &&
|
||||
t.FSName == vt.FSName && t.Flags == vt.Flags && t.Size == vt.Size && t.Perm == vt.Perm)
|
||||
return ok && t.Valid() && vt.Valid() &&
|
||||
t.FSName == vt.FSName &&
|
||||
t.Path.Is(vt.Path) &&
|
||||
t.Flags == vt.Flags &&
|
||||
t.Size == vt.Size &&
|
||||
t.Perm == vt.Perm
|
||||
}
|
||||
func (*MountTmpfsOp) prefix() string { return "mounting" }
|
||||
func (t *MountTmpfsOp) String() string { return fmt.Sprintf("tmpfs on %q size %d", t.Path, t.Size) }
|
||||
|
@ -6,6 +6,14 @@ import (
|
||||
)
|
||||
|
||||
func TestMountTmpfsOp(t *testing.T) {
|
||||
checkOpsValid(t, []opValidTestCase{
|
||||
{"nil", (*MountTmpfsOp)(nil), false},
|
||||
{"zero", new(MountTmpfsOp), false},
|
||||
{"nil path", &MountTmpfsOp{FSName: "tmpfs"}, false},
|
||||
{"zero fsname", &MountTmpfsOp{Path: MustAbs("/tmp/")}, false},
|
||||
{"valid", &MountTmpfsOp{FSName: "tmpfs", Path: MustAbs("/tmp/")}, true},
|
||||
})
|
||||
|
||||
checkOpsBuilder(t, []opsBuilderTestCase{
|
||||
{"runtime", new(Ops).Tmpfs(
|
||||
MustAbs("/run/user"),
|
||||
|
@ -49,6 +49,24 @@ func TestEscapeOverlayDataSegment(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
type opValidTestCase struct {
|
||||
name string
|
||||
op Op
|
||||
want bool
|
||||
}
|
||||
|
||||
func checkOpsValid(t *testing.T, testCases []opValidTestCase) {
|
||||
t.Run("valid", func(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
if got := tc.op.Valid(); got != tc.want {
|
||||
t.Errorf("Valid: %v, want %v", got, tc.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
type opsBuilderTestCase struct {
|
||||
name string
|
||||
ops *Ops
|
||||
|
Loading…
x
Reference in New Issue
Block a user