diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/Kconfig | 2 | ||||
-rw-r--r-- | fs/Makefile | 2 | ||||
-rw-r--r-- | fs/btrfs/Kconfig | 9 | ||||
-rw-r--r-- | fs/btrfs/Makefile | 8 | ||||
-rw-r--r-- | fs/btrfs/btrfs.c | 227 | ||||
-rw-r--r-- | fs/btrfs/btrfs.h | 89 | ||||
-rw-r--r-- | fs/btrfs/btrfs_tree.h | 766 | ||||
-rw-r--r-- | fs/btrfs/chunk-map.c | 178 | ||||
-rw-r--r-- | fs/btrfs/compression.c | 134 | ||||
-rw-r--r-- | fs/btrfs/conv-funcs.h | 176 | ||||
-rw-r--r-- | fs/btrfs/ctree.c | 289 | ||||
-rw-r--r-- | fs/btrfs/ctree.h | 334 | ||||
-rw-r--r-- | fs/btrfs/dev.c | 26 | ||||
-rw-r--r-- | fs/btrfs/dir-item.c | 125 | ||||
-rw-r--r-- | fs/btrfs/extent-io.c | 120 | ||||
-rw-r--r-- | fs/btrfs/hash.c | 38 | ||||
-rw-r--r-- | fs/btrfs/inode.c | 385 | ||||
-rw-r--r-- | fs/btrfs/root.c | 93 | ||||
-rw-r--r-- | fs/btrfs/subvolume.c | 131 | ||||
-rw-r--r-- | fs/btrfs/super.c | 233 | ||||
-rw-r--r-- | fs/ext4/dev.c | 84 | ||||
-rw-r--r-- | fs/ext4/ext4_common.c | 4 | ||||
-rw-r--r-- | fs/ext4/ext4fs.c | 1 | ||||
-rw-r--r-- | fs/fat/fat.c | 43 | ||||
-rw-r--r-- | fs/fs.c | 16 | ||||
-rw-r--r-- | fs/fs_internal.c | 92 | ||||
-rw-r--r-- | fs/jffs2/jffs2_nand_1pass.c | 2 | ||||
-rw-r--r-- | fs/reiserfs/dev.c | 78 | ||||
-rw-r--r-- | fs/yaffs2/yaffs_uboot_glue.c | 2 | ||||
-rw-r--r-- | fs/zfs/dev.c | 86 |
30 files changed, 3519 insertions, 254 deletions
@@ -4,6 +4,8 @@ menu "File systems" +source "fs/btrfs/Kconfig" + source "fs/cbfs/Kconfig" source "fs/ext4/Kconfig" diff --git a/fs/Makefile b/fs/Makefile index 5770f41..8a8175b 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_SPL_EXT_SUPPORT) += ext4/ else obj-y += fs.o +obj-$(CONFIG_FS_BTRFS) += btrfs/ obj-$(CONFIG_FS_CBFS) += cbfs/ obj-$(CONFIG_CMD_CRAMFS) += cramfs/ obj-$(CONFIG_FS_EXT4) += ext4/ @@ -23,3 +24,4 @@ obj-$(CONFIG_CMD_UBIFS) += ubifs/ obj-$(CONFIG_YAFFS2) += yaffs2/ obj-$(CONFIG_CMD_ZFS) += zfs/ endif +obj-y += fs_internal.o diff --git a/fs/btrfs/Kconfig b/fs/btrfs/Kconfig new file mode 100644 index 0000000..22909d9 --- /dev/null +++ b/fs/btrfs/Kconfig @@ -0,0 +1,9 @@ +config FS_BTRFS + bool "Enable BTRFS filesystem support" + select CRC32C + select LZO + select RBTREE + help + This provides a single-device read-only BTRFS support. BTRFS is a + next-generation Linux file system based on the copy-on-write + principle. diff --git a/fs/btrfs/Makefile b/fs/btrfs/Makefile new file mode 100644 index 0000000..0173155 --- /dev/null +++ b/fs/btrfs/Makefile @@ -0,0 +1,8 @@ +# +# 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-y := btrfs.o chunk-map.o compression.o ctree.o dev.o dir-item.o \ + extent-io.o hash.o inode.o root.o subvolume.o super.o diff --git a/fs/btrfs/btrfs.c b/fs/btrfs/btrfs.c new file mode 100644 index 0000000..4140e2b --- /dev/null +++ b/fs/btrfs/btrfs.c @@ -0,0 +1,227 @@ +/* + * BTRFS filesystem implementation for U-Boot + * + * 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include "btrfs.h" +#include <config.h> +#include <malloc.h> +#include <linux/time.h> + +struct btrfs_info btrfs_info; + +static int readdir_callback(const struct btrfs_root *root, + struct btrfs_dir_item *item) +{ + static const char typestr[BTRFS_FT_MAX][4] = { + [BTRFS_FT_UNKNOWN] = " ? ", + [BTRFS_FT_REG_FILE] = " ", + [BTRFS_FT_DIR] = "DIR", + [BTRFS_FT_CHRDEV] = "CHR", + [BTRFS_FT_BLKDEV] = "BLK", + [BTRFS_FT_FIFO] = "FIF", + [BTRFS_FT_SOCK] = "SCK", + [BTRFS_FT_SYMLINK] = "SYM", + [BTRFS_FT_XATTR] = " ? ", + }; + struct btrfs_inode_item inode; + const char *name = (const char *) (item + 1); + char filetime[32], *target = NULL; + time_t mtime; + + if (btrfs_lookup_inode(root, &item->location, &inode, NULL)) { + printf("%s: Cannot find inode item for directory entry %.*s!\n", + __func__, item->name_len, name); + return 0; + } + + mtime = inode.mtime.sec; + ctime_r(&mtime, filetime); + + if (item->type == BTRFS_FT_SYMLINK) { + target = malloc(min(inode.size + 1, + (u64) btrfs_info.sb.sectorsize)); + + if (target && btrfs_readlink(root, item->location.objectid, + target)) { + free(target); + target = NULL; + } + + if (!target) + printf("%s: Cannot read symlink target!\n", __func__); + } + + printf("<%s> ", typestr[item->type]); + if (item->type == BTRFS_FT_CHRDEV || item->type == BTRFS_FT_BLKDEV) + printf("%4u,%5u ", (unsigned int) (inode.rdev >> 20), + (unsigned int) (inode.rdev & 0xfffff)); + else + printf("%10llu ", inode.size); + + printf("%24.24s %.*s", filetime, item->name_len, name); + + if (item->type == BTRFS_FT_SYMLINK) { + printf(" -> %s", target ? target : "?"); + if (target) + free(target); + } + + printf("\n"); + + return 0; +} + +int btrfs_probe(struct blk_desc *fs_dev_desc, disk_partition_t *fs_partition) +{ + btrfs_blk_desc = fs_dev_desc; + btrfs_part_info = fs_partition; + + memset(&btrfs_info, 0, sizeof(btrfs_info)); + + btrfs_hash_init(); + if (btrfs_read_superblock()) + return -1; + + if (btrfs_chunk_map_init()) { + printf("%s: failed to init chunk map\n", __func__); + return -1; + } + + btrfs_info.tree_root.objectid = 0; + btrfs_info.tree_root.bytenr = btrfs_info.sb.root; + btrfs_info.chunk_root.objectid = 0; + btrfs_info.chunk_root.bytenr = btrfs_info.sb.chunk_root; + + if (btrfs_read_chunk_tree()) { + printf("%s: failed to read chunk tree\n", __func__); + return -1; + } + + if (btrfs_find_root(btrfs_get_default_subvol_objectid(), + &btrfs_info.fs_root, NULL)) { + printf("%s: failed to find default subvolume\n", __func__); + return -1; + } + + return 0; +} + +int btrfs_ls(const char *path) +{ + struct btrfs_root root = btrfs_info.fs_root; + u64 inr; + u8 type; + + inr = btrfs_lookup_path(&root, root.root_dirid, path, &type, NULL, 40); + + if (inr == -1ULL) { + printf("Cannot lookup path %s\n", path); + return 1; + } + + if (type != BTRFS_FT_DIR) { + printf("Not a directory: %s\n", path); + return 1; + } + + if (btrfs_readdir(&root, inr, readdir_callback)) { + printf("An error occured while listing directory %s\n", path); + return 1; + } + + return 0; +} + +int btrfs_exists(const char *file) +{ + struct btrfs_root root = btrfs_info.fs_root; + u64 inr; + u8 type; + + inr = btrfs_lookup_path(&root, root.root_dirid, file, &type, NULL, 40); + + return (inr != -1ULL && type == BTRFS_FT_REG_FILE); +} + +int btrfs_size(const char *file, loff_t *size) +{ + struct btrfs_root root = btrfs_info.fs_root; + struct btrfs_inode_item inode; + u64 inr; + u8 type; + + inr = btrfs_lookup_path(&root, root.root_dirid, file, &type, &inode, + 40); + + if (inr == -1ULL) { + printf("Cannot lookup file %s\n", file); + return 1; + } + + if (type != BTRFS_FT_REG_FILE) { + printf("Not a regular file: %s\n", file); + return 1; + } + + *size = inode.size; + return 0; +} + +int btrfs_read(const char *file, void *buf, loff_t offset, loff_t len, + loff_t *actread) +{ + struct btrfs_root root = btrfs_info.fs_root; + struct btrfs_inode_item inode; + u64 inr, rd; + u8 type; + + inr = btrfs_lookup_path(&root, root.root_dirid, file, &type, &inode, + 40); + + if (inr == -1ULL) { + printf("Cannot lookup file %s\n", file); + return 1; + } + + if (type != BTRFS_FT_REG_FILE) { + printf("Not a regular file: %s\n", file); + return 1; + } + + if (!len) + len = inode.size; + + if (len > inode.size - offset) + len = inode.size - offset; + + rd = btrfs_file_read(&root, inr, offset, len, buf); + if (rd == -1ULL) { + printf("An error occured while reading file %s\n", file); + return 1; + } + + *actread = rd; + return 0; +} + +void btrfs_close(void) +{ + btrfs_chunk_map_exit(); +} + +int btrfs_uuid(char *uuid_str) +{ +#ifdef CONFIG_LIB_UUID + uuid_bin_to_str(btrfs_info.sb.fsid, uuid_str, UUID_STR_FORMAT_STD); + return 0; +#endif + return -ENOSYS; +} + +/* + btrfs_list_subvols(); +*/ diff --git a/fs/btrfs/btrfs.h b/fs/btrfs/btrfs.h new file mode 100644 index 0000000..4247cbb --- /dev/null +++ b/fs/btrfs/btrfs.h @@ -0,0 +1,89 @@ +/* + * BTRFS filesystem implementation for U-Boot + * + * 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __BTRFS_BTRFS_H__ +#define __BTRFS_BTRFS_H__ + +#include <linux/rbtree.h> +#include "conv-funcs.h" + +struct btrfs_info { + struct btrfs_super_block sb; + struct btrfs_root_backup *root_backup; + + struct btrfs_root tree_root; + struct btrfs_root fs_root; + struct btrfs_root chunk_root; + + struct rb_root chunks_root; +}; + +extern struct btrfs_info btrfs_info; + +/* hash.c */ +void btrfs_hash_init(void); +u32 btrfs_crc32c(u32, const void *, size_t); +u32 btrfs_csum_data(char *, u32, size_t); +void btrfs_csum_final(u32, void *); + +static inline u64 btrfs_name_hash(const char *name, int len) +{ + return btrfs_crc32c((u32) ~1, name, len); +} + +/* dev.c */ +extern struct blk_desc *btrfs_blk_desc; +extern disk_partition_t *btrfs_part_info; + +int btrfs_devread(u64, int, void *); + +/* chunk-map.c */ +u64 btrfs_map_logical_to_physical(u64); +int btrfs_chunk_map_init(void); +void btrfs_chunk_map_exit(void); +int btrfs_read_chunk_tree(void); + +/* compression.c */ +u32 btrfs_decompress(u8 type, const char *, u32, char *, u32); + +/* super.c */ +int btrfs_read_superblock(void); + +/* dir-item.c */ +typedef int (*btrfs_readdir_callback_t)(const struct btrfs_root *, + struct btrfs_dir_item *); + +int btrfs_lookup_dir_item(const struct btrfs_root *, u64, const char *, int, + struct btrfs_dir_item *); +int btrfs_readdir(const struct btrfs_root *, u64, btrfs_readdir_callback_t); + +/* root.c */ +int btrfs_find_root(u64, struct btrfs_root *, struct btrfs_root_item *); +u64 btrfs_lookup_root_ref(u64, struct btrfs_root_ref *, char *); + +/* inode.c */ +u64 btrfs_lookup_inode_ref(struct btrfs_root *, u64, struct btrfs_inode_ref *, + char *); +int btrfs_lookup_inode(const struct btrfs_root *, struct btrfs_key *, + struct btrfs_inode_item *, struct btrfs_root *); +int btrfs_readlink(const struct btrfs_root *, u64, char *); +u64 btrfs_lookup_path(struct btrfs_root *, u64, const char *, u8 *, + struct btrfs_inode_item *, int); +u64 btrfs_file_read(const struct btrfs_root *, u64, u64, u64, char *); + +/* subvolume.c */ +u64 btrfs_get_default_subvol_objectid(void); + +/* extent-io.c */ +u64 btrfs_read_extent_inline(struct btrfs_path *, + struct btrfs_file_extent_item *, u64, u64, + char *); +u64 btrfs_read_extent_reg(struct btrfs_path *, struct btrfs_file_extent_item *, + u64, u64, char *); + +#endif /* !__BTRFS_BTRFS_H__ */ diff --git a/fs/btrfs/btrfs_tree.h b/fs/btrfs/btrfs_tree.h new file mode 100644 index 0000000..f171b24 --- /dev/null +++ b/fs/btrfs/btrfs_tree.h @@ -0,0 +1,766 @@ +/* + * From linux/include/uapi/linux/btrfs_tree.h + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __BTRFS_BTRFS_TREE_H__ +#define __BTRFS_BTRFS_TREE_H__ + +#include <common.h> + +#define BTRFS_VOL_NAME_MAX 255 +#define BTRFS_NAME_MAX 255 +#define BTRFS_LABEL_SIZE 256 +#define BTRFS_FSID_SIZE 16 +#define BTRFS_UUID_SIZE 16 + +/* + * This header contains the structure definitions and constants used + * by file system objects that can be retrieved using + * the BTRFS_IOC_SEARCH_TREE ioctl. That means basically anything that + * is needed to describe a leaf node's key or item contents. + */ + +/* holds pointers to all of the tree roots */ +#define BTRFS_ROOT_TREE_OBJECTID 1ULL + +/* stores information about which extents are in use, and reference counts */ +#define BTRFS_EXTENT_TREE_OBJECTID 2ULL + +/* + * chunk tree stores translations from logical -> physical block numbering + * the super block points to the chunk tree + */ +#define BTRFS_CHUNK_TREE_OBJECTID 3ULL + +/* + * stores information about which areas of a given device are in use. + * one per device. The tree of tree roots points to the device tree + */ +#define BTRFS_DEV_TREE_OBJECTID 4ULL + +/* one per subvolume, storing files and directories */ +#define BTRFS_FS_TREE_OBJECTID 5ULL + +/* directory objectid inside the root tree */ +#define BTRFS_ROOT_TREE_DIR_OBJECTID 6ULL + +/* holds checksums of all the data extents */ +#define BTRFS_CSUM_TREE_OBJECTID 7ULL + +/* holds quota configuration and tracking */ +#define BTRFS_QUOTA_TREE_OBJECTID 8ULL + +/* for storing items that use the BTRFS_UUID_KEY* types */ +#define BTRFS_UUID_TREE_OBJECTID 9ULL + +/* tracks free space in block groups. */ +#define BTRFS_FREE_SPACE_TREE_OBJECTID 10ULL + +/* device stats in the device tree */ +#define BTRFS_DEV_STATS_OBJECTID 0ULL + +/* for storing balance parameters in the root tree */ +#define BTRFS_BALANCE_OBJECTID -4ULL + +/* orhpan objectid for tracking unlinked/truncated files */ +#define BTRFS_ORPHAN_OBJECTID -5ULL + +/* does write ahead logging to speed up fsyncs */ +#define BTRFS_TREE_LOG_OBJECTID -6ULL +#define BTRFS_TREE_LOG_FIXUP_OBJECTID -7ULL + +/* for space balancing */ +#define BTRFS_TREE_RELOC_OBJECTID -8ULL +#define BTRFS_DATA_RELOC_TREE_OBJECTID -9ULL + +/* + * extent checksums all have this objectid + * this allows them to share the logging tree + * for fsyncs + */ +#define BTRFS_EXTENT_CSUM_OBJECTID -10ULL + +/* For storing free space cache */ +#define BTRFS_FREE_SPACE_OBJECTID -11ULL + +/* + * The inode number assigned to the special inode for storing + * free ino cache + */ +#define BTRFS_FREE_INO_OBJECTID -12ULL + +/* dummy objectid represents multiple objectids */ +#define BTRFS_MULTIPLE_OBJECTIDS -255ULL + +/* + * All files have objectids in this range. + */ +#define BTRFS_FIRST_FREE_OBJECTID 256ULL +#define BTRFS_LAST_FREE_OBJECTID -256ULL +#define BTRFS_FIRST_CHUNK_TREE_OBJECTID 256ULL + + +/* + * the device items go into the chunk tree. The key is in the form + * [ 1 BTRFS_DEV_ITEM_KEY device_id ] + */ +#define BTRFS_DEV_ITEMS_OBJECTID 1ULL + +#define BTRFS_BTREE_INODE_OBJECTID 1 + +#define BTRFS_EMPTY_SUBVOL_DIR_OBJECTID 2 + +#define BTRFS_DEV_REPLACE_DEVID 0ULL + +/* + * inode items have the data typically returned from stat and store other + * info about object characteristics. There is one for every file and dir in + * the FS + */ +#define BTRFS_INODE_ITEM_KEY 1 +#define BTRFS_INODE_REF_KEY 12 +#define BTRFS_INODE_EXTREF_KEY 13 +#define BTRFS_XATTR_ITEM_KEY 24 +#define BTRFS_ORPHAN_ITEM_KEY 48 +/* reserve 2-15 close to the inode for later flexibility */ + +/* + * dir items are the name -> inode pointers in a directory. There is one + * for every name in a directory. + */ +#define BTRFS_DIR_LOG_ITEM_KEY 60 +#define BTRFS_DIR_LOG_INDEX_KEY 72 +#define BTRFS_DIR_ITEM_KEY 84 +#define BTRFS_DIR_INDEX_KEY 96 +/* + * extent data is for file data + */ +#define BTRFS_EXTENT_DATA_KEY 108 + +/* + * extent csums are stored in a separate tree and hold csums for + * an entire extent on disk. + */ +#define BTRFS_EXTENT_CSUM_KEY 128 + +/* + * root items point to tree roots. They are typically in the root + * tree used by the super block to find all the other trees + */ +#define BTRFS_ROOT_ITEM_KEY 132 + +/* + * root backrefs tie subvols and snapshots to the directory entries that + * reference them + */ +#define BTRFS_ROOT_BACKREF_KEY 144 + +/* + * root refs make a fast index for listing all of the snapshots and + * subvolumes referenced by a given root. They point directly to the + * directory item in the root that references the subvol + */ +#define BTRFS_ROOT_REF_KEY 156 + +/* + * extent items are in the extent map tree. These record which blocks + * are used, and how many references there are to each block + */ +#define BTRFS_EXTENT_ITEM_KEY 168 + +/* + * The same as the BTRFS_EXTENT_ITEM_KEY, except it's metadata we already know + * the length, so we save the level in key->offset instead of the length. + */ +#define BTRFS_METADATA_ITEM_KEY 169 + +#define BTRFS_TREE_BLOCK_REF_KEY 176 + +#define BTRFS_EXTENT_DATA_REF_KEY 178 + +#define BTRFS_EXTENT_REF_V0_KEY 180 + +#define BTRFS_SHARED_BLOCK_REF_KEY 182 + +#define BTRFS_SHARED_DATA_REF_KEY 184 + +/* + * block groups give us hints into the extent allocation trees. Which + * blocks are free etc etc + */ +#define BTRFS_BLOCK_GROUP_ITEM_KEY 192 + +/* + * Every block group is represented in the free space tree by a free space info + * item, which stores some accounting information. It is keyed on + * (block_group_start, FREE_SPACE_INFO, block_group_length). + */ +#define BTRFS_FREE_SPACE_INFO_KEY 198 + +/* + * A free space extent tracks an extent of space that is free in a block group. + * It is keyed on (start, FREE_SPACE_EXTENT, length). + */ +#define BTRFS_FREE_SPACE_EXTENT_KEY 199 + +/* + * When a block group becomes very fragmented, we convert it to use bitmaps + * instead of extents. A free space bitmap is keyed on + * (start, FREE_SPACE_BITMAP, length); the corresponding item is a bitmap with + * (length / sectorsize) bits. + */ +#define BTRFS_FREE_SPACE_BITMAP_KEY 200 + +#define BTRFS_DEV_EXTENT_KEY 204 +#define BTRFS_DEV_ITEM_KEY 216 +#define BTRFS_CHUNK_ITEM_KEY 228 + +/* + * Records the overall state of the qgroups. + * There's only one instance of this key present, + * (0, BTRFS_QGROUP_STATUS_KEY, 0) + */ +#define BTRFS_QGROUP_STATUS_KEY 240 +/* + * Records the currently used space of the qgroup. + * One key per qgroup, (0, BTRFS_QGROUP_INFO_KEY, qgroupid). + */ +#define BTRFS_QGROUP_INFO_KEY 242 +/* + * Contains the user configured limits for the qgroup. + * One key per qgroup, (0, BTRFS_QGROUP_LIMIT_KEY, qgroupid). + */ +#define BTRFS_QGROUP_LIMIT_KEY 244 +/* + * Records the child-parent relationship of qgroups. For + * each relation, 2 keys are present: + * (childid, BTRFS_QGROUP_RELATION_KEY, parentid) + * (parentid, BTRFS_QGROUP_RELATION_KEY, childid) + */ +#define BTRFS_QGROUP_RELATION_KEY 246 + +/* + * Obsolete name, see BTRFS_TEMPORARY_ITEM_KEY. + */ +#define BTRFS_BALANCE_ITEM_KEY 248 + +/* + * The key type for tree items that are stored persistently, but do not need to + * exist for extended period of time. The items can exist in any tree. + * + * [subtype, BTRFS_TEMPORARY_ITEM_KEY, data] + * + * Existing items: + * + * - balance status item + * (BTRFS_BALANCE_OBJECTID, BTRFS_TEMPORARY_ITEM_KEY, 0) + */ +#define BTRFS_TEMPORARY_ITEM_KEY 248 + +/* + * Obsolete name, see BTRFS_PERSISTENT_ITEM_KEY + */ +#define BTRFS_DEV_STATS_KEY 249 + +/* + * The key type for tree items that are stored persistently and usually exist + * for a long period, eg. filesystem lifetime. The item kinds can be status + * information, stats or preference values. The item can exist in any tree. + * + * [subtype, BTRFS_PERSISTENT_ITEM_KEY, data] + * + * Existing items: + * + * - device statistics, store IO stats in the device tree, one key for all + * stats + * (BTRFS_DEV_STATS_OBJECTID, BTRFS_DEV_STATS_KEY, 0) + */ +#define BTRFS_PERSISTENT_ITEM_KEY 249 + +/* + * Persistantly stores the device replace state in the device tree. + * The key is built like this: (0, BTRFS_DEV_REPLACE_KEY, 0). + */ +#define BTRFS_DEV_REPLACE_KEY 250 + +/* + * Stores items that allow to quickly map UUIDs to something else. + * These items are part of the filesystem UUID tree. + * The key is built like this: + * (UUID_upper_64_bits, BTRFS_UUID_KEY*, UUID_lower_64_bits). + */ +#if BTRFS_UUID_SIZE != 16 +#error "UUID items require BTRFS_UUID_SIZE == 16!" +#endif +#define BTRFS_UUID_KEY_SUBVOL 251 /* for UUIDs assigned to subvols */ +#define BTRFS_UUID_KEY_RECEIVED_SUBVOL 252 /* for UUIDs assigned to + * received subvols */ + +/* + * string items are for debugging. They just store a short string of + * data in the FS + */ +#define BTRFS_STRING_ITEM_KEY 253 + + + +/* 32 bytes in various csum fields */ +#define BTRFS_CSUM_SIZE 32 + +/* csum types */ +#define BTRFS_CSUM_TYPE_CRC32 0 + +/* + * flags definitions for directory entry item type + * + * Used by: + * struct btrfs_dir_item.type + */ +#define BTRFS_FT_UNKNOWN 0 +#define BTRFS_FT_REG_FILE 1 +#define BTRFS_FT_DIR 2 +#define BTRFS_FT_CHRDEV 3 +#define BTRFS_FT_BLKDEV 4 +#define BTRFS_FT_FIFO 5 +#define BTRFS_FT_SOCK 6 +#define BTRFS_FT_SYMLINK 7 +#define BTRFS_FT_XATTR 8 +#define BTRFS_FT_MAX 9 + +/* + * The key defines the order in the tree, and so it also defines (optimal) + * block layout. + * + * objectid corresponds to the inode number. + * + * type tells us things about the object, and is a kind of stream selector. + * so for a given inode, keys with type of 1 might refer to the inode data, + * type of 2 may point to file data in the btree and type == 3 may point to + * extents. + * + * offset is the starting byte offset for this key in the stream. + */ + +struct btrfs_key { + __u64 objectid; + __u8 type; + __u64 offset; +} __attribute__ ((__packed__)); + +struct btrfs_dev_item { + /* the internal btrfs device id */ + __u64 devid; + + /* size of the device */ + __u64 total_bytes; + + /* bytes used */ + __u64 bytes_used; + + /* optimal io alignment for this device */ + __u32 io_align; + + /* optimal io width for this device */ + __u32 io_width; + + /* minimal io size for this device */ + __u32 sector_size; + + /* type and info about this device */ + __u64 type; + + /* expected generation for this device */ + __u64 generation; + + /* + * starting byte of this partition on the device, + * to allow for stripe alignment in the future + */ + __u64 start_offset; + + /* grouping information for allocation decisions */ + __u32 dev_group; + + /* seek speed 0-100 where 100 is fastest */ + __u8 seek_speed; + + /* bandwidth 0-100 where 100 is fastest */ + __u8 bandwidth; + + /* btrfs generated uuid for this device */ + __u8 uuid[BTRFS_UUID_SIZE]; + + /* uuid of FS who owns this device */ + __u8 fsid[BTRFS_UUID_SIZE]; +} __attribute__ ((__packed__)); + +struct btrfs_stripe { + __u64 devid; + __u64 offset; + __u8 dev_uuid[BTRFS_UUID_SIZE]; +} __attribute__ ((__packed__)); + +struct btrfs_chunk { + /* size of this chunk in bytes */ + __u64 length; + + /* objectid of the root referencing this chunk */ + __u64 owner; + + __u64 stripe_len; + __u64 type; + + /* optimal io alignment for this chunk */ + __u32 io_align; + + /* optimal io width for this chunk */ + __u32 io_width; + + /* minimal io size for this chunk */ + __u32 sector_size; + + /* 2^16 stripes is quite a lot, a second limit is the size of a single + * item in the btree + */ + __u16 num_stripes; + + /* sub stripes only matter for raid10 */ + __u16 sub_stripes; + struct btrfs_stripe stripe; + /* additional stripes go here */ +} __attribute__ ((__packed__)); + +#define BTRFS_FREE_SPACE_EXTENT 1 +#define BTRFS_FREE_SPACE_BITMAP 2 + +struct btrfs_free_space_entry { + __u64 offset; + __u64 bytes; + __u8 type; +} __attribute__ ((__packed__)); + +struct btrfs_free_space_header { + struct btrfs_key location; + __u64 generation; + __u64 num_entries; + __u64 num_bitmaps; +} __attribute__ ((__packed__)); + +#define BTRFS_HEADER_FLAG_WRITTEN (1ULL << 0) +#define BTRFS_HEADER_FLAG_RELOC (1ULL << 1) + +/* Super block flags */ +/* Errors detected */ +#define BTRFS_SUPER_FLAG_ERROR (1ULL << 2) + +#define BTRFS_SUPER_FLAG_SEEDING (1ULL << 32) +#define BTRFS_SUPER_FLAG_METADUMP (1ULL << 33) + + +/* + * items in the extent btree are used to record the objectid of the + * owner of the block and the number of references + */ + +struct btrfs_extent_item { + __u64 refs; + __u64 generation; + __u64 flags; +} __attribute__ ((__packed__)); + + +#define BTRFS_EXTENT_FLAG_DATA (1ULL << 0) +#define BTRFS_EXTENT_FLAG_TREE_BLOCK (1ULL << 1) + +/* following flags only apply to tree blocks */ + +/* use full backrefs for extent pointers in the block */ +#define BTRFS_BLOCK_FLAG_FULL_BACKREF (1ULL << 8) + +/* + * this flag is only used internally by scrub and may be changed at any time + * it is only declared here to avoid collisions + */ +#define BTRFS_EXTENT_FLAG_SUPER (1ULL << 48) + +struct btrfs_tree_block_info { + struct btrfs_key key; + __u8 level; +} __attribute__ ((__packed__)); + +struct btrfs_extent_data_ref { + __u64 root; + __u64 objectid; + __u64 offset; + __u32 count; +} __attribute__ ((__packed__)); + +struct btrfs_shared_data_ref { + __u32 count; +} __attribute__ ((__packed__)); + +struct btrfs_extent_inline_ref { + __u8 type; + __u64 offset; +} __attribute__ ((__packed__)); + +/* dev extents record free space on individual devices. The owner + * field points back to the chunk allocation mapping tree that allocated + * the extent. The chunk tree uuid field is a way to double check the owner + */ +struct btrfs_dev_extent { + __u64 chunk_tree; + __u64 chunk_objectid; + __u64 chunk_offset; + __u64 length; + __u8 chunk_tree_uuid[BTRFS_UUID_SIZE]; +} __attribute__ ((__packed__)); + +struct btrfs_inode_ref { + __u64 index; + __u16 name_len; + /* name goes here */ +} __attribute__ ((__packed__)); + +struct btrfs_inode_extref { + __u64 parent_objectid; + __u64 index; + __u16 name_len; + __u8 name[0]; + /* name goes here */ +} __attribute__ ((__packed__)); + +struct btrfs_timespec { + __u64 sec; + __u32 nsec; +} __attribute__ ((__packed__)); + +struct btrfs_inode_item { + /* nfs style generation number */ + __u64 generation; + /* transid that last touched this inode */ + __u64 transid; + __u64 size; + __u64 nbytes; + __u64 block_group; + __u32 nlink; + __u32 uid; + __u32 gid; + __u32 mode; + __u64 rdev; + __u64 flags; + + /* modification sequence number for NFS */ + __u64 sequence; + + /* + * a little future expansion, for more than this we can + * just grow the inode item and version it + */ + __u64 reserved[4]; + struct btrfs_timespec atime; + struct btrfs_timespec ctime; + struct btrfs_timespec mtime; + struct btrfs_timespec otime; +} __attribute__ ((__packed__)); + +struct btrfs_dir_log_item { + __u64 end; +} __attribute__ ((__packed__)); + +struct btrfs_dir_item { + struct btrfs_key location; + __u64 transid; + __u16 data_len; + __u16 name_len; + __u8 type; +} __attribute__ ((__packed__)); + +#define BTRFS_ROOT_SUBVOL_RDONLY (1ULL << 0) + +/* + * Internal in-memory flag that a subvolume has been marked for deletion but + * still visible as a directory + */ +#define BTRFS_ROOT_SUBVOL_DEAD (1ULL << 48) + +struct btrfs_root_item { + struct btrfs_inode_item inode; + __u64 generation; + __u64 root_dirid; + __u64 bytenr; + __u64 byte_limit; + __u64 bytes_used; + __u64 last_snapshot; + __u64 flags; + __u32 refs; + struct btrfs_key drop_progress; + __u8 drop_level; + __u8 level; + + /* + * The following fields appear after subvol_uuids+subvol_times + * were introduced. + */ + + /* + * This generation number is used to test if the new fields are valid + * and up to date while reading the root item. Every time the root item + * is written out, the "generation" field is copied into this field. If + * anyone ever mounted the fs with an older kernel, we will have + * mismatching generation values here and thus must invalidate the + * new fields. See btrfs_update_root and btrfs_find_last_root for + * details. + * the offset of generation_v2 is also used as the start for the memset + * when invalidating the fields. + */ + __u64 generation_v2; + __u8 uuid[BTRFS_UUID_SIZE]; + __u8 parent_uuid[BTRFS_UUID_SIZE]; + __u8 received_uuid[BTRFS_UUID_SIZE]; + __u64 ctransid; /* updated when an inode changes */ + __u64 otransid; /* trans when created */ + __u64 stransid; /* trans when sent. non-zero for received subvol */ + __u64 rtransid; /* trans when received. non-zero for received subvol */ + struct btrfs_timespec ctime; + struct btrfs_timespec otime; + struct btrfs_timespec stime; + struct btrfs_timespec rtime; + __u64 reserved[8]; /* for future */ +} __attribute__ ((__packed__)); + +/* + * this is used for both forward and backward root refs + */ +struct btrfs_root_ref { + __u64 dirid; + __u64 sequence; + __u16 name_len; +} __attribute__ ((__packed__)); + +#define BTRFS_FILE_EXTENT_INLINE 0 +#define BTRFS_FILE_EXTENT_REG 1 +#define BTRFS_FILE_EXTENT_PREALLOC 2 + +enum btrfs_compression_type { + BTRFS_COMPRESS_NONE = 0, + BTRFS_COMPRESS_ZLIB = 1, + BTRFS_COMPRESS_LZO = 2, + BTRFS_COMPRESS_TYPES = 2, + BTRFS_COMPRESS_LAST = 3, +}; + +struct btrfs_file_extent_item { + /* + * transaction id that created this extent + */ + __u64 generation; + /* + * max number of bytes to hold this extent in ram + * when we split a compressed extent we can't know how big + * each of the resulting pieces will be. So, this is + * an upper limit on the size of the extent in ram instead of + * an exact limit. + */ + __u64 ram_bytes; + + /* + * 32 bits for the various ways we might encode the data, + * including compression and encryption. If any of these + * are set to something a given disk format doesn't understand + * it is treated like an incompat flag for reading and writing, + * but not for stat. + */ + __u8 compression; + __u8 encryption; + __u16 other_encoding; /* spare for later use */ + + /* are we inline data or a real extent? */ + __u8 type; + + /* + * disk space consumed by the extent, checksum blocks are included + * in these numbers + * + * At this offset in the structure, the inline extent data start. + */ + __u64 disk_bytenr; + __u64 disk_num_bytes; + /* + * the logical offset in file blocks (no csums) + * this extent record is for. This allows a file extent to point + * into the middle of an existing extent on disk, sharing it + * between two snapshots (useful if some bytes in the middle of the + * extent have changed + */ + __u64 offset; + /* + * the logical number of file blocks (no csums included). This + * always reflects the size uncompressed and without encoding. + */ + __u64 num_bytes; + +} __attribute__ ((__packed__)); + +struct btrfs_csum_item { + __u8 csum; +} __attribute__ ((__packed__)); + +/* different types of block groups (and chunks) */ +#define BTRFS_BLOCK_GROUP_DATA (1ULL << 0) +#define BTRFS_BLOCK_GROUP_SYSTEM (1ULL << 1) +#define BTRFS_BLOCK_GROUP_METADATA (1ULL << 2) +#define BTRFS_BLOCK_GROUP_RAID0 (1ULL << 3) +#define BTRFS_BLOCK_GROUP_RAID1 (1ULL << 4) +#define BTRFS_BLOCK_GROUP_DUP (1ULL << 5) +#define BTRFS_BLOCK_GROUP_RAID10 (1ULL << 6) +#define BTRFS_BLOCK_GROUP_RAID5 (1ULL << 7) +#define BTRFS_BLOCK_GROUP_RAID6 (1ULL << 8) +#define BTRFS_BLOCK_GROUP_RESERVED (BTRFS_AVAIL_ALLOC_BIT_SINGLE | \ + BTRFS_SPACE_INFO_GLOBAL_RSV) + +enum btrfs_raid_types { + BTRFS_RAID_RAID10, + BTRFS_RAID_RAID1, + BTRFS_RAID_DUP, + BTRFS_RAID_RAID0, + BTRFS_RAID_SINGLE, + BTRFS_RAID_RAID5, + BTRFS_RAID_RAID6, + BTRFS_NR_RAID_TYPES +}; + +#define BTRFS_BLOCK_GROUP_TYPE_MASK (BTRFS_BLOCK_GROUP_DATA | \ + BTRFS_BLOCK_GROUP_SYSTEM | \ + BTRFS_BLOCK_GROUP_METADATA) + +#define BTRFS_BLOCK_GROUP_PROFILE_MASK (BTRFS_BLOCK_GROUP_RAID0 | \ + BTRFS_BLOCK_GROUP_RAID1 | \ + BTRFS_BLOCK_GROUP_RAID5 | \ + BTRFS_BLOCK_GROUP_RAID6 | \ + BTRFS_BLOCK_GROUP_DUP | \ + BTRFS_BLOCK_GROUP_RAID10) +#define BTRFS_BLOCK_GROUP_RAID56_MASK (BTRFS_BLOCK_GROUP_RAID5 | \ + BTRFS_BLOCK_GROUP_RAID6) + +/* + * We need a bit for restriper to be able to tell when chunks of type + * SINGLE are available. This "extended" profile format is used in + * fs_info->avail_*_alloc_bits (in-memory) and balance item fields + * (on-disk). The corresponding on-disk bit in chunk.type is reserved + * to avoid remappings between two formats in future. + */ +#define BTRFS_AVAIL_ALLOC_BIT_SINGLE (1ULL << 48) + +/* + * A fake block group type that is used to communicate global block reserve + * size to userspace via the SPACE_INFO ioctl. + */ +#define BTRFS_SPACE_INFO_GLOBAL_RSV (1ULL << 49) + +#define BTRFS_EXTENDED_PROFILE_MASK (BTRFS_BLOCK_GROUP_PROFILE_MASK | \ + BTRFS_AVAIL_ALLOC_BIT_SINGLE) + +#endif /* __BTRFS_BTRFS_TREE_H__ */ diff --git a/fs/btrfs/chunk-map.c b/fs/btrfs/chunk-map.c new file mode 100644 index 0000000..48407f3 --- /dev/null +++ b/fs/btrfs/chunk-map.c @@ -0,0 +1,178 @@ +/* + * BTRFS filesystem implementation for U-Boot + * + * 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include "btrfs.h" +#include <malloc.h> + +struct chunk_map_item { + struct rb_node node; + u64 logical; + u64 length; + u64 physical; +}; + +static int add_chunk_mapping(struct btrfs_key *key, struct btrfs_chunk *chunk) +{ + struct btrfs_stripe *stripe; + u64 block_profile = chunk->type & BTRFS_BLOCK_GROUP_PROFILE_MASK; + struct rb_node **new = &(btrfs_info.chunks_root.rb_node), *prnt = NULL; + struct chunk_map_item *map_item; + + if (block_profile && block_profile != BTRFS_BLOCK_GROUP_DUP) { + printf("%s: unsupported chunk profile %llu\n", __func__, + block_profile); + return -1; + } else if (!chunk->length) { + printf("%s: zero length chunk\n", __func__); + return -1; + } + + stripe = &chunk->stripe; + btrfs_stripe_to_cpu(stripe); + + while (*new) { + struct chunk_map_item *this; + + this = rb_entry(*new, struct chunk_map_item, node); + + prnt = *new; + if (key->offset < this->logical) { + new = &((*new)->rb_left); + } else if (key->offset > this->logical) { + new = &((*new)->rb_right); + } else { + debug("%s: Logical address %llu already in map!\n", + __func__, key->offset); + return 0; + } + } + + map_item = malloc(sizeof(struct chunk_map_item)); + if (!map_item) + return -1; + + map_item->logical = key->offset; + map_item->length = chunk->length; + map_item->physical = le64_to_cpu(chunk->stripe.offset); + rb_link_node(&map_item->node, prnt, new); + rb_insert_color(&map_item->node, &btrfs_info.chunks_root); + + debug("%s: Mapping %llu to %llu\n", __func__, map_item->logical, + map_item->physical); + + return 0; +} + +u64 btrfs_map_logical_to_physical(u64 logical) +{ + struct rb_node *node = btrfs_info.chunks_root.rb_node; + + while (node) { + struct chunk_map_item *item; + + item = rb_entry(node, struct chunk_map_item, node); + + if (item->logical > logical) + node = node->rb_left; + else if (logical > item->logical + item->length) + node = node->rb_right; + else + return item->physical + logical - item->logical; + } + + printf("%s: Cannot map logical address %llu to physical\n", __func__, + logical); + + return -1ULL; +} + +void btrfs_chunk_map_exit(void) +{ + struct rb_node *now, *next; + struct chunk_map_item *item; + + for (now = rb_first_postorder(&btrfs_info.chunks_root); now; now = next) + { + item = rb_entry(now, struct chunk_map_item, node); + next = rb_next_postorder(now); + free(item); + } +} + +int btrfs_chunk_map_init(void) +{ + u8 sys_chunk_array_copy[sizeof(btrfs_info.sb.sys_chunk_array)]; + u8 * const start = sys_chunk_array_copy; + u8 * const end = start + btrfs_info.sb.sys_chunk_array_size; + u8 *cur; + struct btrfs_key *key; + struct btrfs_chunk *chunk; + + btrfs_info.chunks_root = RB_ROOT; + + memcpy(sys_chunk_array_copy, btrfs_info.sb.sys_chunk_array, + sizeof(sys_chunk_array_copy)); + + for (cur = start; cur < end;) { + key = (struct btrfs_key *) cur; + cur += sizeof(struct btrfs_key); + chunk = (struct btrfs_chunk *) cur; + + btrfs_key_to_cpu(key); + btrfs_chunk_to_cpu(chunk); + + if (key->type != BTRFS_CHUNK_ITEM_KEY) { + printf("%s: invalid key type %u\n", __func__, + key->type); + return -1; + } + + if (add_chunk_mapping(key, chunk)) + return -1; + + cur += sizeof(struct btrfs_chunk); + cur += sizeof(struct btrfs_stripe) * (chunk->num_stripes - 1); + } + + return 0; +} + +int btrfs_read_chunk_tree(void) +{ + struct btrfs_path path; + struct btrfs_key key, *found_key; + struct btrfs_chunk *chunk; + int res; + + key.objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID; + key.type = BTRFS_CHUNK_ITEM_KEY; + key.offset = 0; + + if (btrfs_search_tree(&btrfs_info.chunk_root, &key, &path)) + return -1; + + do { + found_key = btrfs_path_leaf_key(&path); + if (btrfs_comp_keys_type(&key, found_key)) + break; + + chunk = btrfs_path_item_ptr(&path, struct btrfs_chunk); + btrfs_chunk_to_cpu(chunk); + if (add_chunk_mapping(found_key, chunk)) { + res = -1; + break; + } + } while (!(res = btrfs_next_slot(&path))); + + btrfs_free_path(&path); + + if (res < 0) + return -1; + + return 0; +} diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c new file mode 100644 index 0000000..a59ff5a --- /dev/null +++ b/fs/btrfs/compression.c @@ -0,0 +1,134 @@ +/* + * BTRFS filesystem implementation for U-Boot + * + * 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include "btrfs.h" +#include <linux/lzo.h> +#include <u-boot/zlib.h> + +static u32 decompress_lzo(const u8 *cbuf, u32 clen, u8 *dbuf, u32 dlen) +{ + u32 tot_len, in_len, res; + size_t out_len; + int ret; + + if (clen < 4) + return -1; + + tot_len = le32_to_cpu(*(u32 *) cbuf); + cbuf += 4; + clen -= 4; + tot_len -= 4; + + if (tot_len == 0 && dlen) + return -1; + if (tot_len < 4) + return -1; + + res = 0; + + while (tot_len > 4) { + in_len = le32_to_cpu(*(u32 *) cbuf); + cbuf += 4; + clen -= 4; + + if (in_len > clen || tot_len < 4 + in_len) + return -1; + + tot_len -= 4 + in_len; + + out_len = dlen; + ret = lzo1x_decompress_safe(cbuf, in_len, dbuf, &out_len); + if (ret != LZO_E_OK) + return -1; + + cbuf += in_len; + clen -= in_len; + dbuf += out_len; + dlen -= out_len; + + res += out_len; + } + + return res; +} + +/* from zutil.h */ +#define PRESET_DICT 0x20 + +static u32 decompress_zlib(const u8 *_cbuf, u32 clen, u8 *dbuf, u32 dlen) +{ + int wbits = MAX_WBITS, ret = -1; + z_stream stream; + u8 *cbuf; + u32 res; + + memset(&stream, 0, sizeof(stream)); + + cbuf = (u8 *) _cbuf; + + stream.total_in = 0; + + stream.next_out = dbuf; + stream.avail_out = dlen; + stream.total_out = 0; + + /* skip adler32 check if deflate and no dictionary */ + if (clen > 2 && !(cbuf[1] & PRESET_DICT) && + ((cbuf[0] & 0x0f) == Z_DEFLATED) && + !(((cbuf[0] << 8) + cbuf[1]) % 31)) { + wbits = -((cbuf[0] >> 4) + 8); + cbuf += 2; + clen -= 2; + } + + if (Z_OK != inflateInit2(&stream, wbits)) + return -1; + + while (stream.total_in < clen) { + stream.next_in = cbuf + stream.total_in; + stream.avail_in = min((u32) (clen - stream.total_in), + (u32) btrfs_info.sb.sectorsize); + + ret = inflate(&stream, Z_NO_FLUSH); + if (ret != Z_OK) + break; + } + + res = stream.total_out; + inflateEnd(&stream); + + if (ret != Z_STREAM_END) + return -1; + + return res; +} + +u32 btrfs_decompress(u8 type, const char *c, u32 clen, char *d, u32 dlen) +{ + u32 res; + const u8 *cbuf; + u8 *dbuf; + + cbuf = (const u8 *) c; + dbuf = (u8 *) d; + + switch (type) { + case BTRFS_COMPRESS_NONE: + res = dlen < clen ? dlen : clen; + memcpy(dbuf, cbuf, res); + return res; + case BTRFS_COMPRESS_ZLIB: + return decompress_zlib(cbuf, clen, dbuf, dlen); + case BTRFS_COMPRESS_LZO: + return decompress_lzo(cbuf, clen, dbuf, dlen); + default: + printf("%s: Unsupported compression in extent: %i\n", __func__, + type); + return -1; + } +} diff --git a/fs/btrfs/conv-funcs.h b/fs/btrfs/conv-funcs.h new file mode 100644 index 0000000..f2e7944 --- /dev/null +++ b/fs/btrfs/conv-funcs.h @@ -0,0 +1,176 @@ +/* + * Functions to convert BTRFS structures from disk to CPU endianness and back. + * + * 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __BTRFS_CONV_FUNCS_H__ +#define __BTRFS_CONV_FUNCS_H__ + +#include "ctree.h" +#include <u-boot/variadic-macro.h> +#include <asm/byteorder.h> + +/* We are using variadic macros and C11 _Generic to achieve compact code. + + We want to define macro DEFINE_CONV(x, ...), where the first argument is the + name of the structure for which it shall define conversion functions (the + names of the functions shall be x_to_cpu and x_to_disk), and the other + arguments are names of the members on which the functions shall do + endianness conversion. */ + +#if defined(__LITTLE_ENDIAN) + +/* If the target machine is little endian, the conversion functions do + nothing, since the on disk format is little endian. */ + +# define DEFINE_CONV(n,...) \ + static inline struct n *n##_to_disk(struct n * r) \ + { \ + return r; \ + } \ + static inline struct n *n##_to_cpu(struct n * r) \ + { \ + return r; \ + } + +# define DEFINE_CONV_ALT(n,a,...) \ + static inline struct n *n##_to_disk_##a(struct n * r) \ + { \ + return r; \ + } \ + static inline struct n *n##_to_cpu_##a(struct n * r) \ + { \ + return r; \ + } + +#else /* !defined(__LITTLE_ENDIAN) */ + +/* Some structures contain not only scalar members, but compound types as well + (for example, struct btrfs_inode_item contains members of type struct + btrfs_timespec. + + For these members we want to call the conversion function recursively, so + first we declare the functions taking pointers to this types (these function + will be defined later by the DEFINE_CONV macro) and then we define + correspond functions taking non-pointers, so that they can be used in the + expansion of the _Generic. */ +# define DEFINE_CONV_FOR_STRUCT(n) \ + static inline struct n * n##_to_disk(struct n *); \ + static inline struct n * n##_to_cpu(struct n *); \ + static inline struct n n##_to_disk_v(struct n x) { \ + return *n##_to_disk(&x); \ + } \ + static inline struct n n##_to_cpu_v(struct n x) { \ + return *n##_to_cpu(&x); \ + } + +DEFINE_CONV_FOR_STRUCT(btrfs_key) +DEFINE_CONV_FOR_STRUCT(btrfs_stripe) +DEFINE_CONV_FOR_STRUCT(btrfs_timespec) +DEFINE_CONV_FOR_STRUCT(btrfs_inode_item) +DEFINE_CONV_FOR_STRUCT(btrfs_root_backup) +DEFINE_CONV_FOR_STRUCT(btrfs_dev_item) + +/* Now define the _Generic for both CPU to LE and LE to CPU */ +# define DEFINE_CONV_CPU_TO_LE(x) \ + (d->x) = _Generic((d->x), \ + __u16: cpu_to_le16, \ + __u32: cpu_to_le32, \ + __u64: cpu_to_le64, \ + struct btrfs_key: btrfs_key_to_disk_v, \ + struct btrfs_stripe: btrfs_stripe_to_disk_v, \ + struct btrfs_timespec: btrfs_timespec_to_disk_v, \ + struct btrfs_inode_item: btrfs_inode_item_to_disk_v, \ + struct btrfs_root_backup: btrfs_root_backup_to_disk_v, \ + struct btrfs_dev_item: btrfs_dev_item_to_disk_v \ + )((d->x)); + +# define DEFINE_CONV_LE_TO_CPU(x) \ + (d->x) = _Generic((d->x), \ + __u16: le16_to_cpu, \ + __u32: le32_to_cpu, \ + __u64: le64_to_cpu, \ + struct btrfs_key: btrfs_key_to_cpu_v, \ + struct btrfs_stripe: btrfs_stripe_to_cpu_v, \ + struct btrfs_timespec: btrfs_timespec_to_cpu_v, \ + struct btrfs_inode_item: btrfs_inode_item_to_cpu_v, \ + struct btrfs_root_backup: btrfs_root_backup_to_cpu_v, \ + struct btrfs_dev_item: btrfs_dev_item_to_cpu_v \ + )((d->x)); + +# define DEFINE_CONV_ONE(t,n,m,...) \ + static inline struct t * n(struct t * d) { \ + CALL_MACRO_FOR_EACH(m, ##__VA_ARGS__) \ + return d; \ + } + +/* Finally define the DEFINE_CONV macro */ +# define DEFINE_CONV(n,...) \ + DEFINE_CONV_ONE(n,n##_to_disk,DEFINE_CONV_CPU_TO_LE,##__VA_ARGS__) \ + DEFINE_CONV_ONE(n,n##_to_cpu,DEFINE_CONV_LE_TO_CPU,##__VA_ARGS__) + +# define DEFINE_CONV_ALT(n,a,...) \ + DEFINE_CONV_ONE(n,n##_to_disk_##a,DEFINE_CONV_CPU_TO_LE, \ + ##__VA_ARGS__) \ + DEFINE_CONV_ONE(n,n##_to_cpu_##a,DEFINE_CONV_LE_TO_CPU,##__VA_ARGS__) + +#endif /* !defined(__LITTLE_ENDIAN) */ + +DEFINE_CONV(btrfs_key, objectid, offset) +DEFINE_CONV(btrfs_dev_item, devid, total_bytes, bytes_used, io_align, io_width, + sector_size, type, generation, start_offset, dev_group) +DEFINE_CONV(btrfs_stripe, devid, offset) +DEFINE_CONV(btrfs_chunk, length, owner, stripe_len, type, io_align, io_width, + sector_size, num_stripes, sub_stripes) +DEFINE_CONV(btrfs_free_space_entry, offset, bytes) +DEFINE_CONV(btrfs_free_space_header, location, generation, num_entries, + num_bitmaps) +DEFINE_CONV(btrfs_extent_item, refs, generation, flags) +DEFINE_CONV(btrfs_tree_block_info, key) +DEFINE_CONV(btrfs_extent_data_ref, root, objectid, offset, count) +DEFINE_CONV(btrfs_shared_data_ref, count) +DEFINE_CONV(btrfs_extent_inline_ref, offset) +DEFINE_CONV(btrfs_dev_extent, chunk_tree, chunk_objectid, chunk_offset, length) +DEFINE_CONV(btrfs_inode_ref, index, name_len) +DEFINE_CONV(btrfs_inode_extref, parent_objectid, index, name_len) +DEFINE_CONV(btrfs_timespec, sec, nsec) +DEFINE_CONV(btrfs_inode_item, generation, transid, size, nbytes, block_group, + nlink, uid, gid, mode, rdev, flags, sequence, atime, ctime, mtime, + otime) +DEFINE_CONV(btrfs_dir_log_item, end) +DEFINE_CONV(btrfs_dir_item, location, transid, data_len, name_len) +DEFINE_CONV(btrfs_root_item, inode, generation, root_dirid, bytenr, byte_limit, + bytes_used, last_snapshot, flags, refs, drop_progress, + generation_v2, ctransid, otransid, stransid, rtransid, ctime, + otime, stime, rtime) +DEFINE_CONV(btrfs_root_ref, dirid, sequence, name_len) +DEFINE_CONV(btrfs_file_extent_item, generation, ram_bytes, other_encoding, + disk_bytenr, disk_num_bytes, offset, num_bytes) +DEFINE_CONV_ALT(btrfs_file_extent_item, inl, generation, ram_bytes, + other_encoding) +DEFINE_CONV(btrfs_dev_replace_item, src_devid, cursor_left, cursor_right, + cont_reading_from_srcdev_mode, replace_state, time_started, + time_stopped, num_write_errors, num_uncorrectable_read_errors) +DEFINE_CONV(btrfs_block_group_item, used, chunk_objectid, flags) +DEFINE_CONV(btrfs_free_space_info, extent_count, flags) + +DEFINE_CONV(btrfs_header, bytenr, flags, generation, owner, nritems) +DEFINE_CONV(btrfs_root_backup, tree_root, tree_root_gen, chunk_root, + chunk_root_gen, extent_root, extent_root_gen, fs_root, fs_root_gen, + dev_root, dev_root_gen, csum_root, csum_root_gen, total_bytes, + bytes_used, num_devices) +DEFINE_CONV(btrfs_super_block, bytenr, flags, magic, generation, root, + chunk_root, log_root, log_root_transid, total_bytes, bytes_used, + root_dir_objectid, num_devices, sectorsize, nodesize, + __unused_leafsize, stripesize, sys_chunk_array_size, + chunk_root_generation, compat_flags, compat_ro_flags, + incompat_flags, csum_type, dev_item, cache_generation, + uuid_tree_generation, super_roots[0], super_roots[1], + super_roots[2], super_roots[3]) +DEFINE_CONV(btrfs_item, key, offset, size) +DEFINE_CONV(btrfs_key_ptr, key, blockptr, generation) + +#endif /* __BTRFS_CONV_FUNCS_H__ */ diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c new file mode 100644 index 0000000..b13ecb9 --- /dev/null +++ b/fs/btrfs/ctree.c @@ -0,0 +1,289 @@ +/* + * BTRFS filesystem implementation for U-Boot + * + * 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include "btrfs.h" +#include <malloc.h> + +int btrfs_comp_keys(struct btrfs_key *a, struct btrfs_key *b) +{ + if (a->objectid > b->objectid) + return 1; + if (a->objectid < b->objectid) + return -1; + if (a->type > b->type) + return 1; + if (a->type < b->type) + return -1; + if (a->offset > b->offset) + return 1; + if (a->offset < b->offset) + return -1; + return 0; +} + +int btrfs_comp_keys_type(struct btrfs_key *a, struct btrfs_key *b) +{ + if (a->objectid > b->objectid) + return 1; + if (a->objectid < b->objectid) + return -1; + if (a->type > b->type) + return 1; + if (a->type < b->type) + return -1; + return 0; +} + +static int generic_bin_search(void *addr, int item_size, struct btrfs_key *key, + int max, int *slot) +{ + int low = 0, high = max, mid, ret; + struct btrfs_key *tmp; + + if (0) { + int i; + printf("\tsearching %llu %i\n", key->objectid, key->type); + for (i = 0; i < max; ++i) { + tmp = (struct btrfs_key *) ((u8 *) addr + i*item_size); + printf("\t\t%llu %i\n", tmp->objectid, tmp->type); + } + printf("\n"); + } + + while (low < high) { + mid = (low + high) / 2; + + tmp = (struct btrfs_key *) ((u8 *) addr + mid*item_size); + ret = btrfs_comp_keys(tmp, key); + + if (ret < 0) { + low = mid + 1; + } else if (ret > 0) { + high = mid; + } else { + *slot = mid; + return 0; + } + } + + *slot = low; + return 1; +} + +int btrfs_bin_search(union btrfs_tree_node *p, struct btrfs_key *key, + int *slot) +{ + void *addr; + unsigned long size; + + if (p->header.level) { + addr = p->node.ptrs; + size = sizeof(struct btrfs_key_ptr); + } else { + addr = p->leaf.items; + size = sizeof(struct btrfs_item); + } + + return generic_bin_search(addr, size, key, p->header.nritems, slot); +} + +static void clear_path(struct btrfs_path *p) +{ + int i; + + for (i = 0; i < BTRFS_MAX_LEVEL; ++i) { + p->nodes[i] = NULL; + p->slots[i] = 0; + } +} + +void btrfs_free_path(struct btrfs_path *p) +{ + int i; + + for (i = 0; i < BTRFS_MAX_LEVEL; ++i) { + if (p->nodes[i]) + free(p->nodes[i]); + } + + clear_path(p); +} + +static int read_tree_node(u64 physical, union btrfs_tree_node **buf) +{ + struct btrfs_header hdr; + unsigned long size, offset = sizeof(hdr); + union btrfs_tree_node *res; + u32 i; + + if (!btrfs_devread(physical, sizeof(hdr), &hdr)) + return -1; + + btrfs_header_to_cpu(&hdr); + + if (hdr.level) + size = sizeof(struct btrfs_node) + + hdr.nritems * sizeof(struct btrfs_key_ptr); + else + size = btrfs_info.sb.nodesize; + + res = malloc(size); + if (!res) { + debug("%s: malloc failed\n", __func__); + return -1; + } + + if (!btrfs_devread(physical + offset, size - offset, + ((u8 *) res) + offset)) { + free(res); + return -1; + } + + res->header = hdr; + if (hdr.level) + for (i = 0; i < hdr.nritems; ++i) + btrfs_key_ptr_to_cpu(&res->node.ptrs[i]); + else + for (i = 0; i < hdr.nritems; ++i) + btrfs_item_to_cpu(&res->leaf.items[i]); + + *buf = res; + + return 0; +} + +int btrfs_search_tree(const struct btrfs_root *root, struct btrfs_key *key, + struct btrfs_path *p) +{ + u8 lvl, prev_lvl; + int i, slot, ret; + u64 logical, physical; + union btrfs_tree_node *buf; + + clear_path(p); + + logical = root->bytenr; + + for (i = 0; i < BTRFS_MAX_LEVEL; ++i) { + physical = btrfs_map_logical_to_physical(logical); + if (physical == -1ULL) + goto err; + + if (read_tree_node(physical, &buf)) + goto err; + + lvl = buf->header.level; + if (i && prev_lvl != lvl + 1) { + printf("%s: invalid level in header at %llu\n", + __func__, logical); + goto err; + } + prev_lvl = lvl; + + ret = btrfs_bin_search(buf, key, &slot); + if (ret < 0) + goto err; + if (ret && slot > 0 && lvl) + slot -= 1; + + p->slots[lvl] = slot; + p->nodes[lvl] = buf; + + if (lvl) + logical = buf->node.ptrs[slot].blockptr; + else + break; + } + + return 0; +err: + btrfs_free_path(p); + return -1; +} + +static int jump_leaf(struct btrfs_path *path, int dir) +{ + struct btrfs_path p; + u32 slot; + int level = 1, from_level, i; + + dir = dir >= 0 ? 1 : -1; + + p = *path; + + while (level < BTRFS_MAX_LEVEL) { + if (!p.nodes[level]) + return 1; + + slot = p.slots[level]; + if ((dir > 0 && slot + dir >= p.nodes[level]->header.nritems) + || (dir < 0 && !slot)) + level++; + else + break; + } + + if (level == BTRFS_MAX_LEVEL) + return 1; + + p.slots[level] = slot + dir; + level--; + from_level = level; + + while (level >= 0) { + u64 logical, physical; + + slot = p.slots[level + 1]; + logical = p.nodes[level + 1]->node.ptrs[slot].blockptr; + physical = btrfs_map_logical_to_physical(logical); + if (physical == -1ULL) + goto err; + + if (read_tree_node(physical, &p.nodes[level])) + goto err; + + if (dir > 0) + p.slots[level] = 0; + else + p.slots[level] = p.nodes[level]->header.nritems - 1; + level--; + } + + /* Free rewritten nodes in path */ + for (i = 0; i <= from_level; ++i) + free(path->nodes[i]); + + *path = p; + return 0; + +err: + /* Free rewritten nodes in p */ + for (i = level + 1; i <= from_level; ++i) + free(p.nodes[i]); + return -1; +} + +int btrfs_prev_slot(struct btrfs_path *p) +{ + if (!p->slots[0]) + return jump_leaf(p, -1); + + p->slots[0]--; + return 0; +} + +int btrfs_next_slot(struct btrfs_path *p) +{ + struct btrfs_leaf *leaf = &p->nodes[0]->leaf; + + if (p->slots[0] >= leaf->header.nritems) + return jump_leaf(p, 1); + + p->slots[0]++; + return 0; +} diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h new file mode 100644 index 0000000..39f4473 --- /dev/null +++ b/fs/btrfs/ctree.h @@ -0,0 +1,334 @@ +/* + * From linux/fs/btrfs/ctree.h + * Copyright (C) 2007,2008 Oracle. All rights reserved. + * + * Modified in 2017 by Marek Behun, CZ.NIC, marek.behun@nic.cz + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __BTRFS_CTREE_H__ +#define __BTRFS_CTREE_H__ + +#include <common.h> +#include <compiler.h> +#include "btrfs_tree.h" + +#define BTRFS_MAGIC 0x4D5F53665248425FULL /* ascii _BHRfS_M, no null */ + +#define BTRFS_MAX_MIRRORS 3 + +#define BTRFS_MAX_LEVEL 8 + +#define BTRFS_COMPAT_EXTENT_TREE_V0 + +/* + * the max metadata block size. This limit is somewhat artificial, + * but the memmove costs go through the roof for larger blocks. + */ +#define BTRFS_MAX_METADATA_BLOCKSIZE 65536 + +/* + * we can actually store much bigger names, but lets not confuse the rest + * of linux + */ +#define BTRFS_NAME_LEN 255 + +/* + * Theoretical limit is larger, but we keep this down to a sane + * value. That should limit greatly the possibility of collisions on + * inode ref items. + */ +#define BTRFS_LINK_MAX 65535U + +static const int btrfs_csum_sizes[] = { 4 }; + +/* four bytes for CRC32 */ +#define BTRFS_EMPTY_DIR_SIZE 0 + +/* ioprio of readahead is set to idle */ +#define BTRFS_IOPRIO_READA (IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE, 0)) + +#define BTRFS_DIRTY_METADATA_THRESH SZ_32M + +#define BTRFS_MAX_EXTENT_SIZE SZ_128M + +/* + * File system states + */ +#define BTRFS_FS_STATE_ERROR 0 +#define BTRFS_FS_STATE_REMOUNTING 1 +#define BTRFS_FS_STATE_TRANS_ABORTED 2 +#define BTRFS_FS_STATE_DEV_REPLACING 3 +#define BTRFS_FS_STATE_DUMMY_FS_INFO 4 + +#define BTRFS_BACKREF_REV_MAX 256 +#define BTRFS_BACKREF_REV_SHIFT 56 +#define BTRFS_BACKREF_REV_MASK (((u64)BTRFS_BACKREF_REV_MAX - 1) << \ + BTRFS_BACKREF_REV_SHIFT) + +#define BTRFS_OLD_BACKREF_REV 0 +#define BTRFS_MIXED_BACKREF_REV 1 + +/* + * every tree block (leaf or node) starts with this header. + */ +struct btrfs_header { + /* these first four must match the super block */ + __u8 csum[BTRFS_CSUM_SIZE]; + __u8 fsid[BTRFS_FSID_SIZE]; /* FS specific uuid */ + __u64 bytenr; /* which block this node is supposed to live in */ + __u64 flags; + + /* allowed to be different from the super from here on down */ + __u8 chunk_tree_uuid[BTRFS_UUID_SIZE]; + __u64 generation; + __u64 owner; + __u32 nritems; + __u8 level; +} __attribute__ ((__packed__)); + +/* + * this is a very generous portion of the super block, giving us + * room to translate 14 chunks with 3 stripes each. + */ +#define BTRFS_SYSTEM_CHUNK_ARRAY_SIZE 2048 + +/* + * just in case we somehow lose the roots and are not able to mount, + * we store an array of the roots from previous transactions + * in the super. + */ +#define BTRFS_NUM_BACKUP_ROOTS 4 +struct btrfs_root_backup { + __u64 tree_root; + __u64 tree_root_gen; + + __u64 chunk_root; + __u64 chunk_root_gen; + + __u64 extent_root; + __u64 extent_root_gen; + + __u64 fs_root; + __u64 fs_root_gen; + + __u64 dev_root; + __u64 dev_root_gen; + + __u64 csum_root; + __u64 csum_root_gen; + + __u64 total_bytes; + __u64 bytes_used; + __u64 num_devices; + /* future */ + __u64 unused_64[4]; + + __u8 tree_root_level; + __u8 chunk_root_level; + __u8 extent_root_level; + __u8 fs_root_level; + __u8 dev_root_level; + __u8 csum_root_level; + /* future and to align */ + __u8 unused_8[10]; +} __attribute__ ((__packed__)); + +/* + * the super block basically lists the main trees of the FS + * it currently lacks any block count etc etc + */ +struct btrfs_super_block { + __u8 csum[BTRFS_CSUM_SIZE]; + /* the first 4 fields must match struct btrfs_header */ + __u8 fsid[BTRFS_FSID_SIZE]; /* FS specific uuid */ + __u64 bytenr; /* this block number */ + __u64 flags; + + /* allowed to be different from the btrfs_header from here own down */ + __u64 magic; + __u64 generation; + __u64 root; + __u64 chunk_root; + __u64 log_root; + + /* this will help find the new super based on the log root */ + __u64 log_root_transid; + __u64 total_bytes; + __u64 bytes_used; + __u64 root_dir_objectid; + __u64 num_devices; + __u32 sectorsize; + __u32 nodesize; + __u32 __unused_leafsize; + __u32 stripesize; + __u32 sys_chunk_array_size; + __u64 chunk_root_generation; + __u64 compat_flags; + __u64 compat_ro_flags; + __u64 incompat_flags; + __u16 csum_type; + __u8 root_level; + __u8 chunk_root_level; + __u8 log_root_level; + struct btrfs_dev_item dev_item; + + char label[BTRFS_LABEL_SIZE]; + + __u64 cache_generation; + __u64 uuid_tree_generation; + + /* future expansion */ + __u64 reserved[30]; + __u8 sys_chunk_array[BTRFS_SYSTEM_CHUNK_ARRAY_SIZE]; + struct btrfs_root_backup super_roots[BTRFS_NUM_BACKUP_ROOTS]; +} __attribute__ ((__packed__)); + +/* + * Compat flags that we support. If any incompat flags are set other than the + * ones specified below then we will fail to mount + */ +#define BTRFS_FEATURE_COMPAT_SUPP 0ULL +#define BTRFS_FEATURE_COMPAT_SAFE_SET 0ULL +#define BTRFS_FEATURE_COMPAT_SAFE_CLEAR 0ULL + +#define BTRFS_FEATURE_COMPAT_RO_SUPP \ + (BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE | \ + BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE_VALID) + +#define BTRFS_FEATURE_COMPAT_RO_SAFE_SET 0ULL +#define BTRFS_FEATURE_COMPAT_RO_SAFE_CLEAR 0ULL + +#define BTRFS_FEATURE_INCOMPAT_SUPP \ + (BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF | \ + BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL | \ + BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS | \ + BTRFS_FEATURE_INCOMPAT_BIG_METADATA | \ + BTRFS_FEATURE_INCOMPAT_COMPRESS_LZO | \ + BTRFS_FEATURE_INCOMPAT_RAID56 | \ + BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF | \ + BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA | \ + BTRFS_FEATURE_INCOMPAT_NO_HOLES) + +#define BTRFS_FEATURE_INCOMPAT_SAFE_SET \ + (BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF) +#define BTRFS_FEATURE_INCOMPAT_SAFE_CLEAR 0ULL + +/* + * A leaf is full of items. offset and size tell us where to find + * the item in the leaf (relative to the start of the data area) + */ +struct btrfs_item { + struct btrfs_key key; + __u32 offset; + __u32 size; +} __attribute__ ((__packed__)); + +/* + * leaves have an item area and a data area: + * [item0, item1....itemN] [free space] [dataN...data1, data0] + * + * The data is separate from the items to get the keys closer together + * during searches. + */ +struct btrfs_leaf { + struct btrfs_header header; + struct btrfs_item items[]; +} __attribute__ ((__packed__)); + +/* + * all non-leaf blocks are nodes, they hold only keys and pointers to + * other blocks + */ +struct btrfs_key_ptr { + struct btrfs_key key; + __u64 blockptr; + __u64 generation; +} __attribute__ ((__packed__)); + +struct btrfs_node { + struct btrfs_header header; + struct btrfs_key_ptr ptrs[]; +} __attribute__ ((__packed__)); + +union btrfs_tree_node { + struct btrfs_header header; + struct btrfs_leaf leaf; + struct btrfs_node node; +}; + +typedef __u8 u8; +typedef __u16 u16; +typedef __u32 u32; +typedef __u64 u64; + +struct btrfs_path { + union btrfs_tree_node *nodes[BTRFS_MAX_LEVEL]; + u32 slots[BTRFS_MAX_LEVEL]; +}; + +struct btrfs_root { + u64 objectid; + u64 bytenr; + u64 root_dirid; +}; + +int btrfs_comp_keys(struct btrfs_key *, struct btrfs_key *); +int btrfs_comp_keys_type(struct btrfs_key *, struct btrfs_key *); +int btrfs_bin_search(union btrfs_tree_node *, struct btrfs_key *, int *); +void btrfs_free_path(struct btrfs_path *); +int btrfs_search_tree(const struct btrfs_root *, struct btrfs_key *, + struct btrfs_path *); +int btrfs_prev_slot(struct btrfs_path *); +int btrfs_next_slot(struct btrfs_path *); + +static inline struct btrfs_key *btrfs_path_leaf_key(struct btrfs_path *p) { + return &p->nodes[0]->leaf.items[p->slots[0]].key; +} + +static inline struct btrfs_key * +btrfs_search_tree_key_type(const struct btrfs_root *root, u64 objectid, + u8 type, struct btrfs_path *path) +{ + struct btrfs_key key, *res; + + key.objectid = objectid; + key.type = type; + key.offset = 0; + + if (btrfs_search_tree(root, &key, path)) + return NULL; + + res = btrfs_path_leaf_key(path); + if (btrfs_comp_keys_type(&key, res)) { + btrfs_free_path(path); + return NULL; + } + + return res; +} + +static inline u32 btrfs_path_item_size(struct btrfs_path *p) +{ + return p->nodes[0]->leaf.items[p->slots[0]].size; +} + +static inline void *btrfs_leaf_data(struct btrfs_leaf *leaf, u32 slot) +{ + return ((u8 *) leaf) + sizeof(struct btrfs_header) + + leaf->items[slot].offset; +} + +static inline void *btrfs_path_leaf_data(struct btrfs_path *p) +{ + return btrfs_leaf_data(&p->nodes[0]->leaf, p->slots[0]); +} + +#define btrfs_item_ptr(l,s,t) \ + ((t *) btrfs_leaf_data((l),(s))) + +#define btrfs_path_item_ptr(p,t) \ + ((t *) btrfs_path_leaf_data((p))) + +#endif /* __BTRFS_CTREE_H__ */ diff --git a/fs/btrfs/dev.c b/fs/btrfs/dev.c new file mode 100644 index 0000000..fd2e9b6 --- /dev/null +++ b/fs/btrfs/dev.c @@ -0,0 +1,26 @@ +/* + * BTRFS filesystem implementation for U-Boot + * + * 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <compiler.h> +#include <fs_internal.h> + +struct blk_desc *btrfs_blk_desc; +disk_partition_t *btrfs_part_info; + +int btrfs_devread(u64 address, int byte_len, void *buf) +{ + lbaint_t sector; + int byte_offset; + + sector = address >> btrfs_blk_desc->log2blksz; + byte_offset = address % btrfs_blk_desc->blksz; + + return fs_devread(btrfs_blk_desc, btrfs_part_info, sector, byte_offset, + byte_len, buf); +} diff --git a/fs/btrfs/dir-item.c b/fs/btrfs/dir-item.c new file mode 100644 index 0000000..decf86e --- /dev/null +++ b/fs/btrfs/dir-item.c @@ -0,0 +1,125 @@ +/* + * BTRFS filesystem implementation for U-Boot + * + * 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include "btrfs.h" + +static int verify_dir_item(struct btrfs_dir_item *item, u32 start, u32 total) +{ + u16 max_len = BTRFS_NAME_LEN; + u32 end; + + if (item->type >= BTRFS_FT_MAX) { + printf("%s: invalid dir item type: %i\n", __func__, item->type); + return 1; + } + + if (item->type == BTRFS_FT_XATTR) + max_len = 255; /* XATTR_NAME_MAX */ + + end = start + sizeof(*item) + item->name_len; + if (item->name_len > max_len || end > total) { + printf("%s: invalid dir item name len: %u\n", __func__, + item->name_len); + return 1; + } + + return 0; +} + +static struct btrfs_dir_item * +btrfs_match_dir_item_name(struct btrfs_path *path, const char *name, + int name_len) +{ + struct btrfs_dir_item *item; + u32 total_len, cur = 0, this_len; + const char *name_ptr; + + item = btrfs_path_item_ptr(path, struct btrfs_dir_item); + + total_len = btrfs_path_item_size(path); + + while (cur < total_len) { + btrfs_dir_item_to_cpu(item); + this_len = sizeof(*item) + item->name_len + item->data_len; + name_ptr = (const char *) (item + 1); + + if (verify_dir_item(item, cur, total_len)) + return NULL; + if (item->name_len == name_len && !memcmp(name_ptr, name, + name_len)) + return item; + + cur += this_len; + item = (struct btrfs_dir_item *) ((u8 *) item + this_len); + } + + return NULL; +} + +int btrfs_lookup_dir_item(const struct btrfs_root *root, u64 dir, + const char *name, int name_len, + struct btrfs_dir_item *item) +{ + struct btrfs_path path; + struct btrfs_key key; + struct btrfs_dir_item *res = NULL; + + key.objectid = dir; + key.type = BTRFS_DIR_ITEM_KEY; + key.offset = btrfs_name_hash(name, name_len); + + if (btrfs_search_tree(root, &key, &path)) + return -1; + + if (btrfs_comp_keys_type(&key, btrfs_path_leaf_key(&path))) + goto out; + + res = btrfs_match_dir_item_name(&path, name, name_len); + if (res) + *item = *res; +out: + btrfs_free_path(&path); + return res ? 0 : -1; +} + +int btrfs_readdir(const struct btrfs_root *root, u64 dir, + btrfs_readdir_callback_t callback) +{ + struct btrfs_path path; + struct btrfs_key key, *found_key; + struct btrfs_dir_item *item; + int res; + + key.objectid = dir; + key.type = BTRFS_DIR_INDEX_KEY; + key.offset = 0; + + if (btrfs_search_tree(root, &key, &path)) + return -1; + + do { + found_key = btrfs_path_leaf_key(&path); + if (btrfs_comp_keys_type(&key, found_key)) + break; + + item = btrfs_path_item_ptr(&path, struct btrfs_dir_item); + btrfs_dir_item_to_cpu(item); + + if (verify_dir_item(item, 0, sizeof(*item) + item->name_len)) + continue; + if (item->type == BTRFS_FT_XATTR) + continue; + + if (callback(root, item)) + break; + } while (!(res = btrfs_next_slot(&path))); + + btrfs_free_path(&path); + + return res < 0 ? -1 : 0; +} diff --git a/fs/btrfs/extent-io.c b/fs/btrfs/extent-io.c new file mode 100644 index 0000000..feb9143 --- /dev/null +++ b/fs/btrfs/extent-io.c @@ -0,0 +1,120 @@ +/* + * BTRFS filesystem implementation for U-Boot + * + * 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include "btrfs.h" +#include <malloc.h> + +u64 btrfs_read_extent_inline(struct btrfs_path *path, + struct btrfs_file_extent_item *extent, u64 offset, + u64 size, char *out) +{ + u32 clen, dlen, orig_size = size, res; + const char *cbuf; + char *dbuf; + const int data_off = offsetof(struct btrfs_file_extent_item, + disk_bytenr); + + clen = btrfs_path_item_size(path) - data_off; + cbuf = (const char *) extent + data_off; + dlen = extent->ram_bytes; + + if (offset > dlen) + return -1ULL; + + if (size > dlen - offset) + size = dlen - offset; + + if (extent->compression == BTRFS_COMPRESS_NONE) { + memcpy(out, cbuf + offset, size); + return size; + } + + if (dlen > orig_size) { + dbuf = malloc(dlen); + if (!dbuf) + return -1ULL; + } else { + dbuf = out; + } + + res = btrfs_decompress(extent->compression, cbuf, clen, dbuf, dlen); + if (res == -1 || res != dlen) + goto err; + + if (dlen > orig_size) { + memcpy(out, dbuf + offset, size); + free(dbuf); + } else if (offset) { + memmove(out, dbuf + offset, size); + } + + return size; + +err: + if (dlen > orig_size) + free(dbuf); + return -1ULL; +} + +u64 btrfs_read_extent_reg(struct btrfs_path *path, + struct btrfs_file_extent_item *extent, u64 offset, + u64 size, char *out) +{ + u64 physical, clen, dlen, orig_size = size; + u32 res; + char *cbuf, *dbuf; + + clen = extent->disk_num_bytes; + dlen = extent->num_bytes; + + if (offset > dlen) + return -1ULL; + + if (size > dlen - offset) + size = dlen - offset; + + physical = btrfs_map_logical_to_physical(extent->disk_bytenr); + if (physical == -1ULL) + return -1ULL; + + if (extent->compression == BTRFS_COMPRESS_NONE) { + physical += extent->offset + offset; + if (!btrfs_devread(physical, size, out)) + return -1ULL; + + return size; + } + + cbuf = malloc(dlen > size ? clen + dlen : clen); + if (!cbuf) + return -1ULL; + + if (dlen > orig_size) + dbuf = cbuf + clen; + else + dbuf = out; + + if (!btrfs_devread(physical, clen, cbuf)) + goto err; + + res = btrfs_decompress(extent->compression, cbuf, clen, dbuf, dlen); + if (res == -1) + goto err; + + if (dlen > orig_size) + memcpy(out, dbuf + offset, size); + else + memmove(out, dbuf + offset, size); + + free(cbuf); + return res; + +err: + free(cbuf); + return -1ULL; +} diff --git a/fs/btrfs/hash.c b/fs/btrfs/hash.c new file mode 100644 index 0000000..f8a50e5 --- /dev/null +++ b/fs/btrfs/hash.c @@ -0,0 +1,38 @@ +/* + * BTRFS filesystem implementation for U-Boot + * + * 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include "btrfs.h" +#include <u-boot/crc.h> + +static u32 btrfs_crc32c_table[256]; + +void btrfs_hash_init(void) +{ + static int inited = 0; + + if (!inited) { + crc32c_init(btrfs_crc32c_table, 0x82F63B78); + inited = 1; + } +} + +u32 btrfs_crc32c(u32 crc, const void *data, size_t length) +{ + return crc32c_cal(crc, (const char *) data, length, + btrfs_crc32c_table); +} + +u32 btrfs_csum_data(char *data, u32 seed, size_t len) +{ + return btrfs_crc32c(seed, data, len); +} + +void btrfs_csum_final(u32 crc, void *result) +{ + *((u32 *) result) = cpu_to_le32(~crc); +} diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c new file mode 100644 index 0000000..0d3da28 --- /dev/null +++ b/fs/btrfs/inode.c @@ -0,0 +1,385 @@ +/* + * BTRFS filesystem implementation for U-Boot + * + * 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include "btrfs.h" +#include <malloc.h> + +u64 btrfs_lookup_inode_ref(struct btrfs_root *root, u64 inr, + struct btrfs_inode_ref *refp, char *name) +{ + struct btrfs_path path; + struct btrfs_key *key; + struct btrfs_inode_ref *ref; + u64 res = -1ULL; + + key = btrfs_search_tree_key_type(root, inr, BTRFS_INODE_REF_KEY, + &path); + + if (!key) + return -1ULL; + + ref = btrfs_path_item_ptr(&path, struct btrfs_inode_ref); + btrfs_inode_ref_to_cpu(ref); + + if (refp) + *refp = *ref; + + if (name) { + if (ref->name_len > BTRFS_NAME_MAX) { + printf("%s: inode name too long: %u\n", __func__, + ref->name_len); + goto out; + } + + memcpy(name, ref + 1, ref->name_len); + } + + res = key->offset; +out: + btrfs_free_path(&path); + return res; +} + +int btrfs_lookup_inode(const struct btrfs_root *root, + struct btrfs_key *location, + struct btrfs_inode_item *item, + struct btrfs_root *new_root) +{ + struct btrfs_root tmp_root = *root; + struct btrfs_path path; + int res = -1; + + if (location->type == BTRFS_ROOT_ITEM_KEY) { + if (btrfs_find_root(location->objectid, &tmp_root, NULL)) + return -1; + + location->objectid = tmp_root.root_dirid; + location->type = BTRFS_INODE_ITEM_KEY; + location->offset = 0; + } + + if (btrfs_search_tree(&tmp_root, location, &path)) + return res; + + if (btrfs_comp_keys(location, btrfs_path_leaf_key(&path))) + goto out; + + if (item) { + *item = *btrfs_path_item_ptr(&path, struct btrfs_inode_item); + btrfs_inode_item_to_cpu(item); + } + + if (new_root) + *new_root = tmp_root; + + res = 0; + +out: + btrfs_free_path(&path); + return res; +} + +int btrfs_readlink(const struct btrfs_root *root, u64 inr, char *target) +{ + struct btrfs_path path; + struct btrfs_key key; + struct btrfs_file_extent_item *extent; + const char *data_ptr; + int res = -1; + + key.objectid = inr; + key.type = BTRFS_EXTENT_DATA_KEY; + key.offset = 0; + + if (btrfs_search_tree(root, &key, &path)) + return -1; + + if (btrfs_comp_keys(&key, btrfs_path_leaf_key(&path))) + goto out; + + extent = btrfs_path_item_ptr(&path, struct btrfs_file_extent_item); + if (extent->type != BTRFS_FILE_EXTENT_INLINE) { + printf("%s: Extent for symlink %llu not of INLINE type\n", + __func__, inr); + goto out; + } + + btrfs_file_extent_item_to_cpu_inl(extent); + + if (extent->compression != BTRFS_COMPRESS_NONE) { + printf("%s: Symlink %llu extent data compressed!\n", __func__, + inr); + goto out; + } else if (extent->encryption != 0) { + printf("%s: Symlink %llu extent data encrypted!\n", __func__, + inr); + goto out; + } else if (extent->ram_bytes >= btrfs_info.sb.sectorsize) { + printf("%s: Symlink %llu extent data too long (%llu)!\n", + __func__, inr, extent->ram_bytes); + goto out; + } + + data_ptr = (const char *) extent + + offsetof(struct btrfs_file_extent_item, disk_bytenr); + + memcpy(target, data_ptr, extent->ram_bytes); + target[extent->ram_bytes] = '\0'; + res = 0; +out: + btrfs_free_path(&path); + return res; +} + +/* inr must be a directory (for regular files with multiple hard links this + function returns only one of the parents of the file) */ +static u64 get_parent_inode(struct btrfs_root *root, u64 inr, + struct btrfs_inode_item *inode_item) +{ + struct btrfs_key key; + u64 res; + + if (inr == BTRFS_FIRST_FREE_OBJECTID) { + if (root->objectid != btrfs_info.fs_root.objectid) { + u64 parent; + struct btrfs_root_ref ref; + + parent = btrfs_lookup_root_ref(root->objectid, &ref, + NULL); + if (parent == -1ULL) + return -1ULL; + + if (btrfs_find_root(parent, root, NULL)) + return -1ULL; + + inr = ref.dirid; + } + + if (inode_item) { + key.objectid = inr; + key.type = BTRFS_INODE_ITEM_KEY; + key.offset = 0; + + if (btrfs_lookup_inode(root, &key, inode_item, NULL)) + return -1ULL; + } + + return inr; + } + + res = btrfs_lookup_inode_ref(root, inr, NULL, NULL); + if (res == -1ULL) + return -1ULL; + + if (inode_item) { + key.objectid = res; + key.type = BTRFS_INODE_ITEM_KEY; + key.offset = 0; + + if (btrfs_lookup_inode(root, &key, inode_item, NULL)) + return -1ULL; + } + + return res; +} + +static inline int next_length(const char *path) +{ + int res = 0; + while (*path != '\0' && *path != '/' && res <= BTRFS_NAME_LEN) + ++res, ++path; + return res; +} + +static inline const char *skip_current_directories(const char *cur) +{ + while (1) { + if (cur[0] == '/') + ++cur; + else if (cur[0] == '.' && cur[1] == '/') + cur += 2; + else + break; + } + + return cur; +} + +/* inode.c, musi vratit aj root stromu kde sa inoda najde */ +u64 btrfs_lookup_path(struct btrfs_root *root, u64 inr, const char *path, + u8 *type_p, struct btrfs_inode_item *inode_item_p, + int symlink_limit) +{ + struct btrfs_dir_item item; + struct btrfs_inode_item inode_item; + u8 type = BTRFS_FT_DIR; + int len, have_inode = 0; + const char *cur = path; + + if (*cur == '/') { + ++cur; + inr = root->root_dirid; + } + + do { + cur = skip_current_directories(cur); + + len = next_length(cur); + if (len > BTRFS_NAME_LEN) { + printf("%s: Name too long at \"%.*s\"\n", __func__, + BTRFS_NAME_LEN, cur); + return -1ULL; + } + + if (len == 1 && cur[0] == '.') + break; + + if (len == 2 && cur[0] == '.' && cur[1] == '.') { + cur += 2; + inr = get_parent_inode(root, inr, &inode_item); + if (inr == -1ULL) + return -1ULL; + + type = BTRFS_FT_DIR; + continue; + } + + if (!*cur) + break; + + if (btrfs_lookup_dir_item(root, inr, cur, len, &item)) + return -1ULL; + + type = item.type; + have_inode = 1; + if (btrfs_lookup_inode(root, &item.location, &inode_item, root)) + return -1ULL; + + if (item.type == BTRFS_FT_SYMLINK && symlink_limit >= 0) { + char *target; + + if (!symlink_limit) { + printf("%s: Too much symlinks!\n", __func__); + return -1ULL; + } + + target = malloc(min(inode_item.size + 1, + (u64) btrfs_info.sb.sectorsize)); + if (!target) + return -1ULL; + + if (btrfs_readlink(root, item.location.objectid, + target)) { + free(target); + return -1ULL; + } + + inr = btrfs_lookup_path(root, inr, target, &type, + &inode_item, symlink_limit - 1); + + free(target); + + if (inr == -1ULL) + return -1ULL; + } else if (item.type != BTRFS_FT_DIR && cur[len]) { + printf("%s: \"%.*s\" not a directory\n", __func__, + (int) (cur - path + len), path); + return -1ULL; + } else { + inr = item.location.objectid; + } + + cur += len; + } while (*cur); + + if (type_p) + *type_p = type; + + if (inode_item_p) { + if (!have_inode) { + struct btrfs_key key; + + key.objectid = inr; + key.type = BTRFS_INODE_ITEM_KEY; + key.offset = 0; + + if (btrfs_lookup_inode(root, &key, &inode_item, NULL)) + return -1ULL; + } + + *inode_item_p = inode_item; + } + + return inr; +} + +u64 btrfs_file_read(const struct btrfs_root *root, u64 inr, u64 offset, + u64 size, char *buf) +{ + struct btrfs_path path; + struct btrfs_key key; + struct btrfs_file_extent_item *extent; + int res; + u64 rd, rd_all = -1ULL; + + key.objectid = inr; + key.type = BTRFS_EXTENT_DATA_KEY; + key.offset = offset; + + if (btrfs_search_tree(root, &key, &path)) + return -1ULL; + + if (btrfs_comp_keys(&key, btrfs_path_leaf_key(&path)) < 0) { + if (btrfs_prev_slot(&path)) + goto out; + + if (btrfs_comp_keys_type(&key, btrfs_path_leaf_key(&path))) + goto out; + } + + rd_all = 0; + + do { + if (btrfs_comp_keys_type(&key, btrfs_path_leaf_key(&path))) + break; + + extent = btrfs_path_item_ptr(&path, + struct btrfs_file_extent_item); + + if (extent->type == BTRFS_FILE_EXTENT_INLINE) { + btrfs_file_extent_item_to_cpu_inl(extent); + rd = btrfs_read_extent_inline(&path, extent, offset, + size, buf); + } else { + btrfs_file_extent_item_to_cpu(extent); + rd = btrfs_read_extent_reg(&path, extent, offset, size, + buf); + } + + if (rd == -1ULL) { + printf("%s: Error reading extent\n", __func__); + rd_all = -1; + goto out; + } + + offset = 0; + buf += rd; + rd_all += rd; + size -= rd; + + if (!size) + break; + } while (!(res = btrfs_next_slot(&path))); + + if (res) + return -1ULL; + +out: + btrfs_free_path(&path); + return rd_all; +} diff --git a/fs/btrfs/root.c b/fs/btrfs/root.c new file mode 100644 index 0000000..c405813 --- /dev/null +++ b/fs/btrfs/root.c @@ -0,0 +1,93 @@ +/* + * BTRFS filesystem implementation for U-Boot + * + * 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include "btrfs.h" + +static void read_root_item(struct btrfs_path *p, struct btrfs_root_item *item) +{ + u32 len; + int reset = 0; + + len = btrfs_path_item_size(p); + memcpy(item, btrfs_path_item_ptr(p, struct btrfs_root_item), len); + btrfs_root_item_to_cpu(item); + + if (len < sizeof(*item)) + reset = 1; + if (!reset && item->generation != item->generation_v2) { + if (item->generation_v2 != 0) + printf("%s: generation != generation_v2 in root item", + __func__); + reset = 1; + } + if (reset) { + memset(&item->generation_v2, 0, + sizeof(*item) - offsetof(struct btrfs_root_item, + generation_v2)); + } +} + +int btrfs_find_root(u64 objectid, struct btrfs_root *root, + struct btrfs_root_item *root_item) +{ + struct btrfs_path path; + struct btrfs_root_item my_root_item; + + if (!btrfs_search_tree_key_type(&btrfs_info.tree_root, objectid, + BTRFS_ROOT_ITEM_KEY, &path)) + return -1; + + if (!root_item) + root_item = &my_root_item; + read_root_item(&path, root_item); + + if (root) { + root->objectid = objectid; + root->bytenr = root_item->bytenr; + root->root_dirid = root_item->root_dirid; + } + + btrfs_free_path(&path); + return 0; +} + +u64 btrfs_lookup_root_ref(u64 subvolid, struct btrfs_root_ref *refp, char *name) +{ + struct btrfs_path path; + struct btrfs_key *key; + struct btrfs_root_ref *ref; + u64 res = -1ULL; + + key = btrfs_search_tree_key_type(&btrfs_info.tree_root, subvolid, + BTRFS_ROOT_BACKREF_KEY, &path); + + if (!key) + return -1ULL; + + ref = btrfs_path_item_ptr(&path, struct btrfs_root_ref); + btrfs_root_ref_to_cpu(ref); + + if (refp) + *refp = *ref; + + if (name) { + if (ref->name_len > BTRFS_VOL_NAME_MAX) { + printf("%s: volume name too long: %u\n", __func__, + ref->name_len); + goto out; + } + + memcpy(name, ref + 1, ref->name_len); + } + + res = key->offset; +out: + btrfs_free_path(&path); + return res; +} + diff --git a/fs/btrfs/subvolume.c b/fs/btrfs/subvolume.c new file mode 100644 index 0000000..54e0ab4 --- /dev/null +++ b/fs/btrfs/subvolume.c @@ -0,0 +1,131 @@ +/* + * BTRFS filesystem implementation for U-Boot + * + * 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include "btrfs.h" +#include <malloc.h> + +static int get_subvol_name(u64 subvolid, char *name, int max_len) +{ + struct btrfs_root_ref rref; + struct btrfs_inode_ref iref; + struct btrfs_root root; + u64 dir; + char tmp[max(BTRFS_VOL_NAME_MAX, BTRFS_NAME_MAX)]; + char *ptr; + + ptr = name + max_len - 1; + *ptr = '\0'; + + while (subvolid != BTRFS_FS_TREE_OBJECTID) { + subvolid = btrfs_lookup_root_ref(subvolid, &rref, tmp); + + if (subvolid == -1ULL) + return -1; + + ptr -= rref.name_len + 1; + if (ptr < name) + goto too_long; + + memcpy(ptr + 1, tmp, rref.name_len); + *ptr = '/'; + + if (btrfs_find_root(subvolid, &root, NULL)) + return -1; + + dir = rref.dirid; + + while (dir != BTRFS_FIRST_FREE_OBJECTID) { + dir = btrfs_lookup_inode_ref(&root, dir, &iref, tmp); + + if (dir == -1ULL) + return -1; + + ptr -= iref.name_len + 1; + if (ptr < name) + goto too_long; + + memcpy(ptr + 1, tmp, iref.name_len); + *ptr = '/'; + } + } + + if (ptr == name + max_len - 1) { + name[0] = '/'; + name[1] = '\0'; + } else { + memmove(name, ptr, name + max_len - ptr); + } + + return 0; + +too_long: + printf("%s: subvolume name too long\n", __func__); + return -1; +} + +u64 btrfs_get_default_subvol_objectid(void) +{ + struct btrfs_dir_item item; + + if (btrfs_lookup_dir_item(&btrfs_info.tree_root, + btrfs_info.sb.root_dir_objectid, "default", 7, + &item)) + return BTRFS_FS_TREE_OBJECTID; + return item.location.objectid; +} + +static void list_subvols(u64 tree, char *nameptr, int max_name_len, int level) +{ + struct btrfs_key key, *found_key; + struct btrfs_path path; + struct btrfs_root_ref *ref; + int res; + + key.objectid = tree; + key.type = BTRFS_ROOT_REF_KEY; + key.offset = 0; + + if (btrfs_search_tree(&btrfs_info.tree_root, &key, &path)) + return; + + do { + found_key = btrfs_path_leaf_key(&path); + if (btrfs_comp_keys_type(&key, found_key)) + break; + + ref = btrfs_path_item_ptr(&path, struct btrfs_root_ref); + btrfs_root_ref_to_cpu(ref); + + printf("ID %llu parent %llu name ", found_key->offset, tree); + if (nameptr && !get_subvol_name(found_key->offset, nameptr, + max_name_len)) + printf("%s\n", nameptr); + else + printf("%.*s\n", (int) ref->name_len, + (const char *) (ref + 1)); + + if (level > 0) + list_subvols(found_key->offset, nameptr, max_name_len, + level - 1); + else + printf("%s: Too much recursion, maybe skipping some " + "subvolumes\n", __func__); + } while (!(res = btrfs_next_slot(&path))); + + btrfs_free_path(&path); +} + +void btrfs_list_subvols(void) +{ + char *nameptr = malloc(4096); + + list_subvols(BTRFS_FS_TREE_OBJECTID, nameptr, nameptr ? 4096 : 0, 40); + + if (nameptr) + free(nameptr); +} diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c new file mode 100644 index 0000000..2529c2b --- /dev/null +++ b/fs/btrfs/super.c @@ -0,0 +1,233 @@ +/* + * BTRFS filesystem implementation for U-Boot + * + * 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include "btrfs.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 + +static int btrfs_newest_root_backup(struct btrfs_super_block *sb) +{ + struct btrfs_root_backup *root_backup; + int i, newest = -1; + + for (i = 0; i < BTRFS_NUM_BACKUP_ROOTS; ++i) { + root_backup = sb->super_roots + i; + if (root_backup->tree_root_gen == sb->generation) + newest = i; + } + + return newest; +} + +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) { + u32 crc = ~(u32) 0; + const int csum_size = sizeof(crc); + char result[csum_size]; + + crc = btrfs_csum_data(raw_disk_sb + BTRFS_CSUM_SIZE, crc, + BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE); + btrfs_csum_final(crc, result); + + if (memcmp(raw_disk_sb, result, csum_size)) + return -1; + } else { + return -1; + } + + 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 < %lu\n", __func__, + sb->sys_chunk_array_size, (u32) 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 + }; + char raw_sb[BTRFS_SUPER_INFO_SIZE]; + struct btrfs_super_block *sb = (struct btrfs_super_block *) raw_sb; + u64 dev_total_bytes; + int i, root_backup_idx; + + 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)) { + printf("%s: invalid checksum at superblock mirror %i\n", + __func__, i); + continue; + } + + btrfs_super_block_to_cpu(sb); + + if (sb->magic != BTRFS_MAGIC) { + printf("%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) { + printf("%s: No valid BTRFS superblock found!\n", __func__); + return -1; + } + + root_backup_idx = btrfs_newest_root_backup(&btrfs_info.sb); + if (root_backup_idx < 0) { + printf("%s: No valid root_backup found!\n", __func__); + return -1; + } + btrfs_info.root_backup = btrfs_info.sb.super_roots + root_backup_idx; + + if (btrfs_info.root_backup->num_devices != 1) { + printf("%s: Unsupported number of devices (%lli). This driver " + "only supports filesystem on one device.\n", __func__, + btrfs_info.root_backup->num_devices); + return -1; + } + + debug("Chosen superblock with generation = %llu\n", + btrfs_info.sb.generation); + + return 0; +} diff --git a/fs/ext4/dev.c b/fs/ext4/dev.c index ae2ba6a..f04fa08 100644 --- a/fs/ext4/dev.c +++ b/fs/ext4/dev.c @@ -26,7 +26,7 @@ #include <common.h> #include <blk.h> #include <config.h> -#include <memalign.h> +#include <fs_internal.h> #include <ext4fs.h> #include <ext_common.h> #include "ext4_common.h" @@ -47,85 +47,11 @@ void ext4fs_set_blk_dev(struct blk_desc *rbdd, disk_partition_t *info) get_fs()->dev_desc->log2blksz; } -int ext4fs_devread(lbaint_t sector, int byte_offset, int byte_len, char *buf) +int ext4fs_devread(lbaint_t sector, int byte_offset, int byte_len, + char *buffer) { - unsigned block_len; - int log2blksz = ext4fs_blk_desc->log2blksz; - ALLOC_CACHE_ALIGN_BUFFER(char, sec_buf, (ext4fs_blk_desc ? - ext4fs_blk_desc->blksz : - 0)); - if (ext4fs_blk_desc == NULL) { - printf("** Invalid Block Device Descriptor (NULL)\n"); - return 0; - } - - /* Check partition boundaries */ - if ((sector + ((byte_offset + byte_len - 1) >> log2blksz)) - >= part_info->size) { - printf("%s read outside partition " LBAFU "\n", __func__, - sector); - return 0; - } - - /* Get the read to the beginning of a partition */ - sector += byte_offset >> log2blksz; - byte_offset &= ext4fs_blk_desc->blksz - 1; - - debug(" <" LBAFU ", %d, %d>\n", sector, byte_offset, byte_len); - - if (byte_offset != 0) { - int readlen; - /* read first part which isn't aligned with start of sector */ - if (blk_dread(ext4fs_blk_desc, part_info->start + sector, 1, - (void *)sec_buf) != 1) { - printf(" ** ext2fs_devread() read error **\n"); - return 0; - } - readlen = min((int)ext4fs_blk_desc->blksz - byte_offset, - byte_len); - memcpy(buf, sec_buf + byte_offset, readlen); - buf += readlen; - byte_len -= readlen; - sector++; - } - - if (byte_len == 0) - return 1; - - /* read sector aligned part */ - block_len = byte_len & ~(ext4fs_blk_desc->blksz - 1); - - if (block_len == 0) { - ALLOC_CACHE_ALIGN_BUFFER(u8, p, ext4fs_blk_desc->blksz); - - block_len = ext4fs_blk_desc->blksz; - blk_dread(ext4fs_blk_desc, part_info->start + sector, 1, - (void *)p); - memcpy(buf, p, byte_len); - return 1; - } - - if (blk_dread(ext4fs_blk_desc, part_info->start + sector, - block_len >> log2blksz, (void *)buf) != - block_len >> log2blksz) { - printf(" ** %s read error - block\n", __func__); - return 0; - } - block_len = byte_len & ~(ext4fs_blk_desc->blksz - 1); - buf += block_len; - byte_len -= block_len; - sector += block_len / ext4fs_blk_desc->blksz; - - if (byte_len != 0) { - /* read rest of data which are not in whole sector */ - if (blk_dread(ext4fs_blk_desc, part_info->start + sector, 1, - (void *)sec_buf) != 1) { - printf("* %s read error - last part\n", __func__); - return 0; - } - memcpy(buf, sec_buf, byte_len); - } - return 1; + return fs_devread(get_fs()->dev_desc, part_info, sector, byte_offset, + byte_len, buffer); } int ext4_read_superblock(char *buffer) diff --git a/fs/ext4/ext4_common.c b/fs/ext4/ext4_common.c index 621c61e..31952f4 100644 --- a/fs/ext4/ext4_common.c +++ b/fs/ext4/ext4_common.c @@ -432,6 +432,10 @@ uint16_t ext4fs_checksum_update(uint32_t i) crc = ext2fs_crc16(crc, desc, offset); offset += sizeof(desc->bg_checksum); /* skip checksum */ assert(offset == sizeof(*desc)); + if (offset < fs->gdsize) { + crc = ext2fs_crc16(crc, (__u8 *)desc + offset, + fs->gdsize - offset); + } } return crc; diff --git a/fs/ext4/ext4fs.c b/fs/ext4/ext4fs.c index 081509d..b0c7303 100644 --- a/fs/ext4/ext4fs.c +++ b/fs/ext4/ext4fs.c @@ -167,6 +167,7 @@ int ext4fs_ls(const char *dirname) FILETYPE_DIRECTORY); if (status != 1) { printf("** Can not find directory. **\n"); + ext4fs_free_node(dirnode, &ext4fs_root->diropen); return 1; } diff --git a/fs/fat/fat.c b/fs/fat/fat.c index 36a309c..7fe7843 100644 --- a/fs/fat/fat.c +++ b/fs/fat/fat.c @@ -495,7 +495,7 @@ read_bootsectandvi(boot_sector *bs, volume_info *volinfo, int *fatsize) return -1; } - block = memalign(ARCH_DMA_MINALIGN, cur_dev->blksz); + block = malloc_cache_aligned(cur_dev->blksz); if (block == NULL) { debug("Error: allocating block\n"); return -1; @@ -599,7 +599,7 @@ static int get_fs_info(fsdata *mydata) mydata->fatbufnum = -1; mydata->fat_dirty = 0; - mydata->fatbuf = memalign(ARCH_DMA_MINALIGN, FATBUFSIZE); + mydata->fatbuf = malloc_cache_aligned(FATBUFSIZE); if (mydata->fatbuf == NULL) { debug("Error: allocating memory\n"); return -1; @@ -710,13 +710,14 @@ static void fat_itr_child(fat_itr *itr, fat_itr *parent) itr->fsdata = parent->fsdata; if (clustnum > 0) { itr->clust = clustnum; + itr->is_root = 0; } else { itr->clust = parent->fsdata->root_cluster; + itr->is_root = 1; } itr->dent = NULL; itr->remaining = 0; itr->last_cluster = 0; - itr->is_root = 0; } static void *next_cluster(fat_itr *itr) @@ -1037,13 +1038,16 @@ int fat_exists(const char *filename) fat_itr *itr; int ret; - itr = malloc(sizeof(fat_itr)); + itr = malloc_cache_aligned(sizeof(fat_itr)); + if (!itr) + return 0; ret = fat_itr_root(itr, &fsdata); if (ret) - return 0; + goto out; ret = fat_itr_resolve(itr, filename, TYPE_ANY); free(fsdata.fatbuf); +out: free(itr); return ret == 0; } @@ -1054,10 +1058,12 @@ int fat_size(const char *filename, loff_t *size) fat_itr *itr; int ret; - itr = malloc(sizeof(fat_itr)); + itr = malloc_cache_aligned(sizeof(fat_itr)); + if (!itr) + return -ENOMEM; ret = fat_itr_root(itr, &fsdata); if (ret) - return ret; + goto out_free_itr; ret = fat_itr_resolve(itr, filename, TYPE_FILE); if (ret) { @@ -1071,12 +1077,13 @@ int fat_size(const char *filename, loff_t *size) *size = 0; ret = 0; } - goto out; + goto out_free_both; } *size = FAT2CPU32(itr->dent->size); +out_free_both: free(fsdata.fatbuf); -out: +out_free_itr: free(itr); return ret; } @@ -1088,20 +1095,23 @@ int file_fat_read_at(const char *filename, loff_t pos, void *buffer, fat_itr *itr; int ret; - itr = malloc(sizeof(fat_itr)); + itr = malloc_cache_aligned(sizeof(fat_itr)); + if (!itr) + return -ENOMEM; ret = fat_itr_root(itr, &fsdata); if (ret) - return ret; + goto out_free_itr; ret = fat_itr_resolve(itr, filename, TYPE_FILE); if (ret) - goto out; + goto out_free_both; printf("reading %s\n", filename); ret = get_contents(&fsdata, itr->dent, pos, buffer, maxsize, actread); -out: +out_free_both: free(fsdata.fatbuf); +out_free_itr: free(itr); return ret; } @@ -1147,17 +1157,18 @@ int fat_opendir(const char *filename, struct fs_dir_stream **dirsp) ret = fat_itr_root(&dir->itr, &dir->fsdata); if (ret) - goto fail; + goto fail_free_dir; ret = fat_itr_resolve(&dir->itr, filename, TYPE_DIR); if (ret) - goto fail; + goto fail_free_both; *dirsp = (struct fs_dir_stream *)dir; return 0; -fail: +fail_free_both: free(dir->fsdata.fatbuf); +fail_free_dir: free(dir); return ret; } @@ -14,6 +14,7 @@ #include <fs.h> #include <sandboxfs.h> #include <ubifs_uboot.h> +#include <btrfs.h> #include <asm/io.h> #include <div64.h> #include <linux/math64.h> @@ -219,6 +220,21 @@ static struct fstype_info fstypes[] = { .opendir = fs_opendir_unsupported, }, #endif +#ifdef CONFIG_FS_BTRFS + { + .fstype = FS_TYPE_BTRFS, + .name = "btrfs", + .null_dev_desc_ok = false, + .probe = btrfs_probe, + .close = btrfs_close, + .ls = btrfs_ls, + .exists = btrfs_exists, + .size = btrfs_size, + .read = btrfs_read, + .write = fs_write_unsupported, + .uuid = btrfs_uuid, + }, +#endif { .fstype = FS_TYPE_ANY, .name = "unsupported", diff --git a/fs/fs_internal.c b/fs/fs_internal.c new file mode 100644 index 0000000..58b4410 --- /dev/null +++ b/fs/fs_internal.c @@ -0,0 +1,92 @@ +/* + * 2017 by Marek Behun <marek.behun@nic.cz> + * + * Derived from code in ext4/dev.c, which was based on reiserfs/dev.c + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <compiler.h> +#include <part.h> +#include <memalign.h> + +int fs_devread(struct blk_desc *blk, disk_partition_t *partition, + lbaint_t sector, int byte_offset, int byte_len, char *buf) +{ + unsigned block_len; + int log2blksz = blk->log2blksz; + ALLOC_CACHE_ALIGN_BUFFER(char, sec_buf, (blk ? blk->blksz : 0)); + if (blk == NULL) { + printf("** Invalid Block Device Descriptor (NULL)\n"); + return 0; + } + + /* Check partition boundaries */ + if ((sector + ((byte_offset + byte_len - 1) >> log2blksz)) + >= partition->size) { + printf("%s read outside partition " LBAFU "\n", __func__, + sector); + return 0; + } + + /* Get the read to the beginning of a partition */ + sector += byte_offset >> log2blksz; + byte_offset &= blk->blksz - 1; + + debug(" <" LBAFU ", %d, %d>\n", sector, byte_offset, byte_len); + + if (byte_offset != 0) { + int readlen; + /* read first part which isn't aligned with start of sector */ + if (blk_dread(blk, partition->start + sector, 1, + (void *)sec_buf) != 1) { + printf(" ** %s read error **\n", __func__); + return 0; + } + readlen = min((int)blk->blksz - byte_offset, + byte_len); + memcpy(buf, sec_buf + byte_offset, readlen); + buf += readlen; + byte_len -= readlen; + sector++; + } + + if (byte_len == 0) + return 1; + + /* read sector aligned part */ + block_len = byte_len & ~(blk->blksz - 1); + + if (block_len == 0) { + ALLOC_CACHE_ALIGN_BUFFER(u8, p, blk->blksz); + + block_len = blk->blksz; + blk_dread(blk, partition->start + sector, 1, + (void *)p); + memcpy(buf, p, byte_len); + return 1; + } + + if (blk_dread(blk, partition->start + sector, + block_len >> log2blksz, (void *)buf) != + block_len >> log2blksz) { + printf(" ** %s read error - block\n", __func__); + return 0; + } + block_len = byte_len & ~(blk->blksz - 1); + buf += block_len; + byte_len -= block_len; + sector += block_len / blk->blksz; + + if (byte_len != 0) { + /* read rest of data which are not in whole sector */ + if (blk_dread(blk, partition->start + sector, 1, + (void *)sec_buf) != 1) { + printf("* %s read error - last part\n", __func__); + return 0; + } + memcpy(buf, sec_buf, byte_len); + } + return 1; +} diff --git a/fs/jffs2/jffs2_nand_1pass.c b/fs/jffs2/jffs2_nand_1pass.c index 1d63fc9..b16005e 100644 --- a/fs/jffs2/jffs2_nand_1pass.c +++ b/fs/jffs2/jffs2_nand_1pass.c @@ -798,7 +798,7 @@ jffs2_1pass_build_lists(struct part_info * part) struct mtdids *id = part->dev->id; mtd = get_nand_dev_by_index(id->num); if (!mtd) { - error("\nno NAND devices available\n"); + pr_err("\nno NAND devices available\n"); return 0; } diff --git a/fs/reiserfs/dev.c b/fs/reiserfs/dev.c index 5a1ab0a..7b786e4 100644 --- a/fs/reiserfs/dev.c +++ b/fs/reiserfs/dev.c @@ -9,7 +9,7 @@ #include <common.h> #include <config.h> #include <reiserfs.h> - +#include <fs_internal.h> #include "reiserfs_private.h" static struct blk_desc *reiserfs_blk_desc; @@ -22,78 +22,8 @@ void reiserfs_set_blk_dev(struct blk_desc *rbdd, disk_partition_t *info) part_info = info; } - -int reiserfs_devread (int sector, int byte_offset, int byte_len, char *buf) +int reiserfs_devread(int sector, int byte_offset, int byte_len, char *buf) { - char sec_buf[SECTOR_SIZE]; - unsigned block_len; -/* - unsigned len = byte_len; - u8 *start = buf; -*/ - /* - * Check partition boundaries - */ - if (sector < 0 - || ((sector + ((byte_offset + byte_len - 1) >> SECTOR_BITS)) - >= part_info->size)) { -/* errnum = ERR_OUTSIDE_PART; */ - printf (" ** reiserfs_devread() read outside partition\n"); - return 0; - } - - /* - * Get the read to the beginning of a partition. - */ - sector += byte_offset >> SECTOR_BITS; - byte_offset &= SECTOR_SIZE - 1; - -#if defined(DEBUG) - printf (" <%d, %d, %d> ", sector, byte_offset, byte_len); -#endif - - - if (reiserfs_blk_desc == NULL) - return 0; - - - if (byte_offset != 0) { - /* read first part which isn't aligned with start of sector */ - if (reiserfs_blk_desc->block_read(reiserfs_blk_desc, - part_info->start + sector, - 1, (void *)sec_buf) != 1) { - printf (" ** reiserfs_devread() read error\n"); - return 0; - } - memcpy(buf, sec_buf+byte_offset, min(SECTOR_SIZE-byte_offset, byte_len)); - buf+=min(SECTOR_SIZE-byte_offset, byte_len); - byte_len-=min(SECTOR_SIZE-byte_offset, byte_len); - sector++; - } - - /* read sector aligned part */ - block_len = byte_len & ~(SECTOR_SIZE-1); - if (reiserfs_blk_desc->block_read(reiserfs_blk_desc, - part_info->start + sector, - block_len / SECTOR_SIZE, (void *)buf) - != block_len/SECTOR_SIZE) { - printf (" ** reiserfs_devread() read error - block\n"); - return 0; - } - buf+=block_len; - byte_len-=block_len; - sector+= block_len/SECTOR_SIZE; - - if ( byte_len != 0 ) { - /* read rest of data which are not in whole sector */ - if (reiserfs_blk_desc->block_read(reiserfs_blk_desc, - part_info->start + sector, - 1, (void *)sec_buf) != 1) { - printf (" ** reiserfs_devread() read error - last part\n"); - return 0; - } - memcpy(buf, sec_buf, byte_len); - } - - return 1; + return fs_devread(reiserfs_blk_desc, part_info, sector, byte_offset, + byte_len, buf); } diff --git a/fs/yaffs2/yaffs_uboot_glue.c b/fs/yaffs2/yaffs_uboot_glue.c index bd66d31..2a70e4a 100644 --- a/fs/yaffs2/yaffs_uboot_glue.c +++ b/fs/yaffs2/yaffs_uboot_glue.c @@ -168,7 +168,7 @@ void cmd_yaffs_devconfig(char *_mp, int flash_dev, mtd = get_nand_dev_by_index(flash_dev); if (!mtd) { - error("\nno NAND devices available\n"); + pr_err("\nno NAND devices available\n"); return; } diff --git a/fs/zfs/dev.c b/fs/zfs/dev.c index 2f409e6..7dda42b 100644 --- a/fs/zfs/dev.c +++ b/fs/zfs/dev.c @@ -11,6 +11,7 @@ #include <common.h> #include <config.h> +#include <fs_internal.h> #include <zfs_common.h> static struct blk_desc *zfs_blk_desc; @@ -25,87 +26,6 @@ void zfs_set_blk_dev(struct blk_desc *rbdd, disk_partition_t *info) /* err */ int zfs_devread(int sector, int byte_offset, int byte_len, char *buf) { - short sec_buffer[SECTOR_SIZE/sizeof(short)]; - char *sec_buf = (char *)sec_buffer; - unsigned block_len; - - /* - * Check partition boundaries - */ - if ((sector < 0) || - ((sector + ((byte_offset + byte_len - 1) >> SECTOR_BITS)) >= - part_info->size)) { - /* errnum = ERR_OUTSIDE_PART; */ - printf(" ** zfs_devread() read outside partition sector %d\n", sector); - return 1; - } - - /* - * Get the read to the beginning of a partition. - */ - sector += byte_offset >> SECTOR_BITS; - byte_offset &= SECTOR_SIZE - 1; - - debug(" <%d, %d, %d>\n", sector, byte_offset, byte_len); - - if (zfs_blk_desc == NULL) { - printf("** Invalid Block Device Descriptor (NULL)\n"); - return 1; - } - - if (byte_offset != 0) { - /* read first part which isn't aligned with start of sector */ - if (zfs_blk_desc->block_read(zfs_blk_desc, - part_info->start + sector, 1, - (void *)sec_buf) != 1) { - printf(" ** zfs_devread() read error **\n"); - return 1; - } - memcpy(buf, sec_buf + byte_offset, - min(SECTOR_SIZE - byte_offset, byte_len)); - buf += min(SECTOR_SIZE - byte_offset, byte_len); - byte_len -= min(SECTOR_SIZE - byte_offset, byte_len); - sector++; - } - - if (byte_len == 0) - return 0; - - /* read sector aligned part */ - block_len = byte_len & ~(SECTOR_SIZE - 1); - - if (block_len == 0) { - u8 p[SECTOR_SIZE]; - - block_len = SECTOR_SIZE; - zfs_blk_desc->block_read(zfs_blk_desc, - part_info->start + sector, - 1, (void *)p); - memcpy(buf, p, byte_len); - return 0; - } - - if (zfs_blk_desc->block_read(zfs_blk_desc, part_info->start + sector, - block_len / SECTOR_SIZE, - (void *)buf) != block_len / SECTOR_SIZE) { - printf(" ** zfs_devread() read error - block\n"); - return 1; - } - - block_len = byte_len & ~(SECTOR_SIZE - 1); - buf += block_len; - byte_len -= block_len; - sector += block_len / SECTOR_SIZE; - - if (byte_len != 0) { - /* read rest of data which are not in whole sector */ - if (zfs_blk_desc->block_read(zfs_blk_desc, - part_info->start + sector, - 1, (void *)sec_buf) != 1) { - printf(" ** zfs_devread() read error - last part\n"); - return 1; - } - memcpy(buf, sec_buf, byte_len); - } - return 0; + return fs_devread(zfs_blk_desc, part_info, sector, byte_offset, + byte_len, buf); } |