Drive: Refuse to eject drives that appear to be in use
authorDavid Zeuthen <davidz@redhat.com>
Tue, 15 May 2012 15:12:57 +0000 (11:12 -0400)
committerDavid Zeuthen <davidz@redhat.com>
Tue, 15 May 2012 15:12:57 +0000 (11:12 -0400)
Unfortunately eject(1) is trying to be "helpful" insofar that it will
unmount partitions in use. While this is indeed handy when running
eject(1) from the command-line it's not a very good idea for us since
with the way its used in udisks, it effectively allows unmounting
partitions possibly mounted by other users.

So check if block devices for the drive is either mounted or unlocked
and bail if this is the case.

Signed-off-by: David Zeuthen <davidz@redhat.com>
doc/udisks2-sections.txt
src/udiskslinuxdrive.c
src/udiskslinuxdriveobject.c
src/udiskslinuxdriveobject.h

index 75ad876..1ac10b6 100644 (file)
@@ -184,6 +184,7 @@ udisks_linux_drive_object_get_block
 udisks_linux_drive_object_get_device
 udisks_linux_drive_object_get_devices
 udisks_linux_drive_object_housekeeping
+udisks_linux_drive_object_is_not_in_use
 <SUBSECTION Standard>
 UDISKS_TYPE_LINUX_DRIVE_OBJECT
 UDISKS_LINUX_DRIVE_OBJECT
index a47e5b0..37ce4d2 100644 (file)
@@ -706,17 +706,25 @@ handle_eject (UDisksDrive           *_drive,
     }
 
   daemon = udisks_linux_drive_object_get_daemon (object);
-  block_object = udisks_linux_drive_object_get_block (object, TRUE);
+  block_object = udisks_linux_drive_object_get_block (object, FALSE);
   if (block_object == NULL)
     {
       g_dbus_method_invocation_return_error (invocation,
                                              UDISKS_ERROR,
                                              UDISKS_ERROR_FAILED,
-                                             "Unable to find physical block device for drive");
+                                             "Unable to find block device for drive");
       goto out;
     }
   block = udisks_object_peek_block (UDISKS_OBJECT (block_object));
 
+  /* refuse to eject if drive appears to be in use */
+  if (!udisks_linux_drive_object_is_not_in_use (object, NULL, &error))
+    {
+      g_prefix_error (&error, "Cannot eject drive in use: ");
+      g_dbus_method_invocation_take_error (invocation, error);
+      goto out;
+    }
+
   error = NULL;
   if (!udisks_daemon_util_get_caller_pid_sync (daemon,
                                                invocation,
index 0da6719..41cc40e 100644 (file)
@@ -881,3 +881,106 @@ udisks_linux_drive_object_housekeeping (UDisksLinuxDriveObject  *object,
  out:
   return ret;
 }
+
+static gboolean
+is_block_unlocked (GList *objects, const gchar *crypto_object_path)
+{
+  gboolean ret = FALSE;
+  GList *l;
+  for (l = objects; l != NULL; l = l->next)
+    {
+      UDisksObject *object = UDISKS_OBJECT (l->data);
+      UDisksBlock *block;
+      block = udisks_object_peek_block (object);
+      if (block != NULL)
+        {
+          if (g_strcmp0 (udisks_block_get_crypto_backing_device (block), crypto_object_path) == 0)
+            {
+              ret = TRUE;
+              goto out;
+            }
+        }
+    }
+ out:
+  return ret;
+}
+
+/**
+ * udisks_linux_drive_object_is_not_in_use:
+ * @object: A #UDisksLinuxDriveObject.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @error: A #GError or %NULL.
+ *
+ * Checks if the drive represented by @object is in use and sets
+ * @error if so.
+ *
+ * Returns: %TRUE if @object is not is use, %FALSE if @error is set.
+ */
+gboolean
+udisks_linux_drive_object_is_not_in_use (UDisksLinuxDriveObject   *object,
+                                         GCancellable             *cancellable,
+                                         GError                  **error)
+{
+  GDBusObjectManagerServer *object_manager;
+  const gchar *drive_object_path;
+  gboolean ret = TRUE;
+  GList *objects = NULL;
+  GList *l;
+
+  g_return_val_if_fail (UDISKS_IS_LINUX_DRIVE_OBJECT (object), FALSE);
+  g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
+  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+  drive_object_path = g_dbus_object_get_object_path (G_DBUS_OBJECT (object));
+
+  object_manager = udisks_daemon_get_object_manager (object->daemon);
+  objects = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (object_manager));
+
+  /* Visit all block devices related to the drive... */
+  for (l = objects; l != NULL; l = l->next)
+    {
+      GDBusObjectSkeleton *iter_object = G_DBUS_OBJECT_SKELETON (l->data);
+      UDisksBlock *block;
+      UDisksFilesystem *filesystem;
+
+      if (!UDISKS_IS_LINUX_BLOCK_OBJECT (iter_object))
+        continue;
+
+      block = udisks_object_peek_block (UDISKS_OBJECT (iter_object));
+      filesystem = udisks_object_peek_filesystem (UDISKS_OBJECT (iter_object));
+
+      if (g_strcmp0 (udisks_block_get_drive (block), drive_object_path) != 0)
+        continue;
+
+      /* bail if block device is mounted */
+      if (filesystem != NULL)
+        {
+          if (g_strv_length ((gchar **) udisks_filesystem_get_mount_points (filesystem)) > 0)
+            {
+              g_set_error (error,
+                           UDISKS_ERROR,
+                           UDISKS_ERROR_DEVICE_BUSY,
+                           "Device %s is mounted",
+                           udisks_block_get_preferred_device (block));
+              ret = FALSE;
+              goto out;
+            }
+        }
+
+      /* bail if block device is unlocked (LUKS) */
+      if (is_block_unlocked (objects, g_dbus_object_get_object_path (G_DBUS_OBJECT (iter_object))))
+        {
+          g_set_error (error,
+                       UDISKS_ERROR,
+                       UDISKS_ERROR_DEVICE_BUSY,
+                       "Encrypted device %s is unlocked",
+                       udisks_block_get_preferred_device (block));
+          ret = FALSE;
+          goto out;
+        }
+    }
+
+ out:
+  g_list_free_full (objects, g_object_unref);
+  return ret;
+}
index 193feb4..2017c8c 100644 (file)
@@ -48,6 +48,10 @@ gboolean                udisks_linux_drive_object_housekeeping  (UDisksLinuxDriv
                                                                  GCancellable             *cancellable,
                                                                  GError                  **error);
 
+gboolean                udisks_linux_drive_object_is_not_in_use (UDisksLinuxDriveObject   *object,
+                                                                 GCancellable             *cancellable,
+                                                                 GError                  **error);
+
 gboolean                udisks_linux_drive_object_should_include_device (GUdevClient  *client,
                                                                          GUdevDevice  *device,
                                                                          gchar       **out_vpd);