18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2007 Oracle. All rights reserved. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/bio.h> 78c2ecf20Sopenharmony_ci#include <linux/slab.h> 88c2ecf20Sopenharmony_ci#include <linux/pagemap.h> 98c2ecf20Sopenharmony_ci#include <linux/highmem.h> 108c2ecf20Sopenharmony_ci#include <linux/sched/mm.h> 118c2ecf20Sopenharmony_ci#include <crypto/hash.h> 128c2ecf20Sopenharmony_ci#include "ctree.h" 138c2ecf20Sopenharmony_ci#include "disk-io.h" 148c2ecf20Sopenharmony_ci#include "transaction.h" 158c2ecf20Sopenharmony_ci#include "volumes.h" 168c2ecf20Sopenharmony_ci#include "print-tree.h" 178c2ecf20Sopenharmony_ci#include "compression.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define __MAX_CSUM_ITEMS(r, size) ((unsigned long)(((BTRFS_LEAF_DATA_SIZE(r) - \ 208c2ecf20Sopenharmony_ci sizeof(struct btrfs_item) * 2) / \ 218c2ecf20Sopenharmony_ci size) - 1)) 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define MAX_CSUM_ITEMS(r, size) (min_t(u32, __MAX_CSUM_ITEMS(r, size), \ 248c2ecf20Sopenharmony_ci PAGE_SIZE)) 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/** 278c2ecf20Sopenharmony_ci * @inode - the inode we want to update the disk_i_size for 288c2ecf20Sopenharmony_ci * @new_i_size - the i_size we want to set to, 0 if we use i_size 298c2ecf20Sopenharmony_ci * 308c2ecf20Sopenharmony_ci * With NO_HOLES set this simply sets the disk_is_size to whatever i_size_read() 318c2ecf20Sopenharmony_ci * returns as it is perfectly fine with a file that has holes without hole file 328c2ecf20Sopenharmony_ci * extent items. 338c2ecf20Sopenharmony_ci * 348c2ecf20Sopenharmony_ci * However without NO_HOLES we need to only return the area that is contiguous 358c2ecf20Sopenharmony_ci * from the 0 offset of the file. Otherwise we could end up adjust i_size up 368c2ecf20Sopenharmony_ci * to an extent that has a gap in between. 378c2ecf20Sopenharmony_ci * 388c2ecf20Sopenharmony_ci * Finally new_i_size should only be set in the case of truncate where we're not 398c2ecf20Sopenharmony_ci * ready to use i_size_read() as the limiter yet. 408c2ecf20Sopenharmony_ci */ 418c2ecf20Sopenharmony_civoid btrfs_inode_safe_disk_i_size_write(struct inode *inode, u64 new_i_size) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci struct btrfs_fs_info *fs_info = BTRFS_I(inode)->root->fs_info; 448c2ecf20Sopenharmony_ci u64 start, end, i_size; 458c2ecf20Sopenharmony_ci int ret; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci i_size = new_i_size ?: i_size_read(inode); 488c2ecf20Sopenharmony_ci if (btrfs_fs_incompat(fs_info, NO_HOLES)) { 498c2ecf20Sopenharmony_ci BTRFS_I(inode)->disk_i_size = i_size; 508c2ecf20Sopenharmony_ci return; 518c2ecf20Sopenharmony_ci } 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci spin_lock(&BTRFS_I(inode)->lock); 548c2ecf20Sopenharmony_ci ret = find_contiguous_extent_bit(&BTRFS_I(inode)->file_extent_tree, 0, 558c2ecf20Sopenharmony_ci &start, &end, EXTENT_DIRTY); 568c2ecf20Sopenharmony_ci if (!ret && start == 0) 578c2ecf20Sopenharmony_ci i_size = min(i_size, end + 1); 588c2ecf20Sopenharmony_ci else 598c2ecf20Sopenharmony_ci i_size = 0; 608c2ecf20Sopenharmony_ci BTRFS_I(inode)->disk_i_size = i_size; 618c2ecf20Sopenharmony_ci spin_unlock(&BTRFS_I(inode)->lock); 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci/** 658c2ecf20Sopenharmony_ci * @inode - the inode we're modifying 668c2ecf20Sopenharmony_ci * @start - the start file offset of the file extent we've inserted 678c2ecf20Sopenharmony_ci * @len - the logical length of the file extent item 688c2ecf20Sopenharmony_ci * 698c2ecf20Sopenharmony_ci * Call when we are inserting a new file extent where there was none before. 708c2ecf20Sopenharmony_ci * Does not need to call this in the case where we're replacing an existing file 718c2ecf20Sopenharmony_ci * extent, however if not sure it's fine to call this multiple times. 728c2ecf20Sopenharmony_ci * 738c2ecf20Sopenharmony_ci * The start and len must match the file extent item, so thus must be sectorsize 748c2ecf20Sopenharmony_ci * aligned. 758c2ecf20Sopenharmony_ci */ 768c2ecf20Sopenharmony_ciint btrfs_inode_set_file_extent_range(struct btrfs_inode *inode, u64 start, 778c2ecf20Sopenharmony_ci u64 len) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci if (len == 0) 808c2ecf20Sopenharmony_ci return 0; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci ASSERT(IS_ALIGNED(start + len, inode->root->fs_info->sectorsize)); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci if (btrfs_fs_incompat(inode->root->fs_info, NO_HOLES)) 858c2ecf20Sopenharmony_ci return 0; 868c2ecf20Sopenharmony_ci return set_extent_bits(&inode->file_extent_tree, start, start + len - 1, 878c2ecf20Sopenharmony_ci EXTENT_DIRTY); 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci/** 918c2ecf20Sopenharmony_ci * @inode - the inode we're modifying 928c2ecf20Sopenharmony_ci * @start - the start file offset of the file extent we've inserted 938c2ecf20Sopenharmony_ci * @len - the logical length of the file extent item 948c2ecf20Sopenharmony_ci * 958c2ecf20Sopenharmony_ci * Called when we drop a file extent, for example when we truncate. Doesn't 968c2ecf20Sopenharmony_ci * need to be called for cases where we're replacing a file extent, like when 978c2ecf20Sopenharmony_ci * we've COWed a file extent. 988c2ecf20Sopenharmony_ci * 998c2ecf20Sopenharmony_ci * The start and len must match the file extent item, so thus must be sectorsize 1008c2ecf20Sopenharmony_ci * aligned. 1018c2ecf20Sopenharmony_ci */ 1028c2ecf20Sopenharmony_ciint btrfs_inode_clear_file_extent_range(struct btrfs_inode *inode, u64 start, 1038c2ecf20Sopenharmony_ci u64 len) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci if (len == 0) 1068c2ecf20Sopenharmony_ci return 0; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci ASSERT(IS_ALIGNED(start + len, inode->root->fs_info->sectorsize) || 1098c2ecf20Sopenharmony_ci len == (u64)-1); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci if (btrfs_fs_incompat(inode->root->fs_info, NO_HOLES)) 1128c2ecf20Sopenharmony_ci return 0; 1138c2ecf20Sopenharmony_ci return clear_extent_bit(&inode->file_extent_tree, start, 1148c2ecf20Sopenharmony_ci start + len - 1, EXTENT_DIRTY, 0, 0, NULL); 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic inline u32 max_ordered_sum_bytes(struct btrfs_fs_info *fs_info, 1188c2ecf20Sopenharmony_ci u16 csum_size) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci u32 ncsums = (PAGE_SIZE - sizeof(struct btrfs_ordered_sum)) / csum_size; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci return ncsums * fs_info->sectorsize; 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ciint btrfs_insert_file_extent(struct btrfs_trans_handle *trans, 1268c2ecf20Sopenharmony_ci struct btrfs_root *root, 1278c2ecf20Sopenharmony_ci u64 objectid, u64 pos, 1288c2ecf20Sopenharmony_ci u64 disk_offset, u64 disk_num_bytes, 1298c2ecf20Sopenharmony_ci u64 num_bytes, u64 offset, u64 ram_bytes, 1308c2ecf20Sopenharmony_ci u8 compression, u8 encryption, u16 other_encoding) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci int ret = 0; 1338c2ecf20Sopenharmony_ci struct btrfs_file_extent_item *item; 1348c2ecf20Sopenharmony_ci struct btrfs_key file_key; 1358c2ecf20Sopenharmony_ci struct btrfs_path *path; 1368c2ecf20Sopenharmony_ci struct extent_buffer *leaf; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci path = btrfs_alloc_path(); 1398c2ecf20Sopenharmony_ci if (!path) 1408c2ecf20Sopenharmony_ci return -ENOMEM; 1418c2ecf20Sopenharmony_ci file_key.objectid = objectid; 1428c2ecf20Sopenharmony_ci file_key.offset = pos; 1438c2ecf20Sopenharmony_ci file_key.type = BTRFS_EXTENT_DATA_KEY; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci path->leave_spinning = 1; 1468c2ecf20Sopenharmony_ci ret = btrfs_insert_empty_item(trans, root, path, &file_key, 1478c2ecf20Sopenharmony_ci sizeof(*item)); 1488c2ecf20Sopenharmony_ci if (ret < 0) 1498c2ecf20Sopenharmony_ci goto out; 1508c2ecf20Sopenharmony_ci BUG_ON(ret); /* Can't happen */ 1518c2ecf20Sopenharmony_ci leaf = path->nodes[0]; 1528c2ecf20Sopenharmony_ci item = btrfs_item_ptr(leaf, path->slots[0], 1538c2ecf20Sopenharmony_ci struct btrfs_file_extent_item); 1548c2ecf20Sopenharmony_ci btrfs_set_file_extent_disk_bytenr(leaf, item, disk_offset); 1558c2ecf20Sopenharmony_ci btrfs_set_file_extent_disk_num_bytes(leaf, item, disk_num_bytes); 1568c2ecf20Sopenharmony_ci btrfs_set_file_extent_offset(leaf, item, offset); 1578c2ecf20Sopenharmony_ci btrfs_set_file_extent_num_bytes(leaf, item, num_bytes); 1588c2ecf20Sopenharmony_ci btrfs_set_file_extent_ram_bytes(leaf, item, ram_bytes); 1598c2ecf20Sopenharmony_ci btrfs_set_file_extent_generation(leaf, item, trans->transid); 1608c2ecf20Sopenharmony_ci btrfs_set_file_extent_type(leaf, item, BTRFS_FILE_EXTENT_REG); 1618c2ecf20Sopenharmony_ci btrfs_set_file_extent_compression(leaf, item, compression); 1628c2ecf20Sopenharmony_ci btrfs_set_file_extent_encryption(leaf, item, encryption); 1638c2ecf20Sopenharmony_ci btrfs_set_file_extent_other_encoding(leaf, item, other_encoding); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci btrfs_mark_buffer_dirty(leaf); 1668c2ecf20Sopenharmony_ciout: 1678c2ecf20Sopenharmony_ci btrfs_free_path(path); 1688c2ecf20Sopenharmony_ci return ret; 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic struct btrfs_csum_item * 1728c2ecf20Sopenharmony_cibtrfs_lookup_csum(struct btrfs_trans_handle *trans, 1738c2ecf20Sopenharmony_ci struct btrfs_root *root, 1748c2ecf20Sopenharmony_ci struct btrfs_path *path, 1758c2ecf20Sopenharmony_ci u64 bytenr, int cow) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci struct btrfs_fs_info *fs_info = root->fs_info; 1788c2ecf20Sopenharmony_ci int ret; 1798c2ecf20Sopenharmony_ci struct btrfs_key file_key; 1808c2ecf20Sopenharmony_ci struct btrfs_key found_key; 1818c2ecf20Sopenharmony_ci struct btrfs_csum_item *item; 1828c2ecf20Sopenharmony_ci struct extent_buffer *leaf; 1838c2ecf20Sopenharmony_ci u64 csum_offset = 0; 1848c2ecf20Sopenharmony_ci u16 csum_size = btrfs_super_csum_size(fs_info->super_copy); 1858c2ecf20Sopenharmony_ci int csums_in_item; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci file_key.objectid = BTRFS_EXTENT_CSUM_OBJECTID; 1888c2ecf20Sopenharmony_ci file_key.offset = bytenr; 1898c2ecf20Sopenharmony_ci file_key.type = BTRFS_EXTENT_CSUM_KEY; 1908c2ecf20Sopenharmony_ci ret = btrfs_search_slot(trans, root, &file_key, path, 0, cow); 1918c2ecf20Sopenharmony_ci if (ret < 0) 1928c2ecf20Sopenharmony_ci goto fail; 1938c2ecf20Sopenharmony_ci leaf = path->nodes[0]; 1948c2ecf20Sopenharmony_ci if (ret > 0) { 1958c2ecf20Sopenharmony_ci ret = 1; 1968c2ecf20Sopenharmony_ci if (path->slots[0] == 0) 1978c2ecf20Sopenharmony_ci goto fail; 1988c2ecf20Sopenharmony_ci path->slots[0]--; 1998c2ecf20Sopenharmony_ci btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]); 2008c2ecf20Sopenharmony_ci if (found_key.type != BTRFS_EXTENT_CSUM_KEY) 2018c2ecf20Sopenharmony_ci goto fail; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci csum_offset = (bytenr - found_key.offset) >> 2048c2ecf20Sopenharmony_ci fs_info->sb->s_blocksize_bits; 2058c2ecf20Sopenharmony_ci csums_in_item = btrfs_item_size_nr(leaf, path->slots[0]); 2068c2ecf20Sopenharmony_ci csums_in_item /= csum_size; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci if (csum_offset == csums_in_item) { 2098c2ecf20Sopenharmony_ci ret = -EFBIG; 2108c2ecf20Sopenharmony_ci goto fail; 2118c2ecf20Sopenharmony_ci } else if (csum_offset > csums_in_item) { 2128c2ecf20Sopenharmony_ci goto fail; 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_csum_item); 2168c2ecf20Sopenharmony_ci item = (struct btrfs_csum_item *)((unsigned char *)item + 2178c2ecf20Sopenharmony_ci csum_offset * csum_size); 2188c2ecf20Sopenharmony_ci return item; 2198c2ecf20Sopenharmony_cifail: 2208c2ecf20Sopenharmony_ci if (ret > 0) 2218c2ecf20Sopenharmony_ci ret = -ENOENT; 2228c2ecf20Sopenharmony_ci return ERR_PTR(ret); 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ciint btrfs_lookup_file_extent(struct btrfs_trans_handle *trans, 2268c2ecf20Sopenharmony_ci struct btrfs_root *root, 2278c2ecf20Sopenharmony_ci struct btrfs_path *path, u64 objectid, 2288c2ecf20Sopenharmony_ci u64 offset, int mod) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci int ret; 2318c2ecf20Sopenharmony_ci struct btrfs_key file_key; 2328c2ecf20Sopenharmony_ci int ins_len = mod < 0 ? -1 : 0; 2338c2ecf20Sopenharmony_ci int cow = mod != 0; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci file_key.objectid = objectid; 2368c2ecf20Sopenharmony_ci file_key.offset = offset; 2378c2ecf20Sopenharmony_ci file_key.type = BTRFS_EXTENT_DATA_KEY; 2388c2ecf20Sopenharmony_ci ret = btrfs_search_slot(trans, root, &file_key, path, ins_len, cow); 2398c2ecf20Sopenharmony_ci return ret; 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci/** 2438c2ecf20Sopenharmony_ci * btrfs_lookup_bio_sums - Look up checksums for a bio. 2448c2ecf20Sopenharmony_ci * @inode: inode that the bio is for. 2458c2ecf20Sopenharmony_ci * @bio: bio to look up. 2468c2ecf20Sopenharmony_ci * @offset: Unless (u64)-1, look up checksums for this offset in the file. 2478c2ecf20Sopenharmony_ci * If (u64)-1, use the page offsets from the bio instead. 2488c2ecf20Sopenharmony_ci * @dst: Buffer of size nblocks * btrfs_super_csum_size() used to return 2498c2ecf20Sopenharmony_ci * checksum (nblocks = bio->bi_iter.bi_size / fs_info->sectorsize). If 2508c2ecf20Sopenharmony_ci * NULL, the checksum buffer is allocated and returned in 2518c2ecf20Sopenharmony_ci * btrfs_io_bio(bio)->csum instead. 2528c2ecf20Sopenharmony_ci * 2538c2ecf20Sopenharmony_ci * Return: BLK_STS_RESOURCE if allocating memory fails, BLK_STS_OK otherwise. 2548c2ecf20Sopenharmony_ci */ 2558c2ecf20Sopenharmony_ciblk_status_t btrfs_lookup_bio_sums(struct inode *inode, struct bio *bio, 2568c2ecf20Sopenharmony_ci u64 offset, u8 *dst) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb); 2598c2ecf20Sopenharmony_ci struct bio_vec bvec; 2608c2ecf20Sopenharmony_ci struct bvec_iter iter; 2618c2ecf20Sopenharmony_ci struct btrfs_csum_item *item = NULL; 2628c2ecf20Sopenharmony_ci struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree; 2638c2ecf20Sopenharmony_ci struct btrfs_path *path; 2648c2ecf20Sopenharmony_ci const bool page_offsets = (offset == (u64)-1); 2658c2ecf20Sopenharmony_ci u8 *csum; 2668c2ecf20Sopenharmony_ci u64 item_start_offset = 0; 2678c2ecf20Sopenharmony_ci u64 item_last_offset = 0; 2688c2ecf20Sopenharmony_ci u64 disk_bytenr; 2698c2ecf20Sopenharmony_ci u64 page_bytes_left; 2708c2ecf20Sopenharmony_ci u32 diff; 2718c2ecf20Sopenharmony_ci int nblocks; 2728c2ecf20Sopenharmony_ci int count = 0; 2738c2ecf20Sopenharmony_ci u16 csum_size = btrfs_super_csum_size(fs_info->super_copy); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci path = btrfs_alloc_path(); 2768c2ecf20Sopenharmony_ci if (!path) 2778c2ecf20Sopenharmony_ci return BLK_STS_RESOURCE; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci nblocks = bio->bi_iter.bi_size >> inode->i_sb->s_blocksize_bits; 2808c2ecf20Sopenharmony_ci if (!dst) { 2818c2ecf20Sopenharmony_ci struct btrfs_io_bio *btrfs_bio = btrfs_io_bio(bio); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci if (nblocks * csum_size > BTRFS_BIO_INLINE_CSUM_SIZE) { 2848c2ecf20Sopenharmony_ci btrfs_bio->csum = kmalloc_array(nblocks, csum_size, 2858c2ecf20Sopenharmony_ci GFP_NOFS); 2868c2ecf20Sopenharmony_ci if (!btrfs_bio->csum) { 2878c2ecf20Sopenharmony_ci btrfs_free_path(path); 2888c2ecf20Sopenharmony_ci return BLK_STS_RESOURCE; 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci } else { 2918c2ecf20Sopenharmony_ci btrfs_bio->csum = btrfs_bio->csum_inline; 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci csum = btrfs_bio->csum; 2948c2ecf20Sopenharmony_ci } else { 2958c2ecf20Sopenharmony_ci csum = dst; 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci if (bio->bi_iter.bi_size > PAGE_SIZE * 8) 2998c2ecf20Sopenharmony_ci path->reada = READA_FORWARD; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci /* 3028c2ecf20Sopenharmony_ci * the free space stuff is only read when it hasn't been 3038c2ecf20Sopenharmony_ci * updated in the current transaction. So, we can safely 3048c2ecf20Sopenharmony_ci * read from the commit root and sidestep a nasty deadlock 3058c2ecf20Sopenharmony_ci * between reading the free space cache and updating the csum tree. 3068c2ecf20Sopenharmony_ci */ 3078c2ecf20Sopenharmony_ci if (btrfs_is_free_space_inode(BTRFS_I(inode))) { 3088c2ecf20Sopenharmony_ci path->search_commit_root = 1; 3098c2ecf20Sopenharmony_ci path->skip_locking = 1; 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci disk_bytenr = (u64)bio->bi_iter.bi_sector << 9; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci bio_for_each_segment(bvec, bio, iter) { 3158c2ecf20Sopenharmony_ci page_bytes_left = bvec.bv_len; 3168c2ecf20Sopenharmony_ci if (count) 3178c2ecf20Sopenharmony_ci goto next; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci if (page_offsets) 3208c2ecf20Sopenharmony_ci offset = page_offset(bvec.bv_page) + bvec.bv_offset; 3218c2ecf20Sopenharmony_ci count = btrfs_find_ordered_sum(BTRFS_I(inode), offset, 3228c2ecf20Sopenharmony_ci disk_bytenr, csum, nblocks); 3238c2ecf20Sopenharmony_ci if (count) 3248c2ecf20Sopenharmony_ci goto found; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci if (!item || disk_bytenr < item_start_offset || 3278c2ecf20Sopenharmony_ci disk_bytenr >= item_last_offset) { 3288c2ecf20Sopenharmony_ci struct btrfs_key found_key; 3298c2ecf20Sopenharmony_ci u32 item_size; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci if (item) 3328c2ecf20Sopenharmony_ci btrfs_release_path(path); 3338c2ecf20Sopenharmony_ci item = btrfs_lookup_csum(NULL, fs_info->csum_root, 3348c2ecf20Sopenharmony_ci path, disk_bytenr, 0); 3358c2ecf20Sopenharmony_ci if (IS_ERR(item)) { 3368c2ecf20Sopenharmony_ci count = 1; 3378c2ecf20Sopenharmony_ci memset(csum, 0, csum_size); 3388c2ecf20Sopenharmony_ci if (BTRFS_I(inode)->root->root_key.objectid == 3398c2ecf20Sopenharmony_ci BTRFS_DATA_RELOC_TREE_OBJECTID) { 3408c2ecf20Sopenharmony_ci set_extent_bits(io_tree, offset, 3418c2ecf20Sopenharmony_ci offset + fs_info->sectorsize - 1, 3428c2ecf20Sopenharmony_ci EXTENT_NODATASUM); 3438c2ecf20Sopenharmony_ci } else { 3448c2ecf20Sopenharmony_ci btrfs_info_rl(fs_info, 3458c2ecf20Sopenharmony_ci "no csum found for inode %llu start %llu", 3468c2ecf20Sopenharmony_ci btrfs_ino(BTRFS_I(inode)), offset); 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci item = NULL; 3498c2ecf20Sopenharmony_ci btrfs_release_path(path); 3508c2ecf20Sopenharmony_ci goto found; 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci btrfs_item_key_to_cpu(path->nodes[0], &found_key, 3538c2ecf20Sopenharmony_ci path->slots[0]); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci item_start_offset = found_key.offset; 3568c2ecf20Sopenharmony_ci item_size = btrfs_item_size_nr(path->nodes[0], 3578c2ecf20Sopenharmony_ci path->slots[0]); 3588c2ecf20Sopenharmony_ci item_last_offset = item_start_offset + 3598c2ecf20Sopenharmony_ci (item_size / csum_size) * 3608c2ecf20Sopenharmony_ci fs_info->sectorsize; 3618c2ecf20Sopenharmony_ci item = btrfs_item_ptr(path->nodes[0], path->slots[0], 3628c2ecf20Sopenharmony_ci struct btrfs_csum_item); 3638c2ecf20Sopenharmony_ci } 3648c2ecf20Sopenharmony_ci /* 3658c2ecf20Sopenharmony_ci * this byte range must be able to fit inside 3668c2ecf20Sopenharmony_ci * a single leaf so it will also fit inside a u32 3678c2ecf20Sopenharmony_ci */ 3688c2ecf20Sopenharmony_ci diff = disk_bytenr - item_start_offset; 3698c2ecf20Sopenharmony_ci diff = diff / fs_info->sectorsize; 3708c2ecf20Sopenharmony_ci diff = diff * csum_size; 3718c2ecf20Sopenharmony_ci count = min_t(int, nblocks, (item_last_offset - disk_bytenr) >> 3728c2ecf20Sopenharmony_ci inode->i_sb->s_blocksize_bits); 3738c2ecf20Sopenharmony_ci read_extent_buffer(path->nodes[0], csum, 3748c2ecf20Sopenharmony_ci ((unsigned long)item) + diff, 3758c2ecf20Sopenharmony_ci csum_size * count); 3768c2ecf20Sopenharmony_cifound: 3778c2ecf20Sopenharmony_ci csum += count * csum_size; 3788c2ecf20Sopenharmony_ci nblocks -= count; 3798c2ecf20Sopenharmony_cinext: 3808c2ecf20Sopenharmony_ci while (count > 0) { 3818c2ecf20Sopenharmony_ci count--; 3828c2ecf20Sopenharmony_ci disk_bytenr += fs_info->sectorsize; 3838c2ecf20Sopenharmony_ci offset += fs_info->sectorsize; 3848c2ecf20Sopenharmony_ci page_bytes_left -= fs_info->sectorsize; 3858c2ecf20Sopenharmony_ci if (!page_bytes_left) 3868c2ecf20Sopenharmony_ci break; /* move to next bio */ 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci WARN_ON_ONCE(count); 3918c2ecf20Sopenharmony_ci btrfs_free_path(path); 3928c2ecf20Sopenharmony_ci return BLK_STS_OK; 3938c2ecf20Sopenharmony_ci} 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ciint btrfs_lookup_csums_range(struct btrfs_root *root, u64 start, u64 end, 3968c2ecf20Sopenharmony_ci struct list_head *list, int search_commit) 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci struct btrfs_fs_info *fs_info = root->fs_info; 3998c2ecf20Sopenharmony_ci struct btrfs_key key; 4008c2ecf20Sopenharmony_ci struct btrfs_path *path; 4018c2ecf20Sopenharmony_ci struct extent_buffer *leaf; 4028c2ecf20Sopenharmony_ci struct btrfs_ordered_sum *sums; 4038c2ecf20Sopenharmony_ci struct btrfs_csum_item *item; 4048c2ecf20Sopenharmony_ci LIST_HEAD(tmplist); 4058c2ecf20Sopenharmony_ci unsigned long offset; 4068c2ecf20Sopenharmony_ci int ret; 4078c2ecf20Sopenharmony_ci size_t size; 4088c2ecf20Sopenharmony_ci u64 csum_end; 4098c2ecf20Sopenharmony_ci u16 csum_size = btrfs_super_csum_size(fs_info->super_copy); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci ASSERT(IS_ALIGNED(start, fs_info->sectorsize) && 4128c2ecf20Sopenharmony_ci IS_ALIGNED(end + 1, fs_info->sectorsize)); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci path = btrfs_alloc_path(); 4158c2ecf20Sopenharmony_ci if (!path) 4168c2ecf20Sopenharmony_ci return -ENOMEM; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci if (search_commit) { 4198c2ecf20Sopenharmony_ci path->skip_locking = 1; 4208c2ecf20Sopenharmony_ci path->reada = READA_FORWARD; 4218c2ecf20Sopenharmony_ci path->search_commit_root = 1; 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci key.objectid = BTRFS_EXTENT_CSUM_OBJECTID; 4258c2ecf20Sopenharmony_ci key.offset = start; 4268c2ecf20Sopenharmony_ci key.type = BTRFS_EXTENT_CSUM_KEY; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); 4298c2ecf20Sopenharmony_ci if (ret < 0) 4308c2ecf20Sopenharmony_ci goto fail; 4318c2ecf20Sopenharmony_ci if (ret > 0 && path->slots[0] > 0) { 4328c2ecf20Sopenharmony_ci leaf = path->nodes[0]; 4338c2ecf20Sopenharmony_ci btrfs_item_key_to_cpu(leaf, &key, path->slots[0] - 1); 4348c2ecf20Sopenharmony_ci if (key.objectid == BTRFS_EXTENT_CSUM_OBJECTID && 4358c2ecf20Sopenharmony_ci key.type == BTRFS_EXTENT_CSUM_KEY) { 4368c2ecf20Sopenharmony_ci offset = (start - key.offset) >> 4378c2ecf20Sopenharmony_ci fs_info->sb->s_blocksize_bits; 4388c2ecf20Sopenharmony_ci if (offset * csum_size < 4398c2ecf20Sopenharmony_ci btrfs_item_size_nr(leaf, path->slots[0] - 1)) 4408c2ecf20Sopenharmony_ci path->slots[0]--; 4418c2ecf20Sopenharmony_ci } 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci while (start <= end) { 4458c2ecf20Sopenharmony_ci leaf = path->nodes[0]; 4468c2ecf20Sopenharmony_ci if (path->slots[0] >= btrfs_header_nritems(leaf)) { 4478c2ecf20Sopenharmony_ci ret = btrfs_next_leaf(root, path); 4488c2ecf20Sopenharmony_ci if (ret < 0) 4498c2ecf20Sopenharmony_ci goto fail; 4508c2ecf20Sopenharmony_ci if (ret > 0) 4518c2ecf20Sopenharmony_ci break; 4528c2ecf20Sopenharmony_ci leaf = path->nodes[0]; 4538c2ecf20Sopenharmony_ci } 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); 4568c2ecf20Sopenharmony_ci if (key.objectid != BTRFS_EXTENT_CSUM_OBJECTID || 4578c2ecf20Sopenharmony_ci key.type != BTRFS_EXTENT_CSUM_KEY || 4588c2ecf20Sopenharmony_ci key.offset > end) 4598c2ecf20Sopenharmony_ci break; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci if (key.offset > start) 4628c2ecf20Sopenharmony_ci start = key.offset; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci size = btrfs_item_size_nr(leaf, path->slots[0]); 4658c2ecf20Sopenharmony_ci csum_end = key.offset + (size / csum_size) * fs_info->sectorsize; 4668c2ecf20Sopenharmony_ci if (csum_end <= start) { 4678c2ecf20Sopenharmony_ci path->slots[0]++; 4688c2ecf20Sopenharmony_ci continue; 4698c2ecf20Sopenharmony_ci } 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci csum_end = min(csum_end, end + 1); 4728c2ecf20Sopenharmony_ci item = btrfs_item_ptr(path->nodes[0], path->slots[0], 4738c2ecf20Sopenharmony_ci struct btrfs_csum_item); 4748c2ecf20Sopenharmony_ci while (start < csum_end) { 4758c2ecf20Sopenharmony_ci size = min_t(size_t, csum_end - start, 4768c2ecf20Sopenharmony_ci max_ordered_sum_bytes(fs_info, csum_size)); 4778c2ecf20Sopenharmony_ci sums = kzalloc(btrfs_ordered_sum_size(fs_info, size), 4788c2ecf20Sopenharmony_ci GFP_NOFS); 4798c2ecf20Sopenharmony_ci if (!sums) { 4808c2ecf20Sopenharmony_ci ret = -ENOMEM; 4818c2ecf20Sopenharmony_ci goto fail; 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci sums->bytenr = start; 4858c2ecf20Sopenharmony_ci sums->len = (int)size; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci offset = (start - key.offset) >> 4888c2ecf20Sopenharmony_ci fs_info->sb->s_blocksize_bits; 4898c2ecf20Sopenharmony_ci offset *= csum_size; 4908c2ecf20Sopenharmony_ci size >>= fs_info->sb->s_blocksize_bits; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci read_extent_buffer(path->nodes[0], 4938c2ecf20Sopenharmony_ci sums->sums, 4948c2ecf20Sopenharmony_ci ((unsigned long)item) + offset, 4958c2ecf20Sopenharmony_ci csum_size * size); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci start += fs_info->sectorsize * size; 4988c2ecf20Sopenharmony_ci list_add_tail(&sums->list, &tmplist); 4998c2ecf20Sopenharmony_ci } 5008c2ecf20Sopenharmony_ci path->slots[0]++; 5018c2ecf20Sopenharmony_ci } 5028c2ecf20Sopenharmony_ci ret = 0; 5038c2ecf20Sopenharmony_cifail: 5048c2ecf20Sopenharmony_ci while (ret < 0 && !list_empty(&tmplist)) { 5058c2ecf20Sopenharmony_ci sums = list_entry(tmplist.next, struct btrfs_ordered_sum, list); 5068c2ecf20Sopenharmony_ci list_del(&sums->list); 5078c2ecf20Sopenharmony_ci kfree(sums); 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci list_splice_tail(&tmplist, list); 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci btrfs_free_path(path); 5128c2ecf20Sopenharmony_ci return ret; 5138c2ecf20Sopenharmony_ci} 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci/* 5168c2ecf20Sopenharmony_ci * btrfs_csum_one_bio - Calculates checksums of the data contained inside a bio 5178c2ecf20Sopenharmony_ci * @inode: Owner of the data inside the bio 5188c2ecf20Sopenharmony_ci * @bio: Contains the data to be checksummed 5198c2ecf20Sopenharmony_ci * @file_start: offset in file this bio begins to describe 5208c2ecf20Sopenharmony_ci * @contig: Boolean. If true/1 means all bio vecs in this bio are 5218c2ecf20Sopenharmony_ci * contiguous and they begin at @file_start in the file. False/0 5228c2ecf20Sopenharmony_ci * means this bio can contains potentially discontigous bio vecs 5238c2ecf20Sopenharmony_ci * so the logical offset of each should be calculated separately. 5248c2ecf20Sopenharmony_ci */ 5258c2ecf20Sopenharmony_ciblk_status_t btrfs_csum_one_bio(struct btrfs_inode *inode, struct bio *bio, 5268c2ecf20Sopenharmony_ci u64 file_start, int contig) 5278c2ecf20Sopenharmony_ci{ 5288c2ecf20Sopenharmony_ci struct btrfs_fs_info *fs_info = inode->root->fs_info; 5298c2ecf20Sopenharmony_ci SHASH_DESC_ON_STACK(shash, fs_info->csum_shash); 5308c2ecf20Sopenharmony_ci struct btrfs_ordered_sum *sums; 5318c2ecf20Sopenharmony_ci struct btrfs_ordered_extent *ordered = NULL; 5328c2ecf20Sopenharmony_ci char *data; 5338c2ecf20Sopenharmony_ci struct bvec_iter iter; 5348c2ecf20Sopenharmony_ci struct bio_vec bvec; 5358c2ecf20Sopenharmony_ci int index; 5368c2ecf20Sopenharmony_ci int nr_sectors; 5378c2ecf20Sopenharmony_ci unsigned long total_bytes = 0; 5388c2ecf20Sopenharmony_ci unsigned long this_sum_bytes = 0; 5398c2ecf20Sopenharmony_ci int i; 5408c2ecf20Sopenharmony_ci u64 offset; 5418c2ecf20Sopenharmony_ci unsigned nofs_flag; 5428c2ecf20Sopenharmony_ci const u16 csum_size = btrfs_super_csum_size(fs_info->super_copy); 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci nofs_flag = memalloc_nofs_save(); 5458c2ecf20Sopenharmony_ci sums = kvzalloc(btrfs_ordered_sum_size(fs_info, bio->bi_iter.bi_size), 5468c2ecf20Sopenharmony_ci GFP_KERNEL); 5478c2ecf20Sopenharmony_ci memalloc_nofs_restore(nofs_flag); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci if (!sums) 5508c2ecf20Sopenharmony_ci return BLK_STS_RESOURCE; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci sums->len = bio->bi_iter.bi_size; 5538c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&sums->list); 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci if (contig) 5568c2ecf20Sopenharmony_ci offset = file_start; 5578c2ecf20Sopenharmony_ci else 5588c2ecf20Sopenharmony_ci offset = 0; /* shut up gcc */ 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci sums->bytenr = (u64)bio->bi_iter.bi_sector << 9; 5618c2ecf20Sopenharmony_ci index = 0; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci shash->tfm = fs_info->csum_shash; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci bio_for_each_segment(bvec, bio, iter) { 5668c2ecf20Sopenharmony_ci if (!contig) 5678c2ecf20Sopenharmony_ci offset = page_offset(bvec.bv_page) + bvec.bv_offset; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci if (!ordered) { 5708c2ecf20Sopenharmony_ci ordered = btrfs_lookup_ordered_extent(inode, offset); 5718c2ecf20Sopenharmony_ci /* 5728c2ecf20Sopenharmony_ci * The bio range is not covered by any ordered extent, 5738c2ecf20Sopenharmony_ci * must be a code logic error. 5748c2ecf20Sopenharmony_ci */ 5758c2ecf20Sopenharmony_ci if (unlikely(!ordered)) { 5768c2ecf20Sopenharmony_ci WARN(1, KERN_WARNING 5778c2ecf20Sopenharmony_ci "no ordered extent for root %llu ino %llu offset %llu\n", 5788c2ecf20Sopenharmony_ci inode->root->root_key.objectid, 5798c2ecf20Sopenharmony_ci btrfs_ino(inode), offset); 5808c2ecf20Sopenharmony_ci kvfree(sums); 5818c2ecf20Sopenharmony_ci return BLK_STS_IOERR; 5828c2ecf20Sopenharmony_ci } 5838c2ecf20Sopenharmony_ci } 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci nr_sectors = BTRFS_BYTES_TO_BLKS(fs_info, 5868c2ecf20Sopenharmony_ci bvec.bv_len + fs_info->sectorsize 5878c2ecf20Sopenharmony_ci - 1); 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci for (i = 0; i < nr_sectors; i++) { 5908c2ecf20Sopenharmony_ci if (offset >= ordered->file_offset + ordered->num_bytes || 5918c2ecf20Sopenharmony_ci offset < ordered->file_offset) { 5928c2ecf20Sopenharmony_ci unsigned long bytes_left; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci sums->len = this_sum_bytes; 5958c2ecf20Sopenharmony_ci this_sum_bytes = 0; 5968c2ecf20Sopenharmony_ci btrfs_add_ordered_sum(ordered, sums); 5978c2ecf20Sopenharmony_ci btrfs_put_ordered_extent(ordered); 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci bytes_left = bio->bi_iter.bi_size - total_bytes; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci nofs_flag = memalloc_nofs_save(); 6028c2ecf20Sopenharmony_ci sums = kvzalloc(btrfs_ordered_sum_size(fs_info, 6038c2ecf20Sopenharmony_ci bytes_left), GFP_KERNEL); 6048c2ecf20Sopenharmony_ci memalloc_nofs_restore(nofs_flag); 6058c2ecf20Sopenharmony_ci if (!sums) 6068c2ecf20Sopenharmony_ci return BLK_STS_RESOURCE; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci sums->len = bytes_left; 6098c2ecf20Sopenharmony_ci ordered = btrfs_lookup_ordered_extent(inode, 6108c2ecf20Sopenharmony_ci offset); 6118c2ecf20Sopenharmony_ci ASSERT(ordered); /* Logic error */ 6128c2ecf20Sopenharmony_ci sums->bytenr = ((u64)bio->bi_iter.bi_sector << 9) 6138c2ecf20Sopenharmony_ci + total_bytes; 6148c2ecf20Sopenharmony_ci index = 0; 6158c2ecf20Sopenharmony_ci } 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci data = kmap_atomic(bvec.bv_page); 6188c2ecf20Sopenharmony_ci crypto_shash_digest(shash, data + bvec.bv_offset 6198c2ecf20Sopenharmony_ci + (i * fs_info->sectorsize), 6208c2ecf20Sopenharmony_ci fs_info->sectorsize, 6218c2ecf20Sopenharmony_ci sums->sums + index); 6228c2ecf20Sopenharmony_ci kunmap_atomic(data); 6238c2ecf20Sopenharmony_ci index += csum_size; 6248c2ecf20Sopenharmony_ci offset += fs_info->sectorsize; 6258c2ecf20Sopenharmony_ci this_sum_bytes += fs_info->sectorsize; 6268c2ecf20Sopenharmony_ci total_bytes += fs_info->sectorsize; 6278c2ecf20Sopenharmony_ci } 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci } 6308c2ecf20Sopenharmony_ci this_sum_bytes = 0; 6318c2ecf20Sopenharmony_ci btrfs_add_ordered_sum(ordered, sums); 6328c2ecf20Sopenharmony_ci btrfs_put_ordered_extent(ordered); 6338c2ecf20Sopenharmony_ci return 0; 6348c2ecf20Sopenharmony_ci} 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci/* 6378c2ecf20Sopenharmony_ci * helper function for csum removal, this expects the 6388c2ecf20Sopenharmony_ci * key to describe the csum pointed to by the path, and it expects 6398c2ecf20Sopenharmony_ci * the csum to overlap the range [bytenr, len] 6408c2ecf20Sopenharmony_ci * 6418c2ecf20Sopenharmony_ci * The csum should not be entirely contained in the range and the 6428c2ecf20Sopenharmony_ci * range should not be entirely contained in the csum. 6438c2ecf20Sopenharmony_ci * 6448c2ecf20Sopenharmony_ci * This calls btrfs_truncate_item with the correct args based on the 6458c2ecf20Sopenharmony_ci * overlap, and fixes up the key as required. 6468c2ecf20Sopenharmony_ci */ 6478c2ecf20Sopenharmony_cistatic noinline void truncate_one_csum(struct btrfs_fs_info *fs_info, 6488c2ecf20Sopenharmony_ci struct btrfs_path *path, 6498c2ecf20Sopenharmony_ci struct btrfs_key *key, 6508c2ecf20Sopenharmony_ci u64 bytenr, u64 len) 6518c2ecf20Sopenharmony_ci{ 6528c2ecf20Sopenharmony_ci struct extent_buffer *leaf; 6538c2ecf20Sopenharmony_ci u16 csum_size = btrfs_super_csum_size(fs_info->super_copy); 6548c2ecf20Sopenharmony_ci u64 csum_end; 6558c2ecf20Sopenharmony_ci u64 end_byte = bytenr + len; 6568c2ecf20Sopenharmony_ci u32 blocksize_bits = fs_info->sb->s_blocksize_bits; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci leaf = path->nodes[0]; 6598c2ecf20Sopenharmony_ci csum_end = btrfs_item_size_nr(leaf, path->slots[0]) / csum_size; 6608c2ecf20Sopenharmony_ci csum_end <<= fs_info->sb->s_blocksize_bits; 6618c2ecf20Sopenharmony_ci csum_end += key->offset; 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci if (key->offset < bytenr && csum_end <= end_byte) { 6648c2ecf20Sopenharmony_ci /* 6658c2ecf20Sopenharmony_ci * [ bytenr - len ] 6668c2ecf20Sopenharmony_ci * [ ] 6678c2ecf20Sopenharmony_ci * [csum ] 6688c2ecf20Sopenharmony_ci * A simple truncate off the end of the item 6698c2ecf20Sopenharmony_ci */ 6708c2ecf20Sopenharmony_ci u32 new_size = (bytenr - key->offset) >> blocksize_bits; 6718c2ecf20Sopenharmony_ci new_size *= csum_size; 6728c2ecf20Sopenharmony_ci btrfs_truncate_item(path, new_size, 1); 6738c2ecf20Sopenharmony_ci } else if (key->offset >= bytenr && csum_end > end_byte && 6748c2ecf20Sopenharmony_ci end_byte > key->offset) { 6758c2ecf20Sopenharmony_ci /* 6768c2ecf20Sopenharmony_ci * [ bytenr - len ] 6778c2ecf20Sopenharmony_ci * [ ] 6788c2ecf20Sopenharmony_ci * [csum ] 6798c2ecf20Sopenharmony_ci * we need to truncate from the beginning of the csum 6808c2ecf20Sopenharmony_ci */ 6818c2ecf20Sopenharmony_ci u32 new_size = (csum_end - end_byte) >> blocksize_bits; 6828c2ecf20Sopenharmony_ci new_size *= csum_size; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci btrfs_truncate_item(path, new_size, 0); 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci key->offset = end_byte; 6878c2ecf20Sopenharmony_ci btrfs_set_item_key_safe(fs_info, path, key); 6888c2ecf20Sopenharmony_ci } else { 6898c2ecf20Sopenharmony_ci BUG(); 6908c2ecf20Sopenharmony_ci } 6918c2ecf20Sopenharmony_ci} 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci/* 6948c2ecf20Sopenharmony_ci * deletes the csum items from the csum tree for a given 6958c2ecf20Sopenharmony_ci * range of bytes. 6968c2ecf20Sopenharmony_ci */ 6978c2ecf20Sopenharmony_ciint btrfs_del_csums(struct btrfs_trans_handle *trans, 6988c2ecf20Sopenharmony_ci struct btrfs_root *root, u64 bytenr, u64 len) 6998c2ecf20Sopenharmony_ci{ 7008c2ecf20Sopenharmony_ci struct btrfs_fs_info *fs_info = trans->fs_info; 7018c2ecf20Sopenharmony_ci struct btrfs_path *path; 7028c2ecf20Sopenharmony_ci struct btrfs_key key; 7038c2ecf20Sopenharmony_ci u64 end_byte = bytenr + len; 7048c2ecf20Sopenharmony_ci u64 csum_end; 7058c2ecf20Sopenharmony_ci struct extent_buffer *leaf; 7068c2ecf20Sopenharmony_ci int ret = 0; 7078c2ecf20Sopenharmony_ci u16 csum_size = btrfs_super_csum_size(fs_info->super_copy); 7088c2ecf20Sopenharmony_ci int blocksize_bits = fs_info->sb->s_blocksize_bits; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci ASSERT(root == fs_info->csum_root || 7118c2ecf20Sopenharmony_ci root->root_key.objectid == BTRFS_TREE_LOG_OBJECTID); 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci path = btrfs_alloc_path(); 7148c2ecf20Sopenharmony_ci if (!path) 7158c2ecf20Sopenharmony_ci return -ENOMEM; 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci while (1) { 7188c2ecf20Sopenharmony_ci key.objectid = BTRFS_EXTENT_CSUM_OBJECTID; 7198c2ecf20Sopenharmony_ci key.offset = end_byte - 1; 7208c2ecf20Sopenharmony_ci key.type = BTRFS_EXTENT_CSUM_KEY; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci path->leave_spinning = 1; 7238c2ecf20Sopenharmony_ci ret = btrfs_search_slot(trans, root, &key, path, -1, 1); 7248c2ecf20Sopenharmony_ci if (ret > 0) { 7258c2ecf20Sopenharmony_ci ret = 0; 7268c2ecf20Sopenharmony_ci if (path->slots[0] == 0) 7278c2ecf20Sopenharmony_ci break; 7288c2ecf20Sopenharmony_ci path->slots[0]--; 7298c2ecf20Sopenharmony_ci } else if (ret < 0) { 7308c2ecf20Sopenharmony_ci break; 7318c2ecf20Sopenharmony_ci } 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci leaf = path->nodes[0]; 7348c2ecf20Sopenharmony_ci btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci if (key.objectid != BTRFS_EXTENT_CSUM_OBJECTID || 7378c2ecf20Sopenharmony_ci key.type != BTRFS_EXTENT_CSUM_KEY) { 7388c2ecf20Sopenharmony_ci break; 7398c2ecf20Sopenharmony_ci } 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci if (key.offset >= end_byte) 7428c2ecf20Sopenharmony_ci break; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci csum_end = btrfs_item_size_nr(leaf, path->slots[0]) / csum_size; 7458c2ecf20Sopenharmony_ci csum_end <<= blocksize_bits; 7468c2ecf20Sopenharmony_ci csum_end += key.offset; 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci /* this csum ends before we start, we're done */ 7498c2ecf20Sopenharmony_ci if (csum_end <= bytenr) 7508c2ecf20Sopenharmony_ci break; 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci /* delete the entire item, it is inside our range */ 7538c2ecf20Sopenharmony_ci if (key.offset >= bytenr && csum_end <= end_byte) { 7548c2ecf20Sopenharmony_ci int del_nr = 1; 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci /* 7578c2ecf20Sopenharmony_ci * Check how many csum items preceding this one in this 7588c2ecf20Sopenharmony_ci * leaf correspond to our range and then delete them all 7598c2ecf20Sopenharmony_ci * at once. 7608c2ecf20Sopenharmony_ci */ 7618c2ecf20Sopenharmony_ci if (key.offset > bytenr && path->slots[0] > 0) { 7628c2ecf20Sopenharmony_ci int slot = path->slots[0] - 1; 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci while (slot >= 0) { 7658c2ecf20Sopenharmony_ci struct btrfs_key pk; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci btrfs_item_key_to_cpu(leaf, &pk, slot); 7688c2ecf20Sopenharmony_ci if (pk.offset < bytenr || 7698c2ecf20Sopenharmony_ci pk.type != BTRFS_EXTENT_CSUM_KEY || 7708c2ecf20Sopenharmony_ci pk.objectid != 7718c2ecf20Sopenharmony_ci BTRFS_EXTENT_CSUM_OBJECTID) 7728c2ecf20Sopenharmony_ci break; 7738c2ecf20Sopenharmony_ci path->slots[0] = slot; 7748c2ecf20Sopenharmony_ci del_nr++; 7758c2ecf20Sopenharmony_ci key.offset = pk.offset; 7768c2ecf20Sopenharmony_ci slot--; 7778c2ecf20Sopenharmony_ci } 7788c2ecf20Sopenharmony_ci } 7798c2ecf20Sopenharmony_ci ret = btrfs_del_items(trans, root, path, 7808c2ecf20Sopenharmony_ci path->slots[0], del_nr); 7818c2ecf20Sopenharmony_ci if (ret) 7828c2ecf20Sopenharmony_ci break; 7838c2ecf20Sopenharmony_ci if (key.offset == bytenr) 7848c2ecf20Sopenharmony_ci break; 7858c2ecf20Sopenharmony_ci } else if (key.offset < bytenr && csum_end > end_byte) { 7868c2ecf20Sopenharmony_ci unsigned long offset; 7878c2ecf20Sopenharmony_ci unsigned long shift_len; 7888c2ecf20Sopenharmony_ci unsigned long item_offset; 7898c2ecf20Sopenharmony_ci /* 7908c2ecf20Sopenharmony_ci * [ bytenr - len ] 7918c2ecf20Sopenharmony_ci * [csum ] 7928c2ecf20Sopenharmony_ci * 7938c2ecf20Sopenharmony_ci * Our bytes are in the middle of the csum, 7948c2ecf20Sopenharmony_ci * we need to split this item and insert a new one. 7958c2ecf20Sopenharmony_ci * 7968c2ecf20Sopenharmony_ci * But we can't drop the path because the 7978c2ecf20Sopenharmony_ci * csum could change, get removed, extended etc. 7988c2ecf20Sopenharmony_ci * 7998c2ecf20Sopenharmony_ci * The trick here is the max size of a csum item leaves 8008c2ecf20Sopenharmony_ci * enough room in the tree block for a single 8018c2ecf20Sopenharmony_ci * item header. So, we split the item in place, 8028c2ecf20Sopenharmony_ci * adding a new header pointing to the existing 8038c2ecf20Sopenharmony_ci * bytes. Then we loop around again and we have 8048c2ecf20Sopenharmony_ci * a nicely formed csum item that we can neatly 8058c2ecf20Sopenharmony_ci * truncate. 8068c2ecf20Sopenharmony_ci */ 8078c2ecf20Sopenharmony_ci offset = (bytenr - key.offset) >> blocksize_bits; 8088c2ecf20Sopenharmony_ci offset *= csum_size; 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci shift_len = (len >> blocksize_bits) * csum_size; 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci item_offset = btrfs_item_ptr_offset(leaf, 8138c2ecf20Sopenharmony_ci path->slots[0]); 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci memzero_extent_buffer(leaf, item_offset + offset, 8168c2ecf20Sopenharmony_ci shift_len); 8178c2ecf20Sopenharmony_ci key.offset = bytenr; 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci /* 8208c2ecf20Sopenharmony_ci * btrfs_split_item returns -EAGAIN when the 8218c2ecf20Sopenharmony_ci * item changed size or key 8228c2ecf20Sopenharmony_ci */ 8238c2ecf20Sopenharmony_ci ret = btrfs_split_item(trans, root, path, &key, offset); 8248c2ecf20Sopenharmony_ci if (ret && ret != -EAGAIN) { 8258c2ecf20Sopenharmony_ci btrfs_abort_transaction(trans, ret); 8268c2ecf20Sopenharmony_ci break; 8278c2ecf20Sopenharmony_ci } 8288c2ecf20Sopenharmony_ci ret = 0; 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci key.offset = end_byte - 1; 8318c2ecf20Sopenharmony_ci } else { 8328c2ecf20Sopenharmony_ci truncate_one_csum(fs_info, path, &key, bytenr, len); 8338c2ecf20Sopenharmony_ci if (key.offset < bytenr) 8348c2ecf20Sopenharmony_ci break; 8358c2ecf20Sopenharmony_ci } 8368c2ecf20Sopenharmony_ci btrfs_release_path(path); 8378c2ecf20Sopenharmony_ci } 8388c2ecf20Sopenharmony_ci btrfs_free_path(path); 8398c2ecf20Sopenharmony_ci return ret; 8408c2ecf20Sopenharmony_ci} 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ciint btrfs_csum_file_blocks(struct btrfs_trans_handle *trans, 8438c2ecf20Sopenharmony_ci struct btrfs_root *root, 8448c2ecf20Sopenharmony_ci struct btrfs_ordered_sum *sums) 8458c2ecf20Sopenharmony_ci{ 8468c2ecf20Sopenharmony_ci struct btrfs_fs_info *fs_info = root->fs_info; 8478c2ecf20Sopenharmony_ci struct btrfs_key file_key; 8488c2ecf20Sopenharmony_ci struct btrfs_key found_key; 8498c2ecf20Sopenharmony_ci struct btrfs_path *path; 8508c2ecf20Sopenharmony_ci struct btrfs_csum_item *item; 8518c2ecf20Sopenharmony_ci struct btrfs_csum_item *item_end; 8528c2ecf20Sopenharmony_ci struct extent_buffer *leaf = NULL; 8538c2ecf20Sopenharmony_ci u64 next_offset; 8548c2ecf20Sopenharmony_ci u64 total_bytes = 0; 8558c2ecf20Sopenharmony_ci u64 csum_offset; 8568c2ecf20Sopenharmony_ci u64 bytenr; 8578c2ecf20Sopenharmony_ci u32 nritems; 8588c2ecf20Sopenharmony_ci u32 ins_size; 8598c2ecf20Sopenharmony_ci int index = 0; 8608c2ecf20Sopenharmony_ci int found_next; 8618c2ecf20Sopenharmony_ci int ret; 8628c2ecf20Sopenharmony_ci u16 csum_size = btrfs_super_csum_size(fs_info->super_copy); 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci path = btrfs_alloc_path(); 8658c2ecf20Sopenharmony_ci if (!path) 8668c2ecf20Sopenharmony_ci return -ENOMEM; 8678c2ecf20Sopenharmony_ciagain: 8688c2ecf20Sopenharmony_ci next_offset = (u64)-1; 8698c2ecf20Sopenharmony_ci found_next = 0; 8708c2ecf20Sopenharmony_ci bytenr = sums->bytenr + total_bytes; 8718c2ecf20Sopenharmony_ci file_key.objectid = BTRFS_EXTENT_CSUM_OBJECTID; 8728c2ecf20Sopenharmony_ci file_key.offset = bytenr; 8738c2ecf20Sopenharmony_ci file_key.type = BTRFS_EXTENT_CSUM_KEY; 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci item = btrfs_lookup_csum(trans, root, path, bytenr, 1); 8768c2ecf20Sopenharmony_ci if (!IS_ERR(item)) { 8778c2ecf20Sopenharmony_ci ret = 0; 8788c2ecf20Sopenharmony_ci leaf = path->nodes[0]; 8798c2ecf20Sopenharmony_ci item_end = btrfs_item_ptr(leaf, path->slots[0], 8808c2ecf20Sopenharmony_ci struct btrfs_csum_item); 8818c2ecf20Sopenharmony_ci item_end = (struct btrfs_csum_item *)((char *)item_end + 8828c2ecf20Sopenharmony_ci btrfs_item_size_nr(leaf, path->slots[0])); 8838c2ecf20Sopenharmony_ci goto found; 8848c2ecf20Sopenharmony_ci } 8858c2ecf20Sopenharmony_ci ret = PTR_ERR(item); 8868c2ecf20Sopenharmony_ci if (ret != -EFBIG && ret != -ENOENT) 8878c2ecf20Sopenharmony_ci goto out; 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci if (ret == -EFBIG) { 8908c2ecf20Sopenharmony_ci u32 item_size; 8918c2ecf20Sopenharmony_ci /* we found one, but it isn't big enough yet */ 8928c2ecf20Sopenharmony_ci leaf = path->nodes[0]; 8938c2ecf20Sopenharmony_ci item_size = btrfs_item_size_nr(leaf, path->slots[0]); 8948c2ecf20Sopenharmony_ci if ((item_size / csum_size) >= 8958c2ecf20Sopenharmony_ci MAX_CSUM_ITEMS(fs_info, csum_size)) { 8968c2ecf20Sopenharmony_ci /* already at max size, make a new one */ 8978c2ecf20Sopenharmony_ci goto insert; 8988c2ecf20Sopenharmony_ci } 8998c2ecf20Sopenharmony_ci } else { 9008c2ecf20Sopenharmony_ci int slot = path->slots[0] + 1; 9018c2ecf20Sopenharmony_ci /* we didn't find a csum item, insert one */ 9028c2ecf20Sopenharmony_ci nritems = btrfs_header_nritems(path->nodes[0]); 9038c2ecf20Sopenharmony_ci if (!nritems || (path->slots[0] >= nritems - 1)) { 9048c2ecf20Sopenharmony_ci ret = btrfs_next_leaf(root, path); 9058c2ecf20Sopenharmony_ci if (ret < 0) { 9068c2ecf20Sopenharmony_ci goto out; 9078c2ecf20Sopenharmony_ci } else if (ret > 0) { 9088c2ecf20Sopenharmony_ci found_next = 1; 9098c2ecf20Sopenharmony_ci goto insert; 9108c2ecf20Sopenharmony_ci } 9118c2ecf20Sopenharmony_ci slot = path->slots[0]; 9128c2ecf20Sopenharmony_ci } 9138c2ecf20Sopenharmony_ci btrfs_item_key_to_cpu(path->nodes[0], &found_key, slot); 9148c2ecf20Sopenharmony_ci if (found_key.objectid != BTRFS_EXTENT_CSUM_OBJECTID || 9158c2ecf20Sopenharmony_ci found_key.type != BTRFS_EXTENT_CSUM_KEY) { 9168c2ecf20Sopenharmony_ci found_next = 1; 9178c2ecf20Sopenharmony_ci goto insert; 9188c2ecf20Sopenharmony_ci } 9198c2ecf20Sopenharmony_ci next_offset = found_key.offset; 9208c2ecf20Sopenharmony_ci found_next = 1; 9218c2ecf20Sopenharmony_ci goto insert; 9228c2ecf20Sopenharmony_ci } 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci /* 9258c2ecf20Sopenharmony_ci * At this point, we know the tree has a checksum item that ends at an 9268c2ecf20Sopenharmony_ci * offset matching the start of the checksum range we want to insert. 9278c2ecf20Sopenharmony_ci * We try to extend that item as much as possible and then add as many 9288c2ecf20Sopenharmony_ci * checksums to it as they fit. 9298c2ecf20Sopenharmony_ci * 9308c2ecf20Sopenharmony_ci * First check if the leaf has enough free space for at least one 9318c2ecf20Sopenharmony_ci * checksum. If it has go directly to the item extension code, otherwise 9328c2ecf20Sopenharmony_ci * release the path and do a search for insertion before the extension. 9338c2ecf20Sopenharmony_ci */ 9348c2ecf20Sopenharmony_ci if (btrfs_leaf_free_space(leaf) >= csum_size) { 9358c2ecf20Sopenharmony_ci btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]); 9368c2ecf20Sopenharmony_ci csum_offset = (bytenr - found_key.offset) >> 9378c2ecf20Sopenharmony_ci fs_info->sb->s_blocksize_bits; 9388c2ecf20Sopenharmony_ci goto extend_csum; 9398c2ecf20Sopenharmony_ci } 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci btrfs_release_path(path); 9428c2ecf20Sopenharmony_ci ret = btrfs_search_slot(trans, root, &file_key, path, 9438c2ecf20Sopenharmony_ci csum_size, 1); 9448c2ecf20Sopenharmony_ci if (ret < 0) 9458c2ecf20Sopenharmony_ci goto out; 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci if (ret > 0) { 9488c2ecf20Sopenharmony_ci if (path->slots[0] == 0) 9498c2ecf20Sopenharmony_ci goto insert; 9508c2ecf20Sopenharmony_ci path->slots[0]--; 9518c2ecf20Sopenharmony_ci } 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci leaf = path->nodes[0]; 9548c2ecf20Sopenharmony_ci btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]); 9558c2ecf20Sopenharmony_ci csum_offset = (bytenr - found_key.offset) >> 9568c2ecf20Sopenharmony_ci fs_info->sb->s_blocksize_bits; 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci if (found_key.type != BTRFS_EXTENT_CSUM_KEY || 9598c2ecf20Sopenharmony_ci found_key.objectid != BTRFS_EXTENT_CSUM_OBJECTID || 9608c2ecf20Sopenharmony_ci csum_offset >= MAX_CSUM_ITEMS(fs_info, csum_size)) { 9618c2ecf20Sopenharmony_ci goto insert; 9628c2ecf20Sopenharmony_ci } 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ciextend_csum: 9658c2ecf20Sopenharmony_ci if (csum_offset == btrfs_item_size_nr(leaf, path->slots[0]) / 9668c2ecf20Sopenharmony_ci csum_size) { 9678c2ecf20Sopenharmony_ci int extend_nr; 9688c2ecf20Sopenharmony_ci u64 tmp; 9698c2ecf20Sopenharmony_ci u32 diff; 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci tmp = sums->len - total_bytes; 9728c2ecf20Sopenharmony_ci tmp >>= fs_info->sb->s_blocksize_bits; 9738c2ecf20Sopenharmony_ci WARN_ON(tmp < 1); 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci extend_nr = max_t(int, 1, (int)tmp); 9768c2ecf20Sopenharmony_ci diff = (csum_offset + extend_nr) * csum_size; 9778c2ecf20Sopenharmony_ci diff = min(diff, 9788c2ecf20Sopenharmony_ci MAX_CSUM_ITEMS(fs_info, csum_size) * csum_size); 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci diff = diff - btrfs_item_size_nr(leaf, path->slots[0]); 9818c2ecf20Sopenharmony_ci diff = min_t(u32, btrfs_leaf_free_space(leaf), diff); 9828c2ecf20Sopenharmony_ci diff /= csum_size; 9838c2ecf20Sopenharmony_ci diff *= csum_size; 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci btrfs_extend_item(path, diff); 9868c2ecf20Sopenharmony_ci ret = 0; 9878c2ecf20Sopenharmony_ci goto csum; 9888c2ecf20Sopenharmony_ci } 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ciinsert: 9918c2ecf20Sopenharmony_ci btrfs_release_path(path); 9928c2ecf20Sopenharmony_ci csum_offset = 0; 9938c2ecf20Sopenharmony_ci if (found_next) { 9948c2ecf20Sopenharmony_ci u64 tmp; 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci tmp = sums->len - total_bytes; 9978c2ecf20Sopenharmony_ci tmp >>= fs_info->sb->s_blocksize_bits; 9988c2ecf20Sopenharmony_ci tmp = min(tmp, (next_offset - file_key.offset) >> 9998c2ecf20Sopenharmony_ci fs_info->sb->s_blocksize_bits); 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci tmp = max_t(u64, 1, tmp); 10028c2ecf20Sopenharmony_ci tmp = min_t(u64, tmp, MAX_CSUM_ITEMS(fs_info, csum_size)); 10038c2ecf20Sopenharmony_ci ins_size = csum_size * tmp; 10048c2ecf20Sopenharmony_ci } else { 10058c2ecf20Sopenharmony_ci ins_size = csum_size; 10068c2ecf20Sopenharmony_ci } 10078c2ecf20Sopenharmony_ci path->leave_spinning = 1; 10088c2ecf20Sopenharmony_ci ret = btrfs_insert_empty_item(trans, root, path, &file_key, 10098c2ecf20Sopenharmony_ci ins_size); 10108c2ecf20Sopenharmony_ci path->leave_spinning = 0; 10118c2ecf20Sopenharmony_ci if (ret < 0) 10128c2ecf20Sopenharmony_ci goto out; 10138c2ecf20Sopenharmony_ci if (WARN_ON(ret != 0)) 10148c2ecf20Sopenharmony_ci goto out; 10158c2ecf20Sopenharmony_ci leaf = path->nodes[0]; 10168c2ecf20Sopenharmony_cicsum: 10178c2ecf20Sopenharmony_ci item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_csum_item); 10188c2ecf20Sopenharmony_ci item_end = (struct btrfs_csum_item *)((unsigned char *)item + 10198c2ecf20Sopenharmony_ci btrfs_item_size_nr(leaf, path->slots[0])); 10208c2ecf20Sopenharmony_ci item = (struct btrfs_csum_item *)((unsigned char *)item + 10218c2ecf20Sopenharmony_ci csum_offset * csum_size); 10228c2ecf20Sopenharmony_cifound: 10238c2ecf20Sopenharmony_ci ins_size = (u32)(sums->len - total_bytes) >> 10248c2ecf20Sopenharmony_ci fs_info->sb->s_blocksize_bits; 10258c2ecf20Sopenharmony_ci ins_size *= csum_size; 10268c2ecf20Sopenharmony_ci ins_size = min_t(u32, (unsigned long)item_end - (unsigned long)item, 10278c2ecf20Sopenharmony_ci ins_size); 10288c2ecf20Sopenharmony_ci write_extent_buffer(leaf, sums->sums + index, (unsigned long)item, 10298c2ecf20Sopenharmony_ci ins_size); 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci index += ins_size; 10328c2ecf20Sopenharmony_ci ins_size /= csum_size; 10338c2ecf20Sopenharmony_ci total_bytes += ins_size * fs_info->sectorsize; 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci btrfs_mark_buffer_dirty(path->nodes[0]); 10368c2ecf20Sopenharmony_ci if (total_bytes < sums->len) { 10378c2ecf20Sopenharmony_ci btrfs_release_path(path); 10388c2ecf20Sopenharmony_ci cond_resched(); 10398c2ecf20Sopenharmony_ci goto again; 10408c2ecf20Sopenharmony_ci } 10418c2ecf20Sopenharmony_ciout: 10428c2ecf20Sopenharmony_ci btrfs_free_path(path); 10438c2ecf20Sopenharmony_ci return ret; 10448c2ecf20Sopenharmony_ci} 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_civoid btrfs_extent_item_to_extent_map(struct btrfs_inode *inode, 10478c2ecf20Sopenharmony_ci const struct btrfs_path *path, 10488c2ecf20Sopenharmony_ci struct btrfs_file_extent_item *fi, 10498c2ecf20Sopenharmony_ci const bool new_inline, 10508c2ecf20Sopenharmony_ci struct extent_map *em) 10518c2ecf20Sopenharmony_ci{ 10528c2ecf20Sopenharmony_ci struct btrfs_fs_info *fs_info = inode->root->fs_info; 10538c2ecf20Sopenharmony_ci struct btrfs_root *root = inode->root; 10548c2ecf20Sopenharmony_ci struct extent_buffer *leaf = path->nodes[0]; 10558c2ecf20Sopenharmony_ci const int slot = path->slots[0]; 10568c2ecf20Sopenharmony_ci struct btrfs_key key; 10578c2ecf20Sopenharmony_ci u64 extent_start, extent_end; 10588c2ecf20Sopenharmony_ci u64 bytenr; 10598c2ecf20Sopenharmony_ci u8 type = btrfs_file_extent_type(leaf, fi); 10608c2ecf20Sopenharmony_ci int compress_type = btrfs_file_extent_compression(leaf, fi); 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci btrfs_item_key_to_cpu(leaf, &key, slot); 10638c2ecf20Sopenharmony_ci extent_start = key.offset; 10648c2ecf20Sopenharmony_ci extent_end = btrfs_file_extent_end(path); 10658c2ecf20Sopenharmony_ci em->ram_bytes = btrfs_file_extent_ram_bytes(leaf, fi); 10668c2ecf20Sopenharmony_ci if (type == BTRFS_FILE_EXTENT_REG || 10678c2ecf20Sopenharmony_ci type == BTRFS_FILE_EXTENT_PREALLOC) { 10688c2ecf20Sopenharmony_ci em->start = extent_start; 10698c2ecf20Sopenharmony_ci em->len = extent_end - extent_start; 10708c2ecf20Sopenharmony_ci em->orig_start = extent_start - 10718c2ecf20Sopenharmony_ci btrfs_file_extent_offset(leaf, fi); 10728c2ecf20Sopenharmony_ci em->orig_block_len = btrfs_file_extent_disk_num_bytes(leaf, fi); 10738c2ecf20Sopenharmony_ci bytenr = btrfs_file_extent_disk_bytenr(leaf, fi); 10748c2ecf20Sopenharmony_ci if (bytenr == 0) { 10758c2ecf20Sopenharmony_ci em->block_start = EXTENT_MAP_HOLE; 10768c2ecf20Sopenharmony_ci return; 10778c2ecf20Sopenharmony_ci } 10788c2ecf20Sopenharmony_ci if (compress_type != BTRFS_COMPRESS_NONE) { 10798c2ecf20Sopenharmony_ci set_bit(EXTENT_FLAG_COMPRESSED, &em->flags); 10808c2ecf20Sopenharmony_ci em->compress_type = compress_type; 10818c2ecf20Sopenharmony_ci em->block_start = bytenr; 10828c2ecf20Sopenharmony_ci em->block_len = em->orig_block_len; 10838c2ecf20Sopenharmony_ci } else { 10848c2ecf20Sopenharmony_ci bytenr += btrfs_file_extent_offset(leaf, fi); 10858c2ecf20Sopenharmony_ci em->block_start = bytenr; 10868c2ecf20Sopenharmony_ci em->block_len = em->len; 10878c2ecf20Sopenharmony_ci if (type == BTRFS_FILE_EXTENT_PREALLOC) 10888c2ecf20Sopenharmony_ci set_bit(EXTENT_FLAG_PREALLOC, &em->flags); 10898c2ecf20Sopenharmony_ci } 10908c2ecf20Sopenharmony_ci } else if (type == BTRFS_FILE_EXTENT_INLINE) { 10918c2ecf20Sopenharmony_ci em->block_start = EXTENT_MAP_INLINE; 10928c2ecf20Sopenharmony_ci em->start = extent_start; 10938c2ecf20Sopenharmony_ci em->len = extent_end - extent_start; 10948c2ecf20Sopenharmony_ci /* 10958c2ecf20Sopenharmony_ci * Initialize orig_start and block_len with the same values 10968c2ecf20Sopenharmony_ci * as in inode.c:btrfs_get_extent(). 10978c2ecf20Sopenharmony_ci */ 10988c2ecf20Sopenharmony_ci em->orig_start = EXTENT_MAP_HOLE; 10998c2ecf20Sopenharmony_ci em->block_len = (u64)-1; 11008c2ecf20Sopenharmony_ci if (!new_inline && compress_type != BTRFS_COMPRESS_NONE) { 11018c2ecf20Sopenharmony_ci set_bit(EXTENT_FLAG_COMPRESSED, &em->flags); 11028c2ecf20Sopenharmony_ci em->compress_type = compress_type; 11038c2ecf20Sopenharmony_ci } 11048c2ecf20Sopenharmony_ci } else { 11058c2ecf20Sopenharmony_ci btrfs_err(fs_info, 11068c2ecf20Sopenharmony_ci "unknown file extent item type %d, inode %llu, offset %llu, " 11078c2ecf20Sopenharmony_ci "root %llu", type, btrfs_ino(inode), extent_start, 11088c2ecf20Sopenharmony_ci root->root_key.objectid); 11098c2ecf20Sopenharmony_ci } 11108c2ecf20Sopenharmony_ci} 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci/* 11138c2ecf20Sopenharmony_ci * Returns the end offset (non inclusive) of the file extent item the given path 11148c2ecf20Sopenharmony_ci * points to. If it points to an inline extent, the returned offset is rounded 11158c2ecf20Sopenharmony_ci * up to the sector size. 11168c2ecf20Sopenharmony_ci */ 11178c2ecf20Sopenharmony_ciu64 btrfs_file_extent_end(const struct btrfs_path *path) 11188c2ecf20Sopenharmony_ci{ 11198c2ecf20Sopenharmony_ci const struct extent_buffer *leaf = path->nodes[0]; 11208c2ecf20Sopenharmony_ci const int slot = path->slots[0]; 11218c2ecf20Sopenharmony_ci struct btrfs_file_extent_item *fi; 11228c2ecf20Sopenharmony_ci struct btrfs_key key; 11238c2ecf20Sopenharmony_ci u64 end; 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci btrfs_item_key_to_cpu(leaf, &key, slot); 11268c2ecf20Sopenharmony_ci ASSERT(key.type == BTRFS_EXTENT_DATA_KEY); 11278c2ecf20Sopenharmony_ci fi = btrfs_item_ptr(leaf, slot, struct btrfs_file_extent_item); 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci if (btrfs_file_extent_type(leaf, fi) == BTRFS_FILE_EXTENT_INLINE) { 11308c2ecf20Sopenharmony_ci end = btrfs_file_extent_ram_bytes(leaf, fi); 11318c2ecf20Sopenharmony_ci end = ALIGN(key.offset + end, leaf->fs_info->sectorsize); 11328c2ecf20Sopenharmony_ci } else { 11338c2ecf20Sopenharmony_ci end = key.offset + btrfs_file_extent_num_bytes(leaf, fi); 11348c2ecf20Sopenharmony_ci } 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci return end; 11378c2ecf20Sopenharmony_ci} 1138