github.com/xhghs/rclone@v1.51.1-0.20200430155106-e186a28cced8/vfs/dir.go (about) 1 package vfs 2 3 import ( 4 "context" 5 "os" 6 "path" 7 "sort" 8 "strings" 9 "sync" 10 "time" 11 12 "github.com/pkg/errors" 13 "github.com/rclone/rclone/fs" 14 "github.com/rclone/rclone/fs/dirtree" 15 "github.com/rclone/rclone/fs/list" 16 "github.com/rclone/rclone/fs/log" 17 "github.com/rclone/rclone/fs/operations" 18 "github.com/rclone/rclone/fs/walk" 19 ) 20 21 // Dir represents a directory entry 22 type Dir struct { 23 vfs *VFS // read only 24 inode uint64 // read only: inode number 25 f fs.Fs // read only 26 27 mu sync.RWMutex // protects the following 28 parent *Dir // parent, nil for root 29 path string 30 modTime time.Time 31 entry fs.Directory 32 read time.Time // time directory entry last read 33 items map[string]Node // directory entries - can be empty but not nil 34 } 35 36 func newDir(vfs *VFS, f fs.Fs, parent *Dir, fsDir fs.Directory) *Dir { 37 return &Dir{ 38 vfs: vfs, 39 f: f, 40 parent: parent, 41 entry: fsDir, 42 path: fsDir.Remote(), 43 modTime: fsDir.ModTime(context.TODO()), 44 inode: newInode(), 45 items: make(map[string]Node), 46 } 47 } 48 49 // String converts it to printablee 50 func (d *Dir) String() string { 51 if d == nil { 52 return "<nil *Dir>" 53 } 54 d.mu.RLock() 55 defer d.mu.RUnlock() 56 return d.path + "/" 57 } 58 59 // IsFile returns false for Dir - satisfies Node interface 60 func (d *Dir) IsFile() bool { 61 return false 62 } 63 64 // IsDir returns true for Dir - satisfies Node interface 65 func (d *Dir) IsDir() bool { 66 return true 67 } 68 69 // Mode bits of the directory - satisfies Node interface 70 func (d *Dir) Mode() (mode os.FileMode) { 71 return d.vfs.Opt.DirPerms 72 } 73 74 // Name (base) of the directory - satisfies Node interface 75 func (d *Dir) Name() (name string) { 76 d.mu.RLock() 77 name = path.Base(d.path) 78 d.mu.RUnlock() 79 if name == "." { 80 name = "/" 81 } 82 return name 83 } 84 85 // Path of the directory - satisfies Node interface 86 func (d *Dir) Path() (name string) { 87 d.mu.RLock() 88 defer d.mu.RUnlock() 89 return d.path 90 } 91 92 // Sys returns underlying data source (can be nil) - satisfies Node interface 93 func (d *Dir) Sys() interface{} { 94 return nil 95 } 96 97 // Inode returns the inode number - satisfies Node interface 98 func (d *Dir) Inode() uint64 { 99 return d.inode 100 } 101 102 // Node returns the Node assocuated with this - satisfies Noder interface 103 func (d *Dir) Node() Node { 104 return d 105 } 106 107 // forgetDirPath clears the cache for itself and all subdirectories if 108 // they match the given path. The path is specified relative from the 109 // directory it is called from. 110 // 111 // It does not invalidate or clear the cache of the parent directory. 112 func (d *Dir) forgetDirPath(relativePath string) { 113 if dir := d.cachedDir(relativePath); dir != nil { 114 dir.walk(func(dir *Dir) { 115 // this is called with the mutex held 116 fs.Debugf(dir.path, "forgetting directory cache") 117 dir.read = time.Time{} 118 dir.items = make(map[string]Node) 119 }) 120 } 121 } 122 123 // ForgetAll ensures the directory and all its children are purged 124 // from the cache. 125 // 126 // It does not invalidate or clear the cache of the parent directory. 127 func (d *Dir) ForgetAll() { 128 d.forgetDirPath("") 129 } 130 131 // invalidateDir invalidates the directory cache for absPath relative to this dir 132 func (d *Dir) invalidateDir(absPath string) { 133 node := d.vfs.root.cachedNode(absPath) 134 if dir, ok := node.(*Dir); ok { 135 dir.mu.Lock() 136 if !dir.read.IsZero() { 137 fs.Debugf(dir.path, "invalidating directory cache") 138 dir.read = time.Time{} 139 } 140 dir.mu.Unlock() 141 } 142 } 143 144 // changeNotify invalidates the directory cache for the relativePath 145 // passed in. 146 // 147 // if entryType is a directory it invalidates the parent of the directory too. 148 func (d *Dir) changeNotify(relativePath string, entryType fs.EntryType) { 149 defer log.Trace(d.path, "relativePath=%q, type=%v", relativePath, entryType)("") 150 d.mu.RLock() 151 absPath := path.Join(d.path, relativePath) 152 d.mu.RUnlock() 153 d.invalidateDir(findParent(absPath)) 154 if entryType == fs.EntryDirectory { 155 d.invalidateDir(absPath) 156 } 157 } 158 159 // ForgetPath clears the cache for itself and all subdirectories if 160 // they match the given path. The path is specified relative from the 161 // directory it is called from. The cache of the parent directory is 162 // marked as stale, but not cleared otherwise. 163 // It is not possible to traverse the directory tree upwards, i.e. 164 // you cannot clear the cache for the Dir's ancestors or siblings. 165 func (d *Dir) ForgetPath(relativePath string, entryType fs.EntryType) { 166 defer log.Trace(d.path, "relativePath=%q, type=%v", relativePath, entryType)("") 167 d.mu.RLock() 168 absPath := path.Join(d.path, relativePath) 169 d.mu.RUnlock() 170 if absPath != "" { 171 d.invalidateDir(findParent(absPath)) 172 } 173 if entryType == fs.EntryDirectory { 174 d.forgetDirPath(relativePath) 175 } 176 } 177 178 // walk runs a function on all cached directories. It will be called 179 // on a directory's children first. 180 // 181 // The mutex will be held for the directory when fun is called 182 func (d *Dir) walk(fun func(*Dir)) { 183 d.mu.Lock() 184 defer d.mu.Unlock() 185 for _, node := range d.items { 186 if dir, ok := node.(*Dir); ok { 187 dir.walk(fun) 188 } 189 } 190 191 fun(d) 192 } 193 194 // age returns the duration since the last time the directory contents 195 // was read and the content is cosidered stale. age will be 0 and 196 // stale true if the last read time is empty. 197 // age must be called with d.mu held. 198 func (d *Dir) _age(when time.Time) (age time.Duration, stale bool) { 199 if d.read.IsZero() { 200 return age, true 201 } 202 age = when.Sub(d.read) 203 stale = age > d.vfs.Opt.DirCacheTime 204 return 205 } 206 207 // rename should be called after the directory is renamed 208 // 209 // Reset the directory to new state, discarding all the objects and 210 // reading everything again 211 func (d *Dir) rename(newParent *Dir, fsDir fs.Directory) { 212 d.ForgetAll() 213 d.mu.Lock() 214 d.parent = newParent 215 d.entry = fsDir 216 d.path = fsDir.Remote() 217 d.modTime = fsDir.ModTime(context.TODO()) 218 d.read = time.Time{} 219 d.mu.Unlock() 220 } 221 222 // addObject adds a new object or directory to the directory 223 // 224 // note that we add new objects rather than updating old ones 225 func (d *Dir) addObject(node Node) { 226 d.mu.Lock() 227 d.items[node.Name()] = node 228 d.mu.Unlock() 229 } 230 231 // delObject removes an object from the directory 232 func (d *Dir) delObject(leaf string) { 233 d.mu.Lock() 234 delete(d.items, leaf) 235 d.mu.Unlock() 236 } 237 238 // read the directory and sets d.items - must be called with the lock held 239 func (d *Dir) _readDir() error { 240 when := time.Now() 241 if age, stale := d._age(when); stale { 242 if age != 0 { 243 fs.Debugf(d.path, "Re-reading directory (%v old)", age) 244 } 245 } else { 246 return nil 247 } 248 entries, err := list.DirSorted(context.TODO(), d.f, false, d.path) 249 if err == fs.ErrorDirNotFound { 250 // We treat directory not found as empty because we 251 // create directories on the fly 252 } else if err != nil { 253 return err 254 } 255 256 err = d._readDirFromEntries(entries, nil, time.Time{}) 257 if err != nil { 258 return err 259 } 260 261 d.read = when 262 return nil 263 } 264 265 // update d.items for each dir in the DirTree below this one and 266 // set the last read time - must be called with the lock held 267 func (d *Dir) _readDirFromDirTree(dirTree dirtree.DirTree, when time.Time) error { 268 return d._readDirFromEntries(dirTree[d.path], dirTree, when) 269 } 270 271 // update d.items and if dirTree is not nil update each dir in the DirTree below this one and 272 // set the last read time - must be called with the lock held 273 func (d *Dir) _readDirFromEntries(entries fs.DirEntries, dirTree dirtree.DirTree, when time.Time) error { 274 var err error 275 // Cache the items by name 276 found := make(map[string]struct{}) 277 for _, entry := range entries { 278 name := path.Base(entry.Remote()) 279 if name == "." || name == ".." { 280 continue 281 } 282 node := d.items[name] 283 found[name] = struct{}{} 284 switch item := entry.(type) { 285 case fs.Object: 286 obj := item 287 // Reuse old file value if it exists 288 if file, ok := node.(*File); node != nil && ok { 289 file.setObjectNoUpdate(obj) 290 } else { 291 node = newFile(d, obj, name) 292 } 293 case fs.Directory: 294 // Reuse old dir value if it exists 295 if node == nil || !node.IsDir() { 296 node = newDir(d.vfs, d.f, d, item) 297 } 298 if dirTree != nil { 299 dir := node.(*Dir) 300 dir.mu.Lock() 301 err = dir._readDirFromDirTree(dirTree, when) 302 if err != nil { 303 dir.read = time.Time{} 304 } else { 305 dir.read = when 306 } 307 dir.mu.Unlock() 308 if err != nil { 309 return err 310 } 311 } 312 default: 313 err = errors.Errorf("unknown type %T", item) 314 fs.Errorf(d, "readDir error: %v", err) 315 return err 316 } 317 d.items[name] = node 318 } 319 // delete unused entries 320 for name := range d.items { 321 if _, ok := found[name]; !ok { 322 delete(d.items, name) 323 } 324 } 325 return nil 326 } 327 328 // readDirTree forces a refresh of the complete directory tree 329 func (d *Dir) readDirTree() error { 330 d.mu.RLock() 331 f, path := d.f, d.path 332 d.mu.RUnlock() 333 when := time.Now() 334 fs.Debugf(path, "Reading directory tree") 335 dt, err := walk.NewDirTree(context.TODO(), f, path, false, -1) 336 if err != nil { 337 return err 338 } 339 d.mu.Lock() 340 defer d.mu.Unlock() 341 d.read = time.Time{} 342 err = d._readDirFromDirTree(dt, when) 343 if err != nil { 344 return err 345 } 346 fs.Debugf(d.path, "Reading directory tree done in %s", time.Since(when)) 347 d.read = when 348 return nil 349 } 350 351 // readDir forces a refresh of the directory 352 func (d *Dir) readDir() error { 353 d.mu.Lock() 354 defer d.mu.Unlock() 355 d.read = time.Time{} 356 return d._readDir() 357 } 358 359 // stat a single item in the directory 360 // 361 // returns ENOENT if not found. 362 // returns a custom error if directory on a case-insensitive file system 363 // contains files with names that differ only by case. 364 func (d *Dir) stat(leaf string) (Node, error) { 365 d.mu.Lock() 366 defer d.mu.Unlock() 367 err := d._readDir() 368 if err != nil { 369 return nil, err 370 } 371 item, ok := d.items[leaf] 372 373 if !ok && d.vfs.Opt.CaseInsensitive { 374 leafLower := strings.ToLower(leaf) 375 for name, node := range d.items { 376 if strings.ToLower(name) == leafLower { 377 if ok { 378 // duplicate case insensitive match is an error 379 return nil, errors.Errorf("duplicate filename %q detected with --vfs-case-insensitive set", leaf) 380 } 381 // found a case insenstive match 382 ok = true 383 item = node 384 } 385 } 386 } 387 388 if !ok { 389 return nil, ENOENT 390 } 391 return item, nil 392 } 393 394 // Check to see if a directory is empty 395 func (d *Dir) isEmpty() (bool, error) { 396 d.mu.Lock() 397 defer d.mu.Unlock() 398 err := d._readDir() 399 if err != nil { 400 return false, err 401 } 402 return len(d.items) == 0, nil 403 } 404 405 // ModTime returns the modification time of the directory 406 func (d *Dir) ModTime() time.Time { 407 d.mu.RLock() 408 defer d.mu.RUnlock() 409 // fs.Debugf(d.path, "Dir.ModTime %v", d.modTime) 410 return d.modTime 411 } 412 413 // Size of the directory 414 func (d *Dir) Size() int64 { 415 return 0 416 } 417 418 // SetModTime sets the modTime for this dir 419 func (d *Dir) SetModTime(modTime time.Time) error { 420 if d.vfs.Opt.ReadOnly { 421 return EROFS 422 } 423 d.mu.Lock() 424 d.modTime = modTime 425 d.mu.Unlock() 426 return nil 427 } 428 429 func (d *Dir) cachedDir(relativePath string) (dir *Dir) { 430 dir, _ = d.cachedNode(relativePath).(*Dir) 431 return 432 } 433 434 func (d *Dir) cachedNode(relativePath string) Node { 435 segments := strings.Split(strings.Trim(relativePath, "/"), "/") 436 var node Node = d 437 for _, s := range segments { 438 if s == "" { 439 continue 440 } 441 if dir, ok := node.(*Dir); ok { 442 dir.mu.Lock() 443 node = dir.items[s] 444 dir.mu.Unlock() 445 446 if node != nil { 447 continue 448 } 449 } 450 return nil 451 } 452 453 return node 454 } 455 456 // Stat looks up a specific entry in the receiver. 457 // 458 // Stat should return a Node corresponding to the entry. If the 459 // name does not exist in the directory, Stat should return ENOENT. 460 // 461 // Stat need not to handle the names "." and "..". 462 func (d *Dir) Stat(name string) (node Node, err error) { 463 // fs.Debugf(path, "Dir.Stat") 464 node, err = d.stat(name) 465 if err != nil { 466 if err != ENOENT { 467 fs.Errorf(d, "Dir.Stat error: %v", err) 468 } 469 return nil, err 470 } 471 // fs.Debugf(path, "Dir.Stat OK") 472 return node, nil 473 } 474 475 // ReadDirAll reads the contents of the directory sorted 476 func (d *Dir) ReadDirAll() (items Nodes, err error) { 477 // fs.Debugf(d.path, "Dir.ReadDirAll") 478 d.mu.Lock() 479 defer d.mu.Unlock() 480 err = d._readDir() 481 if err != nil { 482 fs.Debugf(d.path, "Dir.ReadDirAll error: %v", err) 483 return nil, err 484 } 485 for _, item := range d.items { 486 items = append(items, item) 487 } 488 sort.Sort(items) 489 // fs.Debugf(d.path, "Dir.ReadDirAll OK with %d entries", len(items)) 490 return items, nil 491 } 492 493 // accessModeMask masks off the read modes from the flags 494 const accessModeMask = (os.O_RDONLY | os.O_WRONLY | os.O_RDWR) 495 496 // Open the directory according to the flags provided 497 func (d *Dir) Open(flags int) (fd Handle, err error) { 498 rdwrMode := flags & accessModeMask 499 if rdwrMode != os.O_RDONLY { 500 fs.Errorf(d, "Can only open directories read only") 501 return nil, EPERM 502 } 503 return newDirHandle(d), nil 504 } 505 506 // Create makes a new file node 507 func (d *Dir) Create(name string, flags int) (*File, error) { 508 // fs.Debugf(path, "Dir.Create") 509 if d.vfs.Opt.ReadOnly { 510 return nil, EROFS 511 } 512 // This gets added to the directory when the file is opened for write 513 return newFile(d, nil, name), nil 514 } 515 516 // Mkdir creates a new directory 517 func (d *Dir) Mkdir(name string) (*Dir, error) { 518 if d.vfs.Opt.ReadOnly { 519 return nil, EROFS 520 } 521 path := path.Join(d.path, name) 522 node, err := d.stat(name) 523 switch err { 524 case ENOENT: 525 // not found, carry on 526 case nil: 527 // found so check what it is 528 if node.IsDir() { 529 return node.(*Dir), err 530 } 531 return nil, EEXIST 532 default: 533 // a different error - report 534 fs.Errorf(d, "Dir.Mkdir failed to read directory: %v", err) 535 return nil, err 536 } 537 // fs.Debugf(path, "Dir.Mkdir") 538 err = d.f.Mkdir(context.TODO(), path) 539 if err != nil { 540 fs.Errorf(d, "Dir.Mkdir failed to create directory: %v", err) 541 return nil, err 542 } 543 fsDir := fs.NewDir(path, time.Now()) 544 dir := newDir(d.vfs, d.f, d, fsDir) 545 d.addObject(dir) 546 // fs.Debugf(path, "Dir.Mkdir OK") 547 return dir, nil 548 } 549 550 // Remove the directory 551 func (d *Dir) Remove() error { 552 if d.vfs.Opt.ReadOnly { 553 return EROFS 554 } 555 // Check directory is empty first 556 empty, err := d.isEmpty() 557 if err != nil { 558 fs.Errorf(d, "Dir.Remove dir error: %v", err) 559 return err 560 } 561 if !empty { 562 fs.Errorf(d, "Dir.Remove not empty") 563 return ENOTEMPTY 564 } 565 // remove directory 566 err = d.f.Rmdir(context.TODO(), d.path) 567 if err != nil { 568 fs.Errorf(d, "Dir.Remove failed to remove directory: %v", err) 569 return err 570 } 571 // Remove the item from the parent directory listing 572 if d.parent != nil { 573 d.parent.delObject(d.Name()) 574 } 575 return nil 576 } 577 578 // RemoveAll removes the directory and any contents recursively 579 func (d *Dir) RemoveAll() error { 580 if d.vfs.Opt.ReadOnly { 581 return EROFS 582 } 583 // Remove contents of the directory 584 nodes, err := d.ReadDirAll() 585 if err != nil { 586 fs.Errorf(d, "Dir.RemoveAll failed to read directory: %v", err) 587 return err 588 } 589 for _, node := range nodes { 590 err = node.RemoveAll() 591 if err != nil { 592 fs.Errorf(node.Path(), "Dir.RemoveAll failed to remove: %v", err) 593 return err 594 } 595 } 596 return d.Remove() 597 } 598 599 // DirEntry returns the underlying fs.DirEntry 600 func (d *Dir) DirEntry() (entry fs.DirEntry) { 601 return d.entry 602 } 603 604 // RemoveName removes the entry with the given name from the receiver, 605 // which must be a directory. The entry to be removed may correspond 606 // to a file (unlink) or to a directory (rmdir). 607 func (d *Dir) RemoveName(name string) error { 608 if d.vfs.Opt.ReadOnly { 609 return EROFS 610 } 611 // fs.Debugf(path, "Dir.Remove") 612 node, err := d.stat(name) 613 if err != nil { 614 fs.Errorf(d, "Dir.Remove error: %v", err) 615 return err 616 } 617 return node.Remove() 618 } 619 620 // Rename the file 621 func (d *Dir) Rename(oldName, newName string, destDir *Dir) error { 622 if d.vfs.Opt.ReadOnly { 623 return EROFS 624 } 625 oldPath := path.Join(d.path, oldName) 626 newPath := path.Join(destDir.path, newName) 627 // fs.Debugf(oldPath, "Dir.Rename to %q", newPath) 628 oldNode, err := d.stat(oldName) 629 if err != nil { 630 fs.Errorf(oldPath, "Dir.Rename error: %v", err) 631 return err 632 } 633 switch x := oldNode.DirEntry().(type) { 634 case nil: 635 if oldFile, ok := oldNode.(*File); ok { 636 if err = oldFile.rename(context.TODO(), destDir, newName); err != nil { 637 fs.Errorf(oldPath, "Dir.Rename error: %v", err) 638 return err 639 } 640 } else { 641 fs.Errorf(oldPath, "Dir.Rename can't rename open file that is not a vfs.File") 642 return EPERM 643 } 644 case fs.Object: 645 if oldFile, ok := oldNode.(*File); ok { 646 if err = oldFile.rename(context.TODO(), destDir, newName); err != nil { 647 fs.Errorf(oldPath, "Dir.Rename error: %v", err) 648 return err 649 } 650 } else { 651 err := errors.Errorf("Fs %q can't rename file that is not a vfs.File", d.f) 652 fs.Errorf(oldPath, "Dir.Rename error: %v", err) 653 return err 654 } 655 case fs.Directory: 656 features := d.f.Features() 657 if features.DirMove == nil && features.Move == nil && features.Copy == nil { 658 err := errors.Errorf("Fs %q can't rename directories (no DirMove, Move or Copy)", d.f) 659 fs.Errorf(oldPath, "Dir.Rename error: %v", err) 660 return err 661 } 662 srcRemote := x.Remote() 663 dstRemote := newPath 664 err = operations.DirMove(context.TODO(), d.f, srcRemote, dstRemote) 665 if err != nil { 666 fs.Errorf(oldPath, "Dir.Rename error: %v", err) 667 return err 668 } 669 newDir := fs.NewDirCopy(context.TODO(), x).SetRemote(newPath) 670 // Update the node with the new details 671 if oldNode != nil { 672 if oldDir, ok := oldNode.(*Dir); ok { 673 fs.Debugf(x, "Updating dir with %v %p", newDir, oldDir) 674 oldDir.rename(destDir, newDir) 675 } 676 } 677 default: 678 err = errors.Errorf("unknown type %T", oldNode) 679 fs.Errorf(d.path, "Dir.Rename error: %v", err) 680 return err 681 } 682 683 // Show moved - delete from old dir and add to new 684 d.delObject(oldName) 685 destDir.addObject(oldNode) 686 687 // fs.Debugf(newPath, "Dir.Rename renamed from %q", oldPath) 688 return nil 689 } 690 691 // Sync the directory 692 // 693 // Note that we don't do anything except return OK 694 func (d *Dir) Sync() error { 695 return nil 696 } 697 698 // VFS returns the instance of the VFS 699 func (d *Dir) VFS() *VFS { 700 return d.vfs 701 } 702 703 // Truncate changes the size of the named file. 704 func (d *Dir) Truncate(size int64) error { 705 return ENOSYS 706 }