From 9d51c0de20170731db25422e858fee0e44b1dbd1 Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Thu, 9 Apr 2009 19:01:18 -0400 Subject: [PATCH 1/7] =?utf-8?q?Bug=20573826=20=E2=80=93=20gdu=20volume=20monitor?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Add the GNOME Disk Utility based volume monitor, see http://bugzilla.gnome.org/show_bug.cgi?id=573826 http://mail.gnome.org/archives/gvfs-list/2009-March/msg00002.html for details. --- configure.ac | 28 +- monitor/Makefile.am | 4 + monitor/gdu/Makefile.am | 53 + monitor/gdu/gdu-volume-monitor-daemon.c | 46 + monitor/gdu/gdu.monitor | 5 + monitor/gdu/ggdudrive.c | 691 ++++++++++ monitor/gdu/ggdudrive.h | 69 + monitor/gdu/ggdumount.c | 958 +++++++++++++ monitor/gdu/ggdumount.h | 64 + monitor/gdu/ggduvolume.c | 1408 +++++++++++++++++++ monitor/gdu/ggduvolume.h | 78 ++ monitor/gdu/ggduvolumemonitor.c | 1432 ++++++++++++++++++++ monitor/gdu/ggduvolumemonitor.h | 60 + .../org.gtk.Private.GduVolumeMonitor.service.in | 3 + monitor/gdu/polkit.c | 137 ++ monitor/gdu/polkit.h | 44 + 16 files changed, 5079 insertions(+), 1 deletions(-) create mode 100644 monitor/gdu/Makefile.am create mode 100644 monitor/gdu/gdu-volume-monitor-daemon.c create mode 100644 monitor/gdu/gdu.monitor create mode 100644 monitor/gdu/ggdudrive.c create mode 100644 monitor/gdu/ggdudrive.h create mode 100644 monitor/gdu/ggdumount.c create mode 100644 monitor/gdu/ggdumount.h create mode 100644 monitor/gdu/ggduvolume.c create mode 100644 monitor/gdu/ggduvolume.h create mode 100644 monitor/gdu/ggduvolumemonitor.c create mode 100644 monitor/gdu/ggduvolumemonitor.h create mode 100644 monitor/gdu/org.gtk.Private.GduVolumeMonitor.service.in create mode 100644 monitor/gdu/polkit.c create mode 100644 monitor/gdu/polkit.h diff --git a/configure.ac b/configure.ac index 311127e..895309b 100644 --- a/configure.ac +++ b/configure.ac @@ -198,6 +198,30 @@ AC_SUBST(GCONF_CFLAGS) AM_CONDITIONAL(USE_GCONF, [test "$msg_gconf" = "yes"]) +dnl ************************************ +dnl *** Check for gnome-disk-utility *** +dnl ************************************ + +AC_ARG_ENABLE(gdu, [ --disable-gdu build without GDU volume monitor]) +msg_gdu=no +GDU_LIBS= +GDU_CFLAGS= +GDU_REQUIRED=0.3 + +if test "x$enable_gdu" != "xno"; 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 +582,7 @@ daemon/Makefile monitor/Makefile monitor/proxy/Makefile monitor/hal/Makefile +monitor/gdu/Makefile monitor/gphoto2/Makefile gconf/Makefile programs/Makefile @@ -579,7 +604,8 @@ echo " 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 " diff --git a/monitor/Makefile.am b/monitor/Makefile.am index d4197d5..f68423e 100644 --- a/monitor/Makefile.am +++ b/monitor/Makefile.am @@ -5,6 +5,10 @@ if USE_HAL SUBDIRS += hal endif +if USE_GDU +SUBDIRS += gdu +endif + if USE_GPHOTO2 SUBDIRS += gphoto2 endif diff --git a/monitor/gdu/Makefile.am b/monitor/gdu/Makefile.am new file mode 100644 index 0000000..10b80f3 --- /dev/null +++ b/monitor/gdu/Makefile.am @@ -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 diff --git a/monitor/gdu/gdu-volume-monitor-daemon.c b/monitor/gdu/gdu-volume-monitor-daemon.c new file mode 100644 index 0000000..cdb4f84 --- /dev/null +++ b/monitor/gdu/gdu-volume-monitor-daemon.c @@ -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); +} diff --git a/monitor/gdu/gdu.monitor b/monitor/gdu/gdu.monitor new file mode 100644 index 0000000..9a97695 --- /dev/null +++ b/monitor/gdu/gdu.monitor @@ -0,0 +1,5 @@ +[RemoteVolumeMonitor] +Name=GProxyVolumeMonitorGdu +DBusName=org.gtk.Private.GduVolumeMonitor +IsNative=true +NativePriority=3 diff --git a/monitor/gdu/ggdudrive.c b/monitor/gdu/ggdudrive.c new file mode 100644 index 0000000..257a113 --- /dev/null +++ b/monitor/gdu/ggdudrive.c @@ -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; +} diff --git a/monitor/gdu/ggdudrive.h b/monitor/gdu/ggdudrive.h new file mode 100644 index 0000000..e182c29 --- /dev/null +++ b/monitor/gdu/ggdudrive.h @@ -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__ */ diff --git a/monitor/gdu/ggdumount.c b/monitor/gdu/ggdumount.c new file mode 100644 index 0000000..27c22d9 --- /dev/null +++ b/monitor/gdu/ggdumount.c @@ -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; +} diff --git a/monitor/gdu/ggdumount.h b/monitor/gdu/ggdumount.h new file mode 100644 index 0000000..62a19d8 --- /dev/null +++ b/monitor/gdu/ggdumount.h @@ -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__ */ diff --git a/monitor/gdu/ggduvolume.c b/monitor/gdu/ggduvolume.c new file mode 100644 index 0000000..b540325 --- /dev/null +++ b/monitor/gdu/ggduvolume.c @@ -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) == 0; + 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); +} diff --git a/monitor/gdu/ggduvolume.h b/monitor/gdu/ggduvolume.h new file mode 100644 index 0000000..8fd7358 --- /dev/null +++ b/monitor/gdu/ggduvolume.h @@ -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__ */ diff --git a/monitor/gdu/ggduvolumemonitor.c b/monitor/gdu/ggduvolumemonitor.c new file mode 100644 index 0000000..32604d0 --- /dev/null +++ b/monitor/gdu/ggduvolumemonitor.c @@ -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); +} diff --git a/monitor/gdu/ggduvolumemonitor.h b/monitor/gdu/ggduvolumemonitor.h new file mode 100644 index 0000000..ec559c4 --- /dev/null +++ b/monitor/gdu/ggduvolumemonitor.h @@ -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__ */ diff --git a/monitor/gdu/org.gtk.Private.GduVolumeMonitor.service.in b/monitor/gdu/org.gtk.Private.GduVolumeMonitor.service.in new file mode 100644 index 0000000..3fe4277 --- /dev/null +++ b/monitor/gdu/org.gtk.Private.GduVolumeMonitor.service.in @@ -0,0 +1,3 @@ +[D-BUS Service] +Name=org.gtk.Private.GduVolumeMonitor +Exec=@libexecdir@/gvfs-gdu-volume-monitor diff --git a/monitor/gdu/polkit.c b/monitor/gdu/polkit.c new file mode 100644 index 0000000..5c37ba6 --- /dev/null +++ b/monitor/gdu/polkit.c @@ -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); +} diff --git a/monitor/gdu/polkit.h b/monitor/gdu/polkit.h new file mode 100644 index 0000000..4b26189 --- /dev/null +++ b/monitor/gdu/polkit.h @@ -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__ */ -- 1.6.2.2