aboutsummaryrefslogtreecommitdiffhomepage
path: root/container/initbind.go
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2025-08-20 01:26:41 +0900
committerOphestra <cat@gensokyo.uk>2025-08-20 01:28:31 +0900
commitc81c9a9d75f179988cf858b85fe2de71bf9c2ff4 (patch)
tree157723a3f9a6f8a456fc939a1ef56e8e597d70f3 /container/initbind.go
parent339e4080dce73d372f6111773066b9c84308b6b7 (diff)
container/init: split setup ops into individual files
This significantly increases readability. Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'container/initbind.go')
-rw-r--r--container/initbind.go95
1 files changed, 95 insertions, 0 deletions
diff --git a/container/initbind.go b/container/initbind.go
new file mode 100644
index 00000000..12db42d8
--- /dev/null
+++ b/container/initbind.go
@@ -0,0 +1,95 @@
+package container
+
+import (
+ "encoding/gob"
+ "fmt"
+ "os"
+ "path/filepath"
+ . "syscall"
+)
+
+func init() { gob.Register(new(BindMountOp)) }
+
+// Bind appends an [Op] that bind mounts host path [BindMountOp.Source] on container path [BindMountOp.Target].
+func (f *Ops) Bind(source, target *Absolute, flags int) *Ops {
+ *f = append(*f, &BindMountOp{nil, source, target, flags})
+ return f
+}
+
+// BindMountOp bind mounts host path Source on container path Target.
+// Note that Flags uses bits declared in this package and should not be set with constants in [syscall].
+type BindMountOp struct {
+ sourceFinal, Source, Target *Absolute
+
+ Flags int
+}
+
+const (
+ // BindOptional skips nonexistent host paths.
+ BindOptional = 1 << iota
+ // BindWritable mounts filesystem read-write.
+ BindWritable
+ // BindDevice allows access to devices (special files) on this filesystem.
+ BindDevice
+)
+
+func (b *BindMountOp) early(*setupState) error {
+ if b.Source == nil || b.Target == nil {
+ return EBADE
+ }
+
+ if pathname, err := filepath.EvalSymlinks(b.Source.String()); err != nil {
+ if os.IsNotExist(err) && b.Flags&BindOptional != 0 {
+ // leave sourceFinal as nil
+ return nil
+ }
+ return wrapErrSelf(err)
+ } else {
+ b.sourceFinal, err = NewAbs(pathname)
+ return err
+ }
+}
+
+func (b *BindMountOp) apply(*setupState) error {
+ if b.sourceFinal == nil {
+ if b.Flags&BindOptional == 0 {
+ // unreachable
+ return EBADE
+ }
+ return nil
+ }
+
+ source := toHost(b.sourceFinal.String())
+ target := toSysroot(b.Target.String())
+
+ // this perm value emulates bwrap behaviour as it clears bits from 0755 based on
+ // op->perms which is never set for any bind setup op so always results in 0700
+ if fi, err := os.Stat(source); err != nil {
+ return wrapErrSelf(err)
+ } else if fi.IsDir() {
+ if err = os.MkdirAll(target, 0700); err != nil {
+ return wrapErrSelf(err)
+ }
+ } else if err = ensureFile(target, 0444, 0700); err != nil {
+ return err
+ }
+
+ var flags uintptr = MS_REC
+ if b.Flags&BindWritable == 0 {
+ flags |= MS_RDONLY
+ }
+ if b.Flags&BindDevice == 0 {
+ flags |= MS_NODEV
+ }
+
+ return hostProc.bindMount(source, target, flags, b.sourceFinal == b.Target)
+}
+
+func (b *BindMountOp) Is(op Op) bool { vb, ok := op.(*BindMountOp); return ok && *b == *vb }
+func (*BindMountOp) prefix() string { return "mounting" }
+func (b *BindMountOp) String() string {
+ if b.Source == b.Target {
+ return fmt.Sprintf("%q flags %#x", b.Source, b.Flags)
+ }
+ return fmt.Sprintf("%q on %q flags %#x", b.Source, b.Target, b.Flags)
+}