diff options
author | Brian J. Tarricone <bjt23@cornell.edu> | 2008-12-25 03:11:54 -0800 |
---|---|---|
committer | Brian J. Tarricone <brian@tarricone.org> | 2009-08-13 00:47:34 -0700 |
commit | a9eafef43216cd31d26c0116f1af806ecc076acc (patch) | |
tree | afc38da4352d257853f433f017a0fe9fc683f982 | |
parent | f7effe6907669d8ed9d1ee4fa2c09bc451df66cd (diff) | |
download | xfconf-fuse-module.tar.gz |
xfconf-fuse: WIPfuse-module
-rw-r--r-- | Makefile.am | 1 | ||||
-rw-r--r-- | configure.ac.in | 5 | ||||
-rw-r--r-- | xfconf-fuse/Makefile.am | 24 | ||||
-rw-r--r-- | xfconf-fuse/xfconf-fuse.c | 454 |
4 files changed, 484 insertions, 0 deletions
diff --git a/Makefile.am b/Makefile.am index 63e4c94..8dd13ea 100644 --- a/Makefile.am +++ b/Makefile.am @@ -4,6 +4,7 @@ SUBDIRS = \ xfconfd \ xfconf-query \ xfconf-perl \ + xfconf-fuse \ po \ docs \ tests diff --git a/configure.ac.in b/configure.ac.in index 0f67470..a3f00c3 100644 --- a/configure.ac.in +++ b/configure.ac.in @@ -67,6 +67,10 @@ XDT_CHECK_PACKAGE([LIBXFCE4UTIL], [libxfce4util-1.0], [4.6.0]) XDT_CHECK_PACKAGE([DBUS], [dbus-1], [1.0.0]) XDT_CHECK_PACKAGE([DBUS_GLIB], [dbus-glib-1], [0.72]) +XDT_CHECK_OPTIONAL_PACKAGE([FUSE], [fuse], [2.5.0], [fuse], + [Whether to enable the FUSE module], [yes]) +AM_CONDITIONAL([BUILD_FUSE_MODULE], [test "x$FUSE_FOUND" = "xyes"]) + dnl see if we can build the perl bindings AC_ARG_ENABLE([perl-bindings], [AC_HELP_STRING([--disable-perl-bindings], @@ -227,6 +231,7 @@ tests/reset-properties/Makefile tests/property-changed-signal/Makefile xfconf/Makefile xfconf/libxfconf-0.pc +xfconf-fuse/Makefile xfconf-perl/Makefile.PL xfconf-perl/Makefile xfconf-perl/Xfconf.pm diff --git a/xfconf-fuse/Makefile.am b/xfconf-fuse/Makefile.am new file mode 100644 index 0000000..49a5c86 --- /dev/null +++ b/xfconf-fuse/Makefile.am @@ -0,0 +1,24 @@ +if BUILD_FUSE_MODULE + +bin_PROGRAMS = xfconf-fuse + +xfconf_fuse_SOURCES = \ + xfconf-fuse.c + +xfconf_fuse_CFLAGS = \ + -I$(top_srcdir) \ + $(GLIB_CFLAGS) \ + $(FUSE_CFLAGS) + +xfconf_fuse_DEPENDENCIES = \ + $(top_builddir)/xfconf/libxfconf-0.la + +xfconf_fuse_LDADD = \ + $(top_builddir)/xfconf/libxfconf-0.la \ + $(GLIB_LIBS) \ + $(FUSE_LIBS) + +endif + +EXTRA_DIST = \ + xfconf-fuse.c diff --git a/xfconf-fuse/xfconf-fuse.c b/xfconf-fuse/xfconf-fuse.c new file mode 100644 index 0000000..69500f2 --- /dev/null +++ b/xfconf-fuse/xfconf-fuse.c @@ -0,0 +1,454 @@ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif + +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif + +#ifdef HAVE_STRING_H +#include <string.h> +#endif + +#define FUSE_USE_VERSION 25 +#include <fuse.h> + +#include "xfconf/xfconf.h" + +#define PTR_TO_UINT64(ptr) ( (guint64)(ptr) ) +#define UINT64_TO_PTR(i) ( (gpointer)(i) ) + +static GHashTable *channel_cache = NULL; /* (gchar *) -> (GHashTable *) */ +static GHashTable *empty_dirs = NULL; + +typedef struct +{ + gulong open_count; + gchar *channel_name; + gchar *property_name; + gboolean is_branch; +} XfconfFuseHandle; + +typedef struct +{ + gchar *property_base; + void *buf; + fuse_fill_dir_t filler; + GHashTable *done; +} DirFillerData; + + +#if 0 +static guint +count_slashes(const gchar *property_name) +{ + gchar *p = (gchar *)property_name; + gint n = 2; + + if(!property_name || (property_name[0] == '/' && !property_name[1])) + return 1; + + while((p = strchr(p+1, '/'))) + ++n; + + return n; +} +#endif + +static gboolean +xfconf_fuse_decompose_path(const gchar *path, + gchar **channel_name, + gchar **property_name, + gboolean *is_branch) +{ + gchar *p, *q; + + if(G_UNLIKELY(path[0] == '/' && !path[1])) { + *channel_name = NULL; + *property_name = NULL; + *is_branch = TRUE; + } else { + p = strstr(path+1, "/"); + if(p) { + *channel_name = g_strndup(path+1, p-(path+1)); + q = p + strlen(p) - 5; + if(!strcmp(q, ".prop")) { + *property_name = g_strndup(p, q-p); + *is_branch = FALSE; + } else { + *property_name = g_strdup(p); + *is_branch = TRUE; + } + } else { + *channel_name = g_strdup(path+1); + *property_name = NULL; + *is_branch = TRUE; + } + } + + return TRUE; +} + +static int +xfconf_fuse_getattr_internal(const gchar *channel_name, + const gchar *property_name, + gboolean is_branch, + struct stat *statbuf) +{ + int ret = 0; + XfconfChannel *channel = NULL; + + if(is_branch) { + if(property_name) { + channel = xfconf_channel_get(channel_name); + GHashTable *properties = xfconf_channel_get_properties(channel, + property_name); + + if(!properties) + ret = -EIO; + else if(g_hash_table_size(properties) == 0 + || (g_hash_table_size(properties) == 1 + && g_hash_table_lookup(properties, property_name))) + { + ret = -ENOENT; + } + + g_hash_table_destroy(properties); + } else if(channel_name) { + gchar **channels = xfconf_list_channels(); + gint i; + + if(!channels) + ret = -EIO; + else { + for(i = 0; channels[i]; ++i) { + if(!strcmp(channels[i], channel_name)) + break; + } + + if(!channels[i]) + ret = -ENOENT; + + g_strfreev(channels); + } + } else { + /* root element */ + } + } else { + /* both channel_name and property_name must be valid here */ + channel = xfconf_channel_get(channel_name); + if(!xfconf_channel_has_property(channel, property_name)) + ret = -ENOENT; + } + + if(ret == 0) { + statbuf->st_uid = getuid(); + statbuf->st_gid = getgid(); + statbuf->st_mode = S_IRUSR; + if(is_branch) { + statbuf->st_mode |= S_IFDIR; + statbuf->st_nlink = 2; + } else { + statbuf->st_mode |= S_IFREG; + statbuf->st_nlink = 1; + } + + if(channel_name) { + if(!channel) + channel = xfconf_channel_get(channel_name); + if(!xfconf_channel_is_property_locked(channel, property_name + ? property_name + : "/")) + { + statbuf->st_mode |= S_IWUSR; + } + } else { + /* root dir */ + statbuf->st_mode |= S_IWUSR; + } + } + + return ret; +} + +static int +xfconf_fuse_getattr(const char *path, + struct stat *statbuf) +{ + int ret = 0; + gchar *channel_name = NULL, *property_name = NULL; + gboolean is_branch = FALSE; + + if(!xfconf_fuse_decompose_path(path, &channel_name, &property_name, &is_branch)) + return -ENOENT; + + ret = xfconf_fuse_getattr_internal(channel_name, property_name, + is_branch, statbuf); + + g_free(channel_name); + g_free(property_name); + + return ret; +} + +static int +xfconf_fuse_opendir(const char *path, + struct fuse_file_info *finfo) +{ + return 0; +} + +static void +fill_dir_from_hashtable(gpointer key, + gpointer value, + gpointer user_data) +{ + DirFillerData *dfdata = user_data; + gchar *start, *end, buf[PATH_MAX]; + + start = (gchar *)key + strlen(dfdata->property_base); + if(start[0] == '/') + ++start; + + if((end = strchr(start+1, '/'))) { + g_strlcpy(buf, start, end-start+1 > sizeof(buf) ? sizeof(buf) : end-start+1); + if(!g_hash_table_lookup(dfdata->done, buf)) { + dfdata->filler(dfdata->buf, buf, NULL, 0); + g_hash_table_insert(dfdata->done, g_strdup(buf), (gpointer)1); + } + } else { + g_strlcpy(buf, start, sizeof(buf)); + g_strlcat(buf, ".prop", sizeof(buf)); + dfdata->filler(dfdata->buf, buf, NULL, 0); + } +} + +static int +xfconf_fuse_readdir(const char *path, + void *buf, + fuse_fill_dir_t filler, + off_t ofs, + struct fuse_file_info *finfo) +{ + gint ret = 0; + gchar *channel_name = NULL, *property_name = NULL; + gboolean is_branch = FALSE; + + if(!xfconf_fuse_decompose_path(path, &channel_name, &property_name, &is_branch)) + return -ENOENT; + + if(!is_branch) { + g_free(channel_name); + g_free(property_name); + return -ENOTDIR; + } + + filler(buf, ".", NULL, 0); + filler(buf, "..", NULL, 0); + + if(!channel_name) { + gchar **channels = xfconf_list_channels(); + + if(!channels) + ret = -EIO; + else { + gint i; + + for(i = 0; channels[i]; ++i) + filler(buf, channels[i], NULL, 0); + g_strfreev(channels); + } + } else { + XfconfChannel *channel = xfconf_channel_get(channel_name); + GHashTable *properties = xfconf_channel_get_properties(channel, + property_name); + if(!properties) + ret = -EIO; + else { + DirFillerData dfdata; + + dfdata.property_base = property_name ? property_name : "/"; + dfdata.buf = buf; + dfdata.filler = filler; + dfdata.done = g_hash_table_new_full(g_str_hash, g_str_equal, + (GDestroyNotify)g_free, + NULL); + + g_hash_table_foreach(properties, fill_dir_from_hashtable, &dfdata); + + g_hash_table_destroy(dfdata.done); + g_hash_table_destroy(properties); + } + } + + g_free(channel_name); + g_free(property_name); + + return ret; +} + +static int +xfconf_fuse_open(const char *path, + struct fuse_file_info *finfo) +{ + gint ret; + struct stat statbuf; + gchar *channel_name = NULL, *property_name = NULL; + gboolean is_branch = FALSE; + XfconfFuseHandle *xfh; + + if(finfo->fh) { + /* FIXME: need to validate new permissions */ + xfh = UINT64_TO_PTR(finfo->fh); + xfh->open_count++; + return 0; + } + + if(finfo->flags & O_APPEND) + return -EACCES; + + if(!xfconf_fuse_decompose_path(path, &channel_name, &property_name, &is_branch)) + return -ENOENT; + + if(is_branch && (finfo->flags & (O_RDWR|O_WRONLY))) { + ret = -EISDIR; + goto out_err; + } + + ret = xfconf_fuse_getattr_internal(channel_name, property_name, + is_branch, &statbuf); + if(ret == -ENOENT) { + if(!(finfo->flags & (O_RDWR|O_WRONLY))) + goto out_err; + } else if(ret < 0) + goto out_err; + + if(!(statbuf.st_mode & S_IWUSR) && (finfo->flags & (O_WRONLY|O_RDWR))) { + ret = -EACCES; + goto out_err; + } + + xfh = g_slice_new0(XfconfFuseHandle); + xfh->open_count = 1; + xfh->channel_name = channel_name; + xfh->property_name = property_name; + xfh->is_branch = is_branch; + finfo->fh = PTR_TO_UINT64(xfh); + + return 0; + +out_err: + + if(xfh) + g_free(xfh); + g_free(channel_name); + g_free(property_name); + + return ret; +} + +static int +xfconf_fuse_read(const char *path, + char *buf, + size_t size, + off_t offset, + struct fuse_file_info *finfo) +{ + XfconfFuseHandle *xfh = UINT64_TO_PTR(finfo->fh); + XfconfChannel *channel; + GValue val = { 0, }; + const gchar *type_str; + + if(G_UNLIKELY(!xfh)) + return -EINVAL; + + channel = xfconf_channel_get(xfh->channel_name); + if(!xfconf_channel_get_property(channel, xfh->property_name, &val)) + return -EIO; + + type_str = _xfconf_string_from_gtype(G_VALUE_TYPE(&val)); + if(!type_str) + type_str = "empty"; + + if(size < strlen(type_str) + 1 + G_VALUE_HOLDS_STRING( + + + +} + +static int +xfconf_fuse_release(const char *path, + struct fuse_file_info *finfo) +{ + XfconfFuseHandle *xfh = UINT64_TO_PTR(finfo->fh); + + if(G_UNLIKELY(!xfh)) + return -EINVAL; + + if(!--xfh->open_count) { + g_free(xfh->channel_name); + g_free(xfh->property_name); + g_slice_free(XfconfFuseHandle, xfh); + finfo->fh = 0; + } + + return 0; +} + + +static struct fuse_operations xfconf_oper = { + .getattr = xfconf_fuse_getattr, + .opendir = xfconf_fuse_opendir, + .readdir = xfconf_fuse_readdir, + .open = xfconf_fuse_open, + .read = xfconf_fuse_read, +// .write = xfconf_fuse_write, + .release = xfconf_fuse_release, +// .unlink = xfconf_fuse_unlink, +// .rmdir = xfconf_fuse_rmdir, +// .mkdir = xfconf_fuse_mkdir, +}; + +int +main(int argc, + char **argv) +{ + int ret; + GError *error = NULL; + + if(!xfconf_init(&error)) { + g_printerr("Failed to initialize xfconf: %s\n", error->message); + g_error_free(error); + return EXIT_FAILURE; + } + + channel_cache = g_hash_table_new_full(g_str_hash, g_str_equal, + (GDestroyNotify)g_free, + (GDestroyNotify)g_hash_table_destroy); + empty_dirs = g_hash_table_new_full(g_str_hash, g_str_equal, + (GDestroyNotify)g_free, NULL); + + ret = fuse_main(argc, argv, &xfconf_oper); + + g_hash_table_destroy(channel_cache); + channel_cache = NULL; + g_hash_table_destroy(empty_dirs); + empty_dirs = NULL; + + return ret; +} |