First cut at device-mapper multipath support
authorDavid Zeuthen <davidz@redhat.com>
Fri, 22 Jan 2010 18:29:09 +0000 (13:29 -0500)
committerDavid Zeuthen <davidz@redhat.com>
Fri, 22 Jan 2010 18:29:09 +0000 (13:29 -0500)
data/org.freedesktop.UDisks.Device.xml
src/device-private.c
src/device-private.h
src/device.c
src/helpers/job-create-partition-table.c
src/helpers/job-create-partition.c
src/helpers/job-delete-partition.c
src/probers/part-id.c
src/probers/udisks-dm-export.c
tools/udisks.c

index 7114b86..84d38d2 100644 (file)
             UNIX special device file for device. Example: <doc:tt>/dev/sda</doc:tt>.
       </doc:para></doc:description></doc:doc>
     </property>
+    <property name="DeviceFilePresentation" type="s" access="read">
+      <doc:doc><doc:description><doc:para>
+            If not blank, the preferred device file (typically a symlink to the value of the <doc:ref type="property" to="Device:DeviceFile">DeviceFile</doc:ref> property) to present in user interface.
+            Example: <doc:tt>/dev/mapper/mpathb</doc:tt> or
+            <doc:tt>/dev/vg_phobos/lv_root</doc:tt>.
+      </doc:para></doc:description></doc:doc>
+    </property>
     <property name="DeviceFileById" type="as" access="read">
       <doc:doc><doc:description><doc:para>
             Symlinks to UNIX special device file that are stable and uniquely identifies the device.
             TRUE if the device is a Linux LVM2 physical. See LinuxLvm2PV properties for details.
       </doc:para></doc:description></doc:doc>
     </property>
+    <property name="DeviceIsLinuxDmmpComponent" type="b" access="read">
+      <doc:doc><doc:description><doc:para>
+            TRUE if the device is a component (e.g. active path) of a Linux dm-multipath device.
+      </doc:para></doc:description></doc:doc>
+    </property>
+    <property name="DeviceIsLinuxDmmp" type="b" access="read">
+      <doc:doc><doc:description><doc:para>
+            TRUE if the device is a Linux dm-multipath device.
+      </doc:para></doc:description></doc:doc>
+    </property>
     <property name="DeviceSize" type="t" access="read">
       <doc:doc><doc:description><doc:para>
             The size of the device in bytes.
       </doc:para></doc:description></doc:doc>
     </property>
 
+    <!-- ******************** -->
+    <!-- Multi-path Component -->
+    <!-- ******************** -->
+
+    <property name="LinuxDmmpComponentHolder" type="o" access="read">
+      <doc:doc><doc:description><doc:para>
+            The object path of the multi-path device the component is currently part of.
+            This property is only valid if
+            <doc:ref type="property" to="Device:DeviceIsLinuxDmmpComponent">DeviceIsLinuxDmmpComponent</doc:ref>
+            is TRUE.
+      </doc:para></doc:description></doc:doc>
+    </property>
+
+    <!-- ***************** -->
+    <!-- Multi-path Device -->
+    <!-- ***************** -->
+
+    <property name="LinuxDmmpName" type="s" access="read">
+      <doc:doc><doc:description><doc:para>
+            The symbolic name for the multipath device, e.g. <doc:tt>mpathb</doc:tt>.
+            This property is only valid if
+            <doc:ref type="property" to="Device:DeviceIsLinuxDmmp">DeviceIsLinuxDmmp</doc:ref>
+            is TRUE.
+      </doc:para></doc:description></doc:doc>
+    </property>
+    <property name="LinuxDmmpSlaves" type="ao" access="read">
+      <doc:doc><doc:description><doc:para>
+            The object paths of currently active component devices, e.g. paths.
+            This property is only valid if
+            <doc:ref type="property" to="Device:DeviceIsLinuxDmmp">DeviceIsLinuxDmmp</doc:ref>
+            is TRUE.
+      </doc:para></doc:description></doc:doc>
+    </property>
+
   </interface>
 
 </node>
index 4d3a7db..7d53ca2 100644 (file)
@@ -199,6 +199,18 @@ device_set_device_file (Device *device,
 }
 
 void
+device_set_device_file_presentation (Device *device,
+                                     const gchar *value)
+{
+  if (G_UNLIKELY (g_strcmp0 (device->priv->device_file_presentation, value) != 0))
+    {
+      g_free (device->priv->device_file_presentation);
+      device->priv->device_file_presentation = g_strdup (value);
+      emit_changed (device, "device_file_presentation");
+    }
+}
+
+void
 device_set_device_file_by_id (Device *device,
                               GStrv value)
 {
@@ -419,6 +431,26 @@ device_set_device_is_linux_lvm2_pv (Device *device, gboolean value)
 }
 
 void
+device_set_device_is_linux_dmmp (Device *device, gboolean value)
+{
+  if (G_UNLIKELY (device->priv->device_is_linux_dmmp != value))
+    {
+      device->priv->device_is_linux_dmmp = value;
+      emit_changed (device, "device_is_linux_dmmp");
+    }
+}
+
+void
+device_set_device_is_linux_dmmp_component (Device *device, gboolean value)
+{
+  if (G_UNLIKELY (device->priv->device_is_linux_dmmp_component != value))
+    {
+      device->priv->device_is_linux_dmmp_component = value;
+      emit_changed (device, "device_is_linux_dmmp_component");
+    }
+}
+
+void
 device_set_device_size (Device *device,
                         guint64 value)
 {
@@ -1501,3 +1533,40 @@ device_set_linux_lvm2_pv_group_logical_volumes (Device *device,
       emit_changed (device, "linux_lvm2_pv_group_logical_volumes");
     }
 }
+
+
+void
+device_set_linux_dmmp_component_holder (Device *device,
+                                        const gchar *value)
+{
+  if (G_UNLIKELY (g_strcmp0 (device->priv->linux_dmmp_component_holder, value) != 0))
+    {
+      g_free (device->priv->linux_dmmp_component_holder);
+      device->priv->linux_dmmp_component_holder = g_strdup (value);
+      emit_changed (device, "linux_dmmp_component_holder");
+    }
+}
+
+void
+device_set_linux_dmmp_name (Device *device,
+                            const gchar *value)
+{
+  if (G_UNLIKELY (g_strcmp0 (device->priv->linux_dmmp_name, value) != 0))
+    {
+      g_free (device->priv->linux_dmmp_name);
+      device->priv->linux_dmmp_name = g_strdup (value);
+      emit_changed (device, "linux_dmmp_name");
+    }
+}
+
+void
+device_set_linux_dmmp_slaves (Device *device,
+                              GStrv value)
+{
+  if (G_UNLIKELY (!ptr_str_array_equals_strv (device->priv->linux_dmmp_slaves, value)))
+    {
+      ptr_str_array_free (device->priv->linux_dmmp_slaves);
+      device->priv->linux_dmmp_slaves = ptr_str_array_from_strv (value);
+      emit_changed (device, "linux_dmmp_slaves");
+    }
+}
index 695acb4..1394ab8 100644 (file)
@@ -96,6 +96,7 @@ struct DevicePrivate
   /**************/
 
   char *device_file;
+  char *device_file_presentation;
   dev_t dev;
   GPtrArray *device_file_by_id;
   GPtrArray *device_file_by_path;
@@ -117,6 +118,8 @@ struct DevicePrivate
   gboolean device_is_linux_md;
   gboolean device_is_linux_lvm2_lv;
   gboolean device_is_linux_lvm2_pv;
+  gboolean device_is_linux_dmmp;
+  gboolean device_is_linux_dmmp_component;
   guint64 device_size;
   guint64 device_block_size;
   gboolean device_is_mounted;
@@ -222,6 +225,11 @@ struct DevicePrivate
   void *drive_ata_smart_blob;
   gsize drive_ata_smart_blob_size;
 
+  gchar *linux_dmmp_component_holder;
+
+  gchar *linux_dmmp_name;
+  GPtrArray *linux_dmmp_slaves;
+
   /* the following properties are not (yet) exported */
   char *dm_name;
   GPtrArray *slaves_objpath;
@@ -239,6 +247,7 @@ void device_set_job_percentage (Device *device, gdouble value);
 void device_set_device_detection_time (Device *device, guint64 value);
 void device_set_device_media_detection_time (Device *device, guint64 value);
 void device_set_device_file (Device *device, const gchar *value);
+void device_set_device_file_presentation (Device *device, const gchar *value);
 void device_set_device_file_by_id (Device *device, GStrv value);
 void device_set_device_file_by_path (Device *device, GStrv value);
 void device_set_device_is_system_internal (Device *device, gboolean value);
@@ -259,6 +268,8 @@ void device_set_device_is_linux_md_component (Device *device, gboolean value);
 void device_set_device_is_linux_md (Device *device, gboolean value);
 void device_set_device_is_linux_lvm2_lv (Device *device, gboolean value);
 void device_set_device_is_linux_lvm2_pv (Device *device, gboolean value);
+void device_set_device_is_linux_dmmp (Device *device, gboolean value);
+void device_set_device_is_linux_dmmp_component (Device *device, gboolean value);
 void device_set_device_size (Device *device, guint64 value);
 void device_set_device_block_size (Device *device, guint64 value);
 void device_set_device_is_mounted (Device *device, gboolean value);
@@ -357,6 +368,11 @@ void device_set_linux_lvm2_pv_group_extent_size (Device *device, guint64 value);
 void device_set_linux_lvm2_pv_group_physical_volumes (Device *device, GStrv value);
 void device_set_linux_lvm2_pv_group_logical_volumes (Device *device, GStrv value);
 
+void device_set_linux_dmmp_component_holder (Device *device, const gchar *value);
+
+void device_set_linux_dmmp_name (Device *device, const gchar *value);
+void device_set_linux_dmmp_slaves (Device *device, GStrv value);
+
 void device_set_dm_name (Device *device, const gchar *value);
 void device_set_slaves_objpath (Device *device, GStrv value);
 void device_set_holders_objpath (Device *device, GStrv value);
index 3ed95fe..83f88e3 100644 (file)
@@ -135,6 +135,17 @@ static void force_luks_teardown (Device *device,
                                  ForceRemovalCompleteFunc callback,
                                  gpointer user_data);
 
+/* TODO: this is kinda a hack */
+static const gchar *
+get_dmmp_device_node (Device *device)
+{
+  static gchar buf[1024];
+
+  g_assert (device->priv->device_is_linux_dmmp);
+  g_snprintf (buf, sizeof (buf), "/dev/mapper/%s", device->priv->linux_dmmp_name);
+  return buf;
+}
+
 enum
   {
     PROP_0,
@@ -145,6 +156,7 @@ enum
     PROP_DEVICE_MAJOR,
     PROP_DEVICE_MINOR,
     PROP_DEVICE_FILE,
+    PROP_DEVICE_FILE_PRESENTATION,
     PROP_DEVICE_FILE_BY_ID,
     PROP_DEVICE_FILE_BY_PATH,
     PROP_DEVICE_IS_SYSTEM_INTERNAL,
@@ -165,6 +177,8 @@ enum
     PROP_DEVICE_IS_LINUX_MD,
     PROP_DEVICE_IS_LINUX_LVM2_LV,
     PROP_DEVICE_IS_LINUX_LVM2_PV,
+    PROP_DEVICE_IS_LINUX_DMMP,
+    PROP_DEVICE_IS_LINUX_DMMP_COMPONENT,
     PROP_DEVICE_SIZE,
     PROP_DEVICE_BLOCK_SIZE,
     PROP_DEVICE_IS_MOUNTED,
@@ -273,6 +287,11 @@ enum
     PROP_LINUX_LVM2_PV_GROUP_EXTENT_SIZE,
     PROP_LINUX_LVM2_PV_GROUP_PHYSICAL_VOLUMES,
     PROP_LINUX_LVM2_PV_GROUP_LOGICAL_VOLUMES,
+
+    PROP_LINUX_DMMP_COMPONENT_HOLDER,
+
+    PROP_LINUX_DMMP_NAME,
+    PROP_LINUX_DMMP_SLAVES,
   };
 
 enum
@@ -333,6 +352,9 @@ get_property (GObject *object,
     case PROP_DEVICE_FILE:
       g_value_set_string (value, device->priv->device_file);
       break;
+    case PROP_DEVICE_FILE_PRESENTATION:
+      g_value_set_string (value, device->priv->device_file_presentation);
+      break;
     case PROP_DEVICE_FILE_BY_ID:
       g_value_set_boxed (value, device->priv->device_file_by_id);
       break;
@@ -393,6 +415,12 @@ get_property (GObject *object,
     case PROP_DEVICE_IS_LINUX_LVM2_PV:
       g_value_set_boolean (value, device->priv->device_is_linux_lvm2_pv);
       break;
+    case PROP_DEVICE_IS_LINUX_DMMP:
+      g_value_set_boolean (value, device->priv->device_is_linux_dmmp);
+      break;
+    case PROP_DEVICE_IS_LINUX_DMMP_COMPONENT:
+      g_value_set_boolean (value, device->priv->device_is_linux_dmmp_component);
+      break;
     case PROP_DEVICE_SIZE:
       g_value_set_uint64 (value, device->priv->device_size);
       break;
@@ -723,6 +751,21 @@ get_property (GObject *object,
       g_value_set_boxed (value, device->priv->linux_lvm2_pv_group_logical_volumes);
       break;
 
+    case PROP_LINUX_DMMP_COMPONENT_HOLDER:
+      if (device->priv->linux_dmmp_component_holder != NULL)
+        g_value_set_boxed (value, device->priv->linux_dmmp_component_holder);
+      else
+        g_value_set_boxed (value, "/");
+      break;
+
+    case PROP_LINUX_DMMP_NAME:
+      g_value_set_string (value, device->priv->linux_dmmp_name);
+      break;
+
+    case PROP_LINUX_DMMP_SLAVES:
+      g_value_set_boxed (value, device->priv->linux_dmmp_slaves);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -804,11 +847,20 @@ device_class_init (DeviceClass *klass)
                                                                                         G_MAXINT64,
                                                                                         0,
                                                                                         G_PARAM_READABLE));
-  g_object_class_install_property (object_class, PROP_DEVICE_FILE, g_param_spec_string ("device-file",
-                                                                                        NULL,
-                                                                                        NULL,
-                                                                                        NULL,
-                                                                                        G_PARAM_READABLE));
+  g_object_class_install_property (object_class,
+                                   PROP_DEVICE_FILE,
+                                   g_param_spec_string ("device-file",
+                                                        NULL,
+                                                        NULL,
+                                                        NULL,
+                                                        G_PARAM_READABLE));
+  g_object_class_install_property (object_class,
+                                   PROP_DEVICE_FILE_PRESENTATION,
+                                   g_param_spec_string ("device-file-presentation",
+                                                        NULL,
+                                                        NULL,
+                                                        NULL,
+                                                        G_PARAM_READABLE));
   g_object_class_install_property (object_class,
                                    PROP_DEVICE_FILE_BY_ID,
                                    g_param_spec_boxed ("device-file-by-id",
@@ -933,6 +985,20 @@ device_class_init (DeviceClass *klass)
                                                          NULL,
                                                          FALSE,
                                                          G_PARAM_READABLE));
+  g_object_class_install_property (object_class,
+                                   PROP_DEVICE_IS_LINUX_DMMP,
+                                   g_param_spec_boolean ("device-is-linux-dmmp",
+                                                         NULL,
+                                                         NULL,
+                                                         FALSE,
+                                                         G_PARAM_READABLE));
+  g_object_class_install_property (object_class,
+                                   PROP_DEVICE_IS_LINUX_DMMP_COMPONENT,
+                                   g_param_spec_boolean ("device-is-linux-dmmp-component",
+                                                         NULL,
+                                                         NULL,
+                                                         FALSE,
+                                                         G_PARAM_READABLE));
 
   g_object_class_install_property (object_class, PROP_DEVICE_SIZE, g_param_spec_uint64 ("device-size",
                                                                                         NULL,
@@ -1540,6 +1606,29 @@ device_class_init (DeviceClass *klass)
                                                        NULL,
                                                        dbus_g_type_get_collection ("GPtrArray", G_TYPE_STRING),
                                                        G_PARAM_READABLE));
+  g_object_class_install_property (object_class,
+                                   PROP_LINUX_DMMP_COMPONENT_HOLDER,
+                                   g_param_spec_boxed ("linux-dmmp-component-holder",
+                                                       NULL,
+                                                       NULL,
+                                                       DBUS_TYPE_G_OBJECT_PATH,
+                                                       G_PARAM_READABLE));
+
+  g_object_class_install_property (object_class,
+                                   PROP_LINUX_DMMP_NAME,
+                                   g_param_spec_string ("linux-dmmp-name",
+                                                        NULL,
+                                                        NULL,
+                                                        NULL,
+                                                        G_PARAM_READABLE));
+  g_object_class_install_property (object_class,
+                                   PROP_LINUX_DMMP_SLAVES,
+                                   g_param_spec_boxed ("linux-dmmp-slaves",
+                                                       NULL,
+                                                       NULL,
+                                                       dbus_g_type_get_collection ("GPtrArray",
+                                                                                   DBUS_TYPE_G_OBJECT_PATH),
+                                                       G_PARAM_READABLE));
 }
 
 static void
@@ -1557,6 +1646,7 @@ device_init (Device *device)
   device->priv->linux_md_slaves = g_ptr_array_new ();
   device->priv->linux_lvm2_pv_group_physical_volumes = g_ptr_array_new ();
   device->priv->linux_lvm2_pv_group_logical_volumes = g_ptr_array_new ();
+  device->priv->linux_dmmp_slaves = g_ptr_array_new ();
 
   device->priv->slaves_objpath = g_ptr_array_new ();
   device->priv->holders_objpath = g_ptr_array_new ();
@@ -1608,6 +1698,7 @@ device_finalize (GObject *object)
 
   /* free properties */
   g_free (device->priv->device_file);
+  g_free (device->priv->device_file_presentation);
   g_ptr_array_foreach (device->priv->device_file_by_id, (GFunc) g_free, NULL);
   g_ptr_array_foreach (device->priv->device_file_by_path, (GFunc) g_free, NULL);
   g_ptr_array_free (device->priv->device_file_by_id, TRUE);
@@ -1668,6 +1759,12 @@ device_finalize (GObject *object)
   g_ptr_array_foreach (device->priv->linux_md_slaves, (GFunc) g_free, NULL);
   g_ptr_array_free (device->priv->linux_md_slaves, TRUE);
 
+  g_free (device->priv->linux_dmmp_component_holder);
+
+  g_free (device->priv->linux_dmmp_name);
+  g_ptr_array_foreach (device->priv->linux_dmmp_slaves, (GFunc) g_free, NULL);
+  g_ptr_array_free (device->priv->linux_dmmp_slaves, TRUE);
+
   g_free (device->priv->linux_lvm2_lv_name);
   g_free (device->priv->linux_lvm2_lv_uuid);
   g_free (device->priv->linux_lvm2_lv_group_name);
@@ -2580,7 +2677,9 @@ update_info_drive (Device *device)
       device_set_drive_serial (device, g_udev_device_get_property (device->priv->d, "ID_SERIAL_SHORT"));
     }
 
-  if (g_udev_device_has_property (device->priv->d, "ID_WWN"))
+  if (g_udev_device_has_property (device->priv->d, "ID_WWN_WITH_EXTENSION"))
+    device_set_drive_wwn (device, g_udev_device_get_property (device->priv->d, "ID_WWN_WITH_EXTENSION") + 2);
+  else if (g_udev_device_has_property (device->priv->d, "ID_WWN"))
     device_set_drive_wwn (device, g_udev_device_get_property (device->priv->d, "ID_WWN") + 2);
 
   /* pick up some things (vendor, model, connection_interface, connection_speed)
@@ -2804,8 +2903,8 @@ update_info_luks_cleartext (Device *device)
 
   ret = FALSE;
 
-  dkd_dm_name = g_udev_device_get_property (device->priv->d, "UDISKS_DM_NAME");
-  dkd_dm_target_types = g_udev_device_get_property (device->priv->d, "UDISKS_DM_TARGET_TYPES");
+  dkd_dm_name = g_udev_device_get_property (device->priv->d, "DM_NAME");
+  dkd_dm_target_types = g_udev_device_get_property (device->priv->d, "UDISKS_DM_TARGETS_TYPE");
   if (dkd_dm_name != NULL && g_strcmp0 (dkd_dm_target_types, "crypt") == 0 && device->priv->slaves_objpath->len == 1)
     {
 
@@ -2913,6 +3012,182 @@ update_info_linux_lvm2_lv (Device *device)
 
 /* ---------------------------------------------------------------------------------------------------- */
 
+/* update device_is_linux_dmmp and linux_dmmp_* properties */
+static gboolean
+update_info_linux_dmmp (Device *device)
+{
+  const gchar *dm_name;
+  const gchar* const *target_types;
+  gboolean is_dmmp;
+  guint n;
+  GPtrArray *p;
+  Device *component;
+  gchar *s;
+
+  is_dmmp = FALSE;
+  p = NULL;
+
+  dm_name = g_udev_device_get_property (device->priv->d, "DM_NAME");
+  if (dm_name == NULL)
+    goto out;
+
+  target_types = g_udev_device_get_property_as_strv (device->priv->d, "UDISKS_DM_TARGETS_TYPE");
+  if (target_types == NULL || g_strcmp0 (target_types[0], "multipath") != 0)
+    goto out;
+
+  if (device->priv->slaves_objpath->len == 0)
+    goto out;
+
+  device_set_linux_dmmp_name (device, dm_name);
+
+  p = g_ptr_array_new ();
+  for (n = 0; n < device->priv->slaves_objpath->len; n++)
+    {
+      const gchar *component_objpath = device->priv->slaves_objpath->pdata[n];
+      if (component == NULL)
+        {
+          component = daemon_local_find_by_object_path (device->priv->daemon, component_objpath);
+        }
+      g_ptr_array_add (p, (gpointer) component_objpath);
+    }
+  g_ptr_array_add (p, NULL);
+  device_set_linux_dmmp_slaves (device, (GStrv) p->pdata);
+
+  if (component == NULL)
+    goto out;
+
+  /* copy some of the Drive* properties from the (first) components - we could check here whether
+   * it matches the other components and warn if it doesn't (it should)
+   *
+   * TODO: what about DriveAdapter and DrivePorts? Set them to NULL for now
+   */
+  device_set_drive_vendor (device, component->priv->drive_vendor);
+  device_set_drive_model (device, component->priv->drive_model);
+  device_set_drive_revision (device, component->priv->drive_revision);
+  device_set_drive_serial (device, component->priv->drive_serial);
+  device_set_drive_wwn (device, component->priv->drive_wwn);
+  device_set_drive_connection_interface (device, "virtual_multipath");
+  device_set_drive_connection_speed (device, 0);
+  /* GStrv vs GPtrArray.. device_set_drive_media_compatibility (device, component->priv->drive_media_compatibility); */
+  device_set_drive_media (device, component->priv->drive_media);
+  device_set_drive_is_media_ejectable (device, component->priv->drive_is_media_ejectable);
+  device_set_drive_can_detach (device, component->priv->drive_can_detach);
+  device_set_drive_can_spindown (device, component->priv->drive_can_spindown);
+  device_set_drive_is_rotational (device, component->priv->drive_is_rotational);
+  device_set_drive_rotation_rate (device, component->priv->drive_rotation_rate);
+  device_set_drive_write_cache (device, component->priv->drive_write_cache);
+
+  s = g_strdup_printf ("/dev/mapper/%s", dm_name);
+  device_set_device_file_presentation (device, s);
+  g_free (s);
+
+  is_dmmp = TRUE;
+
+ out:
+  if (p != NULL)
+    g_ptr_array_free (p, TRUE);
+  device_set_device_is_linux_dmmp (device, is_dmmp);
+  return TRUE;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+/* updates device_is_partition and partition_* properties for dm-0 "partitions" on a multi-path device  */
+static gboolean
+update_info_partition_on_linux_dmmp (Device *device)
+{
+  const gchar *dm_name;
+  const gchar* const *targets_type;
+  const gchar* const *targets_params;
+  gchar *params;
+  gint partition_slave_major;
+  gint partition_slave_minor;
+  guint64 offset_sectors;
+  guint64 offset;
+  guint partition_number;
+  Device *partition_slave;
+  gchar *s;
+
+  params = NULL;
+
+  dm_name = g_udev_device_get_property (device->priv->d, "DM_NAME");
+  if (dm_name == NULL)
+    goto out;
+
+  targets_type = g_udev_device_get_property_as_strv (device->priv->d, "UDISKS_DM_TARGETS_TYPE");
+  if (targets_type == NULL || g_strcmp0 (targets_type[0], "linear") != 0)
+    goto out;
+
+  targets_params = g_udev_device_get_property_as_strv (device->priv->d, "UDISKS_DM_TARGETS_PARAMS");
+  if (targets_params == NULL)
+    goto out;
+  params = decode_udev_encoded_string (targets_params[0]);
+
+  if (sscanf (params,
+              "%d:%d %" G_GUINT64_FORMAT,
+              &partition_slave_major,
+              &partition_slave_minor,
+              &offset_sectors) != 3)
+    goto out;
+
+  partition_slave = daemon_local_find_by_dev (device->priv->daemon,
+                                              makedev (partition_slave_major, partition_slave_minor));
+  if (partition_slave == NULL)
+    goto out;
+
+  offset = offset_sectors * 512;
+
+  device_set_partition_slave (device, partition_slave->priv->object_path);
+  device_set_partition_offset (device, offset);
+  device_set_partition_size (device, device->priv->device_size);
+  partition_number = g_udev_device_get_property_as_int (device->priv->d, "UDISKS_PARTITION_NUMBER");
+  if (partition_number > 0)
+    device_set_partition_number (device, partition_number);
+
+  /* all the other Partition* has been set as part of update_info_partition() by reading
+   * UDISKS_PARTITION_* properties
+   */
+
+  device_set_device_is_partition (device, TRUE);
+  device_set_device_is_drive (device, FALSE);
+
+  s = g_strdup_printf ("/dev/mapper/%s", dm_name);
+  device_set_device_file_presentation (device, s);
+  g_free (s);
+
+ out:
+  g_free (params);
+  return TRUE;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+/* update device_is_linux_dmmp_component and linux_dmmp_component_* properties */
+static gboolean
+update_info_linux_dmmp_component (Device *device)
+{
+  gboolean is_dmmp_component;
+
+  is_dmmp_component = FALSE;
+
+  if (device->priv->holders_objpath->len == 1)
+    {
+      Device *holder;
+
+      holder = daemon_local_find_by_object_path (device->priv->daemon, device->priv->holders_objpath->pdata[0]);
+      if (holder != NULL && holder->priv->device_is_linux_dmmp)
+        {
+          is_dmmp_component = TRUE;
+          device_set_linux_dmmp_component_holder (device, holder->priv->object_path);
+        }
+    }
+
+  device_set_device_is_linux_dmmp_component (device, is_dmmp_component);
+  return TRUE;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
 /* update device_is_linux_lvm2_pv and linux_lvm2_pv_* properties */
 static gboolean
 update_info_linux_lvm2_pv (Device *device)
@@ -3799,9 +4074,9 @@ update_info (Device *device)
       goto out;
     }
 
-  /* ignore dm devices that are not active */
-  if (g_str_has_prefix (g_udev_device_get_name (device->priv->d), "dm-")
-      && g_udev_device_get_property (device->priv->d, "UDISKS_DM_STATE") == NULL)
+  /* ignore dm devices that are suspended */
+  if (g_str_has_prefix (g_udev_device_get_name (device->priv->d), "dm-") &&
+      g_strcmp0 (g_udev_device_get_property (device->priv->d, "DM_SUSPENDED"), "0") != 0)
     goto out;
 
   major = g_udev_device_get_property_as_int (device->priv->d, "MAJOR");
@@ -4086,6 +4361,18 @@ update_info (Device *device)
   if (!update_info_linux_lvm2_pv (device))
     goto out;
 
+  /* device_is_linux_dmmp and linux_dmmp_* properties */
+  if (!update_info_linux_dmmp (device))
+    goto out;
+
+  /* device_is_partition and partition_* properties for dm-0 "partitions" on a multi-path device  */
+  if (!update_info_partition_on_linux_dmmp (device))
+    goto out;
+
+  /* device_is_linux_dmmp_component and linux_dmmp_component_* properties */
+  if (!update_info_linux_dmmp_component (device))
+    goto out;
+
   /* device_is_linux_md_component and linux_md_component_* properties */
   if (!update_info_linux_md_component (device))
     goto out;
@@ -4273,8 +4560,19 @@ device_local_is_busy (Device *device,
   /* or if another block device is using/holding us (e.g. if holders/ is non-empty in sysfs) */
   if (device->priv->holders_objpath->len > 0)
     {
-      g_set_error (error, ERROR, ERROR_BUSY, "One or more block devices are holding %s", device->priv->device_file);
-      goto out;
+      if (device->priv->device_is_linux_dmmp)
+        {
+          /* This is OK */
+        }
+      else
+        {
+          g_set_error (error,
+                       ERROR,
+                       ERROR_BUSY,
+                       "One or more block devices are holding %s",
+                       device->priv->device_file);
+          goto out;
+        }
     }
 
   /* If we are an extended partition, we are also busy if one or more logical partitions are busy
@@ -6690,7 +6988,10 @@ device_partition_delete_authorized_cb (Daemon *daemon,
 
   n = 0;
   argv[n++] = PACKAGE_LIBEXEC_DIR "/udisks-helper-delete-partition";
-  argv[n++] = enclosing_device->priv->device_file;
+  if (enclosing_device->priv->device_is_linux_dmmp)
+    argv[n++] = (gchar *) get_dmmp_device_node (enclosing_device);
+  else
+    argv[n++] = enclosing_device->priv->device_file;
   argv[n++] = device->priv->device_file;
   argv[n++] = offset_as_string;
   argv[n++] = size_as_string;
@@ -7528,8 +7829,10 @@ device_partition_create_authorized_cb (Daemon *daemon,
 
   n = 0;
   argv[n++] = PACKAGE_LIBEXEC_DIR "/udisks-helper-create-partition";
-  argv[n++] = device->priv->device_file;
-  ;
+  if (device->priv->device_is_linux_dmmp)
+    argv[n++] = (gchar *) get_dmmp_device_node (device);
+  else
+    argv[n++] = device->priv->device_file;
   argv[n++] = offset_as_string;
   argv[n++] = size_as_string;
   argv[n++] = (char *) type;
@@ -8002,7 +8305,10 @@ device_partition_table_create_authorized_cb (Daemon *daemon,
 
   n = 0;
   argv[n++] = PACKAGE_LIBEXEC_DIR "/udisks-helper-create-partition-table";
-  argv[n++] = device->priv->device_file;
+  if (device->priv->device_is_linux_dmmp)
+    argv[n++] = (gchar *) get_dmmp_device_node (device);
+  else
+    argv[n++] = device->priv->device_file;
   argv[n++] = (char *) scheme;
   for (m = 0; options[m] != NULL; m++)
     {
index 2051c08..c93223b 100644 (file)
@@ -107,11 +107,14 @@ main (int argc,
         ret = 0;
     }
 
-  /* reread partition table */
-  if (!reread_partition_table (device))
+  /* tell kernel reread partition table (but only if we are a kernel partition) */
+  if (!g_str_has_prefix (device, "/dev/mapper/mpath"))
     {
-      ret = 1;
-      goto out;
+      if (!reread_partition_table (device))
+        {
+          ret = 1;
+          goto out;
+        }
     }
 
  out:
index 8a8ee6b..f34fbfe 100644 (file)
@@ -133,33 +133,36 @@ main (int argc,
             }
         }
 
-      /* OK, now tell the kernel about the newly added partition */
-      fd = open (device, O_RDONLY);
-      if (fd < 0)
+      /* OK, now tell the kernel about the newly added partition (but only if we are a kernel partition) */
+      if (!g_str_has_prefix (device, "/dev/mapper/mpath"))
         {
-          g_printerr ("Cannot open %s: %m\n", device);
-          goto out;
-        }
-      memset (&a, '\0', sizeof(struct blkpg_ioctl_arg));
-      memset (&p, '\0', sizeof(struct blkpg_partition));
-      p.pno = out_num;
-      p.start = out_start;
-      p.length = out_size;
-      a.op = BLKPG_ADD_PARTITION;
-      a.datalen = sizeof(p);
-      a.data = &p;
-      if (ioctl (fd, BLKPG, &a) == -1)
-        {
-          g_printerr ("Error doing BLKPG ioctl with BLKPG_ADD_PARTITION for partition %d "
-                      "of size %" G_GUINT64_FORMAT " at offset %" G_GUINT64_FORMAT " on %s: %m\n",
-                      out_num,
-                      out_start,
-                      out_size,
-                      device);
+          fd = open (device, O_RDONLY);
+          if (fd < 0)
+            {
+              g_printerr ("Cannot open %s: %m\n", device);
+              goto out;
+            }
+          memset (&a, '\0', sizeof(struct blkpg_ioctl_arg));
+          memset (&p, '\0', sizeof(struct blkpg_partition));
+          p.pno = out_num;
+          p.start = out_start;
+          p.length = out_size;
+          a.op = BLKPG_ADD_PARTITION;
+          a.datalen = sizeof(p);
+          a.data = &p;
+          if (ioctl (fd, BLKPG, &a) == -1)
+            {
+              g_printerr ("Error doing BLKPG ioctl with BLKPG_ADD_PARTITION for partition %d "
+                          "of size %" G_GUINT64_FORMAT " at offset %" G_GUINT64_FORMAT " on %s: %m\n",
+                          out_num,
+                          out_start,
+                          out_size,
+                          device);
+              close (fd);
+              goto out;
+            }
           close (fd);
-          goto out;
         }
-      close (fd);
 
       /* send the start and size back to the daemon - it needs to know, to
        * wait for the created partition, because the partition may not have
index 1eca3fd..44945d2 100644 (file)
@@ -91,35 +91,45 @@ main (int argc,
       goto out;
     }
 
-  /* don't ask libparted to poke the kernel - it won't work if other partitions are mounted/busy */
-  if (part_del_partition ((char *) device, offset, FALSE))
-    {
-      gint fd;
-      struct blkpg_ioctl_arg a;
-      struct blkpg_partition p;
+  gboolean is_kernel_partition;
+  is_kernel_partition = !g_str_has_prefix (device, "/dev/mapper/mpath");
 
-      /* now, ask the kernel to delete the partition */
-      fd = open (device, O_RDONLY);
-      if (fd < 0)
-        {
-          g_printerr ("Cannot open %s: %m\n", device);
-          goto out;
-        }
-      memset (&a, '\0', sizeof(struct blkpg_ioctl_arg));
-      memset (&p, '\0', sizeof(struct blkpg_partition));
-      p.pno = part_number;
-      a.op = BLKPG_DEL_PARTITION;
-      a.datalen = sizeof(p);
-      a.data = &p;
-      if (ioctl (fd, BLKPG, &a) == -1)
+  /* don't ask libparted to poke the kernel - it won't work if other
+   * partitions are mounted/busy (unless it's not a kernel partition)
+   */
+  if (part_del_partition ((char *) device,
+                          offset,
+                          is_kernel_partition ? FALSE : TRUE))
+    {
+      /* now, ask the kernel to delete the partition  (but only if we are a kernel partition) */
+      if (is_kernel_partition)
         {
-          g_printerr ("Error doing BLKPG ioctl with BLKPG_DEL_PARTITION for partition %d on %s: %m\n",
-                      part_number,
-                      device);
+          gint fd;
+          struct blkpg_ioctl_arg a;
+          struct blkpg_partition p;
+
+          fd = open (device, O_RDONLY);
+          if (fd < 0)
+            {
+              g_printerr ("Cannot open %s: %m\n", device);
+              goto out;
+            }
+          memset (&a, '\0', sizeof(struct blkpg_ioctl_arg));
+          memset (&p, '\0', sizeof(struct blkpg_partition));
+          p.pno = part_number;
+          a.op = BLKPG_DEL_PARTITION;
+          a.datalen = sizeof(p);
+          a.data = &p;
+          if (ioctl (fd, BLKPG, &a) == -1)
+            {
+              g_printerr ("Error doing BLKPG ioctl with BLKPG_DEL_PARTITION for partition %d on %s: %m\n",
+                          part_number,
+                          device);
+              close (fd);
+              goto out;
+            }
           close (fd);
-          goto out;
         }
-      close (fd);
 
       /* zero the contents of what was the _partition_
        *
@@ -129,9 +139,9 @@ main (int argc,
       /* scrub signatures */
       if (!scrub_signatures (device, offset, size))
         {
-          g_printerr        ("Cannot scrub filesystem signatures at "
-                             "offset=%" G_GINT64_FORMAT " and size=%" G_GINT64_FORMAT "\n",
-                             offset, size);
+          g_printerr ("Cannot scrub filesystem signatures at "
+                      "offset=%" G_GINT64_FORMAT " and size=%" G_GINT64_FORMAT "\n",
+                      offset, size);
         }
       else
         {
index d6aebbc..b3d8f60 100644 (file)
@@ -41,6 +41,53 @@ usage (int argc,
   exit (1);
 }
 
+static gchar *
+decode_udev_encoded_string (const gchar *str)
+{
+  GString *s;
+  gchar *ret;
+  const gchar *end_valid;
+  guint n;
+
+  s = g_string_new (NULL);
+  for (n = 0; str[n] != '\0'; n++)
+    {
+      if (str[n] == '\\')
+        {
+          gint val;
+
+          if (str[n + 1] != 'x' || str[n + 2] == '\0' || str[n + 3] == '\0')
+            {
+              g_print ("**** NOTE: malformed encoded string '%s'\n", str);
+              break;
+            }
+
+          val = (g_ascii_xdigit_value (str[n + 2]) << 4) | g_ascii_xdigit_value (str[n + 3]);
+
+          g_string_append_c (s, val);
+
+          n += 3;
+        }
+      else
+        {
+          g_string_append_c (s, str[n]);
+        }
+    }
+
+  if (!g_utf8_validate (s->str, -1, &end_valid))
+    {
+      g_print ("**** NOTE: The string '%s' is not valid UTF-8. Invalid characters begins at '%s'\n", s->str, end_valid);
+      ret = g_strndup (s->str, end_valid - s->str);
+      g_string_free (s, TRUE);
+    }
+  else
+    {
+      ret = g_string_free (s, FALSE);
+    }
+
+  return ret;
+}
+
 static int
 sysfs_get_int (const char *dir,
                const char *attribute)
@@ -81,13 +128,220 @@ sysfs_get_uint64 (const char *dir,
   return result;
 }
 
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gchar *
+get_syspath (struct udev *udev,
+             const gchar *device_file)
+{
+  struct udev_device *device;
+  struct stat statbuf;
+  gchar *ret;
+
+  ret = NULL;
+
+  if (stat (device_file, &statbuf) != 0)
+    {
+      g_printerr ("Error statting %s: %m\n", device_file);
+      goto out;
+    }
+
+  device = udev_device_new_from_devnum (udev, 'b', statbuf.st_rdev);
+  if (device == NULL)
+    {
+      g_printerr ("Error getting udev device for %s: %m\n", device_file);
+      goto out;
+    }
+  ret = g_strdup (udev_device_get_syspath (device));
+  udev_device_unref (device);
+
+ out:
+  return ret;
+}
+
+/**
+ * get_part_table_device_file:
+ * @udev: An udev context.
+ * @given_device_file: The device file given on the command line.
+ * @out_offset: Return location for offset or %NULL.
+ * @out_partition_number: Return location for partition number or %NULL.
+ *
+ * If @given_device_file is not a partition, returns a copy of it and
+ * sets @out_offset and @out_partition_number to 0.
+ *
+ * Otherwise, returns the device file for the block device for which
+ * @given_device_file is a partition of and returns the offset of the
+ * partition in @out_offset and the partition number in
+ * @out_partition_number.
+ *
+ * If something goes wrong, %NULL is returned.
+ */
+static gchar *
+get_part_table_device_file (struct udev *udev,
+                            const gchar *given_device_file,
+                            guint64     *out_offset,
+                            guint       *out_partition_number)
+{
+  gchar *ret;
+  guint64 offset;
+  guint partition_number;
+  gchar *devpath;
+
+  devpath = NULL;
+  offset = 0;
+  ret = NULL;
+
+  devpath = get_syspath (udev, given_device_file);
+  if (devpath == NULL)
+    goto out;
+
+  partition_number = sysfs_get_int (devpath, "partition");
+
+  /* find device file for partition table device */
+  if (partition_number > 0)
+    {
+      struct udev_device *device;
+      gchar *partition_table_devpath;
+      guint n;
+
+      /* partition */
+      partition_table_devpath = g_strdup (devpath);
+      for (n = strlen (partition_table_devpath) - 1; partition_table_devpath[n] != '/'; n--)
+        partition_table_devpath[n] = '\0';
+      partition_table_devpath[n] = '\0';
+
+      device = udev_device_new_from_syspath (udev, partition_table_devpath);
+      if (device == NULL)
+        {
+          g_printerr ("Error getting udev device for syspath %s: %m\n", partition_table_devpath);
+          goto out;
+        }
+      ret = g_strdup (udev_device_get_devnode (device));
+      udev_device_unref (device);
+      if (ret == NULL)
+        {
+          /* This Should Not Happen™, but was reported in a distribution upgrade
+             scenario, so handle it gracefully */
+          g_printerr ("Error getting devnode from udev device path %s: %m\n", partition_table_devpath);
+          goto out;
+        }
+      g_free (partition_table_devpath);
+
+      offset = sysfs_get_uint64 (devpath, "start") * 512;
+    }
+  else
+    {
+      struct udev_device *device;
+      const char *targets_type;
+      const char *encoded_targets_params;
+
+      device = udev_device_new_from_syspath (udev, devpath);
+      if (device == NULL)
+        {
+          g_printerr ("Error getting udev device for syspath %s: %m\n", devpath);
+          goto out;
+        }
+
+      targets_type = udev_device_get_property_value (device, "UDISKS_DM_TARGETS_TYPE");
+      encoded_targets_params = udev_device_get_property_value (device, "UDISKS_DM_TARGETS_PARAMS");
+      if (g_strcmp0 (targets_type, "linear") == 0)
+        {
+          gint partition_slave_major;
+          gint partition_slave_minor;
+          guint64 offset_sectors;
+          gchar *targets_params;
+
+          targets_params = decode_udev_encoded_string (encoded_targets_params);
+          if (sscanf (targets_params,
+                      "%d:%d\x20%" G_GUINT64_FORMAT,
+                      &partition_slave_major,
+                      &partition_slave_minor,
+                      &offset_sectors) == 3)
+            {
+              struct udev_device *mp_device;
+
+              mp_device = udev_device_new_from_devnum (udev, 'b', makedev (partition_slave_major,
+                                                                           partition_slave_minor));
+              if (mp_device != NULL)
+                {
+                  const char *dm_uuid;
+
+                  ret = g_strdup (udev_device_get_devnode (mp_device));
+                  offset = offset_sectors * 512;
+
+                  /* now figure out partition_number
+                   *
+                   * this is kind of a hack.. but works since UUID is of the
+                   * form part2-mpath-3600508b400105df70000e00000d80000
+                   */
+                  partition_number = 0;
+                  dm_uuid = udev_device_get_property_value (device, "DM_UUID");
+                  if (dm_uuid != NULL && g_str_has_prefix (dm_uuid, "part"))
+                    partition_number = atoi (dm_uuid + 4);
+
+                  udev_device_unref (mp_device);
+                  g_free (targets_params);
+                  udev_device_unref (device);
+                  goto out;
+                }
+            }
+          g_free (targets_params);
+        }
+      udev_device_unref (device);
+
+      /* not a kernel partition */
+      ret = g_strdup (given_device_file);
+      partition_number = 0;
+    }
+
+ out:
+  if (out_offset != NULL)
+    *out_offset = offset;
+  if (out_partition_number != NULL)
+    *out_partition_number = partition_number;
+
+  g_free (devpath);
+
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static guint
+count_entries (PartitionTable *pt)
+{
+  guint ret;
+  guint num_top_level;
+  guint n;
+
+  ret = 0;
+
+  num_top_level = part_table_get_num_entries (pt);
+  for (n = 0; n < num_top_level; n++)
+    {
+      PartitionTable *nested;
+
+      if (part_table_entry_is_in_use (pt, n))
+        ret++;
+
+      nested = part_table_entry_get_nested (pt, n);
+      if (nested != NULL)
+        {
+          ret += part_table_get_num_entries (nested);
+        }
+    }
+
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
 int
 main (int argc,
       char *argv[])
 {
   guint n;
   gint fd;
-  gint partition_number;
   gchar *devpath;
   const gchar *device_file;
   gchar *partition_table_device_file;
@@ -128,75 +382,21 @@ main (int argc,
       goto out;
     }
 
-  devpath = getenv ("DEVPATH");
-  if (devpath != NULL)
-    {
-      devpath = g_build_filename ("/sys", devpath, NULL);
-    }
-  else
-    {
-      struct udev_device *device;
-      struct stat statbuf;
-
-      if (stat (device_file, &statbuf) != 0)
-        {
-          g_printerr ("Error statting %s: %m\n", device_file);
-          goto out;
-        }
-
-      device = udev_device_new_from_devnum (udev, 'b', statbuf.st_rdev);
-      if (device == NULL)
-        {
-          g_printerr ("Error getting udev device for %s: %m\n", device_file);
-          goto out;
-        }
-      devpath = g_strdup (udev_device_get_syspath (device));
-      udev_device_unref (device);
-    }
-
-  partition_number = sysfs_get_int (devpath, "partition");
-
-  /* find device file for partition table device */
-  if (partition_number > 0)
-    {
-      struct udev_device *device;
-      gchar *partition_table_devpath;
-
-      /* partition */
-      partition_table_devpath = g_strdup (devpath);
-      for (n = strlen (partition_table_devpath) - 1; partition_table_devpath[n] != '/'; n--)
-        partition_table_devpath[n] = '\0';
-      partition_table_devpath[n] = '\0';
+  guint64 partition_offset;
+  guint partition_number;
 
-      device = udev_device_new_from_syspath (udev, partition_table_devpath);
-      if (device == NULL)
-        {
-          g_printerr ("Error getting udev device for syspath %s: %m\n", partition_table_devpath);
-          goto out;
-        }
-      partition_table_device_file = g_strdup (udev_device_get_devnode (device));
-      udev_device_unref (device);
-      if (partition_table_device_file == NULL)
-        {
-          /* This Should Not Happen™, but was reported in a distribution upgrade 
-             scenario, so handle it gracefully */
-          g_printerr ("Error getting devnode from udev device path %s: %m\n", partition_table_devpath);
-          goto out;
-        }
-      g_free (partition_table_devpath);
-    }
-  else
-    {
-      /* not partition */
-      partition_table_device_file = g_strdup (device_file);
-    }
+  partition_table_device_file = get_part_table_device_file (udev,
+                                                            device_file,
+                                                            &partition_offset,
+                                                            &partition_number);
+  g_printerr ("got `%s' and %" G_GUINT64_FORMAT "\n", partition_table_device_file, partition_offset);
 
   fd = open (partition_table_device_file, O_RDONLY);
 
   /* TODO: right now we also use part_id to determine if media is available or not. This
    *       should probably be done elsewhere
    */
-  if (partition_number == 0)
+  if (partition_offset == 0)
     {
       if (fd < 0)
         {
@@ -221,9 +421,8 @@ main (int argc,
     }
   close (fd);
 
-  if (partition_number > 0)
+  if (partition_offset > 0)
     {
-      guint64 partition_offset;
       PartitionTable *partition_table_for_entry;
       gint entry_num;
       gchar *type;
@@ -234,7 +433,6 @@ main (int argc,
       guint64 size;
 
       /* partition */
-      partition_offset = sysfs_get_uint64 (devpath, "start") * 512;
       part_table_find (partition_table, partition_offset, &partition_table_for_entry, &entry_num);
       if (entry_num == -1)
         {
@@ -273,6 +471,7 @@ main (int argc,
     {
       g_print ("UDISKS_PARTITION_TABLE=1\n");
       g_print ("UDISKS_PARTITION_TABLE_SCHEME=%s\n", part_get_scheme_name (part_table_get_scheme (partition_table)));
+      g_print ("UDISKS_PARTITION_TABLE_COUNT=%d\n", count_entries (partition_table));
     }
 
  out:
index 4f05b13..b008d92 100644 (file)
@@ -18,6 +18,194 @@ usage (void)
 
 /* ---------------------------------------------------------------------------------------------------- */
 
+
+/* This code is from udev - will become public libudev API at some point */
+
+/* count of characters used to encode one unicode char */
+static int utf8_encoded_expected_len(const char *str)
+{
+        unsigned char c = (unsigned char)str[0];
+
+        if (c < 0x80)
+                return 1;
+        if ((c & 0xe0) == 0xc0)
+                return 2;
+        if ((c & 0xf0) == 0xe0)
+                return 3;
+        if ((c & 0xf8) == 0xf0)
+                return 4;
+        if ((c & 0xfc) == 0xf8)
+                return 5;
+        if ((c & 0xfe) == 0xfc)
+                return 6;
+        return 0;
+}
+
+/* decode one unicode char */
+static int utf8_encoded_to_unichar(const char *str)
+{
+        int unichar;
+        int len;
+        int i;
+
+        len = utf8_encoded_expected_len(str);
+        switch (len) {
+        case 1:
+                return (int)str[0];
+        case 2:
+                unichar = str[0] & 0x1f;
+                break;
+        case 3:
+                unichar = (int)str[0] & 0x0f;
+                break;
+        case 4:
+                unichar = (int)str[0] & 0x07;
+                break;
+        case 5:
+                unichar = (int)str[0] & 0x03;
+                break;
+        case 6:
+                unichar = (int)str[0] & 0x01;
+                break;
+        default:
+                return -1;
+        }
+
+        for (i = 1; i < len; i++) {
+                if (((int)str[i] & 0xc0) != 0x80)
+                        return -1;
+                unichar <<= 6;
+                unichar |= (int)str[i] & 0x3f;
+        }
+
+        return unichar;
+}
+
+/* expected size used to encode one unicode char */
+static int utf8_unichar_to_encoded_len(int unichar)
+{
+        if (unichar < 0x80)
+                return 1;
+        if (unichar < 0x800)
+                return 2;
+        if (unichar < 0x10000)
+                return 3;
+        if (unichar < 0x200000)
+                return 4;
+        if (unichar < 0x4000000)
+                return 5;
+        return 6;
+}
+
+/* check if unicode char has a valid numeric range */
+static int utf8_unichar_valid_range(int unichar)
+{
+        if (unichar > 0x10ffff)
+                return 0;
+        if ((unichar & 0xfffff800) == 0xd800)
+                return 0;
+        if ((unichar > 0xfdcf) && (unichar < 0xfdf0))
+                return 0;
+        if ((unichar & 0xffff) == 0xffff)
+                return 0;
+        return 1;
+}
+
+/* validate one encoded unicode char and return its length */
+static int utf8_encoded_valid_unichar(const char *str)
+{
+        int len;
+        int unichar;
+        int i;
+
+        len = utf8_encoded_expected_len(str);
+        if (len == 0)
+                return -1;
+
+        /* ascii is valid */
+        if (len == 1)
+                return 1;
+
+        /* check if expected encoded chars are available */
+        for (i = 0; i < len; i++)
+                if ((str[i] & 0x80) != 0x80)
+                        return -1;
+
+        unichar = utf8_encoded_to_unichar(str);
+
+        /* check if encoded length matches encoded value */
+        if (utf8_unichar_to_encoded_len(unichar) != len)
+                return -1;
+
+        /* check if value has valid range */
+        if (!utf8_unichar_valid_range(unichar))
+                return -1;
+
+        return len;
+}
+
+static int is_whitelisted(char c, const char *white)
+{
+        if ((c >= '0' && c <= '9') ||
+            (c >= 'A' && c <= 'Z') ||
+            (c >= 'a' && c <= 'z') ||
+            strchr("#+-.:=@_", c) != NULL ||
+            (white != NULL && strchr(white, c) != NULL))
+                return 1;
+        return 0;
+}
+
+/**
+ * _udev_util_encode_string:
+ * @str: input string to be encoded
+ * @str_enc: output string to store the encoded input string
+ * @len: maximum size of the output string, which may be
+ *       four times as long as the input string
+ *
+ * Encode all potentially unsafe characters of a string to the
+ * corresponding hex value prefixed by '\x'.
+ *
+ * Returns: 0 if the entire string was copied, non-zero otherwise.
+ */
+static int
+_udev_util_encode_string(const char *str, char *str_enc, size_t len)
+{
+        size_t i, j;
+
+        if (str == NULL || str_enc == NULL)
+                return -1;
+
+        for (i = 0, j = 0; str[i] != '\0'; i++) {
+                int seqlen;
+
+                seqlen = utf8_encoded_valid_unichar(&str[i]);
+                if (seqlen > 1) {
+                        if (len-j < (size_t)seqlen)
+                                goto err;
+                        memcpy(&str_enc[j], &str[i], seqlen);
+                        j += seqlen;
+                        i += (seqlen-1);
+                } else if (str[i] == '\\' || !is_whitelisted(str[i], NULL)) {
+                        if (len-j < 4)
+                                goto err;
+                        sprintf(&str_enc[j], "\\x%02x", (unsigned char) str[i]);
+                        j += 4;
+                } else {
+                        if (len-j < 1)
+                                goto err;
+                        str_enc[j] = str[i];
+                        j++;
+                }
+        }
+        if (len-j < 1)
+                goto err;
+        str_enc[j] = '\0';
+        return 0;
+err:
+        return -1;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
 /* based on the export patch in https://bugzilla.redhat.com/show_bug.cgi?id=438604 */
 
 static int
@@ -30,13 +218,17 @@ dm_export (int major, int minor)
   char *target_type;
   char *params;
   const char *name;
-  const char *uuid;
   struct dm_info info;
+  GString *target_types_str;
+  GString *start_str;
+  GString *length_str;
+  GString *params_str;
+  gchar buf[4096];
 
   ret = FALSE;
   dmt = NULL;
 
-  dmt = dm_task_create (DM_DEVICE_STATUS);
+  dmt = dm_task_create (DM_DEVICE_TABLE);
   if (dmt == NULL)
     {
       perror ("dm_task_create");
@@ -73,63 +265,60 @@ dm_export (int major, int minor)
       perror ("dm_task_get_name");
       goto out;
     }
-  g_print ("UDISKS_DM_NAME=%s\n", name);
-
-  uuid = dm_task_get_uuid (dmt);
-  if (uuid != NULL)
-    {
-      g_print ("UDISKS_DM_UUID=%s\n", uuid);
-    }
 
   if (!info.exists)
     {
-      g_print ("UDISKS_DM_STATE=NOTPRESENT\n");
       goto out;
     }
 
-  g_print ("UDISKS_DM_STATE=%s\n",
-           info.suspended ? "SUSPENDED" :
-           (info.read_only ? " READONLY" : "ACTIVE"));
-
-  if (!info.live_table && !info.inactive_table)
-    {
-      g_print ("UDISKS_DM_TABLE_STATE=NONE\n");
-    }
-  else
-    {
-      g_print ("UDISKS_DM_TABLE_STATE=%s%s%s\n",
-               info.live_table ? "LIVE" : "",
-               info.live_table && info.inactive_table ? "/" : "",
-               info.inactive_table ? "INACTIVE" : "");
-    }
-
-  if (info.open_count != -1)
-    {
-      g_print ("UDISKS_DM_OPENCOUNT=%d\n", info.open_count);
-    }
-
-  g_print ("UDISKS_DM_LAST_EVENT_NR=%" G_GUINT32_FORMAT "\n", (guint32) info.event_nr);
-
-  g_print ("UDISKS_DM_MAJOR=%d\n", info.major);
-  g_print ("UDISKS_DM_MINOR=%d\n", info.minor);
-
   if (info.target_count != -1)
-    g_print ("UDISKS_DM_TARGET_COUNT=%d\n", info.target_count);
+    g_print ("UDISKS_DM_TARGETS_COUNT=%d\n", info.target_count);
+
+  target_types_str = g_string_new (NULL);
+  start_str = g_string_new (NULL);
+  length_str = g_string_new (NULL);
+  params_str = g_string_new (NULL);
 
-  /* export all table types */
+  /* export all tables */
   next = NULL;
-  next = dm_get_next_target (dmt, next, &start, &length, &target_type, &params);
-  if (target_type != NULL)
+  do
     {
-      g_print ("UDISKS_DM_TARGET_TYPES=%s", target_type);
-      while (next != NULL)
+      next = dm_get_next_target (dmt, next, &start, &length, &target_type, &params);
+      if (target_type != NULL)
+        {
+          g_string_append (target_types_str, target_type);
+          g_string_append_printf (start_str, "%" G_GUINT64_FORMAT, start);
+          g_string_append_printf (length_str, "%" G_GUINT64_FORMAT, length);
+          if (params != NULL && strlen (params) > 0)
+            {
+              _udev_util_encode_string (params, buf, sizeof (buf));
+              g_string_append (params_str, buf);
+            }
+        }
+
+      if (next != NULL)
         {
-          next = dm_get_next_target (dmt, next, &start, &length, &target_type, &params);
-          if (target_type)
-            g_print (",%s", target_type);
+          g_string_append_c (target_types_str, ' ');
+          g_string_append_c (start_str, ' ');
+          g_string_append_c (length_str, ' ');
+          g_string_append_c (params_str, ' ');
         }
-      g_print ("\n");
     }
+  while (next != NULL);
+
+  if (target_types_str->len > 0)
+      g_print ("UDISKS_DM_TARGETS_TYPE=%s\n", target_types_str->str);
+  if (start_str->len > 0)
+      g_print ("UDISKS_DM_TARGETS_START=%s\n", start_str->str);
+  if (length_str->len > 0)
+      g_print ("UDISKS_DM_TARGETS_LENGTH=%s\n", length_str->str);
+  if (params_str->len > 0)
+      g_print ("UDISKS_DM_TARGETS_PARAMS=%s\n", params_str->str);
+
+  g_string_free (target_types_str, TRUE);
+  g_string_free (start_str, TRUE);
+  g_string_free (length_str, TRUE);
+  g_string_free (params_str, TRUE);
 
   ret = TRUE;
 
index 7517fa1..ece774c 100644 (file)
@@ -291,6 +291,7 @@ typedef struct
   gint64 device_major;
   gint64 device_minor;
   char *device_file;
+  char *device_file_presentation;
   char **device_file_by_id;
   char **device_file_by_path;
   gboolean device_is_system_internal;
@@ -312,6 +313,8 @@ typedef struct
   gboolean device_is_linux_md;
   gboolean device_is_linux_lvm2_lv;
   gboolean device_is_linux_lvm2_pv;
+  gboolean device_is_linux_dmmp;
+  gboolean device_is_linux_dmmp_component;
   char **device_mount_paths;
   uid_t device_mounted_by_uid;
   gboolean device_presentation_hide;
@@ -420,6 +423,12 @@ typedef struct
   guint64 linux_lvm2_pv_group_extent_size;
   char **linux_lvm2_pv_group_physical_volumes;
   char **linux_lvm2_pv_group_logical_volumes;
+
+  gchar *linux_dmmp_component_holder;
+
+  gchar *linux_dmmp_name;
+  gchar **linux_dmmp_slaves;
+
 } DeviceProperties;
 
 static void
@@ -442,6 +451,8 @@ collect_props (const char *key,
     props->device_minor = g_value_get_int64 (value);
   else if (strcmp (key, "DeviceFile") == 0)
     props->device_file = g_strdup (g_value_get_string (value));
+  else if (strcmp (key, "DeviceFilePresentation") == 0)
+    props->device_file_presentation = g_strdup (g_value_get_string (value));
   else if (strcmp (key, "DeviceFileById") == 0)
     props->device_file_by_id = g_strdupv (g_value_get_boxed (value));
   else if (strcmp (key, "DeviceFileByPath") == 0)
@@ -482,6 +493,10 @@ collect_props (const char *key,
     props->device_is_linux_lvm2_lv = g_value_get_boolean (value);
   else if (strcmp (key, "DeviceIsLinuxLvm2PV") == 0)
     props->device_is_linux_lvm2_pv = g_value_get_boolean (value);
+  else if (strcmp (key, "DeviceIsLinuxDmmp") == 0)
+    props->device_is_linux_dmmp = g_value_get_boolean (value);
+  else if (strcmp (key, "DeviceIsLinuxDmmpComponent") == 0)
+    props->device_is_linux_dmmp_component = g_value_get_boolean (value);
   else if (strcmp (key, "DeviceIsMounted") == 0)
     props->device_is_mounted = g_value_get_boolean (value);
   else if (strcmp (key, "DeviceMountPaths") == 0)
@@ -711,6 +726,24 @@ collect_props (const char *key,
   else if (strcmp (key, "LinuxLvm2PVGroupLogicalVolumes") == 0)
     props->linux_lvm2_pv_group_logical_volumes = g_strdupv (g_value_get_boxed (value));
 
+  else if (strcmp (key, "LinuxDmmpComponentHolder") == 0)
+    props->linux_dmmp_component_holder = g_strdup (g_value_get_boxed (value));
+
+  else if (strcmp (key, "LinuxDmmpName") == 0)
+    props->linux_dmmp_name = g_strdup (g_value_get_string (value));
+  else if (strcmp (key, "LinuxDmmpSlaves") == 0)
+    {
+      guint n;
+      GPtrArray *object_paths;
+
+      object_paths = g_value_get_boxed (value);
+
+      props->linux_dmmp_slaves = g_new0 (char *, object_paths->len + 1);
+      for (n = 0; n < object_paths->len; n++)
+        props->linux_dmmp_slaves[n] = g_strdup (object_paths->pdata[n]);
+      props->linux_dmmp_slaves[n] = NULL;
+    }
+
   else
     handled = FALSE;
 
@@ -723,6 +756,7 @@ device_properties_free (DeviceProperties *props)
 {
   g_free (props->native_path);
   g_free (props->device_file);
+  g_free (props->device_file_presentation);
   g_strfreev (props->device_file_by_id);
   g_strfreev (props->device_file_by_path);
   g_strfreev (props->device_mount_paths);
@@ -785,6 +819,11 @@ device_properties_free (DeviceProperties *props)
   g_strfreev (props->linux_lvm2_pv_group_physical_volumes);
   g_strfreev (props->linux_lvm2_pv_group_logical_volumes);
 
+  g_free (props->linux_dmmp_component_holder);
+
+  g_free (props->linux_dmmp_name);
+  g_strfreev (props->linux_dmmp_slaves);
+
   g_free (props);
 }
 
@@ -1091,6 +1130,9 @@ do_show_info (const char *object_path)
            props->device_major,
            props->device_minor);
   g_print ("  device-file:                 %s\n", props->device_file);
+  g_print ("    presentation:              %s\n",
+           (props->device_file_presentation != NULL && strlen (props->device_file_presentation) > 0) ?
+           props->device_file_presentation : "(not set)");
   for (n = 0; props->device_file_by_id[n] != NULL; n++)
     g_print ("    by-id:                     %s\n", (char *) props->device_file_by_id[n]);
   for (n = 0; props->device_file_by_path[n] != NULL; n++)
@@ -1207,6 +1249,19 @@ do_show_info (const char *object_path)
       for (n = 0; props->linux_lvm2_pv_group_logical_volumes[n] != NULL; n++)
         g_print ("      %s\n", props->linux_lvm2_pv_group_logical_volumes[n]);
     }
+  if (props->device_is_linux_dmmp)
+    {
+      g_print ("  dm-multipath:\n");
+      g_print ("    name:                      %s\n", props->linux_dmmp_name);
+      g_print ("    components:\n");
+      for (n = 0; props->linux_dmmp_slaves[n] != NULL; n++)
+        g_print ("      %s\n", props->linux_dmmp_slaves[n]);
+    }
+  if (props->device_is_linux_dmmp_component)
+    {
+      g_print ("  dm-multipath component:\n");
+      g_print ("    multipath device:          %s\n", props->linux_dmmp_component_holder);
+    }
 
   if (props->device_is_luks)
     {