aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--check/absolute.go6
-rw-r--r--hst/fs.go2
-rw-r--r--hst/fs_test.go9
-rw-r--r--hst/fsoverlay.go36
-rw-r--r--hst/fsoverlay_test.go14
-rw-r--r--internal/outcome/spcontainer.go4
6 files changed, 59 insertions, 12 deletions
diff --git a/check/absolute.go b/check/absolute.go
index 1a050d06..c5970abb 100644
--- a/check/absolute.go
+++ b/check/absolute.go
@@ -31,6 +31,8 @@ func (e AbsoluteError) Is(target error) bool {
type Absolute struct{ pathname unique.Handle[string] }
var (
+ _ fmt.GoStringer = new(Absolute)
+
_ encoding.TextAppender = new(Absolute)
_ encoding.TextMarshaler = new(Absolute)
_ encoding.TextUnmarshaler = new(Absolute)
@@ -40,6 +42,10 @@ var (
_ encoding.BinaryUnmarshaler = new(Absolute)
)
+func (a *Absolute) GoString() string {
+ return fmt.Sprintf("check.MustAbs(%q)", a.String())
+}
+
// ok returns whether [Absolute] is not the zero value.
func (a *Absolute) ok() bool { return a != nil && *a != (Absolute{}) }
diff --git a/hst/fs.go b/hst/fs.go
index cb35f925..2f2c377c 100644
--- a/hst/fs.go
+++ b/hst/fs.go
@@ -37,6 +37,8 @@ type Ops interface {
Bind(source, target *check.Absolute, flags int) Ops
// Overlay appends an op that mounts the overlay pseudo filesystem.
Overlay(target, state, work *check.Absolute, layers ...*check.Absolute) Ops
+ // OverlayEphemeral appends a MountOverlayOp with an ephemeral upperdir and workdir.
+ OverlayEphemeral(target *check.Absolute, layers ...*check.Absolute) Ops
// OverlayReadonly appends an op that mounts the overlay pseudo filesystem readonly.
OverlayReadonly(target *check.Absolute, layers ...*check.Absolute) Ops
diff --git a/hst/fs_test.go b/hst/fs_test.go
index a88970b7..8ef268fc 100644
--- a/hst/fs_test.go
+++ b/hst/fs_test.go
@@ -3,6 +3,7 @@ package hst_test
import (
"encoding/json"
"errors"
+ "fmt"
"os"
"reflect"
"strings"
@@ -283,11 +284,11 @@ func checkFs(t *testing.T, testCases []fsTestCase) {
if !reflect.DeepEqual(ops, &tc.ops) {
gotString := new(strings.Builder)
for _, op := range *ops {
- gotString.WriteString("\n" + op.String())
+ gotString.WriteString("\n" + fmt.Sprintf("%#v", op))
}
wantString := new(strings.Builder)
for _, op := range tc.ops {
- wantString.WriteString("\n" + op.String())
+ wantString.WriteString("\n" + fmt.Sprintf("%#v", op))
}
t.Errorf("Apply: %s, want %s", gotString, wantString)
}
@@ -339,6 +340,10 @@ func (p opsAdapter) Overlay(target, state, work *check.Absolute, layers ...*chec
return opsAdapter{p.Ops.Overlay(target, state, work, layers...)}
}
+func (p opsAdapter) OverlayEphemeral(target *check.Absolute, layers ...*check.Absolute) hst.Ops {
+ return opsAdapter{p.Ops.OverlayEphemeral(target, layers...)}
+}
+
func (p opsAdapter) OverlayReadonly(target *check.Absolute, layers ...*check.Absolute) hst.Ops {
return opsAdapter{p.Ops.OverlayReadonly(target, layers...)}
}
diff --git a/hst/fsoverlay.go b/hst/fsoverlay.go
index b06c1e7e..16a5e335 100644
--- a/hst/fsoverlay.go
+++ b/hst/fsoverlay.go
@@ -2,6 +2,7 @@ package hst
import (
"encoding/gob"
+ "slices"
"strings"
"hakurei.app/check"
@@ -40,7 +41,7 @@ func (o *FSOverlay) Valid() bool {
}
if o.Upper != nil { // rw
- return o.Work != nil && len(o.Lower) > 0
+ return o.Work != nil || len(o.Lower) > 0
} else { // ro
return len(o.Lower) >= 2
}
@@ -58,8 +59,11 @@ func (o *FSOverlay) Host() []*check.Absolute {
return nil
}
p := make([]*check.Absolute, 0, 2+len(o.Lower))
- if o.Upper != nil && o.Work != nil {
- p = append(p, o.Upper, o.Work)
+ if o.Upper != nil {
+ p = append(p, o.Upper)
+ if o.Work != nil {
+ p = append(p, o.Work)
+ }
}
p = append(p, o.Lower...)
return p
@@ -70,11 +74,18 @@ func (o *FSOverlay) Apply(z *ApplyState) {
return
}
- if o.Upper != nil && o.Work != nil {
- z.Overlay(o.Target, o.Upper, o.Work, o.Lower...)
+ if o.Upper != nil {
if o.Target.Is(fhs.AbsRoot) {
z.NoRemountRoot = true
}
+ if o.Work != nil {
+ z.Overlay(o.Target, o.Upper, o.Work, o.Lower...)
+ } else {
+ z.OverlayEphemeral(o.Target, slices.Concat(
+ o.Lower,
+ []*check.Absolute{o.Upper})...,
+ )
+ }
} else {
z.OverlayReadonly(o.Target, o.Lower...)
}
@@ -90,12 +101,19 @@ func (o *FSOverlay) String() string {
lower[i] = check.EscapeOverlayDataSegment(a.String())
}
- if o.Upper != nil && o.Work != nil {
- return "w*" + strings.Join(append([]string{
+ if o.Upper != nil {
+ if o.Work != nil {
+ return "w*" + strings.Join(append([]string{
+ check.EscapeOverlayDataSegment(o.Target.String()),
+ check.EscapeOverlayDataSegment(o.Upper.String()),
+ check.EscapeOverlayDataSegment(o.Work.String())},
+ lower...), check.SpecialOverlayPath)
+ }
+ return "e*" + strings.Join(append([]string{
check.EscapeOverlayDataSegment(o.Target.String()),
- check.EscapeOverlayDataSegment(o.Upper.String()),
- check.EscapeOverlayDataSegment(o.Work.String())},
+ check.EscapeOverlayDataSegment(o.Upper.String())},
lower...), check.SpecialOverlayPath)
+
} else {
return "*" + strings.Join(append([]string{
check.EscapeOverlayDataSegment(o.Target.String())},
diff --git a/hst/fsoverlay_test.go b/hst/fsoverlay_test.go
index 1b14e7c9..e1ae21b5 100644
--- a/hst/fsoverlay_test.go
+++ b/hst/fsoverlay_test.go
@@ -5,6 +5,7 @@ import (
"hakurei.app/check"
"hakurei.app/container"
+ "hakurei.app/fhs"
"hakurei.app/hst"
)
@@ -14,7 +15,7 @@ func TestFSOverlay(t *testing.T) {
checkFs(t, []fsTestCase{
{"nil", (*hst.FSOverlay)(nil), false, nil, nil, nil, "<invalid>"},
{"nil lower", &hst.FSOverlay{Target: m("/etc"), Lower: []*check.Absolute{nil}}, false, nil, nil, nil, "<invalid>"},
- {"zero lower", &hst.FSOverlay{Target: m("/etc"), Upper: m("/"), Work: m("/")}, false, nil, nil, nil, "<invalid>"},
+ {"zero lower", &hst.FSOverlay{Target: m("/etc"), Work: m("/")}, false, nil, nil, nil, "<invalid>"},
{"zero lower ro", &hst.FSOverlay{Target: m("/etc")}, false, nil, nil, nil, "<invalid>"},
{"short lower", &hst.FSOverlay{Target: m("/etc"), Lower: ms("/etc")}, false, nil, nil, nil, "<invalid>"},
@@ -62,5 +63,16 @@ func TestFSOverlay(t *testing.T) {
Work: m("/tmp/work"),
}}, m("/"), ms("/tmp/upper", "/tmp/work", "/tmp/.src0", "/tmp/.src1"),
"w*/:/tmp/upper:/tmp/work:/tmp/.src0:/tmp/.src1"},
+
+ {"ephemeral", &hst.FSOverlay{
+ Target: m("/"),
+ Lower: ms("/tmp/.src0", "/tmp/.src1"),
+ Upper: m("/tmp/upper"),
+ }, true, container.Ops{&container.MountOverlayOp{
+ Target: m("/"),
+ Lower: ms("/tmp/.src0", "/tmp/.src1", "/tmp/upper"),
+ Upper: fhs.AbsRoot,
+ }}, m("/"), ms("/tmp/upper", "/tmp/.src0", "/tmp/.src1"),
+ "e*/:/tmp/upper:/tmp/.src0:/tmp/.src1"},
})
}
diff --git a/internal/outcome/spcontainer.go b/internal/outcome/spcontainer.go
index d9398d2c..97d98dd1 100644
--- a/internal/outcome/spcontainer.go
+++ b/internal/outcome/spcontainer.go
@@ -382,6 +382,10 @@ func (p opsAdapter) Overlay(target, state, work *check.Absolute, layers ...*chec
return opsAdapter{p.Ops.Overlay(target, state, work, layers...)}
}
+func (p opsAdapter) OverlayEphemeral(target *check.Absolute, layers ...*check.Absolute) hst.Ops {
+ return opsAdapter{p.Ops.OverlayEphemeral(target, layers...)}
+}
+
func (p opsAdapter) OverlayReadonly(target *check.Absolute, layers ...*check.Absolute) hst.Ops {
return opsAdapter{p.Ops.OverlayReadonly(target, layers...)}
}