2020-05-20 12:39:20 +02:00
|
|
|
// Package mount implements a FUSE mounting system for rclone remotes.
|
2016-07-18 00:03:23 +02:00
|
|
|
|
2020-02-26 17:28:55 +01:00
|
|
|
// +build linux,go1.13 darwin,go1.13 freebsd,go1.13
|
2016-07-18 00:03:23 +02:00
|
|
|
|
|
|
|
package mount
|
|
|
|
|
|
|
|
import (
|
2018-07-18 17:21:35 +02:00
|
|
|
"fmt"
|
2016-09-09 09:39:19 +02:00
|
|
|
"os"
|
2017-03-17 11:20:28 +01:00
|
|
|
"os/signal"
|
|
|
|
"syscall"
|
2016-09-09 09:39:19 +02:00
|
|
|
|
2016-07-18 00:03:23 +02:00
|
|
|
"bazil.org/fuse"
|
2017-05-02 23:35:07 +02:00
|
|
|
fusefs "bazil.org/fuse/fs"
|
2017-11-19 23:03:49 +01:00
|
|
|
"github.com/okzk/sdnotify"
|
2016-07-18 00:03:23 +02:00
|
|
|
"github.com/pkg/errors"
|
2019-07-28 19:47:38 +02:00
|
|
|
"github.com/rclone/rclone/cmd/mountlib"
|
|
|
|
"github.com/rclone/rclone/fs"
|
|
|
|
"github.com/rclone/rclone/lib/atexit"
|
|
|
|
"github.com/rclone/rclone/vfs"
|
2016-07-18 00:03:23 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
func init() {
|
2020-02-11 13:05:43 +01:00
|
|
|
mountlib.NewMountCommand("mount", false, Mount)
|
2020-04-25 07:03:07 +02:00
|
|
|
// Add mount to rc
|
|
|
|
mountlib.AddRc("mount", mount)
|
2016-07-18 00:03:23 +02:00
|
|
|
}
|
|
|
|
|
2017-05-02 23:35:07 +02:00
|
|
|
// mountOptions configures the options from the command line flags
|
2020-07-22 18:58:49 +02:00
|
|
|
func mountOptions(VFS *vfs.VFS, device string) (options []fuse.MountOption) {
|
2017-05-02 23:35:07 +02:00
|
|
|
options = []fuse.MountOption{
|
2017-06-19 14:44:49 +02:00
|
|
|
fuse.MaxReadahead(uint32(mountlib.MaxReadAhead)),
|
2017-05-02 23:35:07 +02:00
|
|
|
fuse.Subtype("rclone"),
|
2018-05-03 10:45:24 +02:00
|
|
|
fuse.FSName(device),
|
|
|
|
fuse.VolumeName(mountlib.VolumeName),
|
2017-05-02 23:35:07 +02:00
|
|
|
|
|
|
|
// Options from benchmarking in the fuse module
|
|
|
|
//fuse.MaxReadahead(64 * 1024 * 1024),
|
|
|
|
//fuse.WritebackCache(),
|
|
|
|
}
|
2020-02-04 11:21:07 +01:00
|
|
|
if mountlib.AsyncRead {
|
|
|
|
options = append(options, fuse.AsyncRead())
|
|
|
|
}
|
2018-05-03 10:45:24 +02:00
|
|
|
if mountlib.NoAppleDouble {
|
|
|
|
options = append(options, fuse.NoAppleDouble())
|
|
|
|
}
|
|
|
|
if mountlib.NoAppleXattr {
|
|
|
|
options = append(options, fuse.NoAppleXattr())
|
|
|
|
}
|
2017-06-19 14:44:49 +02:00
|
|
|
if mountlib.AllowNonEmpty {
|
2017-05-02 23:35:07 +02:00
|
|
|
options = append(options, fuse.AllowNonEmptyMount())
|
|
|
|
}
|
2017-06-19 14:44:49 +02:00
|
|
|
if mountlib.AllowOther {
|
2017-05-02 23:35:07 +02:00
|
|
|
options = append(options, fuse.AllowOther())
|
|
|
|
}
|
2017-06-19 14:44:49 +02:00
|
|
|
if mountlib.AllowRoot {
|
2020-02-25 15:21:03 +01:00
|
|
|
// options = append(options, fuse.AllowRoot())
|
|
|
|
fs.Errorf(nil, "Ignoring --allow-root. Support has been removed upstream - see https://github.com/bazil/fuse/issues/144 for more info")
|
2017-05-02 23:35:07 +02:00
|
|
|
}
|
2017-06-19 14:44:49 +02:00
|
|
|
if mountlib.DefaultPermissions {
|
2017-05-02 23:35:07 +02:00
|
|
|
options = append(options, fuse.DefaultPermissions())
|
|
|
|
}
|
2020-07-22 18:58:49 +02:00
|
|
|
if VFS.Opt.ReadOnly {
|
2017-05-02 23:35:07 +02:00
|
|
|
options = append(options, fuse.ReadOnly())
|
|
|
|
}
|
2017-06-19 14:44:49 +02:00
|
|
|
if mountlib.WritebackCache {
|
2017-05-02 23:35:07 +02:00
|
|
|
options = append(options, fuse.WritebackCache())
|
|
|
|
}
|
2018-07-18 17:21:35 +02:00
|
|
|
if mountlib.DaemonTimeout != 0 {
|
|
|
|
options = append(options, fuse.DaemonTimeout(fmt.Sprint(int(mountlib.DaemonTimeout.Seconds()))))
|
|
|
|
}
|
2017-11-07 18:09:08 +01:00
|
|
|
if len(mountlib.ExtraOptions) > 0 {
|
2017-06-19 14:44:49 +02:00
|
|
|
fs.Errorf(nil, "-o/--option not supported with this FUSE backend")
|
|
|
|
}
|
2019-08-15 21:05:54 +02:00
|
|
|
if len(mountlib.ExtraFlags) > 0 {
|
2017-06-19 14:44:49 +02:00
|
|
|
fs.Errorf(nil, "--fuse-flag not supported with this FUSE backend")
|
|
|
|
}
|
2017-05-02 23:35:07 +02:00
|
|
|
return options
|
|
|
|
}
|
|
|
|
|
|
|
|
// mount the file system
|
|
|
|
//
|
|
|
|
// The mount point will be ready when this returns.
|
|
|
|
//
|
|
|
|
// returns an error, and an error channel for the serve process to
|
|
|
|
// report an error when fusermount is called.
|
2020-07-22 18:58:49 +02:00
|
|
|
func mount(VFS *vfs.VFS, mountpoint string) (<-chan error, func() error, error) {
|
|
|
|
f := VFS.Fs()
|
2017-05-02 23:35:07 +02:00
|
|
|
fs.Debugf(f, "Mounting on %q", mountpoint)
|
2020-07-22 18:58:49 +02:00
|
|
|
c, err := fuse.Mount(mountpoint, mountOptions(VFS, f.Name()+":"+f.Root())...)
|
2017-05-02 23:35:07 +02:00
|
|
|
if err != nil {
|
2020-07-22 18:58:49 +02:00
|
|
|
return nil, nil, err
|
2017-05-02 23:35:07 +02:00
|
|
|
}
|
|
|
|
|
2020-07-22 18:58:49 +02:00
|
|
|
filesys := NewFS(VFS)
|
2017-05-02 23:35:07 +02:00
|
|
|
server := fusefs.New(c, nil)
|
|
|
|
|
|
|
|
// Serve the mount point in the background returning error to errChan
|
|
|
|
errChan := make(chan error, 1)
|
|
|
|
go func() {
|
|
|
|
err := server.Serve(filesys)
|
|
|
|
closeErr := c.Close()
|
|
|
|
if err == nil {
|
|
|
|
err = closeErr
|
|
|
|
}
|
|
|
|
errChan <- err
|
|
|
|
}()
|
|
|
|
|
|
|
|
// check if the mount process has an error to report
|
|
|
|
<-c.Ready
|
|
|
|
if err := c.MountError; err != nil {
|
2020-07-22 18:58:49 +02:00
|
|
|
return nil, nil, err
|
2017-05-02 23:35:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
unmount := func() error {
|
2017-11-07 19:03:23 +01:00
|
|
|
// Shutdown the VFS
|
|
|
|
filesys.VFS.Shutdown()
|
2017-05-02 23:35:07 +02:00
|
|
|
return fuse.Unmount(mountpoint)
|
|
|
|
}
|
|
|
|
|
2020-07-22 18:58:49 +02:00
|
|
|
return errChan, unmount, nil
|
2017-05-02 23:35:07 +02:00
|
|
|
}
|
|
|
|
|
2016-07-18 00:03:23 +02:00
|
|
|
// Mount mounts the remote at mountpoint.
|
|
|
|
//
|
|
|
|
// If noModTime is set then it
|
2020-07-22 18:58:49 +02:00
|
|
|
func Mount(VFS *vfs.VFS, mountpoint string) error {
|
2017-06-19 14:44:49 +02:00
|
|
|
if mountlib.DebugFUSE {
|
2016-07-18 00:03:23 +02:00
|
|
|
fuse.Debug = func(msg interface{}) {
|
2017-02-09 12:01:20 +01:00
|
|
|
fs.Debugf("fuse", "%v", msg)
|
2016-07-18 00:03:23 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Mount it
|
2020-07-22 18:58:49 +02:00
|
|
|
errChan, unmount, err := mount(VFS, mountpoint)
|
2016-07-18 00:03:23 +02:00
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "failed to mount FUSE fs")
|
|
|
|
}
|
|
|
|
|
2017-05-07 14:04:20 +02:00
|
|
|
sigInt := make(chan os.Signal, 1)
|
|
|
|
signal.Notify(sigInt, syscall.SIGINT, syscall.SIGTERM)
|
|
|
|
sigHup := make(chan os.Signal, 1)
|
|
|
|
signal.Notify(sigHup, syscall.SIGHUP)
|
2018-05-12 11:40:44 +02:00
|
|
|
atexit.IgnoreSignals()
|
2019-11-11 23:08:52 +01:00
|
|
|
atexit.Register(func() {
|
|
|
|
_ = unmount()
|
|
|
|
})
|
2017-05-07 14:04:20 +02:00
|
|
|
|
2018-08-18 15:27:43 +02:00
|
|
|
if err := sdnotify.Ready(); err != nil && err != sdnotify.ErrSdNotifyNoSocket {
|
2017-11-19 23:03:49 +01:00
|
|
|
return errors.Wrap(err, "failed to notify systemd")
|
|
|
|
}
|
|
|
|
|
2017-05-07 14:04:20 +02:00
|
|
|
waitloop:
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
// umount triggered outside the app
|
|
|
|
case err = <-errChan:
|
|
|
|
break waitloop
|
|
|
|
// Program abort: umount
|
|
|
|
case <-sigInt:
|
|
|
|
err = unmount()
|
|
|
|
break waitloop
|
|
|
|
// user sent SIGHUP to clear the cache
|
|
|
|
case <-sigHup:
|
2020-07-22 18:58:49 +02:00
|
|
|
root, err := VFS.Root()
|
2017-05-07 14:04:20 +02:00
|
|
|
if err != nil {
|
2020-07-22 18:58:49 +02:00
|
|
|
fs.Errorf(VFS.Fs(), "Error reading root: %v", err)
|
2017-05-07 14:04:20 +02:00
|
|
|
} else {
|
|
|
|
root.ForgetAll()
|
|
|
|
}
|
|
|
|
}
|
2017-03-17 11:20:28 +01:00
|
|
|
}
|
|
|
|
|
2018-08-18 15:27:43 +02:00
|
|
|
_ = sdnotify.Stopping()
|
2016-07-18 00:03:23 +02:00
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "failed to umount FUSE fs")
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|