From e09b6ffceb194871046d516225bae7e77b6868ab Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Thu, 20 Sep 2012 13:13:23 -0400 Subject: [PATCH] Handle nested arrays We actually need mdadm(8) changes for this to work properly otherwise the MD_* properties end up overwriting each other. We also have to be careful since a single GUdevDevice instance can be in two distinct UDisksLinuxMDRaidObject. http://people.freedesktop.org/~david/gnome-disks-mdraid-20120920-1.png Signed-off-by: David Zeuthen --- src/udiskslinuxblock.c | 36 +++--- src/udiskslinuxmdraid.c | 85 +++++++++----- src/udiskslinuxmdraidobject.c | 264 +++++++++++++++++++++++++++--------------- src/udiskslinuxmdraidobject.h | 8 +- src/udiskslinuxprovider.c | 131 +++++++++++++++------ 5 files changed, 348 insertions(+), 176 deletions(-) diff --git a/src/udiskslinuxblock.c b/src/udiskslinuxblock.c index 5038e78..609098a 100644 --- a/src/udiskslinuxblock.c +++ b/src/udiskslinuxblock.c @@ -278,29 +278,35 @@ update_mdraid (UDisksLinuxBlock *block, GDBusObjectManagerServer *object_manager) { UDisksBlock *iface = UDISKS_BLOCK (block); - const gchar *md_uuid; + const gchar *uuid; const gchar *objpath_mdraid = "/"; const gchar *objpath_mdraid_member = "/"; UDisksLinuxMDRaidObject *object = NULL; - md_uuid = g_udev_device_get_property (device, "MD_UUID"); - if (md_uuid == NULL || strlen (md_uuid) == 0) - goto out; - - object = find_mdraid (object_manager, md_uuid); - if (object == NULL) - goto out; + uuid = g_udev_device_get_property (device, "MD_UUID"); + if (uuid != NULL && strlen (uuid) > 0) + { + object = find_mdraid (object_manager, uuid); + if (object != NULL) + { + objpath_mdraid = g_dbus_object_get_object_path (G_DBUS_OBJECT (object)); + g_clear_object (&object); + } + } - /* TODO: find a better way to distinguish member vs array ? */ - if (g_str_has_prefix (g_udev_device_get_device_file (device), "/dev/md")) - objpath_mdraid = g_dbus_object_get_object_path (G_DBUS_OBJECT (object)); - else - objpath_mdraid_member = g_dbus_object_get_object_path (G_DBUS_OBJECT (object)); + uuid = g_udev_device_get_property (device, "MD_MEMBER_UUID"); + if (uuid != NULL && strlen (uuid) > 0) + { + object = find_mdraid (object_manager, uuid); + if (object != NULL) + { + objpath_mdraid_member = g_dbus_object_get_object_path (G_DBUS_OBJECT (object)); + g_clear_object (&object); + } + } - out: udisks_block_set_mdraid (iface, objpath_mdraid); udisks_block_set_mdraid_member (iface, objpath_mdraid_member); - g_clear_object (&object); } /* ---------------------------------------------------------------------------------------------------- */ diff --git a/src/udiskslinuxmdraid.c b/src/udiskslinuxmdraid.c index e71ca3d..fe1e3d1 100644 --- a/src/udiskslinuxmdraid.c +++ b/src/udiskslinuxmdraid.c @@ -189,6 +189,7 @@ on_polling_timout (gpointer user_data) { UDisksLinuxMDRaid *mdraid = UDISKS_LINUX_MDRAID (user_data); UDisksLinuxMDRaidObject *object = NULL; + GUdevDevice *raid_device; udisks_debug ("polling timeout"); @@ -197,7 +198,12 @@ on_polling_timout (gpointer user_data) goto out; /* synthesize uevent */ - udisks_linux_mdraid_object_uevent (object, "change", NULL); + raid_device = udisks_linux_mdraid_object_get_device (object); + if (raid_device != NULL) + { + udisks_linux_mdraid_object_uevent (object, "change", raid_device, FALSE); + g_object_unref (raid_device); + } out: g_clear_object (&object); @@ -271,6 +277,8 @@ udisks_linux_mdraid_update (UDisksLinuxMDRaid *mdraid, GList *member_devices = NULL; GUdevDevice *device = NULL; const gchar *level = NULL; + const gchar *uuid = NULL; + const gchar *name = NULL; gchar *sync_action = NULL; gchar *sync_completed = NULL; gchar *bitmap_location = NULL; @@ -300,12 +308,21 @@ udisks_linux_mdraid_update (UDisksLinuxMDRaid *mdraid, * former, if available */ if (member_devices != NULL) - device = G_UDEV_DEVICE (member_devices->data); + { + device = G_UDEV_DEVICE (member_devices->data); + num_devices = g_udev_device_get_property_as_int (device, "MD_MEMBER_DEVICES"); + level = g_udev_device_get_property (device, "MD_MEMBER_LEVEL"); + uuid = g_udev_device_get_property (device, "MD_MEMBER_UUID"); + name = g_udev_device_get_property (device, "MD_MEMBER_NAME"); + } else - device = G_UDEV_DEVICE (raid_device); - - num_devices = g_udev_device_get_property_as_int (device, "MD_DEVICES"); - level = g_udev_device_get_property (device, "MD_LEVEL"); + { + device = G_UDEV_DEVICE (raid_device); + num_devices = g_udev_device_get_property_as_int (device, "MD_DEVICES"); + level = g_udev_device_get_property (device, "MD_LEVEL"); + uuid = g_udev_device_get_property (device, "MD_UUID"); + name = g_udev_device_get_property (device, "MD_NAME"); + } /* figure out size */ if (raid_device != NULL) @@ -317,8 +334,8 @@ udisks_linux_mdraid_update (UDisksLinuxMDRaid *mdraid, /* TODO: need MD_ARRAY_SIZE, see https://bugs.freedesktop.org/show_bug.cgi?id=53239#c5 */ } - udisks_mdraid_set_uuid (iface, g_udev_device_get_property (device, "MD_UUID")); - udisks_mdraid_set_name (iface, g_udev_device_get_property (device, "MD_NAME")); + udisks_mdraid_set_uuid (iface, uuid); + udisks_mdraid_set_name (iface, name); udisks_mdraid_set_level (iface, level); udisks_mdraid_set_num_devices (iface, num_devices); udisks_mdraid_set_size (iface, size); @@ -331,6 +348,7 @@ udisks_linux_mdraid_update (UDisksLinuxMDRaid *mdraid, */ if (num_members >= num_devices) can_start = TRUE; + if (g_strcmp0 (level, "raid1") == 0 || g_strcmp0 (level, "raid4") == 0 || g_strcmp0 (level, "raid5") == 0 || @@ -349,18 +367,29 @@ udisks_linux_mdraid_update (UDisksLinuxMDRaid *mdraid, if (raid_device != NULL) { - /* Can't use GUdevDevice methods as they cache the result and these variables vary */ - degraded = read_sysfs_attr_as_int (raid_device, "md/degraded"); - sync_action = read_sysfs_attr (raid_device, "md/sync_action"); - if (sync_action != NULL) - g_strstrip (sync_action); - sync_completed = read_sysfs_attr (raid_device, "md/sync_completed"); - if (sync_completed != NULL) - g_strstrip (sync_completed); - bitmap_location = read_sysfs_attr (raid_device, "md/bitmap/location"); - if (bitmap_location != NULL) - g_strstrip (bitmap_location); - chunk_size = read_sysfs_attr_as_uint64 (raid_device, "md/chunk_size"); + if (g_strcmp0 (level, "raid1") == 0 || + g_strcmp0 (level, "raid4") == 0 || + g_strcmp0 (level, "raid5") == 0 || + g_strcmp0 (level, "raid6") == 0 || + g_strcmp0 (level, "raid10") == 0) + { + /* Can't use GUdevDevice methods as they cache the result and these variables vary */ + degraded = read_sysfs_attr_as_int (raid_device, "md/degraded"); + sync_action = read_sysfs_attr (raid_device, "md/sync_action"); + if (sync_action != NULL) + g_strstrip (sync_action); + sync_completed = read_sysfs_attr (raid_device, "md/sync_completed"); + if (sync_completed != NULL) + g_strstrip (sync_completed); + bitmap_location = read_sysfs_attr (raid_device, "md/bitmap/location"); + if (bitmap_location != NULL) + g_strstrip (bitmap_location); + chunk_size = read_sysfs_attr_as_uint64 (raid_device, "md/chunk_size"); + } + else + { + /* no redundancy, e.g. raid0 or linear */ + } } udisks_mdraid_set_degraded (iface, degraded); udisks_mdraid_set_sync_action (iface, sync_action); @@ -414,8 +443,8 @@ udisks_linux_mdraid_update (UDisksLinuxMDRaid *mdraid, if (md_dir != NULL) { gchar buf[256]; - const gchar *name; - while ((name = g_dir_read_name (md_dir)) != NULL) + const gchar *file_name; + while ((file_name = g_dir_read_name (md_dir)) != NULL) { gchar *block_sysfs_path = NULL; UDisksObject *member_object = NULL; @@ -425,10 +454,10 @@ udisks_linux_mdraid_update (UDisksLinuxMDRaid *mdraid, gint member_slot_as_int = -1; guint64 member_errors = 0; - if (!g_str_has_prefix (name, "dev-")) + if (!g_str_has_prefix (file_name, "dev-")) goto member_done; - snprintf (buf, sizeof (buf), "%s/block", name); + snprintf (buf, sizeof (buf), "%s/block", file_name); block_sysfs_path = udisks_daemon_util_resolve_link (md_dir_name, buf); if (block_sysfs_path == NULL) { @@ -444,7 +473,7 @@ udisks_linux_mdraid_update (UDisksLinuxMDRaid *mdraid, goto member_done; } - snprintf (buf, sizeof (buf), "md/%s/state", name); + snprintf (buf, sizeof (buf), "md/%s/state", file_name); member_state = read_sysfs_attr (raid_device, buf); if (member_state != NULL) { @@ -452,7 +481,7 @@ udisks_linux_mdraid_update (UDisksLinuxMDRaid *mdraid, member_state_elements = g_strsplit (member_state, ",", 0); } - snprintf (buf, sizeof (buf), "md/%s/slot", name); + snprintf (buf, sizeof (buf), "md/%s/slot", file_name); member_slot = read_sysfs_attr (raid_device, buf); if (member_slot != NULL) { @@ -461,7 +490,7 @@ udisks_linux_mdraid_update (UDisksLinuxMDRaid *mdraid, member_slot_as_int = atoi (member_slot); } - snprintf (buf, sizeof (buf), "md/%s/errors", name); + snprintf (buf, sizeof (buf), "md/%s/errors", file_name); member_errors = read_sysfs_attr_as_uint64 (raid_device, buf); g_ptr_array_add (p, @@ -496,8 +525,6 @@ udisks_linux_mdraid_update (UDisksLinuxMDRaid *mdraid, } udisks_mdraid_set_active_devices (iface, g_variant_builder_end (&builder)); - /* TODO: set other stuff */ - out: g_free (sync_completed); g_free (sync_action); diff --git a/src/udiskslinuxmdraidobject.c b/src/udiskslinuxmdraidobject.c index 9895c44..a46a4d8 100644 --- a/src/udiskslinuxmdraidobject.c +++ b/src/udiskslinuxmdraidobject.c @@ -55,8 +55,14 @@ struct _UDisksLinuxMDRaidObject UDisksDaemon *daemon; + /* The UUID for the object */ + gchar *uuid; + + /* The GUdevDevice for the RAID device (e.g. /dev/md0), if any */ + GUdevDevice *raid_device; + /* list of GUdevDevice objects for detected member devices */ - GList *devices; + GList *member_devices; /* interfaces */ UDisksMDRaid *iface_mdraid; @@ -74,8 +80,8 @@ struct _UDisksLinuxMDRaidObjectClass enum { PROP_0, + PROP_UUID, PROP_DAEMON, - PROP_DEVICE }; static void @@ -104,12 +110,16 @@ udisks_linux_mdraid_object_finalize (GObject *_object) remove_watches (object); - g_list_foreach (object->devices, (GFunc) g_object_unref, NULL); - g_list_free (object->devices); - if (object->iface_mdraid != NULL) g_object_unref (object->iface_mdraid); + g_clear_object (&object->raid_device); + + g_list_foreach (object->member_devices, (GFunc) g_object_unref, NULL); + g_list_free (object->member_devices); + + g_free (object->uuid); + if (G_OBJECT_CLASS (udisks_linux_mdraid_object_parent_class)->finalize != NULL) G_OBJECT_CLASS (udisks_linux_mdraid_object_parent_class)->finalize (_object); } @@ -150,14 +160,8 @@ udisks_linux_mdraid_object_set_property (GObject *__object, object->daemon = g_value_get_object (value); break; - case PROP_DEVICE: - /* initial coldplug */ - { - GUdevDevice *device; - g_assert (object->devices == NULL); - device = G_UDEV_DEVICE (g_value_get_object (value)); - udisks_linux_mdraid_object_uevent (object, "add", device); - } + case PROP_UUID: + object->uuid = g_value_dup_string (value); break; default: @@ -200,7 +204,7 @@ udisks_linux_mdraid_object_constructed (GObject *_object) gchar *s; /* compute the object path */ - uuid = udisks_mdraid_dup_uuid (object->iface_mdraid); + uuid = g_strdup (object->uuid); strip_and_replace_with_uscore (uuid); s = g_strdup_printf ("/org/freedesktop/UDisks2/mdraid/%s", uuid); g_free (uuid); @@ -239,41 +243,39 @@ udisks_linux_mdraid_object_class_init (UDisksLinuxMDRaidObjectClass *klass) G_PARAM_STATIC_STRINGS)); /** - * UDisksLinuxMDRaidObject:device: + * UDisksLinuxMDRaidObject:uuid: * - * The #GUdevDevice for the object. Connect to the #GObject::notify - * signal to get notified whenever this is updated. + * The UUID for the array. */ g_object_class_install_property (gobject_class, - PROP_DEVICE, - g_param_spec_object ("device", - "Device", - "The device for the object", - G_UDEV_TYPE_DEVICE, + PROP_UUID, + g_param_spec_string ("uuid", + "UUID", + "The UUID for the array", + NULL, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - } /** * udisks_linux_mdraid_object_new: * @daemon: A #UDisksDaemon. - * @device: The #GUdevDevice for the sysfs block device. + * @uuid: The UUID for the array. * - * Create a new mdraid object. + * Create a new MDRaid object. * * Returns: A #UDisksLinuxMDRaidObject object. Free with g_object_unref(). */ UDisksLinuxMDRaidObject * udisks_linux_mdraid_object_new (UDisksDaemon *daemon, - GUdevDevice *device) + const gchar *uuid) { g_return_val_if_fail (UDISKS_IS_DAEMON (daemon), NULL); - g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL); + g_return_val_if_fail (uuid != NULL, NULL); return UDISKS_LINUX_MDRAID_OBJECT (g_object_new (UDISKS_TYPE_LINUX_MDRAID_OBJECT, "daemon", daemon, - "device", device, + "uuid", uuid, NULL)); } @@ -305,19 +307,12 @@ GList * udisks_linux_mdraid_object_get_members (UDisksLinuxMDRaidObject *object) { GList *ret = NULL; - GList *l; g_return_val_if_fail (UDISKS_IS_LINUX_MDRAID_OBJECT (object), NULL); - for (l = object->devices; l != NULL; l = l->next) - { - GUdevDevice *device = G_UDEV_DEVICE (l->data); - /* TODO: find a better way to distinguish member vs array ? */ - if (!g_str_has_prefix (g_udev_device_get_device_file (device), "/dev/md")) - { - ret = g_list_prepend (ret, g_object_ref (device)); - } - } + ret = g_list_copy (object->member_devices); + g_list_foreach (ret, (GFunc) g_object_ref, NULL); + return ret; } @@ -334,21 +329,11 @@ GUdevDevice * udisks_linux_mdraid_object_get_device (UDisksLinuxMDRaidObject *object) { GUdevDevice *ret = NULL; - GList *l; g_return_val_if_fail (UDISKS_IS_LINUX_MDRAID_OBJECT (object), NULL); - for (l = object->devices; l != NULL; l = l->next) - { - GUdevDevice *device = G_UDEV_DEVICE (l->data); - /* TODO: find a better way to distinguish member vs array ? */ - if (g_str_has_prefix (g_udev_device_get_device_file (device), "/dev/md")) - { - ret = g_object_ref (device); - goto out; - } - } - out: + ret = object->raid_device != NULL ? g_object_ref (object->raid_device) : NULL; + return ret; } @@ -441,13 +426,14 @@ mdraid_update (UDisksLinuxMDRaidObject *object, /* ---------------------------------------------------------------------------------------------------- */ static GList * -find_link_for_sysfs_path (UDisksLinuxMDRaidObject *object, - const gchar *sysfs_path) +find_link_for_sysfs_path_for_member (UDisksLinuxMDRaidObject *object, + const gchar *sysfs_path) { GList *l; GList *ret; ret = NULL; - for (l = object->devices; l != NULL; l = l->next) + + for (l = object->member_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) @@ -504,6 +490,7 @@ attr_changed (GIOChannel *channel, gpointer user_data) { UDisksLinuxMDRaidObject *object = UDISKS_LINUX_MDRAID_OBJECT (user_data); + gboolean bail = TRUE; GError *error = NULL; gchar *str = NULL; gsize len = 0; @@ -513,26 +500,31 @@ attr_changed (GIOChannel *channel, if (g_io_channel_seek_position (channel, 0, G_SEEK_SET, &error) != G_IO_STATUS_NORMAL) { - udisks_warning ("Error seeking in channel: %s (%s, %d)", - error->message, g_quark_to_string (error->domain), error->code); + udisks_warning ("Error seeking in channel (uuid %s): %s (%s, %d)", + object->uuid, error->message, g_quark_to_string (error->domain), error->code); g_clear_error (&error); + bail = TRUE; goto out; } if (g_io_channel_read_to_end (channel, &str, &len, &error) != G_IO_STATUS_NORMAL) { - udisks_warning ("Error reading: %s (%s, %d)", - error->message, g_quark_to_string (error->domain), error->code); + udisks_warning ("Error reading (uuid %s): %s (%s, %d)", + object->uuid, error->message, g_quark_to_string (error->domain), error->code); g_clear_error (&error); + bail = TRUE; goto out; } g_free (str); /* synthesize uevent */ - udisks_linux_mdraid_object_uevent (object, "change", NULL); + if (object->raid_device != NULL) + udisks_linux_mdraid_object_uevent (object, "change", object->raid_device, FALSE); out: + if (bail) + remove_watches (object); return TRUE; /* keep event source around */ } @@ -548,8 +540,8 @@ attr_changed (GIOChannel *channel, */ static void -md_device_added (UDisksLinuxMDRaidObject *object, - GUdevDevice *device) +raid_device_added (UDisksLinuxMDRaidObject *object, + GUdevDevice *device) { g_assert (object->sync_action_source == NULL); g_assert (object->degraded_source == NULL); @@ -566,8 +558,8 @@ md_device_added (UDisksLinuxMDRaidObject *object, } static void -md_device_removed (UDisksLinuxMDRaidObject *object, - GUdevDevice *device) +raid_device_removed (UDisksLinuxMDRaidObject *object, + GUdevDevice *device) { /* udisks_debug ("stop watching %s", g_udev_device_get_sysfs_path (device)); */ remove_watches (object); @@ -580,73 +572,153 @@ md_device_removed (UDisksLinuxMDRaidObject *object, * @object: A #UDisksLinuxMDRaidObject. * @action: Uevent action or %NULL * @device: A #GUdevDevice device object or %NULL if the device hasn't changed. + * @is_member: %TRUE if @device is a member, %FALSE if it's the raid device. * * Updates all information on interfaces on @mdraid. */ void udisks_linux_mdraid_object_uevent (UDisksLinuxMDRaidObject *object, const gchar *action, - GUdevDevice *device) + GUdevDevice *device, + gboolean is_member) { - GList *link; gboolean conf_changed = FALSE; - gboolean is_md_device = FALSE; g_return_if_fail (UDISKS_IS_LINUX_MDRAID_OBJECT (object)); - g_return_if_fail (device == NULL || G_UDEV_IS_DEVICE (device)); - - if (device != NULL && - g_str_has_prefix (g_udev_device_get_name (device), "md")) /* TODO: better heuristic */ - is_md_device = TRUE; - - /* Skip partitions of md devices */ - if (is_md_device && device != NULL && - g_strcmp0 (g_udev_device_get_devtype (device), "disk") != 0) - goto out; + g_return_if_fail (G_UDEV_IS_DEVICE (device)); + udisks_debug ("is_member=%d for uuid %s and device %s", is_member, object->uuid, g_udev_device_get_device_file (device)); - link = NULL; - if (device != NULL) - link = find_link_for_sysfs_path (object, g_udev_device_get_sysfs_path (device)); - if (g_strcmp0 (action, "remove") == 0) + if (is_member) { - if (link != NULL) + GList *link = NULL; + link = NULL; + if (device != NULL) + link = find_link_for_sysfs_path_for_member (object, g_udev_device_get_sysfs_path (device)); + + if (g_strcmp0 (action, "remove") == 0) { - if (is_md_device) - md_device_removed (object, device); - g_object_unref (G_UDEV_DEVICE (link->data)); - object->devices = g_list_delete_link (object->devices, link); + if (link != NULL) + { + g_object_unref (G_UDEV_DEVICE (link->data)); + object->member_devices = g_list_delete_link (object->member_devices, link); + } + else + { + udisks_warning ("MDRaid with UUID %s doesn't have member device with sysfs path %s on remove event", + object->uuid, + g_udev_device_get_sysfs_path (device)); + } } else { - udisks_warning ("MDRaid doesn't have device with sysfs path %s on remove event", - g_udev_device_get_sysfs_path (device)); + if (link != NULL) + { + if (device != link->data) + { + g_object_unref (G_UDEV_DEVICE (link->data)); + link->data = g_object_ref (device); + } + } + else + { + if (device != NULL) + { + object->member_devices = g_list_append (object->member_devices, g_object_ref (device)); + } + } } } else { - if (link != NULL) + /* Skip partitions of raid devices */ + if (g_strcmp0 (g_udev_device_get_devtype (device), "disk") != 0) + goto out; + + if (g_strcmp0 (action, "remove") == 0) { - g_object_unref (G_UDEV_DEVICE (link->data)); - link->data = g_object_ref (device); + if (object->raid_device != NULL) + if (g_strcmp0 (g_udev_device_get_sysfs_path (object->raid_device), + g_udev_device_get_sysfs_path (device)) == 0) + { + g_clear_object (&object->raid_device); + raid_device_removed (object, object->raid_device); + } + else + { + udisks_warning ("MDRaid with UUID %s doesn't have raid device with sysfs path %s on remove event (it has %s)", + object->uuid, + g_udev_device_get_sysfs_path (device), + g_udev_device_get_sysfs_path (object->raid_device)); + } + else + { + udisks_warning ("MDRaid with UUID %s doesn't have raid device with sysfs path %s on remove event", + object->uuid, + g_udev_device_get_sysfs_path (device)); + } } else { - if (device != NULL) + if (object->raid_device == NULL) + { + object->raid_device = g_object_ref (device); + raid_device_added (object, object->raid_device); + } + else { - object->devices = g_list_append (object->devices, g_object_ref (device)); - if (is_md_device) - md_device_added (object, device); + if (device != object->raid_device) + { + g_clear_object (&object->raid_device); + object->raid_device = g_object_ref (device); + } } } } - conf_changed = FALSE; - conf_changed |= update_iface (object, action, mdraid_check, mdraid_connect, mdraid_update, - UDISKS_TYPE_LINUX_MDRAID, &object->iface_mdraid); + /* if we don't have any devices, no point in updating (we should get nuked soon anyway) */ + if (udisks_linux_mdraid_object_have_devices (object)) + { + conf_changed = FALSE; + conf_changed |= update_iface (object, action, mdraid_check, mdraid_connect, mdraid_update, + UDISKS_TYPE_LINUX_MDRAID, &object->iface_mdraid); + } out: ; } /* ---------------------------------------------------------------------------------------------------- */ +/** + * udisks_linux_mdraid_object_have_devices: + * @object: A #UDisksLinuxMDRaidObject. + * + * Checks if there are any devices associated with @object at + * all. This includes both member devices and the raid device. + * + * Returns: %TRUE if at least one device is associated with @object, %FALSE otherwise. + */ +gboolean +udisks_linux_mdraid_object_have_devices (UDisksLinuxMDRaidObject *object) +{ + g_return_val_if_fail (UDISKS_IS_LINUX_MDRAID_OBJECT (object), FALSE); + + return g_list_length (object->member_devices) > 0 || object->raid_device != NULL; +} + +/** + * udisks_linux_mdraid_object_get_uuid: + * @object: A #UDisksLinuxMDRaidObject. + * + * Gets the UUID for @object. + * + * Returns: (transfer none): The UUID for object. Do not free, the string belongs to @object. + */ +const gchar * +udisks_linux_mdraid_object_get_uuid (UDisksLinuxMDRaidObject *object) +{ + g_return_val_if_fail (UDISKS_IS_LINUX_MDRAID_OBJECT (object), NULL); + return object->uuid; +} + + diff --git a/src/udiskslinuxmdraidobject.h b/src/udiskslinuxmdraidobject.h index b167481..5094863 100644 --- a/src/udiskslinuxmdraidobject.h +++ b/src/udiskslinuxmdraidobject.h @@ -32,14 +32,18 @@ G_BEGIN_DECLS GType udisks_linux_mdraid_object_get_type (void) G_GNUC_CONST; UDisksLinuxMDRaidObject *udisks_linux_mdraid_object_new (UDisksDaemon *daemon, - GUdevDevice *device); + const gchar *uuid); void udisks_linux_mdraid_object_uevent (UDisksLinuxMDRaidObject *object, const gchar *action, - GUdevDevice *device); + GUdevDevice *device, + gboolean is_member); +const gchar *udisks_linux_mdraid_object_get_uuid (UDisksLinuxMDRaidObject *object); UDisksDaemon *udisks_linux_mdraid_object_get_daemon (UDisksLinuxMDRaidObject *object); GList *udisks_linux_mdraid_object_get_members (UDisksLinuxMDRaidObject *object); GUdevDevice *udisks_linux_mdraid_object_get_device (UDisksLinuxMDRaidObject *object); +gboolean udisks_linux_mdraid_object_have_devices (UDisksLinuxMDRaidObject *object); + G_END_DECLS #endif /* __UDISKS_LINUX_MDRAID_OBJECT_H__ */ diff --git a/src/udiskslinuxprovider.c b/src/udiskslinuxprovider.c index 274e7aa..450ea75 100644 --- a/src/udiskslinuxprovider.c +++ b/src/udiskslinuxprovider.c @@ -68,6 +68,7 @@ struct _UDisksLinuxProvider /* maps from array UUID and sysfs_path to UDisksLinuxMDRaidObject instances */ GHashTable *uuid_to_mdraid; GHashTable *sysfs_path_to_mdraid; + GHashTable *sysfs_path_to_mdraid_members; GFileMonitor *etc_udisks2_dir_monitor; @@ -137,6 +138,7 @@ udisks_linux_provider_finalize (GObject *object) g_hash_table_unref (provider->sysfs_path_to_drive); g_hash_table_unref (provider->uuid_to_mdraid); g_hash_table_unref (provider->sysfs_path_to_mdraid); + g_hash_table_unref (provider->sysfs_path_to_mdraid_members); g_object_unref (provider->gudev_client); udisks_object_skeleton_set_manager (provider->manager_object, NULL); @@ -339,6 +341,10 @@ udisks_linux_provider_start (UDisksProvider *_provider) g_str_equal, g_free, NULL); + provider->sysfs_path_to_mdraid_members = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + NULL); devices = g_udev_client_query_by_subsystem (provider->gudev_client, "block"); /* make sure we process sda before sdz and sdz before sdaa */ @@ -466,46 +472,67 @@ perform_initial_housekeeping_for_drive (GIOSchedulerJob *job, /* ---------------------------------------------------------------------------------------------------- */ /* called with lock held */ + static void -handle_block_uevent_for_mdraid (UDisksLinuxProvider *provider, - const gchar *action, - GUdevDevice *device) +maybe_remove_mdraid_object (UDisksLinuxProvider *provider, + UDisksLinuxMDRaidObject *object) +{ + gchar *object_uuid = NULL; + UDisksDaemon *daemon = NULL; + + /* remove the object only if there are no devices left */ + if (udisks_linux_mdraid_object_have_devices (object)) + goto out; + + daemon = udisks_provider_get_daemon (UDISKS_PROVIDER (provider)); + + object_uuid = g_strdup (udisks_linux_mdraid_object_get_uuid (object)); + g_dbus_object_manager_server_unexport (udisks_daemon_get_object_manager (daemon), + g_dbus_object_get_object_path (G_DBUS_OBJECT (object))); + g_warn_if_fail (g_hash_table_remove (provider->uuid_to_mdraid, object_uuid)); + + out: + g_free (object_uuid); +} + +static void +handle_block_uevent_for_mdraid_with_uuid (UDisksLinuxProvider *provider, + const gchar *action, + GUdevDevice *device, + const gchar *uuid, + gboolean is_member) { UDisksLinuxMDRaidObject *object; UDisksDaemon *daemon; const gchar *sysfs_path; - const gchar *uuid; daemon = udisks_provider_get_daemon (UDISKS_PROVIDER (provider)); sysfs_path = g_udev_device_get_sysfs_path (device); - uuid = g_udev_device_get_property (device, "MD_UUID"); - /* if there's no UUID, treat as remove event */ + /* if uuid is NULL, consider it a remove event */ if (uuid == NULL) action = "remove"; if (g_strcmp0 (action, "remove") == 0) { - object = g_hash_table_lookup (provider->sysfs_path_to_mdraid, sysfs_path); + /* first check if this device was a member */ + object = g_hash_table_lookup (provider->sysfs_path_to_mdraid_members, sysfs_path); if (object != NULL) { - GList *devices; - - udisks_linux_mdraid_object_uevent (object, action, device); + udisks_linux_mdraid_object_uevent (object, action, device, TRUE /* is_member */); + g_warn_if_fail (g_hash_table_remove (provider->sysfs_path_to_mdraid_members, sysfs_path)); + maybe_remove_mdraid_object (provider, object); + udisks_debug ("removed member for %s", sysfs_path); + } + /* then check if the device was the raid device */ + object = g_hash_table_lookup (provider->sysfs_path_to_mdraid, sysfs_path); + if (object != NULL) + { + udisks_linux_mdraid_object_uevent (object, action, device, FALSE /* is_member */); g_warn_if_fail (g_hash_table_remove (provider->sysfs_path_to_mdraid, sysfs_path)); - - devices = udisks_linux_mdraid_object_get_members (object); - if (devices == NULL) - { - const gchar *existing_uuid; - existing_uuid = g_object_get_data (G_OBJECT (object), "x-uuid"); - g_dbus_object_manager_server_unexport (udisks_daemon_get_object_manager (daemon), - g_dbus_object_get_object_path (G_DBUS_OBJECT (object))); - g_warn_if_fail (g_hash_table_remove (provider->uuid_to_mdraid, existing_uuid)); - } - g_list_foreach (devices, (GFunc) g_object_unref, NULL); - g_list_free (devices); + maybe_remove_mdraid_object (provider, object); + udisks_debug ("removed object for %s", sysfs_path); } } else @@ -516,21 +543,29 @@ handle_block_uevent_for_mdraid (UDisksLinuxProvider *provider, object = g_hash_table_lookup (provider->uuid_to_mdraid, uuid); if (object != NULL) { - if (g_hash_table_lookup (provider->sysfs_path_to_mdraid, sysfs_path) == NULL) - g_hash_table_insert (provider->sysfs_path_to_mdraid, g_strdup (sysfs_path), object); - udisks_linux_mdraid_object_uevent (object, action, device); + if (is_member) + { + if (g_hash_table_lookup (provider->sysfs_path_to_mdraid_members, sysfs_path) == NULL) + g_hash_table_insert (provider->sysfs_path_to_mdraid_members, g_strdup (sysfs_path), object); + } + else + { + if (g_hash_table_lookup (provider->sysfs_path_to_mdraid, sysfs_path) == NULL) + g_hash_table_insert (provider->sysfs_path_to_mdraid, g_strdup (sysfs_path), object); + } + udisks_linux_mdraid_object_uevent (object, action, device, is_member); } else { - object = udisks_linux_mdraid_object_new (daemon, device); - if (object != NULL) - { - g_object_set_data_full (G_OBJECT (object), "x-uuid", g_strdup (uuid), g_free); - g_dbus_object_manager_server_export_uniquely (udisks_daemon_get_object_manager (daemon), - G_DBUS_OBJECT_SKELETON (object)); - g_hash_table_insert (provider->uuid_to_mdraid, g_strdup (uuid), object); - g_hash_table_insert (provider->sysfs_path_to_mdraid, g_strdup (sysfs_path), object); - } + object = udisks_linux_mdraid_object_new (daemon, uuid); + udisks_linux_mdraid_object_uevent (object, action, device, is_member); + g_dbus_object_manager_server_export_uniquely (udisks_daemon_get_object_manager (daemon), + G_DBUS_OBJECT_SKELETON (object)); + g_hash_table_insert (provider->uuid_to_mdraid, g_strdup (uuid), object); + if (is_member) + g_hash_table_insert (provider->sysfs_path_to_mdraid_members, g_strdup (sysfs_path), object); + else + g_hash_table_insert (provider->sysfs_path_to_mdraid, g_strdup (sysfs_path), object); } } @@ -538,6 +573,34 @@ handle_block_uevent_for_mdraid (UDisksLinuxProvider *provider, ; } +static void +handle_block_uevent_for_mdraid (UDisksLinuxProvider *provider, + const gchar *action, + GUdevDevice *device) +{ + const gchar *uuid; + const gchar *member_uuid; + + /* For nested RAID levels, a device can be both a member of one + * array and the RAID device for another. Therefore we need to + * consider both UUIDs. + * + * For removal, we also need to consider the case where there is no + * UUID. + */ + uuid = g_udev_device_get_property (device, "MD_UUID"); + member_uuid = g_udev_device_get_property (device, "MD_MEMBER_UUID"); + + if (uuid != NULL) + handle_block_uevent_for_mdraid_with_uuid (provider, action, device, uuid, FALSE); + + if (member_uuid != NULL) + handle_block_uevent_for_mdraid_with_uuid (provider, action, device, member_uuid, TRUE); + + if (uuid == NULL && member_uuid == NULL) + handle_block_uevent_for_mdraid_with_uuid (provider, action, device, NULL, FALSE); +} + /* ---------------------------------------------------------------------------------------------------- */ /* called with lock held */ -- 2.7.4