mirror of
https://github.com/rclone/rclone.git
synced 2025-01-03 04:49:47 +01:00
vfs: remove virtual directory entries on backends which can have empty dirs
Before this change we only removed virtual directory entries when they appeared in the listing. This works fine except for when virtual directory entries are deleted outside rclone. This change deletes directory virtual directory entries for backends which can have empty directories before reading the directory. See: https://forum.rclone.org/t/google-drive-rclone-rc-operations-mkdir-fails-on-repeats/17787
This commit is contained in:
parent
67098511db
commit
394a4b0afe
44
vfs/dir.go
44
vfs/dir.go
@ -42,9 +42,11 @@ type Dir struct {
|
||||
type vState byte
|
||||
|
||||
const (
|
||||
vOK vState = iota // Not virtual
|
||||
vAdd // added file or directory
|
||||
vDel // removed file or directory
|
||||
vOK vState = iota // Not virtual
|
||||
vAddFile // added file
|
||||
vDelFile // removed file
|
||||
vAddDir // added directory
|
||||
vDelDir // removed directory
|
||||
)
|
||||
|
||||
func newDir(vfs *VFS, f fs.Fs, parent *Dir, fsDir fs.Directory) *Dir {
|
||||
@ -295,8 +297,12 @@ func (d *Dir) addObject(node Node) {
|
||||
if d.virtual == nil {
|
||||
d.virtual = make(map[string]vState)
|
||||
}
|
||||
d.virtual[leaf] = vAdd
|
||||
fs.Debugf(d.path, "Added virtual directory entry %v: %q", vAdd, leaf)
|
||||
virtualState := vAddFile
|
||||
if node.IsDir() {
|
||||
virtualState = vAddDir
|
||||
}
|
||||
d.virtual[leaf] = virtualState
|
||||
fs.Debugf(d.path, "Added virtual directory entry %v: %q", virtualState, leaf)
|
||||
d.mu.Unlock()
|
||||
}
|
||||
|
||||
@ -339,8 +345,8 @@ func (d *Dir) delObject(leaf string) {
|
||||
if d.virtual == nil {
|
||||
d.virtual = make(map[string]vState)
|
||||
}
|
||||
d.virtual[leaf] = vDel
|
||||
fs.Debugf(d.path, "Added virtual directory entry %v: %q", vDel, leaf)
|
||||
d.virtual[leaf] = vDelFile
|
||||
fs.Debugf(d.path, "Added virtual directory entry %v: %q", vDelFile, leaf)
|
||||
d.mu.Unlock()
|
||||
}
|
||||
|
||||
@ -392,6 +398,22 @@ func (d *Dir) _readDirFromDirTree(dirTree dirtree.DirTree, when time.Time) error
|
||||
// set the last read time - must be called with the lock held
|
||||
func (d *Dir) _readDirFromEntries(entries fs.DirEntries, dirTree dirtree.DirTree, when time.Time) error {
|
||||
var err error
|
||||
|
||||
// For backends which can have empty directories remove
|
||||
// virtual directory entries before doing the listing - they
|
||||
// should definitely appear in the listing.
|
||||
if d.f.Features().CanHaveEmptyDirectories {
|
||||
for name, virtualState := range d.virtual {
|
||||
if virtualState == vAddDir {
|
||||
delete(d.virtual, name)
|
||||
if len(d.virtual) == 0 {
|
||||
d.virtual = nil
|
||||
}
|
||||
fs.Debugf(d.path, "Removed virtual directory entry %v: %q", virtualState, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cache the items by name
|
||||
found := make(map[string]struct{})
|
||||
for _, entry := range entries {
|
||||
@ -403,7 +425,7 @@ func (d *Dir) _readDirFromEntries(entries fs.DirEntries, dirTree dirtree.DirTree
|
||||
found[name] = struct{}{}
|
||||
virtualState := d.virtual[name]
|
||||
switch virtualState {
|
||||
case vAdd:
|
||||
case vAddFile, vAddDir:
|
||||
// item was added to the dir but since it is found in a
|
||||
// listing is no longer virtual
|
||||
delete(d.virtual, name)
|
||||
@ -411,7 +433,7 @@ func (d *Dir) _readDirFromEntries(entries fs.DirEntries, dirTree dirtree.DirTree
|
||||
d.virtual = nil
|
||||
}
|
||||
fs.Debugf(d.path, "Removed virtual directory entry %v: %q", virtualState, name)
|
||||
case vDel:
|
||||
case vDelFile, vDelDir:
|
||||
// item is deleted from the dir so skip it
|
||||
continue
|
||||
case vOK:
|
||||
@ -453,7 +475,7 @@ func (d *Dir) _readDirFromEntries(entries fs.DirEntries, dirTree dirtree.DirTree
|
||||
}
|
||||
// delete unused entries
|
||||
for name := range d.items {
|
||||
if _, ok := found[name]; !ok && d.virtual[name] != vAdd {
|
||||
if _, ok := found[name]; !ok && d.virtual[name] != vAddFile && d.virtual[name] != vAddDir {
|
||||
// item was added to the dir but wasn't found in the
|
||||
// listing - remove it unless it was virtually added
|
||||
delete(d.items, name)
|
||||
@ -461,7 +483,7 @@ func (d *Dir) _readDirFromEntries(entries fs.DirEntries, dirTree dirtree.DirTree
|
||||
}
|
||||
// delete unused virtuals
|
||||
for name, virtualState := range d.virtual {
|
||||
if _, ok := found[name]; !ok && virtualState == vDel {
|
||||
if _, ok := found[name]; !ok && (virtualState == vDelFile || virtualState == vDelDir) {
|
||||
// We have a virtual delete but the item wasn't found in
|
||||
// the listing so no longer needs a virtual delete.
|
||||
delete(d.virtual, name)
|
||||
|
@ -9,13 +9,15 @@ func _() {
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[vOK-0]
|
||||
_ = x[vAdd-1]
|
||||
_ = x[vDel-2]
|
||||
_ = x[vAddFile-1]
|
||||
_ = x[vDelFile-2]
|
||||
_ = x[vAddDir-3]
|
||||
_ = x[vDelDir-4]
|
||||
}
|
||||
|
||||
const _vState_name = "vOKvAddvDel"
|
||||
const _vState_name = "vOKvAddFilevDelFilevAddDirvDelDir"
|
||||
|
||||
var _vState_index = [...]uint8{0, 3, 7, 11}
|
||||
var _vState_index = [...]uint8{0, 3, 11, 19, 26, 33}
|
||||
|
||||
func (i vState) String() string {
|
||||
if i >= vState(len(_vState_index)-1) {
|
||||
|
Loading…
Reference in New Issue
Block a user