[gvfs] Bug 587484 – Interaction when unmounting mounts and misc fixes



commit 8af5a5581e11d9c07e8ba3a17eb2cc93b5c70631
Author: David Zeuthen <davidz redhat com>
Date:   Tue Jun 30 23:19:01 2009 -0400

    Bug 587484 â?? Interaction when unmounting mounts and misc fixes
    
    - Port everything to use _with_operation() variants of unmount/eject
       methods
    
     - Add support for g_file_poll_mountable()
       - new job class: GVfsJobPollMountable
    
     - Pass mount operation for unmount/eject ops on GDaemonFile and
       GDaemonMount
       - receive in the appropriate GVfsJob classes
       - also pass unmount flags where it was missing
       - port all backends to use this
    
     - Teach GMountSource and GMountOperationDBus about the new
       GMountOperation::show-processes signal
       - also provide new API
         - g_mount_source_is_dummy() - e.g. when the client didn't
           passed NULL for the GMountOperation
         - g_mount_source_abort() - to send the ::abort signal to the
           client-side GMountOperation
         - make the client-side of GMountSource return ::reply with
           NOT_HANDLED when we do an abort
    
     - Refactor the mount operation handling in GProxyVolumeMonitor
    
     - Pass mount operation for unmount/ejects in GProxyVolumeMonitor
    
     - Pass the process id of the actual reader/writer in OpenForRead
       and OpenForWrite daemon methods
       - add some private API for making the FUSE client set the
         pid of the POSIX client (otherwise it looks like the FUSE
         client is blocking) and pass the right pid.
    
         This is because the FUSE client is basically impersonating the
         POSIX processes.
    
     - Make the process id mentioned above available in appropriate
       GVfsJob classes
       - GVfsJobOpenForRead
       - GVfsJobOpenForWrite
       - GVfsChannel
    
     - Provide API to get a list of all blocking clients, e.g. an array
       of GPid
       - g_vfs_daemon_get_blocking_processes()
    
     - Provide convenience API to easily doing the right thing on unmount;
       e.g. interact with the user about blocking processes - see the gphoto2
       backend for example usage
       - g_vfs_backend_has_blocking_processes()
       - g_vfs_backend_unmount_with_operation() and
         g_vfs_backend_unmount_with_operation_finish()
    
     - Only the gphoto2 backend supports ::show-processes right now. Support
       for other backends will be added shortly.
    
     - Implement support for ::show-processes in the GDU volume monitor
       - right now we don't support "Unmount Anyway" since it requires
         ABI changes in libgdu.so - this will be changed as soon as there's
         a new gnome-disk-utility release

 client/gdaemonfile.c                         |  215 +++++++-
 client/gdaemonmount.c                        |   50 ++-
 client/gvfsfusedaemon.c                      |   25 +
 common/gmountoperationdbus.c                 |   89 +++
 common/gmountsource.c                        |  254 ++++++++-
 common/gmountsource.h                        |   25 +
 common/gvfsdaemonprotocol.h                  |    2 +
 daemon/Makefile.am                           |    1 +
 daemon/gvfsbackend.c                         |  250 ++++++++
 daemon/gvfsbackend.h                         |   43 ++-
 daemon/gvfsbackendarchive.c                  |    4 +-
 daemon/gvfsbackendcdda.c                     |    4 +-
 daemon/gvfsbackendcomputer.c                 |  178 +++++-
 daemon/gvfsbackenddav.c                      |    4 +-
 daemon/gvfsbackendftp.c                      |    4 +-
 daemon/gvfsbackendgphoto2.c                  |   96 +++-
 daemon/gvfsbackendlocaltest.c                |    4 +-
 daemon/gvfschannel.c                         |   30 +-
 daemon/gvfschannel.h                         |    1 +
 daemon/gvfsdaemon.c                          |   32 +
 daemon/gvfsdaemon.h                          |    1 +
 daemon/gvfsjobopenforread.c                  |   12 +-
 daemon/gvfsjobopenforread.h                  |    3 +
 daemon/gvfsjobopenforwrite.c                 |   12 +-
 daemon/gvfsjobopenforwrite.h                 |    3 +
 daemon/gvfsjobpollmountable.c                |  159 ++++++
 daemon/gvfsjobpollmountable.h                |   63 ++
 daemon/gvfsjobstopmountable.c                |   13 +-
 daemon/gvfsjobstopmountable.h                |    1 +
 daemon/gvfsjobunmount.c                      |   41 ++-
 daemon/gvfsjobunmount.h                      |    2 +
 daemon/gvfsjobunmountmountable.c             |   19 +-
 daemon/gvfsjobunmountmountable.h             |    1 +
 daemon/gvfsreadchannel.c                     |    4 +-
 daemon/gvfsreadchannel.h                     |    3 +-
 daemon/gvfswritechannel.c                    |    4 +-
 daemon/gvfswritechannel.h                    |    3 +-
 monitor/gdu/ggdudrive.c                      |   74 ++-
 monitor/gdu/ggdumount.c                      |  621 +++++++++++++++-----
 monitor/gdu/ggduvolume.c                     |   45 +-
 monitor/hal/ghaldrive.c                      |   49 ++-
 monitor/hal/ghalmount.c                      |   76 ++-
 monitor/hal/ghalvolume.c                     |   41 +-
 monitor/proxy/Makefile.am                    |    1 +
 monitor/proxy/gproxydrive.c                  |  331 +++---------
 monitor/proxy/gproxymount.c                  |   87 +++-
 monitor/proxy/gproxymountoperation.c         |  438 ++++++++++++++
 monitor/proxy/gproxymountoperation.h         |   54 ++
 monitor/proxy/gproxyshadowmount.c            |   91 +++-
 monitor/proxy/gproxyvolume.c                 |  299 ++---------
 monitor/proxy/gproxyvolume.h                 |    9 -
 monitor/proxy/gproxyvolumemonitor.c          |   78 ++--
 monitor/proxy/gvfsproxyvolumemonitordaemon.c |  787 ++++++++++----------------
 programs/gvfs-mount.c                        |    7 +-
 54 files changed, 3319 insertions(+), 1424 deletions(-)
---
diff --git a/client/gdaemonfile.c b/client/gdaemonfile.c
index 592b6d6..8e14929 100644
--- a/client/gdaemonfile.c
+++ b/client/gdaemonfile.c
@@ -81,6 +81,29 @@ g_daemon_file_init (GDaemonFile *daemon_file)
 {
 }
 
+static guint32
+get_pid_for_file (GFile *file)
+{
+  guint32 pid;
+
+  pid = 0;
+  if (file == NULL)
+    goto out;
+
+  /* The fuse client sets this to convey the pid of the client - see
+   * set_pid_for_file() in gvfsfusedaemon.c
+   */
+  pid = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (file), "gvfs-fuse-client-pid"));
+  if (pid != 0)
+    goto out;
+
+  /* otherwise assume the client is this process */
+  pid = (guint32) getpid ();
+
+ out:
+  return pid;
+}
+
 GFile *
 g_daemon_file_new (GMountSpec *mount_spec,
 		   const char *path)
@@ -1010,11 +1033,16 @@ g_daemon_file_read_async (GFile *file,
 			  GAsyncReadyCallback callback,
 			  gpointer callback_data)
 {
+  guint32 pid;
+
+  pid = get_pid_for_file (file);
+
   do_async_path_call (file,
 		      G_VFS_DBUS_MOUNT_OP_OPEN_FOR_READ,
 		      cancellable,
 		      callback, callback_data,
 		      read_async_cb, NULL, NULL,
+                      DBUS_TYPE_UINT32, &pid,
 		      0);
 }
 
@@ -1044,11 +1072,15 @@ g_daemon_file_read (GFile *file,
   DBusMessage *reply;
   guint32 fd_id;
   dbus_bool_t can_seek;
+  guint32 pid;
+
+  pid = get_pid_for_file (file);
 
   reply = do_sync_path_call (file, 
 			     G_VFS_DBUS_MOUNT_OP_OPEN_FOR_READ,
 			     NULL, &connection,
 			     cancellable, error,
+                             DBUS_TYPE_UINT32, &pid,
 			     0);
   if (reply == NULL)
     return NULL;
@@ -1093,6 +1125,9 @@ g_daemon_file_append_to (GFile *file,
   dbus_bool_t make_backup;
   guint32 dbus_flags;
   char *etag;
+  guint32 pid;
+
+  pid = get_pid_for_file (file);
 
   mode = 1;
   etag = "";
@@ -1107,6 +1142,7 @@ g_daemon_file_append_to (GFile *file,
 			     DBUS_TYPE_STRING, &etag,
 			     DBUS_TYPE_BOOLEAN, &make_backup,
 			     DBUS_TYPE_UINT32, &dbus_flags,
+                             DBUS_TYPE_UINT32, &pid,
 			     0);
   if (reply == NULL)
     return NULL;
@@ -1152,6 +1188,9 @@ g_daemon_file_create (GFile *file,
   dbus_bool_t make_backup;
   char *etag;
   guint32 dbus_flags;
+  guint32 pid;
+
+  pid = get_pid_for_file (file);
 
   mode = 0;
   etag = "";
@@ -1166,6 +1205,7 @@ g_daemon_file_create (GFile *file,
 			     DBUS_TYPE_STRING, &etag,
 			     DBUS_TYPE_BOOLEAN, &make_backup,
 			     DBUS_TYPE_UINT32, &dbus_flags,
+                             DBUS_TYPE_UINT32, &pid,
 			     0);
   if (reply == NULL)
     return NULL;
@@ -1212,6 +1252,9 @@ g_daemon_file_replace (GFile *file,
   guint64 initial_offset;
   dbus_bool_t dbus_make_backup;
   guint32 dbus_flags;
+  guint32 pid;
+
+  pid = get_pid_for_file (file);
 
   mode = 2;
   dbus_make_backup = make_backup;
@@ -1228,6 +1271,7 @@ g_daemon_file_replace (GFile *file,
 			     DBUS_TYPE_STRING, &etag,
 			     DBUS_TYPE_BOOLEAN, &dbus_make_backup,
 			     DBUS_TYPE_UINT32, &dbus_flags,
+                             DBUS_TYPE_UINT32, &pid,
 			     0);
   if (reply == NULL)
     return NULL;
@@ -1405,7 +1449,7 @@ start_mountable_async_cb (DBusMessage *reply,
 static void
 g_daemon_file_start_mountable (GFile               *file,
 			       GDriveStartFlags     flags,
-			       GMountOperation     *start_operation,
+			       GMountOperation     *mount_operation,
 			       GCancellable        *cancellable,
 			       GAsyncReadyCallback  callback,
 			       gpointer             user_data)
@@ -1413,20 +1457,20 @@ g_daemon_file_start_mountable (GFile               *file,
   GMountSource *mount_source;
   const char *dbus_id, *obj_path;
 
-  mount_source = g_mount_operation_dbus_wrap (start_operation, _g_daemon_vfs_get_async_bus ());
+  mount_source = g_mount_operation_dbus_wrap (mount_operation, _g_daemon_vfs_get_async_bus ());
 
   dbus_id = g_mount_source_get_dbus_id (mount_source);
   obj_path = g_mount_source_get_obj_path (mount_source);
 
-  if (start_operation)
-    g_object_ref (start_operation);
+  if (mount_operation)
+    g_object_ref (mount_operation);
 
   do_async_path_call (file,
 		      G_VFS_DBUS_MOUNT_OP_START_MOUNTABLE,
 		      cancellable,
 		      callback, user_data,
 		      start_mountable_async_cb,
-		      start_operation, start_operation ? g_object_unref : NULL,
+		      mount_operation, mount_operation ? g_object_unref : NULL,
 		      DBUS_TYPE_STRING, &dbus_id,
 		      DBUS_TYPE_OBJECT_PATH, &obj_path,
 		      0);
@@ -1455,11 +1499,22 @@ stop_mountable_async_cb (DBusMessage *reply,
 static void
 g_daemon_file_stop_mountable (GFile               *file,
                               GMountUnmountFlags   flags,
+                              GMountOperation     *mount_operation,
                               GCancellable        *cancellable,
                               GAsyncReadyCallback  callback,
                               gpointer             user_data)
 {
   guint32 dbus_flags;
+  GMountSource *mount_source;
+  const char *dbus_id, *obj_path;
+
+  mount_source = g_mount_operation_dbus_wrap (mount_operation, _g_daemon_vfs_get_async_bus ());
+
+  dbus_id = g_mount_source_get_dbus_id (mount_source);
+  obj_path = g_mount_source_get_obj_path (mount_source);
+
+  if (mount_operation)
+    g_object_ref (mount_operation);
 
   dbus_flags = flags;
   do_async_path_call (file,
@@ -1467,9 +1522,12 @@ g_daemon_file_stop_mountable (GFile               *file,
 		      cancellable,
 		      callback, user_data,
 		      stop_mountable_async_cb,
-		      NULL, NULL,
+		      mount_operation, mount_operation ? g_object_unref : NULL,
 		      DBUS_TYPE_UINT32, &dbus_flags,
+                      DBUS_TYPE_STRING, &dbus_id, DBUS_TYPE_OBJECT_PATH, &obj_path,
 		      0);
+
+  g_object_unref (mount_source);
 }
 
 static gboolean
@@ -1492,13 +1550,24 @@ eject_mountable_async_cb (DBusMessage *reply,
 }
 
 static void
-g_daemon_file_eject_mountable (GFile               *file,
-			       GMountUnmountFlags   flags,
-			       GCancellable        *cancellable,
-			       GAsyncReadyCallback  callback,
-			       gpointer             user_data)
+g_daemon_file_eject_mountable_with_operation (GFile               *file,
+                                              GMountUnmountFlags   flags,
+                                              GMountOperation     *mount_operation,
+                                              GCancellable        *cancellable,
+                                              GAsyncReadyCallback  callback,
+                                              gpointer             user_data)
 {
   guint32 dbus_flags;
+  GMountSource *mount_source;
+  const char *dbus_id, *obj_path;
+
+  mount_source = g_mount_operation_dbus_wrap (mount_operation, _g_daemon_vfs_get_async_bus ());
+
+  dbus_id = g_mount_source_get_dbus_id (mount_source);
+  obj_path = g_mount_source_get_obj_path (mount_source);
+
+  if (mount_operation)
+    g_object_ref (mount_operation);
 
   dbus_flags = flags;
   do_async_path_call (file,
@@ -1506,9 +1575,30 @@ g_daemon_file_eject_mountable (GFile               *file,
 		      cancellable,
 		      callback, user_data,
 		      eject_mountable_async_cb,
-		      NULL, NULL,
+		      mount_operation, mount_operation ? g_object_unref : NULL,
 		      DBUS_TYPE_UINT32, &dbus_flags,
+                      DBUS_TYPE_STRING, &dbus_id, DBUS_TYPE_OBJECT_PATH, &obj_path,
 		      0);
+
+  g_object_unref (mount_source);
+}
+
+static gboolean
+g_daemon_file_eject_mountable_with_operation_finish (GFile               *file,
+                                                     GAsyncResult        *result,
+                                                     GError             **error)
+{
+  return TRUE;
+}
+
+static void
+g_daemon_file_eject_mountable (GFile               *file,
+			       GMountUnmountFlags   flags,
+			       GCancellable        *cancellable,
+			       GAsyncReadyCallback  callback,
+			       gpointer             user_data)
+{
+  g_daemon_file_eject_mountable_with_operation (file, flags, NULL, cancellable, callback, user_data);
 }
 
 static gboolean
@@ -1516,7 +1606,7 @@ g_daemon_file_eject_mountable_finish (GFile               *file,
 				      GAsyncResult        *result,
 				      GError             **error)
 {
-  return TRUE;
+  return g_daemon_file_eject_mountable_with_operation_finish (file, result, error);
 }
 
 static void
@@ -1530,13 +1620,24 @@ unmount_mountable_async_cb (DBusMessage *reply,
 }
 
 static void
-g_daemon_file_unmount_mountable (GFile               *file,
-				 GMountUnmountFlags   flags,
-				 GCancellable        *cancellable,
-				 GAsyncReadyCallback  callback,
-				 gpointer             user_data)
+g_daemon_file_unmount_mountable_with_operation (GFile               *file,
+                                                GMountUnmountFlags   flags,
+                                                GMountOperation     *mount_operation,
+                                                GCancellable        *cancellable,
+                                                GAsyncReadyCallback  callback,
+                                                gpointer             user_data)
 {
   guint32 dbus_flags;
+  GMountSource *mount_source;
+  const char *dbus_id, *obj_path;
+
+  mount_source = g_mount_operation_dbus_wrap (mount_operation, _g_daemon_vfs_get_async_bus ());
+
+  dbus_id = g_mount_source_get_dbus_id (mount_source);
+  obj_path = g_mount_source_get_obj_path (mount_source);
+
+  if (mount_operation)
+    g_object_ref (mount_operation);
 
   dbus_flags = flags;
   do_async_path_call (file,
@@ -1544,9 +1645,64 @@ g_daemon_file_unmount_mountable (GFile               *file,
 		      cancellable,
 		      callback, user_data,
 		      unmount_mountable_async_cb,
-		      NULL, NULL,
+		      mount_operation, mount_operation ? g_object_unref : NULL,
 		      DBUS_TYPE_UINT32, &dbus_flags,
+                      DBUS_TYPE_STRING, &dbus_id, DBUS_TYPE_OBJECT_PATH, &obj_path,
 		      0);
+
+  g_object_unref (mount_source);
+}
+
+static gboolean
+g_daemon_file_unmount_mountable_with_operation_finish (GFile               *file,
+                                                       GAsyncResult        *result,
+                                                       GError             **error)
+{
+  return TRUE;
+}
+
+static void
+poll_mountable_async_cb (DBusMessage *reply,
+                         DBusConnection *connection,
+                         GSimpleAsyncResult *result,
+                         GCancellable *cancellable,
+                         gpointer callback_data)
+{
+  _g_simple_async_result_complete_with_cancellable (result, cancellable);
+}
+
+static void
+g_daemon_file_poll_mountable (GFile               *file,
+                              GCancellable        *cancellable,
+                              GAsyncReadyCallback  callback,
+                              gpointer             user_data)
+{
+  do_async_path_call (file,
+		      G_VFS_DBUS_MOUNT_OP_POLL_MOUNTABLE,
+		      cancellable,
+		      callback, user_data,
+		      poll_mountable_async_cb,
+		      NULL,
+		      0,
+                      0);
+}
+
+static gboolean
+g_daemon_file_poll_mountable_finish (GFile               *file,
+                                     GAsyncResult        *result,
+                                     GError             **error)
+{
+  return TRUE;
+}
+
+static void
+g_daemon_file_unmount_mountable (GFile               *file,
+				 GMountUnmountFlags   flags,
+				 GCancellable        *cancellable,
+				 GAsyncReadyCallback  callback,
+				 gpointer             user_data)
+{
+  g_daemon_file_unmount_mountable_with_operation (file, flags, NULL, cancellable, callback, user_data);
 }
 
 static gboolean
@@ -1554,10 +1710,9 @@ g_daemon_file_unmount_mountable_finish (GFile               *file,
 					GAsyncResult        *result,
 					GError             **error)
 {
-  return TRUE;
+  return g_daemon_file_unmount_mountable_with_operation_finish (file, result, error);
 }
 
-
 typedef struct {
   GFile *file;
   GMountOperation *mount_operation;
@@ -2516,6 +2671,9 @@ g_daemon_file_append_to_async (GFile                      *file,
   dbus_bool_t make_backup;
   guint32 dbus_flags;
   char *etag;
+  guint32 pid;
+
+  pid = get_pid_for_file (file);
 
   mode = 1;
   etag = "";
@@ -2531,6 +2689,7 @@ g_daemon_file_append_to_async (GFile                      *file,
                       DBUS_TYPE_STRING, &etag,
                       DBUS_TYPE_BOOLEAN, &make_backup,
                       DBUS_TYPE_UINT32, &dbus_flags,
+                      DBUS_TYPE_UINT32, &pid,
                       0);
 }
 
@@ -2594,6 +2753,9 @@ g_daemon_file_create_async (GFile                      *file,
   dbus_bool_t make_backup;
   char *etag;
   guint32 dbus_flags;
+  guint32 pid;
+
+  pid = get_pid_for_file (file);
 
   mode = 0;
   etag = "";
@@ -2609,6 +2771,7 @@ g_daemon_file_create_async (GFile                      *file,
                       DBUS_TYPE_STRING, &etag,
                       DBUS_TYPE_BOOLEAN, &make_backup,
                       DBUS_TYPE_UINT32, &dbus_flags,
+                      DBUS_TYPE_UINT32, &pid,
                       0);
 }
 
@@ -2854,6 +3017,9 @@ g_daemon_file_replace_async (GFile                      *file,
   dbus_bool_t dbus_make_backup = make_backup;
   guint32 dbus_flags = flags;
   guint16 mode = 2;
+  guint32 pid;
+
+  pid = get_pid_for_file (file);
   
   if (etag == NULL)
     etag = "";
@@ -2867,6 +3033,7 @@ g_daemon_file_replace_async (GFile                      *file,
                       DBUS_TYPE_STRING, &etag,
                       DBUS_TYPE_BOOLEAN, &dbus_make_backup,
                       DBUS_TYPE_UINT32, &dbus_flags,
+                      DBUS_TYPE_UINT32, &pid,
                       0);
 }
 
@@ -3007,8 +3174,14 @@ g_daemon_file_file_iface_init (GFileIface *iface)
   iface->mount_mountable_finish = g_daemon_file_mount_mountable_finish;
   iface->unmount_mountable = g_daemon_file_unmount_mountable;
   iface->unmount_mountable_finish = g_daemon_file_unmount_mountable_finish;
+  iface->unmount_mountable_with_operation = g_daemon_file_unmount_mountable_with_operation;
+  iface->unmount_mountable_with_operation_finish = g_daemon_file_unmount_mountable_with_operation_finish;
   iface->eject_mountable = g_daemon_file_eject_mountable;
   iface->eject_mountable_finish = g_daemon_file_eject_mountable_finish;
+  iface->eject_mountable_with_operation = g_daemon_file_eject_mountable_with_operation;
+  iface->eject_mountable_with_operation_finish = g_daemon_file_eject_mountable_with_operation_finish;
+  iface->poll_mountable = g_daemon_file_poll_mountable;
+  iface->poll_mountable_finish = g_daemon_file_poll_mountable_finish;
   iface->query_filesystem_info = g_daemon_file_query_filesystem_info;
   iface->query_filesystem_info_async = g_daemon_file_query_filesystem_info_async;
   iface->query_filesystem_info_finish = g_daemon_file_query_filesystem_info_finish;
diff --git a/client/gdaemonmount.c b/client/gdaemonmount.c
index 2169cd9..703cbd6 100644
--- a/client/gdaemonmount.c
+++ b/client/gdaemonmount.c
@@ -33,6 +33,8 @@
 #include "gdaemonfile.h"
 #include "gvfsdaemonprotocol.h"
 #include "gdbusutils.h"
+#include "gmountsource.h"
+#include "gmountoperationdbus.h"
 
 /* Protects all fields of GDaemonMount that can change
    which at this point is just foreign_volume */
@@ -173,17 +175,20 @@ unmount_reply (DBusMessage *reply,
 }
 
 static void
-g_daemon_mount_unmount (GMount *mount,
-			GMountUnmountFlags flags,
-			GCancellable *cancellable,
-			GAsyncReadyCallback callback,
-			gpointer         user_data)
+g_daemon_mount_unmount_with_operation (GMount *mount,
+                                       GMountUnmountFlags flags,
+                                       GMountOperation *mount_operation,
+                                       GCancellable *cancellable,
+                                       GAsyncReadyCallback callback,
+                                       gpointer         user_data)
 {
   GDaemonMount *daemon_mount = G_DAEMON_MOUNT (mount);
   DBusMessage *message;
   GMountInfo *mount_info;
   GSimpleAsyncResult *res;
   guint32 dbus_flags;
+  GMountSource *mount_source;
+  const char *dbus_id, *obj_path;
 
   mount_info = daemon_mount->mount_info;
 
@@ -193,18 +198,45 @@ g_daemon_mount_unmount (GMount *mount,
 				  G_VFS_DBUS_MOUNT_INTERFACE,
 				  G_VFS_DBUS_MOUNT_OP_UNMOUNT);
 
+  mount_source = g_mount_operation_dbus_wrap (mount_operation, _g_daemon_vfs_get_async_bus ());
+  dbus_id = g_mount_source_get_dbus_id (mount_source);
+  obj_path = g_mount_source_get_obj_path (mount_source);
+
   dbus_flags = flags;
-  _g_dbus_message_append_args (message, DBUS_TYPE_UINT32, &dbus_flags, 0);
+  _g_dbus_message_append_args (message,
+                               DBUS_TYPE_STRING, &dbus_id, DBUS_TYPE_OBJECT_PATH, &obj_path,
+                               DBUS_TYPE_UINT32, &dbus_flags,
+                               0);
   
   res = g_simple_async_result_new (G_OBJECT (mount),
 				   callback, user_data,
-				   g_daemon_mount_unmount);
+				   g_daemon_mount_unmount_with_operation);
   
   _g_vfs_daemon_call_async (message,
 			    unmount_reply, res,
 			    cancellable);
   
   dbus_message_unref (message);
+
+  g_object_unref (mount_source);
+}
+
+static gboolean
+g_daemon_mount_unmount_with_operation_finish (GMount *mount,
+                                              GAsyncResult *result,
+                                              GError **error)
+{
+  return TRUE;
+}
+
+static void
+g_daemon_mount_unmount (GMount *mount,
+			GMountUnmountFlags flags,
+			GCancellable *cancellable,
+			GAsyncReadyCallback callback,
+			gpointer         user_data)
+{
+  g_daemon_mount_unmount_with_operation (mount, flags, NULL, cancellable, callback, user_data);
 }
 
 static gboolean
@@ -212,7 +244,7 @@ g_daemon_mount_unmount_finish (GMount *mount,
 				GAsyncResult *result,
 				GError **error)
 {
-  return TRUE;
+  return g_daemon_mount_unmount_with_operation_finish (mount, result, error);
 }
 
 static char **
@@ -272,6 +304,8 @@ g_daemon_mount_mount_iface_init (GMountIface *iface)
   iface->can_eject = g_daemon_mount_can_eject;
   iface->unmount = g_daemon_mount_unmount;
   iface->unmount_finish = g_daemon_mount_unmount_finish;
+  iface->unmount_with_operation = g_daemon_mount_unmount_with_operation;
+  iface->unmount_with_operation_finish = g_daemon_mount_unmount_with_operation_finish;
   iface->guess_content_type = g_daemon_mount_guess_content_type;
   iface->guess_content_type_finish = g_daemon_mount_guess_content_type_finish;
   iface->guess_content_type_sync = g_daemon_mount_guess_content_type_sync;
diff --git a/client/gvfsfusedaemon.c b/client/gvfsfusedaemon.c
index 0da76e4..7481d24 100644
--- a/client/gvfsfusedaemon.c
+++ b/client/gvfsfusedaemon.c
@@ -184,6 +184,29 @@ errno_from_error (GError *error)
   return EIO;
 }
 
+/* Conveys the pid of the client to filesystem backends; see
+ * get_pid_for_file() in gdaemonfile.c
+ *
+ * can only be called during a filesystem op
+ */
+static void
+set_pid_for_file (GFile *file)
+{
+  struct fuse_context *context;
+
+  if (file == NULL)
+    goto out;
+
+  context = fuse_get_context ();
+  if (context == NULL)
+    goto out;
+
+  g_object_set_data (G_OBJECT (file), "gvfs-fuse-client-pid", GUINT_TO_POINTER (context->pid));
+
+ out:
+  ;
+}
+
 static FileHandle *
 file_handle_new (const gchar *path)
 {
@@ -992,6 +1015,7 @@ vfs_open (const gchar *path, struct fuse_file_info *fi)
               debug_print ("vfs_open: flags=%o\n", fi->flags);
 
               /* Set up a stream here, so we can check for errors */
+              set_pid_for_file (file);
 
               if (fi->flags & O_WRONLY || fi->flags & O_RDWR)
                 result = setup_output_stream (file, fh);
@@ -1075,6 +1099,7 @@ vfs_create (const gchar *path, mode_t mode, struct fuse_file_info *fi)
             }
 
           file_output_stream = g_file_create (file, 0, NULL, &error);
+          set_pid_for_file (file);
           if (file_output_stream)
             {
               FileHandle *fh = get_or_create_file_handle_for_path (path);
diff --git a/common/gmountoperationdbus.c b/common/gmountoperationdbus.c
index b8ac4f7..9122042 100644
--- a/common/gmountoperationdbus.c
+++ b/common/gmountoperationdbus.c
@@ -51,6 +51,8 @@ static void              mount_op_ask_password        (GMountOperationDBus *op_d
 						       DBusMessage         *message);
 static void              mount_op_ask_question        (GMountOperationDBus *op_dbus,
 						       DBusMessage         *message);
+static void              mount_op_show_processes      (GMountOperationDBus *op_dbus,
+						       DBusMessage         *message);
 static void              mount_op_aborted             (GMountOperationDBus *op_dbus,
 						       DBusMessage         *message);
 
@@ -135,6 +137,10 @@ mount_op_message_function (DBusConnection  *connection,
     mount_op_ask_question (op_dbus, message);
   else if (dbus_message_is_method_call (message,
 					G_VFS_DBUS_MOUNT_OPERATION_INTERFACE,
+					G_VFS_DBUS_MOUNT_OPERATION_OP_SHOW_PROCESSES))
+    mount_op_show_processes (op_dbus, message);
+  else if (dbus_message_is_method_call (message,
+					G_VFS_DBUS_MOUNT_OPERATION_INTERFACE,
 					G_VFS_DBUS_MOUNT_OPERATION_OP_ABORTED))
     mount_op_aborted (op_dbus, message);
   else
@@ -320,8 +326,91 @@ mount_op_ask_question (GMountOperationDBus *op_dbus,
 }
 
 static void
+show_processes_reply (GMountOperation *op,
+                      GMountOperationResult result,
+                      gpointer data)
+{
+  DBusMessage *reply = data;
+  guint32 choice;
+  dbus_bool_t handled, abort_dbus;
+  GMountOperationDBus *op_dbus;
+
+  op_dbus = g_object_get_data (G_OBJECT (op), "dbus-op");
+
+  handled = (result != G_MOUNT_OPERATION_UNHANDLED);
+  abort_dbus = (result == G_MOUNT_OPERATION_ABORTED);
+
+  choice = g_mount_operation_get_choice (op);
+
+  _g_dbus_message_append_args (reply,
+			       DBUS_TYPE_BOOLEAN, &handled,
+			       DBUS_TYPE_BOOLEAN, &abort_dbus,
+			       DBUS_TYPE_UINT32, &choice,
+			       0);
+
+  mount_op_send_reply (op_dbus, reply);
+}
+
+static void
+mount_op_show_processes (GMountOperationDBus *op_dbus,
+                         DBusMessage         *message)
+{
+  const char *message_string;
+  char **choices;
+  int num_choices;
+  gint32 **process_pids;
+  int num_process_pids;
+  DBusMessage *reply;
+  DBusError error;
+  DBusMessageIter iter;
+  GArray *processes;
+
+  reply = NULL;
+
+  dbus_message_iter_init (message, &iter);
+  dbus_error_init (&error);
+  if (!_g_dbus_message_iter_get_args (&iter,
+				      &error,
+				      DBUS_TYPE_STRING, &message_string,
+				      DBUS_TYPE_ARRAY, DBUS_TYPE_STRING,
+				      &choices, &num_choices,
+				      DBUS_TYPE_ARRAY, DBUS_TYPE_INT32,
+				      &process_pids, &num_process_pids,
+				      0))
+    {
+      reply = dbus_message_new_error (message, error.name, error.message);
+      if (reply == NULL)
+	_g_dbus_oom ();
+      if (!dbus_connection_send (op_dbus->connection, reply, NULL))
+	_g_dbus_oom ();
+      dbus_message_unref (reply);
+      dbus_error_free (&error);
+      return;
+    }
+
+  processes = g_array_sized_new (FALSE, FALSE, sizeof (GPid), num_process_pids);
+  g_array_append_vals (processes, process_pids, num_process_pids);
+
+  reply = dbus_message_new_method_return (message);
+  if (reply == NULL)
+    _g_dbus_oom ();
+
+  g_signal_connect (op_dbus->op, "reply", (GCallback)show_processes_reply, reply);
+
+  g_signal_emit_by_name (op_dbus->op, "show_processes",
+			 message_string,
+                         processes,
+			 choices);
+
+  dbus_free_string_array (choices);
+  g_array_unref (processes);
+}
+
+static void
 mount_op_aborted (GMountOperationDBus *op_dbus,
 		  DBusMessage         *message)
 {
+  /* also emit reply to make the all DBus ops return */
+  g_mount_operation_reply (op_dbus->op, G_MOUNT_OPERATION_UNHANDLED);
   g_signal_emit_by_name (op_dbus->op, "aborted");
 }
diff --git a/common/gmountsource.c b/common/gmountsource.c
index ac705fa..5ee964e 100644
--- a/common/gmountsource.c
+++ b/common/gmountsource.c
@@ -699,12 +699,241 @@ op_ask_question (GMountOperation *op,
   return TRUE;
 }
 
+typedef struct ShowProcessesData ShowProcessesData;
+
+struct ShowProcessesData {
+
+  /* results: */
+  gboolean aborted;
+  guint32  choice;
+};
+
+/* the callback from dbus -> main thread */
+static void
+show_processes_reply (DBusMessage *reply,
+                      GError      *error,
+                      gpointer     _data)
+{
+  GSimpleAsyncResult *result;
+  ShowProcessesData *data;
+  dbus_bool_t handled, aborted;
+  guint32 choice;
+  DBusMessageIter iter;
+
+  result = G_SIMPLE_ASYNC_RESULT (_data);
+  handled = TRUE;
+
+  data = g_new0 (ShowProcessesData, 1);
+  g_simple_async_result_set_op_res_gpointer (result, data, g_free);
+
+  if (reply == NULL)
+    {
+      data->aborted = TRUE;
+    }
+  else
+    {
+      dbus_message_iter_init (reply, &iter);
+      if (!_g_dbus_message_iter_get_args (&iter, NULL,
+					  DBUS_TYPE_BOOLEAN, &handled,
+					  DBUS_TYPE_BOOLEAN, &aborted,
+					  DBUS_TYPE_UINT32, &choice,
+					  0))
+	data->aborted = TRUE;
+      else
+	{
+	  data->aborted = aborted;
+	  data->choice = choice;
+	}
+    }
+
+  if (handled == FALSE)
+    {
+      g_simple_async_result_set_error (result, G_IO_ERROR, G_IO_ERROR_FAILED, "Internal Error");
+    }
+
+  g_simple_async_result_complete (result);
+}
+
+void
+g_mount_source_show_processes_async (GMountSource        *source,
+                                     const char          *message_string,
+                                     GArray              *processes,
+                                     const char         **choices,
+                                     gint                 n_choices,
+                                     GAsyncReadyCallback  callback,
+                                     gpointer             user_data)
+{
+  GSimpleAsyncResult *result;
+  DBusMessage *message;
+
+  /* If no dbus id specified, reply that we weren't handled */
+  if (source->dbus_id[0] == 0)
+    {
+      g_simple_async_report_error_in_idle (G_OBJECT (source),
+					   callback,
+					   user_data,
+					   G_IO_ERROR, G_IO_ERROR_FAILED,
+					   "Internal Error");
+      return;
+    }
+
+  result = g_simple_async_result_new (G_OBJECT (source), callback, user_data,
+                                      g_mount_source_show_processes_async);
+
+  if (message_string == NULL)
+    message_string = "";
+
+  message = dbus_message_new_method_call (source->dbus_id,
+					  source->obj_path,
+					  G_VFS_DBUS_MOUNT_OPERATION_INTERFACE,
+					  G_VFS_DBUS_MOUNT_OPERATION_OP_SHOW_PROCESSES);
+
+  _g_dbus_message_append_args (message,
+			       DBUS_TYPE_STRING, &message_string,
+			       DBUS_TYPE_ARRAY, DBUS_TYPE_STRING,
+			       &choices, n_choices,
+			       DBUS_TYPE_ARRAY, DBUS_TYPE_INT32,
+			       &processes->data, processes->len,
+			       0);
+
+  /* 30 minute timeout */
+  _g_dbus_connection_call_async (NULL, message, 1000 * 60 * 30,
+				 show_processes_reply, result);
+  dbus_message_unref (message);
+}
+
+gboolean
+g_mount_source_show_processes_finish (GMountSource *source,
+                                      GAsyncResult *result,
+                                      gboolean     *aborted,
+                                      gint         *choice_out)
+{
+  ShowProcessesData *data, def= { FALSE, };
+  GSimpleAsyncResult *simple;
+
+  simple = G_SIMPLE_ASYNC_RESULT (result);
+
+  if (g_simple_async_result_propagate_error (simple, NULL))
+    data = &def;
+  else
+    data = (ShowProcessesData *) g_simple_async_result_get_op_res_gpointer (simple);
+
+  if (aborted)
+    *aborted = data->aborted;
+
+  if (choice_out)
+    *choice_out = data->choice;
+
+  return data != &def;
+}
+
+gboolean
+g_mount_source_show_processes (GMountSource *source,
+                               const char   *message,
+                               GArray       *processes,
+                               const char  **choices,
+                               gint          n_choices,
+                               gboolean     *aborted_out,
+                               gint         *choice_out)
+{
+  gint choice;
+  gboolean handled, aborted;
+  AskSyncData data = {NULL};
+
+  data.mutex = g_mutex_new ();
+  data.cond = g_cond_new ();
+
+  g_mutex_lock (data.mutex);
+
+  g_mount_source_show_processes_async (source,
+                                       message,
+                                       processes,
+                                       choices,
+                                       n_choices,
+                                       ask_reply_sync,
+                                       &data);
+
+  g_cond_wait (data.cond, data.mutex);
+  g_mutex_unlock (data.mutex);
+
+  g_cond_free (data.cond);
+  g_mutex_free (data.mutex);
+
+  handled = g_mount_source_show_processes_finish (source,
+                                                  data.result,
+                                                  &aborted,
+                                                  &choice);
+
+  g_object_unref (data.result);
+
+  if (aborted_out)
+    *aborted_out = aborted;
+
+  if (choice_out)
+    *choice_out = choice;
+
+  return handled;
+}
+
+static void
+op_show_processes_reply (GObject      *source_object,
+                         GAsyncResult *res,
+                         gpointer      user_data)
+{
+  GMountOperationResult result;
+  GMountOperation *op;
+  GMountSource *source;
+  gboolean handled, aborted;
+  gint choice;
+
+  source = G_MOUNT_SOURCE (source_object);
+  op = G_MOUNT_OPERATION (user_data);
+
+  handled = g_mount_source_show_processes_finish (source,
+                                                  res,
+                                                  &aborted,
+                                                  &choice);
+
+  if (!handled)
+    result = G_MOUNT_OPERATION_UNHANDLED;
+  else if (aborted)
+    result = G_MOUNT_OPERATION_ABORTED;
+  else
+    {
+      result = G_MOUNT_OPERATION_HANDLED;
+      g_mount_operation_set_choice (op, choice);
+    }
+
+  g_mount_operation_reply (op, result);
+  g_object_unref (op);
+}
+
 static gboolean
-op_aborted (GMountOperation *op,
-	    GMountSource    *source)
+op_show_processes (GMountOperation *op,
+                   const char      *message,
+                   GArray          *processes,
+                   const char     **choices,
+                   GMountSource    *mount_source)
+{
+  g_mount_source_show_processes_async (mount_source,
+                                       message,
+                                       processes,
+                                       choices,
+                                       g_strv_length ((gchar **) choices),
+                                       op_show_processes_reply,
+                                       g_object_ref (op));
+  g_signal_stop_emission_by_name (op, "show_processes");
+  return TRUE;
+}
+
+gboolean
+g_mount_source_abort (GMountSource *source)
 {
   DBusMessage *message;
   DBusConnection *connection;
+  gboolean ret;
+
+  ret = FALSE;
 
   /* If no dbus id specified, reply that we weren't handled */
   if (source->dbus_id[0] == 0)
@@ -713,7 +942,7 @@ op_aborted (GMountOperation *op,
   connection = dbus_bus_get (DBUS_BUS_SESSION, NULL);
   if (connection == NULL)
     goto out;
-  
+
   message = dbus_message_new_method_call (source->dbus_id,
 					  source->obj_path,
 					  G_VFS_DBUS_MOUNT_OPERATION_INTERFACE,
@@ -725,8 +954,24 @@ op_aborted (GMountOperation *op,
       dbus_message_unref (message);
     }
 
+  ret = TRUE;
+
  out:
-  return TRUE;
+  return ret;
+}
+
+static void
+op_aborted (GMountOperation *op,
+	    GMountSource    *source)
+{
+  g_mount_source_abort (source);
+}
+
+gboolean
+g_mount_source_is_dummy (GMountSource *source)
+{
+  g_return_val_if_fail (G_IS_MOUNT_SOURCE (source), TRUE);
+  return source->dbus_id[0] == 0;
 }
 
 
@@ -742,6 +987,7 @@ g_mount_source_get_operation (GMountSource *mount_source)
 
   g_signal_connect (op, "ask_password", (GCallback)op_ask_password, mount_source);
   g_signal_connect (op, "ask_question", (GCallback)op_ask_question, mount_source);
+  g_signal_connect (op, "show_processes", (GCallback)op_show_processes, mount_source);
   g_signal_connect (op, "aborted", (GCallback)op_aborted, mount_source);
 
   return op;
diff --git a/common/gmountsource.h b/common/gmountsource.h
index e429835..1f973a1 100644
--- a/common/gmountsource.h
+++ b/common/gmountsource.h
@@ -104,6 +104,31 @@ gboolean     g_mount_source_ask_question_finish       (GMountSource
                                                        gboolean                  *aborted,
 						       gint                      *choice_out);
 
+gboolean      g_mount_source_show_processes           (GMountSource              *mount_source,
+						       const char                *message,
+                                                       GArray                    *processes,
+						       const char               **choices,
+						       gint                       n_choices,
+						       gboolean                  *aborted,
+						       gint                      *choice_out);
+
+void          g_mount_source_show_processes_async     (GMountSource              *mount_source,
+						       const char                *message,
+                                                       GArray                    *processes,
+						       const char               **choices,
+						       gint                       n_choices,
+                                                       GAsyncReadyCallback        callback,
+                                                       gpointer                   user_data);
+
+gboolean     g_mount_source_show_processes_finish     (GMountSource              *source,
+                                                       GAsyncResult              *result,
+                                                       gboolean                  *aborted,
+                                                       gint                      *choice_out);
+
+gboolean     g_mount_source_abort                     (GMountSource              *source);
+
+gboolean     g_mount_source_is_dummy                  (GMountSource              *source);
+
 
 const char *  g_mount_source_get_dbus_id              (GMountSource              *mount_source);
 const char *  g_mount_source_get_obj_path             (GMountSource              *mount_source);
diff --git a/common/gvfsdaemonprotocol.h b/common/gvfsdaemonprotocol.h
index 7765854..98779f7 100644
--- a/common/gvfsdaemonprotocol.h
+++ b/common/gvfsdaemonprotocol.h
@@ -39,6 +39,7 @@ G_BEGIN_DECLS
 #define G_VFS_DBUS_MOUNT_OP_EJECT_MOUNTABLE "EjectMountable"
 #define G_VFS_DBUS_MOUNT_OP_START_MOUNTABLE "StartMountable"
 #define G_VFS_DBUS_MOUNT_OP_STOP_MOUNTABLE "StopMountable"
+#define G_VFS_DBUS_MOUNT_OP_POLL_MOUNTABLE "PollMountable"
 #define G_VFS_DBUS_MOUNT_OP_SET_DISPLAY_NAME "SetDisplayName"
 #define G_VFS_DBUS_MOUNT_OP_DELETE "Delete"
 #define G_VFS_DBUS_MOUNT_OP_TRASH "Trash"
@@ -75,6 +76,7 @@ G_BEGIN_DECLS
 #define G_VFS_DBUS_MOUNT_OPERATION_INTERFACE "org.gtk.vfs.MountOperation"
 #define G_VFS_DBUS_MOUNT_OPERATION_OP_ASK_PASSWORD "askPassword"
 #define G_VFS_DBUS_MOUNT_OPERATION_OP_ASK_QUESTION "askQuestion"
+#define G_VFS_DBUS_MOUNT_OPERATION_OP_SHOW_PROCESSES "showProcesses"
 #define G_VFS_DBUS_MOUNT_OPERATION_OP_ABORTED "aborted"
 
 /* Implemented by the spawner of a process, the spawned process sends the
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index ae24771..1c0d725 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -126,6 +126,7 @@ libdaemon_la_SOURCES = \
 	gvfsjobunmountmountable.c gvfsjobunmountmountable.h \
 	gvfsjobstartmountable.c gvfsjobstartmountable.h \
 	gvfsjobstopmountable.c gvfsjobstopmountable.h \
+	gvfsjobpollmountable.c gvfsjobpollmountable.h \
 	gvfsjobopenforread.c gvfsjobopenforread.h \
 	gvfsjobopeniconforread.c gvfsjobopeniconforread.h \
 	gvfsjobread.c gvfsjobread.h \
diff --git a/daemon/gvfsbackend.c b/daemon/gvfsbackend.c
index 3be3cdb..3bc877e 100644
--- a/daemon/gvfsbackend.c
+++ b/daemon/gvfsbackend.c
@@ -48,6 +48,7 @@
 #include <gvfsjobunmountmountable.h>
 #include <gvfsjobstartmountable.h>
 #include <gvfsjobstopmountable.h>
+#include <gvfsjobpollmountable.h>
 #include <gvfsjobmakedirectory.h>
 #include <gvfsjobmakesymlink.h>
 #include <gvfsjobcreatemonitor.h>
@@ -543,6 +544,10 @@ backend_dbus_handler (DBusConnection  *connection,
     job = g_vfs_job_stop_mountable_new (connection, message, backend);
   else if (dbus_message_is_method_call (message,
 					G_VFS_DBUS_MOUNT_INTERFACE,
+					G_VFS_DBUS_MOUNT_OP_POLL_MOUNTABLE))
+    job = g_vfs_job_poll_mountable_new (connection, message, backend);
+  else if (dbus_message_is_method_call (message,
+					G_VFS_DBUS_MOUNT_INTERFACE,
 					G_VFS_DBUS_MOUNT_OP_SET_DISPLAY_NAME))
     job = g_vfs_job_set_display_name_new (connection, message, backend);
   else if (dbus_message_is_method_call (message,
@@ -693,3 +698,248 @@ g_vfs_backend_unregister_mount (GVfsBackend *backend,
 				 callback, user_data);
   dbus_message_unref (message);
 }
+
+/* ------------------------------------------------------------------------------------------------- */
+
+typedef struct
+{
+  GVfsBackend *backend;
+  GMountSource *mount_source;
+
+  gboolean ret;
+  gboolean aborted;
+  gint choice;
+
+  const gchar *message;
+  const gchar *choices[3];
+
+  gboolean no_more_processes;
+
+  GAsyncReadyCallback callback;
+  gpointer            user_data;
+
+  guint timeout_id;
+} UnmountWithOpData;
+
+static void
+complete_unmount_with_op (UnmountWithOpData *data)
+{
+  gboolean ret;
+  GSimpleAsyncResult *simple;
+
+  g_source_remove (data->timeout_id);
+
+  ret = TRUE;
+
+  if (data->no_more_processes)
+    {
+      /* do nothing, e.g. return TRUE to signal we should unmount */
+    }
+  else
+    {
+      if (data->aborted || data->choice == 1)
+        {
+          ret = FALSE;
+        }
+    }
+
+  simple = g_simple_async_result_new (G_OBJECT (data->backend),
+                                      data->callback,
+                                      data->user_data,
+                                      NULL);
+  g_simple_async_result_set_op_res_gboolean (simple, ret);
+  g_simple_async_result_complete (simple);
+  g_object_unref (simple);
+}
+
+static void
+on_show_processes_reply (GMountSource  *mount_source,
+                         GAsyncResult  *res,
+                         gpointer       user_data)
+{
+  UnmountWithOpData *data = user_data;
+
+  /* Do nothing if we've handled this already */
+  if (data->no_more_processes)
+    return;
+
+  data->ret = g_mount_source_show_processes_finish (mount_source,
+                                                    res,
+                                                    &data->aborted,
+                                                    &data->choice);
+
+  complete_unmount_with_op (data);
+}
+
+static gboolean
+on_update_processes_timeout (gpointer user_data)
+{
+  UnmountWithOpData *data = user_data;
+  GArray *processes;
+
+  processes = g_vfs_daemon_get_blocking_processes (g_vfs_backend_get_daemon (data->backend));
+  if (processes->len == 0)
+    {
+      /* no more processes, abort mount op */
+      g_mount_source_abort (data->mount_source);
+      data->no_more_processes = TRUE;
+      complete_unmount_with_op (data);
+    }
+  else
+    {
+      /* ignore reply */
+      g_mount_source_show_processes_async (data->mount_source,
+                                           data->message,
+                                           processes,
+                                           data->choices,
+                                           g_strv_length ((gchar **) data->choices),
+                                           NULL,
+                                           NULL);
+    }
+
+  g_array_unref (processes);
+
+  /* keep timeout around */
+  return TRUE;
+}
+
+static void
+unmount_with_op_data_free (UnmountWithOpData *data)
+{
+  g_free (data);
+}
+
+
+/**
+ * g_vfs_backend_unmount_with_operation_finish:
+ * @backend: A #GVfsBackend.
+ * @res: A #GAsyncResult obtained from the @callback function passed
+ *     to g_vfs_backend_unmount_with_operation().
+ *
+ * Gets the result of the operation started by
+ * gvfs_backend_unmount_with_operation_sync().
+ *
+ * Returns: %TRUE if the backend should be unmounted (either no blocking
+ *     processes or the user decided to unmount anyway), %FALSE if
+ *     no action should be taken.
+ */
+gboolean
+g_vfs_backend_unmount_with_operation_finish (GVfsBackend *backend,
+                                             GAsyncResult *res)
+{
+  gboolean ret;
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+
+  if (g_simple_async_result_propagate_error (simple, NULL))
+    {
+      ret = FALSE;
+    }
+  else
+    {
+      ret = g_simple_async_result_get_op_res_gboolean (simple);
+    }
+
+  return ret;
+}
+
+/**
+ * gvfs_backend_unmount_with_operation:
+ * @backend: A #GVfsBackend.
+ * @callback: A #GAsyncReadyCallback.
+ * @user_data: User data to pass to @callback.
+ *
+ * Utility function to checks if there are pending operations on
+ * @backend preventing unmount. If not, then @callback is invoked
+ * immediately.
+ *
+ * Otherwise, a dialog will be shown (using @mount_source) to interact
+ * with the user about blocking processes (e.g. using the
+ * #GMountOperation::show-processes signal). The list of blocking
+ * processes is continuously updated.
+ *
+ * Once the user has decided (or if it's not possible to interact with
+ * the user), @callback will be invoked. You can then call
+ * g_vfs_backend_unmount_with_operation_finish() to get the result
+ * of the operation.
+ */
+void
+g_vfs_backend_unmount_with_operation (GVfsBackend        *backend,
+                                      GMountSource       *mount_source,
+                                      GAsyncReadyCallback callback,
+                                      gpointer            user_data)
+{
+  GArray *processes;
+  UnmountWithOpData *data;
+
+  g_return_if_fail (G_VFS_IS_BACKEND (backend));
+  g_return_if_fail (G_IS_MOUNT_SOURCE (mount_source));
+  g_return_if_fail (callback != NULL);
+
+  processes = g_vfs_daemon_get_blocking_processes (g_vfs_backend_get_daemon (backend));
+  /* if no processes are blocking, complete immediately */
+  if (processes->len == 0)
+    {
+      GSimpleAsyncResult *simple;
+      simple = g_simple_async_result_new (G_OBJECT (backend),
+                                          callback,
+                                          user_data,
+                                          NULL);
+      g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+      g_simple_async_result_complete (simple);
+      g_object_unref (simple);
+      goto out;
+    }
+
+  data = g_new0 (UnmountWithOpData, 1);
+  data->backend = backend;
+  data->mount_source = mount_source;
+  data->callback = callback;
+  data->user_data = user_data;
+
+  data->choices[0] = _("Unmount Anyway");
+  data->choices[1] = _("Cancel");
+  data->choices[2] = NULL;
+  data->message = _("Volume is busy\n"
+                    "One or more applications are keeping the volume busy.");
+
+  /* free data when the mount source goes away */
+  g_object_set_data_full (G_OBJECT (mount_source),
+                          "unmount-op-data",
+                          data,
+                          (GDestroyNotify) unmount_with_op_data_free);
+
+  /* show processes */
+  g_mount_source_show_processes_async (mount_source,
+                                       data->message,
+                                       processes,
+                                       data->choices,
+                                       g_strv_length ((gchar **) data->choices),
+                                       (GAsyncReadyCallback) on_show_processes_reply,
+                                       data);
+
+  /* update these processes every two secs */
+  data->timeout_id = g_timeout_add_seconds (2,
+                                            on_update_processes_timeout,
+                                            data);
+
+ out:
+  g_array_unref (processes);
+
+}
+
+gboolean
+g_vfs_backend_has_blocking_processes (GVfsBackend  *backend)
+{
+  gboolean ret;
+  GArray *processes;
+
+  ret = FALSE;
+  processes = g_vfs_daemon_get_blocking_processes (g_vfs_backend_get_daemon (backend));
+  if (processes->len > 0)
+    ret = TRUE;
+
+  g_array_unref (processes);
+
+  return ret;
+}
+
diff --git a/daemon/gvfsbackend.h b/daemon/gvfsbackend.h
index ee68bd8..137001e 100644
--- a/daemon/gvfsbackend.h
+++ b/daemon/gvfsbackend.h
@@ -49,6 +49,7 @@ typedef struct _GVfsJobMountMountable   GVfsJobMountMountable;
 typedef struct _GVfsJobUnmountMountable GVfsJobUnmountMountable;
 typedef struct _GVfsJobStartMountable   GVfsJobStartMountable;
 typedef struct _GVfsJobStopMountable    GVfsJobStopMountable;
+typedef struct _GVfsJobPollMountable    GVfsJobPollMountable;
 typedef struct _GVfsJobOpenForRead      GVfsJobOpenForRead;
 typedef struct _GVfsJobOpenIconForRead  GVfsJobOpenIconForRead;
 typedef struct _GVfsJobSeekRead         GVfsJobSeekRead;
@@ -100,9 +101,13 @@ struct _GVfsBackendClass
    */
 
   void     (*unmount)           (GVfsBackend *backend,
-				 GVfsJobUnmount *job);
+				 GVfsJobUnmount *job,
+				 GMountUnmountFlags flags,
+                                 GMountSource *mount_source);
   gboolean (*try_unmount)       (GVfsBackend *backend,
-				 GVfsJobUnmount *job);
+				 GVfsJobUnmount *job,
+				 GMountUnmountFlags flags,
+                                 GMountSource *mount_source);
   void     (*mount)             (GVfsBackend *backend,
 				 GVfsJobMount *job,
 				 GMountSpec *mount_spec,
@@ -124,19 +129,23 @@ struct _GVfsBackendClass
   void     (*unmount_mountable) (GVfsBackend *backend,
 				 GVfsJobUnmountMountable *job,
 				 const char *filename,
-				 GMountUnmountFlags flags);
+				 GMountUnmountFlags flags,
+                                 GMountSource *mount_source);
   gboolean (*try_unmount_mountable)(GVfsBackend *backend,
 				    GVfsJobUnmountMountable *job,
 				    const char *filename,
-				    GMountUnmountFlags flags);
+				    GMountUnmountFlags flags,
+                                    GMountSource *mount_source);
   void     (*eject_mountable)   (GVfsBackend *backend,
 				 GVfsJobUnmountMountable *job,
 				 const char *filename,
-				 GMountUnmountFlags flags);
+				 GMountUnmountFlags flags,
+                                 GMountSource *mount_source);
   gboolean (*try_eject_mountable)(GVfsBackend *backend,
 				  GVfsJobUnmountMountable *job,
 				  const char *filename,
-				  GMountUnmountFlags flags);
+				  GMountUnmountFlags flags,
+                                  GMountSource *mount_source);
   void     (*open_for_read)     (GVfsBackend *backend,
 				 GVfsJobOpenForRead *job,
 				 const char *filename);
@@ -429,11 +438,19 @@ struct _GVfsBackendClass
   void     (*stop_mountable) (GVfsBackend *backend,
                               GVfsJobStopMountable *job,
                               const char *filename,
-                              GMountUnmountFlags flags);
+                              GMountUnmountFlags flags,
+                              GMountSource *mount_source);
   gboolean (*try_stop_mountable)   (GVfsBackend *backend,
 				    GVfsJobStopMountable *job,
 				    const char *filename,
-				    GMountUnmountFlags flags);
+				    GMountUnmountFlags flags,
+                                    GMountSource *mount_source);
+  void     (*poll_mountable) (GVfsBackend *backend,
+                              GVfsJobPollMountable *job,
+                              const char *filename);
+  gboolean (*try_poll_mountable)   (GVfsBackend *backend,
+				    GVfsJobPollMountable *job,
+				    const char *filename);
 };
 
 GType g_vfs_backend_get_type (void) G_GNUC_CONST;
@@ -478,6 +495,16 @@ void        g_vfs_backend_add_auto_info                  (GVfsBackend
 							  GFileInfo             *info,
 							  const char            *uri);
 
+gboolean    g_vfs_backend_has_blocking_processes         (GVfsBackend           *backend);
+
+gboolean    g_vfs_backend_unmount_with_operation_finish (GVfsBackend  *backend,
+                                                         GAsyncResult *res);
+
+void        g_vfs_backend_unmount_with_operation (GVfsBackend        *backend,
+                                                  GMountSource       *mount_source,
+                                                  GAsyncReadyCallback callback,
+                                                  gpointer            user_data);
+
 G_END_DECLS
 
 #endif /* __G_VFS_BACKEND_H__ */
diff --git a/daemon/gvfsbackendarchive.c b/daemon/gvfsbackendarchive.c
index bbca3ca..17f3fc4 100644
--- a/daemon/gvfsbackendarchive.c
+++ b/daemon/gvfsbackendarchive.c
@@ -583,7 +583,9 @@ do_mount (GVfsBackend *backend,
 
 static void
 do_unmount (GVfsBackend *backend,
-	    GVfsJobUnmount *job)
+	    GVfsJobUnmount *job,
+            GMountUnmountFlags flags,
+            GMountSource *mount_source)
 {
   GVfsBackendArchive *ba = G_VFS_BACKEND_ARCHIVE (backend);
 
diff --git a/daemon/gvfsbackendcdda.c b/daemon/gvfsbackendcdda.c
index 9b30753..e1871c6 100644
--- a/daemon/gvfsbackendcdda.c
+++ b/daemon/gvfsbackendcdda.c
@@ -428,7 +428,9 @@ try_mount (GVfsBackend *backend,
 
 static void
 do_unmount (GVfsBackend *backend,
-            GVfsJobUnmount *job)
+            GVfsJobUnmount *job,
+            GMountUnmountFlags flags,
+            GMountSource *mount_source)
 {
   GError *error;
   GVfsBackendCdda *cdda_backend = G_VFS_BACKEND_CDDA (backend);
diff --git a/daemon/gvfsbackendcomputer.c b/daemon/gvfsbackendcomputer.c
index 9783df6..4f4b9ca 100644
--- a/daemon/gvfsbackendcomputer.c
+++ b/daemon/gvfsbackendcomputer.c
@@ -62,11 +62,15 @@ typedef struct {
   GIcon *icon;
   GFile *root;
   int prio;
+  gchar *unix_device_file;
   gboolean can_mount;
   gboolean can_unmount;
   gboolean can_eject;
   gboolean can_start;
+  gboolean can_start_degraded;
   gboolean can_stop;
+  gboolean can_poll_for_media;
+  gboolean is_media_check_automatic;
   GDriveStartStopType start_stop_type;
   
   GDrive *drive;
@@ -96,6 +100,7 @@ G_DEFINE_TYPE (GVfsBackendComputer, g_vfs_backend_computer, G_VFS_TYPE_BACKEND)
 static void
 computer_file_free (ComputerFile *file)
 {
+  g_free (file->unix_device_file);
   g_free (file->filename);
   g_free (file->display_name);
   if (file->icon)
@@ -137,7 +142,10 @@ computer_file_equal (ComputerFile *a,
       a->can_unmount != b->can_unmount ||
       a->can_eject != b->can_eject ||
       a->can_start != b->can_start ||
+      a->can_start_degraded != b->can_start_degraded ||
       a->can_stop != b->can_stop ||
+      a->can_poll_for_media != b->can_poll_for_media ||
+      a->is_media_check_automatic != b->is_media_check_automatic ||
       a->start_stop_type != b->start_stop_type)
     return FALSE;
 
@@ -427,6 +435,9 @@ recompute_files (GVfsBackendComputer *backend)
             {
               display_name = g_mount_get_name (file->mount);
             }
+          if (file->volume != NULL)
+            file->unix_device_file = g_volume_get_identifier (file->volume,
+                                                              G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
           file->icon = g_mount_get_icon (file->mount);
           file->display_name = display_name;
           file->root = g_mount_get_root (file->mount);
@@ -447,6 +458,8 @@ recompute_files (GVfsBackendComputer *backend)
             {
               display_name = g_volume_get_name (file->volume);
             }
+          file->unix_device_file = g_volume_get_identifier (file->volume,
+                                                            G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
           file->icon = g_volume_get_icon (file->volume);
           file->display_name = display_name;
           file->can_mount = g_volume_can_mount (file->volume);
@@ -455,6 +468,8 @@ recompute_files (GVfsBackendComputer *backend)
         }
       else /* drive */
         {
+          file->unix_device_file = g_drive_get_identifier (file->drive,
+                                                            G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
           file->icon = g_drive_get_icon (file->drive);
           file->display_name = g_drive_get_name (file->drive);
           file->can_eject = g_drive_can_eject (file->drive);
@@ -464,7 +479,10 @@ recompute_files (GVfsBackendComputer *backend)
       if (file->drive)
         {
           file->can_start = g_drive_can_start (file->drive);
+          file->can_start_degraded = g_drive_can_start_degraded (file->drive);
           file->can_stop = g_drive_can_stop (file->drive);
+          file->can_poll_for_media = g_drive_can_poll_for_media (file->drive);
+          file->is_media_check_automatic = g_drive_is_media_check_automatic (file->drive);
           file->start_stop_type = g_drive_get_start_stop_type (file->drive);
           if (file->can_start)
             file->can_mount = FALSE;
@@ -657,11 +675,16 @@ file_info_from_file (ComputerFile *file,
   g_file_info_set_sort_order (info, file->prio);
 
   g_file_info_set_file_type (info, G_FILE_TYPE_MOUNTABLE);
+  if (file->unix_device_file != NULL)
+    g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_MOUNTABLE_UNIX_DEVICE_FILE, file->unix_device_file);
   g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_MOUNT, file->can_mount);
   g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_UNMOUNT, file->can_unmount);
   g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_EJECT, file->can_eject);
   g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_START, file->can_start);
+  g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_START_DEGRADED, file->can_start_degraded);
   g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_STOP, file->can_stop);
+  g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_POLL, file->can_poll_for_media);
+  g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_MOUNTABLE_IS_MEDIA_CHECK_AUTOMATIC, file->is_media_check_automatic);
   g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_MOUNTABLE_START_STOP_TYPE, file->start_stop_type);
 
   g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE, FALSE);
@@ -984,7 +1007,7 @@ unmount_mount_cb (GObject *source_object,
   mount = G_MOUNT (source_object);
 
   error = NULL;
-  if (g_mount_unmount_finish (mount, res, &error))
+  if (g_mount_unmount_with_operation_finish (mount, res, &error))
     g_vfs_job_succeeded (G_VFS_JOB (job));
   else
     {
@@ -998,9 +1021,11 @@ static gboolean
 try_unmount_mountable (GVfsBackend *backend,
 		       GVfsJobUnmountMountable *job,
 		       const char *filename,
-		       GMountUnmountFlags flags)
+		       GMountUnmountFlags flags,
+                       GMountSource *mount_source)
 {
   ComputerFile *file;
+  GMountOperation *mount_op;
 
   file = lookup (G_VFS_BACKEND_COMPUTER (backend),
                  G_VFS_JOB (job), filename);
@@ -1013,11 +1038,15 @@ try_unmount_mountable (GVfsBackend *backend,
     {
       if (file->mount)
         {
-          g_mount_unmount (file->mount,
-                         flags,
-                         G_VFS_JOB (job)->cancellable,
-                         unmount_mount_cb,
-                         job);
+          mount_op = g_mount_source_get_operation (mount_source);
+          /* free mount_op when job is completed */
+          g_object_set_data_full (G_OBJECT (job), "gvfs-backend-computer-mount-op", mount_op, g_object_unref);
+          g_mount_unmount_with_operation (file->mount,
+                                          flags,
+                                          mount_op,
+                                          G_VFS_JOB (job)->cancellable,
+                                          unmount_mount_cb,
+                                          job);
         }
       else
         {
@@ -1042,7 +1071,7 @@ eject_mount_cb (GObject *source_object,
   mount = G_MOUNT (source_object);
 
   error = NULL;
-  if (g_mount_eject_finish (mount, res, &error))
+  if (g_mount_eject_with_operation_finish (mount, res, &error))
     g_vfs_job_succeeded (G_VFS_JOB (job));
   else
     {
@@ -1063,7 +1092,7 @@ eject_volume_cb (GObject *source_object,
   volume = G_VOLUME (source_object);
 
   error = NULL;
-  if (g_volume_eject_finish (volume, res, &error))
+  if (g_volume_eject_with_operation_finish (volume, res, &error))
     g_vfs_job_succeeded (G_VFS_JOB (job));
   else
     {
@@ -1085,7 +1114,7 @@ eject_drive_cb (GObject *source_object,
   drive = G_DRIVE (source_object);
 
   error = NULL;
-  if (g_drive_eject_finish (drive, res, &error))
+  if (g_drive_eject_with_operation_finish (drive, res, &error))
     g_vfs_job_succeeded (G_VFS_JOB (job));
   else
     {
@@ -1098,9 +1127,11 @@ static gboolean
 try_eject_mountable (GVfsBackend *backend,
                      GVfsJobUnmountMountable *job,
                      const char *filename,
-                     GMountUnmountFlags flags)
+                     GMountUnmountFlags flags,
+                     GMountSource *mount_source)
 {
   ComputerFile *file;
+  GMountOperation *mount_op;
 
   file = lookup (G_VFS_BACKEND_COMPUTER (backend),
                  G_VFS_JOB (job), filename);
@@ -1113,27 +1144,39 @@ try_eject_mountable (GVfsBackend *backend,
     {
       if (file->mount)
         {
-          g_mount_eject (file->mount,
-                         flags,
-                         G_VFS_JOB (job)->cancellable,
-                         eject_mount_cb,
-                         job);
+          mount_op = g_mount_source_get_operation (mount_source);
+          /* free mount_op when job is completed */
+          g_object_set_data_full (G_OBJECT (job), "gvfs-backend-computer-mount-op", mount_op, g_object_unref);
+          g_mount_eject_with_operation (file->mount,
+                                        flags,
+                                        mount_op,
+                                        G_VFS_JOB (job)->cancellable,
+                                        eject_mount_cb,
+                                        job);
         }
       else if (file->volume)
         {
-          g_volume_eject (file->volume,
-                          flags,
-                          G_VFS_JOB (job)->cancellable,
-                          eject_volume_cb,
-                          job);
+          mount_op = g_mount_source_get_operation (mount_source);
+          /* free mount_op when job is completed */
+          g_object_set_data_full (G_OBJECT (job), "gvfs-backend-computer-mount-op", mount_op, g_object_unref);
+          g_volume_eject_with_operation (file->volume,
+                                         flags,
+                                         mount_op,
+                                         G_VFS_JOB (job)->cancellable,
+                                         eject_volume_cb,
+                                         job);
         }
       else if (file->drive)
         {
-          g_drive_eject (file->drive,
-                         flags,
-                         G_VFS_JOB (job)->cancellable,
-                         eject_drive_cb,
-                         job);
+          mount_op = g_mount_source_get_operation (mount_source);
+          /* free mount_op when job is completed */
+          g_object_set_data_full (G_OBJECT (job), "gvfs-backend-computer-mount-op", mount_op, g_object_unref);
+          g_drive_eject_with_operation (file->drive,
+                                        flags,
+                                        mount_op,
+                                        G_VFS_JOB (job)->cancellable,
+                                        eject_drive_cb,
+                                        job);
         }
       else
         {
@@ -1177,7 +1220,7 @@ try_start_mountable (GVfsBackend *backend,
                      GMountSource *mount_source)
 {
   ComputerFile *file;
-  GMountOperation *start_op;
+  GMountOperation *mount_op;
 
   file = lookup (G_VFS_BACKEND_COMPUTER (backend),
                  G_VFS_JOB (job), filename);
@@ -1192,12 +1235,12 @@ try_start_mountable (GVfsBackend *backend,
     {
       if (file->drive != NULL)
         {
-          start_op = g_mount_source_get_operation (mount_source);
-          /* free start_op when job is completed */
-          g_object_set_data_full (G_OBJECT (job), "gvfs-backend-computer-start-op", start_op, g_object_unref);
+          mount_op = g_mount_source_get_operation (mount_source);
+          /* free mount_op when job is completed */
+          g_object_set_data_full (G_OBJECT (job), "gvfs-backend-computer-start-op", mount_op, g_object_unref);
           g_drive_start (file->drive,
                          0,
-                         start_op,
+                         mount_op,
                          G_VFS_JOB (job)->cancellable,
                          drive_start_cb,
                          job);
@@ -1246,9 +1289,11 @@ static gboolean
 try_stop_mountable (GVfsBackend *backend,
                     GVfsJobStopMountable *job,
                     const char *filename,
-                    GMountUnmountFlags flags)
+                    GMountUnmountFlags flags,
+                    GMountSource *mount_source)
 {
   ComputerFile *file;
+  GMountOperation *mount_op;
 
   file = lookup (G_VFS_BACKEND_COMPUTER (backend),
                  G_VFS_JOB (job), filename);
@@ -1263,8 +1308,12 @@ try_stop_mountable (GVfsBackend *backend,
     {
       if (file->drive != NULL)
         {
+          mount_op = g_mount_source_get_operation (mount_source);
+          /* free mount_op when job is completed */
+          g_object_set_data_full (G_OBJECT (job), "gvfs-backend-computer-start-op", mount_op, g_object_unref);
           g_drive_stop (file->drive,
                         flags,
+                        mount_op,
                         G_VFS_JOB (job)->cancellable,
                         drive_stop_cb,
                         job);
@@ -1286,6 +1335,70 @@ try_stop_mountable (GVfsBackend *backend,
 }
 
 static void
+drive_poll_for_media_cb (GObject *source_object,
+                         GAsyncResult *res,
+                         gpointer user_data)
+{
+  GVfsJobPollMountable *job = user_data;
+  GError *error;
+  GDrive *drive;
+
+  drive = G_DRIVE (source_object);
+
+  error = NULL;
+  if (g_drive_poll_for_media_finish (drive, res, &error))
+    {
+      g_vfs_job_succeeded (G_VFS_JOB (job));
+    }
+  else
+    {
+      g_vfs_job_failed_from_error  (G_VFS_JOB (job), error);
+      g_error_free (error);
+    }
+}
+
+static gboolean
+try_poll_mountable (GVfsBackend *backend,
+                    GVfsJobPollMountable *job,
+                    const char *filename)
+{
+  ComputerFile *file;
+
+  file = lookup (G_VFS_BACKEND_COMPUTER (backend),
+                 G_VFS_JOB (job), filename);
+
+  if (file == &root)
+    {
+      g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
+                        G_IO_ERROR_NOT_MOUNTABLE_FILE,
+                        _("Not a mountable file"));
+    }
+  else if (file != NULL)
+    {
+      if (file->drive != NULL)
+        {
+          g_drive_poll_for_media (file->drive,
+                                  G_VFS_JOB (job)->cancellable,
+                                  drive_poll_for_media_cb,
+                                  job);
+        }
+      else
+        {
+          g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
+                            G_IO_ERROR_NOT_SUPPORTED,
+                            _("Can't poll file"));
+        }
+    }
+  else
+    {
+      g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
+                        G_IO_ERROR_NOT_SUPPORTED,
+                        _("Can't poll file"));
+    }
+  return TRUE;
+}
+
+static void
 g_vfs_backend_computer_class_init (GVfsBackendComputerClass *klass)
 {
   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
@@ -1303,4 +1416,5 @@ g_vfs_backend_computer_class_init (GVfsBackendComputerClass *klass)
   backend_class->try_eject_mountable = try_eject_mountable;
   backend_class->try_start_mountable = try_start_mountable;
   backend_class->try_stop_mountable = try_stop_mountable;
+  backend_class->try_poll_mountable = try_poll_mountable;
 }
diff --git a/daemon/gvfsbackenddav.c b/daemon/gvfsbackenddav.c
index ef3b55d..95dc428 100644
--- a/daemon/gvfsbackenddav.c
+++ b/daemon/gvfsbackenddav.c
@@ -2306,7 +2306,9 @@ do_set_display_name (GVfsBackend           *backend,
 
 static gboolean
 try_unmount (GVfsBackend    *backend,
-             GVfsJobUnmount *job)
+             GVfsJobUnmount *job,
+             GMountUnmountFlags flags,
+             GMountSource *mount_source)
 {
   _exit (0);
 }
diff --git a/daemon/gvfsbackendftp.c b/daemon/gvfsbackendftp.c
index aef5687..d76e7ad 100644
--- a/daemon/gvfsbackendftp.c
+++ b/daemon/gvfsbackendftp.c
@@ -528,7 +528,9 @@ try_mount (GVfsBackend *backend,
 
 static void
 do_unmount (GVfsBackend *   backend,
-            GVfsJobUnmount *job)
+            GVfsJobUnmount *job,
+            GMountUnmountFlags flags,
+            GMountSource *mount_source)
 {
   GVfsBackendFtp *ftp = G_VFS_BACKEND_FTP (backend);
   GVfsFtpConnection *conn;
diff --git a/daemon/gvfsbackendgphoto2.c b/daemon/gvfsbackendgphoto2.c
index d6d6520..9ef15a6 100644
--- a/daemon/gvfsbackendgphoto2.c
+++ b/daemon/gvfsbackendgphoto2.c
@@ -51,12 +51,13 @@
 #include "gvfsjobwrite.h"
 #include "gvfsjobclosewrite.h"
 #include "gvfsjobcreatemonitor.h"
+#include "gvfsjobunmount.h"
 #include "gvfsmonitor.h"
 #include "gvfsjobseekwrite.h"
 #include "gvfsicon.h"
 
 /* showing debug traces */
-#if 0
+#if 1
 #define DEBUG_SHOW_TRACES 1
 #endif
 
@@ -175,9 +176,6 @@ struct _GVfsBackendGphoto2
   /* see comment in ensure_ignore_prefix() */
   char *ignore_prefix;
 
-  /* list of open files */
-  int num_open_files_for_reading;
-
   DBusConnection *dbus_connection;
   LibHalContext *hal_ctx;
   char *hal_udi;
@@ -220,6 +218,9 @@ struct _GVfsBackendGphoto2
   GList *dir_monitor_proxies;
   GList *file_monitor_proxies;
 
+  /* list of open read handles (only used on the IO thread) */
+  GList *open_read_handles;
+
   /* list of open write handles (only used on the IO thread) */
   GList *open_write_handles;
 };
@@ -1630,31 +1631,76 @@ try_mount (GVfsBackend *backend,
 /* ------------------------------------------------------------------------------------------------- */
 
 static void
-do_unmount (GVfsBackend *backend,
-            GVfsJobUnmount *job)
+unmount_with_op_cb (GVfsBackend  *backend,
+                    GAsyncResult *res,
+                    gpointer      user_data)
 {
-  GError *error;
-  GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
-  int num_open_files;
+  GVfsJobUnmount *job = G_VFS_JOB_UNMOUNT (user_data);
+  gboolean should_unmount;
 
-  num_open_files = gphoto2_backend->num_open_files_for_reading + g_list_length (gphoto2_backend->open_write_handles);
+  DEBUG ("In unmount_with_op_cb");
 
-  if (num_open_files > 0)
+  should_unmount = g_vfs_backend_unmount_with_operation_finish (backend,
+                                                                res);
+
+  DEBUG ("should_unmount=%d", should_unmount);
+
+  if (should_unmount)
+    {
+
+      DEBUG ("unmounted %p", backend);
+      g_vfs_job_succeeded (G_VFS_JOB (job));
+    }
+  else
     {
-      error = g_error_new (G_IO_ERROR, G_IO_ERROR_BUSY, 
-                           ngettext("File system is busy: %d open file",
-                                    "File system is busy: %d open files",
-                                    num_open_files),
-                           num_open_files);
+      GError *error;
+      error = g_error_new (G_IO_ERROR,
+                           G_IO_ERROR_FAILED_HANDLED,
+                           _("Filesystem is busy"));
       g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
       g_error_free (error);
-      return;
     }
+}
 
-  release_device (gphoto2_backend);
-  g_vfs_job_succeeded (G_VFS_JOB (job));
+static gboolean
+try_unmount (GVfsBackend        *backend,
+             GVfsJobUnmount     *job,
+             GMountUnmountFlags  flags,
+             GMountSource       *mount_source)
+{
+  DEBUG ("In try_unmount, unmount_flags=%d", flags, mount_source);
+
+  if (flags & G_MOUNT_UNMOUNT_FORCE)
+    {
+      DEBUG ("forcibly unmounted %p", backend);
+      g_vfs_job_succeeded (G_VFS_JOB (job));
+    }
+  else if (g_mount_source_is_dummy (mount_source))
+    {
+      if (g_vfs_backend_has_blocking_processes (backend))
+        {
+          GError *error;
+          error = g_error_new (G_IO_ERROR,
+                               G_IO_ERROR_BUSY,
+                               _("Filesystem is busy"));
+          g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+          g_error_free (error);
+        }
+      else
+        {
+          DEBUG ("unmounted %p", backend);
+          g_vfs_job_succeeded (G_VFS_JOB (job));
+        }
+    }
+  else
+    {
+      g_vfs_backend_unmount_with_operation (backend,
+                                            mount_source,
+                                            (GAsyncReadyCallback) unmount_with_op_cb,
+                                            job);
+    }
 
-  DEBUG ("unmounted %p", backend);
+  return TRUE;
 }
 
 /* ------------------------------------------------------------------------------------------------- */
@@ -1742,7 +1788,7 @@ do_open_for_read_real (GVfsBackend *backend,
          read_handle->data, read_handle->size, read_handle, get_preview);
 
   g_mutex_lock (gphoto2_backend->lock);
-  gphoto2_backend->num_open_files_for_reading++;
+  gphoto2_backend->open_read_handles = g_list_prepend (gphoto2_backend->open_read_handles, read_handle);
   g_mutex_unlock (gphoto2_backend->lock);
       
   read_handle->cursor = 0;
@@ -1889,11 +1935,11 @@ do_close_read (GVfsBackend *backend,
 
   DEBUG ("close_read() handle=%p", handle);
 
-  free_read_handle (read_handle);
-
   g_mutex_lock (gphoto2_backend->lock);
-  gphoto2_backend->num_open_files_for_reading--;
+  gphoto2_backend->open_read_handles = g_list_remove (gphoto2_backend->open_read_handles, read_handle);
   g_mutex_unlock (gphoto2_backend->lock);
+
+  free_read_handle (read_handle);
   
   g_vfs_job_succeeded (G_VFS_JOB (job));
 }
@@ -3403,7 +3449,7 @@ g_vfs_backend_gphoto2_class_init (GVfsBackendGphoto2Class *klass)
 
   backend_class->try_mount = try_mount;
   backend_class->mount = do_mount;
-  backend_class->unmount = do_unmount;
+  backend_class->try_unmount = try_unmount;
   backend_class->open_icon_for_read = do_open_icon_for_read;
   backend_class->open_for_read = do_open_for_read;
   backend_class->try_read = try_read;
diff --git a/daemon/gvfsbackendlocaltest.c b/daemon/gvfsbackendlocaltest.c
index ac30adc..d36c8c5 100644
--- a/daemon/gvfsbackendlocaltest.c
+++ b/daemon/gvfsbackendlocaltest.c
@@ -253,7 +253,9 @@ do_mount (GVfsBackend *backend,
 
 
 static void
-do_unmount (GVfsBackend *backend, GVfsJobUnmount *job)
+do_unmount (GVfsBackend *backend, GVfsJobUnmount *job,
+            GMountUnmountFlags flags,
+            GMountSource *mount_source)
 {
   GVfsBackendLocalTest *op_backend;
 
diff --git a/daemon/gvfschannel.c b/daemon/gvfschannel.c
index 0a2b584..efe2376 100644
--- a/daemon/gvfschannel.c
+++ b/daemon/gvfschannel.c
@@ -53,7 +53,8 @@ G_DEFINE_TYPE_WITH_CODE (GVfsChannel, g_vfs_channel, G_TYPE_OBJECT,
 
 enum {
   PROP_0,
-  PROP_BACKEND
+  PROP_BACKEND,
+  PROP_ACTUAL_CONSUMER
 };
 
 typedef struct
@@ -85,6 +86,7 @@ struct _GVfsChannelPrivate
   GInputStream *command_stream;
   GOutputStream *reply_stream;
   int remote_fd;
+  GPid actual_consumer;
   
   GVfsBackendHandle backend_handle;
   GVfsJob *current_job;
@@ -172,6 +174,17 @@ g_vfs_channel_class_init (GVfsChannelClass *klass)
 							G_VFS_TYPE_BACKEND,
 							G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
 							G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
+
+  g_object_class_install_property (gobject_class,
+				   PROP_ACTUAL_CONSUMER,
+				   g_param_spec_int ("actual-consumer",
+                                                     P_("Actual Consumer"),
+                                                     P_("The process id of the remote end"),
+                                                     G_MININT,
+                                                     G_MAXINT,
+                                                     0,
+                                                     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+                                                     G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
 }
 
 static void
@@ -213,6 +226,11 @@ g_vfs_channel_set_property (GObject         *object,
 	g_object_unref (channel->priv->backend);
       channel->priv->backend = G_VFS_BACKEND (g_value_dup_object (value));
       break;
+
+    case PROP_ACTUAL_CONSUMER:
+      channel->priv->actual_consumer = g_value_get_int (value);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -232,6 +250,9 @@ g_vfs_channel_get_property (GObject    *object,
     case PROP_BACKEND:
       g_value_set_object (value, channel->priv->backend);
       break;
+    case PROP_ACTUAL_CONSUMER:
+      g_value_set_int (value, channel->priv->actual_consumer);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -718,3 +739,10 @@ g_vfs_channel_get_current_seq_nr (GVfsChannel *channel)
 {
   return channel->priv->current_job_seq_nr;
 }
+
+GPid
+g_vfs_channel_get_actual_consumer (GVfsChannel *channel)
+{
+  return channel->priv->actual_consumer;
+}
+
diff --git a/daemon/gvfschannel.h b/daemon/gvfschannel.h
index 0dc92fc..60b29af 100644
--- a/daemon/gvfschannel.h
+++ b/daemon/gvfschannel.h
@@ -83,6 +83,7 @@ void              g_vfs_channel_send_reply         (GVfsChannel
 						    const void                    *data,
 						    gsize                          data_len);
 guint32           g_vfs_channel_get_current_seq_nr (GVfsChannel                   *channel);
+GPid              g_vfs_channel_get_actual_consumer (GVfsChannel                  *channel);
 
 /* TODO: i/o priority? */
 
diff --git a/daemon/gvfsdaemon.c b/daemon/gvfsdaemon.c
index cf4264e..8b7d521 100644
--- a/daemon/gvfsdaemon.c
+++ b/daemon/gvfsdaemon.c
@@ -40,6 +40,8 @@
 #include <gvfsdaemonprotocol.h>
 #include <gvfsdaemonutils.h>
 #include <gvfsjobmount.h>
+#include <gvfsjobopenforread.h>
+#include <gvfsjobopenforwrite.h>
 #include <gdbusutils.h>
 
 enum {
@@ -1065,3 +1067,33 @@ g_vfs_daemon_initiate_mount (GVfsDaemon *daemon,
   job = g_vfs_job_mount_new (mount_spec, mount_source, is_automount, request, backend);
   g_vfs_daemon_queue_job (daemon, job);
 }
+
+/**
+ * g_vfs_daemon_get_blocking_processes:
+ * @daemon: A #GVfsDaemon.
+ *
+ * Gets all processes that blocks unmounting, e.g. processes with open
+ * file handles.
+ *
+ * Returns: An array of #GPid. Free with g_array_unref().
+ */
+GArray *
+g_vfs_daemon_get_blocking_processes (GVfsDaemon *daemon)
+{
+  GArray *processes;
+  GList *l;
+
+  processes = g_array_new (FALSE, FALSE, sizeof (GPid));
+  for (l = daemon->job_sources; l != NULL; l = l->next)
+    {
+      if (G_VFS_IS_CHANNEL (l->data))
+        {
+          GPid pid;
+          pid = g_vfs_channel_get_actual_consumer (G_VFS_CHANNEL (l->data));
+          g_array_append_val (processes, pid);
+        }
+    }
+
+  return processes;
+}
+
diff --git a/daemon/gvfsdaemon.h b/daemon/gvfsdaemon.h
index f40f216..805b75f 100644
--- a/daemon/gvfsdaemon.h
+++ b/daemon/gvfsdaemon.h
@@ -70,6 +70,7 @@ void        g_vfs_daemon_initiate_mount  (GVfsDaemon                    *daemon,
 					  GMountSource                  *mount_source,
 					  gboolean                       is_automount,
 					  DBusMessage                   *request);
+GArray     *g_vfs_daemon_get_blocking_processes (GVfsDaemon             *daemon);
 
 G_END_DECLS
 
diff --git a/daemon/gvfsjobopenforread.c b/daemon/gvfsjobopenforread.c
index 0775db4..a2abbeb 100644
--- a/daemon/gvfsjobopenforread.c
+++ b/daemon/gvfsjobopenforread.c
@@ -91,11 +91,13 @@ g_vfs_job_open_for_read_new (DBusConnection *connection,
   DBusError derror;
   int path_len;
   const char *path_data;
+  guint32 pid;
   
   dbus_error_init (&derror);
   if (!dbus_message_get_args (message, &derror, 
 			      DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
 			      &path_data, &path_len,
+                              DBUS_TYPE_UINT32, &pid,
 			      0))
     {
       reply = dbus_message_new_error (message,
@@ -114,6 +116,7 @@ g_vfs_job_open_for_read_new (DBusConnection *connection,
 
   job->filename = g_strndup (path_data, path_len);
   job->backend = backend;
+  job->pid = pid;
   
   return G_VFS_JOB (job);
 }
@@ -182,7 +185,8 @@ create_reply (GVfsJob *job,
   g_assert (open_job->backend_handle != NULL);
 
   error = NULL;
-  channel = g_vfs_read_channel_new (open_job->backend);
+  channel = g_vfs_read_channel_new (open_job->backend,
+                                    open_job->pid);
 
   remote_fd = g_vfs_channel_steal_remote_fd (G_VFS_CHANNEL (channel));
   if (!dbus_connection_send_fd (connection, 
@@ -217,3 +221,9 @@ static void
 finished (GVfsJob *job)
 {
 }
+
+GPid
+g_vfs_job_open_for_read_get_pid (GVfsJobOpenForRead *job)
+{
+  return job->pid;
+}
diff --git a/daemon/gvfsjobopenforread.h b/daemon/gvfsjobopenforread.h
index a15e2fe..af7cd4a 100644
--- a/daemon/gvfsjobopenforread.h
+++ b/daemon/gvfsjobopenforread.h
@@ -48,6 +48,8 @@ struct _GVfsJobOpenForRead
   GVfsBackendHandle backend_handle;
   gboolean can_seek;
   GVfsReadChannel *read_channel;
+
+  GPid pid;
 };
 
 struct _GVfsJobOpenForReadClass
@@ -64,6 +66,7 @@ void             g_vfs_job_open_for_read_set_handle    (GVfsJobOpenForRead *job,
 							GVfsBackendHandle   handle);
 void             g_vfs_job_open_for_read_set_can_seek  (GVfsJobOpenForRead *job,
 							gboolean            can_seek);
+GPid             g_vfs_job_open_for_read_get_pid       (GVfsJobOpenForRead *job);
 
 G_END_DECLS
 
diff --git a/daemon/gvfsjobopenforwrite.c b/daemon/gvfsjobopenforwrite.c
index f456f83..48ec026 100644
--- a/daemon/gvfsjobopenforwrite.c
+++ b/daemon/gvfsjobopenforwrite.c
@@ -96,6 +96,7 @@ g_vfs_job_open_for_write_new (DBusConnection *connection,
   dbus_bool_t make_backup;
   const char *etag;
   guint32 flags;
+  guint32 pid;
 
   path = NULL;
   dbus_error_init (&derror);
@@ -106,6 +107,7 @@ g_vfs_job_open_for_write_new (DBusConnection *connection,
 				      DBUS_TYPE_STRING, &etag,
 				      DBUS_TYPE_BOOLEAN, &make_backup,
 				      DBUS_TYPE_UINT32, &flags,
+                                      DBUS_TYPE_UINT32, &pid,
 				      0))
     {
       reply = dbus_message_new_error (message,
@@ -130,6 +132,7 @@ g_vfs_job_open_for_write_new (DBusConnection *connection,
   job->make_backup = make_backup;
   job->flags = flags;
   job->backend = backend;
+  job->pid = pid;
   
   return G_VFS_JOB (job);
 }
@@ -273,7 +276,8 @@ create_reply (GVfsJob *job,
   g_assert (open_job->backend_handle != NULL);
 
   error = NULL;
-  channel = g_vfs_write_channel_new (open_job->backend);
+  channel = g_vfs_write_channel_new (open_job->backend,
+                                     open_job->pid);
 
   remote_fd = g_vfs_channel_steal_remote_fd (G_VFS_CHANNEL (channel));
   if (!dbus_connection_send_fd (connection, 
@@ -310,3 +314,9 @@ static void
 finished (GVfsJob *job)
 {
 }
+
+GPid
+g_vfs_job_open_for_write_get_pid (GVfsJobOpenForWrite *job)
+{
+  return job->pid;
+}
diff --git a/daemon/gvfsjobopenforwrite.h b/daemon/gvfsjobopenforwrite.h
index 4f6469d..2cc7655 100644
--- a/daemon/gvfsjobopenforwrite.h
+++ b/daemon/gvfsjobopenforwrite.h
@@ -61,6 +61,8 @@ struct _GVfsJobOpenForWrite
   gboolean can_seek;
   goffset initial_offset;
   GVfsWriteChannel *write_channel;
+
+  GPid pid;
 };
 
 struct _GVfsJobOpenForWriteClass
@@ -79,6 +81,7 @@ void     g_vfs_job_open_for_write_set_can_seek       (GVfsJobOpenForWrite *job,
 						      gboolean             can_seek);
 void     g_vfs_job_open_for_write_set_initial_offset (GVfsJobOpenForWrite *job,
 						      goffset              initial_offset);
+GPid     g_vfs_job_open_for_write_get_pid            (GVfsJobOpenForWrite *job);
 
 G_END_DECLS
 
diff --git a/daemon/gvfsjobpollmountable.c b/daemon/gvfsjobpollmountable.c
new file mode 100644
index 0000000..f4c9898
--- /dev/null
+++ b/daemon/gvfsjobpollmountable.c
@@ -0,0 +1,159 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl redhat com>
+ */
+
+#include <config.h>
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <glib/gi18n.h>
+#include "gvfsjobpollmountable.h"
+#include "gdbusutils.h"
+#include "gvfsdaemonutils.h"
+
+G_DEFINE_TYPE (GVfsJobPollMountable, g_vfs_job_poll_mountable, G_VFS_TYPE_JOB_DBUS)
+
+static void         run          (GVfsJob        *job);
+static gboolean     try          (GVfsJob        *job);
+static DBusMessage *create_reply (GVfsJob        *job,
+				  DBusConnection *connection,
+				  DBusMessage    *message);
+
+static void
+g_vfs_job_poll_mountable_finalize (GObject *object)
+{
+  GVfsJobPollMountable *job;
+
+  job = G_VFS_JOB_POLL_MOUNTABLE (object);
+
+  g_free (job->filename);
+  
+  if (G_OBJECT_CLASS (g_vfs_job_poll_mountable_parent_class)->finalize)
+    (*G_OBJECT_CLASS (g_vfs_job_poll_mountable_parent_class)->finalize) (object);
+}
+
+static void
+g_vfs_job_poll_mountable_class_init (GVfsJobPollMountableClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GVfsJobClass *job_class = G_VFS_JOB_CLASS (klass);
+  GVfsJobDBusClass *job_dbus_class = G_VFS_JOB_DBUS_CLASS (klass);
+  
+  gobject_class->finalize = g_vfs_job_poll_mountable_finalize;
+  job_class->run = run;
+  job_class->try = try;
+  job_dbus_class->create_reply = create_reply;
+}
+
+static void
+g_vfs_job_poll_mountable_init (GVfsJobPollMountable *job)
+{
+}
+
+GVfsJob *
+g_vfs_job_poll_mountable_new (DBusConnection *connection,
+                              DBusMessage *message,
+                              GVfsBackend *backend)
+{
+  GVfsJobPollMountable *job;
+  DBusMessage *reply;
+  DBusMessageIter iter;
+  DBusError derror;
+  char *path;
+
+  dbus_error_init (&derror);
+  dbus_message_iter_init (message, &iter);
+
+  path = NULL;
+  if (!_g_dbus_message_iter_get_args (&iter, &derror,
+				      G_DBUS_TYPE_CSTRING, &path,
+				      0))
+    {
+      g_free (path);
+      reply = dbus_message_new_error (message,
+				      derror.name,
+                                      derror.message);
+      dbus_error_free (&derror);
+
+      dbus_connection_send (connection, reply, NULL);
+      return NULL;
+    }
+
+  job = g_object_new (G_VFS_TYPE_JOB_POLL_MOUNTABLE,
+		      "message", message,
+		      "connection", connection,
+		      NULL);
+
+  job->filename = path;
+  job->backend = backend;
+
+  return G_VFS_JOB (job);
+}
+
+static void
+run (GVfsJob *job)
+{
+  GVfsJobPollMountable *op_job = G_VFS_JOB_POLL_MOUNTABLE (job);
+  GVfsBackendClass *class = G_VFS_BACKEND_GET_CLASS (op_job->backend);
+
+  if (class->poll_mountable == NULL)
+    {
+      g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                        _("Operation not supported by backend"));
+      return;
+    }
+
+  class->poll_mountable (op_job->backend,
+                         op_job,
+                         op_job->filename);
+}
+
+static gboolean
+try (GVfsJob *job)
+{
+  GVfsJobPollMountable *op_job = G_VFS_JOB_POLL_MOUNTABLE (job);
+  GVfsBackendClass *class = G_VFS_BACKEND_GET_CLASS (op_job->backend);
+
+  if (class->try_poll_mountable == NULL)
+    return FALSE;
+
+  return class->try_poll_mountable (op_job->backend,
+                                    op_job,
+                                    op_job->filename);
+}
+
+/* Might be called on an i/o thread */
+static DBusMessage *
+create_reply (GVfsJob *job,
+	      DBusConnection *connection,
+	      DBusMessage *message)
+{
+  DBusMessage *reply;
+
+  reply = dbus_message_new_method_return (message);
+  
+  return reply;
+}
diff --git a/daemon/gvfsjobpollmountable.h b/daemon/gvfsjobpollmountable.h
new file mode 100644
index 0000000..4c96d55
--- /dev/null
+++ b/daemon/gvfsjobpollmountable.h
@@ -0,0 +1,63 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl redhat com>
+ */
+
+#ifndef __G_VFS_JOB_POLL_MOUNTABLE_H__
+#define __G_VFS_JOB_POLL_MOUNTABLE_H__
+
+#include <gio/gio.h>
+#include <gvfsjob.h>
+#include <gvfsjobdbus.h>
+#include <gvfsbackend.h>
+
+G_BEGIN_DECLS
+
+#define G_VFS_TYPE_JOB_POLL_MOUNTABLE         (g_vfs_job_poll_mountable_get_type ())
+#define G_VFS_JOB_POLL_MOUNTABLE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_VFS_TYPE_JOB_POLL_MOUNTABLE, GVfsJobPollMountable))
+#define G_VFS_JOB_POLL_MOUNTABLE_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_VFS_TYPE_JOB_POLL_MOUNTABLE, GVfsJobPollMountableClass))
+#define G_VFS_IS_JOB_POLL_MOUNTABLE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_VFS_TYPE_JOB_POLL_MOUNTABLE))
+#define G_VFS_IS_JOB_POLL_MOUNTABLE_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_VFS_TYPE_JOB_POLL_MOUNTABLE))
+#define G_VFS_JOB_POLL_MOUNTABLE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_VFS_TYPE_JOB_POLL_MOUNTABLE, GVfsJobPollMountableClass))
+
+typedef struct _GVfsJobPollMountableClass   GVfsJobPollMountableClass;
+
+struct _GVfsJobPollMountable
+{
+  GVfsJobDBus parent_instance;
+
+  GVfsBackend *backend;
+  char *filename;
+};
+
+struct _GVfsJobPollMountableClass
+{
+  GVfsJobDBusClass parent_class;
+};
+
+GType g_vfs_job_poll_mountable_get_type (void) G_GNUC_CONST;
+
+GVfsJob *g_vfs_job_poll_mountable_new        (DBusConnection        *connection,
+                                              DBusMessage           *message,
+                                              GVfsBackend           *backend);
+
+G_END_DECLS
+
+#endif /* __G_VFS_JOB_POLL_MOUNTABLE_H__ */
diff --git a/daemon/gvfsjobstopmountable.c b/daemon/gvfsjobstopmountable.c
index 63df2af..d2a2c09 100644
--- a/daemon/gvfsjobstopmountable.c
+++ b/daemon/gvfsjobstopmountable.c
@@ -49,6 +49,9 @@ g_vfs_job_stop_mountable_finalize (GObject *object)
 
   job = G_VFS_JOB_STOP_MOUNTABLE (object);
 
+  if (job->mount_source)
+    g_object_unref (job->mount_source);
+
   g_free (job->filename);
   
   if (G_OBJECT_CLASS (g_vfs_job_stop_mountable_parent_class)->finalize)
@@ -83,6 +86,7 @@ g_vfs_job_stop_mountable_new (DBusConnection *connection,
   DBusMessageIter iter;
   DBusError derror;
   char *path;
+  const char *dbus_id, *obj_path;
   guint32 flags;
   
   dbus_error_init (&derror);
@@ -92,6 +96,8 @@ g_vfs_job_stop_mountable_new (DBusConnection *connection,
   if (!_g_dbus_message_iter_get_args (&iter, &derror, 
 				      G_DBUS_TYPE_CSTRING, &path,
 				      DBUS_TYPE_UINT32, &flags,
+                                      DBUS_TYPE_STRING, &dbus_id,
+                                      DBUS_TYPE_OBJECT_PATH, &obj_path,
 				      0))
     {
       g_free (path);
@@ -111,6 +117,7 @@ g_vfs_job_stop_mountable_new (DBusConnection *connection,
 
   job->filename = path;
   job->backend = backend;
+  job->mount_source = g_mount_source_new (dbus_id, obj_path);
   job->flags = flags;
   
   return G_VFS_JOB (job);
@@ -132,7 +139,8 @@ run (GVfsJob *job)
   class->stop_mountable (op_job->backend,
                          op_job,
                          op_job->filename,
-                         op_job->flags);
+                         op_job->flags,
+                         op_job->mount_source);
 }
 
 static gboolean
@@ -147,7 +155,8 @@ try (GVfsJob *job)
   return class->try_stop_mountable (op_job->backend,
                                     op_job,
                                     op_job->filename,
-                                    op_job->flags);
+                                    op_job->flags,
+                                    op_job->mount_source);
 }
 
 /* Might be called on an i/o thread */
diff --git a/daemon/gvfsjobstopmountable.h b/daemon/gvfsjobstopmountable.h
index e584ce5..cdc5be4 100644
--- a/daemon/gvfsjobstopmountable.h
+++ b/daemon/gvfsjobstopmountable.h
@@ -46,6 +46,7 @@ struct _GVfsJobStopMountable
   GVfsBackend *backend;
   char *filename;
   GMountUnmountFlags flags;
+  GMountSource *mount_source;
 };
 
 struct _GVfsJobStopMountableClass
diff --git a/daemon/gvfsjobunmount.c b/daemon/gvfsjobunmount.c
index 1d7d0a6..142a5a6 100644
--- a/daemon/gvfsjobunmount.c
+++ b/daemon/gvfsjobunmount.c
@@ -46,6 +46,13 @@ static DBusMessage *create_reply (GVfsJob *job,
 static void
 g_vfs_job_unmount_finalize (GObject *object)
 {
+  GVfsJobUnmount *job;
+
+   job = G_VFS_JOB_UNMOUNT (object);
+
+  if (job->mount_source)
+    g_object_unref (job->mount_source);
+
   if (G_OBJECT_CLASS (g_vfs_job_unmount_parent_class)->finalize)
     (*G_OBJECT_CLASS (g_vfs_job_unmount_parent_class)->finalize) (object);
 }
@@ -77,6 +84,30 @@ g_vfs_job_unmount_new (DBusConnection *connection,
 		       GVfsBackend *backend)
 {
   GVfsJobUnmount *job;
+  DBusMessage *reply;
+  DBusMessageIter iter;
+  DBusError derror;
+  const char *dbus_id, *obj_path;
+  guint32 flags;
+  
+  
+  dbus_error_init (&derror);
+  dbus_message_iter_init (message, &iter);
+
+  if (!_g_dbus_message_iter_get_args (&iter, &derror, 
+                                      DBUS_TYPE_STRING, &dbus_id,
+                                      DBUS_TYPE_OBJECT_PATH, &obj_path,
+                                      DBUS_TYPE_UINT32, &flags,
+				      0))
+    {
+      reply = dbus_message_new_error (message,
+				      derror.name,
+                                      derror.message);
+      dbus_error_free (&derror);
+
+      dbus_connection_send (connection, reply, NULL);
+      return NULL;
+    }
 
   g_debug ("g_vfs_job_unmount_new request: %p\n", message);
   
@@ -86,6 +117,8 @@ g_vfs_job_unmount_new (DBusConnection *connection,
 		      NULL);
 
   job->backend = backend;
+  job->flags = flags;
+  job->mount_source = g_mount_source_new (dbus_id, obj_path);
   
   return G_VFS_JOB (job);
 }
@@ -97,7 +130,9 @@ run (GVfsJob *job)
   GVfsBackendClass *class = G_VFS_BACKEND_GET_CLASS (op_job->backend);
 
   class->unmount (op_job->backend,
-		  op_job);
+		  op_job,
+                  op_job->flags,
+                  op_job->mount_source);
 }
 
 static gboolean
@@ -119,7 +154,9 @@ try (GVfsJob *job)
     }
 
   return class->try_unmount (op_job->backend,
-			     op_job);
+			     op_job,
+                             op_job->flags,
+                             op_job->mount_source);
 }
 
 static void
diff --git a/daemon/gvfsjobunmount.h b/daemon/gvfsjobunmount.h
index aaa18db..ca8a23e 100644
--- a/daemon/gvfsjobunmount.h
+++ b/daemon/gvfsjobunmount.h
@@ -44,6 +44,8 @@ struct _GVfsJobUnmount
   GVfsJobDBus parent_instance;
 
   GVfsBackend *backend;
+  GMountUnmountFlags flags;
+  GMountSource *mount_source;
 };
 
 struct _GVfsJobUnmountClass
diff --git a/daemon/gvfsjobunmountmountable.c b/daemon/gvfsjobunmountmountable.c
index cb9eb22..2599fcd 100644
--- a/daemon/gvfsjobunmountmountable.c
+++ b/daemon/gvfsjobunmountmountable.c
@@ -49,6 +49,9 @@ g_vfs_job_unmount_mountable_finalize (GObject *object)
 
   job = G_VFS_JOB_UNMOUNT_MOUNTABLE (object);
 
+  if (job->mount_source)
+    g_object_unref (job->mount_source);
+
   g_free (job->filename);
   
   if (G_OBJECT_CLASS (g_vfs_job_unmount_mountable_parent_class)->finalize)
@@ -85,6 +88,7 @@ g_vfs_job_unmount_mountable_new (DBusConnection *connection,
   DBusError derror;
   char *path;
   guint32 flags;
+  const char *dbus_id, *obj_path;
   
   dbus_error_init (&derror);
   dbus_message_iter_init (message, &iter);
@@ -93,6 +97,8 @@ g_vfs_job_unmount_mountable_new (DBusConnection *connection,
   if (!_g_dbus_message_iter_get_args (&iter, &derror, 
 				      G_DBUS_TYPE_CSTRING, &path,
 				      DBUS_TYPE_UINT32, &flags,
+                                      DBUS_TYPE_STRING, &dbus_id,
+                                      DBUS_TYPE_OBJECT_PATH, &obj_path,
 				      0))
     {
       g_free (path);
@@ -114,6 +120,7 @@ g_vfs_job_unmount_mountable_new (DBusConnection *connection,
   job->backend = backend;
   job->eject = eject;
   job->flags = flags;
+  job->mount_source = g_mount_source_new (dbus_id, obj_path);
   
   return G_VFS_JOB (job);
 }
@@ -136,7 +143,8 @@ run (GVfsJob *job)
       class->eject_mountable (op_job->backend,
 			      op_job,
 			      op_job->filename,
-			      op_job->flags);
+			      op_job->flags,
+                              op_job->mount_source);
     }
   else
     {
@@ -150,7 +158,8 @@ run (GVfsJob *job)
       class->unmount_mountable (op_job->backend,
 				op_job,
 				op_job->filename,
-				op_job->flags);
+				op_job->flags,
+                                op_job->mount_source);
     }
 }
 
@@ -168,7 +177,8 @@ try (GVfsJob *job)
       return class->try_eject_mountable (op_job->backend,
 					 op_job,
 					 op_job->filename,
-					 op_job->flags);
+					 op_job->flags,
+                                         op_job->mount_source);
     }
   else
     {
@@ -178,7 +188,8 @@ try (GVfsJob *job)
       return class->try_unmount_mountable (op_job->backend,
 					   op_job,
 					   op_job->filename,
-					   op_job->flags);
+					   op_job->flags,
+                                           op_job->mount_source);
     }
 }
 
diff --git a/daemon/gvfsjobunmountmountable.h b/daemon/gvfsjobunmountmountable.h
index 8a1a49d..74e101f 100644
--- a/daemon/gvfsjobunmountmountable.h
+++ b/daemon/gvfsjobunmountmountable.h
@@ -47,6 +47,7 @@ struct _GVfsJobUnmountMountable
   char *filename;
   gboolean eject;
   GMountUnmountFlags flags;
+  GMountSource *mount_source;
 };
 
 struct _GVfsJobUnmountMountableClass
diff --git a/daemon/gvfsreadchannel.c b/daemon/gvfsreadchannel.c
index 1f043b8..e4834ec 100644
--- a/daemon/gvfsreadchannel.c
+++ b/daemon/gvfsreadchannel.c
@@ -282,9 +282,11 @@ g_vfs_read_channel_send_data (GVfsReadChannel  *read_channel,
 
 
 GVfsReadChannel *
-g_vfs_read_channel_new (GVfsBackend *backend)
+g_vfs_read_channel_new (GVfsBackend *backend,
+                        GPid         actual_consumer)
 {
   return g_object_new (G_VFS_TYPE_READ_CHANNEL,
 		       "backend", backend,
+                       "actual-consumer", actual_consumer,
 		       NULL);
 }
diff --git a/daemon/gvfsreadchannel.h b/daemon/gvfsreadchannel.h
index a9a18bb..458e65f 100644
--- a/daemon/gvfsreadchannel.h
+++ b/daemon/gvfsreadchannel.h
@@ -47,7 +47,8 @@ struct _GVfsReadChannelClass
 
 GType g_vfs_read_channel_get_type (void) G_GNUC_CONST;
 
-GVfsReadChannel *g_vfs_read_channel_new                (GVfsBackend        *backend);
+GVfsReadChannel *g_vfs_read_channel_new                (GVfsBackend        *backend,
+                                                        GPid                actual_consumer);
 void            g_vfs_read_channel_send_data          (GVfsReadChannel     *read_channel,
 						       char               *buffer,
 						       gsize               count);
diff --git a/daemon/gvfswritechannel.c b/daemon/gvfswritechannel.c
index 1646ace..aae7f38 100644
--- a/daemon/gvfswritechannel.c
+++ b/daemon/gvfswritechannel.c
@@ -218,9 +218,11 @@ g_vfs_write_channel_send_written (GVfsWriteChannel  *write_channel,
 
 
 GVfsWriteChannel *
-g_vfs_write_channel_new (GVfsBackend *backend)
+g_vfs_write_channel_new (GVfsBackend *backend,
+                         GPid         actual_consumer)
 {
   return g_object_new (G_VFS_TYPE_WRITE_CHANNEL,
 		       "backend", backend,
+                       "actual-consumer", actual_consumer,
 		       NULL);
 }
diff --git a/daemon/gvfswritechannel.h b/daemon/gvfswritechannel.h
index ce4dc14..65ed9d0 100644
--- a/daemon/gvfswritechannel.h
+++ b/daemon/gvfswritechannel.h
@@ -47,7 +47,8 @@ struct _GVfsWriteChannelClass
 
 GType g_vfs_write_channel_get_type (void) G_GNUC_CONST;
 
-GVfsWriteChannel *g_vfs_write_channel_new              (GVfsBackend      *backend);
+GVfsWriteChannel *g_vfs_write_channel_new              (GVfsBackend      *backend,
+                                                        GPid              actual_consumer);
 void              g_vfs_write_channel_send_written     (GVfsWriteChannel *write_channel,
 							gsize             bytes_written);
 void              g_vfs_write_channel_send_closed      (GVfsWriteChannel *write_channel,
diff --git a/monitor/gdu/ggdudrive.c b/monitor/gdu/ggdudrive.c
index c89928c..7eb98b2 100644
--- a/monitor/gdu/ggdudrive.c
+++ b/monitor/gdu/ggdudrive.c
@@ -54,6 +54,7 @@ struct _GGduDrive {
 
   GDriveStartStopType start_stop_type;
   gboolean can_start;
+  gboolean can_start_degraded;
   gboolean can_stop;
 };
 
@@ -131,6 +132,7 @@ update_drive (GGduDrive *drive)
   gboolean old_has_media;
   gboolean old_can_eject;
   gboolean old_can_start;
+  gboolean old_can_start_degraded;
   gboolean old_can_stop;
   gboolean old_start_stop_type;
   gboolean old_is_media_check_automatic;
@@ -141,6 +143,7 @@ update_drive (GGduDrive *drive)
   old_has_media = drive->has_media;
   old_can_eject = drive->can_eject;
   old_can_start = drive->can_start;
+  old_can_start_degraded = drive->can_start_degraded;
   old_can_stop = drive->can_stop;
   old_start_stop_type = drive->start_stop_type;
   old_can_poll_for_media = drive->can_poll_for_media;
@@ -193,11 +196,18 @@ update_drive (GGduDrive *drive)
   /* determine start/stop type */
   drive->can_stop = FALSE;
   drive->can_start = FALSE;
+  drive->can_start_degraded = FALSE;
   drive->start_stop_type = G_DRIVE_START_STOP_TYPE_UNKNOWN;
   if (gdu_drive_is_activatable (GDU_DRIVE (drive->presentable)))
     {
+      gboolean can_activate;
+      gboolean degraded;
+
+      can_activate = gdu_drive_can_activate (GDU_DRIVE (drive->presentable), &degraded);
+
       drive->can_stop  = gdu_drive_can_deactivate (GDU_DRIVE (drive->presentable));
-      drive->can_start = gdu_drive_can_activate (GDU_DRIVE (drive->presentable), NULL);
+      drive->can_start = can_activate && !degraded;
+      drive->can_start_degraded = can_activate && degraded;
       drive->start_stop_type = G_DRIVE_START_STOP_TYPE_MULTIDISK;
     }
   else if (device != NULL && gdu_device_drive_get_can_detach (device))
@@ -232,6 +242,7 @@ update_drive (GGduDrive *drive)
            */
           drive->can_stop = TRUE;
           drive->can_start = FALSE;
+          drive->can_start_degraded = FALSE;
           drive->start_stop_type = G_DRIVE_START_STOP_TYPE_SHUTDOWN;
         }
     }
@@ -253,6 +264,7 @@ update_drive (GGduDrive *drive)
               (old_has_media == drive->has_media) &&
               (old_can_eject == drive->can_eject) &&
               (old_can_start == drive->can_start) &&
+              (old_can_start_degraded == drive->can_start_degraded) &&
               (old_can_stop == drive->can_stop) &&
               (old_start_stop_type == drive->start_stop_type) &&
               (old_is_media_check_automatic == drive->is_media_check_automatic) &&
@@ -433,6 +445,13 @@ g_gdu_drive_can_start (GDrive *_drive)
 }
 
 static gboolean
+g_gdu_drive_can_start_degraded (GDrive *_drive)
+{
+  GGduDrive *drive = G_GDU_DRIVE (_drive);
+  return drive->can_start_degraded;
+}
+
+static gboolean
 g_gdu_drive_can_stop (GDrive *_drive)
 {
   GGduDrive *drive = G_GDU_DRIVE (_drive);
@@ -458,6 +477,7 @@ typedef struct {
   GDrive *drive;
   GAsyncReadyCallback callback;
   gpointer user_data;
+  GMountOperation *mount_operation;
   GCancellable *cancellable;
   GMountUnmountFlags flags;
 
@@ -510,11 +530,12 @@ unmount_mounts_do (UnmountMountsOp *data)
 
       /*g_warning ("unmounting %p", mount);*/
 
-      g_mount_unmount (mount,
-                       data->flags,
-                       data->cancellable,
-                       unmount_mounts_cb,
-                       data);
+      g_mount_unmount_with_operation (mount,
+                                      data->flags,
+                                      data->mount_operation,
+                                      data->cancellable,
+                                      unmount_mounts_cb,
+                                      data);
     }
 }
 
@@ -528,7 +549,7 @@ unmount_mounts_cb (GObject *source_object,
   GSimpleAsyncResult *simple;
   GError *error = NULL;
 
-  if (!g_mount_unmount_finish (mount, res, &error))
+  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->code != G_IO_ERROR_FAILED_HANDLED)
@@ -564,6 +585,7 @@ unmount_mounts_cb (GObject *source_object,
 static void
 unmount_mounts (GGduDrive           *drive,
                 GMountUnmountFlags   flags,
+                GMountOperation     *mount_operation,
                 GCancellable        *cancellable,
                 GAsyncReadyCallback  callback,
                 gpointer             user_data,
@@ -576,6 +598,7 @@ unmount_mounts (GGduDrive           *drive,
 
   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;
@@ -689,17 +712,19 @@ g_gdu_drive_eject_on_all_unmounted (GDrive              *_drive,
 }
 
 static void
-g_gdu_drive_eject (GDrive              *_drive,
-                   GMountUnmountFlags   flags,
-                   GCancellable        *cancellable,
-                   GAsyncReadyCallback  callback,
-                   gpointer             user_data)
+g_gdu_drive_eject_with_operation (GDrive              *_drive,
+                                  GMountUnmountFlags   flags,
+                                  GMountOperation     *mount_operation,
+                                  GCancellable        *cancellable,
+                                  GAsyncReadyCallback  callback,
+                                  gpointer             user_data)
 {
   GGduDrive *drive = G_GDU_DRIVE (_drive);
 
   /* 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,
@@ -708,11 +733,29 @@ g_gdu_drive_eject (GDrive              *_drive,
 }
 
 static gboolean
+g_gdu_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
+g_gdu_drive_eject (GDrive              *drive,
+                   GMountUnmountFlags   flags,
+                   GCancellable        *cancellable,
+                   GAsyncReadyCallback  callback,
+                   gpointer             user_data)
+{
+  g_gdu_drive_eject_with_operation (drive, flags, NULL, cancellable, callback, user_data);
+}
+
+static gboolean
 g_gdu_drive_eject_finish (GDrive        *drive,
                           GAsyncResult  *result,
                           GError       **error)
 {
-  return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error);
+  return g_gdu_drive_eject_with_operation_finish (drive, result, error);
 }
 
 /* ---------------------------------------------------------------------------------------------------- */
@@ -807,6 +850,7 @@ g_gdu_drive_stop_on_all_unmounted (GDrive              *_drive,
 static void
 g_gdu_drive_stop (GDrive              *_drive,
                   GMountUnmountFlags   flags,
+                  GMountOperation     *mount_operation,
                   GCancellable        *cancellable,
                   GAsyncReadyCallback  callback,
                   gpointer             user_data)
@@ -816,6 +860,7 @@ g_gdu_drive_stop (GDrive              *_drive,
   /* 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,
@@ -1132,6 +1177,8 @@ g_gdu_drive_drive_iface_init (GDriveIface *iface)
   iface->can_poll_for_media = g_gdu_drive_can_poll_for_media;
   iface->eject = g_gdu_drive_eject;
   iface->eject_finish = g_gdu_drive_eject_finish;
+  iface->eject_with_operation = g_gdu_drive_eject_with_operation;
+  iface->eject_with_operation_finish = g_gdu_drive_eject_with_operation_finish;
   iface->poll_for_media = g_gdu_drive_poll_for_media;
   iface->poll_for_media_finish = g_gdu_drive_poll_for_media_finish;
   iface->get_identifier = g_gdu_drive_get_identifier;
@@ -1139,6 +1186,7 @@ g_gdu_drive_drive_iface_init (GDriveIface *iface)
 
   iface->get_start_stop_type = g_gdu_drive_get_start_stop_type;
   iface->can_start = g_gdu_drive_can_start;
+  iface->can_start_degraded = g_gdu_drive_can_start_degraded;
   iface->can_stop = g_gdu_drive_can_stop;
   iface->start = g_gdu_drive_start;
   iface->start_finish = g_gdu_drive_start_finish;
diff --git a/monitor/gdu/ggdumount.c b/monitor/gdu/ggdumount.c
index 78088d5..946510f 100644
--- a/monitor/gdu/ggdumount.c
+++ b/monitor/gdu/ggdumount.c
@@ -39,7 +39,7 @@
 #include "ggduvolume.h"
 
 #define BUSY_UNMOUNT_NUM_ATTEMPTS              5
-#define BUSY_UNMOUNT_MS_DELAY_BETWEEN_ATTEMPTS 500
+#define BUSY_UNMOUNT_MS_DELAY_BETWEEN_ATTEMPTS 100
 
 struct _GGduMount
 {
@@ -456,7 +456,6 @@ typedef struct {
   GMount *mount;
 
   gchar **argv;
-  guint num_attempts_left;
 
   GAsyncReadyCallback callback;
   gpointer user_data;
@@ -467,14 +466,14 @@ typedef struct {
   guint error_channel_source_id;
   GString *error_string;
 
-} UnmountOp;
+} BinUnmountData;
 
-static gboolean unmount_attempt (UnmountOp *data);
+static gboolean bin_unmount_attempt (BinUnmountData *data);
 
 static void
-unmount_cb (GPid pid, gint status, gpointer user_data)
+bin_unmount_cb (GPid pid, gint status, gpointer user_data)
 {
-  UnmountOp *data = user_data;
+  BinUnmountData *data = user_data;
   GSimpleAsyncResult *simple;
 
   g_spawn_close_pid (pid);
@@ -489,15 +488,6 @@ unmount_cb (GPid pid, gint status, gpointer user_data)
       if (data->error_string->str != NULL && strstr (data->error_string->str, "is busy") != NULL)
         error_code = G_IO_ERROR_BUSY;
 
-      if (error_code == G_IO_ERROR_BUSY && data->num_attempts_left > 0)
-        {
-          g_timeout_add (BUSY_UNMOUNT_MS_DELAY_BETWEEN_ATTEMPTS,
-                         (GSourceFunc) unmount_attempt,
-                         data);
-          data->num_attempts_left -= 1;
-          goto out;
-        }
-
       error = g_error_new_literal (G_IO_ERROR,
                                    error_code,
                                    data->error_string->str);
@@ -524,17 +514,14 @@ unmount_cb (GPid pid, gint status, gpointer user_data)
   close (data->error_fd);
   g_strfreev (data->argv);
   g_free (data);
-
- out:
-  ;
 }
 
 static gboolean
-unmount_read_error (GIOChannel *channel,
-                    GIOCondition condition,
-                    gpointer user_data)
+bin_unmount_read_error (GIOChannel *channel,
+                        GIOCondition condition,
+                        gpointer user_data)
 {
-  UnmountOp *data = user_data;
+  BinUnmountData *data = user_data;
   gchar buf[BUFSIZ];
   gsize bytes_read;
   GError *error;
@@ -565,7 +552,7 @@ read:
 }
 
 static gboolean
-unmount_attempt (UnmountOp *data)
+bin_unmount_attempt (BinUnmountData *data)
 {
   GPid child_pid;
   GError *error;
@@ -578,8 +565,8 @@ unmount_attempt (UnmountOp *data)
                                  NULL,         /* child_setup */
                                  NULL,         /* user_data for child_setup */
                                  &child_pid,
-                                 NULL,           /* standard_input */
-                                 NULL,           /* standard_output */
+                                 NULL,         /* standard_input */
+                                 NULL,         /* standard_output */
                                  &(data->error_fd),
                                  &error)) {
     g_assert (error != NULL);
@@ -593,8 +580,8 @@ unmount_attempt (UnmountOp *data)
   if (error != NULL)
     goto handle_error;
 
-  data->error_channel_source_id = g_io_add_watch (data->error_channel, G_IO_IN, unmount_read_error, data);
-  g_child_watch_add (child_pid, unmount_cb, data);
+  data->error_channel_source_id = g_io_add_watch (data->error_channel, G_IO_IN, bin_unmount_read_error, data);
+  g_child_watch_add (child_pid, bin_unmount_cb, data);
 
 handle_error:
 
@@ -622,116 +609,345 @@ handle_error:
 }
 
 static void
-unmount_do (GMount              *mount,
-            GCancellable        *cancellable,
-            GAsyncReadyCallback  callback,
-            gpointer             user_data,
-            char               **argv)
+bin_unmount_do (GMount              *mount,
+                GCancellable        *cancellable,
+                GAsyncReadyCallback  callback,
+                gpointer             user_data,
+                char               **argv)
 {
-  UnmountOp *data;
+  BinUnmountData *data;
 
-  data = g_new0 (UnmountOp, 1);
+  data = g_new0 (BinUnmountData, 1);
   data->mount = mount;
   data->callback = callback;
   data->user_data = user_data;
   data->cancellable = cancellable;
-  data->num_attempts_left = BUSY_UNMOUNT_NUM_ATTEMPTS;
   data->argv = g_strdupv (argv);
 
-  unmount_attempt (data);
+  bin_unmount_attempt (data);
 }
 
 /* ---------------------------------------------------------------------------------------------------- */
-static void
-luks_lock_cb (GduDevice *device,
-              GError    *error,
-              gpointer   user_data)
+
+static GArray *
+_get_busy_processes (GduDevice *device)
 {
-  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+  GArray *processes;
+  GList *open_files, *l;
 
-  if (error != NULL)
+  processes = g_array_new (FALSE, FALSE, sizeof (GPid));
+
+  open_files = gdu_device_filesystem_list_open_files_sync (device, NULL);
+  for (l = open_files; l != NULL; l = l->next)
     {
-      /* We could handle PolicyKit integration here but this action is allowed by default
-       * and this won't be needed when porting to PolicyKit 1.0 anyway
-       */
-      g_simple_async_result_set_from_error (simple, error);
-      g_error_free (error);
+      GduProcess *gdup = GDU_PROCESS (l->data);
+      GPid pid = gdu_process_get_id (gdup);
+      g_array_append_val (processes, pid);
     }
+  g_list_foreach (open_files, (GFunc) g_object_unref, NULL);
+  g_list_free (open_files);
 
-  g_simple_async_result_complete (simple);
-  g_object_unref (simple);
+  return processes;
 }
 
-static gboolean gdu_unmount_attempt (GSimpleAsyncResult *simple);
+typedef struct
+{
+  gint ref_count;
+
+  GGduMount *mount;
+  GMountUnmountFlags flags;
+  GduDevice *device;
+
+  GAsyncReadyCallback callback;
+  gpointer user_data;
+
+  GCancellable *cancellable;
+  GMountOperation *mount_operation;
+
+  gulong cancelled_handler_id;
+  gulong mount_op_reply_handler_id;
+  guint retry_unmount_timer_id;
+
+  gboolean completed;
+} _GduUnmountData;
+
+static _GduUnmountData *
+_gdu_unmount_data_ref (_GduUnmountData *data)
+{
+  g_atomic_int_inc (&data->ref_count);
+  return data;
+}
 
 static void
-gdu_unmount_cb (GduDevice *device,
-                GError    *error,
-                gpointer   user_data)
+_gdu_unmount_data_unref (_GduUnmountData *data)
 {
-  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+  if (!g_atomic_int_dec_and_test (&data->ref_count))
+    return;
 
-  if (error != NULL)
+  if (data->cancelled_handler_id > 0)
+    g_signal_handler_disconnect (data->cancellable, data->cancelled_handler_id);
+  if (data->mount_op_reply_handler_id > 0)
     {
-      gint error_code;
+      /* 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);
+    }
 
-      /*g_debug ("domain=%s error_code=%d '%s'", g_quark_to_string (error->domain), error->code, error->message);*/
+  g_object_unref (data->mount);
+  if (data->cancellable != NULL)
+    g_object_unref (data->cancellable);
+  if (data->mount_operation != NULL)
+    g_object_unref (data->mount_operation);
+  g_object_unref (data->device);
 
-      error_code = G_IO_ERROR_FAILED;
+  g_free (data);
+}
+
+static void
+_gdu_unmount_luks_lock_cb (GduDevice *device,
+                           GError    *error,
+                           gpointer   user_data)
+{
+  _GduUnmountData *data = user_data;
+  GSimpleAsyncResult *simple;
+
+  if (error != NULL)
+    {
+      /* translate to GduError to GIOError */
       if (error->domain == GDU_ERROR && error->code == GDU_ERROR_BUSY)
-        error_code = G_IO_ERROR_BUSY;
+        error->code = G_IO_ERROR_BUSY;
+      else
+        error->code = G_IO_ERROR_FAILED;
+      error->domain = G_IO_ERROR;
+
+      if (!data->completed)
+        {
+          simple = g_simple_async_result_new_from_error (G_OBJECT (data->mount),
+                                                         data->callback,
+                                                         data->user_data,
+                                                         error);
+          g_error_free (error);
+          data->completed = TRUE;
+          _gdu_unmount_data_unref (data);
+          g_simple_async_result_complete_in_idle (simple);
+          g_object_unref (simple);
+        }
+      goto out;
+    }
 
-      /* We could handle PolicyKit integration here but this action is allowed by default
-       * and this won't be needed when porting to PolicyKit 1.0 anyway
+  /* we're done */
+  if (!data->completed)
+    {
+      simple = g_simple_async_result_new (G_OBJECT (data->mount),
+                                          data->callback,
+                                          data->user_data,
+                                          NULL);
+      data->completed = TRUE;
+      _gdu_unmount_data_unref (data);
+      g_simple_async_result_complete_in_idle (simple);
+      g_object_unref (simple);
+    }
+
+ out:
+  /* release the ref taken when calling gdu_device_op_luks_lock() */
+  _gdu_unmount_data_unref (data);
+}
+
+static void _gdu_unmount_attempt (_GduUnmountData *data,
+                                  gboolean         force_unmount);
+
+static void
+_gdu_unmount_on_mount_op_reply (GMountOperation       *mount_operation,
+                                GMountOperationResult result,
+                                gpointer              user_data)
+{
+  _GduUnmountData *data = user_data;
+  GSimpleAsyncResult *simple;
+  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);
+
+  if (result == G_MOUNT_OPERATION_ABORTED ||
+      (result == G_MOUNT_OPERATION_HANDLED && choice == 1))
+    {
+      /* don't show an error dialog here */
+      if (!data->completed)
+        {
+          simple = g_simple_async_result_new_error (G_OBJECT (data->mount),
+                                                    data->callback,
+                                                    data->user_data,
+                                                    G_IO_ERROR,
+                                                    G_IO_ERROR_FAILED_HANDLED,
+                                                    "GMountOperation aborted (user should never see this "
+                                                    "error since it is G_IO_ERROR_FAILED_HANDLED)");
+          data->completed = TRUE;
+          _gdu_unmount_data_unref (data);
+          g_simple_async_result_complete_in_idle (simple);
+          g_object_unref (simple);
+        }
+    }
+  else if (result == G_MOUNT_OPERATION_HANDLED)
+    {
+      /* user chose force unmount => try again with force_unmount==TRUE */
+
+      _gdu_unmount_attempt (data, TRUE);
+    }
+  else
+    {
+      /* result == G_MOUNT_OPERATION_UNHANDLED => GMountOperation instance doesn't
+       * support :show-processes signal
        */
+      if (!data->completed)
+        {
+          simple = g_simple_async_result_new_error (G_OBJECT (data->mount),
+                                                    data->callback,
+                                                    data->user_data,
+                                                    G_IO_ERROR,
+                                                    G_IO_ERROR_BUSY,
+                                                    _("One or more programs are preventing the unmount operation."));
+          data->completed = TRUE;
+          _gdu_unmount_data_unref (data);
+          g_simple_async_result_complete_in_idle (simple);
+          g_object_unref (simple);
+        }
+    }
+}
+
+static gboolean
+_gdu_unmount_on_retry_unmount_timer (gpointer user_data)
+{
+  _GduUnmountData *data = user_data;
+
+  /* we're removing the timeout */
+  data->retry_unmount_timer_id = 0;
+
+  if (data->completed)
+    goto out;
 
-      if (error_code == G_IO_ERROR_BUSY)
+  /* timeout expired => try again */
+  _gdu_unmount_attempt (data, FALSE);
+
+ out:
+  return FALSE;
+}
+
+
+static void
+_gdu_unmount_cb (GduDevice *device,
+                 GError    *error,
+                 gpointer   user_data)
+{
+  _GduUnmountData *data = user_data;
+  GSimpleAsyncResult *simple;
+
+  if (error != NULL)
+    {
+      /* translate to GduError to GIOError */
+      if (error->domain == GDU_ERROR && error->code == GDU_ERROR_BUSY)
+        error->code = G_IO_ERROR_BUSY;
+      else
+        error->code = G_IO_ERROR_FAILED;
+      error->domain = G_IO_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)
         {
-          guint num_attempts_left;
+          GArray *processes;
 
-          num_attempts_left = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (simple), "num-attempts-left"));
+          /* TODO: might be better to do this Async - punt on this until gdu uses GCancellable... */
+          processes = _get_busy_processes (device);
 
-          if (num_attempts_left > 0)
+          if (processes != NULL && processes->len > 0)
             {
-              num_attempts_left -= 1;
-              g_object_set_data (G_OBJECT (simple),
-                                 "num-attempts-left",
-                                 GUINT_TO_POINTER (num_attempts_left));
-
-              g_timeout_add (BUSY_UNMOUNT_MS_DELAY_BETWEEN_ATTEMPTS,
-                             (GSourceFunc) gdu_unmount_attempt,
-                             simple);
+              const gchar *choices[3] = {NULL, NULL, NULL};
+              const gchar *message;
+
+              if (data->mount_op_reply_handler_id == 0)
+                {
+                  data->mount_op_reply_handler_id = g_signal_connect (data->mount_operation,
+                                                                      "reply",
+                                                                      G_CALLBACK (_gdu_unmount_on_mount_op_reply),
+                                                                      data);
+                }
+              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,
+                                                                        _gdu_unmount_on_retry_unmount_timer,
+                                                                        data);
+                }
               goto out;
             }
+          else
+            {
+              if (processes != NULL)
+                g_array_unref (processes);
+              /* falls through to reporting an error */
+            }
         }
 
-      g_simple_async_result_set_error (simple,
-                                       G_IO_ERROR,
-                                       error_code,
-                                       "%s",
-                                       error->message);
-      g_error_free (error);
-      g_simple_async_result_complete (simple);
-      g_object_unref (simple);
+      /* otherwise, just report the error */
+
+      if (!data->completed)
+        {
+          simple = g_simple_async_result_new_from_error (G_OBJECT (data->mount),
+                                                         data->callback,
+                                                         data->user_data,
+                                                         error);
+          g_error_free (error);
+          data->completed = TRUE;
+          _gdu_unmount_data_unref (data);
+          g_simple_async_result_complete_in_idle (simple);
+          g_object_unref (simple);
+        }
       goto out;
     }
 
-  /* if volume is a cleartext LUKS block device, then also lock this one */
+  /* success! if we unmounted a cleartext device, also tear down the crypto mapping */
   if (gdu_device_is_luks_cleartext (device))
     {
       const gchar *luks_cleartext_slave_object_path;
       GduDevice *luks_cleartext_slave;
       GduPool *pool;
 
-      luks_cleartext_slave_object_path = gdu_device_luks_cleartext_get_slave (device);
+      luks_cleartext_slave_object_path = gdu_device_luks_cleartext_get_slave (data->device);
       if (luks_cleartext_slave_object_path == NULL)
         {
-          g_simple_async_result_set_error (simple,
-                                           G_IO_ERROR,
-                                           G_IO_ERROR_FAILED,
-                                           "Cannot get LUKS cleartext slave");
-          g_simple_async_result_complete (simple);
-          g_object_unref (simple);
+          if (!data->completed)
+            {
+              simple = g_simple_async_result_new_error (G_OBJECT (data->mount),
+                                                        data->callback,
+                                                        data->user_data,
+                                                        G_IO_ERROR,
+                                                        G_IO_ERROR_FAILED,
+                                                        _("Cannot get LUKS cleartext slave"));
+              data->completed = TRUE;
+              _gdu_unmount_data_unref (data);
+              g_simple_async_result_complete_in_idle (simple);
+              g_object_unref (simple);
+            }
           goto out;
         }
 
@@ -741,67 +957,134 @@ gdu_unmount_cb (GduDevice *device,
 
       if (luks_cleartext_slave == NULL)
         {
-          g_simple_async_result_set_error (simple,
-                                           G_IO_ERROR,
-                                           G_IO_ERROR_FAILED,
-                                           "Cannot get LUKS cleartext slave");
-          g_simple_async_result_complete (simple);
-          g_object_unref (simple);
+          if (!data->completed)
+            {
+              simple = g_simple_async_result_new_error (G_OBJECT (data->mount),
+                                                        data->callback,
+                                                        data->user_data,
+                                                        G_IO_ERROR,
+                                                        G_IO_ERROR_FAILED,
+                                                        _("Cannot get LUKS cleartext slave from path `%s'"),
+                                                        luks_cleartext_slave_object_path);
+              data->completed = TRUE;
+              _gdu_unmount_data_unref (data);
+              g_simple_async_result_complete_in_idle (simple);
+              g_object_unref (simple);
+            }
           goto out;
         }
 
+      /* take an extra ref on data since we may complete before the callback */
       gdu_device_op_luks_lock (luks_cleartext_slave,
-                               luks_lock_cb,
-                               simple);
+                               _gdu_unmount_luks_lock_cb,
+                               _gdu_unmount_data_ref (data));
 
       g_object_unref (luks_cleartext_slave);
       goto out;
     }
 
-  g_simple_async_result_complete (simple);
-  g_object_unref (simple);
+  /* not a cleartext device => we're done */
+  if (!data->completed)
+    {
+      simple = g_simple_async_result_new (G_OBJECT (data->mount),
+                                          data->callback,
+                                          data->user_data,
+                                          NULL);
+      data->completed = TRUE;
+      _gdu_unmount_data_unref (data);
+      g_simple_async_result_complete_in_idle (simple);
+      g_object_unref (simple);
+    }
 
  out:
-  ;
+  /* release the ref taken when calling gdu_device_op_filesystem_unmount() */
+  _gdu_unmount_data_unref (data);
 }
 
-static gboolean
-gdu_unmount_attempt (GSimpleAsyncResult *simple)
+static void
+_gdu_unmount_attempt (_GduUnmountData *data,
+                      gboolean         force_unmount)
 {
-  GduDevice *device;
+  /* TODO: honor force_unmount */
 
-  /* TODO: honor flags */
-  device = g_object_get_data (G_OBJECT (simple), "gdu-device");
-  gdu_device_op_filesystem_unmount (device, gdu_unmount_cb, simple);
-  return FALSE;
+  /* take an extra ref on data since we may complete before the callback */
+  gdu_device_op_filesystem_unmount (data->device, _gdu_unmount_cb, _gdu_unmount_data_ref (data));
 }
 
+
 static void
-g_gdu_mount_unmount (GMount              *_mount,
-                     GMountUnmountFlags   flags,
-                     GCancellable        *cancellable,
-                     GAsyncReadyCallback  callback,
-                     gpointer             user_data)
+_gdu_unmount_on_cancelled (GMount   *mount,
+                           gpointer  user_data)
+{
+  _GduUnmountData *data = user_data;
+
+  if (!data->completed)
+    {
+      GSimpleAsyncResult *simple;
+
+      simple = g_simple_async_result_new_error (G_OBJECT (data->mount),
+                                                data->callback,
+                                                data->user_data,
+                                                G_IO_ERROR,
+                                                G_IO_ERROR_CANCELLED,
+                                                _("Operation was cancelled"));
+      data->completed = TRUE;
+      _gdu_unmount_data_unref (data);
+      g_simple_async_result_complete_in_idle (simple);
+      g_object_unref (simple);
+    }
+}
+
+static void
+_gdu_unmount_do (GGduMount           *mount,
+                 GMountUnmountFlags   flags,
+                 GMountOperation     *mount_operation,
+                 GCancellable        *cancellable,
+                 GAsyncReadyCallback  callback,
+                 gpointer             user_data,
+                 GduDevice           *device)
+{
+  _GduUnmountData *data;
+
+  data = g_new0 (_GduUnmountData, 1);
+  data->ref_count = 1;
+  data->mount = g_object_ref (mount);
+  data->flags = flags;
+  data->mount_operation = mount_operation != NULL ? g_object_ref (mount_operation) : NULL;
+  data->cancellable = cancellable != NULL ? g_object_ref (cancellable) : NULL;
+  data->callback = callback;
+  data->user_data = user_data;
+  data->device = g_object_ref (device);
+
+  /* operation may be cancelled at any time */
+  if (data->cancellable != NULL)
+    {
+      data->cancelled_handler_id = g_signal_connect (data->cancellable,
+                                                     "cancelled",
+                                                     G_CALLBACK (_gdu_unmount_on_cancelled),
+                                                     data);
+    }
+
+  /* attempt to unmount */
+  _gdu_unmount_attempt (data, FALSE);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+g_gdu_mount_unmount_with_operation (GMount              *_mount,
+                                    GMountUnmountFlags   flags,
+                                    GMountOperation     *mount_operation,
+                                    GCancellable        *cancellable,
+                                    GAsyncReadyCallback  callback,
+                                    gpointer             user_data)
 {
   GGduMount *mount = G_GDU_MOUNT (_mount);
-  GSimpleAsyncResult *simple;
   GduPresentable *gdu_volume;
 
   /* emit the ::mount-pre-unmount signal */
   g_signal_emit_by_name (mount->volume_monitor, "mount-pre-unmount", mount);
 
-  /* If we end up with G_IO_ERROR_BUSY, try again BUSY_UNMOUNT_NUM_ATTEMPTS times
-   * waiting BUSY_UNMOUNT_MS_DELAY_BETWEEN_ATTEMPTS milliseconds between each
-   * attempt.
-   *
-   * This is to give other processes recieving the ::mount-pre-unmount signal some
-   * time to close file descriptors.
-   *
-   * TODO: Unfortunately this code is a bit messy because we take two different
-   *       codepaths depending on whether we use GDU or the native unmount command.
-   *       It would be good to clean this up.
-   */
-
   gdu_volume = NULL;
   if (mount->volume != NULL)
     gdu_volume = g_gdu_volume_get_presentable_with_cleartext (mount->volume);
@@ -817,22 +1100,18 @@ g_gdu_mount_unmount (GMount              *_mount,
       else
         argv[1] = mount->device_file;
 
-      unmount_do (_mount, cancellable, callback, user_data, argv);
+      bin_unmount_do (_mount, cancellable, callback, user_data, argv);
     }
   else if (gdu_volume != NULL)
     {
-      simple = g_simple_async_result_new (G_OBJECT (mount),
-                                          callback,
-                                          user_data,
-                                          NULL);
-
-      g_object_set_data (G_OBJECT (simple),
-                         "num-attempts-left",
-                         GUINT_TO_POINTER (BUSY_UNMOUNT_NUM_ATTEMPTS));
-
       if (mount->is_burn_mount)
         {
-          /* burn mounts are really never mounted... */
+          /* burn mounts are really never mounted so complete successfully immediately */
+          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);
         }
@@ -840,12 +1119,13 @@ g_gdu_mount_unmount (GMount              *_mount,
         {
           GduDevice *device;
           device = gdu_presentable_get_device (gdu_volume);
-          g_object_set_data_full (G_OBJECT (simple), "gdu-device", device, g_object_unref);
-          gdu_unmount_attempt (simple);
+          _gdu_unmount_do (mount, flags, mount_operation, cancellable, callback, user_data, device);
+          g_object_unref (device);
         }
     }
   else
     {
+      GSimpleAsyncResult *simple;
       simple = g_simple_async_result_new_error (G_OBJECT (mount),
                                                 callback,
                                                 user_data,
@@ -858,11 +1138,29 @@ g_gdu_mount_unmount (GMount              *_mount,
 }
 
 static gboolean
-g_gdu_mount_unmount_finish (GMount       *mount,
+g_gdu_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
+g_gdu_mount_unmount (GMount              *mount,
+                     GMountUnmountFlags   flags,
+                     GCancellable        *cancellable,
+                     GAsyncReadyCallback  callback,
+                     gpointer             user_data)
+{
+  g_gdu_mount_unmount_with_operation (mount, flags, NULL, cancellable, callback, user_data);
+}
+
+static gboolean
+g_gdu_mount_unmount_finish (GMount        *mount,
                             GAsyncResult  *result,
                             GError       **error)
 {
-  return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error);
+  return g_gdu_mount_unmount_with_operation_finish (mount, result, error);
 }
 
 /* ---------------------------------------------------------------------------------------------------- */
@@ -885,11 +1183,12 @@ eject_wrapper_callback (GObject *source_object,
 }
 
 static void
-g_gdu_mount_eject (GMount              *mount,
-                   GMountUnmountFlags   flags,
-                   GCancellable        *cancellable,
-                   GAsyncReadyCallback  callback,
-                   gpointer             user_data)
+g_gdu_mount_eject_with_operation (GMount              *mount,
+                                  GMountUnmountFlags   flags,
+                                  GMountOperation     *mount_operation,
+                                  GCancellable        *cancellable,
+                                  GAsyncReadyCallback  callback,
+                                  gpointer             user_data)
 {
   GGduMount *gdu_mount = G_GDU_MOUNT (mount);
   GDrive *drive;
@@ -905,7 +1204,7 @@ g_gdu_mount_eject (GMount              *mount,
       data->object = g_object_ref (mount);
       data->callback = callback;
       data->user_data = user_data;
-      g_drive_eject (drive, flags, cancellable, eject_wrapper_callback, data);
+      g_drive_eject_with_operation (drive, flags, mount_operation, cancellable, eject_wrapper_callback, data);
       g_object_unref (drive);
     }
   else
@@ -924,9 +1223,9 @@ g_gdu_mount_eject (GMount              *mount,
 }
 
 static gboolean
-g_gdu_mount_eject_finish (GMount        *_mount,
-                          GAsyncResult  *result,
-                          GError       **error)
+g_gdu_mount_eject_with_operation_finish (GMount        *_mount,
+                                         GAsyncResult  *result,
+                                         GError       **error)
 {
   GGduMount *mount = G_GDU_MOUNT (_mount);
   GDrive *drive;
@@ -940,7 +1239,7 @@ g_gdu_mount_eject_finish (GMount        *_mount,
 
   if (drive != NULL)
     {
-      res = g_drive_eject_finish (drive, result, error);
+      res = g_drive_eject_with_operation_finish (drive, result, error);
       g_object_unref (drive);
     }
   else
@@ -952,6 +1251,24 @@ g_gdu_mount_eject_finish (GMount        *_mount,
   return res;
 }
 
+static void
+g_gdu_mount_eject (GMount              *mount,
+                   GMountUnmountFlags   flags,
+                   GCancellable        *cancellable,
+                   GAsyncReadyCallback  callback,
+                   gpointer             user_data)
+{
+  g_gdu_mount_eject_with_operation (mount, flags, NULL, cancellable, callback, user_data);
+}
+
+static gboolean
+g_gdu_mount_eject_finish (GMount        *mount,
+                          GAsyncResult  *result,
+                          GError       **error)
+{
+  return g_gdu_mount_eject_with_operation_finish (mount, result, error);
+}
+
 /* TODO: handle force_rescan */
 static gchar **
 g_gdu_mount_guess_content_type_sync (GMount              *_mount,
@@ -1063,8 +1380,12 @@ g_gdu_mount_mount_iface_init (GMountIface *iface)
   iface->can_eject = g_gdu_mount_can_eject;
   iface->unmount = g_gdu_mount_unmount;
   iface->unmount_finish = g_gdu_mount_unmount_finish;
+  iface->unmount_with_operation = g_gdu_mount_unmount_with_operation;
+  iface->unmount_with_operation_finish = g_gdu_mount_unmount_with_operation_finish;
   iface->eject = g_gdu_mount_eject;
   iface->eject_finish = g_gdu_mount_eject_finish;
+  iface->eject_with_operation = g_gdu_mount_eject_with_operation;
+  iface->eject_with_operation_finish = g_gdu_mount_eject_with_operation_finish;
   iface->guess_content_type = g_gdu_mount_guess_content_type;
   iface->guess_content_type_finish = g_gdu_mount_guess_content_type_finish;
   iface->guess_content_type_sync = g_gdu_mount_guess_content_type_sync;
diff --git a/monitor/gdu/ggduvolume.c b/monitor/gdu/ggduvolume.c
index 73ad0fc..a20b0bb 100644
--- a/monitor/gdu/ggduvolume.c
+++ b/monitor/gdu/ggduvolume.c
@@ -1015,10 +1015,6 @@ mount_with_mount_operation (MountOpData *data)
   if (toplevel != NULL)
     drive_name = gdu_presentable_get_name (toplevel);
 
-  /* This is going to look ass until bug 573416 is fixed. Unfortunately
-   * the gtk+ maintain has stated "oh, I stopped using luks" but that's
-   * more of a gtk+ problem ;-)
-   */
   if (drive_name != NULL)
     {
       if (gdu_device_is_partition (device))
@@ -1494,11 +1490,12 @@ eject_wrapper_callback (GObject *source_object,
 }
 
 static void
-g_gdu_volume_eject (GVolume              *volume,
-                    GMountUnmountFlags   flags,
-                    GCancellable        *cancellable,
-                    GAsyncReadyCallback  callback,
-                    gpointer             user_data)
+g_gdu_volume_eject_with_operation (GVolume              *volume,
+                                   GMountUnmountFlags   flags,
+                                   GMountOperation     *mount_operation,
+                                   GCancellable        *cancellable,
+                                   GAsyncReadyCallback  callback,
+                                   gpointer             user_data)
 {
   GGduVolume *gdu_volume = G_GDU_VOLUME (volume);
   GGduDrive *drive;
@@ -1514,7 +1511,7 @@ g_gdu_volume_eject (GVolume              *volume,
       data->object = g_object_ref (volume);
       data->callback = callback;
       data->user_data = user_data;
-      g_drive_eject (G_DRIVE (drive), flags, cancellable, eject_wrapper_callback, data);
+      g_drive_eject_with_operation (G_DRIVE (drive), flags, mount_operation, cancellable, eject_wrapper_callback, data);
       g_object_unref (drive);
     }
   else
@@ -1532,9 +1529,9 @@ g_gdu_volume_eject (GVolume              *volume,
 }
 
 static gboolean
-g_gdu_volume_eject_finish (GVolume        *volume,
-                           GAsyncResult  *result,
-                           GError       **error)
+g_gdu_volume_eject_with_operation_finish (GVolume        *volume,
+                                          GAsyncResult  *result,
+                                          GError       **error)
 {
   GGduVolume *gdu_volume = G_GDU_VOLUME (volume);
   gboolean res;
@@ -1542,7 +1539,7 @@ g_gdu_volume_eject_finish (GVolume        *volume,
   res = TRUE;
   if (gdu_volume->drive != NULL)
     {
-      res = g_drive_eject_finish (G_DRIVE (gdu_volume->drive), result, error);
+      res = g_drive_eject_with_operation_finish (G_DRIVE (gdu_volume->drive), result, error);
     }
   else
     {
@@ -1553,6 +1550,24 @@ g_gdu_volume_eject_finish (GVolume        *volume,
   return res;
 }
 
+static void
+g_gdu_volume_eject (GVolume              *volume,
+                    GMountUnmountFlags   flags,
+                    GCancellable        *cancellable,
+                    GAsyncReadyCallback  callback,
+                    gpointer             user_data)
+{
+  g_gdu_volume_eject_with_operation (volume, flags, NULL, cancellable, callback, user_data);
+}
+
+static gboolean
+g_gdu_volume_eject_finish (GVolume        *volume,
+                           GAsyncResult  *result,
+                           GError       **error)
+{
+  return g_gdu_volume_eject_with_operation_finish (volume, result, error);
+}
+
 static char *
 g_gdu_volume_get_identifier (GVolume     *_volume,
                              const char  *kind)
@@ -1637,6 +1652,8 @@ g_gdu_volume_volume_iface_init (GVolumeIface *iface)
   iface->mount_finish = g_gdu_volume_mount_finish;
   iface->eject = g_gdu_volume_eject;
   iface->eject_finish = g_gdu_volume_eject_finish;
+  iface->eject_with_operation = g_gdu_volume_eject_with_operation;
+  iface->eject_with_operation_finish = g_gdu_volume_eject_with_operation_finish;
   iface->get_identifier = g_gdu_volume_get_identifier;
   iface->enumerate_identifiers = g_gdu_volume_enumerate_identifiers;
   iface->get_activation_root = g_gdu_volume_get_activation_root;
diff --git a/monitor/hal/ghaldrive.c b/monitor/hal/ghaldrive.c
index b6f3814..e778f0a 100644
--- a/monitor/hal/ghaldrive.c
+++ b/monitor/hal/ghaldrive.c
@@ -516,7 +516,6 @@ static gboolean
 g_hal_drive_can_eject (GDrive *drive)
 {
   GHalDrive *hal_drive = G_HAL_DRIVE (drive);
-  gboolean res;
   
   return hal_drive->can_eject;
 }
@@ -621,6 +620,7 @@ typedef struct {
   GDrive *drive;
   GAsyncReadyCallback callback;
   gpointer user_data;
+  GMountOperation *mount_operation;
   GCancellable *cancellable;
   GMountUnmountFlags flags;
 
@@ -652,7 +652,7 @@ _eject_unmount_mounts_cb (GObject *source_object,
   GSimpleAsyncResult *simple;
   GError *error = NULL;
 
-  if (!g_mount_unmount_finish (mount, res, &error))
+  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->code != G_IO_ERROR_FAILED_HANDLED)
@@ -710,20 +710,22 @@ _eject_unmount_mounts (UnmountMountsOp *data)
 
       /*g_warning ("unmounting %p", mount);*/
 
-      g_mount_unmount (mount,
-                       data->flags,
-                       data->cancellable,
-                       _eject_unmount_mounts_cb,
-                       data);
+      g_mount_unmount_with_operation (mount,
+                                      data->flags,
+                                      data->mount_operation,
+                                      data->cancellable,
+                                      _eject_unmount_mounts_cb,
+                                      data);
     }
 }
 
 static void
-g_hal_drive_eject (GDrive              *drive,
-                   GMountUnmountFlags   flags,
-                   GCancellable        *cancellable,
-                   GAsyncReadyCallback  callback,
-                   gpointer             user_data)
+g_hal_drive_eject_with_operation (GDrive              *drive,
+                                  GMountUnmountFlags   flags,
+                                  GMountOperation     *mount_operation,
+                                  GCancellable        *cancellable,
+                                  GAsyncReadyCallback  callback,
+                                  gpointer             user_data)
 {
   GHalDrive *hal_drive = G_HAL_DRIVE (drive);
   UnmountMountsOp *data;
@@ -733,6 +735,7 @@ g_hal_drive_eject (GDrive              *drive,
 
   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;
@@ -752,11 +755,29 @@ g_hal_drive_eject (GDrive              *drive,
 }
 
 static gboolean
+g_hal_drive_eject_with_operation_finish (GDrive        *drive,
+                                         GAsyncResult  *result,
+                                         GError       **error)
+{
+  return TRUE;
+}
+
+static void
+g_hal_drive_eject (GDrive              *drive,
+                   GMountUnmountFlags   flags,
+                   GCancellable        *cancellable,
+                   GAsyncReadyCallback  callback,
+                   gpointer             user_data)
+{
+  g_hal_drive_eject (drive, flags, cancellable, callback, user_data);
+}
+
+static gboolean
 g_hal_drive_eject_finish (GDrive        *drive,
                           GAsyncResult  *result,
                           GError       **error)
 {
-  return TRUE;
+  return g_hal_drive_eject_with_operation_finish (drive, result, error);
 }
 
 typedef struct {
@@ -929,6 +950,8 @@ g_hal_drive_drive_iface_init (GDriveIface *iface)
   iface->can_poll_for_media = g_hal_drive_can_poll_for_media;
   iface->eject = g_hal_drive_eject;
   iface->eject_finish = g_hal_drive_eject_finish;
+  iface->eject_with_operation = g_hal_drive_eject_with_operation;
+  iface->eject_with_operation_finish = g_hal_drive_eject_with_operation_finish;
   iface->poll_for_media = g_hal_drive_poll_for_media;
   iface->poll_for_media_finish = g_hal_drive_poll_for_media_finish;
   iface->get_identifier = g_hal_drive_get_identifier;
diff --git a/monitor/hal/ghalmount.c b/monitor/hal/ghalmount.c
index 2037108..56f4055 100644
--- a/monitor/hal/ghalmount.c
+++ b/monitor/hal/ghalmount.c
@@ -915,11 +915,12 @@ unmount_do (GMount               *mount,
 }
 
 static void
-g_hal_mount_unmount (GMount              *mount,
-                     GMountUnmountFlags   flags,
-                     GCancellable        *cancellable,
-                     GAsyncReadyCallback  callback,
-                     gpointer             user_data)
+g_hal_mount_unmount_with_operation (GMount              *mount,
+                                    GMountUnmountFlags   flags,
+                                    GMountOperation     *mount_operation,
+                                    GCancellable        *cancellable,
+                                    GAsyncReadyCallback  callback,
+                                    gpointer             user_data)
 {
   GHalMount *hal_mount = G_HAL_MOUNT (mount);
   char *argv[] = {"gnome-mount", "-u", "-b", "-d", NULL, NULL};
@@ -945,11 +946,29 @@ g_hal_mount_unmount (GMount              *mount,
 }
 
 static gboolean
-g_hal_mount_unmount_finish (GMount       *mount,
+g_hal_mount_unmount_with_operation_finish (GMount       *mount,
+                                           GAsyncResult  *result,
+                                           GError       **error)
+{
+  return TRUE;
+}
+
+static void
+g_hal_mount_unmount (GMount              *mount,
+                     GMountUnmountFlags   flags,
+                     GCancellable        *cancellable,
+                     GAsyncReadyCallback  callback,
+                     gpointer             user_data)
+{
+  return g_hal_mount_unmount_with_operation (mount, flags, NULL, cancellable, callback, user_data);
+}
+
+static gboolean
+g_hal_mount_unmount_finish (GMount        *mount,
                             GAsyncResult  *result,
                             GError       **error)
 {
-  return TRUE;
+  return g_hal_mount_unmount_with_operation_finish (mount, result, error);
 }
 
 typedef struct {
@@ -970,11 +989,12 @@ eject_wrapper_callback (GObject *source_object,
 }
 
 static void
-g_hal_mount_eject (GMount              *mount,
-                   GMountUnmountFlags   flags,
-                   GCancellable        *cancellable,
-                   GAsyncReadyCallback  callback,
-                   gpointer             user_data)
+g_hal_mount_eject_with_operation (GMount              *mount,
+                                  GMountUnmountFlags   flags,
+                                  GMountOperation     *mount_operation,
+                                  GCancellable        *cancellable,
+                                  GAsyncReadyCallback  callback,
+                                  gpointer             user_data)
 {
   GHalMount *hal_mount = G_HAL_MOUNT (mount);
   GDrive *drive;
@@ -990,15 +1010,15 @@ g_hal_mount_eject (GMount              *mount,
       data->object = g_object_ref (mount);
       data->callback = callback;
       data->user_data = user_data;
-      g_drive_eject (drive, flags, cancellable, eject_wrapper_callback, data);
+      g_drive_eject_with_operation (drive, flags, mount_operation, cancellable, eject_wrapper_callback, data);
       g_object_unref (drive);
     }
 }
 
 static gboolean
-g_hal_mount_eject_finish (GMount        *mount,
-                          GAsyncResult  *result,
-                          GError       **error)
+g_hal_mount_eject_with_operation_finish (GMount        *mount,
+                                         GAsyncResult  *result,
+                                         GError       **error)
 {
   GHalMount *hal_mount = G_HAL_MOUNT (mount);
   GDrive *drive;
@@ -1012,12 +1032,30 @@ g_hal_mount_eject_finish (GMount        *mount,
   
   if (drive != NULL)
     {
-      res = g_drive_eject_finish (drive, result, error);
+      res = g_drive_eject_with_operation_finish (drive, result, error);
       g_object_unref (drive);
     }
   return res;
 }
 
+static void
+g_hal_mount_eject (GMount              *mount,
+                   GMountUnmountFlags   flags,
+                   GCancellable        *cancellable,
+                   GAsyncReadyCallback  callback,
+                   gpointer             user_data)
+{
+  return g_hal_mount_eject_with_operation (mount, flags, NULL, cancellable, callback, user_data);
+}
+
+static gboolean
+g_hal_mount_eject_finish (GMount        *mount,
+                          GAsyncResult  *result,
+                          GError       **error)
+{
+  return g_hal_mount_eject_with_operation_finish (mount, result, error);
+}
+
 /* TODO: handle force_rescan */
 static char **
 g_hal_mount_guess_content_type_sync (GMount              *mount,
@@ -1142,8 +1180,12 @@ g_hal_mount_mount_iface_init (GMountIface *iface)
   iface->can_eject = g_hal_mount_can_eject;
   iface->unmount = g_hal_mount_unmount;
   iface->unmount_finish = g_hal_mount_unmount_finish;
+  iface->unmount_with_operation = g_hal_mount_unmount_with_operation;
+  iface->unmount_with_operation_finish = g_hal_mount_unmount_with_operation_finish;
   iface->eject = g_hal_mount_eject;
   iface->eject_finish = g_hal_mount_eject_finish;
+  iface->eject_with_operation = g_hal_mount_eject_with_operation;
+  iface->eject_with_operation_finish = g_hal_mount_eject_with_operation_finish;
   iface->guess_content_type = g_hal_mount_guess_content_type;
   iface->guess_content_type_finish = g_hal_mount_guess_content_type_finish;
   iface->guess_content_type_sync = g_hal_mount_guess_content_type_sync;
diff --git a/monitor/hal/ghalvolume.c b/monitor/hal/ghalvolume.c
index 47dda66..cef37f7 100644
--- a/monitor/hal/ghalvolume.c
+++ b/monitor/hal/ghalvolume.c
@@ -859,11 +859,12 @@ eject_wrapper_callback (GObject *source_object,
 }
 
 static void
-g_hal_volume_eject (GVolume              *volume,
-                    GMountUnmountFlags   flags,
-                    GCancellable        *cancellable,
-                    GAsyncReadyCallback  callback,
-                    gpointer             user_data)
+g_hal_volume_eject_with_operation (GVolume              *volume,
+                                   GMountUnmountFlags   flags,
+                                   GMountOperation     *mount_operation,
+                                   GCancellable        *cancellable,
+                                   GAsyncReadyCallback  callback,
+                                   gpointer             user_data)
 {
   GHalVolume *hal_volume = G_HAL_VOLUME (volume);
   GHalDrive *drive;
@@ -881,25 +882,43 @@ g_hal_volume_eject (GVolume              *volume,
       data->object = g_object_ref (volume);
       data->callback = callback;
       data->user_data = user_data;
-      g_drive_eject (G_DRIVE (drive), flags, cancellable, eject_wrapper_callback, data);
+      g_drive_eject_with_operation (G_DRIVE (drive), flags, mount_operation, cancellable, eject_wrapper_callback, data);
       g_object_unref (drive);
     }
 }
 
 static gboolean
-g_hal_volume_eject_finish (GVolume        *volume,
-                          GAsyncResult  *result,
-                          GError       **error)
+g_hal_volume_eject_with_operation_finish (GVolume        *volume,
+                                          GAsyncResult  *result,
+                                          GError       **error)
 {
   GHalVolume *hal_volume = G_HAL_VOLUME (volume);
   gboolean res;
 
   res = TRUE;
   if (hal_volume->drive != NULL)
-    res = g_drive_eject_finish (G_DRIVE (hal_volume->drive), result, error);
+    res = g_drive_eject_with_operation_finish (G_DRIVE (hal_volume->drive), result, error);
   return res;
 }
 
+static void
+g_hal_volume_eject (GVolume              *volume,
+                    GMountUnmountFlags   flags,
+                    GCancellable        *cancellable,
+                    GAsyncReadyCallback  callback,
+                    gpointer             user_data)
+{
+  return g_hal_volume_eject_with_operation (volume, flags, NULL, cancellable, callback, user_data);
+}
+
+static gboolean
+g_hal_volume_eject_finish (GVolume        *volume,
+                           GAsyncResult  *result,
+                           GError       **error)
+{
+  return g_hal_volume_eject_with_operation_finish (volume, result, error);
+}
+
 static char *
 g_hal_volume_get_identifier (GVolume              *volume,
                              const char          *kind)
@@ -980,6 +999,8 @@ g_hal_volume_volume_iface_init (GVolumeIface *iface)
   iface->mount_finish = g_hal_volume_mount_finish;
   iface->eject = g_hal_volume_eject;
   iface->eject_finish = g_hal_volume_eject_finish;
+  iface->eject_with_operation = g_hal_volume_eject_with_operation;
+  iface->eject_with_operation_finish = g_hal_volume_eject_with_operation_finish;
   iface->get_identifier = g_hal_volume_get_identifier;
   iface->enumerate_identifiers = g_hal_volume_enumerate_identifiers;
   iface->get_activation_root = g_hal_volume_get_activation_root;
diff --git a/monitor/proxy/Makefile.am b/monitor/proxy/Makefile.am
index e85ea27..0c22f11 100644
--- a/monitor/proxy/Makefile.am
+++ b/monitor/proxy/Makefile.am
@@ -14,6 +14,7 @@ libgioremote_volume_monitor_la_SOURCES =				\
 	gproxymount.c			gproxymount.h			\
 	gproxyshadowmount.c		gproxyshadowmount.h		\
 	gproxyvolumemonitor.c		gproxyvolumemonitor.h		\
+	gproxymountoperation.c		gproxymountoperation.h		\
 	$(NULL)
 
 libgioremote_volume_monitor_la_CFLAGS =					\
diff --git a/monitor/proxy/gproxydrive.c b/monitor/proxy/gproxydrive.c
index 5f027b8..bfe6ce9 100644
--- a/monitor/proxy/gproxydrive.c
+++ b/monitor/proxy/gproxydrive.c
@@ -35,6 +35,7 @@
 #include "gproxyvolumemonitor.h"
 #include "gproxydrive.h"
 #include "gproxyvolume.h"
+#include "gproxymountoperation.h"
 
 /* Protects all fields of GProxyDrive that can change */
 G_LOCK_DEFINE_STATIC(proxy_drive);
@@ -54,12 +55,11 @@ struct _GProxyDrive {
   gboolean has_media;
   gboolean is_media_removable;
   gboolean can_start;
+  gboolean can_start_degraded;
   gboolean can_stop;
   GDriveStartStopType start_stop_type;
 
   GHashTable *identifiers;
-
-  GHashTable *hash_start_op_id_to_data;
 };
 
 static void g_proxy_drive_drive_iface_init (GDriveIface *iface);
@@ -92,8 +92,6 @@ g_proxy_drive_finalize (GObject *object)
   if (drive->identifiers != NULL)
     g_hash_table_unref (drive->identifiers);
 
-  g_hash_table_unref (drive->hash_start_op_id_to_data);
-
   if (G_OBJECT_CLASS (g_proxy_drive_parent_class)->finalize)
     (*G_OBJECT_CLASS (g_proxy_drive_parent_class)->finalize) (object);
 }
@@ -114,7 +112,6 @@ g_proxy_drive_class_finalize (GProxyDriveClass *klass)
 static void
 g_proxy_drive_init (GProxyDrive *proxy_drive)
 {
-  proxy_drive->hash_start_op_id_to_data = g_hash_table_new (g_str_hash, g_str_equal);
 }
 
 GProxyDrive *
@@ -138,12 +135,13 @@ g_proxy_drive_new (GProxyVolumeMonitor *volume_monitor)
  * boolean              is-media-removable
  * boolean              is-media-check-automatic
  * boolean              can-start
+ * boolean              can-start-degraded
  * boolean              can-stop
  * uint32               start-stop-type
  * array:string         volume-ids
  * dict:string->string  identifiers
  */
-#define DRIVE_STRUCT_TYPE "(sssbbbbbbuasa{ss})"
+#define DRIVE_STRUCT_TYPE "(sssbbbbbbbbuasa{ss})"
 
 void
 g_proxy_drive_update (GProxyDrive         *drive,
@@ -160,6 +158,7 @@ g_proxy_drive_update (GProxyDrive         *drive,
   dbus_bool_t is_media_removable;
   dbus_bool_t is_media_check_automatic;
   dbus_bool_t can_start;
+  dbus_bool_t can_start_degraded;
   dbus_bool_t can_stop;
   dbus_uint32_t start_stop_type;
   GPtrArray *volume_ids;
@@ -184,6 +183,8 @@ g_proxy_drive_update (GProxyDrive         *drive,
   dbus_message_iter_next (&iter_struct);
   dbus_message_iter_get_basic (&iter_struct, &can_start);
   dbus_message_iter_next (&iter_struct);
+  dbus_message_iter_get_basic (&iter_struct, &can_start_degraded);
+  dbus_message_iter_next (&iter_struct);
   dbus_message_iter_get_basic (&iter_struct, &can_stop);
   dbus_message_iter_next (&iter_struct);
   dbus_message_iter_get_basic (&iter_struct, &start_stop_type);
@@ -236,6 +237,7 @@ g_proxy_drive_update (GProxyDrive         *drive,
   drive->is_media_removable = is_media_removable;
   drive->is_media_check_automatic = is_media_check_automatic;
   drive->can_start = can_start;
+  drive->can_start_degraded = can_start_degraded;
   drive->can_stop = can_stop;
   drive->start_stop_type = start_stop_type;
   drive->identifiers = identifiers != NULL ? g_hash_table_ref (identifiers) : NULL;
@@ -390,6 +392,19 @@ g_proxy_drive_can_start (GDrive *drive)
 }
 
 static gboolean
+g_proxy_drive_can_start_degraded (GDrive *drive)
+{
+  GProxyDrive *proxy_drive = G_PROXY_DRIVE (drive);
+  gboolean res;
+
+  G_LOCK (proxy_drive);
+  res = proxy_drive->can_start_degraded;
+  G_UNLOCK (proxy_drive);
+
+  return res;
+}
+
+static gboolean
 g_proxy_drive_can_stop (GDrive *drive)
 {
   GProxyDrive *proxy_drive = G_PROXY_DRIVE (drive);
@@ -471,6 +486,8 @@ typedef struct {
   gchar *cancellation_id;
   GCancellable *cancellable;
   gulong cancelled_handler_id;
+
+  const gchar *mount_op_id;
 } DBusOp;
 
 static void
@@ -556,6 +573,7 @@ eject_cb (DBusMessage *reply,
       g_object_unref (simple);
     }
 
+  g_proxy_mount_operation_destroy (data->mount_op_id);
   g_object_unref (data->drive);
   g_free (data->cancellation_id);
   if (data->cancellable != NULL)
@@ -564,11 +582,12 @@ eject_cb (DBusMessage *reply,
 }
 
 static void
-g_proxy_drive_eject (GDrive              *drive,
-                     GMountUnmountFlags   flags,
-                     GCancellable        *cancellable,
-                     GAsyncReadyCallback  callback,
-                     gpointer             user_data)
+g_proxy_drive_eject_with_operation (GDrive              *drive,
+                                    GMountUnmountFlags   flags,
+                                    GMountOperation     *mount_operation,
+                                    GCancellable        *cancellable,
+                                    GAsyncReadyCallback  callback,
+                                    gpointer             user_data)
 {
   GProxyDrive *proxy_drive = G_PROXY_DRIVE (drive);
   DBusConnection *connection;
@@ -598,6 +617,7 @@ g_proxy_drive_eject (GDrive              *drive,
   data->drive = g_object_ref (drive);
   data->callback = callback;
   data->user_data = user_data;
+  data->mount_op_id = g_proxy_mount_operation_wrap (mount_operation, proxy_drive->volume_monitor);
 
   if (cancellable != NULL)
     {
@@ -627,12 +647,14 @@ g_proxy_drive_eject (GDrive              *drive,
                             &(data->cancellation_id),
                             DBUS_TYPE_UINT32,
                             &_flags,
+                            DBUS_TYPE_STRING,
+                            &(data->mount_op_id),
                             DBUS_TYPE_INVALID);
   G_UNLOCK (proxy_drive);
 
   _g_dbus_connection_call_async (connection,
                                  message,
-                                 -1,
+                                 G_PROXY_VOLUME_MONITOR_DBUS_TIMEOUT, /* 30 minute timeout */
                                  (GAsyncDBusCallback) eject_cb,
                                  data);
   dbus_connection_unref (connection);
@@ -642,9 +664,9 @@ g_proxy_drive_eject (GDrive              *drive,
 }
 
 static gboolean
-g_proxy_drive_eject_finish (GDrive        *drive,
-                            GAsyncResult  *result,
-                            GError       **error)
+g_proxy_drive_eject_with_operation_finish (GDrive        *drive,
+                                           GAsyncResult  *result,
+                                           GError       **error)
 {
   if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
     return FALSE;
@@ -654,6 +676,26 @@ g_proxy_drive_eject_finish (GDrive        *drive,
 /* ---------------------------------------------------------------------------------------------------- */
 
 static void
+g_proxy_drive_eject (GDrive              *drive,
+                     GMountUnmountFlags   flags,
+                     GCancellable        *cancellable,
+                     GAsyncReadyCallback  callback,
+                     gpointer             user_data)
+{
+  g_proxy_drive_eject_with_operation (drive, flags, NULL, cancellable, callback, user_data);
+}
+
+static gboolean
+g_proxy_drive_eject_finish (GDrive        *drive,
+                            GAsyncResult  *result,
+                            GError       **error)
+{
+  return g_proxy_drive_eject_with_operation_finish (drive, result, error);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
 stop_cb (DBusMessage *reply,
           GError *error,
           DBusOp *data)
@@ -679,6 +721,7 @@ stop_cb (DBusMessage *reply,
       g_object_unref (simple);
     }
 
+  g_proxy_mount_operation_destroy (data->mount_op_id);
   g_object_unref (data->drive);
   g_free (data->cancellation_id);
   if (data->cancellable != NULL)
@@ -689,6 +732,7 @@ stop_cb (DBusMessage *reply,
 static void
 g_proxy_drive_stop (GDrive              *drive,
                     GMountUnmountFlags   flags,
+                    GMountOperation     *mount_operation,
                     GCancellable        *cancellable,
                     GAsyncReadyCallback  callback,
                     gpointer             user_data)
@@ -721,6 +765,7 @@ g_proxy_drive_stop (GDrive              *drive,
   data->drive = g_object_ref (drive);
   data->callback = callback;
   data->user_data = user_data;
+  data->mount_op_id = g_proxy_mount_operation_wrap (mount_operation, proxy_drive->volume_monitor);
 
   if (cancellable != NULL)
     {
@@ -750,12 +795,14 @@ g_proxy_drive_stop (GDrive              *drive,
                             &(data->cancellation_id),
                             DBUS_TYPE_UINT32,
                             &_flags,
+                            DBUS_TYPE_STRING,
+                            &(data->mount_op_id),
                             DBUS_TYPE_INVALID);
   G_UNLOCK (proxy_drive);
 
   _g_dbus_connection_call_async (connection,
                                  message,
-                                 -1,
+                                 G_PROXY_VOLUME_MONITOR_DBUS_TIMEOUT, /* 30 minute timeout */
                                  (GAsyncDBusCallback) stop_cb,
                                  data);
   dbus_connection_unref (connection);
@@ -785,9 +832,7 @@ typedef struct {
   GCancellable *cancellable;
   gulong cancelled_handler_id;
 
-  gchar *start_op_id;
-  GMountOperation *start_operation;
-  gulong reply_handler_id;
+  const gchar *mount_op_id;
 } DBusStartOp;
 
 static void
@@ -817,16 +862,9 @@ start_cb (DBusMessage  *reply,
     }
 
   /* free DBusStartOp */
-  if (strlen (data->start_op_id) > 0)
-    g_hash_table_remove (data->drive->hash_start_op_id_to_data, data->start_op_id);
+  g_proxy_mount_operation_destroy (data->mount_op_id);
   g_object_unref (data->drive);
 
-  g_free (data->start_op_id);
-  if (data->reply_handler_id > 0)
-    g_signal_handler_disconnect (data->start_operation, data->reply_handler_id);
-  if (data->start_operation != NULL)
-    g_object_unref (data->start_operation);
-
   g_free (data->cancellation_id);
   if (data->cancellable != NULL)
     g_object_unref (data->cancellable);
@@ -881,7 +919,7 @@ start_cancelled (GCancellable *cancellable,
 static void
 g_proxy_drive_start (GDrive              *drive,
                      GDriveStartFlags     flags,
-                     GMountOperation     *start_operation,
+                     GMountOperation     *mount_operation,
                      GCancellable        *cancellable,
                      GAsyncReadyCallback  callback,
                      gpointer             user_data)
@@ -927,18 +965,7 @@ g_proxy_drive_start (GDrive              *drive,
       data->cancellation_id = g_strdup ("");
     }
 
-  if (start_operation != NULL)
-    {
-      data->start_op_id = g_strdup_printf ("%p", start_operation);
-      data->start_operation = g_object_ref (start_operation);
-      g_hash_table_insert (proxy_drive->hash_start_op_id_to_data,
-                           data->start_op_id,
-                           data);
-    }
-  else
-    {
-      data->start_op_id = g_strdup ("");
-    }
+  data->mount_op_id = g_proxy_mount_operation_wrap (mount_operation, proxy_drive->volume_monitor);
 
   connection = g_proxy_volume_monitor_get_dbus_connection (proxy_drive->volume_monitor);
   name = g_proxy_volume_monitor_get_dbus_name (proxy_drive->volume_monitor);
@@ -955,7 +982,7 @@ g_proxy_drive_start (GDrive              *drive,
                             DBUS_TYPE_UINT32,
                             &(flags),
                             DBUS_TYPE_STRING,
-                            &(data->start_op_id),
+                            &(data->mount_op_id),
                             DBUS_TYPE_INVALID);
   G_UNLOCK (proxy_drive);
 
@@ -971,225 +998,6 @@ g_proxy_drive_start (GDrive              *drive,
   ;
 }
 
-
-static void
-start_op_reply_cb (DBusMessage *reply,
-                   GError      *error,
-                   DBusStartOp      *data)
-{
-  if (error != NULL)
-    {
-      g_warning ("Error from StartOpReply(): %s", error->message);
-    }
-}
-
-static void
-start_operation_reply (GMountOperation        *start_operation,
-                       GMountOperationResult  result,
-                       gpointer               user_data)
-{
-  DBusStartOp *data = user_data;
-  DBusConnection *connection;
-  const char *name;
-  DBusMessage *message;
-  const char *user_name;
-  const char *domain;
-  const char *password;
-  char *encoded_password;
-  dbus_uint32_t password_save;
-  dbus_uint32_t choice;
-  dbus_bool_t anonymous;
-
-  connection = g_proxy_volume_monitor_get_dbus_connection (data->drive->volume_monitor);
-  name = g_proxy_volume_monitor_get_dbus_name (data->drive->volume_monitor);
-
-  user_name     = g_mount_operation_get_username (start_operation);
-  domain        = g_mount_operation_get_domain (start_operation);
-  password      = g_mount_operation_get_password (start_operation);
-  password_save = g_mount_operation_get_password_save (start_operation);
-  choice        = g_mount_operation_get_choice (start_operation);
-  anonymous     = g_mount_operation_get_anonymous (start_operation);
-
-  if (user_name == NULL)
-    user_name = "";
-  if (domain == NULL)
-    domain = "";
-  if (password == NULL)
-    password = "";
-
-  /* NOTE: this is not to add "security", it's merely to prevent accidental exposure
-   *       of passwords when running dbus-monitor
-   */
-  encoded_password = g_base64_encode ((const guchar *) password, (gsize) (strlen (password) + 1));
-
-  message = dbus_message_new_method_call (name,
-                                          "/org/gtk/Private/RemoteVolumeMonitor",
-                                          "org.gtk.Private.RemoteVolumeMonitor",
-                                          "StartOpReply");
-  dbus_message_append_args (message,
-                            DBUS_TYPE_STRING,
-                            &(data->drive->id),
-                            DBUS_TYPE_STRING,
-                            &(data->start_op_id),
-                            DBUS_TYPE_INT32,
-                            &result,
-                            DBUS_TYPE_STRING,
-                            &user_name,
-                            DBUS_TYPE_STRING,
-                            &domain,
-                            DBUS_TYPE_STRING,
-                            &encoded_password,
-                            DBUS_TYPE_INT32,
-                            &password_save,
-                            DBUS_TYPE_INT32,
-                            &choice,
-                            DBUS_TYPE_BOOLEAN,
-                            &anonymous,
-                            DBUS_TYPE_INVALID);
-
-  _g_dbus_connection_call_async (connection,
-                                 message,
-                                 -1,
-                                 (GAsyncDBusCallback) start_op_reply_cb,
-                                 data);
-
-  g_free (encoded_password);
-  dbus_message_unref (message);
-  dbus_connection_unref (connection);
-}
-
-void
-g_proxy_drive_handle_start_op_ask_password (GProxyDrive        *drive,
-                                            DBusMessageIter    *iter)
-{
-  const char *start_op_id;
-  const char *message;
-  const char *default_user;
-  const char *default_domain;
-  dbus_int32_t flags;
-  DBusStartOp *data;
-
-  dbus_message_iter_get_basic (iter, &start_op_id);
-  dbus_message_iter_next (iter);
-
-  dbus_message_iter_get_basic (iter, &message);
-  dbus_message_iter_next (iter);
-
-  dbus_message_iter_get_basic (iter, &default_user);
-  dbus_message_iter_next (iter);
-
-  dbus_message_iter_get_basic (iter, &default_domain);
-  dbus_message_iter_next (iter);
-
-  dbus_message_iter_get_basic (iter, &flags);
-  dbus_message_iter_next (iter);
-
-  data = g_hash_table_lookup (drive->hash_start_op_id_to_data, start_op_id);
-
-  /* since eavesdropping is enabled on the session bus we get this signal even if it
-   * is for another application; so silently ignore it if it's not for us
-   */
-  if (data == NULL)
-    goto out;
-
-  if (data->reply_handler_id == 0)
-    {
-      data->reply_handler_id = g_signal_connect (data->start_operation,
-                                                 "reply",
-                                                 G_CALLBACK (start_operation_reply),
-                                                 data);
-    }
-
-  g_signal_emit_by_name (data->start_operation,
-                         "ask-password",
-                         message,
-                         default_user,
-                         default_domain,
-                         flags);
-
- out:
-  ;
-}
-
-void
-g_proxy_drive_handle_start_op_ask_question (GProxyDrive        *drive,
-                                            DBusMessageIter    *iter)
-{
-  const char *start_op_id;
-  const char *message;
-  GPtrArray *choices;
-  DBusMessageIter iter_array;
-  DBusStartOp *data;
-
-  choices = NULL;
-
-  dbus_message_iter_get_basic (iter, &start_op_id);
-  dbus_message_iter_next (iter);
-
-  dbus_message_iter_get_basic (iter, &message);
-  dbus_message_iter_next (iter);
-
-  choices = g_ptr_array_new ();
-  dbus_message_iter_recurse (iter, &iter_array);
-  while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
-    {
-      const char *choice;
-      dbus_message_iter_get_basic (&iter_array, &choice);
-      dbus_message_iter_next (&iter_array);
-
-      g_ptr_array_add (choices, g_strdup (choice));
-    }
-  g_ptr_array_add (choices, NULL);
-
-  data = g_hash_table_lookup (drive->hash_start_op_id_to_data, start_op_id);
-
-  /* since eavesdropping is enabled on the session bus we get this signal even if it
-   * is for another application; so silently ignore it if it's not for us
-   */
-  if (data == NULL)
-    goto out;
-
-  if (data->reply_handler_id == 0)
-    {
-      data->reply_handler_id = g_signal_connect (data->start_operation,
-                                                 "reply",
-                                                 G_CALLBACK (start_operation_reply),
-                                                 data);
-    }
-
-  g_signal_emit_by_name (data->start_operation,
-                         "ask-question",
-                         message,
-                         choices->pdata);
-
- out:
-  g_ptr_array_free (choices, TRUE);
-}
-
-void
-g_proxy_drive_handle_start_op_aborted (GProxyDrive        *drive,
-                                       DBusMessageIter    *iter)
-{
-  const char *start_op_id;
-  DBusStartOp *data;
-
-  dbus_message_iter_get_basic (iter, &start_op_id);
-  dbus_message_iter_next (iter);
-
-  data = g_hash_table_lookup (drive->hash_start_op_id_to_data, start_op_id);
-
-  /* since eavesdropping is enabled on the session bus we get this signal even if it
-   * is for another application; so silently ignore it if it's not for us
-   */
-  if (data == NULL)
-    goto out;
-
-  g_signal_emit_by_name (data->start_operation, "aborted");
-
- out:
-  ;
-}
-
 static gboolean
 g_proxy_drive_start_finish (GDrive        *drive,
                             GAsyncResult  *result,
@@ -1335,11 +1143,14 @@ g_proxy_drive_drive_iface_init (GDriveIface *iface)
   iface->can_poll_for_media = g_proxy_drive_can_poll_for_media;
   iface->eject = g_proxy_drive_eject;
   iface->eject_finish = g_proxy_drive_eject_finish;
+  iface->eject_with_operation = g_proxy_drive_eject_with_operation;
+  iface->eject_with_operation_finish = g_proxy_drive_eject_with_operation_finish;
   iface->poll_for_media = g_proxy_drive_poll_for_media;
   iface->poll_for_media_finish = g_proxy_drive_poll_for_media_finish;
   iface->get_identifier = g_proxy_drive_get_identifier;
   iface->enumerate_identifiers = g_proxy_drive_enumerate_identifiers;
   iface->can_start = g_proxy_drive_can_start;
+  iface->can_start_degraded = g_proxy_drive_can_start_degraded;
   iface->start = g_proxy_drive_start;
   iface->start_finish = g_proxy_drive_start_finish;
   iface->can_stop = g_proxy_drive_can_stop;
diff --git a/monitor/proxy/gproxymount.c b/monitor/proxy/gproxymount.c
index 5b2a640..6e61ae7 100644
--- a/monitor/proxy/gproxymount.c
+++ b/monitor/proxy/gproxymount.c
@@ -36,6 +36,7 @@
 #include "gproxyvolumemonitor.h"
 #include "gproxymount.h"
 #include "gproxyvolume.h"
+#include "gproxymountoperation.h"
 
 /* Protects all fields of GProxyMount that can change */
 G_LOCK_DEFINE_STATIC(proxy_mount);
@@ -372,11 +373,12 @@ eject_wrapper_callback (GObject *source_object,
 }
 
 static void
-g_proxy_mount_eject (GMount              *mount,
-                     GMountUnmountFlags   flags,
-                     GCancellable        *cancellable,
-                     GAsyncReadyCallback  callback,
-                     gpointer             user_data)
+g_proxy_mount_eject_with_operation (GMount              *mount,
+                                    GMountUnmountFlags   flags,
+                                    GMountOperation     *mount_operation,
+                                    GCancellable        *cancellable,
+                                    GAsyncReadyCallback  callback,
+                                    gpointer             user_data)
 {
   GDrive *drive;
 
@@ -389,15 +391,15 @@ g_proxy_mount_eject (GMount              *mount,
       data->object = g_object_ref (mount);
       data->callback = callback;
       data->user_data = user_data;
-      g_drive_eject (drive, flags, cancellable, eject_wrapper_callback, data);
+      g_drive_eject_with_operation (drive, flags, mount_operation, cancellable, eject_wrapper_callback, data);
       g_object_unref (drive);
     }
 }
 
 static gboolean
-g_proxy_mount_eject_finish (GMount        *mount,
-                            GAsyncResult  *result,
-                            GError       **error)
+g_proxy_mount_eject_with_operation_finish (GMount        *mount,
+                                           GAsyncResult  *result,
+                                           GError       **error)
 {
   GDrive *drive;
   gboolean res;
@@ -408,12 +410,30 @@ g_proxy_mount_eject_finish (GMount        *mount,
 
   if (drive != NULL)
     {
-      res = g_drive_eject_finish (drive, result, error);
+      res = g_drive_eject_with_operation_finish (drive, result, error);
       g_object_unref (drive);
     }
   return res;
 }
 
+static void
+g_proxy_mount_eject (GMount              *mount,
+                     GMountUnmountFlags   flags,
+                     GCancellable        *cancellable,
+                     GAsyncReadyCallback  callback,
+                     gpointer             user_data)
+{
+  g_proxy_mount_eject_with_operation (mount, flags, NULL, cancellable, callback, user_data);
+}
+
+static gboolean
+g_proxy_mount_eject_finish (GMount        *mount,
+                            GAsyncResult  *result,
+                            GError       **error)
+{
+  return g_proxy_mount_eject_with_operation_finish (mount, result, error);
+}
+
 typedef struct {
   GProxyMount *mount;
   GAsyncReadyCallback callback;
@@ -422,6 +442,8 @@ typedef struct {
   gchar *cancellation_id;
   GCancellable *cancellable;
   gulong cancelled_handler_id;
+
+  const gchar *mount_op_id;
 } DBusOp;
 
 static void
@@ -504,6 +526,7 @@ unmount_cb (DBusMessage *reply,
       g_object_unref (simple);
     }
 
+  g_proxy_mount_operation_destroy (data->mount_op_id);
   g_object_unref (data->mount);
   g_free (data->cancellation_id);
   if (data->cancellable != NULL)
@@ -512,11 +535,12 @@ unmount_cb (DBusMessage *reply,
 }
 
 static void
-g_proxy_mount_unmount (GMount              *mount,
-                       GMountUnmountFlags   flags,
-                       GCancellable        *cancellable,
-                       GAsyncReadyCallback  callback,
-                       gpointer             user_data)
+g_proxy_mount_unmount_with_operation (GMount              *mount,
+                                      GMountUnmountFlags   flags,
+                                      GMountOperation     *mount_operation,
+                                      GCancellable        *cancellable,
+                                      GAsyncReadyCallback  callback,
+                                      gpointer             user_data)
 {
   GProxyMount *proxy_mount = G_PROXY_MOUNT (mount);
   DBusConnection *connection;
@@ -546,6 +570,7 @@ g_proxy_mount_unmount (GMount              *mount,
   data->mount = g_object_ref (mount);
   data->callback = callback;
   data->user_data = user_data;
+  data->mount_op_id = g_proxy_mount_operation_wrap (mount_operation, proxy_mount->volume_monitor);
 
   if (cancellable != NULL)
     {
@@ -575,12 +600,14 @@ g_proxy_mount_unmount (GMount              *mount,
                             &(data->cancellation_id),
                             DBUS_TYPE_UINT32,
                             &_flags,
+                            DBUS_TYPE_STRING,
+                            &(data->mount_op_id),
                             DBUS_TYPE_INVALID);
   G_UNLOCK (proxy_mount);
 
   _g_dbus_connection_call_async (connection,
                                  message,
-                                 -1,
+                                 G_PROXY_VOLUME_MONITOR_DBUS_TIMEOUT, /* 30 minute timeout */
                                  (GAsyncDBusCallback) unmount_cb,
                                  data);
 
@@ -591,9 +618,9 @@ g_proxy_mount_unmount (GMount              *mount,
 }
 
 static gboolean
-g_proxy_mount_unmount_finish (GMount        *mount,
-                              GAsyncResult  *result,
-                              GError       **error)
+g_proxy_mount_unmount_with_operation_finish (GMount        *mount,
+                                             GAsyncResult  *result,
+                                             GError       **error)
 {
   if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
     return FALSE;
@@ -601,6 +628,24 @@ g_proxy_mount_unmount_finish (GMount        *mount,
 }
 
 static void
+g_proxy_mount_unmount (GMount              *mount,
+                       GMountUnmountFlags   flags,
+                       GCancellable        *cancellable,
+                       GAsyncReadyCallback  callback,
+                       gpointer             user_data)
+{
+  g_proxy_mount_unmount_with_operation (mount, flags, NULL, cancellable, callback, user_data);
+}
+
+static gboolean
+g_proxy_mount_unmount_finish (GMount        *mount,
+                              GAsyncResult  *result,
+                              GError       **error)
+{
+  return g_proxy_mount_unmount_with_operation_finish (mount, result, error);
+}
+
+static void
 g_proxy_mount_guess_content_type (GMount              *mount,
                                   gboolean             force_rescan,
                                   GCancellable        *cancellable,
@@ -651,8 +696,12 @@ g_proxy_mount_mount_iface_init (GMountIface *iface)
   iface->can_eject = g_proxy_mount_can_eject;
   iface->unmount = g_proxy_mount_unmount;
   iface->unmount_finish = g_proxy_mount_unmount_finish;
+  iface->unmount_with_operation = g_proxy_mount_unmount_with_operation;
+  iface->unmount_with_operation_finish = g_proxy_mount_unmount_with_operation_finish;
   iface->eject = g_proxy_mount_eject;
   iface->eject_finish = g_proxy_mount_eject_finish;
+  iface->eject_with_operation = g_proxy_mount_eject_with_operation;
+  iface->eject_with_operation_finish = g_proxy_mount_eject_with_operation_finish;
   iface->guess_content_type = g_proxy_mount_guess_content_type;
   iface->guess_content_type_finish = g_proxy_mount_guess_content_type_finish;
   iface->guess_content_type_sync = g_proxy_mount_guess_content_type_sync;
diff --git a/monitor/proxy/gproxymountoperation.c b/monitor/proxy/gproxymountoperation.c
new file mode 100644
index 0000000..6dc286a
--- /dev/null
+++ b/monitor/proxy/gproxymountoperation.c
@@ -0,0 +1,438 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* gvfs - extensions for gio
+ *
+ * Copyright (C) 2006-2009 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#include <config.h>
+
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <gdbusutils.h>
+
+#include "gproxymountoperation.h"
+
+/* for protecting the id_to_op and id_count */
+G_LOCK_DEFINE_STATIC(proxy_op);
+
+/* map from id to GMountOperation */
+static GHashTable *id_to_op = NULL;
+
+static guint id_count = 1;
+
+typedef struct
+{
+  gchar *id;
+  GMountOperation *op;
+  GProxyVolumeMonitor *monitor;
+  gulong reply_handler_id;
+} ProxyMountOpData;
+
+static void
+proxy_mount_op_data_free (ProxyMountOpData *data)
+{
+  g_free (data->id);
+  if (data->reply_handler_id > 0)
+    g_signal_handler_disconnect (data->op, data->reply_handler_id);
+  g_object_unref (data->op);
+  g_object_unref (data->monitor);
+  g_free (data);
+}
+
+/* must be called with lock held */
+static ProxyMountOpData *
+proxy_mount_op_data_new (GMountOperation *op,
+                         GProxyVolumeMonitor *monitor)
+{
+  ProxyMountOpData *data;
+
+  data = g_new0 (ProxyMountOpData, 1);
+  data->id = g_strdup_printf ("%d:%d", getpid (), id_count++);
+  data->op = g_object_ref (op);
+  data->monitor = g_object_ref (monitor);
+  return data;
+}
+
+/* must be called with lock held */
+static void
+ensure_hash (void)
+{
+  if (id_to_op == NULL)
+    id_to_op = g_hash_table_new_full (g_str_hash,
+                                      g_str_equal,
+                                      NULL,
+                                      (GDestroyNotify) proxy_mount_op_data_free);
+}
+
+const gchar *
+g_proxy_mount_operation_wrap (GMountOperation *op,
+                              GProxyVolumeMonitor *monitor)
+{
+  ProxyMountOpData *data;
+
+  if (op == NULL)
+    return "";
+
+  G_LOCK (proxy_op);
+
+  ensure_hash ();
+
+  data = proxy_mount_op_data_new (op, monitor);
+  g_hash_table_insert (id_to_op,
+                       data->id,
+                       data);
+
+  G_UNLOCK (proxy_op);
+
+  return data->id;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+mount_op_reply_cb (DBusMessage *reply,
+                   GError      *error,
+                   gpointer     user_data)
+{
+  if (error != NULL)
+    {
+      g_warning ("Error from MountOpReply(): %s", error->message);
+    }
+}
+
+static void
+mount_operation_reply (GMountOperation        *mount_operation,
+                       GMountOperationResult  result,
+                       gpointer               user_data)
+{
+  ProxyMountOpData *data = user_data;
+  DBusConnection *connection;
+  const gchar *name;
+  DBusMessage *message;
+  const gchar *user_name;
+  const gchar *domain;
+  const gchar *password;
+  gchar *encoded_password;
+  dbus_uint32_t password_save;
+  dbus_uint32_t choice;
+  dbus_bool_t anonymous;
+
+  connection = g_proxy_volume_monitor_get_dbus_connection (data->monitor);
+  name = g_proxy_volume_monitor_get_dbus_name (data->monitor);
+
+  user_name     = g_mount_operation_get_username (mount_operation);
+  domain        = g_mount_operation_get_domain (mount_operation);
+  password      = g_mount_operation_get_password (mount_operation);
+  password_save = g_mount_operation_get_password_save (mount_operation);
+  choice        = g_mount_operation_get_choice (mount_operation);
+  anonymous     = g_mount_operation_get_anonymous (mount_operation);
+
+  if (user_name == NULL)
+    user_name = "";
+  if (domain == NULL)
+    domain = "";
+  if (password == NULL)
+    password = "";
+
+  /* NOTE: this is not to add "security", it's merely to prevent accidental exposure
+   *       of passwords when running dbus-monitor
+   */
+  encoded_password = g_base64_encode ((const guchar *) password, (gsize) (strlen (password) + 1));
+
+  message = dbus_message_new_method_call (name,
+                                          "/org/gtk/Private/RemoteVolumeMonitor",
+                                          "org.gtk.Private.RemoteVolumeMonitor",
+                                          "MountOpReply");
+  dbus_message_append_args (message,
+                            DBUS_TYPE_STRING,
+                            &(data->id),
+                            DBUS_TYPE_INT32,
+                            &result,
+                            DBUS_TYPE_STRING,
+                            &user_name,
+                            DBUS_TYPE_STRING,
+                            &domain,
+                            DBUS_TYPE_STRING,
+                            &encoded_password,
+                            DBUS_TYPE_INT32,
+                            &password_save,
+                            DBUS_TYPE_INT32,
+                            &choice,
+                            DBUS_TYPE_BOOLEAN,
+                            &anonymous,
+                            DBUS_TYPE_INVALID);
+
+  _g_dbus_connection_call_async (connection,
+                                 message,
+                                 -1,
+                                 (GAsyncDBusCallback) mount_op_reply_cb,
+                                 data);
+
+  g_free (encoded_password);
+  dbus_message_unref (message);
+  dbus_connection_unref (connection);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+void
+g_proxy_mount_operation_handle_ask_password (const gchar      *wrapped_id,
+                                             DBusMessageIter  *iter)
+{
+  ProxyMountOpData *data;
+  const gchar *message;
+  const gchar *default_user;
+  const gchar *default_domain;
+  dbus_int32_t flags;
+
+  g_return_if_fail (wrapped_id != NULL);
+  g_return_if_fail (iter != NULL);
+
+  G_LOCK (proxy_op);
+  data = g_hash_table_lookup (id_to_op, wrapped_id);
+  G_UNLOCK (proxy_op);
+
+  if (data == NULL)
+    {
+      g_warning ("%s: No GMountOperation for id `%s'",
+                 G_STRFUNC,
+                 wrapped_id);
+      goto out;
+    }
+
+  dbus_message_iter_get_basic (iter, &message);
+  dbus_message_iter_next (iter);
+
+  dbus_message_iter_get_basic (iter, &default_user);
+  dbus_message_iter_next (iter);
+
+  dbus_message_iter_get_basic (iter, &default_domain);
+  dbus_message_iter_next (iter);
+
+  dbus_message_iter_get_basic (iter, &flags);
+  dbus_message_iter_next (iter);
+
+  if (data->reply_handler_id == 0)
+    {
+      data->reply_handler_id = g_signal_connect (data->op,
+                                                 "reply",
+                                                 G_CALLBACK (mount_operation_reply),
+                                                 data);
+    }
+
+  g_signal_emit_by_name (data->op,
+                         "ask-password",
+                         message,
+                         default_user,
+                         default_domain,
+                         flags);
+
+ out:
+  ;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+void
+g_proxy_mount_operation_handle_ask_question (const gchar      *wrapped_id,
+                                             DBusMessageIter  *iter)
+{
+  ProxyMountOpData *data;
+  const gchar *message;
+  GPtrArray *choices;
+  DBusMessageIter iter_array;
+
+  g_return_if_fail (wrapped_id != NULL);
+  g_return_if_fail (iter != NULL);
+
+  choices = NULL;
+
+  G_LOCK (proxy_op);
+  data = g_hash_table_lookup (id_to_op, wrapped_id);
+  G_UNLOCK (proxy_op);
+
+  if (data == NULL)
+    {
+      g_warning ("%s: No GMountOperation for id `%s'",
+                 G_STRFUNC,
+                 wrapped_id);
+      goto out;
+    }
+
+  dbus_message_iter_get_basic (iter, &message);
+  dbus_message_iter_next (iter);
+
+  choices = g_ptr_array_new ();
+  dbus_message_iter_recurse (iter, &iter_array);
+  while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
+    {
+      const gchar *choice;
+      dbus_message_iter_get_basic (&iter_array, &choice);
+      dbus_message_iter_next (&iter_array);
+
+      g_ptr_array_add (choices, g_strdup (choice));
+    }
+  g_ptr_array_add (choices, NULL);
+
+  if (data->reply_handler_id == 0)
+    {
+      data->reply_handler_id = g_signal_connect (data->op,
+                                                 "reply",
+                                                 G_CALLBACK (mount_operation_reply),
+                                                 data);
+    }
+
+  g_signal_emit_by_name (data->op,
+                         "ask-question",
+                         message,
+                         choices->pdata);
+
+ out:
+  g_ptr_array_free (choices, TRUE);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+void
+g_proxy_mount_operation_handle_show_processes (const gchar      *wrapped_id,
+                                               DBusMessageIter  *iter)
+{
+  ProxyMountOpData *data;
+  const gchar *message;
+  GPtrArray *choices;
+  GArray *processes;
+  DBusMessageIter iter_array;
+
+  g_return_if_fail (wrapped_id != NULL);
+  g_return_if_fail (iter != NULL);
+
+  choices = NULL;
+
+  G_LOCK (proxy_op);
+  data = g_hash_table_lookup (id_to_op, wrapped_id);
+  G_UNLOCK (proxy_op);
+
+  if (data == NULL)
+    {
+      g_warning ("%s: No GMountOperation for id `%s'",
+                 G_STRFUNC,
+                 wrapped_id);
+      goto out;
+    }
+
+  dbus_message_iter_get_basic (iter, &message);
+  dbus_message_iter_next (iter);
+
+  processes = g_array_new (FALSE, FALSE, sizeof (GPid));
+  dbus_message_iter_recurse (iter, &iter_array);
+  while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
+    {
+      GPid pid;
+
+      dbus_message_iter_get_basic (&iter_array, &pid);
+      dbus_message_iter_next (&iter_array);
+      g_array_append_val (processes, pid);
+    }
+
+  dbus_message_iter_next (iter);
+
+  choices = g_ptr_array_new ();
+  dbus_message_iter_recurse (iter, &iter_array);
+  while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
+    {
+      const gchar *choice;
+      dbus_message_iter_get_basic (&iter_array, &choice);
+      dbus_message_iter_next (&iter_array);
+
+      g_ptr_array_add (choices, g_strdup (choice));
+    }
+  g_ptr_array_add (choices, NULL);
+
+  if (data->reply_handler_id == 0)
+    {
+      data->reply_handler_id = g_signal_connect (data->op,
+                                                 "reply",
+                                                 G_CALLBACK (mount_operation_reply),
+                                                 data);
+    }
+
+  g_signal_emit_by_name (data->op,
+                         "show-processes",
+                         message,
+                         processes,
+                         choices->pdata);
+
+ out:
+  g_ptr_array_free (choices, TRUE);
+  g_array_unref (processes);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+void
+g_proxy_mount_operation_handle_aborted (const gchar      *wrapped_id,
+                                        DBusMessageIter  *iter)
+{
+  ProxyMountOpData *data;
+
+  g_return_if_fail (wrapped_id != NULL);
+  g_return_if_fail (iter != NULL);
+
+  G_LOCK (proxy_op);
+  data = g_hash_table_lookup (id_to_op, wrapped_id);
+  G_UNLOCK (proxy_op);
+
+  if (data == NULL)
+    {
+      g_warning ("%s: No GMountOperation for id `%s'",
+                 G_STRFUNC,
+                 wrapped_id);
+      goto out;
+    }
+
+  g_signal_emit_by_name (data->op, "aborted");
+
+ out:
+  ;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+void
+g_proxy_mount_operation_destroy (const gchar *wrapped_id)
+{
+  g_return_if_fail (wrapped_id != NULL);
+
+  if (strlen (wrapped_id) == 0)
+    return;
+
+  G_LOCK (proxy_op);
+  if (!g_hash_table_remove (id_to_op, wrapped_id))
+    {
+      g_warning ("%s: No GMountOperation for id `%s'",
+                 G_STRFUNC,
+                 wrapped_id);
+    }
+  G_UNLOCK (proxy_op);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
diff --git a/monitor/proxy/gproxymountoperation.h b/monitor/proxy/gproxymountoperation.h
new file mode 100644
index 0000000..a57b50e
--- /dev/null
+++ b/monitor/proxy/gproxymountoperation.h
@@ -0,0 +1,54 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* gvfs - extensions for gio
+ *
+ * Copyright (C) 2006-2009 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#ifndef __G_PROXY_MOUNT_OPERATION_H__
+#define __G_PROXY_MOUNT_OPERATION_H__
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include "gproxyvolumemonitor.h"
+
+G_BEGIN_DECLS
+
+const gchar *g_proxy_mount_operation_wrap                (GMountOperation     *op,
+                                                          GProxyVolumeMonitor *monitor);
+
+void  g_proxy_mount_operation_handle_ask_password   (const gchar      *wrapped_id,
+                                                     DBusMessageIter  *iter);
+
+void  g_proxy_mount_operation_handle_ask_question   (const gchar      *wrapped_id,
+                                                     DBusMessageIter  *iter);
+
+void  g_proxy_mount_operation_handle_show_processes (const gchar      *wrapped_id,
+                                                     DBusMessageIter  *iter);
+
+void  g_proxy_mount_operation_handle_aborted        (const gchar      *wrapped_id,
+                                                     DBusMessageIter  *iter);
+
+void  g_proxy_mount_operation_destroy               (const gchar      *wrapped_id);
+
+
+G_END_DECLS
+
+#endif /* __G_PROXY_MOUNT_OPERATION_H__ */
diff --git a/monitor/proxy/gproxyshadowmount.c b/monitor/proxy/gproxyshadowmount.c
index e3cb42e..d9d34de 100644
--- a/monitor/proxy/gproxyshadowmount.c
+++ b/monitor/proxy/gproxyshadowmount.c
@@ -329,53 +329,78 @@ setup_async_wrapper (GMount *mount,
                      
 
 static void
-g_proxy_shadow_mount_eject (GMount              *mount,
-                            GMountUnmountFlags   flags,
-                            GCancellable        *cancellable,
-                            GAsyncReadyCallback  callback,
-                            gpointer             user_data)
+g_proxy_shadow_mount_eject_with_operation (GMount              *mount,
+                                           GMountUnmountFlags   flags,
+                                           GMountOperation     *mount_operation,
+                                           GCancellable        *cancellable,
+                                           GAsyncReadyCallback  callback,
+                                           gpointer             user_data)
 {
   GProxyShadowMount *proxy_shadow_mount = G_PROXY_SHADOW_MOUNT (mount);
   AsyncWrapperOp *data;
 
   data = setup_async_wrapper (mount, callback, user_data);
   G_LOCK (proxy_shadow_mount);
-  g_volume_eject (G_VOLUME (proxy_shadow_mount->volume),
-                  flags, cancellable,
-                  async_wrapper_callback, data);
+  g_volume_eject_with_operation (G_VOLUME (proxy_shadow_mount->volume),
+                                 flags, mount_operation, cancellable,
+                                 async_wrapper_callback, data);
   G_UNLOCK (proxy_shadow_mount);
 }
 
 static gboolean
-g_proxy_shadow_mount_eject_finish (GMount        *mount,
-                                   GAsyncResult  *result,
-                                   GError       **error)
+g_proxy_shadow_mount_eject_with_operation_finish (GMount        *mount,
+                                                  GAsyncResult  *result,
+                                                  GError       **error)
 {
   GProxyShadowMount *proxy_shadow_mount = G_PROXY_SHADOW_MOUNT (mount);
   gboolean res;
 
   G_LOCK (proxy_shadow_mount);
-  res = g_volume_eject_finish (G_VOLUME (proxy_shadow_mount->volume), result, error);
+  res = g_volume_eject_with_operation_finish (G_VOLUME (proxy_shadow_mount->volume), result, error);
   G_UNLOCK (proxy_shadow_mount);
 
   return res;
 }
 
 static void
+g_proxy_shadow_mount_unmount_with_operation (GMount              *mount,
+                                             GMountUnmountFlags   flags,
+                                             GMountOperation     *mount_operation,
+                                             GCancellable        *cancellable,
+                                             GAsyncReadyCallback  callback,
+                                             gpointer             user_data)
+{
+  GProxyShadowMount *proxy_shadow_mount = G_PROXY_SHADOW_MOUNT (mount);
+  AsyncWrapperOp *data;
+
+  data = setup_async_wrapper (mount, callback, user_data);
+  g_mount_unmount_with_operation (proxy_shadow_mount->real_mount,
+                                  flags,
+                                  mount_operation,
+                                  cancellable,
+                                  async_wrapper_callback, data);
+}
+
+static gboolean
+g_proxy_shadow_mount_unmount_with_operation_finish (GMount        *mount,
+                                                    GAsyncResult  *result,
+                                                    GError       **error)
+{
+  GProxyShadowMount *proxy_shadow_mount = G_PROXY_SHADOW_MOUNT (mount);
+
+  return g_mount_unmount_with_operation_finish (proxy_shadow_mount->real_mount,
+                                                result,
+                                                error);
+}
+
+static void
 g_proxy_shadow_mount_unmount (GMount              *mount,
                               GMountUnmountFlags   flags,
                               GCancellable        *cancellable,
                               GAsyncReadyCallback  callback,
                               gpointer             user_data)
 {
-  GProxyShadowMount *proxy_shadow_mount = G_PROXY_SHADOW_MOUNT (mount);
-  AsyncWrapperOp *data;
-
-  data = setup_async_wrapper (mount, callback, user_data);
-  g_mount_unmount (proxy_shadow_mount->real_mount,
-                   flags,
-                   cancellable,
-                   async_wrapper_callback, data);
+  g_proxy_shadow_mount_unmount_with_operation (mount, flags, NULL, cancellable, callback, user_data);
 }
 
 static gboolean
@@ -383,11 +408,25 @@ g_proxy_shadow_mount_unmount_finish (GMount        *mount,
                                      GAsyncResult  *result,
                                      GError       **error)
 {
-  GProxyShadowMount *proxy_shadow_mount = G_PROXY_SHADOW_MOUNT (mount);
+  return g_proxy_shadow_mount_unmount_with_operation_finish (mount, result, error);
+}
 
-  return g_mount_unmount_finish (proxy_shadow_mount->real_mount,
-                                 result,
-                                 error);
+static void
+g_proxy_shadow_mount_eject (GMount              *mount,
+                            GMountUnmountFlags   flags,
+                            GCancellable        *cancellable,
+                            GAsyncReadyCallback  callback,
+                            gpointer             user_data)
+{
+  g_proxy_shadow_mount_eject_with_operation (mount, flags, NULL, cancellable, callback, user_data);
+}
+
+static gboolean
+g_proxy_shadow_mount_eject_finish (GMount        *mount,
+                                   GAsyncResult  *result,
+                                   GError       **error)
+{
+  return g_proxy_shadow_mount_eject_with_operation_finish (mount, result, error);
 }
 
 static void
@@ -457,8 +496,12 @@ g_proxy_shadow_mount_mount_iface_init (GMountIface *iface)
   iface->can_eject = g_proxy_shadow_mount_can_eject;
   iface->unmount = g_proxy_shadow_mount_unmount;
   iface->unmount_finish = g_proxy_shadow_mount_unmount_finish;
+  iface->unmount_with_operation = g_proxy_shadow_mount_unmount_with_operation;
+  iface->unmount_with_operation_finish = g_proxy_shadow_mount_unmount_with_operation_finish;
   iface->eject = g_proxy_shadow_mount_eject;
   iface->eject_finish = g_proxy_shadow_mount_eject_finish;
+  iface->eject_with_operation = g_proxy_shadow_mount_eject_with_operation;
+  iface->eject_with_operation_finish = g_proxy_shadow_mount_eject_with_operation_finish;
   iface->guess_content_type = g_proxy_shadow_mount_guess_content_type;
   iface->guess_content_type_finish = g_proxy_shadow_mount_guess_content_type_finish;
   iface->guess_content_type_sync = g_proxy_shadow_mount_guess_content_type_sync;
diff --git a/monitor/proxy/gproxyvolume.c b/monitor/proxy/gproxyvolume.c
index 30c717f..0115901 100644
--- a/monitor/proxy/gproxyvolume.c
+++ b/monitor/proxy/gproxyvolume.c
@@ -37,6 +37,8 @@
 #include "gproxyvolume.h"
 #include "gproxymount.h"
 
+#include "gproxymountoperation.h"
+
 static void signal_emit_in_idle (gpointer object, const char *signal_name, gpointer other_object);
 
 /* Protects all fields of GProxyVolume that can change */
@@ -63,8 +65,6 @@ struct _GProxyVolume {
   gboolean should_automount;
 
   GProxyShadowMount *shadow_mount;
-
-  GHashTable *hash_mount_op_id_to_data;
 };
 
 static void g_proxy_volume_volume_iface_init (GVolumeIface *iface);
@@ -127,8 +127,6 @@ g_proxy_volume_finalize (GObject *object)
       g_object_unref (volume->volume_monitor);
     }
 
-  g_hash_table_unref (volume->hash_mount_op_id_to_data);
-
   if (G_OBJECT_CLASS (g_proxy_volume_parent_class)->finalize)
     (*G_OBJECT_CLASS (g_proxy_volume_parent_class)->finalize) (object);
 }
@@ -185,7 +183,6 @@ g_proxy_volume_class_finalize (GProxyVolumeClass *klass)
 static void
 g_proxy_volume_init (GProxyVolume *proxy_volume)
 {
-  proxy_volume->hash_mount_op_id_to_data = g_hash_table_new (g_str_hash, g_str_equal);
 }
 
 GProxyVolume *
@@ -598,11 +595,12 @@ eject_wrapper_callback (GObject *source_object,
 }
 
 static void
-g_proxy_volume_eject (GVolume              *volume,
-                    GMountUnmountFlags   flags,
-                    GCancellable        *cancellable,
-                    GAsyncReadyCallback  callback,
-                    gpointer             user_data)
+g_proxy_volume_eject_with_operation (GVolume             *volume,
+                                     GMountUnmountFlags   flags,
+                                     GMountOperation     *mount_operation,
+                                     GCancellable        *cancellable,
+                                     GAsyncReadyCallback  callback,
+                                     gpointer             user_data)
 {
   GProxyVolume *proxy_volume = G_PROXY_VOLUME (volume);
   GProxyDrive *drive;
@@ -623,15 +621,15 @@ g_proxy_volume_eject (GVolume              *volume,
       data->object = g_object_ref (volume);
       data->callback = callback;
       data->user_data = user_data;
-      g_drive_eject (G_DRIVE (drive), flags, cancellable, eject_wrapper_callback, data);
+      g_drive_eject_with_operation (G_DRIVE (drive), flags, mount_operation, cancellable, eject_wrapper_callback, data);
       g_object_unref (drive);
     }
 }
 
 static gboolean
-g_proxy_volume_eject_finish (GVolume        *volume,
-                          GAsyncResult  *result,
-                          GError       **error)
+g_proxy_volume_eject_with_operation_finish (GVolume        *volume,
+                                            GAsyncResult  *result,
+                                            GError       **error)
 {
   GProxyVolume *proxy_volume = G_PROXY_VOLUME (volume);
   GProxyDrive *drive;
@@ -647,13 +645,35 @@ g_proxy_volume_eject_finish (GVolume        *volume,
 
   if (drive != NULL)
     {
-      res = g_drive_eject_finish (G_DRIVE (drive), result, error);
+      res = g_drive_eject_with_operation_finish (G_DRIVE (drive), result, error);
       g_object_unref (drive);
     }
 
   return res;
 }
 
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+g_proxy_volume_eject (GVolume              *volume,
+                      GMountUnmountFlags   flags,
+                      GCancellable        *cancellable,
+                      GAsyncReadyCallback  callback,
+                      gpointer             user_data)
+{
+  g_proxy_volume_eject_with_operation (volume, flags, NULL, cancellable, callback, user_data);
+}
+
+static gboolean
+g_proxy_volume_eject_finish (GVolume        *volume,
+                             GAsyncResult  *result,
+                             GError       **error)
+{
+  return g_proxy_volume_eject_with_operation_finish (volume, result, error);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
 static char *
 g_proxy_volume_get_identifier (GVolume              *volume,
                                const char          *kind)
@@ -705,9 +725,7 @@ typedef struct {
   GCancellable *cancellable;
   gulong cancelled_handler_id;
 
-  gchar *mount_op_id;
-  GMountOperation *mount_operation;
-  gulong reply_handler_id;
+  const gchar *mount_op_id;
 } DBusOp;
 
 static void
@@ -737,16 +755,9 @@ mount_cb (DBusMessage *reply,
     }
 
   /* free DBusOp */
-  if (strlen (data->mount_op_id) > 0)
-    g_hash_table_remove (data->volume->hash_mount_op_id_to_data, data->mount_op_id);
+  g_proxy_mount_operation_destroy (data->mount_op_id);
   g_object_unref (data->volume);
 
-  g_free (data->mount_op_id);
-  if (data->reply_handler_id > 0)
-    g_signal_handler_disconnect (data->mount_operation, data->reply_handler_id);
-  if (data->mount_operation != NULL)
-    g_object_unref (data->mount_operation);
-
   g_free (data->cancellation_id);
   if (data->cancellable != NULL)
     g_object_unref (data->cancellable);
@@ -902,18 +913,7 @@ g_proxy_volume_mount (GVolume             *volume,
           data->cancellation_id = g_strdup ("");
         }
 
-      if (mount_operation != NULL)
-        {
-          data->mount_op_id = g_strdup_printf ("%p", mount_operation);
-          data->mount_operation = g_object_ref (mount_operation);
-          g_hash_table_insert (proxy_volume->hash_mount_op_id_to_data,
-                               data->mount_op_id,
-                               data);
-        }
-      else
-        {
-          data->mount_op_id = g_strdup ("");
-        }
+      data->mount_op_id = g_proxy_mount_operation_wrap (mount_operation, proxy_volume->volume_monitor);
 
       connection = g_proxy_volume_monitor_get_dbus_connection (proxy_volume->volume_monitor);
       name = g_proxy_volume_monitor_get_dbus_name (proxy_volume->volume_monitor);
@@ -936,7 +936,7 @@ g_proxy_volume_mount (GVolume             *volume,
 
       _g_dbus_connection_call_async (connection,
                                      message,
-                                     G_PROXY_VOLUME_MONITOR_DBUS_TIMEOUT,                /* 30 minute timeout */
+                                     G_PROXY_VOLUME_MONITOR_DBUS_TIMEOUT, /* 30 minute timeout */
                                      (GAsyncDBusCallback) mount_cb,
                                      data);
       dbus_message_unref (message);
@@ -947,225 +947,6 @@ g_proxy_volume_mount (GVolume             *volume,
   ;
 }
 
-
-static void
-mount_op_reply_cb (DBusMessage *reply,
-                   GError      *error,
-                   DBusOp      *data)
-{
-  if (error != NULL)
-    {
-      g_warning ("Error from MountOpReply(): %s", error->message);
-    }
-}
-
-static void
-mount_operation_reply (GMountOperation        *mount_operation,
-                       GMountOperationResult  result,
-                       gpointer               user_data)
-{
-  DBusOp *data = user_data;
-  DBusConnection *connection;
-  const char *name;
-  DBusMessage *message;
-  const char *user_name;
-  const char *domain;
-  const char *password;
-  char *encoded_password;
-  dbus_uint32_t password_save;
-  dbus_uint32_t choice;
-  dbus_bool_t anonymous;
-
-  connection = g_proxy_volume_monitor_get_dbus_connection (data->volume->volume_monitor);
-  name = g_proxy_volume_monitor_get_dbus_name (data->volume->volume_monitor);
-
-  user_name     = g_mount_operation_get_username (mount_operation);
-  domain        = g_mount_operation_get_domain (mount_operation);
-  password      = g_mount_operation_get_password (mount_operation);
-  password_save = g_mount_operation_get_password_save (mount_operation);
-  choice        = g_mount_operation_get_choice (mount_operation);
-  anonymous     = g_mount_operation_get_anonymous (mount_operation);
-
-  if (user_name == NULL)
-    user_name = "";
-  if (domain == NULL)
-    domain = "";
-  if (password == NULL)
-    password = "";
-
-  /* NOTE: this is not to add "security", it's merely to prevent accidental exposure
-   *       of passwords when running dbus-monitor
-   */
-  encoded_password = g_base64_encode ((const guchar *) password, (gsize) (strlen (password) + 1));
-
-  message = dbus_message_new_method_call (name,
-                                          "/org/gtk/Private/RemoteVolumeMonitor",
-                                          "org.gtk.Private.RemoteVolumeMonitor",
-                                          "MountOpReply");
-  dbus_message_append_args (message,
-                            DBUS_TYPE_STRING,
-                            &(data->volume->id),
-                            DBUS_TYPE_STRING,
-                            &(data->mount_op_id),
-                            DBUS_TYPE_INT32,
-                            &result,
-                            DBUS_TYPE_STRING,
-                            &user_name,
-                            DBUS_TYPE_STRING,
-                            &domain,
-                            DBUS_TYPE_STRING,
-                            &encoded_password,
-                            DBUS_TYPE_INT32,
-                            &password_save,
-                            DBUS_TYPE_INT32,
-                            &choice,
-                            DBUS_TYPE_BOOLEAN,
-                            &anonymous,
-                            DBUS_TYPE_INVALID);
-
-  _g_dbus_connection_call_async (connection,
-                                 message,
-                                 -1,
-                                 (GAsyncDBusCallback) mount_op_reply_cb,
-                                 data);
-
-  g_free (encoded_password);
-  dbus_message_unref (message);
-  dbus_connection_unref (connection);
-}
-
-void
-g_proxy_volume_handle_mount_op_ask_password (GProxyVolume        *volume,
-                                             DBusMessageIter     *iter)
-{
-  const char *mount_op_id;
-  const char *message;
-  const char *default_user;
-  const char *default_domain;
-  dbus_int32_t flags;
-  DBusOp *data;
-
-  dbus_message_iter_get_basic (iter, &mount_op_id);
-  dbus_message_iter_next (iter);
-
-  dbus_message_iter_get_basic (iter, &message);
-  dbus_message_iter_next (iter);
-
-  dbus_message_iter_get_basic (iter, &default_user);
-  dbus_message_iter_next (iter);
-
-  dbus_message_iter_get_basic (iter, &default_domain);
-  dbus_message_iter_next (iter);
-
-  dbus_message_iter_get_basic (iter, &flags);
-  dbus_message_iter_next (iter);
-
-  data = g_hash_table_lookup (volume->hash_mount_op_id_to_data, mount_op_id);
-
-  /* since eavesdropping is enabled on the session bus we get this signal even if it
-   * is for another application; so silently ignore it if it's not for us
-   */
-  if (data == NULL)
-    goto out;
-
-  if (data->reply_handler_id == 0)
-    {
-      data->reply_handler_id = g_signal_connect (data->mount_operation,
-                                                 "reply",
-                                                 G_CALLBACK (mount_operation_reply),
-                                                 data);
-    }
-
-  g_signal_emit_by_name (data->mount_operation,
-                         "ask-password",
-                         message,
-                         default_user,
-                         default_domain,
-                         flags);
-
- out:
-  ;
-}
-
-void
-g_proxy_volume_handle_mount_op_ask_question (GProxyVolume        *volume,
-                                             DBusMessageIter     *iter)
-{
-  const char *mount_op_id;
-  const char *message;
-  GPtrArray *choices;
-  DBusMessageIter iter_array;
-  DBusOp *data;
-
-  choices = NULL;
-
-  dbus_message_iter_get_basic (iter, &mount_op_id);
-  dbus_message_iter_next (iter);
-
-  dbus_message_iter_get_basic (iter, &message);
-  dbus_message_iter_next (iter);
-
-  choices = g_ptr_array_new ();
-  dbus_message_iter_recurse (iter, &iter_array);
-  while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
-    {
-      const char *choice;
-      dbus_message_iter_get_basic (&iter_array, &choice);
-      dbus_message_iter_next (&iter_array);
-
-      g_ptr_array_add (choices, g_strdup (choice));
-    }
-  g_ptr_array_add (choices, NULL);
-
-  data = g_hash_table_lookup (volume->hash_mount_op_id_to_data, mount_op_id);
-
-  /* since eavesdropping is enabled on the session bus we get this signal even if it
-   * is for another application; so silently ignore it if it's not for us
-   */
-  if (data == NULL)
-    goto out;
-
-  if (data->reply_handler_id == 0)
-    {
-      data->reply_handler_id = g_signal_connect (data->mount_operation,
-                                                 "reply",
-                                                 G_CALLBACK (mount_operation_reply),
-                                                 data);
-    }
-
-  g_signal_emit_by_name (data->mount_operation,
-                         "ask-question",
-                         message,
-                         choices->pdata);
-
- out:
-  g_ptr_array_free (choices, TRUE);
-}
-
-void
-g_proxy_volume_handle_mount_op_aborted (GProxyVolume        *volume,
-                                        DBusMessageIter     *iter)
-{
-  const char *mount_op_id;
-  DBusOp *data;
-
-  dbus_message_iter_get_basic (iter, &mount_op_id);
-  dbus_message_iter_next (iter);
-
-  data = g_hash_table_lookup (volume->hash_mount_op_id_to_data, mount_op_id);
-
-  /* since eavesdropping is enabled on the session bus we get this signal even if it
-   * is for another application; so silently ignore it if it's not for us
-   */
-  if (data == NULL)
-    goto out;
-
-  g_signal_emit_by_name (data->mount_operation, "aborted");
-
- out:
-  ;
-}
-
 static gboolean
 g_proxy_volume_mount_finish (GVolume        *volume,
                              GAsyncResult  *result,
@@ -1201,6 +982,8 @@ g_proxy_volume_volume_iface_init (GVolumeIface *iface)
   iface->mount_finish = g_proxy_volume_mount_finish;
   iface->eject = g_proxy_volume_eject;
   iface->eject_finish = g_proxy_volume_eject_finish;
+  iface->eject_with_operation = g_proxy_volume_eject_with_operation;
+  iface->eject_with_operation_finish = g_proxy_volume_eject_with_operation_finish;
   iface->get_identifier = g_proxy_volume_get_identifier;
   iface->enumerate_identifiers = g_proxy_volume_enumerate_identifiers;
   iface->get_activation_root = g_proxy_volume_get_activation_root;
diff --git a/monitor/proxy/gproxyvolume.h b/monitor/proxy/gproxyvolume.h
index ea2d98d..953b800 100644
--- a/monitor/proxy/gproxyvolume.h
+++ b/monitor/proxy/gproxyvolume.h
@@ -53,15 +53,6 @@ void          g_proxy_volume_register            (GIOModule           *module);
 
 GProxyShadowMount *g_proxy_volume_get_shadow_mount (GProxyVolume        *volume);
 
-void          g_proxy_volume_handle_mount_op_ask_password (GProxyVolume        *volume,
-                                                           DBusMessageIter     *iter);
-
-void          g_proxy_volume_handle_mount_op_ask_question (GProxyVolume        *volume,
-                                                           DBusMessageIter     *iter);
-
-void          g_proxy_volume_handle_mount_op_aborted      (GProxyVolume        *volume,
-                                                           DBusMessageIter     *iter);
-
 G_END_DECLS
 
 #endif /* __G_PROXY_VOLUME_H__ */
diff --git a/monitor/proxy/gproxyvolumemonitor.c b/monitor/proxy/gproxyvolumemonitor.c
index 3b5a25d..cacb896 100644
--- a/monitor/proxy/gproxyvolumemonitor.c
+++ b/monitor/proxy/gproxyvolumemonitor.c
@@ -41,6 +41,7 @@
 #include "gproxymount.h"
 #include "gproxyvolume.h"
 #include "gproxydrive.h"
+#include "gproxymountoperation.h"
 
 G_LOCK_DEFINE_STATIC(proxy_vm);
 
@@ -640,10 +641,7 @@ filter_function (DBusConnection *connection, DBusMessage *message, void *user_da
             dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "DriveConnected") ||
             dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "DriveDisconnected") ||
             dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "DriveEjectButton") ||
-            dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "DriveStopButton") ||
-            dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "StartOpAskPassword") ||
-            dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "StartOpAskQuestion") ||
-            dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "StartOpAborted"))
+            dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "DriveStopButton"))
     {
 
       dbus_message_iter_init (message, &iter);
@@ -706,32 +704,11 @@ filter_function (DBusConnection *connection, DBusMessage *message, void *user_da
               signal_emit_in_idle (monitor, "drive-stop-button", drive);
             }
         }
-      else if (strcmp (member, "StartOpAskPassword") == 0)
-        {
-          drive = g_hash_table_lookup (monitor->drives, id);
-          if (drive != NULL)
-            g_proxy_drive_handle_start_op_ask_password (drive, &iter);
-        }
-      else if (strcmp (member, "StartOpAskQuestion") == 0)
-        {
-          drive = g_hash_table_lookup (monitor->drives, id);
-          if (drive != NULL)
-            g_proxy_drive_handle_start_op_ask_question (drive, &iter);
-        }
-      else if (strcmp (member, "StartOpAborted") == 0)
-        {
-          drive = g_hash_table_lookup (monitor->drives, id);
-          if (drive != NULL)
-            g_proxy_drive_handle_start_op_aborted (drive, &iter);
-        }
 
     }
   else if (dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "VolumeChanged") ||
            dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "VolumeAdded") ||
-           dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "VolumeRemoved") ||
-           dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "MountOpAskPassword") ||
-           dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "MountOpAskQuestion") ||
-           dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "MountOpAborted"))
+           dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "VolumeRemoved"))
     {
       dbus_message_iter_init (message, &iter);
       dbus_message_iter_get_basic (&iter, &the_dbus_name);
@@ -786,24 +763,6 @@ filter_function (DBusConnection *connection, DBusMessage *message, void *user_da
               g_object_unref (volume);
             }
         }
-      else if (strcmp (member, "MountOpAskPassword") == 0)
-        {
-          volume = g_hash_table_lookup (monitor->volumes, id);
-          if (volume != NULL)
-            g_proxy_volume_handle_mount_op_ask_password (volume, &iter);
-        }
-      else if (strcmp (member, "MountOpAskQuestion") == 0)
-        {
-          volume = g_hash_table_lookup (monitor->volumes, id);
-          if (volume != NULL)
-            g_proxy_volume_handle_mount_op_ask_question (volume, &iter);
-        }
-      else if (strcmp (member, "MountOpAborted") == 0)
-        {
-          volume = g_hash_table_lookup (monitor->volumes, id);
-          if (volume != NULL)
-            g_proxy_volume_handle_mount_op_aborted (volume, &iter);
-        }
 
     }
   else if (dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "MountChanged") ||
@@ -864,6 +823,37 @@ filter_function (DBusConnection *connection, DBusMessage *message, void *user_da
             }
         }
     }
+  else if (dbus_message_is_method_call (message, "org.gtk.Private.RemoteVolumeMonitor", "MountOpAskPassword") ||
+           dbus_message_is_method_call (message, "org.gtk.Private.RemoteVolumeMonitor", "MountOpAskQuestion") ||
+           dbus_message_is_method_call (message, "org.gtk.Private.RemoteVolumeMonitor", "MountOpShowProcesses") ||
+           dbus_message_is_method_call (message, "org.gtk.Private.RemoteVolumeMonitor", "MountOpAborted"))
+    {
+      dbus_message_iter_init (message, &iter);
+      dbus_message_iter_get_basic (&iter, &the_dbus_name);
+      dbus_message_iter_next (&iter);
+      dbus_message_iter_get_basic (&iter, &id);
+      dbus_message_iter_next (&iter);
+
+      if (strcmp (the_dbus_name, klass->dbus_name) != 0)
+        goto not_for_us;
+
+      if (strcmp (member, "MountOpAskPassword") == 0)
+        {
+          g_proxy_mount_operation_handle_ask_password (id, &iter);
+        }
+      else if (strcmp (member, "MountOpAskQuestion") == 0)
+        {
+          g_proxy_mount_operation_handle_ask_question (id, &iter);
+        }
+      else if (strcmp (member, "MountOpShowProcesses") == 0)
+        {
+          g_proxy_mount_operation_handle_show_processes (id, &iter);
+        }
+      else if (strcmp (member, "MountOpAborted") == 0)
+        {
+          g_proxy_mount_operation_handle_aborted (id, &iter);
+        }
+    }
 
  not_for_us:
   G_UNLOCK (proxy_vm);
diff --git a/monitor/proxy/gvfsproxyvolumemonitordaemon.c b/monitor/proxy/gvfsproxyvolumemonitordaemon.c
index d066672..20de42d 100644
--- a/monitor/proxy/gvfsproxyvolumemonitordaemon.c
+++ b/monitor/proxy/gvfsproxyvolumemonitordaemon.c
@@ -35,7 +35,16 @@
 
 /* ---------------------------------------------------------------------------------------------------- */
 
+static GVolumeMonitor *monitor = NULL;
+static DBusConnection *connection = NULL;
+static GType the_volume_monitor_type;
+static const char *the_dbus_name = NULL;
+static GList *outstanding_ops = NULL;
+static GList *outstanding_mount_op_objects = NULL;
+static GHashTable *unique_names_being_watched = NULL;
+
 /* #define DEBUG_ENABLED */
+#define DEBUG_ENABLED
 
 #ifdef DEBUG_ENABLED
 static void
@@ -107,25 +116,218 @@ g_proxy_mount_operation_ask_question (GMountOperation *op,
 }
 
 static void
+g_proxy_mount_operation_show_processes (GMountOperation *op,
+                                        const gchar          *message,
+                                        GArray               *processes,
+                                        const gchar          *choices[])
+{
+  /* do nothing */
+}
+
+static void
 g_proxy_mount_operation_class_init (GProxyMountOperationClass *klass)
 {
   GMountOperationClass *mount_op_class;
 
   mount_op_class = G_MOUNT_OPERATION_CLASS (klass);
 
-  mount_op_class->ask_password = g_proxy_mount_operation_ask_password;
-  mount_op_class->ask_question = g_proxy_mount_operation_ask_question;
+  mount_op_class->ask_password   = g_proxy_mount_operation_ask_password;
+  mount_op_class->ask_question   = g_proxy_mount_operation_ask_question;
+  mount_op_class->show_processes = g_proxy_mount_operation_show_processes;
 }
 
+static void
+ask_password_cb (GMountOperation  *mount_operation,
+                 const gchar      *message_to_show,
+                 const gchar      *default_user,
+                 const gchar      *default_domain,
+                 GAskPasswordFlags flags,
+                 gpointer          user_data)
+{
+  DBusMessage *message;
+  DBusMessageIter iter;
+  const gchar *mount_op_id;
+  const gchar *mount_op_owner;
 
-static GVolumeMonitor *monitor = NULL;
-static DBusConnection *connection = NULL;
-static GType the_volume_monitor_type;
-static const char *the_dbus_name = NULL;
+  print_debug ("in ask_password_cb %s", message_to_show);
 
-static GList *outstanding_ops = NULL;
+  mount_op_id = g_object_get_data (G_OBJECT (mount_operation), "mount_op_id");
+  mount_op_owner = g_object_get_data (G_OBJECT (mount_operation), "mount_op_owner");
+
+  message = dbus_message_new_method_call (mount_op_owner,
+                                          "/org/gtk/Private/RemoteVolumeMonitor",
+                                          "org.gtk.Private.RemoteVolumeMonitor",
+                                          "MountOpAskPassword");
+  dbus_message_iter_init_append (message, &iter);
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &the_dbus_name);
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &mount_op_id);
+
+  if (message_to_show == NULL)
+    message_to_show = "";
+
+  if (default_user == NULL)
+    default_user = "";
+
+  if (default_domain == NULL)
+    default_domain = "";
+
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &message_to_show);
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &default_user);
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &default_domain);
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &flags);
+
+  dbus_connection_send (connection, message, NULL);
+  dbus_message_unref (message);
+}
+
+static void
+ask_question_cb (GMountOperation  *mount_operation,
+                 const gchar      *message_to_show,
+                 gchar           **choices,
+                 gpointer          user_data)
+{
+  DBusMessage *message;
+  DBusMessageIter iter;
+  DBusMessageIter iter_string_array;
+  const gchar *mount_op_id;
+  const gchar *mount_op_owner;
+  guint n;
+
+  print_debug ("in ask_question_cb %s", message_to_show);
+
+  mount_op_id = g_object_get_data (G_OBJECT (mount_operation), "mount_op_id");
+  mount_op_owner = g_object_get_data (G_OBJECT (mount_operation), "mount_op_owner");
+
+  message = dbus_message_new_method_call (mount_op_owner,
+                                          "/org/gtk/Private/RemoteVolumeMonitor",
+                                          "org.gtk.Private.RemoteVolumeMonitor",
+                                          "MountOpAskQuestion");
+  dbus_message_iter_init_append (message, &iter);
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &the_dbus_name);
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &mount_op_id);
+
+  if (message_to_show == NULL)
+    message_to_show = "";
+
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &message_to_show);
+
+  dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &iter_string_array);
+  for (n = 0; choices != NULL && choices[n] != NULL; n++)
+    dbus_message_iter_append_basic (&iter_string_array, DBUS_TYPE_STRING, &(choices[n]));
+  dbus_message_iter_close_container (&iter, &iter_string_array);
+
+  dbus_connection_send (connection, message, NULL);
+  dbus_message_unref (message);
+}
+
+static void
+show_processes_cb (GMountOperation  *mount_operation,
+                   const gchar      *message_to_show,
+                   GArray           *processes,
+                   gchar           **choices,
+                   gpointer          user_data)
+{
+  DBusMessage *message;
+  DBusMessageIter iter;
+  DBusMessageIter iter_string_array;
+  const gchar *mount_op_id;
+  const gchar *mount_op_owner;
+  guint n;
+
+  print_debug ("in show_processes_cb %s", message_to_show);
+
+  mount_op_id = g_object_get_data (G_OBJECT (mount_operation), "mount_op_id");
+  mount_op_owner = g_object_get_data (G_OBJECT (mount_operation), "mount_op_owner");
+
+  message = dbus_message_new_method_call (mount_op_owner,
+                                          "/org/gtk/Private/RemoteVolumeMonitor",
+                                          "org.gtk.Private.RemoteVolumeMonitor",
+                                          "MountOpShowProcesses");
+  dbus_message_iter_init_append (message, &iter);
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &the_dbus_name);
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &mount_op_id);
+
+  if (message_to_show == NULL)
+    message_to_show = "";
+
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &message_to_show);
+
+  dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_INT32_AS_STRING, &iter_string_array);
+  for (n = 0; processes != NULL && n < processes->len; n++)
+    {
+      GPid pid;
+      pid = g_array_index (processes, GPid, n);
+      dbus_message_iter_append_basic (&iter_string_array, DBUS_TYPE_INT32, &pid);
+    }
+  dbus_message_iter_close_container (&iter, &iter_string_array);
+
+  dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &iter_string_array);
+  for (n = 0; choices != NULL && choices[n] != NULL; n++)
+    dbus_message_iter_append_basic (&iter_string_array, DBUS_TYPE_STRING, &(choices[n]));
+  dbus_message_iter_close_container (&iter, &iter_string_array);
+
+  dbus_connection_send (connection, message, NULL);
+  dbus_message_unref (message);
+}
+
+static void
+aborted_cb (GMountOperation  *mount_operation,
+            gpointer          user_data)
+{
+  DBusMessage *message;
+  DBusMessageIter iter;
+  const gchar *mount_op_id;
+  const gchar *mount_op_owner;
+
+  print_debug ("in aborted_cb");
+
+  mount_op_id = g_object_get_data (G_OBJECT (mount_operation), "mount_op_id");
+  mount_op_owner = g_object_get_data (G_OBJECT (mount_operation), "mount_op_owner");
+
+  message = dbus_message_new_method_call (mount_op_owner,
+                                          "/org/gtk/Private/RemoteVolumeMonitor",
+                                          "org.gtk.Private.RemoteVolumeMonitor",
+                                          "MountOpAborted");
+  dbus_message_iter_init_append (message, &iter);
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &the_dbus_name);
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &mount_op_id);
+
+  dbus_connection_send (connection, message, NULL);
+  dbus_message_unref (message);
+}
+
+static void
+mount_op_destroyed_cb (gpointer user_data,
+                       GObject *where_the_mount_op_was)
+{
+  outstanding_mount_op_objects = g_list_remove (outstanding_mount_op_objects, where_the_mount_op_was);
+}
+
+static GMountOperation *
+wrap_mount_op (const gchar *mount_op_id,
+               const gchar *mount_op_owner)
+{
+  GMountOperation *op;
+
+  op = g_proxy_mount_operation_new ();
+  //op = g_mount_operation_new ();
+  g_signal_connect (op, "ask-password", G_CALLBACK (ask_password_cb), NULL);
+  g_signal_connect (op, "ask-question", G_CALLBACK (ask_question_cb), NULL);
+  g_signal_connect (op, "show-processes", G_CALLBACK (show_processes_cb), NULL);
+  g_signal_connect (op, "aborted", G_CALLBACK (aborted_cb), NULL);
+  g_object_set_data_full (G_OBJECT (op), "mount_op_id", g_strdup (mount_op_id), g_free);
+  g_object_set_data_full (G_OBJECT (op), "mount_op_owner", g_strdup (mount_op_owner), g_free);
+
+  outstanding_mount_op_objects = g_list_prepend (outstanding_mount_op_objects, op);
+  g_object_weak_ref (G_OBJECT (op),
+                     mount_op_destroyed_cb,
+                     NULL);
+
+  return op;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
 
-static GHashTable *unique_names_being_watched = NULL;
 
 static void
 cancellable_destroyed_cb (gpointer user_data,
@@ -206,12 +408,13 @@ static void monitor_try_create (void);
  * boolean              is-media-removable
  * boolean              is-media-check-automatic
  * boolean              can-start
+ * boolean              can-start-degraded
  * boolean              can-stop
  * uint32               start-stop-type
  * array:string         volume-ids
  * dict:string->string  identifiers
  */
-#define DRIVE_STRUCT_TYPE "(sssbbbbbbbuasa{ss})"
+#define DRIVE_STRUCT_TYPE "(sssbbbbbbbbuasa{ss})"
 
 static void
 append_drive (GDrive *drive, DBusMessageIter *iter_array)
@@ -229,6 +432,7 @@ append_drive (GDrive *drive, DBusMessageIter *iter_array)
   gboolean is_media_removable;
   gboolean is_media_check_automatic;
   gboolean can_start;
+  gboolean can_start_degraded;
   gboolean can_stop;
   GDriveStartStopType start_stop_type;
   GList *volumes, *l;
@@ -250,6 +454,7 @@ append_drive (GDrive *drive, DBusMessageIter *iter_array)
   is_media_removable = g_drive_is_media_removable (drive);
   is_media_check_automatic = g_drive_is_media_check_automatic (drive);
   can_start = g_drive_can_start (drive);
+  can_start_degraded = g_drive_can_start_degraded (drive);
   can_stop = g_drive_can_stop (drive);
   start_stop_type = g_drive_get_start_stop_type (drive);
   volumes = g_drive_get_volumes (drive);
@@ -267,6 +472,7 @@ append_drive (GDrive *drive, DBusMessageIter *iter_array)
   dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_BOOLEAN, &is_media_removable);
   dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_BOOLEAN, &is_media_check_automatic);
   dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_BOOLEAN, &can_start);
+  dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_BOOLEAN, &can_start_degraded);
   dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_BOOLEAN, &can_stop);
   dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_UINT32, &start_stop_type);
 
@@ -562,9 +768,10 @@ mount_unmount_cb (GMount *mount, GAsyncResult *result, DBusMessage *message)
   print_debug ("in mount_unmount_cb");
 
   g_object_set_data (G_OBJECT (mount), "cancellable", NULL);
+  g_object_set_data (G_OBJECT (mount), "mount_operation", NULL);
 
   error = NULL;
-  if (!g_mount_unmount_finish (mount, result, &error))
+  if (!g_mount_unmount_with_operation_finish (mount, result, &error))
     {
       print_debug ("  error: %s", error->message);
       reply = _dbus_message_new_from_gerror (message, error);
@@ -579,6 +786,8 @@ mount_unmount_cb (GMount *mount, GAsyncResult *result, DBusMessage *message)
   dbus_connection_send (connection, reply, NULL);
   dbus_message_unref (message);
   dbus_message_unref (reply);
+
+  g_object_unref (mount);
 }
 
 static DBusHandlerResult
@@ -587,7 +796,9 @@ handle_mount_unmount (DBusConnection *connection, DBusMessage *message)
   const char *id;
   const char *cancellation_id;
   const char *sender;
+  const char *mount_op_id;
   GCancellable *cancellable;
+  GMountOperation *mount_operation;
   dbus_uint32_t unmount_flags;
   DBusError dbus_error;
   GList *mounts, *l;
@@ -603,6 +814,7 @@ handle_mount_unmount (DBusConnection *connection, DBusMessage *message)
                               DBUS_TYPE_STRING, &id,
                               DBUS_TYPE_STRING, &cancellation_id,
                               DBUS_TYPE_UINT32, &unmount_flags,
+                              DBUS_TYPE_STRING, &mount_op_id,
                               DBUS_TYPE_INVALID))
     {
       g_warning ("Error parsing args for MountUnmount(): %s: %s", dbus_error.name, dbus_error.message);
@@ -663,11 +875,20 @@ handle_mount_unmount (DBusConnection *connection, DBusMessage *message)
                      cancellable_destroyed_cb,
                      NULL);
 
-  g_mount_unmount (mount,
-                   unmount_flags,
-                   NULL,
-                   (GAsyncReadyCallback) mount_unmount_cb,
-                   dbus_message_ref (message));
+  mount_operation = NULL;
+  if (mount_op_id != NULL && strlen (mount_op_id) > 0)
+    {
+      mount_operation = wrap_mount_op (mount_op_id, sender);
+      g_object_set_data_full (G_OBJECT (mount), "mount_operation", mount_operation, g_object_unref);
+    }
+
+  g_object_ref (mount);
+  g_mount_unmount_with_operation (mount,
+                                  unmount_flags,
+                                  mount_operation,
+                                  cancellable,
+                                  (GAsyncReadyCallback) mount_unmount_cb,
+                                  dbus_message_ref (message));
 
  out:
   if (mounts != NULL)
@@ -683,7 +904,6 @@ handle_mount_unmount (DBusConnection *connection, DBusMessage *message)
 static DBusHandlerResult
 handle_mount_op_reply (DBusConnection *connection, DBusMessage *message)
 {
-  const char *id;
   const char *mount_op_id;
   dbus_int32_t result;
   const char *user_name;
@@ -697,17 +917,17 @@ handle_mount_op_reply (DBusConnection *connection, DBusMessage *message)
   DBusError dbus_error;
   DBusHandlerResult ret;
   GList *volumes, *l;
-  GVolume *volume;
   DBusMessage *reply;
   GMountOperation *mount_operation;
+  const gchar *sender;
 
   volumes = NULL;
   decoded_password = NULL;
   ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 
   dbus_error_init (&dbus_error);
-  if (!dbus_message_get_args (message, &dbus_error,
-                              DBUS_TYPE_STRING, &id,
+  if (!dbus_message_get_args (message,
+                              &dbus_error,
                               DBUS_TYPE_STRING, &mount_op_id,
                               DBUS_TYPE_INT32, &result,
                               DBUS_TYPE_STRING, &user_name,
@@ -727,34 +947,25 @@ handle_mount_op_reply (DBusConnection *connection, DBusMessage *message)
 
   ret = DBUS_HANDLER_RESULT_HANDLED;
 
-  volume = NULL;
-  volumes = g_volume_monitor_get_volumes (monitor);
-  for (l = volumes; l != NULL; l = l->next)
-    {
-      char *volume_id;
-
-      volume = G_VOLUME (l->data);
-      volume_id = g_strdup_printf ("%p", volume);
-      if (strcmp (volume_id, id) == 0)
-        break;
-
-      g_free (volume_id);
-    }
-  if (l == NULL)
-    volume = NULL;
+  sender = dbus_message_get_sender (message);
 
-  if (volume == NULL)
+  /* Find the op */
+  for (l = outstanding_mount_op_objects; l != NULL; l = l->next)
     {
-      DBusMessage *reply;
-      reply = dbus_message_new_error (message,
-                                      "org.gtk.Private.RemoteVolumeMonitor.NotFound",
-                                      "The given volume was not found");
-      dbus_connection_send (connection, reply, NULL);
-      dbus_message_unref (reply);
-      goto out;
+      GMountOperation *op = G_MOUNT_OPERATION (l->data);
+      const gchar *owner;
+      const gchar *id;
+
+      owner = g_object_get_data (G_OBJECT (op), "mount_op_owner");
+      id = g_object_get_data (G_OBJECT (op), "mount_op_id");
+      if (g_strcmp0 (owner, sender) == 0 && g_strcmp0 (id, mount_op_id) == 0)
+        {
+          print_debug ("found mount_op");
+          mount_operation = op;
+          break;
+        }
     }
 
-  mount_operation = g_object_get_data (G_OBJECT (volume), "mount_operation");
   if (mount_operation == NULL)
     {
       DBusMessage *reply;
@@ -783,11 +994,6 @@ handle_mount_op_reply (DBusConnection *connection, DBusMessage *message)
 
  out:
   g_free (decoded_password);
-  if (volumes != NULL)
-    {
-      g_list_foreach (volumes, (GFunc) g_object_unref, NULL);
-      g_list_free (volumes);
-    }
   return ret;
 }
 
@@ -822,146 +1028,6 @@ volume_mount_cb (GVolume *volume, GAsyncResult *result, DBusMessage *message)
   dbus_message_unref (reply);
 }
 
-static void
-ask_password_cb (GMountOperation  *mount_operation,
-                 const gchar      *message_to_show,
-                 const gchar      *default_user,
-                 const gchar      *default_domain,
-                 GAskPasswordFlags flags,
-                 gpointer          user_data)
-{
-  gchar *id;
-  DBusMessage *message;
-  DBusMessageIter iter;
-  GVolume *volume;
-  const gchar *mount_op_id;
-  const gchar *mount_op_owner;
-
-  print_debug ("in ask_password_cb %s", message_to_show);
-
-  volume = G_VOLUME (user_data);
-
-  id = g_strdup_printf ("%p", volume);
-
-  mount_op_id = g_object_get_data (G_OBJECT (mount_operation), "mount_op_id");
-  mount_op_owner = g_object_get_data (G_OBJECT (mount_operation), "mount_op_owner");
-
-  message = dbus_message_new_signal ("/org/gtk/Private/RemoteVolumeMonitor",
-                                     "org.gtk.Private.RemoteVolumeMonitor",
-                                     "MountOpAskPassword");
-  dbus_message_iter_init_append (message, &iter);
-  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &the_dbus_name);
-  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &id);
-  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &mount_op_id);
-
-  if (message_to_show == NULL)
-    message_to_show = "";
-
-  if (default_user == NULL)
-    default_user = "";
-
-  if (default_domain == NULL)
-    default_domain = "";
-
-  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &message_to_show);
-  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &default_user);
-  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &default_domain);
-  dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &flags);
-
-  dbus_message_set_destination (message, mount_op_owner);
-
-  dbus_connection_send (connection, message, NULL);
-  dbus_message_unref (message);
-
-  g_free (id);
-}
-
-static void
-ask_question_cb (GMountOperation  *mount_operation,
-                 const gchar      *message_to_show,
-                 gchar           **choices,
-                 gpointer          user_data)
-{
-  gchar *id;
-  DBusMessage *message;
-  DBusMessageIter iter;
-  DBusMessageIter iter_string_array;
-  const gchar *mount_op_id;
-  const gchar *mount_op_owner;
-  GVolume *volume;
-  guint n;
-
-  print_debug ("in ask_question_cb %s", message_to_show);
-
-  volume = G_VOLUME (user_data);
-
-  id = g_strdup_printf ("%p", volume);
-
-  mount_op_id = g_object_get_data (G_OBJECT (mount_operation), "mount_op_id");
-  mount_op_owner = g_object_get_data (G_OBJECT (mount_operation), "mount_op_owner");
-
-  message = dbus_message_new_signal ("/org/gtk/Private/RemoteVolumeMonitor",
-                                     "org.gtk.Private.RemoteVolumeMonitor",
-                                     "MountOpAskQuestion");
-  dbus_message_iter_init_append (message, &iter);
-  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &the_dbus_name);
-  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &id);
-  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &mount_op_id);
-
-  if (message_to_show == NULL)
-    message_to_show = "";
-
-  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &message_to_show);
-
-  dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &iter_string_array);
-  for (n = 0; choices != NULL && choices[n] != NULL; n++)
-    dbus_message_iter_append_basic (&iter_string_array, DBUS_TYPE_STRING, &(choices[n]));
-  dbus_message_iter_close_container (&iter, &iter_string_array);
-
-  dbus_message_set_destination (message, mount_op_owner);
-
-  dbus_connection_send (connection, message, NULL);
-  dbus_message_unref (message);
-
-  g_free (id);
-}
-
-static void
-aborted_cb (GMountOperation  *mount_operation,
-            gpointer          user_data)
-{
-  gchar *id;
-  DBusMessage *message;
-  DBusMessageIter iter;
-  GVolume *volume;
-  const gchar *mount_op_id;
-  const gchar *mount_op_owner;
-
-  print_debug ("in aborted_cb");
-
-  volume = G_VOLUME (user_data);
-
-  id = g_strdup_printf ("%p", volume);
-
-  mount_op_id = g_object_get_data (G_OBJECT (mount_operation), "mount_op_id");
-  mount_op_owner = g_object_get_data (G_OBJECT (mount_operation), "mount_op_owner");
-
-  message = dbus_message_new_signal ("/org/gtk/Private/RemoteVolumeMonitor",
-                                     "org.gtk.Private.RemoteVolumeMonitor",
-                                     "MountOpAborted");
-  dbus_message_iter_init_append (message, &iter);
-  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &the_dbus_name);
-  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &id);
-  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &mount_op_id);
-
-  dbus_message_set_destination (message, mount_op_owner);
-
-  dbus_connection_send (connection, message, NULL);
-  dbus_message_unref (message);
-
-  g_free (id);
-}
-
 static DBusHandlerResult
 handle_volume_mount (DBusConnection *connection, DBusMessage *message)
 {
@@ -1040,12 +1106,7 @@ handle_volume_mount (DBusConnection *connection, DBusMessage *message)
   mount_operation = NULL;
   if (mount_op_id != NULL && strlen (mount_op_id) > 0)
     {
-      mount_operation = g_proxy_mount_operation_new ();
-      g_signal_connect (mount_operation, "ask-password", G_CALLBACK (ask_password_cb), volume);
-      g_signal_connect (mount_operation, "ask-question", G_CALLBACK (ask_question_cb), volume);
-      g_signal_connect (mount_operation, "aborted", G_CALLBACK (aborted_cb), volume);
-      g_object_set_data_full (G_OBJECT (mount_operation), "mount_op_id", g_strdup (mount_op_id), g_free);
-      g_object_set_data_full (G_OBJECT (mount_operation), "mount_op_owner", g_strdup (sender), g_free);
+      mount_operation = wrap_mount_op (mount_op_id, sender);
       g_object_set_data_full (G_OBJECT (volume), "mount_operation", mount_operation, g_object_unref);
     }
 
@@ -1085,9 +1146,10 @@ drive_eject_cb (GDrive *drive, GAsyncResult *result, DBusMessage *message)
   print_debug ("in drive_eject_cb");
 
   g_object_set_data (G_OBJECT (drive), "cancellable", NULL);
+  g_object_set_data (G_OBJECT (drive), "mount_operation", NULL);
 
   error = NULL;
-  if (!g_drive_eject_finish (drive, result, &error))
+  if (!g_drive_eject_with_operation_finish (drive, result, &error))
     {
       print_debug ("  error: %s", error->message);
       reply = _dbus_message_new_from_gerror (message, error);
@@ -1110,6 +1172,8 @@ handle_drive_eject (DBusConnection *connection, DBusMessage *message)
   const char *id;
   const char *cancellation_id;
   const char *sender;
+  const char *mount_op_id;
+  GMountOperation *mount_operation;
   GCancellable *cancellable;
   dbus_uint32_t unmount_flags;
   DBusError dbus_error;
@@ -1126,7 +1190,8 @@ handle_drive_eject (DBusConnection *connection, DBusMessage *message)
   if (!dbus_message_get_args (message, &dbus_error,
                               DBUS_TYPE_STRING, &id,
                               DBUS_TYPE_STRING, &cancellation_id,
-                              DBUS_TYPE_UINT32 &unmount_flags,
+                              DBUS_TYPE_UINT32, &unmount_flags,
+                              DBUS_TYPE_STRING, &mount_op_id,
                               DBUS_TYPE_INVALID))
     {
       g_warning ("Error parsing args for DriveEject(): %s: %s", dbus_error.name, dbus_error.message);
@@ -1187,126 +1252,21 @@ handle_drive_eject (DBusConnection *connection, DBusMessage *message)
                      cancellable_destroyed_cb,
                      NULL);
 
-  g_drive_eject (drive,
-                 unmount_flags,
-                 cancellable,
-                 (GAsyncReadyCallback) drive_eject_cb,
-                 dbus_message_ref (message));
-
- out:
-  if (drives != NULL)
-    {
-      g_list_foreach (drives, (GFunc) g_object_unref, NULL);
-      g_list_free (drives);
-    }
-  return ret;
-}
-
-/* ---------------------------------------------------------------------------------------------------- */
-
-static DBusHandlerResult
-handle_start_op_reply (DBusConnection *connection, DBusMessage *message)
-{
-  const char *id;
-  const char *start_op_id;
-  dbus_int32_t result;
-  const char *user_name;
-  const char *domain;
-  const char *encoded_password;
-  char *decoded_password;
-  gsize decoded_password_len;
-  dbus_int32_t password_save;
-  dbus_int32_t choice;
-  dbus_bool_t anonymous;
-  DBusError dbus_error;
-  DBusHandlerResult ret;
-  GList *drives, *l;
-  GDrive *drive;
-  DBusMessage *reply;
-  GMountOperation *start_operation;
-
-  drives = NULL;
-  decoded_password = NULL;
-  ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-
-  dbus_error_init (&dbus_error);
-  if (!dbus_message_get_args (message, &dbus_error,
-                              DBUS_TYPE_STRING, &id,
-                              DBUS_TYPE_STRING, &start_op_id,
-                              DBUS_TYPE_INT32, &result,
-                              DBUS_TYPE_STRING, &user_name,
-                              DBUS_TYPE_STRING, &domain,
-                              DBUS_TYPE_STRING, &encoded_password,
-                              DBUS_TYPE_INT32, &password_save,
-                              DBUS_TYPE_INT32, &choice,
-                              DBUS_TYPE_BOOLEAN, &anonymous,
-                              DBUS_TYPE_INVALID))
-    {
-      g_warning ("Error parsing args for StartOpReply(): %s: %s", dbus_error.name, dbus_error.message);
-      dbus_error_free (&dbus_error);
-      goto out;
-    }
-
-  print_debug ("in handle_start_op_reply");
-
-  ret = DBUS_HANDLER_RESULT_HANDLED;
-
-  drive = NULL;
-  drives = g_volume_monitor_get_connected_drives (monitor);
-  for (l = drives; l != NULL; l = l->next)
-    {
-      char *drive_id;
-
-      drive = G_DRIVE (l->data);
-      drive_id = g_strdup_printf ("%p", drive);
-      if (strcmp (drive_id, id) == 0)
-        break;
-
-      g_free (drive_id);
-    }
-  if (l == NULL)
-    drive = NULL;
-
-  if (drive == NULL)
-    {
-      DBusMessage *reply;
-      reply = dbus_message_new_error (message,
-                                      "org.gtk.Private.RemoteVolumeMonitor.NotFound",
-                                      "The given drive was not found");
-      dbus_connection_send (connection, reply, NULL);
-      dbus_message_unref (reply);
-      goto out;
-    }
-
-  start_operation = g_object_get_data (G_OBJECT (drive), "start_operation");
-  if (start_operation == NULL)
+  mount_operation = NULL;
+  if (mount_op_id != NULL && strlen (mount_op_id) > 0)
     {
-      DBusMessage *reply;
-      reply = dbus_message_new_error (message,
-                                      "org.gtk.Private.RemoteVolumeMonitor.NotFound",
-                                      "No outstanding mount operation");
-      dbus_connection_send (connection, reply, NULL);
-      dbus_message_unref (reply);
-      goto out;
+      mount_operation = wrap_mount_op (mount_op_id, sender);
+      g_object_set_data_full (G_OBJECT (drive), "mount_operation", mount_operation, g_object_unref);
     }
 
-  decoded_password = (gchar *) g_base64_decode (encoded_password, &decoded_password_len);
-
-  g_mount_operation_set_username (start_operation, user_name);
-  g_mount_operation_set_domain (start_operation, domain);
-  g_mount_operation_set_password (start_operation, decoded_password);
-  g_mount_operation_set_password_save (start_operation, password_save);
-  g_mount_operation_set_choice (start_operation, choice);
-  g_mount_operation_set_anonymous (start_operation, anonymous);
-
-  g_mount_operation_reply (start_operation, result);
-
-  reply = dbus_message_new_method_return (message);
-  dbus_connection_send (connection, reply, NULL);
-  dbus_message_unref (reply);
+  g_drive_eject_with_operation (drive,
+                                unmount_flags,
+                                mount_operation,
+                                cancellable,
+                                (GAsyncReadyCallback) drive_eject_cb,
+                                dbus_message_ref (message));
 
  out:
-  g_free (decoded_password);
   if (drives != NULL)
     {
       g_list_foreach (drives, (GFunc) g_object_unref, NULL);
@@ -1326,6 +1286,7 @@ drive_stop_cb (GDrive *drive, GAsyncResult *result, DBusMessage *message)
   print_debug ("in drive_stop_cb");
 
   g_object_set_data (G_OBJECT (drive), "cancellable", NULL);
+  g_object_set_data (G_OBJECT (drive), "mount_operation", NULL);
 
   error = NULL;
   if (!g_drive_stop_finish (drive, result, &error))
@@ -1351,6 +1312,8 @@ handle_drive_stop (DBusConnection *connection, DBusMessage *message)
   const char *id;
   const char *cancellation_id;
   const char *sender;
+  const char *mount_op_id;
+  GMountOperation *mount_operation;
   GCancellable *cancellable;
   dbus_uint32_t unmount_flags;
   DBusError dbus_error;
@@ -1367,7 +1330,8 @@ handle_drive_stop (DBusConnection *connection, DBusMessage *message)
   if (!dbus_message_get_args (message, &dbus_error,
                               DBUS_TYPE_STRING, &id,
                               DBUS_TYPE_STRING, &cancellation_id,
-                              DBUS_TYPE_UINT32 &unmount_flags,
+                              DBUS_TYPE_UINT32, &unmount_flags,
+                              DBUS_TYPE_STRING, &mount_op_id,
                               DBUS_TYPE_INVALID))
     {
       g_warning ("Error parsing args for DriveStop(): %s: %s", dbus_error.name, dbus_error.message);
@@ -1428,8 +1392,16 @@ handle_drive_stop (DBusConnection *connection, DBusMessage *message)
                      cancellable_destroyed_cb,
                      NULL);
 
+  mount_operation = NULL;
+  if (mount_op_id != NULL && strlen (mount_op_id) > 0)
+    {
+      mount_operation = wrap_mount_op (mount_op_id, sender);
+      g_object_set_data_full (G_OBJECT (drive), "mount_operation", mount_operation, g_object_unref);
+    }
+
   g_drive_stop (drive,
                 unmount_flags,
+                mount_operation,
                 cancellable,
                 (GAsyncReadyCallback) drive_stop_cb,
                 dbus_message_ref (message));
@@ -1453,7 +1425,7 @@ drive_start_cb (GDrive *drive, GAsyncResult *result, DBusMessage *message)
 
   print_debug ("in drive_start_cb");
 
-  g_object_set_data (G_OBJECT (drive), "start_operation", NULL);
+  g_object_set_data (G_OBJECT (drive), "mount_operation", NULL);
   g_object_set_data (G_OBJECT (drive), "cancellable", NULL);
 
   error = NULL;
@@ -1474,159 +1446,19 @@ drive_start_cb (GDrive *drive, GAsyncResult *result, DBusMessage *message)
   dbus_message_unref (reply);
 }
 
-static void
-start_ask_password_cb (GMountOperation  *start_operation,
-                       const gchar      *message_to_show,
-                       const gchar      *default_user,
-                       const gchar      *default_domain,
-                       GAskPasswordFlags flags,
-                       gpointer          user_data)
-{
-  gchar *id;
-  DBusMessage *message;
-  DBusMessageIter iter;
-  GDrive *drive;
-  const gchar *start_op_id;
-  const gchar *start_op_owner;
-
-  print_debug ("in ask_password_cb %s", message_to_show);
-
-  drive = G_DRIVE (user_data);
-
-  id = g_strdup_printf ("%p", drive);
-
-  start_op_id = g_object_get_data (G_OBJECT (start_operation), "start_op_id");
-  start_op_owner = g_object_get_data (G_OBJECT (start_operation), "start_op_owner");
-
-  message = dbus_message_new_signal ("/org/gtk/Private/RemoteVolumeMonitor",
-                                     "org.gtk.Private.RemoteVolumeMonitor",
-                                     "StartOpAskPassword");
-  dbus_message_iter_init_append (message, &iter);
-  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &the_dbus_name);
-  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &id);
-  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &start_op_id);
-
-  if (message_to_show == NULL)
-    message_to_show = "";
-
-  if (default_user == NULL)
-    default_user = "";
-
-  if (default_domain == NULL)
-    default_domain = "";
-
-  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &message_to_show);
-  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &default_user);
-  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &default_domain);
-  dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &flags);
-
-  dbus_message_set_destination (message, start_op_owner);
-
-  dbus_connection_send (connection, message, NULL);
-  dbus_message_unref (message);
-
-  g_free (id);
-}
-
-static void
-start_ask_question_cb (GMountOperation  *start_operation,
-                       const gchar      *message_to_show,
-                       gchar           **choices,
-                       gpointer          user_data)
-{
-  gchar *id;
-  DBusMessage *message;
-  DBusMessageIter iter;
-  DBusMessageIter iter_string_array;
-  const gchar *start_op_id;
-  const gchar *start_op_owner;
-  GDrive *drive;
-  guint n;
-
-  print_debug ("in ask_question_cb %s", message_to_show);
-
-  drive = G_DRIVE (user_data);
-
-  id = g_strdup_printf ("%p", drive);
-
-  start_op_id = g_object_get_data (G_OBJECT (start_operation), "start_op_id");
-  start_op_owner = g_object_get_data (G_OBJECT (start_operation), "start_op_owner");
-
-  message = dbus_message_new_signal ("/org/gtk/Private/RemoteVolumeMonitor",
-                                     "org.gtk.Private.RemoteVolumeMonitor",
-                                     "StartOpAskQuestion");
-  dbus_message_iter_init_append (message, &iter);
-  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &the_dbus_name);
-  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &id);
-  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &start_op_id);
-
-  if (message_to_show == NULL)
-    message_to_show = "";
-
-  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &message_to_show);
-
-  dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &iter_string_array);
-  for (n = 0; choices != NULL && choices[n] != NULL; n++)
-    dbus_message_iter_append_basic (&iter_string_array, DBUS_TYPE_STRING, &(choices[n]));
-  dbus_message_iter_close_container (&iter, &iter_string_array);
-
-  dbus_message_set_destination (message, start_op_owner);
-
-  dbus_connection_send (connection, message, NULL);
-  dbus_message_unref (message);
-
-  g_free (id);
-}
-
-static void
-start_aborted_cb (GMountOperation  *start_operation,
-                  gpointer          user_data)
-{
-  gchar *id;
-  DBusMessage *message;
-  DBusMessageIter iter;
-  GDrive *drive;
-  const gchar *start_op_id;
-  const gchar *start_op_owner;
-
-  print_debug ("in aborted_cb");
-
-  drive = G_DRIVE (user_data);
-
-  id = g_strdup_printf ("%p", drive);
-
-  start_op_id = g_object_get_data (G_OBJECT (start_operation), "start_op_id");
-  start_op_owner = g_object_get_data (G_OBJECT (start_operation), "start_op_owner");
-
-  message = dbus_message_new_signal ("/org/gtk/Private/RemoteVolumeMonitor",
-                                     "org.gtk.Private.RemoteVolumeMonitor",
-                                     "StartOpAborted");
-  dbus_message_iter_init_append (message, &iter);
-  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &the_dbus_name);
-  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &id);
-  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &start_op_id);
-
-  dbus_message_set_destination (message, start_op_owner);
-
-  dbus_connection_send (connection, message, NULL);
-  dbus_message_unref (message);
-
-  g_free (id);
-}
-
 static DBusHandlerResult
 handle_drive_start (DBusConnection *connection, DBusMessage *message)
 {
   const char *id;
   const char *cancellation_id;
   const char *sender;
-  const char *start_op_id;
+  const char *mount_op_id;
   GDriveStartFlags flags;
   DBusError dbus_error;
   GList *drives, *l;
   GDrive *drive;
   DBusHandlerResult ret;
-  GMountOperation *start_operation;
+  GMountOperation *mount_operation;
   GCancellable *cancellable;
 
   drives = NULL;
@@ -1637,7 +1469,7 @@ handle_drive_start (DBusConnection *connection, DBusMessage *message)
                               DBUS_TYPE_STRING, &id,
                               DBUS_TYPE_STRING, &cancellation_id,
                               DBUS_TYPE_UINT32, &flags,
-                              DBUS_TYPE_STRING, &start_op_id,
+                              DBUS_TYPE_STRING, &mount_op_id,
                               DBUS_TYPE_INVALID))
     {
       g_warning ("Error parsing args for DriveStart(): %s: %s", dbus_error.name, dbus_error.message);
@@ -1689,16 +1521,11 @@ handle_drive_start (DBusConnection *connection, DBusMessage *message)
       goto out;
     }
 
-  start_operation = NULL;
-  if (start_op_id != NULL && strlen (start_op_id) > 0)
+  mount_operation = NULL;
+  if (mount_op_id != NULL && strlen (mount_op_id) > 0)
     {
-      start_operation = g_proxy_mount_operation_new ();
-      g_signal_connect (start_operation, "ask-password", G_CALLBACK (start_ask_password_cb), drive);
-      g_signal_connect (start_operation, "ask-question", G_CALLBACK (start_ask_question_cb), drive);
-      g_signal_connect (start_operation, "aborted", G_CALLBACK (start_aborted_cb), drive);
-      g_object_set_data_full (G_OBJECT (start_operation), "start_op_id", g_strdup (start_op_id), g_free);
-      g_object_set_data_full (G_OBJECT (start_operation), "start_op_owner", g_strdup (sender), g_free);
-      g_object_set_data_full (G_OBJECT (drive), "start_operation", start_operation, g_object_unref);
+      mount_operation = wrap_mount_op (mount_op_id, sender);
+      g_object_set_data_full (G_OBJECT (drive), "mount_operation", mount_operation, g_object_unref);
     }
 
   cancellable = g_cancellable_new ();
@@ -1712,7 +1539,7 @@ handle_drive_start (DBusConnection *connection, DBusMessage *message)
 
   g_drive_start (drive,
                  flags,
-                 start_operation,
+                 mount_operation,
                  cancellable,
                  (GAsyncReadyCallback) drive_start_cb,
                  dbus_message_ref (message));
@@ -1973,6 +1800,23 @@ filter_function (DBusConnection *connection, DBusMessage *message, void *user_da
         {
           GList *l;
 
+          /* if @name has outstanding mount operation objects; abort them */
+          for (l = outstanding_mount_op_objects; l != NULL; l = l->next)
+            {
+              GMountOperation *op = G_MOUNT_OPERATION (l->data);
+              const gchar *owner;
+              const gchar *id;
+
+              owner = g_object_get_data (G_OBJECT (op), "mount_op_owner");
+              id = g_object_get_data (G_OBJECT (op), "mount_op_id");
+              if (g_strcmp0 (owner, name) == 0)
+                {
+                  print_debug ("****** name `%s' has an outstanding mount operation object, aborting it",
+                               name);
+                  g_mount_operation_reply (op, G_MOUNT_OPERATION_ABORTED);
+                }
+            }
+
           /* see if @name has outstanding ops; if so, cancel them */
           for (l = outstanding_ops; l != NULL; l = l->next)
             {
@@ -1983,7 +1827,8 @@ filter_function (DBusConnection *connection, DBusMessage *message, void *user_da
               print_debug ("looking at op for %s", owner);
               if (g_strcmp0 (owner, name) == 0)
                 {
-                  print_debug ("****** name has an outstanding op");
+                  print_debug ("****** name `%s' has an outstanding op, cancelling it",
+                               name);
                   g_cancellable_cancel (cancellable);
                 }
             }
@@ -2017,9 +1862,6 @@ filter_function (DBusConnection *connection, DBusMessage *message, void *user_da
               else if (dbus_message_is_method_call (message, "org.gtk.Private.RemoteVolumeMonitor", "MountUnmount"))
                 ret = handle_mount_unmount (connection, message);
 
-              else if (dbus_message_is_method_call (message, "org.gtk.Private.RemoteVolumeMonitor", "MountOpReply"))
-                ret = handle_mount_op_reply (connection, message);
-
               else if (dbus_message_is_method_call (message, "org.gtk.Private.RemoteVolumeMonitor", "VolumeMount"))
                 ret = handle_volume_mount (connection, message);
 
@@ -2035,9 +1877,8 @@ filter_function (DBusConnection *connection, DBusMessage *message, void *user_da
               else if (dbus_message_is_method_call (message, "org.gtk.Private.RemoteVolumeMonitor", "DriveStop"))
                 ret = handle_drive_stop (connection, message);
 
-              else if (dbus_message_is_method_call (message, "org.gtk.Private.RemoteVolumeMonitor", "StartOpReply"))
-                ret = handle_start_op_reply (connection, message);
-
+              else if (dbus_message_is_method_call (message, "org.gtk.Private.RemoteVolumeMonitor", "MountOpReply"))
+                ret = handle_mount_op_reply (connection, message);
             }
         }
     }
diff --git a/programs/gvfs-mount.c b/programs/gvfs-mount.c
index b2699a6..91a451d 100644
--- a/programs/gvfs-mount.c
+++ b/programs/gvfs-mount.c
@@ -232,7 +232,7 @@ unmount_done_cb (GObject *object,
   gboolean succeeded;
   GError *error = NULL;
 
-  succeeded = g_mount_unmount_finish (G_MOUNT (object), res, &error);
+  succeeded = g_mount_unmount_with_operation_finish (G_MOUNT (object), res, &error);
 
   g_object_unref (G_MOUNT (object));
 
@@ -250,6 +250,7 @@ unmount (GFile *file)
 {
   GMount *mount;
   GError *error = NULL;
+  GMountOperation *mount_op;
 
   if (file == NULL)
     return;
@@ -261,7 +262,9 @@ unmount (GFile *file)
       return;
     }
 
-  g_mount_unmount (mount, 0, NULL, unmount_done_cb, NULL);
+  mount_op = new_mount_op ();
+  g_mount_unmount_with_operation (mount, 0, mount_op, NULL, unmount_done_cb, NULL);
+  g_object_unref (mount_op);
 
   outstanding_mounts++;
 }



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