2013-05-03 21:11:00 +04:00
|
|
|
/*
|
|
|
|
* Copyright (C) by Olivier Goffart <ogoffart@owncloud.com>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
|
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
|
|
* for more details.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef OWNCLOUDPROPAGATOR_H
|
|
|
|
#define OWNCLOUDPROPAGATOR_H
|
|
|
|
|
2013-05-15 17:22:20 +04:00
|
|
|
#include <QHash>
|
2013-05-16 15:54:22 +04:00
|
|
|
#include <QObject>
|
2014-09-29 12:30:39 +04:00
|
|
|
#include <QMap>
|
|
|
|
#include <QElapsedTimer>
|
|
|
|
#include <QTimer>
|
|
|
|
#include <QPointer>
|
|
|
|
#include <QIODevice>
|
2014-11-07 13:41:21 +03:00
|
|
|
#include <QMutex>
|
2013-05-05 13:41:31 +04:00
|
|
|
|
2017-08-14 17:19:52 +03:00
|
|
|
#include "csync_util.h"
|
2013-05-03 21:11:00 +04:00
|
|
|
#include "syncfileitem.h"
|
2017-09-01 19:11:43 +03:00
|
|
|
#include "common/syncjournaldb.h"
|
2014-09-29 12:30:39 +04:00
|
|
|
#include "bandwidthmanager.h"
|
2014-12-18 14:09:48 +03:00
|
|
|
#include "accountfwd.h"
|
2017-12-05 18:54:02 +03:00
|
|
|
#include "syncoptions.h"
|
2013-05-03 21:11:00 +04:00
|
|
|
|
2014-11-10 00:34:07 +03:00
|
|
|
namespace OCC {
|
2013-05-03 21:11:00 +04:00
|
|
|
|
2017-05-09 15:24:11 +03:00
|
|
|
Q_DECLARE_LOGGING_CATEGORY(lcPropagator)
|
|
|
|
|
2015-10-01 12:39:09 +03:00
|
|
|
/** Free disk space threshold below which syncs will abort and not even start.
|
|
|
|
*/
|
|
|
|
qint64 criticalFreeSpaceLimit();
|
|
|
|
|
|
|
|
/** The client will not intentionally reduce the available free disk space below
|
|
|
|
* this limit.
|
|
|
|
*
|
|
|
|
* Uploads will still run and downloads that are small enough will continue too.
|
|
|
|
*/
|
|
|
|
qint64 freeSpaceLimit();
|
|
|
|
|
2013-10-16 13:59:54 +04:00
|
|
|
class SyncJournalDb;
|
2013-10-28 13:47:10 +04:00
|
|
|
class OwncloudPropagator;
|
2017-12-02 13:40:43 +03:00
|
|
|
class PropagatorCompositeJob;
|
2013-05-06 18:59:11 +04:00
|
|
|
|
2015-06-29 19:56:09 +03:00
|
|
|
/**
|
|
|
|
* @brief the base class of propagator jobs
|
2014-11-18 19:54:53 +03:00
|
|
|
*
|
|
|
|
* This can either be a job, or a container for jobs.
|
2015-10-05 06:20:09 +03:00
|
|
|
* If it is a composite job, it then inherits from PropagateDirectory
|
2014-11-18 19:54:53 +03:00
|
|
|
*
|
2015-06-29 19:56:09 +03:00
|
|
|
* @ingroup libsync
|
2014-11-18 19:54:53 +03:00
|
|
|
*/
|
2017-05-17 11:55:42 +03:00
|
|
|
class PropagatorJob : public QObject
|
|
|
|
{
|
2013-05-16 15:54:22 +04:00
|
|
|
Q_OBJECT
|
2014-11-18 19:35:31 +03:00
|
|
|
|
2013-10-28 13:47:10 +04:00
|
|
|
public:
|
2017-05-17 11:55:42 +03:00
|
|
|
explicit PropagatorJob(OwncloudPropagator *propagator);
|
2014-11-18 19:54:53 +03:00
|
|
|
|
2017-08-11 01:03:03 +03:00
|
|
|
enum AbortType {
|
|
|
|
Synchronous,
|
|
|
|
Asynchronous
|
|
|
|
};
|
|
|
|
|
2014-11-18 19:35:31 +03:00
|
|
|
enum JobState {
|
|
|
|
NotYetStarted,
|
|
|
|
Running,
|
|
|
|
Finished
|
|
|
|
};
|
2014-11-18 19:54:53 +03:00
|
|
|
JobState _state;
|
2014-11-18 19:35:31 +03:00
|
|
|
|
|
|
|
enum JobParallelism {
|
2014-11-18 19:54:53 +03:00
|
|
|
|
|
|
|
/** Jobs can be run in parallel to this job */
|
2014-11-18 19:35:31 +03:00
|
|
|
FullParallelism,
|
2015-12-22 15:02:02 +03:00
|
|
|
|
|
|
|
/** No other job shall be started until this one has finished.
|
|
|
|
So this job is guaranteed to finish before any jobs below it
|
|
|
|
are executed. */
|
2014-11-18 19:35:31 +03:00
|
|
|
WaitForFinished,
|
|
|
|
};
|
|
|
|
|
2014-11-18 19:54:53 +03:00
|
|
|
virtual JobParallelism parallelism() { return FullParallelism; }
|
2014-01-07 18:42:21 +04:00
|
|
|
|
2016-02-25 19:40:24 +03:00
|
|
|
/**
|
|
|
|
* For "small" jobs
|
|
|
|
*/
|
|
|
|
virtual bool isLikelyFinishedQuickly() { return false; }
|
|
|
|
|
2015-10-01 12:39:09 +03:00
|
|
|
/** The space that the running jobs need to complete but don't actually use yet.
|
|
|
|
*
|
|
|
|
* Note that this does *not* include the disk space that's already
|
|
|
|
* in use by running jobs for things like a download-in-progress.
|
|
|
|
*/
|
|
|
|
virtual qint64 committedDiskSpace() const { return 0; }
|
|
|
|
|
2018-01-17 12:59:47 +03:00
|
|
|
/** Set the associated composite job
|
2017-12-02 13:40:43 +03:00
|
|
|
*
|
2018-01-17 12:59:47 +03:00
|
|
|
* Used only from PropagatorCompositeJob itself, when a job is added
|
|
|
|
* and from PropagateDirectory to associate the subJobs with the first
|
|
|
|
* job.
|
2017-12-02 13:40:43 +03:00
|
|
|
*/
|
2018-01-17 12:59:47 +03:00
|
|
|
void setAssociatedComposite(PropagatorCompositeJob *job) { _associatedComposite = job; }
|
2017-12-02 13:40:43 +03:00
|
|
|
|
2013-10-28 13:47:10 +04:00
|
|
|
public slots:
|
2017-08-11 01:03:03 +03:00
|
|
|
/*
|
|
|
|
* Asynchronous abort requires emit of abortFinished() signal,
|
|
|
|
* while synchronous is expected to abort immedietaly.
|
|
|
|
*/
|
|
|
|
virtual void abort(PropagatorJob::AbortType abortType) {
|
|
|
|
if (abortType == AbortType::Asynchronous)
|
|
|
|
emit abortFinished();
|
|
|
|
}
|
2014-11-18 19:54:53 +03:00
|
|
|
|
|
|
|
/** Starts this job, or a new subjob
|
|
|
|
* returns true if a job was started.
|
|
|
|
*/
|
2017-02-14 14:46:44 +03:00
|
|
|
virtual bool scheduleSelfOrChild() = 0;
|
2013-10-28 13:47:10 +04:00
|
|
|
signals:
|
2014-02-12 14:07:34 +04:00
|
|
|
/**
|
|
|
|
* Emitted when the job is fully finished
|
|
|
|
*/
|
2013-10-28 13:47:10 +04:00
|
|
|
void finished(SyncFileItem::Status);
|
2014-02-12 14:07:34 +04:00
|
|
|
|
2017-08-11 01:03:03 +03:00
|
|
|
/**
|
|
|
|
* Emitted when the abort is fully finished
|
|
|
|
*/
|
|
|
|
void abortFinished(SyncFileItem::Status status = SyncFileItem::NormalError);
|
2017-01-17 16:29:12 +03:00
|
|
|
protected:
|
|
|
|
OwncloudPropagator *propagator() const;
|
2017-12-02 13:40:43 +03:00
|
|
|
|
|
|
|
/** If this job gets added to a composite job, this will point to the parent.
|
2018-01-17 12:59:47 +03:00
|
|
|
*
|
|
|
|
* For the PropagateDirectory::_firstJob it will point to
|
|
|
|
* PropagateDirectory::_subJobs.
|
2017-12-02 13:40:43 +03:00
|
|
|
*
|
|
|
|
* That can be useful for jobs that want to spawn follow-up jobs without
|
|
|
|
* becoming composite jobs themselves.
|
|
|
|
*/
|
2018-01-17 12:59:47 +03:00
|
|
|
PropagatorCompositeJob *_associatedComposite = nullptr;
|
2013-10-28 13:47:10 +04:00
|
|
|
};
|
2013-05-16 15:54:22 +04:00
|
|
|
|
2014-12-06 14:27:50 +03:00
|
|
|
/*
|
|
|
|
* Abstract class to propagate a single item
|
|
|
|
*/
|
2017-05-17 11:55:42 +03:00
|
|
|
class PropagateItemJob : public PropagatorJob
|
|
|
|
{
|
2014-12-06 14:27:50 +03:00
|
|
|
Q_OBJECT
|
|
|
|
protected:
|
2018-03-27 11:39:58 +03:00
|
|
|
virtual void done(SyncFileItem::Status status, const QString &errorString = QString());
|
2014-12-06 14:27:50 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* set a custom restore job message that is used if the restore job succeeded.
|
|
|
|
* It is displayed in the activity view.
|
|
|
|
*/
|
2017-05-17 11:55:42 +03:00
|
|
|
QString restoreJobMsg() const
|
|
|
|
{
|
2015-04-15 16:19:11 +03:00
|
|
|
return _item->_isRestoration ? _item->_errorString : QString();
|
2014-12-06 14:27:50 +03:00
|
|
|
}
|
2017-05-17 11:55:42 +03:00
|
|
|
void setRestoreJobMsg(const QString &msg = QString())
|
|
|
|
{
|
2015-04-15 16:19:11 +03:00
|
|
|
_item->_isRestoration = true;
|
|
|
|
_item->_errorString = msg;
|
2014-12-06 14:27:50 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
protected slots:
|
2017-02-14 14:46:44 +03:00
|
|
|
void slotRestoreJobFinished(SyncFileItem::Status status);
|
2014-12-06 14:27:50 +03:00
|
|
|
|
|
|
|
private:
|
|
|
|
QScopedPointer<PropagateItemJob> _restoreJob;
|
|
|
|
|
|
|
|
public:
|
2017-05-17 11:55:42 +03:00
|
|
|
PropagateItemJob(OwncloudPropagator *propagator, const SyncFileItemPtr &item)
|
|
|
|
: PropagatorJob(propagator)
|
|
|
|
, _item(item)
|
|
|
|
{
|
|
|
|
}
|
2017-03-09 11:41:36 +03:00
|
|
|
~PropagateItemJob();
|
2014-12-06 14:27:50 +03:00
|
|
|
|
2018-11-12 20:39:50 +03:00
|
|
|
bool scheduleSelfOrChild() override
|
2017-05-17 11:55:42 +03:00
|
|
|
{
|
2014-12-10 15:01:36 +03:00
|
|
|
if (_state != NotYetStarted) {
|
|
|
|
return false;
|
|
|
|
}
|
2017-05-17 11:55:42 +03:00
|
|
|
const char *instruction_str = csync_instruction_str(_item->_instruction);
|
2017-03-30 14:46:20 +03:00
|
|
|
qCInfo(lcPropagator) << "Starting" << instruction_str << "propagation of" << _item->_file << "by" << this;
|
|
|
|
|
2014-12-10 15:01:36 +03:00
|
|
|
_state = Running;
|
|
|
|
QMetaObject::invokeMethod(this, "start"); // We could be in a different thread (neon jobs)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-05-17 11:55:42 +03:00
|
|
|
SyncFileItemPtr _item;
|
2014-12-10 15:01:36 +03:00
|
|
|
|
|
|
|
public slots:
|
|
|
|
virtual void start() = 0;
|
2014-12-06 14:27:50 +03:00
|
|
|
};
|
|
|
|
|
2015-06-29 19:56:09 +03:00
|
|
|
/**
|
2017-02-13 19:21:55 +03:00
|
|
|
* @brief Job that runs subjobs. It becomes finished only when all subjobs are finished.
|
2015-06-29 19:56:09 +03:00
|
|
|
* @ingroup libsync
|
2013-10-28 13:47:10 +04:00
|
|
|
*/
|
2017-05-17 11:55:42 +03:00
|
|
|
class PropagatorCompositeJob : public PropagatorJob
|
|
|
|
{
|
2013-10-28 13:47:10 +04:00
|
|
|
Q_OBJECT
|
|
|
|
public:
|
2017-02-13 20:24:34 +03:00
|
|
|
QVector<PropagatorJob *> _jobsToDo;
|
2017-02-14 01:16:20 +03:00
|
|
|
SyncFileItemVector _tasksToDo;
|
2017-02-13 20:24:34 +03:00
|
|
|
QVector<PropagatorJob *> _runningJobs;
|
2017-05-17 11:55:42 +03:00
|
|
|
SyncFileItem::Status _hasError; // NoStatus, or NormalError / SoftError if there was an error
|
2017-08-11 01:03:03 +03:00
|
|
|
quint64 _abortsCount;
|
2013-05-03 21:11:00 +04:00
|
|
|
|
2017-02-13 19:21:55 +03:00
|
|
|
explicit PropagatorCompositeJob(OwncloudPropagator *propagator)
|
2013-10-28 13:47:10 +04:00
|
|
|
: PropagatorJob(propagator)
|
2017-08-11 01:03:03 +03:00
|
|
|
, _hasError(SyncFileItem::NoStatus), _abortsCount(0)
|
2017-05-17 11:55:42 +03:00
|
|
|
{
|
|
|
|
}
|
2013-10-28 13:47:10 +04:00
|
|
|
|
2020-08-12 18:19:49 +03:00
|
|
|
// Don't delete jobs in _jobsToDo and _runningJobs: they have parents
|
|
|
|
// that will be responsible for cleanup. Deleting them here would risk
|
|
|
|
// deleting something that has already been deleted by a shared parent.
|
|
|
|
virtual ~PropagatorCompositeJob() = default;
|
2013-10-28 13:47:10 +04:00
|
|
|
|
2017-12-02 13:40:43 +03:00
|
|
|
void appendJob(PropagatorJob *job);
|
2017-05-17 11:55:42 +03:00
|
|
|
void appendTask(const SyncFileItemPtr &item)
|
|
|
|
{
|
2017-02-14 01:16:20 +03:00
|
|
|
_tasksToDo.append(item);
|
2013-10-28 13:47:10 +04:00
|
|
|
}
|
|
|
|
|
2018-11-12 20:43:58 +03:00
|
|
|
bool scheduleSelfOrChild() override;
|
|
|
|
JobParallelism parallelism() override;
|
2017-08-11 01:03:03 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Abort synchronously or asynchronously - some jobs
|
|
|
|
* require to be finished without immediete abort (abort on job might
|
|
|
|
* cause conflicts/duplicated files - owncloud/client/issues/5949)
|
|
|
|
*/
|
2018-11-12 20:43:58 +03:00
|
|
|
void abort(PropagatorJob::AbortType abortType) override
|
2017-05-17 11:55:42 +03:00
|
|
|
{
|
2017-08-11 01:03:03 +03:00
|
|
|
if (!_runningJobs.empty()) {
|
|
|
|
_abortsCount = _runningJobs.size();
|
|
|
|
foreach (PropagatorJob *j, _runningJobs) {
|
|
|
|
if (abortType == AbortType::Asynchronous) {
|
|
|
|
connect(j, &PropagatorJob::abortFinished,
|
|
|
|
this, &PropagatorCompositeJob::slotSubJobAbortFinished);
|
|
|
|
}
|
|
|
|
j->abort(abortType);
|
|
|
|
}
|
|
|
|
} else if (abortType == AbortType::Asynchronous){
|
|
|
|
emit abortFinished();
|
|
|
|
}
|
2014-02-06 17:52:56 +04:00
|
|
|
}
|
2013-05-04 17:32:11 +04:00
|
|
|
|
2018-11-12 20:39:50 +03:00
|
|
|
qint64 committedDiskSpace() const override;
|
2015-10-01 12:39:09 +03:00
|
|
|
|
2013-10-28 13:47:10 +04:00
|
|
|
private slots:
|
2017-08-11 01:03:03 +03:00
|
|
|
void slotSubJobAbortFinished();
|
2017-05-17 11:55:42 +03:00
|
|
|
bool possiblyRunNextJob(PropagatorJob *next)
|
|
|
|
{
|
2014-11-18 19:35:31 +03:00
|
|
|
if (next->_state == NotYetStarted) {
|
2017-09-20 11:14:48 +03:00
|
|
|
connect(next, &PropagatorJob::finished, this, &PropagatorCompositeJob::slotSubJobFinished);
|
2014-11-18 19:35:31 +03:00
|
|
|
}
|
2017-02-14 14:46:44 +03:00
|
|
|
return next->scheduleSelfOrChild();
|
2013-10-28 13:47:10 +04:00
|
|
|
}
|
|
|
|
|
2014-02-06 15:11:45 +04:00
|
|
|
void slotSubJobFinished(SyncFileItem::Status status);
|
2017-02-14 01:16:20 +03:00
|
|
|
void finalize();
|
2013-10-28 13:47:10 +04:00
|
|
|
};
|
|
|
|
|
2017-02-13 19:21:55 +03:00
|
|
|
/**
|
|
|
|
* @brief Propagate a directory, and all its sub entries.
|
|
|
|
* @ingroup libsync
|
|
|
|
*/
|
2017-05-17 11:55:42 +03:00
|
|
|
class OWNCLOUDSYNC_EXPORT PropagateDirectory : public PropagatorJob
|
|
|
|
{
|
2017-02-13 19:21:55 +03:00
|
|
|
Q_OBJECT
|
|
|
|
public:
|
|
|
|
SyncFileItemPtr _item;
|
|
|
|
// e.g: create the directory
|
2017-05-17 11:55:42 +03:00
|
|
|
QScopedPointer<PropagateItemJob> _firstJob;
|
2017-02-13 19:21:55 +03:00
|
|
|
|
|
|
|
PropagatorCompositeJob _subJobs;
|
|
|
|
|
|
|
|
explicit PropagateDirectory(OwncloudPropagator *propagator, const SyncFileItemPtr &item = SyncFileItemPtr(new SyncFileItem));
|
|
|
|
|
2017-05-17 11:55:42 +03:00
|
|
|
void appendJob(PropagatorJob *job)
|
|
|
|
{
|
2017-02-14 01:16:20 +03:00
|
|
|
_subJobs.appendJob(job);
|
|
|
|
}
|
|
|
|
|
2017-05-17 11:55:42 +03:00
|
|
|
void appendTask(const SyncFileItemPtr &item)
|
|
|
|
{
|
2017-02-14 01:16:20 +03:00
|
|
|
_subJobs.appendTask(item);
|
2017-02-13 19:21:55 +03:00
|
|
|
}
|
|
|
|
|
2018-11-12 20:43:58 +03:00
|
|
|
bool scheduleSelfOrChild() override;
|
|
|
|
JobParallelism parallelism() override;
|
|
|
|
void abort(PropagatorJob::AbortType abortType) override
|
2017-05-17 11:55:42 +03:00
|
|
|
{
|
2017-02-13 19:21:55 +03:00
|
|
|
if (_firstJob)
|
2017-08-11 01:03:03 +03:00
|
|
|
// Force first job to abort synchronously
|
|
|
|
// even if caller allows async abort (asyncAbort)
|
|
|
|
_firstJob->abort(AbortType::Synchronous);
|
|
|
|
|
|
|
|
if (abortType == AbortType::Asynchronous){
|
|
|
|
connect(&_subJobs, &PropagatorCompositeJob::abortFinished, this, &PropagateDirectory::abortFinished);
|
|
|
|
}
|
|
|
|
_subJobs.abort(abortType);
|
2017-02-13 19:21:55 +03:00
|
|
|
}
|
|
|
|
|
2017-05-17 11:55:42 +03:00
|
|
|
void increaseAffectedCount()
|
|
|
|
{
|
2017-02-13 19:21:55 +03:00
|
|
|
_firstJob->_item->_affectedItems++;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-11-12 20:39:50 +03:00
|
|
|
qint64 committedDiskSpace() const override
|
2017-05-17 11:55:42 +03:00
|
|
|
{
|
2017-02-13 19:21:55 +03:00
|
|
|
return _subJobs.committedDiskSpace();
|
|
|
|
}
|
|
|
|
|
|
|
|
private slots:
|
|
|
|
|
|
|
|
void slotFirstJobFinished(SyncFileItem::Status status);
|
|
|
|
void slotSubJobsFinished(SyncFileItem::Status status);
|
2017-08-11 01:03:03 +03:00
|
|
|
|
2017-02-13 19:21:55 +03:00
|
|
|
};
|
|
|
|
|
2013-10-28 13:47:10 +04:00
|
|
|
|
2015-06-29 19:56:09 +03:00
|
|
|
/**
|
|
|
|
* @brief Dummy job that just mark it as completed and ignored
|
|
|
|
* @ingroup libsync
|
2015-06-26 18:07:47 +03:00
|
|
|
*/
|
2017-05-17 11:55:42 +03:00
|
|
|
class PropagateIgnoreJob : public PropagateItemJob
|
|
|
|
{
|
2013-10-28 13:47:10 +04:00
|
|
|
Q_OBJECT
|
|
|
|
public:
|
2017-05-17 11:55:42 +03:00
|
|
|
PropagateIgnoreJob(OwncloudPropagator *propagator, const SyncFileItemPtr &item)
|
|
|
|
: PropagateItemJob(propagator, item)
|
|
|
|
{
|
|
|
|
}
|
2018-11-12 20:39:50 +03:00
|
|
|
void start() override
|
2017-05-17 11:55:42 +03:00
|
|
|
{
|
2015-04-15 16:19:11 +03:00
|
|
|
SyncFileItem::Status status = _item->_status;
|
|
|
|
done(status == SyncFileItem::NoStatus ? SyncFileItem::FileIgnored : status, _item->_errorString);
|
2013-10-28 13:47:10 +04:00
|
|
|
}
|
|
|
|
};
|
2013-08-14 21:59:16 +04:00
|
|
|
|
2017-05-17 11:55:42 +03:00
|
|
|
class OwncloudPropagator : public QObject
|
|
|
|
{
|
2013-10-28 13:47:10 +04:00
|
|
|
Q_OBJECT
|
|
|
|
public:
|
2014-02-05 23:18:03 +04:00
|
|
|
const QString _localDir; // absolute path to the local directory. ends with '/'
|
2016-11-15 20:47:04 +03:00
|
|
|
const QString _remoteFolder; // remote folder, ends with '/'
|
2014-02-17 20:31:03 +04:00
|
|
|
|
2017-05-17 11:55:42 +03:00
|
|
|
SyncJournalDb *const _journal;
|
2015-10-05 06:20:09 +03:00
|
|
|
bool _finishedEmited; // used to ensure that finished is only emitted once
|
2013-10-16 18:47:24 +04:00
|
|
|
|
2013-05-03 21:11:00 +04:00
|
|
|
public:
|
2015-10-20 17:58:32 +03:00
|
|
|
OwncloudPropagator(AccountPtr account, const QString &localDir,
|
2017-05-17 11:55:42 +03:00
|
|
|
const QString &remoteFolder, SyncJournalDb *progressDb)
|
|
|
|
: _localDir((localDir.endsWith(QChar('/'))) ? localDir : localDir + '/')
|
|
|
|
, _remoteFolder((remoteFolder.endsWith(QChar('/'))) ? remoteFolder : remoteFolder + '/')
|
|
|
|
, _journal(progressDb)
|
|
|
|
, _finishedEmited(false)
|
|
|
|
, _bandwidthManager(this)
|
|
|
|
, _anotherSyncNeeded(false)
|
|
|
|
, _chunkSize(10 * 1000 * 1000) // 10 MB, overridden in setSyncOptions
|
|
|
|
, _account(account)
|
|
|
|
{
|
2017-08-11 01:03:03 +03:00
|
|
|
qRegisterMetaType<PropagatorJob::AbortType>("PropagatorJob::AbortType");
|
2017-05-17 11:55:42 +03:00
|
|
|
}
|
2013-10-28 13:47:10 +04:00
|
|
|
|
2015-03-27 13:11:44 +03:00
|
|
|
~OwncloudPropagator();
|
|
|
|
|
2019-09-28 10:17:12 +03:00
|
|
|
void start(const SyncFileItemVector &_syncedItems,
|
|
|
|
const bool &hasChange = false,
|
|
|
|
const int &lastChangeInstruction = 0,
|
|
|
|
const bool &hasDelete = false,
|
|
|
|
const int &lastDeleteInstruction = 0);
|
2013-05-16 15:54:22 +04:00
|
|
|
|
2017-05-17 11:55:42 +03:00
|
|
|
const SyncOptions &syncOptions() const;
|
|
|
|
void setSyncOptions(const SyncOptions &syncOptions);
|
2017-03-24 17:01:50 +03:00
|
|
|
|
2014-02-05 23:18:03 +04:00
|
|
|
QAtomicInt _downloadLimit;
|
|
|
|
QAtomicInt _uploadLimit;
|
2014-11-21 18:55:46 +03:00
|
|
|
BandwidthManager _bandwidthManager;
|
2013-08-14 21:59:16 +04:00
|
|
|
|
2014-02-06 17:52:56 +04:00
|
|
|
QAtomicInt _abortRequested; // boolean set by the main thread to abort.
|
2013-10-02 21:41:17 +04:00
|
|
|
|
2016-05-20 16:06:07 +03:00
|
|
|
/** The list of currently active jobs.
|
|
|
|
This list contains the jobs that are currently using ressources and is used purely to
|
|
|
|
know how many jobs there is currently running for the scheduler.
|
|
|
|
Jobs add themself to the list when they do an assynchronous operation.
|
|
|
|
Jobs can be several time on the list (example, when several chunks are uploaded in parallel)
|
|
|
|
*/
|
2017-05-17 11:55:42 +03:00
|
|
|
QList<PropagateItemJob *> _activeJobList;
|
2014-02-12 14:07:34 +04:00
|
|
|
|
2014-09-10 19:25:13 +04:00
|
|
|
/** We detected that another sync is required after this one */
|
|
|
|
bool _anotherSyncNeeded;
|
|
|
|
|
2017-07-12 10:58:15 +03:00
|
|
|
/** Per-folder quota guesses.
|
|
|
|
*
|
|
|
|
* This starts out empty. When an upload in a folder fails due to insufficent
|
|
|
|
* remote quota, the quota guess is updated to be attempted_size-1 at maximum.
|
|
|
|
*
|
|
|
|
* Note that it will usually just an upper limit for the actual quota - but
|
|
|
|
* since the quota on the server might change at any time it can sometimes be
|
|
|
|
* wrong in the other direction as well.
|
|
|
|
*
|
|
|
|
* This allows skipping of uploads that have a very high likelihood of failure.
|
|
|
|
*/
|
|
|
|
QHash<QString, quint64> _folderQuota;
|
|
|
|
|
2017-02-17 11:28:20 +03:00
|
|
|
/* the maximum number of jobs using bandwidth (uploads or downloads, in parallel) */
|
|
|
|
int maximumActiveTransferJob();
|
2017-03-24 17:01:50 +03:00
|
|
|
|
|
|
|
/** The size to use for upload chunks.
|
|
|
|
*
|
|
|
|
* Will be dynamically adjusted after each chunk upload finishes
|
|
|
|
* if Capabilities::desiredChunkUploadDuration has a target
|
|
|
|
* chunk-upload duration set.
|
|
|
|
*/
|
|
|
|
quint64 _chunkSize;
|
2017-04-20 12:14:53 +03:00
|
|
|
quint64 smallFileSize();
|
2017-03-24 17:01:50 +03:00
|
|
|
|
2015-10-05 06:20:09 +03:00
|
|
|
/* The maximum number of active jobs in parallel */
|
2016-02-25 19:40:24 +03:00
|
|
|
int hardMaximumActiveJob();
|
2014-09-15 19:55:55 +04:00
|
|
|
|
2017-03-07 13:47:38 +03:00
|
|
|
/** Check whether a download would clash with an existing file
|
|
|
|
* in filesystems that are only case-preserving.
|
|
|
|
*/
|
2017-05-17 11:55:42 +03:00
|
|
|
bool localFileNameClash(const QString &relfile);
|
2017-03-07 13:47:38 +03:00
|
|
|
|
|
|
|
/** Check whether a file is properly accessible for upload.
|
|
|
|
*
|
|
|
|
* It is possible to create files with filenames that differ
|
|
|
|
* only by case in NTFS, but most operations such as stat and
|
|
|
|
* open only target one of these by default.
|
|
|
|
*
|
|
|
|
* When that happens, we want to avoid uploading incorrect data
|
|
|
|
* and give up on the file.
|
|
|
|
*/
|
2017-05-17 11:55:42 +03:00
|
|
|
bool hasCaseClashAccessibilityProblem(const QString &relfile);
|
2017-03-07 13:47:38 +03:00
|
|
|
|
2017-11-29 00:17:29 +03:00
|
|
|
/* returns the local file path for the given tmp_file_name */
|
2017-05-17 11:55:42 +03:00
|
|
|
QString getFilePath(const QString &tmp_file_name) const;
|
2014-02-12 14:07:34 +04:00
|
|
|
|
2017-12-02 13:40:43 +03:00
|
|
|
/** Creates the job for an item.
|
|
|
|
*/
|
2017-05-17 11:55:42 +03:00
|
|
|
PropagateItemJob *createJob(const SyncFileItemPtr &item);
|
2017-12-02 13:40:43 +03:00
|
|
|
|
2017-02-14 14:46:44 +03:00
|
|
|
void scheduleNextJob();
|
2017-05-17 11:55:42 +03:00
|
|
|
void reportProgress(const SyncFileItem &, quint64 bytes);
|
2017-02-14 14:46:44 +03:00
|
|
|
|
2017-05-17 11:55:42 +03:00
|
|
|
void abort()
|
|
|
|
{
|
2017-09-28 10:52:21 +03:00
|
|
|
bool alreadyAborting = _abortRequested.fetchAndStoreOrdered(true);
|
|
|
|
if (alreadyAborting)
|
|
|
|
return;
|
2014-05-13 15:39:00 +04:00
|
|
|
if (_rootJob) {
|
2017-08-11 01:03:03 +03:00
|
|
|
// Connect to abortFinished which signals that abort has been asynchronously finished
|
|
|
|
connect(_rootJob.data(), &PropagateDirectory::abortFinished, this, &OwncloudPropagator::emitFinished);
|
|
|
|
|
|
|
|
// Use Queued Connection because we're possibly already in an item's finished stack
|
|
|
|
QMetaObject::invokeMethod(_rootJob.data(), "abort", Qt::QueuedConnection,
|
|
|
|
Q_ARG(PropagatorJob::AbortType, PropagatorJob::AbortType::Asynchronous));
|
|
|
|
|
|
|
|
// Give asynchronous abort 5000 msec to finish on its own
|
|
|
|
QTimer::singleShot(5000, this, SLOT(abortTimeout()));
|
|
|
|
} else {
|
|
|
|
// No root job, call emitFinished
|
|
|
|
emitFinished(SyncFileItem::NormalError);
|
2014-05-13 15:39:00 +04:00
|
|
|
}
|
2014-02-06 17:52:56 +04:00
|
|
|
}
|
2014-02-17 20:31:03 +04:00
|
|
|
|
2014-12-18 14:09:48 +03:00
|
|
|
AccountPtr account() const;
|
|
|
|
|
2017-05-17 11:55:42 +03:00
|
|
|
enum DiskSpaceResult {
|
2015-10-01 12:39:09 +03:00
|
|
|
DiskSpaceOk,
|
|
|
|
DiskSpaceFailure,
|
|
|
|
DiskSpaceCritical
|
|
|
|
};
|
|
|
|
|
|
|
|
/** Checks whether there's enough disk space available to complete
|
|
|
|
* all jobs that are currently running.
|
|
|
|
*/
|
|
|
|
DiskSpaceResult diskSpaceCheck() const;
|
2014-11-07 13:41:21 +03:00
|
|
|
|
2018-01-17 12:59:47 +03:00
|
|
|
/** Handles a conflict by renaming the file 'item'.
|
|
|
|
*
|
|
|
|
* Sets up conflict records.
|
|
|
|
*
|
|
|
|
* It also creates a new upload job in composite if the item that's
|
|
|
|
* moved away is a file and conflict uploads are requested.
|
|
|
|
*
|
|
|
|
* Returns true on success, false and error on error.
|
|
|
|
*/
|
|
|
|
bool createConflict(const SyncFileItemPtr &item,
|
|
|
|
PropagatorCompositeJob *composite, QString *error);
|
|
|
|
|
2014-05-29 14:15:13 +04:00
|
|
|
private slots:
|
|
|
|
|
2017-08-11 01:03:03 +03:00
|
|
|
void abortTimeout()
|
|
|
|
{
|
|
|
|
// Abort synchronously and finish
|
|
|
|
_rootJob.data()->abort(PropagatorJob::AbortType::Synchronous);
|
|
|
|
emitFinished(SyncFileItem::NormalError);
|
|
|
|
}
|
|
|
|
|
2015-10-05 06:20:09 +03:00
|
|
|
/** Emit the finished signal and make sure it is only emitted once */
|
2017-05-17 11:55:42 +03:00
|
|
|
void emitFinished(SyncFileItem::Status status)
|
|
|
|
{
|
2014-05-29 14:15:13 +04:00
|
|
|
if (!_finishedEmited)
|
2016-08-02 11:30:49 +03:00
|
|
|
emit finished(status == SyncFileItem::Success);
|
2014-05-29 14:15:13 +04:00
|
|
|
_finishedEmited = true;
|
|
|
|
}
|
|
|
|
|
2017-02-14 14:46:44 +03:00
|
|
|
void scheduleNextJobImpl();
|
2014-11-18 19:35:31 +03:00
|
|
|
|
2013-05-16 15:54:22 +04:00
|
|
|
signals:
|
2017-12-02 13:40:43 +03:00
|
|
|
void newItem(const SyncFileItemPtr &);
|
2017-01-25 13:12:38 +03:00
|
|
|
void itemCompleted(const SyncFileItemPtr &);
|
2017-05-17 11:55:42 +03:00
|
|
|
void progress(const SyncFileItem &, quint64 bytes);
|
2016-08-02 11:30:49 +03:00
|
|
|
void finished(bool success);
|
2013-11-25 01:26:50 +04:00
|
|
|
|
2016-04-29 17:14:18 +03:00
|
|
|
/** Emitted when propagation has problems with a locked file. */
|
|
|
|
void seenLockedFile(const QString &fileName);
|
|
|
|
|
2016-06-09 13:07:18 +03:00
|
|
|
/** Emitted when propagation touches a file.
|
|
|
|
*
|
|
|
|
* Used to track our own file modifications such that notifications
|
|
|
|
* from the file watcher about these can be ignored.
|
|
|
|
*/
|
|
|
|
void touchedFile(const QString &fileName);
|
|
|
|
|
2017-06-28 13:45:54 +03:00
|
|
|
void insufficientLocalStorage();
|
2017-07-07 16:11:00 +03:00
|
|
|
void insufficientRemoteStorage();
|
2017-06-28 13:45:54 +03:00
|
|
|
|
2014-11-07 13:41:21 +03:00
|
|
|
private:
|
2014-12-18 14:09:48 +03:00
|
|
|
AccountPtr _account;
|
2017-03-16 16:19:36 +03:00
|
|
|
QScopedPointer<PropagateDirectory> _rootJob;
|
2017-03-24 17:01:50 +03:00
|
|
|
SyncOptions _syncOptions;
|
2013-05-03 21:11:00 +04:00
|
|
|
};
|
|
|
|
|
2015-06-26 18:07:47 +03:00
|
|
|
|
2015-06-29 19:56:09 +03:00
|
|
|
/**
|
|
|
|
* @brief Job that wait for all the poll jobs to be completed
|
|
|
|
* @ingroup libsync
|
2015-06-26 18:07:47 +03:00
|
|
|
*/
|
2017-05-17 11:55:42 +03:00
|
|
|
class CleanupPollsJob : public QObject
|
|
|
|
{
|
2014-07-28 14:12:52 +04:00
|
|
|
Q_OBJECT
|
2017-05-17 11:55:42 +03:00
|
|
|
QVector<SyncJournalDb::PollInfo> _pollInfos;
|
2014-12-18 14:09:48 +03:00
|
|
|
AccountPtr _account;
|
2014-07-28 14:12:52 +04:00
|
|
|
SyncJournalDb *_journal;
|
|
|
|
QString _localPath;
|
2017-05-17 11:55:42 +03:00
|
|
|
|
2014-07-28 14:12:52 +04:00
|
|
|
public:
|
2017-05-17 11:55:42 +03:00
|
|
|
explicit CleanupPollsJob(const QVector<SyncJournalDb::PollInfo> &pollInfos, AccountPtr account,
|
2018-11-12 20:46:39 +03:00
|
|
|
SyncJournalDb *journal, const QString &localPath, QObject *parent = nullptr)
|
2017-05-17 11:55:42 +03:00
|
|
|
: QObject(parent)
|
|
|
|
, _pollInfos(pollInfos)
|
|
|
|
, _account(account)
|
|
|
|
, _journal(journal)
|
|
|
|
, _localPath(localPath)
|
|
|
|
{
|
|
|
|
}
|
2014-07-28 14:12:52 +04:00
|
|
|
|
2015-03-27 13:11:44 +03:00
|
|
|
~CleanupPollsJob();
|
|
|
|
|
2016-07-14 10:21:28 +03:00
|
|
|
/**
|
|
|
|
* Start the job. After the job is completed, it will emit either finished or aborted, and it
|
|
|
|
* will destroy itself.
|
|
|
|
*/
|
2014-07-28 14:12:52 +04:00
|
|
|
void start();
|
|
|
|
signals:
|
|
|
|
void finished();
|
2014-07-29 17:51:22 +04:00
|
|
|
void aborted(const QString &error);
|
2014-07-28 14:12:52 +04:00
|
|
|
private slots:
|
|
|
|
void slotPollFinished();
|
|
|
|
};
|
2013-05-03 21:11:00 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|