New Disco algorithm: Type change (file to dir)

This commit is contained in:
Olivier Goffart 2018-07-17 17:18:07 +02:00 committed by Kevin Ottens
parent 1e8c37d3d6
commit 35e40b58ca
No known key found for this signature in database
GPG key ID: 074BBBCB8DECC9E2
2 changed files with 51 additions and 34 deletions

View 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;

View 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;