/* * this makes the path point to (logical EXTENT_ITEM *) * returns BTRFS_EXTENT_FLAG_DATA for data, BTRFS_EXTENT_FLAG_TREE_BLOCK for * tree blocks and <0 on error. */ int extent_from_logical(struct btrfs_fs_info *fs_info, u64 logical, struct btrfs_path *path, struct btrfs_key *found_key, u64 *flags_ret) { int ret; u64 flags; u32 item_size; struct extent_buffer *eb; struct btrfs_extent_item *ei; struct btrfs_key key; key.type = BTRFS_EXTENT_ITEM_KEY; key.objectid = logical; key.offset = (u64)-1; ret = btrfs_search_slot(NULL, fs_info->extent_root, &key, path, 0, 0); if (ret < 0) return ret; ret = btrfs_previous_item(fs_info->extent_root, path, 0, BTRFS_EXTENT_ITEM_KEY); if (ret < 0) return ret; btrfs_item_key_to_cpu(path->nodes[0], found_key, path->slots[0]); if (found_key->type != BTRFS_EXTENT_ITEM_KEY || found_key->objectid > logical || found_key->objectid + found_key->offset <= logical) { pr_debug("logical %llu is not within any extent\n", (unsigned long long)logical); return -ENOENT; } eb = path->nodes[0]; item_size = btrfs_item_size_nr(eb, path->slots[0]); BUG_ON(item_size < sizeof(*ei)); ei = btrfs_item_ptr(eb, path->slots[0], struct btrfs_extent_item); flags = btrfs_extent_flags(eb, ei); pr_debug("logical %llu is at position %llu within the extent (%llu " "EXTENT_ITEM %llu) flags %#llx size %u\n", (unsigned long long)logical, (unsigned long long)(logical - found_key->objectid), (unsigned long long)found_key->objectid, (unsigned long long)found_key->offset, (unsigned long long)flags, item_size); WARN_ON(!flags_ret); if (flags_ret) { if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) *flags_ret = BTRFS_EXTENT_FLAG_TREE_BLOCK; else if (flags & BTRFS_EXTENT_FLAG_DATA) *flags_ret = BTRFS_EXTENT_FLAG_DATA; else BUG_ON(1); return 0; } return -EIO; }
/* * this makes the path point to (logical EXTENT_ITEM *) * returns BTRFS_EXTENT_FLAG_DATA for data, BTRFS_EXTENT_FLAG_TREE_BLOCK for * tree blocks and <0 on error. */ int extent_from_logical(struct btrfs_fs_info *fs_info, u64 logical, struct btrfs_path *path, struct btrfs_key *found_key) { int ret; u64 flags; u32 item_size; struct extent_buffer *eb; struct btrfs_extent_item *ei; struct btrfs_key key; key.type = BTRFS_EXTENT_ITEM_KEY; key.objectid = logical; key.offset = (u64)-1; ret = btrfs_search_slot(NULL, fs_info->extent_root, &key, path, 0, 0); if (ret < 0) return ret; ret = btrfs_previous_item(fs_info->extent_root, path, 0, BTRFS_EXTENT_ITEM_KEY); if (ret < 0) return ret; btrfs_item_key_to_cpu(path->nodes[0], found_key, path->slots[0]); if (found_key->type != BTRFS_EXTENT_ITEM_KEY || found_key->objectid > logical || found_key->objectid + found_key->offset <= logical) return -ENOENT; eb = path->nodes[0]; item_size = btrfs_item_size_nr(eb, path->slots[0]); BUG_ON(item_size < sizeof(*ei)); ei = btrfs_item_ptr(eb, path->slots[0], struct btrfs_extent_item); flags = btrfs_extent_flags(eb, ei); if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) return BTRFS_EXTENT_FLAG_TREE_BLOCK; if (flags & BTRFS_EXTENT_FLAG_DATA) return BTRFS_EXTENT_FLAG_DATA; return -EIO; }
/* * If prepare_del is given, this will setup search_slot() for delete. * Caller needs to do proper locking. * * Return > 0 for found. * Return 0 for not found. * Return < 0 for error. */ static int ondisk_search_bytenr(struct btrfs_trans_handle *trans, struct btrfs_dedup_info *dedup_info, struct btrfs_path *path, u64 bytenr, int prepare_del) { struct btrfs_key key; struct btrfs_root *dedup_root = dedup_info->dedup_root; int ret; int ins_len = 0; int cow = 0; if (prepare_del) { if (WARN_ON(trans == NULL)) return -EINVAL; cow = 1; ins_len = -1; } key.objectid = bytenr; key.type = BTRFS_DEDUP_BYTENR_ITEM_KEY; key.offset = (u64)-1; ret = btrfs_search_slot(trans, dedup_root, &key, path, ins_len, cow); if (ret < 0) return ret; WARN_ON(ret == 0); ret = btrfs_previous_item(dedup_root, path, bytenr, BTRFS_DEDUP_BYTENR_ITEM_KEY); if (ret < 0) return ret; if (ret > 0) return 0; return 1; }
/* * Return 0 for not found * Return >0 for found and set bytenr_ret * Return <0 for error */ static int ondisk_search_hash(struct btrfs_dedup_info *dedup_info, u8 *hash, u64 *bytenr_ret, u32 *num_bytes_ret) { struct btrfs_path *path; struct btrfs_key key; struct btrfs_root *dedup_root = dedup_info->dedup_root; u8 *buf = NULL; u64 hash_key; int hash_len = btrfs_dedup_sizes[dedup_info->hash_type]; int ret; path = btrfs_alloc_path(); if (!path) return -ENOMEM; buf = kmalloc(hash_len, GFP_NOFS); if (!buf) { ret = -ENOMEM; goto out; } memcpy(&hash_key, hash + hash_len - 8, 8); key.objectid = hash_key; key.type = BTRFS_DEDUP_HASH_ITEM_KEY; key.offset = (u64)-1; ret = btrfs_search_slot(NULL, dedup_root, &key, path, 0, 0); if (ret < 0) goto out; WARN_ON(ret == 0); while (1) { struct extent_buffer *node; struct btrfs_dedup_hash_item *hash_item; int slot; ret = btrfs_previous_item(dedup_root, path, hash_key, BTRFS_DEDUP_HASH_ITEM_KEY); if (ret < 0) goto out; if (ret > 0) { ret = 0; goto out; } node = path->nodes[0]; slot = path->slots[0]; btrfs_item_key_to_cpu(node, &key, slot); if (key.type != BTRFS_DEDUP_HASH_ITEM_KEY || memcmp(&key.objectid, hash + hash_len - 8, 8)) break; hash_item = btrfs_item_ptr(node, slot, struct btrfs_dedup_hash_item); read_extent_buffer(node, buf, (unsigned long)(hash_item + 1), hash_len); if (!memcmp(buf, hash, hash_len)) { ret = 1; *bytenr_ret = key.offset; *num_bytes_ret = btrfs_dedup_hash_len(node, hash_item); break; } } out: kfree(buf); btrfs_free_path(path); return ret; }
/* * Get the first file extent that covers (part of) the given range * Unlike kernel using extent_map to handle hole even no-hole is enabled, * progs don't have such infrastructure, so caller should do extra care * for no-hole. * * return 0 for found, and path points to the file extent. * return >0 for not found, and path points to the insert position. * return <0 for error. */ int btrfs_get_extent(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_path *path, u64 ino, u64 offset, u64 len, int ins_len) { struct btrfs_key key; struct btrfs_key found_key; struct btrfs_file_extent_item *fi_item; u64 end = 0; int ret = 0; int not_found = 1; key.objectid = ino; key.type = BTRFS_EXTENT_DATA_KEY; key.offset = offset; ret = btrfs_search_slot(trans, root, &key, path, ins_len, ins_len ? 1 : 0); if (ret <= 0) goto out; if (ret > 0) { /* Check preivous file extent */ ret = btrfs_previous_item(root, path, ino, BTRFS_EXTENT_DATA_KEY); if (ret < 0) goto out; if (ret > 0) goto check_next; } btrfs_item_key_to_cpu(path->nodes[0], &found_key, path->slots[0]); if (found_key.objectid != ino || found_key.type != BTRFS_EXTENT_DATA_KEY) goto check_next; fi_item = btrfs_item_ptr(path->nodes[0], path->slots[0], struct btrfs_file_extent_item); end = found_key.offset + btrfs_file_extent_ram_bytes(path->nodes[0], fi_item); /* * existing file extent * |--------| |----| * |-------| * offset + len * OR * |---------------| * |-------| */ if (end > offset) { not_found = 0; goto out; } check_next: ret = btrfs_next_item(root, path); if (ret) goto out; btrfs_item_key_to_cpu(path->nodes[0], &found_key, path->slots[0]); if (found_key.objectid != ino || found_key.type != BTRFS_EXTENT_DATA_KEY) { ret = 1; goto out; } if (found_key.offset < offset + len) /* * existing file extent * |---| |------| * |-------| * offset + len */ not_found = 0; else /* * existing file extent * |----| |----| * |----| * offset + len */ not_found = 1; /* * To keep the search hehavior consistent with search_slot(), * we need to go back to the prev leaf's nritem slot if * we are at the first slot of the leaf. */ if (path->slots[0] == 0) { ret = btrfs_prev_leaf(root, path); /* Not possible */ if (ret) goto out; path->slots[0] = btrfs_header_nritems(path->nodes[0]); } out: if (ret == 0) ret = not_found; return ret; }