Browse Source

Fix files not unlocking after lock time expired

Signed-off-by: Claudio Cambra <claudio.cambra@gmail.com>
Claudio Cambra 3 years ago
parent
commit
fd332a52e2

+ 11 - 2
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();
 }
 

+ 1 - 1
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

+ 20 - 6
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<QByteArray> &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)) {

+ 1 - 0
src/libsync/discoveryphase.h

@@ -283,6 +283,7 @@ public:
     // output
     QByteArray _dataFingerprint;
     bool _anotherSyncNeeded = false;
+    int _scheduleSyncInSecs = -1;
 
 signals:
     void fatalError(const QString &errorString);

+ 16 - 2
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<qint64>(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;
         }