#define FUSE_USE_VERSION 30 #include #include #include #include #include #include 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); return NULL; } 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); return 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_main_loop_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 { G_LOCK(mounts); gboolean mount_exists = g_hash_table_lookup(mounts, path + 1) != NULL; G_UNLOCK(mounts); if (mount_exists) { stbuf->st_mode = S_IFLNK | 0444; stbuf->st_nlink = 1; } else { return -ENOENT; } } 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); return 0; } static int volumesfs_readlink(const char *path, char *buf, size_t size) { if (strcmp(path, "/") == 0) { return -EINVAL; } if (size == 0) { return -EINVAL; } G_LOCK(mounts); gboolean mount_exists; gchar *mountpoint = g_hash_table_lookup(mounts, path + 1); if (mountpoint) { g_strlcpy(buf, mountpoint, size); mount_exists = TRUE; } else { mount_exists = FALSE; } G_UNLOCK(mounts); if (mount_exists) { 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); }