From: Christian Kamm Date: Mon, 26 Nov 2018 10:33:29 +0000 (+0100) Subject: vfs: Separate vfs availability from new-files-virtual X-Git-Tag: archive/raspbian/3.16.7-1_deb13u1+rpi1~1^2~12^2~21^2~468^2~376 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=305d439c415623c1b9dcad32dca1423eddcb0285;p=nextcloud-desktop.git vfs: Separate vfs availability from new-files-virtual This helps support 2.5 settings where there are virtual files in the tree but new files aren't created virtual. It's also a prelude for #6815 There's currently no way of - upgrading vfs plugins (a silent suffix->winvfs upgrade is attempted once only, when moving to master) - disabling vfs capabilities outright --- diff --git a/src/gui/accountsettings.cpp b/src/gui/accountsettings.cpp index e4dbe7256..b9b6cf79a 100644 --- a/src/gui/accountsettings.cpp +++ b/src/gui/accountsettings.cpp @@ -418,7 +418,7 @@ void AccountSettings::slotCustomContextMenuRequested(const QPoint &pos) ac = menu->addAction(tr("Edit Ignored Files")); connect(ac, &QAction::triggered, this, &AccountSettings::slotEditCurrentIgnoredFiles); - if (!_ui->_folderList->isExpanded(index) && !folder->useVirtualFiles()) { + if (!_ui->_folderList->isExpanded(index) && !folder->newFilesAreVirtual()) { ac = menu->addAction(tr("Choose what to sync")); ac->setEnabled(folderConnected); connect(ac, &QAction::triggered, this, &AccountSettings::doExpand); @@ -439,21 +439,21 @@ void AccountSettings::slotCustomContextMenuRequested(const QPoint &pos) ac = menu->addAction(tr("Remove folder sync connection")); connect(ac, &QAction::triggered, this, &AccountSettings::slotRemoveCurrentFolder); - if (Theme::instance()->showVirtualFilesOption() || folder->useVirtualFiles()) { + if (Theme::instance()->showVirtualFilesOption() || folder->newFilesAreVirtual()) { ac = menu->addAction(tr("Create virtual files for new files (Experimental)")); ac->setCheckable(true); - ac->setChecked(folder->useVirtualFiles()); + ac->setChecked(folder->newFilesAreVirtual()); connect(ac, &QAction::toggled, this, [folder, this](bool checked) { if (!checked) { if (folder) - folder->setUseVirtualFiles(false); + folder->setNewFilesAreVirtual(false); // Make sure the size is recomputed as the virtual file indicator changes _ui->_folderList->doItemsLayout(); return; } OwncloudWizard::askExperimentalVirtualFilesFeature([folder, this](bool enable) { if (enable && folder) - folder->setUseVirtualFiles(enable); + folder->setNewFilesAreVirtual(enable); // Also wipe selective sync settings bool ok = false; @@ -538,6 +538,8 @@ void AccountSettings::slotFolderWizardAccepted() if (folderWizard->property("useVirtualFiles").toBool()) { definition.virtualFilesMode = bestAvailableVfsMode(); + if (definition.virtualFilesMode != Vfs::Off) + definition.newFilesAreVirtual = true; } { diff --git a/src/gui/folder.cpp b/src/gui/folder.cpp index a5aec9b49..107e8644f 100644 --- a/src/gui/folder.cpp +++ b/src/gui/folder.cpp @@ -120,7 +120,9 @@ Folder::Folder(const FolderDefinition &definition, // Potentially upgrade suffix vfs to windows vfs ENFORCE(_vfs); - if (_definition.virtualFilesMode == Vfs::WithSuffix && _definition.upgradeVfsMode) { + if (_definition.virtualFilesMode == Vfs::WithSuffix + && _definition.upgradeVfsMode + && isVfsPluginAvailable(Vfs::WindowsCfApi)) { if (auto winvfs = createVfsFromPlugin(Vfs::WindowsCfApi)) { // Wipe the existing suffix files from fs and journal SyncEngine::wipeVirtualFiles(path(), _journal, *_vfs); @@ -619,7 +621,7 @@ void Folder::dehydrateFile(const QString &_relativepath) slotScheduleThisFolder(); } -void Folder::setUseVirtualFiles(bool enabled) +void Folder::setSupportsVirtualFiles(bool enabled) { Vfs::Mode newMode = _definition.virtualFilesMode; if (enabled && _definition.virtualFilesMode == Vfs::Off) { @@ -645,6 +647,17 @@ void Folder::setUseVirtualFiles(bool enabled) } } +bool Folder::newFilesAreVirtual() const +{ + return _definition.newFilesAreVirtual; +} + +void Folder::setNewFilesAreVirtual(bool enabled) +{ + _definition.newFilesAreVirtual = enabled; + saveToSettings(); +} + void Folder::saveToSettings() const { // Remove first to make sure we don't get duplicates @@ -659,7 +672,7 @@ void Folder::saveToSettings() const return other != this && other->cleanPath() == this->cleanPath(); }); - if (useVirtualFiles() || _saveInFoldersWithPlaceholders) { + if (supportsVirtualFiles() || _saveInFoldersWithPlaceholders) { // If virtual files are enabled or even were enabled at some point, // save the folder to a group that will not be read by older (<2.5.0) clients. // The name is from when virtual files were called placeholders. @@ -828,6 +841,7 @@ void Folder::setSyncOptions() opt._confirmExternalStorage = cfgFile.confirmExternalStorage(); opt._moveFilesToTrash = cfgFile.moveToTrash(); opt._vfs = _vfs; + opt._newFilesAreVirtual = _definition.newFilesAreVirtual; QByteArray chunkSizeEnv = qgetenv("OWNCLOUD_CHUNK_SIZE"); if (!chunkSizeEnv.isEmpty()) { @@ -1187,7 +1201,7 @@ void Folder::registerFolderWatcher() _folderWatcher->startNotificatonTest(path() + QLatin1String(".owncloudsync.log")); } -bool Folder::useVirtualFiles() const +bool Folder::supportsVirtualFiles() const { return _definition.virtualFilesMode != Vfs::Off; } @@ -1234,11 +1248,10 @@ void FolderDefinition::save(QSettings &settings, const FolderDefinition &folder) settings.setValue(QLatin1String("paused"), folder.paused); settings.setValue(QLatin1String("ignoreHiddenFiles"), folder.ignoreHiddenFiles); settings.setValue(QLatin1String(versionC), maxSettingsVersion()); + settings.setValue(QLatin1String("usePlaceholders"), folder.newFilesAreVirtual); settings.setValue(QStringLiteral("virtualFilesMode"), Vfs::modeToString(folder.virtualFilesMode)); - // to support older versions: there usePlaceholders means suffix placeholders - settings.setValue(QLatin1String("usePlaceholders"), folder.virtualFilesMode == Vfs::WithSuffix); // Happens only on Windows when the explorer integration is enabled. if (!folder.navigationPaneClsid.isNull()) @@ -1259,17 +1272,17 @@ bool FolderDefinition::load(QSettings &settings, const QString &alias, folder->paused = settings.value(QLatin1String("paused")).toBool(); folder->ignoreHiddenFiles = settings.value(QLatin1String("ignoreHiddenFiles"), QVariant(true)).toBool(); folder->navigationPaneClsid = settings.value(QLatin1String("navigationPaneClsid")).toUuid(); + folder->newFilesAreVirtual = settings.value(QLatin1String("usePlaceholders")).toBool(); - folder->virtualFilesMode = Vfs::Off; + folder->virtualFilesMode = Vfs::WithSuffix; QString vfsModeString = settings.value(QStringLiteral("virtualFilesMode")).toString(); if (!vfsModeString.isEmpty()) { if (auto mode = Vfs::modeFromString(vfsModeString)) { folder->virtualFilesMode = *mode; } else { - qCWarning(lcFolder) << "Unknown virtualFilesMode:" << vfsModeString << "assuming 'off'"; + qCWarning(lcFolder) << "Unknown virtualFilesMode:" << vfsModeString << "assuming 'suffix'"; } - } else if (settings.value(QLatin1String("usePlaceholders")).toBool()) { - folder->virtualFilesMode = Vfs::WithSuffix; + } else { folder->upgradeVfsMode = true; } diff --git a/src/gui/folder.h b/src/gui/folder.h index 41171e13e..c8d37da1a 100644 --- a/src/gui/folder.h +++ b/src/gui/folder.h @@ -63,11 +63,12 @@ public: bool ignoreHiddenFiles = false; /// Which virtual files setting the folder uses Vfs::Mode virtualFilesMode = Vfs::Off; + /// Whether new files are virtual + bool newFilesAreVirtual = false; /// The CLSID where this folder appears in registry for the Explorer navigation pane entry. QUuid navigationPaneClsid; - /// Whether this suffix-vfs should be migrated to a better - /// vfs plugin if possible + /// Whether the vfs mode shall silently be updated if possible bool upgradeVfsMode = false; /// Saves the folder definition, creating a new settings group. @@ -249,9 +250,18 @@ public: */ void registerFolderWatcher(); - /** virtual files of some kind are enabled */ - bool useVirtualFiles() const; - void setUseVirtualFiles(bool enabled); + /** virtual files of some kind are enabled + * + * This is independent of whether new files will be virtual. It's possible to have this enabled + * and never have an automatic virtual file. But when it's on, the shell context menu will allow + * users to make existing files virtual. + */ + bool supportsVirtualFiles() const; + void setSupportsVirtualFiles(bool enabled); + + /** whether new remote files shall become virtual locally */ + bool newFilesAreVirtual() const; + void setNewFilesAreVirtual(bool enabled); signals: void syncStateChange(); diff --git a/src/gui/folderstatusmodel.cpp b/src/gui/folderstatusmodel.cpp index dff804e3a..04db5adef 100644 --- a/src/gui/folderstatusmodel.cpp +++ b/src/gui/folderstatusmodel.cpp @@ -219,7 +219,7 @@ QVariant FolderStatusModel::data(const QModelIndex &index, int role) const case FolderStatusDelegate::FolderErrorMsg: return f->syncResult().errorStrings(); case FolderStatusDelegate::FolderInfoMsg: - return f->useVirtualFiles() + return f->newFilesAreVirtual() ? QStringList(tr("New files are being created as virtual files.")) : QStringList(); case FolderStatusDelegate::SyncRunning: @@ -370,7 +370,7 @@ int FolderStatusModel::rowCount(const QModelIndex &parent) const auto info = infoForIndex(parent); if (!info) return 0; - if (info->_folder && info->_folder->useVirtualFiles()) + if (info->_folder && info->_folder->newFilesAreVirtual()) return 0; if (info->hasLabel()) return 1; @@ -527,7 +527,7 @@ bool FolderStatusModel::hasChildren(const QModelIndex &parent) const if (!info) return false; - if (info->_folder && info->_folder->useVirtualFiles()) + if (info->_folder && info->_folder->newFilesAreVirtual()) return false; if (!info->_fetched) @@ -555,7 +555,7 @@ bool FolderStatusModel::canFetchMore(const QModelIndex &parent) const // Keep showing the error to the user, it will be hidden when the account reconnects return false; } - if (info->_folder && info->_folder->useVirtualFiles()) { + if (info->_folder && info->_folder->newFilesAreVirtual()) { // Selective sync is hidden in that case return false; } diff --git a/src/gui/owncloudsetupwizard.cpp b/src/gui/owncloudsetupwizard.cpp index eb6656c76..d402839d2 100644 --- a/src/gui/owncloudsetupwizard.cpp +++ b/src/gui/owncloudsetupwizard.cpp @@ -637,6 +637,8 @@ void OwncloudSetupWizard::slotAssistantFinished(int result) folderDefinition.ignoreHiddenFiles = folderMan->ignoreHiddenFiles(); if (_ocWizard->useVirtualFileSync()) { folderDefinition.virtualFilesMode = bestAvailableVfsMode(); + if (folderDefinition.virtualFilesMode != Vfs::Off) + folderDefinition.newFilesAreVirtual = true; } if (folderMan->navigationPaneHelper().showInExplorerNavigationPane()) folderDefinition.navigationPaneClsid = QUuid::createUuid(); diff --git a/src/gui/socketapi.cpp b/src/gui/socketapi.cpp index f1aa3fb54..de0923180 100644 --- a/src/gui/socketapi.cpp +++ b/src/gui/socketapi.cpp @@ -1003,10 +1003,10 @@ void SocketApi::command_GET_MENU_ITEMS(const QString &argument, OCC::SocketListe } } } - if (hasVirtualFile || (hasDir && syncFolder->useVirtualFiles())) + if (hasVirtualFile || (hasDir && syncFolder->supportsVirtualFiles())) listener->sendMessage(QLatin1String("MENU_ITEM:DOWNLOAD_VIRTUAL_FILE::") + tr("Download file(s)", "", files.size())); - if ((hasNormalFiles || hasDir) && syncFolder->useVirtualFiles()) + if ((hasNormalFiles || hasDir) && syncFolder->supportsVirtualFiles()) listener->sendMessage(QLatin1String("MENU_ITEM:REPLACE_VIRTUAL_FILE::") + tr("Replace file(s) by virtual file", "", files.size())); } diff --git a/src/libsync/discovery.cpp b/src/libsync/discovery.cpp index ad20e6ab2..db690ae8d 100644 --- a/src/libsync/discovery.cpp +++ b/src/libsync/discovery.cpp @@ -399,8 +399,8 @@ void ProcessDirectoryJob::processFileAnalyzeRemoteInfo( return; } // Turn new remote files into virtual files if the option is enabled. - auto &vfs = _discoveryData->_syncOptions._vfs; - if (!localEntry.isValid() && vfs->mode() != Vfs::Off && item->_type == ItemTypeFile) { + auto &opts = _discoveryData->_syncOptions; + if (!localEntry.isValid() && opts._vfs->mode() != Vfs::Off && opts._newFilesAreVirtual && item->_type == ItemTypeFile) { item->_type = ItemTypeVirtualFile; if (isVfsWithSuffix()) addVirtualFileSuffix(path._original); diff --git a/src/libsync/discoveryphase.cpp b/src/libsync/discoveryphase.cpp index e97af0e7e..cc0a47ee8 100644 --- a/src/libsync/discoveryphase.cpp +++ b/src/libsync/discoveryphase.cpp @@ -100,7 +100,7 @@ void DiscoveryPhase::checkSelectiveSyncNewFolder(const QString &path, RemotePerm } auto limit = _syncOptions._newBigFolderSizeLimit; - if (limit < 0 || _syncOptions._vfs->mode() != Vfs::Off) { + if (limit < 0 || (_syncOptions._vfs->mode() != Vfs::Off && _syncOptions._newFilesAreVirtual)) { // no limit, everything is allowed; return callback(false); } diff --git a/src/libsync/syncoptions.h b/src/libsync/syncoptions.h index 8de39a789..f40954b04 100644 --- a/src/libsync/syncoptions.h +++ b/src/libsync/syncoptions.h @@ -44,6 +44,9 @@ struct SyncOptions /** Create a virtual file for new files instead of downloading. May not be null */ QSharedPointer _vfs; + /** True if new files shall be virtual */ + bool _newFilesAreVirtual = false; + /** The initial un-adjusted chunk size in bytes for chunked uploads, both * for old and new chunking algorithm, which classifies the item to be chunked * diff --git a/test/testsyncvirtualfiles.cpp b/test/testsyncvirtualfiles.cpp index 040caac08..b1f5d53f1 100644 --- a/test/testsyncvirtualfiles.cpp +++ b/test/testsyncvirtualfiles.cpp @@ -63,6 +63,7 @@ SyncOptions vfsSyncOptions() { SyncOptions options; options._vfs.reset(createVfsFromPlugin(Vfs::WithSuffix).release()); + options._newFilesAreVirtual = true; return options; } @@ -418,71 +419,26 @@ private slots: QVERIFY(!dbRecord(fakeFolder, "A/a1.nextcloud").isValid()); } - // Check what happens if vfs mode is disabled - void testSwitchOfVfs() + void testNewFilesNotVirtual() { - QSKIP("Does not work with the new discovery because the way we simulate the old client does not work"); FakeFolder fakeFolder{ FileInfo() }; SyncOptions syncOptions = vfsSyncOptions(); fakeFolder.syncEngine().setSyncOptions(syncOptions); QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState()); - // Create a virtual file fakeFolder.remoteModifier().mkdir("A"); fakeFolder.remoteModifier().insert("A/a1"); QVERIFY(fakeFolder.syncOnce()); QVERIFY(fakeFolder.currentLocalState().find("A/a1.nextcloud")); - // Switch off new files becoming virtual files - syncOptions._vfs.reset(new VfsOff); + syncOptions._newFilesAreVirtual = false; fakeFolder.syncEngine().setSyncOptions(syncOptions); - // A sync that doesn't do remote discovery will wipe the placeholder, but not redownload - QVERIFY(fakeFolder.syncOnce()); - QVERIFY(!fakeFolder.currentLocalState().find("A/a1.nextcloud")); - QVERIFY(!fakeFolder.currentLocalState().find("A/a1")); - QVERIFY(fakeFolder.currentRemoteState().find("A/a1")); - QVERIFY(!fakeFolder.currentRemoteState().find("A/a1.nextcloud")); - - // But with a remote discovery the virtual files will be removed and - // the remote files will be downloaded. - fakeFolder.syncJournal().forceRemoteDiscoveryNextSync(); - QVERIFY(fakeFolder.syncOnce()); - QVERIFY(fakeFolder.currentLocalState().find("A/a1")); - QVERIFY(!fakeFolder.currentLocalState().find("A/a1.nextcloud")); - QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState()); - } - - // Older versions may leave db entries for foo and foo.nextcloud - void testOldVersion2() - { - QSKIP("Does not work with the new discovery because the way we simulate the old client does not work"); - FakeFolder fakeFolder{ FileInfo() }; - - // Sync a file - fakeFolder.remoteModifier().mkdir("A"); - fakeFolder.remoteModifier().insert("A/a1"); - QVERIFY(fakeFolder.syncOnce()); - QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState()); - - // Create the virtual file too - // In the wild, the new version would create the virtual file and the db entry - // while the old version would download the plain file. - fakeFolder.localModifier().insert("A/a1.nextcloud"); - auto &db = fakeFolder.syncJournal(); - SyncJournalFileRecord rec; - db.getFileRecord(QByteArray("A/a1"), &rec); - rec._type = ItemTypeVirtualFile; - rec._path = "A/a1.nextcloud"; - db.setFileRecord(rec); - - fakeFolder.syncEngine().setSyncOptions(vfsSyncOptions()); - - // Check that a sync removes the virtual file and its db entry + // Create a new remote file, it'll not be virtual + fakeFolder.remoteModifier().insert("A/a2"); QVERIFY(fakeFolder.syncOnce()); - QVERIFY(!fakeFolder.currentLocalState().find("A/a1.nextcloud")); - QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState()); - QVERIFY(!dbRecord(fakeFolder, "A/a1.nextcloud").isValid()); + QVERIFY(fakeFolder.currentLocalState().find("A/a2")); + QVERIFY(!fakeFolder.currentLocalState().find("A/a2.nextcloud")); } void testDownloadRecursive()