diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/btrfs/Makefile | 2 | ||||
-rw-r--r-- | fs/btrfs/btrfs.c | 1 | ||||
-rw-r--r-- | fs/btrfs/compat.h | 57 | ||||
-rw-r--r-- | fs/btrfs/ctree.c | 32 | ||||
-rw-r--r-- | fs/btrfs/ctree.h | 67 | ||||
-rw-r--r-- | fs/btrfs/disk-io.c | 213 | ||||
-rw-r--r-- | fs/btrfs/disk-io.h | 7 | ||||
-rw-r--r-- | fs/btrfs/super.c | 258 |
8 files changed, 378 insertions, 259 deletions
diff --git a/fs/btrfs/Makefile b/fs/btrfs/Makefile index 84b3b5ec0e..bd4b848d1b 100644 --- a/fs/btrfs/Makefile +++ b/fs/btrfs/Makefile @@ -3,4 +3,4 @@ # 2017 Marek Behun, CZ.NIC, marek.behun@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 d5a87c175c..78a32497b5 100644 --- a/fs/btrfs/btrfs.c +++ b/fs/btrfs/btrfs.c @@ -11,6 +11,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 0000000000..b354c17a5c --- /dev/null +++ b/fs/btrfs/compat.h @@ -0,0 +1,57 @@ +// 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() used in btrfs-progs */ +#define error(fmt, ...) pr_err("BTRFS: " fmt "\n", ##__VA_ARGS__) + +#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, + struct disk_partition *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 28f98d43ad..1d8f7e168f 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c @@ -10,6 +10,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 156ce69ed0..02125e5e10 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 58c32b548e..edebf741fd 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,@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, struct disk_partition *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) { + pr_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; + } + + pr_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 efb715828c..fe4093cbd8 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, struct disk_partition *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 101572b6d5..0000000000 --- a/fs/btrfs/super.c +++ /dev/null @@ -1,258 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * BTRFS filesystem implementation for U-Boot - * - * 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz - */ - -#include <common.h> -#include <log.h> -#include <memalign.h> -#include <part.h> -#include <linux/compat.h> -#include "btrfs.h" -#include "disk-io.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 (sb->sectorsize != PAGE_SIZE) { - printf( - "%s: Unsupported sector size (%u), only supports %u as sector size\n", - __func__, sb->sectorsize, PAGE_SIZE); - 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; -} |