container: allow additional state between ops
All checks were successful
Test / Create distribution (push) Successful in 33s
Test / Sandbox (push) Successful in 2m12s
Test / Hakurei (push) Successful in 3m15s
Test / Hpkg (push) Successful in 4m8s
Test / Sandbox (race detector) (push) Successful in 4m21s
Test / Hakurei (race detector) (push) Successful in 5m8s
Test / Flake checks (push) Successful in 1m26s

This is useful for ops that need to be aware of previous instances of themselves.

Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
Ophestra 2025-08-17 01:29:54 +09:00
parent f9edec7e41
commit 8aa65f28c6
Signed by: cat
SSH Key Fingerprint: SHA256:gQ67O0enBZ7UdZypgtspB2FDM1g3GVw8nX0XSdcFw8Q
4 changed files with 44 additions and 38 deletions

View File

@ -20,8 +20,8 @@ func (f *Ops) Etc(host *Absolute, prefix string) *Ops {
type AutoEtcOp struct{ Prefix string }
func (e *AutoEtcOp) early(*Params) error { return nil }
func (e *AutoEtcOp) apply(*Params) error {
func (e *AutoEtcOp) early(*setupState) error { return nil }
func (e *AutoEtcOp) apply(*setupState) error {
const target = sysrootPath + FHSEtc
rel := e.hostRel() + "/"

View File

@ -28,7 +28,7 @@ type AutoRootOp struct {
resolved []Op
}
func (r *AutoRootOp) early(params *Params) error {
func (r *AutoRootOp) early(state *setupState) error {
if r.Host == nil {
return syscall.EBADE
}
@ -45,7 +45,7 @@ func (r *AutoRootOp) early(params *Params) error {
Target: AbsFHSRoot.Append(name),
Flags: r.Flags,
}
if err = op.early(params); err != nil {
if err = op.early(state); err != nil {
return err
}
r.resolved = append(r.resolved, op)
@ -55,10 +55,10 @@ func (r *AutoRootOp) early(params *Params) error {
}
}
func (r *AutoRootOp) apply(params *Params) error {
func (r *AutoRootOp) apply(state *setupState) error {
for _, op := range r.resolved {
msg.Verbosef("%s %s", op.prefix(), op)
if err := op.apply(params); err != nil {
if err := op.apply(state); err != nil {
return err
}
}

View File

@ -121,6 +121,8 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) {
log.Fatalf("cannot make / rslave: %v", err)
}
state := &setupState{Params: &params.Params}
/* early is called right before pivot_root into intermediate root;
this step is mostly for gathering information that would otherwise be difficult to obtain
via library functions after pivot_root, and implementations are expected to avoid changing
@ -130,7 +132,7 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) {
log.Fatalf("invalid op %d", i)
}
if err := op.early(&params.Params); err != nil {
if err := op.early(state); err != nil {
msg.PrintBaseErr(err,
fmt.Sprintf("cannot prepare op %d:", i))
msg.BeforeExit()
@ -170,7 +172,7 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) {
for i, op := range *params.Ops {
// ops already checked during early setup
msg.Verbosef("%s %s", op.prefix(), op)
if err := op.apply(&params.Params); err != nil {
if err := op.apply(state); err != nil {
msg.PrintBaseErr(err,
fmt.Sprintf("cannot apply op %d:", i))
msg.BeforeExit()

View File

@ -31,14 +31,18 @@ type (
// Implementations of this interface are sent as a stream of gobs.
Op interface {
// early is called in host root.
early(params *Params) error
early(state *setupState) error
// apply is called in intermediate root.
apply(params *Params) error
apply(state *setupState) error
prefix() string
Is(op Op) bool
fmt.Stringer
}
setupState struct {
*Params
}
)
// Grow grows the slice Ops points to using [slices.Grow].
@ -57,8 +61,8 @@ type RemountOp struct {
Flags uintptr
}
func (*RemountOp) early(*Params) error { return nil }
func (r *RemountOp) apply(*Params) error {
func (*RemountOp) early(*setupState) error { return nil }
func (r *RemountOp) apply(*setupState) error {
if r.Target == nil {
return EBADE
}
@ -93,7 +97,7 @@ const (
BindDevice
)
func (b *BindMountOp) early(*Params) error {
func (b *BindMountOp) early(*setupState) error {
if b.Source == nil || b.Target == nil {
return EBADE
}
@ -110,7 +114,7 @@ func (b *BindMountOp) early(*Params) error {
}
}
func (b *BindMountOp) apply(*Params) error {
func (b *BindMountOp) apply(*setupState) error {
if b.sourceFinal == nil {
if b.Flags&BindOptional == 0 {
// unreachable
@ -166,13 +170,13 @@ type MountProcOp struct {
Target *Absolute
}
func (p *MountProcOp) early(*Params) error { return nil }
func (p *MountProcOp) apply(params *Params) error {
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, params.ParentPerm); err != nil {
if err := os.MkdirAll(target, state.ParentPerm); err != nil {
return wrapErrSelf(err)
}
return wrapErrSuffix(Mount(SourceProc, target, FstypeProc, MS_NOSUID|MS_NOEXEC|MS_NODEV, zeroString),
@ -207,20 +211,20 @@ type MountDevOp struct {
Write bool
}
func (d *MountDevOp) early(*Params) error { return nil }
func (d *MountDevOp) apply(params *Params) error {
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, params.ParentPerm); err != nil {
if err := mountTmpfs(SourceTmpfsDevtmpfs, target, MS_NOSUID|MS_NODEV, 0, state.ParentPerm); err != nil {
return err
}
for _, name := range []string{"null", "zero", "full", "random", "urandom", "tty"} {
targetPath := path.Join(target, name)
if err := ensureFile(targetPath, 0444, params.ParentPerm); err != nil {
if err := ensureFile(targetPath, 0444, state.ParentPerm); err != nil {
return err
}
if err := hostProc.bindMount(
@ -252,7 +256,7 @@ func (d *MountDevOp) apply(params *Params) error {
devPtsPath := path.Join(target, "pts")
for _, name := range []string{path.Join(target, "shm"), devPtsPath} {
if err := os.Mkdir(name, params.ParentPerm); err != nil {
if err := os.Mkdir(name, state.ParentPerm); err != nil {
return wrapErrSelf(err)
}
}
@ -263,11 +267,11 @@ func (d *MountDevOp) apply(params *Params) error {
fmt.Sprintf("cannot mount devpts on %q:", devPtsPath))
}
if params.RetainSession {
if state.RetainSession {
var buf [8]byte
if _, _, errno := Syscall(SYS_IOCTL, 1, TIOCGWINSZ, uintptr(unsafe.Pointer(&buf[0]))); errno == 0 {
consolePath := path.Join(target, "console")
if err := ensureFile(consolePath, 0444, params.ParentPerm); err != nil {
if err := ensureFile(consolePath, 0444, state.ParentPerm); err != nil {
return err
}
if name, err := os.Readlink(hostProc.stdout()); err != nil {
@ -285,7 +289,7 @@ func (d *MountDevOp) apply(params *Params) error {
if d.Mqueue {
mqueueTarget := path.Join(target, "mqueue")
if err := os.Mkdir(mqueueTarget, params.ParentPerm); err != nil {
if err := os.Mkdir(mqueueTarget, state.ParentPerm); err != nil {
return wrapErrSelf(err)
}
if err := Mount(SourceMqueue, mqueueTarget, FstypeMqueue, MS_NOSUID|MS_NOEXEC|MS_NODEV, zeroString); err != nil {
@ -331,8 +335,8 @@ type MountTmpfsOp struct {
Perm os.FileMode
}
func (t *MountTmpfsOp) early(*Params) error { return nil }
func (t *MountTmpfsOp) apply(*Params) error {
func (t *MountTmpfsOp) early(*setupState) error { return nil }
func (t *MountTmpfsOp) apply(*setupState) error {
if t.Path == nil {
return EBADE
}
@ -394,7 +398,7 @@ type MountOverlayOp struct {
ephemeral bool
}
func (o *MountOverlayOp) early(*Params) error {
func (o *MountOverlayOp) early(*setupState) error {
if o.Work == nil && o.Upper != nil {
switch o.Upper.String() {
case FHSRoot: // ephemeral
@ -444,12 +448,12 @@ func (o *MountOverlayOp) early(*Params) error {
return nil
}
func (o *MountOverlayOp) apply(params *Params) error {
func (o *MountOverlayOp) apply(state *setupState) error {
if o.Target == nil {
return EBADE
}
target := toSysroot(o.Target.String())
if err := os.MkdirAll(target, params.ParentPerm); err != nil {
if err := os.MkdirAll(target, state.ParentPerm); err != nil {
return wrapErrSelf(err)
}
@ -517,7 +521,7 @@ type SymlinkOp struct {
Dereference bool
}
func (l *SymlinkOp) early(*Params) error {
func (l *SymlinkOp) early(*setupState) error {
if l.Dereference {
if !isAbs(l.LinkName) {
return msg.WrapErr(EBADE, fmt.Sprintf("path %q is not absolute", l.LinkName))
@ -531,12 +535,12 @@ func (l *SymlinkOp) early(*Params) error {
return nil
}
func (l *SymlinkOp) apply(params *Params) error {
func (l *SymlinkOp) apply(state *setupState) error {
if l.Target == nil {
return EBADE
}
target := toSysroot(l.Target.String())
if err := os.MkdirAll(path.Dir(target), params.ParentPerm); err != nil {
if err := os.MkdirAll(path.Dir(target), state.ParentPerm); err != nil {
return wrapErrSelf(err)
}
if err := os.Symlink(l.LinkName, target); err != nil {
@ -564,8 +568,8 @@ type MkdirOp struct {
Perm os.FileMode
}
func (m *MkdirOp) early(*Params) error { return nil }
func (m *MkdirOp) apply(*Params) error {
func (m *MkdirOp) early(*setupState) error { return nil }
func (m *MkdirOp) apply(*setupState) error {
if m.Path == nil {
return EBADE
}
@ -598,8 +602,8 @@ type TmpfileOp struct {
Data []byte
}
func (t *TmpfileOp) early(*Params) error { return nil }
func (t *TmpfileOp) apply(params *Params) error {
func (t *TmpfileOp) early(*setupState) error { return nil }
func (t *TmpfileOp) apply(state *setupState) error {
if t.Path == nil {
return EBADE
}
@ -618,7 +622,7 @@ func (t *TmpfileOp) apply(params *Params) error {
}
target := toSysroot(t.Path.String())
if err := ensureFile(target, 0444, params.ParentPerm); err != nil {
if err := ensureFile(target, 0444, state.ParentPerm); err != nil {
return err
} else if err = hostProc.bindMount(
tmpPath,