Rename detection: File size must be equal
authorChristian Kamm <mail@ckamm.de>
Thu, 5 Oct 2017 09:39:35 +0000 (11:39 +0200)
committerRoeland Jago Douma <roeland@famdouma.nl>
Thu, 5 Oct 2017 20:01:42 +0000 (22:01 +0200)
Comparison of file sizes for potential conflicts was added in
0eb9401c624f20a128b46f8eb1fa5a984f9ef61e, but did not extend to checking
the file size in case of potential local moves.

This commit adds this check and adds tests for various move+change
scenarios.

src/csync/csync_update.cpp
test/testsyncengine.cpp

index 9412d51855769b4acccb17c46b941fea52274a4b..0271b1c569f2e4d11dfedfd37ed1091e0a4e6467 100644 (file)
@@ -265,8 +265,8 @@ static int _csync_detect_update(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> f
           fs->instruction = CSYNC_INSTRUCTION_NEW;
 
           bool isRename =
-              base.isValid() && base._inode == fs->inode && base._type == fs->type
-                  && (base._modtime == fs->modtime || fs->type == CSYNC_FTW_TYPE_DIR)
+              base.isValid() && base._type == fs->type
+                  && ((base._modtime == fs->modtime && base._fileSize == fs->size) || fs->type == CSYNC_FTW_TYPE_DIR)
 #ifdef NO_RENAME_EXTENSION
                   && _csync_sameextension(base._path, fs->path)
 #endif
index 169c2e6fb1b468279e8ec0fdf9143480d1ce93ac..9c1b7a2c021e8f74add3d1d8759fed114e3eb124 100644 (file)
@@ -535,6 +535,84 @@ private slots:
         QCOMPARE(nPUT, 6);
         QCOMPARE(n507, 3);
     }
+
+    void testLocalMove()
+    {
+        FakeFolder fakeFolder{ FileInfo::A12_B12_C12_S12() };
+
+        int nPUT = 0;
+        int nDELETE = 0;
+        fakeFolder.setServerOverride([&](QNetworkAccessManager::Operation op, const QNetworkRequest &) {
+            if (op == QNetworkAccessManager::PutOperation)
+                ++nPUT;
+            if (op == QNetworkAccessManager::DeleteOperation)
+                ++nDELETE;
+            return nullptr;
+        });
+
+        // For directly editing the remote checksum
+        FileInfo &remoteInfo = fakeFolder.remoteModifier();
+
+        // Simple move causing a remote rename
+        fakeFolder.localModifier().rename("A/a1", "A/a1m");
+        QVERIFY(fakeFolder.syncOnce());
+        QCOMPARE(fakeFolder.currentLocalState(), remoteInfo);
+        QCOMPARE(nPUT, 0);
+
+        // Move-and-change, causing a upload and delete
+        fakeFolder.localModifier().rename("A/a2", "A/a2m");
+        fakeFolder.localModifier().appendByte("A/a2m");
+        QVERIFY(fakeFolder.syncOnce());
+        QCOMPARE(fakeFolder.currentLocalState(), remoteInfo);
+        QCOMPARE(nPUT, 1);
+        QCOMPARE(nDELETE, 1);
+
+        // Move-and-change, mtime+content only
+        fakeFolder.localModifier().rename("B/b1", "B/b1m");
+        fakeFolder.localModifier().setContents("B/b1m", 'C');
+        QVERIFY(fakeFolder.syncOnce());
+        QCOMPARE(fakeFolder.currentLocalState(), remoteInfo);
+        QCOMPARE(nPUT, 2);
+        QCOMPARE(nDELETE, 2);
+
+        // Move-and-change, size+content only
+        auto mtime = fakeFolder.remoteModifier().find("B/b2")->lastModified;
+        fakeFolder.localModifier().rename("B/b2", "B/b2m");
+        fakeFolder.localModifier().appendByte("B/b2m");
+        fakeFolder.localModifier().setModTime("B/b2m", mtime);
+        QVERIFY(fakeFolder.syncOnce());
+        QCOMPARE(fakeFolder.currentLocalState(), remoteInfo);
+        QCOMPARE(nPUT, 3);
+        QCOMPARE(nDELETE, 3);
+
+        // Move-and-change, content only -- c1 has no checksum, so we fail to detect this!
+        mtime = fakeFolder.remoteModifier().find("C/c1")->lastModified;
+        fakeFolder.localModifier().rename("C/c1", "C/c1m");
+        fakeFolder.localModifier().setContents("C/c1m", 'C');
+        fakeFolder.localModifier().setModTime("C/c1m", mtime);
+        QVERIFY(fakeFolder.syncOnce());
+        QCOMPARE(nPUT, 3);
+        QCOMPARE(nDELETE, 3);
+        QVERIFY(!(fakeFolder.currentLocalState() == remoteInfo));
+
+        // cleanup, and upload a file that will have a checksum in the db
+        fakeFolder.localModifier().remove("C/c1m");
+        fakeFolder.localModifier().insert("C/c3");
+        QVERIFY(fakeFolder.syncOnce());
+        QCOMPARE(fakeFolder.currentLocalState(), remoteInfo);
+        QCOMPARE(nPUT, 4);
+        QCOMPARE(nDELETE, 4);
+
+        // Move-and-change, content only, this time while having a checksum
+        mtime = fakeFolder.remoteModifier().find("C/c3")->lastModified;
+        fakeFolder.localModifier().rename("C/c3", "C/c3m");
+        fakeFolder.localModifier().setContents("C/c3m", 'C');
+        fakeFolder.localModifier().setModTime("C/c3m", mtime);
+        QVERIFY(fakeFolder.syncOnce());
+        QCOMPARE(nPUT, 5);
+        QCOMPARE(nDELETE, 5);
+        QCOMPARE(fakeFolder.currentLocalState(), remoteInfo);
+    }
 };
 
 QTEST_GUILESS_MAIN(TestSyncEngine)