// SPDX-License-Identifier: GPL-2.0+ /* * BTRFS filesystem implementation for U-Boot * * 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz */ #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; }