summaryrefslogtreecommitdiff
path: root/fs/btrfs/inode.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/btrfs/inode.c')
-rw-r--r--fs/btrfs/inode.c385
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;
+}