diff mbox series

[03/26] fs: btrfs: Cross-port btrfs_read_dev_super() from btrfs-progs

Message ID 20200422065009.69392-4-wqu@suse.com
State Superseded
Headers show
Series fs: btrfs: Re-implement btrfs support using the more widely used extent buffer base code | expand

Commit Message

Qu Wenruo April 22, 2020, 6:49 a.m. UTC
This patch will use generic code shared from btrfs-progs to read one
super block from block device.

To support the btrfs-progs style code, the following codes are also
cross-ported:
- BTRFS_SETGET_FUNC for btrfs_super_block
- btrfs_check_super() function
- Move btrfs_read_superblock() to disk-io.[ch]
  Since super.c only contains pretty small amount of code, and
  the extra check will be covered in later root read patches.

The different between this implementation from btrfs-progs are:
- No sbflags/sb_bytenr support
  Since we only need to read the primary super block (like kernel), thus
  sbflags/sb_bytenr used by super block recovery is not needed.

Also, this changes the following behavior of U-boot btrfs:
- Only reads the primary super block
  The old implementation will read all 3 super blocks, with one
  non-existing backup super.
  This is not correct, especially for fs created after btrfs.

  Just like kernel, we only check the primary super block.

Signed-off-by: Qu Wenruo <wqu at suse.com>
---
 fs/btrfs/Makefile  |   2 +-
 fs/btrfs/btrfs.c   |   1 +
 fs/btrfs/compat.h  |  56 ++++++++++
 fs/btrfs/ctree.c   |  32 ++++++
 fs/btrfs/ctree.h   |  67 ++++++++++++
 fs/btrfs/disk-io.c | 213 ++++++++++++++++++++++++++++++++++++++
 fs/btrfs/disk-io.h |   7 ++
 fs/btrfs/super.c   | 247 ---------------------------------------------
 8 files changed, 377 insertions(+), 248 deletions(-)
 create mode 100644 fs/btrfs/compat.h
 delete mode 100644 fs/btrfs/super.c

Comments

Marek BehĂșn April 22, 2020, 8:26 a.m. UTC | #1
On Wed, 22 Apr 2020 14:49:46 +0800
Qu Wenruo <wqu at suse.com> wrote:

> +/* A simple wraper to for error() from btrfs-progs */
> +#define error(...) { printf(__VA_ARGS__); printf("\n"); }

Is this from btrfs-progs?
I don't like these macros much, I prefer to use static inline functions.

static inline void error(const char *fmt, ...)
{
	printf(fmt, __builtin_va_arg_pack());
	printf("\n");
}

Attribute for printf can be added to check printf specifiers:
  __attribute__((format (__printf__, 1, 2)))

It is possible that this won't compile when optimizations are disabled.
In that case more attributes are needed
  __always_inline __attribute__((__gnu_inline__))
These could be defined as a macro in include/linux/compiler-gcc.h
  #define __gnu_inline __always_inline __attribute__((__gnu_inline__))

Marek
Qu Wenruo April 22, 2020, 8:34 a.m. UTC | #2
On 2020/4/22 ??4:26, Marek Behun wrote:
> On Wed, 22 Apr 2020 14:49:46 +0800
> Qu Wenruo <wqu at suse.com> wrote:
> 
>> +/* A simple wraper to for error() from btrfs-progs */
>> +#define error(...) { printf(__VA_ARGS__); printf("\n"); }
> 
> Is this from btrfs-progs?

Nope, anything in compat.h is mostly for different projects to share the
same code.

> I don't like these macros much, I prefer to use static inline functions.
> 
> static inline void error(const char *fmt, ...)
> {
> 	printf(fmt, __builtin_va_arg_pack());
> 	printf("\n");
> }

That looks much better, especially we can tell compiler that we want
printf check on the parameters.

Thanks,
Qu

> 
> Attribute for printf can be added to check printf specifiers:
>   __attribute__((format (__printf__, 1, 2)))
> 
> It is possible that this won't compile when optimizations are disabled.
> In that case more attributes are needed
>   __always_inline __attribute__((__gnu_inline__))
> These could be defined as a macro in include/linux/compiler-gcc.h
>   #define __gnu_inline __always_inline __attribute__((__gnu_inline__))
> 
> Marek
> 

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 488 bytes
Desc: OpenPGP digital signature
URL: <https://lists.denx.de/pipermail/u-boot/attachments/20200422/c21dd025/attachment.sig>
diff mbox series

Patch

diff --git a/fs/btrfs/Makefile b/fs/btrfs/Makefile
index 84b3b5ec0e8a..bd4b848d1b1f 100644
--- a/fs/btrfs/Makefile
+++ b/fs/btrfs/Makefile
@@ -3,4 +3,4 @@ 
 # 2017 Marek Behun, CZ.NIC, marek.behun at nic.cz
 
 obj-y := btrfs.o chunk-map.o compression.o ctree.o dev.o dir-item.o \
-	extent-io.o inode.o root.o subvolume.o super.o crypto/hash.o disk-io.o
+	extent-io.o inode.o root.o subvolume.o crypto/hash.o disk-io.o
diff --git a/fs/btrfs/btrfs.c b/fs/btrfs/btrfs.c
index 012d8afa2f53..ec993a193106 100644
--- a/fs/btrfs/btrfs.c
+++ b/fs/btrfs/btrfs.c
@@ -10,6 +10,7 @@ 
 #include <linux/time.h>
 #include "btrfs.h"
 #include "crypto/hash.h"
+#include "disk-io.h"
 
 struct btrfs_info btrfs_info;
 
diff --git a/fs/btrfs/compat.h b/fs/btrfs/compat.h
new file mode 100644
index 000000000000..376a035cd6db
--- /dev/null
+++ b/fs/btrfs/compat.h
@@ -0,0 +1,56 @@ 
+// SPDX-License-Identifier: GPL-2.0+
+
+#ifndef __BTRFS_COMPAT_H__
+#define __BTRFS_COMPAT_H__
+
+#include <linux/errno.h>
+#include <fs_internal.h>
+#include <uuid.h>
+
+/* Provide a compatibility layer to make code syncing easier */
+
+/* A simple wraper to for error() from btrfs-progs */
+#define error(...) { printf(__VA_ARGS__); printf("\n"); }
+
+#define BTRFS_UUID_UNPARSED_SIZE	37
+
+/*
+ * Macros to generate set/get funcs for the struct fields
+ * assume there is a lefoo_to_cpu for every type, so lets make a simple
+ * one for u8:
+ */
+#define le8_to_cpu(v) (v)
+#define cpu_to_le8(v) (v)
+#define __le8 u8
+
+/*
+ * Read data from device specified by @desc and @part
+ *
+ * U-boot equivalent of pread().
+ *
+ * Return the bytes of data read.
+ * Return <0 for error.
+ */
+static inline int __btrfs_devread(struct blk_desc *desc, disk_partition_t *part,
+				  void *buf, size_t size, u64 offset)
+{
+	lbaint_t sector;
+	int byte_offset;
+	int ret;
+
+	sector = offset >> desc->log2blksz;
+	byte_offset = offset % desc->blksz;
+
+	/* fs_devread() return 0 for error, >0 for success */
+	ret = fs_devread(desc, part, sector, byte_offset, size, buf);
+	if (!ret)
+		return -EIO;
+	return size;
+}
+
+static inline void uuid_unparse(const u8 *uuid, char *out)
+{
+	return uuid_bin_to_str((unsigned char *)uuid, out, 0);
+}
+
+#endif
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c
index 7fae383f1509..bdf8e18fd3ee 100644
--- a/fs/btrfs/ctree.c
+++ b/fs/btrfs/ctree.c
@@ -9,6 +9,38 @@ 
 #include <malloc.h>
 #include <memalign.h>
 
+static const struct btrfs_csum {
+	u16 size;
+	const char name[14];
+} btrfs_csums[] = {
+	[BTRFS_CSUM_TYPE_CRC32]		= {  4, "crc32c" },
+	[BTRFS_CSUM_TYPE_XXHASH]	= {  8, "xxhash64" },
+	[BTRFS_CSUM_TYPE_SHA256]	= { 32, "sha256" },
+	[BTRFS_CSUM_TYPE_BLAKE2]	= { 32, "blake2" },
+};
+
+u16 btrfs_super_csum_size(const struct btrfs_super_block *sb)
+{
+	const u16 csum_type = btrfs_super_csum_type(sb);
+
+	return btrfs_csums[csum_type].size;
+}
+
+const char *btrfs_super_csum_name(u16 csum_type)
+{
+	return btrfs_csums[csum_type].name;
+}
+
+size_t btrfs_super_num_csums(void)
+{
+	return ARRAY_SIZE(btrfs_csums);
+}
+
+u16 btrfs_csum_type_size(u16 csum_type)
+{
+	return btrfs_csums[csum_type].size;
+}
+
 int btrfs_comp_keys(struct btrfs_key *a, struct btrfs_key *b)
 {
 	if (a->objectid > b->objectid)
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 156ce69ed022..02125e5e100c 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -12,6 +12,7 @@ 
 #include <common.h>
 #include <compiler.h>
 #include "kernel-shared/btrfs_tree.h"
+#include "compat.h"
 
 #define BTRFS_MAX_MIRRORS 3
 
@@ -47,6 +48,16 @@ 
 #define BTRFS_FS_STATE_DEV_REPLACING	3
 #define BTRFS_FS_STATE_DUMMY_FS_INFO	4
 
+#define BTRFS_SETGET_STACK_FUNCS(name, type, member, bits)		\
+static inline u##bits btrfs_##name(const type *s)			\
+{									\
+	return le##bits##_to_cpu(s->member);				\
+}									\
+static inline void btrfs_set_##name(type *s, u##bits val)		\
+{									\
+	s->member = cpu_to_le##bits(val);				\
+}
+
 union btrfs_tree_node {
 	struct btrfs_header header;
 	struct btrfs_leaf leaf;
@@ -122,4 +133,60 @@  static inline void *btrfs_path_leaf_data(struct btrfs_path *p)
 #define btrfs_path_item_ptr(p,t)		\
 	((t *) btrfs_path_leaf_data((p)))
 
+u16 btrfs_super_csum_size(const struct btrfs_super_block *s);
+const char *btrfs_super_csum_name(u16 csum_type);
+u16 btrfs_csum_type_size(u16 csum_type);
+size_t btrfs_super_num_csums(void);
+
+/* struct btrfs_super_block */
+
+BTRFS_SETGET_STACK_FUNCS(super_bytenr, struct btrfs_super_block, bytenr, 64);
+BTRFS_SETGET_STACK_FUNCS(super_flags, struct btrfs_super_block, flags, 64);
+BTRFS_SETGET_STACK_FUNCS(super_generation, struct btrfs_super_block,
+			 generation, 64);
+BTRFS_SETGET_STACK_FUNCS(super_root, struct btrfs_super_block, root, 64);
+BTRFS_SETGET_STACK_FUNCS(super_sys_array_size,
+			 struct btrfs_super_block, sys_chunk_array_size, 32);
+BTRFS_SETGET_STACK_FUNCS(super_chunk_root_generation,
+			 struct btrfs_super_block, chunk_root_generation, 64);
+BTRFS_SETGET_STACK_FUNCS(super_root_level, struct btrfs_super_block,
+			 root_level, 8);
+BTRFS_SETGET_STACK_FUNCS(super_chunk_root, struct btrfs_super_block,
+			 chunk_root, 64);
+BTRFS_SETGET_STACK_FUNCS(super_chunk_root_level, struct btrfs_super_block,
+			 chunk_root_level, 8);
+BTRFS_SETGET_STACK_FUNCS(super_log_root, struct btrfs_super_block,
+			 log_root, 64);
+BTRFS_SETGET_STACK_FUNCS(super_log_root_transid, struct btrfs_super_block,
+			 log_root_transid, 64);
+BTRFS_SETGET_STACK_FUNCS(super_log_root_level, struct btrfs_super_block,
+			 log_root_level, 8);
+BTRFS_SETGET_STACK_FUNCS(super_total_bytes, struct btrfs_super_block,
+			 total_bytes, 64);
+BTRFS_SETGET_STACK_FUNCS(super_bytes_used, struct btrfs_super_block,
+			 bytes_used, 64);
+BTRFS_SETGET_STACK_FUNCS(super_sectorsize, struct btrfs_super_block,
+			 sectorsize, 32);
+BTRFS_SETGET_STACK_FUNCS(super_nodesize, struct btrfs_super_block,
+			 nodesize, 32);
+BTRFS_SETGET_STACK_FUNCS(super_stripesize, struct btrfs_super_block,
+			 stripesize, 32);
+BTRFS_SETGET_STACK_FUNCS(super_root_dir, struct btrfs_super_block,
+			 root_dir_objectid, 64);
+BTRFS_SETGET_STACK_FUNCS(super_num_devices, struct btrfs_super_block,
+			 num_devices, 64);
+BTRFS_SETGET_STACK_FUNCS(super_compat_flags, struct btrfs_super_block,
+			 compat_flags, 64);
+BTRFS_SETGET_STACK_FUNCS(super_compat_ro_flags, struct btrfs_super_block,
+			 compat_ro_flags, 64);
+BTRFS_SETGET_STACK_FUNCS(super_incompat_flags, struct btrfs_super_block,
+			 incompat_flags, 64);
+BTRFS_SETGET_STACK_FUNCS(super_csum_type, struct btrfs_super_block,
+			 csum_type, 16);
+BTRFS_SETGET_STACK_FUNCS(super_cache_generation, struct btrfs_super_block,
+			 cache_generation, 64);
+BTRFS_SETGET_STACK_FUNCS(super_uuid_tree_generation, struct btrfs_super_block,
+			 uuid_tree_generation, 64);
+BTRFS_SETGET_STACK_FUNCS(super_magic, struct btrfs_super_block, magic, 64);
+
 #endif /* __BTRFS_CTREE_H__ */
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 58c32b548e50..819068b936ec 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -1,7 +1,12 @@ 
 // SPDX-License-Identifier: GPL-2.0+
 #include <common.h>
 #include <fs_internal.h>
+#include <uuid.h>
+#include <memalign.h>
+#include "kernel-shared/btrfs_tree.h"
 #include "disk-io.h"
+#include "ctree.h"
+#include "btrfs.h"
 #include "crypto/hash.h"
 
 int btrfs_csum_data(u16 csum_type, const u8 *data, u8 *out, size_t len)
@@ -20,3 +25,211 @@  int btrfs_csum_data(u16 csum_type, const u8 *data, u8 *out, size_t len)
 		return -EINVAL;
 	}
 }
+
+/*
+ * Check if the super is valid:
+ * - nodesize/sectorsize - minimum, maximum, alignment
+ * - tree block starts   - alignment
+ * - number of devices   - something sane
+ * - sys array size      - maximum
+ */
+static int btrfs_check_super(struct btrfs_super_block *sb)
+{
+	u8 result[BTRFS_CSUM_SIZE];
+	u16 csum_type;
+	int csum_size;
+	u8 *metadata_uuid;
+
+	if (btrfs_super_magic(sb) != BTRFS_MAGIC)
+		return -EIO;
+
+	csum_type = btrfs_super_csum_type(sb);
+	if (csum_type >= btrfs_super_num_csums()) {
+		error("unsupported checksum algorithm %u", csum_type);
+		return -EIO;
+	}
+	csum_size = btrfs_super_csum_size(sb);
+
+	btrfs_csum_data(csum_type, (u8 *)sb + BTRFS_CSUM_SIZE,
+			result, BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE);
+
+	if (memcmp(result, sb->csum, csum_size)) {
+		error("superblock checksum mismatch");
+		return -EIO;
+	}
+	if (btrfs_super_root_level(sb) >= BTRFS_MAX_LEVEL) {
+		error("tree_root level too big: %d >= %d",
+			btrfs_super_root_level(sb), BTRFS_MAX_LEVEL);
+		goto error_out;
+	}
+	if (btrfs_super_chunk_root_level(sb) >= BTRFS_MAX_LEVEL) {
+		error("chunk_root level too big: %d >= %d",
+			btrfs_super_chunk_root_level(sb), BTRFS_MAX_LEVEL);
+		goto error_out;
+	}
+	if (btrfs_super_log_root_level(sb) >= BTRFS_MAX_LEVEL) {
+		error("log_root level too big: %d >= %d",
+			btrfs_super_log_root_level(sb), BTRFS_MAX_LEVEL);
+		goto error_out;
+	}
+
+	if (!IS_ALIGNED(btrfs_super_root(sb), 4096)) {
+		error("tree_root block unaligned: %llu", btrfs_super_root(sb));
+		goto error_out;
+	}
+	if (!IS_ALIGNED(btrfs_super_chunk_root(sb), 4096)) {
+		error("chunk_root block unaligned: %llu",
+			btrfs_super_chunk_root(sb));
+		goto error_out;
+	}
+	if (!IS_ALIGNED(btrfs_super_log_root(sb), 4096)) {
+		error("log_root block unaligned: %llu",
+			btrfs_super_log_root(sb));
+		goto error_out;
+	}
+	if (btrfs_super_nodesize(sb) < 4096) {
+		error("nodesize too small: %u < 4096",
+			btrfs_super_nodesize(sb));
+		goto error_out;
+	}
+	if (!IS_ALIGNED(btrfs_super_nodesize(sb), 4096)) {
+		error("nodesize unaligned: %u", btrfs_super_nodesize(sb));
+		goto error_out;
+	}
+	if (btrfs_super_sectorsize(sb) < 4096) {
+		error("sectorsize too small: %u < 4096",
+			btrfs_super_sectorsize(sb));
+		goto error_out;
+	}
+	if (!IS_ALIGNED(btrfs_super_sectorsize(sb), 4096)) {
+		error("sectorsize unaligned: %u", btrfs_super_sectorsize(sb));
+		goto error_out;
+	}
+	if (btrfs_super_total_bytes(sb) == 0) {
+		error("invalid total_bytes 0");
+		goto error_out;
+	}
+	if (btrfs_super_bytes_used(sb) < 6 * btrfs_super_nodesize(sb)) {
+		error("invalid bytes_used %llu", btrfs_super_bytes_used(sb));
+		goto error_out;
+	}
+	if ((btrfs_super_stripesize(sb) != 4096)
+		&& (btrfs_super_stripesize(sb) != btrfs_super_sectorsize(sb))) {
+		error("invalid stripesize %u", btrfs_super_stripesize(sb));
+		goto error_out;
+	}
+
+	if (btrfs_super_incompat_flags(sb) & BTRFS_FEATURE_INCOMPAT_METADATA_UUID)
+		metadata_uuid = sb->metadata_uuid;
+	else
+		metadata_uuid = sb->fsid;
+
+	if (memcmp(metadata_uuid, sb->dev_item.fsid, BTRFS_FSID_SIZE) != 0) {
+		char fsid[BTRFS_UUID_UNPARSED_SIZE];
+		char dev_fsid[BTRFS_UUID_UNPARSED_SIZE];
+
+		uuid_unparse(sb->metadata_uuid, fsid);
+		uuid_unparse(sb->dev_item.fsid, dev_fsid);
+		error("dev_item UUID does not match fsid: %s != %s",
+			dev_fsid, fsid);
+		goto error_out;
+	}
+
+	/*
+	 * Hint to catch really bogus numbers, bitflips or so
+	 */
+	if (btrfs_super_num_devices(sb) > (1UL << 31)) {
+		error("suspicious number of devices: %llu",
+			btrfs_super_num_devices(sb));
+	}
+
+	if (btrfs_super_num_devices(sb) == 0) {
+		error("number of devices is 0");
+		goto error_out;
+	}
+
+	/*
+	 * Obvious sys_chunk_array corruptions, it must hold at least one key
+	 * and one chunk
+	 */
+	if (btrfs_super_sys_array_size(sb) > BTRFS_SYSTEM_CHUNK_ARRAY_SIZE) {
+		error("system chunk array too big %u > %u",
+		      btrfs_super_sys_array_size(sb),
+		      BTRFS_SYSTEM_CHUNK_ARRAY_SIZE);
+		goto error_out;
+	}
+	if (btrfs_super_sys_array_size(sb) < sizeof(struct btrfs_disk_key)
+			+ sizeof(struct btrfs_chunk)) {
+		error("system chunk array too small %u < %zu",
+		      btrfs_super_sys_array_size(sb),
+		      sizeof(struct btrfs_disk_key) +
+		      sizeof(struct btrfs_chunk));
+		goto error_out;
+	}
+
+	return 0;
+
+error_out:
+	error("superblock checksum matches but it has invalid members");
+	return -EIO;
+}
+
+/*
+ * btrfs_read_dev_super - read a valid primary superblock from a block device
+ * @desc, at part:	file descriptor of the device
+ * @sb:		buffer where the superblock is going to be read in
+ *
+ * Unlike the btrfs-progs/kernel version, here we ony care about the first
+ * super block, thus it's much simpler.
+ */
+int btrfs_read_dev_super(struct blk_desc *desc, disk_partition_t *part,
+			 struct btrfs_super_block *sb)
+{
+	char tmp[BTRFS_SUPER_INFO_SIZE];
+	struct btrfs_super_block *buf = (struct btrfs_super_block *)tmp;
+	int ret;
+
+	ret = __btrfs_devread(desc, part, tmp, BTRFS_SUPER_INFO_SIZE,
+			      BTRFS_SUPER_INFO_OFFSET);
+	if (ret < BTRFS_SUPER_INFO_SIZE)
+		return -EIO;
+
+	if (btrfs_super_bytenr(buf) != BTRFS_SUPER_INFO_OFFSET)
+		return -EIO;
+
+	if (btrfs_check_super(buf))
+		return -EIO;
+
+	memcpy(sb, buf, BTRFS_SUPER_INFO_SIZE);
+	return 0;
+}
+
+int btrfs_read_superblock(void)
+{
+	ALLOC_CACHE_ALIGN_BUFFER(char, raw_sb, BTRFS_SUPER_INFO_SIZE);
+	struct btrfs_super_block *sb = (struct btrfs_super_block *) raw_sb;
+	int ret;
+
+
+	btrfs_info.sb.generation = 0;
+
+	ret = btrfs_read_dev_super(btrfs_blk_desc, btrfs_part_info, sb);
+	if (ret < 0) {
+		debug("%s: No valid BTRFS superblock found!\n", __func__);
+		return ret;
+	}
+	btrfs_super_block_to_cpu(sb);
+	memcpy(&btrfs_info.sb, sb, sizeof(*sb));
+
+	if (btrfs_info.sb.num_devices != 1) {
+		printf("%s: Unsupported number of devices (%lli). This driver "
+		       "only supports filesystem on one device.\n", __func__,
+		       btrfs_info.sb.num_devices);
+		return -1;
+	}
+
+	debug("Chosen superblock with generation = %llu\n",
+	      btrfs_info.sb.generation);
+
+	return 0;
+}
diff --git a/fs/btrfs/disk-io.h b/fs/btrfs/disk-io.h
index efb715828cce..671f1ac55c68 100644
--- a/fs/btrfs/disk-io.h
+++ b/fs/btrfs/disk-io.h
@@ -2,10 +2,14 @@ 
 #ifndef __BTRFS_DISK_IO_H__
 #define __BTRFS_DISK_IO_H__
 
+#include <linux/sizes.h>
+#include <fs_internal.h>
 #include "crypto/hash.h"
 #include "ctree.h"
 #include "disk-io.h"
 
+#define BTRFS_SUPER_INFO_OFFSET SZ_64K
+#define BTRFS_SUPER_INFO_SIZE	SZ_4K
 static inline u64 btrfs_name_hash(const char *name, int len)
 {
 	u32 crc;
@@ -17,4 +21,7 @@  static inline u64 btrfs_name_hash(const char *name, int len)
 
 int btrfs_csum_data(u16 csum_type, const u8 *data, u8 *out, size_t len);
 
+int btrfs_read_dev_super(struct blk_desc *desc, disk_partition_t *part,
+			 struct btrfs_super_block *sb);
+int btrfs_read_superblock(void);
 #endif
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
deleted file mode 100644
index 7891d84ed369..000000000000
--- a/fs/btrfs/super.c
+++ /dev/null
@@ -1,247 +0,0 @@ 
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * BTRFS filesystem implementation for U-Boot
- *
- * 2017 Marek Behun, CZ.NIC, marek.behun at nic.cz
- */
-
-#include "btrfs.h"
-#include "disk-io.h"
-#include <memalign.h>
-
-#define BTRFS_SUPER_FLAG_SUPP	(BTRFS_HEADER_FLAG_WRITTEN	\
-				 | BTRFS_HEADER_FLAG_RELOC	\
-				 | BTRFS_SUPER_FLAG_ERROR	\
-				 | BTRFS_SUPER_FLAG_SEEDING	\
-				 | BTRFS_SUPER_FLAG_METADUMP)
-
-#define BTRFS_SUPER_INFO_SIZE	4096
-
-/*
- * checks if a valid root backup is present.
- * considers the case when all root backups empty valid.
- * returns -1 in case of invalid root backup and 0 for valid.
- */
-static int btrfs_check_super_roots(struct btrfs_super_block *sb)
-{
-	struct btrfs_root_backup *root_backup;
-	int i, newest = -1;
-	int num_empty = 0;
-
-	for (i = 0; i < BTRFS_NUM_BACKUP_ROOTS; ++i) {
-		root_backup = sb->super_roots + i;
-
-		if (root_backup->tree_root == 0 && root_backup->tree_root_gen == 0)
-			num_empty++;
-
-		if (root_backup->tree_root_gen == sb->generation)
-			newest = i;
-	}
-
-	if (num_empty == BTRFS_NUM_BACKUP_ROOTS) {
-		return 0;
-	} else if (newest >= 0) {
-		return 0;
-	}
-
-	return -1;
-}
-
-static inline int is_power_of_2(u64 x)
-{
-	return !(x & (x - 1));
-}
-
-static int btrfs_check_super_csum(char *raw_disk_sb)
-{
-	struct btrfs_super_block *disk_sb =
-		(struct btrfs_super_block *) raw_disk_sb;
-	u16 csum_type = le16_to_cpu(disk_sb->csum_type);
-
-	if (csum_type == BTRFS_CSUM_TYPE_CRC32 ||
-	    csum_type == BTRFS_CSUM_TYPE_SHA256 ||
-	    csum_type == BTRFS_CSUM_TYPE_XXHASH) {
-		u8 result[BTRFS_CSUM_SIZE];
-
-		btrfs_csum_data(csum_type, (u8 *)raw_disk_sb + BTRFS_CSUM_SIZE,
-			result, BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE);
-
-		if (memcmp(raw_disk_sb, result, BTRFS_CSUM_SIZE))
-			return -EIO;
-	} else if (csum_type == BTRFS_CSUM_TYPE_BLAKE2){
-		printf("Blake2 csum type is not supported yet\n");
-		return -ENOTSUPP;
-	}
-
-	return 0;
-}
-
-static int btrfs_check_super(struct btrfs_super_block *sb)
-{
-	int ret = 0;
-
-	if (sb->flags & ~BTRFS_SUPER_FLAG_SUPP) {
-		printf("%s: Unsupported flags: %llu\n", __func__,
-		       sb->flags & ~BTRFS_SUPER_FLAG_SUPP);
-	}
-
-	if (sb->root_level > BTRFS_MAX_LEVEL) {
-		printf("%s: tree_root level too big: %d >= %d\n", __func__,
-		       sb->root_level, BTRFS_MAX_LEVEL);
-		ret = -1;
-	}
-
-	if (sb->chunk_root_level > BTRFS_MAX_LEVEL) {
-		printf("%s: chunk_root level too big: %d >= %d\n", __func__,
-		       sb->chunk_root_level, BTRFS_MAX_LEVEL);
-		ret = -1;
-	}
-
-	if (sb->log_root_level > BTRFS_MAX_LEVEL) {
-		printf("%s: log_root level too big: %d >= %d\n", __func__,
-		       sb->log_root_level, BTRFS_MAX_LEVEL);
-		ret = -1;
-	}
-
-	if (!is_power_of_2(sb->sectorsize) || sb->sectorsize < 4096 ||
-	    sb->sectorsize > BTRFS_MAX_METADATA_BLOCKSIZE) {
-		printf("%s: invalid sectorsize %u\n", __func__,
-		       sb->sectorsize);
-		ret = -1;
-	}
-
-	if (!is_power_of_2(sb->nodesize) || sb->nodesize < sb->sectorsize ||
-	    sb->nodesize > BTRFS_MAX_METADATA_BLOCKSIZE) {
-		printf("%s: invalid nodesize %u\n", __func__, sb->nodesize);
-		ret = -1;
-	}
-
-	if (sb->nodesize != sb->__unused_leafsize) {
-		printf("%s: invalid leafsize %u, should be %u\n", __func__,
-		       sb->__unused_leafsize, sb->nodesize);
-		ret = -1;
-	}
-
-	if (!IS_ALIGNED(sb->root, sb->sectorsize)) {
-		printf("%s: tree_root block unaligned: %llu\n", __func__,
-		       sb->root);
-		ret = -1;
-	}
-
-	if (!IS_ALIGNED(sb->chunk_root, sb->sectorsize)) {
-		printf("%s: chunk_root block unaligned: %llu\n", __func__,
-		       sb->chunk_root);
-		ret = -1;
-	}
-
-	if (!IS_ALIGNED(sb->log_root, sb->sectorsize)) {
-		printf("%s: log_root block unaligned: %llu\n", __func__,
-		       sb->log_root);
-		ret = -1;
-	}
-
-	if (memcmp(sb->fsid, sb->dev_item.fsid, BTRFS_UUID_SIZE) != 0) {
-		printf("%s: dev_item UUID does not match fsid\n", __func__);
-		ret = -1;
-	}
-
-	if (sb->bytes_used < 6*sb->nodesize) {
-		printf("%s: bytes_used is too small %llu\n", __func__,
-		       sb->bytes_used);
-		ret = -1;
-	}
-
-	if (!is_power_of_2(sb->stripesize)) {
-		printf("%s: invalid stripesize %u\n", __func__, sb->stripesize);
-		ret = -1;
-	}
-
-	if (sb->sys_chunk_array_size > BTRFS_SYSTEM_CHUNK_ARRAY_SIZE) {
-		printf("%s: system chunk array too big %u > %u\n", __func__,
-		       sb->sys_chunk_array_size, BTRFS_SYSTEM_CHUNK_ARRAY_SIZE);
-		ret = -1;
-	}
-
-	if (sb->sys_chunk_array_size < sizeof(struct btrfs_key) +
-	    sizeof(struct btrfs_chunk)) {
-		printf("%s: system chunk array too small %u < %zu\n", __func__,
-		       sb->sys_chunk_array_size, sizeof(struct btrfs_key)
-		       + sizeof(struct btrfs_chunk));
-		ret = -1;
-	}
-
-	return ret;
-}
-
-int btrfs_read_superblock(void)
-{
-	const u64 superblock_offsets[4] = {
-		0x10000ull,
-		0x4000000ull,
-		0x4000000000ull,
-		0x4000000000000ull
-	};
-	ALLOC_CACHE_ALIGN_BUFFER(char, raw_sb, BTRFS_SUPER_INFO_SIZE);
-	struct btrfs_super_block *sb = (struct btrfs_super_block *) raw_sb;
-	u64 dev_total_bytes;
-	int i;
-
-	dev_total_bytes = (u64) btrfs_part_info->size * btrfs_part_info->blksz;
-
-	btrfs_info.sb.generation = 0;
-
-	for (i = 0; i < 4; ++i) {
-		if (superblock_offsets[i] + sizeof(sb) > dev_total_bytes)
-			break;
-
-		if (!btrfs_devread(superblock_offsets[i], BTRFS_SUPER_INFO_SIZE,
-				   raw_sb))
-			break;
-
-		if (btrfs_check_super_csum(raw_sb)) {
-			debug("%s: invalid checksum at superblock mirror %i\n",
-			      __func__, i);
-			continue;
-		}
-
-		btrfs_super_block_to_cpu(sb);
-
-		if (sb->magic != BTRFS_MAGIC) {
-			debug("%s: invalid BTRFS magic 0x%016llX at "
-			      "superblock mirror %i\n", __func__, sb->magic, i);
-		} else if (sb->bytenr != superblock_offsets[i]) {
-			printf("%s: invalid bytenr 0x%016llX (expected "
-			       "0x%016llX) at superblock mirror %i\n",
-			       __func__, sb->bytenr, superblock_offsets[i], i);
-		} else if (btrfs_check_super(sb)) {
-			printf("%s: Checking superblock mirror %i failed\n",
-			       __func__, i);
-		} else if (sb->generation > btrfs_info.sb.generation) {
-			memcpy(&btrfs_info.sb, sb, sizeof(*sb));
-		} else {
-			/* Nothing */
-		}
-	}
-
-	if (!btrfs_info.sb.generation) {
-		debug("%s: No valid BTRFS superblock found!\n", __func__);
-		return -1;
-	}
-
-	if (btrfs_check_super_roots(&btrfs_info.sb)) {
-		printf("%s: No valid root_backup found!\n", __func__);
-		return -1;
-	}
-
-	if (btrfs_info.sb.num_devices != 1) {
-		printf("%s: Unsupported number of devices (%lli). This driver "
-		       "only supports filesystem on one device.\n", __func__,
-		       btrfs_info.sb.num_devices);
-		return -1;
-	}
-
-	debug("Chosen superblock with generation = %llu\n",
-	      btrfs_info.sb.generation);
-
-	return 0;
-}