Vfs: dehydration is separate action
authorChristian Kamm <mail@ckamm.de>
Fri, 1 Feb 2019 10:23:00 +0000 (11:23 +0100)
committerKevin Ottens <kevin.ottens@nextcloud.com>
Tue, 15 Dec 2020 09:58:39 +0000 (10:58 +0100)
Allows for better attribute preservation.

Also add verifyFileUnchanged() call before dehydration to avoid data
loss when discovery takes a while.

src/common/vfs.h
src/libsync/propagatedownload.cpp
src/libsync/vfs/suffix/vfs_suffix.cpp
src/libsync/vfs/suffix/vfs_suffix.h

index 5d3e993ff7e788381961420fa87f133d121f1b07..7239a911c32ab46f6483b01acfe166ebf10891de 100644 (file)
@@ -143,7 +143,14 @@ public:
     virtual bool updateMetadata(const QString &filePath, time_t modtime, quint64 size, const QByteArray &fileId, QString *error) = 0;
 
     /// Create a new dehydrated placeholder. Called from PropagateDownload.
-    virtual void createPlaceholder(const QString &syncFolder, const SyncFileItem &item) = 0;
+    virtual void createPlaceholder(const SyncFileItem &item) = 0;
+
+    /** Convert a hydrated placeholder to a dehydrated one. Called from PropagateDownlaod.
+     *
+     * This is different from delete+create because preserving some file metadata
+     * (like pin states) may be essential for some vfs plugins.
+     */
+    virtual void dehydratePlaceholder(const SyncFileItem &item) = 0;
 
     /** Convert a new file to a hydrated placeholder.
      *
@@ -247,7 +254,8 @@ public:
     bool isHydrating() const override { return false; }
 
     bool updateMetadata(const QString &, time_t, quint64, const QByteArray &, QString *) override { return true; }
-    void createPlaceholder(const QString &, const SyncFileItem &) override {}
+    void createPlaceholder(const SyncFileItem &) override {}
+    void dehydratePlaceholder(const SyncFileItem &) override {}
     void convertToPlaceholder(const QString &, const SyncFileItem &, const QString &) override {}
 
     bool isDehydratedPlaceholder(const QString &) override { return false; }
index 7ae067797d6a3f7527d043bd8e59325a692404a1..d926542b45e1cf6b169cfd37c9ed0dbf5e3315b7 100644 (file)
@@ -417,31 +417,29 @@ void PropagateDownloadFile::startAfterIsEncryptedIsChecked()
     auto &syncOptions = propagator()->syncOptions();
     auto &vfs = syncOptions._vfs;
 
-    // For virtual files just create the file and be done
+    // For virtual files just dehydrate or create the file and be done
     if (_item->_type == ItemTypeVirtualFileDehydration) {
-        _item->_type = ItemTypeVirtualFile;
-        // TODO: Could dehydrate without wiping the file entirely
-        // TODO: That would be useful as it could preserve file attributes (pins)
-        auto fn = propagator()->getFilePath(_item->_file);
-        qCDebug(lcPropagateDownload) << "dehydration: wiping base file" << fn;
-        propagator()->_journal->deleteFileRecord(_item->_file);
-        QFile::remove(fn);
-
-        if (vfs->mode() == Vfs::WithSuffix) {
-            // Normally new suffix-virtual files already have the suffix included in the path
-            // but for dehydrations that isn't the case. Adjust it here.
-            _item->_file.append(vfs->fileSuffix());
+        QString fsPath = propagator()->getFilePath(_item->_file);
+        if (!FileSystem::verifyFileUnchanged(fsPath, _item->_previousSize, _item->_previousModtime)) {
+            propagator()->_anotherSyncNeeded = true;
+            done(SyncFileItem::SoftError, tr("File has changed since discovery"));
+            return;
         }
+
+        qCDebug(lcPropagateDownload) << "dehydrating file" << _item->_file;
+        _item->_type = ItemTypeVirtualFile; // Needed?
+        vfs->dehydratePlaceholder(*_item);
+        propagator()->_journal->deleteFileRecord(_item->_file);
+        updateMetadata(false);
+        return;
     }
     if (vfs->mode() == Vfs::Off && _item->_type == ItemTypeVirtualFile) {
         qCWarning(lcPropagateDownload) << "ignored virtual file type of" << _item->_file;
         _item->_type = ItemTypeFile;
     }
     if (_item->_type == ItemTypeVirtualFile) {
-        auto fn = propagator()->getFilePath(_item->_file);
-        qCDebug(lcPropagateDownload) << "creating virtual file" << fn;
-
-        vfs->createPlaceholder(propagator()->_localDir, *_item);
+        qCDebug(lcPropagateDownload) << "creating virtual file" << _item->_file;
+        vfs->createPlaceholder(*_item);
         updateMetadata(false);
         return;
     }
index 711ee14ea2521ae07150fdf15777479316684bab..f489aa407d8f5bf181a0dd71fef48a0f6ee774f2 100644 (file)
@@ -59,10 +59,10 @@ bool VfsSuffix::updateMetadata(const QString &filePath, time_t modtime, quint64,
     return true;
 }
 
-void VfsSuffix::createPlaceholder(const QString &syncFolder, const SyncFileItem &item)
+void VfsSuffix::createPlaceholder(const SyncFileItem &item)
 {
     // The concrete shape of the placeholder is also used in isDehydratedPlaceholder() below
-    QString fn = syncFolder + item._file;
+    QString fn = _setupParams.filesystemPath + item._file;
     QFile file(fn);
     file.open(QFile::ReadWrite | QFile::Truncate);
     file.write(" ");
@@ -70,6 +70,14 @@ void VfsSuffix::createPlaceholder(const QString &syncFolder, const SyncFileItem
     FileSystem::setModTime(fn, item._modtime);
 }
 
+void VfsSuffix::dehydratePlaceholder(const SyncFileItem &item)
+{
+    QFile::remove(_setupParams.filesystemPath + item._file);
+    SyncFileItem virtualItem(item);
+    virtualItem._file.append(fileSuffix());
+    createPlaceholder(virtualItem);
+}
+
 void VfsSuffix::convertToPlaceholder(const QString &, const SyncFileItem &, const QString &)
 {
     // Nothing necessary
index 97d444ffc3d2f9048101b47926c9b21d5356a4ee..8f2c1e106994a142517ed98d2da88b14a4ed26f4 100644 (file)
@@ -39,7 +39,8 @@ public:
 
     bool updateMetadata(const QString &filePath, time_t modtime, quint64 size, const QByteArray &fileId, QString *error) override;
 
-    void createPlaceholder(const QString &syncFolder, const SyncFileItem &item) override;
+    void createPlaceholder(const SyncFileItem &item) override;
+    void dehydratePlaceholder(const SyncFileItem &item) override;
     void convertToPlaceholder(const QString &filename, const SyncFileItem &item, const QString &) override;
 
     bool isDehydratedPlaceholder(const QString &filePath) override;