summaryrefslogtreecommitdiff
path: root/daemon
diff options
context:
space:
mode:
authorAlexander Larsson <alexl@redhat.com>2007-10-04 14:08:11 +0000
committerAlexander Larsson <alexl@src.gnome.org>2007-10-04 14:08:11 +0000
commitefd8ab55dd85159397c043b03efe54d47c28c307 (patch)
tree870cad51a8485bf4ab0b3c3d756138aa3ab70193 /daemon
parent14f210480005deb8a9cb4e23d07f8ba979ee29bb (diff)
downloadgvfs-efd8ab55dd85159397c043b03efe54d47c28c307.tar.gz
Allow NULL end pointer
2007-10-04 Alexander Larsson <alexl@redhat.com> * common/gvfsuriutils.c: (g_uri_unescape_string): Allow NULL end pointer * daemon/gvfsjob.[ch]: Add cancellable to GVfsJob for backends that want one * daemon/gvfsjobenumerate.[ch]: * daemon/gvfsjobqueryinfo.[ch]: Also store attribute in string form. Needed if you want to pass it on into gio. * daemon/Makefile.am: * daemon/gvfsbackendtrash.[ch]: * daemon/trash.mount.in: Added. Added trash backend svn path=/trunk/; revision=968
Diffstat (limited to 'daemon')
-rw-r--r--daemon/Makefile.am19
-rw-r--r--daemon/gvfsbackendtrash.c806
-rw-r--r--daemon/gvfsbackendtrash.h28
-rw-r--r--daemon/gvfsjob.c5
-rw-r--r--daemon/gvfsjob.h4
-rw-r--r--daemon/gvfsjobenumerate.c2
-rw-r--r--daemon/gvfsjobenumerate.h1
-rw-r--r--daemon/gvfsjobqueryinfo.c2
-rw-r--r--daemon/gvfsjobqueryinfo.h1
-rw-r--r--daemon/trash.mount.in5
10 files changed, 869 insertions, 4 deletions
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index 49c51eae..8b34b6b2 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -34,10 +34,10 @@ EXTRA_DIST = gvfs-daemon.service.in
DISTCLEANFILES = gvfs-daemon.service
-libexec_PROGRAMS=gvfs-daemon gvfs-daemon-ftp gvfs-daemon-sftp
+libexec_PROGRAMS=gvfs-daemon gvfs-daemon-ftp gvfs-daemon-sftp gvfs-daemon-trash
-mount_in_files = ftp.mount.in sftp.mount.in
-mount_DATA = ftp.mount sftp.mount
+mount_in_files = ftp.mount.in sftp.mount.in trash.mount.in
+mount_DATA = ftp.mount sftp.mount trash.mount
if HAVE_SAMBA
mount_in_files += smb.mount.in smb-browse.mount.in
@@ -156,3 +156,16 @@ gvfs_daemon_sftp_CPPFLAGS = \
-DBACKEND_TYPES='"sftp", G_VFS_TYPE_BACKEND_SFTP,'
gvfs_daemon_sftp_LDADD = $(libraries)
+
+gvfs_daemon_trash_SOURCES = \
+ gvfsbackendtrash.c gvfsbackendtrash.h \
+ daemon-main.c daemon-main.h \
+ daemon-main-generic.c
+
+gvfs_daemon_trash_CPPFLAGS = \
+ -DBACKEND_HEADER=gvfsbackendtrash.h \
+ -DDEFAULT_BACKEND_TYPE=trash \
+ -DMAX_JOB_THREADS=10 \
+ -DBACKEND_TYPES='"trash", G_VFS_TYPE_BACKEND_TRASH,'
+
+gvfs_daemon_trash_LDADD = $(libraries)
diff --git a/daemon/gvfsbackendtrash.c b/daemon/gvfsbackendtrash.c
new file mode 100644
index 00000000..6e1215e3
--- /dev/null
+++ b/daemon/gvfsbackendtrash.c
@@ -0,0 +1,806 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include <glib/gstdio.h>
+#include <glib/gi18n.h>
+#include <gio/gioerror.h>
+#include <gio/gfile.h>
+#include <gio/gvolumemonitor.h>
+#include <gio/gthemedicon.h>
+
+#include "gvfsuriutils.h"
+
+#include "gvfsbackendtrash.h"
+#include "gvfsjobopenforread.h"
+#include "gvfsjobread.h"
+#include "gvfsjobseekread.h"
+#include "gvfsjobopenforwrite.h"
+#include "gvfsjobwrite.h"
+#include "gvfsjobclosewrite.h"
+#include "gvfsjobseekwrite.h"
+#include "gvfsjobsetdisplayname.h"
+#include "gvfsjobqueryinfo.h"
+#include "gvfsjobdelete.h"
+#include "gvfsjobqueryfsinfo.h"
+#include "gvfsjobqueryattributes.h"
+#include "gvfsjobenumerate.h"
+#include "gvfsdaemonprotocol.h"
+
+
+struct _GVfsBackendTrash
+{
+ GVfsBackend parent_instance;
+
+};
+
+
+G_DEFINE_TYPE (GVfsBackendTrash, g_vfs_backend_trash, G_VFS_TYPE_BACKEND);
+
+static char *
+escape_pathname (const char *dir)
+{
+ const char *p;
+ char *d, *res;
+ int count;
+ char c;
+
+ count = 0;
+ p = dir;
+ while (*p)
+ {
+ if (*p == '%' ||
+ *p == '/')
+ count++;
+ p++;
+ }
+
+ res = g_malloc (strlen (dir) + count + 1);
+
+ p = dir;
+ d = res;
+ while (*p)
+ {
+ c = *p++;
+ if (c == '%')
+ {
+ *d++ = '%';
+ *d++ = '%';
+ }
+ else if (c == '/')
+ {
+ *d++ = '%';
+ *d++ = 's';
+ }
+ else
+ *d++ = c;
+ }
+ *d = 0;
+
+ return res;
+}
+
+static char *
+unescape_pathname (const char *escaped_dir, int len)
+{
+ char *dir, *d;
+ const char *p, *end;
+ char c;
+
+ if (len == -1)
+ len = strlen (escaped_dir);
+
+ dir = g_malloc (len + 1);
+
+ p = escaped_dir;
+ d = dir;
+ end = p + len;
+ while (p < end)
+ {
+ c = *p++;
+ if (c == '%')
+ {
+ if (p == end)
+ *d++ = '%';
+ else
+ {
+ c = *p++;
+ if (c == 's')
+ *d++ = '/';
+ else
+ *d++ = '%';
+ }
+ }
+ else
+ *d++ = c;
+ }
+ *d = 0;
+
+ return dir;
+}
+
+static char *
+get_top_dir_for_trash_dir (const char *trash_dir)
+{
+ char *basename, *dirname;
+ char *user_trash_basename;
+ char *user_sys_dir, *res;
+
+ basename = g_path_get_basename (trash_dir);
+ if (strcmp (basename, "Trash") == 0)
+ {
+ /* This is $XDG_DATA_DIR/Trash */
+ g_free (basename);
+ return g_path_get_dirname (trash_dir);
+ }
+
+ user_trash_basename = g_strdup_printf (".Trash-%d", getuid());
+ if (strcmp (basename, user_trash_basename) == 0)
+ {
+ g_free (user_trash_basename);
+ g_free (basename);
+ return g_path_get_dirname (trash_dir);
+ }
+ g_free (user_trash_basename);
+
+ user_sys_dir = g_strdup_printf ("%d", getuid());
+ if (strcmp (basename, user_sys_dir) == 0)
+ {
+ g_free (user_sys_dir);
+ dirname = g_path_get_dirname (trash_dir);
+ g_free (basename);
+ basename = g_path_get_basename (dirname);
+
+ if (strcmp (basename, ".Trash") == 0)
+ {
+ res = g_path_get_dirname (dirname);
+ g_free (dirname);
+ g_free (basename);
+ return res;
+ }
+
+ g_free (dirname);
+ }
+ g_free (user_sys_dir);
+ g_free (basename);
+
+ /* Weird, but we return something at least */
+ return g_strdup (trash_dir);
+}
+
+/* FALSE => root */
+static gboolean
+decode_path (const char *filename, char **trashdir, char **trashfile, char **relative_path, char **topdir)
+{
+ const char *first_entry, *first_entry_end;
+ char *first_item;
+
+ if (*filename == 0 || *filename != '/')
+ return FALSE;
+
+ while (*filename == '/')
+ filename++;
+
+ if (*filename == 0)
+ return FALSE;
+
+ first_entry = filename;
+
+ while (*filename != 0 && *filename != '/')
+ filename++;
+
+ first_entry_end = filename;
+
+ while (*filename == '/')
+ filename++;
+
+ first_item = unescape_pathname (first_entry, first_entry_end - first_entry);
+ *trashfile = g_path_get_basename (first_item);
+ *trashdir = g_path_get_dirname (first_item);
+ *topdir = get_top_dir_for_trash_dir (*trashdir);
+
+ if (*filename)
+ *relative_path = g_strdup (filename);
+ else
+ *relative_path = NULL;
+ return TRUE;
+}
+
+static void
+g_vfs_backend_trash_finalize (GObject *object)
+{
+ GVfsBackendTrash *backend;
+
+ backend = G_VFS_BACKEND_TRASH (object);
+
+ if (G_OBJECT_CLASS (g_vfs_backend_trash_parent_class)->finalize)
+ (*G_OBJECT_CLASS (g_vfs_backend_trash_parent_class)->finalize) (object);
+}
+
+static void
+g_vfs_backend_trash_init (GVfsBackendTrash *trash_backend)
+{
+ GVfsBackend *backend = G_VFS_BACKEND (trash_backend);
+ GMountSpec *mount_spec;
+
+ g_vfs_backend_set_display_name (backend, "trash");
+
+ mount_spec = g_mount_spec_new ("trash");
+ g_vfs_backend_set_mount_spec (backend, mount_spec);
+ g_mount_spec_unref (mount_spec);
+
+}
+
+static gboolean
+try_mount (GVfsBackend *backend,
+ GVfsJobMount *job,
+ GMountSpec *mount_spec,
+ GMountSource *mount_source,
+ gboolean is_automount)
+{
+ g_vfs_job_succeeded (G_VFS_JOB (job));
+ return TRUE;
+ }
+
+static void
+do_open_for_read (GVfsBackend *backend,
+ GVfsJobOpenForRead *job,
+ const char *filename)
+{
+ char *trashdir, *topdir, *relative_path, *trashfile;
+
+ if (!decode_path (filename, &trashdir, &trashfile, &relative_path, &topdir))
+ g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
+ G_IO_ERROR_IS_DIRECTORY,
+ _("Can't open directory"));
+ else
+ {
+ GFile *file;
+ char *dir;
+ GError *error;
+ GFileInputStream *stream;
+
+ dir = g_build_filename (trashdir, "files", trashfile, relative_path, NULL);
+ file = g_file_new_for_path (dir);
+
+ error = NULL;
+ stream = g_file_read (file,
+ G_VFS_JOB (job)->cancellable,
+ &error);
+ g_object_unref (file);
+
+ if (stream)
+ {
+ g_vfs_job_open_for_read_set_handle (job, stream);
+ g_vfs_job_open_for_read_set_can_seek (job,
+ g_file_input_stream_can_seek (G_FILE_INPUT_STREAM (stream)));
+ g_vfs_job_succeeded (G_VFS_JOB (job));
+ }
+ else
+ {
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ g_error_free (error);
+ }
+
+ g_free (trashdir);
+ g_free (trashfile);
+ g_free (relative_path);
+ g_free (topdir);
+ }
+}
+
+
+static void
+do_read (GVfsBackend *backend,
+ GVfsJobRead *job,
+ GVfsBackendHandle _handle,
+ char *buffer,
+ gsize bytes_requested)
+{
+ GInputStream *stream;
+ gssize res;
+ GError *error;
+
+ stream = G_INPUT_STREAM (_handle);
+
+ error = NULL;
+ res = g_input_stream_read (stream,
+ buffer, bytes_requested,
+ G_VFS_JOB (job)->cancellable,
+ &error);
+
+ if (res != -1)
+ {
+ g_vfs_job_read_set_size (job, res);
+ g_vfs_job_succeeded (G_VFS_JOB (job));
+ }
+ else
+ {
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ g_error_free (error);
+ }
+}
+
+static void
+do_seek_on_read (GVfsBackend *backend,
+ GVfsJobSeekRead *job,
+ GVfsBackendHandle _handle,
+ goffset offset,
+ GSeekType type)
+{
+ GFileInputStream *stream;
+ GError *error;
+
+ stream = G_FILE_INPUT_STREAM (_handle);
+
+ error = NULL;
+ if (g_file_input_stream_seek (stream,
+ offset, type,
+ G_VFS_JOB (job)->cancellable,
+ &error))
+ {
+ g_vfs_job_seek_read_set_offset (job,
+ g_file_input_stream_tell (stream));
+ g_vfs_job_succeeded (G_VFS_JOB (job));
+ }
+ else
+ {
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ g_error_free (error);
+ }
+}
+
+static void
+do_close_read (GVfsBackend *backend,
+ GVfsJobCloseRead *job,
+ GVfsBackendHandle _handle)
+{
+ GInputStream *stream;
+ GError *error;
+
+ stream = G_INPUT_STREAM (_handle);
+
+ error = NULL;
+ if (g_input_stream_close (stream,
+ G_VFS_JOB (job)->cancellable,
+ &error))
+ g_vfs_job_succeeded (G_VFS_JOB (job));
+ else
+ {
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ g_error_free (error);
+ }
+
+}
+
+static void
+add_extra_trash_info (GFileInfo *file_info,
+ const char *topdir,
+ const char *info_dir,
+ const char *filename,
+ const char *relative_path)
+{
+ char *info_filename;
+ char *info_path;
+ char *orig_path, *orig_path_key, *orig_path_unescaped, *date;
+ GKeyFile *keyfile;
+ char *display_name;
+
+
+ /* Override all writability */
+ g_file_info_set_attribute_boolean (file_info,
+ G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE,
+ FALSE);
+ g_file_info_set_attribute_boolean (file_info,
+ G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE,
+ FALSE);
+ g_file_info_set_attribute_boolean (file_info,
+ G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME,
+ FALSE);
+
+ /* But we can delete */
+ g_file_info_set_attribute_boolean (file_info,
+ G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE,
+ TRUE);
+
+ info_filename = g_strconcat (filename, ".trashinfo", NULL);
+ info_path = g_build_filename (info_dir, info_filename, NULL);
+ g_free (info_filename);
+
+ keyfile = g_key_file_new ();
+ if (g_key_file_load_from_file (keyfile, info_path, G_KEY_FILE_NONE, NULL))
+ {
+ orig_path_key = g_key_file_get_string (keyfile, "Trash Info", "Path", NULL);
+ if (orig_path_key)
+ {
+ orig_path_unescaped = g_uri_unescape_string (orig_path_key, NULL, "");
+
+ if (orig_path_unescaped)
+ {
+ /* Set display name and edit name based of original basename */
+ display_name = g_filename_display_basename (orig_path_unescaped);
+
+ g_file_info_set_edit_name (file_info, display_name);
+
+ if (strstr (display_name, "\357\277\275") != NULL)
+ {
+ char *p = display_name;
+ display_name = g_strconcat (display_name, _(" (invalid encoding)"), NULL);
+ g_free (p);
+ }
+ g_file_info_set_display_name (file_info, display_name);
+ g_free (display_name);
+
+
+ /* Set orig_path */
+
+ if (g_path_is_absolute (orig_path_unescaped))
+ orig_path = g_build_filename (orig_path_unescaped, relative_path, NULL);
+ else
+ orig_path = g_build_filename (topdir, orig_path_unescaped, relative_path, NULL);
+
+
+ g_file_info_set_attribute_byte_string (file_info,
+ "trash:orig_path",
+ orig_path);
+ g_free (orig_path);
+ g_free (orig_path_unescaped);
+ }
+
+
+ g_free (orig_path_key);
+ }
+
+ date = g_key_file_get_string (keyfile, "Trash Info", "DeletionDate", NULL);
+ if (date && g_utf8_validate (date, -1, NULL))
+ g_file_info_set_attribute_string (file_info,
+ "trash:deletion_date",
+ date);
+ g_free (date);
+ }
+ g_key_file_free (keyfile);
+ g_free (info_path);
+}
+
+static void
+enumerate_root_trashdir (GVfsBackend *backend,
+ GVfsJobEnumerate *job,
+ const char *topdir,
+ const char *trashdir)
+{
+ GFile *file, *files_file;
+ GFileEnumerator *enumerator;
+ GFileInfo *info;
+ const char *name;
+ char *new_name, *new_name_escaped;
+ char *info_dir;
+
+ info_dir = g_build_filename (trashdir, "info", NULL);
+
+ file = g_file_new_for_path (trashdir);
+ files_file = g_file_get_child (file, "files");
+ enumerator =
+ g_file_enumerate_children (files_file,
+ job->attributes,
+ job->flags,
+ G_VFS_JOB (job)->cancellable,
+ NULL);
+ g_object_unref (files_file);
+ g_object_unref (file);
+
+ if (enumerator)
+ {
+ while ((info = g_file_enumerator_next_file (enumerator,
+ G_VFS_JOB (job)->cancellable,
+ NULL)) != NULL)
+ {
+ name = g_file_info_get_name (info);
+
+ /* Get the display name, etc */
+ add_extra_trash_info (info,
+ topdir,
+ info_dir,
+ name,
+ NULL);
+
+
+ /* Update the name to also have the trash dir */
+ new_name = g_build_filename (trashdir, name, NULL);
+ new_name_escaped = escape_pathname (new_name);
+ g_free (new_name);
+ g_file_info_set_name (info, new_name_escaped);
+ g_free (new_name_escaped);
+
+ g_vfs_job_enumerate_add_info (job, info);
+ g_object_unref (info);
+ }
+
+ g_file_enumerator_close (enumerator,
+ G_VFS_JOB (job)->cancellable,
+ NULL);
+ g_object_unref (enumerator);
+ }
+}
+
+
+static void
+enumerate_root_topdir (GVfsBackend *backend,
+ GVfsJobEnumerate *job,
+ const char *topdir)
+{
+ struct stat statbuf;
+ char *sysadmin_dir, *sysadmin_dir_uid;
+ char *user_trash_basename, *user_trash;
+
+ /* TODO: This stats all filesystems. It should take more care to not get locked up
+ on e.g. hanged nfs mounts. See gnome-vfs-unix-mounts.c for some code to handle this. */
+
+ sysadmin_dir = g_build_filename (topdir, ".Trash", NULL);
+ if (lstat (sysadmin_dir, &statbuf) == 0 &&
+ S_ISDIR (statbuf.st_mode) &&
+ statbuf.st_mode & S_ISVTX)
+ {
+ /* We have a valid sysadmin .Trash dir, look for uid subdir */
+ sysadmin_dir_uid = g_strdup_printf ("%s/%d", sysadmin_dir, getuid());
+
+ if (lstat (sysadmin_dir_uid, &statbuf) == 0 &&
+ S_ISDIR (statbuf.st_mode) &&
+ statbuf.st_uid == getuid())
+ enumerate_root_trashdir (backend, job, topdir, sysadmin_dir_uid);
+
+ g_free (sysadmin_dir_uid);
+ }
+ g_free (sysadmin_dir);
+
+ user_trash_basename = g_strdup_printf (".Trash-%d", getuid());
+ user_trash = g_build_filename (topdir, user_trash_basename, NULL);
+ g_free (user_trash_basename);
+
+ if (lstat (user_trash, &statbuf) == 0 &&
+ S_ISDIR (statbuf.st_mode) &&
+ statbuf.st_uid == getuid())
+ enumerate_root_trashdir (backend, job, topdir, user_trash);
+
+ g_free (user_trash);
+}
+
+static void
+enumerate_root (GVfsBackend *backend,
+ GVfsJobEnumerate *job)
+{
+ GVolumeMonitor *monitor;
+ GList *volumes, *l;
+ GVolume *volume;
+ GFile *topdir_file;
+ char *topdir;
+ char *home_trash;
+
+ /* Always succeeds */
+ g_vfs_job_succeeded (G_VFS_JOB (job));
+
+ home_trash = g_build_filename (g_get_user_data_dir (), "Trash", NULL);
+ enumerate_root_trashdir (backend, job, g_get_user_data_dir (), home_trash);
+ g_free (home_trash);
+
+ monitor = g_volume_monitor_get ();
+
+
+ volumes = g_volume_monitor_get_mounted_volumes (monitor);
+
+ for (l = volumes; l != NULL; l = l->next)
+ {
+ volume = l->data;
+
+ topdir_file = g_volume_get_root (volume);
+ if (topdir_file)
+ {
+ if (g_file_is_native (topdir_file))
+ {
+ topdir = g_file_get_path (topdir_file);
+
+ if (topdir)
+ enumerate_root_topdir (backend, job, topdir);
+
+ g_free (topdir);
+ }
+
+ g_object_unref (topdir_file);
+ }
+
+ g_object_unref (volume);
+ }
+ g_list_free (volumes);
+
+
+ g_vfs_job_enumerate_done (job);
+}
+
+static void
+do_enumerate (GVfsBackend *backend,
+ GVfsJobEnumerate *job,
+ const char *filename,
+ GFileAttributeMatcher *attribute_matcher,
+ GFileQueryInfoFlags flags)
+{
+ char *trashdir, *topdir, *relative_path, *trashfile;
+
+ if (!decode_path (filename, &trashdir, &trashfile, &relative_path, &topdir))
+ enumerate_root (backend, job);
+ else
+ {
+ GFile *file;
+ GFileEnumerator *enumerator;
+ GFileInfo *info;
+ const char *name;
+ char *dir;
+ GError *error;
+
+ dir = g_build_filename (trashdir, "files", trashfile, relative_path, NULL);
+ file = g_file_new_for_path (dir);
+ error = NULL;
+ enumerator =
+ g_file_enumerate_children (file,
+ job->attributes,
+ job->flags,
+ G_VFS_JOB (job)->cancellable,
+ &error);
+ g_free (dir);
+ g_object_unref (file);
+
+ if (enumerator)
+ {
+ g_vfs_job_succeeded (G_VFS_JOB (job));
+
+ while ((info = g_file_enumerator_next_file (enumerator,
+ G_VFS_JOB (job)->cancellable,
+ NULL)) != NULL)
+ {
+ name = g_file_info_get_name (info);
+
+ g_vfs_job_enumerate_add_info (job, info);
+ g_object_unref (info);
+ }
+
+ g_file_enumerator_close (enumerator,
+ G_VFS_JOB (job)->cancellable,
+ NULL);
+ g_object_unref (enumerator);
+
+ g_vfs_job_enumerate_done (job);
+ }
+ else
+ {
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ g_error_free (error);
+ }
+
+ g_free (trashdir);
+ g_free (trashfile);
+ g_free (relative_path);
+ g_free (topdir);
+ }
+}
+
+static void
+do_query_info (GVfsBackend *backend,
+ GVfsJobQueryInfo *job,
+ const char *filename,
+ GFileQueryInfoFlags flags,
+ GFileInfo *info,
+ GFileAttributeMatcher *matcher)
+{
+ char *trashdir, *topdir, *relative_path, *trashfile;
+ GIcon *icon;
+
+ if (!decode_path (filename, &trashdir, &trashfile, &relative_path, &topdir))
+ {
+ /* The trash:/// root */
+ g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY);
+ g_file_info_set_display_name (info, _("Trashcan"));
+ g_file_info_set_content_type (info, "inode/directory");
+
+ /* TODO: Add -full version? */
+ icon = g_themed_icon_new ("user-trash");
+ /*TODO: Crashes: g_file_info_set_icon (info, icon); */
+ g_object_unref (icon);
+
+ g_file_info_set_attribute_boolean (info,
+ G_FILE_ATTRIBUTE_STD_IS_VIRTUAL,
+ TRUE);
+ g_file_info_set_attribute_boolean (info,
+ G_FILE_ATTRIBUTE_ACCESS_CAN_READ,
+ TRUE);
+ g_file_info_set_attribute_boolean (info,
+ G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE,
+ FALSE);
+ g_file_info_set_attribute_boolean (info,
+ G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE,
+ FALSE);
+ g_file_info_set_attribute_boolean (info,
+ G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE,
+ FALSE);
+ g_file_info_set_attribute_boolean (info,
+ G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME,
+ FALSE);
+
+ g_vfs_job_succeeded (G_VFS_JOB (job));
+ }
+ else
+ {
+ GFile *file;
+ GFileInfo *local_info;
+ char *path;
+ GError *error;
+ char *info_dir;
+
+ path = g_build_filename (trashdir, "files", trashfile, relative_path, NULL);
+ file = g_file_new_for_path (path);
+ g_free (path);
+
+ error = NULL;
+ local_info = g_file_query_info (file,
+ job->attributes,
+ job->flags,
+ G_VFS_JOB (job)->cancellable,
+ &error);
+ g_object_unref (file);
+
+ if (local_info)
+ {
+ g_file_info_copy_into (local_info, info);
+
+ info_dir = g_build_filename (trashdir, "info", NULL);
+ add_extra_trash_info (info,
+ topdir,
+ info_dir,
+ trashfile,
+ relative_path);
+ g_free (info_dir);
+
+ g_object_unref (local_info);
+ g_vfs_job_succeeded (G_VFS_JOB (job));
+ }
+ else
+ {
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ g_error_free (error);
+ }
+
+ g_free (trashdir);
+ g_free (trashfile);
+ g_free (relative_path);
+ g_free (topdir);
+ }
+}
+
+static void
+do_delete (GVfsBackend *backend,
+ GVfsJobDelete *job,
+ const char *filename)
+{
+}
+
+static void
+g_vfs_backend_trash_class_init (GVfsBackendTrashClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GVfsBackendClass *backend_class = G_VFS_BACKEND_CLASS (klass);
+
+ gobject_class->finalize = g_vfs_backend_trash_finalize;
+
+ backend_class->try_mount = try_mount;
+ backend_class->open_for_read = do_open_for_read;
+ backend_class->read = do_read;
+ backend_class->seek_on_read = do_seek_on_read;
+ backend_class->close_read = do_close_read;
+ backend_class->query_info = do_query_info;
+ backend_class->enumerate = do_enumerate;
+ backend_class->delete = do_delete;
+}
diff --git a/daemon/gvfsbackendtrash.h b/daemon/gvfsbackendtrash.h
new file mode 100644
index 00000000..4f60ae6a
--- /dev/null
+++ b/daemon/gvfsbackendtrash.h
@@ -0,0 +1,28 @@
+#ifndef __G_VFS_BACKEND_TRASH_H__
+#define __G_VFS_BACKEND_TRASH_H__
+
+#include <gvfsbackend.h>
+#include <gmountspec.h>
+
+G_BEGIN_DECLS
+
+#define G_VFS_TYPE_BACKEND_TRASH (g_vfs_backend_trash_get_type ())
+#define G_VFS_BACKEND_TRASH(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_VFS_TYPE_BACKEND_TRASH, GVfsBackendTrash))
+#define G_VFS_BACKEND_TRASH_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_VFS_TYPE_BACKEND_TRASH, GVfsBackendTrashClass))
+#define G_VFS_IS_BACKEND_TRASH(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_VFS_TYPE_BACKEND_TRASH))
+#define G_VFS_IS_BACKEND_TRASH_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_VFS_TYPE_BACKEND_TRASH))
+#define G_VFS_BACKEND_TRASH_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_VFS_TYPE_BACKEND_TRASH, GVfsBackendTrashClass))
+
+typedef struct _GVfsBackendTrash GVfsBackendTrash;
+typedef struct _GVfsBackendTrashClass GVfsBackendTrashClass;
+
+struct _GVfsBackendTrashClass
+{
+ GVfsBackendClass parent_class;
+};
+
+GType g_vfs_backend_trash_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __G_VFS_BACKEND_TRASH_H__ */
diff --git a/daemon/gvfsjob.c b/daemon/gvfsjob.c
index 22ff91b9..95129237 100644
--- a/daemon/gvfsjob.c
+++ b/daemon/gvfsjob.c
@@ -57,6 +57,8 @@ g_vfs_job_finalize (GObject *object)
if (job->backend_data_destroy)
job->backend_data_destroy (job->backend_data);
+
+ g_object_unref (job->cancellable);
if (G_OBJECT_CLASS (g_vfs_job_parent_class)->finalize)
(*G_OBJECT_CLASS (g_vfs_job_parent_class)->finalize) (object);
@@ -111,6 +113,8 @@ static void
g_vfs_job_init (GVfsJob *job)
{
job->priv = G_TYPE_INSTANCE_GET_PRIVATE (job, G_VFS_TYPE_JOB, GVfsJobPrivate);
+
+ job->cancellable = g_cancellable_new ();
}
@@ -182,6 +186,7 @@ g_vfs_job_cancel (GVfsJob *job)
job->cancelled = TRUE;
g_signal_emit (job, signals[CANCELLED], 0);
+ g_cancellable_cancel (job->cancellable);
}
static void
diff --git a/daemon/gvfsjob.h b/daemon/gvfsjob.h
index be41ccb7..f8d4e2c5 100644
--- a/daemon/gvfsjob.h
+++ b/daemon/gvfsjob.h
@@ -2,6 +2,7 @@
#define __G_VFS_JOB_H__
#include <glib-object.h>
+#include <gio/gcancellable.h>
G_BEGIN_DECLS
@@ -32,7 +33,8 @@ struct _GVfsJob
guint sent_reply : 1;
guint finished : 1;
GError *error;
-
+ GCancellable *cancellable;
+
GVfsJobPrivate *priv;
};
diff --git a/daemon/gvfsjobenumerate.c b/daemon/gvfsjobenumerate.c
index d30971bf..ac97da93 100644
--- a/daemon/gvfsjobenumerate.c
+++ b/daemon/gvfsjobenumerate.c
@@ -29,6 +29,7 @@ g_vfs_job_enumerate_finalize (GObject *object)
job = G_VFS_JOB_ENUMERATE (object);
g_free (job->filename);
+ g_free (job->attributes);
g_file_attribute_matcher_unref (job->attribute_matcher);
g_free (job->object_path);
@@ -95,6 +96,7 @@ g_vfs_job_enumerate_new (DBusConnection *connection,
job->object_path = g_strdup (obj_path);
job->filename = g_strndup (path_data, path_len);
job->backend = backend;
+ job->attributes = g_strdup (attributes);
job->attribute_matcher = g_file_attribute_matcher_new (attributes);
job->flags = flags;
diff --git a/daemon/gvfsjobenumerate.h b/daemon/gvfsjobenumerate.h
index 9784609a..115672e5 100644
--- a/daemon/gvfsjobenumerate.h
+++ b/daemon/gvfsjobenumerate.h
@@ -24,6 +24,7 @@ struct _GVfsJobEnumerate
GVfsBackend *backend;
char *filename;
char *object_path;
+ char *attributes;
GFileAttributeMatcher *attribute_matcher;
GFileQueryInfoFlags flags;
diff --git a/daemon/gvfsjobqueryinfo.c b/daemon/gvfsjobqueryinfo.c
index 2155267b..47dfd412 100644
--- a/daemon/gvfsjobqueryinfo.c
+++ b/daemon/gvfsjobqueryinfo.c
@@ -31,6 +31,7 @@ g_vfs_job_query_info_finalize (GObject *object)
g_object_unref (job->file_info);
g_free (job->filename);
+ g_free (job->attributes);
g_file_attribute_matcher_unref (job->attribute_matcher);
if (G_OBJECT_CLASS (g_vfs_job_query_info_parent_class)->finalize)
@@ -92,6 +93,7 @@ g_vfs_job_query_info_new (DBusConnection *connection,
job->filename = g_strndup (path_data, path_len);
job->backend = backend;
+ job->attributes = g_strdup (attributes);
job->attribute_matcher = g_file_attribute_matcher_new (attributes);
job->flags = flags;
diff --git a/daemon/gvfsjobqueryinfo.h b/daemon/gvfsjobqueryinfo.h
index d180e757..5eb89ec1 100644
--- a/daemon/gvfsjobqueryinfo.h
+++ b/daemon/gvfsjobqueryinfo.h
@@ -23,6 +23,7 @@ struct _GVfsJobQueryInfo
GVfsBackend *backend;
char *filename;
+ char *attributes;
GFileAttributeMatcher *attribute_matcher;
GFileQueryInfoFlags flags;
diff --git a/daemon/trash.mount.in b/daemon/trash.mount.in
new file mode 100644
index 00000000..d937ad6d
--- /dev/null
+++ b/daemon/trash.mount.in
@@ -0,0 +1,5 @@
+[Mount]
+Type=trash
+Exec=@libexecdir@/gvfs-daemon-trash
+AutoMount=true
+