package container import ( "encoding/gob" "fmt" "os" "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 } func (b *BindMountOp) Valid() bool { return b != nil && b.Source != nil && b.Target != nil } 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, k syscallDispatcher) error { if pathname, err := k.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, k syscallDispatcher) error { if b.sourceFinal == nil { if b.Flags&BindOptional == 0 { // unreachable return msg.WrapErr(os.ErrClosed, "impossible bind state reached") } 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 := k.stat(source); err != nil { return wrapErrSelf(err) } else if fi.IsDir() { if err = k.mkdirAll(target, 0700); err != nil { return wrapErrSelf(err) } } else if err = k.ensureFile(target, 0444, 0700); err != nil { return err } var flags uintptr = syscall.MS_REC if b.Flags&BindWritable == 0 { flags |= syscall.MS_RDONLY } if b.Flags&BindDevice == 0 { flags |= syscall.MS_NODEV } return k.bindMount(source, target, flags, b.sourceFinal == b.Target) } func (b *BindMountOp) Is(op Op) bool { vb, ok := op.(*BindMountOp) return ok && b.Valid() && vb.Valid() && b.Source.Is(vb.Source) && b.Target.Is(vb.Target) && b.Flags == vb.Flags } func (*BindMountOp) prefix() string { return "mounting" } func (b *BindMountOp) String() string { if b.Source == nil || b.Target == nil { return "" } if b.Source.String() == b.Target.String() { return fmt.Sprintf("%q flags %#x", b.Source, b.Flags) } return fmt.Sprintf("%q on %q flags %#x", b.Source, b.Target, b.Flags) }