summaryrefslogtreecommitdiff
path: root/client/gvfsfusedaemon.c
diff options
context:
space:
mode:
authorAlexander Larsson <alexl@src.gnome.org>2007-09-13 13:49:39 +0000
committerAlexander Larsson <alexl@src.gnome.org>2007-09-13 13:49:39 +0000
commitf9f3cc4462976e0df48820d2bdedd86612e97249 (patch)
tree57a8fd749eeed2ada714a451e2ee1e927feae454 /client/gvfsfusedaemon.c
parentedafdc36125604f24f8376c291a30e50df7d4449 (diff)
downloadgvfs-f9f3cc4462976e0df48820d2bdedd86612e97249.tar.gz
Move the FUSE daemon from fuse/ into client/ and use GDaemon* directly.
Original git commit by Hans Petter Jansson <hpj@fatty.amok> at 1179365820 -0500 svn path=/trunk/; revision=583
Diffstat (limited to 'client/gvfsfusedaemon.c')
-rw-r--r--client/gvfsfusedaemon.c1211
1 files changed, 1211 insertions, 0 deletions
diff --git a/client/gvfsfusedaemon.c b/client/gvfsfusedaemon.c
new file mode 100644
index 00000000..2a3001ec
--- /dev/null
+++ b/client/gvfsfusedaemon.c
@@ -0,0 +1,1211 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/vfs.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib/gprintf.h>
+
+#include "gdaemonvfs.h"
+#include "gdaemonfile.h"
+#include <gmounttracker.h>
+
+#define FUSE_USE_VERSION 26
+#include <fuse.h>
+
+typedef struct
+{
+ time_t creation_time;
+ GMountInfo *info;
+}
+MountRecord;
+
+static GThread *subthread = NULL;
+static GMainLoop *subthread_main_loop = NULL;
+static GVfs *gvfs = NULL;
+
+static GMountTracker *mount_tracker = NULL;
+
+/* Contains pointers to MountRecord */
+static GList *mount_list = NULL;
+static GMutex *mount_list_mutex;
+
+static time_t daemon_creation_time;
+static uid_t daemon_uid;
+static gid_t daemon_gid;
+
+/* ------- *
+ * Helpers *
+ * ------- */
+
+static void
+debug_print (const gchar *message, ...)
+{
+#ifdef DEBUG_ENABLED
+
+ static FILE *debug_fhd = NULL;
+ va_list var_args;
+
+ if (!debug_fhd)
+ debug_fhd = fopen ("/home/hpj/vfs.debug", "at");
+
+ if (!debug_fhd)
+ return;
+
+ va_start (var_args, message);
+ g_vfprintf (debug_fhd, message, var_args);
+ va_end (var_args);
+
+ fflush (debug_fhd);
+
+#endif
+}
+
+static MountRecord *
+mount_record_new (GMountInfo *mount_info)
+{
+ MountRecord *mount_record;
+
+ mount_record = g_new (MountRecord, 1);
+
+ mount_record->info = mount_info;
+ mount_record->creation_time = time (NULL);
+
+ return mount_record;
+}
+
+static void
+mount_record_free (MountRecord *mount_record)
+{
+ g_mount_info_free (mount_record->info);
+ g_free (mount_record);
+}
+
+static void
+mount_list_lock (void)
+{
+ g_mutex_lock (mount_list_mutex);
+}
+
+static void
+mount_list_unlock (void)
+{
+ g_mutex_unlock (mount_list_mutex);
+}
+
+static void
+mount_list_free (void)
+{
+ g_list_foreach (mount_list, (GFunc) mount_record_free, NULL);
+ g_list_free (mount_list);
+ mount_list = NULL;
+}
+
+#if 0
+
+static void
+mount_list_update (void)
+{
+ GList *tracker_list;
+ GList *l;
+
+ tracker_list = g_mount_tracker_list_mounts (mount_tracker);
+
+ for (l = tracker_list; l; l = g_list_next (l))
+ {
+ GMountInfo *this_mount_info = l->data;
+
+ if (!mount_record_find_by_mount_spec (this_mount_info->mount_spec))
+ {
+ mount_list_lock ();
+ mount_list = g_list_prepend (mount_list, mount_record_new (this_mount_info));
+ mount_list_unlock ();
+ }
+ else
+ {
+ g_mount_info_free (this_mount_info);
+ }
+ }
+
+ g_list_free (tracker_list);
+}
+
+#endif
+
+#if 0
+
+static gint
+file_info_get_attribute_as_int (GFileInfo *file_info, const gchar *attribute)
+{
+ GFileAttributeType attribute_type;
+ gint int_result;
+
+ attribute_type = g_file_info_get_attribute_type (file_info, attribute);
+
+ switch (attribute_type)
+ {
+ case G_FILE_ATTRIBUTE_TYPE_UINT32:
+ int_result = g_file_info_get_attribute_uint32 (file_info, attribute);
+ break;
+
+ case G_FILE_ATTRIBUTE_TYPE_INT32:
+ int_result = g_file_info_get_attribute_int32 (file_info, attribute);
+ break;
+
+ case G_FILE_ATTRIBUTE_TYPE_UINT64:
+ int_result = g_file_info_get_attribute_uint64 (file_info, attribute);
+ break;
+
+ case G_FILE_ATTRIBUTE_TYPE_INT64:
+ int_result = g_file_info_get_attribute_int64 (file_info, attribute);
+ break;
+
+ default:
+ int_result = 0;
+ g_assert_not_reached ();
+ break;
+ }
+
+ return int_result;
+}
+
+#endif
+
+static guint
+file_info_get_attribute_as_uint (GFileInfo *file_info, const gchar *attribute)
+{
+ GFileAttributeType attribute_type;
+ guint uint_result;
+
+ attribute_type = g_file_info_get_attribute_type (file_info, attribute);
+
+ switch (attribute_type)
+ {
+ case G_FILE_ATTRIBUTE_TYPE_UINT32:
+ uint_result = g_file_info_get_attribute_uint32 (file_info, attribute);
+ break;
+
+ case G_FILE_ATTRIBUTE_TYPE_INT32:
+ uint_result = g_file_info_get_attribute_int32 (file_info, attribute);
+ break;
+
+ case G_FILE_ATTRIBUTE_TYPE_UINT64:
+ uint_result = g_file_info_get_attribute_uint64 (file_info, attribute);
+ break;
+
+ case G_FILE_ATTRIBUTE_TYPE_INT64:
+ uint_result = g_file_info_get_attribute_int64 (file_info, attribute);
+ break;
+
+ default:
+ uint_result = 0;
+ g_assert_not_reached ();
+ break;
+ }
+
+ return uint_result;
+}
+
+static gboolean
+path_is_mount_list (const gchar *path)
+{
+ return strcmp (path, "/") ? FALSE : TRUE;
+}
+
+static gchar
+nibble_to_ascii (guint value)
+{
+ gchar c;
+
+ g_assert (value < 16);
+
+ if (value < 10)
+ c = '0' + value;
+ else
+ c = 'a' + value - 10;
+
+ return c;
+}
+
+static gchar *
+escape_to_uri_syntax (const gchar *string, const gchar *allowed_characters)
+{
+ gchar *escaped_string;
+ gint i;
+ gint j = 0;
+
+ if (!string)
+ return NULL;
+
+ if (!allowed_characters)
+ allowed_characters = "";
+
+ escaped_string = g_malloc (strlen (string) * 3 + 1);
+
+ for (i = 0; string [i]; i++)
+ {
+ guchar c = string [i];
+
+ if (strchr (allowed_characters, c))
+ {
+ escaped_string [j++] = c;
+ continue;
+ }
+
+ escaped_string [j++] = '%';
+ escaped_string [j++] = nibble_to_ascii (c >> 4);
+ escaped_string [j++] = nibble_to_ascii (c & 0x0f);
+ }
+
+ escaped_string [j] = '\0';
+ escaped_string = g_realloc (escaped_string, j + 1);
+
+ return escaped_string;
+}
+
+static gchar *
+escape_fs_name (const gchar *name)
+{
+ return escape_to_uri_syntax (name, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-+@#$., ");
+}
+
+#if 0
+
+static gchar *
+escape_uri_component (const gchar *uri_component)
+{
+ return escape_to_uri_syntax (uri_component, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/_-+., ");
+}
+
+#endif
+
+static MountRecord *
+mount_record_find_by_mount_spec (GMountSpec *mount_spec)
+{
+ MountRecord *mount_record = NULL;
+ GList *l;
+
+ g_assert (mount_spec != NULL);
+
+ mount_list_lock ();
+
+ for (l = mount_list; !mount_record && l; l = g_list_next (l))
+ {
+ MountRecord *this_mount_record = l->data;
+ GMountSpec *this_mount_spec = this_mount_record->info->mount_spec;
+
+ if (g_mount_spec_equal (mount_spec, this_mount_spec))
+ mount_record = this_mount_record;
+ }
+
+ mount_list_unlock ();
+
+ return mount_record;
+}
+
+static gchar *
+mount_info_to_mount_name (GMountInfo *mount_info)
+{
+ g_assert (mount_info != NULL);
+
+ return escape_fs_name (mount_info->display_name);
+}
+
+static MountRecord *
+mount_record_find_by_mount_name (const gchar *mount_name)
+{
+ MountRecord *mount_record = NULL;
+ GList *l;
+
+ g_assert (mount_name != NULL);
+
+ mount_list_lock ();
+
+ for (l = mount_list; !mount_record && l; l = g_list_next (l))
+ {
+ MountRecord *this_mount_record = l->data;
+ GMountInfo *this_mount_info = this_mount_record->info;
+ gchar *this_mount_name = mount_info_to_mount_name (this_mount_info);
+
+ g_assert (this_mount_name != NULL);
+
+ if (!strcmp (mount_name, this_mount_name))
+ mount_record = this_mount_record;
+
+ g_free (this_mount_name);
+ }
+
+ mount_list_unlock ();
+
+ return mount_record;
+}
+
+#if 0
+
+static gchar *
+mount_spec_to_uri (GMountSpec *mount_spec)
+{
+ const gchar *type;
+ gchar *uri = NULL;
+
+ type = g_mount_spec_get (mount_spec, "type");
+ if (!type)
+ return NULL;
+
+ if (!strcmp (type, "smb-share"))
+ {
+ gchar *server = escape_uri_component (g_mount_spec_get (mount_spec, "server"));
+ gchar *share = escape_uri_component (g_mount_spec_get (mount_spec, "share"));
+ gchar *domain = escape_uri_component (g_mount_spec_get (mount_spec, "domain"));
+ gchar *user = escape_uri_component (g_mount_spec_get (mount_spec, "user"));
+
+ if (server && share)
+ {
+ uri = g_strdup_printf ("smb://%s%s%s%s%s/%s",
+ domain ? domain : "",
+ domain ? ";" : "",
+ user ? user : "",
+ user ? "@" : "",
+ server,
+ share);
+ }
+
+ g_free (server);
+ g_free (share);
+ g_free (domain);
+ g_free (user);
+ }
+
+ return uri;
+}
+
+#endif
+
+static gboolean
+path_to_mount_record_and_path (const gchar *full_path, MountRecord **mount_record, gchar **mount_path)
+{
+ MountRecord *mount_record_internal;
+ gchar *mount_path_internal = NULL;
+ gchar *mount_name;
+ const gchar *s1;
+ const gchar *s2;
+ gboolean success = FALSE;
+
+ s1 = full_path;
+ if (*s1 == '/')
+ s1++;
+
+ if (*s1)
+ {
+ s2 = strchr (s1, '/');
+ if (!s2)
+ s2 = s1 + strlen (s1);
+
+ mount_name = g_strndup (s1, s2 - s1);
+ mount_record_internal = mount_record_find_by_mount_name (mount_name);
+ g_free (mount_name);
+
+ if (mount_record_internal)
+ {
+ if (mount_record)
+ *mount_record = mount_record_internal;
+
+ if (*s2)
+ {
+ /* s2 is at the initial '/' of the mount subpath */
+ mount_path_internal = g_strdup (s2);
+ }
+ else
+ {
+ /* No subpath specified; we want the mount's root */
+ mount_path_internal = g_strdup ("/");
+ }
+
+ if (mount_path)
+ *mount_path = mount_path_internal;
+
+ success = TRUE;
+ }
+ }
+
+ return success;
+}
+
+static GFile *
+file_from_mount_record_and_path (MountRecord *mount_record, const gchar *path)
+{
+#if 0
+ gchar *mount_uri;
+ gchar *base_uri;
+ gchar *escaped_path;
+#endif
+ GFile *file;
+
+ file = g_daemon_file_new (mount_record->info->mount_spec, path);
+
+#if 0
+ mount_uri = mount_spec_to_uri (mount_record->info->mount_spec);
+ escaped_path = escape_uri_component (path);
+ base_uri = g_strconcat (mount_uri, escaped_path, NULL);
+ file = g_file_get_for_uri (base_uri);
+
+ g_free (mount_uri);
+ g_free (escaped_path);
+ g_free (base_uri);
+#endif
+
+ g_assert (file != NULL);
+
+ return file;
+}
+
+static GFile *
+file_from_full_path (const gchar *path)
+{
+ MountRecord *mount_record;
+ gchar *mount_path;
+ GFile *file = NULL;
+
+ if (path_to_mount_record_and_path (path, &mount_record, &mount_path))
+ {
+ file = file_from_mount_record_and_path (mount_record, mount_path);
+ g_free (mount_path);
+ }
+
+ return file;
+}
+
+/* ------------- *
+ * VFS functions *
+ * ------------- */
+
+static gint
+vfs_statfs (const gchar *path, struct statvfs *stbuf)
+{
+ debug_print ("vfs_statfs: %s\n", path);
+
+ memset (stbuf, 0, sizeof (*stbuf));
+
+ stbuf->f_bsize = 4096;
+ stbuf->f_frsize = 4096; /* Ignored by FUSE */
+ stbuf->f_blocks = 0;
+ stbuf->f_bfree = 0;
+ stbuf->f_bavail = 0;
+ stbuf->f_files = 0;
+ stbuf->f_ffree = 0;
+ stbuf->f_favail = 0; /* Ignored by FUSE */
+ stbuf->f_fsid = 1; /* Ignored by FUSE */
+ stbuf->f_flag = 0; /* Ignored by FUSE */
+ stbuf->f_namemax = 1024;
+
+ return 0;
+}
+
+static mode_t
+file_info_get_stat_mode (GFileInfo *file_info)
+{
+ GFileType file_type;
+ GFileAccessRights file_access;
+ mode_t unix_mode;
+
+ file_type = g_file_info_get_file_type (file_info);
+ file_access = g_file_info_get_access_rights (file_info);
+
+ switch (file_type)
+ {
+ case G_FILE_TYPE_REGULAR:
+ unix_mode = S_IFREG;
+ break;
+
+ case G_FILE_TYPE_DIRECTORY:
+ case G_FILE_TYPE_MOUNTABLE:
+ unix_mode = S_IFDIR;
+ break;
+
+ case G_FILE_TYPE_SYMBOLIC_LINK:
+ case G_FILE_TYPE_SHORTCUT:
+ unix_mode = S_IFLNK;
+ break;
+
+ case G_FILE_TYPE_SPECIAL:
+ default:
+ unix_mode = 0;
+ break;
+ }
+
+ if (file_access & G_FILE_ACCESS_CAN_READ)
+ unix_mode |= S_IRUSR;
+ if (file_access & G_FILE_ACCESS_CAN_WRITE)
+ unix_mode |= S_IWUSR;
+ if (file_access & G_FILE_ACCESS_CAN_EXECUTE)
+ unix_mode |= S_IXUSR;
+
+ return unix_mode;
+}
+
+static gint
+getattr_for_file (GFile *file, struct stat *sbuf)
+{
+ GFileInfo *file_info;
+ GError *error = NULL;
+ gint result = 0;
+
+ file_info = g_file_get_info (file, "*", 0, NULL, &error);
+
+ if (file_info)
+ {
+ GTimeVal mod_time;
+
+ sbuf->st_mode = file_info_get_stat_mode (file_info);
+ sbuf->st_size = g_file_info_get_size (file_info);
+ sbuf->st_uid = daemon_uid;
+ sbuf->st_gid = daemon_gid;
+
+ if (g_file_info_has_attribute (file_info, G_FILE_ATTRIBUTE_UNIX_UID))
+ sbuf->st_uid = file_info_get_attribute_as_uint (file_info, G_FILE_ATTRIBUTE_UNIX_UID);
+ if (g_file_info_has_attribute (file_info, G_FILE_ATTRIBUTE_UNIX_GID))
+ sbuf->st_gid = file_info_get_attribute_as_uint (file_info, G_FILE_ATTRIBUTE_UNIX_GID);
+
+ g_file_info_get_modification_time (file_info, &mod_time);
+ sbuf->st_mtime = mod_time.tv_sec;
+ sbuf->st_ctime = mod_time.tv_sec;
+ sbuf->st_atime = mod_time.tv_sec;
+
+ if (g_file_info_has_attribute (file_info, G_FILE_ATTRIBUTE_UNIX_CTIME))
+ sbuf->st_ctime = file_info_get_attribute_as_uint (file_info, G_FILE_ATTRIBUTE_UNIX_CTIME);
+ if (g_file_info_has_attribute (file_info, G_FILE_ATTRIBUTE_UNIX_ATIME))
+ sbuf->st_atime = file_info_get_attribute_as_uint (file_info, G_FILE_ATTRIBUTE_UNIX_ATIME);
+
+ if (g_file_info_has_attribute (file_info, G_FILE_ATTRIBUTE_UNIX_NLINK))
+ {
+ /* This tends to return 2 for directories, which means 'find' won't
+ * recurse correctly. */
+ sbuf->st_nlink = file_info_get_attribute_as_uint (file_info, G_FILE_ATTRIBUTE_UNIX_NLINK);
+ }
+ else
+ {
+ /* Makes 'find' work */
+ if (sbuf->st_mode & S_IFDIR)
+ sbuf->st_nlink = 2;
+ else
+ sbuf->st_nlink = 1;
+ }
+
+ g_object_unref (file_info);
+ }
+ else
+ {
+ /* TODO: Figure out the right error code */
+ result = -ENOENT;
+ }
+
+ return result;
+}
+
+static gint
+vfs_getattr (const gchar *path, struct stat *sbuf)
+{
+ GFile *file;
+ gint result = 0;
+
+ debug_print ("vfs_getattr: %s\n", path);
+
+ memset (sbuf, 0, sizeof (*sbuf));
+
+ sbuf->st_dev = 0; /* dev_t ID of device containing file */
+ sbuf->st_ino = 0; /* ino_t inode number */
+ sbuf->st_uid = 0; /* uid_t user ID of owner */
+ sbuf->st_gid = 0; /* gid_t group ID of owner */
+ sbuf->st_rdev = 0; /* dev_t device ID (if special file) */
+ sbuf->st_size = 0; /* off_t total size, in bytes */
+ sbuf->st_blocks = 0; /* blkcnt_t number of blocks allocated */
+ sbuf->st_atime = 0; /* time_t time of last access */
+ sbuf->st_mtime = 0; /* time_t time of last modification */
+ sbuf->st_ctime = 0; /* time_t time of last status change */
+ sbuf->st_blksize = 4096; /* blksize_t blocksize for filesystem I/O */
+
+ if (path_is_mount_list (path))
+ {
+ /* Mount list */
+
+ sbuf->st_mode = S_IFDIR | 0555; /* mode_t protection */
+ sbuf->st_nlink = 2 + g_list_length (mount_list); /* nlink_t number of hard links */
+ sbuf->st_atime = daemon_creation_time;
+ sbuf->st_mtime = daemon_creation_time;
+ sbuf->st_ctime = daemon_creation_time;
+ sbuf->st_uid = daemon_uid;
+ sbuf->st_gid = daemon_gid;
+ }
+ else if ((file = file_from_full_path (path)))
+ {
+ /* Submount */
+
+ result = getattr_for_file (file, sbuf);
+ g_object_unref (file);
+ }
+ else
+ {
+ result = -ENOENT;
+ }
+
+ return result;
+}
+
+static gint
+vfs_readlink (const gchar *path, gchar *target, size_t size)
+{
+ debug_print ("vfs_readlink: %s\n", path);
+
+ return 0;
+}
+
+static gint
+vfs_open (const gchar *path, struct fuse_file_info *fi)
+{
+ GFile *file;
+ gint result = 0;
+
+ debug_print ("vfs_open: %s\n", path);
+
+ if ((file = file_from_full_path (path)))
+ {
+ GFileInfo *file_info;
+ GError *error = NULL;
+
+ file_info = g_file_get_info (file, "*", 0, NULL, &error);
+
+ if (file_info)
+ {
+ GFileType file_type = g_file_info_get_file_type (file_info);
+
+ if (file_type == G_FILE_TYPE_REGULAR)
+ {
+ /* TODO: Check permissions. Apparently, permissions are currently
+ * shot at some lower GVFS level, so we implement this when it
+ * works properly. */
+
+ /* TODO: Cache the GFile for performance? */
+
+ /* Success */
+ }
+ else if (file_type == G_FILE_TYPE_DIRECTORY)
+ {
+ /* EISDIR is supposedly only for attempts to write to directory handles,
+ * but outside readdir(), we don't support reading them either. */
+ result = -EISDIR;
+ }
+ else
+ {
+ result = -EACCES;
+ }
+
+ g_object_unref (file_info);
+ }
+ else
+ {
+ /* TODO: Figure out the correct error code */
+ result = -ENOENT;
+ }
+
+ g_object_unref (file);
+ }
+ else
+ {
+ result = -ENOENT;
+ }
+
+ return result;
+}
+
+static gint
+vfs_release (const gchar *path, struct fuse_file_info *fi)
+{
+ debug_print ("vfs_release: %s\n", path);
+
+ return 0;
+}
+
+static gint
+read_stream (GInputStream *input_stream, gchar *output_buf, size_t output_buf_size, off_t offset)
+{
+ gint n_bytes_skipped = 0;
+ gint n_bytes_read = 0;
+ gint result = 0;
+ GError *error = NULL;
+
+ debug_print ("read_stream: %d bytes at offset %d.\n", output_buf_size, offset);
+
+ if (offset > 0)
+ n_bytes_skipped = g_input_stream_skip (input_stream, offset, NULL, &error);
+
+ if (n_bytes_skipped == offset && !error)
+ {
+ while (n_bytes_read < output_buf_size)
+ {
+ gint part_result;
+
+ part_result = g_input_stream_read (input_stream,
+ output_buf + n_bytes_read,
+ output_buf_size - n_bytes_read,
+ NULL,
+ &error);
+
+ if (part_result < 1)
+ break;
+
+ n_bytes_read += part_result;
+ }
+ }
+
+ result = n_bytes_read;
+
+ if (n_bytes_read < output_buf_size)
+ {
+ if (!error)
+ g_input_stream_close (input_stream, NULL, &error);
+
+ if (error)
+ {
+ /* TODO: Check error */
+ result = -EIO;
+ }
+ }
+
+ return result;
+}
+
+static gint
+vfs_read (const gchar *path, gchar *buf, size_t size,
+ off_t offset, struct fuse_file_info *fi)
+{
+ GFile *file;
+ gint result = 0;
+
+ debug_print ("vfs_read: %s\n", path);
+
+ if ((file = file_from_full_path (path)))
+ {
+ GInputStream *input_stream;
+ GError *error = NULL;
+
+ input_stream = G_INPUT_STREAM (g_file_read (file, NULL, &error));
+
+ if (input_stream)
+ {
+ result = read_stream (input_stream, buf, size, offset);
+ g_input_stream_close (input_stream, NULL, NULL);
+
+ g_object_unref (input_stream);
+ }
+ else
+ {
+ result = -EIO;
+ }
+
+ g_object_unref (file);
+ }
+ else
+ {
+ result = -EIO;
+ }
+
+ return result;
+}
+
+static gint
+vfs_write (const gchar *path, const gchar *buf, size_t len, off_t offset,
+ struct fuse_file_info *fi)
+{
+ debug_print ("vfs_write: %s\n", path);
+
+ return 0;
+}
+
+static gint
+vfs_flush (const gchar *path, struct fuse_file_info *fi)
+{
+ debug_print ("vfs_flush: %s\n", path);
+
+ return 0;
+};
+
+static gint
+vfs_opendir (const gchar *path, struct fuse_file_info *fi)
+{
+ MountRecord *mount_record;
+ gchar *mount_path;
+ gint result = 0;
+
+ debug_print ("vfs_opendir: %s\n", path);
+
+ if (path_is_mount_list (path))
+ {
+ /* Mount list */
+ }
+ else if (path_to_mount_record_and_path (path, &mount_record, &mount_path))
+ {
+ /* Submount */
+
+ /* TODO */
+
+ g_free (mount_path);
+ }
+ else
+ {
+ /* Not found */
+
+ result = -ENOENT;
+ }
+
+ return result;
+}
+
+static gint
+readdir_for_file (GFile *base_file, gpointer buf, fuse_fill_dir_t filler)
+{
+ GFileEnumerator *enumerator;
+ GFileInfo *file_info;
+ GError *error = NULL;
+
+ g_assert (base_file != NULL);
+
+ enumerator = g_file_enumerate_children (base_file, "*", 0, NULL, &error);
+ if (!enumerator)
+ {
+ /* TODO: Figure out the correct error code */
+ return -ENOENT;
+ }
+
+ filler (buf, ".", NULL, 0);
+ filler (buf, "..", NULL, 0);
+
+ while ((file_info = g_file_enumerator_next_file (enumerator, NULL, &error)) != NULL)
+ {
+ filler (buf, g_file_info_get_name (file_info), NULL, 0);
+ g_object_unref (file_info);
+ }
+
+ g_object_unref (enumerator);
+
+ return 0;
+}
+
+static gint
+vfs_readdir (const gchar *path, gpointer buf, fuse_fill_dir_t filler, off_t offset,
+ struct fuse_file_info *fi)
+{
+ GFile *base_file;
+ gint result = 0;
+
+ debug_print ("vfs_readdir: %s\n", path);
+
+ if (path_is_mount_list (path))
+ {
+ GList *l;
+
+ /* Mount list */
+
+ filler (buf, ".", NULL, 0);
+ filler (buf, "..", NULL, 0);
+
+ mount_list_lock ();
+
+ for (l = mount_list; l; l = g_list_next (l))
+ {
+ MountRecord *this_mount_record = l->data;
+ gchar *mount_name;
+
+ mount_name = mount_info_to_mount_name (this_mount_record->info);
+ filler (buf, mount_name, NULL, 0);
+ g_free (mount_name);
+ }
+
+ mount_list_unlock ();
+ }
+ else if ((base_file = file_from_full_path (path)))
+ {
+ /* Submount */
+
+ result = readdir_for_file (base_file, buf, filler);
+
+ g_object_unref (base_file);
+ }
+ else
+ {
+ /* Not found */
+
+ result = -ENOENT;
+ }
+
+ return result;
+}
+
+static void
+mount_tracker_mounted_cb (GMountTracker *tracer, GMountInfo *mount_info)
+{
+ MountRecord *mount_record;
+
+ mount_record = mount_record_find_by_mount_spec (mount_info->mount_spec);
+ g_assert (mount_record == NULL);
+
+ mount_record = mount_record_new (g_mount_info_dup (mount_info));
+
+ mount_list_lock ();
+ mount_list = g_list_prepend (mount_list, mount_record);
+ mount_list_unlock ();
+}
+
+static void
+mount_tracker_unmounted_cb (GMountTracker *tracer, GMountInfo *mount_info)
+{
+ MountRecord *mount_record;
+
+ mount_record = mount_record_find_by_mount_spec (mount_info->mount_spec);
+ g_assert (mount_record != NULL);
+
+ mount_list_lock ();
+ mount_list = g_list_remove (mount_list, mount_record);
+ mount_list_unlock ();
+
+ mount_record_free (mount_record);
+}
+
+static gpointer
+subthread_main (gpointer data)
+{
+ g_signal_connect (mount_tracker, "mounted", (GCallback) mount_tracker_mounted_cb, NULL);
+ g_signal_connect (mount_tracker, "unmounted", (GCallback) mount_tracker_unmounted_cb, NULL);
+
+ g_main_loop_run (subthread_main_loop);
+
+ g_signal_handlers_disconnect_by_func (mount_tracker, mount_tracker_mounted_cb, NULL);
+ g_signal_handlers_disconnect_by_func (mount_tracker, mount_tracker_unmounted_cb, NULL);
+
+ g_main_loop_unref (subthread_main_loop);
+
+ g_object_unref (mount_tracker);
+ mount_tracker = NULL;
+
+ return NULL;
+}
+
+static gpointer
+vfs_init (struct fuse_conn_info *conn)
+{
+ daemon_creation_time = time (NULL);
+ daemon_uid = getuid ();
+ daemon_gid = getgid ();
+
+ mount_list_mutex = g_mutex_new ();
+
+ /* Initializes D-Bus and other VFS necessities */
+ gvfs = G_VFS (g_daemon_vfs_new ());
+
+ mount_tracker = g_mount_tracker_new ();
+
+ subthread_main_loop = g_main_loop_new (NULL, FALSE);
+ subthread = g_thread_create ((GThreadFunc) subthread_main, NULL, FALSE, NULL);
+
+ return NULL;
+}
+
+static void
+vfs_destroy (gpointer param)
+{
+ mount_list_free ();
+ g_main_loop_quit (subthread_main_loop);
+ g_mutex_free (mount_list_mutex);
+ g_object_unref (gvfs);
+}
+
+static struct fuse_operations vfs_oper =
+{
+ .init = vfs_init,
+ .destroy = vfs_destroy,
+
+ .getattr = vfs_getattr,
+ .readdir = vfs_readdir,
+
+ .statfs = vfs_statfs,
+
+ .opendir = vfs_opendir,
+ .readdir = vfs_readdir,
+#if 0
+ .releasedir = vfs_releasedir,
+ .fsyncdir = vfs_fsyncdir,
+#endif
+ .readlink = vfs_readlink,
+
+ .open = vfs_open,
+ .release = vfs_release,
+ .flush = vfs_flush,
+
+ .read = vfs_read,
+ .write = vfs_write,
+
+#if 0
+ .mknod = vfs_mknod,
+ .unlink = vfs_unlink,
+ .rmdir = vfs_rmdir,
+ .symlink = vfs_symlink,
+ .chmod = vfs_chmod,
+ .chown = vfs_chown,
+ .truncate = vfs_truncate,
+ .utime = vfs_utime,
+ .fsync = vfs_fsync,
+ .setxattr = vfs_setxattr,
+ .getxattr = vfs_getxattr,
+ .listxattr = vfs_listxattr,
+ .removexattr = vfs_removexattr,
+#endif
+};
+
+gint
+main (gint argc, gchar *argv [])
+{
+ g_type_init ();
+ g_thread_init (NULL);
+
+ return fuse_main (argc, argv, &vfs_oper, NULL /* user data */);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+#if 0
+
+#define MOUNT_NAME_SEPARATOR_S ":"
+
+static gchar *
+generate_options_string (const gchar *first_option_to_omit, ...)
+{
+ va_list vargs;
+
+ va_start (first_option_to_omit, &vargs);
+
+ va_end ();
+}
+
+static gchar *
+mount_spec_to_mount_name (GMountSpec *mount_spec)
+{
+ const gchar *type;
+
+ GDecodedUri *decoded_uri;
+ gchar *mount_name;
+ gchar *temp;
+
+ type = g_mount_spec_get (mount_spec, "type");
+ if (!type)
+ return NULL;
+
+ if (!strcmp (type, "smb-share"))
+ {
+ const gchar *server;
+ const gchar *share;
+
+ server = g_mount_spec_get (mount_spec, "server");
+ share = g_mount_spec_get (mount_spec, "share");
+
+ if (server && share)
+ {
+ mount_name = g_strjoin (MOUNT_NAME_SEPARATOR_S,
+ type,
+ server,
+ share,
+ NULL);
+ }
+ }
+
+
+
+
+
+
+
+ /* TODO: Escape all strings */
+
+ decoded_uri = _g_decode_uri (uri);
+ g_assert (decoded_uri != NULL);
+
+ mount_name = g_strjoin (":",
+ decoded_uri->scheme,
+ decoded_uri->host,
+ decoded_uri->path,
+ NULL);
+
+ if (decoded_uri->query)
+ {
+ temp = g_strjoin ("?",
+ mount_name,
+ decoded_uri->query,
+ NULL);
+
+ g_free (mount_name);
+ mount_name = temp;
+ }
+
+ if (decoded_uri->fragment)
+ {
+ temp = g_strjoin ("#",
+ mount_name,
+ decoded_uri->fragment,
+ NULL);
+
+ g_free (mount_name);
+ mount_name = temp;
+ }
+
+ if (decoded_uri->port != -1)
+ {
+ gchar *port_opt_str;
+
+ port_opt_str = g_strdup_printf ("port=%d", decoded_uri->port);
+
+ temp = g_strjoin (":",
+ mount_name,
+ port_opt_str,
+ NULL);
+
+ g_free (mount_name);
+ mount_name = temp;
+ }
+
+ if (decoded_uri->userinfo)
+ {
+ gchar *user_opt_str;
+
+ user_opt_str = g_strdup_printf ("user=%s", decoded_uri->userinfo);
+
+ temp = g_strjoin (":",
+ mount_name,
+ user_opt_str,
+ NULL);
+
+ g_free (mount_name);
+ mount_name = temp;
+ }
+
+ return mount_name;
+}
+
+#endif
+