github.com/xhghs/rclone@v1.51.1-0.20200430155106-e186a28cced8/vfs/vfs.go (about) 1 // Package vfs provides a virtual filing system layer over rclone's 2 // native objects. 3 // 4 // It attempts to behave in a similar way to Go's filing system 5 // manipulation code in the os package. The same named function 6 // should behave in an identical fashion. The objects also obey Go's 7 // standard interfaces. 8 // 9 // Note that paths don't start or end with /, so the root directory 10 // may be referred to as "". However Stat strips slashes so you can 11 // use paths with slashes in. 12 // 13 // It also includes directory caching 14 // 15 // The vfs package returns Error values to signal precisely which 16 // error conditions have ocurred. It may also return general errors 17 // it receives. It tries to use os Error values (eg os.ErrExist) 18 // where possible. 19 package vfs 20 21 import ( 22 "context" 23 "fmt" 24 "os" 25 "path" 26 "runtime" 27 "strings" 28 "sync" 29 "sync/atomic" 30 "time" 31 32 "github.com/rclone/rclone/fs" 33 "github.com/rclone/rclone/fs/log" 34 ) 35 36 // DefaultOpt is the default values uses for Opt 37 var DefaultOpt = Options{ 38 NoModTime: false, 39 NoChecksum: false, 40 NoSeek: false, 41 DirCacheTime: 5 * 60 * time.Second, 42 PollInterval: time.Minute, 43 ReadOnly: false, 44 Umask: 0, 45 UID: ^uint32(0), // these values instruct WinFSP-FUSE to use the current user 46 GID: ^uint32(0), // overriden for non windows in mount_unix.go 47 DirPerms: os.FileMode(0777), 48 FilePerms: os.FileMode(0666), 49 CacheMode: CacheModeOff, 50 CacheMaxAge: 3600 * time.Second, 51 CachePollInterval: 60 * time.Second, 52 ChunkSize: 128 * fs.MebiByte, 53 ChunkSizeLimit: -1, 54 CacheMaxSize: -1, 55 CaseInsensitive: runtime.GOOS == "windows" || runtime.GOOS == "darwin", // default to true on Windows and Mac, false otherwise 56 } 57 58 // Node represents either a directory (*Dir) or a file (*File) 59 type Node interface { 60 os.FileInfo 61 IsFile() bool 62 Inode() uint64 63 SetModTime(modTime time.Time) error 64 Sync() error 65 Remove() error 66 RemoveAll() error 67 DirEntry() fs.DirEntry 68 VFS() *VFS 69 Open(flags int) (Handle, error) 70 Truncate(size int64) error 71 Path() string 72 } 73 74 // Check interfaces 75 var ( 76 _ Node = (*File)(nil) 77 _ Node = (*Dir)(nil) 78 ) 79 80 // Nodes is a slice of Node 81 type Nodes []Node 82 83 // Sort functions 84 func (ns Nodes) Len() int { return len(ns) } 85 func (ns Nodes) Swap(i, j int) { ns[i], ns[j] = ns[j], ns[i] } 86 func (ns Nodes) Less(i, j int) bool { return ns[i].Path() < ns[j].Path() } 87 88 // Noder represents something which can return a node 89 type Noder interface { 90 fmt.Stringer 91 Node() Node 92 } 93 94 // Check interfaces 95 var ( 96 _ Noder = (*File)(nil) 97 _ Noder = (*Dir)(nil) 98 _ Noder = (*ReadFileHandle)(nil) 99 _ Noder = (*WriteFileHandle)(nil) 100 _ Noder = (*RWFileHandle)(nil) 101 _ Noder = (*DirHandle)(nil) 102 ) 103 104 // OsFiler is the methods on *os.File 105 type OsFiler interface { 106 Chdir() error 107 Chmod(mode os.FileMode) error 108 Chown(uid, gid int) error 109 Close() error 110 Fd() uintptr 111 Name() string 112 Read(b []byte) (n int, err error) 113 ReadAt(b []byte, off int64) (n int, err error) 114 Readdir(n int) ([]os.FileInfo, error) 115 Readdirnames(n int) (names []string, err error) 116 Seek(offset int64, whence int) (ret int64, err error) 117 Stat() (os.FileInfo, error) 118 Sync() error 119 Truncate(size int64) error 120 Write(b []byte) (n int, err error) 121 WriteAt(b []byte, off int64) (n int, err error) 122 WriteString(s string) (n int, err error) 123 } 124 125 // Handle is the interface statisified by open files or directories. 126 // It is the methods on *os.File, plus a few more useful for FUSE 127 // filingsystems. Not all of them are supported. 128 type Handle interface { 129 OsFiler 130 // Additional methods useful for FUSE filesystems 131 Flush() error 132 Release() error 133 Node() Node 134 // Size() int64 135 } 136 137 // baseHandle implements all the missing methods 138 type baseHandle struct{} 139 140 func (h baseHandle) Chdir() error { return ENOSYS } 141 func (h baseHandle) Chmod(mode os.FileMode) error { return ENOSYS } 142 func (h baseHandle) Chown(uid, gid int) error { return ENOSYS } 143 func (h baseHandle) Close() error { return ENOSYS } 144 func (h baseHandle) Fd() uintptr { return 0 } 145 func (h baseHandle) Name() string { return "" } 146 func (h baseHandle) Read(b []byte) (n int, err error) { return 0, ENOSYS } 147 func (h baseHandle) ReadAt(b []byte, off int64) (n int, err error) { return 0, ENOSYS } 148 func (h baseHandle) Readdir(n int) ([]os.FileInfo, error) { return nil, ENOSYS } 149 func (h baseHandle) Readdirnames(n int) (names []string, err error) { return nil, ENOSYS } 150 func (h baseHandle) Seek(offset int64, whence int) (ret int64, err error) { return 0, ENOSYS } 151 func (h baseHandle) Stat() (os.FileInfo, error) { return nil, ENOSYS } 152 func (h baseHandle) Sync() error { return nil } 153 func (h baseHandle) Truncate(size int64) error { return ENOSYS } 154 func (h baseHandle) Write(b []byte) (n int, err error) { return 0, ENOSYS } 155 func (h baseHandle) WriteAt(b []byte, off int64) (n int, err error) { return 0, ENOSYS } 156 func (h baseHandle) WriteString(s string) (n int, err error) { return 0, ENOSYS } 157 func (h baseHandle) Flush() (err error) { return ENOSYS } 158 func (h baseHandle) Release() (err error) { return ENOSYS } 159 func (h baseHandle) Node() Node { return nil } 160 161 //func (h baseHandle) Size() int64 { return 0 } 162 163 // Check interfaces 164 var ( 165 _ OsFiler = (*os.File)(nil) 166 _ Handle = (*baseHandle)(nil) 167 _ Handle = (*ReadFileHandle)(nil) 168 _ Handle = (*WriteFileHandle)(nil) 169 _ Handle = (*DirHandle)(nil) 170 ) 171 172 // VFS represents the top level filing system 173 type VFS struct { 174 f fs.Fs 175 root *Dir 176 Opt Options 177 cache *cache 178 cancel context.CancelFunc 179 usageMu sync.Mutex 180 usageTime time.Time 181 usage *fs.Usage 182 pollChan chan time.Duration 183 } 184 185 // Options is options for creating the vfs 186 type Options struct { 187 NoSeek bool // don't allow seeking if set 188 NoChecksum bool // don't check checksums if set 189 ReadOnly bool // if set VFS is read only 190 NoModTime bool // don't read mod times for files 191 DirCacheTime time.Duration // how long to consider directory listing cache valid 192 PollInterval time.Duration 193 Umask int 194 UID uint32 195 GID uint32 196 DirPerms os.FileMode 197 FilePerms os.FileMode 198 ChunkSize fs.SizeSuffix // if > 0 read files in chunks 199 ChunkSizeLimit fs.SizeSuffix // if > ChunkSize double the chunk size after each chunk until reached 200 CacheMode CacheMode 201 CacheMaxAge time.Duration 202 CacheMaxSize fs.SizeSuffix 203 CachePollInterval time.Duration 204 CaseInsensitive bool 205 } 206 207 // New creates a new VFS and root directory. If opt is nil, then 208 // DefaultOpt will be used 209 func New(f fs.Fs, opt *Options) *VFS { 210 fsDir := fs.NewDir("", time.Now()) 211 vfs := &VFS{ 212 f: f, 213 } 214 215 // Make a copy of the options 216 if opt != nil { 217 vfs.Opt = *opt 218 } else { 219 vfs.Opt = DefaultOpt 220 } 221 222 // Mask the permissions with the umask 223 vfs.Opt.DirPerms &= ^os.FileMode(vfs.Opt.Umask) 224 vfs.Opt.FilePerms &= ^os.FileMode(vfs.Opt.Umask) 225 226 // Make sure directories are returned as directories 227 vfs.Opt.DirPerms |= os.ModeDir 228 229 // Create root directory 230 vfs.root = newDir(vfs, f, nil, fsDir) 231 232 // Start polling function 233 if do := vfs.f.Features().ChangeNotify; do != nil { 234 vfs.pollChan = make(chan time.Duration) 235 do(context.TODO(), vfs.root.changeNotify, vfs.pollChan) 236 vfs.pollChan <- vfs.Opt.PollInterval 237 } else { 238 fs.Infof(f, "poll-interval is not supported by this remote") 239 } 240 241 vfs.SetCacheMode(vfs.Opt.CacheMode) 242 243 // add the remote control 244 vfs.addRC() 245 return vfs 246 } 247 248 // Fs returns the Fs passed into the New call 249 func (vfs *VFS) Fs() fs.Fs { 250 return vfs.f 251 } 252 253 // SetCacheMode change the cache mode 254 func (vfs *VFS) SetCacheMode(cacheMode CacheMode) { 255 vfs.Shutdown() 256 vfs.cache = nil 257 if cacheMode > CacheModeOff { 258 ctx, cancel := context.WithCancel(context.Background()) 259 cache, err := newCache(ctx, vfs.f, &vfs.Opt) // FIXME pass on context or get from Opt? 260 if err != nil { 261 fs.Errorf(nil, "Failed to create vfs cache - disabling: %v", err) 262 vfs.Opt.CacheMode = CacheModeOff 263 cancel() 264 return 265 } 266 vfs.Opt.CacheMode = cacheMode 267 vfs.cancel = cancel 268 vfs.cache = cache 269 } 270 } 271 272 // Shutdown stops any background go-routines 273 func (vfs *VFS) Shutdown() { 274 if vfs.cancel != nil { 275 vfs.cancel() 276 vfs.cancel = nil 277 } 278 } 279 280 // CleanUp deletes the contents of the on disk cache 281 func (vfs *VFS) CleanUp() error { 282 if vfs.Opt.CacheMode == CacheModeOff { 283 return nil 284 } 285 return vfs.cache.cleanUp() 286 } 287 288 // FlushDirCache empties the directory cache 289 func (vfs *VFS) FlushDirCache() { 290 vfs.root.ForgetAll() 291 } 292 293 // WaitForWriters sleeps until all writers have finished or 294 // time.Duration has elapsed 295 func (vfs *VFS) WaitForWriters(timeout time.Duration) { 296 defer log.Trace(nil, "timeout=%v", timeout)("") 297 const tickTime = 1 * time.Second 298 deadline := time.NewTimer(timeout) 299 defer deadline.Stop() 300 tick := time.NewTimer(tickTime) 301 defer tick.Stop() 302 tick.Stop() 303 for { 304 writers := 0 305 vfs.root.walk(func(d *Dir) { 306 fs.Debugf(d.path, "Looking for writers") 307 // NB d.mu is held by walk() here 308 for leaf, item := range d.items { 309 fs.Debugf(leaf, "reading active writers") 310 if file, ok := item.(*File); ok { 311 n := file.activeWriters() 312 if n != 0 { 313 fs.Debugf(file, "active writers %d", n) 314 } 315 writers += n 316 } 317 } 318 }) 319 if writers == 0 { 320 return 321 } 322 fs.Debugf(nil, "Still %d writers active, waiting %v", writers, tickTime) 323 tick.Reset(tickTime) 324 select { 325 case <-tick.C: 326 break 327 case <-deadline.C: 328 fs.Errorf(nil, "Exiting even though %d writers are active after %v", writers, timeout) 329 return 330 } 331 } 332 } 333 334 // Root returns the root node 335 func (vfs *VFS) Root() (*Dir, error) { 336 // fs.Debugf(vfs.f, "Root()") 337 return vfs.root, nil 338 } 339 340 var inodeCount uint64 341 342 // newInode creates a new unique inode number 343 func newInode() (inode uint64) { 344 return atomic.AddUint64(&inodeCount, 1) 345 } 346 347 // Stat finds the Node by path starting from the root 348 // 349 // It is the equivalent of os.Stat - Node contains the os.FileInfo 350 // interface. 351 func (vfs *VFS) Stat(path string) (node Node, err error) { 352 path = strings.Trim(path, "/") 353 node = vfs.root 354 for path != "" { 355 i := strings.IndexRune(path, '/') 356 var name string 357 if i < 0 { 358 name, path = path, "" 359 } else { 360 name, path = path[:i], path[i+1:] 361 } 362 if name == "" { 363 continue 364 } 365 dir, ok := node.(*Dir) 366 if !ok { 367 // We need to look in a directory, but found a file 368 return nil, ENOENT 369 } 370 node, err = dir.Stat(name) 371 if err != nil { 372 return nil, err 373 } 374 } 375 return 376 } 377 378 // StatParent finds the parent directory and the leaf name of a path 379 func (vfs *VFS) StatParent(name string) (dir *Dir, leaf string, err error) { 380 name = strings.Trim(name, "/") 381 parent, leaf := path.Split(name) 382 node, err := vfs.Stat(parent) 383 if err != nil { 384 return nil, "", err 385 } 386 if node.IsFile() { 387 return nil, "", os.ErrExist 388 } 389 dir = node.(*Dir) 390 return dir, leaf, nil 391 } 392 393 // decodeOpenFlags returns a string representing the open flags 394 func decodeOpenFlags(flags int) string { 395 var out []string 396 rdwrMode := flags & accessModeMask 397 switch rdwrMode { 398 case os.O_RDONLY: 399 out = append(out, "O_RDONLY") 400 case os.O_WRONLY: 401 out = append(out, "O_WRONLY") 402 case os.O_RDWR: 403 out = append(out, "O_RDWR") 404 default: 405 out = append(out, fmt.Sprintf("0x%X", rdwrMode)) 406 } 407 if flags&os.O_APPEND != 0 { 408 out = append(out, "O_APPEND") 409 } 410 if flags&os.O_CREATE != 0 { 411 out = append(out, "O_CREATE") 412 } 413 if flags&os.O_EXCL != 0 { 414 out = append(out, "O_EXCL") 415 } 416 if flags&os.O_SYNC != 0 { 417 out = append(out, "O_SYNC") 418 } 419 if flags&os.O_TRUNC != 0 { 420 out = append(out, "O_TRUNC") 421 } 422 flags &^= accessModeMask | os.O_APPEND | os.O_CREATE | os.O_EXCL | os.O_SYNC | os.O_TRUNC 423 if flags != 0 { 424 out = append(out, fmt.Sprintf("0x%X", flags)) 425 } 426 return strings.Join(out, "|") 427 } 428 429 // OpenFile a file according to the flags and perm provided 430 func (vfs *VFS) OpenFile(name string, flags int, perm os.FileMode) (fd Handle, err error) { 431 defer log.Trace(name, "flags=%s, perm=%v", decodeOpenFlags(flags), perm)("fd=%v, err=%v", &fd, &err) 432 433 // http://pubs.opengroup.org/onlinepubs/7908799/xsh/open.html 434 // The result of using O_TRUNC with O_RDONLY is undefined. 435 // Linux seems to truncate the file, but we prefer to return EINVAL 436 if flags&accessModeMask == os.O_RDONLY && flags&os.O_TRUNC != 0 { 437 return nil, EINVAL 438 } 439 440 node, err := vfs.Stat(name) 441 if err != nil { 442 if err != ENOENT || flags&os.O_CREATE == 0 { 443 return nil, err 444 } 445 // If not found and O_CREATE then create the file 446 dir, leaf, err := vfs.StatParent(name) 447 if err != nil { 448 return nil, err 449 } 450 node, err = dir.Create(leaf, flags) 451 if err != nil { 452 return nil, err 453 } 454 } 455 return node.Open(flags) 456 } 457 458 // Rename oldName to newName 459 func (vfs *VFS) Rename(oldName, newName string) error { 460 // find the parent directories 461 oldDir, oldLeaf, err := vfs.StatParent(oldName) 462 if err != nil { 463 return err 464 } 465 newDir, newLeaf, err := vfs.StatParent(newName) 466 if err != nil { 467 return err 468 } 469 err = oldDir.Rename(oldLeaf, newLeaf, newDir) 470 if err != nil { 471 return err 472 } 473 return nil 474 } 475 476 // Statfs returns into about the filing system if known 477 // 478 // The values will be -1 if they aren't known 479 // 480 // This information is cached for the DirCacheTime interval 481 func (vfs *VFS) Statfs() (total, used, free int64) { 482 // defer log.Trace("/", "")("total=%d, used=%d, free=%d", &total, &used, &free) 483 vfs.usageMu.Lock() 484 defer vfs.usageMu.Unlock() 485 total, used, free = -1, -1, -1 486 doAbout := vfs.f.Features().About 487 if doAbout == nil { 488 return 489 } 490 if vfs.usageTime.IsZero() || time.Since(vfs.usageTime) >= vfs.Opt.DirCacheTime { 491 var err error 492 vfs.usage, err = doAbout(context.TODO()) 493 vfs.usageTime = time.Now() 494 if err != nil { 495 fs.Errorf(vfs.f, "Statfs failed: %v", err) 496 return 497 } 498 } 499 if u := vfs.usage; u != nil { 500 if u.Total != nil { 501 total = *u.Total 502 } 503 if u.Free != nil { 504 free = *u.Free 505 } 506 if u.Used != nil { 507 used = *u.Used 508 } 509 } 510 return 511 }