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 }