diff --git a/vfs/cache.go b/vfs/cache.go index acb51138a..c84d2c76b 100644 --- a/vfs/cache.go +++ b/vfs/cache.go @@ -270,12 +270,50 @@ func (c *cache) exists(name string) bool { if err != nil { return false } - if fi.IsDir() { + // checks for non-regular files (e.g. directories, symlinks, devices, etc.) + if !fi.Mode().IsRegular() { return false } return true } +// renames the file in cache +func (c *cache) rename(name string, newName string) (err error) { + osOldPath := c.toOSPath(name) + osNewPath := c.toOSPath(newName) + sfi, err := os.Stat(osOldPath) + if err != nil { + return errors.Wrapf(err, "Failed to stat source: %s", osOldPath) + } + if !sfi.Mode().IsRegular() { + // cannot copy non-regular files (e.g., directories, symlinks, devices, etc.) + return errors.Errorf("Non-regular source file: %s (%q)", sfi.Name(), sfi.Mode().String()) + } + dfi, err := os.Stat(osNewPath) + if err != nil { + if !os.IsNotExist(err) { + return errors.Wrapf(err, "Failed to stat destination: %s", osNewPath) + } + parent := findParent(osNewPath) + err = os.MkdirAll(parent, 0700) + if err != nil { + return errors.Wrapf(err, "Failed to create parent dir: %s", parent) + } + } else { + if !(dfi.Mode().IsRegular()) { + return errors.Errorf("Non-regular destination file: %s (%q)", dfi.Name(), dfi.Mode().String()) + } + if os.SameFile(sfi, dfi) { + return nil + } + } + if err = os.Rename(osOldPath, osNewPath); err != nil { + return errors.Wrapf(err, "Failed to rename in cache: %s to %s", osOldPath, osNewPath) + } + fs.Infof(name, "Renamed in cache") + return nil +} + // _close marks name as closed - must be called with the lock held func (c *cache) _close(isFile bool, name string) { for { diff --git a/vfs/file.go b/vfs/file.go index ed6b19ca2..2747e958d 100644 --- a/vfs/file.go +++ b/vfs/file.go @@ -129,6 +129,14 @@ func (f *File) rename(ctx context.Context, destDir *Dir, newName string) error { fs.Errorf(f.Path(), "File.Rename error: %v", err) return err } + + // Rename in the cache too if it exists + if f.d.vfs.Opt.CacheMode >= CacheModeWrites && f.d.vfs.cache.exists(f.Path()) { + if err := f.d.vfs.cache.rename(f.Path(), newPath); err != nil { + fs.Infof(f.Path(), "File.Rename failed in Cache: %v", err) + } + } + // newObject can be nil here for example if --dry-run if newObject == nil { err = errors.New("rename failed: nil object returned") diff --git a/vfs/file_test.go b/vfs/file_test.go index 06219b737..70848ef63 100644 --- a/vfs/file_test.go +++ b/vfs/file_test.go @@ -21,7 +21,7 @@ func fileCreate(t *testing.T, r *fstest.Run) (*VFS, *File, fstest.Item) { node, err := vfs.Stat("dir/file1") require.NoError(t, err) - require.True(t, node.IsFile()) + require.True(t, node.Mode().IsRegular()) return vfs, node.(*File), file1 }