diff options
| author | Ophestra <cat@gensokyo.uk> | 2026-01-08 07:36:01 +0900 |
|---|---|---|
| committer | Ophestra <cat@gensokyo.uk> | 2026-01-08 07:55:09 +0900 |
| commit | 7bd4d7d0e6937c858e5c960931ae81e2fe59195a (patch) | |
| tree | 91167880b2bd34d1425cdad5c95f8b527966c766 /internal/pkg/exec.go | |
| parent | b3c30bcc51bc41feb58bf88c54a0fdd792fcc0c0 (diff) | |
internal/pkg: support explicit overlay mount
This removes all but the /work/ auto overlay behaviour and enables much greater flexibility. This also renames ExecContainerPath to ExecPath so it is easier to type.
Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'internal/pkg/exec.go')
| -rw-r--r-- | internal/pkg/exec.go | 145 |
1 files changed, 69 insertions, 76 deletions
diff --git a/internal/pkg/exec.go b/internal/pkg/exec.go index de29ff5d..c12c9097 100644 --- a/internal/pkg/exec.go +++ b/internal/pkg/exec.go @@ -20,17 +20,27 @@ import ( // AbsWork is the container pathname [CureContext.GetWorkDir] is mounted on. var AbsWork = fhs.AbsRoot.Append("work/") -// ExecContainerPath is an [Artifact] and the [check.Absolute] pathname to make -// it available under in the container. -type ExecContainerPath struct { +// ExecPath is a slice of [Artifact] and the [check.Absolute] pathname to make +// it available at under in the container. +type ExecPath struct { + // Pathname in the container mount namespace. P *check.Absolute - A Artifact + // Artifacts to mount on the pathname, must contain at least one [Artifact]. + // If there are multiple entries or W is true, P is set up as an overlay + // mount, and entries of A must not implement [File]. + A []Artifact + // Whether to make the mount point writable via an invisible tmpfs upperdir. + W bool } -// MustPath returns [ExecContainerPath] for pathname and [Artifact] and panics -// if pathname is not absolute. -func MustPath(pathname string, a Artifact) ExecContainerPath { - return ExecContainerPath{check.MustAbs(pathname), a} +// Path returns a populated [ExecPath]. +func Path(pathname *check.Absolute, writable bool, a ...Artifact) ExecPath { + return ExecPath{pathname, a, writable} +} + +// MustPath is like [Path], but takes a string pathname via [check.MustAbs]. +func MustPath(pathname string, writable bool, a ...Artifact) ExecPath { + return ExecPath{check.MustAbs(pathname), a, writable} } // An execArtifact is an [Artifact] that produces output by running a program @@ -41,8 +51,8 @@ func MustPath(pathname string, a Artifact) ExecContainerPath { type execArtifact struct { // Caller-supplied context. ctx context.Context - // Caller-supplied inner read-only bind mounts. - paths []ExecContainerPath + // Caller-supplied inner mount points. + paths []ExecPath // Caller-supplied logging facility, passed through to [container] and used // internally to produce verbose output. msg message.Msg @@ -95,20 +105,11 @@ func (a *execNetArtifact) Cure(c *CureContext) error { // // The working and temporary directories are both created and mounted writable // on [AbsWork] and [fhs.AbsTmp] respectively. If one or more paths target -// [fhs.AbsTmp], the final entry is set up as a writable overlay mount on /tmp -// backed by the host side temporary directory. If one or more paths target // [AbsWork], the final entry is set up as a writable overlay mount on /work for // which the upperdir is the host side work directory. In this configuration, -// the program must avoid causing whiteout files to be created, cure fails if -// upperdir ends up with anything other than directory, regular or symlink -// entries. -// -// If the first path targets [fhs.AbsRoot], it is made writable via an overlay -// mount with writes going to an ephemeral tmpfs bound to the lifetime of the -// container. This is primarily to make it possible for [container] to set up -// mount points targeting paths not available in the [Artifact] backing root, -// and to accommodate poorly written programs that insist on writing to awkward -// paths, it must not be used as scratch space. +// the W field is ignored, and the program must avoid causing whiteout files to +// be created. Cure fails if upperdir ends up with entries other than directory, +// regular or symlink. // // If checksum is non-nil, the resulting [Artifact] implements [KnownChecksum] // and its container runs in the host net namespace. @@ -126,7 +127,7 @@ func NewExec( path *check.Absolute, args []string, - paths ...ExecContainerPath, + paths ...ExecPath, ) Artifact { a := execArtifact{ctx, paths, msg, cures, dir, env, path, args} if checksum == nil { @@ -142,17 +143,22 @@ func (a *execArtifact) Kind() Kind { return KindExec } func (a *execArtifact) Params() []byte { var buf bytes.Buffer for _, p := range a.paths { + if p.W { + buf.WriteByte(1) + } else { + buf.WriteByte(0) + } if p.P != nil { buf.WriteString(p.P.String()) } else { buf.WriteString("invalid P\x00") } - if p.A != nil { - id := Ident(p.A) + buf.WriteByte(0) + for _, d := range p.A { + id := Ident(d) buf.Write(id[:]) - } else { - buf.WriteString("invalid A\x00") } + buf.WriteByte(0) } buf.WriteByte(0) buf.WriteString(a.dir.String()) @@ -170,15 +176,13 @@ func (a *execArtifact) Params() []byte { } // Dependencies returns a slice of all artifacts collected from caller-supplied -// [ExecContainerPath]. +// [ExecPath]. func (a *execArtifact) Dependencies() []Artifact { - artifacts := make([]Artifact, 0, len(a.paths)) + artifacts := make([][]Artifact, 0, len(a.paths)) for _, p := range a.paths { - if p.A != nil { - artifacts = append(artifacts, p.A) - } + artifacts = append(artifacts, p.A) } - return artifacts + return slices.Concat(artifacts...) } // Cure cures the [Artifact] by curing all its dependencies then running the @@ -200,35 +204,42 @@ func (a *execArtifact) cure(c *CureContext, hostNet bool) (err error) { cures = runtime.NumCPU() } - overlayTempIndex, overlayWorkIndex := -1, -1 - paths := make([][2]*check.Absolute, len(a.paths)) + overlayWorkIndex := -1 + type curePath struct { + // Copied from ExecPath.P. + dst *check.Absolute + // Cured from ExecPath.A. + src []*check.Absolute + } + paths := make([]curePath, len(a.paths)) for i, p := range a.paths { - if p.P == nil || p.A == nil { + if p.P == nil || len(p.A) == 0 { return os.ErrInvalid } - if p.P.Is(fhs.AbsTmp) { - overlayTempIndex = i - } else if p.P.Is(AbsWork) { + if p.P.Is(AbsWork) { overlayWorkIndex = i } - paths[i][1] = p.P + paths[i].dst = p.P + paths[i].src = make([]*check.Absolute, len(p.A)) } if len(paths) > 0 { type cureArtifact struct { // Index of pending Artifact in paths. - index int + index [2]int // Pending artifact. a Artifact } ac := make(chan cureArtifact, len(paths)) for i, p := range a.paths { - ac <- cureArtifact{i, p.A} + for j, d := range p.A { + ac <- cureArtifact{[2]int{i, j}, d} + } } type cureRes struct { // Index of result in paths. - index int + index [2]int // Cured pathname. pathname *check.Absolute // Error returned by c. @@ -260,7 +271,7 @@ func (a *execArtifact) cure(c *CureContext, hostNet bool) (err error) { if cr.err != nil { errs = append(errs, cr.err) } else { - paths[cr.index][0] = cr.pathname + paths[cr.index[0]].src[cr.index[1]] = cr.pathname } if count == len(paths) { @@ -293,32 +304,9 @@ func (a *execArtifact) cure(c *CureContext, hostNet bool) (err error) { z.Dir, z.Env, z.Path, z.Args = a.dir, a.env, a.path, a.args z.Grow(len(paths) + 4) - if len(paths) > 0 && paths[0][1].Is(fhs.AbsRoot) { - z.OverlayEphemeral(fhs.AbsRoot, paths[0][0]) - paths = paths[1:] - overlayTempIndex-- - overlayWorkIndex-- - } temp, work := c.GetTempDir(), c.GetWorkDir() for i, b := range paths { - if i == overlayTempIndex { - tempUpper := temp.Append("upper") - if err = os.MkdirAll(tempUpper.String(), 0700); err != nil { - return - } - tempWork := temp.Append("work") - if err = os.MkdirAll(tempWork.String(), 0700); err != nil { - return - } - z.Overlay( - fhs.AbsTmp, - tempUpper, - tempWork, - b[0], - ) - continue - } if i == overlayWorkIndex { if err = os.MkdirAll(work.String(), 0700); err != nil { return @@ -331,11 +319,18 @@ func (a *execArtifact) cure(c *CureContext, hostNet bool) (err error) { AbsWork, work, tempWork, - b[0], + b.src..., ) continue } - z.Bind(b[0], b[1], 0) + + if a.paths[i].W { + z.OverlayEphemeral(b.dst, b.src...) + } else if len(b.src) == 1 { + z.Bind(b.src[0], b.dst, 0) + } else { + z.OverlayReadonly(b.dst, b.src...) + } } if overlayWorkIndex < 0 { z.Bind( @@ -344,13 +339,11 @@ func (a *execArtifact) cure(c *CureContext, hostNet bool) (err error) { std.BindWritable|std.BindEnsure, ) } - if overlayTempIndex < 0 { - z.Bind( - c.GetTempDir(), - fhs.AbsTmp, - std.BindWritable|std.BindEnsure, - ) - } + z.Bind( + c.GetTempDir(), + fhs.AbsTmp, + std.BindWritable|std.BindEnsure, + ) z.Proc(fhs.AbsProc).Dev(fhs.AbsDev, true) if err = z.Start(); err != nil { |
