summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAudrius Butkevicius <audrius.butkevicius@gmail.com>2020-05-16 13:39:27 +0100
committerJakob Borg <jakob@kastelo.net>2020-05-16 14:40:20 +0200
commit258341f8bf978a93a4e42c8e75e188f416215247 (patch)
tree6e2f7dd9e068b4f01a7f15ffda20c4c75429fe87
parentf5ca21368250394b8468e27ecc2ecb6d42752d2f (diff)
lib/model: Fix rename handling (ref #6650) (#6652)v1.6.0-rc.3
-rw-r--r--lib/db/lowlevel.go4
-rw-r--r--lib/db/set_test.go10
-rw-r--r--lib/model/folder.go23
-rw-r--r--lib/model/folder_sendrecv_test.go2
-rw-r--r--lib/model/model_test.go126
5 files changed, 153 insertions, 12 deletions
diff --git a/lib/db/lowlevel.go b/lib/db/lowlevel.go
index bbeae7fcc..e372d2c67 100644
--- a/lib/db/lowlevel.go
+++ b/lib/db/lowlevel.go
@@ -198,7 +198,7 @@ func (db *Lowlevel) updateLocalFiles(folder []byte, fs []protocol.FileInfo, meta
blocksHashSame := ok && bytes.Equal(ef.BlocksHash, f.BlocksHash)
if ok {
- if len(ef.Blocks) != 0 && !ef.IsInvalid() {
+ if len(ef.Blocks) != 0 && !ef.IsInvalid() && ef.Size > 0 {
for _, block := range ef.Blocks {
keyBuf, err = db.keyer.GenerateBlockMapKey(keyBuf, folder, block.Hash, name)
if err != nil {
@@ -259,7 +259,7 @@ func (db *Lowlevel) updateLocalFiles(folder []byte, fs []protocol.FileInfo, meta
}
l.Debugf("adding sequence; folder=%q sequence=%v %v", folder, f.Sequence, f.Name)
- if len(f.Blocks) != 0 && !f.IsInvalid() {
+ if len(f.Blocks) != 0 && !f.IsInvalid() && f.Size > 0 {
for i, block := range f.Blocks {
binary.BigEndian.PutUint32(blockBuf, uint32(i))
keyBuf, err = db.keyer.GenerateBlockMapKey(keyBuf, folder, block.Hash, name)
diff --git a/lib/db/set_test.go b/lib/db/set_test.go
index 5a475b150..b6b2cd777 100644
--- a/lib/db/set_test.go
+++ b/lib/db/set_test.go
@@ -484,11 +484,11 @@ func TestUpdateToInvalid(t *testing.T) {
f := db.NewBlockFinder(ldb)
localHave := fileList{
- protocol.FileInfo{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(1)},
- protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Blocks: genBlocks(2)},
- protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(5), LocalFlags: protocol.FlagLocalIgnored},
- protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Blocks: genBlocks(7)},
- protocol.FileInfo{Name: "e", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, LocalFlags: protocol.FlagLocalIgnored},
+ protocol.FileInfo{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(1), Size: 1},
+ protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Blocks: genBlocks(2), Size: 1},
+ protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(5), LocalFlags: protocol.FlagLocalIgnored, Size: 1},
+ protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Blocks: genBlocks(7), Size: 1},
+ protocol.FileInfo{Name: "e", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, LocalFlags: protocol.FlagLocalIgnored, Size: 1},
}
replace(s, protocol.LocalDeviceID, localHave)
diff --git a/lib/model/folder.go b/lib/model/folder.go
index c9b7d14fd..8db1dfb58 100644
--- a/lib/model/folder.go
+++ b/lib/model/folder.go
@@ -453,20 +453,27 @@ func (f *folder) scanSubdirs(subDirs []string) error {
}()
f.clearScanErrors(subDirs)
+ alreadyUsed := make(map[string]struct{})
for res := range fchan {
if res.Err != nil {
f.newScanError(res.Path, res.Err)
continue
}
- if err := batch.flushIfFull(); err != nil {
- return err
+
+ if batch.full() {
+ if err := batch.flush(); err != nil {
+ return err
+ }
+ snap.Release()
+ snap = f.fset.Snapshot()
+ alreadyUsed = make(map[string]struct{})
}
batch.append(res.File)
changes++
if f.localFlags&protocol.FlagLocalReceiveOnly == 0 {
- if nf, ok := f.findRename(snap, mtimefs, res.File); ok {
+ if nf, ok := f.findRename(snap, mtimefs, res.File, alreadyUsed); ok {
batch.append(nf)
changes++
}
@@ -622,8 +629,8 @@ func (f *folder) scanSubdirs(subDirs []string) error {
return nil
}
-func (f *folder) findRename(snap *db.Snapshot, mtimefs fs.Filesystem, file protocol.FileInfo) (protocol.FileInfo, bool) {
- if len(file.Blocks) == 0 {
+func (f *folder) findRename(snap *db.Snapshot, mtimefs fs.Filesystem, file protocol.FileInfo, alreadyUsed map[string]struct{}) (protocol.FileInfo, bool) {
+ if len(file.Blocks) == 0 || file.Size == 0 {
return protocol.FileInfo{}, false
}
@@ -639,6 +646,10 @@ func (f *folder) findRename(snap *db.Snapshot, mtimefs fs.Filesystem, file proto
default:
}
+ if _, ok := alreadyUsed[fi.Name]; ok {
+ return true
+ }
+
if fi.ShouldConflict() {
return true
}
@@ -658,6 +669,8 @@ func (f *folder) findRename(snap *db.Snapshot, mtimefs fs.Filesystem, file proto
return true
}
+ alreadyUsed[fi.Name] = struct{}{}
+
nf = fi
nf.SetDeleted(f.shortID)
nf.LocalFlags = f.localFlags
diff --git a/lib/model/folder_sendrecv_test.go b/lib/model/folder_sendrecv_test.go
index b6dfb6f56..874fe570a 100644
--- a/lib/model/folder_sendrecv_test.go
+++ b/lib/model/folder_sendrecv_test.go
@@ -213,6 +213,7 @@ func TestCopierFinder(t *testing.T) {
existingBlocks := []int{0, 2, 3, 4, 0, 0, 7, 0}
existingFile := setupFile(fs.TempName("file"), existingBlocks)
+ existingFile.Size = 1
requiredFile := existingFile
requiredFile.Blocks = blocks[1:]
requiredFile.Name = "file2"
@@ -422,6 +423,7 @@ func TestCopierCleanup(t *testing.T) {
// Create a file
file := setupFile("test", []int{0})
+ file.Size = 1
m, f := setupSendReceiveFolder(file)
defer cleanupSRFolder(f, m)
diff --git a/lib/model/model_test.go b/lib/model/model_test.go
index a773f645b..7be967562 100644
--- a/lib/model/model_test.go
+++ b/lib/model/model_test.go
@@ -3609,6 +3609,132 @@ func TestRenameSequenceOrder(t *testing.T) {
}
}
+func TestRenameSameFile(t *testing.T) {
+ wcfg, fcfg := tmpDefaultWrapper()
+ m := setupModel(wcfg)
+ defer cleanupModel(m)
+
+ ffs := fcfg.Filesystem()
+ must(t, writeFile(ffs, "file", []byte("file"), 0644))
+
+ m.ScanFolders()
+
+ count := 0
+ snap := dbSnapshot(t, m, "default")
+ snap.WithHave(protocol.LocalDeviceID, func(i db.FileIntf) bool {
+ count++
+ return true
+ })
+ snap.Release()
+
+ if count != 1 {
+ t.Errorf("Unexpected count: %d != %d", count, 1)
+ }
+
+ must(t, ffs.Rename("file", "file1"))
+ must(t, osutil.Copy(ffs, ffs, "file1", "file0"))
+ must(t, osutil.Copy(ffs, ffs, "file1", "file2"))
+ must(t, osutil.Copy(ffs, ffs, "file1", "file3"))
+ must(t, osutil.Copy(ffs, ffs, "file1", "file4"))
+
+ m.ScanFolders()
+
+ snap = dbSnapshot(t, m, "default")
+ defer snap.Release()
+
+ prevSeq := int64(0)
+ seen := false
+ snap.WithHaveSequence(0, func(i db.FileIntf) bool {
+ if i.SequenceNo() <= prevSeq {
+ t.Fatalf("non-increasing sequences: %d <= %d", i.SequenceNo(), prevSeq)
+ }
+ if i.FileName() == "file" {
+ if seen {
+ t.Fatal("already seen file")
+ }
+ seen = true
+ }
+ prevSeq = i.SequenceNo()
+ return true
+ })
+}
+
+func TestRenameEmptyFile(t *testing.T) {
+ wcfg, fcfg := tmpDefaultWrapper()
+ m := setupModel(wcfg)
+ defer cleanupModel(m)
+
+ ffs := fcfg.Filesystem()
+
+ must(t, writeFile(ffs, "file", []byte("data"), 0644))
+ must(t, writeFile(ffs, "empty", nil, 0644))
+
+ m.ScanFolders()
+
+ snap := dbSnapshot(t, m, "default")
+ defer snap.Release()
+ empty, eok := snap.Get(protocol.LocalDeviceID, "empty")
+ if !eok {
+ t.Fatal("failed to find empty file")
+ }
+ file, fok := snap.Get(protocol.LocalDeviceID, "file")
+ if !fok {
+ t.Fatal("failed to find non-empty file")
+ }
+
+ count := 0
+ snap.WithBlocksHash(empty.BlocksHash, func(_ db.FileIntf) bool {
+ count++
+ return true
+ })
+
+ if count != 0 {
+ t.Fatalf("Found %d entries for empty file, expected 0", count)
+ }
+
+ count = 0
+ snap.WithBlocksHash(file.BlocksHash, func(_ db.FileIntf) bool {
+ count++
+ return true
+ })
+
+ if count != 1 {
+ t.Fatalf("Found %d entries for non-empty file, expected 1", count)
+ }
+
+ must(t, ffs.Rename("file", "new-file"))
+ must(t, ffs.Rename("empty", "new-empty"))
+
+ // Scan
+ m.ScanFolders()
+
+ snap = dbSnapshot(t, m, "default")
+ defer snap.Release()
+
+ count = 0
+ snap.WithBlocksHash(empty.BlocksHash, func(_ db.FileIntf) bool {
+ count++
+ return true
+ })
+
+ if count != 0 {
+ t.Fatalf("Found %d entries for empty file, expected 0", count)
+ }
+
+ count = 0
+ snap.WithBlocksHash(file.BlocksHash, func(i db.FileIntf) bool {
+ count++
+ if i.FileName() != "new-file" {
+ t.Fatalf("unexpected file name %s, expected new-file", i.FileName())
+ }
+ return true
+ })
+
+ if count != 1 {
+ t.Fatalf("Found %d entries for non-empty file, expected 1", count)
+ }
+}
+
func TestBlockListMap(t *testing.T) {
wcfg, fcfg := tmpDefaultWrapper()
m := setupModel(wcfg)