summaryrefslogtreecommitdiff
path: root/src/volumesfs.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/volumesfs.c')
-rw-r--r--src/volumesfs.c162
1 files changed, 162 insertions, 0 deletions
diff --git a/src/volumesfs.c b/src/volumesfs.c
new file mode 100644
index 0000000..dc67f77
--- /dev/null
+++ b/src/volumesfs.c
@@ -0,0 +1,162 @@
+#define FUSE_USE_VERSION 30
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <fuse.h>
+
+#include <udisks/udisks.h>
+
+static GMainLoop *main_loop;
+static GThread *sub_thread;
+static UDisksClient *client;
+
+static GHashTable *mounts;
+G_LOCK_DEFINE_STATIC(mounts);
+
+static void udisks_changed(UDisksClient *client, gpointer user_data)
+{
+ GDBusObjectManager *manager = udisks_client_get_object_manager(client);
+ GList *objs = g_dbus_object_manager_get_objects(manager);
+
+ G_LOCK(mounts);
+
+ g_debug("udisks changed");
+
+ g_hash_table_remove_all(mounts);
+
+ for (GList *l = objs; l; l = g_list_next(l)) {
+ UDisksObject *obj = UDISKS_OBJECT(l->data);
+ UDisksFilesystem *filesys = udisks_object_get_filesystem(obj);
+ UDisksBlock *block = udisks_object_get_block(obj);
+ if (block && filesys) {
+ const gchar *name = udisks_block_get_id_label(block);
+ if (!name || strlen(name) == 0) {
+ name = udisks_block_get_id_uuid(block);
+ }
+
+ const gchar * const * mpoints = udisks_filesystem_get_mount_points(filesys);
+ const gchar * mountpoint = mpoints && mpoints[0] ? mpoints[0] : NULL;
+
+ if (name && mountpoint) {
+ g_debug("%s -> %s", name, mountpoint);
+ g_hash_table_insert(mounts, g_strdup(name), g_strdup(mountpoint));
+ }
+ }
+ if (filesys) g_object_unref(filesys);
+ if (block) g_object_unref(block);
+ }
+
+ G_UNLOCK(mounts);
+
+ g_list_free_full(objs, g_object_unref);
+}
+
+static gpointer sub_thread_main(gpointer user_data)
+{
+ GError *error = NULL;
+
+ client = udisks_client_new_sync(NULL, &error);
+ if (!client) {
+ g_printerr("Could not connect to UDisks2 service: %s", error->message);
+ g_error_free(error);
+ }
+
+ g_signal_connect(client, "changed",
+ G_CALLBACK(udisks_changed), NULL);
+
+ udisks_changed(client, NULL);
+
+ g_main_loop_run(main_loop);
+}
+
+static void * volumesfs_init(struct fuse_conn_info *conn)
+{
+ main_loop = g_main_loop_new(NULL, FALSE);
+
+ mounts = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+
+ sub_thread = g_thread_new("volumesfs-sub-thread", sub_thread_main, NULL);
+}
+
+static void volumesfs_destroy(void *user_data)
+{
+ g_main_loop_quit(main_loop);
+
+ g_thread_join(sub_thread);
+
+ g_object_unref(client);
+ g_hash_table_destroy(mounts);
+ g_object_unref(main_loop);
+}
+
+static int volumesfs_getattr(const char *path, struct stat *stbuf)
+{
+ if (strcmp(path, "/") == 0) {
+ stbuf->st_mode = S_IFDIR | 0755;
+ stbuf->st_nlink = 2;
+ } else {
+ stbuf->st_mode = S_IFLNK | 0444;
+ stbuf->st_nlink = 1;
+ }
+ return 0;
+}
+
+static int volumesfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
+ off_t offset, struct fuse_file_info *fi)
+{
+ if (strcmp(path, "/") != 0) {
+ return -ENOENT;
+ }
+
+ G_LOCK(mounts);
+
+ (void) offset;
+ (void) fi;
+
+ filler(buf, ".", NULL, 0);
+ filler(buf, "..", NULL, 0);
+
+
+ GHashTableIter iter;
+ gchar *name;
+ g_hash_table_iter_init(&iter, mounts);
+ while (g_hash_table_iter_next(&iter, (void**)&name, NULL)) {
+ filler(buf, name, NULL, 0);
+ }
+
+ G_UNLOCK(mounts);
+}
+
+static int volumesfs_readlink(const char *path, char *buf, size_t size)
+{
+ if (strcmp(path, "/") == 0) {
+ return -EINVAL;
+ }
+
+ G_LOCK(mounts);
+ gchar *mountpoint = g_hash_table_lookup(mounts, path + 1);
+ G_UNLOCK(mounts);
+
+ if (mountpoint) {
+ strcpy(buf, mountpoint);
+ return 0;
+ } else {
+ return -ENOENT;
+ }
+}
+
+static struct fuse_operations volumesfs_oper = {
+ .init = volumesfs_init,
+ .destroy = volumesfs_destroy,
+ .getattr = volumesfs_getattr,
+ .readdir = volumesfs_readdir,
+ .readlink = volumesfs_readlink
+};
+
+int main(int argc, char *argv[])
+{
+ return fuse_main(argc, argv, &volumesfs_oper, NULL);
+}