diff --git a/common/extent-tree-utils.c b/common/extent-tree-utils.c index 9f7e543f3..135798b98 100644 --- a/common/extent-tree-utils.c +++ b/common/extent-tree-utils.c @@ -15,18 +15,13 @@ */ #include "kerncompat.h" -#include #include #include "kernel-shared/accessors.h" #include "kernel-shared/uapi/btrfs_tree.h" #include "kernel-shared/ctree.h" #include "kernel-shared/disk-io.h" -#include "kernel-shared/file-item.h" #include "kernel-shared/transaction.h" -#include "kernel-shared/free-space-tree.h" -#include "common/internal.h" #include "common/extent-tree-utils.h" -#include "common/messages.h" /* * Search in extent tree to found next meta/data extent. Caller needs to check @@ -50,238 +45,3 @@ int btrfs_next_extent_item(struct btrfs_root *root, struct btrfs_path *path, return 0; } } - -static void __get_extent_size(struct btrfs_root *root, struct btrfs_path *path, - u64 *start, u64 *len) -{ - struct btrfs_key key; - - btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); - BUG_ON(!(key.type == BTRFS_EXTENT_ITEM_KEY || - key.type == BTRFS_METADATA_ITEM_KEY)); - *start = key.objectid; - if (key.type == BTRFS_EXTENT_ITEM_KEY) - *len = key.offset; - else - *len = root->fs_info->nodesize; -} - -/* - * Find first overlap extent for range [bytenr, bytenr + len). - * - * Return 0 for found and point path to it. - * Return >0 for not found. - * Return <0 for err - */ -static int btrfs_search_overlap_extent(struct btrfs_root *root, - struct btrfs_path *path, u64 bytenr, u64 len) -{ - struct btrfs_key key; - u64 cur_start; - u64 cur_len; - int ret; - - key.objectid = bytenr; - key.type = BTRFS_EXTENT_DATA_KEY; - key.offset = (u64)-1; - - ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); - if (ret < 0) - return ret; - if (ret == 0) { - error_msg(ERROR_MSG_UNEXPECTED, "EXTENT_DATA found at %llu", bytenr); - return -EUCLEAN; - } - - ret = btrfs_previous_extent_item(root, path, 0); - if (ret < 0) - return ret; - /* No previous, check next extent. */ - if (ret > 0) - goto next; - __get_extent_size(root, path, &cur_start, &cur_len); - /* Tail overlap. */ - if (cur_start + cur_len > bytenr) - return 1; - -next: - ret = btrfs_next_extent_item(root, path, bytenr + len); - if (ret < 0) - return ret; - /* No next, prev already checked, no overlap. */ - if (ret > 0) - return 0; - __get_extent_size(root, path, &cur_start, &cur_len); - /* Head overlap.*/ - if (cur_start < bytenr + len) - return 1; - return 0; -} - -static int __btrfs_record_file_extent(struct btrfs_trans_handle *trans, - struct btrfs_root *root, u64 objectid, - struct btrfs_inode_item *inode, - u64 file_pos, u64 disk_bytenr, - u64 *ret_num_bytes) -{ - int ret; - struct btrfs_fs_info *info = root->fs_info; - struct btrfs_root *extent_root = btrfs_extent_root(info, disk_bytenr); - struct extent_buffer *leaf; - struct btrfs_file_extent_item *fi; - struct btrfs_key ins_key; - struct btrfs_path *path; - struct btrfs_extent_item *ei; - u64 nbytes; - u64 extent_num_bytes; - u64 extent_bytenr; - u64 extent_offset; - u64 num_bytes = *ret_num_bytes; - - /* - * @objectid should be an inode number, thus it must not be smaller - * than BTRFS_FIRST_FREE_OBJECTID. - */ - UASSERT(objectid >= BTRFS_FIRST_FREE_OBJECTID); - - /* - * All supported file system should not use its 0 extent. As it's for - * hole. And hole extent has no size limit, no need to loop. - */ - if (disk_bytenr == 0) { - ret = btrfs_insert_file_extent(trans, root, objectid, - file_pos, disk_bytenr, - num_bytes, num_bytes); - return ret; - } - num_bytes = min_t(u64, num_bytes, BTRFS_MAX_EXTENT_SIZE); - - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - - /* First to check extent overlap. */ - ret = btrfs_search_overlap_extent(extent_root, path, disk_bytenr, num_bytes); - if (ret < 0) - goto fail; - if (ret > 0) { - /* Found overlap. */ - u64 cur_start; - u64 cur_len; - - __get_extent_size(extent_root, path, &cur_start, &cur_len); - /* For convert case, this extent should be a subset of existing one. */ - if (disk_bytenr < cur_start) { - error_msg(ERROR_MSG_UNEXPECTED, - "invalid range disk_bytenr < cur_start: %llu < %llu", - disk_bytenr, cur_start); - ret = -EUCLEAN; - goto fail; - } - - extent_bytenr = cur_start; - extent_num_bytes = cur_len; - extent_offset = disk_bytenr - extent_bytenr; - } else { - /* No overlap, create new extent. */ - btrfs_release_path(path); - ins_key.objectid = disk_bytenr; - ins_key.type = BTRFS_EXTENT_ITEM_KEY; - ins_key.offset = num_bytes; - - ret = btrfs_insert_empty_item(trans, extent_root, path, - &ins_key, sizeof(*ei)); - if (ret == 0) { - leaf = path->nodes[0]; - ei = btrfs_item_ptr(leaf, path->slots[0], - struct btrfs_extent_item); - - btrfs_set_extent_refs(leaf, ei, 0); - btrfs_set_extent_generation(leaf, ei, trans->transid); - btrfs_set_extent_flags(leaf, ei, - BTRFS_EXTENT_FLAG_DATA); - btrfs_mark_buffer_dirty(leaf); - - ret = btrfs_update_block_group(trans, disk_bytenr, - num_bytes, 1, 0); - if (ret) - goto fail; - } else if (ret != -EEXIST) { - goto fail; - } - - ret = remove_from_free_space_tree(trans, disk_bytenr, num_bytes); - if (ret) - goto fail; - - btrfs_run_delayed_refs(trans, -1); - extent_bytenr = disk_bytenr; - extent_num_bytes = num_bytes; - extent_offset = 0; - } - btrfs_release_path(path); - ins_key.objectid = objectid; - ins_key.type = BTRFS_EXTENT_DATA_KEY; - ins_key.offset = file_pos; - ret = btrfs_insert_empty_item(trans, root, path, &ins_key, sizeof(*fi)); - if (ret) - goto fail; - leaf = path->nodes[0]; - fi = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_file_extent_item); - btrfs_set_file_extent_generation(leaf, fi, trans->transid); - btrfs_set_file_extent_type(leaf, fi, BTRFS_FILE_EXTENT_REG); - btrfs_set_file_extent_disk_bytenr(leaf, fi, extent_bytenr); - btrfs_set_file_extent_disk_num_bytes(leaf, fi, extent_num_bytes); - btrfs_set_file_extent_offset(leaf, fi, extent_offset); - btrfs_set_file_extent_num_bytes(leaf, fi, num_bytes); - btrfs_set_file_extent_ram_bytes(leaf, fi, extent_num_bytes); - btrfs_set_file_extent_compression(leaf, fi, 0); - btrfs_set_file_extent_encryption(leaf, fi, 0); - btrfs_set_file_extent_other_encoding(leaf, fi, 0); - btrfs_mark_buffer_dirty(leaf); - - nbytes = btrfs_stack_inode_nbytes(inode) + num_bytes; - btrfs_set_stack_inode_nbytes(inode, nbytes); - btrfs_release_path(path); - - ret = btrfs_inc_extent_ref(trans, extent_bytenr, extent_num_bytes, - 0, root->root_key.objectid, objectid, - file_pos - extent_offset); - if (ret) - goto fail; - ret = 0; - *ret_num_bytes = min(extent_num_bytes - extent_offset, num_bytes); -fail: - btrfs_free_path(path); - return ret; -} - -/* - * Record a file extent. Do all the required works, such as inserting file - * extent item, inserting extent item and backref item into extent tree and - * updating block accounting. - */ -int btrfs_record_file_extent(struct btrfs_trans_handle *trans, - struct btrfs_root *root, u64 objectid, - struct btrfs_inode_item *inode, - u64 file_pos, u64 disk_bytenr, - u64 num_bytes) -{ - u64 cur_disk_bytenr = disk_bytenr; - u64 cur_file_pos = file_pos; - u64 cur_num_bytes = num_bytes; - int ret = 0; - - while (num_bytes > 0) { - ret = __btrfs_record_file_extent(trans, root, objectid, - inode, cur_file_pos, - cur_disk_bytenr, - &cur_num_bytes); - if (ret < 0) - break; - cur_disk_bytenr += cur_num_bytes; - cur_file_pos += cur_num_bytes; - num_bytes -= cur_num_bytes; - } - return ret; -} diff --git a/common/extent-tree-utils.h b/common/extent-tree-utils.h index f03d9c438..e9d121345 100644 --- a/common/extent-tree-utils.h +++ b/common/extent-tree-utils.h @@ -27,10 +27,5 @@ struct btrfs_trans_handle; int btrfs_next_extent_item(struct btrfs_root *root, struct btrfs_path *path, u64 max_objectid); -int btrfs_record_file_extent(struct btrfs_trans_handle *trans, - struct btrfs_root *root, u64 objectid, - struct btrfs_inode_item *inode, - u64 file_pos, u64 disk_bytenr, - u64 num_bytes); #endif diff --git a/convert/common.c b/convert/common.c index b093fdb5f..802b809ca 100644 --- a/convert/common.c +++ b/convert/common.c @@ -27,10 +27,13 @@ #include "kernel-shared/disk-io.h" #include "kernel-shared/volumes.h" #include "kernel-shared/accessors.h" -#include "kernel-shared/uapi/btrfs_tree.h" +#include "kernel-shared/file-item.h" +#include "kernel-shared/transaction.h" +#include "kernel-shared/free-space-tree.h" #include "common/path-utils.h" #include "common/messages.h" #include "common/string-utils.h" +#include "common/extent-tree-utils.h" #include "common/fsfeatures.h" #include "mkfs/common.h" #include "convert/common.h" @@ -908,3 +911,235 @@ int make_convert_btrfs(int fd, struct btrfs_mkfs_config *cfg, return ret; } +static void __get_extent_size(struct btrfs_root *root, struct btrfs_path *path, + u64 *start, u64 *len) +{ + struct btrfs_key key; + + btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); + BUG_ON(!(key.type == BTRFS_EXTENT_ITEM_KEY || + key.type == BTRFS_METADATA_ITEM_KEY)); + *start = key.objectid; + if (key.type == BTRFS_EXTENT_ITEM_KEY) + *len = key.offset; + else + *len = root->fs_info->nodesize; +} + +/* + * Find first overlap extent for range [bytenr, bytenr + len). + * + * Return 0 for found and point path to it. + * Return >0 for not found. + * Return <0 for err + */ +static int btrfs_search_overlap_extent(struct btrfs_root *root, + struct btrfs_path *path, u64 bytenr, u64 len) +{ + struct btrfs_key key; + u64 cur_start; + u64 cur_len; + int ret; + + key.objectid = bytenr; + key.type = BTRFS_EXTENT_DATA_KEY; + key.offset = (u64)-1; + + ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); + if (ret < 0) + return ret; + if (ret == 0) { + error_msg(ERROR_MSG_UNEXPECTED, "EXTENT_DATA found at %llu", bytenr); + return -EUCLEAN; + } + + ret = btrfs_previous_extent_item(root, path, 0); + if (ret < 0) + return ret; + /* No previous, check next extent. */ + if (ret > 0) + goto next; + __get_extent_size(root, path, &cur_start, &cur_len); + /* Tail overlap. */ + if (cur_start + cur_len > bytenr) + return 1; + +next: + ret = btrfs_next_extent_item(root, path, bytenr + len); + if (ret < 0) + return ret; + /* No next, prev already checked, no overlap. */ + if (ret > 0) + return 0; + __get_extent_size(root, path, &cur_start, &cur_len); + /* Head overlap.*/ + if (cur_start < bytenr + len) + return 1; + return 0; +} + +static int __btrfs_convert_file_extent(struct btrfs_trans_handle *trans, + struct btrfs_root *root, u64 objectid, + struct btrfs_inode_item *inode, + u64 file_pos, u64 disk_bytenr, + u64 *ret_num_bytes) +{ + int ret; + struct btrfs_fs_info *info = root->fs_info; + struct btrfs_root *extent_root = btrfs_extent_root(info, disk_bytenr); + struct btrfs_file_extent_item stack_fi = { 0 }; + struct btrfs_key ins_key; + struct btrfs_path *path; + struct btrfs_extent_item *ei; + u64 nbytes; + u64 extent_num_bytes; + u64 extent_bytenr; + u64 extent_offset; + u64 num_bytes = *ret_num_bytes; + + /* + * @objectid should be an inode number, thus it must not be smaller + * than BTRFS_FIRST_FREE_OBJECTID. + */ + UASSERT(objectid >= BTRFS_FIRST_FREE_OBJECTID); + + /* + * All supported file system should not use its 0 extent. As it's for + * hole. And hole extent has no size limit, no need to loop. + */ + if (disk_bytenr == 0) { + btrfs_set_stack_file_extent_type(&stack_fi, BTRFS_FILE_EXTENT_REG); + btrfs_set_stack_file_extent_num_bytes(&stack_fi, num_bytes); + btrfs_set_stack_file_extent_ram_bytes(&stack_fi, num_bytes); + ret = btrfs_insert_file_extent(trans, root, objectid, + file_pos, &stack_fi); + return ret; + } + num_bytes = min_t(u64, num_bytes, BTRFS_MAX_EXTENT_SIZE); + + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + /* First to check extent overlap. */ + ret = btrfs_search_overlap_extent(extent_root, path, disk_bytenr, num_bytes); + if (ret < 0) + goto fail; + if (ret > 0) { + /* Found overlap. */ + u64 cur_start; + u64 cur_len; + + __get_extent_size(extent_root, path, &cur_start, &cur_len); + /* For convert case, this extent should be a subset of existing one. */ + if (disk_bytenr < cur_start) { + error_msg(ERROR_MSG_UNEXPECTED, + "invalid range disk_bytenr < cur_start: %llu < %llu", + disk_bytenr, cur_start); + ret = -EUCLEAN; + goto fail; + } + + extent_bytenr = cur_start; + extent_num_bytes = cur_len; + extent_offset = disk_bytenr - extent_bytenr; + } else { + /* No overlap, create new extent. */ + btrfs_release_path(path); + ins_key.objectid = disk_bytenr; + ins_key.type = BTRFS_EXTENT_ITEM_KEY; + ins_key.offset = num_bytes; + + ret = btrfs_insert_empty_item(trans, extent_root, path, + &ins_key, sizeof(*ei)); + if (ret == 0) { + struct extent_buffer *leaf; + + leaf = path->nodes[0]; + ei = btrfs_item_ptr(leaf, path->slots[0], + struct btrfs_extent_item); + + btrfs_set_extent_refs(leaf, ei, 0); + btrfs_set_extent_generation(leaf, ei, trans->transid); + btrfs_set_extent_flags(leaf, ei, + BTRFS_EXTENT_FLAG_DATA); + btrfs_mark_buffer_dirty(leaf); + + ret = btrfs_update_block_group(trans, disk_bytenr, + num_bytes, 1, 0); + if (ret) + goto fail; + } else if (ret != -EEXIST) { + goto fail; + } + + ret = remove_from_free_space_tree(trans, disk_bytenr, num_bytes); + if (ret) + goto fail; + + btrfs_run_delayed_refs(trans, -1); + extent_bytenr = disk_bytenr; + extent_num_bytes = num_bytes; + extent_offset = 0; + } + btrfs_release_path(path); + ins_key.objectid = objectid; + ins_key.type = BTRFS_EXTENT_DATA_KEY; + ins_key.offset = file_pos; + btrfs_set_stack_file_extent_type(&stack_fi, BTRFS_FILE_EXTENT_REG); + btrfs_set_stack_file_extent_disk_bytenr(&stack_fi, extent_bytenr); + btrfs_set_stack_file_extent_disk_num_bytes(&stack_fi, extent_num_bytes); + btrfs_set_stack_file_extent_offset(&stack_fi, extent_offset); + btrfs_set_stack_file_extent_num_bytes(&stack_fi, num_bytes); + btrfs_set_stack_file_extent_ram_bytes(&stack_fi, extent_num_bytes); + ret = btrfs_insert_file_extent(trans, root, objectid, file_pos, &stack_fi); + if (ret) + goto fail; + + nbytes = btrfs_stack_inode_nbytes(inode) + num_bytes; + btrfs_set_stack_inode_nbytes(inode, nbytes); + + ret = btrfs_inc_extent_ref(trans, extent_bytenr, extent_num_bytes, + 0, root->root_key.objectid, objectid, + file_pos - extent_offset); + if (ret) + goto fail; + ret = 0; + *ret_num_bytes = min(extent_num_bytes - extent_offset, num_bytes); +fail: + btrfs_free_path(path); + return ret; +} + +/* + * Insert file extent using converted image. Do all the required works, + * such as inserting file extent item, inserting extent item and backref item + * into extent tree and updating block accounting. + * + * This is for btrfs-convert only, thus it won't support compressed regular + * file extents. + */ +int btrfs_convert_file_extent(struct btrfs_trans_handle *trans, + struct btrfs_root *root, u64 objectid, + struct btrfs_inode_item *inode, + u64 file_pos, u64 disk_bytenr, + u64 num_bytes) +{ + u64 cur_disk_bytenr = disk_bytenr; + u64 cur_file_pos = file_pos; + u64 cur_num_bytes = num_bytes; + int ret = 0; + + while (num_bytes > 0) { + ret = __btrfs_convert_file_extent(trans, root, objectid, + inode, cur_file_pos, + cur_disk_bytenr, + &cur_num_bytes); + if (ret < 0) + break; + cur_disk_bytenr += cur_num_bytes; + cur_file_pos += cur_num_bytes; + num_bytes -= cur_num_bytes; + } + return ret; +} diff --git a/convert/common.h b/convert/common.h index 581ef3298..7678c1656 100644 --- a/convert/common.h +++ b/convert/common.h @@ -23,6 +23,7 @@ #define __BTRFS_CONVERT_COMMON_H__ #include "kerncompat.h" +#include "kernel-shared/uapi/btrfs_tree.h" #include "common/extent-cache.h" struct btrfs_mkfs_config; @@ -84,4 +85,9 @@ static inline u64 range_end(const struct simple_range *range) return (range->start + range->len); } +int btrfs_convert_file_extent(struct btrfs_trans_handle *trans, + struct btrfs_root *root, u64 objectid, + struct btrfs_inode_item *inode, + u64 file_pos, u64 disk_bytenr, + u64 num_bytes); #endif diff --git a/convert/main.c b/convert/main.c index a227cc6fe..805682705 100644 --- a/convert/main.c +++ b/convert/main.c @@ -337,7 +337,7 @@ static int create_image_file_range(struct btrfs_trans_handle *trans, error("remaining length not sectorsize aligned: %llu", len); return -EINVAL; } - ret = btrfs_record_file_extent(trans, root, ino, inode, bytenr, + ret = btrfs_convert_file_extent(trans, root, ino, inode, bytenr, disk_bytenr, len); if (ret < 0) return ret; @@ -426,8 +426,8 @@ static int migrate_one_reserved_range(struct btrfs_trans_handle *trans, break; /* Now handle extent item and file extent things */ - ret = btrfs_record_file_extent(trans, root, ino, inode, cur_off, - key.objectid, key.offset); + ret = btrfs_convert_file_extent(trans, root, ino, inode, cur_off, + key.objectid, key.offset); if (ret < 0) break; /* Finally, insert csum items */ @@ -438,7 +438,7 @@ static int migrate_one_reserved_range(struct btrfs_trans_handle *trans, /* Don't forget to insert hole */ hole_len = cur_off - hole_start; if (hole_len) { - ret = btrfs_record_file_extent(trans, root, ino, inode, + ret = btrfs_convert_file_extent(trans, root, ino, inode, hole_start, 0, hole_len); if (ret < 0) break; @@ -455,7 +455,7 @@ static int migrate_one_reserved_range(struct btrfs_trans_handle *trans, * | Hole | */ if (range_end(range) - hole_start > 0) - ret = btrfs_record_file_extent(trans, root, ino, inode, + ret = btrfs_convert_file_extent(trans, root, ino, inode, hole_start, 0, range_end(range) - hole_start); return ret; } diff --git a/convert/source-fs.c b/convert/source-fs.c index 97c989d9a..8a296bd9c 100644 --- a/convert/source-fs.c +++ b/convert/source-fs.c @@ -260,7 +260,7 @@ int record_file_blocks(struct blk_iterate_data *data, /* Hole, pass it to record_file_extent directly */ if (old_disk_bytenr == 0) - return btrfs_record_file_extent(data->trans, root, + return btrfs_convert_file_extent(data->trans, root, data->objectid, data->inode, file_pos, 0, num_bytes); @@ -314,7 +314,7 @@ int record_file_blocks(struct blk_iterate_data *data, real_disk_bytenr = 0; cur_len = min(key.offset + extent_num_bytes, old_disk_bytenr + num_bytes) - cur_off; - ret = btrfs_record_file_extent(data->trans, data->root, + ret = btrfs_convert_file_extent(data->trans, data->root, data->objectid, data->inode, file_pos, real_disk_bytenr, cur_len); if (ret < 0) diff --git a/convert/source-reiserfs.c b/convert/source-reiserfs.c index 3475b1527..32d60c09f 100644 --- a/convert/source-reiserfs.c +++ b/convert/source-reiserfs.c @@ -364,7 +364,7 @@ static int convert_direct(struct btrfs_trans_handle *trans, if (ret) return ret; - return btrfs_record_file_extent(trans, root, objectid, inode, offset, + return btrfs_convert_file_extent(trans, root, objectid, inode, offset, key.objectid, sectorsize); } diff --git a/kernel-shared/file-item.c b/kernel-shared/file-item.c index eb9024022..0de2a216c 100644 --- a/kernel-shared/file-item.c +++ b/kernel-shared/file-item.c @@ -33,55 +33,31 @@ size) - 1)) int btrfs_insert_file_extent(struct btrfs_trans_handle *trans, struct btrfs_root *root, - u64 objectid, u64 pos, u64 offset, - u64 disk_num_bytes, u64 num_bytes) + u64 ino, u64 file_pos, + struct btrfs_file_extent_item *stack_fi) { int ret = 0; int is_hole = 0; - struct btrfs_file_extent_item *item; struct btrfs_key file_key; - struct btrfs_path *path; - struct extent_buffer *leaf; - if (offset == 0) + if (btrfs_stack_file_extent_disk_bytenr(stack_fi) == 0) is_hole = 1; + /* For NO_HOLES, we don't insert hole file extent */ if (btrfs_fs_incompat(root->fs_info, NO_HOLES) && is_hole) return 0; /* For hole, its disk_bytenr and disk_num_bytes must be 0 */ if (is_hole) - disk_num_bytes = 0; - - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; + btrfs_set_stack_file_extent_disk_num_bytes(stack_fi, 0); - file_key.objectid = objectid; + file_key.objectid = ino; file_key.type = BTRFS_EXTENT_DATA_KEY; - file_key.offset = pos; + file_key.offset = file_pos; - ret = btrfs_insert_empty_item(trans, root, path, &file_key, - sizeof(*item)); - if (ret < 0) - goto out; - BUG_ON(ret); - leaf = path->nodes[0]; - item = btrfs_item_ptr(leaf, path->slots[0], - struct btrfs_file_extent_item); - btrfs_set_file_extent_disk_bytenr(leaf, item, offset); - btrfs_set_file_extent_disk_num_bytes(leaf, item, disk_num_bytes); - btrfs_set_file_extent_offset(leaf, item, 0); - btrfs_set_file_extent_num_bytes(leaf, item, num_bytes); - btrfs_set_file_extent_ram_bytes(leaf, item, num_bytes); - btrfs_set_file_extent_generation(leaf, item, trans->transid); - btrfs_set_file_extent_type(leaf, item, BTRFS_FILE_EXTENT_REG); - btrfs_set_file_extent_compression(leaf, item, 0); - btrfs_set_file_extent_encryption(leaf, item, 0); - btrfs_set_file_extent_other_encoding(leaf, item, 0); - btrfs_mark_buffer_dirty(leaf); -out: - btrfs_free_path(path); + btrfs_set_stack_file_extent_generation(stack_fi, trans->transid); + ret = btrfs_insert_item(trans, root, &file_key, stack_fi, + sizeof(struct btrfs_file_extent_item)); return ret; } diff --git a/kernel-shared/file-item.h b/kernel-shared/file-item.h index 2c1e17c99..cb38a1fc0 100644 --- a/kernel-shared/file-item.h +++ b/kernel-shared/file-item.h @@ -85,8 +85,8 @@ u64 btrfs_file_extent_end(const struct btrfs_path *path); */ int btrfs_insert_file_extent(struct btrfs_trans_handle *trans, struct btrfs_root *root, - u64 objectid, u64 pos, u64 offset, - u64 disk_num_bytes, u64 num_bytes); + u64 ino, u64 file_pos, + struct btrfs_file_extent_item *stack_fi); int btrfs_csum_file_block(struct btrfs_trans_handle *trans, u64 logical, u64 csum_objectid, u32 csum_type, const char *data); int btrfs_insert_inline_extent(struct btrfs_trans_handle *trans, diff --git a/kernel-shared/file.c b/kernel-shared/file.c index 414a01b3e..7ed1b6891 100644 --- a/kernel-shared/file.c +++ b/kernel-shared/file.c @@ -152,6 +152,7 @@ int btrfs_punch_hole(struct btrfs_trans_handle *trans, u64 ino, u64 offset, u64 len) { struct btrfs_path *path; + struct btrfs_file_extent_item stack_fi = { 0 }; int ret = 0; path = btrfs_alloc_path(); @@ -166,7 +167,10 @@ int btrfs_punch_hole(struct btrfs_trans_handle *trans, goto out; } - ret = btrfs_insert_file_extent(trans, root, ino, offset, 0, 0, len); + btrfs_set_stack_file_extent_type(&stack_fi, BTRFS_FILE_EXTENT_REG); + btrfs_set_stack_file_extent_num_bytes(&stack_fi, len); + btrfs_set_stack_file_extent_ram_bytes(&stack_fi, len); + ret = btrfs_insert_file_extent(trans, root, ino, offset, &stack_fi); out: btrfs_free_path(path); return ret; diff --git a/mkfs/rootdir.c b/mkfs/rootdir.c index ffe9aa1f9..5fa480886 100644 --- a/mkfs/rootdir.c +++ b/mkfs/rootdir.c @@ -36,6 +36,7 @@ #include "kernel-shared/disk-io.h" #include "kernel-shared/transaction.h" #include "kernel-shared/file-item.h" +#include "kernel-shared/free-space-tree.h" #include "common/internal.h" #include "common/messages.h" #include "common/utils.h" @@ -362,6 +363,98 @@ static int add_symbolic_link(struct btrfs_trans_handle *trans, return ret; } +static int insert_reserved_file_extent(struct btrfs_trans_handle *trans, + struct btrfs_root *root, u64 ino, + struct btrfs_inode_item *inode, + u64 file_pos, + struct btrfs_file_extent_item *stack_fi) +{ + struct btrfs_fs_info *fs_info = root->fs_info; + struct btrfs_root *extent_root; + struct extent_buffer *leaf; + struct btrfs_key ins_key; + struct btrfs_path *path; + struct btrfs_extent_item *ei; + u64 disk_bytenr = btrfs_stack_file_extent_disk_bytenr(stack_fi); + u64 disk_num_bytes = btrfs_stack_file_extent_disk_num_bytes(stack_fi); + u64 num_bytes = btrfs_stack_file_extent_num_bytes(stack_fi); + int ret; + + extent_root = btrfs_extent_root(fs_info, disk_bytenr); + /* + * @ino should be an inode number, thus it must not be smaller + * than BTRFS_FIRST_FREE_OBJECTID. + */ + UASSERT(ino >= BTRFS_FIRST_FREE_OBJECTID); + + /* The reserved data extent should never exceed the upper limit. */ + UASSERT(disk_num_bytes <= BTRFS_MAX_EXTENT_SIZE); + + /* + * All supported file system should not use its 0 extent. As it's for + * hole. And hole extent has no size limit, no need to loop. + */ + if (disk_bytenr == 0) + return btrfs_insert_file_extent(trans, root, ino, + file_pos, stack_fi); + + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + ins_key.objectid = disk_bytenr; + ins_key.type = BTRFS_EXTENT_ITEM_KEY; + ins_key.offset = num_bytes; + + /* Update extent tree. */ + ret = btrfs_insert_empty_item(trans, extent_root, path, + &ins_key, sizeof(*ei)); + if (ret == 0) { + leaf = path->nodes[0]; + ei = btrfs_item_ptr(leaf, path->slots[0], + struct btrfs_extent_item); + + btrfs_set_extent_refs(leaf, ei, 0); + btrfs_set_extent_generation(leaf, ei, trans->transid); + btrfs_set_extent_flags(leaf, ei, + BTRFS_EXTENT_FLAG_DATA); + btrfs_mark_buffer_dirty(leaf); + + ret = btrfs_update_block_group(trans, disk_bytenr, + num_bytes, 1, 0); + if (ret) + goto fail; + } else if (ret != -EEXIST) { + goto fail; + } + btrfs_release_path(path); + + ret = remove_from_free_space_tree(trans, disk_bytenr, num_bytes); + if (ret) + goto fail; + + btrfs_run_delayed_refs(trans, -1); + + ins_key.objectid = ino; + ins_key.type = BTRFS_EXTENT_DATA_KEY; + ins_key.offset = file_pos; + ret = btrfs_insert_file_extent(trans, root, ino, file_pos, stack_fi); + if (ret) + goto fail; + btrfs_set_stack_inode_nbytes(inode, + btrfs_stack_inode_nbytes(inode) + num_bytes); + + ret = btrfs_inc_extent_ref(trans, disk_bytenr, num_bytes, + 0, root->root_key.objectid, ino, + file_pos); + if (ret) + goto fail; + ret = 0; +fail: + btrfs_free_path(path); + return ret; +} + static int add_file_items(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_inode_item *btrfs_inode, u64 objectid, @@ -471,8 +564,15 @@ static int add_file_items(struct btrfs_trans_handle *trans, } if (bytes_read) { - ret = btrfs_record_file_extent(trans, root, objectid, - btrfs_inode, file_pos, first_block, cur_bytes); + struct btrfs_file_extent_item stack_fi = { 0 }; + + btrfs_set_stack_file_extent_type(&stack_fi, BTRFS_FILE_EXTENT_REG); + btrfs_set_stack_file_extent_disk_bytenr(&stack_fi, first_block); + btrfs_set_stack_file_extent_disk_num_bytes(&stack_fi, cur_bytes); + btrfs_set_stack_file_extent_num_bytes(&stack_fi, cur_bytes); + btrfs_set_stack_file_extent_ram_bytes(&stack_fi, cur_bytes); + ret = insert_reserved_file_extent(trans, root, objectid, + btrfs_inode, file_pos, &stack_fi); if (ret) goto end;