2015-07-24 08:45:16 +03:00
|
|
|
package fuse
|
2015-07-24 08:39:04 +03:00
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"os/exec"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"syscall"
|
2015-07-24 08:48:23 +03:00
|
|
|
|
|
|
|
"github.com/jacobsa/fuse/internal/buffer"
|
2015-07-24 08:39:04 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
var errNoAvail = errors.New("no available fuse devices")
|
|
|
|
var errNotLoaded = errors.New("osxfusefs is not loaded")
|
|
|
|
|
|
|
|
func loadOSXFUSE() error {
|
|
|
|
cmd := exec.Command("/Library/Filesystems/osxfusefs.fs/Support/load_osxfusefs")
|
|
|
|
cmd.Dir = "/"
|
|
|
|
cmd.Stdout = os.Stdout
|
|
|
|
cmd.Stderr = os.Stderr
|
|
|
|
err := cmd.Run()
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2015-07-24 08:48:17 +03:00
|
|
|
func openOSXFUSEDev() (dev *os.File, err error) {
|
|
|
|
// Try each device name.
|
2015-07-24 08:39:04 +03:00
|
|
|
for i := uint64(0); ; i++ {
|
2015-07-24 08:48:17 +03:00
|
|
|
path := fmt.Sprintf("/dev/osxfuse%d", i)
|
|
|
|
dev, err = os.OpenFile(path, os.O_RDWR, 0000)
|
2015-07-24 08:39:04 +03:00
|
|
|
if os.IsNotExist(err) {
|
|
|
|
if i == 0 {
|
2015-07-24 08:48:17 +03:00
|
|
|
// Not even the first device was found. Fuse must not be loaded.
|
|
|
|
err = errNotLoaded
|
|
|
|
return
|
2015-07-24 08:39:04 +03:00
|
|
|
}
|
|
|
|
|
2015-07-24 08:48:17 +03:00
|
|
|
// Otherwise we've run out of kernel-provided devices
|
|
|
|
err = errNoAvail
|
|
|
|
return
|
2015-07-24 08:39:04 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if err2, ok := err.(*os.PathError); ok && err2.Err == syscall.EBUSY {
|
2015-07-24 08:48:17 +03:00
|
|
|
// This device is in use; try the next one.
|
2015-07-24 08:39:04 +03:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2015-07-24 08:48:17 +03:00
|
|
|
return
|
2015-07-24 08:39:04 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-24 08:51:18 +03:00
|
|
|
func callMount(
|
|
|
|
dir string,
|
2015-07-24 08:54:02 +03:00
|
|
|
cfg *MountConfig,
|
2015-07-24 08:51:18 +03:00
|
|
|
dev *os.File,
|
|
|
|
ready chan<- error) (err error) {
|
|
|
|
const bin = "/Library/Filesystems/osxfusefs.fs/Support/mount_osxfusefs"
|
2015-07-24 08:39:04 +03:00
|
|
|
|
2015-07-24 08:51:18 +03:00
|
|
|
// The mount helper doesn't understand any escaping.
|
2015-07-24 08:54:02 +03:00
|
|
|
for k, v := range cfg.toMap() {
|
2015-07-24 08:39:04 +03:00
|
|
|
if strings.Contains(k, ",") || strings.Contains(v, ",") {
|
2015-07-24 08:51:18 +03:00
|
|
|
return fmt.Errorf(
|
|
|
|
"mount options cannot contain commas on darwin: %q=%q",
|
|
|
|
k,
|
|
|
|
v)
|
2015-07-24 08:39:04 +03:00
|
|
|
}
|
|
|
|
}
|
2015-07-24 08:51:18 +03:00
|
|
|
|
|
|
|
// Call the mount helper, passing in the device file and saving output into a
|
|
|
|
// buffer.
|
2015-07-24 08:39:04 +03:00
|
|
|
cmd := exec.Command(
|
|
|
|
bin,
|
2015-07-24 08:54:02 +03:00
|
|
|
"-o", cfg.toOptionsString(),
|
2015-07-24 08:39:04 +03:00
|
|
|
// Tell osxfuse-kext how large our buffer is. It must split
|
|
|
|
// writes larger than this into multiple writes.
|
|
|
|
//
|
|
|
|
// OSXFUSE seems to ignore InitResponse.MaxWrite, and uses
|
|
|
|
// this instead.
|
2015-07-24 08:48:23 +03:00
|
|
|
"-o", "iosize="+strconv.FormatUint(buffer.MaxWriteSize, 10),
|
2015-07-24 08:39:04 +03:00
|
|
|
// refers to fd passed in cmd.ExtraFiles
|
|
|
|
"3",
|
|
|
|
dir,
|
|
|
|
)
|
2015-07-24 08:51:18 +03:00
|
|
|
cmd.ExtraFiles = []*os.File{dev}
|
2015-07-24 08:39:04 +03:00
|
|
|
cmd.Env = os.Environ()
|
|
|
|
cmd.Env = append(cmd.Env, "MOUNT_FUSEFS_CALL_BY_LIB=")
|
|
|
|
cmd.Env = append(cmd.Env, "MOUNT_FUSEFS_DAEMON_PATH="+bin)
|
2015-07-24 08:51:18 +03:00
|
|
|
|
2015-07-24 08:39:04 +03:00
|
|
|
var buf bytes.Buffer
|
|
|
|
cmd.Stdout = &buf
|
|
|
|
cmd.Stderr = &buf
|
|
|
|
|
2015-07-24 08:51:18 +03:00
|
|
|
err = cmd.Start()
|
2015-07-24 08:39:04 +03:00
|
|
|
if err != nil {
|
2015-07-24 08:51:18 +03:00
|
|
|
return
|
2015-07-24 08:39:04 +03:00
|
|
|
}
|
2015-07-24 08:51:18 +03:00
|
|
|
|
|
|
|
// In the background, wait for the command to complete.
|
2015-07-24 08:39:04 +03:00
|
|
|
go func() {
|
|
|
|
err := cmd.Wait()
|
|
|
|
if err != nil {
|
|
|
|
if buf.Len() > 0 {
|
|
|
|
output := buf.Bytes()
|
|
|
|
output = bytes.TrimRight(output, "\n")
|
2015-07-24 08:51:18 +03:00
|
|
|
err = fmt.Errorf("%v: %s", err, output)
|
2015-07-24 08:39:04 +03:00
|
|
|
}
|
|
|
|
}
|
2015-07-24 08:51:18 +03:00
|
|
|
|
|
|
|
ready <- err
|
2015-07-24 08:39:04 +03:00
|
|
|
}()
|
2015-07-24 08:51:18 +03:00
|
|
|
|
|
|
|
return
|
2015-07-24 08:39:04 +03:00
|
|
|
}
|
|
|
|
|
2015-07-24 08:45:16 +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,
|
2015-07-24 08:54:02 +03:00
|
|
|
cfg *MountConfig,
|
2015-07-24 08:45:16 +03:00
|
|
|
ready chan<- error) (dev *os.File, err error) {
|
|
|
|
// Open the device.
|
|
|
|
dev, err = openOSXFUSEDev()
|
|
|
|
|
|
|
|
// Special case: we may need to explicitly load osxfuse. Load it, then try
|
|
|
|
// again.
|
2015-07-24 08:39:04 +03:00
|
|
|
if err == errNotLoaded {
|
|
|
|
err = loadOSXFUSE()
|
|
|
|
if err != nil {
|
2015-07-24 08:45:16 +03:00
|
|
|
err = fmt.Errorf("loadOSXFUSE: %v", err)
|
|
|
|
return
|
2015-07-24 08:39:04 +03:00
|
|
|
}
|
2015-07-24 08:45:16 +03:00
|
|
|
|
|
|
|
dev, err = openOSXFUSEDev()
|
2015-07-24 08:39:04 +03:00
|
|
|
}
|
2015-07-24 08:45:16 +03:00
|
|
|
|
|
|
|
// Propagate errors.
|
2015-07-24 08:39:04 +03:00
|
|
|
if err != nil {
|
2015-07-24 08:45:16 +03:00
|
|
|
err = fmt.Errorf("openOSXFUSEDev: %v", err)
|
|
|
|
return
|
2015-07-24 08:39:04 +03:00
|
|
|
}
|
2015-07-24 08:45:16 +03:00
|
|
|
|
|
|
|
// Call the mount binary with the device.
|
2015-07-24 08:54:02 +03:00
|
|
|
err = callMount(dir, cfg, dev, ready)
|
2015-07-24 08:39:04 +03:00
|
|
|
if err != nil {
|
2015-07-24 08:45:16 +03:00
|
|
|
dev.Close()
|
|
|
|
err = fmt.Errorf("callMount: %v", err)
|
|
|
|
return
|
2015-07-24 08:39:04 +03:00
|
|
|
}
|
2015-07-24 08:45:16 +03:00
|
|
|
|
|
|
|
return
|
2015-07-24 08:39:04 +03:00
|
|
|
}
|