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.
This commit is contained in:
Christian Kamm 2016-01-06 10:01:22 +01:00
parent d4b6b5cb1d
commit abf5a5ad1e
19 changed files with 221 additions and 100 deletions

View file

@ -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 {

View file

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

View file

@ -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;
}

View file

@ -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 }
};

View file

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

View file

@ -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

View file

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

View file

@ -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);
}

View file

@ -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

View file

@ -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);
}
/**

View file

@ -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)
{

View file

@ -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<GETFileJob> _job;
QFile _tmpFile;
bool _deleteExisting;
};
}

View file

@ -16,6 +16,7 @@
#include "owncloudpropagator_p.h"
#include "account.h"
#include "syncjournalfilerecord.h"
#include "propagateremotedelete.h"
#include <QFile>
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--;

View file

@ -25,13 +25,24 @@ namespace OCC {
class PropagateRemoteMkdir : public PropagateItemJob {
Q_OBJECT
QPointer<AbstractNetworkJob> _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();

View file

@ -24,6 +24,7 @@
#include "propagatorjobs.h"
#include "checksums.h"
#include "syncengine.h"
#include "propagateremotedelete.h"
#include <json.h>
#include <QNetworkAccessManager>
@ -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<PUTFileJob*>(job)) {
currentChunk = qMin(currentChunk, putJob->_chunk - 1);
}
}
pi._chunk = (currentChunk + _startChunk + 1) % _chunkCount ; // next chunk to start with
pi._transferid = _transferId;

View file

@ -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<PUTFileJob*> _jobs; /// network jobs that are currently in transit
QVector<AbstractNetworkJob*> _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);

View file

@ -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);
}
}

View file

@ -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; }
};
}

View file

@ -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);