package sandbox import ( "encoding/gob" "errors" "fmt" "os" "path" "syscall" "git.gensokyo.uk/security/fortify/internal/fmsg" ) func init() { gob.Register(new(BindMount)) } const ( BindOptional = 1 << iota BindRecursive BindWritable BindDevices ) type BindMount struct { Source, Target string Flags int } func (b *BindMount) apply() error { if !path.IsAbs(b.Source) || !path.IsAbs(b.Target) { return syscall.EBADE } source, target := toHost(b.Source), toSysroot(b.Target) if fi, err := os.Stat(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 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 } return fmsg.WrapErrorSuffix(syscall.Mount(source, target, "", flags, ""), fmt.Sprintf("cannot bind %q on %q:", b.Source, b.Target)) } func (b *BindMount) Is(op FSOp) bool { vb, ok := op.(*BindMount); return ok && *b == *vb } func (b *BindMount) String() string { return fmt.Sprintf("bind mount %q on %q %v", b.Source, b.Target, b.Flags&BindWritable != 0) } func (f *Filesystem) Bind(source, target string, flags int) *Filesystem { *f = append(*f, &BindMount{source, target, flags | BindRecursive}) return f }