1package fuse 2 3import ( 4 "fmt" 5 "log" 6 "net" 7 "os" 8 "os/exec" 9 "strings" 10 "sync" 11 "syscall" 12) 13 14func handleFusermountStderr(errCh chan<- error) func(line string) (ignore bool) { 15 return func(line string) (ignore bool) { 16 if line == `fusermount: failed to open /etc/fuse.conf: Permission denied` { 17 // Silence this particular message, it occurs way too 18 // commonly and isn't very relevant to whether the mount 19 // succeeds or not. 20 return true 21 } 22 23 const ( 24 noMountpointPrefix = `fusermount: failed to access mountpoint ` 25 noMountpointSuffix = `: No such file or directory` 26 ) 27 if strings.HasPrefix(line, noMountpointPrefix) && strings.HasSuffix(line, noMountpointSuffix) { 28 // re-extract it from the error message in case some layer 29 // changed the path 30 mountpoint := line[len(noMountpointPrefix) : len(line)-len(noMountpointSuffix)] 31 err := &MountpointDoesNotExistError{ 32 Path: mountpoint, 33 } 34 select { 35 case errCh <- err: 36 return true 37 default: 38 // not the first error; fall back to logging it 39 return false 40 } 41 } 42 43 return false 44 } 45} 46 47// isBoringFusermountError returns whether the Wait error is 48// uninteresting; exit status 1 is. 49func isBoringFusermountError(err error) bool { 50 if err, ok := err.(*exec.ExitError); ok && err.Exited() { 51 if status, ok := err.Sys().(syscall.WaitStatus); ok && status.ExitStatus() == 1 { 52 return true 53 } 54 } 55 return false 56} 57 58func mount(dir string, conf *mountConfig, ready chan<- struct{}, errp *error) (fusefd *os.File, err error) { 59 // linux mount is never delayed 60 close(ready) 61 62 fds, err := syscall.Socketpair(syscall.AF_FILE, syscall.SOCK_STREAM, 0) 63 if err != nil { 64 return nil, fmt.Errorf("socketpair error: %v", err) 65 } 66 67 writeFile := os.NewFile(uintptr(fds[0]), "fusermount-child-writes") 68 defer writeFile.Close() 69 70 readFile := os.NewFile(uintptr(fds[1]), "fusermount-parent-reads") 71 defer readFile.Close() 72 73 cmd := exec.Command( 74 "fusermount", 75 "-o", conf.getOptions(), 76 "--", 77 dir, 78 ) 79 cmd.Env = append(os.Environ(), "_FUSE_COMMFD=3") 80 81 cmd.ExtraFiles = []*os.File{writeFile} 82 83 var wg sync.WaitGroup 84 stdout, err := cmd.StdoutPipe() 85 if err != nil { 86 return nil, fmt.Errorf("setting up fusermount stderr: %v", err) 87 } 88 stderr, err := cmd.StderrPipe() 89 if err != nil { 90 return nil, fmt.Errorf("setting up fusermount stderr: %v", err) 91 } 92 93 if err := cmd.Start(); err != nil { 94 return nil, fmt.Errorf("fusermount: %v", err) 95 } 96 helperErrCh := make(chan error, 1) 97 wg.Add(2) 98 go lineLogger(&wg, "mount helper output", neverIgnoreLine, stdout) 99 go lineLogger(&wg, "mount helper error", handleFusermountStderr(helperErrCh), stderr) 100 wg.Wait() 101 if err := cmd.Wait(); err != nil { 102 // see if we have a better error to report 103 select { 104 case helperErr := <-helperErrCh: 105 // log the Wait error if it's not what we expected 106 if !isBoringFusermountError(err) { 107 log.Printf("mount helper failed: %v", err) 108 } 109 // and now return what we grabbed from stderr as the real 110 // error 111 return nil, helperErr 112 default: 113 // nope, fall back to generic message 114 } 115 116 return nil, fmt.Errorf("fusermount: %v", err) 117 } 118 119 c, err := net.FileConn(readFile) 120 if err != nil { 121 return nil, fmt.Errorf("FileConn from fusermount socket: %v", err) 122 } 123 defer c.Close() 124 125 uc, ok := c.(*net.UnixConn) 126 if !ok { 127 return nil, fmt.Errorf("unexpected FileConn type; expected UnixConn, got %T", c) 128 } 129 130 buf := make([]byte, 32) // expect 1 byte 131 oob := make([]byte, 32) // expect 24 bytes 132 _, oobn, _, _, err := uc.ReadMsgUnix(buf, oob) 133 scms, err := syscall.ParseSocketControlMessage(oob[:oobn]) 134 if err != nil { 135 return nil, fmt.Errorf("ParseSocketControlMessage: %v", err) 136 } 137 if len(scms) != 1 { 138 return nil, fmt.Errorf("expected 1 SocketControlMessage; got scms = %#v", scms) 139 } 140 scm := scms[0] 141 gotFds, err := syscall.ParseUnixRights(&scm) 142 if err != nil { 143 return nil, fmt.Errorf("syscall.ParseUnixRights: %v", err) 144 } 145 if len(gotFds) != 1 { 146 return nil, fmt.Errorf("wanted 1 fd; got %#v", gotFds) 147 } 148 f := os.NewFile(uintptr(gotFds[0]), "/dev/fuse") 149 return f, nil 150} 151