[gvfs/wip/oholy/storaged] Add storaged volume monitor



commit 27430703cb7ac571acaea927bd2dd24f4c45397d
Author: Ondrej Holy <oholy redhat com>
Date:   Fri Sep 25 08:24:50 2015 +0200

    Add storaged volume monitor
    
    It is just copy of udisks2 volume monitor using storaged api.

 configure.ac                                       |   21 +
 monitor/Makefile.am                                |    6 +-
 monitor/storaged/.gitignore                        |    1 +
 monitor/storaged/Makefile.am                       |   56 +
 monitor/storaged/gvfsstorageddrive.c               |  987 ++++++++++
 monitor/storaged/gvfsstorageddrive.h               |   52 +
 monitor/storaged/gvfsstoragedmount.c               | 1378 ++++++++++++++
 monitor/storaged/gvfsstoragedmount.h               |   61 +
 monitor/storaged/gvfsstoragedutils.c               |  828 +++++++++
 monitor/storaged/gvfsstoragedutils.h               |   64 +
 monitor/storaged/gvfsstoragedvolume.c              | 1786 +++++++++++++++++++
 monitor/storaged/gvfsstoragedvolume.h              |   67 +
 monitor/storaged/gvfsstoragedvolumemonitor.c       | 1885 ++++++++++++++++++++
 monitor/storaged/gvfsstoragedvolumemonitor.h       |   55 +
 .../org.gtk.vfs.StoragedVolumeMonitor.service.in   |    3 +
 monitor/storaged/storaged.monitor                  |    5 +
 monitor/storaged/storagedvolumemonitordaemon.c     |   46 +
 17 files changed, 7300 insertions(+), 1 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index e5b4849..628fb51 100644
--- a/configure.ac
+++ b/configure.ac
@@ -260,6 +260,25 @@ fi
 
 AM_CONDITIONAL(USE_UDISKS2, [test "$msg_udisks2" = "yes"])
 
+dnl **************************
+dnl *** Check for storaged ***
+dnl **************************
+
+AC_ARG_ENABLE([storaged], [AS_HELP_STRING([--disable-storaged],[build without libstoraged])])
+msg_storaged=no
+STORAGED_REQUIRED=1.97
+
+if test "x$enable_storaged" != "xno"; then
+  PKG_CHECK_EXISTS([storaged >= $STORAGED_REQUIRED], [msg_storaged=yes])
+
+  if test "x$msg_storaged" = "xyes"; then
+    PKG_CHECK_MODULES([STORAGED],[storaged >= $STORAGED_REQUIRED])
+    AC_DEFINE([HAVE_STORAGED], 1, [Define to 1 if libstoraged is available])
+  fi
+fi
+
+AM_CONDITIONAL(USE_STORAGED, [test "$msg_storaged" = "yes"])
+
 dnl **********************************
 dnl *** Check for libsystemd-login ***
 dnl **********************************
@@ -912,6 +931,7 @@ monitor/gphoto2/Makefile
 monitor/afc/Makefile
 monitor/mtp/Makefile
 monitor/goa/Makefile
+monitor/storaged/Makefile
 programs/Makefile
 programs/completion/Makefile
 man/Makefile
@@ -943,6 +963,7 @@ echo "
        Build HAL volume monitor:     $msg_hal (with fast init path: $have_hal_fast_init)
        Build GDU volume monitor:     $msg_gdu
        Build udisks2 volume monitor: $msg_udisks2
+        Build storaged volume monitor: $msg_storaged
        Build GOA volume monitor:     $msg_goa
         Use libsystemd-login:         $msg_libsystemd_login
         Use GCR:                      $msg_gcr
diff --git a/monitor/Makefile.am b/monitor/Makefile.am
index f7cf7a2..014cbf2 100644
--- a/monitor/Makefile.am
+++ b/monitor/Makefile.am
@@ -1,4 +1,4 @@
-DIST_SUBDIRS = proxy hal gdu gphoto2 afc udisks2 mtp goa
+DIST_SUBDIRS = proxy hal gdu gphoto2 afc udisks2 mtp goa storaged
 SUBDIRS = proxy
 
 if USE_HAL
@@ -28,3 +28,7 @@ endif
 if USE_GOA
 SUBDIRS += goa
 endif
+
+if USE_STORAGED
+SUBDIRS += storaged
+endif
diff --git a/monitor/storaged/.gitignore b/monitor/storaged/.gitignore
new file mode 100644
index 0000000..ba968b0
--- /dev/null
+++ b/monitor/storaged/.gitignore
@@ -0,0 +1 @@
+gvfs-storaged-volume-monitor
diff --git a/monitor/storaged/Makefile.am b/monitor/storaged/Makefile.am
new file mode 100644
index 0000000..398e1d7
--- /dev/null
+++ b/monitor/storaged/Makefile.am
@@ -0,0 +1,56 @@
+
+NULL =
+
+libexec_PROGRAMS = gvfs-storaged-volume-monitor
+
+gvfs_storaged_volume_monitor_SOURCES = \
+       storagedvolumemonitordaemon.c \
+       gvfsstoragedvolumemonitor.c gvfsstoragedvolumemonitor.h \
+       gvfsstorageddrive.c gvfsstorageddrive.h \
+       gvfsstoragedvolume.c gvfsstoragedvolume.h \
+       gvfsstoragedmount.c gvfsstoragedmount.h \
+       gvfsstoragedutils.c gvfsstoragedutils.h \
+       $(NULL)
+
+gvfs_storaged_volume_monitor_CFLAGS = \
+       -DG_LOG_DOMAIN=\"GVFS-Storaged\" \
+       -I$(top_srcdir)/common \
+       -I$(top_srcdir)/monitor/proxy \
+       $(GLIB_CFLAGS) \
+       $(STORAGED_CFLAGS) \
+       $(GUDEV_CFLAGS) \
+       $(LIBSYSTEMD_LOGIN_CFLAGS) \
+       $(KEYRING_CFLAGS) \
+       -DGIO_MODULE_DIR=\"$(GIO_MODULE_DIR)\" \
+       -DGVFS_LOCALEDIR=\""$(localedir)"\" \
+       -DG_DISABLE_DEPRECATED \
+       $(NULL)
+
+gvfs_storaged_volume_monitor_LDFLAGS = \
+       $(NULL)
+
+gvfs_storaged_volume_monitor_LDADD = \
+       $(GLIB_LIBS) \
+       $(STORAGED_LIBS) \
+       $(GUDEV_LIBS) \
+       $(LIBSYSTEMD_LOGIN_LIBS) \
+       $(KEYRING_LIBS) \
+       $(top_builddir)/common/libgvfscommon.la \
+       $(top_builddir)/common/libgvfscommon-monitor.la \
+       $(top_builddir)/monitor/proxy/libgvfsproxyvolumemonitordaemon-noin.la \
+       $(NULL)
+
+remote_volume_monitorsdir = $(datadir)/gvfs/remote-volume-monitors
+remote_volume_monitors_DATA = storaged.monitor
+
+servicedir = $(datadir)/dbus-1/services
+service_in_files = org.gtk.vfs.StoragedVolumeMonitor.service.in
+service_DATA = $(service_in_files:.service.in=.service)
+
+$(service_DATA): $(service_in_files) Makefile
+       $(AM_V_GEN) $(SED) -e "s|\ libexecdir\@|$(libexecdir)|" $< > $@
+
+clean-local:
+       rm -f *~ *.loT $(BUILT_SOURCES) $(service_DATA)
+
+EXTRA_DIST = $(service_in_files) storaged.monitor
diff --git a/monitor/storaged/gvfsstorageddrive.c b/monitor/storaged/gvfsstorageddrive.c
new file mode 100644
index 0000000..de3d7f0
--- /dev/null
+++ b/monitor/storaged/gvfsstorageddrive.c
@@ -0,0 +1,987 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* gvfs - extensions for gio
+ *
+ * Copyright (C) 2006-2012 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., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, 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 "gvfsstoragedvolumemonitor.h"
+#include "gvfsstorageddrive.h"
+#include "gvfsstoragedvolume.h"
+#include "gvfsstoragedutils.h"
+
+typedef struct _GVfsStoragedDriveClass GVfsStoragedDriveClass;
+
+struct _GVfsStoragedDriveClass
+{
+  GObjectClass parent_class;
+};
+
+struct _GVfsStoragedDrive
+{
+  GObject parent;
+
+  GVfsStoragedVolumeMonitor  *monitor; /* owned by volume monitor */
+  GList                     *volumes; /* entries in list are owned by volume monitor */
+
+  /* If TRUE, the drive was discovered at coldplug time */
+  gboolean coldplug;
+
+  StoragedDrive *storaged_drive;
+
+  GIcon *icon;
+  GIcon *symbolic_icon;
+  gchar *name;
+  gchar *sort_key;
+  gchar *device_file;
+  dev_t dev;
+  gboolean is_media_removable;
+  gboolean has_media;
+  gboolean can_eject;
+  gboolean can_stop;
+};
+
+static void gvfs_storaged_drive_drive_iface_init (GDriveIface *iface);
+
+static void on_storaged_drive_notify (GObject     *object,
+                                    GParamSpec  *pspec,
+                                    gpointer     user_data);
+
+G_DEFINE_TYPE_EXTENDED (GVfsStoragedDrive, gvfs_storaged_drive, G_TYPE_OBJECT, 0,
+                        G_IMPLEMENT_INTERFACE (G_TYPE_DRIVE, gvfs_storaged_drive_drive_iface_init))
+
+static void
+gvfs_storaged_drive_finalize (GObject *object)
+{
+  GVfsStoragedDrive *drive = GVFS_STORAGED_DRIVE (object);
+  GList *l;
+
+  for (l = drive->volumes; l != NULL; l = l->next)
+    {
+      GVfsStoragedVolume *volume = l->data;
+      gvfs_storaged_volume_unset_drive (volume, drive);
+    }
+
+  if (drive->storaged_drive != NULL)
+    {
+      g_signal_handlers_disconnect_by_func (drive->storaged_drive, on_storaged_drive_notify, drive);
+      g_object_unref (drive->storaged_drive);
+    }
+
+  g_clear_object (&drive->icon);
+  g_clear_object (&drive->symbolic_icon);
+  g_free (drive->name);
+  g_free (drive->sort_key);
+  g_free (drive->device_file);
+
+  G_OBJECT_CLASS (gvfs_storaged_drive_parent_class)->finalize (object);
+}
+
+static void
+gvfs_storaged_drive_class_init (GVfsStoragedDriveClass *klass)
+{
+  GObjectClass *gobject_class;
+
+  gobject_class = G_OBJECT_CLASS (klass);
+  gobject_class->finalize = gvfs_storaged_drive_finalize;
+}
+
+static void
+gvfs_storaged_drive_init (GVfsStoragedDrive *gdu_drive)
+{
+}
+
+static void
+emit_changed (GVfsStoragedDrive *drive)
+{
+  g_signal_emit_by_name (drive, "changed");
+  g_signal_emit_by_name (drive->monitor, "drive-changed", drive);
+}
+
+
+#if STORAGED_CHECK_VERSION(2,0,90)
+static gpointer
+_g_object_ref0 (gpointer object)
+{
+  if (object != NULL)
+    return g_object_ref (G_OBJECT (object));
+  else
+    return NULL;
+}
+#endif
+
+static gboolean
+update_drive (GVfsStoragedDrive *drive)
+{
+  StoragedClient *storaged_client;
+  gboolean changed;
+  GIcon *old_icon;
+  GIcon *old_symbolic_icon;
+  gchar *old_name;
+  gchar *old_sort_key;
+  gchar *old_device_file;
+  dev_t old_dev;
+  gboolean old_is_media_removable;
+  gboolean old_has_media;
+  gboolean old_can_eject;
+  gboolean old_can_stop;
+  StoragedBlock *block;
+#if STORAGED_CHECK_VERSION(2,0,90)
+  StoragedObjectInfo *info = NULL;
+#endif
+
+  storaged_client = gvfs_storaged_volume_monitor_get_storaged_client (drive->monitor);
+
+  /* ---------------------------------------------------------------------------------------------------- */
+  /* 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_stop = drive->can_stop;
+
+  old_name = g_strdup (drive->name);
+  old_sort_key = g_strdup (drive->sort_key);
+  old_device_file = g_strdup (drive->device_file);
+  old_dev = drive->dev;
+  old_icon = drive->icon != NULL ? g_object_ref (drive->icon) : NULL;
+  old_symbolic_icon = drive->symbolic_icon != NULL ? g_object_ref (drive->symbolic_icon) : NULL;
+
+  /* ---------------------------------------------------------------------------------------------------- */
+  /* reset */
+
+  drive->is_media_removable = drive->has_media = drive->can_eject = drive->can_stop = FALSE;
+  g_free (drive->name); drive->name = NULL;
+  g_free (drive->sort_key); drive->sort_key = NULL;
+  g_free (drive->device_file); drive->device_file = NULL;
+  drive->dev = 0;
+  g_clear_object (&drive->icon);
+  g_clear_object (&drive->symbolic_icon);
+
+  /* ---------------------------------------------------------------------------------------------------- */
+  /* in with the new */
+
+  block = storaged_client_get_block_for_drive (storaged_client,
+                                             drive->storaged_drive,
+                                             FALSE);
+  if (block != NULL)
+    {
+      drive->device_file = storaged_block_dup_device (block);
+      drive->dev = storaged_block_get_device_number (block);
+
+      g_object_unref (block);
+    }
+
+  drive->sort_key = g_strdup (storaged_drive_get_sort_key (drive->storaged_drive));
+
+  drive->is_media_removable = storaged_drive_get_media_removable (drive->storaged_drive);
+  if (drive->is_media_removable)
+    {
+      drive->has_media = storaged_drive_get_media_available (drive->storaged_drive);
+    }
+  else
+    {
+      drive->has_media = TRUE;
+    }
+  drive->can_eject = storaged_drive_get_ejectable (drive->storaged_drive);
+
+#if STORAGED_CHECK_VERSION(2,0,90)
+  {
+    StoragedObject *object = (StoragedObject *) g_dbus_interface_get_object (G_DBUS_INTERFACE 
(drive->storaged_drive));
+    if (object != NULL)
+      {
+        info = storaged_client_get_object_info (storaged_client, object);
+        if (info != NULL)
+          {
+            drive->name = g_strdup (storaged_object_info_get_name (info));
+            drive->icon = _g_object_ref0 (storaged_object_info_get_icon (info));
+            drive->symbolic_icon = _g_object_ref0 (storaged_object_info_get_icon_symbolic (info));
+          }
+      }
+  }
+#else
+  storaged_client_get_drive_info (storaged_client,
+                                drive->storaged_drive,
+                                NULL,         /* drive_name */
+                                &drive->name,
+                                &drive->icon,
+                                NULL,         /* media_desc */
+                                NULL);        /* media_icon */
+#endif
+
+#if STORAGED_CHECK_VERSION(2,0,90)
+  {
+    /* If can_stop is TRUE, then
+     *
+     *  - the GUI (e.g. Files, Shell) will call GDrive.stop() whenever the
+     *    user presses the Eject icon, which will result in:
+     *
+     *  - us calling StoragedDrive.PowerOff() on GDrive.stop(), which
+     *    will result in:
+     *
+     *  - Storaged asking the kernel to power off the USB port the drive
+     *    is connected to, which will result in
+     *
+     *  - Most drives powering off (especially true for bus-powered
+     *    drives such as 2.5" HDDs and USB sticks), which will result in
+     *
+     *  - Users feeling warm and cozy when they see the LED on the
+     *    device turn off (win)
+     *
+     * Obviously this is unwanted if
+     *
+     *  - the drive is using removable media (e.g. optical discs,
+     *    flash media etc); or
+     *
+     *  - the device is internal
+     *
+     * So for the latter, only do this for drives we appear *during*
+     * the login session.  Note that this heuristic has the nice
+     * side-effect that USB-attached hard disks that are plugged in
+     * when the computer starts up will not be powered off when the
+     * user clicks the "eject" icon.
+     */
+    if (!drive->is_media_removable && !drive->coldplug)
+      {
+        if (storaged_drive_get_can_power_off (drive->storaged_drive))
+          {
+            drive->can_stop = TRUE;
+          }
+      }
+  }
+#endif
+
+  /* ---------------------------------------------------------------------------------------------------- */
+  /* fallbacks */
+
+  /* Never use empty/blank names (#582772) */
+  if (drive->name == NULL || strlen (drive->name) == 0)
+    {
+      if (drive->device_file != NULL)
+        drive->name = g_strdup_printf (_("Unnamed Drive (%s)"), drive->device_file);
+      else
+        drive->name = g_strdup (_("Unnamed Drive"));
+    }
+  if (drive->icon == NULL)
+    drive->icon = g_themed_icon_new ("drive-removable-media");
+  if (drive->symbolic_icon == NULL)
+    drive->symbolic_icon = g_themed_icon_new ("drive-removable-media-symbolic");
+
+  /* ---------------------------------------------------------------------------------------------------- */
+  /* 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_can_stop == drive->can_stop) &&
+              (g_strcmp0 (old_name, drive->name) == 0) &&
+              (g_strcmp0 (old_sort_key, drive->sort_key) == 0) &&
+              (g_strcmp0 (old_device_file, drive->device_file) == 0) &&
+              (old_dev == drive->dev) &&
+              g_icon_equal (old_icon, drive->icon) &&
+              g_icon_equal (old_symbolic_icon, drive->symbolic_icon)
+              );
+
+  /* free old values */
+  g_free (old_name);
+  g_free (old_sort_key);
+  g_free (old_device_file);
+  g_clear_object (&old_icon);
+  g_clear_object (&old_symbolic_icon);
+
+  /*g_debug ("in update_drive(); has_media=%d changed=%d", drive->has_media, changed);*/
+
+#if STORAGED_CHECK_VERSION(2,0,90)
+  g_clear_object (&info);
+#endif
+
+  return changed;
+}
+
+static void
+on_storaged_drive_notify (GObject     *object,
+                        GParamSpec  *pspec,
+                        gpointer     user_data)
+{
+  GVfsStoragedDrive *drive = GVFS_STORAGED_DRIVE (user_data);
+  if (update_drive (drive))
+    emit_changed (drive);
+}
+
+GVfsStoragedDrive *
+gvfs_storaged_drive_new (GVfsStoragedVolumeMonitor  *monitor,
+                        StoragedDrive               *storaged_drive,
+                        gboolean                   coldplug)
+{
+  GVfsStoragedDrive *drive;
+
+  drive = g_object_new (GVFS_TYPE_STORAGED_DRIVE, NULL);
+  drive->monitor = monitor;
+  drive->coldplug = coldplug;
+
+  drive->storaged_drive = g_object_ref (storaged_drive);
+  g_signal_connect (drive->storaged_drive,
+                    "notify",
+                    G_CALLBACK (on_storaged_drive_notify),
+                    drive);
+
+  update_drive (drive);
+
+  return drive;
+}
+
+void
+gvfs_storaged_drive_disconnected (GVfsStoragedDrive *drive)
+{
+  GList *l, *volumes;
+
+  volumes = drive->volumes;
+  drive->volumes = NULL;
+  for (l = volumes; l != NULL; l = l->next)
+    {
+      GVfsStoragedVolume *volume = l->data;
+      gvfs_storaged_volume_unset_drive (volume, drive);
+    }
+  g_list_free (volumes);
+}
+
+void
+gvfs_storaged_drive_set_volume (GVfsStoragedDrive  *drive,
+                               GVfsStoragedVolume *volume)
+{
+  if (g_list_find (drive->volumes, volume) == NULL)
+    {
+      drive->volumes = g_list_prepend (drive->volumes, volume);
+      emit_changed (drive);
+    }
+}
+
+void
+gvfs_storaged_drive_unset_volume (GVfsStoragedDrive  *drive,
+                                 GVfsStoragedVolume *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 *
+gvfs_storaged_drive_get_icon (GDrive *_drive)
+{
+  GVfsStoragedDrive *drive = GVFS_STORAGED_DRIVE (_drive);
+  return drive->icon != NULL ? g_object_ref (drive->icon) : NULL;
+}
+
+static GIcon *
+gvfs_storaged_drive_get_symbolic_icon (GDrive *_drive)
+{
+  GVfsStoragedDrive *drive = GVFS_STORAGED_DRIVE (_drive);
+  return drive->symbolic_icon != NULL ? g_object_ref (drive->symbolic_icon) : NULL;
+}
+
+static char *
+gvfs_storaged_drive_get_name (GDrive *_drive)
+{
+  GVfsStoragedDrive *drive = GVFS_STORAGED_DRIVE (_drive);
+  return g_strdup (drive->name);
+}
+
+static GList *
+gvfs_storaged_drive_get_volumes (GDrive *_drive)
+{
+  GVfsStoragedDrive *drive = GVFS_STORAGED_DRIVE (_drive);
+  GList *l;
+  l = g_list_copy (drive->volumes);
+  g_list_foreach (l, (GFunc) g_object_ref, NULL);
+  return l;
+}
+
+static gboolean
+gvfs_storaged_drive_has_volumes (GDrive *_drive)
+{
+  GVfsStoragedDrive *drive = GVFS_STORAGED_DRIVE (_drive);
+  gboolean res;
+  res = drive->volumes != NULL;
+  return res;
+}
+
+static gboolean
+gvfs_storaged_drive_is_media_removable (GDrive *_drive)
+{
+  GVfsStoragedDrive *drive = GVFS_STORAGED_DRIVE (_drive);
+  return drive->is_media_removable;
+}
+
+static gboolean
+gvfs_storaged_drive_has_media (GDrive *_drive)
+{
+  GVfsStoragedDrive *drive = GVFS_STORAGED_DRIVE (_drive);
+  return drive->has_media;
+}
+
+static gboolean
+gvfs_storaged_drive_is_media_check_automatic (GDrive *_drive)
+{
+  return TRUE;
+}
+
+static gboolean
+gvfs_storaged_drive_can_eject (GDrive *_drive)
+{
+  GVfsStoragedDrive *drive = GVFS_STORAGED_DRIVE (_drive);
+  return drive->can_eject;
+}
+
+static gboolean
+gvfs_storaged_drive_can_poll_for_media (GDrive *_drive)
+{
+  return FALSE;
+}
+
+static gboolean
+gvfs_storaged_drive_can_start (GDrive *_drive)
+{
+  return FALSE;
+}
+
+static gboolean
+gvfs_storaged_drive_can_start_degraded (GDrive *_drive)
+{
+  return FALSE;
+}
+
+static gboolean
+gvfs_storaged_drive_can_stop (GDrive *_drive)
+{
+  GVfsStoragedDrive *drive = GVFS_STORAGED_DRIVE (_drive);
+  return drive->can_stop;
+}
+
+static GDriveStartStopType
+gvfs_storaged_drive_get_start_stop_type (GDrive *_drive)
+{
+  return G_DRIVE_START_STOP_TYPE_SHUTDOWN;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static char *
+gvfs_storaged_drive_get_identifier (GDrive      *_drive,
+                                   const gchar *kind)
+{
+  GVfsStoragedDrive *drive = GVFS_STORAGED_DRIVE (_drive);
+  gchar *ret = NULL;
+
+  if (drive->device_file != NULL)
+    {
+      if (g_strcmp0 (kind, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE) == 0)
+        ret = g_strdup (drive->device_file);
+    }
+  return ret;
+}
+
+static gchar **
+gvfs_storaged_drive_enumerate_identifiers (GDrive *_drive)
+{
+  GVfsStoragedDrive *drive = GVFS_STORAGED_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);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+typedef void (*UnmountsMountsFunc)  (GDrive              *drive,
+                                     GMountOperation     *mount_operation,
+                                     GCancellable        *cancellable,
+                                     GAsyncReadyCallback  callback,
+                                     gpointer             user_data,
+                                     gpointer             on_all_unmounted_data);
+
+typedef struct {
+  GDrive *drive;
+  GAsyncReadyCallback callback;
+  gpointer user_data;
+  GMountOperation *mount_operation;
+  GCancellable *cancellable;
+  GMountUnmountFlags flags;
+
+  GList *pending_mounts;
+
+  UnmountsMountsFunc on_all_unmounted;
+  gpointer on_all_unmounted_data;
+} UnmountMountsOp;
+
+static void
+free_unmount_mounts_op (UnmountMountsOp *data)
+{
+  g_list_free_full (data->pending_mounts, g_object_unref);
+
+  g_object_unref (data->drive);
+  g_free (data);
+}
+
+static void
+unmount_mounts_cb (GObject       *source_object,
+                   GAsyncResult  *res,
+                   gpointer       user_data);
+
+static void
+unmount_mounts_do (UnmountMountsOp *data)
+{
+  if (data->pending_mounts == NULL)
+    {
+      data->on_all_unmounted (data->drive,
+                              data->mount_operation,
+                              data->cancellable,
+                              data->callback,
+                              data->user_data,
+                              data->on_all_unmounted_data);
+
+      free_unmount_mounts_op (data);
+    }
+  else
+    {
+      GMount *mount;
+      mount = data->pending_mounts->data;
+      data->pending_mounts = g_list_remove (data->pending_mounts, mount);
+
+      g_mount_unmount_with_operation (mount,
+                                      data->flags,
+                                      data->mount_operation,
+                                      data->cancellable,
+                                      unmount_mounts_cb,
+                                      data);
+    }
+}
+
+static void
+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_with_operation_finish (mount, res, &error))
+    {
+      /* make the error dialog more targeted to the drive.. unless the user has already seen a dialog */
+      if (error->domain == G_IO_ERROR && error->code == G_IO_ERROR_BUSY)
+        {
+          g_error_free (error);
+          error = g_error_new (G_IO_ERROR,
+                               G_IO_ERROR_BUSY,
+                               _("Failed to eject medium; one or more volumes on the medium are busy."));
+        }
+
+      if (data->mount_operation != NULL)
+        gvfs_storaged_unmount_notify_stop (data->mount_operation);
+
+      /* 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
+    {
+      /* move on to the next mount.. */
+      unmount_mounts_do (data);
+    }
+  g_object_unref (mount);
+}
+
+static void
+unmount_mounts (GVfsStoragedDrive    *drive,
+                GMountUnmountFlags   flags,
+                GMountOperation     *mount_operation,
+                GCancellable        *cancellable,
+                GAsyncReadyCallback  callback,
+                gpointer             user_data,
+                UnmountsMountsFunc   on_all_unmounted,
+                gpointer             on_all_unmounted_data)
+{
+  GMount *mount;
+  UnmountMountsOp *data;
+  GList *l;
+
+  data = g_new0 (UnmountMountsOp, 1);
+  data->drive = g_object_ref (drive);
+  data->mount_operation = mount_operation;
+  data->cancellable = cancellable;
+  data->callback = callback;
+  data->user_data = user_data;
+  data->flags = flags;
+  data->on_all_unmounted = on_all_unmounted;
+  data->on_all_unmounted_data = on_all_unmounted_data;
+
+  for (l = drive->volumes; l != NULL; l = l->next)
+    {
+      GVfsStoragedVolume *volume = GVFS_STORAGED_VOLUME (l->data);
+      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));
+    }
+
+  unmount_mounts_do (data);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+typedef struct
+{
+  GSimpleAsyncResult *simple;
+
+  GVfsStoragedDrive *drive;
+  GMountOperation *mount_operation;
+} EjectData;
+
+static void
+eject_data_free (EjectData *data)
+{
+  g_object_unref (data->simple);
+  g_clear_object (&data->drive);
+  g_clear_object (&data->mount_operation);
+
+  g_free (data);
+}
+
+static void
+eject_cb (GObject      *source_object,
+          GAsyncResult *res,
+          gpointer      user_data)
+{
+  EjectData *data = user_data;
+  GError *error;
+
+  error = NULL;
+  if (!storaged_drive_call_eject_finish (STORAGED_DRIVE (source_object), res, &error))
+    {
+      gvfs_storaged_utils_storaged_error_to_gio_error (error);
+      g_simple_async_result_take_error (data->simple, error);
+    }
+
+  if (data->mount_operation != NULL)
+    {
+      /* If we fail send an ::aborted signal to make any notification go away */
+      if (error != NULL)
+        g_signal_emit_by_name (data->mount_operation, "aborted");
+
+      gvfs_storaged_unmount_notify_stop (data->mount_operation);
+    }
+
+  g_simple_async_result_complete (data->simple);
+  eject_data_free (data);
+}
+
+static void
+gvfs_storaged_drive_eject_on_all_unmounted (GDrive              *_drive,
+                                           GMountOperation     *mount_operation,
+                                           GCancellable        *cancellable,
+                                           GAsyncReadyCallback  callback,
+                                           gpointer             user_data,
+                                           gpointer             on_all_unmounted_data)
+{
+  GVfsStoragedDrive *drive = GVFS_STORAGED_DRIVE (_drive);
+  GVariantBuilder builder;
+  EjectData *data;
+
+  data = g_new0 (EjectData, 1);
+  data->simple = g_simple_async_result_new (G_OBJECT (drive),
+                                            callback,
+                                            user_data,
+                                            gvfs_storaged_drive_eject_on_all_unmounted);
+  data->drive = g_object_ref (drive);
+  if (mount_operation != NULL)
+    data->mount_operation = g_object_ref (mount_operation);
+
+  g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
+  if (mount_operation == NULL)
+    {
+      g_variant_builder_add (&builder,
+                             "{sv}",
+                             "auth.no_user_interaction", g_variant_new_boolean (TRUE));
+    }
+  storaged_drive_call_eject (drive->storaged_drive,
+                           g_variant_builder_end (&builder),
+                           cancellable,
+                           eject_cb,
+                           data);
+}
+
+static void
+gvfs_storaged_drive_eject_with_operation (GDrive              *_drive,
+                                         GMountUnmountFlags   flags,
+                                         GMountOperation     *mount_operation,
+                                         GCancellable        *cancellable,
+                                         GAsyncReadyCallback  callback,
+                                         gpointer             user_data)
+{
+  GVfsStoragedDrive *drive = GVFS_STORAGED_DRIVE (_drive);
+
+  /* This information is needed in GVfsDdisks2Volume when apps have
+   * open files on the device ... we need to know if the button should
+   * be "Unmount Anyway", "Eject Anyway" or "Power Off Anyway"
+   */
+  if (mount_operation != NULL)
+    {
+      g_object_set_data (G_OBJECT (mount_operation), "x-storaged-is-eject", GINT_TO_POINTER (1));
+      gvfs_storaged_unmount_notify_start (mount_operation, NULL, _drive, FALSE);
+    }
+
+  /* first we need to go through all the volumes and unmount their assoicated mounts (if any) */
+  unmount_mounts (drive,
+                  flags,
+                  mount_operation,
+                  cancellable,
+                  callback,
+                  user_data,
+                  gvfs_storaged_drive_eject_on_all_unmounted,
+                  NULL);
+}
+
+static gboolean
+gvfs_storaged_drive_eject_with_operation_finish (GDrive        *drive,
+                                                GAsyncResult  *result,
+                                                GError       **error)
+{
+  return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error);
+}
+
+static void
+gvfs_storaged_drive_eject (GDrive              *drive,
+                          GMountUnmountFlags   flags,
+                          GCancellable        *cancellable,
+                          GAsyncReadyCallback  callback,
+                          gpointer             user_data)
+{
+  gvfs_storaged_drive_eject_with_operation (drive, flags, NULL, cancellable, callback, user_data);
+}
+
+static gboolean
+gvfs_storaged_drive_eject_finish (GDrive        *drive,
+                                 GAsyncResult  *result,
+                                 GError       **error)
+{
+  return gvfs_storaged_drive_eject_with_operation_finish (drive, result, error);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+#if STORAGED_CHECK_VERSION(2,0,90)
+
+typedef struct
+{
+  GSimpleAsyncResult *simple;
+
+  GVfsStoragedDrive *drive;
+  GMountOperation *mount_operation;
+} StopData;
+
+static void
+stop_data_free (StopData *data)
+{
+  g_object_unref (data->simple);
+  g_clear_object (&data->drive);
+  g_clear_object (&data->mount_operation);
+
+  g_free (data);
+}
+
+static void
+power_off_cb (GObject      *source_object,
+              GAsyncResult *res,
+              gpointer      user_data)
+{
+  StopData *data = user_data;
+  GError *error;
+
+  error = NULL;
+  if (!storaged_drive_call_power_off_finish (STORAGED_DRIVE (source_object), res, &error))
+    {
+      gvfs_storaged_utils_storaged_error_to_gio_error (error);
+      g_simple_async_result_take_error (data->simple, error);
+    }
+
+  if (data->mount_operation != NULL)
+    {
+      /* If we fail send an ::aborted signal to make any notification go away */
+      if (error != NULL)
+        g_signal_emit_by_name (data->mount_operation, "aborted");
+
+      gvfs_storaged_unmount_notify_stop (data->mount_operation);
+    }
+
+  g_simple_async_result_complete (data->simple);
+  stop_data_free (data);
+}
+
+static void
+gvfs_storaged_drive_stop_on_all_unmounted (GDrive              *_drive,
+                                          GMountOperation     *mount_operation,
+                                          GCancellable        *cancellable,
+                                          GAsyncReadyCallback  callback,
+                                          gpointer             user_data,
+                                          gpointer             on_all_unmounted_data)
+{
+  GVfsStoragedDrive *drive = GVFS_STORAGED_DRIVE (_drive);
+  GVariantBuilder builder;
+  StopData *data;
+
+  data = g_new0 (StopData, 1);
+  data->simple = g_simple_async_result_new (G_OBJECT (drive),
+                                            callback,
+                                            user_data,
+                                            gvfs_storaged_drive_stop_on_all_unmounted);
+  data->drive = g_object_ref (drive);
+  if (mount_operation != NULL)
+    data->mount_operation = g_object_ref (mount_operation);
+
+  g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
+  if (mount_operation == NULL)
+    {
+      g_variant_builder_add (&builder,
+                             "{sv}",
+                             "auth.no_user_interaction", g_variant_new_boolean (TRUE));
+    }
+  storaged_drive_call_power_off (drive->storaged_drive,
+                               g_variant_builder_end (&builder),
+                               cancellable,
+                               power_off_cb,
+                               data);
+}
+
+static void
+gvfs_storaged_drive_stop (GDrive              *_drive,
+                         GMountUnmountFlags   flags,
+                         GMountOperation     *mount_operation,
+                         GCancellable        *cancellable,
+                         GAsyncReadyCallback  callback,
+                         gpointer             user_data)
+{
+  GVfsStoragedDrive *drive = GVFS_STORAGED_DRIVE (_drive);
+
+  /* This information is needed in GVfsDdisks2Volume when apps have
+   * open files on the device ... we need to know if the button should
+   * be "Unmount Anyway", "Eject Anyway" or "Power Off Anyway"
+   */
+  if (mount_operation != NULL)
+    {
+      g_object_set_data (G_OBJECT (mount_operation), "x-storaged-is-stop", GINT_TO_POINTER (1));
+      gvfs_storaged_unmount_notify_start (mount_operation, NULL, _drive, FALSE);
+    }
+
+  /* first we need to go through all the volumes and unmount their assoicated mounts (if any) */
+  unmount_mounts (drive,
+                  flags,
+                  mount_operation,
+                  cancellable,
+                  callback,
+                  user_data,
+                  gvfs_storaged_drive_stop_on_all_unmounted,
+                  NULL);
+}
+
+static gboolean
+gvfs_storaged_drive_stop_finish (GDrive        *drive,
+                                GAsyncResult  *result,
+                                GError       **error)
+{
+  return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error);
+}
+
+#endif /* STORAGED_CHECK_VERSION(2,0,90) */
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static const gchar *
+gvfs_storaged_drive_get_sort_key (GDrive *_drive)
+{
+  GVfsStoragedDrive *drive = GVFS_STORAGED_DRIVE (_drive);
+  return drive->sort_key;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+gvfs_storaged_drive_drive_iface_init (GDriveIface *iface)
+{
+  iface->get_name = gvfs_storaged_drive_get_name;
+  iface->get_icon = gvfs_storaged_drive_get_icon;
+  iface->get_symbolic_icon = gvfs_storaged_drive_get_symbolic_icon;
+  iface->has_volumes = gvfs_storaged_drive_has_volumes;
+  iface->get_volumes = gvfs_storaged_drive_get_volumes;
+  iface->is_media_removable = gvfs_storaged_drive_is_media_removable;
+  iface->has_media = gvfs_storaged_drive_has_media;
+  iface->is_media_check_automatic = gvfs_storaged_drive_is_media_check_automatic;
+  iface->can_eject = gvfs_storaged_drive_can_eject;
+  iface->can_poll_for_media = gvfs_storaged_drive_can_poll_for_media;
+  iface->get_identifier = gvfs_storaged_drive_get_identifier;
+  iface->enumerate_identifiers = gvfs_storaged_drive_enumerate_identifiers;
+  iface->get_start_stop_type = gvfs_storaged_drive_get_start_stop_type;
+  iface->can_start = gvfs_storaged_drive_can_start;
+  iface->can_start_degraded = gvfs_storaged_drive_can_start_degraded;
+  iface->can_stop = gvfs_storaged_drive_can_stop;
+  iface->eject = gvfs_storaged_drive_eject;
+  iface->eject_finish = gvfs_storaged_drive_eject_finish;
+  iface->eject_with_operation = gvfs_storaged_drive_eject_with_operation;
+  iface->eject_with_operation_finish = gvfs_storaged_drive_eject_with_operation_finish;
+  iface->get_sort_key = gvfs_storaged_drive_get_sort_key;
+#if 0
+  iface->poll_for_media = gvfs_storaged_drive_poll_for_media;
+  iface->poll_for_media_finish = gvfs_storaged_drive_poll_for_media_finish;
+  iface->start = gvfs_storaged_drive_start;
+  iface->start_finish = gvfs_storaged_drive_start_finish;
+#endif
+
+#if STORAGED_CHECK_VERSION(2,0,90)
+  iface->stop = gvfs_storaged_drive_stop;
+  iface->stop_finish = gvfs_storaged_drive_stop_finish;
+#endif
+}
+
+StoragedDrive *
+gvfs_storaged_drive_get_storaged_drive (GVfsStoragedDrive *drive)
+{
+  return drive->storaged_drive;
+}
diff --git a/monitor/storaged/gvfsstorageddrive.h b/monitor/storaged/gvfsstorageddrive.h
new file mode 100644
index 0000000..1b76529
--- /dev/null
+++ b/monitor/storaged/gvfsstorageddrive.h
@@ -0,0 +1,52 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* gvfs - extensions for gio
+ *
+ * Copyright (C) 2006-2012 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., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#ifndef __GVFS_STORAGED_DRIVE_H__
+#define __GVFS_STORAGED_DRIVE_H__
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include "gvfsstoragedvolumemonitor.h"
+
+G_BEGIN_DECLS
+
+#define GVFS_TYPE_STORAGED_DRIVE  (gvfs_storaged_drive_get_type ())
+#define GVFS_STORAGED_DRIVE(o)    (G_TYPE_CHECK_INSTANCE_CAST ((o), GVFS_TYPE_STORAGED_DRIVE, 
GVfsStoragedDrive))
+#define GVFS_IS_STORAGED_DRIVE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVFS_TYPE_STORAGED_DRIVE))
+
+GType             gvfs_storaged_drive_get_type        (void) G_GNUC_CONST;
+GVfsStoragedDrive *gvfs_storaged_drive_new             (GVfsStoragedVolumeMonitor *monitor,
+                                                      StoragedDrive              *storaged_drive,
+                                                      gboolean                   coldplug);
+void              gvfs_storaged_drive_disconnected    (GVfsStoragedDrive         *drive);
+
+void              gvfs_storaged_drive_set_volume      (GVfsStoragedDrive         *drive,
+                                                      GVfsStoragedVolume        *volume);
+void              gvfs_storaged_drive_unset_volume    (GVfsStoragedDrive         *drive,
+                                                      GVfsStoragedVolume        *volume);
+StoragedDrive     *gvfs_storaged_drive_get_storaged_drive (GVfsStoragedDrive         *drive);
+
+G_END_DECLS
+
+#endif /* __GVFS_STORAGED_DRIVE_H__ */
diff --git a/monitor/storaged/gvfsstoragedmount.c b/monitor/storaged/gvfsstoragedmount.c
new file mode 100644
index 0000000..f15048c
--- /dev/null
+++ b/monitor/storaged/gvfsstoragedmount.c
@@ -0,0 +1,1378 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* gvfs - extensions for gio
+ *
+ * Copyright (C) 2006-2012 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., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#include <config.h>
+
+#include <string.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+#include <gio/gio.h>
+
+#include <gvfsmountinfo.h>
+
+#include <gudev/gudev.h>
+
+#include "gvfsstoragedvolumemonitor.h"
+#include "gvfsstoragedmount.h"
+#include "gvfsstoragedvolume.h"
+#include "gvfsstorageddrive.h"
+#include "gvfsstoragedutils.h"
+
+typedef struct _GVfsStoragedMountClass GVfsStoragedMountClass;
+struct _GVfsStoragedMountClass
+{
+  GObjectClass parent_class;
+};
+
+struct _GVfsStoragedMount
+{
+  GObject parent;
+
+  GVfsStoragedVolumeMonitor *monitor; /* owned by volume monitor */
+
+  /* may be NULL */
+  GVfsStoragedVolume        *volume;  /* owned by volume monitor */
+
+  /* may be NULL */
+  GUnixMountEntry *mount_entry;
+
+  /* the following members are set in update_mount() */
+  GFile *root;
+  GIcon *icon;
+  GIcon *symbolic_icon;
+  gchar *name;
+  gchar *sort_key;
+  gchar *uuid;
+  gchar *device_file;
+  gchar *mount_path;
+  gboolean can_unmount;
+  gchar *mount_entry_name;
+  gchar *mount_entry_fs_type;
+
+  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;
+
+  gchar *bdmv_volume_info_name;
+  GIcon *bdmv_volume_info_icon;
+  gboolean searched_for_bdmv_volume_info;
+};
+
+static gboolean update_mount (GVfsStoragedMount *mount);
+
+static void gvfs_storaged_mount_mount_iface_init (GMountIface *iface);
+
+G_DEFINE_TYPE_EXTENDED (GVfsStoragedMount, gvfs_storaged_mount, G_TYPE_OBJECT, 0,
+                        G_IMPLEMENT_INTERFACE (G_TYPE_MOUNT,
+                                               gvfs_storaged_mount_mount_iface_init))
+
+static void on_volume_changed (GVolume *volume, gpointer user_data);
+
+static void
+gvfs_storaged_mount_finalize (GObject *object)
+{
+  GVfsStoragedMount *mount = GVFS_STORAGED_MOUNT (object);
+
+  if (mount->volume != NULL)
+    {
+      g_signal_handlers_disconnect_by_func (mount->volume, on_volume_changed, mount);
+      gvfs_storaged_volume_unset_mount (mount->volume, mount);
+    }
+
+  g_clear_object (&mount->root);
+  g_clear_object (&mount->icon);
+  g_clear_object (&mount->symbolic_icon);
+  g_free (mount->name);
+  g_free (mount->sort_key);
+  g_free (mount->uuid);
+  g_free (mount->device_file);
+  g_free (mount->mount_path);
+
+  g_free (mount->mount_entry_name);
+
+  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);
+
+  G_OBJECT_CLASS (gvfs_storaged_mount_parent_class)->finalize (object);
+}
+
+static void
+gvfs_storaged_mount_class_init (GVfsStoragedMountClass *klass)
+{
+  GObjectClass *gobject_class;
+
+  gobject_class = G_OBJECT_CLASS (klass);
+  gobject_class->finalize = gvfs_storaged_mount_finalize;
+}
+
+static void
+gvfs_storaged_mount_init (GVfsStoragedMount *mount)
+{
+}
+
+static void
+emit_changed (GVfsStoragedMount *mount)
+{
+  g_signal_emit_by_name (mount, "changed");
+  g_signal_emit_by_name (mount->monitor, "mount-changed", mount);
+}
+
+static void
+got_autorun_info_cb (GObject      *source_object,
+                     GAsyncResult *res,
+                     gpointer      user_data)
+{
+  GVfsStoragedMount *mount = GVFS_STORAGED_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)
+{
+  GVfsStoragedMount *mount = GVFS_STORAGED_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 void
+got_bdmv_volume_info_cb (GObject      *source_object,
+                         GAsyncResult *res,
+                         gpointer      user_data)
+{
+  GVfsStoragedMount *mount = GVFS_STORAGED_MOUNT (user_data);
+  mount->bdmv_volume_info_icon = g_vfs_mount_info_query_bdmv_volume_info_finish (G_FILE (source_object),
+                                                                                 res,
+                                                                                 
&(mount->bdmv_volume_info_name),
+                                                                                 NULL);
+  if (update_mount (mount))
+    emit_changed (mount);
+  g_object_unref (mount);
+}
+
+static gboolean
+update_mount (GVfsStoragedMount *mount)
+{
+  gboolean changed;
+  gboolean old_can_unmount;
+  gchar *old_name;
+  GIcon *old_icon;
+  GIcon *old_symbolic_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;
+  old_symbolic_icon = mount->symbolic_icon != NULL ? g_object_ref (mount->symbolic_icon) : NULL;
+
+  /* reset */
+  mount->can_unmount = FALSE;
+  g_clear_object (&mount->icon);
+  g_clear_object (&mount->symbolic_icon);
+  g_free (mount->name); mount->name = NULL;
+
+  /* in with the new */
+  if (mount->volume != NULL)
+    {
+      mount->can_unmount = TRUE;
+
+      /* icon order of preference: bdmv, xdg, autorun, probed */
+      if (mount->bdmv_volume_info_icon != NULL)
+        mount->icon = g_object_ref (mount->bdmv_volume_info_icon);
+      else 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));
+
+      /* name order of preference : bdmv, xdg, probed */
+      if (mount->bdmv_volume_info_name != NULL)
+        mount->name = g_strdup (mount->bdmv_volume_info_name);
+      else 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));
+
+      mount->symbolic_icon = g_volume_get_symbolic_icon (G_VOLUME (mount->volume));
+    }
+  else
+    {
+      mount->can_unmount = TRUE;
+
+      if (mount->icon != NULL)
+        g_object_unref (mount->icon);
+
+      /* icon order of preference: bdmv, xdg, autorun, probed */
+      if (mount->bdmv_volume_info_icon != NULL)
+        mount->icon = g_object_ref (mount->bdmv_volume_info_icon);
+      else 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 = gvfs_storaged_utils_icon_from_fs_type (g_unix_mount_get_fs_type 
(mount->mount_entry));
+        }
+
+      g_free (mount->name);
+
+      /* name order of preference: bdmv, xdg, probed */
+      if (mount->bdmv_volume_info_name != NULL)
+        mount->name = g_strdup (mount->bdmv_volume_info_name);
+      else 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);
+
+      mount->symbolic_icon = gvfs_storaged_utils_symbolic_icon_from_fs_type (g_unix_mount_get_fs_type 
(mount->mount_entry));
+    }
+
+  /* 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) &&
+              g_icon_equal (old_symbolic_icon, mount->symbolic_icon));
+
+  /* free old values */
+  g_free (old_name);
+  g_clear_object (&old_icon);
+  g_clear_object (&old_symbolic_icon);
+
+  /*g_debug ("in update_mount(), changed=%d", changed);*/
+
+  /* search for BDMV */
+  if (!mount->searched_for_bdmv_volume_info)
+    {
+      mount->searched_for_bdmv_volume_info = TRUE;
+      g_vfs_mount_info_query_bdmv_volume_info (mount->root,
+                                              NULL,
+                                              got_bdmv_volume_info_cb,
+                                              g_object_ref (mount));
+    }
+
+  /* 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
+on_volume_changed (GVolume  *volume,
+                   gpointer  user_data)
+{
+  GVfsStoragedMount *mount = GVFS_STORAGED_MOUNT (user_data);
+  if (update_mount (mount))
+    emit_changed (mount);
+}
+
+GVfsStoragedMount *
+gvfs_storaged_mount_new (GVfsStoragedVolumeMonitor *monitor,
+                        GUnixMountEntry          *mount_entry, /* takes ownership */
+                        GVfsStoragedVolume        *volume)
+{
+  GVfsStoragedMount *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 (GVFS_TYPE_STORAGED_MOUNT, NULL);
+  mount->monitor = monitor;
+  mount->sort_key = g_strdup_printf ("gvfs.time_detected_usec.%" G_GINT64_FORMAT, g_get_real_time ());
+
+  if (mount_entry != NULL)
+    {
+      mount->mount_entry = mount_entry; /* takes ownership */
+      mount->mount_entry_name = g_unix_mount_guess_name (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)
+    {
+      gvfs_storaged_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 (on_volume_changed), mount);
+    }
+
+  update_mount (mount);
+
+ out:
+
+  return mount;
+}
+
+void
+gvfs_storaged_mount_unmounted (GVfsStoragedMount *mount)
+{
+  if (mount->volume != NULL)
+    {
+      gvfs_storaged_volume_unset_mount (mount->volume, mount);
+      g_signal_handlers_disconnect_by_func (mount->volume, on_volume_changed, mount);
+      mount->volume = NULL;
+      emit_changed (mount);
+    }
+}
+
+void
+gvfs_storaged_mount_unset_volume (GVfsStoragedMount   *mount,
+                                 GVfsStoragedVolume  *volume)
+{
+  if (mount->volume == volume)
+    {
+      g_signal_handlers_disconnect_by_func (mount->volume, on_volume_changed, mount);
+      mount->volume = NULL;
+      emit_changed (mount);
+    }
+}
+
+void
+gvfs_storaged_mount_set_volume (GVfsStoragedMount   *mount,
+                               GVfsStoragedVolume  *volume)
+{
+  if (mount->volume != volume)
+    {
+      if (mount->volume != NULL)
+        gvfs_storaged_mount_unset_volume (mount, mount->volume);
+      mount->volume = volume;
+      if (mount->volume != NULL)
+        {
+          gvfs_storaged_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 (on_volume_changed), mount);
+        }
+      update_mount (mount);
+      emit_changed (mount);
+    }
+}
+
+static GFile *
+gvfs_storaged_mount_get_root (GMount *_mount)
+{
+  GVfsStoragedMount *mount = GVFS_STORAGED_MOUNT (_mount);
+  return mount->root != NULL ? g_object_ref (mount->root) : NULL;
+}
+
+static GIcon *
+gvfs_storaged_mount_get_icon (GMount *_mount)
+{
+  GVfsStoragedMount *mount = GVFS_STORAGED_MOUNT (_mount);
+  return mount->icon != NULL ? g_object_ref (mount->icon) : NULL;
+}
+
+static GIcon *
+gvfs_storaged_mount_get_symbolic_icon (GMount *_mount)
+{
+  GVfsStoragedMount *mount = GVFS_STORAGED_MOUNT (_mount);
+  return mount->symbolic_icon != NULL ? g_object_ref (mount->symbolic_icon) : NULL;
+}
+
+static gchar *
+gvfs_storaged_mount_get_uuid (GMount *_mount)
+{
+  GVfsStoragedMount *mount = GVFS_STORAGED_MOUNT (_mount);
+  return g_strdup (mount->uuid);
+}
+
+static gchar *
+gvfs_storaged_mount_get_name (GMount *_mount)
+{
+  GVfsStoragedMount *mount = GVFS_STORAGED_MOUNT (_mount);
+  return g_strdup (mount->name);
+}
+
+gboolean
+gvfs_storaged_mount_has_uuid (GVfsStoragedMount *_mount,
+                             const gchar      *uuid)
+{
+  GVfsStoragedMount *mount = GVFS_STORAGED_MOUNT (_mount);
+  return g_strcmp0 (mount->uuid, uuid) == 0;
+}
+
+const gchar *
+gvfs_storaged_mount_get_mount_path (GVfsStoragedMount *mount)
+{
+  return mount->mount_path;
+}
+
+GUnixMountEntry *
+gvfs_storaged_mount_get_mount_entry (GVfsStoragedMount *_mount)
+{
+  GVfsStoragedMount *mount = GVFS_STORAGED_MOUNT (_mount);
+  return mount->mount_entry;
+}
+
+static GDrive *
+gvfs_storaged_mount_get_drive (GMount *_mount)
+{
+  GVfsStoragedMount *mount = GVFS_STORAGED_MOUNT (_mount);
+  GDrive *drive = NULL;
+
+  if (mount->volume != NULL)
+    drive = g_volume_get_drive (G_VOLUME (mount->volume));
+  return drive;
+}
+
+static GVolume *
+gvfs_storaged_mount_get_volume_ (GMount *_mount)
+{
+  GVfsStoragedMount *mount = GVFS_STORAGED_MOUNT (_mount);
+  GVolume *volume = NULL;
+
+  if (mount->volume != NULL)
+    volume = G_VOLUME (g_object_ref (mount->volume));
+  return volume;
+}
+
+static gboolean
+gvfs_storaged_mount_can_unmount (GMount *_mount)
+{
+  GVfsStoragedMount *mount = GVFS_STORAGED_MOUNT (_mount);
+  return mount->can_unmount;
+}
+
+static gboolean
+gvfs_storaged_mount_can_eject (GMount *_mount)
+{
+  GVfsStoragedMount *mount = GVFS_STORAGED_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
+{
+  volatile gint ref_count;
+  GSimpleAsyncResult *simple;
+  gboolean in_progress;
+  gboolean completed;
+
+  GVfsStoragedMount *mount;
+
+  StoragedEncrypted *encrypted;
+  StoragedFilesystem *filesystem;
+
+  GCancellable *cancellable;
+
+  GMountOperation *mount_operation;
+  GMountUnmountFlags flags;
+
+  gulong mount_op_reply_handler_id;
+  guint retry_unmount_timer_id;
+
+  GMountOperationResult reply_result;
+  gint reply_choice;
+  gboolean reply_set;
+} UnmountData;
+
+static UnmountData *
+unmount_data_ref (UnmountData *data)
+{
+  g_atomic_int_inc (&data->ref_count);
+  return data;
+}
+
+static void
+unmount_data_unref (UnmountData *data)
+{
+  if (g_atomic_int_dec_and_test (&data->ref_count))
+    {
+      g_object_unref (data->simple);
+
+      if (data->mount_op_reply_handler_id > 0)
+        {
+          /* make the operation dialog go away */
+          g_signal_emit_by_name (data->mount_operation, "aborted");
+          g_signal_handler_disconnect (data->mount_operation, data->mount_op_reply_handler_id);
+        }
+      if (data->retry_unmount_timer_id > 0)
+        {
+          g_source_remove (data->retry_unmount_timer_id);
+          data->retry_unmount_timer_id = 0;
+        }
+
+      g_clear_object (&data->mount);
+      g_clear_object (&data->cancellable);
+      g_clear_object (&data->mount_operation);
+      g_clear_object (&data->encrypted);
+      g_clear_object (&data->filesystem);
+      g_free (data);
+    }
+}
+
+static gboolean
+unmount_operation_is_eject (GMountOperation *op)
+{
+  return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (op), "x-storaged-is-eject"));
+}
+
+static gboolean
+unmount_operation_is_stop (GMountOperation *op)
+{
+  return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (op), "x-storaged-is-stop"));
+}
+
+static void
+unmount_data_complete (UnmountData *data,
+                       gboolean     complete_idle)
+{
+  if (data->mount_operation &&
+      !unmount_operation_is_eject (data->mount_operation))
+    gvfs_storaged_unmount_notify_stop (data->mount_operation);
+
+  if (complete_idle)
+    g_simple_async_result_complete_in_idle (data->simple);
+  else
+    g_simple_async_result_complete (data->simple);
+
+  data->in_progress = FALSE;
+  data->completed = TRUE;
+  unmount_data_unref (data);
+}
+
+static void unmount_do (UnmountData *data, gboolean force);
+
+static gboolean
+on_retry_timer_cb (gpointer user_data)
+{
+  UnmountData *data = user_data;
+
+  if (data->retry_unmount_timer_id == 0)
+    goto out;
+
+  /* we're removing the timeout */
+  data->retry_unmount_timer_id = 0;
+
+  if (data->completed || data->in_progress)
+    goto out;
+
+  /* timeout expired => try again */
+  unmount_do (data, FALSE);
+
+ out:
+  return FALSE; /* remove timeout */
+}
+
+static void
+mount_op_reply_handle (UnmountData *data)
+{
+  data->reply_set = FALSE;
+
+  if (data->reply_result == G_MOUNT_OPERATION_ABORTED ||
+      (data->reply_result == G_MOUNT_OPERATION_HANDLED &&
+       data->reply_choice == 1))
+    {
+      /* don't show an error dialog here */
+      g_simple_async_result_set_error (data->simple,
+                                       G_IO_ERROR,
+                                       G_IO_ERROR_FAILED_HANDLED,
+                                       "GMountOperation aborted (user should never see this "
+                                       "error since it is G_IO_ERROR_FAILED_HANDLED)");
+      unmount_data_complete (data, TRUE);
+    }
+  else if (data->reply_result == G_MOUNT_OPERATION_HANDLED)
+    {
+      /* user chose force unmount => try again with force_unmount==TRUE */
+      unmount_do (data, TRUE);
+    }
+  else
+    {
+      /* result == G_MOUNT_OPERATION_UNHANDLED => GMountOperation instance doesn't
+       * support :show-processes signal
+       */
+      g_simple_async_result_set_error (data->simple,
+                                       G_IO_ERROR,
+                                       G_IO_ERROR_BUSY,
+                                       _("One or more programs are preventing the unmount operation."));
+      unmount_data_complete (data, TRUE);
+    }
+}
+
+static void
+on_mount_op_reply (GMountOperation       *mount_operation,
+                   GMountOperationResult result,
+                   gpointer              user_data)
+{
+  UnmountData *data = user_data;
+  gint choice;
+
+  /* disconnect the signal handler */
+  g_warn_if_fail (data->mount_op_reply_handler_id != 0);
+  g_signal_handler_disconnect (data->mount_operation,
+                               data->mount_op_reply_handler_id);
+  data->mount_op_reply_handler_id = 0;
+
+  choice = g_mount_operation_get_choice (mount_operation);
+  data->reply_result = result;
+  data->reply_choice = choice;
+  data->reply_set = TRUE;
+  if (!data->completed || !data->in_progress)
+    mount_op_reply_handle (data);
+}
+
+static void
+lsof_command_cb (GObject       *source_object,
+                 GAsyncResult  *res,
+                 gpointer       user_data)
+{
+  UnmountData *data = user_data;
+  GError *error;
+  gint exit_status;
+  GArray *processes;
+  const gchar *choices[3] = {NULL, NULL, NULL};
+  const gchar *message;
+  gchar *standard_output = NULL;
+  const gchar *p;
+
+  processes = g_array_new (FALSE, FALSE, sizeof (GPid));
+
+  error = NULL;
+  if (!gvfs_storaged_utils_spawn_finish (res,
+                                        &exit_status,
+                                        &standard_output,
+                                        NULL, /* gchar **out_standard_error */
+                                        &error))
+    {
+      g_printerr ("Error launching lsof(1): %s (%s, %d)\n",
+                  error->message, g_quark_to_string (error->domain), error->code);
+      g_error_free (error);
+      goto out;
+    }
+
+  if (!(WIFEXITED (exit_status) && WEXITSTATUS (exit_status) == 0))
+    {
+      g_printerr ("lsof(1) did not exit normally\n");
+      goto out;
+    }
+
+  p = standard_output;
+  while (TRUE)
+    {
+      GPid pid;
+      gchar *endp;
+
+      if (*p == '\0')
+        break;
+
+      pid = strtol (p, &endp, 10);
+      if (pid == 0 && p == endp)
+        break;
+
+      g_array_append_val (processes, pid);
+
+      p = endp;
+    }
+
+ out:
+  if (!data->completed)
+    {
+      gboolean is_eject;
+      gboolean is_stop;
+
+      is_eject = unmount_operation_is_eject (data->mount_operation);
+      is_stop = unmount_operation_is_stop (data->mount_operation);
+
+      /* We want to emit the 'show-processes' signal even if launching
+       * lsof(1) failed or if it didn't return any PIDs. This is because
+       * it won't show e.g. root-owned processes operating on files
+       * on the mount point.
+       *
+       * (unfortunately there's no way to convey that it failed)
+       */
+      if (data->mount_op_reply_handler_id == 0)
+        {
+          data->mount_op_reply_handler_id = g_signal_connect (data->mount_operation,
+                                                              "reply",
+                                                              G_CALLBACK (on_mount_op_reply),
+                                                              data);
+        }
+      if (is_eject || is_stop)
+        {
+          /* Note that the GUI (Shell, Files) currently use the term
+           * "Eject" for both GDrive.stop() and GDrive.eject().
+           */
+          choices[0] = _("Eject Anyway");
+        }
+      else
+        {
+          choices[0] = _("Unmount Anyway");
+        }
+      choices[1] = _("Cancel");
+      message = _("Volume is busy\n"
+                  "One or more applications are keeping the volume busy.");
+      g_signal_emit_by_name (data->mount_operation,
+                             "show-processes",
+                             message,
+                             processes,
+                             choices);
+      /* set up a timer to try unmounting every two seconds - this will also
+       * update the list of busy processes
+       */
+      if (data->retry_unmount_timer_id == 0)
+        {
+          data->retry_unmount_timer_id = g_timeout_add_seconds (2,
+                                                                on_retry_timer_cb,
+                                                                data);
+        }
+      g_array_free (processes, TRUE);
+      g_free (standard_output);
+    }
+  unmount_data_unref (data); /* return ref */
+}
+
+
+static void
+unmount_show_busy (UnmountData  *data,
+                   const gchar  *mount_point)
+{
+  gchar *escaped_mount_point;
+
+  data->in_progress = FALSE;
+
+  /* We received an reply during an unmount operation which could not complete.
+   * Handle the reply now. */
+  if (data->reply_set)
+    {
+      mount_op_reply_handle (data);
+      return;
+    }
+
+  escaped_mount_point = g_strescape (mount_point, NULL);
+  gvfs_storaged_utils_spawn (10, /* timeout in seconds */
+                            data->cancellable,
+                            lsof_command_cb,
+                            unmount_data_ref (data),
+                            "lsof -t \"%s\"",
+                            escaped_mount_point);
+  g_free (escaped_mount_point);
+}
+
+static void
+lock_cb (GObject       *source_object,
+         GAsyncResult  *res,
+         gpointer       user_data)
+{
+  StoragedEncrypted *encrypted = STORAGED_ENCRYPTED (source_object);
+  UnmountData *data = user_data;
+  GError *error;
+
+  error = NULL;
+  if (!storaged_encrypted_call_lock_finish (encrypted,
+                                          res,
+                                          &error))
+    g_simple_async_result_take_error (data->simple, error);
+  unmount_data_complete (data, FALSE);
+}
+
+static void
+unmount_cb (GObject       *source_object,
+            GAsyncResult  *res,
+            gpointer       user_data)
+{
+  StoragedFilesystem *filesystem = STORAGED_FILESYSTEM (source_object);
+  UnmountData *data = user_data;
+  GError *error;
+
+  error = NULL;
+  if (!storaged_filesystem_call_unmount_finish (filesystem,
+                                              res,
+                                              &error))
+    {
+      gvfs_storaged_utils_storaged_error_to_gio_error (error);
+
+      /* if the user passed in a GMountOperation, then do the GMountOperation::show-processes dance ... */
+      if (error->code == G_IO_ERROR_BUSY && data->mount_operation != NULL)
+        {
+          unmount_show_busy (data, storaged_filesystem_get_mount_points (filesystem)[0]);
+          goto out;
+        }
+      g_simple_async_result_take_error (data->simple, error);
+    }
+  else
+    {
+      gvfs_storaged_volume_monitor_update (data->mount->monitor);
+      if (data->encrypted != NULL)
+        {
+          storaged_encrypted_call_lock (data->encrypted,
+                                      g_variant_new ("a{sv}", NULL), /* options */
+                                      data->cancellable,
+                                      lock_cb,
+                                      data);
+          goto out;
+        }
+    }
+
+  unmount_data_complete (data, FALSE);
+ out:
+  ;
+}
+
+
+/* ------------------------------ */
+
+static void
+umount_command_cb (GObject       *source_object,
+                   GAsyncResult  *res,
+                   gpointer       user_data)
+{
+  UnmountData *data = user_data;
+  GError *error;
+  gint exit_status;
+  gchar *standard_error = NULL;
+
+  error = NULL;
+  if (!gvfs_storaged_utils_spawn_finish (res,
+                                        &exit_status,
+                                        NULL, /* gchar **out_standard_output */
+                                        &standard_error,
+                                        &error))
+    {
+      g_simple_async_result_take_error (data->simple, error);
+      unmount_data_complete (data, FALSE);
+      goto out;
+    }
+
+  if (WIFEXITED (exit_status) && WEXITSTATUS (exit_status) == 0)
+    {
+      gvfs_storaged_volume_monitor_update (data->mount->monitor);
+      unmount_data_complete (data, FALSE);
+      goto out;
+    }
+
+  if (standard_error != NULL &&
+      (strstr (standard_error, "device is busy") != NULL ||
+       strstr (standard_error, "target is busy") != NULL))
+    {
+      unmount_show_busy (data, data->mount->mount_path);
+      goto out;
+    }
+
+  g_simple_async_result_set_error (data->simple,
+                                   G_IO_ERROR,
+                                   G_IO_ERROR_FAILED,
+                                   "%s", standard_error);
+  unmount_data_complete (data, FALSE);
+
+ out:
+  g_free (standard_error);
+}
+
+static void
+unmount_do (UnmountData *data,
+            gboolean     force)
+{
+  GVariantBuilder builder;
+
+  data->in_progress = TRUE;
+
+  if (data->mount_operation != NULL)
+    gvfs_storaged_unmount_notify_start (data->mount_operation, 
+                                       G_MOUNT (data->mount), NULL,
+                                       (data->filesystem == NULL));
+
+  /* Use the umount(8) command if there is no block device / filesystem */
+  if (data->filesystem == NULL)
+    {
+      gchar *escaped_mount_path;
+      escaped_mount_path = g_strescape (data->mount->mount_path, NULL);
+      gvfs_storaged_utils_spawn (10, /* timeout in seconds */
+                                data->cancellable,
+                                umount_command_cb,
+                                data,
+                                "umount %s \"%s\"",
+                                force ? "-l " : "",
+                                escaped_mount_path);
+      g_free (escaped_mount_path);
+      goto out;
+    }
+
+  g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
+  if (data->mount_operation == NULL)
+    {
+      g_variant_builder_add (&builder,
+                             "{sv}",
+                             "auth.no_user_interaction", g_variant_new_boolean (TRUE));
+    }
+  if (force || data->flags & G_MOUNT_UNMOUNT_FORCE)
+    {
+      g_variant_builder_add (&builder,
+                             "{sv}",
+                             "force", g_variant_new_boolean (TRUE));
+    }
+  g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (data->filesystem), G_MAXINT);
+  storaged_filesystem_call_unmount (data->filesystem,
+                                  g_variant_builder_end (&builder),
+                                  data->cancellable,
+                                  unmount_cb,
+                                  data);
+
+ out:
+  ;
+}
+
+static void
+gvfs_storaged_mount_unmount_with_operation (GMount              *_mount,
+                                           GMountUnmountFlags   flags,
+                                           GMountOperation     *mount_operation,
+                                           GCancellable        *cancellable,
+                                           GAsyncReadyCallback  callback,
+                                           gpointer             user_data)
+{
+  GVfsStoragedMount *mount = GVFS_STORAGED_MOUNT (_mount);
+  UnmountData *data;
+  StoragedBlock *block;
+
+  /* first emit the ::mount-pre-unmount signal */
+  g_signal_emit_by_name (mount->monitor, "mount-pre-unmount", mount);
+
+  data = g_new0 (UnmountData, 1);
+  data->ref_count = 1;
+  data->simple = g_simple_async_result_new (G_OBJECT (mount),
+                                            callback,
+                                            user_data,
+                                            gvfs_storaged_mount_unmount_with_operation);
+  data->mount = g_object_ref (mount);
+  data->cancellable = cancellable != NULL ? g_object_ref (cancellable) : NULL;
+  data->mount_operation = mount_operation != NULL ? g_object_ref (mount_operation) : NULL;
+  data->flags = flags;
+
+  if (mount->is_burn_mount)
+    {
+      /* burn mounts are really never mounted so complete successfully immediately */
+      unmount_data_complete (data, TRUE);
+      goto out;
+    }
+
+  block = NULL;
+  if (mount->volume != NULL)
+    block = gvfs_storaged_volume_get_block (data->mount->volume);
+  if (block != NULL)
+    {
+      GDBusObject *object;
+      object = g_dbus_interface_get_object (G_DBUS_INTERFACE (block));
+      if (object == NULL)
+        {
+          g_simple_async_result_set_error (data->simple,
+                                           G_IO_ERROR,
+                                           G_IO_ERROR_FAILED,
+                                           "No object for D-Bus interface");
+
+          unmount_data_complete (data, FALSE);
+          goto out;
+        }
+      data->filesystem = storaged_object_get_filesystem (STORAGED_OBJECT (object));
+      if (data->filesystem == NULL)
+        {
+          StoragedBlock *cleartext_block;
+
+          data->encrypted = storaged_object_get_encrypted (STORAGED_OBJECT (object));
+          if (data->encrypted == NULL)
+            {
+              g_simple_async_result_set_error (data->simple,
+                                               G_IO_ERROR,
+                                               G_IO_ERROR_FAILED,
+                                               "No filesystem or encrypted interface on D-Bus object");
+              unmount_data_complete (data, FALSE);
+              goto out;
+            }
+
+          cleartext_block = storaged_client_get_cleartext_block 
(gvfs_storaged_volume_monitor_get_storaged_client (mount->monitor),
+                                                               block);
+          if (cleartext_block != NULL)
+            {
+              data->filesystem = storaged_object_get_filesystem (STORAGED_OBJECT 
(g_dbus_interface_get_object (G_DBUS_INTERFACE (cleartext_block))));
+              g_object_unref (cleartext_block);
+              if (data->filesystem == NULL)
+                {
+                  g_simple_async_result_set_error (data->simple,
+                                                   G_IO_ERROR,
+                                                   G_IO_ERROR_FAILED,
+                                                   "No filesystem interface on D-Bus object for cleartext 
device");
+                  unmount_data_complete (data, FALSE);
+                  goto out;
+                }
+            }
+        }
+      g_assert (data->filesystem != NULL);
+    }
+  unmount_do (data, FALSE /* force */);
+
+ out:
+  ;
+}
+
+static gboolean
+gvfs_storaged_mount_unmount_with_operation_finish (GMount        *mount,
+                                                  GAsyncResult  *result,
+                                                  GError       **error)
+{
+  return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+gvfs_storaged_mount_unmount (GMount              *mount,
+                            GMountUnmountFlags   flags,
+                            GCancellable        *cancellable,
+                            GAsyncReadyCallback  callback,
+                            gpointer             user_data)
+{
+  gvfs_storaged_mount_unmount_with_operation (mount, flags, NULL, cancellable, callback, user_data);
+}
+
+static gboolean
+gvfs_storaged_mount_unmount_finish (GMount        *mount,
+                                   GAsyncResult  *result,
+                                   GError       **error)
+{
+  return gvfs_storaged_mount_unmount_with_operation_finish (mount, 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
+gvfs_storaged_mount_eject_with_operation (GMount              *_mount,
+                                         GMountUnmountFlags   flags,
+                                         GMountOperation     *mount_operation,
+                                         GCancellable        *cancellable,
+                                         GAsyncReadyCallback  callback,
+                                         gpointer             user_data)
+{
+  GVfsStoragedMount *mount = GVFS_STORAGED_MOUNT (_mount);
+  GDrive *drive;
+
+  drive = NULL;
+  if (mount->volume != NULL)
+    drive = g_volume_get_drive (G_VOLUME (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_with_operation (drive, flags, mount_operation, 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
+gvfs_storaged_mount_eject_with_operation_finish (GMount        *_mount,
+                                                GAsyncResult  *result,
+                                                GError       **error)
+{
+  GVfsStoragedMount *mount = GVFS_STORAGED_MOUNT (_mount);
+  gboolean ret = TRUE;
+  GDrive *drive;
+
+  drive = NULL;
+  if (mount->volume != NULL)
+    drive = g_volume_get_drive (G_VOLUME (mount->volume));
+
+  if (drive != NULL)
+    {
+      ret = g_drive_eject_with_operation_finish (drive, result, error);
+      g_object_unref (drive);
+    }
+  else
+    {
+      g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error);
+      ret = FALSE;
+    }
+  return ret;
+}
+
+static void
+gvfs_storaged_mount_eject (GMount              *mount,
+                          GMountUnmountFlags   flags,
+                          GCancellable        *cancellable,
+                          GAsyncReadyCallback  callback,
+                          gpointer             user_data)
+{
+  gvfs_storaged_mount_eject_with_operation (mount, flags, NULL, cancellable, callback, user_data);
+}
+
+static gboolean
+gvfs_storaged_mount_eject_finish (GMount        *mount,
+                                 GAsyncResult  *result,
+                                 GError       **error)
+{
+  return gvfs_storaged_mount_eject_with_operation_finish (mount, result, error);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gchar **
+gvfs_storaged_mount_guess_content_type_sync (GMount        *_mount,
+                                            gboolean       force_rescan,
+                                            GCancellable  *cancellable,
+                                            GError       **error)
+{
+  GVfsStoragedMount *mount = GVFS_STORAGED_MOUNT (_mount);
+  gchar **x_content_types;
+  GPtrArray *p;
+  gchar **ret;
+  guint n;
+
+  p = g_ptr_array_new ();
+
+  /* doesn't make sense to probe blank discs - look at the disc type instead */
+  if (mount->is_burn_mount)
+    {
+      GDrive *drive;
+      drive = gvfs_storaged_mount_get_drive (_mount);
+      if (drive != NULL)
+        {
+          StoragedDrive *storaged_drive = gvfs_storaged_drive_get_storaged_drive (GVFS_STORAGED_DRIVE 
(drive));;
+          const gchar *media = storaged_drive_get_media (storaged_drive);
+          if (media != NULL)
+            {
+              if (g_str_has_prefix (media, "optical_dvd"))
+                g_ptr_array_add (p, g_strdup ("x-content/blank-dvd"));
+              else if (g_str_has_prefix (media, "optical_hddvd"))
+                g_ptr_array_add (p, g_strdup ("x-content/blank-hddvd"));
+              else if (g_str_has_prefix (media, "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 */
+            }
+          g_object_unref (drive);
+        }
+    }
+  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 (mount->device_file != NULL)
+    {
+      GUdevDevice *gudev_device;
+      gudev_device = g_udev_client_query_by_device_file (gvfs_storaged_volume_monitor_get_gudev_client 
(mount->monitor),
+                                                         mount->device_file);
+      if (gudev_device != NULL)
+        {
+          /* Check if its bootable */
+          const gchar *boot_sys_id;
+
+          boot_sys_id = g_udev_device_get_property (gudev_device,
+                                                    "ID_FS_BOOT_SYSTEM_ID");
+          if (boot_sys_id != NULL ||
+              g_udev_device_get_property_as_boolean (gudev_device, "OSINFO_BOOTABLE"))
+            g_ptr_array_add (p, g_strdup ("x-content/bootable-media"));
+
+          /* Check for media player */
+          if (g_udev_device_has_property (gudev_device, "ID_MEDIA_PLAYER"))
+            g_ptr_array_add (p, g_strdup ("x-content/audio-player"));
+
+          g_object_unref (gudev_device);
+        }
+    }
+
+  if (p->len == 0)
+    {
+      ret = NULL;
+      g_ptr_array_free (p, TRUE);
+    }
+  else
+    {
+      g_ptr_array_add (p, NULL);
+      ret = (char **) g_ptr_array_free (p, FALSE);
+    }
+  return ret;
+}
+
+/* since we're an out-of-process volume monitor we'll just do this sync */
+static void
+gvfs_storaged_mount_guess_content_type (GMount              *mount,
+                                       gboolean             force_rescan,
+                                       GCancellable        *cancellable,
+                                       GAsyncReadyCallback  callback,
+                                       gpointer             user_data)
+{
+  GSimpleAsyncResult *simple;
+  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 **
+gvfs_storaged_mount_guess_content_type_finish (GMount        *mount,
+                                              GAsyncResult  *result,
+                                              GError       **error)
+{
+  return gvfs_storaged_mount_guess_content_type_sync (mount, FALSE, NULL, error);
+}
+/* ---------------------------------------------------------------------------------------------------- */
+
+static const gchar *
+gvfs_storaged_mount_get_sort_key (GMount *_mount)
+{
+  GVfsStoragedMount *mount = GVFS_STORAGED_MOUNT (_mount);
+  return mount->sort_key;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+gvfs_storaged_mount_mount_iface_init (GMountIface *iface)
+{
+  iface->get_root = gvfs_storaged_mount_get_root;
+  iface->get_name = gvfs_storaged_mount_get_name;
+  iface->get_icon = gvfs_storaged_mount_get_icon;
+  iface->get_symbolic_icon = gvfs_storaged_mount_get_symbolic_icon;
+  iface->get_uuid = gvfs_storaged_mount_get_uuid;
+  iface->get_drive = gvfs_storaged_mount_get_drive;
+  iface->get_volume = gvfs_storaged_mount_get_volume_;
+  iface->can_unmount = gvfs_storaged_mount_can_unmount;
+  iface->can_eject = gvfs_storaged_mount_can_eject;
+  iface->unmount = gvfs_storaged_mount_unmount;
+  iface->unmount_finish = gvfs_storaged_mount_unmount_finish;
+  iface->unmount_with_operation = gvfs_storaged_mount_unmount_with_operation;
+  iface->unmount_with_operation_finish = gvfs_storaged_mount_unmount_with_operation_finish;
+  iface->eject = gvfs_storaged_mount_eject;
+  iface->eject_finish = gvfs_storaged_mount_eject_finish;
+  iface->eject_with_operation = gvfs_storaged_mount_eject_with_operation;
+  iface->eject_with_operation_finish = gvfs_storaged_mount_eject_with_operation_finish;
+  iface->guess_content_type = gvfs_storaged_mount_guess_content_type;
+  iface->guess_content_type_finish = gvfs_storaged_mount_guess_content_type_finish;
+  iface->guess_content_type_sync = gvfs_storaged_mount_guess_content_type_sync;
+  iface->get_sort_key = gvfs_storaged_mount_get_sort_key;
+}
+
+gboolean
+gvfs_storaged_mount_has_volume (GVfsStoragedMount   *mount,
+                               GVfsStoragedVolume  *volume)
+{
+  return mount->volume == volume;
+}
+
+GVfsStoragedVolume *
+gvfs_storaged_mount_get_volume (GVfsStoragedMount *mount)
+{
+  return mount->volume;
+}
diff --git a/monitor/storaged/gvfsstoragedmount.h b/monitor/storaged/gvfsstoragedmount.h
new file mode 100644
index 0000000..d347221
--- /dev/null
+++ b/monitor/storaged/gvfsstoragedmount.h
@@ -0,0 +1,61 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* gvfs - extensions for gio
+ *
+ * Copyright (C) 2006-2012 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., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#ifndef __GVFS_STORAGED_MOUNT_H__
+#define __GVFS_STORAGED_MOUNT_H__
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include "gvfsstoragedvolumemonitor.h"
+
+G_BEGIN_DECLS
+
+#define GVFS_TYPE_STORAGED_MOUNT  (gvfs_storaged_mount_get_type ())
+#define GVFS_STORAGED_MOUNT(o)    (G_TYPE_CHECK_INSTANCE_CAST ((o), GVFS_TYPE_STORAGED_MOUNT, 
GVfsStoragedMount))
+#define GVFS_IS_STORAGED_MOUNT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVFS_TYPE_STORAGED_MOUNT))
+
+
+GType             gvfs_storaged_mount_get_type       (void) G_GNUC_CONST;
+GVfsStoragedMount *gvfs_storaged_mount_new            (GVfsStoragedVolumeMonitor *monitor,
+                                                     GUnixMountEntry          *mount_entry,
+                                                     GVfsStoragedVolume        *volume);
+void              gvfs_storaged_mount_unmounted      (GVfsStoragedMount         *mount);
+
+gboolean          gvfs_storaged_mount_has_uuid       (GVfsStoragedMount         *mount,
+                                                     const gchar              *uuid);
+
+void              gvfs_storaged_mount_set_volume     (GVfsStoragedMount         *mount,
+                                                     GVfsStoragedVolume        *volume);
+void              gvfs_storaged_mount_unset_volume   (GVfsStoragedMount         *mount,
+                                                     GVfsStoragedVolume        *volume);
+gboolean          gvfs_storaged_mount_has_volume     (GVfsStoragedMount         *mount,
+                                                     GVfsStoragedVolume        *volume);
+GVfsStoragedVolume *gvfs_storaged_mount_get_volume    (GVfsStoragedMount         *mount);
+
+const gchar      *gvfs_storaged_mount_get_mount_path  (GVfsStoragedMount        *mount);
+GUnixMountEntry  *gvfs_storaged_mount_get_mount_entry (GVfsStoragedMount        *mount);
+
+G_END_DECLS
+
+#endif /* __GVFS_STORAGED_MOUNT_H__ */
diff --git a/monitor/storaged/gvfsstoragedutils.c b/monitor/storaged/gvfsstoragedutils.c
new file mode 100644
index 0000000..c7d7793
--- /dev/null
+++ b/monitor/storaged/gvfsstoragedutils.c
@@ -0,0 +1,828 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* gvfs - extensions for gio
+ *
+ * Copyright (C) 2006-2012 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., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#include <config.h>
+
+#include <string.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+#include <gio/gio.h>
+
+#include "gvfsstoragedutils.h"
+
+void
+gvfs_storaged_utils_storaged_error_to_gio_error (GError *error)
+{
+  g_return_if_fail (error != NULL);
+
+  if (error->domain == STORAGED_ERROR)
+    {
+      switch (error->code)
+        {
+        case STORAGED_ERROR_DEVICE_BUSY:
+          error->code = G_IO_ERROR_BUSY;
+          break;
+        case STORAGED_ERROR_NOT_AUTHORIZED_DISMISSED:
+          error->code = G_IO_ERROR_FAILED_HANDLED;
+          break;
+        default:
+          error->code = G_IO_ERROR_FAILED;
+          break;
+        }
+    }
+  else
+    {
+      error->code = G_IO_ERROR_FAILED;
+    }
+
+  error->domain = G_IO_ERROR;
+  g_dbus_error_strip_remote_error (error);
+}
+
+
+GIcon *
+gvfs_storaged_utils_icon_from_fs_type (const gchar *fs_type)
+{
+  const gchar *icon_name;
+  if (g_strcmp0 (fs_type, "nfs") == 0 ||
+      g_strcmp0 (fs_type, "nfs4") == 0 ||
+      g_strcmp0 (fs_type, "cifs") == 0)
+    {
+      icon_name = "folder-remote";
+    }
+  else
+    {
+      icon_name = "drive-removable-media";
+    }
+  return g_themed_icon_new_with_default_fallbacks (icon_name);
+}
+
+GIcon *
+gvfs_storaged_utils_symbolic_icon_from_fs_type (const gchar *fs_type)
+{
+  const gchar *icon_name;
+  if (g_strcmp0 (fs_type, "nfs") == 0 ||
+      g_strcmp0 (fs_type, "nfs4") == 0 ||
+      g_strcmp0 (fs_type, "cifs") == 0)
+    {
+      icon_name = "folder-remote-symbolic";
+    }
+  else
+    {
+      icon_name = "drive-removable-media-symbolic";
+    }
+  return g_themed_icon_new_with_default_fallbacks (icon_name);
+}
+
+gchar *
+gvfs_storaged_utils_lookup_fstab_options_value (const gchar *fstab_options,
+                                               const gchar *key)
+{
+  gchar *ret = NULL;
+
+  if (fstab_options != NULL)
+    {
+      const gchar *start;
+      guint n;
+
+      start = strstr (fstab_options, key);
+      if (start != NULL)
+        {
+          start += strlen (key);
+          for (n = 0; start[n] != ',' && start[n] != '\0'; n++)
+            ;
+          if (n == 0)
+            ret = g_strdup ("");
+          else if (n >= 1)
+            ret = g_uri_unescape_segment (start, start + n, NULL);
+        }
+    }
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+typedef struct
+{
+  GSimpleAsyncResult *simple; /* borrowed reference */
+  GMainContext *main_context; /* may be NULL */
+
+  gchar *command_line;
+
+  GCancellable *cancellable;  /* may be NULL */
+  gulong cancellable_handler_id;
+
+  GPid child_pid;
+  gint child_stdout_fd;
+  gint child_stderr_fd;
+
+  GIOChannel *child_stdout_channel;
+  GIOChannel *child_stderr_channel;
+
+  GSource *child_watch_source;
+  GSource *child_stdout_source;
+  GSource *child_stderr_source;
+
+  gboolean timed_out;
+  GSource *timeout_source;
+
+  GString *child_stdout;
+  GString *child_stderr;
+
+  gint exit_status;
+} SpawnData;
+
+static void
+child_watch_from_release_cb (GPid     pid,
+                             gint     status,
+                             gpointer user_data)
+{
+}
+
+static void
+spawn_data_free (SpawnData *data)
+{
+  if (data->timeout_source != NULL)
+    {
+      g_source_destroy (data->timeout_source);
+      data->timeout_source = NULL;
+    }
+
+  /* Nuke the child, if necessary */
+  if (data->child_watch_source != NULL)
+    {
+      g_source_destroy (data->child_watch_source);
+      data->child_watch_source = NULL;
+    }
+
+  if (data->child_pid != 0)
+    {
+      GSource *source;
+      kill (data->child_pid, SIGTERM);
+      /* OK, we need to reap for the child ourselves - we don't want
+       * to use waitpid() because that might block the calling
+       * thread (the child might handle SIGTERM and use several
+       * seconds for cleanup/rollback).
+       *
+       * So we use GChildWatch instead.
+       *
+       * Avoid taking a references to ourselves. but note that we need
+       * to pass the GSource so we can nuke it once handled.
+       */
+      source = g_child_watch_source_new (data->child_pid);
+      g_source_set_callback (source,
+                             (GSourceFunc) child_watch_from_release_cb,
+                             source,
+                             (GDestroyNotify) g_source_destroy);
+      g_source_attach (source, data->main_context);
+      g_source_unref (source);
+      data->child_pid = 0;
+    }
+
+  if (data->child_stdout != NULL)
+    {
+      g_string_free (data->child_stdout, TRUE);
+      data->child_stdout = NULL;
+    }
+
+  if (data->child_stderr != NULL)
+    {
+      g_string_free (data->child_stderr, TRUE);
+      data->child_stderr = NULL;
+    }
+
+  if (data->child_stdout_channel != NULL)
+    {
+      g_io_channel_unref (data->child_stdout_channel);
+      data->child_stdout_channel = NULL;
+    }
+  if (data->child_stderr_channel != NULL)
+    {
+      g_io_channel_unref (data->child_stderr_channel);
+      data->child_stderr_channel = NULL;
+    }
+
+  if (data->child_stdout_source != NULL)
+    {
+      g_source_destroy (data->child_stdout_source);
+      data->child_stdout_source = NULL;
+    }
+  if (data->child_stderr_source != NULL)
+    {
+      g_source_destroy (data->child_stderr_source);
+      data->child_stderr_source = NULL;
+    }
+
+  if (data->child_stdout_fd != -1)
+    {
+      g_warn_if_fail (close (data->child_stdout_fd) == 0);
+      data->child_stdout_fd = -1;
+    }
+  if (data->child_stderr_fd != -1)
+    {
+      g_warn_if_fail (close (data->child_stderr_fd) == 0);
+      data->child_stderr_fd = -1;
+    }
+
+  if (data->cancellable_handler_id > 0)
+    {
+      g_cancellable_disconnect (data->cancellable, data->cancellable_handler_id);
+      data->cancellable_handler_id = 0;
+    }
+
+  if (data->main_context != NULL)
+    g_main_context_unref (data->main_context);
+
+  if (data->cancellable != NULL)
+    g_object_unref (data->cancellable);
+
+  g_free (data->command_line);
+
+  g_slice_free (SpawnData, data);
+}
+
+/* called in the thread where @cancellable was cancelled */
+static void
+on_cancelled (GCancellable *cancellable,
+              gpointer      user_data)
+{
+  SpawnData *data = user_data;
+  GError *error;
+
+  error = NULL;
+  g_warn_if_fail (g_cancellable_set_error_if_cancelled (cancellable, &error));
+  g_simple_async_result_take_error (data->simple, error);
+  g_simple_async_result_complete_in_idle (data->simple);
+  g_object_unref (data->simple);
+}
+
+static gboolean
+read_child_stderr (GIOChannel *channel,
+                   GIOCondition condition,
+                   gpointer user_data)
+{
+  SpawnData *data = user_data;
+  gchar buf[1024];
+  gsize bytes_read;
+
+  g_io_channel_read_chars (channel, buf, sizeof buf, &bytes_read, NULL);
+  g_string_append_len (data->child_stderr, buf, bytes_read);
+  return TRUE;
+}
+
+static gboolean
+read_child_stdout (GIOChannel *channel,
+                   GIOCondition condition,
+                   gpointer user_data)
+{
+  SpawnData *data = user_data;
+  gchar buf[1024];
+  gsize bytes_read;
+
+  g_io_channel_read_chars (channel, buf, sizeof buf, &bytes_read, NULL);
+  g_string_append_len (data->child_stdout, buf, bytes_read);
+  return TRUE;
+}
+
+static void
+child_watch_cb (GPid     pid,
+                gint     status,
+                gpointer user_data)
+{
+  SpawnData *data = user_data;
+  gchar *buf;
+  gsize buf_size;
+
+  if (g_io_channel_read_to_end (data->child_stdout_channel, &buf, &buf_size, NULL) == G_IO_STATUS_NORMAL)
+    {
+      g_string_append_len (data->child_stdout, buf, buf_size);
+      g_free (buf);
+    }
+  if (g_io_channel_read_to_end (data->child_stderr_channel, &buf, &buf_size, NULL) == G_IO_STATUS_NORMAL)
+    {
+      g_string_append_len (data->child_stderr, buf, buf_size);
+      g_free (buf);
+    }
+
+  data->exit_status = status;
+
+  /* ok, child watch is history, make sure we don't free it in spawn_data_free() */
+  data->child_pid = 0;
+  data->child_watch_source = NULL;
+
+  /* we're done */
+  g_simple_async_result_complete_in_idle (data->simple);
+  g_object_unref (data->simple);
+}
+
+static gboolean
+timeout_cb (gpointer user_data)
+{
+  SpawnData *data = user_data;
+
+  data->timed_out = TRUE;
+
+  /* ok, timeout is history, make sure we don't free it in spawn_data_free() */
+  data->timeout_source = NULL;
+
+  /* we're done */
+  g_simple_async_result_complete_in_idle (data->simple);
+  g_object_unref (data->simple);
+
+  return FALSE; /* remove source */
+}
+
+void
+gvfs_storaged_utils_spawn (guint                timeout_seconds,
+                          GCancellable        *cancellable,
+                          GAsyncReadyCallback  callback,
+                          gpointer             user_data,
+                          const gchar         *command_line_format,
+                          ...)
+{
+  va_list var_args;
+  SpawnData *data;
+  GError *error;
+  gint child_argc;
+  gchar **child_argv = NULL;
+
+  data = g_slice_new0 (SpawnData);
+  data->simple = g_simple_async_result_new (NULL,
+                                            callback,
+                                            user_data,
+                                            gvfs_storaged_utils_spawn);
+  data->main_context = g_main_context_get_thread_default ();
+  if (data->main_context != NULL)
+    g_main_context_ref (data->main_context);
+
+  data->cancellable = cancellable != NULL ? g_object_ref (cancellable) : NULL;
+
+  va_start (var_args, command_line_format);
+  data->command_line = g_strdup_vprintf (command_line_format, var_args);
+  va_end (var_args);
+
+  data->child_stdout = g_string_new (NULL);
+  data->child_stderr = g_string_new (NULL);
+  data->child_stdout_fd = -1;
+  data->child_stderr_fd = -1;
+
+  /* the life-cycle of SpawnData is tied to its GSimpleAsyncResult */
+  g_simple_async_result_set_op_res_gpointer (data->simple, data, (GDestroyNotify) spawn_data_free);
+
+  error = NULL;
+  if (data->cancellable != NULL)
+    {
+      /* could already be cancelled */
+      error = NULL;
+      if (g_cancellable_set_error_if_cancelled (data->cancellable, &error))
+        {
+          g_simple_async_result_take_error (data->simple, error);
+          g_simple_async_result_complete_in_idle (data->simple);
+          g_object_unref (data->simple);
+          goto out;
+        }
+
+      data->cancellable_handler_id = g_cancellable_connect (data->cancellable,
+                                                            G_CALLBACK (on_cancelled),
+                                                            data,
+                                                            NULL);
+    }
+
+  error = NULL;
+  if (!g_shell_parse_argv (data->command_line,
+                           &child_argc,
+                           &child_argv,
+                           &error))
+    {
+      g_prefix_error (&error,
+                      "Error parsing command-line `%s': ",
+                      data->command_line);
+      g_simple_async_result_take_error (data->simple, error);
+      g_simple_async_result_complete_in_idle (data->simple);
+      g_object_unref (data->simple);
+      goto out;
+    }
+
+  error = NULL;
+  if (!g_spawn_async_with_pipes (NULL, /* working directory */
+                                 child_argv,
+                                 NULL, /* envp */
+                                 G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
+                                 NULL, /* child_setup */
+                                 NULL, /* child_setup's user_data */
+                                 &(data->child_pid),
+                                 NULL, /* gint *stdin_fd */
+                                 &(data->child_stdout_fd),
+                                 &(data->child_stderr_fd),
+                                 &error))
+    {
+      g_prefix_error (&error,
+                      "Error spawning command-line `%s': ",
+                      data->command_line);
+      g_simple_async_result_take_error (data->simple, error);
+      g_simple_async_result_complete_in_idle (data->simple);
+      g_object_unref (data->simple);
+      goto out;
+    }
+
+  if (timeout_seconds > 0)
+    {
+      data->timeout_source = g_timeout_source_new_seconds (timeout_seconds);
+      g_source_set_priority (data->timeout_source, G_PRIORITY_DEFAULT);
+      g_source_set_callback (data->timeout_source, timeout_cb, data, NULL);
+      g_source_attach (data->timeout_source, data->main_context);
+      g_source_unref (data->timeout_source);
+    }
+
+  data->child_watch_source = g_child_watch_source_new (data->child_pid);
+  g_source_set_callback (data->child_watch_source, (GSourceFunc) child_watch_cb, data, NULL);
+  g_source_attach (data->child_watch_source, data->main_context);
+  g_source_unref (data->child_watch_source);
+
+  data->child_stdout_channel = g_io_channel_unix_new (data->child_stdout_fd);
+  g_io_channel_set_flags (data->child_stdout_channel, G_IO_FLAG_NONBLOCK, NULL);
+  data->child_stdout_source = g_io_create_watch (data->child_stdout_channel, G_IO_IN);
+  g_source_set_callback (data->child_stdout_source, (GSourceFunc) read_child_stdout, data, NULL);
+  g_source_attach (data->child_stdout_source, data->main_context);
+  g_source_unref (data->child_stdout_source);
+
+  data->child_stderr_channel = g_io_channel_unix_new (data->child_stderr_fd);
+  g_io_channel_set_flags (data->child_stderr_channel, G_IO_FLAG_NONBLOCK, NULL);
+  data->child_stderr_source = g_io_create_watch (data->child_stderr_channel, G_IO_IN);
+  g_source_set_callback (data->child_stderr_source, (GSourceFunc) read_child_stderr, data, NULL);
+  g_source_attach (data->child_stderr_source, data->main_context);
+  g_source_unref (data->child_stderr_source);
+
+ out:
+  g_strfreev (child_argv);
+}
+
+gboolean
+gvfs_storaged_utils_spawn_finish (GAsyncResult   *res,
+                                 gint           *out_exit_status,
+                                 gchar         **out_standard_output,
+                                 gchar         **out_standard_error,
+                                 GError        **error)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+  SpawnData *data;
+  gboolean ret = FALSE;
+
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (res), FALSE);
+  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+  g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == gvfs_storaged_utils_spawn);
+
+  if (g_simple_async_result_propagate_error (simple, error))
+    goto out;
+
+  data = g_simple_async_result_get_op_res_gpointer (simple);
+
+  if (data->timed_out)
+    {
+      g_set_error (error,
+                   G_IO_ERROR,
+                   G_IO_ERROR_TIMED_OUT,
+                   _("Timed out running command-line `%s'"),
+                   data->command_line);
+      goto out;
+    }
+
+  if (out_exit_status != NULL)
+    *out_exit_status = data->exit_status;
+
+  if (out_standard_output != NULL)
+    *out_standard_output = g_strdup (data->child_stdout->str);
+
+  if (out_standard_error != NULL)
+    *out_standard_error = g_strdup (data->child_stderr->str);
+
+  ret = TRUE;
+
+ out:
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+#if defined(HAVE_LIBSYSTEMD_LOGIN)
+#include <systemd/sd-login.h>
+
+static const gchar *
+get_seat (void)
+{
+  static gsize once = 0;
+  static char *seat = NULL;
+
+  if (g_once_init_enter (&once))
+    {
+      char *session = NULL;
+      if (sd_pid_get_session (getpid (), &session) == 0)
+        {
+          sd_session_get_seat (session, &seat);
+          free (session);
+          /* we intentionally leak seat here... */
+        }
+      g_once_init_leave (&once, (gsize) 1);
+    }
+  return seat;
+}
+
+#else
+
+static const gchar *
+get_seat (void)
+{
+  return NULL;
+}
+
+#endif
+
+gboolean
+gvfs_storaged_utils_is_drive_on_our_seat (StoragedDrive *drive)
+{
+  gboolean ret = FALSE;
+  const gchar *seat;
+  const gchar *drive_seat = NULL;
+
+  /* assume our own seat if we don't have seat-support or it doesn't work */
+  seat = get_seat ();
+  if (seat == NULL)
+    {
+      ret = TRUE;
+      goto out;
+    }
+
+  /* If the device is not tagged, assume that udisks does not have
+   * working seat-support... so just assume it's available at our
+   * seat.
+   *
+   * Note that seat support was added in udisks 1.95.0 (and so was the
+   * STORAGED_CHECK_VERSION macro).
+   */
+  drive_seat = storaged_drive_get_seat (drive);
+
+  if (drive_seat == NULL || strlen (drive_seat) == 0)
+    {
+      ret = TRUE;
+      goto out;
+    }
+
+  /* Otherwise, check if it's on our seat */
+  if (g_strcmp0 (seat, drive_seat) == 0)
+    ret = TRUE;
+
+ out:
+  return ret;
+}
+
+/* unmount progress notification utilities */
+typedef struct {
+  GMount *mount;
+  GDrive *drive;
+
+  GMountOperation *op;
+  gboolean op_aborted;
+  gboolean generic_text;
+  gboolean show_processes_up;
+
+  guint unmount_timer_id;
+  gboolean unmount_fired;
+} UnmountNotifyData;
+
+static gboolean
+unmount_notify_should_show (UnmountNotifyData *data)
+{
+  GVolume *volume;
+  gchar *identifier = NULL;
+  gboolean retval = TRUE;
+
+  if (data->mount)
+    {
+      volume = g_mount_get_volume (data->mount);
+
+      if (volume)
+        {
+          identifier = g_volume_get_identifier (volume, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
+          g_object_unref (volume);
+        }
+    }
+  else if (data->drive)
+    {
+      identifier = g_drive_get_identifier (data->drive, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
+    }
+
+  if (identifier && g_str_has_prefix (identifier, "/dev/sr"))
+    retval = FALSE;
+
+  g_free (identifier);
+
+  return retval;
+}
+
+static gchar *
+unmount_notify_get_name (UnmountNotifyData *data)
+{
+  if (data->mount)
+    return g_mount_get_name (data->mount);
+  else
+    return g_drive_get_name (data->drive);
+}
+
+static gboolean
+unmount_notify_timer_cb (gpointer user_data)
+{
+  UnmountNotifyData *data = user_data;
+  gchar *message, *name;
+  const gchar *format;
+
+  data->unmount_timer_id = 0;
+
+  if (data->unmount_fired)
+    goto out;
+
+  /* TODO: it would be nice to include and update the time left and
+   * bytes left fields.
+   */
+  data->unmount_fired = TRUE;
+
+  name = unmount_notify_get_name (data);
+  format = data->generic_text ?
+    _("Unmounting %s\nPlease wait") :
+    _("Writing data to %s\nDon't unplug until finished");
+
+  message = g_strdup_printf (format, name);
+  g_signal_emit_by_name (data->op, "show-unmount-progress",
+                         message, -1, -1);
+  g_free (message);
+  g_free (name);
+
+ out:
+  return FALSE;
+}
+
+static void
+unmount_notify_ensure_timer (UnmountNotifyData *data)
+{
+  if (data->unmount_timer_id > 0)
+    return;
+
+  if (!unmount_notify_should_show (data))
+    return;
+
+  data->unmount_timer_id = 
+    g_timeout_add (1500, unmount_notify_timer_cb, data);
+}
+
+static void
+unmount_notify_stop_timer (UnmountNotifyData *data)
+{
+  if (data->unmount_timer_id > 0)
+    {
+      g_source_remove (data->unmount_timer_id);
+      data->unmount_timer_id = 0;
+    }
+}
+
+static void
+unmount_notify_op_show_processes (UnmountNotifyData *data)
+{
+  unmount_notify_stop_timer (data);
+  data->show_processes_up = TRUE;
+}
+
+static void
+unmount_notify_op_aborted (UnmountNotifyData *data)
+{
+  unmount_notify_stop_timer (data);
+  data->op_aborted = TRUE;
+}
+
+static void
+unmount_notify_op_reply (UnmountNotifyData *data,
+                         GMountOperationResult result)
+{
+  gint choice;
+
+  choice = g_mount_operation_get_choice (data->op);
+
+  if ((result == G_MOUNT_OPERATION_HANDLED && data->show_processes_up && choice == 1) ||
+      result == G_MOUNT_OPERATION_ABORTED)
+    unmount_notify_op_aborted (data);
+  else if (result == G_MOUNT_OPERATION_HANDLED)
+    unmount_notify_ensure_timer (data);
+
+  data->show_processes_up = FALSE;
+}
+
+static void
+unmount_notify_data_free (gpointer user_data)
+{
+  UnmountNotifyData *data = user_data;
+
+  unmount_notify_stop_timer (data);
+
+  g_clear_object (&data->mount);
+  g_clear_object (&data->drive);
+
+  g_slice_free (UnmountNotifyData, data);
+}
+
+static UnmountNotifyData *
+unmount_notify_data_for_operation (GMountOperation *op,
+                                   GMount          *mount,
+                                   GDrive          *drive,
+                                   gboolean         generic_text)
+{
+  UnmountNotifyData *data;
+
+  data = g_object_get_data (G_OBJECT (op), "x-storaged-notify-data");
+  if (data != NULL)
+    return data;
+
+  data = g_slice_new0 (UnmountNotifyData);
+  data->op = op;
+  data->generic_text = generic_text;
+
+  if (mount)
+    data->mount = g_object_ref (mount);
+  if (drive)
+    data->drive = g_object_ref (drive);
+
+  g_object_set_data_full (G_OBJECT (data->op),
+                          "x-storaged-notify-data", data, 
+                          unmount_notify_data_free);
+
+  g_signal_connect_swapped (data->op, "aborted",
+                            G_CALLBACK (unmount_notify_op_aborted), data);
+  g_signal_connect_swapped (data->op, "show-processes",
+                            G_CALLBACK (unmount_notify_op_show_processes), data);
+  g_signal_connect_swapped (data->op, "reply",
+                            G_CALLBACK (unmount_notify_op_reply), data);
+
+  return data;
+}
+
+void
+gvfs_storaged_unmount_notify_start (GMountOperation *op,
+                                   GMount          *mount,
+                                   GDrive          *drive,
+                                   gboolean         generic_text)
+{
+  UnmountNotifyData *data;
+
+  data = unmount_notify_data_for_operation (op, mount, drive, generic_text);
+  unmount_notify_ensure_timer (data);
+}
+
+void
+gvfs_storaged_unmount_notify_stop (GMountOperation *op)
+{
+  gchar *message, *name;
+  const gchar *format;
+  UnmountNotifyData *data = g_object_get_data (G_OBJECT (op), "x-storaged-notify-data");
+
+  if (data == NULL)
+    return;
+
+  unmount_notify_stop_timer (data);
+
+  if (data->op_aborted)
+    return;
+
+  name = unmount_notify_get_name (data);
+  format = data->generic_text ?
+    _("%s has been unmounted\n") : _("You can now unplug %s\n");
+
+  message = g_strdup_printf (format, name);
+  g_signal_emit_by_name (data->op, "show-unmount-progress",
+                         message, 0, 0);
+
+  g_free (message);
+  g_free (name);
+}
diff --git a/monitor/storaged/gvfsstoragedutils.h b/monitor/storaged/gvfsstoragedutils.h
new file mode 100644
index 0000000..fdd0d40
--- /dev/null
+++ b/monitor/storaged/gvfsstoragedutils.h
@@ -0,0 +1,64 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* gvfs - extensions for gio
+ *
+ * Copyright (C) 2006-2012 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., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#ifndef __GVFS_STORAGED_UTILS_H__
+#define __GVFS_STORAGED_UTILS_H__
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include "gvfsstoragedvolumemonitor.h"
+
+G_BEGIN_DECLS
+
+void   gvfs_storaged_utils_storaged_error_to_gio_error (GError *error);
+GIcon *gvfs_storaged_utils_icon_from_fs_type (const gchar *fs_type);
+GIcon *gvfs_storaged_utils_symbolic_icon_from_fs_type (const gchar *fs_type);
+
+gchar *gvfs_storaged_utils_lookup_fstab_options_value (const gchar *fstab_options,
+                                                      const gchar *key);
+
+void     gvfs_storaged_utils_spawn (guint                timeout_seconds,
+                                   GCancellable        *cancellable,
+                                   GAsyncReadyCallback  callback,
+                                   gpointer             user_data,
+                                   const gchar         *command_line_format,
+                                   ...);
+
+gboolean gvfs_storaged_utils_spawn_finish (GAsyncResult   *res,
+                                          gint           *out_exit_status,
+                                          gchar         **out_standard_output,
+                                          gchar         **out_standard_error,
+                                          GError        **error);
+
+gboolean gvfs_storaged_utils_is_drive_on_our_seat (StoragedDrive *drive);
+
+void     gvfs_storaged_unmount_notify_start (GMountOperation *op,
+                                            GMount          *mount,
+                                            GDrive          *drive,
+                                            gboolean         generic_text);
+void     gvfs_storaged_unmount_notify_stop  (GMountOperation *op);
+
+G_END_DECLS
+
+#endif /* __GVFS_STORAGED_UTILS_H__ */
diff --git a/monitor/storaged/gvfsstoragedvolume.c b/monitor/storaged/gvfsstoragedvolume.c
new file mode 100644
index 0000000..d29a5b5
--- /dev/null
+++ b/monitor/storaged/gvfsstoragedvolume.c
@@ -0,0 +1,1786 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* gvfs - extensions for gio
+ *
+ * Copyright (C) 2006-2012 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., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, 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>
+
+#ifdef HAVE_KEYRING
+#define SECRET_API_SUBJECT_TO_CHANGE
+#include <libsecret/secret.h>
+#endif
+
+#include "gvfsstorageddrive.h"
+#include "gvfsstoragedvolume.h"
+#include "gvfsstoragedmount.h"
+#include "gvfsstoragedutils.h"
+
+typedef struct _GVfsStoragedVolumeClass GVfsStoragedVolumeClass;
+
+struct _GVfsStoragedVolumeClass
+{
+  GObjectClass parent_class;
+};
+
+struct MountData;
+typedef struct MountData MountData;
+
+static void mount_cancel_pending_op (MountData *data);
+
+struct _GVfsStoragedVolume
+{
+  GObject parent;
+
+  GVfsStoragedVolumeMonitor *monitor; /* owned by volume monitor */
+  GVfsStoragedMount         *mount;   /* owned by volume monitor */
+  GVfsStoragedDrive         *drive;   /* owned by volume monitor */
+
+  /* If TRUE, the volume was discovered at coldplug time */
+  gboolean coldplug;
+
+  /* exactly one of these are set */
+  StoragedBlock *block;
+  GUnixMountPoint *mount_point;
+
+  /* set in update_volume() */
+  GIcon *icon;
+  GIcon *symbolic_icon;
+  GFile *activation_root;
+  gchar *name;
+  gchar *sort_key;
+  gchar *device_file;
+  dev_t dev;
+  gchar *uuid;
+  gboolean can_mount;
+  gboolean should_automount;
+
+  /* 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.
+   */
+  MountData *mount_pending_op;
+};
+
+static void gvfs_storaged_volume_volume_iface_init (GVolumeIface *iface);
+
+static void on_block_changed (GObject    *object,
+                              GParamSpec *pspec,
+                              gpointer    user_data);
+
+static void on_storaged_client_changed (StoragedClient *client,
+                                      gpointer      user_data);
+
+G_DEFINE_TYPE_EXTENDED (GVfsStoragedVolume, gvfs_storaged_volume, G_TYPE_OBJECT, 0,
+                        G_IMPLEMENT_INTERFACE (G_TYPE_VOLUME, gvfs_storaged_volume_volume_iface_init))
+
+static void
+gvfs_storaged_volume_finalize (GObject *object)
+{
+  GVfsStoragedVolume *volume = GVFS_STORAGED_VOLUME (object);
+
+  g_signal_handlers_disconnect_by_func (gvfs_storaged_volume_monitor_get_storaged_client (volume->monitor),
+                                        G_CALLBACK (on_storaged_client_changed),
+                                        volume);
+
+  if (volume->mount != NULL)
+    {
+      gvfs_storaged_mount_unset_volume (volume->mount, volume);
+    }
+
+  if (volume->drive != NULL)
+    {
+      gvfs_storaged_drive_unset_volume (volume->drive, volume);
+    }
+
+  if (volume->block != NULL)
+    {
+      g_signal_handlers_disconnect_by_func (volume->block, G_CALLBACK (on_block_changed), volume);
+      g_object_unref (volume->block);
+    }
+
+  if (volume->mount_point != NULL)
+    g_unix_mount_point_free (volume->mount_point);
+
+  g_clear_object (&volume->icon);
+  g_clear_object (&volume->symbolic_icon);
+  g_clear_object (&volume->activation_root);
+
+  g_free (volume->name);
+  g_free (volume->sort_key);
+  g_free (volume->device_file);
+  g_free (volume->uuid);
+
+  G_OBJECT_CLASS (gvfs_storaged_volume_parent_class)->finalize (object);
+}
+
+static void
+gvfs_storaged_volume_class_init (GVfsStoragedVolumeClass *klass)
+{
+  GObjectClass *gobject_class;
+
+  gobject_class = G_OBJECT_CLASS (klass);
+  gobject_class->finalize = gvfs_storaged_volume_finalize;
+}
+
+static void
+gvfs_storaged_volume_init (GVfsStoragedVolume *volume)
+{
+}
+
+static void
+emit_changed (GVfsStoragedVolume *volume)
+{
+  g_signal_emit_by_name (volume, "changed");
+  g_signal_emit_by_name (volume->monitor, "volume-changed", volume);
+}
+
+static void
+apply_options_from_fstab (GVfsStoragedVolume *volume,
+                          const gchar       *fstab_options)
+{
+  gchar *name;
+  gchar *icon_name;
+  gchar *symbolic_icon_name;
+
+  name = gvfs_storaged_utils_lookup_fstab_options_value (fstab_options, "x-gvfs-name=");
+  if (name != NULL)
+    {
+      g_free (volume->name);
+      volume->name = name;
+    }
+
+  icon_name = gvfs_storaged_utils_lookup_fstab_options_value (fstab_options, "x-gvfs-icon=");
+  if (icon_name != NULL)
+    {
+      g_clear_object (&volume->icon);
+      volume->icon = g_themed_icon_new_with_default_fallbacks (icon_name);
+      g_free (icon_name);
+    }
+
+  symbolic_icon_name = gvfs_storaged_utils_lookup_fstab_options_value (fstab_options, 
"x-gvfs-symbolic-icon=");
+  if (symbolic_icon_name != NULL)
+    {
+      g_clear_object (&volume->symbolic_icon);
+      volume->symbolic_icon = g_themed_icon_new_with_default_fallbacks (symbolic_icon_name);
+      g_free (symbolic_icon_name);
+    }
+}
+
+
+#if STORAGED_CHECK_VERSION(2,0,90)
+static gpointer
+_g_object_ref0 (gpointer object)
+{
+  if (object != NULL)
+    return g_object_ref (G_OBJECT (object));
+  else
+    return NULL;
+}
+#endif
+
+static gboolean
+update_volume (GVfsStoragedVolume *volume)
+{
+  StoragedClient *storaged_client;
+  gboolean changed;
+  gboolean old_can_mount;
+  gboolean old_should_automount;
+  gchar *old_name;
+  gchar *old_device_file;
+  dev_t old_dev;
+  GIcon *old_icon;
+  StoragedDrive *storaged_drive;
+  gchar *s;
+
+  storaged_client = gvfs_storaged_volume_monitor_get_storaged_client (volume->monitor);
+
+  /* ---------------------------------------------------------------------------------------------------- */
+  /* 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_dev = volume->dev;
+  old_icon = volume->icon != NULL ? g_object_ref (volume->icon) : NULL;
+
+  /* ---------------------------------------------------------------------------------------------------- */
+  /* reset */
+
+  volume->can_mount = volume->should_automount = FALSE;
+  g_free (volume->name); volume->name = NULL;
+  g_free (volume->device_file); volume->device_file = NULL;
+  volume->dev = 0;
+  g_clear_object (&volume->icon);
+  g_clear_object (&volume->symbolic_icon);
+
+  /* ---------------------------------------------------------------------------------------------------- */
+  /* in with the new */
+
+  if (volume->block != NULL)
+    {
+      const gchar *hint;
+      StoragedBlock *block;
+      StoragedBlock *cleartext_block;
+      GVariantIter iter;
+      const gchar *configuration_type;
+      GVariant *configuration_value;
+      StoragedLoop *loop = NULL;
+
+      loop = storaged_client_get_loop_for_block (storaged_client,
+                                               volume->block);
+
+      /* If unlocked, use the values from the unlocked block device for presentation */
+      cleartext_block = storaged_client_get_cleartext_block (storaged_client, volume->block);
+      if (cleartext_block != NULL)
+        block = cleartext_block;
+      else
+        block = volume->block;
+
+      volume->dev = storaged_block_get_device_number (block);
+      volume->device_file = storaged_block_dup_device (block);
+
+      if (strlen (storaged_block_get_id_label (block)) > 0)
+        {
+          volume->name = g_strdup (storaged_block_get_id_label (block));
+        }
+      else if (g_strcmp0 (storaged_block_get_id_type (block), "crypto_LUKS") == 0)
+        {
+          s = storaged_client_get_size_for_display (storaged_client, storaged_block_get_size 
(volume->block), FALSE, FALSE);
+          /* Translators: This is used for encrypted volumes.
+           *              The first %s is the formatted size (e.g. "42.0 MB").
+           */
+          volume->name = g_strdup_printf (_("%s Encrypted"), s);
+          g_free (s);
+        }
+      else
+        {
+          guint64 size = storaged_block_get_size (block);
+          if (size > 0)
+            {
+              s = storaged_client_get_size_for_display (storaged_client, size, FALSE, FALSE);
+              /* Translators: This is used for volume with no filesystem label.
+               *              The first %s is the formatted size (e.g. "42.0 MB").
+               */
+              volume->name = g_strdup_printf (_("%s Volume"), s);
+              g_free (s);
+            }
+        }
+
+      storaged_drive = storaged_client_get_drive_for_block (storaged_client, volume->block);
+      if (storaged_drive != NULL)
+        {
+          gchar *drive_desc = NULL;
+          GIcon *drive_icon = NULL;
+          GIcon *drive_symbolic_icon = NULL;
+          gchar *media_desc = NULL;
+          GIcon *media_icon = NULL;
+          GIcon *media_symbolic_icon = NULL;
+
+#if STORAGED_CHECK_VERSION(2,0,90)
+          {
+            StoragedObject *object = (StoragedObject *) g_dbus_interface_get_object (G_DBUS_INTERFACE 
(storaged_drive));
+            if (object != NULL)
+              {
+                StoragedObjectInfo *info = storaged_client_get_object_info (storaged_client, object);
+                if (info != NULL)
+                  {
+                    drive_desc = g_strdup (storaged_object_info_get_description (info));
+                    drive_icon = _g_object_ref0 (storaged_object_info_get_icon (info));
+                    drive_symbolic_icon = _g_object_ref0 (storaged_object_info_get_icon_symbolic (info));
+                    media_desc = g_strdup (storaged_object_info_get_media_description (info));
+                    media_icon = _g_object_ref0 (storaged_object_info_get_media_icon (info));
+                    media_symbolic_icon = _g_object_ref0 (storaged_object_info_get_media_icon_symbolic 
(info));
+                    g_object_unref (info);
+                  }
+              }
+          }
+#else
+          storaged_client_get_drive_info (storaged_client,
+                                        storaged_drive,
+                                        NULL, /* drive_name */
+                                        &drive_desc,
+                                        &drive_icon,
+                                        &media_desc,
+                                        &media_icon);
+#endif
+
+          if (media_desc == NULL)
+            {
+              media_desc = drive_desc;
+              drive_desc = NULL;
+            }
+          if (media_icon == NULL)
+            {
+              media_icon = drive_icon;
+              drive_icon = NULL;
+            }
+          if (media_symbolic_icon == NULL)
+            {
+              media_symbolic_icon = drive_symbolic_icon;
+              drive_symbolic_icon = NULL;
+            }
+
+          /* Override name for blank and audio discs */
+          if (storaged_drive_get_optical_blank (storaged_drive))
+            {
+              g_free (volume->name);
+              volume->name = g_strdup (media_desc);
+            }
+          else if (volume->activation_root != NULL && g_file_has_uri_scheme (volume->activation_root, 
"cdda"))
+            {
+              g_free (volume->name);
+              volume->name = g_strdup (_("Audio Disc"));
+            }
+
+          volume->icon = media_icon != NULL ? g_object_ref (media_icon) : NULL;
+          volume->symbolic_icon = media_symbolic_icon != NULL ? g_object_ref (media_symbolic_icon) : NULL;
+
+          /* use media_desc if we haven't figured out a name yet (applies to e.g.
+           * /dev/fd0 since its size is 0)
+           */
+          if (volume->name == NULL)
+            {
+              volume->name = media_desc;
+              media_desc = NULL;
+            }
+
+          g_free (media_desc);
+          g_clear_object (&media_icon);
+          g_clear_object (&media_symbolic_icon);
+
+          /* Only automount drives attached to the same seat as we're running on
+           */
+          if (gvfs_storaged_utils_is_drive_on_our_seat (storaged_drive))
+            {
+              /* Only automount filesystems from drives of known types/interconnects:
+               *
+               *  - USB
+               *  - Firewire
+               *  - sdio
+               *  - optical discs
+               *
+               * The mantra here is "be careful" - we really don't want to
+               * automount filesystems from all devices in a SAN etc - We
+               * REALLY need to be CAREFUL here.
+               *
+               * Fortunately udisks provides a property just for this.
+               */
+              if (storaged_block_get_hint_auto (volume->block))
+                {
+                  gboolean just_plugged_in = FALSE;
+                  /* Also, if a volume (partition) appear _much later_ than when media was inserted it
+                   * can only be because the media was repartitioned. We don't want to automount
+                   * such volumes. So only mark volumes appearing just after their drive.
+                   *
+                   * There's a catch here - if the volume was discovered at coldplug-time (typically
+                   * when the user desktop session started), we can't use this heuristic
+                   */
+                  if (g_get_real_time () - storaged_drive_get_time_media_detected (storaged_drive) < 5 * 
G_USEC_PER_SEC)
+                    just_plugged_in = TRUE;
+                  if (volume->coldplug || just_plugged_in)
+                    volume->should_automount = TRUE;
+                }
+            }
+          g_object_unref (storaged_drive);
+        }
+      else
+        {
+          /* No StoragedDrive, but we do have a StoragedBlock (example: /dev/loop0). Use
+           * that to get the icons via StoragedObjectInfo
+           */
+#if STORAGED_CHECK_VERSION(2,0,90)
+          {
+            StoragedObject *object = (StoragedObject *) g_dbus_interface_get_object (G_DBUS_INTERFACE 
(volume->block));
+            if (object != NULL)
+              {
+                StoragedObjectInfo *info = storaged_client_get_object_info (storaged_client, object);
+                if (info != NULL)
+                  {
+                    volume->icon = _g_object_ref0 (storaged_object_info_get_icon (info));
+                    volume->symbolic_icon = _g_object_ref0 (storaged_object_info_get_icon_symbolic (info));
+                    g_object_unref (info);
+                  }
+              }
+          }
+#endif
+        }
+
+      /* Also automount loop devices set up by the user himself - e.g. via the
+       * udisks interfaces or the gnome-disk-image-mounter(1) command
+       */
+      if (loop != NULL)
+        {
+          if (storaged_loop_get_setup_by_uid (loop) == getuid ())
+            {
+              volume->should_automount = TRUE;
+            }
+        }
+
+      /* Use hints, if available */
+      hint = storaged_block_get_hint_name (volume->block);
+      if (hint != NULL && strlen (hint) > 0)
+        {
+          g_free (volume->name);
+          volume->name = g_strdup (hint);
+        }
+      hint = storaged_block_get_hint_icon_name (volume->block);
+      if (hint != NULL && strlen (hint) > 0)
+        {
+          g_clear_object (&volume->icon);
+          volume->icon = g_themed_icon_new_with_default_fallbacks (hint);
+        }
+#if STORAGED_CHECK_VERSION(2,0,90)
+      hint = storaged_block_get_hint_symbolic_icon_name (volume->block);
+      if (hint != NULL && strlen (hint) > 0)
+        {
+          g_clear_object (&volume->symbolic_icon);
+          volume->symbolic_icon = g_themed_icon_new_with_default_fallbacks (hint);
+        }
+#endif
+
+      /* Use x-gvfs-name=The%20Name, x-gvfs-icon=foo-name, x-gvfs-icon=foo-name-symbolic, if available */
+      g_variant_iter_init (&iter, storaged_block_get_configuration (block));
+      while (g_variant_iter_next (&iter, "(&s a{sv})", &configuration_type, &configuration_value))
+        {
+          if (g_strcmp0 (configuration_type, "fstab") == 0)
+            {
+              const gchar *fstab_options;
+              if (g_variant_lookup (configuration_value, "opts", "^&ay", &fstab_options))
+                apply_options_from_fstab (volume, fstab_options);
+            }
+          g_variant_unref (configuration_value);
+        }
+
+      /* Add an emblem, depending on whether the encrypted volume is locked or unlocked */
+      if (g_strcmp0 (storaged_block_get_id_type (volume->block), "crypto_LUKS") == 0)
+        {
+          GEmblem *emblem;
+          GIcon *padlock;
+          GIcon *emblemed_icon;
+
+          if (volume->icon == NULL)
+            volume->icon = g_themed_icon_new ("drive-removable-media");
+
+          if (cleartext_block != NULL)
+            padlock = g_themed_icon_new ("changes-allow");
+          else
+            padlock = g_themed_icon_new ("changes-prevent");
+          emblem = g_emblem_new_with_origin (padlock, G_EMBLEM_ORIGIN_DEVICE);
+          emblemed_icon = g_emblemed_icon_new (volume->icon, emblem);
+          g_object_unref (padlock);
+          g_object_unref (emblem);
+
+          g_object_unref (volume->icon);
+          volume->icon = emblemed_icon;
+        }
+
+      g_clear_object (&cleartext_block);
+      g_clear_object (&loop);
+    }
+  else
+    {
+      apply_options_from_fstab (volume, g_unix_mount_point_get_options (volume->mount_point));
+      if (volume->name == NULL)
+        volume->name = g_unix_mount_point_guess_name (volume->mount_point);
+      if (volume->icon == NULL)
+        volume->icon = gvfs_storaged_utils_icon_from_fs_type (g_unix_mount_point_get_fs_type 
(volume->mount_point));
+      if (volume->symbolic_icon == NULL)
+        volume->symbolic_icon = gvfs_storaged_utils_symbolic_icon_from_fs_type 
(g_unix_mount_point_get_fs_type (volume->mount_point));
+    }
+
+  if (volume->mount == NULL)
+    volume->can_mount = TRUE;
+
+  /* ---------------------------------------------------------------------------------------------------- */
+  /* fallbacks */
+
+  if (volume->name == NULL)
+    {
+      /* Translators: Name used for volume */
+      volume->name = g_strdup (_("Volume"));
+    }
+  if (volume->icon == NULL)
+    volume->icon = g_themed_icon_new ("drive-removable-media");
+  if (volume->symbolic_icon == NULL)
+    volume->symbolic_icon = g_themed_icon_new ("drive-removable-media-symbolic");
+
+  /* ---------------------------------------------------------------------------------------------------- */
+  /* 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) &&
+              (old_dev == volume->dev) &&
+              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);
+
+  return changed;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+update_volume_on_event (GVfsStoragedVolume *volume)
+{
+  if (update_volume (volume))
+    {
+      emit_changed (volume);
+      /* It could be that volume->dev changed (cryptotext volume morphing into a cleartext
+       * volume)... since this is used to associated mounts with volumes (see the loop over
+       * @unchanged in gvfsstoragedvolumemonitor.c:update_mounts()) we need to over
+       * the volume monitor
+       */
+      gvfs_storaged_volume_monitor_update (volume->monitor);
+    }
+}
+
+static void
+on_block_changed (GObject    *object,
+                  GParamSpec *pspec,
+                  gpointer    user_data)
+{
+  GVfsStoragedVolume *volume = GVFS_STORAGED_VOLUME (user_data);
+  update_volume_on_event (volume);
+}
+
+static void
+on_storaged_client_changed (StoragedClient *client,
+                          gpointer      user_data)
+{
+  GVfsStoragedVolume *volume = GVFS_STORAGED_VOLUME (user_data);
+  update_volume_on_event (volume);
+}
+
+/* takes ownership of @mount_point if not NULL */
+GVfsStoragedVolume *
+gvfs_storaged_volume_new (GVfsStoragedVolumeMonitor   *monitor,
+                         StoragedBlock                *block,
+                         GUnixMountPoint            *mount_point,
+                         GVfsStoragedDrive           *drive,
+                         GFile                      *activation_root,
+                         gboolean                    coldplug)
+{
+  GVfsStoragedVolume *volume;
+
+  volume = g_object_new (GVFS_TYPE_STORAGED_VOLUME, NULL);
+  volume->monitor = monitor;
+  volume->coldplug = coldplug;
+
+  volume->sort_key = g_strdup_printf ("gvfs.time_detected_usec.%" G_GINT64_FORMAT, g_get_real_time ());
+
+  if (block != NULL)
+    {
+      volume->block = g_object_ref (block);
+      g_signal_connect (volume->block, "notify", G_CALLBACK (on_block_changed), volume);
+    }
+  else if (mount_point != NULL)
+    {
+      volume->mount_point = mount_point;
+    }
+  else
+    {
+      g_assert_not_reached ();
+    }
+
+  volume->activation_root = activation_root != NULL ? g_object_ref (activation_root) : NULL;
+
+  volume->drive = drive;
+  if (drive != NULL)
+    gvfs_storaged_drive_set_volume (drive, volume);
+
+  update_volume (volume);
+
+  /* For LUKS devices, we also need to listen for changes on any possible cleartext device */
+  if (volume->block != NULL && g_strcmp0 (storaged_block_get_id_type (volume->block), "crypto_LUKS") == 0)
+    {
+      g_signal_connect (gvfs_storaged_volume_monitor_get_storaged_client (volume->monitor),
+                        "changed",
+                        G_CALLBACK (on_storaged_client_changed),
+                        volume);
+    }
+
+  return volume;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+void
+gvfs_storaged_volume_removed (GVfsStoragedVolume *volume)
+{
+  if (volume->mount_pending_op != NULL)
+    mount_cancel_pending_op (volume->mount_pending_op);
+
+  if (volume->mount != NULL)
+    {
+      gvfs_storaged_mount_unset_volume (volume->mount, volume);
+      volume->mount = NULL;
+    }
+
+  if (volume->drive != NULL)
+    {
+      gvfs_storaged_drive_unset_volume (volume->drive, volume);
+      volume->drive = NULL;
+    }
+}
+
+void
+gvfs_storaged_volume_set_mount (GVfsStoragedVolume *volume,
+                               GVfsStoragedMount  *mount)
+{
+  if (volume->mount != mount)
+    {
+      if (volume->mount != NULL)
+        gvfs_storaged_mount_unset_volume (volume->mount, volume);
+
+      volume->mount = mount;
+
+      emit_changed (volume);
+    }
+}
+
+void
+gvfs_storaged_volume_unset_mount (GVfsStoragedVolume *volume,
+                                 GVfsStoragedMount  *mount)
+{
+  if (volume->mount == mount)
+    {
+      volume->mount = NULL;
+      emit_changed (volume);
+    }
+}
+
+void
+gvfs_storaged_volume_set_drive (GVfsStoragedVolume *volume,
+                               GVfsStoragedDrive  *drive)
+{
+  if (volume->drive != drive)
+    {
+      if (volume->drive != NULL)
+        gvfs_storaged_drive_unset_volume (volume->drive, volume);
+      volume->drive = drive;
+      emit_changed (volume);
+    }
+}
+
+void
+gvfs_storaged_volume_unset_drive (GVfsStoragedVolume *volume,
+                                 GVfsStoragedDrive  *drive)
+{
+  if (volume->drive == drive)
+    {
+      volume->drive = NULL;
+      emit_changed (volume);
+    }
+}
+
+static GIcon *
+gvfs_storaged_volume_get_icon (GVolume *_volume)
+{
+  GVfsStoragedVolume *volume = GVFS_STORAGED_VOLUME (_volume);
+  return volume->icon != NULL ? g_object_ref (volume->icon) : NULL;
+}
+
+static GIcon *
+gvfs_storaged_volume_get_symbolic_icon (GVolume *_volume)
+{
+  GVfsStoragedVolume *volume = GVFS_STORAGED_VOLUME (_volume);
+  return volume->symbolic_icon != NULL ? g_object_ref (volume->symbolic_icon) : NULL;
+}
+
+static char *
+gvfs_storaged_volume_get_name (GVolume *_volume)
+{
+  GVfsStoragedVolume *volume = GVFS_STORAGED_VOLUME (_volume);
+  return g_strdup (volume->name);
+}
+
+static char *
+gvfs_storaged_volume_get_uuid (GVolume *_volume)
+{
+  GVfsStoragedVolume *volume = GVFS_STORAGED_VOLUME (_volume);
+  return g_strdup (volume->uuid);
+}
+
+static gboolean
+gvfs_storaged_volume_can_mount (GVolume *_volume)
+{
+  GVfsStoragedVolume *volume = GVFS_STORAGED_VOLUME (_volume);
+  return volume->can_mount;
+}
+
+static gboolean
+gvfs_storaged_volume_can_eject (GVolume *_volume)
+{
+  GVfsStoragedVolume *volume = GVFS_STORAGED_VOLUME (_volume);
+  gboolean can_eject = FALSE;
+
+  if (volume->drive != NULL)
+    can_eject = g_drive_can_eject (G_DRIVE (volume->drive));
+  return can_eject;
+}
+
+static gboolean
+gvfs_storaged_volume_should_automount (GVolume *_volume)
+{
+  GVfsStoragedVolume *volume = GVFS_STORAGED_VOLUME (_volume);
+  return volume->should_automount;
+}
+
+static GDrive *
+gvfs_storaged_volume_get_drive (GVolume *volume)
+{
+  GVfsStoragedVolume *gdu_volume = GVFS_STORAGED_VOLUME (volume);
+  GDrive *drive = NULL;
+
+  if (gdu_volume->drive != NULL)
+    drive = g_object_ref (gdu_volume->drive);
+  return drive;
+}
+
+static GMount *
+gvfs_storaged_volume_get_mount (GVolume *volume)
+{
+  GVfsStoragedVolume *gdu_volume = GVFS_STORAGED_VOLUME (volume);
+  GMount *mount = NULL;
+
+  if (gdu_volume->mount != NULL)
+    mount = g_object_ref (gdu_volume->mount);
+  return mount;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gboolean
+gvfs_storaged_volume_is_network_class (GVfsStoragedVolume *volume)
+{
+  gboolean ret = FALSE;
+  if (volume->mount_point != NULL)
+    {
+      const gchar *fstype = g_unix_mount_point_get_fs_type (volume->mount_point);
+      if (g_strcmp0 (fstype, "nfs") == 0 ||
+          g_strcmp0 (fstype, "nfs4") == 0 ||
+          g_strcmp0 (fstype, "cifs") == 0 ||
+          g_strcmp0 (fstype, "smbfs") == 0 ||
+          g_strcmp0 (fstype, "ncpfs") == 0)
+        ret = TRUE;
+    }
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+/* can remove this once we depend on gio >= 2.31.19 */
+#ifndef G_VOLUME_IDENTIFIER_KIND_CLASS
+#define G_VOLUME_IDENTIFIER_KIND_CLASS "class"
+#endif
+
+static gchar *
+gvfs_storaged_volume_get_identifier (GVolume      *_volume,
+                                    const gchar  *kind)
+{
+  GVfsStoragedVolume *volume = GVFS_STORAGED_VOLUME (_volume);
+  const gchar *label;
+  const gchar *uuid;
+  gchar *ret = NULL;
+
+  if (volume->block != NULL)
+    {
+      label = storaged_block_get_id_label (volume->block);
+      uuid = storaged_block_get_id_uuid (volume->block);
+
+      if (strcmp (kind, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE) == 0)
+        ret = g_strdup (volume->device_file);
+      else if (strcmp (kind, G_VOLUME_IDENTIFIER_KIND_LABEL) == 0)
+        ret = strlen (label) > 0 ? g_strdup (label) : NULL;
+      else if (strcmp (kind, G_VOLUME_IDENTIFIER_KIND_UUID) == 0)
+        ret = strlen (uuid) > 0 ? g_strdup (uuid) : NULL;
+    }
+  if (strcmp (kind, G_VOLUME_IDENTIFIER_KIND_CLASS) == 0)
+    ret = g_strdup (gvfs_storaged_volume_is_network_class (volume) ? "network" : "device");
+
+  return ret;
+}
+
+static gchar **
+gvfs_storaged_volume_enumerate_identifiers (GVolume *_volume)
+{
+  GVfsStoragedVolume *volume = GVFS_STORAGED_VOLUME (_volume);
+  GPtrArray *p;
+
+  p = g_ptr_array_new ();
+  g_ptr_array_add (p, g_strdup (G_VOLUME_IDENTIFIER_KIND_CLASS));
+  if (volume->block != NULL)
+    {
+      const gchar *label;
+      const gchar *uuid;
+      label = storaged_block_get_id_label (volume->block);
+      uuid = storaged_block_get_id_uuid (volume->block);
+      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 *
+gvfs_storaged_volume_get_activation_root (GVolume *_volume)
+{
+  GVfsStoragedVolume *volume = GVFS_STORAGED_VOLUME (_volume);
+  return volume->activation_root != NULL ? g_object_ref (volume->activation_root) : NULL;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+#ifdef HAVE_KEYRING
+static SecretSchema luks_passphrase_schema =
+{
+  "org.gnome.GVfs.Luks.Password",
+  SECRET_SCHEMA_DONT_MATCH_NAME,
+  {
+    { "gvfs-luks-uuid", SECRET_SCHEMA_ATTRIBUTE_STRING },
+    { NULL, 0 },
+  }
+};
+#endif
+
+struct MountData
+{
+  GSimpleAsyncResult *simple;
+
+  GVfsStoragedVolume *volume;
+  GCancellable *cancellable;
+
+  gulong mount_operation_reply_handler_id;
+  gulong mount_operation_aborted_handler_id;
+  GMountOperation *mount_operation;
+
+  gchar *passphrase;
+
+  gchar *passphrase_from_keyring;
+  GPasswordSave password_save;
+
+  gchar *uuid_of_encrypted_to_unlock;
+  gchar *desc_of_encrypted_to_unlock;
+  StoragedEncrypted *encrypted_to_unlock;
+  StoragedFilesystem *filesystem_to_mount;
+
+  gboolean checked_keyring;
+};
+
+static void
+mount_data_free (MountData *data)
+{
+  if (data->volume->mount_pending_op == data)
+    data->volume->mount_pending_op = NULL;
+
+  g_object_unref (data->simple);
+
+  g_clear_object (&data->volume);
+  g_clear_object (&data->cancellable);
+
+  if (data->mount_operation != NULL)
+    {
+      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_aborted_handler_id != 0)
+        g_signal_handler_disconnect (data->mount_operation, data->mount_operation_aborted_handler_id);
+      g_object_unref (data->mount_operation);
+    }
+
+#ifdef HAVE_KEYRING
+  secret_password_free (data->passphrase);
+  secret_password_free (data->passphrase_from_keyring);
+#else
+  g_free (data->passphrase);
+  g_free (data->passphrase_from_keyring);
+#endif
+
+  g_free (data->uuid_of_encrypted_to_unlock);
+  g_free (data->desc_of_encrypted_to_unlock);
+  g_clear_object (&data->encrypted_to_unlock);
+  g_clear_object (&data->filesystem_to_mount);
+  g_free (data);
+}
+
+static void
+mount_cancel_pending_op (MountData *data)
+{
+  g_cancellable_cancel (data->cancellable);
+  /* send an ::aborted signal to make the dialog go away */
+  if (data->mount_operation != NULL)
+    g_signal_emit_by_name (data->mount_operation, "aborted");
+}
+
+/* ------------------------------ */
+
+static void
+mount_command_cb (GObject       *source_object,
+                  GAsyncResult  *res,
+                  gpointer       user_data)
+{
+  MountData *data = user_data;
+  GError *error;
+  gint exit_status;
+  gchar *standard_error = NULL;
+
+  /* NOTE: for e.g. NFS and CIFS mounts we could do GMountOperation stuff and pipe a
+   * password to mount(8)'s stdin channel
+   *
+   * NOTE: if this fails because the user is not authorized (e.g. EPERM), we could
+   * run it through a polkit-ified setuid root helper
+   */
+
+  error = NULL;
+  if (!gvfs_storaged_utils_spawn_finish (res,
+                                        &exit_status,
+                                        NULL, /* gchar **out_standard_output */
+                                        &standard_error,
+                                        &error))
+    {
+      g_simple_async_result_take_error (data->simple, error);
+      g_simple_async_result_complete (data->simple);
+      mount_data_free (data);
+      goto out;
+    }
+
+  if (WIFEXITED (exit_status) && WEXITSTATUS (exit_status) == 0)
+    {
+      gvfs_storaged_volume_monitor_update (data->volume->monitor);
+      g_simple_async_result_complete (data->simple);
+      mount_data_free (data);
+      goto out;
+    }
+
+  g_simple_async_result_set_error (data->simple,
+                                   G_IO_ERROR,
+                                   G_IO_ERROR_FAILED,
+                                   "%s", standard_error);
+  g_simple_async_result_complete (data->simple);
+  mount_data_free (data);
+
+ out:
+  g_free (standard_error);
+}
+
+/* ------------------------------ */
+
+static void
+ensure_autoclear (MountData *data)
+{
+  StoragedLoop *loop;
+  loop = storaged_client_get_loop_for_block (gvfs_storaged_volume_monitor_get_storaged_client 
(data->volume->monitor),
+                                           data->volume->block);
+  if (loop != NULL)
+    {
+      if (!storaged_loop_get_autoclear (loop) && storaged_loop_get_setup_by_uid (loop) == getuid ())
+        {
+          /* we don't care about the result */
+          storaged_loop_call_set_autoclear (loop, TRUE,
+                                          g_variant_new ("a{sv}", NULL), /* options */
+                                          NULL, NULL, NULL);
+        }
+      g_object_unref (loop);
+    }
+}
+
+/* ------------------------------ */
+
+
+static void
+mount_cb (GObject       *source_object,
+          GAsyncResult  *res,
+          gpointer       user_data)
+{
+  MountData *data = user_data;
+  gchar *mount_path;
+  GError *error;
+
+  error = NULL;
+  if (!storaged_filesystem_call_mount_finish (STORAGED_FILESYSTEM (source_object),
+                                            &mount_path,
+                                            res,
+                                            &error))
+    {
+      gvfs_storaged_utils_storaged_error_to_gio_error (error);
+      g_simple_async_result_take_error (data->simple, error);
+      g_simple_async_result_complete (data->simple);
+    }
+  else
+    {
+      /* if mounting worked, ensure that the loop device goes away when unmounted */
+      ensure_autoclear (data);
+
+      gvfs_storaged_volume_monitor_update (data->volume->monitor);
+      g_simple_async_result_complete (data->simple);
+      g_free (mount_path);
+    }
+  mount_data_free (data);
+}
+
+static void
+do_mount (MountData *data)
+{
+  GVariantBuilder builder;
+
+  g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
+  if (data->mount_operation == NULL)
+    {
+      g_variant_builder_add (&builder,
+                             "{sv}",
+                             "auth.no_user_interaction", g_variant_new_boolean (TRUE));
+    }
+  storaged_filesystem_call_mount (data->filesystem_to_mount,
+                                g_variant_builder_end (&builder),
+                                data->cancellable,
+                                mount_cb,
+                                data);
+}
+
+/* ------------------------------ */
+
+#ifdef HAVE_KEYRING
+static void
+luks_store_passphrase_cb (GObject      *source,
+                          GAsyncResult *result,
+                          gpointer      user_data)
+{
+  MountData *data = user_data;
+  GError *error = NULL;
+
+  if (secret_password_store_finish (result, &error))
+    {
+      /* everything is good */
+      do_mount (data);
+    }
+  else
+    {
+      /* report failure */
+      g_simple_async_result_set_error (data->simple,
+                                       G_IO_ERROR,
+                                       G_IO_ERROR_FAILED,
+                                       _("Error storing passphrase in keyring (%s)"),
+                                       error->message);
+      g_simple_async_result_complete (data->simple);
+      g_error_free (error);
+      mount_data_free (data);
+    }
+}
+#endif
+
+
+static void do_unlock (MountData *data);
+
+
+#ifdef HAVE_KEYRING
+static void
+luks_delete_passphrase_cb (GObject      *source,
+                           GAsyncResult *result,
+                           gpointer      user_data)
+{
+  MountData *data = user_data;
+  GError *error = NULL;
+
+  secret_password_clear_finish (result, &error);
+  if (!error)
+    {
+      /* with the bad passphrase out of the way, try again */
+      g_free (data->passphrase);
+      data->passphrase = NULL;
+      do_unlock (data);
+    }
+  else
+    {
+      /* report failure */
+      g_simple_async_result_set_error (data->simple,
+                                       G_IO_ERROR,
+                                       G_IO_ERROR_FAILED,
+                                       _("Error deleting invalid passphrase from keyring (%s)"),
+                                       error->message);
+      g_simple_async_result_complete (data->simple);
+      g_error_free (error);
+      mount_data_free (data);
+    }
+}
+#endif
+
+static void
+unlock_cb (GObject       *source_object,
+           GAsyncResult  *res,
+           gpointer       user_data)
+{
+  MountData *data = user_data;
+  gchar *cleartext_device = NULL;
+  GError *error;
+
+  error = NULL;
+  if (!storaged_encrypted_call_unlock_finish (STORAGED_ENCRYPTED (source_object),
+                                            &cleartext_device,
+                                            res,
+                                            &error))
+    {
+#ifdef HAVE_KEYRING
+      /* If this failed with a passphrase read from the keyring, try again
+       * this time prompting the user...
+       *
+       * TODO: ideally check against something like STORAGED_ERROR_PASSPHRASE_INVALID
+       * when such a thing is available in udisks
+       */
+      if (data->passphrase_from_keyring != NULL &&
+          g_strcmp0 (data->passphrase, data->passphrase_from_keyring) == 0)
+        {
+          /* nuke the invalid passphrase from keyring... */
+          secret_password_clear (&luks_passphrase_schema, data->cancellable,
+                                 luks_delete_passphrase_cb, data,
+                                 "gvfs-luks-uuid", data->uuid_of_encrypted_to_unlock,
+                                 NULL); /* sentinel */
+          goto out;
+        }
+#endif
+      gvfs_storaged_utils_storaged_error_to_gio_error (error);
+      g_simple_async_result_take_error (data->simple, error);
+      g_simple_async_result_complete (data->simple);
+      mount_data_free (data);
+      goto out;
+    }
+  else
+    {
+      StoragedObject *object;
+
+      /* if unlocking worked, ensure that the loop device goes away when locked */
+      ensure_autoclear (data);
+
+      gvfs_storaged_volume_monitor_update (data->volume->monitor);
+
+      object = storaged_client_peek_object (gvfs_storaged_volume_monitor_get_storaged_client 
(data->volume->monitor),
+                                          cleartext_device);
+      data->filesystem_to_mount = object != NULL ? storaged_object_get_filesystem (object) : NULL;
+      if (data->filesystem_to_mount == NULL)
+        {
+          g_simple_async_result_set_error (data->simple,
+                                           G_IO_ERROR,
+                                           G_IO_ERROR_FAILED,
+                                           _("The unlocked device does not have a recognizable file system 
on it"));
+          g_simple_async_result_complete (data->simple);
+          mount_data_free (data);
+          goto out;
+        }
+
+#ifdef HAVE_KEYRING
+      /* passphrase worked - save it in the keyring if requested */
+      if (data->password_save != G_PASSWORD_SAVE_NEVER)
+        {
+          const gchar *keyring;
+          gchar *display_name;
+
+          switch (data->password_save)
+            {
+            case G_PASSWORD_SAVE_NEVER:
+              g_assert_not_reached ();
+              break;
+            case G_PASSWORD_SAVE_FOR_SESSION:
+              keyring = SECRET_COLLECTION_SESSION;
+              break;
+            case G_PASSWORD_SAVE_PERMANENTLY:
+              keyring = SECRET_COLLECTION_DEFAULT;
+              break;
+            default:
+              keyring = SECRET_COLLECTION_DEFAULT;
+              break;
+            }
+
+          display_name = g_strdup_printf (_("Encryption passphrase for %s"),
+                                          data->desc_of_encrypted_to_unlock);
+
+          secret_password_store (&luks_passphrase_schema,
+                                 keyring, display_name, data->passphrase,
+                                 data->cancellable,
+                                 luks_store_passphrase_cb, data,
+                                 "gvfs-luks-uuid", data->uuid_of_encrypted_to_unlock,
+                                 NULL); /* sentinel */
+          goto out;
+        }
+#endif
+
+      /* OK, ready to rock */
+      do_mount (data);
+    }
+
+ out:
+  g_free (cleartext_device);
+}
+
+static void
+on_mount_operation_reply (GMountOperation       *mount_operation,
+                          GMountOperationResult result,
+                          gpointer              user_data)
+{
+  MountData *data = user_data;
+
+  /* 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 (data->mount_operation_aborted_handler_id != 0)
+    {
+      g_signal_handler_disconnect (data->mount_operation, data->mount_operation_aborted_handler_id);
+      data->mount_operation_aborted_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);
+      mount_data_free (data);
+      goto out;
+    }
+
+  data->passphrase = g_strdup (g_mount_operation_get_password (mount_operation));
+  data->password_save = g_mount_operation_get_password_save (mount_operation);
+
+  /* Don't save password in keyring just yet - check if it works first */
+
+  do_unlock (data);
+
+ out:
+  ;
+}
+
+static void
+on_mount_operation_aborted (GMountOperation       *mount_operation,
+                            gpointer              user_data)
+{
+  on_mount_operation_reply (mount_operation, G_MOUNT_OPERATION_ABORTED, user_data);
+}
+
+static gboolean
+has_crypttab_passphrase (MountData *data)
+{
+  gboolean ret = FALSE;
+  GVariantIter iter;
+  GVariant *configuration_value;
+  const gchar *configuration_type;
+
+  g_variant_iter_init (&iter, storaged_block_get_configuration (data->volume->block));
+  while (g_variant_iter_next (&iter, "(&s a{sv})", &configuration_type, &configuration_value))
+    {
+      if (g_strcmp0 (configuration_type, "crypttab") == 0)
+        {
+          const gchar *passphrase_path;
+          if (g_variant_lookup (configuration_value, "passphrase-path", "^&ay", &passphrase_path))
+            {
+              if (passphrase_path != NULL && strlen (passphrase_path) > 0)
+                {
+                  ret = TRUE;
+                  g_variant_unref (configuration_value);
+                  goto out;
+                }
+            }
+        }
+      g_variant_unref (configuration_value);
+    }
+ out:
+  return ret;
+}
+
+#ifdef HAVE_KEYRING
+static void
+luks_find_passphrase_cb (GObject      *source,
+                         GAsyncResult *result,
+                         gpointer      user_data)
+{
+  MountData *data = user_data;
+  gchar *password;
+
+  password = secret_password_lookup_finish (result, NULL);
+
+  /* Don't fail if a keyring error occured - just continue and request
+   * the passphrase from the user...
+   */
+  if (password)
+    {
+      data->passphrase = password;
+      data->passphrase_from_keyring = g_strdup (password);
+    }
+  /* try again */
+  do_unlock (data);
+}
+#endif
+
+static void
+do_unlock (MountData *data)
+{
+  GVariantBuilder builder;
+
+  if (data->passphrase == NULL)
+    {
+      /* If the passphrase is in the crypttab file, no need to ask the user, just use a blank passphrase */
+      if (has_crypttab_passphrase (data))
+        {
+          data->passphrase = g_strdup ("");
+        }
+      else
+        {
+          gchar *message;
+
+#ifdef HAVE_KEYRING
+          /* check if the passphrase is in the user's keyring */
+          if (!data->checked_keyring)
+            {
+              data->checked_keyring = TRUE;
+              secret_password_lookup (&luks_passphrase_schema, data->cancellable,
+                                      luks_find_passphrase_cb, data,
+                                      "gvfs-luks-uuid", data->uuid_of_encrypted_to_unlock,
+                                      NULL); /* sentinel */
+              goto out;
+            }
+#endif
+
+          if (data->mount_operation == NULL)
+            {
+              g_simple_async_result_set_error (data->simple,
+                                               G_IO_ERROR,
+                                               G_IO_ERROR_FAILED,
+                                               _("A passphrase is required to access the volume"));
+              g_simple_async_result_complete (data->simple);
+              mount_data_free (data);
+              goto out;
+            }
+
+          data->mount_operation_reply_handler_id = g_signal_connect (data->mount_operation,
+                                                                     "reply",
+                                                                     G_CALLBACK (on_mount_operation_reply),
+                                                                     data);
+          data->mount_operation_aborted_handler_id = g_signal_connect (data->mount_operation,
+                                                                       "aborted",
+                                                                       G_CALLBACK 
(on_mount_operation_aborted),
+                                                                       data);
+          /* Translators: This is the message shown to users */
+          message = g_strdup_printf (_("Enter a passphrase to unlock the volume\n"
+                                       "The passphrase is needed to access encrypted data on %s."),
+                                     data->desc_of_encrypted_to_unlock);
+
+          /* NOTE: We (currently) don't offer the user to save the
+           * passphrase in the keyring or /etc/crypttab - compared to
+           * the gdu volume monitor (that used the keyring for this)
+           * this is a "regression" but it's done this way on purpose
+           * because
+           *
+           *  - if the device is encrypted, it was probably the intent
+           *    that the passphrase is to be used every time it's used
+           *
+           *  - supporting both /etc/crypttab and the keyring is confusing
+           *    and leaves the user to wonder where the key is stored.
+           *
+           *  - the user can add an /etc/crypttab entry himself either
+           *    manually or through palimpsest
+           */
+          g_signal_emit_by_name (data->mount_operation,
+                                 "ask-password",
+                                 message,
+                                 NULL,
+                                 NULL,
+                                 G_ASK_PASSWORD_NEED_PASSWORD |
+                                 G_ASK_PASSWORD_SAVING_SUPPORTED);
+          g_free (message);
+          goto out;
+        }
+    }
+
+  g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
+  if (data->mount_operation == NULL)
+    {
+      g_variant_builder_add (&builder,
+                             "{sv}",
+                             "auth.no_user_interaction", g_variant_new_boolean (TRUE));
+    }
+  storaged_encrypted_call_unlock (data->encrypted_to_unlock,
+                                data->passphrase,
+                                g_variant_builder_end (&builder),
+                                data->cancellable,
+                                unlock_cb,
+                                data);
+ out:
+  ;
+}
+
+
+static void
+gvfs_storaged_volume_mount (GVolume             *_volume,
+                           GMountMountFlags     flags,
+                           GMountOperation     *mount_operation,
+                           GCancellable        *cancellable,
+                           GAsyncReadyCallback  callback,
+                           gpointer             user_data)
+{
+  GVfsStoragedVolume *volume = GVFS_STORAGED_VOLUME (_volume);
+  GDBusObject *object;
+  StoragedBlock *block;
+  StoragedFilesystem *filesystem;
+  MountData *data;
+
+  data = g_new0 (MountData, 1);
+  data->simple = g_simple_async_result_new (G_OBJECT (volume),
+                                            callback,
+                                            user_data,
+                                            gvfs_storaged_volume_mount);
+  data->volume = g_object_ref (volume);
+  data->cancellable = cancellable != NULL ? g_object_ref (cancellable) : NULL;
+  data->mount_operation = mount_operation != NULL ? g_object_ref (mount_operation) : NULL;
+
+  if (volume->mount_pending_op != NULL)
+    {
+      g_simple_async_result_set_error (data->simple,
+                                       G_IO_ERROR,
+                                       G_IO_ERROR_FAILED,
+                                       "A mount operation is already pending");
+      g_simple_async_result_complete (data->simple);
+      mount_data_free (data);
+      goto out;
+    }
+  volume->mount_pending_op = data;
+
+  /* Use the mount(8) command if there is no block device */
+  if (volume->block == NULL)
+    {
+      gchar *escaped_mount_path;
+      escaped_mount_path = g_strescape (g_unix_mount_point_get_mount_path (data->volume->mount_point), NULL);
+      gvfs_storaged_utils_spawn (10, /* timeout in seconds */
+                                data->cancellable,
+                                mount_command_cb,
+                                data,
+                                "mount \"%s\"",
+                                escaped_mount_path);
+      g_free (escaped_mount_path);
+      goto out;
+    }
+
+  /* if encrypted and already unlocked, just mount the cleartext block device */
+  block = storaged_client_get_cleartext_block (gvfs_storaged_volume_monitor_get_storaged_client 
(volume->monitor),
+                                             volume->block);
+  if (block != NULL)
+    g_object_unref (block);
+  else
+    block = volume->block;
+
+  object = g_dbus_interface_get_object (G_DBUS_INTERFACE (block));
+  if (object == NULL)
+    {
+      g_simple_async_result_set_error (data->simple,
+                                       G_IO_ERROR,
+                                       G_IO_ERROR_FAILED,
+                                       "No object for D-Bus interface");
+      g_simple_async_result_complete (data->simple);
+      mount_data_free (data);
+      goto out;
+    }
+
+  filesystem = storaged_object_peek_filesystem (STORAGED_OBJECT (object));
+  if (filesystem == NULL)
+    {
+      data->encrypted_to_unlock = storaged_object_get_encrypted (STORAGED_OBJECT (object));
+      if (data->encrypted_to_unlock != NULL)
+        {
+          StoragedDrive *storaged_drive;
+
+          /* This description is used in both the prompt and the display-name of
+           * the key stored in the user's keyring ...
+           *
+           * NOTE: we want a little bit more detail than what g_drive_get_name()
+           * gives us, since this is going to be used to refer to the device even
+           * when not plugged in
+           */
+          storaged_drive = storaged_client_get_drive_for_block 
(gvfs_storaged_volume_monitor_get_storaged_client (volume->monitor),
+                                                            block);
+          if (storaged_drive != NULL)
+            {
+              gchar *drive_name = NULL;
+              gchar *drive_desc = NULL;
+
+#if STORAGED_CHECK_VERSION(2,0,90)
+              {
+                StoragedObject *object = (StoragedObject *) g_dbus_interface_get_object (G_DBUS_INTERFACE 
(storaged_drive));
+                if (object != NULL)
+                  {
+                    StoragedObjectInfo *info = storaged_client_get_object_info 
(gvfs_storaged_volume_monitor_get_storaged_client (volume->monitor),
+                                                                            object);
+                    if (info != NULL)
+                      {
+                        drive_name = g_strdup (storaged_object_info_get_name (info));
+                        drive_desc = g_strdup (storaged_object_info_get_description (info));
+                        g_object_unref (info);
+                      }
+                  }
+              }
+#else
+              storaged_client_get_drive_info (gvfs_storaged_volume_monitor_get_storaged_client 
(volume->monitor),
+                                            storaged_drive,
+                                            &drive_name,
+                                            &drive_desc,
+                                            NULL,  /* drive_icon */
+                                            NULL,  /* media_desc */
+                                            NULL); /* media_icon */
+#endif
+              /* Translators: this is used to describe the drive the encrypted media
+               * is on - the first %s is the name (such as 'WD 2500JB External'), the
+               * second %s is the description ('250 GB Hard Disk').
+               */
+              data->desc_of_encrypted_to_unlock = g_strdup_printf (_("%s (%s)"),
+                                                                   drive_name,
+                                                                   drive_desc);
+              g_free (drive_desc);
+              g_free (drive_name);
+              g_object_unref (storaged_drive);
+            }
+          else
+            {
+              data->desc_of_encrypted_to_unlock = storaged_block_dup_preferred_device (block);
+            }
+          data->uuid_of_encrypted_to_unlock = storaged_block_dup_id_uuid (block);
+
+          do_unlock (data);
+          goto out;
+        }
+
+      g_simple_async_result_set_error (data->simple,
+                                       G_IO_ERROR,
+                                       G_IO_ERROR_FAILED,
+                                       "No .Filesystem or .Encrypted interface on D-Bus object");
+      g_simple_async_result_complete (data->simple);
+      mount_data_free (data);
+      goto out;
+    }
+
+  data->filesystem_to_mount = g_object_ref (filesystem);
+  do_mount (data);
+
+ out:
+  ;
+}
+
+static gboolean
+gvfs_storaged_volume_mount_finish (GVolume       *volume,
+                                  GAsyncResult  *result,
+                                  GError       **error)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
+  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
+gvfs_storaged_volume_eject_with_operation (GVolume              *_volume,
+                                          GMountUnmountFlags   flags,
+                                          GMountOperation     *mount_operation,
+                                          GCancellable        *cancellable,
+                                          GAsyncReadyCallback  callback,
+                                          gpointer             user_data)
+{
+  GVfsStoragedVolume *volume = GVFS_STORAGED_VOLUME (_volume);
+  GVfsStoragedDrive *drive;
+
+  drive = NULL;
+  if (volume->drive != NULL)
+    drive = g_object_ref (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_with_operation (G_DRIVE (drive), flags, mount_operation, 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
+gvfs_storaged_volume_eject_with_operation_finish (GVolume        *_volume,
+                                                 GAsyncResult  *result,
+                                                 GError       **error)
+{
+  GVfsStoragedVolume *volume = GVFS_STORAGED_VOLUME (_volume);
+  gboolean ret = TRUE;
+
+  if (volume->drive != NULL)
+    {
+      ret = g_drive_eject_with_operation_finish (G_DRIVE (volume->drive), result, error);
+    }
+  else
+    {
+      g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error);
+      ret = FALSE;
+    }
+  return ret;
+}
+
+static void
+gvfs_storaged_volume_eject (GVolume              *volume,
+                           GMountUnmountFlags   flags,
+                           GCancellable        *cancellable,
+                           GAsyncReadyCallback  callback,
+                           gpointer             user_data)
+{
+  gvfs_storaged_volume_eject_with_operation (volume, flags, NULL, cancellable, callback, user_data);
+}
+
+static gboolean
+gvfs_storaged_volume_eject_finish (GVolume        *volume,
+                                  GAsyncResult  *result,
+                                  GError       **error)
+{
+  return gvfs_storaged_volume_eject_with_operation_finish (volume, result, error);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static const gchar *
+gvfs_storaged_volume_get_sort_key (GVolume *_volume)
+{
+  GVfsStoragedVolume *volume = GVFS_STORAGED_VOLUME (_volume);
+  return volume->sort_key;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+gvfs_storaged_volume_volume_iface_init (GVolumeIface *iface)
+{
+  iface->get_name = gvfs_storaged_volume_get_name;
+  iface->get_icon = gvfs_storaged_volume_get_icon;
+  iface->get_symbolic_icon = gvfs_storaged_volume_get_symbolic_icon;
+  iface->get_uuid = gvfs_storaged_volume_get_uuid;
+  iface->get_drive = gvfs_storaged_volume_get_drive;
+  iface->get_mount = gvfs_storaged_volume_get_mount;
+  iface->can_mount = gvfs_storaged_volume_can_mount;
+  iface->can_eject = gvfs_storaged_volume_can_eject;
+  iface->should_automount = gvfs_storaged_volume_should_automount;
+  iface->get_activation_root = gvfs_storaged_volume_get_activation_root;
+  iface->enumerate_identifiers = gvfs_storaged_volume_enumerate_identifiers;
+  iface->get_identifier = gvfs_storaged_volume_get_identifier;
+
+  iface->mount_fn = gvfs_storaged_volume_mount;
+  iface->mount_finish = gvfs_storaged_volume_mount_finish;
+  iface->eject = gvfs_storaged_volume_eject;
+  iface->eject_finish = gvfs_storaged_volume_eject_finish;
+  iface->eject_with_operation = gvfs_storaged_volume_eject_with_operation;
+  iface->eject_with_operation_finish = gvfs_storaged_volume_eject_with_operation_finish;
+  iface->get_sort_key = gvfs_storaged_volume_get_sort_key;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+StoragedBlock *
+gvfs_storaged_volume_get_block (GVfsStoragedVolume *volume)
+{
+  g_return_val_if_fail (GVFS_IS_STORAGED_VOLUME (volume), NULL);
+  return volume->block;
+}
+
+GUnixMountPoint *
+gvfs_storaged_volume_get_mount_point (GVfsStoragedVolume *volume)
+{
+  g_return_val_if_fail (GVFS_IS_STORAGED_VOLUME (volume), NULL);
+  return volume->mount_point;
+}
+
+dev_t
+gvfs_storaged_volume_get_dev (GVfsStoragedVolume *volume)
+{
+  g_return_val_if_fail (GVFS_IS_STORAGED_VOLUME (volume), 0);
+  return volume->dev;
+}
+
+gboolean
+gvfs_storaged_volume_has_uuid (GVfsStoragedVolume *volume,
+                              const gchar       *uuid)
+{
+  g_return_val_if_fail (GVFS_IS_STORAGED_VOLUME (volume), FALSE);
+  return g_strcmp0 (volume->uuid, uuid) == 0;
+}
diff --git a/monitor/storaged/gvfsstoragedvolume.h b/monitor/storaged/gvfsstoragedvolume.h
new file mode 100644
index 0000000..2c396c9
--- /dev/null
+++ b/monitor/storaged/gvfsstoragedvolume.h
@@ -0,0 +1,67 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* gvfs - extensions for gio
+ *
+ * Copyright (C) 2006-2012 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., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#ifndef __GVFS_STORAGED_VOLUME_H__
+#define __GVFS_STORAGED_VOLUME_H__
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include "gvfsstoragedvolumemonitor.h"
+
+G_BEGIN_DECLS
+
+#define GVFS_TYPE_STORAGED_VOLUME  (gvfs_storaged_volume_get_type ())
+#define GVFS_STORAGED_VOLUME(o)    (G_TYPE_CHECK_INSTANCE_CAST ((o), GVFS_TYPE_STORAGED_VOLUME, 
GVfsStoragedVolume))
+#define GVFS_IS_STORAGED_VOLUME(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVFS_TYPE_STORAGED_VOLUME))
+
+GType              gvfs_storaged_volume_get_type    (void) G_GNUC_CONST;
+
+GVfsStoragedVolume *gvfs_storaged_volume_new         (GVfsStoragedVolumeMonitor   *monitor,
+                                                    StoragedBlock                *block,
+                                                    GUnixMountPoint            *mount_point,
+                                                    GVfsStoragedDrive           *drive,
+                                                    GFile                      *activation_root,
+                                                    gboolean                    coldplug);
+void               gvfs_storaged_volume_removed     (GVfsStoragedVolume          *volume);
+
+StoragedBlock       *gvfs_storaged_volume_get_block       (GVfsStoragedVolume      *volume);
+GUnixMountPoint   *gvfs_storaged_volume_get_mount_point (GVfsStoragedVolume      *volume);
+dev_t              gvfs_storaged_volume_get_dev         (GVfsStoragedVolume      *volume);
+
+void               gvfs_storaged_volume_set_mount   (GVfsStoragedVolume          *volume,
+                                                    GVfsStoragedMount           *mount);
+void               gvfs_storaged_volume_unset_mount (GVfsStoragedVolume          *volume,
+                                                    GVfsStoragedMount           *mount);
+
+void               gvfs_storaged_volume_set_drive   (GVfsStoragedVolume          *volume,
+                                                    GVfsStoragedDrive           *drive);
+void               gvfs_storaged_volume_unset_drive (GVfsStoragedVolume          *volume,
+                                                    GVfsStoragedDrive           *drive);
+
+gboolean           gvfs_storaged_volume_has_uuid    (GVfsStoragedVolume          *volume,
+                                                    const gchar                *uuid);
+
+G_END_DECLS
+
+#endif /* __GVFS_STORAGED_VOLUME_H__ */
diff --git a/monitor/storaged/gvfsstoragedvolumemonitor.c b/monitor/storaged/gvfsstoragedvolumemonitor.c
new file mode 100644
index 0000000..8afaec3
--- /dev/null
+++ b/monitor/storaged/gvfsstoragedvolumemonitor.c
@@ -0,0 +1,1885 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* gvfs - extensions for gio
+ *
+ * Copyright (C) 2011-2012 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., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#include <config.h>
+
+#include <limits.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+#include <gio/gio.h>
+
+#include "gvfsstoragedvolumemonitor.h"
+#include "gvfsstorageddrive.h"
+#include "gvfsstoragedvolume.h"
+#include "gvfsstoragedmount.h"
+#include "gvfsstoragedutils.h"
+
+static GVfsStoragedVolumeMonitor *the_volume_monitor = NULL;
+
+typedef struct _GVfsStoragedVolumeMonitorClass GVfsStoragedVolumeMonitorClass;
+
+struct _GVfsStoragedVolumeMonitorClass
+{
+  GNativeVolumeMonitorClass parent_class;
+};
+
+struct _GVfsStoragedVolumeMonitor
+{
+  GNativeVolumeMonitor parent;
+
+  StoragedClient *client;
+  GUdevClient *gudev_client;
+  GUnixMountMonitor *mount_monitor;
+
+  GList *drives;
+  GList *volumes;
+  GList *fstab_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 StoragedClient *get_storaged_client_sync (GError **error);
+
+static void update_all               (GVfsStoragedVolumeMonitor  *monitor,
+                                      gboolean                   emit_changes,
+                                      gboolean                   coldplug);
+static void update_drives            (GVfsStoragedVolumeMonitor  *monitor,
+                                      GList                    **added_drives,
+                                      GList                    **removed_drives,
+                                      gboolean                   coldplug);
+static void update_volumes           (GVfsStoragedVolumeMonitor  *monitor,
+                                      GList                    **added_volumes,
+                                      GList                    **removed_volumes,
+                                      gboolean                   coldplug);
+static void update_fstab_volumes     (GVfsStoragedVolumeMonitor  *monitor,
+                                      GList                    **added_volumes,
+                                      GList                    **removed_volumes,
+                                      gboolean                   coldplug);
+static void update_mounts            (GVfsStoragedVolumeMonitor  *monitor,
+                                      GList                    **added_mounts,
+                                      GList                    **removed_mounts,
+                                      gboolean                   coldplug);
+static void update_discs             (GVfsStoragedVolumeMonitor  *monitor,
+                                      GList                    **added_volumes,
+                                      GList                    **removed_volumes,
+                                      GList                    **added_mounts,
+                                      GList                    **removed_mounts,
+                                      gboolean                   coldplug);
+
+
+static void on_client_changed (StoragedClient *client,
+                               gpointer      user_data);
+
+static void mountpoints_changed      (GUnixMountMonitor  *mount_monitor,
+                                      gpointer            user_data);
+
+static void mounts_changed           (GUnixMountMonitor  *mount_monitor,
+                                      gpointer            user_data);
+
+G_DEFINE_TYPE (GVfsStoragedVolumeMonitor, gvfs_storaged_volume_monitor, G_TYPE_NATIVE_VOLUME_MONITOR)
+
+static void
+gvfs_storaged_volume_monitor_dispose (GObject *object)
+{
+  the_volume_monitor = NULL;
+
+  if (G_OBJECT_CLASS (gvfs_storaged_volume_monitor_parent_class)->dispose != NULL)
+    G_OBJECT_CLASS (gvfs_storaged_volume_monitor_parent_class)->dispose (object);
+}
+
+static void
+gvfs_storaged_volume_monitor_finalize (GObject *object)
+{
+  GVfsStoragedVolumeMonitor *monitor = GVFS_STORAGED_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_clear_object (&monitor->mount_monitor);
+
+  g_signal_handlers_disconnect_by_func (monitor->client,
+                                        G_CALLBACK (on_client_changed),
+                                        monitor);
+
+  g_clear_object (&monitor->client);
+  g_clear_object (&monitor->gudev_client);
+
+  g_list_free_full (monitor->drives, g_object_unref);
+  g_list_free_full (monitor->volumes, g_object_unref);
+  g_list_free_full (monitor->fstab_volumes, g_object_unref);
+  g_list_free_full (monitor->mounts, g_object_unref);
+
+  g_list_free_full (monitor->disc_volumes, g_object_unref);
+  g_list_free_full (monitor->disc_mounts, g_object_unref);
+
+  G_OBJECT_CLASS (gvfs_storaged_volume_monitor_parent_class)->finalize (object);
+}
+
+static GList *
+get_mounts (GVolumeMonitor *_monitor)
+{
+  GVfsStoragedVolumeMonitor *monitor = GVFS_STORAGED_VOLUME_MONITOR (_monitor);
+  GList *ret;
+
+  ret = g_list_copy (monitor->mounts);
+  ret = g_list_concat (ret, g_list_copy (monitor->disc_mounts));
+  g_list_foreach (ret, (GFunc) g_object_ref, NULL);
+  return ret;
+}
+
+static GList *
+get_volumes (GVolumeMonitor *_monitor)
+{
+  GVfsStoragedVolumeMonitor *monitor = GVFS_STORAGED_VOLUME_MONITOR (_monitor);
+  GList *ret;
+
+  ret = g_list_copy (monitor->volumes);
+  ret = g_list_concat (ret, g_list_copy (monitor->fstab_volumes));
+  ret = g_list_concat (ret, g_list_copy (monitor->disc_volumes));
+  g_list_foreach (ret, (GFunc) g_object_ref, NULL);
+  return ret;
+}
+
+static GList *
+get_connected_drives (GVolumeMonitor *_monitor)
+{
+  GVfsStoragedVolumeMonitor *monitor = GVFS_STORAGED_VOLUME_MONITOR (_monitor);
+  GList *ret;
+
+  ret = g_list_copy (monitor->drives);
+  g_list_foreach (ret, (GFunc) g_object_ref, NULL);
+  return ret;
+}
+
+static GVolume *
+get_volume_for_uuid (GVolumeMonitor *_monitor,
+                     const gchar    *uuid)
+{
+  GVfsStoragedVolumeMonitor *monitor = GVFS_STORAGED_VOLUME_MONITOR (_monitor);
+  GVfsStoragedVolume *volume;
+  GList *l;
+
+  for (l = monitor->volumes; l != NULL; l = l->next)
+    {
+      volume = l->data;
+      if (gvfs_storaged_volume_has_uuid (l->data, uuid))
+        goto found;
+    }
+  for (l = monitor->fstab_volumes; l != NULL; l = l->next)
+    {
+      volume = l->data;
+      if (gvfs_storaged_volume_has_uuid (l->data, uuid))
+        goto found;
+    }
+  for (l = monitor->disc_volumes; l != NULL; l = l->next)
+    {
+      volume = l->data;
+      if (gvfs_storaged_volume_has_uuid (volume, uuid))
+        goto found;
+    }
+
+  return NULL;
+
+ found:
+  return G_VOLUME (g_object_ref (volume));
+}
+
+static GMount *
+get_mount_for_uuid (GVolumeMonitor *_monitor,
+                    const gchar    *uuid)
+{
+  GVfsStoragedVolumeMonitor *monitor = GVFS_STORAGED_VOLUME_MONITOR (_monitor);
+  GVfsStoragedMount *mount;
+  GList *l;
+
+  for (l = monitor->mounts; l != NULL; l = l->next)
+    {
+      mount = l->data;
+      if (gvfs_storaged_mount_has_uuid (l->data, uuid))
+        goto found;
+    }
+  for (l = monitor->disc_mounts; l != NULL; l = l->next)
+    {
+      mount = l->data;
+      if (gvfs_storaged_mount_has_uuid (mount, uuid))
+        goto found;
+    }
+
+  return NULL;
+
+ found:
+  return G_MOUNT (g_object_ref (mount));
+}
+
+static GMount *
+get_mount_for_mount_path (const gchar  *mount_path,
+                          GCancellable *cancellable)
+{
+  GVfsStoragedVolumeMonitor *monitor = NULL;
+  GMount *ret = NULL;
+
+  if (the_volume_monitor == NULL)
+    {
+      /* Bah, no monitor is set up.. so we have to create one, find
+       * what the user asks for and throw it away again.
+       */
+      monitor = GVFS_STORAGED_VOLUME_MONITOR (gvfs_storaged_volume_monitor_new ());
+    }
+  else
+    {
+      monitor = g_object_ref (the_volume_monitor);
+    }
+
+  /* creation of the volume monitor could fail */
+  if (monitor != NULL)
+    {
+      GList *l;
+      for (l = monitor->mounts; l != NULL; l = l->next)
+        {
+          GVfsStoragedMount *mount = GVFS_STORAGED_MOUNT (l->data);
+          if (g_strcmp0 (gvfs_storaged_mount_get_mount_path (mount), mount_path) == 0)
+            {
+              ret = g_object_ref (mount);
+              goto out;
+            }
+        }
+    }
+
+ out:
+  if (monitor != NULL)
+    g_object_unref (monitor);
+  return ret;
+}
+
+static GObject *
+gvfs_storaged_volume_monitor_constructor (GType                  type,
+                                         guint                  n_construct_properties,
+                                         GObjectConstructParam *construct_properties)
+{
+  GObject *ret = NULL;
+  GObjectClass *parent_class;
+
+  if (the_volume_monitor != NULL)
+    {
+      ret = g_object_ref (the_volume_monitor);
+      goto out;
+    }
+
+  parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (g_type_class_peek (type)));
+  ret = parent_class->constructor (type,
+                                   n_construct_properties,
+                                   construct_properties);
+
+  the_volume_monitor = GVFS_STORAGED_VOLUME_MONITOR (ret);
+
+ out:
+  return ret;
+}
+
+static void
+gvfs_storaged_volume_monitor_init (GVfsStoragedVolumeMonitor *monitor)
+{
+  monitor->gudev_client = g_udev_client_new (NULL); /* don't listen to any changes */
+
+  monitor->client = get_storaged_client_sync (NULL);
+  g_signal_connect (monitor->client,
+                    "changed",
+                    G_CALLBACK (on_client_changed),
+                    monitor);
+
+  monitor->mount_monitor = g_unix_mount_monitor_get ();
+  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);
+
+  update_all (monitor, FALSE, TRUE);
+}
+
+static gboolean
+is_supported (void)
+{
+  if (get_storaged_client_sync (NULL) != NULL)
+    return TRUE;
+  return FALSE;
+}
+
+static void
+gvfs_storaged_volume_monitor_class_init (GVfsStoragedVolumeMonitorClass *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 = gvfs_storaged_volume_monitor_constructor;
+  gobject_class->finalize = gvfs_storaged_volume_monitor_finalize;
+  gobject_class->dispose = gvfs_storaged_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;
+}
+
+/**
+ * gvfs_storaged_volume_monitor_new:
+ *
+ * Returns:  a new #GVolumeMonitor.
+ **/
+GVolumeMonitor *
+gvfs_storaged_volume_monitor_new (void)
+{
+  return G_VOLUME_MONITOR (g_object_new (GVFS_TYPE_STORAGED_VOLUME_MONITOR, NULL));
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+StoragedClient *
+gvfs_storaged_volume_monitor_get_storaged_client (GVfsStoragedVolumeMonitor *monitor)
+{
+  g_return_val_if_fail (GVFS_IS_STORAGED_VOLUME_MONITOR (monitor), NULL);
+  return monitor->client;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+GUdevClient *
+gvfs_storaged_volume_monitor_get_gudev_client (GVfsStoragedVolumeMonitor *monitor)
+{
+  g_return_val_if_fail (GVFS_IS_STORAGED_VOLUME_MONITOR (monitor), NULL);
+  return monitor->gudev_client;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+void
+gvfs_storaged_volume_monitor_update (GVfsStoragedVolumeMonitor *monitor)
+{
+  g_return_if_fail (GVFS_IS_STORAGED_VOLUME_MONITOR (monitor));
+  storaged_client_settle (monitor->client);
+  update_all (monitor, TRUE, FALSE);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static StoragedClient *
+get_storaged_client_sync (GError **error)
+{
+  static StoragedClient *_client = NULL;
+  static GError *_error = NULL;
+  static volatile gsize initialized = 0;
+
+  if (g_once_init_enter (&initialized))
+    {
+      _client = storaged_client_new_sync (NULL, &_error);
+      g_once_init_leave (&initialized, 1);
+    }
+
+  if (_error != NULL && error != NULL)
+    *error = g_error_copy (_error);
+
+  return _client;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+diff_sorted_lists (GList         *list1,
+                   GList         *list2,
+                   GCompareFunc   compare,
+                   GList        **added,
+                   GList        **removed,
+                   GList        **unchanged)
+{
+  int order;
+
+  *added = *removed = NULL;
+  if (unchanged != NULL)
+    *unchanged = 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 */
+          if (unchanged != NULL)
+            *unchanged = g_list_prepend (*unchanged, list1->data);
+          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 void
+object_list_emit (GVfsStoragedVolumeMonitor *monitor,
+                  const gchar              *monitor_signal,
+                  const gchar              *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
+on_client_changed (StoragedClient  *client,
+                   gpointer       user_data)
+{
+  GVfsStoragedVolumeMonitor *monitor = GVFS_STORAGED_VOLUME_MONITOR (user_data);
+  update_all (monitor, TRUE, FALSE);
+}
+
+static void
+mountpoints_changed (GUnixMountMonitor *mount_monitor,
+                     gpointer           user_data)
+{
+  GVfsStoragedVolumeMonitor *monitor = GVFS_STORAGED_VOLUME_MONITOR (user_data);
+  update_all (monitor, TRUE, FALSE);
+}
+
+static void
+mounts_changed (GUnixMountMonitor *mount_monitor,
+                gpointer           user_data)
+{
+  GVfsStoragedVolumeMonitor *monitor = GVFS_STORAGED_VOLUME_MONITOR (user_data);
+  update_all (monitor, TRUE, FALSE);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+update_all (GVfsStoragedVolumeMonitor *monitor,
+            gboolean                  emit_changes,
+            gboolean                  coldplug)
+{
+  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, coldplug);
+  update_volumes (monitor, &added_volumes, &removed_volumes, coldplug);
+  update_fstab_volumes (monitor, &added_volumes, &removed_volumes, coldplug);
+  update_mounts (monitor, &added_mounts, &removed_mounts, coldplug);
+  update_discs (monitor,
+                &added_volumes, &removed_volumes,
+                &added_mounts, &removed_mounts,
+                coldplug);
+
+  if (emit_changes)
+    {
+      object_list_emit (monitor,
+                        "drive-disconnected", NULL,
+                        removed_drives);
+      object_list_emit (monitor,
+                        "drive-connected", NULL,
+                        added_drives);
+
+      object_list_emit (monitor,
+                        "volume-removed", "removed",
+                        removed_volumes);
+      object_list_emit (monitor,
+                        "volume-added", NULL,
+                        added_volumes);
+
+      object_list_emit (monitor,
+                        "mount-removed", "unmounted",
+                        removed_mounts);
+      object_list_emit (monitor,
+                        "mount-added", NULL,
+                        added_mounts);
+    }
+
+  g_list_free_full (removed_drives, g_object_unref);
+  g_list_free_full (added_drives, g_object_unref);
+  g_list_free_full (removed_volumes, g_object_unref);
+  g_list_free_full (added_volumes, g_object_unref);
+  g_list_free_full (removed_mounts, g_object_unref);
+  g_list_free_full (added_mounts, g_object_unref);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static GUnixMountPoint *
+get_mount_point_for_mount (GUnixMountEntry *mount_entry)
+{
+  GUnixMountPoint *ret = NULL;
+  GList *mount_points, *l;
+
+  mount_points = g_unix_mount_points_get (NULL);
+  for (l = mount_points; l != NULL; l = l->next)
+    {
+      GUnixMountPoint *mount_point = l->data;
+      if (g_strcmp0 (g_unix_mount_get_mount_path (mount_entry),
+                     g_unix_mount_point_get_mount_path (mount_point)) == 0)
+        {
+          ret = mount_point;
+          goto out;
+        }
+    }
+
+ out:
+  for (l = mount_points; l != NULL; l = l->next)
+    {
+      GUnixMountPoint *mount_point = l->data;
+      if (G_LIKELY (mount_point != ret))
+        g_unix_mount_point_free (mount_point);
+    }
+  g_list_free (mount_points);
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gboolean
+should_include (const gchar *mount_path,
+                const gchar *options)
+{
+  gboolean ret = FALSE;
+  const gchar *home_dir = NULL;
+  const gchar *user_name;
+  gsize user_name_len;
+
+  g_return_val_if_fail (mount_path != NULL, FALSE);
+
+  /* The x-gvfs-show option trumps everything else */
+  if (options != NULL)
+    {
+      gchar *value;
+      value = gvfs_storaged_utils_lookup_fstab_options_value (options, "x-gvfs-show");
+      if (value != NULL)
+        {
+          ret = TRUE;
+          g_free (value);
+          goto out;
+        }
+      value = gvfs_storaged_utils_lookup_fstab_options_value (options, "x-gvfs-hide");
+      if (value != NULL)
+        {
+          ret = FALSE;
+          g_free (value);
+          goto out;
+        }
+    }
+
+  /* Never display internal mountpoints */
+  if (g_unix_is_mount_path_system_internal (mount_path))
+    goto out;
+
+  /* Only display things in
+   * - /media; and
+   * - $HOME; and
+   * - /run/media/$USER
+   */
+
+  /* Hide mounts within a subdirectory starting with a "." - suppose it was a purpose to hide this mount */
+  if (g_strstr_len (mount_path, -1, "/.") != NULL)
+    goto out;
+
+  /* Check /media */
+  if (g_str_has_prefix (mount_path, "/media/"))
+    {
+      ret = TRUE;
+      goto out;
+    }
+
+  /* Check home dir */
+  home_dir = g_get_home_dir ();
+  if (home_dir != NULL)
+    {
+      if (g_str_has_prefix (mount_path, home_dir) && mount_path[strlen (home_dir)] == G_DIR_SEPARATOR)
+        {
+          ret = TRUE;
+          goto out;
+        }
+    }
+
+  /* Check /run/media/$USER/ */
+  user_name = g_get_user_name ();
+  user_name_len = strlen (user_name);
+  if (strncmp (mount_path, "/run/media/", sizeof ("/run/media/") - 1) == 0 &&
+      strncmp (mount_path + sizeof ("/run/media/") - 1, user_name, user_name_len) == 0 &&
+      mount_path[sizeof ("/run/media/") - 1 + user_name_len] == '/')
+    {
+      ret = TRUE;
+      goto out;
+    }
+
+ out:
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gboolean
+should_include_mount_point (GVfsStoragedVolumeMonitor  *monitor,
+                            GUnixMountPoint           *mount_point)
+{
+  return should_include (g_unix_mount_point_get_mount_path (mount_point),
+                         g_unix_mount_point_get_options (mount_point));
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gboolean
+should_include_mount (GVfsStoragedVolumeMonitor  *monitor,
+                      GUnixMountEntry           *mount_entry)
+{
+  GUnixMountPoint *mount_point;
+  gboolean ret;
+
+  /* if mounted at the designated mount point, use that info to decide */
+  mount_point = get_mount_point_for_mount (mount_entry);
+  if (mount_point != NULL)
+    {
+      ret = should_include_mount_point (monitor, mount_point);
+      g_unix_mount_point_free (mount_point);
+      goto out;
+    }
+
+  /* otherwise, use the mount's info */
+  ret = should_include (g_unix_mount_get_mount_path (mount_entry),
+                        NULL); /* no mount options yet - see bug 668132 */
+
+ out:
+  return ret;
+}
+
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gboolean
+should_include_volume_check_mount_points (GVfsStoragedVolumeMonitor *monitor,
+                                          StoragedBlock              *block)
+{
+  gboolean ret = TRUE;
+  GDBusObject *obj;
+  StoragedFilesystem *fs;
+  const gchar* const *mount_points;
+  guint n;
+
+  obj = g_dbus_interface_get_object (G_DBUS_INTERFACE (block));
+  if (obj == NULL)
+    goto out;
+
+  fs = storaged_object_peek_filesystem (STORAGED_OBJECT (obj));
+  if (fs == NULL)
+    goto out;
+
+  mount_points = storaged_filesystem_get_mount_points (fs);
+  for (n = 0; mount_points != NULL && mount_points[n] != NULL; n++)
+    {
+      const gchar *mount_point = mount_points[n];
+      GUnixMountEntry *mount_entry;
+
+      mount_entry = g_unix_mount_at (mount_point, NULL);
+      if (mount_entry != NULL)
+        {
+          if (!should_include_mount (monitor, mount_entry))
+            {
+              g_unix_mount_free (mount_entry);
+              ret = FALSE;
+              goto out;
+            }
+          g_unix_mount_free (mount_entry);
+        }
+    }
+
+ out:
+  return ret;
+}
+
+static gboolean
+should_include_volume_check_configuration (GVfsStoragedVolumeMonitor *monitor,
+                                           StoragedBlock              *block)
+{
+  gboolean ret = TRUE;
+  GVariantIter iter;
+  const gchar *configuration_type;
+  GVariant *configuration_value;
+
+  g_variant_iter_init (&iter, storaged_block_get_configuration (block));
+  while (g_variant_iter_next (&iter, "(&s a{sv})", &configuration_type, &configuration_value))
+    {
+      if (g_strcmp0 (configuration_type, "fstab") == 0)
+        {
+          const gchar *fstab_dir;
+          const gchar *fstab_options;
+          if (g_variant_lookup (configuration_value, "dir", "^&ay", &fstab_dir) &&
+              g_variant_lookup (configuration_value, "opts", "^&ay", &fstab_options))
+            {
+              if (!should_include (fstab_dir, fstab_options))
+                {
+                  ret = FALSE;
+                  g_variant_unref (configuration_value);
+                  goto out;
+                }
+            }
+        }
+      g_variant_unref (configuration_value);
+    }
+
+ out:
+  return ret;
+}
+
+static gboolean should_include_drive (GVfsStoragedVolumeMonitor *monitor,
+                                      StoragedDrive              *drive);
+
+static gboolean
+should_include_volume (GVfsStoragedVolumeMonitor *monitor,
+                       StoragedBlock              *block,
+                       gboolean                  allow_encrypted_cleartext)
+{
+  gboolean ret = FALSE;
+  GDBusObject *object;
+  StoragedFilesystem *filesystem;
+  StoragedDrive *storaged_drive = NULL;
+  const gchar* const *mount_points;
+  StoragedLoop *loop = NULL;
+
+  /* Block:Ignore trumps everything */
+  if (storaged_block_get_hint_ignore (block))
+    goto out;
+
+  /* If the device (or if a partition, its containing device) is a
+   * loop device, check the SetupByUid property - we don't want to
+   * show loop devices set up by other users
+   */
+  loop = storaged_client_get_loop_for_block (monitor->client, block);
+  if (loop != NULL)
+    {
+      GDBusObject *loop_object;
+      StoragedBlock *block_for_loop;
+      guint setup_by_uid;
+
+      setup_by_uid = storaged_loop_get_setup_by_uid (loop);
+      if (setup_by_uid != 0 && setup_by_uid != getuid ())
+        goto out;
+
+      /* Work-around bug in Linux where partitions of a loop
+       * device (e.g. /dev/loop0p1) are lingering even when the
+       * parent loop device (e.g. /dev/loop0) has been cleared
+       */
+      loop_object = g_dbus_interface_get_object (G_DBUS_INTERFACE (loop));
+      if (loop_object == NULL)
+        goto out;
+      block_for_loop = storaged_object_peek_block (STORAGED_OBJECT (loop_object));
+      if (block_for_loop == NULL)
+        goto out;
+      if (storaged_block_get_size (block_for_loop) == 0)
+        goto out;
+    }
+
+  /* ignore the volume if the drive is ignored */
+  storaged_drive = storaged_client_get_drive_for_block (monitor->client, block);
+  if (storaged_drive != NULL)
+    {
+      if (!should_include_drive (monitor, storaged_drive))
+        {
+          goto out;
+        }
+    }
+
+  /* show encrypted volumes... */
+  if (g_strcmp0 (storaged_block_get_id_type (block), "crypto_LUKS") == 0)
+    {
+      StoragedBlock *cleartext_block;
+      /* ... unless the volume is unlocked and we don't want to show the cleartext volume */
+      cleartext_block = storaged_client_get_cleartext_block (monitor->client, block);
+      if (cleartext_block != NULL)
+        {
+          ret = should_include_volume (monitor, cleartext_block, TRUE);
+          g_object_unref (cleartext_block);
+        }
+      else
+        {
+          ret = TRUE;
+        }
+      goto out;
+    }
+
+  if (!allow_encrypted_cleartext)
+    {
+      /* ... but not unlocked volumes (because the volume for the encrypted part morphs
+       * into the cleartext part when unlocked)
+       */
+      if (g_strcmp0 (storaged_block_get_crypto_backing_device (block), "/") != 0)
+        {
+          goto out;
+        }
+    }
+
+  /* Check should_include_mount() for all mount points, if any - e.g. if a volume
+   * is mounted in a place where the mount is to be ignored, we ignore the volume
+   * as well
+   */
+  if (!should_include_volume_check_mount_points (monitor, block))
+    goto out;
+
+  object = g_dbus_interface_get_object (G_DBUS_INTERFACE (block));
+  if (object == NULL)
+    goto out;
+
+  filesystem = storaged_object_peek_filesystem (STORAGED_OBJECT (object));
+  if (filesystem == NULL)
+    goto out;
+
+  /* If not mounted but the volume is referenced in /etc/fstab and
+   * that configuration indicates the volume should be ignored, then
+   * do so
+   */
+  mount_points = storaged_filesystem_get_mount_points (filesystem);
+  if (mount_points == NULL || g_strv_length ((gchar **) mount_points) == 0)
+    {
+      if (!should_include_volume_check_configuration (monitor, block))
+        goto out;
+    }
+
+  /* otherwise, we're good to go */
+  ret = TRUE;
+
+ out:
+  g_clear_object (&storaged_drive);
+  g_clear_object (&loop);
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gboolean
+should_include_drive (GVfsStoragedVolumeMonitor *monitor,
+                      StoragedDrive              *drive)
+{
+  gboolean ret = TRUE;
+
+  /* Don't include drives on other seats */
+  if (!gvfs_storaged_utils_is_drive_on_our_seat (drive))
+    {
+      ret = FALSE;
+      goto out;
+    }
+
+  /* NOTE: For now, we just include a drive no matter its
+   * content. This may be wrong ... for example non-removable drives
+   * without anything visible (such RAID components) should probably
+   * not be shown. Then again, the GNOME 3 user interface doesn't
+   * really show GDrive instances except for in the computer:///
+   * location in Nautilus....
+   */
+
+ out:
+
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gint
+storaged_drive_compare (StoragedDrive *a, StoragedDrive *b)
+{
+  GDBusObject *oa = g_dbus_interface_get_object (G_DBUS_INTERFACE (a));
+  GDBusObject *ob = g_dbus_interface_get_object (G_DBUS_INTERFACE (b));
+  /* Either or both of oa, ob can be NULL for the case where a drive
+   * is removed but we still hold a reference to the drive interface
+   */
+  if (oa != NULL && ob != NULL)
+    return g_strcmp0 (g_dbus_object_get_object_path (oa), g_dbus_object_get_object_path (ob));
+  else
+    return (const gchar*) ob - (const gchar*) oa;
+}
+
+static gint
+block_compare (StoragedBlock *a, StoragedBlock *b)
+{
+  return g_strcmp0 (storaged_block_get_device (a), storaged_block_get_device (b));
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static GVfsStoragedVolume *
+find_disc_volume_for_storaged_drive (GVfsStoragedVolumeMonitor *monitor,
+                                   StoragedDrive              *storaged_drive)
+{
+  GVfsStoragedVolume *ret = NULL;
+  GList *l;
+
+  for (l = monitor->disc_volumes; l != NULL; l = l->next)
+    {
+      GVolume *volume = G_VOLUME (l->data);
+      GDrive *drive = g_volume_get_drive (volume);
+      if (drive != NULL)
+        {
+          if (gvfs_storaged_drive_get_storaged_drive (GVFS_STORAGED_DRIVE (drive)) == storaged_drive)
+            {
+              ret = GVFS_STORAGED_VOLUME (volume);
+              g_object_unref (drive);
+              goto out;
+            }
+          g_object_unref (drive);
+        }
+    }
+
+ out:
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static GVfsStoragedMount *
+find_disc_mount_for_storaged_drive (GVfsStoragedVolumeMonitor *monitor,
+                                  StoragedDrive              *storaged_drive)
+{
+  GVfsStoragedMount *ret = NULL;
+  GList *l;
+
+  for (l = monitor->disc_mounts; l != NULL; l = l->next)
+    {
+      GMount *mount = G_MOUNT (l->data);
+      GVolume *volume;
+
+      volume = g_mount_get_volume (mount);
+      if (volume != NULL)
+        {
+          GDrive *drive = g_volume_get_drive (volume);
+          if (drive != NULL)
+            {
+              if (gvfs_storaged_drive_get_storaged_drive (GVFS_STORAGED_DRIVE (drive)) == storaged_drive)
+                {
+                  ret = GVFS_STORAGED_MOUNT (mount);
+                  g_object_unref (volume);
+                  g_object_unref (drive);
+                  goto out;
+                }
+              g_object_unref (drive);
+            }
+          g_object_unref (volume);
+        }
+    }
+
+ out:
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static GVfsStoragedDrive *
+find_drive_for_storaged_drive (GVfsStoragedVolumeMonitor *monitor,
+                             StoragedDrive              *storaged_drive)
+{
+  GVfsStoragedDrive *ret = NULL;
+  GList *l;
+
+  for (l = monitor->drives; l != NULL; l = l->next)
+    {
+      GVfsStoragedDrive *drive = GVFS_STORAGED_DRIVE (l->data);
+      if (gvfs_storaged_drive_get_storaged_drive (drive) == storaged_drive)
+        {
+          ret = drive;
+          goto out;
+        }
+    }
+
+ out:
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static GVfsStoragedVolume *
+find_volume_for_block (GVfsStoragedVolumeMonitor *monitor,
+                       StoragedBlock              *block)
+{
+  GVfsStoragedVolume *ret = NULL;
+  GList *l;
+
+  for (l = monitor->volumes; l != NULL; l = l->next)
+    {
+      GVfsStoragedVolume *volume = GVFS_STORAGED_VOLUME (l->data);
+      if (gvfs_storaged_volume_get_block (volume) == block)
+        {
+          ret = volume;
+          goto out;
+        }
+    }
+
+ out:
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static GVfsStoragedVolume *
+find_fstab_volume_for_mount_point (GVfsStoragedVolumeMonitor *monitor,
+                                   GUnixMountPoint          *mount_point)
+{
+  GVfsStoragedVolume *ret = NULL;
+  GList *l;
+
+  for (l = monitor->fstab_volumes; l != NULL; l = l->next)
+    {
+      GVfsStoragedVolume *volume = GVFS_STORAGED_VOLUME (l->data);
+      if (g_unix_mount_point_compare (gvfs_storaged_volume_get_mount_point (volume), mount_point) == 0)
+        {
+          ret = volume;
+          goto out;
+        }
+    }
+
+ out:
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gboolean
+mount_point_matches_mount_entry (GUnixMountPoint *mount_point,
+                                 GUnixMountEntry *mount_entry)
+{
+  gboolean ret = FALSE;
+  gchar *mp_path = NULL;
+  gchar *mp_entry = NULL;
+
+  mp_path = g_strdup (g_unix_mount_point_get_mount_path (mount_point));
+  mp_entry = g_strdup (g_unix_mount_get_mount_path (mount_entry));
+
+  if (g_str_has_suffix (mp_path, "/"))
+    mp_path[strlen(mp_path) - 1] = '\0';
+  if (g_str_has_suffix (mp_entry, "/"))
+    mp_entry[strlen(mp_entry) - 1] = '\0';
+
+  if (g_strcmp0 (mp_path, mp_entry) != 0)
+    goto out;
+
+  ret = TRUE;
+
+ out:
+  g_free (mp_path);
+  g_free (mp_entry);
+  return ret;
+}
+
+static GVfsStoragedVolume *
+find_fstab_volume_for_mount_entry (GVfsStoragedVolumeMonitor *monitor,
+                                   GUnixMountEntry          *mount_entry)
+{
+  GVfsStoragedVolume *ret = NULL;
+  GList *l;
+
+  for (l = monitor->fstab_volumes; l != NULL; l = l->next)
+    {
+      GVfsStoragedVolume *volume = GVFS_STORAGED_VOLUME (l->data);
+      if (mount_point_matches_mount_entry (gvfs_storaged_volume_get_mount_point (volume), mount_entry))
+        {
+          ret = volume;
+          goto out;
+        }
+    }
+
+ out:
+  return ret;
+}
+
+static GVfsStoragedMount *
+find_lonely_mount_for_mount_point (GVfsStoragedVolumeMonitor *monitor,
+                                   GUnixMountPoint          *mount_point)
+{
+  GVfsStoragedMount *ret = NULL;
+  GList *l;
+
+  for (l = monitor->mounts; l != NULL; l = l->next)
+    {
+      GVfsStoragedMount *mount = GVFS_STORAGED_MOUNT (l->data);
+      if (mount_point != NULL &&
+          mount_point_matches_mount_entry (mount_point, gvfs_storaged_mount_get_mount_entry (mount)))
+        {
+          GVolume *volume = g_mount_get_volume (G_MOUNT (mount));
+          if (volume != NULL)
+            {
+              g_object_unref (volume);
+            }
+          else
+            {
+              ret = mount;
+              goto out;
+            }
+        }
+    }
+
+ out:
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static GVfsStoragedVolume *
+find_volume_for_device (GVfsStoragedVolumeMonitor *monitor,
+                        const gchar              *device)
+{
+  GVfsStoragedVolume *ret = NULL;
+  GList *blocks = NULL;
+  GList *l;
+  struct stat statbuf;
+
+  /* don't consider e.g. network mounts */
+  if (g_str_has_prefix (device, "LABEL="))
+    {
+      blocks = storaged_client_get_block_for_label (monitor->client, device + 6);
+      if (blocks != NULL)
+        device = storaged_block_get_device (STORAGED_BLOCK (blocks->data));
+      else
+        goto out;
+    }
+  else if (g_str_has_prefix (device, "UUID="))
+    {
+      blocks = storaged_client_get_block_for_uuid (monitor->client, device + 6);
+      if (blocks != NULL)
+        device = storaged_block_get_device (STORAGED_BLOCK (blocks->data));
+      else
+        goto out;
+    }
+  else if (!g_str_has_prefix (device, "/dev/"))
+    {
+      goto out;
+    }
+
+  if (stat (device, &statbuf) != 0)
+    goto out;
+
+  for (l = monitor->volumes; l != NULL; l = l->next)
+    {
+      GVfsStoragedVolume *volume = GVFS_STORAGED_VOLUME (l->data);
+      if (gvfs_storaged_volume_get_dev (volume) == statbuf.st_rdev)
+        {
+          ret = volume;
+          goto out;
+        }
+    }
+
+  for (l = monitor->disc_volumes; l != NULL; l = l->next)
+    {
+      GVfsStoragedVolume *volume = GVFS_STORAGED_VOLUME (l->data);
+      if (gvfs_storaged_volume_get_dev (volume) == statbuf.st_rdev)
+        {
+          ret = volume;
+          goto out;
+        }
+    }
+
+ out:
+  g_list_free_full (blocks, g_object_unref);
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static GVfsStoragedMount *
+find_mount_by_mount_path (GVfsStoragedVolumeMonitor *monitor,
+                          const gchar              *mount_path)
+{
+  GVfsStoragedMount *ret = NULL;
+  GList *l;
+
+  for (l = monitor->mounts; l != NULL; l = l->next)
+    {
+      GVfsStoragedMount *mount = GVFS_STORAGED_MOUNT (l->data);
+      if (g_strcmp0 (gvfs_storaged_mount_get_mount_path (mount), mount_path) == 0)
+        {
+          ret = mount;
+          goto out;
+        }
+    }
+ out:
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+update_drives (GVfsStoragedVolumeMonitor  *monitor,
+               GList                    **added_drives,
+               GList                    **removed_drives,
+               gboolean                   coldplug)
+{
+  GList *cur_storaged_drives;
+  GList *new_storaged_drives;
+  GList *removed, *added;
+  GList *l;
+  GVfsStoragedDrive *drive;
+  GList *objects;
+
+  objects = g_dbus_object_manager_get_objects (storaged_client_get_object_manager (monitor->client));
+
+  cur_storaged_drives = NULL;
+  for (l = monitor->drives; l != NULL; l = l->next)
+    {
+      cur_storaged_drives = g_list_prepend (cur_storaged_drives,
+                                          gvfs_storaged_drive_get_storaged_drive (GVFS_STORAGED_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_storaged_drives = NULL;
+  for (l = objects; l != NULL; l = l->next)
+    {
+      StoragedDrive *storaged_drive = storaged_object_peek_drive (STORAGED_OBJECT (l->data));
+      if (storaged_drive == NULL)
+        continue;
+      if (should_include_drive (monitor, storaged_drive))
+        new_storaged_drives = g_list_prepend (new_storaged_drives, storaged_drive);
+    }
+
+  cur_storaged_drives = g_list_sort (cur_storaged_drives, (GCompareFunc) storaged_drive_compare);
+  new_storaged_drives = g_list_sort (new_storaged_drives, (GCompareFunc) storaged_drive_compare);
+  diff_sorted_lists (cur_storaged_drives,
+                     new_storaged_drives, (GCompareFunc) storaged_drive_compare,
+                     &added, &removed, NULL);
+
+  for (l = removed; l != NULL; l = l->next)
+    {
+      StoragedDrive *storaged_drive = STORAGED_DRIVE (l->data);
+
+      drive = find_drive_for_storaged_drive (monitor, storaged_drive);
+      if (drive != NULL)
+        {
+          /*g_debug ("removing drive %s", gdu_presentable_get_id (p));*/
+          gvfs_storaged_drive_disconnected (drive);
+          monitor->drives = g_list_remove (monitor->drives, drive);
+          *removed_drives = g_list_prepend (*removed_drives, g_object_ref (drive));
+          g_object_unref (drive);
+        }
+    }
+
+  for (l = added; l != NULL; l = l->next)
+    {
+      StoragedDrive *storaged_drive = STORAGED_DRIVE (l->data);
+
+      drive = find_drive_for_storaged_drive (monitor, storaged_drive);
+      if (drive == NULL)
+        {
+          /*g_debug ("adding drive %s", gdu_presentable_get_id (p));*/
+          drive = gvfs_storaged_drive_new (monitor, storaged_drive, coldplug);
+          if (storaged_drive != NULL)
+            {
+              monitor->drives = g_list_prepend (monitor->drives, drive);
+              *added_drives = g_list_prepend (*added_drives, g_object_ref (drive));
+            }
+        }
+    }
+
+  g_list_free (added);
+  g_list_free (removed);
+
+  g_list_free (cur_storaged_drives);
+  g_list_free (new_storaged_drives);
+
+  g_list_free_full (objects, g_object_unref);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+update_volumes (GVfsStoragedVolumeMonitor  *monitor,
+                GList                    **added_volumes,
+                GList                    **removed_volumes,
+                gboolean                   coldplug)
+{
+  GList *cur_block_volumes;
+  GList *new_block_volumes;
+  GList *removed, *added;
+  GList *l;
+  GVfsStoragedVolume *volume;
+  GList *objects;
+
+  objects = g_dbus_object_manager_get_objects (storaged_client_get_object_manager (monitor->client));
+
+  cur_block_volumes = NULL;
+  for (l = monitor->volumes; l != NULL; l = l->next)
+    {
+      cur_block_volumes = g_list_prepend (cur_block_volumes,
+                                          gvfs_storaged_volume_get_block (GVFS_STORAGED_VOLUME (l->data)));
+    }
+
+  new_block_volumes = NULL;
+  for (l = objects; l != NULL; l = l->next)
+    {
+      StoragedBlock *block = storaged_object_peek_block (STORAGED_OBJECT (l->data));
+      if (block == NULL)
+        continue;
+      if (should_include_volume (monitor, block, FALSE))
+        new_block_volumes = g_list_prepend (new_block_volumes, block);
+    }
+
+  cur_block_volumes = g_list_sort (cur_block_volumes, (GCompareFunc) block_compare);
+  new_block_volumes = g_list_sort (new_block_volumes, (GCompareFunc) block_compare);
+  diff_sorted_lists (cur_block_volumes,
+                     new_block_volumes, (GCompareFunc) block_compare,
+                     &added, &removed, NULL);
+
+  for (l = removed; l != NULL; l = l->next)
+    {
+      StoragedBlock *block = STORAGED_BLOCK (l->data);
+      volume = find_volume_for_block (monitor, block);
+      if (volume != NULL)
+        {
+          gvfs_storaged_volume_removed (volume);
+          monitor->volumes = g_list_remove (monitor->volumes, volume);
+          *removed_volumes = g_list_prepend (*removed_volumes, g_object_ref (volume));
+          g_object_unref (volume);
+        }
+    }
+
+  for (l = added; l != NULL; l = l->next)
+    {
+      StoragedBlock *block = STORAGED_BLOCK (l->data);
+      volume = find_volume_for_block (monitor, block);
+      if (volume == NULL)
+        {
+          GVfsStoragedDrive *drive = NULL;
+          StoragedDrive *storaged_drive;
+
+          storaged_drive = storaged_client_get_drive_for_block (monitor->client, block);
+          if (storaged_drive != NULL)
+            {
+              drive = find_drive_for_storaged_drive (monitor, storaged_drive);
+              g_object_unref (storaged_drive);
+            }
+          volume = gvfs_storaged_volume_new (monitor,
+                                            block,
+                                            NULL, /* mount_point */
+                                            drive,
+                                            NULL, /* activation_root */
+                                            coldplug);
+          if (volume != NULL)
+            {
+              monitor->volumes = g_list_prepend (monitor->volumes, volume);
+              *added_volumes = g_list_prepend (*added_volumes, g_object_ref (volume));
+            }
+         }
+    }
+
+  g_list_free (added);
+  g_list_free (removed);
+  g_list_free (new_block_volumes);
+  g_list_free (cur_block_volumes);
+
+  g_list_free_full (objects, g_object_unref);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gboolean
+have_storaged_volume_for_mount_point (GVfsStoragedVolumeMonitor *monitor,
+                                    GUnixMountPoint          *mount_point)
+{
+  gboolean ret = FALSE;
+
+  if (find_volume_for_device (monitor, g_unix_mount_point_get_device_path (mount_point)) == NULL)
+    goto out;
+
+  ret = TRUE;
+
+ out:
+  return ret;
+}
+
+static gboolean
+mount_point_has_device (GVfsStoragedVolumeMonitor  *monitor,
+                        GUnixMountPoint           *mount_point)
+{
+  gboolean ret = FALSE;
+  const gchar *device;
+  struct stat statbuf;
+  StoragedBlock *block;
+  GList *blocks = NULL;
+
+  device = g_unix_mount_point_get_device_path (mount_point);
+  if (g_str_has_prefix (device, "LABEL="))
+    {
+      blocks = storaged_client_get_block_for_label (monitor->client, device + 6);
+      if (blocks != NULL)
+        device = storaged_block_get_device (STORAGED_BLOCK (blocks->data));
+      else
+        goto out;
+    }
+  else if (g_str_has_prefix (device, "UUID="))
+    {
+      blocks = storaged_client_get_block_for_uuid (monitor->client, device + 6);
+      if (blocks != NULL)
+        device = storaged_block_get_device (STORAGED_BLOCK (blocks->data));
+      else
+        goto out;
+    }
+  else if (!g_str_has_prefix (device, "/dev/"))
+    {
+      /* NFS, CIFS and other non-device mounts always have a device */
+      ret = TRUE;
+      goto out;
+    }
+
+  if (stat (device, &statbuf) != 0)
+    goto out;
+
+  if (statbuf.st_rdev == 0)
+    goto out;
+
+  /* assume non-existant if media is not available */
+  block = storaged_client_get_block_for_dev (monitor->client, statbuf.st_rdev);
+  if (block != NULL)
+    {
+      StoragedDrive *drive;
+      drive = storaged_client_get_drive_for_block (monitor->client, block);
+      if (drive != NULL)
+        {
+          if (!storaged_drive_get_media_available (drive))
+            {
+              g_object_unref (drive);
+              g_object_unref (block);
+              goto out;
+            }
+          g_object_unref (drive);
+        }
+      g_object_unref (block);
+    }
+  else
+    {
+      /* not known by udisks, assume media is available */
+    }
+
+  ret = TRUE;
+
+ out:
+  g_list_free_full (blocks, g_object_unref);
+  return ret;
+}
+
+static void
+update_fstab_volumes (GVfsStoragedVolumeMonitor  *monitor,
+                      GList                    **added_volumes,
+                      GList                    **removed_volumes,
+                      gboolean                   coldplug)
+{
+  GList *cur_mount_points;
+  GList *new_mount_points;
+  GList *added;
+  GList *removed;
+  GList *l, *ll;
+  GVfsStoragedVolume *volume;
+
+  cur_mount_points = NULL;
+  for (l = monitor->fstab_volumes; l != NULL; l = l->next)
+    {
+      GUnixMountPoint *mount_point = gvfs_storaged_volume_get_mount_point (GVFS_STORAGED_VOLUME (l->data));
+      if (mount_point != NULL)
+        cur_mount_points = g_list_prepend (cur_mount_points, mount_point);
+    }
+
+  new_mount_points = g_unix_mount_points_get (NULL);
+  /* filter the mount points that we don't want to include */
+  for (l = new_mount_points; l != NULL; l = ll)
+    {
+      GUnixMountPoint *mount_point = l->data;
+
+      ll = l->next;
+
+      if (!should_include_mount_point (monitor, mount_point) ||
+          have_storaged_volume_for_mount_point (monitor, mount_point) ||
+          !mount_point_has_device (monitor, mount_point))
+        {
+          new_mount_points = g_list_remove_link (new_mount_points, l);
+          g_unix_mount_point_free (mount_point);
+        }
+    }
+
+  cur_mount_points = g_list_sort (cur_mount_points, (GCompareFunc) g_unix_mount_point_compare);
+  new_mount_points = g_list_sort (new_mount_points, (GCompareFunc) g_unix_mount_point_compare);
+  diff_sorted_lists (cur_mount_points,
+                     new_mount_points, (GCompareFunc) g_unix_mount_point_compare,
+                     &added, &removed, NULL);
+
+  for (l = removed; l != NULL; l = l->next)
+    {
+      GUnixMountPoint *mount_point = l->data;
+      volume = find_fstab_volume_for_mount_point (monitor, mount_point);
+      if (volume != NULL)
+        {
+          gvfs_storaged_volume_removed (volume);
+          monitor->fstab_volumes = g_list_remove (monitor->fstab_volumes, volume);
+          *removed_volumes = g_list_prepend (*removed_volumes, g_object_ref (volume));
+          g_object_unref (volume);
+        }
+    }
+
+  for (l = added; l != NULL; l = l->next)
+    {
+      GUnixMountPoint *mount_point = l->data;
+
+      volume = find_fstab_volume_for_mount_point (monitor, mount_point);
+      if (volume != NULL)
+        continue;
+
+      volume = gvfs_storaged_volume_new (monitor,
+                                        NULL,        /* block */
+                                        mount_point,
+                                        NULL,        /* drive */
+                                        NULL,        /* activation_root */
+                                        coldplug);
+      if (volume != NULL)
+        {
+          GVfsStoragedMount *mount;
+
+          monitor->fstab_volumes = g_list_prepend (monitor->fstab_volumes, volume);
+          *added_volumes = g_list_prepend (*added_volumes, g_object_ref (volume));
+          /* since @volume takes ownership of @mount_point, don't free it below */
+          new_mount_points = g_list_remove (new_mount_points, mount_point);
+
+          /* Could be there's already a mount for this volume - for example, the
+           * user could just have added it to the /etc/fstab file
+           */
+          mount = find_lonely_mount_for_mount_point (monitor, mount_point);
+          if (mount != NULL)
+            gvfs_storaged_mount_set_volume (mount, volume);
+        }
+    }
+
+  g_list_free_full (new_mount_points, (GDestroyNotify) g_unix_mount_point_free);
+
+  g_list_free (cur_mount_points);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+update_mounts (GVfsStoragedVolumeMonitor  *monitor,
+               GList                    **added_mounts,
+               GList                    **removed_mounts,
+               gboolean                   coldplug)
+{
+  GList *cur_mounts;
+  GList *new_mounts;
+  GList *removed, *added, *unchanged;
+  GList *l, *ll;
+  GVfsStoragedMount *mount;
+  GVfsStoragedVolume *volume;
+
+  cur_mounts = NULL;
+  for (l = monitor->mounts; l != NULL; l = l->next)
+    {
+      GUnixMountEntry *mount_entry;
+
+      mount = GVFS_STORAGED_MOUNT (l->data);
+      mount_entry = gvfs_storaged_mount_get_mount_entry (mount);
+      if (mount_entry != NULL)
+        cur_mounts = g_list_prepend (cur_mounts, mount_entry);
+    }
+
+  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;
+      if (!should_include_mount (monitor, mount_entry))
+        {
+          g_unix_mount_free (mount_entry);
+          new_mounts = g_list_delete_link (new_mounts, l);
+        }
+    }
+
+  cur_mounts = g_list_sort (cur_mounts, (GCompareFunc) g_unix_mount_compare);
+  new_mounts = g_list_sort (new_mounts, (GCompareFunc) g_unix_mount_compare);
+  diff_sorted_lists (cur_mounts,
+                     new_mounts, (GCompareFunc) g_unix_mount_compare,
+                     &added, &removed, &unchanged);
+
+  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 != NULL)
+        {
+          gvfs_storaged_mount_unmounted (mount);
+          monitor->mounts = g_list_remove (monitor->mounts, mount);
+          *removed_mounts = g_list_prepend (*removed_mounts, g_object_ref (mount));
+          /*g_debug ("removed mount at %s", gvfs_storaged_mount_get_mount_path (mount));*/
+          g_object_unref (mount);
+        }
+    }
+
+  for (l = added; l != NULL; l = l->next)
+    {
+      GUnixMountEntry *mount_entry = l->data;
+      volume = find_volume_for_device (monitor, g_unix_mount_get_device_path (mount_entry));
+      if (volume == NULL)
+        volume = find_fstab_volume_for_mount_entry (monitor, mount_entry);
+      mount = gvfs_storaged_mount_new (monitor, mount_entry, volume); /* adopts mount_entry */
+      if (mount != NULL)
+        {
+          monitor->mounts = g_list_prepend (monitor->mounts, mount);
+          *added_mounts = g_list_prepend (*added_mounts, g_object_ref (mount));
+          /*g_debug ("added mount at %s for %p", gvfs_storaged_mount_get_mount_path (mount), volume);*/
+          /* since @mount takes ownership of @mount_entry, don't free it below */
+          new_mounts = g_list_remove (new_mounts, mount_entry);
+        }
+    }
+
+  /* Handle the case where the volume containing the mount appears *after*
+   * the mount.
+   *
+   * This can happen when unlocking+mounting a LUKS device and the two
+   * operations are *right* after each other. In that case we get the
+   * event from GUnixMountMonitor (which monitors /proc/mounts) before
+   * the event from udisks.
+   */
+  for (l = unchanged; 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 == NULL)
+        {
+          g_warning ("No mount object for path %s", g_unix_mount_get_mount_path (mount_entry));
+          continue;
+        }
+      if (gvfs_storaged_mount_get_volume (mount) == NULL)
+        {
+          volume = find_volume_for_device (monitor, g_unix_mount_get_device_path (mount_entry));
+          if (volume == NULL)
+            volume = find_fstab_volume_for_mount_entry (monitor, mount_entry);
+          if (volume != NULL)
+            gvfs_storaged_mount_set_volume (mount, volume);
+        }
+    }
+
+  g_list_free (added);
+  g_list_free (removed);
+  g_list_free (unchanged);
+
+  g_list_free_full (new_mounts, (GDestroyNotify) g_unix_mount_free);
+
+  g_list_free (cur_mounts);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+update_discs (GVfsStoragedVolumeMonitor  *monitor,
+              GList                    **added_volumes,
+              GList                    **removed_volumes,
+              GList                    **added_mounts,
+              GList                    **removed_mounts,
+              gboolean                   coldplug)
+{
+  GList *objects;
+  GList *cur_disc_drives;
+  GList *new_disc_drives;
+  GList *removed, *added;
+  GList *l;
+  GVfsStoragedDrive *drive;
+  GVfsStoragedVolume *volume;
+  GVfsStoragedMount *mount;
+
+  /* we also need to generate GVolume + GMount objects for
+   *
+   * - optical discs with audio
+   * - optical discs that are blank
+   *
+   */
+
+  objects = g_dbus_object_manager_get_objects (storaged_client_get_object_manager (monitor->client));
+
+  cur_disc_drives = NULL;
+  for (l = monitor->disc_volumes; l != NULL; l = l->next)
+    {
+      volume = GVFS_STORAGED_VOLUME (l->data);
+      drive = GVFS_STORAGED_DRIVE (g_volume_get_drive (G_VOLUME (volume)));
+      if (drive != NULL)
+        {
+          cur_disc_drives = g_list_prepend (cur_disc_drives, gvfs_storaged_drive_get_storaged_drive (drive));
+          g_object_unref (drive);
+        }
+    }
+
+  new_disc_drives = NULL;
+  for (l = objects; l != NULL; l = l->next)
+    {
+      StoragedDrive *storaged_drive = storaged_object_peek_drive (STORAGED_OBJECT (l->data));
+      if (storaged_drive == NULL)
+        continue;
+
+      if (!should_include_drive (monitor, storaged_drive))
+        continue;
+
+      /* only consider blank and audio discs */
+      if (!(storaged_drive_get_optical_blank (storaged_drive) ||
+            storaged_drive_get_optical_num_audio_tracks (storaged_drive) > 0))
+        continue;
+
+      new_disc_drives = g_list_prepend (new_disc_drives, storaged_drive);
+    }
+
+  cur_disc_drives = g_list_sort (cur_disc_drives, (GCompareFunc) storaged_drive_compare);
+  new_disc_drives = g_list_sort (new_disc_drives, (GCompareFunc) storaged_drive_compare);
+  diff_sorted_lists (cur_disc_drives, new_disc_drives, (GCompareFunc) storaged_drive_compare,
+                     &added, &removed, NULL);
+
+  for (l = removed; l != NULL; l = l->next)
+    {
+      StoragedDrive *storaged_drive = STORAGED_DRIVE (l->data);
+
+      volume = find_disc_volume_for_storaged_drive (monitor, storaged_drive);
+      mount = find_disc_mount_for_storaged_drive (monitor, storaged_drive);
+
+      if (mount != NULL)
+        {
+          gvfs_storaged_mount_unmounted (mount);
+          monitor->disc_mounts = g_list_remove (monitor->disc_mounts, mount);
+          *removed_mounts = g_list_prepend (*removed_mounts, mount);
+        }
+      if (volume != NULL)
+        {
+          gvfs_storaged_volume_removed (volume);
+          monitor->disc_volumes = g_list_remove (monitor->disc_volumes, volume);
+          *removed_volumes = g_list_prepend (*removed_volumes, volume);
+        }
+    }
+
+  for (l = added; l != NULL; l = l->next)
+    {
+      StoragedDrive *storaged_drive = STORAGED_DRIVE (l->data);
+      StoragedBlock *block;
+
+      block = storaged_client_get_block_for_drive (monitor->client, storaged_drive, FALSE);
+      if (block != NULL)
+        {
+          volume = find_disc_volume_for_storaged_drive (monitor, storaged_drive);
+          if (volume == NULL)
+            {
+              gchar *uri;
+              GFile *activation_root;
+              if (storaged_drive_get_optical_blank (storaged_drive))
+                {
+                  uri = g_strdup ("burn://");
+                }
+              else
+                {
+                  gchar *basename = g_path_get_basename (storaged_block_get_device (block));
+                  uri = g_strdup_printf ("cdda://%s", basename);
+                  g_free (basename);
+                }
+              activation_root = g_file_new_for_uri (uri);
+              volume = gvfs_storaged_volume_new (monitor,
+                                                block,
+                                                NULL, /* mount_point */
+                                                find_drive_for_storaged_drive (monitor, storaged_drive),
+                                                activation_root,
+                                                coldplug);
+              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 (storaged_drive_get_optical_blank (storaged_drive))
+                    {
+                      mount = gvfs_storaged_mount_new (monitor,
+                                                      NULL, /* GUnixMountEntry */
+                                                      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 (activation_root);
+              g_free (uri);
+            }
+          g_object_unref (block);
+        }
+    }
+
+  g_list_free (added);
+  g_list_free (removed);
+
+  g_list_free (new_disc_drives);
+  g_list_free (cur_disc_drives);
+
+  g_list_free_full (objects, g_object_unref);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
diff --git a/monitor/storaged/gvfsstoragedvolumemonitor.h b/monitor/storaged/gvfsstoragedvolumemonitor.h
new file mode 100644
index 0000000..0e05505
--- /dev/null
+++ b/monitor/storaged/gvfsstoragedvolumemonitor.h
@@ -0,0 +1,55 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* gvfs - extensions for gio
+ *
+ * Copyright (C) 2006-2012 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., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#ifndef __GVFS_STORAGED_VOLUME_MONITOR_H__
+#define __GVFS_STORAGED_VOLUME_MONITOR_H__
+
+#include <glib-object.h>
+#include <gio/gio.h>
+#include <gio/gunixmounts.h>
+
+#include <storaged/storaged.h>
+#include <gudev/gudev.h>
+
+G_BEGIN_DECLS
+
+#define GVFS_TYPE_STORAGED_VOLUME_MONITOR  (gvfs_storaged_volume_monitor_get_type ())
+#define GVFS_STORAGED_VOLUME_MONITOR(o)    (G_TYPE_CHECK_INSTANCE_CAST ((o), 
GVFS_TYPE_STORAGED_VOLUME_MONITOR, GVfsStoragedVolumeMonitor))
+#define GVFS_IS_STORAGED_VOLUME_MONITOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), 
GVFS_TYPE_STORAGED_VOLUME_MONITOR))
+
+typedef struct _GVfsStoragedVolumeMonitor GVfsStoragedVolumeMonitor;
+
+/* Forward definitions */
+typedef struct _GVfsStoragedDrive GVfsStoragedDrive;
+typedef struct _GVfsStoragedVolume GVfsStoragedVolume;
+typedef struct _GVfsStoragedMount GVfsStoragedMount;
+
+GType           gvfs_storaged_volume_monitor_get_type          (void) G_GNUC_CONST;
+GVolumeMonitor *gvfs_storaged_volume_monitor_new               (void);
+StoragedClient   *gvfs_storaged_volume_monitor_get_storaged_client (GVfsStoragedVolumeMonitor *monitor);
+void            gvfs_storaged_volume_monitor_update            (GVfsStoragedVolumeMonitor *monitor);
+GUdevClient    *gvfs_storaged_volume_monitor_get_gudev_client  (GVfsStoragedVolumeMonitor *monitor);
+
+G_END_DECLS
+
+#endif /* __GVFS_STORAGED_VOLUME_MONITOR_H__ */
diff --git a/monitor/storaged/org.gtk.vfs.StoragedVolumeMonitor.service.in 
b/monitor/storaged/org.gtk.vfs.StoragedVolumeMonitor.service.in
new file mode 100644
index 0000000..5361779
--- /dev/null
+++ b/monitor/storaged/org.gtk.vfs.StoragedVolumeMonitor.service.in
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name=org.gtk.vfs.StoragedVolumeMonitor
+Exec= libexecdir@/gvfs-storaged-volume-monitor
diff --git a/monitor/storaged/storaged.monitor b/monitor/storaged/storaged.monitor
new file mode 100644
index 0000000..67cd536
--- /dev/null
+++ b/monitor/storaged/storaged.monitor
@@ -0,0 +1,5 @@
+[RemoteVolumeMonitor]
+Name=GProxyVolumeMonitorStoraged
+DBusName=org.gtk.vfs.StoragedVolumeMonitor
+IsNative=true
+NativePriority=5
diff --git a/monitor/storaged/storagedvolumemonitordaemon.c b/monitor/storaged/storagedvolumemonitordaemon.c
new file mode 100644
index 0000000..d0c4152
--- /dev/null
+++ b/monitor/storaged/storagedvolumemonitordaemon.c
@@ -0,0 +1,46 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* gvfs - extensions for gio
+ *
+ * Copyright (C) 2006-2012 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., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, 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 "gvfsstoragedvolumemonitor.h"
+
+int
+main (int argc, char *argv[])
+{
+  g_vfs_proxy_volume_monitor_daemon_init ();
+
+  g_set_application_name (_("GVfs Storaged Volume Monitor"));
+
+  return g_vfs_proxy_volume_monitor_daemon_main (argc,
+                                                 argv,
+                                                 "org.gtk.vfs.StoragedVolumeMonitor",
+                                                 GVFS_TYPE_STORAGED_VOLUME_MONITOR);
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]