diff --git a/internal/pkg/dir_test.go b/internal/pkg/dir_test.go index 4047261..5b098b3 100644 --- a/internal/pkg/dir_test.go +++ b/internal/pkg/dir_test.go @@ -295,9 +295,9 @@ func TestFlatten(t *testing.T) { "checksum/OLBgp1GsljhM2TJ-sbHjaiH9txEUvgdDTAzHv2P24donTt6_529l-9Ua0vFImLlb": {Mode: 0400, Data: []byte{}}, "identifier": {Mode: fs.ModeDir | 0700}, + "identifier/U2cbgVgEtjfRuvHfE1cQnZ3t8yoexULQyo_VLgvxAVJSsobMcNaFIsuDWtmt7kzK": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9")}, "identifier/_gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/OLBgp1GsljhM2TJ-sbHjaiH9txEUvgdDTAzHv2P24donTt6_529l-9Ua0vFImLlb")}, "identifier/nfeISfLeFDr1k-g3hpE1oZ440kTqDdfF8TDpoLdbTPqaMMIl95oiqcvqjRkMjubA": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")}, - "identifier/oHuqV7p0v1Vd8IdAzjyYM8sfCS0P2LR5tfv5cb6Gbf2ZWUm8Ec-7hYPJ_qr183m7": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9")}, "temp": {Mode: fs.ModeDir | 0700}, "work": {Mode: fs.ModeDir | 0700}, @@ -311,13 +311,13 @@ func TestFlatten(t *testing.T) { {Mode: 0400, Path: "checksum/OLBgp1GsljhM2TJ-sbHjaiH9txEUvgdDTAzHv2P24donTt6_529l-9Ua0vFImLlb", Data: []byte{}}, {Mode: fs.ModeDir | 0700, Path: "identifier"}, + {Mode: fs.ModeSymlink | 0777, Path: "identifier/U2cbgVgEtjfRuvHfE1cQnZ3t8yoexULQyo_VLgvxAVJSsobMcNaFIsuDWtmt7kzK", Data: []byte("../checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9")}, {Mode: fs.ModeSymlink | 0777, Path: "identifier/_gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", Data: []byte("../checksum/OLBgp1GsljhM2TJ-sbHjaiH9txEUvgdDTAzHv2P24donTt6_529l-9Ua0vFImLlb")}, {Mode: fs.ModeSymlink | 0777, Path: "identifier/nfeISfLeFDr1k-g3hpE1oZ440kTqDdfF8TDpoLdbTPqaMMIl95oiqcvqjRkMjubA", Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")}, - {Mode: fs.ModeSymlink | 0777, Path: "identifier/oHuqV7p0v1Vd8IdAzjyYM8sfCS0P2LR5tfv5cb6Gbf2ZWUm8Ec-7hYPJ_qr183m7", Data: []byte("../checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9")}, {Mode: fs.ModeDir | 0700, Path: "temp"}, {Mode: fs.ModeDir | 0700, Path: "work"}, - }, pkg.MustDecode("7PoPpWLjFPXIymbuIYLZAzOpCYr-2PN4CZ11jFdO-mDlnZNgFO3JyOtK8HW8Jxvm"), nil}, + }, pkg.MustDecode("UiV6kMz7KrTsc_yphiyQzFLqjRanHxUOwrBMtkKuWo4mOO6WgPFAcoUEeSp7eVIW"), nil}, {"testtool net", fstest.MapFS{ ".": {Mode: fs.ModeDir | 0500}, @@ -340,7 +340,7 @@ func TestFlatten(t *testing.T) { "identifier": {Mode: fs.ModeDir | 0700}, "identifier/_gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/OLBgp1GsljhM2TJ-sbHjaiH9txEUvgdDTAzHv2P24donTt6_529l-9Ua0vFImLlb")}, - "identifier/I3T53NtN6HPAyyodHtq2B0clcsoS1nPdvCEb-Zc5K-hoqFGL2od1mftHhwG7gX1S": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/a1F_i9PVQI4qMcoHgTQkORuyWLkC1GLIxOhDt2JpU1NGAxWc5VJzdlfRK-PYBh3W")}, + "identifier/QdsJhGgnk5N2xdUNGcndXQxFKifxf1V_2t9X8CQ-pDcg24x6mGJC_BiLfGbs6Qml": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/a1F_i9PVQI4qMcoHgTQkORuyWLkC1GLIxOhDt2JpU1NGAxWc5VJzdlfRK-PYBh3W")}, "identifier/nfeISfLeFDr1k-g3hpE1oZ440kTqDdfF8TDpoLdbTPqaMMIl95oiqcvqjRkMjubA": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")}, "temp": {Mode: fs.ModeDir | 0700}, @@ -355,13 +355,13 @@ func TestFlatten(t *testing.T) { {Mode: 0400, Path: "checksum/a1F_i9PVQI4qMcoHgTQkORuyWLkC1GLIxOhDt2JpU1NGAxWc5VJzdlfRK-PYBh3W/check", Data: []byte("net")}, {Mode: fs.ModeDir | 0700, Path: "identifier"}, - {Mode: fs.ModeSymlink | 0777, Path: "identifier/I3T53NtN6HPAyyodHtq2B0clcsoS1nPdvCEb-Zc5K-hoqFGL2od1mftHhwG7gX1S", Data: []byte("../checksum/a1F_i9PVQI4qMcoHgTQkORuyWLkC1GLIxOhDt2JpU1NGAxWc5VJzdlfRK-PYBh3W")}, + {Mode: fs.ModeSymlink | 0777, Path: "identifier/QdsJhGgnk5N2xdUNGcndXQxFKifxf1V_2t9X8CQ-pDcg24x6mGJC_BiLfGbs6Qml", Data: []byte("../checksum/a1F_i9PVQI4qMcoHgTQkORuyWLkC1GLIxOhDt2JpU1NGAxWc5VJzdlfRK-PYBh3W")}, {Mode: fs.ModeSymlink | 0777, Path: "identifier/_gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", Data: []byte("../checksum/OLBgp1GsljhM2TJ-sbHjaiH9txEUvgdDTAzHv2P24donTt6_529l-9Ua0vFImLlb")}, {Mode: fs.ModeSymlink | 0777, Path: "identifier/nfeISfLeFDr1k-g3hpE1oZ440kTqDdfF8TDpoLdbTPqaMMIl95oiqcvqjRkMjubA", Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")}, {Mode: fs.ModeDir | 0700, Path: "temp"}, {Mode: fs.ModeDir | 0700, Path: "work"}, - }, pkg.MustDecode("bBQVFIt0FnOulljgpLnGtuzHSFgwiCMjc4pmc4rHRqXKQ60Q5aBVYp5f6aH9VdZi"), nil}, + }, pkg.MustDecode("ek4K-0d4iRSArkY2TCs3WK34DbiYeOmhE_4vsJTSu_6roY4ZF3YG6eKRooal-i1o"), nil}, {"sample exec container overlay root", fstest.MapFS{ ".": {Mode: fs.ModeDir | 0700}, @@ -372,7 +372,7 @@ func TestFlatten(t *testing.T) { "checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU": {Mode: fs.ModeDir | 0500}, "identifier": {Mode: fs.ModeDir | 0700}, - "identifier/cIjP14zs5el6W_BQhufL_c0vWg-V6Z6pDpsbEa3sYtZ1381u1bKnH3N16RIrw-1S": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9")}, + "identifier/5ey2wpmMpj483YYa7ZZQciYLA2cx3_l167JCqWW4Pd-5DVp81dj9EsBtVTwYptF6": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9")}, "identifier/nfeISfLeFDr1k-g3hpE1oZ440kTqDdfF8TDpoLdbTPqaMMIl95oiqcvqjRkMjubA": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")}, "temp": {Mode: fs.ModeDir | 0700}, @@ -386,42 +386,12 @@ func TestFlatten(t *testing.T) { {Mode: fs.ModeDir | 0500, Path: "checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU"}, {Mode: fs.ModeDir | 0700, Path: "identifier"}, - {Mode: fs.ModeSymlink | 0777, Path: "identifier/cIjP14zs5el6W_BQhufL_c0vWg-V6Z6pDpsbEa3sYtZ1381u1bKnH3N16RIrw-1S", Data: []byte("../checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9")}, + {Mode: fs.ModeSymlink | 0777, Path: "identifier/5ey2wpmMpj483YYa7ZZQciYLA2cx3_l167JCqWW4Pd-5DVp81dj9EsBtVTwYptF6", Data: []byte("../checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9")}, {Mode: fs.ModeSymlink | 0777, Path: "identifier/nfeISfLeFDr1k-g3hpE1oZ440kTqDdfF8TDpoLdbTPqaMMIl95oiqcvqjRkMjubA", Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")}, {Mode: fs.ModeDir | 0700, Path: "temp"}, {Mode: fs.ModeDir | 0700, Path: "work"}, - }, pkg.MustDecode("gFT9kprYBqEJKifJIl2sHn_3TgULWVLTU4DrYAHiGcRmcdFRZ0YtjiROW820cAEc"), nil}, - - {"sample exec container overlay temp", fstest.MapFS{ - ".": {Mode: fs.ModeDir | 0700}, - - "checksum": {Mode: fs.ModeDir | 0700}, - "checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9": {Mode: fs.ModeDir | 0500}, - "checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9/check": {Mode: 0400, Data: []byte{0}}, - "checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU": {Mode: fs.ModeDir | 0500}, - - "identifier": {Mode: fs.ModeDir | 0700}, - "identifier/3ELJ8l42g6XeIoLGR9LheVhMIwSIleD6VrhsliBuon5DAdBOwFSMqd7aiUI4fll7": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9")}, - "identifier/nfeISfLeFDr1k-g3hpE1oZ440kTqDdfF8TDpoLdbTPqaMMIl95oiqcvqjRkMjubA": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")}, - - "temp": {Mode: fs.ModeDir | 0700}, - "work": {Mode: fs.ModeDir | 0700}, - }, []pkg.FlatEntry{ - {Mode: fs.ModeDir | 0700, Path: "."}, - - {Mode: fs.ModeDir | 0700, Path: "checksum"}, - {Mode: fs.ModeDir | 0500, Path: "checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9"}, - {Mode: 0400, Path: "checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9/check", Data: []byte{0}}, - {Mode: fs.ModeDir | 0500, Path: "checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU"}, - - {Mode: fs.ModeDir | 0700, Path: "identifier"}, - {Mode: fs.ModeSymlink | 0777, Path: "identifier/3ELJ8l42g6XeIoLGR9LheVhMIwSIleD6VrhsliBuon5DAdBOwFSMqd7aiUI4fll7", Data: []byte("../checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9")}, - {Mode: fs.ModeSymlink | 0777, Path: "identifier/nfeISfLeFDr1k-g3hpE1oZ440kTqDdfF8TDpoLdbTPqaMMIl95oiqcvqjRkMjubA", Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")}, - - {Mode: fs.ModeDir | 0700, Path: "temp"}, - {Mode: fs.ModeDir | 0700, Path: "work"}, - }, pkg.MustDecode("_r1IBeMWCkLwQ9Im9w0tV9_CWIOfQlXkkP2CogPHLmZp_AB6W3_8HVZqDV00dNAm"), nil}, + }, pkg.MustDecode("VIqqpf0ip9jcyw63i6E8lCMGUcLivQBe4Bevt3WusNac-1MSy5bzB647qGUBzl-W"), nil}, {"sample exec container overlay work", fstest.MapFS{ ".": {Mode: fs.ModeDir | 0700}, @@ -432,7 +402,7 @@ func TestFlatten(t *testing.T) { "checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU": {Mode: fs.ModeDir | 0500}, "identifier": {Mode: fs.ModeDir | 0700}, - "identifier/VMCurZKCA_MV80zb-ZBWVytfl3rhYOKJeo2u9l-OuaytQ_w_r4EsqgJ2nfO93x5_": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9")}, + "identifier/5ynpgDp5mcAmbFndOESXjQnOy_RG6furIxDpQz6c0bImBBLMAI8FouNbAB0uNs6f": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9")}, "identifier/nfeISfLeFDr1k-g3hpE1oZ440kTqDdfF8TDpoLdbTPqaMMIl95oiqcvqjRkMjubA": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")}, "temp": {Mode: fs.ModeDir | 0700}, @@ -446,12 +416,12 @@ func TestFlatten(t *testing.T) { {Mode: fs.ModeDir | 0500, Path: "checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU"}, {Mode: fs.ModeDir | 0700, Path: "identifier"}, - {Mode: fs.ModeSymlink | 0777, Path: "identifier/VMCurZKCA_MV80zb-ZBWVytfl3rhYOKJeo2u9l-OuaytQ_w_r4EsqgJ2nfO93x5_", Data: []byte("../checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9")}, + {Mode: fs.ModeSymlink | 0777, Path: "identifier/5ynpgDp5mcAmbFndOESXjQnOy_RG6furIxDpQz6c0bImBBLMAI8FouNbAB0uNs6f", Data: []byte("../checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9")}, {Mode: fs.ModeSymlink | 0777, Path: "identifier/nfeISfLeFDr1k-g3hpE1oZ440kTqDdfF8TDpoLdbTPqaMMIl95oiqcvqjRkMjubA", Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")}, {Mode: fs.ModeDir | 0700, Path: "temp"}, {Mode: fs.ModeDir | 0700, Path: "work"}, - }, pkg.MustDecode("-DrfvuB9gUAT-Tgw6V1KjFyosYGMGKJW7KMZFF1Ew8jZ9LJ82FtXf0wTgM3fO0oD"), nil}, + }, pkg.MustDecode("RibudsoY1X4_dtshfvL5LYfCPcxVnP0ikOn3yBHzOrt6BpevQiANLJF6Xua76-gM"), nil}, {"sample file short", fstest.MapFS{ ".": {Mode: fs.ModeDir | 0700}, diff --git a/internal/pkg/exec.go b/internal/pkg/exec.go index de29ff5..c12c909 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 { diff --git a/internal/pkg/exec_test.go b/internal/pkg/exec_test.go index 17172ea..d0a5457 100644 --- a/internal/pkg/exec_test.go +++ b/internal/pkg/exec_test.go @@ -11,7 +11,6 @@ import ( "testing" "hakurei.app/container/check" - "hakurei.app/container/fhs" "hakurei.app/container/stub" "hakurei.app/hst" "hakurei.app/internal/pkg" @@ -50,20 +49,20 @@ func TestExec(t *testing.T) { check.MustAbs("/opt/bin/testtool"), []string{"testtool"}, - pkg.MustPath("/file", newStubFile( + pkg.MustPath("/file", false, newStubFile( pkg.KindHTTPGet, pkg.ID{0xfe, 0}, nil, nil, nil, )), - pkg.MustPath("/.hakurei", stubArtifact{ + pkg.MustPath("/.hakurei", false, stubArtifact{ kind: pkg.KindTar, params: []byte("empty directory"), cure: func(c *pkg.CureContext) error { return os.MkdirAll(c.GetWorkDir().String(), 0700) }, }), - pkg.MustPath("/opt", testtool), + pkg.MustPath("/opt", false, testtool), ), ignorePathname, wantChecksumOffline, nil}, {"error passthrough", pkg.NewExec( @@ -76,7 +75,7 @@ func TestExec(t *testing.T) { check.MustAbs("/opt/bin/testtool"), []string{"testtool"}, - pkg.MustPath("/proc/nonexistent", stubArtifact{ + pkg.MustPath("/proc/nonexistent", false, stubArtifact{ kind: pkg.KindTar, params: []byte("doomed artifact"), cure: func(c *pkg.CureContext) error { @@ -95,7 +94,7 @@ func TestExec(t *testing.T) { check.MustAbs("/opt/bin/testtool"), []string{"testtool"}, - pkg.ExecContainerPath{}, + pkg.ExecPath{}, ), nil, pkg.Checksum{}, os.ErrInvalid}, }) @@ -116,7 +115,7 @@ func TestExec(t *testing.T) { } testtoolDestroy(t, base, c) - }, pkg.MustDecode("7PoPpWLjFPXIymbuIYLZAzOpCYr-2PN4CZ11jFdO-mDlnZNgFO3JyOtK8HW8Jxvm")}, + }, pkg.MustDecode("UiV6kMz7KrTsc_yphiyQzFLqjRanHxUOwrBMtkKuWo4mOO6WgPFAcoUEeSp7eVIW")}, {"net", nil, func(t *testing.T, base *check.Absolute, c *pkg.Cache) { c.SetStrict(true) @@ -139,25 +138,25 @@ func TestExec(t *testing.T) { check.MustAbs("/opt/bin/testtool"), []string{"testtool", "net"}, - pkg.MustPath("/file", newStubFile( + pkg.MustPath("/file", false, newStubFile( pkg.KindHTTPGet, pkg.ID{0xfe, 0}, nil, nil, nil, )), - pkg.MustPath("/.hakurei", stubArtifact{ + pkg.MustPath("/.hakurei", false, stubArtifact{ kind: pkg.KindTar, params: []byte("empty directory"), cure: func(c *pkg.CureContext) error { return os.MkdirAll(c.GetWorkDir().String(), 0700) }, }), - pkg.MustPath("/opt", testtool), + pkg.MustPath("/opt", false, testtool), ), ignorePathname, wantChecksum, nil}, }) testtoolDestroy(t, base, c) - }, pkg.MustDecode("bBQVFIt0FnOulljgpLnGtuzHSFgwiCMjc4pmc4rHRqXKQ60Q5aBVYp5f6aH9VdZi")}, + }, pkg.MustDecode("ek4K-0d4iRSArkY2TCs3WK34DbiYeOmhE_4vsJTSu_6roY4ZF3YG6eKRooal-i1o")}, {"overlay root", nil, func(t *testing.T, base *check.Absolute, c *pkg.Cache) { c.SetStrict(true) @@ -177,61 +176,19 @@ func TestExec(t *testing.T) { check.MustAbs("/opt/bin/testtool"), []string{"testtool"}, - pkg.MustPath("/", stubArtifact{ + pkg.MustPath("/", true, stubArtifact{ kind: pkg.KindTar, params: []byte("empty directory"), cure: func(c *pkg.CureContext) error { return os.MkdirAll(c.GetWorkDir().String(), 0700) }, }), - pkg.MustPath("/opt", testtool), + pkg.MustPath("/opt", false, testtool), ), ignorePathname, wantChecksumOffline, nil}, }) testtoolDestroy(t, base, c) - }, pkg.MustDecode("gFT9kprYBqEJKifJIl2sHn_3TgULWVLTU4DrYAHiGcRmcdFRZ0YtjiROW820cAEc")}, - - {"overlay temp", nil, func(t *testing.T, base *check.Absolute, c *pkg.Cache) { - c.SetStrict(true) - testtool, testtoolDestroy := newTesttool() - - msg := message.New(log.New(os.Stderr, "container: ", 0)) - msg.SwapVerbose(testing.Verbose()) - - cureMany(t, c, []cureStep{ - {"container", pkg.NewExec( - t.Context(), - msg, - 0, - nil, - pkg.AbsWork, - []string{"HAKUREI_TEST=1", "HAKUREI_ROOT=1"}, - check.MustAbs("/tmp/bin/testtool"), - []string{"testtool"}, - - pkg.MustPath("/", stubArtifact{ - kind: pkg.KindTar, - params: []byte("empty directory"), - cure: func(c *pkg.CureContext) error { - return os.MkdirAll(c.GetWorkDir().String(), 0700) - }, - }), - pkg.MustPath("/tmp/", stubArtifact{ - kind: pkg.KindTar, - params: []byte("empty directory"), - cure: func(c *pkg.CureContext) error { - return os.MkdirAll(c.GetWorkDir().String(), 0700) - }, - }), - pkg.ExecContainerPath{ - P: fhs.AbsTmp, - A: testtool, - }, - ), ignorePathname, wantChecksumOffline, nil}, - }) - - testtoolDestroy(t, base, c) - }, pkg.MustDecode("_r1IBeMWCkLwQ9Im9w0tV9_CWIOfQlXkkP2CogPHLmZp_AB6W3_8HVZqDV00dNAm")}, + }, pkg.MustDecode("VIqqpf0ip9jcyw63i6E8lCMGUcLivQBe4Bevt3WusNac-1MSy5bzB647qGUBzl-W")}, {"overlay work", nil, func(t *testing.T, base *check.Absolute, c *pkg.Cache) { c.SetStrict(true) @@ -251,29 +208,24 @@ func TestExec(t *testing.T) { check.MustAbs("/work/bin/testtool"), []string{"testtool"}, - pkg.MustPath("/", stubArtifact{ + pkg.MustPath("/", true, stubArtifact{ kind: pkg.KindTar, params: []byte("empty directory"), cure: func(c *pkg.CureContext) error { return os.MkdirAll(c.GetWorkDir().String(), 0700) }, - }), - pkg.MustPath("/work/", stubArtifact{ + }), pkg.MustPath("/work/", false, stubArtifact{ kind: pkg.KindTar, params: []byte("empty directory"), cure: func(c *pkg.CureContext) error { return os.MkdirAll(c.GetWorkDir().String(), 0700) }, - }), - pkg.ExecContainerPath{ - P: pkg.AbsWork, - A: testtool, - }, + }), pkg.Path(pkg.AbsWork, false /* ignored */, testtool), ), ignorePathname, wantChecksumOffline, nil}, }) testtoolDestroy(t, base, c) - }, pkg.MustDecode("-DrfvuB9gUAT-Tgw6V1KjFyosYGMGKJW7KMZFF1Ew8jZ9LJ82FtXf0wTgM3fO0oD")}, + }, pkg.MustDecode("RibudsoY1X4_dtshfvL5LYfCPcxVnP0ikOn3yBHzOrt6BpevQiANLJF6Xua76-gM")}, }) } diff --git a/internal/pkg/testdata/main.go b/internal/pkg/testdata/main.go index b3ce494..dc50f38 100644 --- a/internal/pkg/testdata/main.go +++ b/internal/pkg/testdata/main.go @@ -24,6 +24,7 @@ func main() { wantArgs := []string{"testtool"} if len(os.Args) == 2 { hostNet = true + log.SetPrefix("testtool(net): ") wantArgs = []string{"testtool", "net"} } if !slices.Equal(os.Args, wantArgs) { @@ -34,27 +35,25 @@ func main() { wantEnv := []string{"HAKUREI_TEST=1"} if len(os.Environ()) == 2 { overlayRoot = true + log.SetPrefix("testtool(overlay root): ") wantEnv = []string{"HAKUREI_TEST=1", "HAKUREI_ROOT=1"} } if !slices.Equal(wantEnv, os.Environ()) { log.Fatalf("Environ: %q, want %q", os.Environ(), wantEnv) } - var overlayTmp, overlayWork bool + var overlayWork bool const ( wantExec = "/opt/bin/testtool" - wantExecTmp = "/tmp/bin/testtool" wantExecWork = "/work/bin/testtool" ) if got, err := os.Executable(); err != nil { log.Fatalf("Executable: error = %v", err) } else if got != wantExec { switch got { - case wantExecTmp: - overlayTmp = true - case wantExecWork: overlayWork = true + log.SetPrefix("testtool(overlay work): ") default: log.Fatalf("Executable: %q, want %q", got, wantExec) @@ -99,12 +98,12 @@ func main() { } const checksumEmptyDir = "MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU" - ident := "oHuqV7p0v1Vd8IdAzjyYM8sfCS0P2LR5tfv5cb6Gbf2ZWUm8Ec-7hYPJ_qr183m7" + ident := "U2cbgVgEtjfRuvHfE1cQnZ3t8yoexULQyo_VLgvxAVJSsobMcNaFIsuDWtmt7kzK" log.Println(m) next := func() { m = m.Next; log.Println(m) } if overlayRoot { - ident = "cIjP14zs5el6W_BQhufL_c0vWg-V6Z6pDpsbEa3sYtZ1381u1bKnH3N16RIrw-1S" + ident = "5ey2wpmMpj483YYa7ZZQciYLA2cx3_l167JCqWW4Pd-5DVp81dj9EsBtVTwYptF6" if m.Root != "/" || m.Target != "/" || m.Source != "overlay" || m.FsType != "overlay" { @@ -122,7 +121,7 @@ func main() { } } else { if hostNet { - ident = "I3T53NtN6HPAyyodHtq2B0clcsoS1nPdvCEb-Zc5K-hoqFGL2od1mftHhwG7gX1S" + ident = "QdsJhGgnk5N2xdUNGcndXQxFKifxf1V_2t9X8CQ-pDcg24x6mGJC_BiLfGbs6Qml" } if m.Root != "/sysroot" || m.Target != "/" { @@ -140,28 +139,11 @@ func main() { } } - if !overlayTmp { - next() // testtool artifact - } else { - ident = "3ELJ8l42g6XeIoLGR9LheVhMIwSIleD6VrhsliBuon5DAdBOwFSMqd7aiUI4fll7" - - next() - if path.Base(m.Root) != checksumEmptyDir || m.Target != "/tmp" { - log.Fatal("unexpected artifact checksum") - } - } - - if overlayTmp { - next() // testtool artifact - if m.Root != "/" || m.Target != "/tmp" || - m.Source != "overlay" || m.FsType != "overlay" { - log.Fatal("unexpected temp mount entry") - } - } + next() // testtool artifact next() if overlayWork { - ident = "VMCurZKCA_MV80zb-ZBWVytfl3rhYOKJeo2u9l-OuaytQ_w_r4EsqgJ2nfO93x5_" + ident = "5ynpgDp5mcAmbFndOESXjQnOy_RG6furIxDpQz6c0bImBBLMAI8FouNbAB0uNs6f" if m.Root != "/" || m.Target != "/work" || m.Source != "overlay" || m.FsType != "overlay" { log.Fatal("unexpected work mount entry") @@ -172,11 +154,9 @@ func main() { } } - if !overlayTmp { - next() - if path.Base(m.Root) != ident || m.Target != "/tmp" { - log.Fatal("unexpected temp mount entry") - } + next() + if path.Base(m.Root) != ident || m.Target != "/tmp" { + log.Fatal("unexpected temp mount entry") } next()