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 060b5ec585fb2..b3c5df9d08262 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 235f0cd729afb..3baf87587f46c 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 c26eb4a90b9f4..d7c5fbac54bc9 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 387633c73f54c..86450a2a53e41 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 ac159c8d02c98..2148c66136755 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 d8f5f8eec022f..1a96ece644c5d 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 49c34c53211d7..ec189b1c97032 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 c928a3a809d23..8b16887956944 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 94ebff914cbe6..8d9b2228ce935 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 165e558cf0fe7..a81b6606f69a3 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 38e04e00ef446..7e8937fc2e4c5 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 85e39b1b00d92..3d73d5f365468 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 de331f4a7a877..ea7039d4f4485 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;