From cfe5e3905201349e9cf3f95d52ff4bd100bde37d Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 14 Apr 2024 21:10:49 +0200 Subject: Adding upstream version 2.39.3. Signed-off-by: Daniel Baumann --- libblkid/src/superblocks/bcache.c | 425 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 425 insertions(+) create mode 100644 libblkid/src/superblocks/bcache.c (limited to 'libblkid/src/superblocks/bcache.c') diff --git a/libblkid/src/superblocks/bcache.c b/libblkid/src/superblocks/bcache.c new file mode 100644 index 0000000..d3afc41 --- /dev/null +++ b/libblkid/src/superblocks/bcache.c @@ -0,0 +1,425 @@ +/* + * Copyright (C) 2013 Rolf Fokkens + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * + * Based on code fragments from bcache-tools by Kent Overstreet: + * http://evilpiepirate.org/git/bcache-tools.git + */ + +#include +#include +#include + +#include "superblocks.h" +#include "crc32c.h" +#include "crc64.h" +#include "xxhash.h" + +#define SB_LABEL_SIZE 32 +#define SB_JOURNAL_BUCKETS 256U + +/* + * The bcache_super_block is adapted from struct cache_sb in kernel. + * https://github.com/torvalds/linux/blob/master/drivers/md/bcache/bcache_ondisk.h + */ +struct bcache_super_block { + uint64_t csum; + uint64_t offset; /* where this super block was written */ + uint64_t version; + uint8_t magic[16]; /* bcache file system identifier */ + uint8_t uuid[16]; /* device identifier */ + uint8_t set_info[16]; /* magic or uuid */ + uint8_t label[SB_LABEL_SIZE]; + uint64_t flags; + uint64_t seq; + + uint64_t feature_compat; + uint64_t feature_incompat; + uint64_t feature_ro_compat; + + uint64_t pad[5]; + + union { + struct { + /* Cache devices */ + uint64_t nbuckets; /* device size */ + + uint16_t block_size; /* sectors */ + uint16_t bucket_size; /* sectors */ + + uint16_t nr_in_set; + uint16_t nr_this_dev; + }; + struct { + /* Backing devices */ + uint64_t data_offset; + }; + }; + + uint32_t last_mount; + + uint16_t first_bucket; + union { + uint16_t njournal_buckets; + uint16_t keys; + }; + uint64_t d[SB_JOURNAL_BUCKETS]; /* journal buckets */ + uint16_t obso_bucket_size_hi; /* obsoleted */ +} __attribute__((packed)); + +struct bcachefs_sb_field { + uint32_t u64s; + uint32_t type; +} __attribute__((packed)); + +struct bcachefs_sb_member { + uint8_t uuid[16]; + uint64_t nbuckets; + uint16_t first_bucket; + uint16_t bucket_size; + uint32_t pad; + uint64_t last_mount; + uint64_t flags[2]; +} __attribute__((packed)); + +struct bcachefs_sb_field_members { + struct bcachefs_sb_field field; + struct bcachefs_sb_member members[]; +} __attribute__((packed)); + +struct bcachefs_sb_disk_group { + uint8_t label[SB_LABEL_SIZE]; + uint64_t flags[2]; +} __attribute__((packed)); + +struct bcachefs_sb_field_disk_groups { + struct bcachefs_sb_field field; + struct bcachefs_sb_disk_group disk_groups[]; +} __attribute__((packed)); + +enum bcachefs_sb_csum_type { + BCACHEFS_SB_CSUM_TYPE_NONE = 0, + BCACHEFS_SB_CSUM_TYPE_CRC32C = 1, + BCACHEFS_SB_CSUM_TYPE_CRC64 = 2, + BCACHEFS_SB_CSUM_TYPE_XXHASH = 7, +}; + +union bcachefs_sb_csum { + uint32_t crc32c; + uint64_t crc64; + XXH64_hash_t xxh64; + uint8_t raw[16]; +} __attribute__((packed)); + +struct bcachefs_sb_layout { + uint8_t magic[16]; + uint8_t layout_type; + uint8_t sb_max_size_bits; + uint8_t nr_superblocks; + uint8_t pad[5]; + uint64_t sb_offset[61]; +} __attribute__((packed)); + +struct bcachefs_super_block { + union bcachefs_sb_csum csum; + uint16_t version; + uint16_t version_min; + uint16_t pad[2]; + uint8_t magic[16]; + uint8_t uuid[16]; + uint8_t user_uuid[16]; + uint8_t label[SB_LABEL_SIZE]; + uint64_t offset; + uint64_t seq; + uint16_t block_size; + uint8_t dev_idx; + uint8_t nr_devices; + uint32_t u64s; + uint64_t time_base_lo; + uint32_t time_base_hi; + uint32_t time_precision; + uint64_t flags[8]; + uint64_t features[2]; + uint64_t compat[2]; + struct bcachefs_sb_layout layout; + struct bcachefs_sb_field _start[]; +} __attribute__((packed)); + +/* magic string */ +#define BCACHE_SB_MAGIC "\xc6\x85\x73\xf6\x4e\x1a\x45\xca\x82\x65\xf5\x7f\x48\xba\x6d\x81" +#define BCACHEFS_SB_MAGIC "\xc6\x85\x73\xf6\x66\xce\x90\xa9\xd9\x6a\x60\xcf\x80\x3d\xf7\xef" +/* magic string len */ +#define BCACHE_SB_MAGIC_LEN (sizeof(BCACHE_SB_MAGIC) - 1) +/* super block offset */ +#define BCACHE_SB_OFF 0x1000U +/* supper block offset in kB */ +#define BCACHE_SB_KBOFF (BCACHE_SB_OFF >> 10) +/* magic string offset within super block */ +#define BCACHE_SB_MAGIC_OFF offsetof(struct bcache_super_block, magic) +/* start of checksummed data within superblock */ +#define BCACHE_SB_CSUMMED_START 8U +/* granularity of offset and length fields within superblock */ +#define BCACHEFS_SECTOR_SIZE 512U +/* maximum superblock size shift */ +#define BCACHEFS_SB_MAX_SIZE_SHIFT 0x10U +/* maximum superblock size */ +#define BCACHEFS_SB_MAX_SIZE (1U << BCACHEFS_SB_MAX_SIZE_SHIFT) +/* fields offset within super block */ +#define BCACHEFS_SB_FIELDS_OFF offsetof(struct bcachefs_super_block, _start) +/* tag value for members field */ +#define BCACHEFS_SB_FIELD_TYPE_MEMBERS 1 +/* tag value for disk_groups field */ +#define BCACHEFS_SB_FIELD_TYPE_DISK_GROUPS 5 +/* version splitting helpers */ +#define BCH_VERSION_MAJOR(_v) ((uint16_t) ((_v) >> 10)) +#define BCH_VERSION_MINOR(_v) ((uint16_t) ((_v) & ~(~0U << 10))) + +#define BYTES(f) ((((uint64_t) le32_to_cpu((f)->u64s)) * 8)) + +static int bcache_verify_checksum(blkid_probe pr, const struct blkid_idmag *mag, + const struct bcache_super_block *bcs) +{ + const unsigned char *csummed; + size_t csummed_size; + uint64_t csum; + + if (le16_to_cpu(bcs->keys) > ARRAY_SIZE(bcs->d)) + return 0; + + /* up to the end of bcs->d[] */ + csummed_size = offsetof(typeof(*bcs), d) + + sizeof(bcs->d[0]) * le16_to_cpu(bcs->keys); + csummed = blkid_probe_get_sb_buffer(pr, mag, csummed_size); + csum = ul_crc64_we(csummed + BCACHE_SB_CSUMMED_START, + csummed_size - BCACHE_SB_CSUMMED_START); + return blkid_probe_verify_csum(pr, csum, le64_to_cpu(bcs->csum)); +} + +static int probe_bcache (blkid_probe pr, const struct blkid_idmag *mag) +{ + struct bcache_super_block *bcs; + + bcs = blkid_probe_get_sb(pr, mag, struct bcache_super_block); + if (!bcs) + return errno ? -errno : BLKID_PROBE_NONE; + + if (!bcache_verify_checksum(pr, mag, bcs)) + return BLKID_PROBE_NONE; + + if (le64_to_cpu(bcs->offset) != BCACHE_SB_OFF / 512) + return BLKID_PROBE_NONE; + + if (blkid_probe_set_uuid(pr, bcs->uuid) < 0) + return BLKID_PROBE_NONE; + + blkid_probe_set_wiper(pr, 0, BCACHE_SB_OFF); + + return BLKID_PROBE_OK; +} + +static void probe_bcachefs_sb_members(blkid_probe pr, + const struct bcachefs_super_block *bcs, + const struct bcachefs_sb_field *field, + uint8_t dev_idx) +{ + struct bcachefs_sb_field_members *members = + (struct bcachefs_sb_field_members *) field; + uint64_t sectors = 0; + uint8_t i; + + if (BYTES(field) != offsetof(typeof(*members), members[bcs->nr_devices])) + return; + + blkid_probe_set_uuid_as(pr, members->members[dev_idx].uuid, "UUID_SUB"); + + for (i = 0; i < bcs->nr_devices; i++) { + struct bcachefs_sb_member *member = &members->members[i]; + sectors += le64_to_cpu(member->nbuckets) * le16_to_cpu(member->bucket_size); + } + blkid_probe_set_fssize(pr, sectors * BCACHEFS_SECTOR_SIZE); +} + +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) +{ + struct bcachefs_sb_field_disk_groups *disk_groups = + (struct bcachefs_sb_field_disk_groups *) field; + + if (BYTES(field) != offsetof(typeof(*disk_groups), disk_groups[bcs->nr_devices])) + return; + + blkid_probe_set_id_label(pr, "LABEL_SUB", + disk_groups->disk_groups[dev_idx].label, + sizeof(disk_groups->disk_groups[dev_idx].label)); +} + +static int is_within_range(void *start, uint64_t size, void *end) +{ + ptrdiff_t diff; + + if (start >= end) + return 0; // should not happen + + diff = (unsigned char *) end - (unsigned char *) start; + return size <= (uint64_t) diff; +} + +static void probe_bcachefs_sb_fields(blkid_probe pr, const struct bcachefs_super_block *bcs, + unsigned char *sb_start, unsigned char *sb_end) +{ + unsigned char *field_addr = sb_start + BCACHEFS_SB_FIELDS_OFF; + + while (1) { + struct bcachefs_sb_field *field = (struct bcachefs_sb_field *) field_addr; + uint64_t field_size; + uint32_t type; + + if (!is_within_range(field, sizeof(*field), sb_end)) + break; + + field_size = BYTES(field); + + if (field_size < sizeof(*field)) + break; + + if (!is_within_range(field, field_size, sb_end)) + break; + + type = le32_to_cpu(field->type); + if (!type) + break; + + if (type == BCACHEFS_SB_FIELD_TYPE_MEMBERS) + probe_bcachefs_sb_members(pr, bcs, field, bcs->dev_idx); + + if (type == BCACHEFS_SB_FIELD_TYPE_DISK_GROUPS) + probe_bcachefs_sb_disk_groups(pr, bcs, field, bcs->dev_idx); + + field_addr += BYTES(field); + } +} + +static int bcachefs_validate_checksum(blkid_probe pr, const struct bcachefs_super_block *bcs, + unsigned char *sb, unsigned char *sb_end) +{ + uint8_t checksum_type = be64_to_cpu(bcs->flags[0]) >> 58; + unsigned char *checksummed_data_start = sb + sizeof(bcs->csum); + size_t checksummed_data_size = sb_end - checksummed_data_start; + + switch (checksum_type) { + case BCACHEFS_SB_CSUM_TYPE_NONE: + return 1; + case BCACHEFS_SB_CSUM_TYPE_CRC32C: { + uint32_t crc = crc32c(~0LL, checksummed_data_start, checksummed_data_size) ^ ~0LL; + return blkid_probe_verify_csum(pr, crc, le32_to_cpu(bcs->csum.crc32c)); + } + case BCACHEFS_SB_CSUM_TYPE_CRC64: { + uint64_t crc = ul_crc64_we(checksummed_data_start, checksummed_data_size); + return blkid_probe_verify_csum(pr, crc, le64_to_cpu(bcs->csum.crc64)); + } + case BCACHEFS_SB_CSUM_TYPE_XXHASH: { + XXH64_hash_t xxh64 = XXH64(checksummed_data_start, checksummed_data_size, 0); + return blkid_probe_verify_csum(pr, xxh64, le64_to_cpu(bcs->csum.xxh64)); + } + default: + DBG(LOWPROBE, ul_debug("bcachefs: unknown checksum type %d, ignoring.", checksum_type)); + return 1; + } +} + +static int probe_bcachefs(blkid_probe pr, const struct blkid_idmag *mag) +{ + struct bcachefs_super_block *bcs; + unsigned char *sb, *sb_end; + uint64_t sb_size, blocksize; + uint16_t version; + + bcs = blkid_probe_get_sb(pr, mag, struct bcachefs_super_block); + if (!bcs) + return errno ? -errno : BLKID_PROBE_NONE; + + if (le64_to_cpu(bcs->offset) != BCACHE_SB_OFF / BCACHEFS_SECTOR_SIZE) + return BLKID_PROBE_NONE; + + if (bcs->nr_devices == 0 || bcs->dev_idx >= bcs->nr_devices) + return BLKID_PROBE_NONE; + + sb_size = BCACHEFS_SB_FIELDS_OFF + BYTES(bcs); + + if (sb_size > BCACHEFS_SB_MAX_SIZE) + return BLKID_PROBE_NONE; + + if (bcs->layout.sb_max_size_bits > BCACHEFS_SB_MAX_SIZE_SHIFT) + return BLKID_PROBE_NONE; + + if (sb_size > (BCACHEFS_SECTOR_SIZE << bcs->layout.sb_max_size_bits)) + return BLKID_PROBE_NONE; + + sb = blkid_probe_get_sb_buffer(pr, mag, sb_size); + if (!sb) + return BLKID_PROBE_NONE; + sb_end = sb + sb_size; + + if (!bcachefs_validate_checksum(pr, bcs, sb, sb_end)) + return BLKID_PROBE_NONE; + + blkid_probe_set_uuid(pr, bcs->user_uuid); + blkid_probe_set_label(pr, bcs->label, sizeof(bcs->label)); + version = le16_to_cpu(bcs->version); + blkid_probe_sprintf_version(pr, "%"PRIu16".%"PRIu16, + BCH_VERSION_MAJOR(version), + BCH_VERSION_MINOR(version)); + blocksize = le16_to_cpu(bcs->block_size); + blkid_probe_set_block_size(pr, blocksize * BCACHEFS_SECTOR_SIZE); + blkid_probe_set_fsblocksize(pr, blocksize * BCACHEFS_SECTOR_SIZE); + blkid_probe_set_wiper(pr, 0, BCACHE_SB_OFF); + + probe_bcachefs_sb_fields(pr, bcs, sb, sb_end); + + return BLKID_PROBE_OK; +} + +const struct blkid_idinfo bcache_idinfo = +{ + .name = "bcache", + .usage = BLKID_USAGE_OTHER, + .probefunc = probe_bcache, + .minsz = 8192, + .magics = + { + { + .magic = BCACHE_SB_MAGIC, + .len = BCACHE_SB_MAGIC_LEN, + .kboff = BCACHE_SB_KBOFF, + .sboff = BCACHE_SB_MAGIC_OFF + }, + { NULL } + } +}; + +const struct blkid_idinfo bcachefs_idinfo = +{ + .name = "bcachefs", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_bcachefs, + .minsz = 256 * BCACHEFS_SECTOR_SIZE, + .magics = { + { + .magic = BCACHE_SB_MAGIC, + .len = BCACHE_SB_MAGIC_LEN, + .kboff = BCACHE_SB_KBOFF, + .sboff = BCACHE_SB_MAGIC_OFF, + }, + { + .magic = BCACHEFS_SB_MAGIC, + .len = BCACHE_SB_MAGIC_LEN, + .kboff = BCACHE_SB_KBOFF, + .sboff = BCACHE_SB_MAGIC_OFF, + }, + { NULL } + } +}; -- cgit v1.2.3