aboutsummaryrefslogtreecommitdiff
path: root/fs/btrfs/dir-item.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/btrfs/dir-item.c')
-rw-r--r--fs/btrfs/dir-item.c192
1 files changed, 118 insertions, 74 deletions
diff --git a/fs/btrfs/dir-item.c b/fs/btrfs/dir-item.c
index 63b5bf0a860..aab197a6d55 100644
--- a/fs/btrfs/dir-item.c
+++ b/fs/btrfs/dir-item.c
@@ -6,119 +6,163 @@
*/
#include "btrfs.h"
+#include "disk-io.h"
-static int verify_dir_item(struct btrfs_dir_item *item, u32 start, u32 total)
+static int verify_dir_item(struct btrfs_root *root,
+ struct extent_buffer *leaf,
+ struct btrfs_dir_item *dir_item)
{
- u16 max_len = BTRFS_NAME_LEN;
- u32 end;
+ u16 namelen = BTRFS_NAME_LEN;
+ u8 type = btrfs_dir_type(leaf, dir_item);
- if (item->type >= BTRFS_FT_MAX) {
- printf("%s: invalid dir item type: %i\n", __func__, item->type);
+ 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;
}
- if (item->type == BTRFS_FT_XATTR)
- max_len = 255; /* XATTR_NAME_MAX */
-
- end = start + sizeof(*item) + item->name_len;
- if (item->name_len > max_len || end > total) {
- printf("%s: invalid dir item name len: %u\n", __func__,
- item->name_len);
+ /* 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;
}
-static struct btrfs_dir_item *
-btrfs_match_dir_item_name(struct btrfs_path *path, const char *name,
- int name_len)
+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 *item;
- u32 total_len, cur = 0, this_len;
- const char *name_ptr;
-
- item = btrfs_path_item_ptr(path, struct btrfs_dir_item);
-
- total_len = btrfs_path_item_size(path);
+ 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;
+ }
- while (cur < total_len) {
- btrfs_dir_item_to_cpu(item);
- this_len = sizeof(*item) + item->name_len + item->data_len;
- name_ptr = (const char *) (item + 1);
+ name_ptr = (unsigned long)(dir_item + 1);
- if (verify_dir_item(item, cur, total_len))
- return NULL;
- if (item->name_len == name_len && !memcmp(name_ptr, name,
- name_len))
- return item;
+ 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;
- item = (struct btrfs_dir_item *) ((u8 *) item + this_len);
+ dir_item = (struct btrfs_dir_item *)((char *)dir_item +
+ this_len);
}
-
return NULL;
}
-int btrfs_lookup_dir_item(const struct btrfs_root *root, u64 dir,
- const char *name, int name_len,
- struct btrfs_dir_item *item)
+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)
{
- struct btrfs_path path;
+ int ret;
struct btrfs_key key;
- struct btrfs_dir_item *res = NULL;
+ 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);
- if (btrfs_search_tree(root, &key, &path))
- return -1;
+ 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]--;
+ }
- if (btrfs_comp_keys_type(&key, btrfs_path_leaf_key(&path)))
- goto out;
+ leaf = path->nodes[0];
+ btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
- res = btrfs_match_dir_item_name(&path, name, name_len);
- if (res)
- *item = *res;
-out:
- btrfs_free_path(&path);
- return res ? 0 : -1;
+ 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);
}
-int btrfs_readdir(const struct btrfs_root *root, u64 dir,
- btrfs_readdir_callback_t callback)
+int btrfs_iter_dir(struct btrfs_root *root, u64 ino,
+ btrfs_iter_dir_callback_t callback)
{
struct btrfs_path path;
- struct btrfs_key key, *found_key;
- struct btrfs_dir_item *item;
- int res = 0;
+ struct btrfs_key key;
+ int ret;
- key.objectid = dir;
+ btrfs_init_path(&path);
+ key.objectid = ino;
key.type = BTRFS_DIR_INDEX_KEY;
key.offset = 0;
- if (btrfs_search_tree(root, &key, &path))
- return -1;
-
+ ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
+ if (ret < 0)
+ return ret;
+ /* Should not happen */
+ if (ret == 0) {
+ ret = -EUCLEAN;
+ goto out;
+ }
+ if (path.slots[0] >= btrfs_header_nritems(path.nodes[0])) {
+ ret = btrfs_next_leaf(root, &path);
+ if (ret < 0)
+ goto out;
+ if (ret > 0) {
+ ret = 0;
+ goto out;
+ }
+ }
do {
- found_key = btrfs_path_leaf_key(&path);
- if (btrfs_comp_keys_type(&key, found_key))
- break;
-
- item = btrfs_path_item_ptr(&path, struct btrfs_dir_item);
- btrfs_dir_item_to_cpu(item);
-
- if (verify_dir_item(item, 0, sizeof(*item) + item->name_len))
- continue;
- if (item->type == BTRFS_FT_XATTR)
- continue;
+ struct btrfs_dir_item *di;
- if (callback(root, item))
+ btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
+ if (key.objectid != ino || key.type != BTRFS_DIR_INDEX_KEY)
break;
- } while (!(res = btrfs_next_slot(&path)));
-
- btrfs_free_path(&path);
-
- return res < 0 ? -1 : 0;
+ di = btrfs_item_ptr(path.nodes[0], path.slots[0],
+ struct btrfs_dir_item);
+ if (verify_dir_item(root, path.nodes[0], di)) {
+ ret = -EUCLEAN;
+ goto out;
+ }
+ ret = callback(root, path.nodes[0], di);
+ if (ret < 0)
+ goto out;
+ } while (!(ret = btrfs_next_item(root, &path)));
+
+ if (ret > 0)
+ ret = 0;
+out:
+ btrfs_release_path(&path);
+ return ret;
}