Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ include(FeatureSummary)
find_package(ECM 6.0.0 REQUIRED NO_MODULE)

set_package_properties(ECM PROPERTIES TYPE REQUIRED DESCRIPTION "Extra CMake Modules." URL "https://projects.kde.org/projects/kdesupport/extra-cmake-modules")
feature_summary(WHAT REQUIRED_PACKAGES_NOT_FOUND FATAL_ON_MISSING_REQUIRED_PACKAGES)

set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules ${ECM_MODULE_PATH} ${CMAKE_MODULE_PATH})

Expand Down Expand Up @@ -148,12 +147,12 @@ if(BUILD_TESTING)
add_subdirectory(test)
endif()

feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES)

if(ECM_VERSION VERSION_GREATER_EQUAL 5.79)
message(STATUS "Suitable ECM ${ECM_VERSION} found, installing clang-format git hook")
include(KDEGitCommitHooks)
kde_configure_git_pre_commit_hook(CHECKS CLANG_FORMAT)
else()
message(WARNING "ECM ${ECM_VERSION} too old, cannot install clang-format git hook")
endif()

feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES INCLUDE_QUIET_PACKAGES)
16 changes: 6 additions & 10 deletions src/gui/folder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,6 @@ Folder::Folder(const FolderDefinition &definition, const AccountStatePtr &accoun
}
});

// Potentially upgrade suffix vfs to windows vfs
OC_ENFORCE(_vfs);
// Initialize the vfs plugin. Do this after the UI is running, so we can show a dialog when something goes wrong.
QTimer::singleShot(0, this, &Folder::startVfs);
}
Expand Down Expand Up @@ -186,7 +184,12 @@ bool Folder::checkLocalPath()
error = pathLengthCheck.error();
}

if (error.isEmpty()) {
// Potentially upgrade suffix vfs to windows vfs
OC_ENFORCE(_vfs);
const auto result = VfsPluginManager::instance().checkAvailability(path(), _vfs->mode());
if (!result) {
error = result.error();
} else if (error.isEmpty()) {
qCDebug(lcFolder) << u"Checked local path ok";
if (!_journal.open()) {
error = tr("Failed to open the database for »%1«.").arg(_definition.localPath());
Expand Down Expand Up @@ -518,13 +521,6 @@ void Folder::startVfs()
OC_ENFORCE(_vfs);
OC_ENFORCE(_vfs->mode() == _definition.virtualFilesMode);

const auto result = Vfs::checkAvailability(path(), _vfs->mode());
if (!result) {
_syncResult.appendErrorString(result.error());
setSyncState(SyncResult::SetupError);
return;
}

VfsSetupParams vfsParams(_accountState->account(), webDavUrl(), _definition.spaceId(), displayName(), _engine.get());
vfsParams.filesystemPath = path();
vfsParams.journal = &_journal;
Expand Down
2 changes: 1 addition & 1 deletion src/gui/folderdefinition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ FolderDefinition FolderDefinition::load(QSettings &settings)
QString vfsModeString = settings.value("virtualFilesMode").toString();

const auto vfs = Utility::isWindows() ? Vfs::Mode::WindowsCfApi : Vfs::Mode::XAttr;
if (auto result = Vfs::checkAvailability(folder.localPath(), vfs); result) {
if (auto result = VfsPluginManager::instance().checkAvailability(folder.localPath(), vfs); result) {
vfsModeString = Utility::enumToString(vfs);
} else {
qCWarning(lcFolder) << u"Failed to upgrade" << folder.localPath() << u"to" << vfs << result.error();
Expand Down
2 changes: 1 addition & 1 deletion src/gui/folderman.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -748,7 +748,7 @@ void FolderMan::slotReloadSyncOptions()

bool FolderMan::checkVfsAvailability(const QString &path, Vfs::Mode mode) const
{
return unsupportedConfiguration(path) && Vfs::checkAvailability(path, mode);
return unsupportedConfiguration(path) && VfsPluginManager::instance().checkAvailability(path, mode);
}

Folder *FolderMan::addFolderFromWizard(const AccountStatePtr &accountStatePtr, FolderDefinition &&folderDefinition, bool useVfs)
Expand Down
2 changes: 1 addition & 1 deletion src/gui/folderwizard/folderwizardlocalpath.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ bool FolderWizardLocalPath::isComplete() const
auto accountUuid = folderWizardPrivate()->accountState()->account()->uuid();
QString errorStr = FolderMan::instance()->checkPathValidityForNewFolder(localPath(), FolderMan::NewFolderType::SpacesSyncRoot, accountUuid);
if (errorStr.isEmpty()) {
if (auto result = Vfs::checkAvailability(localPath(), VfsPluginManager::instance().bestAvailableVfsMode()); !result) {
if (auto result = VfsPluginManager::instance().checkAvailability(localPath(), VfsPluginManager::instance().bestAvailableVfsMode()); !result) {
errorStr = result.error();
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/gui/newwizard/pages/accountconfiguredwizardpage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ AccountConfiguredWizardPage::AccountConfiguredWizardPage(const QString &defaultS
// the directory chooser should guarantee that the directory exists
Q_ASSERT(QDir(directory).exists());

if (auto result = Vfs::checkAvailability(directory, VfsPluginManager::instance().bestAvailableVfsMode()); !result) {
if (auto result = VfsPluginManager::instance().checkAvailability(directory, VfsPluginManager::instance().bestAvailableVfsMode()); !result) {
auto *box =
new FontIconMessageBox({Resources::FontIcon::DefaultGlyphes::Warning}, tr("Sync location not supported"), result.error(), QMessageBox::Ok, this);
box->setAttribute(Qt::WA_DeleteOnClose);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ void AccountConfiguredSetupWizardState::evaluatePage()
return;
}

if (auto result = Vfs::checkAvailability(syncTargetDir, VfsPluginManager::instance().bestAvailableVfsMode()); !result) {
if (auto result = VfsPluginManager::instance().checkAvailability(syncTargetDir, VfsPluginManager::instance().bestAvailableVfsMode()); !result) {
emitEvaluationFailedError(result.error());
return;
}
Expand Down
4 changes: 2 additions & 2 deletions src/libsync/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
find_package(LibreGraphAPI 1.0.4 REQUIRED)
set_package_properties(LibreGraphAPI PROPERTIES
URL https://github.com/owncloud/libre-graph-api-cpp-qt-client.git
DESCRIPTION "Libre Graph is a free API for cloud collaboration inspired by the MS Graph API."
TYPE OPTIONAL
DESCRIPTION "Libre Graph is a free API for cloud collaboration inspired by the MS Graph API"
TYPE REQUIRED
)

configure_file(config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)
Expand Down
3 changes: 3 additions & 0 deletions src/libsync/common/plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@

#pragma once

#include "libsync/common/result.h"
#include "libsync/opencloudsynclib.h"

#include <QObject>

namespace OCC {
Expand All @@ -28,6 +30,7 @@ class OPENCLOUD_SYNC_EXPORT PluginFactory
public:
virtual ~PluginFactory();
virtual QObject *create(QObject *parent) = 0;
virtual Result<void, QString> checkAvailability(const QString &path) const = 0;
};

template <class PluginClass>
Expand Down
3 changes: 2 additions & 1 deletion src/libsync/logger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,8 @@ void Logger::open(const QString &name)
}

if (!openSucceeded) {
std::cerr << "Failed to open the log file" << std::endl;
std::cerr << "Failed to open the log file: '" << qPrintable(_logFile.fileName()) << "' Error: '" << qPrintable(_logFile.errorString()) << "'"
<< std::endl;
return;
}
_logstream.reset(new QTextStream(&_logFile));
Expand Down
95 changes: 44 additions & 51 deletions src/libsync/vfs/vfs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,22 @@
*/

#include "vfs.h"
#include "../common/plugin.h"
#include "../common/syncjournaldb.h"
#include "common/filesystembase.h"
#include "common/version.h"

#include "libsync/common/filesystembase.h"
#include "libsync/common/plugin.h"
#include "libsync/common/syncjournaldb.h"
#include "libsync/common/version.h"
#include "libsync/filesystem.h"

#include <QApplication>
#include <QCoreApplication>
#include <QDir>
#include <QLoggingCategory>
#include <QPluginLoader>

#ifdef Q_OS_WIN
#include <qt_windows.h>
#endif

using namespace OCC;
using namespace Qt::Literals::StringLiterals;

Expand Down Expand Up @@ -73,41 +76,6 @@ QString Utility::enumToString(Vfs::Mode mode)
Q_UNREACHABLE();
}

Result<void, QString> Vfs::checkAvailability(const QString &path, Vfs::Mode mode)
{
#ifdef Q_OS_WIN
const auto canonicalPath = [path] {
QFileInfo info(path);
if (info.exists()) {
return info.canonicalFilePath();
} else {
return info.absoluteFilePath();
}
}();
const auto fileSystem = FileSystem::fileSystemForPath(canonicalPath);
if (fileSystem.startsWith("ReFS"_L1, Qt::CaseInsensitive)) {
return tr("ReFS is currently not supported.");
}
if (mode == Mode::WindowsCfApi) {
if (QDir(canonicalPath).isRoot()) {
return tr("The Virtual filesystem feature does not support a drive as sync root");
}

if (!fileSystem.startsWith("NTFS"_L1, Qt::CaseInsensitive)) {
return tr("The Virtual filesystem feature requires a NTFS file system, %1 is using %2").arg(path, fileSystem);
}
const auto type = GetDriveTypeW(reinterpret_cast<const wchar_t *>(canonicalPath.mid(0, 3).utf16()));
if (type == DRIVE_REMOTE) {
return tr("The Virtual filesystem feature is not supported on network drives");
}
}
#else
Q_UNUSED(mode);
Q_UNUSED(path);
#endif
return {};
}

void Vfs::start(const VfsSetupParams &params)
{
_setupParams = std::make_unique<VfsSetupParams>(params);
Expand Down Expand Up @@ -219,39 +187,64 @@ Vfs::Mode OCC::VfsPluginManager::bestAvailableVfsMode() const
Q_UNREACHABLE();
}

std::unique_ptr<Vfs> OCC::VfsPluginManager::createVfsFromPlugin(Vfs::Mode mode) const

std::pair<QString, PluginFactory *> OCC::VfsPluginManager::createVfsPluginFactory(Vfs::Mode mode) const
{
auto name = Utility::enumToString(mode);
if (name.isEmpty())
return nullptr;
return {};
auto pluginPath = pluginFileName(QStringLiteral("vfs"), name);

if (!isVfsPluginAvailable(mode)) {
qCCritical(lcPlugin) << u"Could not load plugin: not existant or bad metadata" << pluginPath;
return nullptr;
return {pluginPath, nullptr};
}

QPluginLoader loader(pluginPath);
auto plugin = loader.instance();
if (!plugin) {
qCCritical(lcPlugin) << u"Could not load plugin" << pluginPath << loader.errorString();
return nullptr;
return {pluginPath, nullptr};
}

auto factory = qobject_cast<PluginFactory *>(plugin);
if (!factory) {
qCCritical(lcPlugin) << u"Plugin" << loader.fileName() << u"does not implement PluginFactory";
return nullptr;
return {pluginPath, nullptr};
}
return {pluginPath, factory};
}

std::unique_ptr<Vfs> OCC::VfsPluginManager::createVfsFromPlugin(Vfs::Mode mode) const
{
const auto [pluginPath, factory] = createVfsPluginFactory(mode);
if (factory) {
auto vfs = std::unique_ptr<Vfs>(qobject_cast<Vfs *>(factory->create(nullptr)));
if (!vfs) {
qCCritical(lcPlugin) << u"Plugin" << pluginPath << u"does not create a Vfs instance";
return nullptr;
}

auto vfs = std::unique_ptr<Vfs>(qobject_cast<Vfs *>(factory->create(nullptr)));
if (!vfs) {
qCCritical(lcPlugin) << u"Plugin" << loader.fileName() << u"does not create a Vfs instance";
return nullptr;
qCInfo(lcPlugin) << u"Created VFS instance from plugin" << pluginPath;
return vfs;
}
return nullptr;
}

Result<void, QString> VfsPluginManager::checkAvailability(const QString &path, Vfs::Mode mode) const
{
const auto canonicalPath = FileSystem::canonicalPath(path);
#ifdef Q_OS_WIN
if (FileSystem::fileSystemForPath(canonicalPath).startsWith("ReFS"_L1, Qt::CaseInsensitive)) {
return QApplication::translate("VfsPluginManager", "ReFS is currently not supported.");
}
#endif
const auto [pluginPath, factory] = createVfsPluginFactory(mode);
if (factory) {
return factory->checkAvailability(canonicalPath);
}

qCInfo(lcPlugin) << u"Created VFS instance from plugin" << pluginPath;
return vfs;
return QApplication::translate("VfsPluginManager", "The Virtual filesystem %1 is not supported on this platform").arg(Utility::enumToString(mode));
}

const VfsPluginManager &VfsPluginManager::instance()
Expand Down
9 changes: 5 additions & 4 deletions src/libsync/vfs/vfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,6 @@ class OPENCLOUD_SYNC_EXPORT Vfs : public QObject

static Optional<Mode> modeFromString(const QString &str);

static Result<void, QString> checkAvailability(const QString &path, OCC::Vfs::Mode mode);

enum class AvailabilityError : uint8_t {
// Availability can't be retrieved due to db error
DbError,
Expand Down Expand Up @@ -182,7 +180,7 @@ class OPENCLOUD_SYNC_EXPORT Vfs : public QObject
* Usually backed by the db's effectivePinState() function but some vfs
* plugins will override it to retrieve the state from elsewhere.
*
* relFilePath is relative to the sync folder. Can be "" for root folder.
* relFilePath is relative to the sync folder. Can be "" for root folder.<
*
* Returns none on retrieval error.
*/
Expand Down Expand Up @@ -268,14 +266,17 @@ class OPENCLOUD_SYNC_EXPORT VfsPluginManager

/// Return the best available VFS mode.
Vfs::Mode bestAvailableVfsMode() const;

/// Create a VFS instance for the mode, returns nullptr on failure.
std::unique_ptr<Vfs> createVfsFromPlugin(Vfs::Mode mode) const;

Result<void, QString> checkAvailability(const QString &path, Vfs::Mode mode) const;

static const VfsPluginManager &instance();

protected:
VfsPluginManager() = default;
std::pair<QString, class PluginFactory *> createVfsPluginFactory(Vfs::Mode mode) const;


private:
static VfsPluginManager *_instance;
Expand Down
8 changes: 6 additions & 2 deletions src/plugins/vfs/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,14 @@ foreach(vfsPlugin ${VIRTUAL_FILE_SYSTEM_PLUGINS})

add_subdirectory(${vfsPluginPath} ${vfsPluginPathOut})

if (TARGET vfs_${vfsPluginName})
if(BUILD_TESTING AND IS_DIRECTORY "${vfsPluginPath}/test")
add_subdirectory("${vfsPluginPath}/test" "${vfsPluginName}_test")
message(STATUS "Added vfsPlugin with tests: ${vfsPluginName}")
message(STATUS "Added VFSPlugin with tests: ${vfsPluginName}")
else()
message(STATUS "Added vfsPlugin without tests: ${vfsPluginName}")
message(STATUS "Added VFSPlugin without tests: ${vfsPluginName}")
endif()
else()
message(STATUS "VFSPlugin ${vfsPluginName} was requested but not added to build")
endif()
endforeach()
16 changes: 16 additions & 0 deletions src/plugins/vfs/cfapi/vfs_cfapi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,22 @@ void VfsCfApi::startImpl(const VfsSetupParams &params)
});
}

Result<void, QString> CfApiVfsPluginFactory::checkAvailability(const QString &path) const
{
if (QDir(path).isRoot()) {
return tr("The Virtual filesystem feature does not support a drive as sync root");
}
const auto fileSystem = FileSystem::fileSystemForPath(path);
if (!fileSystem.startsWith("NTFS"_L1, Qt::CaseInsensitive)) {
return tr("The Virtual filesystem feature requires a NTFS file system, %1 is using %2").arg(path, fileSystem);
}
const auto type = GetDriveTypeW(reinterpret_cast<const wchar_t *>(path.mid(0, 3).utf16()));
if (type == DRIVE_REMOTE) {
return tr("The Virtual filesystem feature is not supported on network drives");
}
return {};
}

void VfsCfApi::stop()
{
if (_connectionKey.Internal != 0) {
Expand Down
2 changes: 2 additions & 0 deletions src/plugins/vfs/cfapi/vfs_cfapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ class CfApiVfsPluginFactory : public QObject, public DefaultPluginFactory<VfsCfA
Q_OBJECT
Q_PLUGIN_METADATA(IID "eu.opencloud.PluginFactory" FILE "libsync/vfs/vfspluginmetadata.json")
Q_INTERFACES(OCC::PluginFactory)
public:
Result<void, QString> checkAvailability(const QString &path) const override;
};

} // namespace OCC
5 changes: 5 additions & 0 deletions src/plugins/vfs/off/vfs_off.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ void VfsOff::startImpl(const VfsSetupParams &)
Q_EMIT started();
}

Result<void, QString> OffVfsPluginFactory::checkAvailability(const QString &) const
{
return {};
}

Result<Vfs::ConvertToPlaceholderResult, QString> VfsOff::updateMetadata(const SyncFileItem &item, const QString &filePath, const QString &replacesFile)
{
Q_UNUSED(replacesFile)
Expand Down
2 changes: 2 additions & 0 deletions src/plugins/vfs/off/vfs_off.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ class OffVfsPluginFactory : public QObject, public DefaultPluginFactory<VfsOff>
Q_OBJECT
Q_PLUGIN_METADATA(IID "eu.opencloud.PluginFactory" FILE "libsync/vfs/vfspluginmetadata.json")
Q_INTERFACES(OCC::PluginFactory)
public:
Result<void, QString> checkAvailability(const QString &path) const override;
};

} // namespace OCC
Loading