mirror of
https://github.com/nextcloud/desktop.git
synced 2024-10-24 05:15:40 +03:00
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:
parent
d4b6b5cb1d
commit
abf5a5ad1e
19 changed files with 221 additions and 100 deletions
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 }
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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--;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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; }
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue