| // 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_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, (struct btrfs_key *)&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, |
| struct disk_partition *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; |
| } |