Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bring back dynamic load of VFS plugins #3437

Merged
merged 2 commits into from
Jul 8, 2021
Merged
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
4 changes: 0 additions & 4 deletions admin/win/msi/Nextcloud.wxs
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,6 @@
<Property Id="INSTALLDIR">
<RegistrySearch Id="RegistryInstallDir" Type="raw" Root="HKLM" Key="Software\$(var.AppVendor)\$(var.AppName)" Win64="no" />
</Property>
<Property Id="WINDOWSRELEASEID">
<RegistrySearch Id="RegistryWindowsReleaseId" Type="raw" Root="HKLM" Key="Software\Microsoft\Windows NT\CurrentVersion" Name="ReleaseId"/>
</Property>

<!-- Detect legacy NSIS installation -->
<Property Id="NSIS_UNINSTALLEXE">
Expand Down Expand Up @@ -217,6 +214,5 @@
<Condition Level="0">(NO_DESKTOP_SHORTCUT=1)</Condition>
</Feature>
</Feature>
<Condition Message="This application only runs on Windows 10, version 1709 or higher!">(VersionNT>=603 AND WINDOWSRELEASEID>=1709)</Condition>
mgallien marked this conversation as resolved.
Show resolved Hide resolved
</Product>
</Wix>
2 changes: 2 additions & 0 deletions src/common/common.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ set(common_SOURCES
${CMAKE_CURRENT_LIST_DIR}/remotepermissions.cpp
${CMAKE_CURRENT_LIST_DIR}/vfs.cpp
${CMAKE_CURRENT_LIST_DIR}/pinstate.cpp
${CMAKE_CURRENT_LIST_DIR}/plugin.cpp
${CMAKE_CURRENT_LIST_DIR}/syncfilestatus.cpp
)

configure_file(${CMAKE_CURRENT_LIST_DIR}/vfspluginmetadata.json.in ${CMAKE_CURRENT_BINARY_DIR}/vfspluginmetadata.json)
33 changes: 33 additions & 0 deletions src/common/plugin.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright (C) by Dominik Schmidt <dschmidt@owncloud.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/

#include "plugin.h"

#include "config.h"

namespace OCC {

PluginFactory::~PluginFactory() = default;

QString pluginFileName(const QString &type, const QString &name)
{
return QStringLiteral("%1sync_%2_%3")
.arg(QStringLiteral(APPLICATION_EXECUTABLE), type, name);
}

}
48 changes: 48 additions & 0 deletions src/common/plugin.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright (C) by Dominik Schmidt <dschmidt@owncloud.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/

#pragma once

#include "ocsynclib.h"
#include <QObject>

namespace OCC {

class OCSYNC_EXPORT PluginFactory
{
public:
virtual ~PluginFactory();
virtual QObject* create(QObject* parent) = 0;
};

template<class PluginClass>
class DefaultPluginFactory : public PluginFactory
{
public:
QObject* create(QObject *parent) override
{
return new PluginClass(parent);
}
};

/// Return the expected name of a plugin, for use with QPluginLoader
QString pluginFileName(const QString &type, const QString &name);

}

Q_DECLARE_INTERFACE(OCC::PluginFactory, "org.owncloud.PluginFactory")
74 changes: 52 additions & 22 deletions src/common/vfs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
*/

#include "vfs.h"
#include "plugin.h"
#include "version.h"
#include "syncjournaldb.h"

Expand All @@ -27,15 +28,6 @@

using namespace OCC;

using MetaObjectHash = QHash<QString, Vfs::Factory>;
Q_GLOBAL_STATIC(MetaObjectHash, vfsFactoryHash);

void Vfs::registerPlugin(const QString &name, Factory factory)
{
Q_ASSERT(!vfsFactoryHash()->contains(name));
vfsFactoryHash()->insert(name, factory);
}

Vfs::Vfs(QObject* parent)
: QObject(parent)
{
Expand Down Expand Up @@ -83,7 +75,8 @@ Result<bool, QString> Vfs::checkAvailability(const QString &path)
}
}
#else
Q_UNUSED(path);
Q_UNUSED(mode)
Q_UNUSED(path)
#endif
return true;
}
Expand Down Expand Up @@ -146,7 +139,7 @@ static QString modeToPluginName(Vfs::Mode mode)
if (mode == Vfs::WithSuffix)
return QStringLiteral("suffix");
if (mode == Vfs::WindowsCfApi)
return QStringLiteral("win");
return QStringLiteral("cfapi");
if (mode == Vfs::XAttr)
return QStringLiteral("xattr");
return QString();
Expand All @@ -157,14 +150,41 @@ Q_LOGGING_CATEGORY(lcPlugin, "plugins", QtInfoMsg)
bool OCC::isVfsPluginAvailable(Vfs::Mode mode)
{
// TODO: cache plugins available?
if (mode == Vfs::Off)
if (mode == Vfs::Off) {
return true;
}

auto name = modeToPluginName(mode);
if (name.isEmpty())
if (name.isEmpty()) {
return false;
}

QPluginLoader loader(pluginFileName(QStringLiteral("vfs"), name));

const auto baseMetaData = loader.metaData();
if (baseMetaData.isEmpty() || !baseMetaData.contains(QStringLiteral("IID"))) {
qCDebug(lcPlugin) << "Plugin doesn't exist" << loader.fileName();
return false;
}
if (baseMetaData[QStringLiteral("IID")].toString() != QStringLiteral("org.owncloud.PluginFactory")) {
qCWarning(lcPlugin) << "Plugin has wrong IID" << loader.fileName() << baseMetaData[QStringLiteral("IID")];
return false;
}

const auto metadata = baseMetaData[QStringLiteral("MetaData")].toObject();
if (metadata[QStringLiteral("type")].toString() != QStringLiteral("vfs")) {
qCWarning(lcPlugin) << "Plugin has wrong type" << loader.fileName() << metadata[QStringLiteral("type")];
return false;
}
if (metadata[QStringLiteral("version")].toString() != QStringLiteral(MIRALL_VERSION_STRING)) {
qCWarning(lcPlugin) << "Plugin has wrong version" << loader.fileName() << metadata[QStringLiteral("version")];
return false;
}

if (!vfsFactoryHash()->contains(name)) {
qCDebug(lcPlugin) << "Plugin isn't registered:" << name;
// Attempting to load the plugin is essential as it could have dependencies that
// can't be resolved and thus not be available after all.
if (!loader.load()) {
qCWarning(lcPlugin) << "Plugin failed to load:" << loader.errorString();
return false;
}

Expand Down Expand Up @@ -210,26 +230,36 @@ std::unique_ptr<Vfs> OCC::createVfsFromPlugin(Vfs::Mode mode)
return std::unique_ptr<Vfs>(new VfsOff);

auto name = modeToPluginName(mode);
if (name.isEmpty())
if (name.isEmpty()) {
return nullptr;
}

const auto pluginPath = pluginFileName(QStringLiteral("vfs"), name);

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

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

const auto factory = vfsFactoryHash()->value(name);
auto factory = qobject_cast<PluginFactory *>(plugin);
if (!factory) {
qCCritical(lcPlugin) << "Could not load plugin" << name;
qCCritical(lcPlugin) << "Plugin" << loader.fileName() << "does not implement PluginFactory";
return nullptr;
}

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

qCInfo(lcPlugin) << "Created VFS instance for:" << name;
qCInfo(lcPlugin) << "Created VFS instance from plugin" << pluginPath;
return vfs;
}
4 changes: 0 additions & 4 deletions src/common/vfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
#pragma once

#include <QObject>
#include <QCoreApplication>
#include <QScopedPointer>
#include <QSharedPointer>

Expand Down Expand Up @@ -126,9 +125,6 @@ class OCSYNC_EXPORT Vfs : public QObject
using AvailabilityResult = Result<VfsItemAvailability, AvailabilityError>;

public:
using Factory = Vfs* (*)();
static void registerPlugin(const QString &name, Factory factory);

explicit Vfs(QObject* parent = nullptr);
virtual ~Vfs();

Expand Down
21 changes: 3 additions & 18 deletions src/libsync/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -58,26 +58,8 @@ set(libsync_SRCS
creds/abstractcredentials.cpp
creds/credentialscommon.cpp
creds/keychainchunk.cpp
vfs/suffix/vfs_suffix.cpp
)

if (WIN32)
set(libsync_SRCS ${libsync_SRCS}
vfs/cfapi/cfapiwrapper.cpp
vfs/cfapi/hydrationjob.cpp
vfs/cfapi/vfs_cfapi.cpp
)
add_definitions(-D_WIN32_WINNT=_WIN32_WINNT_WIN10)
list(APPEND OS_SPECIFIC_LINK_LIBRARIES cldapi)
elseif(LINUX) # elseif(LINUX OR APPLE)
set(libsync_SRCS ${libsync_SRCS} vfs/xattr/vfs_xattr.cpp)
if (APPLE)
set(libsync_SRCS ${libsync_SRCS} vfs/xattr/xattrwrapper_mac.cpp)
else()
set(libsync_SRCS ${libsync_SRCS} vfs/xattr/xattrwrapper_linux.cpp)
endif()
endif()

if(TOKEN_AUTH_ONLY)
set (libsync_SRCS ${libsync_SRCS} creds/tokencredentials.cpp)
else()
Expand Down Expand Up @@ -159,3 +141,6 @@ if(NOT BUILD_OWNCLOUD_OSX_BUNDLE)
else()
install(TARGETS ${synclib_NAME} DESTINATION ${OWNCLOUD_OSX_BUNDLE}/Contents/MacOS)
endif()


add_subdirectory(vfs)
3 changes: 2 additions & 1 deletion src/libsync/propagatedownload.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
*/
#pragma once

#include "owncloudlib.h"
#include "owncloudpropagator.h"
#include "networkjobs.h"
#include "clientsideencryption.h"
Expand All @@ -27,7 +28,7 @@ class PropagateDownloadEncrypted;
* @brief The GETFileJob class
* @ingroup libsync
*/
class GETFileJob : public AbstractNetworkJob
class OWNCLOUDSYNC_EXPORT GETFileJob : public AbstractNetworkJob
{
Q_OBJECT
QIODevice *_device;
Expand Down
30 changes: 30 additions & 0 deletions src/libsync/vfs/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Globbing for plugins has a problem with in-source builds
# that create directories for the build.
#file(GLOB VIRTUAL_FILE_SYSTEM_PLUGINS RELATIVE ${CMAKE_CURRENT_LIST_DIR} "*")

list(APPEND VIRTUAL_FILE_SYSTEM_PLUGINS "suffix" "cfapi" "xattr")

message("list of plugins ${VIRTUAL_FILE_SYSTEM_PLUGINS}")

foreach(vfsPlugin ${VIRTUAL_FILE_SYSTEM_PLUGINS})
set(vfsPluginPath ${vfsPlugin})
get_filename_component(vfsPluginName ${vfsPlugin} NAME)
message("discovery ${vfsPlugin}")
if (NOT IS_ABSOLUTE ${vfsPlugin})
set(vfsPluginPath "${CMAKE_CURRENT_SOURCE_DIR}/${vfsPlugin}")
message("${vfsPluginPath}")
endif()
if(NOT IS_DIRECTORY ${vfsPluginPath})
continue()
endif()

message("${vfsPluginPath} ${vfsPluginName}")
add_subdirectory(${vfsPluginPath} ${vfsPluginName})

if(UNIT_TESTING AND IS_DIRECTORY "${vfsPluginPath}/test")
add_subdirectory("${vfsPluginPath}/test" "${vfsPluginName}_test")
message(STATUS "Added vfsPlugin with tests: ${vfsPluginName}")
else()
message(STATUS "Added vfsPlugin without tests: ${vfsPluginName}")
endif()
endforeach()
36 changes: 36 additions & 0 deletions src/libsync/vfs/cfapi/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
if (WIN32)
add_definitions(-D_WIN32_WINNT=_WIN32_WINNT_WIN10)

add_library("${synclib_NAME}_vfs_cfapi" SHARED
cfapiwrapper.cpp
hydrationjob.cpp
vfs_cfapi.cpp
)

target_link_libraries("${synclib_NAME}_vfs_cfapi"
"${synclib_NAME}"
cldapi
)

set_target_properties("${synclib_NAME}_vfs_cfapi" PROPERTIES
LIBRARY_OUTPUT_DIRECTORY ${BIN_OUTPUT_DIRECTORY}
RUNTIME_OUTPUT_DIRECTORY ${BIN_OUTPUT_DIRECTORY}
PREFIX ""
AUTOMOC TRUE
)

target_include_directories("${synclib_NAME}_vfs_cfapi" BEFORE PUBLIC ${CMAKE_CURRENT_BINARY_DIR} INTERFACE ${CMAKE_BINARY_DIR})

set(vfs_installdir "${PLUGINDIR}")

GENERATE_EXPORT_HEADER( "${synclib_NAME}_vfs_cfapi"
BASE_NAME "${synclib_NAME}_vfs_cfapi"
EXPORT_MACRO_NAME NEXTCLOUD_CFAPI_EXPORT
EXPORT_FILE_NAME cfapiexport.h
)

INSTALL(TARGETS "${synclib_NAME}_vfs_cfapi"
LIBRARY DESTINATION "${vfs_installdir}"
RUNTIME DESTINATION "${vfs_installdir}"
)
endif()
2 changes: 2 additions & 0 deletions src/libsync/vfs/cfapi/cfapiwrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
#include "hydrationjob.h"
#include "vfs_cfapi.h"

#include <QCoreApplication>
#include <QEventLoop>
#include <QDir>
#include <QFileInfo>
#include <QLocalSocket>
Expand Down
Loading