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..34b49efdf5b1b 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,13 @@ OCC::Result OCC::CfApiWrapper::up { return updatePlaceholderState(path, {}, {}, fileId, replacesPath, CfApiUpdateMetadataType::OnlyBasicMetadata); } + +bool OCC::CfApiWrapper::isPlaceHolderInSync(const QString &filePath) +{ + const auto originalInfo = findPlaceholderInfo(filePath); + if (originalInfo) { + return originalInfo->InSyncState == CF_IN_SYNC_STATE_IN_SYNC; + } else { + 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;