diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/Kconfig | 2 | ||||
-rw-r--r-- | fs/Makefile | 1 | ||||
-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/fs.c | 16 |
6 files changed, 263 insertions, 0 deletions
diff --git a/fs/Kconfig b/fs/Kconfig index e6803ac8cb2..1cb9831be8a 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 b53c9d7cca7..8a8175b7af3 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 00000000000..22909d9fccc --- /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 00000000000..01731557e69 --- /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 00000000000..4140e2bc206 --- /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(); +*/ @@ -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", |