New Disco algorithm: Type change (file to dir)
authorOlivier Goffart <ogoffart@woboq.com>
Tue, 17 Jul 2018 15:18:07 +0000 (17:18 +0200)
committerKevin Ottens <kevin.ottens@nextcloud.com>
Tue, 15 Dec 2020 09:57:59 +0000 (10:57 +0100)
src/libsync/discovery.cpp
src/libsync/syncengine.cpp

index 463e3e6315b6d32d93008e20a76d770812c3f831..5a50992d1a7cf83963a1cc59406767a5de8d5fe2 100644 (file)
@@ -94,7 +94,9 @@ void ProcessDirectoryJob::start()
         auto dh = csync_vio_local_opendir((_discoveryData->_localDir + _currentFolder._local).toUtf8());
         if (!dh) {
             qCInfo(lcDisco) << "Error while opening directory" << (_discoveryData->_localDir + _currentFolder._local) << errno;
-            serverJob->abort();
+            if (serverJob) {
+                serverJob->abort();
+            }
             QString errorString = tr("Error while opening directory %1").arg(_discoveryData->_localDir + _currentFolder._local);
             if (errno == EACCES) {
                 errorString = tr("Directory not accessible on client, permission denied");
@@ -326,9 +328,9 @@ bool ProcessDirectoryJob::handleExcluded(const QString &path, bool isDirectory,
         item->_errorString = tr("Stat failed.");
         break;
     case CSYNC_FILE_EXCLUDE_CONFLICT:
-        qFatal("TODO: conflicts");
-#if 0
+        item->_errorString = tr("Conflict: Server version downloaded, local copy renamed and not uploaded.");
         item->_status = SyncFileItem::Conflict;
+#if 0 // TODO: port this
         if (_propagator->account()->capabilities().uploadConflictFiles()) {
             // For uploaded conflict files, files with no action performed on them should
             // be displayed: but we mustn't overwrite the instruction if something happens
@@ -337,9 +339,6 @@ bool ProcessDirectoryJob::handleExcluded(const QString &path, bool isDirectory,
                 item->_errorString = tr("Unresolved conflict.");
                 item->_instruction = CSYNC_INSTRUCTION_IGNORE;
             }
-        } else {
-            item->_errorString = tr("Conflict: Server version downloaded, local copy renamed and not uploaded.");
-        }
 #endif
         break;
     case CSYNC_FILE_EXCLUDE_CANNOT_ENCODE: // FIXME!
@@ -578,6 +577,13 @@ void ProcessDirectoryJob::processFile(PathTuple path,
             if (item->_instruction == CSYNC_INSTRUCTION_NEW) {
                 postProcessNew();
             }
+        } else if (serverEntry.isDirectory != (dbEntry._type == ItemTypeDirectory)) {
+            // If the type of the entity changed, it's like NEW, but
+            // needs to delete the other entity first.
+            item->_instruction = CSYNC_INSTRUCTION_TYPE_CHANGE;
+            item->_direction = SyncFileItem::Down;
+            item->_modtime = serverEntry.modtime;
+            item->_size = serverEntry.size;
         } else if (dbEntry._type == ItemTypeVirtualFileDownload) {
             item->_direction = SyncFileItem::Down;
             item->_instruction = CSYNC_INSTRUCTION_NEW;
@@ -602,7 +608,8 @@ void ProcessDirectoryJob::processFile(PathTuple path,
             recurseQueryServer = ParentNotChanged;
         }
     }
-    bool serverModified = item->_instruction == CSYNC_INSTRUCTION_NEW || item->_instruction == CSYNC_INSTRUCTION_SYNC || item->_instruction == CSYNC_INSTRUCTION_RENAME;
+    bool serverModified = item->_instruction == CSYNC_INSTRUCTION_NEW || item->_instruction == CSYNC_INSTRUCTION_SYNC
+        || item->_instruction == CSYNC_INSTRUCTION_RENAME || item->_instruction == CSYNC_INSTRUCTION_TYPE_CHANGE;
     if ((dbEntry.isValid() && dbEntry._type == ItemTypeVirtualFile) || (localEntry.isValid() && localEntry.isVirtualFile)) {
         // Do not download virtual files
         if (serverModified || dbEntry._type != ItemTypeVirtualFile)
@@ -610,9 +617,12 @@ void ProcessDirectoryJob::processFile(PathTuple path,
         serverModified = false;
         item->_type = ItemTypeVirtualFile;
     }
-    _childModified |= serverModified;
+    if (!_dirItem || _dirItem->_direction == SyncFileItem::Up) {
+        _childModified |= serverModified;
+    }
     if (localEntry.isValid()) {
         item->_inode = localEntry.inode;
+        bool typeChange = dbEntry.isValid() && localEntry.isDirectory != (dbEntry._type == ItemTypeDirectory);
         if (localEntry.isVirtualFile) {
             item->_type = ItemTypeVirtualFile;
             if (_queryServer != ParentNotChanged && !serverEntry.isValid()) {
@@ -622,7 +632,8 @@ void ProcessDirectoryJob::processFile(PathTuple path,
                 item->_instruction = CSYNC_INSTRUCTION_UPDATE_METADATA;
                 item->_direction = SyncFileItem::Down; // Does not matter
             }
-        } else if (dbEntry.isValid() && ((dbEntry._modtime == localEntry.modtime && dbEntry._fileSize == localEntry.size) || (localEntry.isDirectory && dbEntry._type == ItemTypeDirectory))) {
+        } else if (dbEntry.isValid() && !typeChange && ((dbEntry._modtime == localEntry.modtime && dbEntry._fileSize == localEntry.size) || (localEntry.isDirectory && dbEntry._type == ItemTypeDirectory))) {
+            // Local file unchanged.
             if (_queryServer != ParentNotChanged && !serverEntry.isValid()) {
                 item->_instruction = CSYNC_INSTRUCTION_REMOVE;
                 item->_direction = SyncFileItem::Down;
@@ -687,6 +698,16 @@ void ProcessDirectoryJob::processFile(PathTuple path,
                 }
             }
             item->_direction = item->_instruction == CSYNC_INSTRUCTION_CONFLICT ? SyncFileItem::None : SyncFileItem::Down;
+        } else if (typeChange) {
+            item->_instruction = CSYNC_INSTRUCTION_TYPE_CHANGE;
+            item->_direction = SyncFileItem::Up;
+            item->_checksumHeader.clear();
+            item->_size = localEntry.size;
+            item->_modtime = localEntry.modtime;
+            item->_type = localEntry.isDirectory ? ItemTypeDirectory : ItemTypeFile;
+            if (!_dirItem || _dirItem->_direction == SyncFileItem::Down) {
+                _childModified = true;
+            }
         } else if (!dbEntry.isValid()) { // New local file
             item->_instruction = CSYNC_INSTRUCTION_NEW;
             item->_direction = SyncFileItem::Up;
@@ -694,8 +715,9 @@ void ProcessDirectoryJob::processFile(PathTuple path,
             item->_size = localEntry.size;
             item->_modtime = localEntry.modtime;
             item->_type = localEntry.isDirectory ? ItemTypeDirectory : ItemTypeFile;
-            _childModified = true;
-
+            if (!_dirItem || _dirItem->_direction == SyncFileItem::Down) {
+                _childModified = true;
+            }
             // Check if it is a rename
             OCC::SyncJournalFileRecord base;
             if (!_discoveryData->_statedb->getFileRecordByInode(localEntry.inode, &base)) {
@@ -807,7 +829,9 @@ void ProcessDirectoryJob::processFile(PathTuple path,
             item->_modtime = localEntry.modtime;
             item->_previousSize = dbEntry._fileSize;
             item->_previousModtime = dbEntry._modtime;
-            _childModified = true;
+            if (!_dirItem || _dirItem->_direction == SyncFileItem::Down) {
+                _childModified = true;
+            }
 
             // Checksum comparison at this stage is only enabled for .eml files,
             // check #4754 #4755
@@ -847,12 +871,12 @@ void ProcessDirectoryJob::processFile(PathTuple path,
 
     qCInfo(lcDisco) << "Discovered" << item->_file << item->_instruction << item->_direction << item->_type;
 
-    if (item->isDirectory()) {
+    if (item->isDirectory() || localEntry.isDirectory || serverEntry.isDirectory) {
         if (item->_instruction == CSYNC_INSTRUCTION_SYNC) {
             item->_instruction = CSYNC_INSTRUCTION_UPDATE_METADATA;
         }
         auto job = new ProcessDirectoryJob(item, recurseQueryServer,
-            localEntry.isValid() || item->_instruction == CSYNC_INSTRUCTION_RENAME ? NormalQuery : ParentDontExist,
+            localEntry.isDirectory || item->_instruction == CSYNC_INSTRUCTION_RENAME ? NormalQuery : ParentDontExist,
             _discoveryData, this);
         job->_currentFolder = path;
         if (item->_instruction == CSYNC_INSTRUCTION_REMOVE) {
@@ -940,6 +964,13 @@ void ProcessDirectoryJob::progress()
                 // re-create directory that has modified contents
                 _dirItem->_instruction = CSYNC_INSTRUCTION_NEW;
             }
+            if (_childModified && _dirItem->_instruction == CSYNC_INSTRUCTION_TYPE_CHANGE) {
+                if (_dirItem->_direction == SyncFileItem::Up) {
+                    _dirItem->_type = ItemTypeDirectory;
+                    _dirItem->_direction = SyncFileItem::Down;
+                }
+                _dirItem->_instruction = CSYNC_INSTRUCTION_CONFLICT;
+            }
             if (_childIgnored && _dirItem->_instruction == CSYNC_INSTRUCTION_REMOVE) {
                 // Do not remove a directory that has ignored files
                 _dirItem->_instruction = CSYNC_INSTRUCTION_NONE;
index 74d1c82ca79ab8a5b13c7860257e68568b549d5b..b45a3259a8f8ebac2bbbf6d53fc50f764ac0a608 100644 (file)
@@ -369,6 +369,11 @@ void SyncEngine::conflictRecordMaintenance()
 
 void OCC::SyncEngine::slotItemDiscovered(const OCC::SyncFileItemPtr &item)
 {
+    _seenFiles.insert(item->_file);
+    if (!item->_renameTarget.isEmpty()) {
+        // Yes, this records both the rename renameTarget and the original so we keep both in case of a rename
+        _seenFiles.insert(item->_renameTarget);
+    }
     if (item->_instruction == CSYNC_INSTRUCTION_UPDATE_METADATA && !item->isDirectory()) {
         // For directories, metadata-only updates will be done after all their files are propagated.
 
@@ -526,14 +531,6 @@ int SyncEngine::treewalkFile(csync_file_stat_t * /*file*/, csync_file_stat_t * /
         item->_serverHasIgnoredFiles = file->has_ignored_files;
     }
 
-    // record the seen files to be able to clean the journal later
-    _seenFiles.insert(item->_file);
-    if (!renameTarget.isEmpty()) {
-        // Yes, this records both the rename renameTarget and the original so we keep both in case of a rename
-        _seenFiles.insert(renameTarget);
-    }
-
-
 
 
     switch (file->error_status) {
@@ -579,18 +576,7 @@ int SyncEngine::treewalkFile(csync_file_stat_t * /*file*/, csync_file_stat_t * /
     int re = 0;
     switch (file->instruction) {
     case CSYNC_INSTRUCTION_NONE: {
-        // Any files that are instruction NONE?
-        if (!isDirectory && (!other || other->instruction == CSYNC_INSTRUCTION_NONE || other->instruction == CSYNC_INSTRUCTION_UPDATE_METADATA)) {
-            _hasNoneFiles = true;
-        }
-        // Put none-instruction conflict files into the syncfileitem list
-        if (account()->capabilities().uploadConflictFiles()
-            && file->error_status == CSYNC_STATUS_INDIVIDUAL_IS_CONFLICT_FILE
-            && item->_instruction == CSYNC_INSTRUCTION_IGNORE) {
-            break;
-        }
-        // No syncing or update to be done.
-        return re;
+       ... ported ....
     }
     case CSYNC_INSTRUCTION_UPDATE_METADATA:
         dir = SyncFileItem::None;