gvfs/gdu-0001-Bug-573826-gdu-volume-monitor.patch
David Zeuthen 5455e5f26a - Clean up gdu patches and bump BR for gdu to 0.3
- Avoiding showing volume for ignored mounts (#495033)
2009-04-10 01:24:24 +00:00

5251 lines
158 KiB
Diff

From 9d51c0de20170731db25422e858fee0e44b1dbd1 Mon Sep 17 00:00:00 2001
From: David Zeuthen <davidz@redhat.com>
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 <davidz@redhat.com>
+ */
+
+#include <config.h>
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+#include <gmodule.h>
+#include <gio/gio.h>
+
+#include <gvfsproxyvolumemonitordaemon.h>
+
+#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 <davidz@redhat.com>
+ */
+
+#include <config.h>
+
+#include <string.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+
+#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 <davidz@redhat.com>
+ */
+
+#ifndef __G_GDU_DRIVE_H__
+#define __G_GDU_DRIVE_H__
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#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 <davidz@redhat.com>
+ */
+
+#include <config.h>
+
+#include <string.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+#include <gio/gio.h>
+
+#include <gvfsmountinfo.h>
+
+#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 <davidz@redhat.com>
+ */
+
+#ifndef __G_GDU_MOUNT_H__
+#define __G_GDU_MOUNT_H__
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#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 <davidz@redhat.com>
+ */
+
+#include <config.h>
+
+#include <string.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+#include <gio/gio.h>
+
+#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 <davidz@redhat.com>
+ */
+
+#ifndef __G_GDU_VOLUME_H__
+#define __G_GDU_VOLUME_H__
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#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 <davidz@redhat.com>
+ */
+
+#include <config.h>
+
+#include <limits.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+#include <gio/gio.h>
+
+#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 <davidz@redhat.com>
+ */
+
+#ifndef __G_GDU_VOLUME_MONITOR_H__
+#define __G_GDU_VOLUME_MONITOR_H__
+
+#include <glib-object.h>
+#include <gio/gio.h>
+#include <gio/gunixmounts.h>
+
+#include <gdu/gdu.h>
+
+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 <davidz@redhat.com>
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <gdbusutils.h>
+
+#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 <davidz@redhat.com>
+ */
+
+
+#ifndef __POLKIT_H__
+#define __POLKIT_H__
+
+#include <glib-object.h>
+#include <gio/gio.h>
+#include <polkit/polkit.h>
+
+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