From 1522d01d5b21a808e190c0b6d8c895b1a075ca3a Mon Sep 17 00:00:00 2001 From: Matthieu Gallien Date: Thu, 11 Apr 2024 18:39:20 +0200 Subject: [PATCH] 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 --- src/common/vfs.h | 8 +++++++- src/csync/csync.h | 1 + src/gui/folder.cpp | 9 +++++++-- src/libsync/discovery.cpp | 6 ++++++ src/libsync/owncloudpropagator.cpp | 13 +++++++++++++ src/libsync/owncloudpropagator.h | 11 +++++++++++ src/libsync/progressdispatcher.cpp | 4 ++++ src/libsync/vfs/cfapi/cfapiwrapper.cpp | 16 +++++++++++++++- src/libsync/vfs/cfapi/cfapiwrapper.h | 1 + src/libsync/vfs/cfapi/vfs_cfapi.cpp | 10 ++++++++++ src/libsync/vfs/cfapi/vfs_cfapi.h | 4 ++++ src/libsync/vfs/suffix/vfs_suffix.h | 2 ++ src/libsync/vfs/xattr/vfs_xattr.h | 2 ++ 13 files changed, 83 insertions(+), 4 deletions(-) diff --git a/src/common/vfs.h b/src/common/vfs.h index 060b5ec585fb..b3c5df9d0826 100644 --- a/src/common/vfs.h +++ b/src/common/vfs.h @@ -187,7 +187,11 @@ class OCSYNC_EXPORT Vfs : public QObject * 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 @@ class OCSYNC_EXPORT VfsOff : public Vfs [[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 235f0cd729af..3baf87587f46 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 c26eb4a90b9f..d7c5fbac54bc 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 387633c73f54..86450a2a53e4 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 ac159c8d02c9..2148c6613675 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 d8f5f8eec022..1a96ece644c5 100644 --- a/src/libsync/owncloudpropagator.h +++ b/src/libsync/owncloudpropagator.h @@ -414,6 +414,17 @@ class PropagateIgnoreJob : public PropagateItemJob 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 49c34c53211d..ec189b1c9703 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 c928a3a809d2..8b1688795694 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 94ebff914cbe..8d9b2228ce93 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 165e558cf0fe..a81b6606f69a 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 38e04e00ef44..7e8937fc2e4c 100644 --- a/src/libsync/vfs/cfapi/vfs_cfapi.h +++ b/src/libsync/vfs/cfapi/vfs_cfapi.h @@ -43,6 +43,10 @@ class VfsCfApi : public Vfs 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 85e39b1b00d9..3d73d5f36546 100644 --- a/src/libsync/vfs/suffix/vfs_suffix.h +++ b/src/libsync/vfs/suffix/vfs_suffix.h @@ -39,6 +39,8 @@ class VfsSuffix : public Vfs [[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 de331f4a7a87..ea7039d4f448 100644 --- a/src/libsync/vfs/xattr/vfs_xattr.h +++ b/src/libsync/vfs/xattr/vfs_xattr.h @@ -39,6 +39,8 @@ class VfsXAttr : public Vfs [[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;