From dd552c22dff5e1c69b25d1f6525d9e9e0e348dc9 Mon Sep 17 00:00:00 2001 From: Penghan Wang <2732352+wph95@users.noreply.github.com> Date: Thu, 3 Mar 2022 16:56:50 +0800 Subject: [PATCH 1/3] fix fusego can't support macFuse (osxFuse v4 --- mount_darwin.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/mount_darwin.go b/mount_darwin.go index 58a727bd..3bedeaee 100644 --- a/mount_darwin.go +++ b/mount_darwin.go @@ -41,6 +41,15 @@ type osxfuseInstallation struct { var ( osxfuseInstallations = []osxfuseInstallation{ + // v4 + { + DevicePrefix: "/dev/macfuse", + Load: "/Library/Filesystems/macfuse.fs/Contents/Resources/load_macfuse", + Mount: "/Library/Filesystems/macfuse.fs/Contents/Resources/mount_macfuse", + DaemonVar: "_FUSE_DAEMON_PATH", + LibVar: "_FUSE_CALL_BY_LIB", + UseCommFD: true, + }, // v3 { DevicePrefix: "/dev/osxfuse", From 965e1e8875897b477196965b3a1b3a1974ca000a Mon Sep 17 00:00:00 2001 From: Penghan Wang <2732352+wph95@users.noreply.github.com> Date: Thu, 3 Mar 2022 17:01:35 +0800 Subject: [PATCH 2/3] Update mount_darwin.go --- mount_darwin.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/mount_darwin.go b/mount_darwin.go index 3bedeaee..8820a3e0 100644 --- a/mount_darwin.go +++ b/mount_darwin.go @@ -47,8 +47,6 @@ var ( Load: "/Library/Filesystems/macfuse.fs/Contents/Resources/load_macfuse", Mount: "/Library/Filesystems/macfuse.fs/Contents/Resources/mount_macfuse", DaemonVar: "_FUSE_DAEMON_PATH", - LibVar: "_FUSE_CALL_BY_LIB", - UseCommFD: true, }, // v3 { From f7de1a3ec4a72b4863382d5cf34a627ecf7dc681 Mon Sep 17 00:00:00 2001 From: penghawa Date: Thu, 3 Mar 2022 17:50:55 +0800 Subject: [PATCH 3/3] copy from https://github.com/jacobsa/fuse/pull/106/files --- mount.go | 82 +++++++++++++++++++++++++++++++++++++++++ mount_darwin.go | 97 +++++++++++++++++++++++++++++++++++++------------ mount_linux.go | 96 +++++------------------------------------------- 3 files changed, 165 insertions(+), 110 deletions(-) diff --git a/mount.go b/mount.go index dce72a8e..543f55bc 100644 --- a/mount.go +++ b/mount.go @@ -17,7 +17,10 @@ package fuse import ( "context" "fmt" + "net" "os" + "os/exec" + "syscall" ) // Server is an interface for any type that knows how to serve ops read from a @@ -93,3 +96,82 @@ func Mount( return mfs, nil } + +func fusermount(binary string, argv []string, additionalEnv []string, wait bool) (*os.File, error) { + // Create a socket pair. + fds, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_STREAM, 0) + if err != nil { + return nil, fmt.Errorf("Socketpair: %v", err) + } + + // Wrap the sockets into os.File objects that we will pass off to fusermount. + writeFile := os.NewFile(uintptr(fds[0]), "fusermount-child-writes") + defer writeFile.Close() + + readFile := os.NewFile(uintptr(fds[1]), "fusermount-parent-reads") + defer readFile.Close() + + // Start fusermount/mount_macfuse/mount_osxfuse. + cmd := exec.Command(binary, argv...) + cmd.Env = append(os.Environ(), "_FUSE_COMMFD=3") + cmd.Env = append(cmd.Env, additionalEnv...) + cmd.ExtraFiles = []*os.File{writeFile} + cmd.Stderr = os.Stderr + + // Run the command. + if wait { + err = cmd.Run() + } else { + err = cmd.Start() + } + if err != nil { + return nil, fmt.Errorf("running %v: %v", binary, err) + } + + // Wrap the socket file in a connection. + c, err := net.FileConn(readFile) + if err != nil { + return nil, fmt.Errorf("FileConn: %v", err) + } + defer c.Close() + + // We expect to have a Unix domain socket. + uc, ok := c.(*net.UnixConn) + if !ok { + return nil, fmt.Errorf("Expected UnixConn, got %T", c) + } + + // Read a message. + buf := make([]byte, 32) // expect 1 byte + oob := make([]byte, 32) // expect 24 bytes + _, oobn, _, _, err := uc.ReadMsgUnix(buf, oob) + if err != nil { + return nil, fmt.Errorf("ReadMsgUnix: %v", err) + } + + // Parse the message. + scms, err := syscall.ParseSocketControlMessage(oob[:oobn]) + if err != nil { + return nil, fmt.Errorf("ParseSocketControlMessage: %v", err) + } + + // We expect one message. + if len(scms) != 1 { + return nil, fmt.Errorf("expected 1 SocketControlMessage; got scms = %#v", scms) + } + + scm := scms[0] + + // Pull out the FD returned by fusermount + gotFds, err := syscall.ParseUnixRights(&scm) + if err != nil { + return nil, fmt.Errorf("syscall.ParseUnixRights: %v", err) + } + + if len(gotFds) != 1 { + return nil, fmt.Errorf("wanted 1 fd; got %#v", gotFds) + } + + // Turn the FD into an os.File. + return os.NewFile(uintptr(gotFds[0]), "/dev/fuse"), nil +} diff --git a/mount_darwin.go b/mount_darwin.go index 8820a3e0..35b1acef 100644 --- a/mount_darwin.go +++ b/mount_darwin.go @@ -37,6 +37,13 @@ type osxfuseInstallation struct { // Environment variable used to pass the path to the executable calling the // mount helper. DaemonVar string + + // Environment variable used to pass the "called by library" flag. + LibVar string + + // Open device manually (false) or receive the FD through a UNIX socket, + // like with fusermount (true) + UseCommFD bool } var ( @@ -47,13 +54,17 @@ var ( Load: "/Library/Filesystems/macfuse.fs/Contents/Resources/load_macfuse", Mount: "/Library/Filesystems/macfuse.fs/Contents/Resources/mount_macfuse", DaemonVar: "_FUSE_DAEMON_PATH", + LibVar: "_FUSE_CALL_BY_LIB", + UseCommFD: true, }, + // v3 { DevicePrefix: "/dev/osxfuse", Load: "/Library/Filesystems/osxfuse.fs/Contents/Resources/load_osxfuse", Mount: "/Library/Filesystems/osxfuse.fs/Contents/Resources/mount_osxfuse", DaemonVar: "MOUNT_OSXFUSE_DAEMON_PATH", + LibVar: "MOUNT_OSXFUSE_CALL_BY_LIB", }, // v2 @@ -62,6 +73,7 @@ var ( Load: "/Library/Filesystems/osxfusefs.fs/Support/load_osxfusefs", Mount: "/Library/Filesystems/osxfusefs.fs/Support/mount_osxfusefs", DaemonVar: "MOUNT_FUSEFS_DAEMON_PATH", + LibVar: "MOUNT_FUSEFS_CALL_BY_LIB", }, } ) @@ -99,50 +111,60 @@ func openOSXFUSEDev(devPrefix string) (dev *os.File, err error) { } } -func callMount( - bin string, - daemonVar string, - dir string, - cfg *MountConfig, - dev *os.File, - ready chan<- error) error { +func convertMountArgs(daemonVar string, libVar string, + cfg *MountConfig) ([]string, []string, error) { // The mount helper doesn't understand any escaping. for k, v := range cfg.toMap() { if strings.Contains(k, ",") || strings.Contains(v, ",") { - return fmt.Errorf( + return nil, nil, fmt.Errorf( "mount options cannot contain commas on darwin: %q=%q", k, v) } } - // Call the mount helper, passing in the device file and saving output into a - // buffer. - cmd := exec.Command( - bin, + env := []string{libVar + "="} + if daemonVar != "" { + env = append(env, daemonVar+"="+os.Args[0]) + } + argv := []string{ "-o", cfg.toOptionsString(), // 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. - "-o", "iosize="+strconv.FormatUint(buffer.MaxWriteSize, 10), + "-o", "iosize=" + strconv.FormatUint(buffer.MaxWriteSize, 10), + } + + return argv, env, nil +} + +func callMount( + bin string, + daemonVar string, + libVar string, + dir string, + cfg *MountConfig, + dev *os.File, + ready chan<- error) error { + + argv, env, err := convertMountArgs(daemonVar, libVar, cfg) + if err != nil { + return err + } + + // Call the mount helper, passing in the device file and saving output into a + // buffer. + argv = append(argv, // refers to fd passed in cmd.ExtraFiles "3", dir, ) + cmd := exec.Command(bin, argv...) cmd.ExtraFiles = []*os.File{dev} - cmd.Env = os.Environ() - // OSXFUSE <3.3.0 - cmd.Env = append(cmd.Env, "MOUNT_FUSEFS_CALL_BY_LIB=") - // OSXFUSE >=3.3.0 - cmd.Env = append(cmd.Env, "MOUNT_OSXFUSE_CALL_BY_LIB=") - - daemon := os.Args[0] - if daemonVar != "" { - cmd.Env = append(cmd.Env, daemonVar+"="+daemon) - } + cmd.Env = env var buf bytes.Buffer cmd.Stdout = &buf @@ -169,6 +191,23 @@ func callMount( return nil } +func callMountCommFD( + bin string, + daemonVar string, + libVar string, + dir string, + cfg *MountConfig) (*os.File, error) { + + argv, env, err := convertMountArgs(daemonVar, libVar, cfg) + if err != nil { + return nil, err + } + env = append(env, "_FUSE_COMMVERS=2") + argv = append(argv, dir) + + return fusermount(bin, argv, env, false) +} + // 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 @@ -184,6 +223,16 @@ func mount( continue } + if loc.UseCommFD { + // Call the mount binary with the device. + ready <- nil + dev, err = callMountCommFD(loc.Mount, loc.DaemonVar, loc.LibVar, dir, cfg) + if err != nil { + return nil, fmt.Errorf("callMount: %v", err) + } + return + } + // Open the device. dev, err = openOSXFUSEDev(loc.DevicePrefix) @@ -204,7 +253,7 @@ func mount( } // Call the mount binary with the device. - if err := callMount(loc.Mount, loc.DaemonVar, dir, cfg, dev, ready); err != nil { + if err := callMount(loc.Mount, loc.DaemonVar, loc.LibVar, dir, cfg, dev, ready); err != nil { dev.Close() return nil, fmt.Errorf("callMount: %v", err) } diff --git a/mount_linux.go b/mount_linux.go index b0348d39..9a764b80 100644 --- a/mount_linux.go +++ b/mount_linux.go @@ -1,99 +1,14 @@ package fuse import ( - "bytes" "errors" "fmt" - "net" "os" - "os/exec" "syscall" "golang.org/x/sys/unix" ) -func fusermount(dir string, cfg *MountConfig) (*os.File, error) { - // Create a socket pair. - fds, err := syscall.Socketpair(syscall.AF_FILE, syscall.SOCK_STREAM, 0) - if err != nil { - return nil, fmt.Errorf("Socketpair: %v", err) - } - - // Wrap the sockets into os.File objects that we will pass off to fusermount. - writeFile := os.NewFile(uintptr(fds[0]), "fusermount-child-writes") - defer writeFile.Close() - - readFile := os.NewFile(uintptr(fds[1]), "fusermount-parent-reads") - defer readFile.Close() - - // Start fusermount, passing it a buffer in which to write stderr. - var stderr bytes.Buffer - - cmd := exec.Command( - "fusermount", - "-o", cfg.toOptionsString(), - "--", - dir, - ) - - cmd.Env = append(os.Environ(), "_FUSE_COMMFD=3") - cmd.ExtraFiles = []*os.File{writeFile} - cmd.Stderr = &stderr - - // Run the command. - err = cmd.Run() - if err != nil { - return nil, fmt.Errorf("running fusermount: %v\n\nstderr:\n%s", err, stderr.Bytes()) - } - - // Wrap the socket file in a connection. - c, err := net.FileConn(readFile) - if err != nil { - return nil, fmt.Errorf("FileConn: %v", err) - } - defer c.Close() - - // We expect to have a Unix domain socket. - uc, ok := c.(*net.UnixConn) - if !ok { - return nil, fmt.Errorf("Expected UnixConn, got %T", c) - } - - // Read a message. - buf := make([]byte, 32) // expect 1 byte - oob := make([]byte, 32) // expect 24 bytes - _, oobn, _, _, err := uc.ReadMsgUnix(buf, oob) - if err != nil { - return nil, fmt.Errorf("ReadMsgUnix: %v", err) - } - - // Parse the message. - scms, err := syscall.ParseSocketControlMessage(oob[:oobn]) - if err != nil { - return nil, fmt.Errorf("ParseSocketControlMessage: %v", err) - } - - // We expect one message. - if len(scms) != 1 { - return nil, fmt.Errorf("expected 1 SocketControlMessage; got scms = %#v", scms) - } - - scm := scms[0] - - // Pull out the FD returned by fusermount - gotFds, err := syscall.ParseUnixRights(&scm) - if err != nil { - return nil, fmt.Errorf("syscall.ParseUnixRights: %v", err) - } - - if len(gotFds) != 1 { - return nil, fmt.Errorf("wanted 1 fd; got %#v", gotFds) - } - - // Turn the FD into an os.File. - return os.NewFile(uintptr(gotFds[0]), "/dev/fuse"), nil -} - func enableFunc(flag uintptr) func(uintptr) uintptr { return func(v uintptr) uintptr { return v | flag @@ -183,7 +98,16 @@ func mount(dir string, cfg *MountConfig, ready chan<- error) (*os.File, error) { // have the CAP_SYS_ADMIN capability. dev, err := directmount(dir, cfg) if err == errFallback { - return fusermount(dir, cfg) + fusermountPath, err := findFusermount() + if err != nil { + return nil, err + } + argv := []string{ + "-o", cfg.toOptionsString(), + "--", + dir, + } + return fusermount(fusermountPath, argv, []string{}, true) } return dev, err }