From 0c936ee3194ac5b533a18fcd08e5d285853af5fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Beh=C3=BAn?= Date: Sun, 3 Sep 2017 17:00:29 +0200 Subject: [PATCH] fs: btrfs: Add U-Boot fs handlers. Signed-off-by: Marek Behun create mode 100644 fs/btrfs/Kconfig create mode 100644 fs/btrfs/Makefile create mode 100644 fs/btrfs/btrfs.c create mode 100644 include/btrfs.h --- fs/Kconfig | 2 + fs/Makefile | 1 + fs/btrfs/Kconfig | 9 +++ fs/btrfs/Makefile | 8 ++ fs/btrfs/btrfs.c | 227 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/fs.c | 16 ++++ include/btrfs.h | 21 +++++ include/fs.h | 1 + 8 files changed, 285 insertions(+) create mode 100644 fs/btrfs/Kconfig create mode 100644 fs/btrfs/Makefile create mode 100644 fs/btrfs/btrfs.c create mode 100644 include/btrfs.h diff --git a/fs/Kconfig b/fs/Kconfig index e6803ac..1cb9831 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -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 b53c9d7..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/ 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 +#include +#include + +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/fs.c b/fs/fs.c index 3481229..84349f3 100644 --- a/fs/fs.c +++ b/fs/fs.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -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/include/btrfs.h b/include/btrfs.h new file mode 100644 index 0000000..7390975 --- /dev/null +++ b/include/btrfs.h @@ -0,0 +1,21 @@ +/* + * BTRFS filesystem implementation for U-Boot + * + * 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __U_BOOT_BTRFS_H__ +#define __U_BOOT_BTRFS_H__ + +int btrfs_probe(struct blk_desc *, disk_partition_t *); +int btrfs_ls(const char *); +int btrfs_exists(const char *); +int btrfs_size(const char *, loff_t *); +int btrfs_read(const char *, void *, loff_t, loff_t, loff_t *); +void btrfs_close(void); +int btrfs_uuid(char *); +void btrfs_list_subvols(void); + +#endif /* __U_BOOT_BTRFS_H__ */ diff --git a/include/fs.h b/include/fs.h index 0869ad6..32fc480 100644 --- a/include/fs.h +++ b/include/fs.h @@ -13,6 +13,7 @@ #define FS_TYPE_EXT 2 #define FS_TYPE_SANDBOX 3 #define FS_TYPE_UBIFS 4 +#define FS_TYPE_BTRFS 5 /* * Tell the fs layer which block device an partition to use for future