Blob Blame Raw
Index: configure.ac
===================================================================
--- configure.ac	(revision 2286)
+++ configure.ac	(working copy)
@@ -198,6 +198,31 @@
 
 AM_CONDITIONAL(USE_GCONF, [test "$msg_gconf" = "yes"])
 
+dnl ************************************
+dnl *** Check for gnome-disk-utility ***
+dnl ************************************
+
+dnl TODO: when there is a more stable version of gdu available, turn this on by default
+AC_ARG_ENABLE(gdu, [  --enable-gdu           build with gdu support])
+msg_gdu=no
+GDU_LIBS=
+GDU_CFLAGS=
+GDU_REQUIRED=0.2
+
+if test "x$enable_gdu" = "xyes"; then
+  PKG_CHECK_EXISTS([gdu >= $GDU_REQUIRED], msg_gdu=yes)
+
+  if test "x$msg_gdu" == "xyes"; then
+    PKG_CHECK_MODULES([GDU],[gdu >= $GDU_REQUIRED])
+    AC_DEFINE(HAVE_GDU, 1, [Define to 1 if gnome-disk-utility is available])
+  fi
+fi
+
+AC_SUBST(GDU_LIBS)
+AC_SUBST(GDU_CFLAGS)
+
+AM_CONDITIONAL(USE_GDU, [test "$msg_gdu" = "yes"])
+
 dnl **********************
 dnl *** Check for HAL ***
 dnl **********************
@@ -558,6 +583,7 @@
 monitor/Makefile
 monitor/proxy/Makefile
 monitor/hal/Makefile
+monitor/gdu/Makefile
 monitor/gphoto2/Makefile
 gconf/Makefile
 programs/Makefile
@@ -579,7 +605,8 @@
 	archive support:	      $msg_archive
         GConf support:                $msg_gconf
         DNS-SD support:               $msg_avahi
-	Use HAL for volume monitor:   $msg_hal (with fast init path: $have_hal_fast_init)
+	Build HAL volume monitor:     $msg_hal (with fast init path: $have_hal_fast_init)
+	Build GDU volume monitor:     $msg_gdu
 	GNOME Keyring support:        $msg_keyring
 	Bash-completion support:      $msg_bash_completion
 "
Index: monitor/Makefile.am
===================================================================
--- monitor/Makefile.am	(revision 2286)
+++ monitor/Makefile.am	(working copy)
@@ -5,6 +5,10 @@
 SUBDIRS += hal
 endif
 
+if USE_GDU
+SUBDIRS += gdu
+endif
+
 if USE_GPHOTO2
 SUBDIRS += gphoto2
 endif
--- /dev/null	2009-03-04 16:07:30.099029290 -0500
+++ monitor/gdu/gdu-volume-monitor-daemon.c	2009-03-01 23:47:15.000000000 -0500
@@ -0,0 +1,46 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* gvfs - extensions for gio
+ *
+ * Copyright (C) 2006-2009 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: David Zeuthen <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);
+}
--- /dev/null	2009-03-04 16:07:30.099029290 -0500
+++ monitor/gdu/ggdudrive.c	2009-03-01 23:48:33.000000000 -0500
@@ -0,0 +1,691 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* gvfs - extensions for gio
+ *
+ * Copyright (C) 2006-2009 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: David Zeuthen <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;
+}
--- /dev/null	2009-03-04 16:07:30.099029290 -0500
+++ monitor/gdu/ggdudrive.h	2009-03-01 23:48:26.000000000 -0500
@@ -0,0 +1,69 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* gvfs - extensions for gio
+ *
+ * Copyright (C) 2006-2009 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: David Zeuthen <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__ */
--- /dev/null	2009-03-04 16:07:30.099029290 -0500
+++ monitor/gdu/ggdumount.c	2009-03-02 14:18:23.000000000 -0500
@@ -0,0 +1,958 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* gvfs - extensions for gio
+ *
+ * Copyright (C) 2006-2009 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: David Zeuthen <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;
+}
--- /dev/null	2009-03-04 16:07:30.099029290 -0500
+++ monitor/gdu/ggdumount.h	2009-03-01 23:48:16.000000000 -0500
@@ -0,0 +1,64 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* gvfs - extensions for gio
+ *
+ * Copyright (C) 2006-2009 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: David Zeuthen <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__ */
--- /dev/null	2009-03-04 16:07:30.099029290 -0500
+++ monitor/gdu/ggduvolume.c	2009-03-04 16:19:41.000000000 -0500
@@ -0,0 +1,1408 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* gvfs - extensions for gio
+ *
+ * Copyright (C) 2006-2009 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: David Zeuthen <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);
+}
--- /dev/null	2009-03-04 16:07:30.099029290 -0500
+++ monitor/gdu/ggduvolume.h	2009-03-01 23:48:05.000000000 -0500
@@ -0,0 +1,78 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* gvfs - extensions for gio
+ *
+ * Copyright (C) 2006-2009 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: David Zeuthen <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__ */
--- /dev/null	2009-03-04 16:07:30.099029290 -0500
+++ monitor/gdu/ggduvolumemonitor.c	2009-03-04 16:29:02.000000000 -0500
@@ -0,0 +1,1432 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* gvfs - extensions for gio
+ *
+ * Copyright (C) 2006-2009 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: David Zeuthen <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);
+}
--- /dev/null	2009-03-04 16:07:30.099029290 -0500
+++ monitor/gdu/ggduvolumemonitor.h	2009-03-01 23:49:48.000000000 -0500
@@ -0,0 +1,60 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* gvfs - extensions for gio
+ *
+ * Copyright (C) 2006-2009 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: David Zeuthen <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__ */
--- /dev/null	2009-03-04 16:07:30.099029290 -0500
+++ monitor/gdu/polkit.c	2009-03-01 23:47:50.000000000 -0500
@@ -0,0 +1,137 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* gvfs - extensions for gio
+ *
+ * Copyright (C) 2006-2009 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: David Zeuthen <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);
+}
--- /dev/null	2009-03-04 16:07:30.099029290 -0500
+++ monitor/gdu/polkit.h	2009-03-01 23:47:41.000000000 -0500
@@ -0,0 +1,44 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* gvfs - extensions for gio
+ *
+ * Copyright (C) 2006-2009 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: David Zeuthen <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__ */
--- /dev/null	2009-03-04 16:07:30.099029290 -0500
+++ monitor/gdu/Makefile.am	2009-03-01 21:13:46.000000000 -0500
@@ -0,0 +1,53 @@
+
+NULL =
+
+libexec_PROGRAMS = gvfs-gdu-volume-monitor
+
+
+gvfs_gdu_volume_monitor_SOURCES =			\
+	gdu-volume-monitor-daemon.c			\
+	ggdudrive.c		ggdudrive.h		\
+	ggduvolume.c		ggduvolume.h		\
+	ggdumount.c		ggdumount.h		\
+	ggduvolumemonitor.c	ggduvolumemonitor.h	\
+	polkit.c		polkit.h		\
+	$(NULL)
+
+gvfs_gdu_volume_monitor_CFLAGS =		\
+	-DG_LOG_DOMAIN=\"GVFS-Gdu\"		\
+	-I$(top_srcdir)/common                  \
+	-I$(top_srcdir)/monitor/proxy           \
+	$(GLIB_CFLAGS)                          \
+	$(GDU_CFLAGS)                           \
+	$(DBUS_CFLAGS)                          \
+	-DGIO_MODULE_DIR=\"$(GIO_MODULE_DIR)\"	\
+	-DGVFS_LOCALEDIR=\""$(localedir)"\"	\
+	-DG_DISABLE_DEPRECATED			\
+	-DGDU_API_IS_SUBJECT_TO_CHANGE		\
+	$(NULL)
+
+gvfs_gdu_volume_monitor_LDFLAGS =	\
+	$(NULL)
+
+gvfs_gdu_volume_monitor_LDADD  =		     			      \
+	$(GLIB_LIBS)                                 			      \
+	$(GDU_LIBS)                                  			      \
+	$(DBUS_LIBS)                                 				\
+	$(top_builddir)/common/libgvfscommon.la 			      \
+	$(top_builddir)/monitor/proxy/libgvfsproxyvolumemonitordaemon-noin.la \
+	$(NULL)
+
+remote_volume_monitorsdir = $(datadir)/gvfs/remote-volume-monitors
+remote_volume_monitors_DATA = gdu.monitor
+
+servicedir       = $(datadir)/dbus-1/services
+service_in_files = org.gtk.Private.GduVolumeMonitor.service.in
+service_DATA     = $(service_in_files:.service.in=.service)
+
+$(service_DATA): $(service_in_files) Makefile
+	@sed -e "s|\@libexecdir\@|$(libexecdir)|" $< > $@
+
+clean-local:
+	rm -f *~ *.loT $(BUILT_SOURCES) $(service_DATA)
+
+EXTRA_DIST = $(service_in_files) gdu.monitor
--- /dev/null	2009-03-04 16:07:30.099029290 -0500
+++ monitor/gdu/gdu.monitor	2009-02-11 06:42:26.000000000 -0500
@@ -0,0 +1,5 @@
+[RemoteVolumeMonitor]
+Name=GProxyVolumeMonitorGdu
+DBusName=org.gtk.Private.GduVolumeMonitor
+IsNative=true
+NativePriority=3
--- /dev/null	2009-03-04 16:07:30.099029290 -0500
+++ monitor/gdu/org.gtk.Private.GduVolumeMonitor.service.in	2009-02-19 22:34:26.000000000 -0500
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name=org.gtk.Private.GduVolumeMonitor
+Exec=@libexecdir@/gvfs-gdu-volume-monitor