// SPDX-License-Identifier: GPL-2.0+ /* * BTRFS filesystem implementation for U-Boot * * 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz */ #include #include #include #include #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] = "FILE", [BTRFS_FT_DIR] = "DIR", [BTRFS_FT_CHRDEV] = "CHRDEV", [BTRFS_FT_BLKDEV] = "BLKDEV", [BTRFS_FT_FIFO] = "FIFO", [BTRFS_FT_SOCK] = "SOCK", [BTRFS_FT_SYMLINK] = "SYMLINK", }; 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("DIR_ITEM.%u", 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; }