Skip to content

Commit

Permalink
bring back dynamic load of VFS plugins
Browse files Browse the repository at this point in the history
Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
  • Loading branch information
mgallien committed Jun 15, 2021
1 parent 3542d17 commit f4897eb
Show file tree
Hide file tree
Showing 19 changed files with 324 additions and 56 deletions.
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")
60 changes: 42 additions & 18 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 @@ -146,7 +138,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 @@ -162,9 +154,33 @@ bool OCC::isVfsPluginAvailable(Vfs::Mode mode)
auto name = modeToPluginName(mode);
if (name.isEmpty())
return false;
auto pluginPath = pluginFileName(QStringLiteral("vfs"), name);
QPluginLoader loader(pluginPath);

if (!vfsFactoryHash()->contains(name)) {
qCDebug(lcPlugin) << "Plugin isn't registered:" << name;
auto basemeta = loader.metaData();
if (basemeta.isEmpty() || !basemeta.contains(QStringLiteral("IID"))) {
qCDebug(lcPlugin) << "Plugin doesn't exist" << loader.fileName();
return false;
}
if (basemeta[QStringLiteral("IID")].toString() != QLatin1String("org.owncloud.PluginFactory")) {
qCWarning(lcPlugin) << "Plugin has wrong IID" << loader.fileName() << basemeta[QStringLiteral("IID")];
return false;
}

auto metadata = basemeta[QStringLiteral("MetaData")].toObject();
if (metadata[QStringLiteral("type")].toString() != QLatin1String("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;
}

// 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 @@ -212,24 +228,32 @@ std::unique_ptr<Vfs> OCC::createVfsFromPlugin(Vfs::Mode mode)
auto name = modeToPluginName(mode);
if (name.isEmpty())
return nullptr;
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 @@ -119,9 +118,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
26 changes: 14 additions & 12 deletions src/libsync/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -58,24 +58,23 @@ 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
)
#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()
#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)
Expand Down Expand Up @@ -159,3 +158,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")

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()
43 changes: 43 additions & 0 deletions src/libsync/vfs/cfapi/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
if (WIN32)
add_library("${synclib_NAME}_vfs_cfapi" SHARED
cfapiwrapper.cpp
hydrationjob.cpp
vfs_cfapi.cpp
)

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

target_compile_definitions("${synclib_NAME}_vfs_cfapi"
PUBLIC -D_WIN32_WINNT=_WIN32_WINNT_WIN10
)

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

if(APPLE)
# for being loadable when client run from build dir
set(vfs_buildoutputdir "${BIN_OUTPUT_DIRECTORY}/${OWNCLOUD_OSX_BUNDLE}/Contents/PlugIns/")
set_target_properties("${synclib_NAME}_vfs_cfapi"
PROPERTIES
LIBRARY_OUTPUT_DIRECTORY ${vfs_buildoutputdir}
RUNTIME_OUTPUT_DIRECTORY ${vfs_buildoutputdir}
)
# For being lodable when client run from install dir (after make macdeployqt)
set(vfs_installdir "${LIB_INSTALL_DIR}/../PlugIns")
else()
set(vfs_installdir "${PLUGINDIR}")
endif()

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

0 comments on commit f4897eb

Please sign in to comment.