2015-07-24 09:10:49 +03:00
|
|
|
package fuse
|
2015-07-24 09:10:41 +03:00
|
|
|
|
|
|
|
import (
|
2018-12-26 17:44:07 +03:00
|
|
|
"errors"
|
2015-07-24 09:10:41 +03:00
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"os/exec"
|
|
|
|
"syscall"
|
|
|
|
|
2018-12-26 17:44:07 +03:00
|
|
|
"golang.org/x/sys/unix"
|
|
|
|
)
|
2015-07-24 09:10:41 +03:00
|
|
|
|
2020-04-23 22:10:43 +03:00
|
|
|
func findFusermount() (string, error) {
|
|
|
|
path, err := exec.LookPath("fusermount3")
|
|
|
|
if err != nil {
|
|
|
|
path, err = exec.LookPath("fusermount")
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return path, nil
|
|
|
|
}
|
|
|
|
|
2018-12-26 17:44:07 +03:00
|
|
|
func enableFunc(flag uintptr) func(uintptr) uintptr {
|
|
|
|
return func(v uintptr) uintptr {
|
|
|
|
return v | flag
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func disableFunc(flag uintptr) func(uintptr) uintptr {
|
|
|
|
return func(v uintptr) uintptr {
|
|
|
|
return v &^ flag
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// As per libfuse/fusermount.c:602: https://bit.ly/2SgtWYM#L602
|
|
|
|
var mountflagopts = map[string]func(uintptr) uintptr{
|
2020-03-23 10:51:36 +03:00
|
|
|
"rw": disableFunc(unix.MS_RDONLY),
|
|
|
|
"ro": enableFunc(unix.MS_RDONLY),
|
2018-12-26 17:44:07 +03:00
|
|
|
"suid": disableFunc(unix.MS_NOSUID),
|
|
|
|
"nosuid": enableFunc(unix.MS_NOSUID),
|
|
|
|
"dev": disableFunc(unix.MS_NODEV),
|
|
|
|
"nodev": enableFunc(unix.MS_NODEV),
|
|
|
|
"exec": disableFunc(unix.MS_NOEXEC),
|
|
|
|
"noexec": enableFunc(unix.MS_NOEXEC),
|
|
|
|
"async": disableFunc(unix.MS_SYNCHRONOUS),
|
|
|
|
"sync": enableFunc(unix.MS_SYNCHRONOUS),
|
|
|
|
"atime": disableFunc(unix.MS_NOATIME),
|
|
|
|
"noatime": enableFunc(unix.MS_NOATIME),
|
|
|
|
"dirsync": enableFunc(unix.MS_DIRSYNC),
|
|
|
|
}
|
|
|
|
|
|
|
|
var errFallback = errors.New("sentinel: fallback to fusermount(1)")
|
|
|
|
|
|
|
|
func directmount(dir string, cfg *MountConfig) (*os.File, error) {
|
2019-07-28 05:48:37 +03:00
|
|
|
// We use syscall.Open + os.NewFile instead of os.OpenFile so that the file
|
|
|
|
// is opened in blocking mode. When opened in non-blocking mode, the Go
|
|
|
|
// runtime tries to use poll(2), which does not work with /dev/fuse.
|
|
|
|
fd, err := syscall.Open("/dev/fuse", syscall.O_RDWR, 0644)
|
2018-12-26 17:44:07 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, errFallback
|
|
|
|
}
|
2019-07-28 05:48:37 +03:00
|
|
|
dev := os.NewFile(uintptr(fd), "/dev/fuse")
|
2018-12-26 17:44:07 +03:00
|
|
|
// As per libfuse/fusermount.c:847: https://bit.ly/2SgtWYM#L847
|
|
|
|
data := fmt.Sprintf("fd=%d,rootmode=40000,user_id=%d,group_id=%d",
|
|
|
|
dev.Fd(), os.Getuid(), os.Getgid())
|
|
|
|
// As per libfuse/fusermount.c:749: https://bit.ly/2SgtWYM#L749
|
|
|
|
mountflag := uintptr(unix.MS_NODEV | unix.MS_NOSUID)
|
|
|
|
opts := cfg.toMap()
|
|
|
|
for k := range opts {
|
|
|
|
fn, ok := mountflagopts[k]
|
|
|
|
if !ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
mountflag = fn(mountflag)
|
|
|
|
delete(opts, k)
|
|
|
|
}
|
|
|
|
delete(opts, "fsname") // handled via fstype mount(2) parameter
|
2020-03-11 11:51:26 +03:00
|
|
|
fstype := "fuse"
|
|
|
|
if subtype, ok := opts["subtype"]; ok {
|
|
|
|
fstype += "." + subtype
|
|
|
|
}
|
|
|
|
delete(opts, "subtype")
|
2018-12-26 17:44:07 +03:00
|
|
|
data += "," + mapToOptionsString(opts)
|
|
|
|
if err := unix.Mount(
|
|
|
|
cfg.FSName, // source
|
|
|
|
dir, // target
|
2020-03-11 11:51:26 +03:00
|
|
|
fstype, // fstype
|
2018-12-26 17:44:07 +03:00
|
|
|
mountflag, // mountflag
|
|
|
|
data, // data
|
|
|
|
); err != nil {
|
|
|
|
if err == syscall.EPERM {
|
|
|
|
return nil, errFallback
|
|
|
|
|
|
|
|
}
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return dev, nil
|
|
|
|
}
|
2015-07-24 09:18:07 +03:00
|
|
|
|
2018-12-26 17:44:07 +03:00
|
|
|
// Begin the process of mounting at the given directory, returning a connection
|
|
|
|
// to the kernel. Mounting continues in the background, and is complete when an
|
|
|
|
// error is written to the supplied channel. The file system may need to
|
|
|
|
// service the connection in order for mounting to complete.
|
|
|
|
func mount(dir string, cfg *MountConfig, ready chan<- error) (*os.File, error) {
|
|
|
|
// On linux, mounting is never delayed.
|
|
|
|
ready <- nil
|
|
|
|
|
|
|
|
// Try mounting without fusermount(1) first: we might be running as root or
|
|
|
|
// have the CAP_SYS_ADMIN capability.
|
|
|
|
dev, err := directmount(dir, cfg)
|
|
|
|
if err == errFallback {
|
2021-10-19 19:50:09 +03:00
|
|
|
fusermountPath, err := findFusermount()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
argv := []string{
|
|
|
|
"-o", cfg.toOptionsString(),
|
|
|
|
"--",
|
|
|
|
dir,
|
|
|
|
}
|
|
|
|
return fusermount(fusermountPath, argv, []string{}, true)
|
2018-12-26 17:44:07 +03:00
|
|
|
}
|
|
|
|
return dev, err
|
2015-07-24 09:10:41 +03:00
|
|
|
}
|