github.com/10XDev/rclone@v1.52.3-0.20200626220027-16af9ab76b2a/cmd/mount/mount.go (about)

     1  // Package mount implements a FUSE mounting system for rclone remotes.
     2  
     3  // +build linux,go1.13 darwin,go1.13 freebsd,go1.13
     4  
     5  package mount
     6  
     7  import (
     8  	"fmt"
     9  	"os"
    10  	"os/signal"
    11  	"syscall"
    12  
    13  	"bazil.org/fuse"
    14  	fusefs "bazil.org/fuse/fs"
    15  	"github.com/okzk/sdnotify"
    16  	"github.com/pkg/errors"
    17  	"github.com/rclone/rclone/cmd/mountlib"
    18  	"github.com/rclone/rclone/fs"
    19  	"github.com/rclone/rclone/lib/atexit"
    20  	"github.com/rclone/rclone/vfs"
    21  	"github.com/rclone/rclone/vfs/vfsflags"
    22  )
    23  
    24  func init() {
    25  	mountlib.NewMountCommand("mount", false, Mount)
    26  	// Add mount to rc
    27  	mountlib.AddRc("mount", mount)
    28  }
    29  
    30  // mountOptions configures the options from the command line flags
    31  func mountOptions(device string) (options []fuse.MountOption) {
    32  	options = []fuse.MountOption{
    33  		fuse.MaxReadahead(uint32(mountlib.MaxReadAhead)),
    34  		fuse.Subtype("rclone"),
    35  		fuse.FSName(device),
    36  		fuse.VolumeName(mountlib.VolumeName),
    37  
    38  		// Options from benchmarking in the fuse module
    39  		//fuse.MaxReadahead(64 * 1024 * 1024),
    40  		//fuse.WritebackCache(),
    41  	}
    42  	if mountlib.AsyncRead {
    43  		options = append(options, fuse.AsyncRead())
    44  	}
    45  	if mountlib.NoAppleDouble {
    46  		options = append(options, fuse.NoAppleDouble())
    47  	}
    48  	if mountlib.NoAppleXattr {
    49  		options = append(options, fuse.NoAppleXattr())
    50  	}
    51  	if mountlib.AllowNonEmpty {
    52  		options = append(options, fuse.AllowNonEmptyMount())
    53  	}
    54  	if mountlib.AllowOther {
    55  		options = append(options, fuse.AllowOther())
    56  	}
    57  	if mountlib.AllowRoot {
    58  		// options = append(options, fuse.AllowRoot())
    59  		fs.Errorf(nil, "Ignoring --allow-root. Support has been removed upstream - see https://github.com/bazil/fuse/issues/144 for more info")
    60  	}
    61  	if mountlib.DefaultPermissions {
    62  		options = append(options, fuse.DefaultPermissions())
    63  	}
    64  	if vfsflags.Opt.ReadOnly {
    65  		options = append(options, fuse.ReadOnly())
    66  	}
    67  	if mountlib.WritebackCache {
    68  		options = append(options, fuse.WritebackCache())
    69  	}
    70  	if mountlib.DaemonTimeout != 0 {
    71  		options = append(options, fuse.DaemonTimeout(fmt.Sprint(int(mountlib.DaemonTimeout.Seconds()))))
    72  	}
    73  	if len(mountlib.ExtraOptions) > 0 {
    74  		fs.Errorf(nil, "-o/--option not supported with this FUSE backend")
    75  	}
    76  	if len(mountlib.ExtraFlags) > 0 {
    77  		fs.Errorf(nil, "--fuse-flag not supported with this FUSE backend")
    78  	}
    79  	return options
    80  }
    81  
    82  // mount the file system
    83  //
    84  // The mount point will be ready when this returns.
    85  //
    86  // returns an error, and an error channel for the serve process to
    87  // report an error when fusermount is called.
    88  func mount(f fs.Fs, mountpoint string) (*vfs.VFS, <-chan error, func() error, error) {
    89  	fs.Debugf(f, "Mounting on %q", mountpoint)
    90  	c, err := fuse.Mount(mountpoint, mountOptions(f.Name()+":"+f.Root())...)
    91  	if err != nil {
    92  		return nil, nil, nil, err
    93  	}
    94  
    95  	filesys := NewFS(f)
    96  	server := fusefs.New(c, nil)
    97  
    98  	// Serve the mount point in the background returning error to errChan
    99  	errChan := make(chan error, 1)
   100  	go func() {
   101  		err := server.Serve(filesys)
   102  		closeErr := c.Close()
   103  		if err == nil {
   104  			err = closeErr
   105  		}
   106  		errChan <- err
   107  	}()
   108  
   109  	// check if the mount process has an error to report
   110  	<-c.Ready
   111  	if err := c.MountError; err != nil {
   112  		return nil, nil, nil, err
   113  	}
   114  
   115  	unmount := func() error {
   116  		// Shutdown the VFS
   117  		filesys.VFS.Shutdown()
   118  		return fuse.Unmount(mountpoint)
   119  	}
   120  
   121  	return filesys.VFS, errChan, unmount, nil
   122  }
   123  
   124  // Mount mounts the remote at mountpoint.
   125  //
   126  // If noModTime is set then it
   127  func Mount(f fs.Fs, mountpoint string) error {
   128  	if mountlib.DebugFUSE {
   129  		fuse.Debug = func(msg interface{}) {
   130  			fs.Debugf("fuse", "%v", msg)
   131  		}
   132  	}
   133  
   134  	// Mount it
   135  	FS, errChan, unmount, err := mount(f, mountpoint)
   136  	if err != nil {
   137  		return errors.Wrap(err, "failed to mount FUSE fs")
   138  	}
   139  
   140  	sigInt := make(chan os.Signal, 1)
   141  	signal.Notify(sigInt, syscall.SIGINT, syscall.SIGTERM)
   142  	sigHup := make(chan os.Signal, 1)
   143  	signal.Notify(sigHup, syscall.SIGHUP)
   144  	atexit.IgnoreSignals()
   145  	atexit.Register(func() {
   146  		_ = unmount()
   147  	})
   148  
   149  	if err := sdnotify.Ready(); err != nil && err != sdnotify.ErrSdNotifyNoSocket {
   150  		return errors.Wrap(err, "failed to notify systemd")
   151  	}
   152  
   153  waitloop:
   154  	for {
   155  		select {
   156  		// umount triggered outside the app
   157  		case err = <-errChan:
   158  			break waitloop
   159  		// Program abort: umount
   160  		case <-sigInt:
   161  			err = unmount()
   162  			break waitloop
   163  		// user sent SIGHUP to clear the cache
   164  		case <-sigHup:
   165  			root, err := FS.Root()
   166  			if err != nil {
   167  				fs.Errorf(f, "Error reading root: %v", err)
   168  			} else {
   169  				root.ForgetAll()
   170  			}
   171  		}
   172  	}
   173  
   174  	_ = sdnotify.Stopping()
   175  	if err != nil {
   176  		return errors.Wrap(err, "failed to umount FUSE fs")
   177  	}
   178  
   179  	return nil
   180  }