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 } type AutoEtcOp struct{ Prefix string }
func (e *AutoEtcOp) early(*Params) error { return nil } func (e *AutoEtcOp) early(*setupState) error { return nil }
func (e *AutoEtcOp) apply(*Params) error { func (e *AutoEtcOp) apply(*setupState) error {
const target = sysrootPath + FHSEtc const target = sysrootPath + FHSEtc
rel := e.hostRel() + "/" rel := e.hostRel() + "/"

View File

@ -28,7 +28,7 @@ type AutoRootOp struct {
resolved []Op resolved []Op
} }
func (r *AutoRootOp) early(params *Params) error { func (r *AutoRootOp) early(state *setupState) error {
if r.Host == nil { if r.Host == nil {
return syscall.EBADE return syscall.EBADE
} }
@ -45,7 +45,7 @@ func (r *AutoRootOp) early(params *Params) error {
Target: AbsFHSRoot.Append(name), Target: AbsFHSRoot.Append(name),
Flags: r.Flags, Flags: r.Flags,
} }
if err = op.early(params); err != nil { if err = op.early(state); err != nil {
return err return err
} }
r.resolved = append(r.resolved, op) 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 { for _, op := range r.resolved {
msg.Verbosef("%s %s", op.prefix(), op) msg.Verbosef("%s %s", op.prefix(), op)
if err := op.apply(params); err != nil { if err := op.apply(state); err != nil {
return err 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) log.Fatalf("cannot make / rslave: %v", err)
} }
state := &setupState{Params: &params.Params}
/* early is called right before pivot_root into intermediate root; /* early is called right before pivot_root into intermediate root;
this step is mostly for gathering information that would otherwise be difficult to obtain 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 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) log.Fatalf("invalid op %d", i)
} }
if err := op.early(&params.Params); err != nil { if err := op.early(state); err != nil {
msg.PrintBaseErr(err, msg.PrintBaseErr(err,
fmt.Sprintf("cannot prepare op %d:", i)) fmt.Sprintf("cannot prepare op %d:", i))
msg.BeforeExit() msg.BeforeExit()
@ -170,7 +172,7 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) {
for i, op := range *params.Ops { for i, op := range *params.Ops {
// ops already checked during early setup // ops already checked during early setup
msg.Verbosef("%s %s", op.prefix(), op) 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, msg.PrintBaseErr(err,
fmt.Sprintf("cannot apply op %d:", i)) fmt.Sprintf("cannot apply op %d:", i))
msg.BeforeExit() msg.BeforeExit()

View File

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