Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
J
jacobsa-fuse
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
jacobsa-fuse
Commits
081e9f4b
Commit
081e9f4b
authored
Sep 23, 2019
by
Michael Stapelberg
Committed by
GitHub
Sep 23, 2019
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #59 from stapelberg/mount
try mounting without fusermount(1) first
parents
659cc51b
53aac50a
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
109 additions
and
34 deletions
+109
-34
mount_config.go
mount_config.go
+7
-3
mount_linux.go
mount_linux.go
+102
-31
No files found.
mount_config.go
View file @
081e9f4b
...
...
@@ -223,10 +223,9 @@ func escapeOptionsKey(s string) (res string) {
return
}
// Create an options string suitable for passing to the mount helper.
func
(
c
*
MountConfig
)
toOptionsString
()
string
{
func
mapToOptionsString
(
opts
map
[
string
]
string
)
string
{
var
components
[]
string
for
k
,
v
:=
range
c
.
toMap
()
{
for
k
,
v
:=
range
opts
{
k
=
escapeOptionsKey
(
k
)
component
:=
k
...
...
@@ -239,3 +238,8 @@ func (c *MountConfig) toOptionsString() string {
return
strings
.
Join
(
components
,
","
)
}
// Create an options string suitable for passing to the mount helper.
func
(
c
*
MountConfig
)
toOptionsString
()
string
{
return
mapToOptionsString
(
c
.
toMap
())
}
mount_linux.go
View file @
081e9f4b
...
...
@@ -2,29 +2,21 @@ package fuse
import
(
"bytes"
"errors"
"fmt"
"net"
"os"
"os/exec"
"syscall"
)
// 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
"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
{
err
=
fmt
.
Errorf
(
"Socketpair: %v"
,
err
)
return
return
nil
,
fmt
.
Errorf
(
"Socketpair: %v"
,
err
)
}
// Wrap the sockets into os.File objects that we will pass off to fusermount.
...
...
@@ -51,23 +43,20 @@ func mount(
// Run the command.
err
=
cmd
.
Run
()
if
err
!=
nil
{
err
=
fmt
.
Errorf
(
"running fusermount: %v
\n\n
stderr:
\n
%s"
,
err
,
stderr
.
Bytes
())
return
return
nil
,
fmt
.
Errorf
(
"running fusermount: %v
\n\n
stderr:
\n
%s"
,
err
,
stderr
.
Bytes
())
}
// Wrap the socket file in a connection.
c
,
err
:=
net
.
FileConn
(
readFile
)
if
err
!=
nil
{
err
=
fmt
.
Errorf
(
"FileConn: %v"
,
err
)
return
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
{
err
=
fmt
.
Errorf
(
"Expected UnixConn, got %T"
,
c
)
return
return
nil
,
fmt
.
Errorf
(
"Expected UnixConn, got %T"
,
c
)
}
// Read a message.
...
...
@@ -75,21 +64,18 @@ func mount(
oob
:=
make
([]
byte
,
32
)
// expect 24 bytes
_
,
oobn
,
_
,
_
,
err
:=
uc
.
ReadMsgUnix
(
buf
,
oob
)
if
err
!=
nil
{
err
=
fmt
.
Errorf
(
"ReadMsgUnix: %v"
,
err
)
return
return
nil
,
fmt
.
Errorf
(
"ReadMsgUnix: %v"
,
err
)
}
// Parse the message.
scms
,
err
:=
syscall
.
ParseSocketControlMessage
(
oob
[
:
oobn
])
if
err
!=
nil
{
err
=
fmt
.
Errorf
(
"ParseSocketControlMessage: %v"
,
err
)
return
return
nil
,
fmt
.
Errorf
(
"ParseSocketControlMessage: %v"
,
err
)
}
// We expect one message.
if
len
(
scms
)
!=
1
{
err
=
fmt
.
Errorf
(
"expected 1 SocketControlMessage; got scms = %#v"
,
scms
)
return
return
nil
,
fmt
.
Errorf
(
"expected 1 SocketControlMessage; got scms = %#v"
,
scms
)
}
scm
:=
scms
[
0
]
...
...
@@ -97,17 +83,102 @@ func mount(
// Pull out the FD returned by fusermount
gotFds
,
err
:=
syscall
.
ParseUnixRights
(
&
scm
)
if
err
!=
nil
{
err
=
fmt
.
Errorf
(
"syscall.ParseUnixRights: %v"
,
err
)
return
return
nil
,
fmt
.
Errorf
(
"syscall.ParseUnixRights: %v"
,
err
)
}
if
len
(
gotFds
)
!=
1
{
err
=
fmt
.
Errorf
(
"wanted 1 fd; got %#v"
,
gotFds
)
return
return
nil
,
fmt
.
Errorf
(
"wanted 1 fd; got %#v"
,
gotFds
)
}
// Turn the FD into an os.File.
dev
=
os
.
NewFile
(
uintptr
(
gotFds
[
0
]),
"/dev/fuse"
)
return
os
.
NewFile
(
uintptr
(
gotFds
[
0
]),
"/dev/fuse"
),
nil
}
func
enableFunc
(
flag
uintptr
)
func
(
uintptr
)
uintptr
{
return
func
(
v
uintptr
)
uintptr
{
return
v
|
flag
}
}
func
disableFunc
(
flag
uintptr
)
func
(
uintptr
)
uintptr
{
return
func
(
v
uintptr
)
uintptr
{
return
v
&^
flag
}
}
// As per libfuse/fusermount.c:602: https://bit.ly/2SgtWYM#L602
var
mountflagopts
=
map
[
string
]
func
(
uintptr
)
uintptr
{
"rw"
:
enableFunc
(
unix
.
MS_RDONLY
),
"ro"
:
disableFunc
(
unix
.
MS_RDONLY
),
"suid"
:
disableFunc
(
unix
.
MS_NOSUID
),
"nosuid"
:
enableFunc
(
unix
.
MS_NOSUID
),
"dev"
:
disableFunc
(
unix
.
MS_NODEV
),
"nodev"
:
enableFunc
(
unix
.
MS_NODEV
),
"exec"
:
disableFunc
(
unix
.
MS_NOEXEC
),
"noexec"
:
enableFunc
(
unix
.
MS_NOEXEC
),
"async"
:
disableFunc
(
unix
.
MS_SYNCHRONOUS
),
"sync"
:
enableFunc
(
unix
.
MS_SYNCHRONOUS
),
"atime"
:
disableFunc
(
unix
.
MS_NOATIME
),
"noatime"
:
enableFunc
(
unix
.
MS_NOATIME
),
"dirsync"
:
enableFunc
(
unix
.
MS_DIRSYNC
),
}
var
errFallback
=
errors
.
New
(
"sentinel: fallback to fusermount(1)"
)
func
directmount
(
dir
string
,
cfg
*
MountConfig
)
(
*
os
.
File
,
error
)
{
// We use syscall.Open + os.NewFile instead of os.OpenFile so that the file
// is opened in blocking mode. When opened in non-blocking mode, the Go
// runtime tries to use poll(2), which does not work with /dev/fuse.
fd
,
err
:=
syscall
.
Open
(
"/dev/fuse"
,
syscall
.
O_RDWR
,
0644
)
if
err
!=
nil
{
return
nil
,
errFallback
}
dev
:=
os
.
NewFile
(
uintptr
(
fd
),
"/dev/fuse"
)
// As per libfuse/fusermount.c:847: https://bit.ly/2SgtWYM#L847
data
:=
fmt
.
Sprintf
(
"fd=%d,rootmode=40000,user_id=%d,group_id=%d"
,
dev
.
Fd
(),
os
.
Getuid
(),
os
.
Getgid
())
// As per libfuse/fusermount.c:749: https://bit.ly/2SgtWYM#L749
mountflag
:=
uintptr
(
unix
.
MS_NODEV
|
unix
.
MS_NOSUID
)
opts
:=
cfg
.
toMap
()
for
k
:=
range
opts
{
fn
,
ok
:=
mountflagopts
[
k
]
if
!
ok
{
continue
}
mountflag
=
fn
(
mountflag
)
delete
(
opts
,
k
)
}
delete
(
opts
,
"fsname"
)
// handled via fstype mount(2) parameter
data
+=
","
+
mapToOptionsString
(
opts
)
if
err
:=
unix
.
Mount
(
cfg
.
FSName
,
// source
dir
,
// target
"fuse"
,
// fstype
mountflag
,
// mountflag
data
,
// data
);
err
!=
nil
{
if
err
==
syscall
.
EPERM
{
return
nil
,
errFallback
}
return
nil
,
err
}
return
dev
,
nil
}
return
// 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
)
(
*
os
.
File
,
error
)
{
// On linux, mounting is never delayed.
ready
<-
nil
// Try mounting without fusermount(1) first: we might be running as root or
// have the CAP_SYS_ADMIN capability.
dev
,
err
:=
directmount
(
dir
,
cfg
)
if
err
==
errFallback
{
return
fusermount
(
dir
,
cfg
)
}
return
dev
,
err
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment