From e3319e5dcb36f1b82963f03c512ee14c143229aa Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Tue, 3 Mar 2009 04:46:29 +0000 Subject: [PATCH] Port to DeviceKit-disks --- gvfs-gdu.patch | 5159 ++++++++++++++++++++++++++++++++++++++++++++++++ gvfs.spec | 21 +- 2 files changed, 5174 insertions(+), 6 deletions(-) create mode 100644 gvfs-gdu.patch diff --git a/gvfs-gdu.patch b/gvfs-gdu.patch new file mode 100644 index 0000000..cfb73ef --- /dev/null +++ b/gvfs-gdu.patch @@ -0,0 +1,5159 @@ +Index: configure.ac +=================================================================== +--- configure.ac (revision 2275) ++++ configure.ac (working copy) +@@ -198,6 +198,31 @@ + + AM_CONDITIONAL(USE_GCONF, [test "$msg_gconf" = "yes"]) + ++dnl ************************************ ++dnl *** Check for gnome-disk-utility *** ++dnl ************************************ ++ ++dnl TODO: when there is a more stable version of gdu available, turn this on by default ++AC_ARG_ENABLE(gdu, [ --enable-gdu build with gdu support]) ++msg_gdu=no ++GDU_LIBS= ++GDU_CFLAGS= ++GDU_REQUIRED=0.2 ++ ++if test "x$enable_gdu" = "xyes"; then ++ PKG_CHECK_EXISTS([gdu >= $GDU_REQUIRED], msg_gdu=yes) ++ ++ if test "x$msg_gdu" == "xyes"; then ++ PKG_CHECK_MODULES([GDU],[gdu >= $GDU_REQUIRED]) ++ AC_DEFINE(HAVE_GDU, 1, [Define to 1 if gnome-disk-utility is available]) ++ fi ++fi ++ ++AC_SUBST(GDU_LIBS) ++AC_SUBST(GDU_CFLAGS) ++ ++AM_CONDITIONAL(USE_GDU, [test "$msg_gdu" = "yes"]) ++ + dnl ********************** + dnl *** Check for HAL *** + dnl ********************** +@@ -558,6 +583,7 @@ + monitor/Makefile + monitor/proxy/Makefile + monitor/hal/Makefile ++monitor/gdu/Makefile + monitor/gphoto2/Makefile + gconf/Makefile + programs/Makefile +@@ -579,7 +605,8 @@ + archive support: $msg_archive + GConf support: $msg_gconf + DNS-SD support: $msg_avahi +- Use HAL for volume monitor: $msg_hal (with fast init path: $have_hal_fast_init) ++ Build HAL volume monitor: $msg_hal (with fast init path: $have_hal_fast_init) ++ Build GDU volume monitor: $msg_gdu + GNOME Keyring support: $msg_keyring + Bash-completion support: $msg_bash_completion + " +Index: monitor/Makefile.am +=================================================================== +--- monitor/Makefile.am (revision 2275) ++++ monitor/Makefile.am (working copy) +@@ -5,6 +5,10 @@ + SUBDIRS += hal + endif + ++if USE_GDU ++SUBDIRS += gdu ++endif ++ + if USE_GPHOTO2 + SUBDIRS += gphoto2 + endif +--- /dev/null 2009-03-02 15:45:22.087299058 -0500 ++++ monitor/gdu/gdu-volume-monitor-daemon.c 2009-03-01 23:47:15.000000000 -0500 +@@ -0,0 +1,46 @@ ++/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ ++/* gvfs - extensions for gio ++ * ++ * Copyright (C) 2006-2009 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General ++ * Public License along with this library; if not, write to the ++ * Free Software Foundation, Inc., 59 Temple Place, Suite 330, ++ * Boston, MA 02111-1307, USA. ++ * ++ * Author: David Zeuthen ++ */ ++ ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "ggduvolumemonitor.h" ++ ++int ++main (int argc, char *argv[]) ++{ ++ g_vfs_proxy_volume_monitor_daemon_init (); ++ ++ g_set_application_name (_("GVfs GDU Volume Monitor")); ++ ++ return g_vfs_proxy_volume_monitor_daemon_main (argc, ++ argv, ++ "org.gtk.Private.GduVolumeMonitor", ++ G_TYPE_GDU_VOLUME_MONITOR); ++} +--- /dev/null 2009-03-02 15:45:22.087299058 -0500 ++++ monitor/gdu/ggdudrive.c 2009-03-01 23:48:33.000000000 -0500 +@@ -0,0 +1,691 @@ ++/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ ++/* gvfs - extensions for gio ++ * ++ * Copyright (C) 2006-2009 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General ++ * Public License along with this library; if not, write to the ++ * Free Software Foundation, Inc., 59 Temple Place, Suite 330, ++ * Boston, MA 02111-1307, USA. ++ * ++ * Author: David Zeuthen ++ */ ++ ++#include ++ ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "ggduvolumemonitor.h" ++#include "ggdudrive.h" ++#include "ggduvolume.h" ++ ++struct _GGduDrive { ++ GObject parent; ++ ++ GVolumeMonitor *volume_monitor; /* owned by volume monitor */ ++ GList *volumes; /* entries in list are owned by volume_monitor */ ++ ++ GduPresentable *presentable; ++ ++ /* the following members need to be set upon construction */ ++ GIcon *icon; ++ gchar *name; ++ gchar *device_file; ++ gboolean is_media_removable; ++ gboolean has_media; ++ gboolean can_eject; ++ gboolean can_poll_for_media; ++ gboolean is_media_check_automatic; ++ time_t time_of_last_media_insertion; ++}; ++ ++static void g_gdu_drive_drive_iface_init (GDriveIface *iface); ++ ++G_DEFINE_TYPE_EXTENDED (GGduDrive, g_gdu_drive, G_TYPE_OBJECT, 0, ++ G_IMPLEMENT_INTERFACE (G_TYPE_DRIVE, ++ g_gdu_drive_drive_iface_init)) ++ ++static void presentable_changed (GduPresentable *presentable, ++ GGduDrive *drive); ++ ++static void presentable_job_changed (GduPresentable *presentable, ++ GGduDrive *drive); ++ ++static void ++g_gdu_drive_finalize (GObject *object) ++{ ++ GList *l; ++ GGduDrive *drive; ++ ++ drive = G_GDU_DRIVE (object); ++ ++ for (l = drive->volumes; l != NULL; l = l->next) ++ { ++ GGduVolume *volume = l->data; ++ g_gdu_volume_unset_drive (volume, drive); ++ } ++ ++ if (drive->presentable != NULL) ++ { ++ g_signal_handlers_disconnect_by_func (drive->presentable, presentable_changed, drive); ++ g_signal_handlers_disconnect_by_func (drive->presentable, presentable_job_changed, drive); ++ g_object_unref (drive->presentable); ++ } ++ ++ if (drive->icon != NULL) ++ g_object_unref (drive->icon); ++ g_free (drive->name); ++ g_free (drive->device_file); ++ ++ if (G_OBJECT_CLASS (g_gdu_drive_parent_class)->finalize) ++ (*G_OBJECT_CLASS (g_gdu_drive_parent_class)->finalize) (object); ++} ++ ++static void ++g_gdu_drive_class_init (GGduDriveClass *klass) ++{ ++ GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ++ ++ gobject_class->finalize = g_gdu_drive_finalize; ++} ++ ++static void ++g_gdu_drive_init (GGduDrive *gdu_drive) ++{ ++} ++ ++static void ++emit_changed (GGduDrive *drive) ++{ ++ g_signal_emit_by_name (drive, "changed"); ++ g_signal_emit_by_name (drive->volume_monitor, "drive_changed", drive); ++} ++ ++static gboolean ++update_drive (GGduDrive *drive) ++{ ++ GduDevice *device; ++ gboolean changed; ++ GIcon *old_icon; ++ gchar *old_name; ++ gchar *old_device_file; ++ gboolean old_is_media_removable; ++ gboolean old_has_media; ++ gboolean old_can_eject; ++ gboolean old_is_media_check_automatic; ++ gboolean old_can_poll_for_media; ++ ++ /* save old values */ ++ old_is_media_removable = drive->is_media_removable; ++ old_has_media = drive->has_media; ++ old_can_eject = drive->can_eject; ++ old_can_poll_for_media = drive->can_poll_for_media; ++ old_is_media_check_automatic = drive->is_media_check_automatic; ++ ++ old_name = g_strdup (drive->name); ++ old_device_file = g_strdup (drive->device_file); ++ old_icon = drive->icon != NULL ? g_object_ref (drive->icon) : NULL; ++ ++ /* in with the new */ ++ device = gdu_presentable_get_device (drive->presentable); ++ ++ if (drive->icon != NULL) ++ g_object_unref (drive->icon); ++ drive->icon = gdu_presentable_get_icon (drive->presentable); ++ ++ g_free (drive->name); ++ drive->name = gdu_presentable_get_name (drive->presentable); ++ ++ /* the GduDevice for an activatable drive (such as RAID) is NULL if the drive is not activated */ ++ if (device == NULL) ++ { ++ g_free (drive->device_file); ++ drive->device_file = NULL; ++ drive->is_media_removable = TRUE; ++ drive->has_media = TRUE; ++ drive->can_eject = FALSE; ++ } ++ else ++ { ++ g_free (drive->device_file); ++ drive->device_file = g_strdup (gdu_device_get_device_file (device)); ++ drive->is_media_removable = gdu_device_is_removable (device); ++ drive->has_media = gdu_device_is_media_available (device); ++ drive->can_eject = gdu_device_drive_get_is_media_ejectable (device) || gdu_device_drive_get_requires_eject (device); ++ drive->is_media_check_automatic = gdu_device_is_media_change_detected (device); ++ drive->can_poll_for_media = TRUE; ++ } ++ ++ if (device != NULL) ++ g_object_unref (device); ++ ++ if (drive->has_media != old_has_media) ++ drive->time_of_last_media_insertion = time (NULL); ++ ++ /* compute whether something changed */ ++ changed = !((old_is_media_removable == drive->is_media_removable) && ++ (old_has_media == drive->has_media) && ++ (old_can_eject == drive->can_eject) && ++ (old_is_media_check_automatic == drive->is_media_check_automatic) && ++ (old_can_poll_for_media == drive->can_poll_for_media) && ++ (g_strcmp0 (old_name, drive->name) == 0) && ++ (g_strcmp0 (old_device_file, drive->device_file) == 0) && ++ g_icon_equal (old_icon, drive->icon) ++ ); ++ ++ /* free old values */ ++ g_free (old_name); ++ g_free (old_device_file); ++ if (old_icon != NULL) ++ g_object_unref (old_icon); ++ ++ /*g_debug ("in update_drive(); has_media=%d changed=%d", drive->has_media, changed);*/ ++ ++ return changed; ++} ++ ++static void ++presentable_changed (GduPresentable *presentable, ++ GGduDrive *drive) ++{ ++ /*g_debug ("drive: presentable_changed: %p: %s", drive, gdu_presentable_get_id (GDU_PRESENTABLE (presentable)));*/ ++ if (update_drive (drive)) ++ emit_changed (drive); ++} ++ ++static void ++presentable_job_changed (GduPresentable *presentable, ++ GGduDrive *drive) ++{ ++ /*g_debug ("drive: presentable_job_changed: %p: %s", drive, gdu_presentable_get_id (GDU_PRESENTABLE (presentable)));*/ ++ if (update_drive (drive)) ++ emit_changed (drive); ++} ++ ++GGduDrive * ++g_gdu_drive_new (GVolumeMonitor *volume_monitor, ++ GduPresentable *presentable) ++{ ++ GGduDrive *drive; ++ ++ drive = g_object_new (G_TYPE_GDU_DRIVE, NULL); ++ drive->volume_monitor = volume_monitor; ++ g_object_add_weak_pointer (G_OBJECT (volume_monitor), (gpointer) &(drive->volume_monitor)); ++ ++ drive->presentable = g_object_ref (presentable); ++ ++ drive->time_of_last_media_insertion = time (NULL); ++ ++ g_signal_connect (drive->presentable, "changed", G_CALLBACK (presentable_changed), drive); ++ g_signal_connect (drive->presentable, "job-changed", G_CALLBACK (presentable_job_changed), drive); ++ ++ update_drive (drive); ++ ++ return drive; ++} ++ ++void ++g_gdu_drive_disconnected (GGduDrive *drive) ++{ ++ GList *l, *volumes; ++ ++ volumes = drive->volumes; ++ drive->volumes = NULL; ++ ++ for (l = volumes; l != NULL; l = l->next) ++ { ++ GGduVolume *volume = l->data; ++ g_gdu_volume_unset_drive (volume, drive); ++ } ++ ++ g_list_free (volumes); ++} ++ ++void ++g_gdu_drive_set_volume (GGduDrive *drive, ++ GGduVolume *volume) ++{ ++ if (g_list_find (drive->volumes, volume) == NULL) ++ { ++ drive->volumes = g_list_prepend (drive->volumes, volume); ++ emit_changed (drive); ++ } ++} ++ ++void ++g_gdu_drive_unset_volume (GGduDrive *drive, ++ GGduVolume *volume) ++{ ++ GList *l; ++ ++ l = g_list_find (drive->volumes, volume); ++ if (l != NULL) ++ { ++ drive->volumes = g_list_delete_link (drive->volumes, l); ++ emit_changed (drive); ++ } ++} ++ ++static GIcon * ++g_gdu_drive_get_icon (GDrive *_drive) ++{ ++ GGduDrive *drive = G_GDU_DRIVE (_drive); ++ return drive->icon != NULL ? g_object_ref (drive->icon) : NULL; ++} ++ ++static char * ++g_gdu_drive_get_name (GDrive *_drive) ++{ ++ GGduDrive *drive = G_GDU_DRIVE (_drive); ++ return g_strdup (drive->name); ++} ++ ++static GList * ++g_gdu_drive_get_volumes (GDrive *_drive) ++{ ++ GGduDrive *drive = G_GDU_DRIVE (_drive); ++ GList *l; ++ ++ l = g_list_copy (drive->volumes); ++ g_list_foreach (l, (GFunc) g_object_ref, NULL); ++ ++ return l; ++} ++ ++static gboolean ++g_gdu_drive_has_volumes (GDrive *_drive) ++{ ++ GGduDrive *drive = G_GDU_DRIVE (drive); ++ gboolean res; ++ ++ res = drive->volumes != NULL; ++ ++ return res; ++} ++ ++static gboolean ++g_gdu_drive_is_media_removable (GDrive *_drive) ++{ ++ GGduDrive *drive = G_GDU_DRIVE (_drive); ++ return drive->is_media_removable; ++} ++ ++static gboolean ++g_gdu_drive_has_media (GDrive *_drive) ++{ ++ GGduDrive *drive = G_GDU_DRIVE (_drive); ++ return drive->has_media; ++} ++ ++static gboolean ++g_gdu_drive_is_media_check_automatic (GDrive *_drive) ++{ ++ GGduDrive *drive = G_GDU_DRIVE (_drive); ++ return drive->is_media_check_automatic; ++} ++ ++static gboolean ++g_gdu_drive_can_eject (GDrive *_drive) ++{ ++ GGduDrive *drive = G_GDU_DRIVE (_drive); ++ return drive->can_eject; ++} ++ ++static gboolean ++g_gdu_drive_can_poll_for_media (GDrive *_drive) ++{ ++ GGduDrive *drive = G_GDU_DRIVE (_drive); ++ return drive->can_poll_for_media; ++} ++ ++static void ++eject_cb (GduDevice *device, ++ GError *error, ++ gpointer user_data) ++{ ++ GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data); ++ ++ if (error != NULL) ++ { ++ /* We could handle PolicyKit integration here but this action is allowed by default ++ * and this won't be needed when porting to PolicyKit 1.0 anyway ++ */ ++ g_simple_async_result_set_from_error (simple, error); ++ g_error_free (error); ++ } ++ ++ g_simple_async_result_complete (simple); ++ g_object_unref (simple); ++} ++ ++ ++static void ++g_gdu_drive_eject_do (GDrive *_drive, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data) ++{ ++ GGduDrive *drive = G_GDU_DRIVE (_drive); ++ GSimpleAsyncResult *simple; ++ GduDevice *device; ++ ++ device = gdu_presentable_get_device (drive->presentable); ++ if (device == NULL) ++ { ++ simple = g_simple_async_result_new_error (G_OBJECT (drive), ++ callback, ++ user_data, ++ G_IO_ERROR, ++ G_IO_ERROR_FAILED, ++ "Drive is activatable and not running"); ++ g_simple_async_result_complete_in_idle (simple); ++ g_object_unref (simple); ++ } ++ else ++ { ++ simple = g_simple_async_result_new (G_OBJECT (drive), ++ callback, ++ user_data, ++ NULL); ++ ++ gdu_device_op_drive_eject (device, eject_cb, simple); ++ g_object_unref (device); ++ } ++} ++ ++typedef struct { ++ GDrive *drive; ++ GAsyncReadyCallback callback; ++ gpointer user_data; ++ GCancellable *cancellable; ++ GMountUnmountFlags flags; ++ ++ GList *pending_mounts; ++} UnmountMountsOp; ++ ++static void ++free_unmount_mounts_op (UnmountMountsOp *data) ++{ ++ GList *l; ++ ++ for (l = data->pending_mounts; l != NULL; l = l->next) ++ { ++ GMount *mount = l->data; ++ g_object_unref (mount); ++ } ++ g_list_free (data->pending_mounts); ++} ++ ++static void _eject_unmount_mounts (UnmountMountsOp *data); ++ ++static void ++_eject_unmount_mounts_cb (GObject *source_object, ++ GAsyncResult *res, ++ gpointer user_data) ++{ ++ UnmountMountsOp *data = user_data; ++ GMount *mount = G_MOUNT (source_object); ++ GSimpleAsyncResult *simple; ++ GError *error = NULL; ++ ++ if (!g_mount_unmount_finish (mount, res, &error)) ++ { ++ /* make the error dialog more targeted to the drive.. unless the user has already seen a dialog */ ++ if (error->code != G_IO_ERROR_FAILED_HANDLED) ++ { ++ g_error_free (error); ++ error = g_error_new (G_IO_ERROR, G_IO_ERROR_BUSY, ++ _("Failed to eject media; one or more volumes on the media are busy.")); ++ } ++ ++ /* unmount failed; need to fail the whole eject operation */ ++ simple = g_simple_async_result_new_from_error (G_OBJECT (data->drive), ++ data->callback, ++ data->user_data, ++ error); ++ g_error_free (error); ++ g_simple_async_result_complete (simple); ++ g_object_unref (simple); ++ ++ free_unmount_mounts_op (data); ++ } ++ else ++ { ++ ++ /*g_warning ("successfully unmounted %p", mount);*/ ++ ++ /* move on to the next mount.. */ ++ _eject_unmount_mounts (data); ++ } ++ ++ g_object_unref (mount); ++} ++ ++static void ++_eject_unmount_mounts (UnmountMountsOp *data) ++{ ++ GMount *mount; ++ ++ if (data->pending_mounts == NULL) ++ { ++ ++ /*g_warning ("all pending mounts done; ejecting drive");*/ ++ ++ g_gdu_drive_eject_do (data->drive, ++ data->cancellable, ++ data->callback, ++ data->user_data); ++ ++ g_object_unref (data->drive); ++ g_free (data); ++ } ++ else ++ { ++ mount = data->pending_mounts->data; ++ data->pending_mounts = g_list_remove (data->pending_mounts, mount); ++ ++ /*g_warning ("unmounting %p", mount);*/ ++ ++ g_mount_unmount (mount, ++ data->flags, ++ data->cancellable, ++ _eject_unmount_mounts_cb, ++ data); ++ } ++} ++ ++static void ++g_gdu_drive_eject (GDrive *drive, ++ GMountUnmountFlags flags, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data) ++{ ++ GGduDrive *gdu_drive = G_GDU_DRIVE (drive); ++ UnmountMountsOp *data; ++ GList *l; ++ ++ /* first we need to go through all the volumes and unmount their assoicated mounts (if any) */ ++ ++ data = g_new0 (UnmountMountsOp, 1); ++ data->drive = g_object_ref (drive); ++ data->cancellable = cancellable; ++ data->callback = callback; ++ data->user_data = user_data; ++ data->flags = flags; ++ ++ for (l = gdu_drive->volumes; l != NULL; l = l->next) ++ { ++ GGduVolume *volume = l->data; ++ GMount *mount; ++ ++ mount = g_volume_get_mount (G_VOLUME (volume)); ++ if (mount != NULL && g_mount_can_unmount (mount)) ++ data->pending_mounts = g_list_prepend (data->pending_mounts, g_object_ref (mount)); ++ } ++ ++ _eject_unmount_mounts (data); ++} ++ ++static gboolean ++g_gdu_drive_eject_finish (GDrive *drive, ++ GAsyncResult *result, ++ GError **error) ++{ ++ return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error); ++} ++ ++static void ++poll_media_cb (GduDevice *device, ++ GError *error, ++ gpointer user_data) ++{ ++ GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data); ++ ++ if (error != NULL) ++ { ++ /* We could handle PolicyKit integration here but this action is allowed by default ++ * and this won't be needed when porting to PolicyKit 1.0 anyway ++ */ ++ g_simple_async_result_set_from_error (simple, error); ++ g_error_free (error); ++ } ++ ++ g_simple_async_result_complete (simple); ++ g_object_unref (simple); ++} ++ ++static void ++g_gdu_drive_poll_for_media (GDrive *_drive, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data) ++{ ++ GGduDrive *drive = G_GDU_DRIVE (_drive); ++ GSimpleAsyncResult *simple; ++ GduDevice *device; ++ ++ device = gdu_presentable_get_device (drive->presentable); ++ if (device == NULL) ++ { ++ simple = g_simple_async_result_new_error (G_OBJECT (drive), ++ callback, ++ user_data, ++ G_IO_ERROR, ++ G_IO_ERROR_FAILED, ++ "Device is not active"); ++ g_simple_async_result_complete_in_idle (simple); ++ g_object_unref (simple); ++ } ++ else ++ { ++ simple = g_simple_async_result_new (G_OBJECT (drive), ++ callback, ++ user_data, ++ NULL); ++ ++ gdu_device_op_drive_poll_media (device, poll_media_cb, simple); ++ g_object_unref (device); ++ } ++} ++ ++static gboolean ++g_gdu_drive_poll_for_media_finish (GDrive *drive, ++ GAsyncResult *result, ++ GError **error) ++{ ++ return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error); ++} ++ ++static char * ++g_gdu_drive_get_identifier (GDrive *_drive, ++ const char *kind) ++{ ++ GGduDrive *drive = G_GDU_DRIVE (_drive); ++ gchar *id; ++ ++ id = NULL; ++ ++ if (drive->device_file != NULL) ++ { ++ if (strcmp (kind, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE) == 0) ++ id = g_strdup (drive->device_file); ++ } ++ ++ return id; ++} ++ ++static char ** ++g_gdu_drive_enumerate_identifiers (GDrive *_drive) ++{ ++ GGduDrive *drive = G_GDU_DRIVE (_drive); ++ GPtrArray *p; ++ ++ p = g_ptr_array_new (); ++ if (drive->device_file != NULL) ++ g_ptr_array_add (p, g_strdup (G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE)); ++ g_ptr_array_add (p, NULL); ++ ++ return (gchar **) g_ptr_array_free (p, FALSE); ++} ++ ++static void ++g_gdu_drive_drive_iface_init (GDriveIface *iface) ++{ ++ iface->get_name = g_gdu_drive_get_name; ++ iface->get_icon = g_gdu_drive_get_icon; ++ iface->has_volumes = g_gdu_drive_has_volumes; ++ iface->get_volumes = g_gdu_drive_get_volumes; ++ iface->is_media_removable = g_gdu_drive_is_media_removable; ++ iface->has_media = g_gdu_drive_has_media; ++ iface->is_media_check_automatic = g_gdu_drive_is_media_check_automatic; ++ iface->can_eject = g_gdu_drive_can_eject; ++ iface->can_poll_for_media = g_gdu_drive_can_poll_for_media; ++ iface->eject = g_gdu_drive_eject; ++ iface->eject_finish = g_gdu_drive_eject_finish; ++ iface->poll_for_media = g_gdu_drive_poll_for_media; ++ iface->poll_for_media_finish = g_gdu_drive_poll_for_media_finish; ++ iface->get_identifier = g_gdu_drive_get_identifier; ++ iface->enumerate_identifiers = g_gdu_drive_enumerate_identifiers; ++} ++ ++gboolean ++g_gdu_drive_has_device_file (GGduDrive *drive, ++ const gchar *device_file) ++{ ++ return g_strcmp0 (drive->device_file, device_file) == 0; ++} ++ ++gboolean ++g_gdu_drive_has_presentable (GGduDrive *drive, ++ GduPresentable *presentable) ++{ ++ return gdu_presentable_get_id (drive->presentable) == gdu_presentable_get_id (presentable); ++} ++ ++time_t ++g_gdu_drive_get_time_of_last_media_insertion (GGduDrive *drive) ++{ ++ return drive->time_of_last_media_insertion; ++} ++ ++GduPresentable * ++g_gdu_drive_get_presentable (GGduDrive *drive) ++{ ++ return drive->presentable; ++} +--- /dev/null 2009-03-02 15:45:22.087299058 -0500 ++++ monitor/gdu/ggdudrive.h 2009-03-01 23:48:26.000000000 -0500 +@@ -0,0 +1,69 @@ ++/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ ++/* gvfs - extensions for gio ++ * ++ * Copyright (C) 2006-2009 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General ++ * Public License along with this library; if not, write to the ++ * Free Software Foundation, Inc., 59 Temple Place, Suite 330, ++ * Boston, MA 02111-1307, USA. ++ * ++ * Author: David Zeuthen ++ */ ++ ++#ifndef __G_GDU_DRIVE_H__ ++#define __G_GDU_DRIVE_H__ ++ ++#include ++#include ++ ++#include "ggduvolumemonitor.h" ++ ++G_BEGIN_DECLS ++ ++#define G_TYPE_GDU_DRIVE (g_gdu_drive_get_type ()) ++#define G_GDU_DRIVE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_GDU_DRIVE, GGduDrive)) ++#define G_GDU_DRIVE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_GDU_DRIVE, GGduDriveClass)) ++#define G_IS_GDU_DRIVE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_GDU_DRIVE)) ++#define G_IS_GDU_DRIVE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_GDU_DRIVE)) ++ ++typedef struct _GGduDriveClass GGduDriveClass; ++ ++struct _GGduDriveClass { ++ GObjectClass parent_class; ++}; ++ ++GType g_gdu_drive_get_type (void) G_GNUC_CONST; ++ ++GGduDrive *g_gdu_drive_new (GVolumeMonitor *volume_monitor, ++ GduPresentable *presentable); ++void g_gdu_drive_set_volume (GGduDrive *drive, ++ GGduVolume *volume); ++void g_gdu_drive_unset_volume (GGduDrive *drive, ++ GGduVolume *volume); ++void g_gdu_drive_disconnected (GGduDrive *drive); ++gboolean g_gdu_drive_has_device_file (GGduDrive *drive, ++ const gchar *device_file); ++time_t g_gdu_drive_get_time_of_last_media_insertion (GGduDrive *drive); ++ ++gboolean g_gdu_drive_has_presentable (GGduDrive *drive, ++ GduPresentable *presentable); ++ ++GduPresentable *g_gdu_drive_get_presentable (GGduDrive *drive); ++ ++ ++char * _drive_get_icon (GduDevice *d); ++ ++G_END_DECLS ++ ++#endif /* __G_GDU_DRIVE_H__ */ +--- /dev/null 2009-03-02 15:45:22.087299058 -0500 ++++ monitor/gdu/ggdumount.c 2009-03-02 14:18:23.000000000 -0500 +@@ -0,0 +1,958 @@ ++/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ ++/* gvfs - extensions for gio ++ * ++ * Copyright (C) 2006-2009 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General ++ * Public License along with this library; if not, write to the ++ * Free Software Foundation, Inc., 59 Temple Place, Suite 330, ++ * Boston, MA 02111-1307, USA. ++ * ++ * Author: David Zeuthen ++ */ ++ ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++ ++#include "ggduvolumemonitor.h" ++#include "ggdumount.h" ++#include "ggduvolume.h" ++ ++struct _GGduMount ++{ ++ GObject parent; ++ ++ GVolumeMonitor *volume_monitor; /* owned by volume monitor */ ++ GGduVolume *volume; /* owned by volume monitor */ ++ ++ /* the following members need to be set upon construction */ ++ GFile *root; ++ GIcon *icon; ++ gchar *name; ++ gchar *uuid; ++ gchar *device_file; ++ gchar *mount_path; ++ gboolean can_unmount; ++ ++ gchar *mount_entry_name; ++ GIcon *mount_entry_icon; ++ ++ gboolean is_burn_mount; ++ ++ GIcon *autorun_icon; ++ gboolean searched_for_autorun; ++ ++ gchar *xdg_volume_info_name; ++ GIcon *xdg_volume_info_icon; ++ gboolean searched_for_xdg_volume_info; ++}; ++ ++static gboolean update_mount (GGduMount *mount); ++ ++static void g_gdu_mount_mount_iface_init (GMountIface *iface); ++ ++G_DEFINE_TYPE_EXTENDED (GGduMount, g_gdu_mount, G_TYPE_OBJECT, 0, ++ G_IMPLEMENT_INTERFACE (G_TYPE_MOUNT, ++ g_gdu_mount_mount_iface_init)) ++ ++static void ++volume_changed (GVolume *volume, ++ gpointer user_data); ++ ++static void ++g_gdu_mount_finalize (GObject *object) ++{ ++ GGduMount *mount; ++ ++ mount = G_GDU_MOUNT (object); ++ ++ if (mount->volume != NULL) ++ { ++ g_signal_handlers_disconnect_by_func (mount->volume, volume_changed, mount); ++ g_gdu_volume_unset_mount (mount->volume, mount); ++ } ++ ++ if (mount->root != NULL) ++ g_object_unref (mount->root); ++ if (mount->icon != NULL) ++ g_object_unref (mount->icon); ++ g_free (mount->name); ++ g_free (mount->uuid); ++ g_free (mount->device_file); ++ g_free (mount->mount_path); ++ ++ g_free (mount->mount_entry_name); ++ if (mount->mount_entry_icon != NULL) ++ g_object_unref (mount->mount_entry_icon); ++ ++ if (mount->autorun_icon != NULL) ++ g_object_unref (mount->autorun_icon); ++ ++ g_free (mount->xdg_volume_info_name); ++ if (mount->xdg_volume_info_icon != NULL) ++ g_object_unref (mount->xdg_volume_info_icon); ++ ++ if (G_OBJECT_CLASS (g_gdu_mount_parent_class)->finalize) ++ (*G_OBJECT_CLASS (g_gdu_mount_parent_class)->finalize) (object); ++} ++ ++static void ++g_gdu_mount_class_init (GGduMountClass *klass) ++{ ++ GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ++ ++ gobject_class->finalize = g_gdu_mount_finalize; ++} ++ ++static void ++g_gdu_mount_init (GGduMount *mount) ++{ ++} ++ ++static void ++emit_changed (GGduMount *mount) ++{ ++ g_signal_emit_by_name (mount, "changed"); ++ g_signal_emit_by_name (mount->volume_monitor, "mount_changed", mount); ++} ++ ++static void ++got_autorun_info_cb (GObject *source_object, ++ GAsyncResult *res, ++ gpointer user_data) ++{ ++ GGduMount *mount = G_GDU_MOUNT (user_data); ++ ++ mount->autorun_icon = g_vfs_mount_info_query_autorun_info_finish (G_FILE (source_object), ++ res, ++ NULL); ++ ++ if (update_mount (mount)) ++ emit_changed (mount); ++ ++ g_object_unref (mount); ++} ++ ++static void ++got_xdg_volume_info_cb (GObject *source_object, ++ GAsyncResult *res, ++ gpointer user_data) ++{ ++ GGduMount *mount = G_GDU_MOUNT (user_data); ++ ++ mount->xdg_volume_info_icon = g_vfs_mount_info_query_xdg_volume_info_finish (G_FILE (source_object), ++ res, ++ &(mount->xdg_volume_info_name), ++ NULL); ++ if (update_mount (mount)) ++ emit_changed (mount); ++ ++ g_object_unref (mount); ++} ++ ++static gboolean ++update_mount (GGduMount *mount) ++{ ++ gboolean changed; ++ gboolean old_can_unmount; ++ gchar *old_name; ++ GIcon *old_icon; ++ ++ /* save old values */ ++ old_can_unmount = mount->can_unmount; ++ old_name = g_strdup (mount->name); ++ old_icon = mount->icon != NULL ? g_object_ref (mount->icon) : NULL; ++ ++ /* in with the new */ ++ if (mount->volume != NULL) ++ { ++ mount->can_unmount = TRUE; ++ ++ if (mount->icon != NULL) ++ g_object_unref (mount->icon); ++ ++ /* order of preference: xdg, autorun, probed */ ++ if (mount->xdg_volume_info_icon != NULL) ++ mount->icon = g_object_ref (mount->xdg_volume_info_icon); ++ else if (mount->autorun_icon != NULL) ++ mount->icon = g_object_ref (mount->autorun_icon); ++ else ++ mount->icon = g_volume_get_icon (G_VOLUME (mount->volume)); ++ ++ g_free (mount->name); ++ ++ /* order of preference : xdg, probed */ ++ if (mount->xdg_volume_info_name != NULL) ++ mount->name = g_strdup (mount->xdg_volume_info_name); ++ else ++ mount->name = g_volume_get_name (G_VOLUME (mount->volume)); ++ } ++ else ++ { ++ mount->can_unmount = TRUE; ++ ++ if (mount->icon != NULL) ++ g_object_unref (mount->icon); ++ ++ /* order of preference: xdg, autorun, probed */ ++ if (mount->xdg_volume_info_icon != NULL) ++ mount->icon = g_object_ref (mount->xdg_volume_info_icon); ++ else if (mount->autorun_icon != NULL) ++ mount->icon = g_object_ref (mount->autorun_icon); ++ else ++ mount->icon = mount->mount_entry_icon != NULL ? g_object_ref (mount->mount_entry_icon) : NULL; ++ ++ g_free (mount->name); ++ ++ /* order of preference : xdg, probed */ ++ if (mount->xdg_volume_info_name != NULL) ++ mount->name = g_strdup (mount->xdg_volume_info_name); ++ else ++ mount->name = g_strdup (mount->mount_entry_name); ++ } ++ ++ /* compute whether something changed */ ++ changed = !((old_can_unmount == mount->can_unmount) && ++ (g_strcmp0 (old_name, mount->name) == 0) && ++ g_icon_equal (old_icon, mount->icon) ++ ); ++ ++ /* free old values */ ++ g_free (old_name); ++ if (old_icon != NULL) ++ g_object_unref (old_icon); ++ ++ /*g_debug ("in update_mount(), changed=%d", changed);*/ ++ ++ /* search for .xdg-volume-info */ ++ if (!mount->searched_for_xdg_volume_info) ++ { ++ mount->searched_for_xdg_volume_info = TRUE; ++ g_vfs_mount_info_query_xdg_volume_info (mount->root, ++ NULL, ++ got_xdg_volume_info_cb, ++ g_object_ref (mount)); ++ } ++ ++ /* search for autorun.inf */ ++ if (!mount->searched_for_autorun) ++ { ++ mount->searched_for_autorun = TRUE; ++ g_vfs_mount_info_query_autorun_info (mount->root, ++ NULL, ++ got_autorun_info_cb, ++ g_object_ref (mount)); ++ } ++ ++ return changed; ++} ++ ++static void ++volume_changed (GVolume *volume, ++ gpointer user_data) ++{ ++ GGduMount *mount = G_GDU_MOUNT (user_data); ++ ++ if (update_mount (mount)) ++ emit_changed (mount); ++} ++ ++GGduMount * ++g_gdu_mount_new (GVolumeMonitor *volume_monitor, ++ GUnixMountEntry *mount_entry, ++ GGduVolume *volume) ++{ ++ GGduMount *mount; ++ ++ mount = NULL; ++ ++ /* Ignore internal mounts unless there's a volume */ ++ if (volume == NULL && (mount_entry != NULL && !g_unix_mount_guess_should_display (mount_entry))) ++ goto out; ++ ++ mount = g_object_new (G_TYPE_GDU_MOUNT, NULL); ++ mount->volume_monitor = volume_monitor; ++ g_object_add_weak_pointer (G_OBJECT (volume_monitor), (gpointer) &(mount->volume_monitor)); ++ ++ if (mount_entry != NULL) ++ { ++ /* No ref on GUnixMountEntry so save values for later use */ ++ mount->mount_entry_name = g_unix_mount_guess_name (mount_entry); ++ mount->mount_entry_icon = g_unix_mount_guess_icon (mount_entry); ++ mount->device_file = g_strdup (g_unix_mount_get_device_path (mount_entry)); ++ mount->mount_path = g_strdup (g_unix_mount_get_mount_path (mount_entry)); ++ mount->root = g_file_new_for_path (mount->mount_path); ++ } ++ else ++ { ++ /* burn:/// mount (the only mounts we support with mount_entry == NULL) */ ++ mount->device_file = NULL; ++ mount->mount_path = NULL; ++ mount->root = g_file_new_for_uri ("burn:///"); ++ mount->is_burn_mount = TRUE; ++ } ++ ++ /* need to set the volume only when the mount is fully constructed */ ++ mount->volume = volume; ++ if (mount->volume != NULL) ++ { ++ g_gdu_volume_set_mount (volume, mount); ++ /* this is for piggy backing on the name and icon of the associated volume */ ++ g_signal_connect (mount->volume, "changed", G_CALLBACK (volume_changed), mount); ++ } ++ ++ update_mount (mount); ++ ++ out: ++ ++ return mount; ++} ++ ++void ++g_gdu_mount_unmounted (GGduMount *mount) ++{ ++ if (mount->volume != NULL) ++ { ++ g_gdu_volume_unset_mount (mount->volume, mount); ++ g_signal_handlers_disconnect_by_func (mount->volume, volume_changed, mount); ++ mount->volume = NULL; ++ emit_changed (mount); ++ } ++} ++ ++void ++g_gdu_mount_unset_volume (GGduMount *mount, ++ GGduVolume *volume) ++{ ++ if (mount->volume == volume) ++ { ++ g_signal_handlers_disconnect_by_func (mount->volume, volume_changed, mount); ++ mount->volume = NULL; ++ emit_changed (mount); ++ } ++} ++ ++static GFile * ++g_gdu_mount_get_root (GMount *_mount) ++{ ++ GGduMount *mount = G_GDU_MOUNT (_mount); ++ return mount->root != NULL ? g_object_ref (mount->root) : NULL; ++} ++ ++static GIcon * ++g_gdu_mount_get_icon (GMount *_mount) ++{ ++ GGduMount *mount = G_GDU_MOUNT (_mount); ++ return mount->icon != NULL ? g_object_ref (mount->icon) : NULL; ++} ++ ++static gchar * ++g_gdu_mount_get_uuid (GMount *_mount) ++{ ++ GGduMount *mount = G_GDU_MOUNT (_mount); ++ return g_strdup (mount->uuid); ++} ++ ++static gchar * ++g_gdu_mount_get_name (GMount *_mount) ++{ ++ GGduMount *mount = G_GDU_MOUNT (_mount); ++ return g_strdup (mount->name); ++} ++ ++gboolean ++g_gdu_mount_has_uuid (GGduMount *_mount, ++ const gchar *uuid) ++{ ++ GGduMount *mount = G_GDU_MOUNT (_mount); ++ return g_strcmp0 (mount->uuid, uuid) == 0; ++} ++ ++gboolean ++g_gdu_mount_has_mount_path (GGduMount *_mount, ++ const gchar *mount_path) ++{ ++ GGduMount *mount = G_GDU_MOUNT (_mount); ++ return g_strcmp0 (mount->mount_path, mount_path) == 0; ++} ++ ++static GDrive * ++g_gdu_mount_get_drive (GMount *_mount) ++{ ++ GGduMount *mount = G_GDU_MOUNT (_mount); ++ GDrive *drive; ++ ++ drive = NULL; ++ if (mount->volume != NULL) ++ drive = g_volume_get_drive (G_VOLUME (mount->volume)); ++ ++ return drive; ++} ++ ++static GVolume * ++g_gdu_mount_get_volume (GMount *_mount) ++{ ++ GGduMount *mount = G_GDU_MOUNT (_mount); ++ GVolume *volume; ++ ++ volume = NULL; ++ if (mount->volume) ++ volume = G_VOLUME (g_object_ref (mount->volume)); ++ ++ return volume; ++} ++ ++static gboolean ++g_gdu_mount_can_unmount (GMount *_mount) ++{ ++ GGduMount *mount = G_GDU_MOUNT (_mount); ++ return mount->can_unmount; ++} ++ ++static gboolean ++g_gdu_mount_can_eject (GMount *_mount) ++{ ++ GGduMount *mount = G_GDU_MOUNT (_mount); ++ GDrive *drive; ++ gboolean can_eject; ++ ++ can_eject = FALSE; ++ if (mount->volume != NULL) ++ { ++ drive = g_volume_get_drive (G_VOLUME (mount->volume)); ++ if (drive != NULL) ++ can_eject = g_drive_can_eject (drive); ++ } ++ ++ return can_eject; ++} ++ ++/* ---------------------------------------------------------------------------------------------------- */ ++ ++typedef struct { ++ GMount *mount; ++ GAsyncReadyCallback callback; ++ gpointer user_data; ++ GCancellable *cancellable; ++ int error_fd; ++ GIOChannel *error_channel; ++ guint error_channel_source_id; ++ GString *error_string; ++} UnmountEjectOp; ++ ++static void ++eject_unmount_cb (GPid pid, gint status, gpointer user_data) ++{ ++ UnmountEjectOp *data = user_data; ++ GSimpleAsyncResult *simple; ++ ++ if (WEXITSTATUS (status) != 0) ++ { ++ GError *error; ++ error = g_error_new_literal (G_IO_ERROR, ++ G_IO_ERROR_FAILED, ++ data->error_string->str); ++ simple = g_simple_async_result_new_from_error (G_OBJECT (data->mount), ++ data->callback, ++ data->user_data, ++ error); ++ g_error_free (error); ++ } ++ else ++ { ++ simple = g_simple_async_result_new (G_OBJECT (data->mount), ++ data->callback, ++ data->user_data, ++ NULL); ++ } ++ ++ g_simple_async_result_complete (simple); ++ g_object_unref (simple); ++ ++ g_source_remove (data->error_channel_source_id); ++ g_io_channel_unref (data->error_channel); ++ g_string_free (data->error_string, TRUE); ++ close (data->error_fd); ++ g_spawn_close_pid (pid); ++ g_free (data); ++} ++ ++static gboolean ++eject_unmount_read_error (GIOChannel *channel, ++ GIOCondition condition, ++ gpointer user_data) ++{ ++ UnmountEjectOp *data = user_data; ++ gchar buf[BUFSIZ]; ++ gsize bytes_read; ++ GError *error; ++ GIOStatus status; ++ ++ error = NULL; ++read: ++ status = g_io_channel_read_chars (channel, buf, sizeof (buf), &bytes_read, &error); ++ if (status == G_IO_STATUS_NORMAL) ++ { ++ g_string_append_len (data->error_string, buf, bytes_read); ++ if (bytes_read == sizeof (buf)) ++ goto read; ++ } ++ else if (status == G_IO_STATUS_EOF) ++ g_string_append_len (data->error_string, buf, bytes_read); ++ else if (status == G_IO_STATUS_ERROR) ++ { ++ if (data->error_string->len > 0) ++ g_string_append (data->error_string, "\n"); ++ ++ g_string_append (data->error_string, error->message); ++ g_error_free (error); ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ ++static void ++eject_unmount_do (GMount *mount, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data, ++ char **argv) ++{ ++ UnmountEjectOp *data; ++ GPid child_pid; ++ GError *error; ++ ++ data = g_new0 (UnmountEjectOp, 1); ++ data->mount = mount; ++ data->callback = callback; ++ data->user_data = user_data; ++ data->cancellable = cancellable; ++ ++ error = NULL; ++ if (!g_spawn_async_with_pipes (NULL, /* working dir */ ++ argv, ++ NULL, /* envp */ ++ G_SPAWN_DO_NOT_REAP_CHILD|G_SPAWN_SEARCH_PATH, ++ NULL, /* child_setup */ ++ NULL, /* user_data for child_setup */ ++ &child_pid, ++ NULL, /* standard_input */ ++ NULL, /* standard_output */ ++ &(data->error_fd), ++ &error)) { ++ g_assert (error != NULL); ++ goto handle_error; ++ } ++ ++ data->error_string = g_string_new (""); ++ ++ data->error_channel = g_io_channel_unix_new (data->error_fd); ++ g_io_channel_set_flags (data->error_channel, G_IO_FLAG_NONBLOCK, &error); ++ if (error != NULL) ++ goto handle_error; ++ ++ data->error_channel_source_id = g_io_add_watch (data->error_channel, G_IO_IN, eject_unmount_read_error, data); ++ g_child_watch_add (child_pid, eject_unmount_cb, data); ++ ++handle_error: ++ ++ if (error != NULL) ++ { ++ GSimpleAsyncResult *simple; ++ simple = g_simple_async_result_new_from_error (G_OBJECT (data->mount), ++ data->callback, ++ data->user_data, ++ error); ++ g_simple_async_result_complete (simple); ++ g_object_unref (simple); ++ ++ if (data->error_string != NULL) ++ g_string_free (data->error_string, TRUE); ++ ++ if (data->error_channel != NULL) ++ g_io_channel_unref (data->error_channel); ++ ++ g_error_free (error); ++ g_free (data); ++ } ++} ++ ++/* ---------------------------------------------------------------------------------------------------- */ ++static void ++luks_lock_cb (GduDevice *device, ++ GError *error, ++ gpointer user_data) ++{ ++ GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data); ++ ++ if (error != NULL) ++ { ++ /* We could handle PolicyKit integration here but this action is allowed by default ++ * and this won't be needed when porting to PolicyKit 1.0 anyway ++ */ ++ g_simple_async_result_set_from_error (simple, error); ++ g_error_free (error); ++ } ++ ++ g_simple_async_result_complete (simple); ++ g_object_unref (simple); ++} ++ ++static void ++unmount_cb (GduDevice *device, ++ GError *error, ++ gpointer user_data) ++{ ++ GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data); ++ ++ if (error != NULL) ++ { ++ /* We could handle PolicyKit integration here but this action is allowed by default ++ * and this won't be needed when porting to PolicyKit 1.0 anyway ++ */ ++ g_simple_async_result_set_from_error (simple, error); ++ g_error_free (error); ++ g_simple_async_result_complete (simple); ++ g_object_unref (simple); ++ goto out; ++ } ++ ++ /* if volume is a cleartext LUKS block device, then also lock this one */ ++ if (gdu_device_is_luks_cleartext (device)) ++ { ++ const gchar *luks_cleartext_slave_object_path; ++ GduDevice *luks_cleartext_slave; ++ GduPool *pool; ++ ++ luks_cleartext_slave_object_path = gdu_device_luks_cleartext_get_slave (device); ++ if (luks_cleartext_slave_object_path == NULL) ++ { ++ g_simple_async_result_set_error (simple, ++ G_IO_ERROR, ++ G_IO_ERROR_FAILED, ++ "Cannot get LUKS cleartext slave"); ++ g_simple_async_result_complete (simple); ++ g_object_unref (simple); ++ goto out; ++ } ++ ++ pool = gdu_device_get_pool (device); ++ luks_cleartext_slave = gdu_pool_get_by_object_path (pool, luks_cleartext_slave_object_path); ++ g_object_unref (pool); ++ ++ if (luks_cleartext_slave == NULL) ++ { ++ g_simple_async_result_set_error (simple, ++ G_IO_ERROR, ++ G_IO_ERROR_FAILED, ++ "Cannot get LUKS cleartext slave"); ++ g_simple_async_result_complete (simple); ++ g_object_unref (simple); ++ goto out; ++ } ++ ++ gdu_device_op_luks_lock (luks_cleartext_slave, ++ luks_lock_cb, ++ simple); ++ ++ g_object_unref (luks_cleartext_slave); ++ goto out; ++ } ++ ++ g_simple_async_result_complete (simple); ++ g_object_unref (simple); ++ ++ out: ++ ; ++} ++ ++static void ++g_gdu_mount_unmount (GMount *_mount, ++ GMountUnmountFlags flags, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data) ++{ ++ GGduMount *mount = G_GDU_MOUNT (_mount); ++ GSimpleAsyncResult *simple; ++ ++ if (mount->volume == NULL) ++ { ++ gchar *argv[] = {"umount", NULL, NULL}; ++ ++ /* TODO: honor flags */ ++ ++ if (mount->mount_path != NULL) ++ argv[1] = mount->mount_path; ++ else ++ argv[1] = mount->device_file; ++ ++ eject_unmount_do (_mount, cancellable, callback, user_data, argv); ++ } ++ else ++ { ++ simple = g_simple_async_result_new (G_OBJECT (mount), ++ callback, ++ user_data, ++ NULL); ++ ++ if (mount->is_burn_mount) ++ { ++ /* burn mounts are really never mounted... */ ++ g_simple_async_result_complete (simple); ++ g_object_unref (simple); ++ } ++ else ++ { ++ GduDevice *device; ++ GduPresentable *volume; ++ ++ /* TODO: honor flags */ ++ ++ volume = g_gdu_volume_get_presentable_with_cleartext (mount->volume); ++ device = gdu_presentable_get_device (volume); ++ ++ gdu_device_op_filesystem_unmount (device, unmount_cb, simple); ++ ++ g_object_unref (device); ++ } ++ } ++} ++ ++static gboolean ++g_gdu_mount_unmount_finish (GMount *mount, ++ GAsyncResult *result, ++ GError **error) ++{ ++ return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error); ++} ++ ++typedef struct { ++ GObject *object; ++ GAsyncReadyCallback callback; ++ gpointer user_data; ++} EjectWrapperOp; ++ ++static void ++eject_wrapper_callback (GObject *source_object, ++ GAsyncResult *res, ++ gpointer user_data) ++{ ++ EjectWrapperOp *data = user_data; ++ data->callback (data->object, res, data->user_data); ++ g_object_unref (data->object); ++ g_free (data); ++} ++ ++static void ++g_gdu_mount_eject (GMount *mount, ++ GMountUnmountFlags flags, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data) ++{ ++ GGduMount *gdu_mount = G_GDU_MOUNT (mount); ++ GDrive *drive; ++ ++ drive = NULL; ++ if (gdu_mount->volume != NULL) ++ drive = g_volume_get_drive (G_VOLUME (gdu_mount->volume)); ++ ++ if (drive != NULL) ++ { ++ EjectWrapperOp *data; ++ data = g_new0 (EjectWrapperOp, 1); ++ data->object = g_object_ref (mount); ++ data->callback = callback; ++ data->user_data = user_data; ++ g_drive_eject (drive, flags, cancellable, eject_wrapper_callback, data); ++ g_object_unref (drive); ++ } ++ else ++ { ++ GSimpleAsyncResult *simple; ++ simple = g_simple_async_result_new_error (G_OBJECT (mount), ++ callback, ++ user_data, ++ G_IO_ERROR, ++ G_IO_ERROR_FAILED, ++ _("Operation not supported by backend")); ++ g_simple_async_result_complete (simple); ++ g_object_unref (simple); ++ } ++ ++} ++ ++static gboolean ++g_gdu_mount_eject_finish (GMount *_mount, ++ GAsyncResult *result, ++ GError **error) ++{ ++ GGduMount *mount = G_GDU_MOUNT (_mount); ++ GDrive *drive; ++ gboolean res; ++ ++ res = TRUE; ++ ++ drive = NULL; ++ if (mount->volume != NULL) ++ drive = g_volume_get_drive (G_VOLUME (mount->volume)); ++ ++ if (drive != NULL) ++ { ++ res = g_drive_eject_finish (drive, result, error); ++ g_object_unref (drive); ++ } ++ else ++ { ++ g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error); ++ res = FALSE; ++ } ++ ++ return res; ++} ++ ++/* TODO: handle force_rescan */ ++static gchar ** ++g_gdu_mount_guess_content_type_sync (GMount *_mount, ++ gboolean force_rescan, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ GGduMount *mount = G_GDU_MOUNT (_mount); ++ const gchar *disc_type; ++ char **x_content_types; ++ GPtrArray *p; ++ gchar **result; ++ GduDevice *device; ++ guint n; ++ ++ p = g_ptr_array_new (); ++ ++ device = NULL; ++ if (mount->volume != NULL) ++ { ++ GduPresentable *presentable; ++ presentable = g_gdu_volume_get_presentable_with_cleartext (mount->volume); ++ device = gdu_presentable_get_device (presentable); ++ } ++ ++ /* doesn't make sense to probe blank discs - look at the disc type instead */ ++ if (device != NULL && gdu_device_optical_disc_get_is_blank (device)) ++ { ++ disc_type = gdu_device_drive_get_media (device); ++ if (disc_type != NULL) ++ { ++ if (g_str_has_prefix (disc_type, "optical_dvd")) ++ g_ptr_array_add (p, g_strdup ("x-content/blank-dvd")); ++ else if (g_str_has_prefix (disc_type, "optical_hddvd")) ++ g_ptr_array_add (p, g_strdup ("x-content/blank-hddvd")); ++ else if (g_str_has_prefix (disc_type, "optical_bd")) ++ g_ptr_array_add (p, g_strdup ("x-content/blank-bd")); ++ else ++ g_ptr_array_add (p, g_strdup ("x-content/blank-cd")); /* assume CD */ ++ } ++ } ++ else ++ { ++ /* sniff content type */ ++ x_content_types = g_content_type_guess_for_tree (mount->root); ++ if (x_content_types != NULL) ++ { ++ for (n = 0; x_content_types[n] != NULL; n++) ++ g_ptr_array_add (p, g_strdup (x_content_types[n])); ++ g_strfreev (x_content_types); ++ } ++ } ++ ++ if (p->len == 0) ++ { ++ result = NULL; ++ g_ptr_array_free (p, TRUE); ++ } ++ else ++ { ++ g_ptr_array_add (p, NULL); ++ result = (char **) g_ptr_array_free (p, FALSE); ++ } ++ ++ if (device != NULL) ++ g_object_unref (device); ++ ++ return result; ++} ++ ++/* since we're an out-of-process volume monitor we'll just do this sync */ ++static void ++g_gdu_mount_guess_content_type (GMount *mount, ++ gboolean force_rescan, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data) ++{ ++ GSimpleAsyncResult *simple; ++ ++ /* TODO: handle force_rescan */ ++ simple = g_simple_async_result_new (G_OBJECT (mount), ++ callback, ++ user_data, ++ NULL); ++ g_simple_async_result_complete (simple); ++ g_object_unref (simple); ++} ++ ++static gchar ** ++g_gdu_mount_guess_content_type_finish (GMount *mount, ++ GAsyncResult *result, ++ GError **error) ++{ ++ return g_gdu_mount_guess_content_type_sync (mount, FALSE, NULL, error); ++} ++ ++static void ++g_gdu_mount_mount_iface_init (GMountIface *iface) ++{ ++ iface->get_root = g_gdu_mount_get_root; ++ iface->get_name = g_gdu_mount_get_name; ++ iface->get_icon = g_gdu_mount_get_icon; ++ iface->get_uuid = g_gdu_mount_get_uuid; ++ iface->get_drive = g_gdu_mount_get_drive; ++ iface->get_volume = g_gdu_mount_get_volume; ++ iface->can_unmount = g_gdu_mount_can_unmount; ++ iface->can_eject = g_gdu_mount_can_eject; ++ iface->unmount = g_gdu_mount_unmount; ++ iface->unmount_finish = g_gdu_mount_unmount_finish; ++ iface->eject = g_gdu_mount_eject; ++ iface->eject_finish = g_gdu_mount_eject_finish; ++ iface->guess_content_type = g_gdu_mount_guess_content_type; ++ iface->guess_content_type_finish = g_gdu_mount_guess_content_type_finish; ++ iface->guess_content_type_sync = g_gdu_mount_guess_content_type_sync; ++} ++ ++gboolean ++g_gdu_mount_has_volume (GGduMount *mount, ++ GGduVolume *volume) ++{ ++ return mount->volume == volume; ++} +--- /dev/null 2009-03-02 15:45:22.087299058 -0500 ++++ monitor/gdu/ggdumount.h 2009-03-01 23:48:16.000000000 -0500 +@@ -0,0 +1,64 @@ ++/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ ++/* gvfs - extensions for gio ++ * ++ * Copyright (C) 2006-2009 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General ++ * Public License along with this library; if not, write to the ++ * Free Software Foundation, Inc., 59 Temple Place, Suite 330, ++ * Boston, MA 02111-1307, USA. ++ * ++ * Author: David Zeuthen ++ */ ++ ++#ifndef __G_GDU_MOUNT_H__ ++#define __G_GDU_MOUNT_H__ ++ ++#include ++#include ++ ++#include "ggduvolumemonitor.h" ++ ++G_BEGIN_DECLS ++ ++#define G_TYPE_GDU_MOUNT (g_gdu_mount_get_type ()) ++#define G_GDU_MOUNT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_GDU_MOUNT, GGduMount)) ++#define G_GDU_MOUNT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_GDU_MOUNT, GGduMountClass)) ++#define G_IS_GDU_MOUNT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_GDU_MOUNT)) ++#define G_IS_GDU_MOUNT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_GDU_MOUNT)) ++ ++typedef struct _GGduMountClass GGduMountClass; ++ ++struct _GGduMountClass { ++ GObjectClass parent_class; ++}; ++ ++GType g_gdu_mount_get_type (void) G_GNUC_CONST; ++ ++GGduMount * g_gdu_mount_new (GVolumeMonitor *volume_monitor, ++ GUnixMountEntry *mount_entry, ++ GGduVolume *volume); ++gboolean g_gdu_mount_has_mount_path (GGduMount *mount, ++ const gchar *mount_path); ++gboolean g_gdu_mount_has_uuid (GGduMount *mount, ++ const gchar *uuid); ++void g_gdu_mount_unset_volume (GGduMount *mount, ++ GGduVolume *volume); ++void g_gdu_mount_unmounted (GGduMount *mount); ++ ++gboolean g_gdu_mount_has_volume (GGduMount *mount, ++ GGduVolume *volume); ++ ++G_END_DECLS ++ ++#endif /* __G_GDU_MOUNT_H__ */ +--- /dev/null 2009-03-02 15:45:22.087299058 -0500 ++++ monitor/gdu/ggduvolume.c 2009-03-02 14:37:46.000000000 -0500 +@@ -0,0 +1,1408 @@ ++/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ ++/* gvfs - extensions for gio ++ * ++ * Copyright (C) 2006-2009 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General ++ * Public License along with this library; if not, write to the ++ * Free Software Foundation, Inc., 59 Temple Place, Suite 330, ++ * Boston, MA 02111-1307, USA. ++ * ++ * Author: David Zeuthen ++ */ ++ ++#include ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include "ggdudrive.h" ++#include "ggduvolume.h" ++#include "ggdumount.h" ++ ++#include "polkit.h" ++ ++typedef struct MountOpData MountOpData; ++ ++static void cancel_pending_mount_op (MountOpData *data); ++ ++struct _GGduVolume ++{ ++ GObject parent; ++ ++ GVolumeMonitor *volume_monitor; /* owned by volume monitor */ ++ GGduMount *mount; /* owned by volume monitor */ ++ GGduDrive *drive; /* owned by volume monitor */ ++ ++ GduVolume *gdu_volume; ++ ++ /* if the volume is encrypted, this is != NULL when unlocked */ ++ GduVolume *cleartext_gdu_volume; ++ ++ /* If a mount operation is in progress, then pending_mount_op is != NULL. This ++ * is used to cancel the operation to make possible authentication dialogs go ++ * away. ++ */ ++ MountOpData *pending_mount_op; ++ ++ /* the following members need to be set upon construction */ ++ GIcon *icon; ++ GFile *activation_root; ++ gchar *name; ++ gchar *device_file; ++ gchar *uuid; ++ gboolean can_mount; ++ gboolean should_automount; ++}; ++ ++static void g_gdu_volume_volume_iface_init (GVolumeIface *iface); ++ ++G_DEFINE_TYPE_EXTENDED (GGduVolume, g_gdu_volume, G_TYPE_OBJECT, 0, ++ G_IMPLEMENT_INTERFACE (G_TYPE_VOLUME, ++ g_gdu_volume_volume_iface_init)) ++ ++static void gdu_volume_changed (GduPresentable *presentable, ++ GGduVolume *volume); ++static void gdu_volume_job_changed (GduPresentable *presentable, ++ GGduVolume *volume); ++ ++static void gdu_cleartext_volume_removed (GduPresentable *presentable, ++ GGduVolume *volume); ++static void gdu_cleartext_volume_changed (GduPresentable *presentable, ++ GGduVolume *volume); ++static void gdu_cleartext_volume_job_changed (GduPresentable *presentable, ++ GGduVolume *volume); ++ ++static void mount_with_mount_operation (MountOpData *data); ++ ++static void ++g_gdu_volume_finalize (GObject *object) ++{ ++ GGduVolume *volume; ++ ++ volume = G_GDU_VOLUME (object); ++ ++ if (volume->mount != NULL) ++ g_gdu_mount_unset_volume (volume->mount, volume); ++ ++ if (volume->drive != NULL) ++ g_gdu_drive_unset_volume (volume->drive, volume); ++ ++ if (volume->gdu_volume != NULL) ++ { ++ g_signal_handlers_disconnect_by_func (volume->gdu_volume, gdu_volume_changed, volume); ++ g_signal_handlers_disconnect_by_func (volume->gdu_volume, gdu_volume_job_changed, volume); ++ g_object_unref (volume->gdu_volume); ++ } ++ ++ if (volume->cleartext_gdu_volume != NULL) ++ { ++ g_signal_handlers_disconnect_by_func (volume->cleartext_gdu_volume, gdu_cleartext_volume_removed, volume); ++ g_signal_handlers_disconnect_by_func (volume->cleartext_gdu_volume, gdu_cleartext_volume_changed, volume); ++ g_signal_handlers_disconnect_by_func (volume->cleartext_gdu_volume, gdu_cleartext_volume_job_changed, volume); ++ g_object_unref (volume->cleartext_gdu_volume); ++ } ++ ++ if (volume->icon != NULL) ++ g_object_unref (volume->icon); ++ if (volume->activation_root != NULL) ++ g_object_unref (volume->activation_root); ++ ++ g_free (volume->name); ++ g_free (volume->device_file); ++ g_free (volume->uuid); ++ ++ if (G_OBJECT_CLASS (g_gdu_volume_parent_class)->finalize) ++ (*G_OBJECT_CLASS (g_gdu_volume_parent_class)->finalize) (object); ++} ++ ++static void ++g_gdu_volume_class_init (GGduVolumeClass *klass) ++{ ++ GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ++ ++ gobject_class->finalize = g_gdu_volume_finalize; ++} ++ ++static void ++g_gdu_volume_init (GGduVolume *gdu_volume) ++{ ++} ++ ++static void ++emit_changed (GGduVolume *volume) ++{ ++ g_signal_emit_by_name (volume, "changed"); ++ g_signal_emit_by_name (volume->volume_monitor, "volume_changed", volume); ++} ++ ++static gboolean ++update_volume (GGduVolume *volume) ++{ ++ GduDevice *device; ++ GduPool *pool; ++ time_t now; ++ gboolean changed; ++ gboolean old_can_mount; ++ gboolean old_should_automount; ++ gchar *old_name; ++ gchar *old_device_file; ++ GIcon *old_icon; ++ gboolean keep_cleartext_volume; ++ ++ /* save old values */ ++ old_can_mount = volume->can_mount; ++ old_should_automount = volume->should_automount; ++ old_name = g_strdup (volume->name); ++ old_device_file = g_strdup (volume->device_file); ++ old_icon = volume->icon != NULL ? g_object_ref (volume->icon) : NULL; ++ ++ /* ---------------------------------------------------------------------------------------------------- */ ++ ++ /* in with the new */ ++ device = gdu_presentable_get_device (GDU_PRESENTABLE (volume->gdu_volume)); ++ pool = gdu_device_get_pool (device); ++ ++ keep_cleartext_volume = FALSE; ++ if (gdu_device_is_luks (device)) ++ { ++ const gchar *holder_objpath; ++ ++ holder_objpath = gdu_device_luks_get_holder (device); ++ if (holder_objpath != NULL && g_strcmp0 (holder_objpath, "/") != 0) ++ { ++ GduDevice *cleartext_device; ++ ++ cleartext_device = gdu_pool_get_by_object_path (pool, holder_objpath); ++ if (cleartext_device != NULL) ++ { ++ GduVolume *cleartext_gdu_volume; ++ ++ cleartext_gdu_volume = GDU_VOLUME (gdu_pool_get_volume_by_device (pool, cleartext_device)); ++ if (cleartext_gdu_volume != volume->cleartext_gdu_volume) ++ { ++ if (volume->cleartext_gdu_volume != NULL) ++ { ++ g_signal_handlers_disconnect_by_func (volume->cleartext_gdu_volume, gdu_cleartext_volume_removed, volume); ++ g_signal_handlers_disconnect_by_func (volume->cleartext_gdu_volume, gdu_cleartext_volume_changed, volume); ++ g_signal_handlers_disconnect_by_func (volume->cleartext_gdu_volume, gdu_cleartext_volume_job_changed, volume); ++ g_object_unref (volume->cleartext_gdu_volume); ++ } ++ ++ volume->cleartext_gdu_volume = g_object_ref (cleartext_gdu_volume); ++ g_signal_connect (volume->cleartext_gdu_volume, "removed", G_CALLBACK (gdu_cleartext_volume_removed), volume); ++ g_signal_connect (volume->cleartext_gdu_volume, "changed", G_CALLBACK (gdu_cleartext_volume_changed), volume); ++ g_signal_connect (volume->cleartext_gdu_volume, "job-changed", G_CALLBACK (gdu_cleartext_volume_job_changed), volume); ++ } ++ g_object_unref (cleartext_gdu_volume); ++ ++ g_object_unref (cleartext_device); ++ ++ keep_cleartext_volume = TRUE; ++ } ++ } ++ } ++ ++ if (!keep_cleartext_volume) ++ { ++ if (volume->cleartext_gdu_volume != NULL) ++ { ++ g_signal_handlers_disconnect_by_func (volume->cleartext_gdu_volume, gdu_cleartext_volume_removed, volume); ++ g_signal_handlers_disconnect_by_func (volume->cleartext_gdu_volume, gdu_cleartext_volume_changed, volume); ++ g_signal_handlers_disconnect_by_func (volume->cleartext_gdu_volume, gdu_cleartext_volume_job_changed, volume); ++ g_object_unref (volume->cleartext_gdu_volume); ++ volume->cleartext_gdu_volume = NULL; ++ } ++ } ++ ++ ++ /* Use data from cleartext LUKS volume if it is unlocked */ ++ if (volume->cleartext_gdu_volume != NULL) ++ { ++ GduDevice *luks_cleartext_volume_device; ++ ++ luks_cleartext_volume_device = gdu_presentable_get_device (GDU_PRESENTABLE (volume->cleartext_gdu_volume)); ++ ++ if (volume->icon != NULL) ++ g_object_unref (volume->icon); ++ volume->icon = gdu_presentable_get_icon (GDU_PRESENTABLE (volume->cleartext_gdu_volume)); ++ ++ g_free (volume->name); ++ volume->name = gdu_presentable_get_name (GDU_PRESENTABLE (volume->cleartext_gdu_volume)); ++ ++ g_free (volume->device_file); ++ volume->device_file = g_strdup (gdu_device_get_device_file (luks_cleartext_volume_device)); ++ ++ volume->can_mount = TRUE; ++ ++ volume->should_automount = FALSE; ++ ++ g_object_unref (luks_cleartext_volume_device); ++ } ++ else ++ { ++ gchar *activation_uri; ++ ++ if (volume->icon != NULL) ++ g_object_unref (volume->icon); ++ volume->icon = gdu_presentable_get_icon (GDU_PRESENTABLE (volume->gdu_volume)); ++ ++ g_free (volume->name); ++ volume->name = gdu_presentable_get_name (GDU_PRESENTABLE (volume->gdu_volume)); ++ ++ /* special case the name and icon for audio discs */ ++ activation_uri = volume->activation_root != NULL ? g_file_get_uri (volume->activation_root) : NULL; ++ if (activation_uri != NULL && g_str_has_prefix (activation_uri, "cdda://")) ++ { ++ if (volume->icon != NULL) ++ g_object_unref (volume->icon); ++ volume->icon = g_themed_icon_new_with_default_fallbacks ("media-optical-audio"); ++ g_free (volume->name); ++ volume->name = g_strdup (_("Audio Disc")); ++ } ++ ++ g_free (volume->device_file); ++ volume->device_file = g_strdup (gdu_device_get_device_file (device)); ++ ++ volume->can_mount = TRUE; ++ ++ /* If a volume (partition) appear _much later_ than when media was insertion it ++ * can only be because the media was repartitioned. We don't want to automount ++ * such volumes. ++ */ ++ volume->should_automount = TRUE; ++ if (volume->drive != NULL) ++ { ++ now = time (NULL); ++ if (now - g_gdu_drive_get_time_of_last_media_insertion (volume->drive) > 5) ++ volume->should_automount = FALSE; ++ } ++ ++ g_free (activation_uri); ++ } ++ ++ g_object_unref (pool); ++ g_object_unref (device); ++ ++ /* ---------------------------------------------------------------------------------------------------- */ ++ ++ /* compute whether something changed */ ++ changed = !((old_can_mount == volume->can_mount) && ++ (old_should_automount == volume->should_automount) && ++ (g_strcmp0 (old_name, volume->name) == 0) && ++ (g_strcmp0 (old_device_file, volume->device_file) == 0) && ++ g_icon_equal (old_icon, volume->icon) ++ ); ++ ++ /* free old values */ ++ g_free (old_name); ++ g_free (old_device_file); ++ if (old_icon != NULL) ++ g_object_unref (old_icon); ++ ++ /*g_debug ("in update_volume(), changed=%d", changed);*/ ++ ++ return changed; ++} ++ ++static void ++gdu_volume_changed (GduPresentable *presentable, ++ GGduVolume *volume) ++{ ++ /*g_debug ("volume: presentable_changed: %p: %s", volume, gdu_presentable_get_id (GDU_PRESENTABLE (presentable)));*/ ++ if (update_volume (volume)) ++ emit_changed (volume); ++} ++ ++static void ++gdu_volume_job_changed (GduPresentable *presentable, ++ GGduVolume *volume) ++{ ++ /*g_debug ("volume: presentable_job_changed %p: %s", volume, gdu_presentable_get_id (GDU_PRESENTABLE (presentable)));*/ ++ if (update_volume (volume)) ++ emit_changed (volume); ++} ++ ++static void ++gdu_cleartext_volume_removed (GduPresentable *presentable, ++ GGduVolume *volume) ++{ ++ /*g_debug ("cleartext volume: presentable_removed: %p: %s", volume, gdu_presentable_get_id (GDU_PRESENTABLE (presentable)));*/ ++ if (update_volume (volume)) ++ emit_changed (volume); ++} ++ ++static void ++gdu_cleartext_volume_changed (GduPresentable *presentable, ++ GGduVolume *volume) ++{ ++ /*g_debug ("cleartext volume: presentable_changed: %p: %s", volume, gdu_presentable_get_id (GDU_PRESENTABLE (presentable)));*/ ++ if (update_volume (volume)) ++ emit_changed (volume); ++} ++ ++static void ++gdu_cleartext_volume_job_changed (GduPresentable *presentable, ++ GGduVolume *volume) ++{ ++ /*g_debug ("cleartext volume: presentable_job_changed %p: %s", volume, gdu_presentable_get_id (GDU_PRESENTABLE (presentable)));*/ ++ if (update_volume (volume)) ++ emit_changed (volume); ++} ++ ++GGduVolume * ++g_gdu_volume_new (GVolumeMonitor *volume_monitor, ++ GduVolume *gdu_volume, ++ GGduDrive *drive, ++ GFile *activation_root) ++{ ++ GGduVolume *volume; ++ ++ volume = g_object_new (G_TYPE_GDU_VOLUME, NULL); ++ volume->volume_monitor = volume_monitor; ++ g_object_add_weak_pointer (G_OBJECT (volume_monitor), (gpointer) &(volume->volume_monitor)); ++ ++ volume->gdu_volume = g_object_ref (gdu_volume); ++ volume->activation_root = activation_root != NULL ? g_object_ref (activation_root) : NULL; ++ ++ g_signal_connect (volume->gdu_volume, "changed", G_CALLBACK (gdu_volume_changed), volume); ++ g_signal_connect (volume->gdu_volume, "job-changed", G_CALLBACK (gdu_volume_job_changed), volume); ++ ++ volume->drive = drive; ++ if (drive != NULL) ++ g_gdu_drive_set_volume (drive, volume); ++ ++ update_volume (volume); ++ ++ return volume; ++} ++ ++void ++g_gdu_volume_removed (GGduVolume *volume) ++{ ++ if (volume->pending_mount_op != NULL) ++ cancel_pending_mount_op (volume->pending_mount_op); ++ ++ if (volume->mount != NULL) ++ { ++ g_gdu_mount_unset_volume (volume->mount, volume); ++ volume->mount = NULL; ++ } ++ ++ if (volume->drive != NULL) ++ { ++ g_gdu_drive_unset_volume (volume->drive, volume); ++ volume->drive = NULL; ++ } ++} ++ ++void ++g_gdu_volume_set_mount (GGduVolume *volume, ++ GGduMount *mount) ++{ ++ if (volume->mount != mount) ++ { ++ ++ if (volume->mount != NULL) ++ g_gdu_mount_unset_volume (volume->mount, volume); ++ ++ volume->mount = mount; ++ ++ emit_changed (volume); ++ } ++} ++ ++void ++g_gdu_volume_unset_mount (GGduVolume *volume, ++ GGduMount *mount) ++{ ++ if (volume->mount == mount) ++ { ++ volume->mount = NULL; ++ emit_changed (volume); ++ } ++} ++ ++void ++g_gdu_volume_set_drive (GGduVolume *volume, ++ GGduDrive *drive) ++{ ++ if (volume->drive != drive) ++ { ++ if (volume->drive != NULL) ++ g_gdu_drive_unset_volume (volume->drive, volume); ++ ++ volume->drive = drive; ++ ++ emit_changed (volume); ++ } ++} ++ ++void ++g_gdu_volume_unset_drive (GGduVolume *volume, ++ GGduDrive *drive) ++{ ++ if (volume->drive == drive) ++ { ++ volume->drive = NULL; ++ emit_changed (volume); ++ } ++} ++ ++static GIcon * ++g_gdu_volume_get_icon (GVolume *_volume) ++{ ++ GGduVolume *volume = G_GDU_VOLUME (_volume); ++ return volume->icon != NULL ? g_object_ref (volume->icon) : NULL; ++} ++ ++static char * ++g_gdu_volume_get_name (GVolume *_volume) ++{ ++ GGduVolume *volume = G_GDU_VOLUME (_volume); ++ return g_strdup (volume->name); ++} ++ ++static char * ++g_gdu_volume_get_uuid (GVolume *_volume) ++{ ++ GGduVolume *volume = G_GDU_VOLUME (_volume); ++ return g_strdup (volume->uuid); ++} ++ ++static gboolean ++g_gdu_volume_can_mount (GVolume *_volume) ++{ ++ GGduVolume *volume = G_GDU_VOLUME (_volume); ++ return volume->can_mount; ++} ++ ++static gboolean ++g_gdu_volume_can_eject (GVolume *_volume) ++{ ++ GGduVolume *volume = G_GDU_VOLUME (_volume); ++ gboolean can_eject; ++ ++ can_eject = FALSE; ++ if (volume->drive != NULL) ++ can_eject = g_drive_can_eject (G_DRIVE (volume->drive)); ++ ++ return can_eject; ++} ++ ++static gboolean ++g_gdu_volume_should_automount (GVolume *_volume) ++{ ++ GGduVolume *volume = G_GDU_VOLUME (_volume); ++ return volume->should_automount; ++} ++ ++static GDrive * ++g_gdu_volume_get_drive (GVolume *volume) ++{ ++ GGduVolume *gdu_volume = G_GDU_VOLUME (volume); ++ GDrive *drive; ++ ++ drive = NULL; ++ if (gdu_volume->drive != NULL) ++ drive = g_object_ref (gdu_volume->drive); ++ ++ return drive; ++} ++ ++static GMount * ++g_gdu_volume_get_mount (GVolume *volume) ++{ ++ GGduVolume *gdu_volume = G_GDU_VOLUME (volume); ++ GMount *mount; ++ ++ mount = NULL; ++ if (gdu_volume->mount != NULL) ++ mount = g_object_ref (gdu_volume->mount); ++ ++ return mount; ++} ++ ++/* ---------------------------------------------------------------------------------------------------- */ ++ ++struct MountOpData ++{ ++ GGduVolume *volume; ++ GduDevice *device_to_mount; ++ GSimpleAsyncResult *simple; ++ GCancellable *cancellable; ++ gulong cancelled_handler_id; ++ ++ GMountOperation *mount_operation; ++ gulong mount_operation_reply_handler_id; ++ ++ gboolean is_cancelled; ++}; ++ ++static void ++mount_op_data_unref (MountOpData *data) ++{ ++ g_object_unref (data->volume); ++ if (data->device_to_mount != NULL) ++ g_object_unref (data->device_to_mount); ++ g_object_unref (data->simple); ++ if (data->cancelled_handler_id != 0) ++ g_signal_handler_disconnect (data->cancellable, data->cancelled_handler_id); ++ if (data->cancellable != NULL) ++ g_object_unref (data->cancellable); ++ if (data->mount_operation_reply_handler_id != 0) ++ g_signal_handler_disconnect (data->mount_operation, data->mount_operation_reply_handler_id); ++ if (data->mount_operation != NULL) ++ g_object_unref (data->mount_operation); ++ g_free (data); ++} ++ ++static void ++cancel_pending_mount_op (MountOpData *data) ++{ ++ /* we are no longer pending */ ++ data->volume->pending_mount_op = NULL; ++ ++ data->is_cancelled = TRUE; ++ ++ /* send an ::aborted signal to make the dialog go away */ ++ if (data->mount_operation != NULL) ++ g_signal_emit_by_name (data->mount_operation, "aborted"); ++ ++ /* complete the operation (sends reply to caller) */ ++ g_simple_async_result_set_error (data->simple, ++ G_IO_ERROR, ++ G_IO_ERROR_FAILED_HANDLED, ++ "Operation was cancelled"); ++ g_simple_async_result_complete (data->simple); ++} ++ ++static void ++mount_cb (GduDevice *device, ++ gchar *mount_point, ++ GError *error, ++ gpointer user_data); ++ ++static void ++mount_obtain_authz_cb (GObject *source_object, ++ GAsyncResult *res, ++ gpointer user_data) ++{ ++ MountOpData *data = user_data; ++ gboolean obtained_authz; ++ GError *error; ++ ++ /* if we've already aborted due to device removal / cancellation, just bail out */ ++ if (data->is_cancelled) ++ goto bailout; ++ ++ error = NULL; ++ obtained_authz = _obtain_authz_finish (res, &error); ++ ++ if (!obtained_authz) ++ { ++ /* be quiet if the daemon is inhibited */ ++ if (error->code == GDU_ERROR_INHIBITED) ++ { ++ error->domain = G_IO_ERROR; ++ error->code = G_IO_ERROR_FAILED_HANDLED; ++ } ++ g_simple_async_result_set_from_error (data->simple, error); ++ g_simple_async_result_complete (data->simple); ++ } ++ else ++ { ++ /* got the authz, now try again */ ++ gdu_device_op_filesystem_mount (data->device_to_mount, mount_cb, data); ++ goto out; ++ } ++ ++ bailout: ++ data->volume->pending_mount_op = NULL; ++ mount_op_data_unref (data); ++ ++ out: ++ ; ++} ++ ++static void ++mount_cb (GduDevice *device, ++ gchar *mount_point, ++ GError *error, ++ gpointer user_data) ++{ ++ MountOpData *data = user_data; ++ ++ /* if we've already aborted due to device removal / cancellation, just bail out */ ++ if (data->is_cancelled) ++ goto bailout; ++ ++ if (error != NULL) ++ { ++ PolKitAction *pk_action; ++ PolKitResult pk_result; ++ ++ /* only attempt to show authentication dialog if we have a mount operation */ ++ if (data->mount_operation != NULL && gdu_error_check_polkit_not_authorized (error, ++ &pk_action, ++ &pk_result)) ++ { ++ if (pk_result != POLKIT_RESULT_NO && pk_result != POLKIT_RESULT_UNKNOWN) ++ { ++ const gchar *action_id; ++ /* try to obtain the authorization */ ++ polkit_action_get_action_id (pk_action, (char **) &action_id); ++ _obtain_authz (action_id, ++ data->cancellable, ++ mount_obtain_authz_cb, ++ data); ++ goto out; ++ } ++ else ++ { ++ g_simple_async_result_set_from_error (data->simple, error); ++ } ++ polkit_action_unref (pk_action); ++ } ++ else ++ { ++ /* be quiet if the daemon is inhibited */ ++ if (error->code == GDU_ERROR_INHIBITED) ++ { ++ error->domain = G_IO_ERROR; ++ error->code = G_IO_ERROR_FAILED_HANDLED; ++ } ++ g_simple_async_result_set_from_error (data->simple, error); ++ } ++ g_error_free (error); ++ } ++ else ++ { ++ g_free (mount_point); ++ } ++ ++ g_simple_async_result_complete (data->simple); ++ ++ bailout: ++ data->volume->pending_mount_op = NULL; ++ mount_op_data_unref (data); ++ ++ out: ++ ; ++} ++ ++static void ++mount_cleartext_device (MountOpData *data, ++ const gchar *object_path_of_cleartext_device) ++{ ++ GduPool *pool; ++ ++ /* if we've already aborted due to device removal / cancellation, just bail out */ ++ if (data->is_cancelled) ++ { ++ mount_op_data_unref (data); ++ goto bailout; ++ } ++ ++ pool = gdu_presentable_get_pool (GDU_PRESENTABLE (data->volume->gdu_volume)); ++ ++ data->device_to_mount = gdu_pool_get_by_object_path (pool, object_path_of_cleartext_device); ++ if (data->device_to_mount == NULL) ++ { ++ g_simple_async_result_set_error (data->simple, ++ G_IO_ERROR, ++ G_IO_ERROR_FAILED, ++ "Successfully unlocked encrypted volume but cleartext device does not exist"); ++ g_simple_async_result_complete (data->simple); ++ data->volume->pending_mount_op = NULL; ++ mount_op_data_unref (data); ++ } ++ else ++ { ++ gdu_device_op_filesystem_mount (data->device_to_mount, mount_cb, data); ++ } ++ ++ g_object_unref (pool); ++ ++ bailout: ++ ; ++} ++ ++static void ++unlock_from_keyring_cb (GduDevice *device, ++ char *object_path_of_cleartext_device, ++ GError *error, ++ gpointer user_data) ++{ ++ MountOpData *data = user_data; ++ ++ /* if we've already aborted due to device removal / cancellation, just bail out */ ++ if (data->is_cancelled) ++ { ++ mount_op_data_unref (data); ++ goto bailout; ++ } ++ ++ if (error != NULL) ++ { ++ /*g_debug ("keyring password didn't work: %s", error->message);*/ ++ ++ /* The password we retrieved from the keyring didn't work. So go ahead and prompt ++ * the user. ++ */ ++ mount_with_mount_operation (data); ++ ++ g_error_free (error); ++ } ++ else ++ { ++ mount_cleartext_device (data, object_path_of_cleartext_device); ++ g_free (object_path_of_cleartext_device); ++ } ++ ++ bailout: ++ ; ++} ++ ++static void ++unlock_cb (GduDevice *device, ++ gchar *object_path_of_cleartext_device, ++ GError *error, ++ gpointer user_data) ++{ ++ MountOpData *data = user_data; ++ ++ /* if we've already aborted due to device removal / cancellation, just bail out */ ++ if (data->is_cancelled) ++ { ++ mount_op_data_unref (data); ++ goto bailout; ++ } ++ ++ if (error != NULL) ++ { ++ /* be quiet if the daemon is inhibited */ ++ if (error->code == GDU_ERROR_INHIBITED) ++ { ++ error->domain = G_IO_ERROR; ++ error->code = G_IO_ERROR_FAILED_HANDLED; ++ } ++ g_simple_async_result_set_from_error (data->simple, error); ++ g_error_free (error); ++ g_simple_async_result_complete (data->simple); ++ data->volume->pending_mount_op = NULL; ++ mount_op_data_unref (data); ++ } ++ else ++ { ++ GPasswordSave password_save; ++ const gchar *password; ++ ++ password_save = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (device), "password-save")); ++ password = g_object_get_data (G_OBJECT (device), "password"); ++ ++ if (password != NULL) ++ { ++ switch (password_save) ++ { ++ case G_PASSWORD_SAVE_FOR_SESSION: ++ gdu_util_save_secret (device, password, TRUE); ++ break; ++ ++ case G_PASSWORD_SAVE_PERMANENTLY: ++ gdu_util_save_secret (device, password, FALSE); ++ break; ++ ++ default: ++ /* do nothing */ ++ break; ++ } ++ } ++ ++ /* now we have a cleartext device; update the GVolume details to show that */ ++ if (update_volume (data->volume)) ++ emit_changed (data->volume); ++ ++ mount_cleartext_device (data, object_path_of_cleartext_device); ++ g_free (object_path_of_cleartext_device); ++ } ++ ++ bailout: ++ ++ /* scrub the password */ ++ g_object_set_data (G_OBJECT (device), "password-save", NULL); ++ g_object_set_data (G_OBJECT (device), "password", NULL); ++} ++ ++static void ++scrub_n_free_string (char *password) ++{ ++ memset (password, '\0', strlen (password)); ++ g_free (password); ++} ++ ++static void ++mount_operation_reply (GMountOperation *mount_operation, ++ GMountOperationResult result, ++ gpointer user_data) ++{ ++ MountOpData *data = user_data; ++ GduDevice *device; ++ const gchar *password; ++ ++ /* if we've already aborted due to device removal, just bail out */ ++ if (data->is_cancelled) ++ { ++ mount_op_data_unref (data); ++ goto out; ++ } ++ ++ /* we got what we wanted; don't listen to any other signals from the mount operation */ ++ if (data->mount_operation_reply_handler_id != 0) ++ { ++ g_signal_handler_disconnect (data->mount_operation, data->mount_operation_reply_handler_id); ++ data->mount_operation_reply_handler_id = 0; ++ } ++ ++ if (result != G_MOUNT_OPERATION_HANDLED) ++ { ++ if (result == G_MOUNT_OPERATION_ABORTED) ++ { ++ /* The user aborted the operation so consider it "handled" */ ++ g_simple_async_result_set_error (data->simple, ++ G_IO_ERROR, ++ G_IO_ERROR_FAILED_HANDLED, ++ "Password dialog aborted (user should never see this error since it is G_IO_ERROR_FAILED_HANDLED)"); ++ } ++ else ++ { ++ g_simple_async_result_set_error (data->simple, ++ G_IO_ERROR, ++ G_IO_ERROR_PERMISSION_DENIED, ++ "Expected G_MOUNT_OPERATION_HANDLED but got %d", result); ++ } ++ g_simple_async_result_complete (data->simple); ++ data->volume->pending_mount_op = NULL; ++ mount_op_data_unref (data); ++ goto out; ++ } ++ ++ password = g_mount_operation_get_password (mount_operation); ++ ++ device = gdu_presentable_get_device (GDU_PRESENTABLE (data->volume->gdu_volume)); ++ ++ g_object_set_data (G_OBJECT (device), ++ "password-save", ++ GINT_TO_POINTER (g_mount_operation_get_password_save (mount_operation))); ++ g_object_set_data_full (G_OBJECT (device), ++ "password", ++ g_strdup (password), ++ (GDestroyNotify) scrub_n_free_string); ++ ++ gdu_device_op_luks_unlock (device, password, unlock_cb, data); ++ ++ g_object_unref (device); ++ ++ out: ++ ; ++} ++ ++static void ++mount_with_mount_operation (MountOpData *data) ++{ ++ gchar *message; ++ gchar *drive_name; ++ GduPresentable *toplevel; ++ GduDevice *device; ++ ++ device = NULL; ++ drive_name = NULL; ++ message = NULL; ++ toplevel = NULL; ++ ++ /* if we've already aborted due to device removal, just bail out */ ++ if (data->is_cancelled) ++ { ++ mount_op_data_unref (data); ++ goto out; ++ } ++ ++ if (data->mount_operation == NULL) ++ { ++ g_simple_async_result_set_error (data->simple, ++ G_IO_ERROR, ++ G_IO_ERROR_FAILED, ++ "Password required to access the encrypted data"); ++ g_simple_async_result_complete (data->simple); ++ data->volume->pending_mount_op = NULL; ++ mount_op_data_unref (data); ++ goto out; ++ } ++ ++ device = gdu_presentable_get_device (GDU_PRESENTABLE (data->volume->gdu_volume)); ++ ++ toplevel = gdu_presentable_get_toplevel (GDU_PRESENTABLE (data->volume->gdu_volume)); ++ if (toplevel != NULL) ++ drive_name = gdu_presentable_get_name (toplevel); ++ ++ /* This is going to look ass until bug 573416 is fixed. Unfortunately ++ * the gtk+ maintain has stated "oh, I stopped using luks" but that's ++ * more of a gtk+ problem ;-) ++ */ ++ if (drive_name != NULL) ++ { ++ if (gdu_device_is_partition (device)) ++ { ++ message = g_strdup_printf (_("Enter a password to unlock the volume\n" ++ "The device \"%s\" contains encrypted data on partition %d."), ++ drive_name, ++ gdu_device_partition_get_number (device)); ++ } ++ else ++ { ++ message = g_strdup_printf (_("Enter a password to unlock the volume\n" ++ "The device \"%s\" contains encrypted data."), ++ drive_name); ++ } ++ } ++ else ++ { ++ message = g_strdup_printf (_("Enter a password to unlock the volume\n" ++ "The device %s contains encrypted data."), ++ gdu_device_get_device_file (device)); ++ } ++ ++ data->mount_operation_reply_handler_id = g_signal_connect (data->mount_operation, ++ "reply", ++ G_CALLBACK (mount_operation_reply), ++ data); ++ ++ g_signal_emit_by_name (data->mount_operation, ++ "ask-password", ++ message, ++ NULL, ++ NULL, ++ G_ASK_PASSWORD_NEED_PASSWORD | ++ G_ASK_PASSWORD_SAVING_SUPPORTED); ++ ++ out: ++ g_free (drive_name); ++ g_free (message); ++ if (device != NULL) ++ g_object_unref (device); ++ if (toplevel != NULL) ++ g_object_unref (toplevel); ++} ++ ++static void ++cancelled_cb (GCancellable *cancellable, ++ GGduVolume *volume) ++{ ++ if (volume->pending_mount_op != NULL) ++ { ++ cancel_pending_mount_op (volume->pending_mount_op); ++ } ++} ++ ++static void ++g_gdu_volume_mount (GVolume *_volume, ++ GMountMountFlags flags, ++ GMountOperation *mount_operation, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data) ++{ ++ GGduVolume *volume = G_GDU_VOLUME (_volume); ++ GSimpleAsyncResult *simple; ++ GduDevice *device; ++ GduPool *pool; ++ const gchar *usage; ++ const gchar *type; ++ MountOpData *data; ++ ++ pool = NULL; ++ device = NULL; ++ ++ if (volume->pending_mount_op != NULL) ++ { ++ simple = g_simple_async_result_new_error (G_OBJECT (volume), ++ callback, ++ user_data, ++ G_IO_ERROR, ++ G_IO_ERROR_FAILED, ++ "A mount operation is already pending"); ++ g_simple_async_result_complete (simple); ++ g_object_unref (simple); ++ goto out; ++ } ++ ++ device = gdu_presentable_get_device (GDU_PRESENTABLE (volume->gdu_volume)); ++ pool = gdu_device_get_pool (device); ++ ++ /* Makes no sense to mount ++ * ++ * - blank discs since these already have a burn:/// mount ++ * - other things that are already mounted ++ * ++ * Unfortunately Nautilus will try to do this anyway. For now, just return success for ++ * such requests. ++ */ ++ if (gdu_device_optical_disc_get_is_blank (device) || gdu_device_is_mounted (device)) ++ { ++ simple = g_simple_async_result_new (G_OBJECT (volume), ++ callback, ++ user_data, ++ g_gdu_volume_mount); ++ g_simple_async_result_complete (simple); ++ g_object_unref (simple); ++ goto out; ++ } ++ ++ data = g_new0 (MountOpData, 1); ++ ++ data->volume = g_object_ref (volume); ++ ++ data->simple = g_simple_async_result_new (G_OBJECT (volume), ++ callback, ++ user_data, ++ g_gdu_volume_mount); ++ ++ data->cancellable = cancellable != NULL ? g_object_ref (cancellable) : NULL; ++ ++ data->mount_operation = mount_operation != NULL ? g_object_ref (mount_operation) : NULL; ++ ++ if (data->cancellable != NULL) ++ data->cancelled_handler_id = g_signal_connect (data->cancellable, "cancelled", G_CALLBACK (cancelled_cb), volume); ++ ++ volume->pending_mount_op = data; ++ ++ /* if the device is already unlocked, just attempt to mount it */ ++ if (volume->cleartext_gdu_volume != NULL) ++ { ++ GduDevice *luks_cleartext_volume_device; ++ const gchar *object_path_of_cleartext_device; ++ ++ luks_cleartext_volume_device = gdu_presentable_get_device (GDU_PRESENTABLE (volume->cleartext_gdu_volume)); ++ ++ object_path_of_cleartext_device = gdu_device_get_object_path (luks_cleartext_volume_device); ++ ++ mount_cleartext_device (data, object_path_of_cleartext_device); ++ ++ g_object_unref (luks_cleartext_volume_device); ++ goto out; ++ } ++ ++ usage = gdu_device_id_get_usage (device); ++ type = gdu_device_id_get_type (device); ++ if (g_strcmp0 (usage, "crypto") == 0 && g_strcmp0 (type, "crypto_LUKS") == 0) ++ { ++ gchar *password; ++ ++ /* if we have the secret in the keyring, try with that first */ ++ password = gdu_util_get_secret (device); ++ if (password != NULL) ++ { ++ gdu_device_op_luks_unlock (device, password, unlock_from_keyring_cb, data); ++ ++ scrub_n_free_string (password); ++ goto out; ++ } ++ ++ /* don't put up a password dialog if the daemon is inhibited */ ++ if (gdu_pool_is_daemon_inhibited (pool)) ++ { ++ g_simple_async_result_set_error (data->simple, ++ G_IO_ERROR, ++ G_IO_ERROR_FAILED_HANDLED, ++ "Daemon is currently inhibited"); ++ g_simple_async_result_complete (data->simple); ++ volume->pending_mount_op = NULL; ++ mount_op_data_unref (data); ++ goto out; ++ } ++ ++ mount_with_mount_operation (data); ++ } ++ else ++ { ++ data->device_to_mount = g_object_ref (device); ++ gdu_device_op_filesystem_mount (data->device_to_mount, mount_cb, data); ++ } ++ ++ out: ++ if (pool != NULL) ++ g_object_unref (pool); ++ if (device != NULL) ++ g_object_unref (device); ++} ++ ++static gboolean ++g_gdu_volume_mount_finish (GVolume *volume, ++ GAsyncResult *result, ++ GError **error) ++{ ++ GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result); ++ ++ g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_gdu_volume_mount); ++ ++ return !g_simple_async_result_propagate_error (simple, error); ++} ++ ++/* ---------------------------------------------------------------------------------------------------- */ ++ ++typedef struct { ++ GObject *object; ++ GAsyncReadyCallback callback; ++ gpointer user_data; ++} EjectWrapperOp; ++ ++static void ++eject_wrapper_callback (GObject *source_object, ++ GAsyncResult *res, ++ gpointer user_data) ++{ ++ EjectWrapperOp *data = user_data; ++ data->callback (data->object, res, data->user_data); ++ g_object_unref (data->object); ++ g_free (data); ++} ++ ++static void ++g_gdu_volume_eject (GVolume *volume, ++ GMountUnmountFlags flags, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data) ++{ ++ GGduVolume *gdu_volume = G_GDU_VOLUME (volume); ++ GGduDrive *drive; ++ ++ drive = NULL; ++ if (gdu_volume->drive != NULL) ++ drive = g_object_ref (gdu_volume->drive); ++ ++ if (drive != NULL) ++ { ++ EjectWrapperOp *data; ++ data = g_new0 (EjectWrapperOp, 1); ++ data->object = g_object_ref (volume); ++ data->callback = callback; ++ data->user_data = user_data; ++ g_drive_eject (G_DRIVE (drive), flags, cancellable, eject_wrapper_callback, data); ++ g_object_unref (drive); ++ } ++ else ++ { ++ GSimpleAsyncResult *simple; ++ simple = g_simple_async_result_new_error (G_OBJECT (volume), ++ callback, ++ user_data, ++ G_IO_ERROR, ++ G_IO_ERROR_FAILED, ++ _("Operation not supported by backend")); ++ g_simple_async_result_complete (simple); ++ g_object_unref (simple); ++ } ++} ++ ++static gboolean ++g_gdu_volume_eject_finish (GVolume *volume, ++ GAsyncResult *result, ++ GError **error) ++{ ++ GGduVolume *gdu_volume = G_GDU_VOLUME (volume); ++ gboolean res; ++ ++ res = TRUE; ++ if (gdu_volume->drive != NULL) ++ { ++ res = g_drive_eject_finish (G_DRIVE (gdu_volume->drive), result, error); ++ } ++ else ++ { ++ g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error); ++ res = FALSE; ++ } ++ ++ return res; ++} ++ ++static char * ++g_gdu_volume_get_identifier (GVolume *_volume, ++ const char *kind) ++{ ++ GGduVolume *volume = G_GDU_VOLUME (_volume); ++ GduDevice *device; ++ const gchar *label; ++ const gchar *uuid; ++ gchar *id; ++ ++ id = NULL; ++ ++ device = gdu_presentable_get_device (GDU_PRESENTABLE (volume->gdu_volume)); ++ ++ label = gdu_device_id_get_label (device); ++ uuid = gdu_device_id_get_uuid (device); ++ ++ g_object_unref (device); ++ ++ if (strcmp (kind, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE) == 0) ++ id = g_strdup (volume->device_file); ++ else if (strcmp (kind, G_VOLUME_IDENTIFIER_KIND_LABEL) == 0) ++ id = strlen (label) > 0 ? g_strdup (label) : NULL; ++ else if (strcmp (kind, G_VOLUME_IDENTIFIER_KIND_UUID) == 0) ++ id = strlen (uuid) > 0 ? g_strdup (uuid) : NULL; ++ ++ return id; ++} ++ ++static char ** ++g_gdu_volume_enumerate_identifiers (GVolume *_volume) ++{ ++ GGduVolume *volume = G_GDU_VOLUME (_volume); ++ GduDevice *device; ++ GPtrArray *p; ++ const gchar *label; ++ const gchar *uuid; ++ ++ device = gdu_presentable_get_device (GDU_PRESENTABLE (volume->gdu_volume)); ++ ++ label = gdu_device_id_get_label (device); ++ uuid = gdu_device_id_get_uuid (device); ++ ++ g_object_unref (device); ++ ++ p = g_ptr_array_new (); ++ g_ptr_array_add (p, g_strdup (G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE)); ++ if (strlen (label) > 0) ++ g_ptr_array_add (p, g_strdup (G_VOLUME_IDENTIFIER_KIND_LABEL)); ++ if (strlen (uuid) > 0) ++ g_ptr_array_add (p, g_strdup (G_VOLUME_IDENTIFIER_KIND_UUID)); ++ ++ g_ptr_array_add (p, NULL); ++ ++ return (gchar **) g_ptr_array_free (p, FALSE); ++} ++ ++static GFile * ++g_gdu_volume_get_activation_root (GVolume *_volume) ++{ ++ GGduVolume *volume = G_GDU_VOLUME (_volume); ++ return volume->activation_root != NULL ? g_object_ref (volume->activation_root) : NULL; ++} ++ ++static void ++g_gdu_volume_volume_iface_init (GVolumeIface *iface) ++{ ++ iface->get_name = g_gdu_volume_get_name; ++ iface->get_icon = g_gdu_volume_get_icon; ++ iface->get_uuid = g_gdu_volume_get_uuid; ++ iface->get_drive = g_gdu_volume_get_drive; ++ iface->get_mount = g_gdu_volume_get_mount; ++ iface->can_mount = g_gdu_volume_can_mount; ++ iface->can_eject = g_gdu_volume_can_eject; ++ iface->should_automount = g_gdu_volume_should_automount; ++ iface->mount_fn = g_gdu_volume_mount; ++ iface->mount_finish = g_gdu_volume_mount_finish; ++ iface->eject = g_gdu_volume_eject; ++ iface->eject_finish = g_gdu_volume_eject_finish; ++ iface->get_identifier = g_gdu_volume_get_identifier; ++ iface->enumerate_identifiers = g_gdu_volume_enumerate_identifiers; ++ iface->get_activation_root = g_gdu_volume_get_activation_root; ++} ++ ++gboolean ++g_gdu_volume_has_device_file (GGduVolume *volume, ++ const gchar *device_file) ++{ ++ const gchar *_device_file; ++ ++ _device_file = volume->device_file; ++ ++ if (volume->cleartext_gdu_volume != NULL) ++ { ++ GduDevice *luks_cleartext_volume_device; ++ luks_cleartext_volume_device = gdu_presentable_get_device (GDU_PRESENTABLE (volume->cleartext_gdu_volume)); ++ _device_file = gdu_device_get_device_file (luks_cleartext_volume_device); ++ g_object_unref (luks_cleartext_volume_device); ++ } ++ ++ return g_strcmp0 (_device_file, device_file) == 0; ++} ++ ++ ++gboolean ++g_gdu_volume_has_mount_path (GGduVolume *volume, ++ const char *mount_path) ++{ ++ GduDevice *device; ++ GduPresentable *presentable; ++ gboolean ret; ++ ++ ret = FALSE; ++ ++ presentable = g_gdu_volume_get_presentable_with_cleartext (volume); ++ if (presentable != NULL) ++ { ++ device = gdu_presentable_get_device (presentable); ++ if (device != NULL) ++ { ++ ret = g_strcmp0 (gdu_device_get_mount_path (device), mount_path); ++ g_object_unref (device); ++ } ++ } ++ ++ return ret; ++} ++ ++gboolean ++g_gdu_volume_has_uuid (GGduVolume *volume, ++ const char *uuid) ++{ ++ const gchar *_uuid; ++ ++ _uuid = volume->uuid; ++ ++ if (volume->cleartext_gdu_volume != NULL) ++ { ++ GduDevice *luks_cleartext_volume_device; ++ luks_cleartext_volume_device = gdu_presentable_get_device (GDU_PRESENTABLE (volume->cleartext_gdu_volume)); ++ _uuid = gdu_device_id_get_uuid (luks_cleartext_volume_device); ++ g_object_unref (luks_cleartext_volume_device); ++ } ++ ++ return g_strcmp0 (_uuid, uuid) == 0; ++} ++ ++GduPresentable * ++g_gdu_volume_get_presentable (GGduVolume *volume) ++{ ++ return GDU_PRESENTABLE (volume->gdu_volume); ++} ++ ++GduPresentable * ++g_gdu_volume_get_presentable_with_cleartext (GGduVolume *volume) ++{ ++ GduVolume *ret; ++ ++ ret = volume->cleartext_gdu_volume; ++ if (ret == NULL) ++ ret = volume->gdu_volume; ++ ++ return GDU_PRESENTABLE (ret); ++} +--- /dev/null 2009-03-02 15:45:22.087299058 -0500 ++++ monitor/gdu/ggduvolume.h 2009-03-01 23:48:05.000000000 -0500 +@@ -0,0 +1,78 @@ ++/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ ++/* gvfs - extensions for gio ++ * ++ * Copyright (C) 2006-2009 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General ++ * Public License along with this library; if not, write to the ++ * Free Software Foundation, Inc., 59 Temple Place, Suite 330, ++ * Boston, MA 02111-1307, USA. ++ * ++ * Author: David Zeuthen ++ */ ++ ++#ifndef __G_GDU_VOLUME_H__ ++#define __G_GDU_VOLUME_H__ ++ ++#include ++#include ++ ++#include "ggduvolumemonitor.h" ++ ++G_BEGIN_DECLS ++ ++#define G_TYPE_GDU_VOLUME (g_gdu_volume_get_type ()) ++#define G_GDU_VOLUME(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_GDU_VOLUME, GGduVolume)) ++#define G_GDU_VOLUME_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_GDU_VOLUME, GGduVolumeClass)) ++#define G_IS_GDU_VOLUME(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_GDU_VOLUME)) ++#define G_IS_GDU_VOLUME_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_GDU_VOLUME)) ++ ++typedef struct _GGduVolumeClass GGduVolumeClass; ++ ++struct _GGduVolumeClass { ++ GObjectClass parent_class; ++}; ++ ++GType g_gdu_volume_get_type (void) G_GNUC_CONST; ++ ++GGduVolume *g_gdu_volume_new (GVolumeMonitor *volume_monitor, ++ GduVolume *gdu_volume, ++ GGduDrive *drive, ++ GFile *activation_root); ++ ++void g_gdu_volume_set_mount (GGduVolume *volume, ++ GGduMount *mount); ++void g_gdu_volume_unset_mount (GGduVolume *volume, ++ GGduMount *mount); ++ ++void g_gdu_volume_set_drive (GGduVolume *volume, ++ GGduDrive *drive); ++void g_gdu_volume_unset_drive (GGduVolume *volume, ++ GGduDrive *drive); ++ ++void g_gdu_volume_removed (GGduVolume *volume); ++ ++gboolean g_gdu_volume_has_mount_path (GGduVolume *volume, ++ const char *mount_path); ++gboolean g_gdu_volume_has_uuid (GGduVolume *volume, ++ const char *uuid); ++gboolean g_gdu_volume_has_device_file (GGduVolume *volume, ++ const gchar *device_file); ++ ++GduPresentable *g_gdu_volume_get_presentable (GGduVolume *volume); ++ ++GduPresentable *g_gdu_volume_get_presentable_with_cleartext (GGduVolume *volume); ++ ++G_END_DECLS ++ ++#endif /* __G_GDU_VOLUME_H__ */ +--- /dev/null 2009-03-02 15:45:22.087299058 -0500 ++++ monitor/gdu/ggduvolumemonitor.c 2009-03-01 23:48:01.000000000 -0500 +@@ -0,0 +1,1432 @@ ++/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ ++/* gvfs - extensions for gio ++ * ++ * Copyright (C) 2006-2009 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General ++ * Public License along with this library; if not, write to the ++ * Free Software Foundation, Inc., 59 Temple Place, Suite 330, ++ * Boston, MA 02111-1307, USA. ++ * ++ * Author: David Zeuthen ++ */ ++ ++#include ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include "ggduvolumemonitor.h" ++#include "ggdumount.h" ++#include "ggduvolume.h" ++#include "ggdudrive.h" ++ ++static GGduVolumeMonitor *the_volume_monitor = NULL; ++ ++struct _GGduVolumeMonitor { ++ GNativeVolumeMonitor parent; ++ ++ GUnixMountMonitor *mount_monitor; ++ ++ GduPool *pool; ++ ++ GList *last_optical_disc_devices; ++ GList *last_mountpoints; ++ GList *last_mounts; ++ ++ GList *drives; ++ GList *volumes; ++ GList *mounts; ++ ++ /* we keep volumes/mounts for blank and audio discs separate to handle e.g. mixed discs properly */ ++ GList *disc_volumes; ++ GList *disc_mounts; ++ ++}; ++ ++static void mountpoints_changed (GUnixMountMonitor *mount_monitor, ++ gpointer user_data); ++static void mounts_changed (GUnixMountMonitor *mount_monitor, ++ gpointer user_data); ++ ++static void presentable_added (GduPool *pool, ++ GduPresentable *presentable, ++ gpointer user_data); ++static void presentable_removed (GduPool *pool, ++ GduPresentable *presentable, ++ gpointer user_data); ++ ++static void update_all (GGduVolumeMonitor *monitor, ++ gboolean emit_changes); ++ ++static void update_drives (GGduVolumeMonitor *monitor, ++ GList **added_drives, ++ GList **removed_drives); ++static void update_volumes (GGduVolumeMonitor *monitor, ++ GList **added_volumes, ++ GList **removed_volumes); ++static void update_mounts (GGduVolumeMonitor *monitor, ++ GList **added_mounts, ++ GList **removed_mounts); ++static void update_discs (GGduVolumeMonitor *monitor, ++ GList **added_volumes, ++ GList **removed_volumes, ++ GList **added_mounts, ++ GList **removed_mounts); ++ ++ ++G_DEFINE_TYPE (GGduVolumeMonitor, g_gdu_volume_monitor, G_TYPE_NATIVE_VOLUME_MONITOR) ++ ++static void ++list_free (GList *objects) ++{ ++ g_list_foreach (objects, (GFunc)g_object_unref, NULL); ++ g_list_free (objects); ++} ++ ++static void ++g_gdu_volume_monitor_dispose (GObject *object) ++{ ++ GGduVolumeMonitor *monitor; ++ ++ monitor = G_GDU_VOLUME_MONITOR (object); ++ ++ the_volume_monitor = NULL; ++ ++ if (G_OBJECT_CLASS (g_gdu_volume_monitor_parent_class)->dispose) ++ (*G_OBJECT_CLASS (g_gdu_volume_monitor_parent_class)->dispose) (object); ++} ++ ++static void ++g_gdu_volume_monitor_finalize (GObject *object) ++{ ++ GGduVolumeMonitor *monitor; ++ ++ monitor = G_GDU_VOLUME_MONITOR (object); ++ ++ g_signal_handlers_disconnect_by_func (monitor->mount_monitor, mountpoints_changed, monitor); ++ g_signal_handlers_disconnect_by_func (monitor->mount_monitor, mounts_changed, monitor); ++ g_signal_handlers_disconnect_by_func (monitor->mount_monitor, presentable_added, monitor); ++ g_signal_handlers_disconnect_by_func (monitor->mount_monitor, presentable_removed, monitor); ++ ++ g_object_unref (monitor->mount_monitor); ++ ++ g_object_unref (monitor->pool); ++ ++ list_free (monitor->last_optical_disc_devices); ++ list_free (monitor->last_mountpoints); ++ g_list_foreach (monitor->last_mounts, ++ (GFunc)g_unix_mount_free, NULL); ++ g_list_free (monitor->last_mounts); ++ ++ list_free (monitor->drives); ++ list_free (monitor->volumes); ++ list_free (monitor->mounts); ++ ++ list_free (monitor->disc_volumes); ++ list_free (monitor->disc_mounts); ++ ++ if (G_OBJECT_CLASS (g_gdu_volume_monitor_parent_class)->finalize) ++ (*G_OBJECT_CLASS (g_gdu_volume_monitor_parent_class)->finalize) (object); ++} ++ ++static GList * ++get_mounts (GVolumeMonitor *volume_monitor) ++{ ++ GGduVolumeMonitor *monitor; ++ GList *l, *ll; ++ ++ monitor = G_GDU_VOLUME_MONITOR (volume_monitor); ++ ++ l = g_list_copy (monitor->mounts); ++ ll = g_list_copy (monitor->disc_mounts); ++ l = g_list_concat (l, ll); ++ ++ g_list_foreach (l, (GFunc)g_object_ref, NULL); ++ ++ return l; ++} ++ ++static GList * ++get_volumes (GVolumeMonitor *volume_monitor) ++{ ++ GGduVolumeMonitor *monitor; ++ GList *l, *ll; ++ ++ monitor = G_GDU_VOLUME_MONITOR (volume_monitor); ++ ++ l = g_list_copy (monitor->volumes); ++ ll = g_list_copy (monitor->disc_volumes); ++ l = g_list_concat (l, ll); ++ ++ g_list_foreach (l, (GFunc)g_object_ref, NULL); ++ ++ return l; ++} ++ ++static GList * ++get_connected_drives (GVolumeMonitor *volume_monitor) ++{ ++ GGduVolumeMonitor *monitor; ++ GList *l; ++ ++ monitor = G_GDU_VOLUME_MONITOR (volume_monitor); ++ ++ l = g_list_copy (monitor->drives); ++ g_list_foreach (l, (GFunc)g_object_ref, NULL); ++ ++ return l; ++} ++ ++static GVolume * ++get_volume_for_uuid (GVolumeMonitor *volume_monitor, const char *uuid) ++{ ++ GGduVolumeMonitor *monitor; ++ GGduVolume *volume; ++ GList *l; ++ ++ monitor = G_GDU_VOLUME_MONITOR (volume_monitor); ++ ++ volume = NULL; ++ ++ for (l = monitor->volumes; l != NULL; l = l->next) ++ { ++ volume = l->data; ++ if (g_gdu_volume_has_uuid (volume, uuid)) ++ goto found; ++ } ++ ++ for (l = monitor->disc_volumes; l != NULL; l = l->next) ++ { ++ volume = l->data; ++ if (g_gdu_volume_has_uuid (volume, uuid)) ++ goto found; ++ } ++ ++ return NULL; ++ ++ found: ++ ++ g_object_ref (volume); ++ ++ return (GVolume *)volume; ++} ++ ++static GMount * ++get_mount_for_uuid (GVolumeMonitor *volume_monitor, const char *uuid) ++{ ++ GGduVolumeMonitor *monitor; ++ GGduMount *mount; ++ GList *l; ++ ++ monitor = G_GDU_VOLUME_MONITOR (volume_monitor); ++ ++ mount = NULL; ++ ++ for (l = monitor->mounts; l != NULL; l = l->next) ++ { ++ mount = l->data; ++ if (g_gdu_mount_has_uuid (mount, uuid)) ++ goto found; ++ } ++ ++ for (l = monitor->disc_mounts; l != NULL; l = l->next) ++ { ++ mount = l->data; ++ if (g_gdu_mount_has_uuid (mount, uuid)) ++ goto found; ++ } ++ ++ return NULL; ++ ++ found: ++ ++ g_object_ref (mount); ++ ++ return (GMount *)mount; ++} ++ ++static GMount * ++get_mount_for_mount_path (const char *mount_path, ++ GCancellable *cancellable) ++{ ++ GMount *mount; ++ GGduMount *gdu_mount; ++ GGduVolumeMonitor *volume_monitor; ++ ++ if (the_volume_monitor == NULL) ++ { ++ /* Dammit, no monitor is set up.. so we have to create one, find ++ * what the user asks for and throw it away again. ++ * ++ * What a waste - especially considering that there's IO ++ * involved in doing this: connect to the system message bus; ++ * IPC to DeviceKit-disks etc etc ++ */ ++ volume_monitor = G_GDU_VOLUME_MONITOR (g_gdu_volume_monitor_new ()); ++ } ++ else ++ { ++ volume_monitor = g_object_ref (the_volume_monitor); ++ } ++ ++ mount = NULL; ++ ++ /* creation of the volume monitor might actually fail */ ++ if (volume_monitor != NULL) ++ { ++ GList *l; ++ ++ for (l = volume_monitor->mounts; l != NULL; l = l->next) ++ { ++ gdu_mount = l->data; ++ ++ if (g_gdu_mount_has_mount_path (gdu_mount, mount_path)) ++ { ++ mount = g_object_ref (gdu_mount); ++ break; ++ } ++ } ++ } ++ ++ g_object_unref (volume_monitor); ++ ++ return (GMount *) mount; ++} ++ ++static void ++mountpoints_changed (GUnixMountMonitor *mount_monitor, ++ gpointer user_data) ++{ ++ GGduVolumeMonitor *monitor = G_GDU_VOLUME_MONITOR (user_data); ++ ++ update_all (monitor, TRUE); ++} ++ ++static void ++mounts_changed (GUnixMountMonitor *mount_monitor, ++ gpointer user_data) ++{ ++ GGduVolumeMonitor *monitor = G_GDU_VOLUME_MONITOR (user_data); ++ ++ update_all (monitor, TRUE); ++} ++ ++static void ++presentable_added (GduPool *pool, ++ GduPresentable *presentable, ++ gpointer user_data) ++{ ++ GGduVolumeMonitor *monitor = G_GDU_VOLUME_MONITOR (user_data); ++ ++ /*g_debug ("presentable_added %p: %s", presentable, gdu_presentable_get_id (presentable));*/ ++ ++ update_all (monitor, TRUE); ++} ++ ++static void ++presentable_removed (GduPool *pool, ++ GduPresentable *presentable, ++ gpointer user_data) ++{ ++ GGduVolumeMonitor *monitor = G_GDU_VOLUME_MONITOR (user_data); ++ ++ /*g_debug ("presentable_removed %p: %s", presentable, gdu_presentable_get_id (presentable));*/ ++ ++ update_all (monitor, TRUE); ++} ++ ++static void ++presentable_changed (GduPool *pool, ++ GduPresentable *presentable, ++ gpointer user_data) ++{ ++ GGduVolumeMonitor *monitor = G_GDU_VOLUME_MONITOR (user_data); ++ ++ /*g_debug ("presentable_changed %p: %s", presentable, gdu_presentable_get_id (presentable));*/ ++ ++ update_all (monitor, TRUE); ++} ++ ++static void ++presentable_job_changed (GduPool *pool, ++ GduPresentable *presentable, ++ gpointer user_data) ++{ ++ GGduVolumeMonitor *monitor = G_GDU_VOLUME_MONITOR (user_data); ++ ++ /*g_debug ("presentable_job_changed %p: %s", presentable, gdu_presentable_get_id (presentable));*/ ++ ++ update_all (monitor, TRUE); ++} ++ ++static GObject * ++g_gdu_volume_monitor_constructor (GType type, ++ guint n_construct_properties, ++ GObjectConstructParam *construct_properties) ++{ ++ GObject *object; ++ GGduVolumeMonitor *monitor; ++ GGduVolumeMonitorClass *klass; ++ GObjectClass *parent_class; ++ ++ if (the_volume_monitor != NULL) ++ { ++ object = g_object_ref (the_volume_monitor); ++ return object; ++ } ++ ++ /*g_warning ("creating gdu vm");*/ ++ ++ object = NULL; ++ ++ /* Invoke parent constructor. */ ++ klass = G_GDU_VOLUME_MONITOR_CLASS (g_type_class_peek (G_TYPE_GDU_VOLUME_MONITOR)); ++ parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass)); ++ object = parent_class->constructor (type, ++ n_construct_properties, ++ construct_properties); ++ ++ monitor = G_GDU_VOLUME_MONITOR (object); ++ ++ monitor->mount_monitor = g_unix_mount_monitor_new (); ++ ++ g_signal_connect (monitor->mount_monitor, ++ "mounts_changed", ++ G_CALLBACK (mounts_changed), ++ monitor); ++ ++ g_signal_connect (monitor->mount_monitor, ++ "mountpoints_changed", ++ G_CALLBACK (mountpoints_changed), ++ monitor); ++ ++ monitor->pool = gdu_pool_new (); ++ ++ g_signal_connect (monitor->pool, ++ "presentable_added", ++ G_CALLBACK (presentable_added), ++ monitor); ++ ++ g_signal_connect (monitor->pool, ++ "presentable_removed", ++ G_CALLBACK (presentable_removed), ++ monitor); ++ ++ g_signal_connect (monitor->pool, ++ "presentable_changed", ++ G_CALLBACK (presentable_changed), ++ monitor); ++ ++ g_signal_connect (monitor->pool, ++ "presentable_job_changed", ++ G_CALLBACK (presentable_job_changed), ++ monitor); ++ ++ update_all (monitor, FALSE); ++ ++ the_volume_monitor = monitor; ++ ++ return object; ++} ++ ++static void ++g_gdu_volume_monitor_init (GGduVolumeMonitor *monitor) ++{ ++} ++ ++static gboolean ++is_supported (void) ++{ ++ /* TODO: return FALSE if DeviceKit-disks is not available */ ++ return TRUE; ++} ++ ++static void ++g_gdu_volume_monitor_class_init (GGduVolumeMonitorClass *klass) ++{ ++ GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ++ GVolumeMonitorClass *monitor_class = G_VOLUME_MONITOR_CLASS (klass); ++ GNativeVolumeMonitorClass *native_class = G_NATIVE_VOLUME_MONITOR_CLASS (klass); ++ ++ gobject_class->constructor = g_gdu_volume_monitor_constructor; ++ gobject_class->finalize = g_gdu_volume_monitor_finalize; ++ gobject_class->dispose = g_gdu_volume_monitor_dispose; ++ ++ monitor_class->get_mounts = get_mounts; ++ monitor_class->get_volumes = get_volumes; ++ monitor_class->get_connected_drives = get_connected_drives; ++ monitor_class->get_volume_for_uuid = get_volume_for_uuid; ++ monitor_class->get_mount_for_uuid = get_mount_for_uuid; ++ monitor_class->is_supported = is_supported; ++ ++ native_class->get_mount_for_mount_path = get_mount_for_mount_path; ++} ++ ++/** ++ * g_gdu_volume_monitor_new: ++ * ++ * Returns: a new #GVolumeMonitor. ++ **/ ++GVolumeMonitor * ++g_gdu_volume_monitor_new (void) ++{ ++ GGduVolumeMonitor *monitor; ++ ++ monitor = g_object_new (G_TYPE_GDU_VOLUME_MONITOR, NULL); ++ ++ return G_VOLUME_MONITOR (monitor); ++} ++ ++static void ++diff_sorted_lists (GList *list1, ++ GList *list2, ++ GCompareFunc compare, ++ GList **added, ++ GList **removed) ++{ ++ int order; ++ ++ *added = *removed = NULL; ++ ++ while (list1 != NULL && ++ list2 != NULL) ++ { ++ order = (*compare) (list1->data, list2->data); ++ if (order < 0) ++ { ++ *removed = g_list_prepend (*removed, list1->data); ++ list1 = list1->next; ++ } ++ else if (order > 0) ++ { ++ *added = g_list_prepend (*added, list2->data); ++ list2 = list2->next; ++ } ++ else ++ { /* same item */ ++ list1 = list1->next; ++ list2 = list2->next; ++ } ++ } ++ ++ while (list1 != NULL) ++ { ++ *removed = g_list_prepend (*removed, list1->data); ++ list1 = list1->next; ++ } ++ while (list2 != NULL) ++ { ++ *added = g_list_prepend (*added, list2->data); ++ list2 = list2->next; ++ } ++} ++ ++static GGduVolume * ++find_volume_for_mount_path (GGduVolumeMonitor *monitor, ++ const char *mount_path) ++{ ++ GList *l; ++ GGduVolume *found; ++ ++ found = NULL; ++ ++ for (l = monitor->volumes; l != NULL; l = l->next) ++ { ++ GGduVolume *volume = l->data; ++ ++ if (g_gdu_volume_has_mount_path (volume, mount_path)) ++ { ++ found = volume; ++ break; ++ } ++ } ++ ++ return found; ++} ++ ++static GGduMount * ++find_mount_by_mount_path (GGduVolumeMonitor *monitor, ++ const char *mount_path) ++{ ++ GList *l; ++ ++ for (l = monitor->mounts; l != NULL; l = l->next) ++ { ++ GGduMount *mount = l->data; ++ ++ if (g_gdu_mount_has_mount_path (mount, mount_path)) ++ return mount; ++ } ++ ++ return NULL; ++} ++ ++/* TODO: move to gio */ ++static gboolean ++_g_unix_mount_point_guess_should_display (GUnixMountPoint *mount_point) ++{ ++ const char *mount_path; ++ ++ mount_path = g_unix_mount_point_get_mount_path (mount_point); ++ ++ /* Never display internal mountpoints */ ++ if (g_unix_is_mount_path_system_internal (mount_path)) ++ return FALSE; ++ ++ /* Only display things in /media (which are generally user mountable) ++ and home dir (fuse stuff) */ ++ if (g_str_has_prefix (mount_path, "/media/")) ++ return TRUE; ++ ++ if (g_str_has_prefix (mount_path, g_get_home_dir ())) ++ return TRUE; ++ ++ return FALSE; ++} ++ ++static GUnixMountPoint * ++get_mount_point_for_device (GduDevice *d, GList *fstab_mount_points) ++{ ++ GList *l; ++ const gchar *device_file; ++ const gchar *mount_path; ++ GUnixMountPoint *ret; ++ ++ ret = NULL; ++ ++ mount_path = gdu_device_get_mount_path (d); ++ ++ device_file = gdu_device_get_device_file (d); ++ ++ for (l = fstab_mount_points; l != NULL; l = l->next) ++ { ++ GUnixMountPoint *mount_point = l->data; ++ const gchar *device_file; ++ const gchar *fstab_mount_path; ++ ++ fstab_mount_path = g_unix_mount_point_get_mount_path (mount_point); ++ if (g_strcmp0 (mount_path, fstab_mount_path) == 0) ++ { ++ ret = mount_point; ++ goto out; ++ } ++ ++ device_file = g_unix_mount_point_get_device_path (mount_point); ++ if (g_str_has_prefix (device_file, "LABEL=")) ++ { ++ if (g_strcmp0 (device_file + 6, gdu_device_id_get_label (d)) == 0) ++ { ++ ret = mount_point; ++ goto out; ++ } ++ } ++ else if (g_str_has_prefix (device_file, "UUID=")) ++ { ++ if (g_ascii_strcasecmp (device_file + 5, gdu_device_id_get_uuid (d)) == 0) ++ { ++ ret = mount_point; ++ goto out; ++ } ++ } ++ else ++ { ++ char resolved_device_file[PATH_MAX]; ++ ++ /* handle symlinks such as /dev/disk/by-uuid/47C2-1994 */ ++ if (realpath (device_file, resolved_device_file) != NULL && ++ g_strcmp0 (resolved_device_file, device_file) == 0) ++ { ++ ret = mount_point; ++ goto out; ++ } ++ } ++ } ++ ++ out: ++ return ret; ++} ++ ++static gboolean ++should_mount_be_ignored (GduPool *pool, GduDevice *d) ++{ ++ gboolean ret; ++ const gchar *mount_path; ++ GUnixMountEntry *mount_entry; ++ ++ ret = FALSE; ++ ++ if (gdu_device_is_mounted (d)) ++ goto out; ++ ++ mount_path = gdu_device_get_mount_path (d); ++ if (mount_path == NULL || strlen (mount_path) == 0) ++ goto out; ++ ++ mount_entry = g_unix_mount_at (mount_path, NULL); ++ if (mount_entry != NULL) ++ { ++ if (!g_unix_mount_guess_should_display (mount_entry)) ++ { ++ ret = TRUE; ++ } ++ g_unix_mount_free (mount_entry); ++ } ++ ++ out: ++ return ret; ++} ++ ++static gboolean ++should_volume_be_ignored (GduPool *pool, GduVolume *volume, GList *fstab_mount_points) ++{ ++ GduDevice *device; ++ gboolean ret; ++ const gchar *usage; ++ const gchar *type; ++ ++ ret = TRUE; ++ device = NULL; ++ ++ device = gdu_presentable_get_device (GDU_PRESENTABLE (volume)); ++ ++ usage = gdu_device_id_get_usage (device); ++ type = gdu_device_id_get_type (device); ++ ++ if (g_strcmp0 (usage, "filesystem") == 0) ++ { ++ GUnixMountPoint *mount_point; ++ ++ /* don't ignore volumes with a mountable filesystem unless ++ * ++ * - volume is referenced in /etc/fstab and deemed to be ignored ++ * ++ * - volume is mounted and should_mount_be_ignored() deems it should be ignored ++ * ++ * - volume is a cleartext LUKS device as the cryptotext LUKS volume will morph ++ * into the cleartext volume when unlocked (see ggduvolume.c) ++ */ ++ ++ if (gdu_device_is_luks_cleartext (device)) ++ goto out; ++ ++ mount_point = get_mount_point_for_device (device, fstab_mount_points); ++ if (mount_point != NULL && !_g_unix_mount_point_guess_should_display (mount_point)) ++ goto out; ++ ++ if (gdu_device_is_mounted (device)) ++ { ++ ret = should_mount_be_ignored (pool, device); ++ goto out; ++ } ++ ++ ret = FALSE; ++ ++ } ++ else if (g_strcmp0 (usage, "crypto") == 0 && g_strcmp0 (type, "crypto_LUKS") == 0) ++ { ++ /* don't ignore LUKS volumes */ ++ ret = FALSE; ++ } ++ ++ out: ++ ++ g_object_unref (device); ++ return ret; ++} ++ ++static gboolean ++should_drive_be_ignored (GduPool *pool, GduDrive *d, GList *fstab_mount_points) ++{ ++ GduDevice *device; ++ gboolean ret; ++ gboolean has_volumes; ++ gboolean all_volumes_are_ignored; ++ GList *enclosed; ++ GList *l; ++ ++ ret = FALSE; ++ device = NULL; ++ enclosed = NULL; ++ ++ device = gdu_presentable_get_device (GDU_PRESENTABLE (d)); ++ ++ /* the GduDevice for an activatable drive (such as RAID) is NULL if the drive is not ++ * activated; never ignore these ++ */ ++ if (device == NULL) ++ goto out; ++ ++ /* never ignore drives with removable media */ ++ if (gdu_device_is_removable (device)) ++ goto out; ++ ++ has_volumes = FALSE; ++ all_volumes_are_ignored = TRUE; ++ ++ /* never ignore a drive if it has volumes that we don't want to ignore */ ++ enclosed = gdu_pool_get_enclosed_presentables (pool, GDU_PRESENTABLE (d)); ++ for (l = enclosed; l != NULL; l = l->next) ++ { ++ GduPresentable *enclosed_presentable = GDU_PRESENTABLE (l->data); ++ ++ /* There might be other presentables that GduVolume objects; for example GduVolumeHole */ ++ if (GDU_IS_VOLUME (enclosed_presentable)) ++ { ++ GduVolume *volume = GDU_VOLUME (enclosed_presentable); ++ ++ has_volumes = TRUE; ++ ++ if (!should_volume_be_ignored (pool, volume, fstab_mount_points)) ++ { ++ all_volumes_are_ignored = FALSE; ++ break; ++ } ++ } ++ } ++ ++ ret = has_volumes && all_volumes_are_ignored; ++ ++ out: ++ g_list_foreach (enclosed, (GFunc) g_object_unref, NULL); ++ g_list_free (enclosed); ++ ++ if (device != NULL) ++ g_object_unref (device); ++ ++ return ret; ++} ++ ++static void ++list_emit (GGduVolumeMonitor *monitor, ++ const char *monitor_signal, ++ const char *object_signal, ++ GList *objects) ++{ ++ GList *l; ++ ++ for (l = objects; l != NULL; l = l->next) ++ { ++ g_signal_emit_by_name (monitor, monitor_signal, l->data); ++ if (object_signal) ++ g_signal_emit_by_name (l->data, object_signal); ++ } ++} ++ ++static void ++update_all (GGduVolumeMonitor *monitor, ++ gboolean emit_changes) ++{ ++ GList *added_drives, *removed_drives; ++ GList *added_volumes, *removed_volumes; ++ GList *added_mounts, *removed_mounts; ++ ++ added_drives = NULL; ++ removed_drives = NULL; ++ added_volumes = NULL; ++ removed_volumes = NULL; ++ added_mounts = NULL; ++ removed_mounts = NULL; ++ ++ update_drives (monitor, &added_drives, &removed_drives); ++ update_volumes (monitor, &added_volumes, &removed_volumes); ++ update_mounts (monitor, &added_mounts, &removed_mounts); ++ update_discs (monitor, ++ &added_volumes, &removed_volumes, ++ &added_mounts, &removed_mounts); ++ ++ if (emit_changes) ++ { ++ list_emit (monitor, ++ "drive_disconnected", NULL, ++ removed_drives); ++ list_emit (monitor, ++ "drive_connected", NULL, ++ added_drives); ++ ++ list_emit (monitor, ++ "volume_removed", "removed", ++ removed_volumes); ++ list_emit (monitor, ++ "volume_added", NULL, ++ added_volumes); ++ ++ list_emit (monitor, ++ "mount_removed", "unmounted", ++ removed_mounts); ++ list_emit (monitor, ++ "mount_added", NULL, ++ added_mounts); ++ } ++ ++ list_free (removed_drives); ++ list_free (added_drives); ++ list_free (removed_volumes); ++ list_free (added_volumes); ++ list_free (removed_mounts); ++ list_free (added_mounts); ++} ++ ++static GGduMount * ++find_disc_mount_for_volume (GGduVolumeMonitor *monitor, ++ GGduVolume *volume) ++{ ++ GList *l; ++ ++ for (l = monitor->disc_mounts; l != NULL; l = l->next) ++ { ++ GGduMount *mount = G_GDU_MOUNT (l->data); ++ ++ if (g_gdu_mount_has_volume (mount, volume)) ++ return mount; ++ } ++ ++ return NULL; ++} ++ ++static GGduVolume * ++find_disc_volume_for_device_file (GGduVolumeMonitor *monitor, ++ const gchar *device_file) ++{ ++ GList *l; ++ ++ for (l = monitor->disc_volumes; l != NULL; l = l->next) ++ { ++ GGduVolume *volume = G_GDU_VOLUME (l->data); ++ ++ if (g_gdu_volume_has_device_file (volume, device_file)) ++ return volume; ++ } ++ ++ return NULL; ++} ++ ++static GGduVolume * ++find_volume_for_device_file (GGduVolumeMonitor *monitor, ++ const gchar *device_file) ++{ ++ GList *l; ++ ++ for (l = monitor->volumes; l != NULL; l = l->next) ++ { ++ GGduVolume *volume = G_GDU_VOLUME (l->data); ++ ++ if (g_gdu_volume_has_device_file (volume, device_file)) ++ return volume; ++ } ++ ++ return NULL; ++} ++ ++static GGduDrive * ++find_drive_by_device_file (GGduVolumeMonitor *monitor, ++ const gchar *device_file) ++{ ++ GList *l; ++ ++ for (l = monitor->drives; l != NULL; l = l->next) ++ { ++ GGduDrive *drive = G_GDU_DRIVE (l->data); ++ ++ if (g_gdu_drive_has_device_file (drive, device_file)) ++ return drive; ++ } ++ ++ return NULL; ++} ++ ++static GGduDrive * ++find_drive_by_presentable (GGduVolumeMonitor *monitor, ++ GduPresentable *presentable) ++{ ++ GList *l; ++ ++ for (l = monitor->drives; l != NULL; l = l->next) ++ { ++ GGduDrive *drive = G_GDU_DRIVE (l->data); ++ ++ if (g_gdu_drive_has_presentable (drive, presentable)) ++ return drive; ++ } ++ ++ return NULL; ++} ++ ++static void ++update_drives (GGduVolumeMonitor *monitor, ++ GList **added_drives, ++ GList **removed_drives) ++{ ++ GList *cur_drives; ++ GList *new_drives; ++ GList *removed, *added; ++ GList *l, *ll; ++ GGduDrive *drive; ++ GList *fstab_mount_points; ++ ++ fstab_mount_points = g_unix_mount_points_get (NULL); ++ ++ cur_drives = NULL; ++ for (l = monitor->drives; l != NULL; l = l->next) ++ cur_drives = g_list_prepend (cur_drives, g_gdu_drive_get_presentable (G_GDU_DRIVE (l->data))); ++ ++ /* remove devices we want to ignore - we do it here so we get to reevaluate ++ * on the next update whether they should still be ignored ++ */ ++ new_drives = gdu_pool_get_presentables (monitor->pool); ++ for (l = new_drives; l != NULL; l = ll) ++ { ++ GduPresentable *p = GDU_PRESENTABLE (l->data); ++ ll = l->next; ++ if (!GDU_IS_DRIVE (p) || should_drive_be_ignored (monitor->pool, GDU_DRIVE (p), fstab_mount_points)) ++ { ++ g_object_unref (p); ++ new_drives = g_list_delete_link (new_drives, l); ++ } ++ } ++ ++ cur_drives = g_list_sort (cur_drives, (GCompareFunc) gdu_presentable_compare); ++ new_drives = g_list_sort (new_drives, (GCompareFunc) gdu_presentable_compare); ++ diff_sorted_lists (cur_drives, ++ new_drives, (GCompareFunc) gdu_presentable_compare, ++ &added, &removed); ++ ++ for (l = removed; l != NULL; l = l->next) ++ { ++ GduPresentable *p = GDU_PRESENTABLE (l->data); ++ GduDevice *d; ++ ++ d = gdu_presentable_get_device (p); ++ ++ drive = find_drive_by_presentable (monitor, p); ++ if (drive != NULL) ++ { ++ /*g_debug ("removing drive %s", gdu_presentable_get_id (p));*/ ++ g_gdu_drive_disconnected (drive); ++ monitor->drives = g_list_remove (monitor->drives, drive); ++ *removed_drives = g_list_prepend (*removed_drives, drive); ++ } ++ if (d != NULL) ++ g_object_unref (d); ++ } ++ ++ for (l = added; l != NULL; l = l->next) ++ { ++ GduPresentable *p = GDU_PRESENTABLE (l->data); ++ GduDevice *d; ++ ++ d = gdu_presentable_get_device (p); ++ ++ drive = find_drive_by_presentable (monitor, p); ++ if (drive == NULL) ++ { ++ /*g_debug ("adding drive %s", gdu_presentable_get_id (p));*/ ++ drive = g_gdu_drive_new (G_VOLUME_MONITOR (monitor), p); ++ if (drive != NULL) ++ { ++ monitor->drives = g_list_prepend (monitor->drives, drive); ++ *added_drives = g_list_prepend (*added_drives, g_object_ref (drive)); ++ } ++ } ++ if (d != NULL) ++ g_object_unref (d); ++ } ++ ++ g_list_free (added); ++ g_list_free (removed); ++ ++ g_list_free (cur_drives); ++ ++ g_list_foreach (new_drives, (GFunc) g_object_unref, NULL); ++ g_list_free (new_drives); ++ ++ g_list_foreach (fstab_mount_points, (GFunc) g_unix_mount_point_free, NULL); ++ g_list_free (fstab_mount_points); ++} ++ ++static void ++update_volumes (GGduVolumeMonitor *monitor, ++ GList **added_volumes, ++ GList **removed_volumes) ++{ ++ GList *cur_volumes; ++ GList *new_volumes; ++ GList *removed, *added; ++ GList *l, *ll; ++ GGduVolume *volume; ++ GGduDrive *drive; ++ GList *fstab_mount_points; ++ ++ fstab_mount_points = g_unix_mount_points_get (NULL); ++ ++ cur_volumes = NULL; ++ for (l = monitor->volumes; l != NULL; l = l->next) ++ cur_volumes = g_list_prepend (cur_volumes, g_gdu_volume_get_presentable (G_GDU_VOLUME (l->data))); ++ ++ /* remove devices we want to ignore - we do it here so we get to reevaluate ++ * on the next update whether they should still be ignored ++ */ ++ new_volumes = gdu_pool_get_presentables (monitor->pool); ++ for (l = new_volumes; l != NULL; l = ll) ++ { ++ GduPresentable *p = GDU_PRESENTABLE (l->data); ++ ll = l->next; ++ if (!GDU_IS_VOLUME (p) || should_volume_be_ignored (monitor->pool, GDU_VOLUME (p), fstab_mount_points)) ++ { ++ g_object_unref (p); ++ new_volumes = g_list_delete_link (new_volumes, l); ++ } ++ } ++ ++ cur_volumes = g_list_sort (cur_volumes, (GCompareFunc) gdu_presentable_compare); ++ new_volumes = g_list_sort (new_volumes, (GCompareFunc) gdu_presentable_compare); ++ diff_sorted_lists (cur_volumes, ++ new_volumes, (GCompareFunc) gdu_presentable_compare, ++ &added, &removed); ++ ++ for (l = removed; l != NULL; l = l->next) ++ { ++ GduPresentable *p = GDU_PRESENTABLE (l->data); ++ GduDevice *d; ++ ++ d = gdu_presentable_get_device (p); ++ ++ volume = find_volume_for_device_file (monitor, gdu_device_get_device_file (d)); ++ if (volume != NULL) ++ { ++ /*g_debug ("removing volume %s", gdu_device_get_device_file (d));*/ ++ g_gdu_volume_removed (volume); ++ monitor->volumes = g_list_remove (monitor->volumes, volume); ++ *removed_volumes = g_list_prepend (*removed_volumes, volume); ++ } ++ g_object_unref (d); ++ } ++ ++ for (l = added; l != NULL; l = l->next) ++ { ++ GduPresentable *p = GDU_PRESENTABLE (l->data); ++ GduDevice *d; ++ ++ d = gdu_presentable_get_device (p); ++ ++ volume = find_volume_for_device_file (monitor, gdu_device_get_device_file (d)); ++ if (volume == NULL) ++ { ++ GduPresentable *toplevel_presentable; ++ ++ toplevel_presentable = gdu_presentable_get_toplevel (p); ++ if (toplevel_presentable != NULL) ++ { ++ GduDevice *toplevel_device; ++ ++ toplevel_device = gdu_presentable_get_device (toplevel_presentable); ++ drive = find_drive_by_device_file (monitor, gdu_device_get_device_file (toplevel_device)); ++ /*g_debug ("adding volume %s (drive %s)", ++ gdu_device_get_device_file (d), ++ gdu_device_get_device_file (toplevel_device));*/ ++ g_object_unref (toplevel_device); ++ g_object_unref (toplevel_presentable); ++ } ++ else ++ { ++ drive = NULL; ++ /*g_debug ("adding volume %s (no drive)", gdu_device_get_device_file (d));*/ ++ } ++ ++ volume = g_gdu_volume_new (G_VOLUME_MONITOR (monitor), ++ GDU_VOLUME (p), ++ drive, ++ NULL); ++ if (volume != NULL) ++ { ++ monitor->volumes = g_list_prepend (monitor->volumes, volume); ++ *added_volumes = g_list_prepend (*added_volumes, g_object_ref (volume)); ++ } ++ } ++ ++ g_object_unref (d); ++ } ++ ++ g_list_free (added); ++ g_list_free (removed); ++ ++ g_list_foreach (new_volumes, (GFunc) g_object_unref, NULL); ++ g_list_free (new_volumes); ++ ++ g_list_free (cur_volumes); ++ ++ g_list_foreach (fstab_mount_points, (GFunc) g_unix_mount_point_free, NULL); ++ g_list_free (fstab_mount_points); ++} ++ ++static void ++update_mounts (GGduVolumeMonitor *monitor, ++ GList **added_mounts, ++ GList **removed_mounts) ++{ ++ GList *new_mounts; ++ GList *removed, *added; ++ GList *l, *ll; ++ GGduMount *mount; ++ GGduVolume *volume; ++ const char *device_file; ++ const char *mount_path; ++ ++ new_mounts = g_unix_mounts_get (NULL); ++ ++ /* remove mounts we want to ignore - we do it here so we get to reevaluate ++ * on the next update whether they should still be ignored ++ */ ++ for (l = new_mounts; l != NULL; l = ll) ++ { ++ GUnixMountEntry *mount_entry = l->data; ++ ll = l->next; ++ ++ /* keep in sync with should_mount_be_ignored() */ ++ if (!g_unix_mount_guess_should_display (mount_entry)) ++ { ++ g_unix_mount_free (mount_entry); ++ new_mounts = g_list_delete_link (new_mounts, l); ++ } ++ } ++ ++ new_mounts = g_list_sort (new_mounts, (GCompareFunc) g_unix_mount_compare); ++ ++ diff_sorted_lists (monitor->last_mounts, ++ new_mounts, (GCompareFunc) g_unix_mount_compare, ++ &added, &removed); ++ ++ for (l = removed; l != NULL; l = l->next) ++ { ++ GUnixMountEntry *mount_entry = l->data; ++ ++ mount = find_mount_by_mount_path (monitor, g_unix_mount_get_mount_path (mount_entry)); ++ if (mount) ++ { ++ /*g_debug ("removing mount %s", g_unix_mount_get_device_path (mount_entry));*/ ++ g_gdu_mount_unmounted (mount); ++ monitor->mounts = g_list_remove (monitor->mounts, mount); ++ ++ *removed_mounts = g_list_prepend (*removed_mounts, mount); ++ } ++ } ++ ++ for (l = added; l != NULL; l = l->next) ++ { ++ GUnixMountEntry *mount_entry = l->data; ++ ++ device_file = g_unix_mount_get_device_path (mount_entry); ++ mount_path = g_unix_mount_get_mount_path (mount_entry); ++ volume = find_volume_for_device_file (monitor, device_file); ++ if (volume == NULL) ++ volume = find_volume_for_mount_path (monitor, mount_path); ++ ++ /*g_debug ("adding mount %s (vol %p)", g_unix_mount_get_device_path (mount_entry), volume);*/ ++ mount = g_gdu_mount_new (G_VOLUME_MONITOR (monitor), mount_entry, volume); ++ if (mount) ++ { ++ monitor->mounts = g_list_prepend (monitor->mounts, mount); ++ *added_mounts = g_list_prepend (*added_mounts, g_object_ref (mount)); ++ } ++ } ++ ++ g_list_free (added); ++ g_list_free (removed); ++ g_list_foreach (monitor->last_mounts, ++ (GFunc)g_unix_mount_free, NULL); ++ g_list_free (monitor->last_mounts); ++ monitor->last_mounts = new_mounts; ++} ++ ++static void ++update_discs (GGduVolumeMonitor *monitor, ++ GList **added_volumes, ++ GList **removed_volumes, ++ GList **added_mounts, ++ GList **removed_mounts) ++{ ++ GList *cur_discs; ++ GList *new_discs; ++ GList *removed, *added; ++ GList *l, *ll; ++ GGduDrive *drive; ++ GGduVolume *volume; ++ GGduMount *mount; ++ ++ /* we also need to generate GVolume + GMount objects for ++ * ++ * - optical discs that have audio ++ * - optical discs that are blank ++ * ++ */ ++ ++ cur_discs = NULL; ++ for (l = monitor->disc_volumes; l != NULL; l = l->next) ++ cur_discs = g_list_prepend (cur_discs, g_gdu_volume_get_presentable (G_GDU_VOLUME (l->data))); ++ ++ new_discs = gdu_pool_get_presentables (monitor->pool); ++ for (l = new_discs; l != NULL; l = ll) ++ { ++ GduPresentable *p = GDU_PRESENTABLE (l->data); ++ GduDevice *d; ++ gboolean ignore; ++ ++ ll = l->next; ++ ignore = TRUE; ++ ++ /* filter out everything but discs that are blank or has audio */ ++ d = gdu_presentable_get_device (p); ++ if (GDU_IS_VOLUME (p) && d != NULL && gdu_device_is_optical_disc (d)) ++ { ++ if (gdu_device_optical_disc_get_num_audio_tracks (d) > 0 || gdu_device_optical_disc_get_is_blank (d)) ++ ignore = FALSE; ++ } ++ ++ if (ignore) ++ { ++ g_object_unref (p); ++ new_discs = g_list_delete_link (new_discs, l); ++ } ++ ++ if (d != NULL) ++ g_object_unref (d); ++ } ++ ++ cur_discs = g_list_sort (cur_discs, (GCompareFunc) gdu_presentable_compare); ++ new_discs = g_list_sort (new_discs, (GCompareFunc) gdu_presentable_compare); ++ diff_sorted_lists (cur_discs, new_discs, (GCompareFunc) gdu_presentable_compare, &added, &removed); ++ ++ for (l = removed; l != NULL; l = l->next) ++ { ++ GduPresentable *p = GDU_PRESENTABLE (l->data); ++ GduDevice *d; ++ ++ d = gdu_presentable_get_device (p); ++ ++ volume = find_disc_volume_for_device_file (monitor, gdu_device_get_device_file (d)); ++ mount = find_disc_mount_for_volume (monitor, volume); ++ ++ if (mount != NULL) ++ { ++ /*g_debug ("removing disc mount %s", gdu_device_get_device_file (d));*/ ++ g_gdu_mount_unmounted (mount); ++ monitor->disc_mounts = g_list_remove (monitor->disc_mounts, mount); ++ *removed_mounts = g_list_prepend (*removed_mounts, mount); ++ } ++ ++ if (volume != NULL) ++ { ++ /*g_debug ("removing disc volume %s", gdu_device_get_device_file (d));*/ ++ g_gdu_volume_removed (volume); ++ monitor->disc_volumes = g_list_remove (monitor->disc_volumes, volume); ++ *removed_volumes = g_list_prepend (*removed_volumes, volume); ++ } ++ ++ g_object_unref (d); ++ } ++ ++ for (l = added; l != NULL; l = l->next) ++ { ++ GduPresentable *p = GDU_PRESENTABLE (l->data); ++ GduDevice *d; ++ gboolean is_blank; ++ ++ d = gdu_presentable_get_device (p); ++ ++ is_blank = gdu_device_optical_disc_get_is_blank (d); ++ ++ volume = find_disc_volume_for_device_file (monitor, gdu_device_get_device_file (d)); ++ if (volume == NULL) ++ { ++ GduPresentable *toplevel_presentable; ++ ++ toplevel_presentable = gdu_presentable_get_toplevel (p); ++ if (toplevel_presentable != NULL) ++ { ++ GduDevice *toplevel_device; ++ ++ toplevel_device = gdu_presentable_get_device (toplevel_presentable); ++ drive = find_drive_by_device_file (monitor, gdu_device_get_device_file (toplevel_device)); ++ /*g_debug ("adding volume %s (drive %s)", ++ gdu_device_get_device_file (d), ++ gdu_device_get_device_file (toplevel_device));*/ ++ g_object_unref (toplevel_device); ++ g_object_unref (toplevel_presentable); ++ } ++ else ++ { ++ drive = NULL; ++ /*g_debug ("adding volume %s (no drive)", gdu_device_get_device_file (d));*/ ++ } ++ ++ mount = NULL; ++ if (is_blank) ++ { ++ volume = g_gdu_volume_new (G_VOLUME_MONITOR (monitor), ++ GDU_VOLUME (p), ++ drive, ++ NULL); ++ mount = g_gdu_mount_new (G_VOLUME_MONITOR (monitor), ++ NULL, ++ volume); ++ } ++ else ++ { ++ gchar *uri; ++ gchar *device_basename; ++ GFile *activation_root; ++ ++ /* the gvfsd-cdda backend uses URI's like these */ ++ device_basename = g_path_get_basename (gdu_device_get_device_file (d)); ++ uri = g_strdup_printf ("cdda://%s", device_basename); ++ activation_root = g_file_new_for_uri (uri); ++ g_free (device_basename); ++ g_free (uri); ++ ++ volume = g_gdu_volume_new (G_VOLUME_MONITOR (monitor), ++ GDU_VOLUME (p), ++ drive, ++ activation_root); ++ ++ g_object_unref (activation_root); ++ } ++ ++ if (volume != NULL) ++ { ++ monitor->disc_volumes = g_list_prepend (monitor->disc_volumes, volume); ++ *added_volumes = g_list_prepend (*added_volumes, g_object_ref (volume)); ++ ++ if (mount != NULL) ++ { ++ monitor->disc_mounts = g_list_prepend (monitor->disc_mounts, mount); ++ *added_mounts = g_list_prepend (*added_mounts, g_object_ref (mount)); ++ } ++ } ++ } ++ ++ g_object_unref (d); ++ } ++ ++ g_list_free (added); ++ g_list_free (removed); ++ ++ g_list_foreach (new_discs, (GFunc) g_object_unref, NULL); ++ g_list_free (new_discs); ++ ++ g_list_free (cur_discs); ++} +--- /dev/null 2009-03-02 15:45:22.087299058 -0500 ++++ monitor/gdu/ggduvolumemonitor.h 2009-03-01 23:49:48.000000000 -0500 +@@ -0,0 +1,60 @@ ++/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ ++/* gvfs - extensions for gio ++ * ++ * Copyright (C) 2006-2009 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General ++ * Public License along with this library; if not, write to the ++ * Free Software Foundation, Inc., 59 Temple Place, Suite 330, ++ * Boston, MA 02111-1307, USA. ++ * ++ * Author: David Zeuthen ++ */ ++ ++#ifndef __G_GDU_VOLUME_MONITOR_H__ ++#define __G_GDU_VOLUME_MONITOR_H__ ++ ++#include ++#include ++#include ++ ++#include ++ ++G_BEGIN_DECLS ++ ++#define G_TYPE_GDU_VOLUME_MONITOR (g_gdu_volume_monitor_get_type ()) ++#define G_GDU_VOLUME_MONITOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_GDU_VOLUME_MONITOR, GGduVolumeMonitor)) ++#define G_GDU_VOLUME_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_GDU_VOLUME_MONITOR, GGduVolumeMonitorClass)) ++#define G_IS_GDU_VOLUME_MONITOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_GDU_VOLUME_MONITOR)) ++#define G_IS_GDU_VOLUME_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_GDU_VOLUME_MONITOR)) ++ ++typedef struct _GGduVolumeMonitor GGduVolumeMonitor; ++typedef struct _GGduVolumeMonitorClass GGduVolumeMonitorClass; ++ ++/* Forward definitions */ ++typedef struct _GGduDrive GGduDrive; ++typedef struct _GGduVolume GGduVolume; ++typedef struct _GGduMount GGduMount; ++ ++struct _GGduVolumeMonitorClass { ++ GNativeVolumeMonitorClass parent_class; ++ ++}; ++ ++GType g_gdu_volume_monitor_get_type (void) G_GNUC_CONST; ++ ++GVolumeMonitor *g_gdu_volume_monitor_new (void); ++ ++G_END_DECLS ++ ++#endif /* __G_GDU_VOLUME_MONITOR_H__ */ +--- /dev/null 2009-03-02 15:45:22.087299058 -0500 ++++ monitor/gdu/polkit.c 2009-03-01 23:47:50.000000000 -0500 +@@ -0,0 +1,137 @@ ++/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ ++/* gvfs - extensions for gio ++ * ++ * Copyright (C) 2006-2009 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General ++ * Public License along with this library; if not, write to the ++ * Free Software Foundation, Inc., 59 Temple Place, Suite 330, ++ * Boston, MA 02111-1307, USA. ++ * ++ * Author: David Zeuthen ++ */ ++ ++#include ++#include ++ ++#include ++ ++#include "polkit.h" ++ ++static void ++_obtain_authz_cb (DBusMessage *reply, ++ GError *error, ++ gpointer user_data) ++{ ++ GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data); ++ gboolean gained_authz; ++ DBusError derror; ++ ++ if (error != NULL) { ++ g_simple_async_result_set_from_error (simple, error); ++ goto out; ++ } ++ ++ dbus_error_init (&derror); ++ if (!dbus_message_get_args (reply, ++ &derror, ++ DBUS_TYPE_BOOLEAN, &gained_authz, ++ DBUS_TYPE_INVALID)) ++ { ++ /* no need to translate; this only happens if the auth agent is buggy */ ++ g_simple_async_result_set_error (simple, ++ G_IO_ERROR, ++ G_IO_ERROR_FAILED, ++ "Error parsing reply for ObtainAuthorization(): %s: %s", ++ derror.name, derror.message); ++ dbus_error_free (&derror); ++ goto out; ++ } ++ ++ if (!gained_authz && error == NULL) ++ { ++ /* no need to translate, is never shown */ ++ g_simple_async_result_set_error (simple, ++ G_IO_ERROR, ++ G_IO_ERROR_FAILED_HANDLED, ++ "Didn't obtain authorization (bug in libgio user, it shouldn't display this error)"); ++ } ++ ++ out: ++ g_simple_async_result_complete (simple); ++ g_object_unref (simple); ++} ++ ++ ++gboolean ++_obtain_authz_finish (GAsyncResult *res, ++ GError **error) ++{ ++ GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res); ++ ++ g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == _obtain_authz); ++ ++ if (g_simple_async_result_propagate_error (simple, error)) ++ return FALSE; ++ else ++ return TRUE; ++} ++ ++void ++_obtain_authz (const gchar *action_id, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data) ++{ ++ DBusConnection *connection; ++ DBusMessage *message; ++ GSimpleAsyncResult *simple; ++ guint xid; ++ guint pid; ++ DBusError derror; ++ ++ dbus_error_init (&derror); ++ ++ /* this connection is already integrated and guaranteed to exist, see gvfsproxyvolumemonitordaemon.c */ ++ connection = dbus_bus_get (DBUS_BUS_SESSION, &derror); ++ ++ simple = g_simple_async_result_new (NULL, ++ callback, ++ user_data, ++ _obtain_authz); ++ ++ message = dbus_message_new_method_call ("org.freedesktop.PolicyKit.AuthenticationAgent", /* bus name */ ++ "/", /* object */ ++ "org.freedesktop.PolicyKit.AuthenticationAgent", /* interface */ ++ "ObtainAuthorization"); ++ ++ xid = 0; ++ pid = getpid (); ++ ++ dbus_message_append_args (message, ++ DBUS_TYPE_STRING, ++ &(action_id), ++ DBUS_TYPE_UINT32, ++ &(xid), ++ DBUS_TYPE_UINT32, ++ &(pid), ++ DBUS_TYPE_INVALID); ++ ++ _g_dbus_connection_call_async (connection, ++ message, ++ -1, ++ (GAsyncDBusCallback) _obtain_authz_cb, ++ simple); ++ dbus_message_unref (message); ++ dbus_connection_unref (connection); ++} +--- /dev/null 2009-03-02 15:45:22.087299058 -0500 ++++ monitor/gdu/polkit.h 2009-03-01 23:47:41.000000000 -0500 +@@ -0,0 +1,44 @@ ++/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ ++/* gvfs - extensions for gio ++ * ++ * Copyright (C) 2006-2009 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General ++ * Public License along with this library; if not, write to the ++ * Free Software Foundation, Inc., 59 Temple Place, Suite 330, ++ * Boston, MA 02111-1307, USA. ++ * ++ * Author: David Zeuthen ++ */ ++ ++ ++#ifndef __POLKIT_H__ ++#define __POLKIT_H__ ++ ++#include ++#include ++#include ++ ++G_BEGIN_DECLS ++ ++void _obtain_authz (const gchar *action_id, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data); ++ ++gboolean _obtain_authz_finish (GAsyncResult *res, ++ GError **error); ++ ++G_END_DECLS ++ ++#endif /* __POLKIT_H__ */ +--- /dev/null 2009-03-02 15:45:22.087299058 -0500 ++++ monitor/gdu/Makefile.am 2009-03-01 21:13:46.000000000 -0500 +@@ -0,0 +1,53 @@ ++ ++NULL = ++ ++libexec_PROGRAMS = gvfs-gdu-volume-monitor ++ ++ ++gvfs_gdu_volume_monitor_SOURCES = \ ++ gdu-volume-monitor-daemon.c \ ++ ggdudrive.c ggdudrive.h \ ++ ggduvolume.c ggduvolume.h \ ++ ggdumount.c ggdumount.h \ ++ ggduvolumemonitor.c ggduvolumemonitor.h \ ++ polkit.c polkit.h \ ++ $(NULL) ++ ++gvfs_gdu_volume_monitor_CFLAGS = \ ++ -DG_LOG_DOMAIN=\"GVFS-Gdu\" \ ++ -I$(top_srcdir)/common \ ++ -I$(top_srcdir)/monitor/proxy \ ++ $(GLIB_CFLAGS) \ ++ $(GDU_CFLAGS) \ ++ $(DBUS_CFLAGS) \ ++ -DGIO_MODULE_DIR=\"$(GIO_MODULE_DIR)\" \ ++ -DGVFS_LOCALEDIR=\""$(localedir)"\" \ ++ -DG_DISABLE_DEPRECATED \ ++ -DGDU_API_IS_SUBJECT_TO_CHANGE \ ++ $(NULL) ++ ++gvfs_gdu_volume_monitor_LDFLAGS = \ ++ $(NULL) ++ ++gvfs_gdu_volume_monitor_LDADD = \ ++ $(GLIB_LIBS) \ ++ $(GDU_LIBS) \ ++ $(DBUS_LIBS) \ ++ $(top_builddir)/common/libgvfscommon.la \ ++ $(top_builddir)/monitor/proxy/libgvfsproxyvolumemonitordaemon-noin.la \ ++ $(NULL) ++ ++remote_volume_monitorsdir = $(datadir)/gvfs/remote-volume-monitors ++remote_volume_monitors_DATA = gdu.monitor ++ ++servicedir = $(datadir)/dbus-1/services ++service_in_files = org.gtk.Private.GduVolumeMonitor.service.in ++service_DATA = $(service_in_files:.service.in=.service) ++ ++$(service_DATA): $(service_in_files) Makefile ++ @sed -e "s|\@libexecdir\@|$(libexecdir)|" $< > $@ ++ ++clean-local: ++ rm -f *~ *.loT $(BUILT_SOURCES) $(service_DATA) ++ ++EXTRA_DIST = $(service_in_files) gdu.monitor +--- /dev/null 2009-03-02 15:45:22.087299058 -0500 ++++ monitor/gdu/gdu.monitor 2009-02-11 06:42:26.000000000 -0500 +@@ -0,0 +1,5 @@ ++[RemoteVolumeMonitor] ++Name=GProxyVolumeMonitorGdu ++DBusName=org.gtk.Private.GduVolumeMonitor ++IsNative=true ++NativePriority=3 +--- /dev/null 2009-03-02 15:45:22.087299058 -0500 ++++ monitor/gdu/org.gtk.Private.GduVolumeMonitor.service.in 2009-02-19 22:34:26.000000000 -0500 +@@ -0,0 +1,3 @@ ++[D-BUS Service] ++Name=org.gtk.Private.GduVolumeMonitor ++Exec=@libexecdir@/gvfs-gdu-volume-monitor diff --git a/gvfs.spec b/gvfs.spec index 807d5d8..c72d1ed 100644 --- a/gvfs.spec +++ b/gvfs.spec @@ -1,7 +1,7 @@ Summary: Backends for the gio framework in GLib Name: gvfs Version: 1.1.7 -Release: 1%{?dist} +Release: 2%{?dist} License: LGPLv2+ Group: System Environment/Libraries URL: http://www.gtk.org @@ -9,7 +9,7 @@ Source: http://download.gnome.org/sources/gvfs/1.1/gvfs-%{version}.tar.bz2 BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) BuildRequires: pkgconfig BuildRequires: glib2-devel >= 2.19.2 -BuildRequires: dbus-glib-devel +BuildRequires: dbus-glib-devel BuildRequires: /usr/bin/ssh BuildRequires: libcdio-devel >= 0.78.2 BuildRequires: hal-devel >= 0.5.10 @@ -18,8 +18,8 @@ BuildRequires: avahi-glib-devel >= 0.6 BuildRequires: gnome-keyring-devel BuildRequires: intltool BuildRequires: gettext-devel -BuildRequires: perl(XML::Parser) BuildRequires: GConf2-devel +BuildRequires: gnome-disk-utility-devel >= 0.2 Requires(post): desktop-file-utils @@ -31,10 +31,12 @@ BuildRequires: libtool # http://bugzilla.gnome.org/show_bug.cgi?id=567235 Patch1: gvfs-0.99.2-archive-integration.patch +# http://bugzilla.gnome.org/show_bug.cgi?id=573826 +Patch2: gvfs-gdu.patch %description -The gvfs package provides backend implementations for the gio -framework in GLib. It includes ftp, sftp, cifs. +The gvfs package provides backend implementations for the gio +framework in GLib. It includes ftp, sftp, cifs. %package devel @@ -113,6 +115,7 @@ media players (Media Transfer Protocol) to applications using gvfs. %prep %setup -q %patch1 -p0 -b .archive-integration +%patch2 -p0 -b .gdu %build @@ -123,7 +126,7 @@ autoheader || : automake || : autoconf || : -%configure +%configure --enable-gdu make # %{?_smp_mflags} %install @@ -170,7 +173,9 @@ update-desktop-database &> /dev/null ||: %{_datadir}/gvfs/mounts/ftp.mount %{_datadir}/dbus-1/services/gvfs-daemon.service %{_datadir}/dbus-1/services/org.gtk.Private.HalVolumeMonitor.service +%{_datadir}/dbus-1/services/org.gtk.Private.GduVolumeMonitor.service %{_datadir}/gvfs/remote-volume-monitors/hal.monitor +%{_datadir}/gvfs/remote-volume-monitors/gdu.monitor %{_libdir}/libgvfscommon.so.* %{_libdir}/libgvfscommon-dnssd.so.* # %{_libdir}/gio/modules/libgiohal-volume-monitor.so @@ -190,6 +195,7 @@ update-desktop-database &> /dev/null ||: %{_libexecdir}/gvfsd-dnssd %{_libexecdir}/gvfsd-network %{_libexecdir}/gvfs-hal-volume-monitor +%{_libexecdir}/gvfs-gdu-volume-monitor %{_bindir}/gvfs-cat %{_bindir}/gvfs-copy %{_bindir}/gvfs-info @@ -253,6 +259,9 @@ update-desktop-database &> /dev/null ||: %changelog +* Mon Mar 2 2009 Matthias Clasen - 1.1.7-2 +- Port to DeviceKit-disks + * Mon Mar 2 2009 Tomas Bzatek - 1.1.7-1 - Update to 1.1.7