From f1002157a58a5a0a559ba9295c3193c300a4235d Mon Sep 17 00:00:00 2001 From: Ophestra Date: Fri, 14 Mar 2025 00:16:41 +0900 Subject: [PATCH] sandbox: separate bind mount function from op This is useful in the implementation of various other ops. Signed-off-by: Ophestra --- internal/sandbox/mount.go | 81 ++++++++++++++++++++++++++++++++++ internal/sandbox/sequential.go | 67 ++-------------------------- 2 files changed, 84 insertions(+), 64 deletions(-) create mode 100644 internal/sandbox/mount.go diff --git a/internal/sandbox/mount.go b/internal/sandbox/mount.go new file mode 100644 index 0000000..3c6ae2d --- /dev/null +++ b/internal/sandbox/mount.go @@ -0,0 +1,81 @@ +package sandbox + +import ( + "errors" + "fmt" + "os" + "strings" + "syscall" + + "git.gensokyo.uk/security/fortify/internal/fmsg" +) + +const ( + BindOptional = 1 << iota + BindSource + BindRecursive + BindWritable + BindDevices +) + +func bindMount(src, dest string, flags int) error { + target := toSysroot(dest) + var source string + + if flags&BindSource == 0 { + // this is what bwrap does, so the behaviour is kept for now, + // however recursively resolving links might improve user experience + if rp, err := realpathHost(src); err != nil { + if os.IsNotExist(err) { + if flags&BindOptional != 0 { + return nil + } else { + return fmsg.WrapError(err, + fmt.Sprintf("path %q does not exist", src)) + } + } + return fmsg.WrapError(err, err.Error()) + } else { + source = toHost(rp) + } + } else if flags&BindOptional != 0 { + return fmsg.WrapError(syscall.EINVAL, + "flag source excludes optional") + } + + if fi, err := os.Stat(source); err != nil { + return fmsg.WrapError(err, err.Error()) + } else if fi.IsDir() { + if err = os.MkdirAll(target, 0755); err != nil { + return fmsg.WrapErrorSuffix(err, + fmt.Sprintf("cannot create directory %q:", dest)) + } + } else if err = ensureFile(target, 0444); err != nil { + if errors.Is(err, syscall.EISDIR) { + return fmsg.WrapError(err, + fmt.Sprintf("path %q is a directory", dest)) + } + return fmsg.WrapErrorSuffix(err, + fmt.Sprintf("cannot create %q:", dest)) + } + + var mf uintptr = syscall.MS_SILENT | syscall.MS_BIND + if flags&BindRecursive != 0 { + mf |= syscall.MS_REC + } + if flags&BindWritable == 0 { + mf |= syscall.MS_RDONLY + } + if flags&BindDevices == 0 { + mf |= syscall.MS_NODEV + } + if fmsg.Load() { + if strings.TrimPrefix(source, hostPath) == strings.TrimPrefix(target, sysrootPath) { + fmsg.Verbosef("resolved %q flags %#x", target, mf) + } else { + fmsg.Verbosef("resolved %q on %q flags %#x", source, target, mf) + } + } + return fmsg.WrapErrorSuffix(syscall.Mount(source, target, "", mf, ""), + fmt.Sprintf("cannot bind %q on %q:", src, dest)) +} diff --git a/internal/sandbox/sequential.go b/internal/sandbox/sequential.go index 603d59e..d4a29fa 100644 --- a/internal/sandbox/sequential.go +++ b/internal/sandbox/sequential.go @@ -2,12 +2,10 @@ package sandbox import ( "encoding/gob" - "errors" "fmt" "math" "os" "path" - "strings" "syscall" "git.gensokyo.uk/security/fortify/internal/fmsg" @@ -15,13 +13,6 @@ import ( func init() { gob.Register(new(BindMount)) } -const ( - BindOptional = 1 << iota - BindRecursive - BindWritable - BindDevices -) - // BindMount bind mounts host path Source on container path Target. type BindMount struct { Source, Target string @@ -31,62 +22,10 @@ type BindMount struct { func (b *BindMount) apply() error { if !path.IsAbs(b.Source) || !path.IsAbs(b.Target) { - return syscall.EBADE + return fmsg.WrapError(syscall.EBADE, + "path is not absolute") } - target := toSysroot(b.Target) - var source string - - // this is what bwrap does, so the behaviour is kept for now, - // however recursively resolving links might improve user experience - if rp, err := realpathHost(b.Source); err != nil { - if os.IsNotExist(err) { - if b.Flags&BindOptional != 0 { - return nil - } else { - return fmsg.WrapError(err, - fmt.Sprintf("path %q does not exist", b.Source)) - } - } - return fmsg.WrapError(err, err.Error()) - } else { - source = toHost(rp) - } - - if fi, err := os.Stat(source); err != nil { - return fmsg.WrapError(err, err.Error()) - } else if fi.IsDir() { - if err = os.MkdirAll(target, 0755); err != nil { - return fmsg.WrapErrorSuffix(err, - fmt.Sprintf("cannot create directory %q:", b.Target)) - } - } else if err = ensureFile(target, 0444); err != nil { - if errors.Is(err, syscall.EISDIR) { - return fmsg.WrapError(err, - fmt.Sprintf("path %q is a directory", b.Target)) - } - return fmsg.WrapErrorSuffix(err, - fmt.Sprintf("cannot create %q:", b.Target)) - } - - var flags uintptr = syscall.MS_SILENT | syscall.MS_BIND - if b.Flags&BindRecursive != 0 { - flags |= syscall.MS_REC - } - if b.Flags&BindWritable == 0 { - flags |= syscall.MS_RDONLY - } - if b.Flags&BindDevices == 0 { - flags |= syscall.MS_NODEV - } - if fmsg.Load() { - if strings.TrimPrefix(source, hostPath) == strings.TrimPrefix(target, sysrootPath) { - fmsg.Verbosef("resolved %q flags %#x", target, flags) - } else { - fmsg.Verbosef("resolved %q on %q flags %#x", source, target, flags) - } - } - return fmsg.WrapErrorSuffix(syscall.Mount(source, target, "", flags, ""), - fmt.Sprintf("cannot bind %q on %q:", b.Source, b.Target)) + return bindMount(b.Source, b.Target, b.Flags) } func (b *BindMount) Is(op Op) bool { vb, ok := op.(*BindMount); return ok && *b == *vb }