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  }