aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal
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
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')
-rw-r--r--internal/sandbox/mount.go81
-rw-r--r--internal/sandbox/sequential.go67
2 files changed, 84 insertions, 64 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))
+}
diff --git a/internal/sandbox/sequential.go b/internal/sandbox/sequential.go
index 603d59e0..d4a29fa5 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
- }
- 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.WrapError(syscall.EBADE,
+ "path is not absolute")
}
- 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 }