Demonstrating mounting a FUSE filesystem using github.com/containerd/containerd/mount instead of fusermount.
$ make
2019/06/18 15:45:29 Mounting hellofs on "./mnt"
$ cd ./mnt
$ ls -lah
total 0
-rw-r--r-- 0 root root 5 Dec 31 1969 hello
$ cat hello
helloCtrl-C out of make will trigger the unmount. If you were using ./mnt when unmounting, you may get this message:
^C2019/06/18 15:47:31 Unmounting "./mnt"
panic: /bin/fusermount: failed to unmount /home/edgarl/go/src/github.com/hinshun/hellofs/mnt: Device or resource busy
(code exit status 1)
// ...
$ ls
ls: cannot access 'mnt': Transport endpoint is not connected
mnt vendor go.mod go.sum hellofs LICENSE main.go Makefile README.mdYou can cleanup the mount by ensuring no processes are still using ./mnt and then run make umount:
// ...
import (
// ...
cmount "github.com/containerd/containerd/mount"
)
// ...
// Create a FUSE FS on the specified mount point. The returned
// mount point is always absolute.
func mount(mountPoint string, opts *MountOptions, ready chan<- error) (fd int, err error) {
user, err := user.Current()
if err != nil {
return 0, err
}
f, err := os.OpenFile("/dev/fuse", os.O_RDWR, 0666)
if err != nil {
return 0, err
}
fd = int(f.Fd())
m := cmount.Mount{
Type: fmt.Sprintf("fuse.%s", opts.Name),
Source: opts.FsName,
Options: []string{
"nosuid",
"nodev",
fmt.Sprintf("fd=%d", fd),
fmt.Sprintf("rootmode=%#o", syscall.S_IFDIR),
fmt.Sprintf("user_id=%s", user.Uid),
fmt.Sprintf("group_id=%s", user.Gid),
},
}
if opts.AllowOther {
m.Options = append(m.Options, "allow_other")
}
m.Options = append(m.Options, opts.Options...)
err = m.Mount(mountPoint)
if err != nil {
return 0, err
}
close(ready)
return fd, err
}FUSE is an userspace filesystem framework, it consists of a kernel module fuse.ko that has a FUSE VFS interfaced by a device /dev/fuse on your system. Many FUSE implementations rely on libfuse (a C library to interact with /dev/fuse and its protocol), and fusermount (a binary owned by root but executable by users with a suid bit set, so that users can use the mount FUSE filesystems without being root). Libraries like github.com/hanwen/go-fuse and github.com/bazil/fuse use fusermount to mount but not libfuse.
fusermount is a binary commonly used to mount FUSE filesystems. FUSE implementers typically call fusermount as a subprocess, and provides a binary like sshfs that users invoke to mount the filesystem instead of calling fusermount, mount(8) or mount(2) directly. The subprocess needs to have an environment variable _FUSE_COMMFD set. The FUSE implementation needs to create a file descriptor, set _FUSE_COMMFD=<fd>, which fusermount will use to pass back the control fd open on /dev/fuse.
mount(8) is a binary that mounts a filesystem. Internally it uses mount(2) the syscall in order to actually get the kernel to mount. mount(8) registers filesystems using executables with the name /sbin/mount.*. These executables need to fulfill a specific interface, that mount(8) will invoke on the /sbin/mount.* binary. You can add a /sbin/mount.<your-fuse-mount-wrapper> that should daemonize a process running your FUSE server. However, mount(2) will not know about these /sbin/mount.* binaries, this is just an implementation detail of mount(8). There is a /sbin/mount.fuse that one of these mount wrappers that will execute fusermount under the hood.
mount(2) is the mount syscall. It only knows about filesystems registered in the kernel (in kernel filesystems, including fuse, register themselves via register_filesystem), which are visible via cat /proc/filesystems. However, there seems to be undocumented behaviour in that if you call mount(2) with a type is prefixed like fuse.<subtype>, it will actually mount via FUSE. The subtype doesn't seem to actually matter other than being the type of the mount when you run mount -l.
The mount(2) signature looks like this:
#include <sys/mount.h>
int mount(const char *source, const char *target,
const char *filesystemtype, unsigned long mountflags,
const void *data);For filesystemtype of fuse.* type:
sourceis unused, you can use it to supply the FUSE name likesshfs.targetis the mountpointfilesystemtypelooks likefuse.*(i.e.fuse.sshfs).mountflagsare flags you can find onmount(2)'s manpage. If mountflags is empty, then it creates a mount. You can supplymountflagsto runmount(2)on existing mounts to change properties like making it readonly, change propagation types, etc. In some of those cases,source,filesystemtypeorfilesystemtypeanddataare ignored.datais an optional buffer to provide filesystem specific options.
For fuse.* filesystem types, the options for data are delimited by comma, and must have these 4(?) fields:
fdis the file descriptor you get when you open/dev/fusewithO_RDWR, this is the control FD where FUSE protocol messages are sent between the FUSE VFS in the kernel, and your FUSE server in userspace.rootmodeis file mode of your mountpoint bitwise AND withS_IFMT, its a bitmask to show only bits of the file mode that says if its a regular, directory, device file, etc. I believe it expects it to beS_IFDIR, which is the bit that represents it being a directory.user_idis the uid of the user mounting.group_idis the gid of the user mounting.
So the FUSE workflow is:
- Open
/dev/fusewithO_RDWRto producecontrol FD. mount(2)withsource=<fuse-name>,target=<mountpoint>,filesystemtype=fuse.<fuse-name>,mountflags=0,data=fd=<control FD>,rootmode=<S_IFDIR in octal str>,user_id=<uid>,group_id=<gid>.- FUSE server reads initialization request from
control FDand responds with some data about its implementation. - FUSE starts listening on
control FD. - I/O performed on file in
mountpointis handled by FUSE VFS in kernel, which sends a request to FUSE server viacontrol FD. - FUSE server responds via
control FD, to complete the I/O.
- http://man7.org/linux/man-pages/man8/mount.fuse.8.html
- https://stackoverflow.com/questions/1554178/how-to-register-fuse-filesystem-type-with-mount8-and-fstab
- https://stackoverflow.com/questions/6469557/mounting-fuse-gives-invalid-argument-error-in-python
- https://unix.stackexchange.com/questions/118090/mounting-mmcblk0p1-failed-with-invalid-argument
- https://engineering.facile.it/blog/eng/write-filesystem-fuse/
- https://github.com/GoogleCloudPlatform/gcsfuse/blob/master/docs/mounting.md#mount8-and-fstab-compatibility
- https://github.com/GoogleCloudPlatform/gcsfuse/blob/master/tools/mount_gcsfuse/main.go