From fd332a52e2215588ef0e6026b2fc16b074bbc1e0 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Tue, 20 Sep 2022 23:03:39 +0200 Subject: [PATCH] Fix files not unlocking after lock time expired Signed-off-by: Claudio Cambra --- src/common/syncjournaldb.cpp | 13 +++++++++++-- src/common/syncjournaldb.h | 2 +- src/libsync/discovery.cpp | 26 ++++++++++++++++++++------ src/libsync/discoveryphase.h | 1 + src/libsync/syncengine.cpp | 18 ++++++++++++++++-- 5 files changed, 49 insertions(+), 11 deletions(-) diff --git a/src/common/syncjournaldb.cpp b/src/common/syncjournaldb.cpp index 801aa5f50..94ec86899 100644 --- a/src/common/syncjournaldb.cpp +++ b/src/common/syncjournaldb.cpp @@ -1351,7 +1351,7 @@ bool SyncJournalDb::updateFileRecordChecksum(const QString &filename, } bool SyncJournalDb::updateLocalMetadata(const QString &filename, - qint64 modtime, qint64 size, quint64 inode) + qint64 modtime, qint64 size, quint64 inode, const SyncJournalFileLockInfo &lockInfo) { QMutexLocker locker(&_mutex); @@ -1365,7 +1365,9 @@ bool SyncJournalDb::updateLocalMetadata(const QString &filename, } const auto query = _queryManager.get(PreparedSqlQueryManager::SetFileRecordLocalMetadataQuery, QByteArrayLiteral("UPDATE metadata" - " SET inode=?2, modtime=?3, filesize=?4" + " SET inode=?2, modtime=?3, filesize=?4, lock=?5, lockType=?6," + " lockOwnerDisplayName=?7, lockOwnerId=?8, lockOwnerEditor = ?9," + " lockTime=?10, lockTimeout=?11" " WHERE phash == ?1;"), _db); if (!query) { @@ -1376,6 +1378,13 @@ bool SyncJournalDb::updateLocalMetadata(const QString &filename, query->bindValue(2, inode); query->bindValue(3, modtime); query->bindValue(4, size); + query->bindValue(5, lockInfo._locked ? 1 : 0); + query->bindValue(6, lockInfo._lockOwnerDisplayName); + query->bindValue(7, lockInfo._lockOwnerId); + query->bindValue(8, lockInfo._lockOwnerType); + query->bindValue(9, lockInfo._lockEditorApp); + query->bindValue(10, lockInfo._lockTime); + query->bindValue(11, lockInfo._lockTimeout); return query->exec(); } diff --git a/src/common/syncjournaldb.h b/src/common/syncjournaldb.h index a68a5d741..998a93392 100644 --- a/src/common/syncjournaldb.h +++ b/src/common/syncjournaldb.h @@ -78,7 +78,7 @@ public: const QByteArray &contentChecksum, const QByteArray &contentChecksumType); [[nodiscard]] bool updateLocalMetadata(const QString &filename, - qint64 modtime, qint64 size, quint64 inode); + qint64 modtime, qint64 size, quint64 inode, const SyncJournalFileLockInfo &lockInfo); /// Return value for hasHydratedOrDehydratedFiles() struct HasHydratedDehydrated diff --git a/src/libsync/discovery.cpp b/src/libsync/discovery.cpp index f627af257..5faaa11ad 100644 --- a/src/libsync/discovery.cpp +++ b/src/libsync/discovery.cpp @@ -394,6 +394,17 @@ void ProcessDirectoryJob::processFile(PathTuple path, if (item->_type == ItemTypeVirtualFileDehydration) item->_type = ItemTypeFile; + // We want to check the lock state of this file after the lock time has expired + if(serverEntry.locked == SyncFileItem::LockStatus::LockedItem) { + const auto lockExpirationTime = serverEntry.lockTime + serverEntry.lockTimeout; + const auto timeRemaining = QDateTime::currentDateTime().secsTo(QDateTime::fromSecsSinceEpoch(lockExpirationTime)); + const auto timerInterval = qMax(5LL, timeRemaining); + + qCInfo(lcDisco) << "Will re-check lock status for:" << path._original << "in:" << timerInterval << "seconds."; + _discoveryData->_anotherSyncNeeded = true; + _discoveryData->_scheduleSyncInSecs = timerInterval; + } + // VFS suffixed files on the server are ignored if (isVfsWithSuffix()) { if (hasVirtualFileSuffix(serverEntry.name) @@ -505,6 +516,7 @@ void ProcessDirectoryJob::processFileAnalyzeRemoteInfo( const bool isVirtualE2EePlaceholder = isDbEntryAnE2EePlaceholder && serverEntry.size >= Constants::e2EeTagSize; const qint64 sizeOnServer = isVirtualE2EePlaceholder ? serverEntry.size - Constants::e2EeTagSize : serverEntry.size; const bool metaDataSizeNeedsUpdateForE2EeFilePlaceholder = isVirtualE2EePlaceholder && dbEntry._fileSize == serverEntry.size; + const bool serverEntryLockedAsBool = serverEntry.locked == SyncFileItem::LockStatus::LockedItem; if (serverEntry.isDirectory != dbEntry.isDirectory()) { // If the type of the entity changed, it's like NEW, but @@ -551,6 +563,8 @@ void ProcessDirectoryJob::processFileAnalyzeRemoteInfo( } item->_instruction = CSYNC_INSTRUCTION_UPDATE_METADATA; item->_direction = SyncFileItem::Down; + } else if(serverEntryLockedAsBool != dbEntry._lockstate._locked) { + item->_instruction = CSYNC_INSTRUCTION_UPDATE_METADATA; } else { // if (is virtual mode enabled and folder is encrypted - check if the size is the same as on the server and then - trigger server query // to update a placeholder with corrected size (-16 Bytes) @@ -815,7 +829,7 @@ void ProcessDirectoryJob::processFileAnalyzeLocalInfo( bool serverModified = item->_instruction == CSYNC_INSTRUCTION_NEW || item->_instruction == CSYNC_INSTRUCTION_SYNC || item->_instruction == CSYNC_INSTRUCTION_RENAME || item->_instruction == CSYNC_INSTRUCTION_TYPE_CHANGE; - + qCDebug(lcDisco) << "File" << item->_file << "- servermodified:" << serverModified << "noServerEntry:" << noServerEntry; @@ -1029,7 +1043,7 @@ void ProcessDirectoryJob::processFileAnalyzeLocalInfo( item->_size = localEntry.size; item->_modtime = localEntry.modtime; _childModified = true; - + qCDebug(lcDisco) << "Local file was changed: File" << item->_file << "item->_instruction:" << item->_instruction << "noServerEntry:" << noServerEntry @@ -1316,7 +1330,7 @@ void ProcessDirectoryJob::processFileAnalyzeLocalInfo( chopVirtualFileSuffix(serverOriginalPath); auto job = new RequestEtagJob(_discoveryData->_account, serverOriginalPath, this); connect(job, &RequestEtagJob::finishedWithResult, this, [=](const HttpResult &etag) mutable { - + if (!etag || (etag.get() != base._etag && !item->isDirectory()) || _discoveryData->isRenamed(originalPath) || (isAnyParentBeingRestored(originalPath) && !isRename(originalPath))) { @@ -1382,7 +1396,7 @@ void ProcessDirectoryJob::processFileConflict(const SyncFileItemPtr &item, Proce << "localEntry.modtime:" << localEntry.modtime; return; } - + if (!serverEntry.checksumHeader.isEmpty()) { qCDebug(lcDisco) << "CSYNC_INSTRUCTION_CONFLICT: File" << item->_file << "if (!serverEntry.checksumHeader.isEmpty())"; qCDebug(lcDisco) << "CSYNC_INSTRUCTION_CONFLICT: serverEntry.size:" << serverEntry.size @@ -1425,7 +1439,7 @@ void ProcessDirectoryJob::processFileConflict(const SyncFileItemPtr &item, Proce } return; } - + if (!up._valid || up._contentChecksum != serverEntry.checksumHeader) { qCDebug(lcDisco) << "CSYNC_INSTRUCTION_SYNC: File" << item->_file << "if (!up._valid && up._contentChecksum != serverEntry.checksumHeader)"; qCDebug(lcDisco) << "CSYNC_INSTRUCTION_SYNC: up._valid:" << up._valid @@ -1636,7 +1650,7 @@ bool ProcessDirectoryJob::isRename(const QString &originalPath) const /* TODO: This was needed at some point to cover an edge case which I am no longer to reproduce and it might no longer be the case. * Still, leaving this here just in case the edge case is caught at some point in future. - * + * OCC::SyncJournalFileRecord base; // are we allowed to rename? if (!_discoveryData || !_discoveryData->_statedb || !_discoveryData->_statedb->getFileRecord(originalPath, &base)) { diff --git a/src/libsync/discoveryphase.h b/src/libsync/discoveryphase.h index d1f723399..f0597170e 100644 --- a/src/libsync/discoveryphase.h +++ b/src/libsync/discoveryphase.h @@ -283,6 +283,7 @@ public: // output QByteArray _dataFingerprint; bool _anotherSyncNeeded = false; + int _scheduleSyncInSecs = -1; signals: void fatalError(const QString &errorString); diff --git a/src/libsync/syncengine.cpp b/src/libsync/syncengine.cpp index 2f0b8eda8..a36e7e33d 100644 --- a/src/libsync/syncengine.cpp +++ b/src/libsync/syncengine.cpp @@ -391,7 +391,17 @@ void OCC::SyncEngine::slotItemDiscovered(const OCC::SyncFileItemPtr &item) emit itemCompleted(item); } else { // Update only outdated data from the disk. - if (!_journal->updateLocalMetadata(item->_file, item->_modtime, item->_size, item->_inode)) { + + SyncJournalFileLockInfo lockInfo; + lockInfo._locked = item->_locked == SyncFileItem::LockStatus::LockedItem; + lockInfo._lockTime = item->_lockTime; + lockInfo._lockTimeout = item->_lockTimeout; + lockInfo._lockOwnerId = item->_lockOwnerId; + lockInfo._lockOwnerType = static_cast(item->_lockOwnerType); + lockInfo._lockOwnerDisplayName = item->_lockOwnerDisplayName; + lockInfo._lockEditorApp = item->_lockOwnerDisplayName; + + if (!_journal->updateLocalMetadata(item->_file, item->_modtime, item->_size, item->_inode, lockInfo)) { qCWarning(lcEngine) << "Could not update local metadata for file" << item->_file; } } @@ -689,7 +699,11 @@ void SyncEngine::slotDiscoveryFinished() restoreOldFiles(_syncItems); } - if (_discoveryPhase->_anotherSyncNeeded && _anotherSyncNeeded == NoFollowUpSync) { + if (_discoveryPhase->_anotherSyncNeeded && _discoveryPhase->_scheduleSyncInSecs > 0) { + QTimer::singleShot(_discoveryPhase->_scheduleSyncInSecs * 1000, this, [this]{ + this->startSync(); + }); + } else if (_discoveryPhase->_anotherSyncNeeded && _anotherSyncNeeded == NoFollowUpSync) { _anotherSyncNeeded = ImmediateFollowUp; }