/* * this is very complex, but the basic idea is to drop all extents * in the range start - end. hint_block is filled in with a block number * that would be a good hint to the block allocator for this file. * * If an extent intersects the range but is not entirely inside the range * it is either truncated or split. Anything entirely inside the range * is deleted from the tree. */ int btrfs_drop_extents(struct btrfs_trans_handle *trans, struct inode *inode, u64 start, u64 end, u64 *hint_byte, int drop_cache) { struct btrfs_root *root = BTRFS_I(inode)->root; struct extent_buffer *leaf; struct btrfs_file_extent_item *fi; struct btrfs_path *path; struct btrfs_key key; struct btrfs_key new_key; u64 search_start = start; u64 disk_bytenr = 0; u64 num_bytes = 0; u64 extent_offset = 0; u64 extent_end = 0; int del_nr = 0; int del_slot = 0; int extent_type; int recow; int ret; if (drop_cache) btrfs_drop_extent_cache(inode, start, end - 1, 0); path = btrfs_alloc_path(); if (!path) return -ENOMEM; while (1) { recow = 0; ret = btrfs_lookup_file_extent(trans, root, path, inode->i_ino, search_start, -1); if (ret < 0) break; if (ret > 0 && path->slots[0] > 0 && search_start == start) { leaf = path->nodes[0]; btrfs_item_key_to_cpu(leaf, &key, path->slots[0] - 1); if (key.objectid == inode->i_ino && key.type == BTRFS_EXTENT_DATA_KEY) path->slots[0]--; } ret = 0; next_slot: leaf = path->nodes[0]; if (path->slots[0] >= btrfs_header_nritems(leaf)) { BUG_ON(del_nr > 0); ret = btrfs_next_leaf(root, path); if (ret < 0) break; if (ret > 0) { ret = 0; break; } leaf = path->nodes[0]; recow = 1; } btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); if (key.objectid > inode->i_ino || key.type > BTRFS_EXTENT_DATA_KEY || key.offset >= end) break; fi = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_file_extent_item); extent_type = btrfs_file_extent_type(leaf, fi); if (extent_type == BTRFS_FILE_EXTENT_REG || extent_type == BTRFS_FILE_EXTENT_PREALLOC) { disk_bytenr = btrfs_file_extent_disk_bytenr(leaf, fi); num_bytes = btrfs_file_extent_disk_num_bytes(leaf, fi); extent_offset = btrfs_file_extent_offset(leaf, fi); extent_end = key.offset + btrfs_file_extent_num_bytes(leaf, fi); } else if (extent_type == BTRFS_FILE_EXTENT_INLINE) { extent_end = key.offset + btrfs_file_extent_inline_len(leaf, fi); } else { WARN_ON(1); extent_end = search_start; } if (extent_end <= search_start) { path->slots[0]++; goto next_slot; } search_start = max(key.offset, start); if (recow) { btrfs_release_path(root, path); continue; } /* * | - range to drop - | * | -------- extent -------- | */ if (start > key.offset && end < extent_end) { BUG_ON(del_nr > 0); BUG_ON(extent_type == BTRFS_FILE_EXTENT_INLINE); memcpy(&new_key, &key, sizeof(new_key)); new_key.offset = start; ret = btrfs_duplicate_item(trans, root, path, &new_key); if (ret == -EAGAIN) { btrfs_release_path(root, path); continue; } if (ret < 0) break; leaf = path->nodes[0]; fi = btrfs_item_ptr(leaf, path->slots[0] - 1, struct btrfs_file_extent_item); btrfs_set_file_extent_num_bytes(leaf, fi, start - key.offset); fi = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_file_extent_item); extent_offset += start - key.offset; btrfs_set_file_extent_offset(leaf, fi, extent_offset); btrfs_set_file_extent_num_bytes(leaf, fi, extent_end - start); btrfs_mark_buffer_dirty(leaf); if (disk_bytenr > 0) { ret = btrfs_inc_extent_ref(trans, root, disk_bytenr, num_bytes, 0, root->root_key.objectid, new_key.objectid, start - extent_offset); BUG_ON(ret); *hint_byte = disk_bytenr; } key.offset = start; } /* * | ---- range to drop ----- | * | -------- extent -------- | */ if (start <= key.offset && end < extent_end) { BUG_ON(extent_type == BTRFS_FILE_EXTENT_INLINE); memcpy(&new_key, &key, sizeof(new_key)); new_key.offset = end; btrfs_set_item_key_safe(trans, root, path, &new_key); extent_offset += end - key.offset; btrfs_set_file_extent_offset(leaf, fi, extent_offset); btrfs_set_file_extent_num_bytes(leaf, fi, extent_end - end); btrfs_mark_buffer_dirty(leaf); if (disk_bytenr > 0) { inode_sub_bytes(inode, end - key.offset); *hint_byte = disk_bytenr; } break; } search_start = extent_end; /* * | ---- range to drop ----- | * | -------- extent -------- | */ if (start > key.offset && end >= extent_end) { BUG_ON(del_nr > 0); BUG_ON(extent_type == BTRFS_FILE_EXTENT_INLINE); btrfs_set_file_extent_num_bytes(leaf, fi, start - key.offset); btrfs_mark_buffer_dirty(leaf); if (disk_bytenr > 0) { inode_sub_bytes(inode, extent_end - start); *hint_byte = disk_bytenr; } if (end == extent_end) break; path->slots[0]++; goto next_slot; } /* * | ---- range to drop ----- | * | ------ extent ------ | */ if (start <= key.offset && end >= extent_end) { if (del_nr == 0) { del_slot = path->slots[0]; del_nr = 1; } else { BUG_ON(del_slot + del_nr != path->slots[0]); del_nr++; } if (extent_type == BTRFS_FILE_EXTENT_INLINE) { inode_sub_bytes(inode, extent_end - key.offset); extent_end = ALIGN(extent_end, root->sectorsize); } else if (disk_bytenr > 0) { ret = btrfs_free_extent(trans, root, disk_bytenr, num_bytes, 0, root->root_key.objectid, key.objectid, key.offset - extent_offset); BUG_ON(ret); inode_sub_bytes(inode, extent_end - key.offset); *hint_byte = disk_bytenr; } if (end == extent_end) break; if (path->slots[0] + 1 < btrfs_header_nritems(leaf)) { path->slots[0]++; goto next_slot; } ret = btrfs_del_items(trans, root, path, del_slot, del_nr); BUG_ON(ret); del_nr = 0; del_slot = 0; btrfs_release_path(root, path); continue; } BUG_ON(1); }
static noinline int test_btrfs_get_extent(u32 sectorsize, u32 nodesize) { struct btrfs_fs_info *fs_info = NULL; struct inode *inode = NULL; struct btrfs_root *root = NULL; struct extent_map *em = NULL; u64 orig_start; u64 disk_bytenr; u64 offset; int ret = -ENOMEM; inode = btrfs_new_test_inode(); if (!inode) { test_msg("Couldn't allocate inode\n"); return ret; } BTRFS_I(inode)->location.type = BTRFS_INODE_ITEM_KEY; BTRFS_I(inode)->location.objectid = BTRFS_FIRST_FREE_OBJECTID; BTRFS_I(inode)->location.offset = 0; fs_info = btrfs_alloc_dummy_fs_info(); if (!fs_info) { test_msg("Couldn't allocate dummy fs info\n"); goto out; } root = btrfs_alloc_dummy_root(fs_info, sectorsize, nodesize); if (IS_ERR(root)) { test_msg("Couldn't allocate root\n"); goto out; } root->node = alloc_dummy_extent_buffer(NULL, nodesize, nodesize); if (!root->node) { test_msg("Couldn't allocate dummy buffer\n"); goto out; } /* * We will just free a dummy node if it's ref count is 2 so we need an * extra ref so our searches don't accidentally release our page. */ extent_buffer_get(root->node); btrfs_set_header_nritems(root->node, 0); btrfs_set_header_level(root->node, 0); ret = -EINVAL; /* First with no extents */ BTRFS_I(inode)->root = root; em = btrfs_get_extent(inode, NULL, 0, 0, sectorsize, 0); if (IS_ERR(em)) { em = NULL; test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start != EXTENT_MAP_HOLE) { test_msg("Expected a hole, got %llu\n", em->block_start); goto out; } if (!test_bit(EXTENT_FLAG_VACANCY, &em->flags)) { test_msg("Vacancy flag wasn't set properly\n"); goto out; } free_extent_map(em); btrfs_drop_extent_cache(inode, 0, (u64)-1, 0); /* * All of the magic numbers are based on the mapping setup in * setup_file_extents, so if you change anything there you need to * update the comment and update the expected values below. */ setup_file_extents(root, sectorsize); em = btrfs_get_extent(inode, NULL, 0, 0, (u64)-1, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start != EXTENT_MAP_HOLE) { test_msg("Expected a hole, got %llu\n", em->block_start); goto out; } if (em->start != 0 || em->len != 5) { test_msg("Unexpected extent wanted start 0 len 5, got start " "%llu len %llu\n", em->start, em->len); goto out; } if (em->flags != 0) { test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); goto out; } offset = em->start + em->len; free_extent_map(em); em = btrfs_get_extent(inode, NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start != EXTENT_MAP_INLINE) { test_msg("Expected an inline, got %llu\n", em->block_start); goto out; } if (em->start != offset || em->len != (sectorsize - 5)) { test_msg("Unexpected extent wanted start %llu len 1, got start " "%llu len %llu\n", offset, em->start, em->len); goto out; } if (em->flags != 0) { test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); goto out; } /* * We don't test anything else for inline since it doesn't get set * unless we have a page for it to write into. Maybe we should change * this? */ offset = em->start + em->len; free_extent_map(em); em = btrfs_get_extent(inode, NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start != EXTENT_MAP_HOLE) { test_msg("Expected a hole, got %llu\n", em->block_start); goto out; } if (em->start != offset || em->len != 4) { test_msg("Unexpected extent wanted start %llu len 4, got start " "%llu len %llu\n", offset, em->start, em->len); goto out; } if (em->flags != 0) { test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); goto out; } offset = em->start + em->len; free_extent_map(em); /* Regular extent */ em = btrfs_get_extent(inode, NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start >= EXTENT_MAP_LAST_BYTE) { test_msg("Expected a real extent, got %llu\n", em->block_start); goto out; } if (em->start != offset || em->len != sectorsize - 1) { test_msg("Unexpected extent wanted start %llu len 4095, got " "start %llu len %llu\n", offset, em->start, em->len); goto out; } if (em->flags != 0) { test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); goto out; } if (em->orig_start != em->start) { test_msg("Wrong orig offset, want %llu, have %llu\n", em->start, em->orig_start); goto out; } offset = em->start + em->len; free_extent_map(em); /* The next 3 are split extents */ em = btrfs_get_extent(inode, NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start >= EXTENT_MAP_LAST_BYTE) { test_msg("Expected a real extent, got %llu\n", em->block_start); goto out; } if (em->start != offset || em->len != sectorsize) { test_msg("Unexpected extent start %llu len %u, " "got start %llu len %llu\n", offset, sectorsize, em->start, em->len); goto out; } if (em->flags != 0) { test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); goto out; } if (em->orig_start != em->start) { test_msg("Wrong orig offset, want %llu, have %llu\n", em->start, em->orig_start); goto out; } disk_bytenr = em->block_start; orig_start = em->start; offset = em->start + em->len; free_extent_map(em); em = btrfs_get_extent(inode, NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start != EXTENT_MAP_HOLE) { test_msg("Expected a hole, got %llu\n", em->block_start); goto out; } if (em->start != offset || em->len != sectorsize) { test_msg("Unexpected extent wanted start %llu len %u, " "got start %llu len %llu\n", offset, sectorsize, em->start, em->len); goto out; } if (em->flags != 0) { test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); goto out; } offset = em->start + em->len; free_extent_map(em); em = btrfs_get_extent(inode, NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start >= EXTENT_MAP_LAST_BYTE) { test_msg("Expected a real extent, got %llu\n", em->block_start); goto out; } if (em->start != offset || em->len != 2 * sectorsize) { test_msg("Unexpected extent wanted start %llu len %u, " "got start %llu len %llu\n", offset, 2 * sectorsize, em->start, em->len); goto out; } if (em->flags != 0) { test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); goto out; } if (em->orig_start != orig_start) { test_msg("Wrong orig offset, want %llu, have %llu\n", orig_start, em->orig_start); goto out; } disk_bytenr += (em->start - orig_start); if (em->block_start != disk_bytenr) { test_msg("Wrong block start, want %llu, have %llu\n", disk_bytenr, em->block_start); goto out; } offset = em->start + em->len; free_extent_map(em); /* Prealloc extent */ em = btrfs_get_extent(inode, NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start >= EXTENT_MAP_LAST_BYTE) { test_msg("Expected a real extent, got %llu\n", em->block_start); goto out; } if (em->start != offset || em->len != sectorsize) { test_msg("Unexpected extent wanted start %llu len %u, " "got start %llu len %llu\n", offset, sectorsize, em->start, em->len); goto out; } if (em->flags != prealloc_only) { test_msg("Unexpected flags set, want %lu have %lu\n", prealloc_only, em->flags); goto out; } if (em->orig_start != em->start) { test_msg("Wrong orig offset, want %llu, have %llu\n", em->start, em->orig_start); goto out; } offset = em->start + em->len; free_extent_map(em); /* The next 3 are a half written prealloc extent */ em = btrfs_get_extent(inode, NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start >= EXTENT_MAP_LAST_BYTE) { test_msg("Expected a real extent, got %llu\n", em->block_start); goto out; } if (em->start != offset || em->len != sectorsize) { test_msg("Unexpected extent wanted start %llu len %u, " "got start %llu len %llu\n", offset, sectorsize, em->start, em->len); goto out; } if (em->flags != prealloc_only) { test_msg("Unexpected flags set, want %lu have %lu\n", prealloc_only, em->flags); goto out; } if (em->orig_start != em->start) { test_msg("Wrong orig offset, want %llu, have %llu\n", em->start, em->orig_start); goto out; } disk_bytenr = em->block_start; orig_start = em->start; offset = em->start + em->len; free_extent_map(em); em = btrfs_get_extent(inode, NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start >= EXTENT_MAP_HOLE) { test_msg("Expected a real extent, got %llu\n", em->block_start); goto out; } if (em->start != offset || em->len != sectorsize) { test_msg("Unexpected extent wanted start %llu len %u, " "got start %llu len %llu\n", offset, sectorsize, em->start, em->len); goto out; } if (em->flags != 0) { test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); goto out; } if (em->orig_start != orig_start) { test_msg("Unexpected orig offset, wanted %llu, have %llu\n", orig_start, em->orig_start); goto out; } if (em->block_start != (disk_bytenr + (em->start - em->orig_start))) { test_msg("Unexpected block start, wanted %llu, have %llu\n", disk_bytenr + (em->start - em->orig_start), em->block_start); goto out; } offset = em->start + em->len; free_extent_map(em); em = btrfs_get_extent(inode, NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start >= EXTENT_MAP_LAST_BYTE) { test_msg("Expected a real extent, got %llu\n", em->block_start); goto out; } if (em->start != offset || em->len != 2 * sectorsize) { test_msg("Unexpected extent wanted start %llu len %u, " "got start %llu len %llu\n", offset, 2 * sectorsize, em->start, em->len); goto out; } if (em->flags != prealloc_only) { test_msg("Unexpected flags set, want %lu have %lu\n", prealloc_only, em->flags); goto out; } if (em->orig_start != orig_start) { test_msg("Wrong orig offset, want %llu, have %llu\n", orig_start, em->orig_start); goto out; } if (em->block_start != (disk_bytenr + (em->start - em->orig_start))) { test_msg("Unexpected block start, wanted %llu, have %llu\n", disk_bytenr + (em->start - em->orig_start), em->block_start); goto out; } offset = em->start + em->len; free_extent_map(em); /* Now for the compressed extent */ em = btrfs_get_extent(inode, NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start >= EXTENT_MAP_LAST_BYTE) { test_msg("Expected a real extent, got %llu\n", em->block_start); goto out; } if (em->start != offset || em->len != 2 * sectorsize) { test_msg("Unexpected extent wanted start %llu len %u," "got start %llu len %llu\n", offset, 2 * sectorsize, em->start, em->len); goto out; } if (em->flags != compressed_only) { test_msg("Unexpected flags set, want %lu have %lu\n", compressed_only, em->flags); goto out; } if (em->orig_start != em->start) { test_msg("Wrong orig offset, want %llu, have %llu\n", em->start, em->orig_start); goto out; } if (em->compress_type != BTRFS_COMPRESS_ZLIB) { test_msg("Unexpected compress type, wanted %d, got %d\n", BTRFS_COMPRESS_ZLIB, em->compress_type); goto out; } offset = em->start + em->len; free_extent_map(em); /* Split compressed extent */ em = btrfs_get_extent(inode, NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start >= EXTENT_MAP_LAST_BYTE) { test_msg("Expected a real extent, got %llu\n", em->block_start); goto out; } if (em->start != offset || em->len != sectorsize) { test_msg("Unexpected extent wanted start %llu len %u," "got start %llu len %llu\n", offset, sectorsize, em->start, em->len); goto out; } if (em->flags != compressed_only) { test_msg("Unexpected flags set, want %lu have %lu\n", compressed_only, em->flags); goto out; } if (em->orig_start != em->start) { test_msg("Wrong orig offset, want %llu, have %llu\n", em->start, em->orig_start); goto out; } if (em->compress_type != BTRFS_COMPRESS_ZLIB) { test_msg("Unexpected compress type, wanted %d, got %d\n", BTRFS_COMPRESS_ZLIB, em->compress_type); goto out; } disk_bytenr = em->block_start; orig_start = em->start; offset = em->start + em->len; free_extent_map(em); em = btrfs_get_extent(inode, NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start >= EXTENT_MAP_LAST_BYTE) { test_msg("Expected a real extent, got %llu\n", em->block_start); goto out; } if (em->start != offset || em->len != sectorsize) { test_msg("Unexpected extent wanted start %llu len %u, " "got start %llu len %llu\n", offset, sectorsize, em->start, em->len); goto out; } if (em->flags != 0) { test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); goto out; } if (em->orig_start != em->start) { test_msg("Wrong orig offset, want %llu, have %llu\n", em->start, em->orig_start); goto out; } offset = em->start + em->len; free_extent_map(em); em = btrfs_get_extent(inode, NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start != disk_bytenr) { test_msg("Block start does not match, want %llu got %llu\n", disk_bytenr, em->block_start); goto out; } if (em->start != offset || em->len != 2 * sectorsize) { test_msg("Unexpected extent wanted start %llu len %u, " "got start %llu len %llu\n", offset, 2 * sectorsize, em->start, em->len); goto out; } if (em->flags != compressed_only) { test_msg("Unexpected flags set, want %lu have %lu\n", compressed_only, em->flags); goto out; } if (em->orig_start != orig_start) { test_msg("Wrong orig offset, want %llu, have %llu\n", em->start, orig_start); goto out; } if (em->compress_type != BTRFS_COMPRESS_ZLIB) { test_msg("Unexpected compress type, wanted %d, got %d\n", BTRFS_COMPRESS_ZLIB, em->compress_type); goto out; } offset = em->start + em->len; free_extent_map(em); /* A hole between regular extents but no hole extent */ em = btrfs_get_extent(inode, NULL, 0, offset + 6, sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start >= EXTENT_MAP_LAST_BYTE) { test_msg("Expected a real extent, got %llu\n", em->block_start); goto out; } if (em->start != offset || em->len != sectorsize) { test_msg("Unexpected extent wanted start %llu len %u, " "got start %llu len %llu\n", offset, sectorsize, em->start, em->len); goto out; } if (em->flags != 0) { test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); goto out; } if (em->orig_start != em->start) { test_msg("Wrong orig offset, want %llu, have %llu\n", em->start, em->orig_start); goto out; } offset = em->start + em->len; free_extent_map(em); em = btrfs_get_extent(inode, NULL, 0, offset, 4096 * 1024, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start != EXTENT_MAP_HOLE) { test_msg("Expected a hole extent, got %llu\n", em->block_start); goto out; } /* * Currently we just return a length that we requested rather than the * length of the actual hole, if this changes we'll have to change this * test. */ if (em->start != offset || em->len != 3 * sectorsize) { test_msg("Unexpected extent wanted start %llu len %u, " "got start %llu len %llu\n", offset, 3 * sectorsize, em->start, em->len); goto out; } if (em->flags != vacancy_only) { test_msg("Unexpected flags set, want %lu have %lu\n", vacancy_only, em->flags); goto out; } if (em->orig_start != em->start) { test_msg("Wrong orig offset, want %llu, have %llu\n", em->start, em->orig_start); goto out; } offset = em->start + em->len; free_extent_map(em); em = btrfs_get_extent(inode, NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start >= EXTENT_MAP_LAST_BYTE) { test_msg("Expected a real extent, got %llu\n", em->block_start); goto out; } if (em->start != offset || em->len != sectorsize) { test_msg("Unexpected extent wanted start %llu len %u," "got start %llu len %llu\n", offset, sectorsize, em->start, em->len); goto out; } if (em->flags != 0) { test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); goto out; } if (em->orig_start != em->start) { test_msg("Wrong orig offset, want %llu, have %llu\n", em->start, em->orig_start); goto out; } ret = 0; out: if (!IS_ERR(em)) free_extent_map(em); iput(inode); btrfs_free_dummy_root(root); btrfs_free_dummy_fs_info(fs_info); return ret; }
/* * this is very complex, but the basic idea is to drop all extents * in the range start - end. hint_block is filled in with a block number * that would be a good hint to the block allocator for this file. * * If an extent intersects the range but is not entirely inside the range * it is either truncated or split. Anything entirely inside the range * is deleted from the tree. * * inline_limit is used to tell this code which offsets in the file to keep * if they contain inline extents. */ noinline int btrfs_drop_extents(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct inode *inode, u64 start, u64 end, u64 inline_limit, u64 *hint_byte) { u64 extent_end = 0; u64 locked_end = end; u64 search_start = start; u64 leaf_start; u64 ram_bytes = 0; u64 orig_parent = 0; u64 disk_bytenr = 0; u8 compression; u8 encryption; u16 other_encoding = 0; u64 root_gen; u64 root_owner; struct extent_buffer *leaf; struct btrfs_file_extent_item *extent; struct btrfs_path *path; struct btrfs_key key; struct btrfs_file_extent_item old; int keep; int slot; int bookend; int found_type = 0; int found_extent; int found_inline; int recow; int ret; inline_limit = 0; btrfs_drop_extent_cache(inode, start, end - 1, 0); path = btrfs_alloc_path(); if (!path) return -ENOMEM; while (1) { recow = 0; btrfs_release_path(root, path); ret = btrfs_lookup_file_extent(trans, root, path, inode->i_ino, search_start, -1); if (ret < 0) goto out; if (ret > 0) { if (path->slots[0] == 0) { ret = 0; goto out; } path->slots[0]--; } next_slot: keep = 0; bookend = 0; found_extent = 0; found_inline = 0; leaf_start = 0; root_gen = 0; root_owner = 0; compression = 0; encryption = 0; extent = NULL; leaf = path->nodes[0]; slot = path->slots[0]; ret = 0; btrfs_item_key_to_cpu(leaf, &key, slot); if (btrfs_key_type(&key) == BTRFS_EXTENT_DATA_KEY && key.offset >= end) { goto out; } if (btrfs_key_type(&key) > BTRFS_EXTENT_DATA_KEY || key.objectid != inode->i_ino) { goto out; } if (recow) { search_start = max(key.offset, start); continue; } if (btrfs_key_type(&key) == BTRFS_EXTENT_DATA_KEY) { extent = btrfs_item_ptr(leaf, slot, struct btrfs_file_extent_item); found_type = btrfs_file_extent_type(leaf, extent); compression = btrfs_file_extent_compression(leaf, extent); encryption = btrfs_file_extent_encryption(leaf, extent); other_encoding = btrfs_file_extent_other_encoding(leaf, extent); if (found_type == BTRFS_FILE_EXTENT_REG || found_type == BTRFS_FILE_EXTENT_PREALLOC) { extent_end = btrfs_file_extent_disk_bytenr(leaf, extent); if (extent_end) *hint_byte = extent_end; extent_end = key.offset + btrfs_file_extent_num_bytes(leaf, extent); ram_bytes = btrfs_file_extent_ram_bytes(leaf, extent); found_extent = 1; } else if (found_type == BTRFS_FILE_EXTENT_INLINE) { found_inline = 1; extent_end = key.offset + btrfs_file_extent_inline_len(leaf, extent); } } else {