diff options
Diffstat (limited to 'fs/btrfs/dir-item.c')
-rw-r--r-- | fs/btrfs/dir-item.c | 192 |
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; } |