github.com/ncw/rclone@v1.48.1-0.20190724201158-a35aa1360e3e/cmd/mountlib/mount.go (about) 1 package mountlib 2 3 import ( 4 "io" 5 "log" 6 "os" 7 "path/filepath" 8 "runtime" 9 "strings" 10 "time" 11 12 "github.com/ncw/rclone/cmd" 13 "github.com/ncw/rclone/fs" 14 "github.com/ncw/rclone/fs/config" 15 "github.com/ncw/rclone/fs/config/flags" 16 "github.com/ncw/rclone/vfs" 17 "github.com/ncw/rclone/vfs/vfsflags" 18 "github.com/pkg/errors" 19 "github.com/spf13/cobra" 20 ) 21 22 // Options set by command line flags 23 var ( 24 DebugFUSE = false 25 AllowNonEmpty = false 26 AllowRoot = false 27 AllowOther = false 28 DefaultPermissions = false 29 WritebackCache = false 30 Daemon = false 31 MaxReadAhead fs.SizeSuffix = 128 * 1024 32 ExtraOptions []string 33 ExtraFlags []string 34 AttrTimeout = 1 * time.Second // how long the kernel caches attribute for 35 VolumeName string 36 NoAppleDouble = true // use noappledouble by default 37 NoAppleXattr = false // do not use noapplexattr by default 38 DaemonTimeout time.Duration // OSXFUSE only 39 ) 40 41 func init() { 42 // DaemonTimeout defaults to non zero for macOS and freebsd 43 if runtime.GOOS == "darwin" || runtime.GOOS == "freebsd" { 44 DaemonTimeout = 15 * time.Minute 45 } 46 } 47 48 // Check is folder is empty 49 func checkMountEmpty(mountpoint string) error { 50 fp, fpErr := os.Open(mountpoint) 51 52 if fpErr != nil { 53 return errors.Wrap(fpErr, "Can not open: "+mountpoint) 54 } 55 defer fs.CheckClose(fp, &fpErr) 56 57 _, fpErr = fp.Readdirnames(1) 58 59 // directory is not empty 60 if fpErr != io.EOF { 61 var e error 62 var errorMsg = "Directory is not empty: " + mountpoint + " If you want to mount it anyway use: --allow-non-empty option" 63 if fpErr == nil { 64 e = errors.New(errorMsg) 65 } else { 66 e = errors.Wrap(fpErr, errorMsg) 67 } 68 return e 69 } 70 return nil 71 } 72 73 // Check the root doesn't overlap the mountpoint 74 func checkMountpointOverlap(root, mountpoint string) error { 75 abs := func(x string) string { 76 if absX, err := filepath.EvalSymlinks(x); err == nil { 77 x = absX 78 } 79 if absX, err := filepath.Abs(x); err == nil { 80 x = absX 81 } 82 x = filepath.ToSlash(x) 83 if !strings.HasSuffix(x, "/") { 84 x += "/" 85 } 86 return x 87 } 88 rootAbs, mountpointAbs := abs(root), abs(mountpoint) 89 if strings.HasPrefix(rootAbs, mountpointAbs) || strings.HasPrefix(mountpointAbs, rootAbs) { 90 return errors.Errorf("mount point %q and directory to be mounted %q mustn't overlap", mountpoint, root) 91 } 92 return nil 93 } 94 95 // NewMountCommand makes a mount command with the given name and Mount function 96 func NewMountCommand(commandName string, Mount func(f fs.Fs, mountpoint string) error) *cobra.Command { 97 var commandDefintion = &cobra.Command{ 98 Use: commandName + " remote:path /path/to/mountpoint", 99 Short: `Mount the remote as file system on a mountpoint.`, 100 Long: ` 101 rclone ` + commandName + ` allows Linux, FreeBSD, macOS and Windows to 102 mount any of Rclone's cloud storage systems as a file system with 103 FUSE. 104 105 First set up your remote using ` + "`rclone config`" + `. Check it works with ` + "`rclone ls`" + ` etc. 106 107 Start the mount like this 108 109 rclone ` + commandName + ` remote:path/to/files /path/to/local/mount 110 111 Or on Windows like this where X: is an unused drive letter 112 113 rclone ` + commandName + ` remote:path/to/files X: 114 115 When the program ends, either via Ctrl+C or receiving a SIGINT or SIGTERM signal, 116 the mount is automatically stopped. 117 118 The umount operation can fail, for example when the mountpoint is busy. 119 When that happens, it is the user's responsibility to stop the mount manually with 120 121 # Linux 122 fusermount -u /path/to/local/mount 123 # OS X 124 umount /path/to/local/mount 125 126 ### Installing on Windows 127 128 To run rclone ` + commandName + ` on Windows, you will need to 129 download and install [WinFsp](http://www.secfs.net/winfsp/). 130 131 WinFsp is an [open source](https://github.com/billziss-gh/winfsp) 132 Windows File System Proxy which makes it easy to write user space file 133 systems for Windows. It provides a FUSE emulation layer which rclone 134 uses combination with 135 [cgofuse](https://github.com/billziss-gh/cgofuse). Both of these 136 packages are by Bill Zissimopoulos who was very helpful during the 137 implementation of rclone ` + commandName + ` for Windows. 138 139 #### Windows caveats 140 141 Note that drives created as Administrator are not visible by other 142 accounts (including the account that was elevated as 143 Administrator). So if you start a Windows drive from an Administrative 144 Command Prompt and then try to access the same drive from Explorer 145 (which does not run as Administrator), you will not be able to see the 146 new drive. 147 148 The easiest way around this is to start the drive from a normal 149 command prompt. It is also possible to start a drive from the SYSTEM 150 account (using [the WinFsp.Launcher 151 infrastructure](https://github.com/billziss-gh/winfsp/wiki/WinFsp-Service-Architecture)) 152 which creates drives accessible for everyone on the system or 153 alternatively using [the nssm service manager](https://nssm.cc/usage). 154 155 ### Limitations 156 157 Without the use of "--vfs-cache-mode" this can only write files 158 sequentially, it can only seek when reading. This means that many 159 applications won't work with their files on an rclone mount without 160 "--vfs-cache-mode writes" or "--vfs-cache-mode full". See the [File 161 Caching](#file-caching) section for more info. 162 163 The bucket based remotes (eg Swift, S3, Google Compute Storage, B2, 164 Hubic) won't work from the root - you will need to specify a bucket, 165 or a path within the bucket. So ` + "`swift:`" + ` won't work whereas 166 ` + "`swift:bucket`" + ` will as will ` + "`swift:bucket/path`" + `. 167 None of these support the concept of directories, so empty 168 directories will have a tendency to disappear once they fall out of 169 the directory cache. 170 171 Only supported on Linux, FreeBSD, OS X and Windows at the moment. 172 173 ### rclone ` + commandName + ` vs rclone sync/copy 174 175 File systems expect things to be 100% reliable, whereas cloud storage 176 systems are a long way from 100% reliable. The rclone sync/copy 177 commands cope with this with lots of retries. However rclone ` + commandName + ` 178 can't use retries in the same way without making local copies of the 179 uploads. Look at the [file caching](#file-caching) 180 for solutions to make ` + commandName + ` more reliable. 181 182 ### Attribute caching 183 184 You can use the flag --attr-timeout to set the time the kernel caches 185 the attributes (size, modification time etc) for directory entries. 186 187 The default is "1s" which caches files just long enough to avoid 188 too many callbacks to rclone from the kernel. 189 190 In theory 0s should be the correct value for filesystems which can 191 change outside the control of the kernel. However this causes quite a 192 few problems such as 193 [rclone using too much memory](https://github.com/ncw/rclone/issues/2157), 194 [rclone not serving files to samba](https://forum.rclone.org/t/rclone-1-39-vs-1-40-mount-issue/5112) 195 and [excessive time listing directories](https://github.com/ncw/rclone/issues/2095#issuecomment-371141147). 196 197 The kernel can cache the info about a file for the time given by 198 "--attr-timeout". You may see corruption if the remote file changes 199 length during this window. It will show up as either a truncated file 200 or a file with garbage on the end. With "--attr-timeout 1s" this is 201 very unlikely but not impossible. The higher you set "--attr-timeout" 202 the more likely it is. The default setting of "1s" is the lowest 203 setting which mitigates the problems above. 204 205 If you set it higher ('10s' or '1m' say) then the kernel will call 206 back to rclone less often making it more efficient, however there is 207 more chance of the corruption issue above. 208 209 If files don't change on the remote outside of the control of rclone 210 then there is no chance of corruption. 211 212 This is the same as setting the attr_timeout option in mount.fuse. 213 214 ### Filters 215 216 Note that all the rclone filters can be used to select a subset of the 217 files to be visible in the mount. 218 219 ### systemd 220 221 When running rclone ` + commandName + ` as a systemd service, it is possible 222 to use Type=notify. In this case the service will enter the started state 223 after the mountpoint has been successfully set up. 224 Units having the rclone ` + commandName + ` service specified as a requirement 225 will see all files and folders immediately in this mode. 226 227 ### chunked reading ### 228 229 --vfs-read-chunk-size will enable reading the source objects in parts. 230 This can reduce the used download quota for some remotes by requesting only chunks 231 from the remote that are actually read at the cost of an increased number of requests. 232 233 When --vfs-read-chunk-size-limit is also specified and greater than --vfs-read-chunk-size, 234 the chunk size for each open file will get doubled for each chunk read, until the 235 specified value is reached. A value of -1 will disable the limit and the chunk size will 236 grow indefinitely. 237 238 With --vfs-read-chunk-size 100M and --vfs-read-chunk-size-limit 0 the following 239 parts will be downloaded: 0-100M, 100M-200M, 200M-300M, 300M-400M and so on. 240 When --vfs-read-chunk-size-limit 500M is specified, the result would be 241 0-100M, 100M-300M, 300M-700M, 700M-1200M, 1200M-1700M and so on. 242 243 Chunked reading will only work with --vfs-cache-mode < full, as the file will always 244 be copied to the vfs cache before opening with --vfs-cache-mode full. 245 ` + vfs.Help, 246 Run: func(command *cobra.Command, args []string) { 247 cmd.CheckArgs(2, 2, command, args) 248 249 if Daemon { 250 config.PassConfigKeyForDaemonization = true 251 } 252 253 mountpoint := args[1] 254 fdst := cmd.NewFsDir(args) 255 if fdst.Name() == "" || fdst.Name() == "local" { 256 err := checkMountpointOverlap(fdst.Root(), mountpoint) 257 if err != nil { 258 log.Fatalf("Fatal error: %v", err) 259 } 260 } 261 262 // Show stats if the user has specifically requested them 263 if cmd.ShowStats() { 264 defer cmd.StartStats()() 265 } 266 267 // Skip checkMountEmpty if --allow-non-empty flag is used or if 268 // the Operating System is Windows 269 if !AllowNonEmpty && runtime.GOOS != "windows" { 270 err := checkMountEmpty(mountpoint) 271 if err != nil { 272 log.Fatalf("Fatal error: %v", err) 273 } 274 } 275 276 // Work out the volume name, removing special 277 // characters from it if necessary 278 if VolumeName == "" { 279 VolumeName = fdst.Name() + ":" + fdst.Root() 280 } 281 VolumeName = strings.Replace(VolumeName, ":", " ", -1) 282 VolumeName = strings.Replace(VolumeName, "/", " ", -1) 283 VolumeName = strings.TrimSpace(VolumeName) 284 285 // Start background task if --background is specified 286 if Daemon { 287 daemonized := startBackgroundMode() 288 if daemonized { 289 return 290 } 291 } 292 293 err := Mount(fdst, mountpoint) 294 if err != nil { 295 log.Fatalf("Fatal error: %v", err) 296 } 297 }, 298 } 299 300 // Register the command 301 cmd.Root.AddCommand(commandDefintion) 302 303 // Add flags 304 flagSet := commandDefintion.Flags() 305 flags.BoolVarP(flagSet, &DebugFUSE, "debug-fuse", "", DebugFUSE, "Debug the FUSE internals - needs -v.") 306 // mount options 307 flags.BoolVarP(flagSet, &AllowNonEmpty, "allow-non-empty", "", AllowNonEmpty, "Allow mounting over a non-empty directory.") 308 flags.BoolVarP(flagSet, &AllowRoot, "allow-root", "", AllowRoot, "Allow access to root user.") 309 flags.BoolVarP(flagSet, &AllowOther, "allow-other", "", AllowOther, "Allow access to other users.") 310 flags.BoolVarP(flagSet, &DefaultPermissions, "default-permissions", "", DefaultPermissions, "Makes kernel enforce access control based on the file mode.") 311 flags.BoolVarP(flagSet, &WritebackCache, "write-back-cache", "", WritebackCache, "Makes kernel buffer writes before sending them to rclone. Without this, writethrough caching is used.") 312 flags.FVarP(flagSet, &MaxReadAhead, "max-read-ahead", "", "The number of bytes that can be prefetched for sequential reads.") 313 flags.DurationVarP(flagSet, &AttrTimeout, "attr-timeout", "", AttrTimeout, "Time for which file/directory attributes are cached.") 314 flags.StringArrayVarP(flagSet, &ExtraOptions, "option", "o", []string{}, "Option for libfuse/WinFsp. Repeat if required.") 315 flags.StringArrayVarP(flagSet, &ExtraFlags, "fuse-flag", "", []string{}, "Flags or arguments to be passed direct to libfuse/WinFsp. Repeat if required.") 316 flags.BoolVarP(flagSet, &Daemon, "daemon", "", Daemon, "Run mount as a daemon (background mode).") 317 flags.StringVarP(flagSet, &VolumeName, "volname", "", VolumeName, "Set the volume name (not supported by all OSes).") 318 flags.DurationVarP(flagSet, &DaemonTimeout, "daemon-timeout", "", DaemonTimeout, "Time limit for rclone to respond to kernel (not supported by all OSes).") 319 320 if runtime.GOOS == "darwin" { 321 flags.BoolVarP(flagSet, &NoAppleDouble, "noappledouble", "", NoAppleDouble, "Sets the OSXFUSE option noappledouble.") 322 flags.BoolVarP(flagSet, &NoAppleXattr, "noapplexattr", "", NoAppleXattr, "Sets the OSXFUSE option noapplexattr.") 323 } 324 325 // Add in the generic flags 326 vfsflags.AddFlags(flagSet) 327 328 return commandDefintion 329 } 330 331 // ClipBlocks clips the blocks pointed to to the OS max 332 func ClipBlocks(b *uint64) { 333 var max uint64 334 switch runtime.GOOS { 335 case "windows": 336 if runtime.GOARCH == "386" { 337 max = (1 << 32) - 1 338 } else { 339 max = (1 << 43) - 1 340 } 341 case "darwin": 342 // OSX FUSE only supports 32 bit number of blocks 343 // https://github.com/osxfuse/osxfuse/issues/396 344 max = (1 << 32) - 1 345 default: 346 // no clipping 347 return 348 } 349 if *b > max { 350 *b = max 351 } 352 }