aboutsummaryrefslogtreecommitdiff
path: root/fs/btrfs/dir-item.c
diff options
context:
space:
mode:
authorQu Wenruo2020-06-24 18:03:05 +0200
committerTom Rini2020-09-07 20:57:27 -0400
commitc921aa20c3682456b59574be8cf3df589d909950 (patch)
treedd08412b23cc1f74712b4d48d39da91f6e4557e2 /fs/btrfs/dir-item.c
parent5bdcb37495d594c352221f582e31a0ec04b7d58b (diff)
fs: btrfs: Implement btrfs_lookup_path()
This is the extent buffer based path lookup routine. To implement this, btrfs_lookup_dir_item() is crossported from btrfs-progs, and implements btrfs_lookup_path() from scratch. Unlike the existing __btrfs_lookup_path(), since btrfs_read_fs_root() will check whether a root is a orphan at read time, there is no need to check root backref, this makes the code a little easier to read. Signed-off-by: Qu Wenruo <wqu@suse.com> Reviewed-by: Marek BehĂșn <marek.behun@nic.cz>
Diffstat (limited to 'fs/btrfs/dir-item.c')
-rw-r--r--fs/btrfs/dir-item.c106
1 files changed, 106 insertions, 0 deletions
diff --git a/fs/btrfs/dir-item.c b/fs/btrfs/dir-item.c
index aea621c72bb..b49aaacbd8f 100644
--- a/fs/btrfs/dir-item.c
+++ b/fs/btrfs/dir-item.c
@@ -8,6 +8,112 @@
#include "btrfs.h"
#include "disk-io.h"
+static int verify_dir_item(struct btrfs_root *root,
+ struct extent_buffer *leaf,
+ struct btrfs_dir_item *dir_item)
+{
+ u16 namelen = BTRFS_NAME_LEN;
+ u8 type = btrfs_dir_type(leaf, dir_item);
+
+ if (type == BTRFS_FT_XATTR)
+ namelen = XATTR_NAME_MAX;
+
+ if (btrfs_dir_name_len(leaf, dir_item) > namelen) {
+ fprintf(stderr, "invalid dir item name len: %u\n",
+ (unsigned)btrfs_dir_data_len(leaf, dir_item));
+ return 1;
+ }
+
+ /* BTRFS_MAX_XATTR_SIZE is the same for all dir items */
+ if ((btrfs_dir_data_len(leaf, dir_item) +
+ btrfs_dir_name_len(leaf, dir_item)) >
+ BTRFS_MAX_XATTR_SIZE(root->fs_info)) {
+ fprintf(stderr, "invalid dir item name + data len: %u + %u\n",
+ (unsigned)btrfs_dir_name_len(leaf, dir_item),
+ (unsigned)btrfs_dir_data_len(leaf, dir_item));
+ return 1;
+ }
+
+ return 0;
+}
+
+struct btrfs_dir_item *btrfs_match_dir_item_name(struct btrfs_root *root,
+ struct btrfs_path *path,
+ const char *name, int name_len)
+{
+ struct btrfs_dir_item *dir_item;
+ unsigned long name_ptr;
+ u32 total_len;
+ u32 cur = 0;
+ u32 this_len;
+ struct extent_buffer *leaf;
+
+ leaf = path->nodes[0];
+ dir_item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_dir_item);
+ total_len = btrfs_item_size_nr(leaf, path->slots[0]);
+ if (verify_dir_item(root, leaf, dir_item))
+ return NULL;
+
+ while(cur < total_len) {
+ this_len = sizeof(*dir_item) +
+ btrfs_dir_name_len(leaf, dir_item) +
+ btrfs_dir_data_len(leaf, dir_item);
+ if (this_len > (total_len - cur)) {
+ fprintf(stderr, "invalid dir item size\n");
+ return NULL;
+ }
+
+ name_ptr = (unsigned long)(dir_item + 1);
+
+ if (btrfs_dir_name_len(leaf, dir_item) == name_len &&
+ memcmp_extent_buffer(leaf, name, name_ptr, name_len) == 0)
+ return dir_item;
+
+ cur += this_len;
+ dir_item = (struct btrfs_dir_item *)((char *)dir_item +
+ this_len);
+ }
+ return NULL;
+}
+
+struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct btrfs_path *path, u64 dir,
+ const char *name, int name_len,
+ int mod)
+{
+ int ret;
+ struct btrfs_key key;
+ int ins_len = mod < 0 ? -1 : 0;
+ int cow = mod != 0;
+ struct btrfs_key found_key;
+ struct extent_buffer *leaf;
+
+ key.objectid = dir;
+ key.type = BTRFS_DIR_ITEM_KEY;
+
+ key.offset = btrfs_name_hash(name, name_len);
+
+ ret = btrfs_search_slot(trans, root, &key, path, ins_len, cow);
+ if (ret < 0)
+ return ERR_PTR(ret);
+ if (ret > 0) {
+ if (path->slots[0] == 0)
+ return NULL;
+ path->slots[0]--;
+ }
+
+ leaf = path->nodes[0];
+ btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
+
+ if (found_key.objectid != dir ||
+ found_key.type != BTRFS_DIR_ITEM_KEY ||
+ found_key.offset != key.offset)
+ return NULL;
+
+ return btrfs_match_dir_item_name(root, path, name, name_len);
+}
+
static int __verify_dir_item(struct btrfs_dir_item *item, u32 start, u32 total)
{
u16 max_len = BTRFS_NAME_LEN;