Avoid multiple Drive objects with the same VPD
authorDavid Zeuthen <davidz@redhat.com>
Wed, 16 Feb 2011 18:46:07 +0000 (13:46 -0500)
committerDavid Zeuthen <davidz@redhat.com>
Wed, 16 Feb 2011 18:46:07 +0000 (13:46 -0500)
So the way this works is that with multiple paths, more than one block
device will point to the Drive in question.

$ ./udisksctl status
PORT  MODEL                     REVISION  SERIAL               BLOCK
--------------------------------------------------------------------------------
      HP LOGICAL VOLUME         3.00      5001438008F61790     sda sda1 sda2
      SEAGATE ST3300657SS       0006      3SJ1QNMQ00009052NECM sdan sdl
      SEAGATE ST3300657SS       0006      3SJ1RFP900009051HZ5S sdaf sdd
      SEAGATE ST3300657SS       0006      3SJ1RJS700009050VYEA sdal sdj
      SEAGATE ST3300657SS       0006      3SJ1RPFJ00009052BZ10 sdah sdf
      SEAGATE ST3300657SS       0006      3SJ1RSJC00009052MWTB sdam sdk
      SEAGATE ST3300657SS       0006      3SJ1RWZ100009101T12H sdai sdg
      SEAGATE ST3300657SS       0006      3SJ1S3NE00009101TB5Y sdao sdm
      SEAGATE ST3300657SS       0006      3SJ1S40K00009101T1XU sdak sdi
      SEAGATE ST3300657SS       0006      3SJ1S4MH00009052RG6Z sdae sdc
      SEAGATE ST3300657SS       0006      3SJ1S66L00009052QGZA sdag sde
      SEAGATE ST3300657SS       0006      3SJ1S7C500009052RKPP sdaj sdh
      SEAGATE ST3300657SS       0006      3SJ1S7K600009051M0CE sdad sdb
      WDC WD1002FAEX-00Y9A0     01.01V10  WD-WCAW30010853      sds
      WDC WD1002FAEX-00Y9A0     01.01V10  WD-WCAW30012560      sdn
      WDC WD1002FAEX-00Y9A0     01.01V10  WD-WCAW30014347      sdac
      WDC WD1002FAEX-00Y9A0     01.01V10  WD-WCAW30021345      sdaa
      WDC WD1002FAEX-00Y9A0     01.01V10  WD-WCAW30024753      sdt
      WDC WD1002FAEX-00Y9A0     01.01V10  WD-WCAW30025874      sdw
      WDC WD1002FAEX-00Y9A0     01.01V10  WD-WCAW30030732      sdr
      WDC WD1002FAEX-00Y9A0     01.01V10  WD-WCAW30031826      sdv
      WDC WD1002FAEX-00Y9A0     01.01V10  WD-WCAW30032003      sdz
      WDC WD1002FAEX-00Y9A0     01.01V10  WD-WCAW30039741      sdp
      WDC WD1002FAEX-00Y9A0     01.01V10  WD-WCAW30063724      sdx
      WDC WD1002FAEX-00Y9A0     01.01V10  WD-WCAW30064055      sdq
      WDC WD1002FAEX-00Y9A0     01.01V10  WD-WCAW30064486      sdy
      WDC WD1002FAEX-00Y9A0     01.01V10  WD-WCAW30064749      sdo
      WDC WD1002FAEX-00Y9A0     01.01V10  WD-WCAW30065104      sdu
      WDC WD1002FAEX-00Y9A0     01.01V10  WD-WCAW30067287      sdab

Still need to hook up support for dm-multipath such that we get e.g.

      SEAGATE ST3300657SS       0006      3SJ1QNMQ00009052NECM sdan sdl mpatha

Signed-off-by: David Zeuthen <davidz@redhat.com>
src/udiskslinuxblock.c
src/udiskslinuxdrive.c
src/udiskslinuxdrive.h
src/udiskslinuxprovider.c
tools/udisksctl.c

index 207dbd0..f94b395 100644 (file)
@@ -374,24 +374,31 @@ find_drive (GDBusObjectManager *object_manager,
     {
       GDBusObject *object = G_DBUS_OBJECT (l->data);
       UDisksLinuxDrive *drive;
-      GUdevDevice *drive_device;
-      const gchar *drive_sysfs_path;
+      GList *drive_devices;
+      GList *j;
 
       if (!UDISKS_IS_LINUX_DRIVE (object))
         continue;
 
       drive = UDISKS_LINUX_DRIVE (object);
-      drive_device = udisks_linux_drive_get_device (drive);
+      drive_devices = udisks_linux_drive_get_devices (drive);
 
-      drive_sysfs_path = g_udev_device_get_sysfs_path (drive_device);
-
-      if (g_str_has_prefix (block_device_sysfs_path, drive_sysfs_path))
+      for (j = drive_devices; j != NULL; j = j->next)
         {
-          ret = g_dbus_object_get_object_path (object);
-          g_object_unref (drive_device);
-          goto out;
+          GUdevDevice *drive_device = G_UDEV_DEVICE (j->data);
+          const gchar *drive_sysfs_path;
+
+          drive_sysfs_path = g_udev_device_get_sysfs_path (drive_device);
+          if (g_str_has_prefix (block_device_sysfs_path, drive_sysfs_path))
+            {
+              ret = g_dbus_object_get_object_path (object);
+              g_list_foreach (drive_devices, (GFunc) g_object_unref, NULL);
+              g_list_free (drive_devices);
+              goto out;
+            }
         }
-      g_object_unref (drive_device);
+      g_list_foreach (drive_devices, (GFunc) g_object_unref, NULL);
+      g_list_free (drive_devices);
     }
 
  out:
index 5712a95..4263cbb 100644 (file)
@@ -48,7 +48,8 @@ struct _UDisksLinuxDrive
 
   UDisksDaemon *daemon;
 
-  GUdevDevice *device;
+  /* list of GUdevDevice objects for scsi_device objects */
+  GList *devices;
 
   /* interfaces */
   UDisksDrive *iface_drive;
@@ -77,7 +78,8 @@ udisks_linux_drive_finalize (GObject *object)
 
   /* note: we don't hold a ref to drive->daemon or drive->mount_monitor */
 
-  g_object_unref (drive->device);
+  g_list_foreach (drive->devices, (GFunc) g_object_unref, NULL);
+  g_list_free (drive->devices);
 
   if (drive->iface_drive != NULL)
     g_object_unref (drive->iface_drive);
@@ -100,10 +102,6 @@ udisks_linux_drive_get_property (GObject    *object,
       g_value_set_object (value, udisks_linux_drive_get_daemon (drive));
       break;
 
-    case PROP_DEVICE:
-      g_value_set_object (value, udisks_linux_drive_get_device (drive));
-      break;
-
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -127,8 +125,8 @@ udisks_linux_drive_set_property (GObject      *object,
       break;
 
     case PROP_DEVICE:
-      g_assert (drive->device == NULL);
-      drive->device = g_value_dup_object (value);
+      g_assert (drive->devices == NULL);
+      drive->devices = g_list_prepend (NULL, g_value_dup_object (value));
       break;
 
     default:
@@ -212,7 +210,7 @@ udisks_linux_drive_constructed (GObject *object)
   GString *str;
 
   /* initial coldplug */
-  udisks_linux_drive_uevent (drive, "add", NULL);
+  udisks_linux_drive_uevent (drive, "add", drive->devices->data);
 
   /* compute the object path */
   vendor = g_strdup (udisks_drive_get_vendor (drive->iface_drive));
@@ -296,7 +294,6 @@ udisks_linux_drive_class_init (UDisksLinuxDriveClass *klass)
                                                         "Device",
                                                         "The device for the object",
                                                         G_UDEV_TYPE_DEVICE,
-                                                        G_PARAM_READABLE |
                                                         G_PARAM_WRITABLE |
                                                         G_PARAM_CONSTRUCT_ONLY |
                                                         G_PARAM_STATIC_STRINGS));
@@ -348,20 +345,22 @@ udisks_linux_drive_get_daemon (UDisksLinuxDrive *drive)
 }
 
 /**
- * udisks_linux_drive_get_device:
+ * udisks_linux_drive_get_devices:
  * @drive: A #UDisksLinuxDrive.
  *
- * Gets the current #GUdevDevice for @drive. Connect to
- * #GObject::notify to track changes to the #UDisksLinuxDrive:device
- * property.
+ * Gets the current #GUdevDevice objects associated with @drive.
  *
- * Returns: A #GUdevDevice. Free with g_object_unref().
+ * Returns: A list of #GUdevDevice objects. Free each element with
+ * g_object_unref(), then free the list with g_list_free().
  */
-GUdevDevice *
-udisks_linux_drive_get_device (UDisksLinuxDrive *drive)
+GList *
+udisks_linux_drive_get_devices (UDisksLinuxDrive *drive)
 {
+  GList *ret;
   g_return_val_if_fail (UDISKS_IS_LINUX_DRIVE (drive), NULL);
-  return g_object_ref (drive->device);
+  ret = g_list_copy (drive->devices);
+  g_list_foreach (ret, (GFunc) g_object_ref, NULL);
+  return ret;
 }
 
 /* ---------------------------------------------------------------------------------------------------- */
@@ -420,9 +419,6 @@ update_iface (UDisksLinuxDrive           *drive,
 }
 
 /* ---------------------------------------------------------------------------------------------------- */
-
-
-/* ---------------------------------------------------------------------------------------------------- */
 /* org.freedesktop.UDisks.Drive */
 
 static gboolean
@@ -437,15 +433,21 @@ drive_update (UDisksLinuxDrive      *drive,
               GDBusInterface        *_iface)
 {
   UDisksDrive *iface = UDISKS_DRIVE (_iface);
+  GUdevDevice *device;
+
+  if (drive->devices == NULL)
+    goto out;
+
+  device = G_UDEV_DEVICE (drive->devices->data);
 
   /* this is the _almost_ the same for both ATA and SCSI devices (cf. udev's ata_id and scsi_id)
    * but we special case since there are subtle differences...
    */
-  if (g_udev_device_get_property_as_boolean (drive->device, "ID_ATA"))
+  if (g_udev_device_get_property_as_boolean (device, "ID_ATA"))
     {
       const gchar *model;
 
-      model = g_udev_device_get_property (drive->device, "ID_MODEL_ENC");
+      model = g_udev_device_get_property (device, "ID_MODEL_ENC");
       if (model != NULL)
         {
           gchar *s;
@@ -455,17 +457,17 @@ drive_update (UDisksLinuxDrive      *drive,
           g_free (s);
         }
 
-      udisks_drive_set_vendor (iface, g_udev_device_get_property (drive->device, ""));
-      udisks_drive_set_revision (iface, g_udev_device_get_property (drive->device, "ID_REVISION"));
-      udisks_drive_set_serial (iface, g_udev_device_get_property (drive->device, "ID_SERIAL_SHORT"));
-      udisks_drive_set_wwn (iface, g_udev_device_get_property (drive->device, "ID_WWN_WITH_EXTENSION"));
+      udisks_drive_set_vendor (iface, g_udev_device_get_property (device, ""));
+      udisks_drive_set_revision (iface, g_udev_device_get_property (device, "ID_REVISION"));
+      udisks_drive_set_serial (iface, g_udev_device_get_property (device, "ID_SERIAL_SHORT"));
+      udisks_drive_set_wwn (iface, g_udev_device_get_property (device, "ID_WWN_WITH_EXTENSION"));
     }
-  else if (g_udev_device_get_property_as_boolean (drive->device, "ID_SCSI"))
+  else if (g_udev_device_get_property_as_boolean (device, "ID_SCSI"))
     {
       const gchar *vendor;
       const gchar *model;
 
-      vendor = g_udev_device_get_property (drive->device, "ID_VENDOR_ENC");
+      vendor = g_udev_device_get_property (device, "ID_VENDOR_ENC");
       if (vendor != NULL)
         {
           gchar *s;
@@ -475,7 +477,7 @@ drive_update (UDisksLinuxDrive      *drive,
           g_free (s);
         }
 
-      model = g_udev_device_get_property (drive->device, "ID_MODEL_ENC");
+      model = g_udev_device_get_property (device, "ID_MODEL_ENC");
       if (model != NULL)
         {
           gchar *s;
@@ -485,23 +487,46 @@ drive_update (UDisksLinuxDrive      *drive,
           g_free (s);
         }
 
-      udisks_drive_set_revision (iface, g_udev_device_get_property (drive->device, "ID_REVISION"));
-      udisks_drive_set_serial (iface, g_udev_device_get_property (drive->device, "ID_SCSI_SERIAL"));
-      udisks_drive_set_wwn (iface, g_udev_device_get_property (drive->device, "ID_WWN_WITH_EXTENSION"));
+      udisks_drive_set_revision (iface, g_udev_device_get_property (device, "ID_REVISION"));
+      udisks_drive_set_serial (iface, g_udev_device_get_property (device, "ID_SCSI_SERIAL"));
+      udisks_drive_set_wwn (iface, g_udev_device_get_property (device, "ID_WWN_WITH_EXTENSION"));
     }
   else
     {
       /* generic fallback... */
-      udisks_drive_set_vendor (iface, g_udev_device_get_property (drive->device, "ID_VENDOR"));
-      udisks_drive_set_model (iface, g_udev_device_get_property (drive->device, "ID_MODEL"));
-      udisks_drive_set_revision (iface, g_udev_device_get_property (drive->device, "ID_REVISION"));
-      udisks_drive_set_serial (iface, g_udev_device_get_property (drive->device, "ID_SERIAL_SHORT"));
-      udisks_drive_set_wwn (iface, g_udev_device_get_property (drive->device, "ID_WWN_WITH_EXTENSION"));
+      udisks_drive_set_vendor (iface, g_udev_device_get_property (device, "ID_VENDOR"));
+      udisks_drive_set_model (iface, g_udev_device_get_property (device, "ID_MODEL"));
+      udisks_drive_set_revision (iface, g_udev_device_get_property (device, "ID_REVISION"));
+      udisks_drive_set_serial (iface, g_udev_device_get_property (device, "ID_SERIAL_SHORT"));
+      udisks_drive_set_wwn (iface, g_udev_device_get_property (device, "ID_WWN_WITH_EXTENSION"));
     }
+
+ out:
+  ;
 }
 
 /* ---------------------------------------------------------------------------------------------------- */
 
+static GList *
+find_link_for_sysfs_path (UDisksLinuxDrive *drive,
+                    const gchar *sysfs_path)
+{
+  GList *l;
+  GList *ret;
+  ret = NULL;
+  for (l = drive->devices; l != NULL; l = l->next)
+    {
+      GUdevDevice *device = G_UDEV_DEVICE (l->data);
+      if (g_strcmp0 (g_udev_device_get_sysfs_path (device), sysfs_path) == 0)
+        {
+          ret = l;
+          goto out;
+        }
+    }
+ out:
+  return ret;
+}
+
 /**
  * udisks_linux_drive_uevent:
  * @drive: A #UDisksLinuxDrive.
@@ -515,14 +540,38 @@ udisks_linux_drive_uevent (UDisksLinuxDrive *drive,
                            const gchar      *action,
                            GUdevDevice      *device)
 {
+  GList *link;
+
   g_return_if_fail (UDISKS_IS_LINUX_DRIVE (drive));
-  g_return_if_fail (device == NULL || G_UDEV_IS_DEVICE (device));
+  g_return_if_fail (G_UDEV_IS_DEVICE (device));
 
-  if (device != NULL)
+  link = find_link_for_sysfs_path (drive, g_udev_device_get_sysfs_path (device));
+  if (g_strcmp0 (action, "remove") == 0)
+    {
+      if (link != NULL)
+        {
+          g_object_unref (G_UDEV_DEVICE (link->data));
+          drive->devices = g_list_delete_link (drive->devices, link);
+        }
+      else
+        {
+          udisks_daemon_log (drive->daemon,
+                             UDISKS_LOG_LEVEL_WARNING,
+                             "Drive doesn't have device with sysfs path %s on remove event",
+                             g_udev_device_get_sysfs_path (device));
+        }
+    }
+  else
     {
-      g_object_unref (drive->device);
-      drive->device = g_object_ref (device);
-      g_object_notify (G_OBJECT (drive), "device");
+      if (link != NULL)
+        {
+          g_object_unref (G_UDEV_DEVICE (link->data));
+          link->data = g_object_ref (device);
+        }
+      else
+        {
+          drive->devices = g_list_append (drive->devices, g_object_ref (device));
+        }
     }
 
   update_iface (drive, action, drive_check, drive_update,
index efbdb30..ae6d022 100644 (file)
@@ -30,14 +30,14 @@ G_BEGIN_DECLS
 #define UDISKS_LINUX_DRIVE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), UDISKS_TYPE_LINUX_DRIVE, UDisksLinuxDrive))
 #define UDISKS_IS_LINUX_DRIVE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), UDISKS_TYPE_LINUX_DRIVE))
 
-GType               udisks_linux_drive_get_type   (void) G_GNUC_CONST;
-UDisksLinuxDrive   *udisks_linux_drive_new        (UDisksDaemon     *daemon,
-                                                   GUdevDevice      *device);
-void                udisks_linux_drive_uevent     (UDisksLinuxDrive *drive,
-                                                   const gchar      *action,
-                                                   GUdevDevice      *device);
-UDisksDaemon       *udisks_linux_drive_get_daemon (UDisksLinuxDrive *drive);
-GUdevDevice        *udisks_linux_drive_get_device (UDisksLinuxDrive *drive);
+GType               udisks_linux_drive_get_type    (void) G_GNUC_CONST;
+UDisksLinuxDrive   *udisks_linux_drive_new         (UDisksDaemon     *daemon,
+                                                    GUdevDevice      *device);
+void                udisks_linux_drive_uevent      (UDisksLinuxDrive *drive,
+                                                    const gchar      *action,
+                                                    GUdevDevice      *device);
+UDisksDaemon       *udisks_linux_drive_get_daemon  (UDisksLinuxDrive *drive);
+GList              *udisks_linux_drive_get_devices (UDisksLinuxDrive *drive);
 
 G_END_DECLS
 
index a536e90..a00dea6 100644 (file)
@@ -20,6 +20,8 @@
 
 #include "config.h"
 
+#include <string.h>
+
 #include "udisksdaemon.h"
 #include "udisksprovider.h"
 #include "udiskslinuxprovider.h"
@@ -52,8 +54,8 @@ struct _UDisksLinuxProvider
   /* maps from sysfs path to UDisksLinuxBlock objects */
   GHashTable *sysfs_to_block;
 
-  /* maps from sysfs path to UDisksLinuxDrive objects */
-  GHashTable *sysfs_to_drive;
+  /* maps from VPD (serial, wwn) to UDisksLinuxDrive objects */
+  GHashTable *vpd_to_drive;
 
   /* maps from sysfs path to UDisksLinuxController objects */
   GHashTable *sysfs_to_controller;
@@ -77,7 +79,7 @@ udisks_linux_provider_finalize (GObject *object)
   UDisksLinuxProvider *provider = UDISKS_LINUX_PROVIDER (object);
 
   g_hash_table_unref (provider->sysfs_to_block);
-  g_hash_table_unref (provider->sysfs_to_drive);
+  g_hash_table_unref (provider->vpd_to_drive);
   g_hash_table_unref (provider->sysfs_to_controller);
   g_object_unref (provider->gudev_client);
 
@@ -97,7 +99,7 @@ on_uevent (GUdevClient  *client,
            gpointer      user_data)
 {
   UDisksLinuxProvider *provider = UDISKS_LINUX_PROVIDER (user_data);
-  g_print ("%s:%s: entering\n", G_STRLOC, G_STRFUNC);
+  //g_print ("%s:%s: entering\n", G_STRLOC, G_STRFUNC);
   udisks_linux_provider_handle_uevent (provider, action, device);
 }
 
@@ -120,10 +122,10 @@ udisks_linux_provider_constructed (GObject *object)
                                                     g_str_equal,
                                                     g_free,
                                                     (GDestroyNotify) g_object_unref);
-  provider->sysfs_to_drive = g_hash_table_new_full (g_str_hash,
-                                                    g_str_equal,
-                                                    g_free,
-                                                    (GDestroyNotify) g_object_unref);
+  provider->vpd_to_drive = g_hash_table_new_full (g_str_hash,
+                                                  g_str_equal,
+                                                  g_free,
+                                                  (GDestroyNotify) g_object_unref);
   provider->sysfs_to_controller = g_hash_table_new_full (g_str_hash,
                                                          g_str_equal,
                                                          g_free,
@@ -239,29 +241,62 @@ handle_scsi_uevent (UDisksLinuxProvider *provider,
                     const gchar         *action,
                     GUdevDevice         *device)
 {
-  const gchar *sysfs_path;
   UDisksLinuxDrive *drive;
   UDisksDaemon *daemon;
+  const gchar *sysfs_path;
+  const gchar *serial;
+  const gchar *wwn;
+  const gchar *vpd;
 
   daemon = udisks_provider_get_daemon (UDISKS_PROVIDER (provider));
   sysfs_path = g_udev_device_get_sysfs_path (device);
 
+  /* prefer WWN to serial */
+  serial = g_udev_device_get_property (device, "ID_SERIAL");
+  wwn = g_udev_device_get_property (device, "ID_WWN_WITH_EXTENSION");
+  if (wwn != NULL && strlen (wwn) > 0)
+    {
+      vpd = wwn;
+    }
+  else if (serial != NULL && strlen (serial) > 0)
+    {
+      vpd = serial;
+    }
+  else
+    {
+      udisks_daemon_log (daemon,
+                         UDISKS_LOG_LEVEL_WARNING,
+                         "Ignoring scsi_device %s with no serial or WWN",
+                         g_udev_device_get_sysfs_path (device));
+      goto out;
+    }
+
   if (g_strcmp0 (action, "remove") == 0)
     {
-      drive = g_hash_table_lookup (provider->sysfs_to_drive, sysfs_path);
+      drive = g_hash_table_lookup (provider->vpd_to_drive, vpd);
       if (drive != NULL)
         {
-          gchar *object_path;
-          object_path = g_dbus_object_get_object_path (G_DBUS_OBJECT (drive));
-          g_dbus_object_manager_unexport (udisks_daemon_get_object_manager (daemon),
-                                          object_path);
-          g_free (object_path);
-          g_warn_if_fail (g_hash_table_remove (provider->sysfs_to_drive, sysfs_path));
+          GList *devices;
+
+          udisks_linux_drive_uevent (drive, action, device);
+
+          devices = udisks_linux_drive_get_devices (drive);
+          if (devices == NULL)
+            {
+              gchar *object_path;
+              object_path = g_dbus_object_get_object_path (G_DBUS_OBJECT (drive));
+              g_dbus_object_manager_unexport (udisks_daemon_get_object_manager (daemon),
+                                              object_path);
+              g_free (object_path);
+              g_warn_if_fail (g_hash_table_remove (provider->vpd_to_drive, vpd));
+            }
+          g_list_foreach (devices, (GFunc) g_object_unref, NULL);
+          g_list_free (devices);
         }
     }
   else
     {
-      drive = g_hash_table_lookup (provider->sysfs_to_drive, sysfs_path);
+      drive = g_hash_table_lookup (provider->vpd_to_drive, vpd);
       if (drive != NULL)
         {
           udisks_linux_drive_uevent (drive, action, device);
@@ -273,10 +308,13 @@ handle_scsi_uevent (UDisksLinuxProvider *provider,
             {
               g_dbus_object_manager_export_and_uniquify (udisks_daemon_get_object_manager (daemon),
                                                          G_DBUS_OBJECT (drive));
-              g_hash_table_insert (provider->sysfs_to_drive, g_strdup (sysfs_path), drive);
+              g_hash_table_insert (provider->vpd_to_drive, g_strdup (vpd), drive);
             }
         }
     }
+
+ out:
+  ;
 }
 
 static void
index 0b7eb21..c1a859e 100644 (file)
@@ -1431,12 +1431,11 @@ handle_command_monitor (gint        *argc,
 
 /* ---------------------------------------------------------------------------------------------------- */
 
-/* built-in assumption: there is only one block device per drive */
-static UDisksBlockDevice *
-find_block_for_drive (GList       *object_proxies,
-                      const gchar *drive_object_path)
+static GList *
+find_block_devices_for_drive (GList       *object_proxies,
+                              const gchar *drive_object_path)
 {
-  UDisksBlockDevice *ret;
+  GList *ret;
   GList *l;
 
   ret = NULL;
@@ -1451,12 +1450,10 @@ find_block_for_drive (GList       *object_proxies,
 
       if (g_strcmp0 (udisks_block_device_get_drive (block), drive_object_path) == 0)
         {
-          ret = block;
-          goto out;
+          ret = g_list_append (ret, g_object_ref (block));
         }
       g_object_unref (block);
     }
- out:
   return ret;
 }
 
@@ -1543,39 +1540,50 @@ handle_command_status (gint        *argc,
    *  - revision  <= 8    (SCSI: 6, ATA: 8)
    *  - serial    <= 20   (SCSI: 16, ATA: 20)
    */
-  g_print ("LOCATION      MODEL                     REVISION  SERIAL               BLOCK\n"
+  g_print ("PORT  MODEL                     REVISION  SERIAL               BLOCK\n"
            "--------------------------------------------------------------------------------\n");
-         /*               SEAGATE ST3300657SS       0006      3SJ1QNMQ00009052NECM sdaa     */
+         /*       SEAGATE ST3300657SS       0006      3SJ1QNMQ00009052NECM sdaa sdab dm-32   */
          /* 01234567890123456789012345678901234567890123456789012345678901234567890123456789 */
 
   /* TODO: sort */
-  //object_proxies = g_list_sort (object_proxies, (GCompareFunc) obj_proxy_cmp_ctds);
+  object_proxies = g_list_sort (object_proxies, (GCompareFunc) obj_proxy_cmp);
   for (l = object_proxies; l != NULL; l = l->next)
     {
       GDBusObjectProxy *object_proxy = G_DBUS_OBJECT_PROXY (l->data);
       UDisksDrive *drive;
-      UDisksBlockDevice *block;
-      const gchar *block_device;
+      GList *block_devices;
       const gchar *vendor;
       const gchar *model;
       const gchar *revision;
       const gchar *serial;
       gchar *vendor_model;
+      GString *str;
+      gchar *block_device;
+      GList *j;
 
       drive = UDISKS_PEEK_DRIVE (object_proxy);
       if (drive == NULL)
         continue;
 
-      block = find_block_for_drive (object_proxies, g_dbus_object_proxy_get_object_path (object_proxy));
-      if (block != NULL)
-        {
-          block_device = udisks_block_device_get_device (block);
-          g_object_unref (block);
-        }
-      else
+      str = g_string_new (NULL);
+      block_devices = find_block_devices_for_drive (object_proxies, g_dbus_object_proxy_get_object_path (object_proxy));
+      for (j = block_devices; j != NULL; j = j->next)
         {
-          block_device = "-";
+          UDisksBlockDevice *block = UDISKS_BLOCK_DEVICE (j->data);
+          const gchar *device_file;
+          if (str->len > 0)
+            g_string_append (str, " ");
+          device_file = udisks_block_device_get_device (block);
+          if (g_str_has_prefix (device_file, "/dev/"))
+            g_string_append (str, device_file + 5);
+          else
+            g_string_append (str, device_file);
         }
+      if (str->len == 0)
+        g_string_append (str, "-");
+      block_device = g_string_free (str, FALSE);
+      g_list_foreach (block_devices, (GFunc) g_object_unref, NULL);
+      g_list_free (block_devices);
 
       vendor = udisks_drive_get_vendor (drive);
       model = udisks_drive_get_model (drive);
@@ -1596,12 +1604,13 @@ handle_command_status (gint        *argc,
         vendor_model = g_strdup ("-");
 
       /* TODO: need to figure out LOCATION */
-      g_print ("%-13s %-25s %-9s %-20s %-8s\n",
+      g_print ("%-5s %-25s %-9s %-20s %-8s\n",
                "",
                vendor_model,
                revision,
                serial,
                block_device);
+      g_free (block_device);
     }