From e9529e8d361480fd064b92c58b64752fc228e3d8 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Fri, 24 Jul 2015 16:18:07 +1000 Subject: [PATCH] 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 }