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