Files
hakurei/container/autoroot.go
T
cat 1c3761bb53
Test / Create distribution (push) Successful in 59s
Test / Sandbox (push) Successful in 3m0s
Test / Hakurei (push) Successful in 4m48s
Test / Sandbox (race detector) (push) Successful in 6m6s
Test / Hakurei (race detector) (push) Successful in 7m32s
Test / ShareFS (push) Successful in 7m54s
Test / Flake checks (push) Successful in 1m19s
container: enter init path early
There is generally no use case where any setup is required before init, and requiring the explicit function call is error-prone and unnecessary. It also causes trouble with packages using a similar trick. This change moves argv0 check early and makes it an import side effect. The stub will be removed in v0.5.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2026-08-25 14:53:29 +09:00

98 lines
2.5 KiB
Go

package container
import (
"fmt"
"hakurei.app/check"
"hakurei.app/fhs"
"hakurei.app/message"
)
// Root is a helper for appending [AutoRootOp] to [Ops].
func (f *Ops) Root(host *check.Absolute, flags int) *Ops {
*f = append(*f, &AutoRootOp{host, flags, nil})
return f
}
// AutoRootOp 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.
type AutoRootOp struct {
Host *check.Absolute
// passed through to bindMount
Flags int
// obtained during early;
// these wrap the underlying Op because BindMountOp is relatively complex,
// so duplicating that code would be unwise
resolved []*BindMountOp
}
func (r *AutoRootOp) Valid() bool { return r != nil && r.Host != nil }
func (r *AutoRootOp) early(state *setupState, k syscallDispatcher) error {
if d, err := k.readdir(r.Host.String()); err != nil {
return err
} else {
r.resolved = make([]*BindMountOp, 0, len(d))
for _, ent := range d {
name := ent.Name()
if IsAutoRootBindable(state, name) {
// careful: the Valid method is skipped, make sure this is always valid
op := &BindMountOp{
Source: r.Host.Append(name),
Target: fhs.AbsRoot.Append(name),
Flags: r.Flags,
}
if err = op.early(state, k); err != nil {
return err
}
r.resolved = append(r.resolved, op)
}
}
return nil
}
}
func (r *AutoRootOp) apply(state *setupState, k syscallDispatcher) error {
if state.nonrepeatable&nrAutoRoot != 0 {
return OpRepeatError("autoroot")
}
state.nonrepeatable |= nrAutoRoot
for _, op := range r.resolved {
// these are exclusively BindMountOp, do not attempt to print identifying message
if err := op.apply(state, k); err != nil {
return err
}
}
return nil
}
func (r *AutoRootOp) late(*setupState, syscallDispatcher) error { return nil }
func (r *AutoRootOp) Is(op Op) bool {
vr, ok := op.(*AutoRootOp)
return ok && r.Valid() && vr.Valid() &&
r.Host.Is(vr.Host) &&
r.Flags == vr.Flags
}
func (*AutoRootOp) prefix() (string, bool) { return "setting up", true }
func (r *AutoRootOp) String() string {
return fmt.Sprintf("auto root %q flags %#x", r.Host, r.Flags)
}
// IsAutoRootBindable returns whether a dir entry name is selected for AutoRoot.
func IsAutoRootBindable(msg message.Msg, name string) bool {
switch name {
case "proc", "dev", "tmp", "mnt", "etc":
case "": // guard against accidentally binding /
// should be unreachable
msg.Verbose("got unexpected root entry")
default:
return true
}
return false
}