Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 25 additions & 8 deletions src/libsync/discoveryphase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -210,18 +210,23 @@ int get_errno_from_http_errcode( int err, const QString & reason ) {


DiscoverySingleDirectoryJob::DiscoverySingleDirectoryJob(const AccountPtr &account, const QString &path, QObject *parent)
: QObject(parent), _subPath(path), _account(account), _ignoredFirst(false)
: QObject(parent), _subPath(path), _account(account), _ignoredFirst(false), _isRootPath(false)
{
}

void DiscoverySingleDirectoryJob::start()
{
// Start the actual HTTP job
LsColJob *lsColJob = new LsColJob(_account, _subPath, this);
lsColJob->setProperties(QList<QByteArray>() << "resourcetype" << "getlastmodified"
<< "getcontentlength" << "getetag" << "http://owncloud.org/ns:id"
<< "http://owncloud.org/ns:downloadURL" << "http://owncloud.org/ns:dDC"
<< "http://owncloud.org/ns:permissions");

QList<QByteArray> props;
props << "resourcetype" << "getlastmodified" << "getcontentlength" << "getetag"
<< "http://owncloud.org/ns:id" << "http://owncloud.org/ns:downloadURL"
<< "http://owncloud.org/ns:dDC" << "http://owncloud.org/ns:permissions";
if (_isRootPath)
props << "http://owncloud.org/ns:data-fingerprint";

lsColJob->setProperties(props);

QObject::connect(lsColJob, SIGNAL(directoryListingIterated(QString,QMap<QString,QString>)),
this, SLOT(directoryListingIteratedSlot(QString,QMap<QString,QString>)));
Expand Down Expand Up @@ -299,12 +304,14 @@ void DiscoverySingleDirectoryJob::directoryListingIteratedSlot(QString file, con
{
//qDebug() << Q_FUNC_INFO << _subPath << file << map.count() << map.keys() << _account->davPath() << _lsColJob->reply()->request().url().path();
if (!_ignoredFirst) {
// First result is the directory itself. Maybe should have a better check for that? FIXME
// The first entry is for the folder itself, we should process it differently.
_ignoredFirst = true;
if (map.contains("permissions")) {
emit firstDirectoryPermissions(map.value("permissions"));
}

if (map.contains("data-fingerprint")) {
_dataFingerprint = map.value("data-fingerprint").toUtf8();
}
} else {
// Remove <webDAV-Url>/folder/ from <webDAV-Url>/folder/subfile.txt
file.remove(0, _lsColJob->reply()->request().url().path().length());
Expand Down Expand Up @@ -426,6 +433,11 @@ void DiscoveryMainThread::doOpendirSlot(const QString &subPath, DiscoveryDirecto
this, SIGNAL(etagConcatenation(QString)));
QObject::connect(_singleDirJob, SIGNAL(etag(QString)),
this, SIGNAL(etag(QString)));

if (!_firstFolderProcessed) {
_singleDirJob->setIsRootPath();
}

_singleDirJob->start();
}

Expand All @@ -441,7 +453,12 @@ void DiscoveryMainThread::singleDirectoryJobResultSlot(const QList<FileStatPoint
_currentDiscoveryDirectoryResult->list = result;
_currentDiscoveryDirectoryResult->code = 0;
_currentDiscoveryDirectoryResult->listIndex = 0;
_currentDiscoveryDirectoryResult = 0; // the sync thread owns it now
_currentDiscoveryDirectoryResult = 0; // the sync thread owns it now

if (!_firstFolderProcessed) {
_firstFolderProcessed = true;
_dataFingerprint = _singleDirJob->_dataFingerprint;
}

_discoveryJob->_vioMutex.lock();
_discoveryJob->_vioWaitCondition.wakeAll();
Expand Down
14 changes: 13 additions & 1 deletion src/libsync/discoveryphase.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ class DiscoverySingleDirectoryJob : public QObject {
Q_OBJECT
public:
explicit DiscoverySingleDirectoryJob(const AccountPtr &account, const QString &path, QObject *parent = 0);
// Specify thgat this is the root and we need to check the data-fingerprint
void setIsRootPath() { _isRootPath = true; }
void start();
void abort();
// This is not actually a network job, it is just a job
Expand All @@ -100,8 +102,15 @@ private slots:
QString _etagConcatenation;
QString _firstEtag;
AccountPtr _account;
// The first result is for the directory itself and need to be ignored.
// This flag is true if it was already ignored.
bool _ignoredFirst;
// Set to true if this is the root path and we need to check the data-fingerprint
bool _isRootPath;
QPointer<LsColJob> _lsColJob;

public:
QByteArray _dataFingerprint;
};

// Lives in main thread. Deleted by the SyncEngine
Expand All @@ -115,13 +124,16 @@ class DiscoveryMainThread : public QObject {
AccountPtr _account;
DiscoveryDirectoryResult *_currentDiscoveryDirectoryResult;
qint64 *_currentGetSizeResult;
bool _firstFolderProcessed;

public:
DiscoveryMainThread(AccountPtr account) : QObject(), _account(account),
_currentDiscoveryDirectoryResult(0), _currentGetSizeResult(0)
_currentDiscoveryDirectoryResult(0), _currentGetSizeResult(0), _firstFolderProcessed(false)
{ }
void abort();

QByteArray _dataFingerprint;


public slots:
// From DiscoveryJob:
Expand Down
2 changes: 1 addition & 1 deletion src/libsync/owncloudpropagator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ void OwncloudPropagator::start(const SyncFileItemVector& items)
connect(_rootJob.data(), SIGNAL(itemCompleted(const SyncFileItem &, const PropagatorJob &)),
this, SIGNAL(itemCompleted(const SyncFileItem &, const PropagatorJob &)));
connect(_rootJob.data(), SIGNAL(progress(const SyncFileItem &,quint64)), this, SIGNAL(progress(const SyncFileItem &,quint64)));
connect(_rootJob.data(), SIGNAL(finished(SyncFileItem::Status)), this, SLOT(emitFinished()));
connect(_rootJob.data(), SIGNAL(finished(SyncFileItem::Status)), this, SLOT(emitFinished(SyncFileItem::Status)));
connect(_rootJob.data(), SIGNAL(ready()), this, SLOT(scheduleNextJob()), Qt::QueuedConnection);

qDebug() << "Using QNAM/HTTP parallel code path";
Expand Down
8 changes: 4 additions & 4 deletions src/libsync/owncloudpropagator.h
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ class OwncloudPropagator : public QObject {
if (_rootJob) {
_rootJob->abort();
}
emitFinished();
emitFinished(SyncFileItem::NormalError);
}

// timeout in seconds
Expand Down Expand Up @@ -349,9 +349,9 @@ class OwncloudPropagator : public QObject {
private slots:

/** Emit the finished signal and make sure it is only emitted once */
void emitFinished() {
void emitFinished(SyncFileItem::Status status) {
if (!_finishedEmited)
emit finished();
emit finished(status == SyncFileItem::Success);
_finishedEmited = true;
}

Expand All @@ -360,7 +360,7 @@ private slots:
signals:
void itemCompleted(const SyncFileItem &, const PropagatorJob &);
void progress(const SyncFileItem&, quint64 bytes);
void finished();
void finished(bool success);

/** Emitted when propagation has problems with a locked file. */
void seenLockedFile(const QString &fileName);
Expand Down
21 changes: 17 additions & 4 deletions src/libsync/syncengine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -915,7 +915,16 @@ void SyncEngine::slotDiscoveryJobFinished(int discoveryResult)
return;
}
}
if (!_hasForwardInTimeFiles && _backInTimeFiles >= 2) {

auto databaseFingerprint = _journal->dataFingerprint();
// If databaseFingerprint is null, this means that there was no information in the database
// (for example, upgrading from a previous version, or first sync)
// Note that an empty ("") fingerprint is valid and means it was empty on the server before.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i still don't understand why there is a difference between empty and null. Why can't they be treated the same?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Null means there was no entry before.

Empty means there was one, and it was empty.
By default, on the server, it is empty.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah. Weeeeird. But I guess this is because it was added later to the server

FYI @rullzer

if (!databaseFingerprint.isNull()
&& _discoveryMainThread->_dataFingerprint != databaseFingerprint) {
qDebug() << "data fingerprint changed, assume restore from backup" << databaseFingerprint << _discoveryMainThread->_dataFingerprint;
restoreOldFiles();
} else if (!_hasForwardInTimeFiles && _backInTimeFiles >= 2) {
qDebug() << "All the changes are bringing files in the past, asking the user";
// this typically happen when a backup is restored on the server
bool restore = false;
Expand Down Expand Up @@ -958,7 +967,7 @@ void SyncEngine::slotDiscoveryJobFinished(int discoveryResult)
this, SLOT(slotItemCompleted(const SyncFileItem &, const PropagatorJob &)));
connect(_propagator.data(), SIGNAL(progress(const SyncFileItem &,quint64)),
this, SLOT(slotProgress(const SyncFileItem &,quint64)));
connect(_propagator.data(), SIGNAL(finished()), this, SLOT(slotFinished()), Qt::QueuedConnection);
connect(_propagator.data(), SIGNAL(finished(bool)), this, SLOT(slotFinished(bool)), Qt::QueuedConnection);
connect(_propagator.data(), SIGNAL(seenLockedFile(QString)), SIGNAL(seenLockedFile(QString)));
connect(_propagator.data(), SIGNAL(touchedFile(QString)), SLOT(slotAddTouchedFile(QString)));

Expand Down Expand Up @@ -1026,18 +1035,22 @@ void SyncEngine::slotItemCompleted(const SyncFileItem &item, const PropagatorJob
emit itemCompleted(item, job);
}

void SyncEngine::slotFinished()
void SyncEngine::slotFinished(bool success)
{
_anotherSyncNeeded = _anotherSyncNeeded || _propagator->_anotherSyncNeeded;

if (success) {
_journal->setDataFingerprint(_discoveryMainThread->_dataFingerprint);
}

// emit the treewalk results.
if( ! _journal->postSyncCleanup( _seenFiles, _temporarilyUnavailablePaths ) ) {
qDebug() << "Cleaning of synced ";
}

_journal->commit("All Finished.", false);
emit treeWalkResult(_syncedItems);
finalize(true); // FIXME: should it be true if there was errors?
finalize(success);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

behaviour change. did you check the implications?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, this has to do when the white list is cleared.

}

void SyncEngine::finalize(bool success)
Expand Down
2 changes: 1 addition & 1 deletion src/libsync/syncengine.h
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ class OWNCLOUDSYNC_EXPORT SyncEngine : public QObject
private slots:
void slotRootEtagReceived(const QString &);
void slotItemCompleted(const SyncFileItem& item, const PropagatorJob & job);
void slotFinished();
void slotFinished(bool success);
void slotProgress(const SyncFileItem& item, quint64 curent);
void slotDiscoveryJobFinished(int updateResult);
void slotCleanPollsJobAborted(const QString &error);
Expand Down
61 changes: 61 additions & 0 deletions src/libsync/syncjournaldb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,13 @@ bool SyncJournalDb::checkConnect()
return sqlFail("Create table version", createQuery);
}

// create the checksumtype table.
createQuery.prepare("CREATE TABLE IF NOT EXISTS datafingerprint("
"fingerprint TEXT UNIQUE"
");");
if (!createQuery.exec()) {
return sqlFail("Create table datafingerprint", createQuery);
}

createQuery.prepare("CREATE TABLE IF NOT EXISTS version("
"major INTEGER(8),"
Expand Down Expand Up @@ -436,6 +443,14 @@ bool SyncJournalDb::checkConnect()
_insertChecksumTypeQuery.reset(new SqlQuery(_db));
_insertChecksumTypeQuery->prepare("INSERT OR IGNORE INTO checksumtype (name) VALUES (?1)");

_getDataFingerprintQuery.reset(new SqlQuery(_db));
_getDataFingerprintQuery->prepare("SELECT fingerprint FROM datafingerprint");

_setDataFingerprintQuery1.reset(new SqlQuery(_db));
_setDataFingerprintQuery1->prepare("DELETE FROM datafingerprint;");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you actually need this query? Why not INSERT OR REPLACE?
If you need it: Why is it not named _deleteDataFingerprintQuery?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because that's a table that can contain several entries, but i only want one.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not make it contain only one by using INSERT OR REPLACE?
Why is this not commented?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How would you write it with INSERT OR REPLACE?

_setDataFingerprintQuery2.reset(new SqlQuery(_db));
_setDataFingerprintQuery2->prepare("INSERT INTO datafingerprint (fingerprint) VALUES (?1);");

// don't start a new transaction now
commitInternal(QString("checkConnect End"), false);

Expand Down Expand Up @@ -472,6 +487,9 @@ void SyncJournalDb::close()
_getChecksumTypeIdQuery.reset(0);
_getChecksumTypeQuery.reset(0);
_insertChecksumTypeQuery.reset(0);
_getDataFingerprintQuery.reset(0);
_setDataFingerprintQuery1.reset(0);
_setDataFingerprintQuery2.reset(0);

_db.close();
_avoidReadFromDbOnNextSyncFilter.clear();
Expand Down Expand Up @@ -1602,6 +1620,49 @@ int SyncJournalDb::mapChecksumType(const QByteArray& checksumType)
return _getChecksumTypeIdQuery->intValue(0);
}

QByteArray SyncJournalDb::dataFingerprint()
{
QMutexLocker locker(&_mutex);
if (!checkConnect()) {
return QByteArray();
}

_getDataFingerprintQuery->reset_and_clear_bindings();
if (!_getDataFingerprintQuery->exec()) {
qWarning() << "Error SQL statement dataFingerprint: "
<< _getDataFingerprintQuery->lastQuery() << " :"
<< _getDataFingerprintQuery->error();
return QByteArray();
}

if (!_getDataFingerprintQuery->next()) {
return QByteArray();
}
return _getDataFingerprintQuery->baValue(0);
}

void SyncJournalDb::setDataFingerprint(const QByteArray &dataFingerprint)
{
QMutexLocker locker(&_mutex);
if (!checkConnect()) {
return;
}

_setDataFingerprintQuery1->reset_and_clear_bindings();
if (!_setDataFingerprintQuery1->exec()) {
qWarning() << "Error SQL statement setDataFingerprint1: "
<< _setDataFingerprintQuery1->lastQuery() << " :"
<< _setDataFingerprintQuery1->error();
}

_setDataFingerprintQuery2->reset_and_clear_bindings();
_setDataFingerprintQuery2->bindValue(1, dataFingerprint);
if (!_setDataFingerprintQuery2->exec()) {
qWarning() << "Error SQL statement setDataFingerprint2: "
<< _setDataFingerprintQuery2->lastQuery() << " :"
<< _setDataFingerprintQuery2->error();
}
}

void SyncJournalDb::commit(const QString& context, bool startTrans)
{
Expand Down
9 changes: 9 additions & 0 deletions src/libsync/syncjournaldb.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,12 @@ class OWNCLOUDSYNC_EXPORT SyncJournalDb : public QObject
*/
QByteArray getChecksumType(int checksumTypeId);

/**
* The data-fingerprint used to detect backup
*/
void setDataFingerprint(const QByteArray &dataFingerprint);
QByteArray dataFingerprint();

private:
bool updateDatabaseStructure();
bool updateMetadataTableStructure();
Expand Down Expand Up @@ -196,6 +202,9 @@ class OWNCLOUDSYNC_EXPORT SyncJournalDb : public QObject
QScopedPointer<SqlQuery> _getChecksumTypeIdQuery;
QScopedPointer<SqlQuery> _getChecksumTypeQuery;
QScopedPointer<SqlQuery> _insertChecksumTypeQuery;
QScopedPointer<SqlQuery> _getDataFingerprintQuery;
QScopedPointer<SqlQuery> _setDataFingerprintQuery1;
QScopedPointer<SqlQuery> _setDataFingerprintQuery2;

/* This is the list of paths we called avoidReadFromDbOnNextSync on.
* It means that they should not be written to the DB in any case since doing
Expand Down