From: Christian Kamm Date: Wed, 6 Jan 2016 09:01:22 +0000 (+0100) Subject: Propagation: Fix dir <-> file changes propagating to server #4302 X-Git-Tag: archive/raspbian/3.16.7-1_deb13u1+rpi1~1^2~1254^2~90 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=abf5a5ad1e4eadbeb24a999aeeab39ed4357eef9;p=nextcloud-desktop.git Propagation: Fix dir <-> file changes propagating to server #4302 * Ensure every time a file becomes a directory or the other way around the item is flagged as INSTRUCTION_TYPE_CHANGE. * Delete the badly-typed entity if necessary in the propagation jobs. --- diff --git a/csync/src/csync.h b/csync/src/csync.h index 06a449756..49cad896c 100644 --- a/csync/src/csync.h +++ b/csync/src/csync.h @@ -134,7 +134,10 @@ enum csync_instructions_e { CSYNC_INSTRUCTION_IGNORE = 0x00000020, /* The file is ignored (UPDATE|RECONCILE) */ CSYNC_INSTRUCTION_SYNC = 0x00000040, /* The file need to be pushed to the other remote (RECONCILE) */ CSYNC_INSTRUCTION_STAT_ERROR = 0x00000080, - CSYNC_INSTRUCTION_ERROR = 0x00000100 + CSYNC_INSTRUCTION_ERROR = 0x00000100, + CSYNC_INSTRUCTION_TYPE_CHANGE = 0x0000200, /* Like NEW, but deletes the old entity first (RECONCILE) + Used when the type of something changes from directory to file + or back. */ }; enum csync_ftw_type_e { diff --git a/csync/src/csync_reconcile.c b/csync/src/csync_reconcile.c index 8fb295f9c..ab25b1c87 100644 --- a/csync/src/csync_reconcile.c +++ b/csync/src/csync_reconcile.c @@ -283,7 +283,13 @@ static int _csync_merge_algorithm_visitor(void *obj, void *data) { break; /* file on the other replica has not been modified */ case CSYNC_INSTRUCTION_NONE: - cur->instruction = CSYNC_INSTRUCTION_SYNC; + if (cur->type != other->type) { + // If the type of the entity changed, it's like NEW, but + // needs to delete the other entity first. + cur->instruction = CSYNC_INSTRUCTION_TYPE_CHANGE; + } else { + cur->instruction = CSYNC_INSTRUCTION_SYNC; + } break; case CSYNC_INSTRUCTION_IGNORE: cur->instruction = CSYNC_INSTRUCTION_IGNORE; diff --git a/csync/src/csync_update.c b/csync/src/csync_update.c index d1667149f..bd0a4c73b 100644 --- a/csync/src/csync_update.c +++ b/csync/src/csync_update.c @@ -301,6 +301,11 @@ static int _csync_detect_update(CSYNC *ctx, const char *file, goto out; } } + + // Preserve the EVAL flag later on if the type has changed. + if (tmp->type != fs->type) + st->child_modified = 1; + st->instruction = CSYNC_INSTRUCTION_EVAL; goto out; } diff --git a/csync/src/csync_util.c b/csync/src/csync_util.c index d9e27b744..b1746b17b 100644 --- a/csync/src/csync_util.c +++ b/csync/src/csync_util.c @@ -55,6 +55,7 @@ static const _instr_code_struct _instr[] = { "INSTRUCTION_SYNC", CSYNC_INSTRUCTION_SYNC }, { "INSTRUCTION_STAT_ERR", CSYNC_INSTRUCTION_STAT_ERROR }, { "INSTRUCTION_ERROR", CSYNC_INSTRUCTION_ERROR }, + { "INSTRUCTION_TYPE_CHANGE", CSYNC_INSTRUCTION_TYPE_CHANGE }, { NULL, CSYNC_INSTRUCTION_ERROR } }; diff --git a/src/gui/folder.cpp b/src/gui/folder.cpp index 04830219d..75648c3bc 100644 --- a/src/gui/folder.cpp +++ b/src/gui/folder.cpp @@ -434,6 +434,7 @@ void Folder::bubbleUpSyncResult() if (!item->hasErrorStatus() && item->_direction == SyncFileItem::Down) { switch (item->_instruction) { case CSYNC_INSTRUCTION_NEW: + case CSYNC_INSTRUCTION_TYPE_CHANGE: newItems++; if (!firstItemNew) firstItemNew = item; diff --git a/src/gui/folderstatusmodel.cpp b/src/gui/folderstatusmodel.cpp index fc3fc81c6..d45800d0f 100644 --- a/src/gui/folderstatusmodel.cpp +++ b/src/gui/folderstatusmodel.cpp @@ -980,6 +980,7 @@ void FolderStatusModel::slotFolderSyncStateChange(Folder *f) if (state == SyncResult::Success) { foreach (const SyncFileItemPtr &i, f->syncResult().syncFileItemVector()) { if (i->_isDirectory && (i->_instruction == CSYNC_INSTRUCTION_NEW + || i->_instruction == CSYNC_INSTRUCTION_TYPE_CHANGE || i->_instruction == CSYNC_INSTRUCTION_REMOVE || i->_instruction == CSYNC_INSTRUCTION_RENAME)) { // There is a new or a removed folder. reset all data diff --git a/src/gui/syncrunfilelog.cpp b/src/gui/syncrunfilelog.cpp index 607ca5662..988d15dea 100644 --- a/src/gui/syncrunfilelog.cpp +++ b/src/gui/syncrunfilelog.cpp @@ -78,6 +78,9 @@ QString SyncRunFileLog::instructionToStr( csync_instructions_e inst ) case CSYNC_INSTRUCTION_ERROR: re = "INST_ERROR"; break; + case CSYNC_INSTRUCTION_TYPE_CHANGE: + re = "INST_TYPE_CHANGE"; + break; } return re; diff --git a/src/libsync/owncloudpropagator.cpp b/src/libsync/owncloudpropagator.cpp index 7e82e8c16..db54bea45 100644 --- a/src/libsync/owncloudpropagator.cpp +++ b/src/libsync/owncloudpropagator.cpp @@ -175,7 +175,8 @@ bool PropagateItemJob::checkForProblemsWithShared(int httpStatusCode, const QStr if( httpStatusCode == 403 && _propagator->isInSharedDirectory(_item->_file )) { if( !_item->_isDirectory ) { SyncFileItemPtr downloadItem(new SyncFileItem(*_item)); - if (downloadItem->_instruction == CSYNC_INSTRUCTION_NEW) { + if (downloadItem->_instruction == CSYNC_INSTRUCTION_NEW + || downloadItem->_instruction == CSYNC_INSTRUCTION_TYPE_CHANGE) { // don't try to recover pushing new files return false; } else if (downloadItem->_instruction == CSYNC_INSTRUCTION_SYNC) { @@ -234,32 +235,39 @@ void PropagateItemJob::slotRestoreJobCompleted(const SyncFileItem& item ) // ================================================================================ PropagateItemJob* OwncloudPropagator::createJob(const SyncFileItemPtr &item) { + bool deleteExisting = item->_instruction == CSYNC_INSTRUCTION_TYPE_CHANGE; switch(item->_instruction) { case CSYNC_INSTRUCTION_REMOVE: if (item->_direction == SyncFileItem::Down) return new PropagateLocalRemove(this, item); else return new PropagateRemoteDelete(this, item); case CSYNC_INSTRUCTION_NEW: + case CSYNC_INSTRUCTION_TYPE_CHANGE: if (item->_isDirectory) { - if (item->_direction == SyncFileItem::Down) return new PropagateLocalMkdir(this, item); - else return new PropagateRemoteMkdir(this, item); + if (item->_direction == SyncFileItem::Down) { + auto job = new PropagateLocalMkdir(this, item); + job->setDeleteExistingFile(deleteExisting); + return job; + } else { + auto job = new PropagateRemoteMkdir(this, item); + job->setDeleteExisting(deleteExisting); + return job; + } } //fall through case CSYNC_INSTRUCTION_SYNC: case CSYNC_INSTRUCTION_CONFLICT: if (item->_isDirectory) { - // Did a file turn into a directory? - if (QFileInfo(getFilePath(item->_file)).isFile()) { - auto job = new PropagateLocalMkdir(this, item); - job->setDeleteExistingFile(true); - return job; - } // Should we set the mtime? return 0; } { if (item->_direction != SyncFileItem::Up) { - return new PropagateDownloadFileQNAM(this, item); + auto job = new PropagateDownloadFileQNAM(this, item); + job->setDeleteExistingFolder(deleteExisting); + return job; } else { - return new PropagateUploadFileQNAM(this, item); + auto job = new PropagateUploadFileQNAM(this, item); + job->setDeleteExisting(deleteExisting); + return job; } } case CSYNC_INSTRUCTION_RENAME: @@ -320,7 +328,9 @@ void OwncloudPropagator::start(const SyncFileItemVector& items) delDirJob->increaseAffectedCount(); } continue; - } else if (item->_instruction == CSYNC_INSTRUCTION_NEW && item->_isDirectory) { + } else if (item->_isDirectory + && (item->_instruction == CSYNC_INSTRUCTION_NEW + || item->_instruction == CSYNC_INSTRUCTION_TYPE_CHANGE)) { // create a new directory within a deleted directory? That can happen if the directory // etag was not fetched properly on the previous sync because the sync was aborted // while uploading this directory (which is now removed). We can ignore it. @@ -345,6 +355,22 @@ void OwncloudPropagator::start(const SyncFileItemVector& items) if (item->_isDirectory) { PropagateDirectory *dir = new PropagateDirectory(this, item); dir->_firstJob.reset(createJob(item)); + + if (item->_instruction == CSYNC_INSTRUCTION_TYPE_CHANGE + && item->_direction == SyncFileItem::Up) { + // Skip all potential uploads to the new folder. + // Processing them now leads to problems with permissions: + // checkForPermissions() has already run and used the permissions + // of the file we're about to delete to decide whether uploading + // to the new dir is ok... + foreach(const SyncFileItemPtr &item2, items) { + if (item2->destination().startsWith(item->destination() + "/")) { + item2->_instruction = CSYNC_INSTRUCTION_NONE; + _anotherSyncNeeded = true; + } + } + } + if (item->_instruction == CSYNC_INSTRUCTION_REMOVE) { // We do the removal of directories at the end, because there might be moves from // these directories that will happen later. @@ -364,23 +390,10 @@ void OwncloudPropagator::start(const SyncFileItemVector& items) } directories.push(qMakePair(item->destination() + "/" , dir)); } else if (PropagateItemJob* current = createJob(item)) { - // If the target of a job is currently a directory, we need to remove it! - // This can happen when what used to be a directory changed to a file on the - // server an a PropagateLocalRename or PropageDownload job wants to run. - if (item->_direction == SyncFileItem::Down - && QFileInfo(getFilePath(item->_file)).isDir()) { - // The DirectoryConflict job *must* run before the file propagation job - // and we also need to make sure other jobs that deal with the files - // in the directory (like removes or moves, in particular of other - // directories!) run first. - // Making it a directory job ensures that moves run first and that the - // (potential) directory rename happens before the file propagation. - // Prepending all jobs to directoriesToRemove ensures that removals of - // subdirectories happen before the directory is renamed. - PropagateDirectory *dir = new PropagateDirectory(this, item); - dir->_firstJob.reset(new PropagateLocalDirectoryConflict(this, item)); - dir->append(current); - directoriesToRemove.prepend(dir); + if (item->_instruction == CSYNC_INSTRUCTION_TYPE_CHANGE) { + // will delete directories, so defer execution + directoriesToRemove.prepend(current); + removedDirectory = item->_file + "/"; } else { directories.top().second->append(current); } diff --git a/src/libsync/progressdispatcher.cpp b/src/libsync/progressdispatcher.cpp index 4caf81859..8f0322887 100644 --- a/src/libsync/progressdispatcher.cpp +++ b/src/libsync/progressdispatcher.cpp @@ -27,6 +27,7 @@ QString Progress::asResultString( const SyncFileItem& item) switch(item._instruction) { case CSYNC_INSTRUCTION_SYNC: case CSYNC_INSTRUCTION_NEW: + case CSYNC_INSTRUCTION_TYPE_CHANGE: if (item._direction != SyncFileItem::Up) { return QCoreApplication::translate( "progress", "Downloaded"); } else { @@ -59,6 +60,7 @@ QString Progress::asActionString( const SyncFileItem &item ) case CSYNC_INSTRUCTION_CONFLICT: case CSYNC_INSTRUCTION_SYNC: case CSYNC_INSTRUCTION_NEW: + case CSYNC_INSTRUCTION_TYPE_CHANGE: if (item._direction != SyncFileItem::Up) return QCoreApplication::translate( "progress", "downloading"); else diff --git a/src/libsync/progressdispatcher.h b/src/libsync/progressdispatcher.h index 0efcee71f..a89e0fd3a 100644 --- a/src/libsync/progressdispatcher.h +++ b/src/libsync/progressdispatcher.h @@ -79,7 +79,8 @@ public: return ! item._isDirectory && ( item._instruction == CSYNC_INSTRUCTION_CONFLICT || item._instruction == CSYNC_INSTRUCTION_SYNC - || item._instruction == CSYNC_INSTRUCTION_NEW); + || item._instruction == CSYNC_INSTRUCTION_NEW + || item._instruction == CSYNC_INSTRUCTION_TYPE_CHANGE); } /** diff --git a/src/libsync/propagatedownload.cpp b/src/libsync/propagatedownload.cpp index f19dce997..2b6e0140e 100644 --- a/src/libsync/propagatedownload.cpp +++ b/src/libsync/propagatedownload.cpp @@ -310,6 +310,15 @@ void PropagateDownloadFileQNAM::start() qDebug() << Q_FUNC_INFO << _item->_file << _propagator->_activeJobs; + if (_deleteExisting) { + deleteExistingFolder(); + + // check for error with deletion + if (_state == Finished) { + return; + } + } + // do a klaas' case clash check. if( _propagator->localFileNameClash(_item->_file) ) { done( SyncFileItem::NormalError, tr("File %1 can not be downloaded because of a local file name clash!") @@ -415,6 +424,11 @@ qint64 PropagateDownloadFileQNAM::committedDiskSpace() const return 0; } +void PropagateDownloadFileQNAM::setDeleteExistingFolder(bool enabled) +{ + _deleteExisting = enabled; +} + const char owncloudCustomSoftErrorStringC[] = "owncloud-custom-soft-error-string"; void PropagateDownloadFileQNAM::slotGetFinished() { @@ -551,6 +565,33 @@ void PropagateDownloadFileQNAM::slotChecksumFail( const QString& errMsg ) done(SyncFileItem::SoftError, errMsg ); // tr("The file downloaded with a broken checksum, will be redownloaded.")); } +void PropagateDownloadFileQNAM::deleteExistingFolder() +{ + QString existingDir = _propagator->getFilePath(_item->_file); + if (!QFileInfo(existingDir).isDir()) { + return; + } + + // Delete the directory if it is empty! + QDir dir(existingDir); + if (dir.entryList(QDir::NoDotAndDotDot|QDir::AllEntries).count() == 0) { + if (dir.rmdir(existingDir)) { + return; + } + // on error, just try to move it away... + } + + QString conflictDir = FileSystem::makeConflictFileName( + existingDir, Utility::qDateTimeFromTime_t(_item->_modtime)); + + _propagator->addTouchedFile(existingDir); + _propagator->addTouchedFile(conflictDir); + QString renameError; + if (!FileSystem::rename(existingDir, conflictDir, &renameError)) { + done(SyncFileItem::NormalError, renameError); + } +} + namespace { // Anonymous namespace for the recall feature static QString makeRecallFileName(const QString &fn) { diff --git a/src/libsync/propagatedownload.h b/src/libsync/propagatedownload.h index 32e05db28..8ba11f1b5 100644 --- a/src/libsync/propagatedownload.h +++ b/src/libsync/propagatedownload.h @@ -110,10 +110,21 @@ class PropagateDownloadFileQNAM : public PropagateItemJob { Q_OBJECT public: PropagateDownloadFileQNAM(OwncloudPropagator* propagator,const SyncFileItemPtr& item) - : PropagateItemJob(propagator, item), _resumeStart(0), _downloadProgress(0) {} + : PropagateItemJob(propagator, item), _resumeStart(0), _downloadProgress(0), _deleteExisting(false) {} void start() Q_DECL_OVERRIDE; qint64 committedDiskSpace() const Q_DECL_OVERRIDE; + /** + * Whether an existing folder with the same name may be deleted before + * the download. + * + * If it's a non-empty folder, it'll be renamed to a conflict-style name + * to preserve any non-synced content that may be inside. + * + * Default: false. + */ + void setDeleteExistingFolder(bool enabled); + private slots: void slotGetFinished(); void abort() Q_DECL_OVERRIDE; @@ -122,10 +133,13 @@ private slots: void slotChecksumFail( const QString& errMsg ); private: + void deleteExistingFolder(); + quint64 _resumeStart; qint64 _downloadProgress; QPointer _job; QFile _tmpFile; + bool _deleteExisting; }; } diff --git a/src/libsync/propagateremotemkdir.cpp b/src/libsync/propagateremotemkdir.cpp index 8498491af..0c91bb255 100644 --- a/src/libsync/propagateremotemkdir.cpp +++ b/src/libsync/propagateremotemkdir.cpp @@ -16,6 +16,7 @@ #include "owncloudpropagator_p.h" #include "account.h" #include "syncjournalfilerecord.h" +#include "propagateremotedelete.h" #include namespace OCC { @@ -27,11 +28,30 @@ void PropagateRemoteMkdir::start() qDebug() << Q_FUNC_INFO << _item->_file; + _propagator->_activeJobs++; + + if (!_deleteExisting) { + return slotStartMkcolJob(); + } + + _job = new DeleteJob(_propagator->account(), + _propagator->_remoteFolder + _item->_file, + this); + connect(_job, SIGNAL(finishedSignal()), SLOT(slotStartMkcolJob())); + _job->start(); +} + +void PropagateRemoteMkdir::slotStartMkcolJob() +{ + if (_propagator->_abortRequested.fetchAndAddRelaxed(0)) + return; + + qDebug() << Q_FUNC_INFO << _item->_file; + _job = new MkColJob(_propagator->account(), _propagator->_remoteFolder + _item->_file, this); connect(_job, SIGNAL(finished(QNetworkReply::NetworkError)), this, SLOT(slotMkcolJobFinished())); - _propagator->_activeJobs++; _job->start(); } @@ -41,6 +61,11 @@ void PropagateRemoteMkdir::abort() _job->reply()->abort(); } +void PropagateRemoteMkdir::setDeleteExisting(bool enabled) +{ + _deleteExisting = enabled; +} + void PropagateRemoteMkdir::slotMkcolJobFinished() { _propagator->_activeJobs--; diff --git a/src/libsync/propagateremotemkdir.h b/src/libsync/propagateremotemkdir.h index 9dcbb73c8..4df6bb506 100644 --- a/src/libsync/propagateremotemkdir.h +++ b/src/libsync/propagateremotemkdir.h @@ -25,13 +25,24 @@ namespace OCC { class PropagateRemoteMkdir : public PropagateItemJob { Q_OBJECT QPointer _job; + bool _deleteExisting; friend class PropagateDirectory; // So it can access the _item; public: PropagateRemoteMkdir (OwncloudPropagator* propagator,const SyncFileItemPtr& item) - : PropagateItemJob(propagator, item) {} + : PropagateItemJob(propagator, item), _deleteExisting(false) {} void start() Q_DECL_OVERRIDE; void abort() Q_DECL_OVERRIDE; + + /** + * Whether an existing entity with the same name may be deleted before + * creating the directory. + * + * Default: false. + */ + void setDeleteExisting(bool enabled); + private slots: + void slotStartMkcolJob(); void slotMkcolJobFinished(); void propfindResult(const QVariantMap &); void propfindError(); diff --git a/src/libsync/propagateupload.cpp b/src/libsync/propagateupload.cpp index 2d5e40eca..e468f8791 100644 --- a/src/libsync/propagateupload.cpp +++ b/src/libsync/propagateupload.cpp @@ -24,6 +24,7 @@ #include "propagatorjobs.h" #include "checksums.h" #include "syncengine.h" +#include "propagateremotedelete.h" #include #include @@ -208,6 +209,29 @@ void PropagateUploadFileQNAM::start() } } + _propagator->_activeJobs++; + + if (!_deleteExisting) { + return slotComputeContentChecksum(); + } + + auto job = new DeleteJob(_propagator->account(), + _propagator->_remoteFolder + _item->_file, + this); + _jobs.append(job); + connect(job, SIGNAL(finishedSignal()), SLOT(slotComputeContentChecksum())); + connect(job, SIGNAL(destroyed(QObject*)), SLOT(slotJobDestroyed(QObject*))); + job->start(); +} + +void PropagateUploadFileQNAM::slotComputeContentChecksum() +{ + if (_propagator->_abortRequested.fetchAndAddRelaxed(0)) { + return; + } + + _propagator->_activeJobs--; // from start + const QString filePath = _propagator->getFilePath(_item->_file); // remember the modtime before checksumming to be able to detect a file @@ -239,6 +263,11 @@ void PropagateUploadFileQNAM::start() computeChecksum->start(filePath); } +void PropagateUploadFileQNAM::setDeleteExisting(bool enabled) +{ + _deleteExisting = enabled; +} + void PropagateUploadFileQNAM::slotComputeTransmissionChecksum(const QByteArray& contentChecksumType, const QByteArray& contentChecksum) { _item->_contentChecksum = contentChecksum; @@ -491,8 +520,10 @@ void PropagateUploadFileQNAM::startNextChunk() headers["OC-Tag"] = ".sys.admin#recall#"; } - if (!_item->_etag.isEmpty() && _item->_etag != "empty_etag" && - _item->_instruction != CSYNC_INSTRUCTION_NEW // On new files never send a If-Match + if (!_item->_etag.isEmpty() && _item->_etag != "empty_etag" + && _item->_instruction != CSYNC_INSTRUCTION_NEW // On new files never send a If-Match + && _item->_instruction != CSYNC_INSTRUCTION_TYPE_CHANGE + && !_deleteExisting ) { // We add quotes because the owncloud server always adds quotes around the etag, and // csync_owncloud.c's owncloud_file_id always strips the quotes. @@ -710,7 +741,9 @@ void PropagateUploadFileQNAM::slotPutFinished() auto currentChunk = job->_chunk; foreach (auto *job, _jobs) { // Take the minimum finished one - currentChunk = qMin(currentChunk, job->_chunk - 1); + if (auto putJob = qobject_cast(job)) { + currentChunk = qMin(currentChunk, putJob->_chunk - 1); + } } pi._chunk = (currentChunk + _startChunk + 1) % _chunkCount ; // next chunk to start with pi._transferid = _transferId; diff --git a/src/libsync/propagateupload.h b/src/libsync/propagateupload.h index 1b4bc74e6..e43835724 100644 --- a/src/libsync/propagateupload.h +++ b/src/libsync/propagateupload.h @@ -177,7 +177,7 @@ private: int _chunkCount; /// Total number of chunks for this file int _transferId; /// transfer id (part of the url) QElapsedTimer _duration; - QVector _jobs; /// network jobs that are currently in transit + QVector _jobs; /// network jobs that are currently in transit bool _finished; // Tells that all the jobs have been finished // measure the performance of checksum calc and upload @@ -186,10 +186,21 @@ private: QByteArray _transmissionChecksum; QByteArray _transmissionChecksumType; + bool _deleteExisting; + public: PropagateUploadFileQNAM(OwncloudPropagator* propagator,const SyncFileItemPtr& item) - : PropagateItemJob(propagator, item), _startChunk(0), _currentChunk(0), _chunkCount(0), _transferId(0), _finished(false) {} + : PropagateItemJob(propagator, item), _startChunk(0), _currentChunk(0), _chunkCount(0), _transferId(0), _finished(false), _deleteExisting(false) {} void start() Q_DECL_OVERRIDE; + + /** + * Whether an existing entity with the same name may be deleted before + * the upload. + * + * Default: false. + */ + void setDeleteExisting(bool enabled); + private slots: void slotPutFinished(); void slotPollFinished(); @@ -200,6 +211,7 @@ private slots: void slotJobDestroyed(QObject *job); void slotStartUpload(const QByteArray& transmissionChecksumType, const QByteArray& transmissionChecksum); void slotComputeTransmissionChecksum(const QByteArray& contentChecksumType, const QByteArray& contentChecksum); + void slotComputeContentChecksum(); private: void startPollJob(const QString& path); diff --git a/src/libsync/propagatorjobs.cpp b/src/libsync/propagatorjobs.cpp index 1e431bda2..a133b6703 100644 --- a/src/libsync/propagatorjobs.cpp +++ b/src/libsync/propagatorjobs.cpp @@ -244,39 +244,4 @@ void PropagateLocalRename::start() done(SyncFileItem::Success); } -void PropagateLocalDirectoryConflict::start() -{ - if (_propagator->_abortRequested.fetchAndAddRelaxed(0)) - return; - - QString existingDir = _propagator->getFilePath(_item->_file); - if (!QFileInfo(existingDir).isDir()) { - done(SyncFileItem::Success); - return; - } - - // Delete the directory if it is empty! - QDir dir(existingDir); - if (dir.entryList(QDir::NoDotAndDotDot|QDir::AllEntries).count() == 0) { - if (dir.rmdir(existingDir)) { - done(SyncFileItem::Success); - return; - } - // on error, just try to move it away... - } - - QString conflictDir = FileSystem::makeConflictFileName( - existingDir, Utility::qDateTimeFromTime_t(_item->_modtime)); - - _propagator->addTouchedFile(existingDir); - _propagator->addTouchedFile(conflictDir); - QString renameError; - if (!FileSystem::rename(existingDir, conflictDir, &renameError)) { - done(SyncFileItem::NormalError, renameError); - return; - } - - done(SyncFileItem::Success); -} - } diff --git a/src/libsync/propagatorjobs.h b/src/libsync/propagatorjobs.h index 5a81d305f..c3898db48 100644 --- a/src/libsync/propagatorjobs.h +++ b/src/libsync/propagatorjobs.h @@ -82,24 +82,4 @@ public: JobParallelism parallelism() Q_DECL_OVERRIDE { return WaitForFinishedInParentDirectory; } }; -/** - * Moves away a local directory when it should become a file. - * - * Example: Locally there's directory foo/ with three files in it, - * one of them ignored, while the server has a file foo. - * In this case, foo/ fill be moved to foo-conflict.../ and the - * file will be downloaded to foo. - * - * If the directory is empty, it will be removed instead. - * - * @ingroup libsync - */ -class PropagateLocalDirectoryConflict : public PropagateItemJob { - Q_OBJECT -public: - PropagateLocalDirectoryConflict (OwncloudPropagator* propagator, const SyncFileItemPtr& item) : PropagateItemJob(propagator, item) {} - void start() Q_DECL_OVERRIDE; - JobParallelism parallelism() Q_DECL_OVERRIDE { return WaitForFinishedInParentDirectory; } -}; - } diff --git a/src/libsync/syncengine.cpp b/src/libsync/syncengine.cpp index 38aff9fc9..6be3e93b3 100644 --- a/src/libsync/syncengine.cpp +++ b/src/libsync/syncengine.cpp @@ -444,7 +444,7 @@ int SyncEngine::treewalkFile( TREE_WALK_FILE *file, bool remote ) item->_errorString = tr("Filename encoding is not valid"); } - item->_isDirectory = file->type == CSYNC_FTW_TYPE_DIR; + bool isDirectory = file->type == CSYNC_FTW_TYPE_DIR; if (file->etag && file->etag[0]) { item->_etag = file->etag; @@ -474,7 +474,7 @@ int SyncEngine::treewalkFile( TREE_WALK_FILE *file, bool remote ) int re = 0; switch(file->instruction) { case CSYNC_INSTRUCTION_NONE: { - if (remote && item->_should_update_metadata && !item->_isDirectory && item->_instruction == CSYNC_INSTRUCTION_NONE) { + if (remote && item->_should_update_metadata && !isDirectory && item->_instruction == CSYNC_INSTRUCTION_NONE) { // Update the database now already: New remote fileid or Etag or RemotePerm // Or for files that were detected as "resolved conflict". // Or a local inode/mtime change (see localMetadataUpdate below) @@ -509,17 +509,18 @@ int SyncEngine::treewalkFile( TREE_WALK_FILE *file, bool remote ) _syncItemMap.remove(key); } // Any files that are instruction NONE? - if (!item->_isDirectory && file->other.instruction == CSYNC_INSTRUCTION_NONE) { + if (!isDirectory && file->other.instruction == CSYNC_INSTRUCTION_NONE) { _hasNoneFiles = true; } // We want to still update etags of directories, other NONE // items can be ignored. - bool directoryEtagUpdate = item->_isDirectory && file->should_update_metadata; + bool directoryEtagUpdate = isDirectory && file->should_update_metadata; bool localMetadataUpdate = !remote && file->should_update_metadata; if (!directoryEtagUpdate) { if (localMetadataUpdate) { // Hack, we want a local metadata update to happen, but only if the // remote tree doesn't ask us to do some kind of propagation. + item->_isDirectory = isDirectory; _syncItemMap.insert(key, item); } return re; @@ -529,7 +530,7 @@ int SyncEngine::treewalkFile( TREE_WALK_FILE *file, bool remote ) case CSYNC_INSTRUCTION_RENAME: dir = !remote ? SyncFileItem::Down : SyncFileItem::Up; item->_renameTarget = renameTarget; - if (item->_isDirectory) + if (isDirectory) _renamedFolders.insert(item->_file, item->_renameTarget); break; case CSYNC_INSTRUCTION_REMOVE: @@ -543,6 +544,7 @@ int SyncEngine::treewalkFile( TREE_WALK_FILE *file, bool remote ) break; case CSYNC_INSTRUCTION_EVAL: case CSYNC_INSTRUCTION_NEW: + case CSYNC_INSTRUCTION_TYPE_CHANGE: case CSYNC_INSTRUCTION_SYNC: case CSYNC_INSTRUCTION_STAT_ERROR: default: @@ -556,6 +558,7 @@ int SyncEngine::treewalkFile( TREE_WALK_FILE *file, bool remote ) } item->_direction = dir; + item->_isDirectory = isDirectory; if (instruction != CSYNC_INSTRUCTION_NONE) { // check for blacklisting of this item. // if the item is on blacklist, the instruction was set to ERROR @@ -1010,6 +1013,7 @@ void SyncEngine::checkForPermission() } switch((*it)->_instruction) { + case CSYNC_INSTRUCTION_TYPE_CHANGE: case CSYNC_INSTRUCTION_NEW: { int slashPos = (*it)->_file.lastIndexOf('/'); QString parentDir = slashPos <= 0 ? "" : (*it)->_file.mid(0, slashPos);