aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal/pkg/exec.go
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2026-01-08 07:36:01 +0900
committerOphestra <cat@gensokyo.uk>2026-01-08 07:55:09 +0900
commit7bd4d7d0e6937c858e5c960931ae81e2fe59195a (patch)
tree91167880b2bd34d1425cdad5c95f8b527966c766 /internal/pkg/exec.go
parentb3c30bcc51bc41feb58bf88c54a0fdd792fcc0c0 (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.go145
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 {