diff options
Diffstat (limited to 'fs/btrfs/inode.c')
-rw-r--r-- | fs/btrfs/inode.c | 385 |
1 files changed, 385 insertions, 0 deletions
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c new file mode 100644 index 0000000000..0d3da28296 --- /dev/null +++ b/fs/btrfs/inode.c @@ -0,0 +1,385 @@ +/* + * 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 <malloc.h> + +u64 btrfs_lookup_inode_ref(struct btrfs_root *root, u64 inr, + struct btrfs_inode_ref *refp, char *name) +{ + struct btrfs_path path; + struct btrfs_key *key; + struct btrfs_inode_ref *ref; + u64 res = -1ULL; + + key = btrfs_search_tree_key_type(root, inr, BTRFS_INODE_REF_KEY, + &path); + + if (!key) + return -1ULL; + + ref = btrfs_path_item_ptr(&path, struct btrfs_inode_ref); + btrfs_inode_ref_to_cpu(ref); + + if (refp) + *refp = *ref; + + if (name) { + if (ref->name_len > BTRFS_NAME_MAX) { + printf("%s: inode name too long: %u\n", __func__, + ref->name_len); + goto out; + } + + memcpy(name, ref + 1, ref->name_len); + } + + res = key->offset; +out: + btrfs_free_path(&path); + return res; +} + +int btrfs_lookup_inode(const struct btrfs_root *root, + struct btrfs_key *location, + struct btrfs_inode_item *item, + struct btrfs_root *new_root) +{ + struct btrfs_root tmp_root = *root; + struct btrfs_path path; + int res = -1; + + if (location->type == BTRFS_ROOT_ITEM_KEY) { + if (btrfs_find_root(location->objectid, &tmp_root, NULL)) + return -1; + + location->objectid = tmp_root.root_dirid; + location->type = BTRFS_INODE_ITEM_KEY; + location->offset = 0; + } + + if (btrfs_search_tree(&tmp_root, location, &path)) + return res; + + if (btrfs_comp_keys(location, btrfs_path_leaf_key(&path))) + goto out; + + if (item) { + *item = *btrfs_path_item_ptr(&path, struct btrfs_inode_item); + btrfs_inode_item_to_cpu(item); + } + + if (new_root) + *new_root = tmp_root; + + res = 0; + +out: + btrfs_free_path(&path); + return res; +} + +int btrfs_readlink(const struct btrfs_root *root, u64 inr, char *target) +{ + struct btrfs_path path; + struct btrfs_key key; + struct btrfs_file_extent_item *extent; + const char *data_ptr; + int res = -1; + + key.objectid = inr; + key.type = BTRFS_EXTENT_DATA_KEY; + key.offset = 0; + + if (btrfs_search_tree(root, &key, &path)) + return -1; + + if (btrfs_comp_keys(&key, btrfs_path_leaf_key(&path))) + goto out; + + extent = btrfs_path_item_ptr(&path, struct btrfs_file_extent_item); + if (extent->type != BTRFS_FILE_EXTENT_INLINE) { + printf("%s: Extent for symlink %llu not of INLINE type\n", + __func__, inr); + goto out; + } + + btrfs_file_extent_item_to_cpu_inl(extent); + + if (extent->compression != BTRFS_COMPRESS_NONE) { + printf("%s: Symlink %llu extent data compressed!\n", __func__, + inr); + goto out; + } else if (extent->encryption != 0) { + printf("%s: Symlink %llu extent data encrypted!\n", __func__, + inr); + goto out; + } else if (extent->ram_bytes >= btrfs_info.sb.sectorsize) { + printf("%s: Symlink %llu extent data too long (%llu)!\n", + __func__, inr, extent->ram_bytes); + goto out; + } + + data_ptr = (const char *) extent + + offsetof(struct btrfs_file_extent_item, disk_bytenr); + + memcpy(target, data_ptr, extent->ram_bytes); + target[extent->ram_bytes] = '\0'; + res = 0; +out: + btrfs_free_path(&path); + return res; +} + +/* inr must be a directory (for regular files with multiple hard links this + function returns only one of the parents of the file) */ +static u64 get_parent_inode(struct btrfs_root *root, u64 inr, + struct btrfs_inode_item *inode_item) +{ + struct btrfs_key key; + u64 res; + + if (inr == BTRFS_FIRST_FREE_OBJECTID) { + if (root->objectid != btrfs_info.fs_root.objectid) { + u64 parent; + struct btrfs_root_ref ref; + + parent = btrfs_lookup_root_ref(root->objectid, &ref, + NULL); + if (parent == -1ULL) + return -1ULL; + + if (btrfs_find_root(parent, root, NULL)) + return -1ULL; + + inr = ref.dirid; + } + + if (inode_item) { + key.objectid = inr; + key.type = BTRFS_INODE_ITEM_KEY; + key.offset = 0; + + if (btrfs_lookup_inode(root, &key, inode_item, NULL)) + return -1ULL; + } + + return inr; + } + + res = btrfs_lookup_inode_ref(root, inr, NULL, NULL); + if (res == -1ULL) + return -1ULL; + + if (inode_item) { + key.objectid = res; + key.type = BTRFS_INODE_ITEM_KEY; + key.offset = 0; + + if (btrfs_lookup_inode(root, &key, inode_item, NULL)) + return -1ULL; + } + + return res; +} + +static inline int next_length(const char *path) +{ + int res = 0; + while (*path != '\0' && *path != '/' && res <= BTRFS_NAME_LEN) + ++res, ++path; + return res; +} + +static inline const char *skip_current_directories(const char *cur) +{ + while (1) { + if (cur[0] == '/') + ++cur; + else if (cur[0] == '.' && cur[1] == '/') + cur += 2; + else + break; + } + + return cur; +} + +/* inode.c, musi vratit aj root stromu kde sa inoda najde */ +u64 btrfs_lookup_path(struct btrfs_root *root, u64 inr, const char *path, + u8 *type_p, struct btrfs_inode_item *inode_item_p, + int symlink_limit) +{ + struct btrfs_dir_item item; + struct btrfs_inode_item inode_item; + u8 type = BTRFS_FT_DIR; + int len, have_inode = 0; + const char *cur = path; + + if (*cur == '/') { + ++cur; + inr = root->root_dirid; + } + + do { + cur = skip_current_directories(cur); + + len = next_length(cur); + if (len > BTRFS_NAME_LEN) { + printf("%s: Name too long at \"%.*s\"\n", __func__, + BTRFS_NAME_LEN, cur); + return -1ULL; + } + + if (len == 1 && cur[0] == '.') + break; + + if (len == 2 && cur[0] == '.' && cur[1] == '.') { + cur += 2; + inr = get_parent_inode(root, inr, &inode_item); + if (inr == -1ULL) + return -1ULL; + + type = BTRFS_FT_DIR; + continue; + } + + if (!*cur) + break; + + if (btrfs_lookup_dir_item(root, inr, cur, len, &item)) + return -1ULL; + + type = item.type; + have_inode = 1; + if (btrfs_lookup_inode(root, &item.location, &inode_item, root)) + return -1ULL; + + if (item.type == BTRFS_FT_SYMLINK && symlink_limit >= 0) { + char *target; + + if (!symlink_limit) { + printf("%s: Too much symlinks!\n", __func__); + return -1ULL; + } + + target = malloc(min(inode_item.size + 1, + (u64) btrfs_info.sb.sectorsize)); + if (!target) + return -1ULL; + + if (btrfs_readlink(root, item.location.objectid, + target)) { + free(target); + return -1ULL; + } + + inr = btrfs_lookup_path(root, inr, target, &type, + &inode_item, symlink_limit - 1); + + free(target); + + if (inr == -1ULL) + return -1ULL; + } else if (item.type != BTRFS_FT_DIR && cur[len]) { + printf("%s: \"%.*s\" not a directory\n", __func__, + (int) (cur - path + len), path); + return -1ULL; + } else { + inr = item.location.objectid; + } + + cur += len; + } while (*cur); + + if (type_p) + *type_p = type; + + if (inode_item_p) { + if (!have_inode) { + struct btrfs_key key; + + key.objectid = inr; + key.type = BTRFS_INODE_ITEM_KEY; + key.offset = 0; + + if (btrfs_lookup_inode(root, &key, &inode_item, NULL)) + return -1ULL; + } + + *inode_item_p = inode_item; + } + + return inr; +} + +u64 btrfs_file_read(const struct btrfs_root *root, u64 inr, u64 offset, + u64 size, char *buf) +{ + struct btrfs_path path; + struct btrfs_key key; + struct btrfs_file_extent_item *extent; + int res; + u64 rd, rd_all = -1ULL; + + key.objectid = inr; + key.type = BTRFS_EXTENT_DATA_KEY; + key.offset = offset; + + if (btrfs_search_tree(root, &key, &path)) + return -1ULL; + + if (btrfs_comp_keys(&key, btrfs_path_leaf_key(&path)) < 0) { + if (btrfs_prev_slot(&path)) + goto out; + + if (btrfs_comp_keys_type(&key, btrfs_path_leaf_key(&path))) + goto out; + } + + rd_all = 0; + + do { + if (btrfs_comp_keys_type(&key, btrfs_path_leaf_key(&path))) + break; + + extent = btrfs_path_item_ptr(&path, + struct btrfs_file_extent_item); + + if (extent->type == BTRFS_FILE_EXTENT_INLINE) { + btrfs_file_extent_item_to_cpu_inl(extent); + rd = btrfs_read_extent_inline(&path, extent, offset, + size, buf); + } else { + btrfs_file_extent_item_to_cpu(extent); + rd = btrfs_read_extent_reg(&path, extent, offset, size, + buf); + } + + if (rd == -1ULL) { + printf("%s: Error reading extent\n", __func__); + rd_all = -1; + goto out; + } + + offset = 0; + buf += rd; + rd_all += rd; + size -= rd; + + if (!size) + break; + } while (!(res = btrfs_next_slot(&path))); + + if (res) + return -1ULL; + +out: + btrfs_free_path(&path); + return rd_all; +} |