From: Matthieu Gallien Date: Thu, 11 Apr 2024 16:39:20 +0000 (+0200) Subject: if a virtual file change but bothing changed: set it as in sync X-Git-Tag: archive/raspbian/3.16.7-1_deb13u1+rpi1~1^2~12^2~7^2~39^2 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=97403e35d62c7d19ba0f2426a69458b6a3ab947f;p=nextcloud-desktop.git if a virtual file change but bothing changed: set it as in sync some software (at least outlook native software) may fiddle with CfApi placeholder metadta and set a .msg file to be out of sync state when opening it in that case, we will let the sync engine go over it and decide what to do it is then possible in that case that we would just put it back in "in sync" state Signed-off-by: Matthieu Gallien --- diff --git a/src/common/vfs.h b/src/common/vfs.h index 060b5ec58..b3c5df9d0 100644 --- a/src/common/vfs.h +++ b/src/common/vfs.h @@ -187,7 +187,11 @@ public: * If the remote metadata changes, the local placeholder's metadata should possibly * change as well. */ - Q_REQUIRED_RESULT virtual Result updateMetadata(const QString &filePath, time_t modtime, qint64 size, const QByteArray &fileId) = 0; + [[nodiscard]] virtual Result updateMetadata(const QString &filePath, time_t modtime, qint64 size, const QByteArray &fileId) = 0; + + [[nodiscard]] virtual Result updatePlaceholderMarkInSync(const QString &filePath, const QByteArray &fileId) = 0; + + [[nodiscard]] virtual bool isPlaceHolderInSync(const QString &filePath) const = 0; /// Create a new dehydrated placeholder. Called from PropagateDownload. Q_REQUIRED_RESULT virtual Result createPlaceholder(const SyncFileItem &item) = 0; @@ -325,6 +329,8 @@ public: [[nodiscard]] bool isHydrating() const override { return false; } Result updateMetadata(const QString &, time_t, qint64, const QByteArray &) override { return {}; } + Result updatePlaceholderMarkInSync(const QString &filePath, const QByteArray &fileId) override {Q_UNUSED(filePath) Q_UNUSED(fileId) return {QString{}};} + [[nodiscard]] bool isPlaceHolderInSync(const QString &filePath) const override { Q_UNUSED(filePath) return true; } Result createPlaceholder(const SyncFileItem &) override { return {}; } Result dehydratePlaceholder(const SyncFileItem &) override { return {}; } Result convertToPlaceholder(const QString &, const SyncFileItem &, const QString &, const UpdateMetadataTypes) override { return ConvertToPlaceholderResult::Ok; } diff --git a/src/csync/csync.h b/src/csync/csync.h index 235f0cd72..3baf87587 100644 --- a/src/csync/csync.h +++ b/src/csync/csync.h @@ -153,6 +153,7 @@ enum SyncInstructions { CSYNC_INSTRUCTION_UPDATE_METADATA = 1 << 10, /* If the etag has been updated and need to be writen to the db, but without any propagation (UPDATE|RECONCILE) */ CSYNC_INSTRUCTION_CASE_CLASH_CONFLICT = 1 << 12, /* The file need to be downloaded because it is a case clash conflict (RECONCILE) */ + CSYNC_INSTRUCTION_UPDATE_VFS_METADATA = 1 << 13, /* vfs item metadata are out of sync and we need to tell operating system about it */ }; Q_ENUM_NS(SyncInstructions) diff --git a/src/gui/folder.cpp b/src/gui/folder.cpp index c26eb4a90..d7c5fbac5 100644 --- a/src/gui/folder.cpp +++ b/src/gui/folder.cpp @@ -621,13 +621,18 @@ void Folder::slotWatchedPathChanged(const QString &path, ChangeReason reason) spurious = true; if (auto pinState = _vfs->pinState(relativePath.toString())) { - if (*pinState == PinState::AlwaysLocal && record.isVirtualFile()) + if (*pinState == PinState::AlwaysLocal && record.isVirtualFile()) { spurious = false; - if (*pinState == PinState::OnlineOnly && record.isFile()) + } + if (*pinState == PinState::OnlineOnly && record.isFile()) { spurious = false; + } } else { spurious = false; } + if (spurious && !_vfs->isPlaceHolderInSync(path)) { + spurious = false; + } } if (spurious) { qCInfo(lcFolder) << "Ignoring spurious notification for file" << relativePath; diff --git a/src/libsync/discovery.cpp b/src/libsync/discovery.cpp index 387633c73..86450a2a5 100644 --- a/src/libsync/discovery.cpp +++ b/src/libsync/discovery.cpp @@ -1680,6 +1680,12 @@ void ProcessDirectoryJob::processFileFinalize( } } + if (_discoveryData->_syncOptions._vfs && + item->_type == CSyncEnums::ItemTypeFile && + !_discoveryData->_syncOptions._vfs->isPlaceHolderInSync(_discoveryData->_localDir + path._local)) { + item->_instruction = CSyncEnums::CSYNC_INSTRUCTION_UPDATE_VFS_METADATA; + } + if (path._original != path._target && (item->_instruction == CSYNC_INSTRUCTION_UPDATE_METADATA || item->_instruction == CSYNC_INSTRUCTION_NONE)) { ASSERT(_dirItem && _dirItem->_instruction == CSYNC_INSTRUCTION_RENAME); // This is because otherwise subitems are not updated! (ideally renaming a directory could diff --git a/src/libsync/owncloudpropagator.cpp b/src/libsync/owncloudpropagator.cpp index ac159c8d0..2148c6613 100644 --- a/src/libsync/owncloudpropagator.cpp +++ b/src/libsync/owncloudpropagator.cpp @@ -396,6 +396,8 @@ PropagateItemJob *OwncloudPropagator::createJob(const SyncFileItemPtr &item) } else { return new PropagateLocalRename(this, item); } + case CSYNC_INSTRUCTION_UPDATE_VFS_METADATA: + return new PropagateVfsUpdateMetadataJob(this, item); case CSYNC_INSTRUCTION_IGNORE: case CSYNC_INSTRUCTION_ERROR: return new PropagateIgnoreJob(this, item); @@ -1764,4 +1766,15 @@ void PropagateIgnoreJob::start() done(status, _item->_errorString, ErrorCategory::NoError); } +void PropagateVfsUpdateMetadataJob::start() +{ + const auto fullFileName = propagator()->fullLocalPath(_item->_file); + const auto result = propagator()->syncOptions()._vfs->updatePlaceholderMarkInSync(fullFileName, _item->_fileId); + emit propagator()->touchedFile(fullFileName); + if (!result) { + qCWarning(lcPropagator()) << "error when updating VFS metadata" << result.error(); + } + done(SyncFileItem::Success, {}, {}); +} + } diff --git a/src/libsync/owncloudpropagator.h b/src/libsync/owncloudpropagator.h index d8f5f8eec..1a96ece64 100644 --- a/src/libsync/owncloudpropagator.h +++ b/src/libsync/owncloudpropagator.h @@ -414,6 +414,17 @@ public: void start() override; }; +class PropagateVfsUpdateMetadataJob : public PropagateItemJob +{ + Q_OBJECT +public: + PropagateVfsUpdateMetadataJob(OwncloudPropagator *propagator, const SyncFileItemPtr &item) + : PropagateItemJob(propagator, item) + { + } + void start() override; +}; + class PropagateUploadFileCommon; class OWNCLOUDSYNC_EXPORT OwncloudPropagator : public QObject diff --git a/src/libsync/progressdispatcher.cpp b/src/libsync/progressdispatcher.cpp index 49c34c532..ec189b1c9 100644 --- a/src/libsync/progressdispatcher.cpp +++ b/src/libsync/progressdispatcher.cpp @@ -56,6 +56,8 @@ QString Progress::asResultString(const SyncFileItem &item) return QCoreApplication::translate("progress", "Error"); case CSYNC_INSTRUCTION_UPDATE_METADATA: return QCoreApplication::translate("progress", "Updated local metadata"); + case CSYNC_INSTRUCTION_UPDATE_VFS_METADATA: + return QCoreApplication::translate("progress", "Updated local virtual files metadata"); case CSYNC_INSTRUCTION_NONE: case CSYNC_INSTRUCTION_EVAL: return QCoreApplication::translate("progress", "Unknown"); @@ -87,6 +89,8 @@ QString Progress::asActionString(const SyncFileItem &item) return QCoreApplication::translate("progress", "error"); case CSYNC_INSTRUCTION_UPDATE_METADATA: return QCoreApplication::translate("progress", "updating local metadata"); + case CSYNC_INSTRUCTION_UPDATE_VFS_METADATA: + return QCoreApplication::translate("progress", "updating local virtual files metadata"); case CSYNC_INSTRUCTION_NONE: case CSYNC_INSTRUCTION_EVAL: break; diff --git a/src/libsync/vfs/cfapi/cfapiwrapper.cpp b/src/libsync/vfs/cfapi/cfapiwrapper.cpp index c928a3a80..8b1688795 100644 --- a/src/libsync/vfs/cfapi/cfapiwrapper.cpp +++ b/src/libsync/vfs/cfapi/cfapiwrapper.cpp @@ -285,7 +285,12 @@ enum class CfApiUpdateMetadataType { AllMetadata, }; -OCC::Result updatePlaceholderState(const QString &path, time_t modtime, qint64 size, const QByteArray &fileId, const QString &replacesPath, CfApiUpdateMetadataType updateType) +OCC::Result updatePlaceholderState(const QString &path, + time_t modtime, + qint64 size, + const QByteArray &fileId, + const QString &replacesPath, + CfApiUpdateMetadataType updateType) { if (updateType == CfApiUpdateMetadataType::AllMetadata && modtime <= 0) { return {QString{"Could not update metadata due to invalid modification time for %1: %2"}.arg(path).arg(modtime)}; @@ -900,3 +905,12 @@ OCC::Result OCC::CfApiWrapper::up { return updatePlaceholderState(path, {}, {}, fileId, replacesPath, CfApiUpdateMetadataType::OnlyBasicMetadata); } + +bool OCC::CfApiWrapper::isPlaceHolderInSync(const QString &filePath) +{ + if (const auto originalInfo = findPlaceholderInfo(filePath)) { + return originalInfo->InSyncState == CF_IN_SYNC_STATE_IN_SYNC; + } + + return true; +} diff --git a/src/libsync/vfs/cfapi/cfapiwrapper.h b/src/libsync/vfs/cfapi/cfapiwrapper.h index 94ebff914..8d9b2228c 100644 --- a/src/libsync/vfs/cfapi/cfapiwrapper.h +++ b/src/libsync/vfs/cfapi/cfapiwrapper.h @@ -98,6 +98,7 @@ NEXTCLOUD_CFAPI_EXPORT Result upd NEXTCLOUD_CFAPI_EXPORT Result convertToPlaceholder(const QString &path, time_t modtime, qint64 size, const QByteArray &fileId, const QString &replacesPath); NEXTCLOUD_CFAPI_EXPORT Result dehydratePlaceholder(const QString &path, time_t modtime, qint64 size, const QByteArray &fileId); NEXTCLOUD_CFAPI_EXPORT Result updatePlaceholderMarkInSync(const QString &path, const QByteArray &fileId, const QString &replacesPath = QString()); +NEXTCLOUD_CFAPI_EXPORT bool isPlaceHolderInSync(const QString &filePath); } diff --git a/src/libsync/vfs/cfapi/vfs_cfapi.cpp b/src/libsync/vfs/cfapi/vfs_cfapi.cpp index 165e558cf..a81b6606f 100644 --- a/src/libsync/vfs/cfapi/vfs_cfapi.cpp +++ b/src/libsync/vfs/cfapi/vfs_cfapi.cpp @@ -198,6 +198,16 @@ Result VfsCfApi::updateMetadata(const QString &filePath, time_t m } } +Result VfsCfApi::updatePlaceholderMarkInSync(const QString &filePath, const QByteArray &fileId) +{ + return cfapi::updatePlaceholderMarkInSync(filePath, fileId, {}); +} + +bool VfsCfApi::isPlaceHolderInSync(const QString &filePath) const +{ + return cfapi::isPlaceHolderInSync(filePath); +} + Result VfsCfApi::createPlaceholder(const SyncFileItem &item) { Q_ASSERT(params().filesystemPath.endsWith('/')); diff --git a/src/libsync/vfs/cfapi/vfs_cfapi.h b/src/libsync/vfs/cfapi/vfs_cfapi.h index 38e04e00e..7e8937fc2 100644 --- a/src/libsync/vfs/cfapi/vfs_cfapi.h +++ b/src/libsync/vfs/cfapi/vfs_cfapi.h @@ -43,6 +43,10 @@ public: Result updateMetadata(const QString &filePath, time_t modtime, qint64 size, const QByteArray &fileId) override; + Result updatePlaceholderMarkInSync(const QString &filePath, const QByteArray &fileId) override; + + [[nodiscard]] bool isPlaceHolderInSync(const QString &filePath) const override; + Result createPlaceholder(const SyncFileItem &item) override; Result dehydratePlaceholder(const SyncFileItem &item) override; Result convertToPlaceholder(const QString &filename, const SyncFileItem &item, const QString &replacesFile, UpdateMetadataTypes updateType) override; diff --git a/src/libsync/vfs/suffix/vfs_suffix.h b/src/libsync/vfs/suffix/vfs_suffix.h index 85e39b1b0..3d73d5f36 100644 --- a/src/libsync/vfs/suffix/vfs_suffix.h +++ b/src/libsync/vfs/suffix/vfs_suffix.h @@ -39,6 +39,8 @@ public: [[nodiscard]] bool isHydrating() const override; Result updateMetadata(const QString &filePath, time_t modtime, qint64 size, const QByteArray &fileId) override; + Result updatePlaceholderMarkInSync(const QString &filePath, const QByteArray &fileId) override {Q_UNUSED(filePath) Q_UNUSED(fileId) return {QString{}};} + [[nodiscard]] bool isPlaceHolderInSync(const QString &filePath) const override { Q_UNUSED(filePath) return true; } Result createPlaceholder(const SyncFileItem &item) override; Result dehydratePlaceholder(const SyncFileItem &item) override; diff --git a/src/libsync/vfs/xattr/vfs_xattr.h b/src/libsync/vfs/xattr/vfs_xattr.h index de331f4a7..ea7039d4f 100644 --- a/src/libsync/vfs/xattr/vfs_xattr.h +++ b/src/libsync/vfs/xattr/vfs_xattr.h @@ -39,6 +39,8 @@ public: [[nodiscard]] bool isHydrating() const override; Result updateMetadata(const QString &filePath, time_t modtime, qint64 size, const QByteArray &fileId) override; + Result updatePlaceholderMarkInSync(const QString &filePath, const QByteArray &fileId) override {Q_UNUSED(filePath) Q_UNUSED(fileId) return {QString{}};} + [[nodiscard]] bool isPlaceHolderInSync(const QString &filePath) const override { Q_UNUSED(filePath) return true; } Result createPlaceholder(const SyncFileItem &item) override; Result dehydratePlaceholder(const SyncFileItem &item) override;