aboutsummaryrefslogtreecommitdiffhomepage
path: root/hst/fsoverlay.go
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2025-08-15 03:30:51 +0900
committerOphestra <cat@gensokyo.uk>2025-08-15 04:00:55 +0900
commit9ed3ba85eaba4bc4bd12b290dc4daf00ffe159dc (patch)
tree03301910c40c59eb94029f1a93b1e74e21b5226a /hst/fsoverlay.go
parent4433c993fae86634b813ee212803a84aaeedd374 (diff)
hst/fs: implement overlay fstype
This finally exposes overlay mounts in the high level hakurei API. Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'hst/fsoverlay.go')
-rw-r--r--hst/fsoverlay.go98
1 files changed, 98 insertions, 0 deletions
diff --git a/hst/fsoverlay.go b/hst/fsoverlay.go
new file mode 100644
index 00000000..cbf7cd6d
--- /dev/null
+++ b/hst/fsoverlay.go
@@ -0,0 +1,98 @@
+package hst
+
+import (
+ "encoding/gob"
+ "strings"
+
+ "hakurei.app/container"
+)
+
+func init() { gob.Register(new(FSOverlay)) }
+
+// FilesystemOverlay is the [FilesystemConfig.Type] name of an overlay mount point.
+const FilesystemOverlay = "overlay"
+
+// FSOverlay represents an overlay mount point.
+type FSOverlay struct {
+ // mount point in container
+ Dst *container.Absolute `json:"dst"`
+
+ // any filesystem, does not need to be on a writable filesystem, must not be nil
+ Lower []*container.Absolute `json:"lower"`
+ // the upperdir is normally on a writable filesystem, leave as nil to mount Lower readonly
+ Upper *container.Absolute `json:"upper,omitempty"`
+ // the workdir needs to be an empty directory on the same filesystem as Upper, must not be nil if Upper is populated
+ Work *container.Absolute `json:"work,omitempty"`
+}
+
+func (o *FSOverlay) Valid() bool {
+ if o == nil || o.Dst == nil {
+ return false
+ }
+
+ for _, a := range o.Lower {
+ if a == nil {
+ return false
+ }
+ }
+
+ if o.Upper != nil { // rw
+ return o.Work != nil && len(o.Lower) > 0
+ } else { // ro
+ return len(o.Lower) >= 2
+ }
+}
+
+func (o *FSOverlay) Target() *container.Absolute {
+ if !o.Valid() {
+ return nil
+ }
+ return o.Dst
+}
+
+func (o *FSOverlay) Host() []*container.Absolute {
+ if !o.Valid() {
+ return nil
+ }
+ p := make([]*container.Absolute, 0, 2+len(o.Lower))
+ if o.Upper != nil && o.Work != nil {
+ p = append(p, o.Upper, o.Work)
+ }
+ p = append(p, o.Lower...)
+ return p
+}
+
+func (o *FSOverlay) Apply(op *container.Ops) {
+ if !o.Valid() {
+ return
+ }
+
+ if o.Upper != nil && o.Work != nil { // rw
+ op.Overlay(o.Dst, o.Upper, o.Work, o.Lower...)
+ } else { // ro
+ op.OverlayReadonly(o.Dst, o.Lower...)
+ }
+}
+
+func (o *FSOverlay) String() string {
+ if !o.Valid() {
+ return "<invalid>"
+ }
+
+ lower := make([]string, len(o.Lower))
+ for i, a := range o.Lower {
+ lower[i] = container.EscapeOverlayDataSegment(a.String())
+ }
+
+ if o.Upper != nil && o.Work != nil {
+ return "w*" + strings.Join(append([]string{
+ container.EscapeOverlayDataSegment(o.Dst.String()),
+ container.EscapeOverlayDataSegment(o.Upper.String()),
+ container.EscapeOverlayDataSegment(o.Work.String())},
+ lower...), container.SpecialOverlayPath)
+ } else {
+ return "*" + strings.Join(append([]string{
+ container.EscapeOverlayDataSegment(o.Dst.String())},
+ lower...), container.SpecialOverlayPath)
+ }
+}