]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
libblkid: (bcachefs) fix LABEL_SUB probing
authorNikita Ofitserov <himikof@gmail.com>
Sun, 4 Jan 2026 22:41:43 +0000 (01:41 +0300)
committerNikita Ofitserov <himikof@gmail.com>
Wed, 7 Jan 2026 14:45:54 +0000 (17:45 +0300)
It was working only for single-device filesystems and
non-hierarchical device labels before.

Signed-off-by: Nikita Ofitserov <himikof@gmail.com>
libblkid/src/superblocks/bcache.c
tests/expected/blkid/low-probe-bcachefs-3

index b155242a3ac96f013361f3d6599251f7442a0260..08be01363e014d8bb0f8330b88ed9acac7ab5cf5 100644 (file)
@@ -12,6 +12,7 @@
 #include <stdio.h>
 #include <inttypes.h>
 
+#include "buffer.h"
 #include "superblocks.h"
 #include "crc32c.h"
 #include "crc64.h"
@@ -265,7 +266,8 @@ static void probe_bcachefs_sb_members(blkid_probe pr,
                                     const struct bcachefs_super_block *bcs,
                                     const struct bcachefs_sb_field *field,
                                     const struct bcachefs_sb_member *members,
-                                    uint16_t member_bytes, size_t member_array_offset)
+                                    uint16_t member_bytes, size_t member_array_offset,
+                                    uint8_t *current_member_group)
 {
        uint64_t sectors = 0;
        uint8_t i;
@@ -290,22 +292,66 @@ static void probe_bcachefs_sb_members(blkid_probe pr,
                sectors += le64_to_cpu(member->nbuckets) * le16_to_cpu(member->bucket_size);
        }
        blkid_probe_set_fssize(pr, sectors * BCACHEFS_SECTOR_SIZE);
+
+       if (member_bytes < FIELD_END(struct bcachefs_sb_member, flags))
+               return;
+
+       *current_member_group = le64_to_cpu(current->flags) >> 20 & 0xFF;
 }
 
-static void probe_bcachefs_sb_disk_groups(blkid_probe pr,
-                                         const struct bcachefs_super_block *bcs,
-                                         const struct bcachefs_sb_field *field,
-                                         uint8_t dev_idx)
+static void probe_bcachefs_device_label(blkid_probe pr,
+                                       const struct bcachefs_sb_field *field,
+                                       unsigned group_idx)
 {
-       struct bcachefs_sb_field_disk_groups *disk_groups =
-                       (struct bcachefs_sb_field_disk_groups *) field;
+       const struct bcachefs_sb_field_disk_groups *disk_groups =
+                       (const struct bcachefs_sb_field_disk_groups *) field;
+       const struct bcachefs_sb_disk_group *group;
+       size_t groups_nr = (BYTES(field) - offsetof(__typeof__(*disk_groups), disk_groups)) / sizeof(*group);
+       size_t nr = 0;
+       size_t total_sublabel_length = 0;
+       uint16_t path[32];
+       struct ul_buffer buf = UL_INIT_BUFFER;
+
+       while (1) {
+               if (nr == ARRAY_SIZE(path) || group_idx >= groups_nr)
+                       return;
+
+               group = disk_groups->disk_groups + group_idx;
+
+               uint64_t group_flags_0 = le64_to_cpu(group->flags[0]);
+               bool deleted = group_flags_0 & 0x1;
+               uint32_t parent = group_flags_0 >> 6 & ((1 << 18) - 1);
+
+               if (deleted)
+                       return;
+
+               path[nr++] = group_idx;
+               total_sublabel_length += strnlen((const char*)group->label, sizeof(group->label));
 
-       if (BYTES(field) != offsetof(__typeof__(*disk_groups), disk_groups[bcs->nr_devices]))
+               if (!parent)
+                       break;
+               group_idx = parent - 1;
+       }
+
+       if (ul_buffer_alloc_data(&buf, total_sublabel_length + nr + 1))
                return;
 
-       blkid_probe_set_id_label(pr, "LABEL_SUB",
-                                disk_groups->disk_groups[dev_idx].label,
-                                sizeof(disk_groups->disk_groups[dev_idx].label));
+       while (nr--) {
+               group_idx = path[nr];
+               group = disk_groups->disk_groups + group_idx;
+
+               ul_buffer_append_data(&buf, (const char*)group->label,
+                       strnlen((const char*)group->label, sizeof(group->label)));
+               ul_buffer_append_data(&buf, nr ? "." : "", 1);
+       }
+
+       if (!ul_buffer_is_empty(&buf)) {
+               blkid_probe_set_id_label(pr, "LABEL_SUB",
+                                        (const unsigned char*)ul_buffer_get_data(&buf, NULL, NULL),
+                                        ul_buffer_get_datasiz(&buf));
+       }
+
+       ul_buffer_free_data(&buf);
 }
 
 static int is_within_range(const void *start, uint64_t size, const void *end)
@@ -323,6 +369,8 @@ static void probe_bcachefs_sb_fields(blkid_probe pr, const struct bcachefs_super
                                     const unsigned char *sb_start, const unsigned char *sb_end)
 {
        const unsigned char *field_addr = sb_start + BCACHEFS_SB_FIELDS_OFF;
+       const struct bcachefs_sb_field_disk_groups *disk_groups = NULL;
+       uint8_t current_member_group = 0;       /* 0 means unset */
 
        while (1) {
                struct bcachefs_sb_field *field = (struct bcachefs_sb_field *) field_addr;
@@ -347,20 +395,25 @@ static void probe_bcachefs_sb_fields(blkid_probe pr, const struct bcachefs_super
                if (type == BCACHEFS_SB_FIELD_TYPE_MEMBERS_V1) {
                        struct bcachefs_sb_field_members_v1 *members = (struct bcachefs_sb_field_members_v1 *) field;
                        probe_bcachefs_sb_members(pr, bcs, field, members->members,
-                               BCACHEFS_SB_FIELD_MEMBERS_V1_BYTES, offsetof(__typeof__(*members), members));
+                               BCACHEFS_SB_FIELD_MEMBERS_V1_BYTES, offsetof(__typeof__(*members), members),
+                               &current_member_group);
                }
 
                if (type == BCACHEFS_SB_FIELD_TYPE_MEMBERS_V2) {
                        struct bcachefs_sb_field_members_v2 *members = (struct bcachefs_sb_field_members_v2 *) field;
                        probe_bcachefs_sb_members(pr, bcs, field, members->members,
-                               le16_to_cpu(members->member_bytes), offsetof(__typeof__(*members), members));
+                               le16_to_cpu(members->member_bytes), offsetof(__typeof__(*members), members),
+                               &current_member_group);
                }
 
                if (type == BCACHEFS_SB_FIELD_TYPE_DISK_GROUPS)
-                       probe_bcachefs_sb_disk_groups(pr, bcs, field, bcs->dev_idx);
+                       disk_groups = (const struct bcachefs_sb_field_disk_groups *) field;
 
                field_addr += BYTES(field);
        }
+
+       if (disk_groups && current_member_group)
+               probe_bcachefs_device_label(pr, &disk_groups->field, current_member_group - 1);
 }
 
 static int bcachefs_validate_checksum(blkid_probe pr, const struct bcachefs_super_block *bcs,
index 4ac5cb9cf31311af3b73a563e9fae1a69308e685..3724aee795c772e774eccf566948ff3625a5a008 100644 (file)
@@ -3,6 +3,8 @@ ID_FS_FSBLOCKSIZE=512
 ID_FS_FSSIZE=8388608
 ID_FS_LABEL=FS_Label
 ID_FS_LABEL_ENC=FS\x20Label
+ID_FS_LABEL_SUB=dev.1
+ID_FS_LABEL_SUB_ENC=dev.1
 ID_FS_TYPE=bcachefs
 ID_FS_USAGE=filesystem
 ID_FS_UUID=2262eedd-dc30-474e-b0b4-1d02cb3aa30d