aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal/sandbox/mount.go
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2025-03-14 00:16:41 +0900
committerOphestra <cat@gensokyo.uk>2025-03-14 00:16:41 +0900
commitf1002157a58a5a0a559ba9295c3193c300a4235d (patch)
treef0f388e84a4a6a30e39efc7bf6ee3202b99ce833 /internal/sandbox/mount.go
parent4133b555ba8dd38accb272d86c01898ac4b99f95 (diff)
sandbox: separate bind mount function from op
This is useful in the implementation of various other ops. Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'internal/sandbox/mount.go')
-rw-r--r--internal/sandbox/mount.go81
1 files changed, 81 insertions, 0 deletions
diff --git a/internal/sandbox/mount.go b/internal/sandbox/mount.go
new file mode 100644
index 00000000..3c6ae2d8
--- /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))
+}