From 6a1e2f5502482ab2dab6d1519f8f8996a0d9593e Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Fri, 24 Jul 2015 16:10:41 +1000 Subject: [PATCH 1/4] Copied mount_linux.go. --- mount_linux.go | 116 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 mount_linux.go diff --git a/mount_linux.go b/mount_linux.go new file mode 100644 index 0000000..dfe70b5 --- /dev/null +++ b/mount_linux.go @@ -0,0 +1,116 @@ +package fuseshim + +import ( + "bufio" + "fmt" + "io" + "log" + "net" + "os" + "os/exec" + "sync" + "syscall" +) + +// Maximum file write size we are prepared to receive from the kernel. Linux +// appears to limit writes to 128 KiB. +const maxWrite = 128 * 1024 + +func lineLogger(wg *sync.WaitGroup, prefix string, r io.ReadCloser) { + defer wg.Done() + + scanner := bufio.NewScanner(r) + for scanner.Scan() { + switch line := scanner.Text(); line { + case `fusermount: failed to open /etc/fuse.conf: Permission denied`: + // Silence this particular message, it occurs way too + // commonly and isn't very relevant to whether the mount + // succeeds or not. + continue + default: + log.Printf("%s: %s", prefix, line) + } + } + if err := scanner.Err(); err != nil { + log.Printf("%s, error reading: %v", prefix, err) + } +} + +func mount(dir string, conf *mountConfig, ready chan<- struct{}, errp *error) (fusefd *os.File, err error) { + // linux mount is never delayed + close(ready) + + fds, err := syscall.Socketpair(syscall.AF_FILE, syscall.SOCK_STREAM, 0) + if err != nil { + return nil, fmt.Errorf("socketpair error: %v", err) + } + + writeFile := os.NewFile(uintptr(fds[0]), "fusermount-child-writes") + defer writeFile.Close() + + readFile := os.NewFile(uintptr(fds[1]), "fusermount-parent-reads") + defer readFile.Close() + + cmd := exec.Command( + "fusermount", + "-o", conf.getOptions(), + "--", + dir, + ) + cmd.Env = append(os.Environ(), "_FUSE_COMMFD=3") + + cmd.ExtraFiles = []*os.File{writeFile} + + var wg sync.WaitGroup + stdout, err := cmd.StdoutPipe() + if err != nil { + return nil, fmt.Errorf("setting up fusermount stderr: %v", err) + } + stderr, err := cmd.StderrPipe() + if err != nil { + return nil, fmt.Errorf("setting up fusermount stderr: %v", err) + } + + if err := cmd.Start(); err != nil { + return nil, fmt.Errorf("fusermount: %v", err) + } + wg.Add(2) + go lineLogger(&wg, "mount helper output", stdout) + go lineLogger(&wg, "mount helper error", stderr) + wg.Wait() + if err := cmd.Wait(); err != nil { + return nil, fmt.Errorf("fusermount: %v", err) + } + + c, err := net.FileConn(readFile) + if err != nil { + return nil, fmt.Errorf("FileConn from fusermount socket: %v", err) + } + defer c.Close() + + uc, ok := c.(*net.UnixConn) + if !ok { + return nil, fmt.Errorf("unexpected FileConn type; expected UnixConn, got %T", c) + } + + buf := make([]byte, 32) // expect 1 byte + oob := make([]byte, 32) // expect 24 bytes + _, oobn, _, _, err := uc.ReadMsgUnix(buf, oob) + scms, err := syscall.ParseSocketControlMessage(oob[:oobn]) + if err != nil { + return nil, fmt.Errorf("ParseSocketControlMessage: %v", err) + } + if len(scms) != 1 { + return nil, fmt.Errorf("expected 1 SocketControlMessage; got scms = %#v", scms) + } + scm := scms[0] + 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) + } + f := os.NewFile(uintptr(gotFds[0]), "/dev/fuse") + return f, nil +} From bd9024064aa1296e90374f1106cd13019132f6be Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Fri, 24 Jul 2015 16:10:49 +1000 Subject: [PATCH 2/4] Fixed the package name. --- mount_linux.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mount_linux.go b/mount_linux.go index dfe70b5..df87ec7 100644 --- a/mount_linux.go +++ b/mount_linux.go @@ -1,4 +1,4 @@ -package fuseshim +package fuse import ( "bufio" From ee57176a0bee565ae241c0c93e33e25623638170 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Fri, 24 Jul 2015 16:10:54 +1000 Subject: [PATCH 3/4] Trimmed a bit. --- mount_linux.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/mount_linux.go b/mount_linux.go index df87ec7..de7a060 100644 --- a/mount_linux.go +++ b/mount_linux.go @@ -12,10 +12,6 @@ import ( "syscall" ) -// Maximum file write size we are prepared to receive from the kernel. Linux -// appears to limit writes to 128 KiB. -const maxWrite = 128 * 1024 - func lineLogger(wg *sync.WaitGroup, prefix string, r io.ReadCloser) { defer wg.Done() From e9529e8d361480fd064b92c58b64752fc228e3d8 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Fri, 24 Jul 2015 16:18:07 +1000 Subject: [PATCH 4/4] Gave mount a makeover. --- mount_linux.go | 94 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 70 insertions(+), 24 deletions(-) diff --git a/mount_linux.go b/mount_linux.go index de7a060..94858bd 100644 --- a/mount_linux.go +++ b/mount_linux.go @@ -32,81 +32,127 @@ func lineLogger(wg *sync.WaitGroup, prefix string, r io.ReadCloser) { } } -func mount(dir string, conf *mountConfig, ready chan<- struct{}, errp *error) (fusefd *os.File, err error) { - // linux mount is never delayed - close(ready) +// 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) (dev *os.File, err error) { + // On linux, mounting is never delayed. + ready <- nil + // Create a socket pair. fds, err := syscall.Socketpair(syscall.AF_FILE, syscall.SOCK_STREAM, 0) if err != nil { - return nil, fmt.Errorf("socketpair error: %v", err) + err = fmt.Errorf("Socketpair: %v", err) + return } + // 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 pipes for stdout and stderr. cmd := exec.Command( "fusermount", - "-o", conf.getOptions(), + "-o", cfg.toOptionsString(), "--", dir, ) - cmd.Env = append(os.Environ(), "_FUSE_COMMFD=3") + cmd.Env = append(os.Environ(), "_FUSE_COMMFD=3") cmd.ExtraFiles = []*os.File{writeFile} - var wg sync.WaitGroup stdout, err := cmd.StdoutPipe() if err != nil { - return nil, fmt.Errorf("setting up fusermount stderr: %v", err) - } - stderr, err := cmd.StderrPipe() - if err != nil { - return nil, fmt.Errorf("setting up fusermount stderr: %v", err) + err = fmt.Errorf("StdoutPipe: %v", err) + return } - if err := cmd.Start(); err != nil { - return nil, fmt.Errorf("fusermount: %v", err) + stderr, err := cmd.StderrPipe() + if err != nil { + err = fmt.Errorf("StderrPipe: %v", err) + return } + + err = cmd.Start() + if err != nil { + err = fmt.Errorf("Starting fusermount: %v", err) + return + } + + // Log fusermount output until it closes stdout and stderr. + var wg sync.WaitGroup wg.Add(2) go lineLogger(&wg, "mount helper output", stdout) go lineLogger(&wg, "mount helper error", stderr) wg.Wait() - if err := cmd.Wait(); err != nil { - return nil, fmt.Errorf("fusermount: %v", err) + + // Wait for the command. + err = cmd.Wait() + if err != nil { + err = fmt.Errorf("fusermount: %v", err) + return } + // Wrap the socket file in a connection. c, err := net.FileConn(readFile) if err != nil { - return nil, fmt.Errorf("FileConn from fusermount socket: %v", err) + err = fmt.Errorf("FileConn: %v", err) + return } defer c.Close() + // We expect to have a Unix domain socket. uc, ok := c.(*net.UnixConn) if !ok { - return nil, fmt.Errorf("unexpected FileConn type; expected UnixConn, got %T", c) + err = fmt.Errorf("Expected UnixConn, got %T", c) + return } + // 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 { + err = fmt.Errorf("ReadMsgUnix: %v", err) + return + } + + // Parse the message. scms, err := syscall.ParseSocketControlMessage(oob[:oobn]) if err != nil { - return nil, fmt.Errorf("ParseSocketControlMessage: %v", err) + err = fmt.Errorf("ParseSocketControlMessage: %v", err) + return } + + // We expect one message. if len(scms) != 1 { - return nil, fmt.Errorf("expected 1 SocketControlMessage; got scms = %#v", scms) + err = fmt.Errorf("expected 1 SocketControlMessage; got scms = %#v", scms) + return } + 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) + err = fmt.Errorf("syscall.ParseUnixRights: %v", err) + return } + if len(gotFds) != 1 { - return nil, fmt.Errorf("wanted 1 fd; got %#v", gotFds) + err = fmt.Errorf("wanted 1 fd; got %#v", gotFds) + return } - f := os.NewFile(uintptr(gotFds[0]), "/dev/fuse") - return f, nil + + // Turn the FD into an os.File. + dev = os.NewFile(uintptr(gotFds[0]), "/dev/fuse") + + return }