diff options
Diffstat (limited to 'roms/u-boot/fs/btrfs/btrfs.c')
-rw-r--r-- | roms/u-boot/fs/btrfs/btrfs.c | 288 |
1 files changed, 288 insertions, 0 deletions
diff --git a/roms/u-boot/fs/btrfs/btrfs.c b/roms/u-boot/fs/btrfs/btrfs.c new file mode 100644 index 000000000..52a243a65 --- /dev/null +++ b/roms/u-boot/fs/btrfs/btrfs.c @@ -0,0 +1,288 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * BTRFS filesystem implementation for U-Boot + * + * 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz + */ + +#include <config.h> +#include <malloc.h> +#include <uuid.h> +#include <linux/time.h> +#include "btrfs.h" +#include "crypto/hash.h" +#include "disk-io.h" + +struct btrfs_fs_info *current_fs_info; + +static int show_dir(struct btrfs_root *root, struct extent_buffer *eb, + struct btrfs_dir_item *di) +{ + struct btrfs_fs_info *fs_info = root->fs_info; + struct btrfs_inode_item ii; + struct btrfs_key key; + static const char* dir_item_str[] = { + [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", + }; + u8 type = btrfs_dir_type(eb, di); + char namebuf[BTRFS_NAME_LEN]; + char *target = NULL; + char filetime[32]; + time_t mtime; + int ret = 0; + + /* skip XATTRs in directory listing */ + if (type == BTRFS_FT_XATTR) + return 0; + + btrfs_dir_item_key_to_cpu(eb, di, &key); + + if (key.type == BTRFS_ROOT_ITEM_KEY) { + struct btrfs_root *subvol; + + /* It's a subvolume, get its mtime from root item */ + subvol = btrfs_read_fs_root(fs_info, &key); + if (IS_ERR(subvol)) { + ret = PTR_ERR(subvol); + error("Can't find root %llu", key.objectid); + return ret; + } + mtime = btrfs_stack_timespec_sec(&subvol->root_item.otime); + } else { + struct btrfs_path path; + + /* It's regular inode, get its mtime from inode item */ + btrfs_init_path(&path); + ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); + if (ret > 0) + ret = -ENOENT; + if (ret < 0) { + error("Can't find inode %llu", key.objectid); + btrfs_release_path(&path); + return ret; + } + read_extent_buffer(path.nodes[0], &ii, + btrfs_item_ptr_offset(path.nodes[0], path.slots[0]), + sizeof(ii)); + btrfs_release_path(&path); + mtime = btrfs_stack_timespec_sec(&ii.mtime); + } + ctime_r(&mtime, filetime); + + if (type == BTRFS_FT_SYMLINK) { + target = malloc(fs_info->sectorsize); + if (!target) { + error("Can't alloc memory for symlink %llu", + key.objectid); + return -ENOMEM; + } + ret = btrfs_readlink(root, key.objectid, target); + if (ret < 0) { + error("Failed to read symlink %llu", key.objectid); + goto out; + } + target[ret] = '\0'; + } + + if (type < ARRAY_SIZE(dir_item_str) && dir_item_str[type]) + printf("<%s> ", dir_item_str[type]); + else + printf("?%3u? ", type); + if (type == BTRFS_FT_CHRDEV || type == BTRFS_FT_BLKDEV) { + ASSERT(key.type == BTRFS_INODE_ITEM_KEY); + printf("%4llu,%5llu ", btrfs_stack_inode_rdev(&ii) >> 20, + btrfs_stack_inode_rdev(&ii) & 0xfffff); + } else { + if (key.type == BTRFS_INODE_ITEM_KEY) + printf("%10llu ", btrfs_stack_inode_size(&ii)); + else + printf("%10llu ", 0ULL); + } + + read_extent_buffer(eb, namebuf, (unsigned long)(di + 1), + btrfs_dir_name_len(eb, di)); + printf("%24.24s %.*s", filetime, btrfs_dir_name_len(eb, di), namebuf); + if (type == BTRFS_FT_SYMLINK) + printf(" -> %s", target ? target : "?"); + printf("\n"); +out: + free(target); + return ret; +} + +int btrfs_probe(struct blk_desc *fs_dev_desc, + struct disk_partition *fs_partition) +{ + struct btrfs_fs_info *fs_info; + int ret = -1; + + btrfs_hash_init(); + fs_info = open_ctree_fs_info(fs_dev_desc, fs_partition); + if (fs_info) { + current_fs_info = fs_info; + ret = 0; + } + return ret; +} + +int btrfs_ls(const char *path) +{ + struct btrfs_fs_info *fs_info = current_fs_info; + struct btrfs_root *root = fs_info->fs_root; + u64 ino = BTRFS_FIRST_FREE_OBJECTID; + u8 type; + int ret; + + ASSERT(fs_info); + ret = btrfs_lookup_path(fs_info->fs_root, BTRFS_FIRST_FREE_OBJECTID, + path, &root, &ino, &type, 40); + if (ret < 0) { + printf("Cannot lookup path %s\n", path); + return ret; + } + + if (type != BTRFS_FT_DIR) { + error("Not a directory: %s", path); + return -ENOENT; + } + ret = btrfs_iter_dir(root, ino, show_dir); + if (ret < 0) { + error("An error occurred while listing directory %s", path); + return ret; + } + return 0; +} + +int btrfs_exists(const char *file) +{ + struct btrfs_fs_info *fs_info = current_fs_info; + struct btrfs_root *root; + u64 ino; + u8 type; + int ret; + + ASSERT(fs_info); + + ret = btrfs_lookup_path(fs_info->fs_root, BTRFS_FIRST_FREE_OBJECTID, + file, &root, &ino, &type, 40); + if (ret < 0) + return 0; + + if (type == BTRFS_FT_REG_FILE) + return 1; + return 0; +} + +int btrfs_size(const char *file, loff_t *size) +{ + struct btrfs_fs_info *fs_info = current_fs_info; + struct btrfs_inode_item *ii; + struct btrfs_root *root; + struct btrfs_path path; + struct btrfs_key key; + u64 ino; + u8 type; + int ret; + + ret = btrfs_lookup_path(fs_info->fs_root, BTRFS_FIRST_FREE_OBJECTID, + file, &root, &ino, &type, 40); + if (ret < 0) { + printf("Cannot lookup file %s\n", file); + return ret; + } + if (type != BTRFS_FT_REG_FILE) { + printf("Not a regular file: %s\n", file); + return -ENOENT; + } + btrfs_init_path(&path); + key.objectid = ino; + key.type = BTRFS_INODE_ITEM_KEY; + key.offset = 0; + + ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); + if (ret < 0) { + printf("Cannot lookup ino %llu\n", ino); + return ret; + } + if (ret > 0) { + printf("Ino %llu does not exist\n", ino); + ret = -ENOENT; + goto out; + } + ii = btrfs_item_ptr(path.nodes[0], path.slots[0], + struct btrfs_inode_item); + *size = btrfs_inode_size(path.nodes[0], ii); +out: + btrfs_release_path(&path); + return ret; +} + +int btrfs_read(const char *file, void *buf, loff_t offset, loff_t len, + loff_t *actread) +{ + struct btrfs_fs_info *fs_info = current_fs_info; + struct btrfs_root *root; + loff_t real_size = 0; + u64 ino; + u8 type; + int ret; + + ASSERT(fs_info); + ret = btrfs_lookup_path(fs_info->fs_root, BTRFS_FIRST_FREE_OBJECTID, + file, &root, &ino, &type, 40); + if (ret < 0) { + error("Cannot lookup file %s", file); + return ret; + } + + if (type != BTRFS_FT_REG_FILE) { + error("Not a regular file: %s", file); + return -EINVAL; + } + + if (!len) { + ret = btrfs_size(file, &real_size); + if (ret < 0) { + error("Failed to get inode size: %s", file); + return ret; + } + len = real_size; + } + + if (len > real_size - offset) + len = real_size - offset; + + ret = btrfs_file_read(root, ino, offset, len, buf); + if (ret < 0) { + error("An error occurred while reading file %s", file); + return ret; + } + + *actread = len; + return 0; +} + +void btrfs_close(void) +{ + if (current_fs_info) { + close_ctree_fs_info(current_fs_info); + current_fs_info = NULL; + } +} + +int btrfs_uuid(char *uuid_str) +{ +#ifdef CONFIG_LIB_UUID + if (current_fs_info) + uuid_bin_to_str(current_fs_info->super_copy->fsid, uuid_str, + UUID_STR_FORMAT_STD); + return 0; +#endif + return -ENOSYS; +} |