@@ -2,29 +2,21 @@ package fuse
22
33import (
44 "bytes"
5+ "errors"
56 "fmt"
67 "net"
78 "os"
89 "os/exec"
910 "syscall"
10- )
1111
12- // Begin the process of mounting at the given directory, returning a connection
13- // to the kernel. Mounting continues in the background, and is complete when an
14- // error is written to the supplied channel. The file system may need to
15- // service the connection in order for mounting to complete.
16- func mount (
17- dir string ,
18- cfg * MountConfig ,
19- ready chan <- error ) (dev * os.File , err error ) {
20- // On linux, mounting is never delayed.
21- ready <- nil
12+ "golang.org/x/sys/unix"
13+ )
2214
15+ func fusermount (dir string , cfg * MountConfig ) (* os.File , error ) {
2316 // Create a socket pair.
2417 fds , err := syscall .Socketpair (syscall .AF_FILE , syscall .SOCK_STREAM , 0 )
2518 if err != nil {
26- err = fmt .Errorf ("Socketpair: %v" , err )
27- return
19+ return nil , fmt .Errorf ("Socketpair: %v" , err )
2820 }
2921
3022 // Wrap the sockets into os.File objects that we will pass off to fusermount.
@@ -51,63 +43,142 @@ func mount(
5143 // Run the command.
5244 err = cmd .Run ()
5345 if err != nil {
54- err = fmt .Errorf ("running fusermount: %v\n \n stderr:\n %s" , err , stderr .Bytes ())
55- return
46+ return nil , fmt .Errorf ("running fusermount: %v\n \n stderr:\n %s" , err , stderr .Bytes ())
5647 }
5748
5849 // Wrap the socket file in a connection.
5950 c , err := net .FileConn (readFile )
6051 if err != nil {
61- err = fmt .Errorf ("FileConn: %v" , err )
62- return
52+ return nil , fmt .Errorf ("FileConn: %v" , err )
6353 }
6454 defer c .Close ()
6555
6656 // We expect to have a Unix domain socket.
6757 uc , ok := c .(* net.UnixConn )
6858 if ! ok {
69- err = fmt .Errorf ("Expected UnixConn, got %T" , c )
70- return
59+ return nil , fmt .Errorf ("Expected UnixConn, got %T" , c )
7160 }
7261
7362 // Read a message.
7463 buf := make ([]byte , 32 ) // expect 1 byte
7564 oob := make ([]byte , 32 ) // expect 24 bytes
7665 _ , oobn , _ , _ , err := uc .ReadMsgUnix (buf , oob )
7766 if err != nil {
78- err = fmt .Errorf ("ReadMsgUnix: %v" , err )
79- return
67+ return nil , fmt .Errorf ("ReadMsgUnix: %v" , err )
8068 }
8169
8270 // Parse the message.
8371 scms , err := syscall .ParseSocketControlMessage (oob [:oobn ])
8472 if err != nil {
85- err = fmt .Errorf ("ParseSocketControlMessage: %v" , err )
86- return
73+ return nil , fmt .Errorf ("ParseSocketControlMessage: %v" , err )
8774 }
8875
8976 // We expect one message.
9077 if len (scms ) != 1 {
91- err = fmt .Errorf ("expected 1 SocketControlMessage; got scms = %#v" , scms )
92- return
78+ return nil , fmt .Errorf ("expected 1 SocketControlMessage; got scms = %#v" , scms )
9379 }
9480
9581 scm := scms [0 ]
9682
9783 // Pull out the FD returned by fusermount
9884 gotFds , err := syscall .ParseUnixRights (& scm )
9985 if err != nil {
100- err = fmt .Errorf ("syscall.ParseUnixRights: %v" , err )
101- return
86+ return nil , fmt .Errorf ("syscall.ParseUnixRights: %v" , err )
10287 }
10388
10489 if len (gotFds ) != 1 {
105- err = fmt .Errorf ("wanted 1 fd; got %#v" , gotFds )
106- return
90+ return nil , fmt .Errorf ("wanted 1 fd; got %#v" , gotFds )
10791 }
10892
10993 // Turn the FD into an os.File.
110- dev = os .NewFile (uintptr (gotFds [0 ]), "/dev/fuse" )
94+ return os .NewFile (uintptr (gotFds [0 ]), "/dev/fuse" ), nil
95+ }
96+
97+ func enableFunc (flag uintptr ) func (uintptr ) uintptr {
98+ return func (v uintptr ) uintptr {
99+ return v | flag
100+ }
101+ }
102+
103+ func disableFunc (flag uintptr ) func (uintptr ) uintptr {
104+ return func (v uintptr ) uintptr {
105+ return v &^ flag
106+ }
107+ }
108+
109+ // As per libfuse/fusermount.c:602: https://bit.ly/2SgtWYM#L602
110+ var mountflagopts = map [string ]func (uintptr ) uintptr {
111+ "rw" : enableFunc (unix .MS_RDONLY ),
112+ "ro" : disableFunc (unix .MS_RDONLY ),
113+ "suid" : disableFunc (unix .MS_NOSUID ),
114+ "nosuid" : enableFunc (unix .MS_NOSUID ),
115+ "dev" : disableFunc (unix .MS_NODEV ),
116+ "nodev" : enableFunc (unix .MS_NODEV ),
117+ "exec" : disableFunc (unix .MS_NOEXEC ),
118+ "noexec" : enableFunc (unix .MS_NOEXEC ),
119+ "async" : disableFunc (unix .MS_SYNCHRONOUS ),
120+ "sync" : enableFunc (unix .MS_SYNCHRONOUS ),
121+ "atime" : disableFunc (unix .MS_NOATIME ),
122+ "noatime" : enableFunc (unix .MS_NOATIME ),
123+ "dirsync" : enableFunc (unix .MS_DIRSYNC ),
124+ }
125+
126+ var errFallback = errors .New ("sentinel: fallback to fusermount(1)" )
127+
128+ func directmount (dir string , cfg * MountConfig ) (* os.File , error ) {
129+ // We use syscall.Open + os.NewFile instead of os.OpenFile so that the file
130+ // is opened in blocking mode. When opened in non-blocking mode, the Go
131+ // runtime tries to use poll(2), which does not work with /dev/fuse.
132+ fd , err := syscall .Open ("/dev/fuse" , syscall .O_RDWR , 0644 )
133+ if err != nil {
134+ return nil , errFallback
135+ }
136+ dev := os .NewFile (uintptr (fd ), "/dev/fuse" )
137+ // As per libfuse/fusermount.c:847: https://bit.ly/2SgtWYM#L847
138+ data := fmt .Sprintf ("fd=%d,rootmode=40000,user_id=%d,group_id=%d" ,
139+ dev .Fd (), os .Getuid (), os .Getgid ())
140+ // As per libfuse/fusermount.c:749: https://bit.ly/2SgtWYM#L749
141+ mountflag := uintptr (unix .MS_NODEV | unix .MS_NOSUID )
142+ opts := cfg .toMap ()
143+ for k := range opts {
144+ fn , ok := mountflagopts [k ]
145+ if ! ok {
146+ continue
147+ }
148+ mountflag = fn (mountflag )
149+ delete (opts , k )
150+ }
151+ delete (opts , "fsname" ) // handled via fstype mount(2) parameter
152+ data += "," + mapToOptionsString (opts )
153+ if err := unix .Mount (
154+ cfg .FSName , // source
155+ dir , // target
156+ "fuse" , // fstype
157+ mountflag , // mountflag
158+ data , // data
159+ ); err != nil {
160+ if err == syscall .EPERM {
161+ return nil , errFallback
162+
163+ }
164+ return nil , err
165+ }
166+ return dev , nil
167+ }
111168
112- return
169+ // Begin the process of mounting at the given directory, returning a connection
170+ // to the kernel. Mounting continues in the background, and is complete when an
171+ // error is written to the supplied channel. The file system may need to
172+ // service the connection in order for mounting to complete.
173+ func mount (dir string , cfg * MountConfig , ready chan <- error ) (* os.File , error ) {
174+ // On linux, mounting is never delayed.
175+ ready <- nil
176+
177+ // Try mounting without fusermount(1) first: we might be running as root or
178+ // have the CAP_SYS_ADMIN capability.
179+ dev , err := directmount (dir , cfg )
180+ if err == errFallback {
181+ return fusermount (dir , cfg )
182+ }
183+ return dev , err
113184}
0 commit comments