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 }
|
type AutoEtcOp struct{ Prefix string }
|
||||||
|
|
||||||
|
func (e *AutoEtcOp) Valid() bool { return e != nil }
|
||||||
func (e *AutoEtcOp) early(*setupState) error { return nil }
|
func (e *AutoEtcOp) early(*setupState) error { return nil }
|
||||||
func (e *AutoEtcOp) apply(state *setupState) error {
|
func (e *AutoEtcOp) apply(state *setupState) error {
|
||||||
if state.nonrepeatable&nrAutoEtc != 0 {
|
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 {
|
func (e *AutoEtcOp) Is(op Op) bool {
|
||||||
ve, ok := op.(*AutoEtcOp)
|
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 (*AutoEtcOp) prefix() string { return "setting up" }
|
||||||
func (e *AutoEtcOp) String() string { return fmt.Sprintf("auto etc %s", e.Prefix) }
|
func (e *AutoEtcOp) String() string { return fmt.Sprintf("auto etc %s", e.Prefix) }
|
||||||
|
@ -3,6 +3,12 @@ package container
|
|||||||
import "testing"
|
import "testing"
|
||||||
|
|
||||||
func TestAutoEtcOp(t *testing.T) {
|
func TestAutoEtcOp(t *testing.T) {
|
||||||
|
checkOpsValid(t, []opValidTestCase{
|
||||||
|
{"nil", (*AutoEtcOp)(nil), false},
|
||||||
|
{"zero", new(AutoEtcOp), true},
|
||||||
|
{"populated", &AutoEtcOp{Prefix: ":3"}, true},
|
||||||
|
})
|
||||||
|
|
||||||
checkOpsBuilder(t, []opsBuilderTestCase{
|
checkOpsBuilder(t, []opsBuilderTestCase{
|
||||||
{"pd", new(Ops).Etc(MustAbs("/etc/"), "048090b6ed8f9ebb10e275ff5d8c0659"), Ops{
|
{"pd", new(Ops).Etc(MustAbs("/etc/"), "048090b6ed8f9ebb10e275ff5d8c0659"), Ops{
|
||||||
&MkdirOp{Path: MustAbs("/etc/"), Perm: 0755},
|
&MkdirOp{Path: MustAbs("/etc/"), Perm: 0755},
|
||||||
|
@ -28,11 +28,9 @@ type AutoRootOp struct {
|
|||||||
resolved []Op
|
resolved []Op
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *AutoRootOp) early(state *setupState) error {
|
func (r *AutoRootOp) Valid() bool { return r != nil && r.Host != nil }
|
||||||
if r.Host == nil {
|
|
||||||
return syscall.EBADE
|
|
||||||
}
|
|
||||||
|
|
||||||
|
func (r *AutoRootOp) early(state *setupState) error {
|
||||||
if d, err := os.ReadDir(r.Host.String()); err != nil {
|
if d, err := os.ReadDir(r.Host.String()); err != nil {
|
||||||
return wrapErrSelf(err)
|
return wrapErrSelf(err)
|
||||||
} else {
|
} else {
|
||||||
@ -72,9 +70,10 @@ func (r *AutoRootOp) apply(state *setupState) error {
|
|||||||
|
|
||||||
func (r *AutoRootOp) Is(op Op) bool {
|
func (r *AutoRootOp) Is(op Op) bool {
|
||||||
vr, ok := op.(*AutoRootOp)
|
vr, ok := op.(*AutoRootOp)
|
||||||
return ok && ((r == nil && vr == nil) || (r != nil && vr != nil &&
|
return ok && r.Valid() && vr.Valid() &&
|
||||||
r.Host != nil && vr.Host != nil && r.Host.Is(vr.Host) &&
|
r.Host.Is(vr.Host) &&
|
||||||
r.Prefix == vr.Prefix && r.Flags == vr.Flags))
|
r.Prefix == vr.Prefix &&
|
||||||
|
r.Flags == vr.Flags
|
||||||
}
|
}
|
||||||
func (*AutoRootOp) prefix() string { return "setting up" }
|
func (*AutoRootOp) prefix() string { return "setting up" }
|
||||||
func (r *AutoRootOp) String() string {
|
func (r *AutoRootOp) String() string {
|
||||||
|
@ -3,6 +3,12 @@ package container
|
|||||||
import "testing"
|
import "testing"
|
||||||
|
|
||||||
func TestAutoRootOp(t *testing.T) {
|
func TestAutoRootOp(t *testing.T) {
|
||||||
|
checkOpsValid(t, []opValidTestCase{
|
||||||
|
{"nil", (*AutoRootOp)(nil), false},
|
||||||
|
{"zero", new(AutoRootOp), false},
|
||||||
|
{"valid", &AutoRootOp{Host: MustAbs("/")}, true},
|
||||||
|
})
|
||||||
|
|
||||||
checkOpsBuilder(t, []opsBuilderTestCase{
|
checkOpsBuilder(t, []opsBuilderTestCase{
|
||||||
{"pd", new(Ops).Root(MustAbs("/"), "048090b6ed8f9ebb10e275ff5d8c0659", BindWritable), Ops{
|
{"pd", new(Ops).Root(MustAbs("/"), "048090b6ed8f9ebb10e275ff5d8c0659", BindWritable), Ops{
|
||||||
&AutoRootOp{
|
&AutoRootOp{
|
||||||
|
@ -52,6 +52,7 @@ type (
|
|||||||
|
|
||||||
prefix() string
|
prefix() string
|
||||||
Is(op Op) bool
|
Is(op Op) bool
|
||||||
|
Valid() bool
|
||||||
fmt.Stringer
|
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
|
via library functions after pivot_root, and implementations are expected to avoid changing
|
||||||
the state of the mount namespace */
|
the state of the mount namespace */
|
||||||
for i, op := range *params.Ops {
|
for i, op := range *params.Ops {
|
||||||
if op == nil {
|
if op == nil || !op.Valid() {
|
||||||
log.Fatalf("invalid op %d", i)
|
log.Fatalf("invalid op at index %d", i)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := op.early(state); err != nil {
|
if err := op.early(state); err != nil {
|
||||||
|
@ -24,6 +24,8 @@ type BindMountOp struct {
|
|||||||
Flags int
|
Flags int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *BindMountOp) Valid() bool { return b != nil && b.Source != nil && b.Target != nil }
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// BindOptional skips nonexistent host paths.
|
// BindOptional skips nonexistent host paths.
|
||||||
BindOptional = 1 << iota
|
BindOptional = 1 << iota
|
||||||
@ -34,10 +36,6 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (b *BindMountOp) early(*setupState) error {
|
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 pathname, err := filepath.EvalSymlinks(b.Source.String()); err != nil {
|
||||||
if os.IsNotExist(err) && b.Flags&BindOptional != 0 {
|
if os.IsNotExist(err) && b.Flags&BindOptional != 0 {
|
||||||
// leave sourceFinal as nil
|
// leave sourceFinal as nil
|
||||||
@ -87,10 +85,10 @@ func (b *BindMountOp) apply(*setupState) error {
|
|||||||
|
|
||||||
func (b *BindMountOp) Is(op Op) bool {
|
func (b *BindMountOp) Is(op Op) bool {
|
||||||
vb, ok := op.(*BindMountOp)
|
vb, ok := op.(*BindMountOp)
|
||||||
return ok && ((b == nil && vb == nil) || (b != nil && vb != nil &&
|
return ok && b.Valid() && vb.Valid() &&
|
||||||
b.Source != nil && vb.Source != nil && b.Source.Is(vb.Source) &&
|
b.Source.Is(vb.Source) &&
|
||||||
b.Target != nil && vb.Target != nil && b.Target.Is(vb.Target) &&
|
b.Target.Is(vb.Target) &&
|
||||||
b.Flags == vb.Flags))
|
b.Flags == vb.Flags
|
||||||
}
|
}
|
||||||
func (*BindMountOp) prefix() string { return "mounting" }
|
func (*BindMountOp) prefix() string { return "mounting" }
|
||||||
func (b *BindMountOp) String() string {
|
func (b *BindMountOp) String() string {
|
||||||
|
@ -3,6 +3,14 @@ package container
|
|||||||
import "testing"
|
import "testing"
|
||||||
|
|
||||||
func TestBindMountOp(t *testing.T) {
|
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{
|
checkOpsBuilder(t, []opsBuilderTestCase{
|
||||||
{"autoetc", new(Ops).Bind(
|
{"autoetc", new(Ops).Bind(
|
||||||
MustAbs("/etc/"),
|
MustAbs("/etc/"),
|
||||||
|
@ -33,11 +33,9 @@ type MountDevOp struct {
|
|||||||
Write bool
|
Write bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *MountDevOp) Valid() bool { return d != nil && d.Target != nil }
|
||||||
func (d *MountDevOp) early(*setupState) error { return nil }
|
func (d *MountDevOp) early(*setupState) error { return nil }
|
||||||
func (d *MountDevOp) apply(state *setupState) error {
|
func (d *MountDevOp) apply(state *setupState) error {
|
||||||
if d.Target == nil {
|
|
||||||
return EBADE
|
|
||||||
}
|
|
||||||
target := toSysroot(d.Target.String())
|
target := toSysroot(d.Target.String())
|
||||||
|
|
||||||
if err := mountTmpfs(SourceTmpfsDevtmpfs, target, MS_NOSUID|MS_NODEV, 0, state.ParentPerm); err != nil {
|
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 {
|
func (d *MountDevOp) Is(op Op) bool {
|
||||||
vd, ok := op.(*MountDevOp)
|
vd, ok := op.(*MountDevOp)
|
||||||
return ok && ((d == nil && vd == nil) || (d != nil && vd != nil &&
|
return ok && d.Valid() && vd.Valid() &&
|
||||||
d.Target != nil && vd.Target != nil && d.Target.Is(vd.Target) &&
|
d.Target.Is(vd.Target) &&
|
||||||
d.Mqueue == vd.Mqueue && d.Write == vd.Write))
|
d.Mqueue == vd.Mqueue &&
|
||||||
|
d.Write == vd.Write
|
||||||
}
|
}
|
||||||
func (*MountDevOp) prefix() string { return "mounting" }
|
func (*MountDevOp) prefix() string { return "mounting" }
|
||||||
func (d *MountDevOp) String() string {
|
func (d *MountDevOp) String() string {
|
||||||
|
@ -3,6 +3,12 @@ package container
|
|||||||
import "testing"
|
import "testing"
|
||||||
|
|
||||||
func TestMountDevOp(t *testing.T) {
|
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{
|
checkOpsBuilder(t, []opsBuilderTestCase{
|
||||||
{"dev", new(Ops).Dev(MustAbs("/dev/"), true), Ops{
|
{"dev", new(Ops).Dev(MustAbs("/dev/"), true), Ops{
|
||||||
&MountDevOp{
|
&MountDevOp{
|
||||||
|
@ -4,7 +4,6 @@ import (
|
|||||||
"encoding/gob"
|
"encoding/gob"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"syscall"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() { gob.Register(new(MkdirOp)) }
|
func init() { gob.Register(new(MkdirOp)) }
|
||||||
@ -21,19 +20,17 @@ type MkdirOp struct {
|
|||||||
Perm os.FileMode
|
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) early(*setupState) error { return nil }
|
||||||
func (m *MkdirOp) apply(*setupState) error {
|
func (m *MkdirOp) apply(*setupState) error {
|
||||||
if m.Path == nil {
|
|
||||||
return syscall.EBADE
|
|
||||||
}
|
|
||||||
return wrapErrSelf(os.MkdirAll(toSysroot(m.Path.String()), m.Perm))
|
return wrapErrSelf(os.MkdirAll(toSysroot(m.Path.String()), m.Perm))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MkdirOp) Is(op Op) bool {
|
func (m *MkdirOp) Is(op Op) bool {
|
||||||
vm, ok := op.(*MkdirOp)
|
vm, ok := op.(*MkdirOp)
|
||||||
return ok && ((m == nil && vm == nil) || (m != nil && vm != nil &&
|
return ok && m.Valid() && vm.Valid() &&
|
||||||
m.Path != nil && vm.Path != nil && m.Path.Is(vm.Path) &&
|
m.Path.Is(vm.Path) &&
|
||||||
m.Perm == vm.Perm))
|
m.Perm == vm.Perm
|
||||||
}
|
}
|
||||||
func (*MkdirOp) prefix() string { return "creating" }
|
func (*MkdirOp) prefix() string { return "creating" }
|
||||||
func (m *MkdirOp) String() string { return fmt.Sprintf("directory %q perm %s", m.Path, m.Perm) }
|
func (m *MkdirOp) String() string { return fmt.Sprintf("directory %q perm %s", m.Path, m.Perm) }
|
||||||
|
@ -3,6 +3,12 @@ package container
|
|||||||
import "testing"
|
import "testing"
|
||||||
|
|
||||||
func TestMkdirOp(t *testing.T) {
|
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{
|
checkOpsBuilder(t, []opsBuilderTestCase{
|
||||||
{"etc", new(Ops).Mkdir(MustAbs("/etc/"), 0), Ops{
|
{"etc", new(Ops).Mkdir(MustAbs("/etc/"), 0), Ops{
|
||||||
&MkdirOp{Path: MustAbs("/etc/")},
|
&MkdirOp{Path: MustAbs("/etc/")},
|
||||||
|
@ -53,10 +53,10 @@ type MountOverlayOp struct {
|
|||||||
lower []string
|
lower []string
|
||||||
// The upperdir is normally on a writable filesystem.
|
// 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.
|
// 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
|
Upper *Absolute
|
||||||
// formatted for [OptionOverlayUpperdir], resolved, prefixed and escaped during early
|
// formatted for [OptionOverlayUpperdir], resolved, prefixed and escaped during early
|
||||||
upper string
|
upper string
|
||||||
@ -68,6 +68,19 @@ type MountOverlayOp struct {
|
|||||||
ephemeral bool
|
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 {
|
func (o *MountOverlayOp) early(*setupState) error {
|
||||||
if o.Work == nil && o.Upper != nil {
|
if o.Work == nil && o.Upper != nil {
|
||||||
switch o.Upper.String() {
|
switch o.Upper.String() {
|
||||||
@ -104,11 +117,7 @@ func (o *MountOverlayOp) early(*setupState) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
o.lower = make([]string, len(o.Lower))
|
o.lower = make([]string, len(o.Lower))
|
||||||
for i, a := range o.Lower {
|
for i, a := range o.Lower { // nil checked in Valid
|
||||||
if a == nil {
|
|
||||||
return EBADE
|
|
||||||
}
|
|
||||||
|
|
||||||
if v, err := filepath.EvalSymlinks(a.String()); err != nil {
|
if v, err := filepath.EvalSymlinks(a.String()); err != nil {
|
||||||
return wrapErrSelf(err)
|
return wrapErrSelf(err)
|
||||||
} else {
|
} else {
|
||||||
@ -119,9 +128,6 @@ func (o *MountOverlayOp) early(*setupState) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (o *MountOverlayOp) apply(state *setupState) error {
|
func (o *MountOverlayOp) apply(state *setupState) error {
|
||||||
if o.Target == nil {
|
|
||||||
return EBADE
|
|
||||||
}
|
|
||||||
target := toSysroot(o.Target.String())
|
target := toSysroot(o.Target.String())
|
||||||
if err := os.MkdirAll(target, state.ParentPerm); err != nil {
|
if err := os.MkdirAll(target, state.ParentPerm); err != nil {
|
||||||
return wrapErrSelf(err)
|
return wrapErrSelf(err)
|
||||||
@ -163,10 +169,10 @@ func (o *MountOverlayOp) apply(state *setupState) error {
|
|||||||
|
|
||||||
func (o *MountOverlayOp) Is(op Op) bool {
|
func (o *MountOverlayOp) Is(op Op) bool {
|
||||||
vo, ok := op.(*MountOverlayOp)
|
vo, ok := op.(*MountOverlayOp)
|
||||||
return ok && ((o == nil && vo == nil) || (o != nil && vo != nil &&
|
return ok && o.Valid() && vo.Valid() &&
|
||||||
o.Target != nil && vo.Target != nil && 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 *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 (*MountOverlayOp) prefix() string { return "mounting" }
|
||||||
func (o *MountOverlayOp) String() string {
|
func (o *MountOverlayOp) String() string {
|
||||||
|
@ -3,6 +3,15 @@ package container
|
|||||||
import "testing"
|
import "testing"
|
||||||
|
|
||||||
func TestMountOverlayOp(t *testing.T) {
|
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{
|
checkOpsBuilder(t, []opsBuilderTestCase{
|
||||||
{"full", new(Ops).Overlay(
|
{"full", new(Ops).Overlay(
|
||||||
MustAbs("/nix/store"),
|
MustAbs("/nix/store"),
|
||||||
|
@ -35,12 +35,9 @@ type TmpfileOp struct {
|
|||||||
Data []byte
|
Data []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *TmpfileOp) Valid() bool { return t != nil && t.Path != nil }
|
||||||
func (t *TmpfileOp) early(*setupState) error { return nil }
|
func (t *TmpfileOp) early(*setupState) error { return nil }
|
||||||
func (t *TmpfileOp) apply(state *setupState) error {
|
func (t *TmpfileOp) apply(state *setupState) error {
|
||||||
if t.Path == nil {
|
|
||||||
return EBADE
|
|
||||||
}
|
|
||||||
|
|
||||||
var tmpPath string
|
var tmpPath string
|
||||||
if f, err := os.CreateTemp(FHSRoot, intermediatePatternTmpfile); err != nil {
|
if f, err := os.CreateTemp(FHSRoot, intermediatePatternTmpfile); err != nil {
|
||||||
return wrapErrSelf(err)
|
return wrapErrSelf(err)
|
||||||
@ -72,7 +69,8 @@ func (t *TmpfileOp) apply(state *setupState) error {
|
|||||||
|
|
||||||
func (t *TmpfileOp) Is(op Op) bool {
|
func (t *TmpfileOp) Is(op Op) bool {
|
||||||
vt, ok := op.(*TmpfileOp)
|
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)
|
string(t.Data) == string(vt.Data)
|
||||||
}
|
}
|
||||||
func (*TmpfileOp) prefix() string { return "placing" }
|
func (*TmpfileOp) prefix() string { return "placing" }
|
||||||
|
@ -3,6 +3,12 @@ package container
|
|||||||
import "testing"
|
import "testing"
|
||||||
|
|
||||||
func TestTmpfileOp(t *testing.T) {
|
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{
|
checkOpsBuilder(t, []opsBuilderTestCase{
|
||||||
{"noref", new(Ops).Place(MustAbs("/etc/passwd"), []byte(`chronos:x:65534:65534:Hakurei:/var/empty:/bin/zsh`)), Ops{
|
{"noref", new(Ops).Place(MustAbs("/etc/passwd"), []byte(`chronos:x:65534:65534:Hakurei:/var/empty:/bin/zsh`)), Ops{
|
||||||
&TmpfileOp{
|
&TmpfileOp{
|
||||||
|
@ -16,15 +16,11 @@ func (f *Ops) Proc(target *Absolute) *Ops {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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 {
|
type MountProcOp struct{ Target *Absolute }
|
||||||
Target *Absolute
|
|
||||||
}
|
|
||||||
|
|
||||||
|
func (p *MountProcOp) Valid() bool { return p != nil && p.Target != nil }
|
||||||
func (p *MountProcOp) early(*setupState) error { return nil }
|
func (p *MountProcOp) early(*setupState) error { return nil }
|
||||||
func (p *MountProcOp) apply(state *setupState) error {
|
func (p *MountProcOp) apply(state *setupState) error {
|
||||||
if p.Target == nil {
|
|
||||||
return EBADE
|
|
||||||
}
|
|
||||||
target := toSysroot(p.Target.String())
|
target := toSysroot(p.Target.String())
|
||||||
if err := os.MkdirAll(target, state.ParentPerm); err != nil {
|
if err := os.MkdirAll(target, state.ParentPerm); err != nil {
|
||||||
return wrapErrSelf(err)
|
return wrapErrSelf(err)
|
||||||
@ -35,8 +31,8 @@ func (p *MountProcOp) apply(state *setupState) error {
|
|||||||
|
|
||||||
func (p *MountProcOp) Is(op Op) bool {
|
func (p *MountProcOp) Is(op Op) bool {
|
||||||
vp, ok := op.(*MountProcOp)
|
vp, ok := op.(*MountProcOp)
|
||||||
return ok && ((p == nil && vp == nil) ||
|
return ok && p.Valid() && vp.Valid() &&
|
||||||
(p.Target != nil && vp.Target != nil && p.Target.Is(vp.Target)))
|
p.Target.Is(vp.Target)
|
||||||
}
|
}
|
||||||
func (*MountProcOp) prefix() string { return "mounting" }
|
func (*MountProcOp) prefix() string { return "mounting" }
|
||||||
func (p *MountProcOp) String() string { return fmt.Sprintf("proc on %q", p.Target) }
|
func (p *MountProcOp) String() string { return fmt.Sprintf("proc on %q", p.Target) }
|
||||||
|
@ -3,6 +3,12 @@ package container
|
|||||||
import "testing"
|
import "testing"
|
||||||
|
|
||||||
func TestMountProcOp(t *testing.T) {
|
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{
|
checkOpsBuilder(t, []opsBuilderTestCase{
|
||||||
{"proc", new(Ops).Proc(MustAbs("/proc/")), Ops{
|
{"proc", new(Ops).Proc(MustAbs("/proc/")), Ops{
|
||||||
&MountProcOp{Target: MustAbs("/proc/")},
|
&MountProcOp{Target: MustAbs("/proc/")},
|
||||||
|
@ -3,7 +3,6 @@ package container
|
|||||||
import (
|
import (
|
||||||
"encoding/gob"
|
"encoding/gob"
|
||||||
"fmt"
|
"fmt"
|
||||||
"syscall"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() { gob.Register(new(RemountOp)) }
|
func init() { gob.Register(new(RemountOp)) }
|
||||||
@ -20,20 +19,18 @@ type RemountOp struct {
|
|||||||
Flags uintptr
|
Flags uintptr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *RemountOp) Valid() bool { return r != nil && r.Target != nil }
|
||||||
func (*RemountOp) early(*setupState) error { return nil }
|
func (*RemountOp) early(*setupState) error { return nil }
|
||||||
func (r *RemountOp) apply(*setupState) error {
|
func (r *RemountOp) apply(*setupState) error {
|
||||||
if r.Target == nil {
|
|
||||||
return syscall.EBADE
|
|
||||||
}
|
|
||||||
return wrapErrSuffix(hostProc.remount(toSysroot(r.Target.String()), r.Flags),
|
return wrapErrSuffix(hostProc.remount(toSysroot(r.Target.String()), r.Flags),
|
||||||
fmt.Sprintf("cannot remount %q:", r.Target))
|
fmt.Sprintf("cannot remount %q:", r.Target))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RemountOp) Is(op Op) bool {
|
func (r *RemountOp) Is(op Op) bool {
|
||||||
vr, ok := op.(*RemountOp)
|
vr, ok := op.(*RemountOp)
|
||||||
return ok && ((r == nil && vr == nil) ||
|
return ok && r.Valid() && vr.Valid() &&
|
||||||
(r.Target != nil && vr.Target != nil && r.Target.Is(vr.Target)) &&
|
r.Target.Is(vr.Target) &&
|
||||||
r.Flags == vr.Flags)
|
r.Flags == vr.Flags
|
||||||
}
|
}
|
||||||
func (*RemountOp) prefix() string { return "remounting" }
|
func (*RemountOp) prefix() string { return "remounting" }
|
||||||
func (r *RemountOp) String() string { return fmt.Sprintf("%q flags %#x", r.Target, r.Flags) }
|
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) {
|
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{
|
checkOpsBuilder(t, []opsBuilderTestCase{
|
||||||
{"root", new(Ops).Remount(MustAbs("/"), syscall.MS_RDONLY), Ops{
|
{"root", new(Ops).Remount(MustAbs("/"), syscall.MS_RDONLY), Ops{
|
||||||
&RemountOp{
|
&RemountOp{
|
||||||
|
@ -26,6 +26,8 @@ type SymlinkOp struct {
|
|||||||
Dereference bool
|
Dereference bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l *SymlinkOp) Valid() bool { return l != nil && l.Target != nil && l.LinkName != zeroString }
|
||||||
|
|
||||||
func (l *SymlinkOp) early(*setupState) error {
|
func (l *SymlinkOp) early(*setupState) error {
|
||||||
if l.Dereference {
|
if l.Dereference {
|
||||||
if !isAbs(l.LinkName) {
|
if !isAbs(l.LinkName) {
|
||||||
@ -41,9 +43,6 @@ func (l *SymlinkOp) early(*setupState) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *SymlinkOp) apply(state *setupState) error {
|
func (l *SymlinkOp) apply(state *setupState) error {
|
||||||
if l.Target == nil {
|
|
||||||
return syscall.EBADE
|
|
||||||
}
|
|
||||||
target := toSysroot(l.Target.String())
|
target := toSysroot(l.Target.String())
|
||||||
if err := os.MkdirAll(path.Dir(target), state.ParentPerm); err != nil {
|
if err := os.MkdirAll(path.Dir(target), state.ParentPerm); err != nil {
|
||||||
return wrapErrSelf(err)
|
return wrapErrSelf(err)
|
||||||
@ -56,9 +55,10 @@ func (l *SymlinkOp) apply(state *setupState) error {
|
|||||||
|
|
||||||
func (l *SymlinkOp) Is(op Op) bool {
|
func (l *SymlinkOp) Is(op Op) bool {
|
||||||
vl, ok := op.(*SymlinkOp)
|
vl, ok := op.(*SymlinkOp)
|
||||||
return ok && ((l == nil && vl == nil) ||
|
return ok && l.Valid() && vl.Valid() &&
|
||||||
(l.Target != nil && vl.Target != nil && l.Target.Is(vl.Target)) &&
|
l.Target.Is(vl.Target) &&
|
||||||
l.LinkName == vl.LinkName && l.Dereference == vl.Dereference)
|
l.LinkName == vl.LinkName &&
|
||||||
|
l.Dereference == vl.Dereference
|
||||||
}
|
}
|
||||||
func (*SymlinkOp) prefix() string { return "creating" }
|
func (*SymlinkOp) prefix() string { return "creating" }
|
||||||
func (l *SymlinkOp) String() string {
|
func (l *SymlinkOp) String() string {
|
||||||
|
@ -3,6 +3,14 @@ package container
|
|||||||
import "testing"
|
import "testing"
|
||||||
|
|
||||||
func TestSymlinkOp(t *testing.T) {
|
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{
|
checkOpsBuilder(t, []opsBuilderTestCase{
|
||||||
{"current-system", new(Ops).Link(
|
{"current-system", new(Ops).Link(
|
||||||
MustAbs("/run/current-system"),
|
MustAbs("/run/current-system"),
|
||||||
|
@ -31,11 +31,9 @@ type MountTmpfsOp struct {
|
|||||||
Perm os.FileMode
|
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) early(*setupState) error { return nil }
|
||||||
func (t *MountTmpfsOp) apply(*setupState) error {
|
func (t *MountTmpfsOp) apply(*setupState) error {
|
||||||
if t.Path == nil {
|
|
||||||
return EBADE
|
|
||||||
}
|
|
||||||
if t.Size < 0 || t.Size > math.MaxUint>>1 {
|
if t.Size < 0 || t.Size > math.MaxUint>>1 {
|
||||||
return msg.WrapErr(EBADE, fmt.Sprintf("size %d out of bounds", t.Size))
|
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 {
|
func (t *MountTmpfsOp) Is(op Op) bool {
|
||||||
vt, ok := op.(*MountTmpfsOp)
|
vt, ok := op.(*MountTmpfsOp)
|
||||||
return ok && ((t == nil && vt == nil) ||
|
return ok && t.Valid() && vt.Valid() &&
|
||||||
(t.Path != nil && vt.Path != nil && t.Path.Is(vt.Path)) &&
|
t.FSName == vt.FSName &&
|
||||||
t.FSName == vt.FSName && t.Flags == vt.Flags && t.Size == vt.Size && t.Perm == vt.Perm)
|
t.Path.Is(vt.Path) &&
|
||||||
|
t.Flags == vt.Flags &&
|
||||||
|
t.Size == vt.Size &&
|
||||||
|
t.Perm == vt.Perm
|
||||||
}
|
}
|
||||||
func (*MountTmpfsOp) prefix() string { return "mounting" }
|
func (*MountTmpfsOp) prefix() string { return "mounting" }
|
||||||
func (t *MountTmpfsOp) String() string { return fmt.Sprintf("tmpfs on %q size %d", t.Path, t.Size) }
|
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) {
|
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{
|
checkOpsBuilder(t, []opsBuilderTestCase{
|
||||||
{"runtime", new(Ops).Tmpfs(
|
{"runtime", new(Ops).Tmpfs(
|
||||||
MustAbs("/run/user"),
|
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 {
|
type opsBuilderTestCase struct {
|
||||||
name string
|
name string
|
||||||
ops *Ops
|
ops *Ops
|
||||||
|
Loading…
x
Reference in New Issue
Block a user