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 (e.g. os.ErrExist) 18// where possible. 19 20//go:generate sh -c "go run make_open_tests.go | gofmt > open_test.go" 21 22package vfs 23 24import ( 25 "context" 26 "fmt" 27 "io/ioutil" 28 "os" 29 "path" 30 "sort" 31 "strings" 32 "sync" 33 "sync/atomic" 34 "time" 35 36 "github.com/rclone/rclone/fs" 37 "github.com/rclone/rclone/fs/cache" 38 "github.com/rclone/rclone/fs/log" 39 "github.com/rclone/rclone/fs/walk" 40 "github.com/rclone/rclone/vfs/vfscache" 41 "github.com/rclone/rclone/vfs/vfscommon" 42) 43 44// Node represents either a directory (*Dir) or a file (*File) 45type Node interface { 46 os.FileInfo 47 IsFile() bool 48 Inode() uint64 49 SetModTime(modTime time.Time) error 50 Sync() error 51 Remove() error 52 RemoveAll() error 53 DirEntry() fs.DirEntry 54 VFS() *VFS 55 Open(flags int) (Handle, error) 56 Truncate(size int64) error 57 Path() string 58 SetSys(interface{}) 59} 60 61// Check interfaces 62var ( 63 _ Node = (*File)(nil) 64 _ Node = (*Dir)(nil) 65) 66 67// Nodes is a slice of Node 68type Nodes []Node 69 70// Sort functions 71func (ns Nodes) Len() int { return len(ns) } 72func (ns Nodes) Swap(i, j int) { ns[i], ns[j] = ns[j], ns[i] } 73func (ns Nodes) Less(i, j int) bool { return ns[i].Path() < ns[j].Path() } 74 75// Noder represents something which can return a node 76type Noder interface { 77 fmt.Stringer 78 Node() Node 79} 80 81// Check interfaces 82var ( 83 _ Noder = (*File)(nil) 84 _ Noder = (*Dir)(nil) 85 _ Noder = (*ReadFileHandle)(nil) 86 _ Noder = (*WriteFileHandle)(nil) 87 _ Noder = (*RWFileHandle)(nil) 88 _ Noder = (*DirHandle)(nil) 89) 90 91// OsFiler is the methods on *os.File 92type OsFiler interface { 93 Chdir() error 94 Chmod(mode os.FileMode) error 95 Chown(uid, gid int) error 96 Close() error 97 Fd() uintptr 98 Name() string 99 Read(b []byte) (n int, err error) 100 ReadAt(b []byte, off int64) (n int, err error) 101 Readdir(n int) ([]os.FileInfo, error) 102 Readdirnames(n int) (names []string, err error) 103 Seek(offset int64, whence int) (ret int64, err error) 104 Stat() (os.FileInfo, error) 105 Sync() error 106 Truncate(size int64) error 107 Write(b []byte) (n int, err error) 108 WriteAt(b []byte, off int64) (n int, err error) 109 WriteString(s string) (n int, err error) 110} 111 112// Handle is the interface satisfied by open files or directories. 113// It is the methods on *os.File, plus a few more useful for FUSE 114// filingsystems. Not all of them are supported. 115type Handle interface { 116 OsFiler 117 // Additional methods useful for FUSE filesystems 118 Flush() error 119 Release() error 120 Node() Node 121 // Size() int64 122} 123 124// baseHandle implements all the missing methods 125type baseHandle struct{} 126 127func (h baseHandle) Chdir() error { return ENOSYS } 128func (h baseHandle) Chmod(mode os.FileMode) error { return ENOSYS } 129func (h baseHandle) Chown(uid, gid int) error { return ENOSYS } 130func (h baseHandle) Close() error { return ENOSYS } 131func (h baseHandle) Fd() uintptr { return 0 } 132func (h baseHandle) Name() string { return "" } 133func (h baseHandle) Read(b []byte) (n int, err error) { return 0, ENOSYS } 134func (h baseHandle) ReadAt(b []byte, off int64) (n int, err error) { return 0, ENOSYS } 135func (h baseHandle) Readdir(n int) ([]os.FileInfo, error) { return nil, ENOSYS } 136func (h baseHandle) Readdirnames(n int) (names []string, err error) { return nil, ENOSYS } 137func (h baseHandle) Seek(offset int64, whence int) (ret int64, err error) { return 0, ENOSYS } 138func (h baseHandle) Stat() (os.FileInfo, error) { return nil, ENOSYS } 139func (h baseHandle) Sync() error { return nil } 140func (h baseHandle) Truncate(size int64) error { return ENOSYS } 141func (h baseHandle) Write(b []byte) (n int, err error) { return 0, ENOSYS } 142func (h baseHandle) WriteAt(b []byte, off int64) (n int, err error) { return 0, ENOSYS } 143func (h baseHandle) WriteString(s string) (n int, err error) { return 0, ENOSYS } 144func (h baseHandle) Flush() (err error) { return ENOSYS } 145func (h baseHandle) Release() (err error) { return ENOSYS } 146func (h baseHandle) Node() Node { return nil } 147 148//func (h baseHandle) Size() int64 { return 0 } 149 150// Check interfaces 151var ( 152 _ OsFiler = (*os.File)(nil) 153 _ Handle = (*baseHandle)(nil) 154 _ Handle = (*ReadFileHandle)(nil) 155 _ Handle = (*WriteFileHandle)(nil) 156 _ Handle = (*DirHandle)(nil) 157) 158 159// VFS represents the top level filing system 160type VFS struct { 161 f fs.Fs 162 root *Dir 163 Opt vfscommon.Options 164 cache *vfscache.Cache 165 cancelCache context.CancelFunc 166 usageMu sync.Mutex 167 usageTime time.Time 168 usage *fs.Usage 169 pollChan chan time.Duration 170 inUse int32 // count of number of opens accessed with atomic 171} 172 173// Keep track of active VFS keyed on fs.ConfigString(f) 174var ( 175 activeMu sync.Mutex 176 active = map[string][]*VFS{} 177) 178 179// New creates a new VFS and root directory. If opt is nil, then 180// DefaultOpt will be used 181func New(f fs.Fs, opt *vfscommon.Options) *VFS { 182 fsDir := fs.NewDir("", time.Now()) 183 vfs := &VFS{ 184 f: f, 185 inUse: int32(1), 186 } 187 188 // Make a copy of the options 189 if opt != nil { 190 vfs.Opt = *opt 191 } else { 192 vfs.Opt = vfscommon.DefaultOpt 193 } 194 195 // Mask the permissions with the umask 196 vfs.Opt.DirPerms &= ^os.FileMode(vfs.Opt.Umask) 197 vfs.Opt.FilePerms &= ^os.FileMode(vfs.Opt.Umask) 198 199 // Make sure directories are returned as directories 200 vfs.Opt.DirPerms |= os.ModeDir 201 202 // Find a VFS with the same name and options and return it if possible 203 activeMu.Lock() 204 defer activeMu.Unlock() 205 configName := fs.ConfigString(f) 206 for _, activeVFS := range active[configName] { 207 if vfs.Opt == activeVFS.Opt { 208 fs.Debugf(f, "Re-using VFS from active cache") 209 atomic.AddInt32(&activeVFS.inUse, 1) 210 return activeVFS 211 } 212 } 213 // Put the VFS into the active cache 214 active[configName] = append(active[configName], vfs) 215 216 // Create root directory 217 vfs.root = newDir(vfs, f, nil, fsDir) 218 219 // Start polling function 220 features := vfs.f.Features() 221 if do := features.ChangeNotify; do != nil { 222 vfs.pollChan = make(chan time.Duration) 223 do(context.TODO(), vfs.root.changeNotify, vfs.pollChan) 224 vfs.pollChan <- vfs.Opt.PollInterval 225 } else if vfs.Opt.PollInterval > 0 { 226 fs.Infof(f, "poll-interval is not supported by this remote") 227 } 228 229 // Warn if can't stream 230 if !vfs.Opt.ReadOnly && vfs.Opt.CacheMode < vfscommon.CacheModeWrites && features.PutStream == nil { 231 fs.Logf(f, "--vfs-cache-mode writes or full is recommended for this remote as it can't stream") 232 } 233 234 vfs.SetCacheMode(vfs.Opt.CacheMode) 235 236 // Pin the Fs into the cache so that when we use cache.NewFs 237 // with the same remote string we get this one. The Pin is 238 // removed when the vfs is finalized 239 cache.PinUntilFinalized(f, vfs) 240 241 return vfs 242} 243 244// Return the number of active cache entries and a VFS if any are in 245// the cache. 246func activeCacheEntries() (vfs *VFS, count int) { 247 activeMu.Lock() 248 for _, vfses := range active { 249 count += len(vfses) 250 if len(vfses) > 0 { 251 vfs = vfses[0] 252 } 253 } 254 activeMu.Unlock() 255 return vfs, count 256} 257 258// Fs returns the Fs passed into the New call 259func (vfs *VFS) Fs() fs.Fs { 260 return vfs.f 261} 262 263// SetCacheMode change the cache mode 264func (vfs *VFS) SetCacheMode(cacheMode vfscommon.CacheMode) { 265 vfs.shutdownCache() 266 vfs.cache = nil 267 if cacheMode > vfscommon.CacheModeOff { 268 ctx, cancel := context.WithCancel(context.Background()) 269 cache, err := vfscache.New(ctx, vfs.f, &vfs.Opt, vfs.AddVirtual) // FIXME pass on context or get from Opt? 270 if err != nil { 271 fs.Errorf(nil, "Failed to create vfs cache - disabling: %v", err) 272 vfs.Opt.CacheMode = vfscommon.CacheModeOff 273 cancel() 274 return 275 } 276 vfs.Opt.CacheMode = cacheMode 277 vfs.cancelCache = cancel 278 vfs.cache = cache 279 } 280} 281 282// shutdown the cache if it was running 283func (vfs *VFS) shutdownCache() { 284 if vfs.cancelCache != nil { 285 vfs.cancelCache() 286 vfs.cancelCache = nil 287 } 288} 289 290// Shutdown stops any background go-routines and removes the VFS from 291// the active ache. 292func (vfs *VFS) Shutdown() { 293 if atomic.AddInt32(&vfs.inUse, -1) > 0 { 294 return 295 } 296 297 // Remove from active cache 298 activeMu.Lock() 299 configName := fs.ConfigString(vfs.f) 300 activeVFSes := active[configName] 301 for i, activeVFS := range activeVFSes { 302 if activeVFS == vfs { 303 activeVFSes[i] = nil 304 active[configName] = append(activeVFSes[:i], activeVFSes[i+1:]...) 305 break 306 } 307 } 308 activeMu.Unlock() 309 310 vfs.shutdownCache() 311} 312 313// CleanUp deletes the contents of the on disk cache 314func (vfs *VFS) CleanUp() error { 315 if vfs.Opt.CacheMode == vfscommon.CacheModeOff { 316 return nil 317 } 318 return vfs.cache.CleanUp() 319} 320 321// FlushDirCache empties the directory cache 322func (vfs *VFS) FlushDirCache() { 323 vfs.root.ForgetAll() 324} 325 326// WaitForWriters sleeps until all writers have finished or 327// time.Duration has elapsed 328func (vfs *VFS) WaitForWriters(timeout time.Duration) { 329 defer log.Trace(nil, "timeout=%v", timeout)("") 330 tickTime := 10 * time.Millisecond 331 deadline := time.NewTimer(timeout) 332 defer deadline.Stop() 333 tick := time.NewTimer(tickTime) 334 defer tick.Stop() 335 tick.Stop() 336 for { 337 writers := vfs.root.countActiveWriters() 338 cacheInUse := 0 339 if vfs.cache != nil { 340 cacheInUse = vfs.cache.TotalInUse() 341 } 342 if writers == 0 && cacheInUse == 0 { 343 return 344 } 345 fs.Debugf(nil, "Still %d writers active and %d cache items in use, waiting %v", writers, cacheInUse, tickTime) 346 tick.Reset(tickTime) 347 select { 348 case <-tick.C: 349 break 350 case <-deadline.C: 351 fs.Errorf(nil, "Exiting even though %d writers active and %d cache items in use after %v\n%s", writers, cacheInUse, timeout, vfs.cache.Dump()) 352 return 353 } 354 tickTime *= 2 355 if tickTime > time.Second { 356 tickTime = time.Second 357 } 358 } 359} 360 361// Root returns the root node 362func (vfs *VFS) Root() (*Dir, error) { 363 // fs.Debugf(vfs.f, "Root()") 364 return vfs.root, nil 365} 366 367var inodeCount uint64 368 369// newInode creates a new unique inode number 370func newInode() (inode uint64) { 371 return atomic.AddUint64(&inodeCount, 1) 372} 373 374// Stat finds the Node by path starting from the root 375// 376// It is the equivalent of os.Stat - Node contains the os.FileInfo 377// interface. 378func (vfs *VFS) Stat(path string) (node Node, err error) { 379 path = strings.Trim(path, "/") 380 node = vfs.root 381 for path != "" { 382 i := strings.IndexRune(path, '/') 383 var name string 384 if i < 0 { 385 name, path = path, "" 386 } else { 387 name, path = path[:i], path[i+1:] 388 } 389 if name == "" { 390 continue 391 } 392 dir, ok := node.(*Dir) 393 if !ok { 394 // We need to look in a directory, but found a file 395 return nil, ENOENT 396 } 397 node, err = dir.Stat(name) 398 if err != nil { 399 return nil, err 400 } 401 } 402 return 403} 404 405// StatParent finds the parent directory and the leaf name of a path 406func (vfs *VFS) StatParent(name string) (dir *Dir, leaf string, err error) { 407 name = strings.Trim(name, "/") 408 parent, leaf := path.Split(name) 409 node, err := vfs.Stat(parent) 410 if err != nil { 411 return nil, "", err 412 } 413 if node.IsFile() { 414 return nil, "", os.ErrExist 415 } 416 dir = node.(*Dir) 417 return dir, leaf, nil 418} 419 420// decodeOpenFlags returns a string representing the open flags 421func decodeOpenFlags(flags int) string { 422 var out []string 423 rdwrMode := flags & accessModeMask 424 switch rdwrMode { 425 case os.O_RDONLY: 426 out = append(out, "O_RDONLY") 427 case os.O_WRONLY: 428 out = append(out, "O_WRONLY") 429 case os.O_RDWR: 430 out = append(out, "O_RDWR") 431 default: 432 out = append(out, fmt.Sprintf("0x%X", rdwrMode)) 433 } 434 if flags&os.O_APPEND != 0 { 435 out = append(out, "O_APPEND") 436 } 437 if flags&os.O_CREATE != 0 { 438 out = append(out, "O_CREATE") 439 } 440 if flags&os.O_EXCL != 0 { 441 out = append(out, "O_EXCL") 442 } 443 if flags&os.O_SYNC != 0 { 444 out = append(out, "O_SYNC") 445 } 446 if flags&os.O_TRUNC != 0 { 447 out = append(out, "O_TRUNC") 448 } 449 flags &^= accessModeMask | os.O_APPEND | os.O_CREATE | os.O_EXCL | os.O_SYNC | os.O_TRUNC 450 if flags != 0 { 451 out = append(out, fmt.Sprintf("0x%X", flags)) 452 } 453 return strings.Join(out, "|") 454} 455 456// OpenFile a file according to the flags and perm provided 457func (vfs *VFS) OpenFile(name string, flags int, perm os.FileMode) (fd Handle, err error) { 458 defer log.Trace(name, "flags=%s, perm=%v", decodeOpenFlags(flags), perm)("fd=%v, err=%v", &fd, &err) 459 460 // http://pubs.opengroup.org/onlinepubs/7908799/xsh/open.html 461 // The result of using O_TRUNC with O_RDONLY is undefined. 462 // Linux seems to truncate the file, but we prefer to return EINVAL 463 if flags&accessModeMask == os.O_RDONLY && flags&os.O_TRUNC != 0 { 464 return nil, EINVAL 465 } 466 467 node, err := vfs.Stat(name) 468 if err != nil { 469 if err != ENOENT || flags&os.O_CREATE == 0 { 470 return nil, err 471 } 472 // If not found and O_CREATE then create the file 473 dir, leaf, err := vfs.StatParent(name) 474 if err != nil { 475 return nil, err 476 } 477 node, err = dir.Create(leaf, flags) 478 if err != nil { 479 return nil, err 480 } 481 } 482 return node.Open(flags) 483} 484 485// Open opens the named file for reading. If successful, methods on 486// the returned file can be used for reading; the associated file 487// descriptor has mode O_RDONLY. 488func (vfs *VFS) Open(name string) (Handle, error) { 489 return vfs.OpenFile(name, os.O_RDONLY, 0) 490} 491 492// Create creates the named file with mode 0666 (before umask), truncating 493// it if it already exists. If successful, methods on the returned 494// File can be used for I/O; the associated file descriptor has mode 495// O_RDWR. 496func (vfs *VFS) Create(name string) (Handle, error) { 497 return vfs.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666) 498} 499 500// Rename oldName to newName 501func (vfs *VFS) Rename(oldName, newName string) error { 502 // find the parent directories 503 oldDir, oldLeaf, err := vfs.StatParent(oldName) 504 if err != nil { 505 return err 506 } 507 newDir, newLeaf, err := vfs.StatParent(newName) 508 if err != nil { 509 return err 510 } 511 err = oldDir.Rename(oldLeaf, newLeaf, newDir) 512 if err != nil { 513 return err 514 } 515 return nil 516} 517 518// This works out the missing values from (total, used, free) using 519// unknownFree as the intended free space 520func fillInMissingSizes(total, used, free, unknownFree int64) (newTotal, newUsed, newFree int64) { 521 if total < 0 { 522 if free >= 0 { 523 total = free 524 } else { 525 total = unknownFree 526 } 527 if used >= 0 { 528 total += used 529 } 530 } 531 // total is now defined 532 if used < 0 { 533 if free >= 0 { 534 used = total - free 535 } else { 536 used = 0 537 } 538 } 539 // used is now defined 540 if free < 0 { 541 free = total - used 542 } 543 return total, used, free 544} 545 546// If the total size isn't known then we will aim for this many bytes free (1 PiB) 547const unknownFreeBytes = 1 << 50 548 549// Statfs returns into about the filing system if known 550// 551// The values will be -1 if they aren't known 552// 553// This information is cached for the DirCacheTime interval 554func (vfs *VFS) Statfs() (total, used, free int64) { 555 // defer log.Trace("/", "")("total=%d, used=%d, free=%d", &total, &used, &free) 556 vfs.usageMu.Lock() 557 defer vfs.usageMu.Unlock() 558 total, used, free = -1, -1, -1 559 doAbout := vfs.f.Features().About 560 if (doAbout != nil || vfs.Opt.UsedIsSize) && (vfs.usageTime.IsZero() || time.Since(vfs.usageTime) >= vfs.Opt.DirCacheTime) { 561 var err error 562 ctx := context.TODO() 563 if doAbout == nil { 564 vfs.usage = &fs.Usage{} 565 } else { 566 vfs.usage, err = doAbout(ctx) 567 } 568 if vfs.Opt.UsedIsSize { 569 var usedBySizeAlgorithm int64 570 // Algorithm from `rclone size` 571 err = walk.ListR(ctx, vfs.f, "", true, -1, walk.ListObjects, func(entries fs.DirEntries) error { 572 entries.ForObject(func(o fs.Object) { 573 usedBySizeAlgorithm += o.Size() 574 }) 575 return nil 576 }) 577 vfs.usage.Used = &usedBySizeAlgorithm 578 } 579 vfs.usageTime = time.Now() 580 if err != nil { 581 fs.Errorf(vfs.f, "Statfs failed: %v", err) 582 return 583 } 584 } 585 if u := vfs.usage; u != nil { 586 if u.Total != nil { 587 total = *u.Total 588 } 589 if u.Free != nil { 590 free = *u.Free 591 } 592 if u.Used != nil { 593 used = *u.Used 594 } 595 } 596 total, used, free = fillInMissingSizes(total, used, free, unknownFreeBytes) 597 return 598} 599 600// Remove removes the named file or (empty) directory. 601func (vfs *VFS) Remove(name string) error { 602 node, err := vfs.Stat(name) 603 if err != nil { 604 return err 605 } 606 err = node.Remove() 607 if err != nil { 608 return err 609 } 610 return nil 611} 612 613// Chtimes changes the access and modification times of the named file, similar 614// to the Unix utime() or utimes() functions. 615// 616// The underlying filesystem may truncate or round the values to a less precise 617// time unit. 618func (vfs *VFS) Chtimes(name string, atime time.Time, mtime time.Time) error { 619 node, err := vfs.Stat(name) 620 if err != nil { 621 return err 622 } 623 err = node.SetModTime(mtime) 624 if err != nil { 625 return err 626 } 627 return nil 628} 629 630// Mkdir creates a new directory with the specified name and permission bits 631// (before umask). 632func (vfs *VFS) Mkdir(name string, perm os.FileMode) error { 633 dir, leaf, err := vfs.StatParent(name) 634 if err != nil { 635 return err 636 } 637 _, err = dir.Mkdir(leaf) 638 if err != nil { 639 return err 640 } 641 return nil 642} 643 644// ReadDir reads the directory named by dirname and returns 645// a list of directory entries sorted by filename. 646func (vfs *VFS) ReadDir(dirname string) ([]os.FileInfo, error) { 647 f, err := vfs.Open(dirname) 648 if err != nil { 649 return nil, err 650 } 651 list, err := f.Readdir(-1) 652 closeErr := f.Close() 653 if err != nil { 654 return nil, err 655 } 656 if closeErr != nil { 657 return nil, closeErr 658 } 659 sort.Slice(list, func(i, j int) bool { return list[i].Name() < list[j].Name() }) 660 return list, nil 661} 662 663// ReadFile reads the file named by filename and returns the contents. 664// A successful call returns err == nil, not err == EOF. Because ReadFile 665// reads the whole file, it does not treat an EOF from Read as an error 666// to be reported. 667func (vfs *VFS) ReadFile(filename string) (b []byte, err error) { 668 f, err := vfs.Open(filename) 669 if err != nil { 670 return nil, err 671 } 672 defer fs.CheckClose(f, &err) 673 return ioutil.ReadAll(f) 674} 675 676// AddVirtual adds the object (file or dir) to the directory cache 677func (vfs *VFS) AddVirtual(remote string, size int64, isDir bool) error { 678 dir, leaf, err := vfs.StatParent(remote) 679 if err != nil { 680 return err 681 } 682 dir.AddVirtual(leaf, size, false) 683 return nil 684} 685