aboutsummaryrefslogtreecommitdiffstats
path: root/roms/u-boot/fs/btrfs/ctree.c
diff options
context:
space:
mode:
authorAngelos Mouzakitis <a.mouzakitis@virtualopensystems.com>2023-10-10 14:33:42 +0000
committerAngelos Mouzakitis <a.mouzakitis@virtualopensystems.com>2023-10-10 14:33:42 +0000
commitaf1a266670d040d2f4083ff309d732d648afba2a (patch)
tree2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/u-boot/fs/btrfs/ctree.c
parente02cda008591317b1625707ff8e115a4841aa889 (diff)
Add submodule dependency filesHEADmaster
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/u-boot/fs/btrfs/ctree.c')
-rw-r--r--roms/u-boot/fs/btrfs/ctree.c742
1 files changed, 742 insertions, 0 deletions
diff --git a/roms/u-boot/fs/btrfs/ctree.c b/roms/u-boot/fs/btrfs/ctree.c
new file mode 100644
index 000000000..5ffced916
--- /dev/null
+++ b/roms/u-boot/fs/btrfs/ctree.c
@@ -0,0 +1,742 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * BTRFS filesystem implementation for U-Boot
+ *
+ * 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz
+ */
+
+#include <linux/kernel.h>
+#include <log.h>
+#include <malloc.h>
+#include <memalign.h>
+#include "btrfs.h"
+#include "disk-io.h"
+
+static const struct btrfs_csum {
+ u16 size;
+ const char name[14];
+} btrfs_csums[] = {
+ [BTRFS_CSUM_TYPE_CRC32] = { 4, "crc32c" },
+ [BTRFS_CSUM_TYPE_XXHASH] = { 8, "xxhash64" },
+ [BTRFS_CSUM_TYPE_SHA256] = { 32, "sha256" },
+ [BTRFS_CSUM_TYPE_BLAKE2] = { 32, "blake2" },
+};
+
+u16 btrfs_super_csum_size(const struct btrfs_super_block *sb)
+{
+ const u16 csum_type = btrfs_super_csum_type(sb);
+
+ return btrfs_csums[csum_type].size;
+}
+
+const char *btrfs_super_csum_name(u16 csum_type)
+{
+ return btrfs_csums[csum_type].name;
+}
+
+size_t btrfs_super_num_csums(void)
+{
+ return ARRAY_SIZE(btrfs_csums);
+}
+
+u16 btrfs_csum_type_size(u16 csum_type)
+{
+ return btrfs_csums[csum_type].size;
+}
+
+struct btrfs_path *btrfs_alloc_path(void)
+{
+ struct btrfs_path *path;
+ path = kzalloc(sizeof(struct btrfs_path), GFP_NOFS);
+ return path;
+}
+
+void btrfs_free_path(struct btrfs_path *p)
+{
+ if (!p)
+ return;
+ btrfs_release_path(p);
+ kfree(p);
+}
+
+void btrfs_release_path(struct btrfs_path *p)
+{
+ int i;
+ for (i = 0; i < BTRFS_MAX_LEVEL; i++) {
+ if (!p->nodes[i])
+ continue;
+ free_extent_buffer(p->nodes[i]);
+ }
+ memset(p, 0, sizeof(*p));
+}
+
+int btrfs_comp_cpu_keys(const struct btrfs_key *k1, const struct btrfs_key *k2)
+{
+ if (k1->objectid > k2->objectid)
+ return 1;
+ if (k1->objectid < k2->objectid)
+ return -1;
+ if (k1->type > k2->type)
+ return 1;
+ if (k1->type < k2->type)
+ return -1;
+ if (k1->offset > k2->offset)
+ return 1;
+ if (k1->offset < k2->offset)
+ return -1;
+ return 0;
+}
+
+static int btrfs_comp_keys(struct btrfs_disk_key *disk,
+ const struct btrfs_key *k2)
+{
+ struct btrfs_key k1;
+
+ btrfs_disk_key_to_cpu(&k1, disk);
+ return btrfs_comp_cpu_keys(&k1, k2);
+}
+
+enum btrfs_tree_block_status
+btrfs_check_node(struct btrfs_fs_info *fs_info,
+ struct btrfs_disk_key *parent_key, struct extent_buffer *buf)
+{
+ int i;
+ struct btrfs_key cpukey;
+ struct btrfs_disk_key key;
+ u32 nritems = btrfs_header_nritems(buf);
+ enum btrfs_tree_block_status ret = BTRFS_TREE_BLOCK_INVALID_NRITEMS;
+
+ if (nritems == 0 || nritems > BTRFS_NODEPTRS_PER_BLOCK(fs_info))
+ goto fail;
+
+ ret = BTRFS_TREE_BLOCK_INVALID_PARENT_KEY;
+ if (parent_key && parent_key->type) {
+ btrfs_node_key(buf, &key, 0);
+ if (memcmp(parent_key, &key, sizeof(key)))
+ goto fail;
+ }
+ ret = BTRFS_TREE_BLOCK_BAD_KEY_ORDER;
+ for (i = 0; nritems > 1 && i < nritems - 2; i++) {
+ btrfs_node_key(buf, &key, i);
+ btrfs_node_key_to_cpu(buf, &cpukey, i + 1);
+ if (btrfs_comp_keys(&key, &cpukey) >= 0)
+ goto fail;
+ }
+ return BTRFS_TREE_BLOCK_CLEAN;
+fail:
+ return ret;
+}
+
+enum btrfs_tree_block_status
+btrfs_check_leaf(struct btrfs_fs_info *fs_info,
+ struct btrfs_disk_key *parent_key, struct extent_buffer *buf)
+{
+ int i;
+ struct btrfs_key cpukey;
+ struct btrfs_disk_key key;
+ u32 nritems = btrfs_header_nritems(buf);
+ enum btrfs_tree_block_status ret = BTRFS_TREE_BLOCK_INVALID_NRITEMS;
+
+ if (nritems * sizeof(struct btrfs_item) > buf->len) {
+ fprintf(stderr, "invalid number of items %llu\n",
+ (unsigned long long)buf->start);
+ goto fail;
+ }
+
+ if (btrfs_header_level(buf) != 0) {
+ ret = BTRFS_TREE_BLOCK_INVALID_LEVEL;
+ fprintf(stderr, "leaf is not a leaf %llu\n",
+ (unsigned long long)btrfs_header_bytenr(buf));
+ goto fail;
+ }
+ if (btrfs_leaf_free_space(buf) < 0) {
+ ret = BTRFS_TREE_BLOCK_INVALID_FREE_SPACE;
+ fprintf(stderr, "leaf free space incorrect %llu %d\n",
+ (unsigned long long)btrfs_header_bytenr(buf),
+ btrfs_leaf_free_space(buf));
+ goto fail;
+ }
+
+ if (nritems == 0)
+ return BTRFS_TREE_BLOCK_CLEAN;
+
+ btrfs_item_key(buf, &key, 0);
+ if (parent_key && parent_key->type &&
+ memcmp(parent_key, &key, sizeof(key))) {
+ ret = BTRFS_TREE_BLOCK_INVALID_PARENT_KEY;
+ fprintf(stderr, "leaf parent key incorrect %llu\n",
+ (unsigned long long)btrfs_header_bytenr(buf));
+ goto fail;
+ }
+ for (i = 0; nritems > 1 && i < nritems - 1; i++) {
+ btrfs_item_key(buf, &key, i);
+ btrfs_item_key_to_cpu(buf, &cpukey, i + 1);
+ if (btrfs_comp_keys(&key, &cpukey) >= 0) {
+ ret = BTRFS_TREE_BLOCK_BAD_KEY_ORDER;
+ fprintf(stderr, "bad key ordering %d %d\n", i, i+1);
+ goto fail;
+ }
+ if (btrfs_item_offset_nr(buf, i) !=
+ btrfs_item_end_nr(buf, i + 1)) {
+ ret = BTRFS_TREE_BLOCK_INVALID_OFFSETS;
+ fprintf(stderr, "incorrect offsets %u %u\n",
+ btrfs_item_offset_nr(buf, i),
+ btrfs_item_end_nr(buf, i + 1));
+ goto fail;
+ }
+ if (i == 0 && btrfs_item_end_nr(buf, i) !=
+ BTRFS_LEAF_DATA_SIZE(fs_info)) {
+ ret = BTRFS_TREE_BLOCK_INVALID_OFFSETS;
+ fprintf(stderr, "bad item end %u wanted %u\n",
+ btrfs_item_end_nr(buf, i),
+ (unsigned)BTRFS_LEAF_DATA_SIZE(fs_info));
+ goto fail;
+ }
+ }
+
+ for (i = 0; i < nritems; i++) {
+ if (btrfs_item_end_nr(buf, i) >
+ BTRFS_LEAF_DATA_SIZE(fs_info)) {
+ btrfs_item_key(buf, &key, 0);
+ ret = BTRFS_TREE_BLOCK_INVALID_OFFSETS;
+ fprintf(stderr, "slot end outside of leaf %llu > %llu\n",
+ (unsigned long long)btrfs_item_end_nr(buf, i),
+ (unsigned long long)BTRFS_LEAF_DATA_SIZE(
+ fs_info));
+ goto fail;
+ }
+ }
+
+ return BTRFS_TREE_BLOCK_CLEAN;
+fail:
+ return ret;
+}
+
+static int noinline check_block(struct btrfs_fs_info *fs_info,
+ struct btrfs_path *path, int level)
+{
+ struct btrfs_disk_key key;
+ struct btrfs_disk_key *key_ptr = NULL;
+ struct extent_buffer *parent;
+ enum btrfs_tree_block_status ret;
+
+ if (path->nodes[level + 1]) {
+ parent = path->nodes[level + 1];
+ btrfs_node_key(parent, &key, path->slots[level + 1]);
+ key_ptr = &key;
+ }
+ if (level == 0)
+ ret = btrfs_check_leaf(fs_info, key_ptr, path->nodes[0]);
+ else
+ ret = btrfs_check_node(fs_info, key_ptr, path->nodes[level]);
+ if (ret == BTRFS_TREE_BLOCK_CLEAN)
+ return 0;
+ return -EIO;
+}
+
+/*
+ * search for key in the extent_buffer. The items start at offset p,
+ * and they are item_size apart. There are 'max' items in p.
+ *
+ * the slot in the array is returned via slot, and it points to
+ * the place where you would insert key if it is not found in
+ * the array.
+ *
+ * slot may point to max if the key is bigger than all of the keys
+ */
+static int generic_bin_search(struct extent_buffer *eb, unsigned long p,
+ int item_size, const struct btrfs_key *key,
+ int max, int *slot)
+{
+ int low = 0;
+ int high = max;
+ int mid;
+ int ret;
+ unsigned long offset;
+ struct btrfs_disk_key *tmp;
+
+ while(low < high) {
+ mid = (low + high) / 2;
+ offset = p + mid * item_size;
+
+ tmp = (struct btrfs_disk_key *)(eb->data + offset);
+ ret = btrfs_comp_keys(tmp, key);
+
+ if (ret < 0)
+ low = mid + 1;
+ else if (ret > 0)
+ high = mid;
+ else {
+ *slot = mid;
+ return 0;
+ }
+ }
+ *slot = low;
+ return 1;
+}
+
+/*
+ * simple bin_search frontend that does the right thing for
+ * leaves vs nodes
+ */
+int btrfs_bin_search(struct extent_buffer *eb, const struct btrfs_key *key,
+ int *slot)
+{
+ if (btrfs_header_level(eb) == 0)
+ return generic_bin_search(eb,
+ offsetof(struct btrfs_leaf, items),
+ sizeof(struct btrfs_item),
+ key, btrfs_header_nritems(eb),
+ slot);
+ else
+ return generic_bin_search(eb,
+ offsetof(struct btrfs_node, ptrs),
+ sizeof(struct btrfs_key_ptr),
+ key, btrfs_header_nritems(eb),
+ slot);
+}
+
+struct extent_buffer *read_node_slot(struct btrfs_fs_info *fs_info,
+ struct extent_buffer *parent, int slot)
+{
+ struct extent_buffer *ret;
+ int level = btrfs_header_level(parent);
+
+ if (slot < 0)
+ return NULL;
+ if (slot >= btrfs_header_nritems(parent))
+ return NULL;
+
+ if (level == 0)
+ return NULL;
+
+ ret = read_tree_block(fs_info, btrfs_node_blockptr(parent, slot),
+ btrfs_node_ptr_generation(parent, slot));
+ if (!extent_buffer_uptodate(ret))
+ return ERR_PTR(-EIO);
+
+ if (btrfs_header_level(ret) != level - 1) {
+ error("child eb corrupted: parent bytenr=%llu item=%d parent level=%d child level=%d",
+ btrfs_header_bytenr(parent), slot,
+ btrfs_header_level(parent), btrfs_header_level(ret));
+ free_extent_buffer(ret);
+ return ERR_PTR(-EIO);
+ }
+ return ret;
+}
+
+int btrfs_find_item(struct btrfs_root *fs_root, struct btrfs_path *found_path,
+ u64 iobjectid, u64 ioff, u8 key_type,
+ struct btrfs_key *found_key)
+{
+ int ret;
+ struct btrfs_key key;
+ struct extent_buffer *eb;
+ struct btrfs_path *path;
+
+ key.type = key_type;
+ key.objectid = iobjectid;
+ key.offset = ioff;
+
+ if (found_path == NULL) {
+ path = btrfs_alloc_path();
+ if (!path)
+ return -ENOMEM;
+ } else
+ path = found_path;
+
+ ret = btrfs_search_slot(NULL, fs_root, &key, path, 0, 0);
+ if ((ret < 0) || (found_key == NULL))
+ goto out;
+
+ eb = path->nodes[0];
+ if (ret && path->slots[0] >= btrfs_header_nritems(eb)) {
+ ret = btrfs_next_leaf(fs_root, path);
+ if (ret)
+ goto out;
+ eb = path->nodes[0];
+ }
+
+ btrfs_item_key_to_cpu(eb, found_key, path->slots[0]);
+ if (found_key->type != key.type ||
+ found_key->objectid != key.objectid) {
+ ret = 1;
+ goto out;
+ }
+
+out:
+ if (path != found_path)
+ btrfs_free_path(path);
+ return ret;
+}
+
+/*
+ * look for key in the tree. path is filled in with nodes along the way
+ * if key is found, we return zero and you can find the item in the leaf
+ * level of the path (level 0)
+ *
+ * If the key isn't found, the path points to the slot where it should
+ * be inserted, and 1 is returned. If there are other errors during the
+ * search a negative error number is returned.
+ *
+ * if ins_len > 0, nodes and leaves will be split as we walk down the
+ * tree. if ins_len < 0, nodes will be merged as we walk down the tree (if
+ * possible)
+ *
+ * NOTE: This version has no COW ability, thus we expect trans == NULL,
+ * ins_len == 0 and cow == 0.
+ */
+int btrfs_search_slot(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root, const struct btrfs_key *key,
+ struct btrfs_path *p, int ins_len, int cow)
+{
+ struct extent_buffer *b;
+ int slot;
+ int ret;
+ int level;
+ struct btrfs_fs_info *fs_info = root->fs_info;
+ u8 lowest_level = 0;
+
+ assert(trans == NULL && ins_len == 0 && cow == 0);
+ lowest_level = p->lowest_level;
+ WARN_ON(lowest_level && ins_len > 0);
+ WARN_ON(p->nodes[0] != NULL);
+
+ b = root->node;
+ extent_buffer_get(b);
+ while (b) {
+ level = btrfs_header_level(b);
+ /*
+ if (cow) {
+ int wret;
+ wret = btrfs_cow_block(trans, root, b,
+ p->nodes[level + 1],
+ p->slots[level + 1],
+ &b);
+ if (wret) {
+ free_extent_buffer(b);
+ return wret;
+ }
+ }
+ */
+ BUG_ON(!cow && ins_len);
+ if (level != btrfs_header_level(b))
+ WARN_ON(1);
+ level = btrfs_header_level(b);
+ p->nodes[level] = b;
+ ret = check_block(fs_info, p, level);
+ if (ret)
+ return -1;
+ ret = btrfs_bin_search(b, key, &slot);
+ if (level != 0) {
+ if (ret && slot > 0)
+ slot -= 1;
+ p->slots[level] = slot;
+ /*
+ if ((p->search_for_split || ins_len > 0) &&
+ btrfs_header_nritems(b) >=
+ BTRFS_NODEPTRS_PER_BLOCK(fs_info) - 3) {
+ int sret = split_node(trans, root, p, level);
+ BUG_ON(sret > 0);
+ if (sret)
+ return sret;
+ b = p->nodes[level];
+ slot = p->slots[level];
+ } else if (ins_len < 0) {
+ int sret = balance_level(trans, root, p,
+ level);
+ if (sret)
+ return sret;
+ b = p->nodes[level];
+ if (!b) {
+ btrfs_release_path(p);
+ goto again;
+ }
+ slot = p->slots[level];
+ BUG_ON(btrfs_header_nritems(b) == 1);
+ }
+ */
+ /* this is only true while dropping a snapshot */
+ if (level == lowest_level)
+ break;
+
+ b = read_node_slot(fs_info, b, slot);
+ if (!extent_buffer_uptodate(b))
+ return -EIO;
+ } else {
+ p->slots[level] = slot;
+ /*
+ if (ins_len > 0 &&
+ ins_len > btrfs_leaf_free_space(b)) {
+ int sret = split_leaf(trans, root, key,
+ p, ins_len, ret == 0);
+ BUG_ON(sret > 0);
+ if (sret)
+ return sret;
+ }
+ */
+ return ret;
+ }
+ }
+ return 1;
+}
+
+/*
+ * Helper to use instead of search slot if no exact match is needed but
+ * instead the next or previous item should be returned.
+ * When find_higher is true, the next higher item is returned, the next lower
+ * otherwise.
+ * When return_any and find_higher are both true, and no higher item is found,
+ * return the next lower instead.
+ * When return_any is true and find_higher is false, and no lower item is found,
+ * return the next higher instead.
+ * It returns 0 if any item is found, 1 if none is found (tree empty), and
+ * < 0 on error
+ */
+int btrfs_search_slot_for_read(struct btrfs_root *root,
+ const struct btrfs_key *key,
+ struct btrfs_path *p, int find_higher,
+ int return_any)
+{
+ int ret;
+ struct extent_buffer *leaf;
+
+again:
+ ret = btrfs_search_slot(NULL, root, key, p, 0, 0);
+ if (ret <= 0)
+ return ret;
+ /*
+ * A return value of 1 means the path is at the position where the item
+ * should be inserted. Normally this is the next bigger item, but in
+ * case the previous item is the last in a leaf, path points to the
+ * first free slot in the previous leaf, i.e. at an invalid item.
+ */
+ leaf = p->nodes[0];
+
+ if (find_higher) {
+ if (p->slots[0] >= btrfs_header_nritems(leaf)) {
+ ret = btrfs_next_leaf(root, p);
+ if (ret <= 0)
+ return ret;
+ if (!return_any)
+ return 1;
+ /*
+ * No higher item found, return the next lower instead
+ */
+ return_any = 0;
+ find_higher = 0;
+ btrfs_release_path(p);
+ goto again;
+ }
+ } else {
+ if (p->slots[0] == 0) {
+ ret = btrfs_prev_leaf(root, p);
+ if (ret < 0)
+ return ret;
+ if (!ret) {
+ leaf = p->nodes[0];
+ if (p->slots[0] == btrfs_header_nritems(leaf))
+ p->slots[0]--;
+ return 0;
+ }
+ if (!return_any)
+ return 1;
+ /*
+ * No lower item found, return the next higher instead
+ */
+ return_any = 0;
+ find_higher = 1;
+ btrfs_release_path(p);
+ goto again;
+ } else {
+ --p->slots[0];
+ }
+ }
+ return 0;
+}
+
+/*
+ * how many bytes are required to store the items in a leaf. start
+ * and nr indicate which items in the leaf to check. This totals up the
+ * space used both by the item structs and the item data
+ */
+static int leaf_space_used(struct extent_buffer *l, int start, int nr)
+{
+ int data_len;
+ int nritems = btrfs_header_nritems(l);
+ int end = min(nritems, start + nr) - 1;
+
+ if (!nr)
+ return 0;
+ data_len = btrfs_item_end_nr(l, start);
+ data_len = data_len - btrfs_item_offset_nr(l, end);
+ data_len += sizeof(struct btrfs_item) * nr;
+ WARN_ON(data_len < 0);
+ return data_len;
+}
+
+/*
+ * The space between the end of the leaf items and
+ * the start of the leaf data. IOW, how much room
+ * the leaf has left for both items and data
+ */
+int btrfs_leaf_free_space(struct extent_buffer *leaf)
+{
+ int nritems = btrfs_header_nritems(leaf);
+ u32 leaf_data_size;
+ int ret;
+
+ BUG_ON(leaf->fs_info && leaf->fs_info->nodesize != leaf->len);
+ leaf_data_size = __BTRFS_LEAF_DATA_SIZE(leaf->len);
+ ret = leaf_data_size - leaf_space_used(leaf, 0 ,nritems);
+ if (ret < 0) {
+ printk("leaf free space ret %d, leaf data size %u, used %d nritems %d\n",
+ ret, leaf_data_size, leaf_space_used(leaf, 0, nritems),
+ nritems);
+ }
+ return ret;
+}
+
+/*
+ * walk up the tree as far as required to find the previous leaf.
+ * returns 0 if it found something or 1 if there are no lesser leaves.
+ * returns < 0 on io errors.
+ */
+int btrfs_prev_leaf(struct btrfs_root *root, struct btrfs_path *path)
+{
+ int slot;
+ int level = 1;
+ struct extent_buffer *c;
+ struct extent_buffer *next = NULL;
+ struct btrfs_fs_info *fs_info = root->fs_info;
+
+ while(level < BTRFS_MAX_LEVEL) {
+ if (!path->nodes[level])
+ return 1;
+
+ slot = path->slots[level];
+ c = path->nodes[level];
+ if (slot == 0) {
+ level++;
+ if (level == BTRFS_MAX_LEVEL)
+ return 1;
+ continue;
+ }
+ slot--;
+
+ next = read_node_slot(fs_info, c, slot);
+ if (!extent_buffer_uptodate(next)) {
+ if (IS_ERR(next))
+ return PTR_ERR(next);
+ return -EIO;
+ }
+ break;
+ }
+ path->slots[level] = slot;
+ while(1) {
+ level--;
+ c = path->nodes[level];
+ free_extent_buffer(c);
+ slot = btrfs_header_nritems(next);
+ if (slot != 0)
+ slot--;
+ path->nodes[level] = next;
+ path->slots[level] = slot;
+ if (!level)
+ break;
+ next = read_node_slot(fs_info, next, slot);
+ if (!extent_buffer_uptodate(next)) {
+ if (IS_ERR(next))
+ return PTR_ERR(next);
+ return -EIO;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Walk up the tree as far as necessary to find the next sibling tree block.
+ * More generic version of btrfs_next_leaf(), as it could find sibling nodes
+ * if @path->lowest_level is not 0.
+ *
+ * returns 0 if it found something or 1 if there are no greater leaves.
+ * returns < 0 on io errors.
+ */
+int btrfs_next_sibling_tree_block(struct btrfs_fs_info *fs_info,
+ struct btrfs_path *path)
+{
+ int slot;
+ int level = path->lowest_level + 1;
+ struct extent_buffer *c;
+ struct extent_buffer *next = NULL;
+
+ BUG_ON(path->lowest_level + 1 >= BTRFS_MAX_LEVEL);
+ do {
+ if (!path->nodes[level])
+ return 1;
+
+ slot = path->slots[level] + 1;
+ c = path->nodes[level];
+ if (slot >= btrfs_header_nritems(c)) {
+ level++;
+ if (level == BTRFS_MAX_LEVEL)
+ return 1;
+ continue;
+ }
+
+ next = read_node_slot(fs_info, c, slot);
+ if (!extent_buffer_uptodate(next))
+ return -EIO;
+ break;
+ } while (level < BTRFS_MAX_LEVEL);
+ path->slots[level] = slot;
+ while(1) {
+ level--;
+ c = path->nodes[level];
+ free_extent_buffer(c);
+ path->nodes[level] = next;
+ path->slots[level] = 0;
+ if (level == path->lowest_level)
+ break;
+ next = read_node_slot(fs_info, next, 0);
+ if (!extent_buffer_uptodate(next))
+ return -EIO;
+ }
+ return 0;
+}
+
+int btrfs_previous_item(struct btrfs_root *root,
+ struct btrfs_path *path, u64 min_objectid,
+ int type)
+{
+ struct btrfs_key found_key;
+ struct extent_buffer *leaf;
+ u32 nritems;
+ int ret;
+
+ while(1) {
+ if (path->slots[0] == 0) {
+ ret = btrfs_prev_leaf(root, path);
+ if (ret != 0)
+ return ret;
+ } else {
+ path->slots[0]--;
+ }
+ leaf = path->nodes[0];
+ nritems = btrfs_header_nritems(leaf);
+ if (nritems == 0)
+ return 1;
+ if (path->slots[0] == nritems)
+ path->slots[0]--;
+
+ btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
+ if (found_key.objectid < min_objectid)
+ break;
+ if (found_key.type == type)
+ return 0;
+ if (found_key.objectid == min_objectid &&
+ found_key.type < type)
+ break;
+ }
+ return 1;
+}