summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMax Kellermann <max@musicpd.org>2018-06-03 11:03:49 +0200
committerMax Kellermann <max@musicpd.org>2018-06-04 22:00:52 +0200
commitba8040d0687aeb5327948d97641be9d7608f1027 (patch)
treef1796c41242cad4f09e663697a64cfa7573a2aa9
parent5fa94d2a853d67a9a7b93f934c3a2eeae6bd2726 (diff)
storage/udisks: new plugin
Documentation will follow soon.
-rw-r--r--Makefile.am9
-rw-r--r--src/lib/dbus/UDisks2.hxx11
-rw-r--r--src/storage/Registry.cxx4
-rw-r--r--src/storage/plugins/UdisksStorage.cxx330
-rw-r--r--src/storage/plugins/UdisksStorage.hxx29
5 files changed, 383 insertions, 0 deletions
diff --git a/Makefile.am b/Makefile.am
index 1550576ec..29e1b9dd6 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -792,6 +792,7 @@ libstorage_a_SOURCES = \
src/storage/FileInfo.hxx
libstorage_a_CPPFLAGS = $(AM_CPPFLAGS) \
+ $(DBUS_CFLAGS) \
$(NFS_CFLAGS) \
$(SMBCLIENT_CFLAGS)
@@ -802,6 +803,14 @@ STORAGE_LIBS = \
$(NFS_LIBS) \
$(SMBCLIENT_LIBS)
+if ENABLE_UDISKS
+libstorage_a_SOURCES += \
+ src/storage/plugins/UdisksStorage.cxx src/storage/plugins/UdisksStorage.hxx
+STORAGE_LIBS += \
+ $(DBUS_LIBS) \
+ libodbus.a
+endif
+
if ENABLE_SMBCLIENT
libstorage_a_SOURCES += \
$(SMBCLIENT_SOURCES) \
diff --git a/src/lib/dbus/UDisks2.hxx b/src/lib/dbus/UDisks2.hxx
index a3675ad62..ed4126cdd 100644
--- a/src/lib/dbus/UDisks2.hxx
+++ b/src/lib/dbus/UDisks2.hxx
@@ -25,6 +25,7 @@
#define UDISKS2_PATH "/org/freedesktop/UDisks2"
#define UDISKS2_INTERFACE "org.freedesktop.UDisks2"
+#define UDISKS2_FILESYSTEM_INTERFACE "org.freedesktop.UDisks2.Filesystem"
namespace ODBus {
class Message;
@@ -48,6 +49,16 @@ struct Object {
(!drive_id.empty() || !block_id.empty());
}
+ template<typename I>
+ bool IsId(I &&other) const noexcept {
+ if (!drive_id.empty())
+ return drive_id == std::forward<I>(other);
+ else if (!block_id.empty())
+ return block_id == std::forward<I>(other);
+ else
+ return false;
+ }
+
std::string GetUri() const noexcept {
if (!drive_id.empty())
return "udisks://" + drive_id;
diff --git a/src/storage/Registry.cxx b/src/storage/Registry.cxx
index 85374886e..7df8dda04 100644
--- a/src/storage/Registry.cxx
+++ b/src/storage/Registry.cxx
@@ -22,6 +22,7 @@
#include "StoragePlugin.hxx"
#include "StorageInterface.hxx"
#include "plugins/LocalStorage.hxx"
+#include "plugins/UdisksStorage.hxx"
#include "plugins/SmbclientStorage.hxx"
#include "plugins/NfsStorage.hxx"
#include "plugins/CurlStorage.hxx"
@@ -34,6 +35,9 @@ const StoragePlugin *const storage_plugins[] = {
#ifdef ENABLE_SMBCLIENT
&smbclient_storage_plugin,
#endif
+#ifdef ENABLE_UDISKS
+ &udisks_storage_plugin,
+#endif
#ifdef ENABLE_NFS
&nfs_storage_plugin,
#endif
diff --git a/src/storage/plugins/UdisksStorage.cxx b/src/storage/plugins/UdisksStorage.cxx
new file mode 100644
index 000000000..2bd294e9b
--- /dev/null
+++ b/src/storage/plugins/UdisksStorage.cxx
@@ -0,0 +1,330 @@
+/*
+ * Copyright 2003-2018 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "UdisksStorage.hxx"
+#include "LocalStorage.hxx"
+#include "storage/StoragePlugin.hxx"
+#include "storage/StorageInterface.hxx"
+#include "storage/FileInfo.hxx"
+#include "lib/dbus/Glue.hxx"
+#include "lib/dbus/AsyncRequest.hxx"
+#include "lib/dbus/Message.hxx"
+#include "lib/dbus/PendingCall.hxx"
+#include "lib/dbus/AppendIter.hxx"
+#include "lib/dbus/ReadIter.hxx"
+#include "lib/dbus/ObjectManager.hxx"
+#include "lib/dbus/UDisks2.hxx"
+#include "thread/Mutex.hxx"
+#include "thread/Cond.hxx"
+#include "thread/SafeSingleton.hxx"
+#include "event/Call.hxx"
+#include "event/DeferEvent.hxx"
+#include "fs/AllocatedPath.hxx"
+#include "util/StringCompare.hxx"
+#include "util/RuntimeError.hxx"
+#include "Log.hxx"
+
+#include <stdexcept>
+
+class UdisksStorage final : public Storage {
+ const std::string base_uri;
+ const std::string id;
+
+ std::string dbus_path;
+
+ SafeSingleton<ODBus::Glue> dbus_glue;
+ ODBus::AsyncRequest list_request;
+ ODBus::AsyncRequest mount_request;
+
+ mutable Mutex mutex;
+ Cond cond;
+
+ bool want_mount = false;
+
+ std::unique_ptr<Storage> mounted_storage;
+
+ std::exception_ptr mount_error;
+
+ DeferEvent defer_mount, defer_unmount;
+
+public:
+ template<typename B, typename I>
+ UdisksStorage(EventLoop &_event_loop, B &&_base_uri, I &&_id)
+ :base_uri(std::forward<B>(_base_uri)),
+ id(std::forward<I>(_id)),
+ dbus_glue(_event_loop),
+ defer_mount(_event_loop, BIND_THIS_METHOD(DeferredMount)),
+ defer_unmount(_event_loop, BIND_THIS_METHOD(DeferredUnmount)) {}
+
+ ~UdisksStorage() noexcept override {
+ if (list_request || mount_request)
+ BlockingCall(GetEventLoop(), [this](){
+ if (list_request)
+ list_request.Cancel();
+ if (mount_request)
+ mount_request.Cancel();
+ });
+
+ try {
+ UnmountWait();
+ } catch (...) {
+ FormatError(std::current_exception(),
+ "Failed to unmount '%s'",
+ base_uri.c_str());
+ }
+ }
+
+ EventLoop &GetEventLoop() noexcept {
+ return defer_mount.GetEventLoop();
+ }
+
+ /* virtual methods from class Storage */
+ StorageFileInfo GetInfo(const char *uri_utf8, bool follow) override {
+ MountWait();
+ return mounted_storage->GetInfo(uri_utf8, follow);
+ }
+
+ std::unique_ptr<StorageDirectoryReader> OpenDirectory(const char *uri_utf8) override {
+ MountWait();
+ return mounted_storage->OpenDirectory(uri_utf8);
+ }
+
+ std::string MapUTF8(const char *uri_utf8) const noexcept override;
+
+ const char *MapToRelativeUTF8(const char *uri_utf8) const noexcept override;
+
+private:
+ void OnListReply(ODBus::Message reply) noexcept;
+
+ void MountWait();
+ void DeferredMount() noexcept;
+ void OnMountNotify(ODBus::Message reply) noexcept;
+
+ void UnmountWait();
+ void DeferredUnmount() noexcept;
+ void OnUnmountNotify(ODBus::Message reply) noexcept;
+};
+
+void
+UdisksStorage::OnListReply(ODBus::Message reply) noexcept
+{
+ using namespace UDisks2;
+
+ try {
+ ParseObjects(reply, [this](Object &&o) {
+ if (o.IsId(id))
+ dbus_path = std::move(o.path);
+ });
+
+ if (dbus_path.empty())
+ throw FormatRuntimeError("No such UDisks2 object: %s",
+ id.c_str());
+ } catch (...) {
+ const std::lock_guard<Mutex> lock(mutex);
+ mount_error = std::current_exception();
+ want_mount = false;
+ cond.broadcast();
+ return;
+ }
+
+ DeferredMount();
+}
+
+void
+UdisksStorage::MountWait()
+{
+ const std::lock_guard<Mutex> lock(mutex);
+
+ if (mounted_storage)
+ /* already mounted */
+ return;
+
+ if (!want_mount) {
+ want_mount = true;
+ defer_mount.Schedule();
+ }
+
+ while (want_mount)
+ cond.wait(mutex);
+
+ if (mount_error)
+ std::rethrow_exception(mount_error);
+}
+
+void
+UdisksStorage::DeferredMount() noexcept
+try {
+ using namespace ODBus;
+
+ auto &connection = dbus_glue->GetConnection();
+
+ if (dbus_path.empty()) {
+ auto msg = Message::NewMethodCall(UDISKS2_INTERFACE,
+ UDISKS2_PATH,
+ DBUS_OM_INTERFACE,
+ "GetManagedObjects");
+ list_request.Send(connection, *msg.Get(),
+ std::bind(&UdisksStorage::OnListReply,
+ this, std::placeholders::_1));
+ return;
+ }
+
+ auto msg = Message::NewMethodCall(UDISKS2_INTERFACE,
+ dbus_path.c_str(),
+ UDISKS2_FILESYSTEM_INTERFACE,
+ "Mount");
+ AppendMessageIter(*msg.Get()).AppendEmptyArray<DictEntryTypeTraits<StringTypeTraits, VariantTypeTraits>>();
+
+ mount_request.Send(connection, *msg.Get(),
+ std::bind(&UdisksStorage::OnMountNotify,
+ this, std::placeholders::_1));
+} catch (...) {
+ const std::lock_guard<Mutex> lock(mutex);
+ mount_error = std::current_exception();
+ want_mount = false;
+ cond.broadcast();
+}
+
+void
+UdisksStorage::OnMountNotify(ODBus::Message reply) noexcept
+try {
+ using namespace ODBus;
+ reply.CheckThrowError();
+
+ ReadMessageIter i(*reply.Get());
+ if (i.GetArgType() != DBUS_TYPE_STRING)
+ throw std::runtime_error("Malformed 'Mount' response");
+
+ const char *mount_path = i.GetString();
+
+ const std::lock_guard<Mutex> lock(mutex);
+ mounted_storage = CreateLocalStorage(Path::FromFS(mount_path));
+ mount_error = {};
+ want_mount = false;
+ cond.broadcast();
+} catch (...) {
+ const std::lock_guard<Mutex> lock(mutex);
+ mount_error = std::current_exception();
+ want_mount = false;
+ cond.broadcast();
+}
+
+void
+UdisksStorage::UnmountWait()
+{
+ const std::lock_guard<Mutex> lock(mutex);
+
+ if (!mounted_storage)
+ /* not mounted */
+ return;
+
+ defer_unmount.Schedule();
+
+ while (mounted_storage)
+ cond.wait(mutex);
+
+ if (mount_error)
+ std::rethrow_exception(mount_error);
+}
+
+void
+UdisksStorage::DeferredUnmount() noexcept
+try {
+ using namespace ODBus;
+
+ auto &connection = dbus_glue->GetConnection();
+ auto msg = Message::NewMethodCall(UDISKS2_INTERFACE,
+ dbus_path.c_str(),
+ UDISKS2_FILESYSTEM_INTERFACE,
+ "Unmount");
+ AppendMessageIter(*msg.Get()).AppendEmptyArray<DictEntryTypeTraits<StringTypeTraits, VariantTypeTraits>>();
+
+ mount_request.Send(connection, *msg.Get(),
+ std::bind(&UdisksStorage::OnUnmountNotify,
+ this, std::placeholders::_1));
+} catch (...) {
+ const std::lock_guard<Mutex> lock(mutex);
+ mount_error = std::current_exception();
+ mounted_storage.reset();
+ cond.broadcast();
+}
+
+void
+UdisksStorage::OnUnmountNotify(ODBus::Message reply) noexcept
+try {
+ using namespace ODBus;
+ reply.CheckThrowError();
+
+ const std::lock_guard<Mutex> lock(mutex);
+ mount_error = {};
+ mounted_storage.reset();
+ cond.broadcast();
+} catch (...) {
+ const std::lock_guard<Mutex> lock(mutex);
+ mount_error = std::current_exception();
+ mounted_storage.reset();
+ cond.broadcast();
+}
+
+std::string
+UdisksStorage::MapUTF8(const char *uri_utf8) const noexcept
+{
+ assert(uri_utf8 != nullptr);
+
+ if (StringIsEmpty(uri_utf8))
+ return base_uri;
+
+ return PathTraitsUTF8::Build(base_uri.c_str(), uri_utf8);
+}
+
+const char *
+UdisksStorage::MapToRelativeUTF8(const char *uri_utf8) const noexcept
+{
+ return PathTraitsUTF8::Relative(base_uri.c_str(), uri_utf8);
+}
+
+static std::unique_ptr<Storage>
+CreateUdisksStorageURI(EventLoop &event_loop, const char *base_uri)
+{
+ const char *id_begin = StringAfterPrefix(base_uri, "udisks://");
+ if (id_begin == nullptr)
+ return nullptr;
+
+ std::string id;
+
+ const char *relative_path = strchr(id_begin, '/');
+ if (relative_path == nullptr) {
+ id = id_begin;
+ relative_path = "";
+ } else {
+ id = {id_begin, relative_path};
+ ++relative_path;
+ }
+
+ // TODO: use relative_path
+
+ return std::make_unique<UdisksStorage>(event_loop, base_uri,
+ std::move(id));
+}
+
+const StoragePlugin udisks_storage_plugin = {
+ "udisks",
+ CreateUdisksStorageURI,
+};
diff --git a/src/storage/plugins/UdisksStorage.hxx b/src/storage/plugins/UdisksStorage.hxx
new file mode 100644
index 000000000..3e3562f4c
--- /dev/null
+++ b/src/storage/plugins/UdisksStorage.hxx
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2003-2018 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_STORAGE_UDISKS_HXX
+#define MPD_STORAGE_UDISKS_HXX
+
+#include "check.h"
+
+struct StoragePlugin;
+
+extern const StoragePlugin udisks_storage_plugin;
+
+#endif