(root)/
util-linux-2.39/
libblkid/
src/
superblocks/
bcache.c
       1  /*
       2   * Copyright (C) 2013 Rolf Fokkens <rolf@fokkens.nl>
       3   *
       4   * This file may be redistributed under the terms of the
       5   * GNU Lesser General Public License.
       6   *
       7   * Based on code fragments from bcache-tools by Kent Overstreet:
       8   * http://evilpiepirate.org/git/bcache-tools.git
       9   */
      10  
      11  #include <stddef.h>
      12  #include <stdio.h>
      13  
      14  #include "superblocks.h"
      15  #include "crc32c.h"
      16  #include "crc64.h"
      17  #include "xxhash.h"
      18  
      19  #define SB_LABEL_SIZE      32
      20  
      21  /*
      22   * The bcache_super_block is heavily simplified version of struct cache_sb in kernel.
      23   * https://github.com/torvalds/linux/blob/master/include/uapi/linux/bcache.h
      24   */
      25  struct bcache_super_block {
      26  	uint64_t		csum;
      27  	uint64_t		offset;		/* where this super block was written */
      28  	uint64_t		version;
      29  	uint8_t			magic[16];	/* bcache file system identifier */
      30  	uint8_t			uuid[16];	/* device identifier */
      31  } __attribute__((packed));
      32  
      33  struct bcachefs_sb_field {
      34  	uint32_t	u64s;
      35  	uint32_t	type;
      36  }  __attribute__((packed));
      37  
      38  struct bcachefs_sb_member {
      39  	uint8_t		uuid[16];
      40  	uint64_t	nbuckets;
      41  	uint16_t	first_bucket;
      42  	uint16_t	bucket_size;
      43  	uint32_t	pad;
      44  	uint64_t	last_mount;
      45  	uint64_t	flags[2];
      46  } __attribute__((packed));
      47  
      48  struct bcachefs_sb_field_members {
      49  	struct bcachefs_sb_field	field;
      50  	struct bcachefs_sb_member	members[];
      51  }  __attribute__((packed));
      52  
      53  enum bcachefs_sb_csum_type {
      54  	BCACHEFS_SB_CSUM_TYPE_NONE = 0,
      55  	BCACHEFS_SB_CSUM_TYPE_CRC32C = 1,
      56  	BCACHEFS_SB_CSUM_TYPE_CRC64 = 2,
      57  	BCACHEFS_SB_CSUM_TYPE_XXHASH = 7,
      58  };
      59  
      60  union bcachefs_sb_csum {
      61  	uint32_t crc32c;
      62  	uint64_t crc64;
      63  	XXH64_hash_t xxh64;
      64  	uint8_t raw[16];
      65  } __attribute__((packed));
      66  
      67  struct bcachefs_super_block {
      68  	union bcachefs_sb_csum	csum;
      69  	uint16_t	version;
      70  	uint16_t	version_min;
      71  	uint16_t	pad[2];
      72  	uint8_t		magic[16];
      73  	uint8_t		uuid[16];
      74  	uint8_t		user_uuid[16];
      75  	uint8_t		label[SB_LABEL_SIZE];
      76  	uint64_t	offset;
      77  	uint64_t	seq;
      78  	uint16_t	block_size;
      79  	uint8_t		dev_idx;
      80  	uint8_t		nr_devices;
      81  	uint32_t	u64s;
      82  	uint64_t	time_base_lo;
      83  	uint32_t	time_base_hi;
      84  	uint32_t	time_precision;
      85  	uint64_t	flags[8];
      86  	uint64_t	features[2];
      87  	uint64_t	compat[2];
      88  	uint8_t		layout[512];
      89  	struct bcachefs_sb_field _start[];
      90  }  __attribute__((packed));
      91  
      92  /* magic string */
      93  #define BCACHE_SB_MAGIC     "\xc6\x85\x73\xf6\x4e\x1a\x45\xca\x82\x65\xf5\x7f\x48\xba\x6d\x81"
      94  #define BCACHEFS_SB_MAGIC   "\xc6\x85\x73\xf6\x66\xce\x90\xa9\xd9\x6a\x60\xcf\x80\x3d\xf7\xef"
      95  /* magic string len */
      96  #define BCACHE_SB_MAGIC_LEN (sizeof(BCACHE_SB_MAGIC) - 1)
      97  /* super block offset */
      98  #define BCACHE_SB_OFF       0x1000
      99  /* supper block offset in kB */
     100  #define BCACHE_SB_KBOFF     (BCACHE_SB_OFF >> 10)
     101  /* magic string offset within super block */
     102  #define BCACHE_SB_MAGIC_OFF offsetof(struct bcache_super_block, magic)
     103  /* start of checksummed data within superblock */
     104  #define BCACHE_SB_CSUMMED_START 8
     105  /* end of checksummed data within superblock */
     106  #define BCACHE_SB_CSUMMED_END 208
     107  /* granularity of offset and length fields within superblock */
     108  #define BCACHEFS_SECTOR_SIZE   512
     109  /* maximum superblock size */
     110  #define BCACHEFS_SB_MAX_SIZE   4096
     111  /* fields offset within super block */
     112  #define BCACHEFS_SB_FIELDS_OFF offsetof(struct bcachefs_super_block, _start)
     113  /* tag value for members field */
     114  #define BCACHEFS_SB_FIELD_TYPE_MEMBERS 1
     115  
     116  #define BYTES(f) ((((uint64_t) le32_to_cpu((f)->u64s)) * 8))
     117  
     118  static int bcache_verify_checksum(blkid_probe pr, const struct blkid_idmag *mag,
     119  		const struct bcache_super_block *bcs)
     120  {
     121  	unsigned char *csummed = blkid_probe_get_sb_buffer(pr, mag, BCACHE_SB_CSUMMED_END);
     122  	uint64_t csum = ul_crc64_we(csummed + BCACHE_SB_CSUMMED_START,
     123  			BCACHE_SB_CSUMMED_END - BCACHE_SB_CSUMMED_START);
     124  	return blkid_probe_verify_csum(pr, csum, le64_to_cpu(bcs->csum));
     125  }
     126  
     127  static int probe_bcache (blkid_probe pr, const struct blkid_idmag *mag)
     128  {
     129  	struct bcache_super_block *bcs;
     130  
     131  	bcs = blkid_probe_get_sb(pr, mag, struct bcache_super_block);
     132  	if (!bcs)
     133  		return errno ? -errno : BLKID_PROBE_NONE;
     134  
     135  	if (!bcache_verify_checksum(pr, mag, bcs))
     136  		return BLKID_PROBE_NONE;
     137  
     138  	if (le64_to_cpu(bcs->offset) != BCACHE_SB_OFF / 512)
     139  		return BLKID_PROBE_NONE;
     140  
     141  	if (blkid_probe_set_uuid(pr, bcs->uuid) < 0)
     142  		return BLKID_PROBE_NONE;
     143  
     144  	blkid_probe_set_wiper(pr, 0, BCACHE_SB_OFF);
     145  
     146  	return BLKID_PROBE_OK;
     147  }
     148  
     149  static void probe_bcachefs_sb_members(blkid_probe pr,
     150  				     const struct bcachefs_super_block *bcs,
     151  				     const struct bcachefs_sb_field *field,
     152  				     uint8_t dev_idx)
     153  {
     154  	struct bcachefs_sb_field_members *members =
     155  			(struct bcachefs_sb_field_members *) field;
     156  	uint64_t sectors = 0;
     157  	uint8_t i;
     158  
     159  	if (BYTES(field) != offsetof(typeof(*members), members[bcs->nr_devices]))
     160  		return;
     161  
     162  	blkid_probe_set_uuid_as(pr, members->members[dev_idx].uuid, "UUID_SUB");
     163  
     164  	for (i = 0; i < bcs->nr_devices; i++) {
     165  		struct bcachefs_sb_member *member = &members->members[i];
     166  		sectors += le64_to_cpu(member->nbuckets) * le16_to_cpu(member->bucket_size);
     167  	}
     168  	blkid_probe_set_fssize(pr, sectors * BCACHEFS_SECTOR_SIZE);
     169  }
     170  
     171  static int is_within_range(void *start, uint64_t size, void *end)
     172  {
     173  	ptrdiff_t diff;
     174  
     175  	if (start >= end)
     176  		return 0; // should not happen
     177  
     178  	diff = (unsigned char *) end - (unsigned char *) start;
     179  	return size <= (uint64_t) diff;
     180  }
     181  
     182  static void probe_bcachefs_sb_fields(blkid_probe pr, const struct bcachefs_super_block *bcs,
     183  				     unsigned char *sb_start, unsigned char *sb_end)
     184  {
     185  	unsigned char *field_addr = sb_start + BCACHEFS_SB_FIELDS_OFF;
     186  
     187  	while (1) {
     188  		struct bcachefs_sb_field *field = (struct bcachefs_sb_field *) field_addr;
     189  		uint64_t field_size;
     190  		uint32_t type;
     191  
     192  		if (!is_within_range(field, sizeof(*field), sb_end))
     193  			break;
     194  
     195  		field_size = BYTES(field);
     196  
     197  		if (field_size < sizeof(*field))
     198  			break;
     199  
     200  		if (!is_within_range(field, field_size, sb_end))
     201  			break;
     202  
     203  		type = le32_to_cpu(field->type);
     204  		if (!type)
     205  			break;
     206  
     207  		if (type == BCACHEFS_SB_FIELD_TYPE_MEMBERS)
     208  			probe_bcachefs_sb_members(pr, bcs, field, bcs->dev_idx);
     209  
     210  		field_addr += BYTES(field);
     211  	}
     212  }
     213  
     214  static int bcachefs_validate_checksum(blkid_probe pr, const struct bcachefs_super_block *bcs,
     215  				      unsigned char *sb, unsigned char *sb_end)
     216  {
     217  	uint8_t checksum_type = be64_to_cpu(bcs->flags[0]) >> 58;
     218  	unsigned char *checksummed_data_start = sb + sizeof(bcs->csum);
     219  	size_t checksummed_data_size = sb_end - checksummed_data_start;
     220  
     221  	switch (checksum_type) {
     222  		case BCACHEFS_SB_CSUM_TYPE_NONE:
     223  			return 1;
     224  		case BCACHEFS_SB_CSUM_TYPE_CRC32C: {
     225  			uint32_t crc = crc32c(~0LL, checksummed_data_start, checksummed_data_size) ^ ~0LL;
     226  			return blkid_probe_verify_csum(pr, crc, le32_to_cpu(bcs->csum.crc32c));
     227  		}
     228  		case BCACHEFS_SB_CSUM_TYPE_CRC64: {
     229  			uint64_t crc = ul_crc64_we(checksummed_data_start, checksummed_data_size);
     230  			return blkid_probe_verify_csum(pr, crc, le64_to_cpu(bcs->csum.crc64));
     231  		}
     232  		case BCACHEFS_SB_CSUM_TYPE_XXHASH: {
     233  			XXH64_hash_t xxh64 = XXH64(checksummed_data_start, checksummed_data_size, 0);
     234  			return blkid_probe_verify_csum(pr, xxh64, le64_to_cpu(bcs->csum.xxh64));
     235  		}
     236  		default:
     237  			DBG(LOWPROBE, ul_debug("bcachefs: unknown checksum type %d, ignoring.", checksum_type));
     238  			return 1;
     239  	}
     240  }
     241  
     242  static int probe_bcachefs(blkid_probe pr, const struct blkid_idmag *mag)
     243  {
     244  	struct bcachefs_super_block *bcs;
     245  	unsigned char *sb, *sb_end;
     246  	uint64_t sb_size, blocksize;
     247  
     248  	bcs = blkid_probe_get_sb(pr, mag, struct bcachefs_super_block);
     249  	if (!bcs)
     250  		return errno ? -errno : BLKID_PROBE_NONE;
     251  
     252  	if (le64_to_cpu(bcs->offset) != BCACHE_SB_OFF / BCACHEFS_SECTOR_SIZE)
     253  		return BLKID_PROBE_NONE;
     254  
     255  	if (bcs->nr_devices == 0 || bcs->dev_idx >= bcs->nr_devices)
     256  		return BLKID_PROBE_NONE;
     257  
     258  	sb_size = BCACHEFS_SB_FIELDS_OFF + BYTES(bcs);
     259  	if (sb_size > BCACHEFS_SB_MAX_SIZE)
     260  		return BLKID_PROBE_NONE;
     261  
     262  	sb = blkid_probe_get_sb_buffer(pr, mag, sb_size);
     263  	if (!sb)
     264  		return BLKID_PROBE_NONE;
     265  	sb_end = sb + sb_size;
     266  
     267  	if (!bcachefs_validate_checksum(pr, bcs, sb, sb_end))
     268  		return BLKID_PROBE_NONE;
     269  
     270  	blkid_probe_set_uuid(pr, bcs->user_uuid);
     271  	blkid_probe_set_label(pr, bcs->label, sizeof(bcs->label));
     272  	blkid_probe_sprintf_version(pr, "%d", le16_to_cpu(bcs->version));
     273  	blocksize = le16_to_cpu(bcs->block_size);
     274  	blkid_probe_set_block_size(pr, blocksize * BCACHEFS_SECTOR_SIZE);
     275  	blkid_probe_set_fsblocksize(pr, blocksize * BCACHEFS_SECTOR_SIZE);
     276  	blkid_probe_set_wiper(pr, 0, BCACHE_SB_OFF);
     277  
     278  	probe_bcachefs_sb_fields(pr, bcs, sb, sb_end);
     279  
     280  	return BLKID_PROBE_OK;
     281  }
     282  
     283  const struct blkid_idinfo bcache_idinfo =
     284  {
     285  	.name		= "bcache",
     286  	.usage		= BLKID_USAGE_OTHER,
     287  	.probefunc	= probe_bcache,
     288  	.minsz		= 8192,
     289  	.magics		=
     290  	{
     291  		{
     292  			.magic = BCACHE_SB_MAGIC,
     293  			.len   = BCACHE_SB_MAGIC_LEN,
     294  			.kboff = BCACHE_SB_KBOFF,
     295  			.sboff = BCACHE_SB_MAGIC_OFF
     296  		},
     297  		{ NULL }
     298  	}
     299  };
     300  
     301  const struct blkid_idinfo bcachefs_idinfo =
     302  {
     303  	.name		= "bcachefs",
     304  	.usage		= BLKID_USAGE_FILESYSTEM,
     305  	.probefunc	= probe_bcachefs,
     306  	.minsz		= 256 * BCACHEFS_SECTOR_SIZE,
     307  	.magics		= {
     308  		{
     309  			.magic = BCACHE_SB_MAGIC,
     310  			.len   = BCACHE_SB_MAGIC_LEN,
     311  			.kboff = BCACHE_SB_KBOFF,
     312  			.sboff = BCACHE_SB_MAGIC_OFF,
     313  		},
     314  		{
     315  			.magic = BCACHEFS_SB_MAGIC,
     316  			.len   = BCACHE_SB_MAGIC_LEN,
     317  			.kboff = BCACHE_SB_KBOFF,
     318  			.sboff = BCACHE_SB_MAGIC_OFF,
     319  		},
     320  		{ NULL }
     321  	}
     322  };