New discovery algorithm: Local rename
authorOlivier Goffart <ogoffart@woboq.com>
Wed, 11 Jul 2018 15:45:47 +0000 (17:45 +0200)
committerKevin Ottens <kevin.ottens@nextcloud.com>
Tue, 15 Dec 2020 09:57:58 +0000 (10:57 +0100)
src/csync/csync_update.cpp
src/libsync/discovery.cpp

index c144cee91c4e81d6213b31a9f454de305f2050fb..f440dcdc7c94f5c0c8bd6bea8077e1fe50a6bd66 100644 (file)
@@ -298,47 +298,7 @@ static int _csync_detect_update(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> f
   } else {
       /* check if it's a file and has been renamed */
       if (ctx->current == LOCAL_REPLICA) {
-          qCInfo(lcUpdate, "Checking for rename based on inode # %" PRId64 "", (uint64_t) fs->inode);
-
-          OCC::SyncJournalFileRecord base;
-          if(!ctx->statedb->getFileRecordByInode(fs->inode, &base)) {
-              ctx->status_code = CSYNC_STATUS_UNSUCCESSFUL;
-              return -1;
-          }
-
-          // Default to NEW unless we're sure it's a rename.
-          fs->instruction = CSYNC_INSTRUCTION_NEW;
-
-          bool isRename =
-              base.isValid() && base._type == fs->type
-                  && ((base._modtime == fs->modtime && base._fileSize == fs->size) || fs->type == ItemTypeDirectory)
-#ifdef NO_RENAME_EXTENSION
-                  && _csync_sameextension(base._path, fs->path)
-#endif
-              ;
-
-
-          // Verify the checksum where possible
-          if (isRename && !base._checksumHeader.isEmpty() && ctx->callbacks.checksum_hook
-              && fs->type == ItemTypeFile) {
-                  fs->checksumHeader = ctx->callbacks.checksum_hook(
-                      _rel_to_abs(ctx, fs->path), base._checksumHeader,
-                      ctx->callbacks.checksum_userdata);
-              if (!fs->checksumHeader.isEmpty()) {
-                  qCInfo(lcUpdate, "checking checksum of potential rename %s %s <-> %s", fs->path.constData(), fs->checksumHeader.constData(), base._checksumHeader.constData());
-                  isRename = fs->checksumHeader == base._checksumHeader;
-              }
-          }
-
-          if (isRename) {
-              qCInfo(lcUpdate, "pot rename detected based on inode # %" PRId64 "", (uint64_t) fs->inode);
-              /* inode found so the file has been renamed */
-              fs->instruction = CSYNC_INSTRUCTION_EVAL_RENAME;
-              if (fs->type == ItemTypeDirectory) {
-                  csync_rename_record(ctx, base._path, fs->path);
-              }
-          }
-          goto out;
+          /* ... PORTED */
 
       } else { /*... PORTED ... */
       }
index dcb2bd9006bf8fab74b1a9f45d87e414192d1196..34a2dc4230cf6a3c4c3d18588db6755a5fb561f1 100644 (file)
@@ -320,6 +320,21 @@ void ProcessDirectoryJob::processFile(PathTuple path,
 
     auto item = SyncFileItem::fromSyncJournalFileRecord(dbEntry);
     item->_file = path._target;
+    item->_originalFile = path._original;
+
+    auto computeLocalChecksum = [&](const QByteArray &type, const QString &path) {
+        if (!type.isEmpty()) {
+            // TODO: compute async?
+            QByteArray checksum = ComputeChecksum::computeNow(_discoveryData->_localDir + path, type);
+            if (!checksum.isEmpty()) {
+                item->_checksumHeader = makeChecksumHeader(type, checksum);
+                return true;
+            }
+        }
+        return false;
+    };
+
+
     auto recurseQueryServer = _queryServer;
     if (_queryServer == NormalQuery && serverEntry.isValid()) {
         item->_checksumHeader = serverEntry.checksumHeader;
@@ -531,7 +546,7 @@ void ProcessDirectoryJob::processFile(PathTuple path,
                 }
             }
             item->_direction = item->_instruction == CSYNC_INSTRUCTION_CONFLICT ? SyncFileItem::None : SyncFileItem::Down;
-        } else if (!dbEntry.isValid()) {
+        } else if (!dbEntry.isValid()) { // New local file
             item->_instruction = CSYNC_INSTRUCTION_NEW;
             item->_direction = SyncFileItem::Up;
             // TODO! rename;
@@ -540,6 +555,56 @@ void ProcessDirectoryJob::processFile(PathTuple path,
             item->_modtime = localEntry.modtime;
             item->_type = localEntry.isDirectory ? ItemTypeDirectory : ItemTypeFile;
             _childModified = true;
+
+            // Check if it is a rename
+            OCC::SyncJournalFileRecord base;
+            if (!_discoveryData->_statedb->getFileRecordByInode(localEntry.inode, &base)) {
+                qFatal("TODO: handle DB Errors");
+            }
+            bool isRename = base.isValid() && base._type == item->_type
+                && ((base._modtime == localEntry.modtime && base._fileSize == localEntry.size) || item->_type == ItemTypeDirectory);
+
+            if (isRename) {
+                //  The old file must have been deleted.
+                isRename = !QFile::exists(_discoveryData->_localDir + base._path);
+            }
+
+            // Verify the checksum where possible
+            if (isRename && !base._checksumHeader.isEmpty() && item->_type == ItemTypeFile) {
+                if (computeLocalChecksum(parseChecksumHeaderType(base._checksumHeader), path._original)) {
+                    qCInfo(lcDisco) << "checking checksum of potential rename " << path._original << item->_checksumHeader << base._checksumHeader;
+                    isRename = item->_checksumHeader == base._checksumHeader;
+                }
+            }
+            if (isRename) {
+                auto originalPath = QString::fromUtf8(base._path);
+                auto it = _discoveryData->_deletedItem.find(originalPath);
+                if (it != _discoveryData->_deletedItem.end()) {
+                    if ((*it)->_instruction != CSYNC_INSTRUCTION_REMOVE)
+                        isRename = false;
+                    else
+                        (*it)->_instruction = CSYNC_INSTRUCTION_NONE;
+                }
+                if (_discoveryData->_renamedItems.contains(originalPath))
+                    isRename = false;
+                if (isRename) {
+                    delete _discoveryData->_queuedDeletedDirectories.take(originalPath);
+                    _discoveryData->_renamedItems.insert(originalPath);
+
+                    item->_modtime = base._modtime;
+                    item->_inode = base._inode;
+                    item->_instruction = CSYNC_INSTRUCTION_RENAME;
+                    item->_direction = SyncFileItem::Up;
+                    item->_renameTarget = path._target;
+                    item->_file = originalPath;
+                    item->_originalFile = originalPath;
+                    item->_fileId = base._fileId;
+                    item->_etag = base._etag;
+                    path._original = originalPath;
+                    path._server = originalPath;
+                    qCInfo(lcDisco) << "Rename detected (up) " << item->_file << " -> " << item->_renameTarget;
+                }
+            }
         } else {
             item->_instruction = CSYNC_INSTRUCTION_SYNC;
             item->_direction = SyncFileItem::Up;
@@ -554,17 +619,9 @@ void ProcessDirectoryJob::processFile(PathTuple path,
             // check #4754 #4755
             bool isEmlFile = path._original.endsWith(QLatin1String(".eml"), Qt::CaseInsensitive);
             if (isEmlFile && dbEntry._fileSize == localEntry.size && !dbEntry._checksumHeader.isEmpty()) {
-                QByteArray type = parseChecksumHeaderType(dbEntry._checksumHeader);
-                if (!type.isEmpty()) {
-                    // TODO: compute async?
-                    QByteArray checksum = ComputeChecksum::computeNow(_discoveryData->_localDir + path._local, type);
-                    if (!checksum.isEmpty()) {
-                        item->_checksumHeader = makeChecksumHeader(type, checksum);
-                        if (item->_checksumHeader == dbEntry._checksumHeader) {
-                            qCInfo(lcDisco) << "NOTE: Checksums are identical, file did not actually change: " << path._local;
-                            item->_instruction = CSYNC_INSTRUCTION_UPDATE_METADATA;
-                        }
-                    }
+                if (computeLocalChecksum(parseChecksumHeaderType(dbEntry._checksumHeader), path._local) && item->_checksumHeader == dbEntry._checksumHeader) {
+                    qCInfo(lcDisco) << "NOTE: Checksums are identical, file did not actually change: " << path._local;
+                    item->_instruction = CSYNC_INSTRUCTION_UPDATE_METADATA;
                 }
             }
         }