// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2018 Ernesto A. Fernández */ #include #include #include #include "apfs.h" /** * apfs_ext_is_hole - Does this extent represent a hole in a sparse file? * @extent: the extent to check */ static inline bool apfs_ext_is_hole(struct apfs_file_extent *extent) { return extent->phys_block_num == 0; } /** * apfs_size_to_blocks - Return the block count for a given size, rounded up * @sb: filesystem superblock * @size: size in bytes * * TODO: reuse for inode.c */ static inline u64 apfs_size_to_blocks(struct super_block *sb, u64 size) { return (size + sb->s_blocksize - 1) >> sb->s_blocksize_bits; } /** * apfs_extent_from_query - Read the extent found by a successful query * @query: the query that found the record * @extent: Return parameter. The extent found. * * Reads the extent record into @extent and performs some basic sanity checks * as a protection against crafted filesystems. Returns 0 on success or * -EFSCORRUPTED otherwise. */ int apfs_extent_from_query(struct apfs_query *query, struct apfs_file_extent *extent) { struct super_block *sb = query->node->object.sb; char *raw = query->node->object.data; u64 ext_len; if (!apfs_is_sealed(sb)) { struct apfs_file_extent_val *ext = NULL; struct apfs_file_extent_key *ext_key = NULL; if (query->len != sizeof(*ext) || query->key_len != sizeof(*ext_key)) { apfs_err(sb, "bad length of key (%d) or value (%d)", query->key_len, query->len); return -EFSCORRUPTED; } ext = (struct apfs_file_extent_val *)(raw + query->off); ext_key = (struct apfs_file_extent_key *)(raw + query->key_off); ext_len = le64_to_cpu(ext->len_and_flags) & APFS_FILE_EXTENT_LEN_MASK; extent->logical_addr = le64_to_cpu(ext_key->logical_addr); extent->phys_block_num = le64_to_cpu(ext->phys_block_num); extent->crypto_id = le64_to_cpu(ext->crypto_id); } else { struct apfs_fext_tree_val *fext_val = NULL; struct apfs_fext_tree_key *fext_key = NULL; if (query->len != sizeof(*fext_val) || query->key_len != sizeof(*fext_key)) { apfs_err(sb, "bad length of sealed key (%d) or value (%d)", query->key_len, query->len); return -EFSCORRUPTED; } fext_val = (struct apfs_fext_tree_val *)(raw + query->off); fext_key = (struct apfs_fext_tree_key *)(raw + query->key_off); ext_len = le64_to_cpu(fext_val->len_and_flags) & APFS_FILE_EXTENT_LEN_MASK; extent->logical_addr = le64_to_cpu(fext_key->logical_addr); extent->phys_block_num = le64_to_cpu(fext_val->phys_block_num); extent->crypto_id = 0; } /* Extent length must be a multiple of the block size */ if (ext_len & (sb->s_blocksize - 1)) { apfs_err(sb, "invalid length (0x%llx)", ext_len); return -EFSCORRUPTED; } extent->len = ext_len; return 0; } /** * apfs_extent_read - Read the extent record that covers a block * @dstream: data stream info * @dsblock: logical number of the wanted block (must be in range) * @extent: Return parameter. The extent found. * * Finds and caches the extent record. On success, returns a pointer to the * cache record; on failure, returns an error code. */ static int apfs_extent_read(struct apfs_dstream_info *dstream, sector_t dsblock, struct apfs_file_extent *extent) { struct super_block *sb = dstream->ds_sb; struct apfs_sb_info *sbi = APFS_SB(sb); struct apfs_superblock *vsb_raw = sbi->s_vsb_raw; struct apfs_key key; struct apfs_query *query; struct apfs_file_extent *cache = &dstream->ds_cached_ext; u64 iaddr = dsblock << sb->s_blocksize_bits; struct apfs_node *root = NULL; int ret = 0; spin_lock(&dstream->ds_ext_lock); if (iaddr >= cache->logical_addr && iaddr < cache->logical_addr + cache->len) { *extent = *cache; spin_unlock(&dstream->ds_ext_lock); return 0; } spin_unlock(&dstream->ds_ext_lock); /* We will search for the extent that covers iblock */ if (!apfs_is_sealed(sb)) { apfs_init_file_extent_key(dstream->ds_id, iaddr, &key); root = sbi->s_cat_root; } else { apfs_init_fext_key(dstream->ds_id, iaddr, &key); root = apfs_read_node(sb, le64_to_cpu(vsb_raw->apfs_fext_tree_oid), APFS_OBJ_PHYSICAL, false /* write */); if (IS_ERR(root)) { apfs_err(sb, "failed to read fext root 0x%llx", le64_to_cpu(vsb_raw->apfs_fext_tree_oid)); return PTR_ERR(root); } } query = apfs_alloc_query(root, NULL /* parent */); if (!query) { ret = -ENOMEM; goto done; } query->key = key; query->flags = apfs_is_sealed(sb) ? APFS_QUERY_FEXT : APFS_QUERY_CAT; ret = apfs_btree_query(sb, &query); if (ret) { apfs_err(sb, "query failed for id 0x%llx, addr 0x%llx", dstream->ds_id, iaddr); if (ret == -ENODATA) ret = -EFSCORRUPTED; goto done; } ret = apfs_extent_from_query(query, extent); if (ret) { apfs_err(sb, "bad extent record for dstream 0x%llx", dstream->ds_id); goto done; } if (iaddr < extent->logical_addr || iaddr >= extent->logical_addr + extent->len) { apfs_err(sb, "no extent for addr 0x%llx in dstream 0x%llx", iaddr, dstream->ds_id); ret = -EFSCORRUPTED; goto done; } /* * For now prioritize the deferral of writes. * i_extent_dirty is protected by the read semaphore. */ if (!dstream->ds_ext_dirty) { spin_lock(&dstream->ds_ext_lock); *cache = *extent; spin_unlock(&dstream->ds_ext_lock); } done: apfs_free_query(query); if (apfs_is_sealed(sb)) apfs_node_free(root); return ret; } /** * apfs_logic_to_phys_bno - Find the physical block number for a dstream block * @dstream: data stream info * @dsblock: logical number of the wanted block * @bno: on return, the physical block number (or zero for holes) * * Returns 0 on success or a negative error code in case of failure. */ int apfs_logic_to_phys_bno(struct apfs_dstream_info *dstream, sector_t dsblock, u64 *bno) { struct super_block *sb = dstream->ds_sb; struct apfs_file_extent ext; u64 blk_off; int ret; ret = apfs_extent_read(dstream, dsblock, &ext); if (ret) return ret; if (apfs_ext_is_hole(&ext)) { *bno = 0; return 0; } /* Find the block offset of iblock within the extent */ blk_off = dsblock - (ext.logical_addr >> sb->s_blocksize_bits); *bno = ext.phys_block_num + blk_off; return 0; } /* This does the same as apfs_get_block(), but without taking any locks */ int __apfs_get_block(struct apfs_dstream_info *dstream, sector_t dsblock, struct buffer_head *bh_result, int create) { struct super_block *sb = dstream->ds_sb; struct apfs_file_extent ext; u64 blk_off, bno, map_len; int ret; ASSERT(!create); if (dsblock >= apfs_size_to_blocks(sb, dstream->ds_size)) return 0; ret = apfs_extent_read(dstream, dsblock, &ext); if (ret) { apfs_err(sb, "extent read failed"); return ret; } /* Find the block offset of iblock within the extent */ blk_off = dsblock - (ext.logical_addr >> sb->s_blocksize_bits); /* Make sure we don't read past the extent boundaries */ map_len = ext.len - (blk_off << sb->s_blocksize_bits); if (bh_result->b_size > map_len) bh_result->b_size = map_len; /* * Save the requested mapping length as apfs_map_bh() replaces it with * the filesystem block size */ map_len = bh_result->b_size; /* Extents representing holes have block number 0 */ if (!apfs_ext_is_hole(&ext)) { /* Find the block number of iblock within the disk */ bno = ext.phys_block_num + blk_off; apfs_map_bh(bh_result, sb, bno); } bh_result->b_size = map_len; return 0; } int apfs_get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create) { struct apfs_nxsb_info *nxi = APFS_NXI(inode->i_sb); struct apfs_inode_info *ai = APFS_I(inode); int ret; down_read(&nxi->nx_big_sem); ret = __apfs_get_block(&ai->i_dstream, iblock, bh_result, create); up_read(&nxi->nx_big_sem); return ret; } /** * apfs_set_extent_length - Set a new length in an extent record's value * @ext: the extent record's value * @len: the new length * * Preserves the flags, though none are defined yet and I don't know if that * will ever be important. */ static inline void apfs_set_extent_length(struct apfs_file_extent_val *ext, u64 len) { u64 len_and_flags = le64_to_cpu(ext->len_and_flags); u64 flags = len_and_flags & APFS_FILE_EXTENT_FLAG_MASK; ext->len_and_flags = cpu_to_le64(flags | len); } static int apfs_range_put_reference(struct super_block *sb, u64 paddr, u64 length); /** * apfs_shrink_extent_head - Shrink an extent record in its head * @query: the query that found the record * @dstream: data stream info * @start: new logical start for the extent * * Also deletes the physical extent records for the head. Returns 0 on success * or a negative error code in case of failure. */ static int apfs_shrink_extent_head(struct apfs_query *query, struct apfs_dstream_info *dstream, u64 start) { struct super_block *sb = query->node->object.sb; struct apfs_file_extent_key key; struct apfs_file_extent_val val; struct apfs_file_extent extent; u64 new_len, head_len; void *raw = NULL; int err = 0; err = apfs_extent_from_query(query, &extent); if (err) { apfs_err(sb, "bad extent record for dstream 0x%llx", dstream->ds_id); return err; } raw = query->node->object.data; key = *(struct apfs_file_extent_key *)(raw + query->key_off); val = *(struct apfs_file_extent_val *)(raw + query->off); new_len = extent.logical_addr + extent.len - start; head_len = extent.len - new_len; /* Delete the physical records for the blocks lost in the shrinkage */ if (!apfs_ext_is_hole(&extent)) { err = apfs_range_put_reference(sb, extent.phys_block_num, head_len); if (err) { apfs_err(sb, "failed to put range 0x%llx-0x%llx", extent.phys_block_num, head_len); return err; } } else { dstream->ds_sparse_bytes -= head_len; } /* This is the actual shrinkage of the logical extent */ key.logical_addr = cpu_to_le64(start); apfs_set_extent_length(&val, new_len); if (!apfs_ext_is_hole(&extent)) le64_add_cpu(&val.phys_block_num, head_len >> sb->s_blocksize_bits); return apfs_btree_replace(query, &key, sizeof(key), &val, sizeof(val)); } /** * apfs_shrink_extent_tail - Shrink an extent record in its tail * @query: the query that found the record * @dstream: data stream info * @end: new logical end for the extent * * Also puts the physical extent records for the tail. Returns 0 on success or * a negative error code in case of failure. */ static int apfs_shrink_extent_tail(struct apfs_query *query, struct apfs_dstream_info *dstream, u64 end) { struct super_block *sb = query->node->object.sb; struct apfs_file_extent_val *val; struct apfs_file_extent extent; u64 new_len, new_blkcount, tail_len; void *raw; int err = 0; ASSERT((end & (sb->s_blocksize - 1)) == 0); err = apfs_query_join_transaction(query); if (err) { apfs_err(sb, "query join failed"); return err; } raw = query->node->object.data; err = apfs_extent_from_query(query, &extent); if (err) { apfs_err(sb, "bad extent record for dstream 0x%llx", dstream->ds_id); return err; } val = raw + query->off; new_len = end - extent.logical_addr; new_blkcount = new_len >> sb->s_blocksize_bits; tail_len = extent.len - new_len; /* Delete the physical records for the blocks lost in the shrinkage */ if (!apfs_ext_is_hole(&extent)) { err = apfs_range_put_reference(sb, extent.phys_block_num + new_blkcount, tail_len); if (err) { apfs_err(sb, "failed to put range 0x%llx-0x%llx", extent.phys_block_num + new_blkcount, tail_len); return err; } } else { dstream->ds_sparse_bytes -= tail_len; } /* This is the actual shrinkage of the logical extent */ apfs_set_extent_length(val, new_len); return err; } /** * apfs_query_found_extent - Did this query find an extent with the right id? * @query: the (successful) query that found the record */ static inline bool apfs_query_found_extent(struct apfs_query *query) { void *raw = query->node->object.data; struct apfs_key_header *hdr; if (query->key_len < sizeof(*hdr)) return false; hdr = raw + query->key_off; if (apfs_cat_type(hdr) != APFS_TYPE_FILE_EXTENT) return false; if (apfs_cat_cnid(hdr) != query->key.id) return false; return true; } /** * apfs_update_tail_extent - Grow the tail extent for a data stream * @dstream: data stream info * @extent: new in-memory extent * * Also takes care of any needed changes to the physical extent records. Returns * 0 on success or a negative error code in case of failure. */ static int apfs_update_tail_extent(struct apfs_dstream_info *dstream, const struct apfs_file_extent *extent) { struct super_block *sb = dstream->ds_sb; struct apfs_sb_info *sbi = APFS_SB(sb); struct apfs_query *query; struct apfs_file_extent_key raw_key; struct apfs_file_extent_val raw_val; u64 extent_id = dstream->ds_id; int ret; u64 new_crypto; apfs_key_set_hdr(APFS_TYPE_FILE_EXTENT, extent_id, &raw_key); raw_key.logical_addr = cpu_to_le64(extent->logical_addr); raw_val.len_and_flags = cpu_to_le64(extent->len); raw_val.phys_block_num = cpu_to_le64(extent->phys_block_num); if (apfs_vol_is_encrypted(sb)) new_crypto = extent_id; else new_crypto = 0; raw_val.crypto_id = cpu_to_le64(new_crypto); query = apfs_alloc_query(sbi->s_cat_root, NULL /* parent */); if (!query) return -ENOMEM; /* We want the last extent record */ apfs_init_file_extent_key(extent_id, -1, &query->key); query->flags = APFS_QUERY_CAT; ret = apfs_btree_query(sb, &query); if (ret && ret != -ENODATA) { apfs_err(sb, "query failed for last extent of id 0x%llx", extent_id); goto out; } if (ret == -ENODATA || !apfs_query_found_extent(query)) { /* We are creating the first extent for the file */ ret = apfs_btree_insert(query, &raw_key, sizeof(raw_key), &raw_val, sizeof(raw_val)); if (ret) { apfs_err(sb, "insertion failed for id 0x%llx, addr 0x%llx", extent_id, extent->logical_addr); goto out; } } else { struct apfs_file_extent tail; ret = apfs_extent_from_query(query, &tail); if (ret) { apfs_err(sb, "bad extent record for dstream 0x%llx", dstream->ds_id); goto out; } if (tail.logical_addr > extent->logical_addr) { apfs_alert(sb, "extent is not tail - bug!"); ret = -EOPNOTSUPP; goto out; } else if (tail.logical_addr == extent->logical_addr) { ret = apfs_btree_replace(query, &raw_key, sizeof(raw_key), &raw_val, sizeof(raw_val)); if (ret) { apfs_err(sb, "update failed for id 0x%llx, addr 0x%llx", extent_id, extent->logical_addr); goto out; } if (apfs_ext_is_hole(&tail)) { dstream->ds_sparse_bytes -= tail.len; } else if (tail.phys_block_num != extent->phys_block_num) { ret = apfs_range_put_reference(sb, tail.phys_block_num, tail.len); if (ret) { apfs_err(sb, "failed to put range 0x%llx-0x%llx", tail.phys_block_num, tail.len); goto out; } } if (new_crypto == tail.crypto_id) goto out; ret = apfs_crypto_adj_refcnt(sb, tail.crypto_id, -1); if (ret) { apfs_err(sb, "failed to put crypto id 0x%llx", tail.crypto_id); goto out; } } else { /* * TODO: we could actually also continue the tail extent * if it's right next to the new one (both logically and * physically), even if they don't overlap. Or maybe we * should always make sure that the tail extent is in * the cache before a write... */ if (extent->logical_addr < tail.logical_addr + tail.len) { ret = apfs_shrink_extent_tail(query, dstream, extent->logical_addr); if (ret) { apfs_err(sb, "failed to shrink tail of dstream 0x%llx", extent_id); goto out; } } ret = apfs_btree_insert(query, &raw_key, sizeof(raw_key), &raw_val, sizeof(raw_val)); if (ret) { apfs_err(sb, "insertion failed for id 0x%llx, addr 0x%llx", extent_id, extent->logical_addr); goto out; } } } ret = apfs_crypto_adj_refcnt(sb, new_crypto, 1); if (ret) apfs_err(sb, "failed to take crypto id 0x%llx", new_crypto); out: apfs_free_query(query); return ret; } /** * apfs_split_extent - Break an extent in two * @query: query pointing to the extent * @div: logical address for the division * * Returns 0 on success or a negative error code in case of failure. */ static int apfs_split_extent(struct apfs_query *query, u64 div) { struct super_block *sb = query->node->object.sb; struct apfs_file_extent_val *val1; struct apfs_file_extent_key key2; struct apfs_file_extent_val val2; struct apfs_file_extent extent; u64 len1, len2, blkcount1; void *raw; int err = 0; err = apfs_query_join_transaction(query); if (err) { apfs_err(sb, "query join failed"); return err; } raw = query->node->object.data; err = apfs_extent_from_query(query, &extent); if (err) { apfs_err(sb, "bad extent record"); return err; } val1 = raw + query->off; val2 = *(struct apfs_file_extent_val *)(raw + query->off); key2 = *(struct apfs_file_extent_key *)(raw + query->key_off); len1 = div - extent.logical_addr; blkcount1 = len1 >> sb->s_blocksize_bits; len2 = extent.len - len1; /* Modify the current extent in place to become the first half */ apfs_set_extent_length(val1, len1); /* Insert the second half right after the first */ key2.logical_addr = cpu_to_le64(div); if (!apfs_ext_is_hole(&extent)) val2.phys_block_num = cpu_to_le64(extent.phys_block_num + blkcount1); apfs_set_extent_length(&val2, len2); err = apfs_btree_insert(query, &key2, sizeof(key2), &val2, sizeof(val2)); if (err) { apfs_err(sb, "insertion failed in division 0x%llx", div); return err; } return apfs_crypto_adj_refcnt(sb, extent.crypto_id, 1); } /** * apfs_update_mid_extent - Create or update a non-tail extent for a dstream * @dstream: data stream info * @extent: new in-memory extent * * Also takes care of any needed changes to the physical extent records. Returns * 0 on success or a negative error code in case of failure. */ static int apfs_update_mid_extent(struct apfs_dstream_info *dstream, const struct apfs_file_extent *extent) { struct super_block *sb = dstream->ds_sb; struct apfs_sb_info *sbi = APFS_SB(sb); struct apfs_key key; struct apfs_query *query; struct apfs_file_extent_key raw_key; struct apfs_file_extent_val raw_val; struct apfs_file_extent prev_ext; u64 extent_id = dstream->ds_id; u64 prev_crypto, new_crypto; u64 prev_start, prev_end; bool second_run = false; int ret; apfs_key_set_hdr(APFS_TYPE_FILE_EXTENT, extent_id, &raw_key); raw_key.logical_addr = cpu_to_le64(extent->logical_addr); raw_val.len_and_flags = cpu_to_le64(extent->len); raw_val.phys_block_num = cpu_to_le64(extent->phys_block_num); if (apfs_vol_is_encrypted(sb)) new_crypto = extent_id; else new_crypto = 0; raw_val.crypto_id = cpu_to_le64(new_crypto); apfs_init_file_extent_key(extent_id, extent->logical_addr, &key); search_and_insert: query = apfs_alloc_query(sbi->s_cat_root, NULL /* parent */); if (!query) return -ENOMEM; query->key = key; query->flags = APFS_QUERY_CAT; ret = apfs_btree_query(sb, &query); if (ret && ret != -ENODATA) { apfs_err(sb, "query failed for id 0x%llx, addr 0x%llx", extent_id, extent->logical_addr); goto out; } if (ret == -ENODATA || !apfs_query_found_extent(query)) { /* * The new extent goes in a hole we just made, right at the * beginning of the file. */ if (!second_run) { apfs_err(sb, "missing extent on dstream 0x%llx", extent_id); ret = -EFSCORRUPTED; } else { ret = apfs_btree_insert(query, &raw_key, sizeof(raw_key), &raw_val, sizeof(raw_val)); if (ret) apfs_err(sb, "insertion failed for id 0x%llx, addr 0x%llx", extent_id, extent->logical_addr); } goto out; } if (apfs_extent_from_query(query, &prev_ext)) { apfs_err(sb, "bad mid extent record on dstream 0x%llx", extent_id); ret = -EFSCORRUPTED; goto out; } prev_crypto = prev_ext.crypto_id; prev_start = prev_ext.logical_addr; prev_end = prev_ext.logical_addr + prev_ext.len; if (prev_end == extent->logical_addr && second_run) { /* The new extent goes in the hole we just made */ ret = apfs_btree_insert(query, &raw_key, sizeof(raw_key), &raw_val, sizeof(raw_val)); if (ret) { apfs_err(sb, "insertion failed for id 0x%llx, addr 0x%llx", extent_id, extent->logical_addr); goto out; } } else if (prev_start == extent->logical_addr && prev_ext.len == extent->len) { /* The old and new extents are the same logical block */ ret = apfs_btree_replace(query, &raw_key, sizeof(raw_key), &raw_val, sizeof(raw_val)); if (ret) { apfs_err(sb, "update failed for id 0x%llx, addr 0x%llx", extent_id, extent->logical_addr); goto out; } if (apfs_ext_is_hole(&prev_ext)) { dstream->ds_sparse_bytes -= prev_ext.len; } else if (prev_ext.phys_block_num != extent->phys_block_num) { ret = apfs_range_put_reference(sb, prev_ext.phys_block_num, prev_ext.len); if (ret) { apfs_err(sb, "failed to put range 0x%llx-0x%llx", prev_ext.phys_block_num, prev_ext.len); goto out; } } ret = apfs_crypto_adj_refcnt(sb, prev_crypto, -1); if (ret) { apfs_err(sb, "failed to put crypto id 0x%llx", prev_crypto); goto out; } } else if (prev_start == extent->logical_addr) { /* The new extent is the first logical block of the old one */ if (second_run) { /* I don't know if this is possible, but be safe */ apfs_alert(sb, "recursion shrinking extent head for dstream 0x%llx", extent_id); ret = -EFSCORRUPTED; goto out; } ret = apfs_shrink_extent_head(query, dstream, extent->logical_addr + extent->len); if (ret) { apfs_err(sb, "failed to shrink extent in dstream 0x%llx", extent_id); goto out; } /* The query should point to the previous record, start again */ apfs_free_query(query); second_run = true; goto search_and_insert; } else if (prev_end == extent->logical_addr + extent->len) { /* The new extent is the last logical block of the old one */ ret = apfs_shrink_extent_tail(query, dstream, extent->logical_addr); if (ret) { apfs_err(sb, "failed to shrink extent in dstream 0x%llx", extent_id); goto out; } ret = apfs_btree_insert(query, &raw_key, sizeof(raw_key), &raw_val, sizeof(raw_val)); if (ret) { apfs_err(sb, "insertion failed for id 0x%llx, addr 0x%llx", extent_id, extent->logical_addr); goto out; } } else if (prev_start < extent->logical_addr && prev_end > extent->logical_addr + extent->len) { /* The new extent is logically in the middle of the old one */ if (second_run) { /* I don't know if this is possible, but be safe */ apfs_alert(sb, "recursion when splitting extents for dstream 0x%llx", extent_id); ret = -EFSCORRUPTED; goto out; } ret = apfs_split_extent(query, extent->logical_addr + extent->len); if (ret) { apfs_err(sb, "failed to split extent in dstream 0x%llx", extent_id); goto out; } /* The split may make the query invalid */ apfs_free_query(query); second_run = true; goto search_and_insert; } else { /* I don't know what this is, be safe */ apfs_alert(sb, "strange extents for dstream 0x%llx", extent_id); ret = -EFSCORRUPTED; goto out; } ret = apfs_crypto_adj_refcnt(sb, new_crypto, 1); if (ret) apfs_err(sb, "failed to take crypto id 0x%llx", new_crypto); out: apfs_free_query(query); return ret; } /** * apfs_update_extent - Create or update the extent record for an extent * @dstream: data stream info * @extent: new in-memory file extent * * The @extent must either be a new tail for the dstream, or a single block. * Returns 0 on success or a negative error code in case of failure. */ static int apfs_update_extent(struct apfs_dstream_info *dstream, const struct apfs_file_extent *extent) { struct super_block *sb = dstream->ds_sb; if (extent->logical_addr + extent->len >= dstream->ds_size) return apfs_update_tail_extent(dstream, extent); if (extent->len > sb->s_blocksize) { apfs_err(sb, "can't create mid extents of length 0x%llx", extent->len); return -EOPNOTSUPP; } return apfs_update_mid_extent(dstream, extent); } static int apfs_extend_phys_extent(struct apfs_query *query, u64 bno, u64 blkcnt, u64 dstream_id) { struct apfs_phys_ext_key raw_key; struct apfs_phys_ext_val raw_val; u64 kind = (u64)APFS_KIND_NEW << APFS_PEXT_KIND_SHIFT; apfs_key_set_hdr(APFS_TYPE_EXTENT, bno, &raw_key); raw_val.len_and_kind = cpu_to_le64(kind | blkcnt); raw_val.owning_obj_id = cpu_to_le64(dstream_id); raw_val.refcnt = cpu_to_le32(1); return apfs_btree_replace(query, &raw_key, sizeof(raw_key), &raw_val, sizeof(raw_val)); } static int apfs_insert_new_phys_extent(struct apfs_query *query, u64 bno, u64 blkcnt, u64 dstream_id) { struct apfs_phys_ext_key raw_key; struct apfs_phys_ext_val raw_val; u64 kind = (u64)APFS_KIND_NEW << APFS_PEXT_KIND_SHIFT; apfs_key_set_hdr(APFS_TYPE_EXTENT, bno, &raw_key); raw_val.len_and_kind = cpu_to_le64(kind | blkcnt); raw_val.owning_obj_id = cpu_to_le64(dstream_id); raw_val.refcnt = cpu_to_le32(1); return apfs_btree_insert(query, &raw_key, sizeof(raw_key), &raw_val, sizeof(raw_val)); } static int apfs_phys_ext_from_query(struct apfs_query *query, struct apfs_phys_extent *pext); /** * apfs_insert_phys_extent - Create or grow the physical record for an extent * @dstream: data stream info for the extent * @extent: new in-memory file extent * * Only works for appending to extents, for now. TODO: reference counting. * Returns 0 on success or a negative error code in case of failure. */ static int apfs_insert_phys_extent(struct apfs_dstream_info *dstream, const struct apfs_file_extent *extent) { struct super_block *sb = dstream->ds_sb; struct apfs_superblock *vsb_raw = APFS_SB(sb)->s_vsb_raw; struct apfs_node *extref_root; struct apfs_query *query = NULL; struct apfs_phys_extent pext; u64 blkcnt = extent->len >> sb->s_blocksize_bits; u64 last_bno, new_base, new_blkcnt; int ret; extref_root = apfs_read_node(sb, le64_to_cpu(vsb_raw->apfs_extentref_tree_oid), APFS_OBJ_PHYSICAL, true /* write */); if (IS_ERR(extref_root)) { apfs_err(sb, "failed to read extref root 0x%llx", le64_to_cpu(vsb_raw->apfs_extentref_tree_oid)); return PTR_ERR(extref_root); } apfs_assert_in_transaction(sb, &vsb_raw->apfs_o); vsb_raw->apfs_extentref_tree_oid = cpu_to_le64(extref_root->object.oid); query = apfs_alloc_query(extref_root, NULL /* parent */); if (!query) { ret = -ENOMEM; goto out; } /* * The cached logical extent may have been split into multiple physical * extents because of clones. If that happens, we want to grow the last * one. */ last_bno = extent->phys_block_num + blkcnt - 1; apfs_init_extent_key(last_bno, &query->key); query->flags = APFS_QUERY_EXTENTREF; ret = apfs_btree_query(sb, &query); if (ret && ret != -ENODATA) { apfs_err(sb, "query failed for paddr 0x%llx", last_bno); goto out; } if (ret == -ENODATA) { /* This is a fresh new physical extent */ ret = apfs_insert_new_phys_extent(query, extent->phys_block_num, blkcnt, dstream->ds_id); if (ret) apfs_err(sb, "insertion failed for paddr 0x%llx", extent->phys_block_num); goto out; } ret = apfs_phys_ext_from_query(query, &pext); if (ret) { apfs_err(sb, "bad pext record for bno 0x%llx", last_bno); goto out; } if (pext.bno + pext.blkcount <= extent->phys_block_num) { /* Also a fresh new physical extent */ ret = apfs_insert_new_phys_extent(query, extent->phys_block_num, blkcnt, dstream->ds_id); if (ret) apfs_err(sb, "insertion failed for paddr 0x%llx", extent->phys_block_num); goto out; } /* * There is an existing physical extent that overlaps the new one. The * cache was dirty, so the existing extent can't cover the whole tail. */ if (pext.bno + pext.blkcount >= extent->phys_block_num + blkcnt) { apfs_err(sb, "dirty cache tail covered by existing physical extent 0x%llx-0x%llx", pext.bno, pext.blkcount); ret = -EFSCORRUPTED; goto out; } if (pext.refcnt == 1) { new_base = pext.bno; new_blkcnt = extent->phys_block_num + blkcnt - new_base; ret = apfs_extend_phys_extent(query, new_base, new_blkcnt, dstream->ds_id); if (ret) apfs_err(sb, "update failed for paddr 0x%llx", new_base); } else { /* * We can't extend this one, because it would extend the other * references as well. */ new_base = pext.bno + pext.blkcount; new_blkcnt = extent->phys_block_num + blkcnt - new_base; ret = apfs_insert_new_phys_extent(query, new_base, new_blkcnt, dstream->ds_id); if (ret) apfs_err(sb, "insertion failed for paddr 0x%llx", new_base); } out: apfs_free_query(query); apfs_node_free(extref_root); return ret; } /** * apfs_phys_ext_from_query - Read the physical extent record found by a query * @query: the (successful) query that found the record * @pext: on return, the physical extent read * * Reads the physical extent record into @pext and performs some basic sanity * checks as a protection against crafted filesystems. Returns 0 on success or * -EFSCORRUPTED otherwise. */ static int apfs_phys_ext_from_query(struct apfs_query *query, struct apfs_phys_extent *pext) { struct super_block *sb = query->node->object.sb; struct apfs_phys_ext_key *key; struct apfs_phys_ext_val *val; char *raw = query->node->object.data; if (query->len != sizeof(*val) || query->key_len != sizeof(*key)) { apfs_err(sb, "bad length of key (%d) or value (%d)", query->key_len, query->len); return -EFSCORRUPTED; } key = (struct apfs_phys_ext_key *)(raw + query->key_off); val = (struct apfs_phys_ext_val *)(raw + query->off); pext->bno = apfs_cat_cnid(&key->hdr); pext->blkcount = le64_to_cpu(val->len_and_kind) & APFS_PEXT_LEN_MASK; pext->len = pext->blkcount << sb->s_blocksize_bits; pext->refcnt = le32_to_cpu(val->refcnt); pext->kind = le64_to_cpu(val->len_and_kind) >> APFS_PEXT_KIND_SHIFT; return 0; } /** * apfs_free_phys_ext - Add all blocks in a physical extent to the free queue * @sb: superblock structure * @pext: physical range to free * * Returns 0 on success or a negative error code in case of failure. */ static int apfs_free_phys_ext(struct super_block *sb, struct apfs_phys_extent *pext) { struct apfs_superblock *vsb_raw = APFS_SB(sb)->s_vsb_raw; apfs_assert_in_transaction(sb, &vsb_raw->apfs_o); le64_add_cpu(&vsb_raw->apfs_fs_alloc_count, -pext->blkcount); le64_add_cpu(&vsb_raw->apfs_total_blocks_freed, pext->blkcount); return apfs_free_queue_insert(sb, pext->bno, pext->blkcount); } /** * apfs_put_phys_extent - Reduce the reference count for a physical extent * @pext: physical extent data, already read * @query: query that found the extent * * Returns 0 on success or a negative error code in case of failure. */ static int apfs_put_phys_extent(struct apfs_phys_extent *pext, struct apfs_query *query) { struct super_block *sb = query->node->object.sb; struct apfs_phys_ext_val *val; void *raw; int err; if (--pext->refcnt == 0) { err = apfs_btree_remove(query); if (err) { apfs_err(sb, "removal failed for paddr 0x%llx", pext->bno); return err; } return pext->kind == APFS_KIND_NEW ? apfs_free_phys_ext(sb, pext) : 0; } err = apfs_query_join_transaction(query); if (err) { apfs_err(sb, "query join failed"); return err; } raw = query->node->object.data; val = raw + query->off; val->refcnt = cpu_to_le32(pext->refcnt); return 0; } /** * apfs_take_phys_extent - Increase the reference count for a physical extent * @pext: physical extent data, already read * @query: query that found the extent * * Returns 0 on success or a negative error code in case of failure. */ static int apfs_take_phys_extent(struct apfs_phys_extent *pext, struct apfs_query *query) { struct super_block *sb = query->node->object.sb; struct apfs_phys_ext_val *val; void *raw; int err; /* An update extent may be dropped when a reference is taken */ if (++pext->refcnt == 0) return apfs_btree_remove(query); err = apfs_query_join_transaction(query); if (err) { apfs_err(sb, "query join failed"); return err; } raw = query->node->object.data; val = raw + query->off; val->refcnt = cpu_to_le32(pext->refcnt); return 0; } /** * apfs_set_phys_ext_length - Set new length in a physical extent record's value * @pext: the physical extent record's value * @len: the new length (in blocks) * * Preserves the kind, though I doubt that's the right thing to do in general. */ static inline void apfs_set_phys_ext_length(struct apfs_phys_ext_val *pext, u64 len) { u64 len_and_kind = le64_to_cpu(pext->len_and_kind); u64 kind = len_and_kind & APFS_PEXT_KIND_MASK; pext->len_and_kind = cpu_to_le64(kind | len); } /** * apfs_split_phys_ext - Break a physical extent in two * @query: query pointing to the extent * @div: first physical block number to come after the division * * Returns 0 on success or a negative error code in case of failure. */ static int apfs_split_phys_ext(struct apfs_query *query, u64 div) { struct super_block *sb = query->node->object.sb; struct apfs_phys_ext_val *val1; struct apfs_phys_ext_key key2; struct apfs_phys_ext_val val2; struct apfs_phys_extent pextent; u64 blkcount1, blkcount2; void *raw; int err = 0; err = apfs_query_join_transaction(query); if (err) { apfs_err(sb, "query join failed"); return err; } raw = query->node->object.data; err = apfs_phys_ext_from_query(query, &pextent); if (err) { apfs_err(sb, "bad pext record over div 0x%llx", div); return err; } val1 = raw + query->off; val2 = *(struct apfs_phys_ext_val *)(raw + query->off); key2 = *(struct apfs_phys_ext_key *)(raw + query->key_off); blkcount1 = div - pextent.bno; blkcount2 = pextent.blkcount - blkcount1; /* Modify the current extent in place to become the first half */ apfs_set_phys_ext_length(val1, blkcount1); /* Insert the second half right after the first */ apfs_key_set_hdr(APFS_TYPE_EXTENT, div, &key2); apfs_set_phys_ext_length(&val2, blkcount2); return apfs_btree_insert(query, &key2, sizeof(key2), &val2, sizeof(val2)); } /** * apfs_create_update_pext - Create a reference update physical extent record * @query: query that searched for the physical extent * @extent: range of physical blocks to update * @diff: reference count change * * Returns 0 on success or a negative error code in case of failure. */ static int apfs_create_update_pext(struct apfs_query *query, const struct apfs_file_extent *extent, u32 diff) { struct super_block *sb = query->node->object.sb; struct apfs_phys_ext_key key = {0}; struct apfs_phys_ext_val val = {0}; apfs_key_set_hdr(APFS_TYPE_EXTENT, extent->phys_block_num, &key); val.len_and_kind = cpu_to_le64((u64)APFS_KIND_UPDATE << APFS_PEXT_KIND_SHIFT | extent->len >> sb->s_blocksize_bits); val.owning_obj_id = cpu_to_le64(APFS_OWNING_OBJ_ID_INVALID); val.refcnt = cpu_to_le32(diff); return apfs_btree_insert(query, &key, sizeof(key), &val, sizeof(val)); } /** * apfs_dstream_cache_is_tail - Is the tail of this dstream in its extent cache? * @dstream: dstream to check */ static inline bool apfs_dstream_cache_is_tail(struct apfs_dstream_info *dstream) { struct apfs_file_extent *cache = &dstream->ds_cached_ext; /* nx_big_sem provides the locking for the cache here */ return cache->len && (dstream->ds_size <= cache->logical_addr + cache->len); } /** * apfs_flush_extent_cache - Write the cached extent to the catalog, if dirty * @dstream: data stream to flush * * Returns 0 on success or a negative error code in case of failure. */ int apfs_flush_extent_cache(struct apfs_dstream_info *dstream) { struct super_block *sb = dstream->ds_sb; struct apfs_file_extent *ext = &dstream->ds_cached_ext; int err; if (!dstream->ds_ext_dirty) return 0; ASSERT(ext->len > 0); err = apfs_update_extent(dstream, ext); if (err) { apfs_err(sb, "extent update failed"); return err; } err = apfs_insert_phys_extent(dstream, ext); if (err) { apfs_err(sb, "pext insertion failed"); return err; } /* * TODO: keep track of the byte and block count through the use of * inode_add_bytes() and inode_set_bytes(). This hasn't been done with * care in the rest of the module and it doesn't seem to matter beyond * stat(), so I'm ignoring it for now. */ dstream->ds_ext_dirty = false; return 0; } /** * apfs_create_hole - Create and insert a hole extent for the dstream * @dstream: data stream info * @start: first logical block number for the hole * @end: first logical block number right after the hole * * Returns 0 on success, or a negative error code in case of failure. * TODO: what happens to the crypto refcount? */ static int apfs_create_hole(struct apfs_dstream_info *dstream, u64 start, u64 end) { struct super_block *sb = dstream->ds_sb; struct apfs_sb_info *sbi = APFS_SB(sb); struct apfs_query *query; struct apfs_file_extent_key raw_key; struct apfs_file_extent_val raw_val; u64 extent_id = dstream->ds_id; int ret; if (start == end) return 0; /* File extent records use addresses, not block numbers */ start <<= sb->s_blocksize_bits; end <<= sb->s_blocksize_bits; apfs_key_set_hdr(APFS_TYPE_FILE_EXTENT, extent_id, &raw_key); raw_key.logical_addr = cpu_to_le64(start); raw_val.len_and_flags = cpu_to_le64(end - start); raw_val.phys_block_num = cpu_to_le64(0); /* It's a hole... */ raw_val.crypto_id = cpu_to_le64(apfs_vol_is_encrypted(sb) ? extent_id : 0); query = apfs_alloc_query(sbi->s_cat_root, NULL /* parent */); if (!query) return -ENOMEM; apfs_init_file_extent_key(extent_id, start, &query->key); query->flags = APFS_QUERY_CAT; ret = apfs_btree_query(sb, &query); if (ret && ret != -ENODATA) { apfs_err(sb, "query failed for id 0x%llx, addr 0x%llx", extent_id, start); goto out; } ret = apfs_btree_insert(query, &raw_key, sizeof(raw_key), &raw_val, sizeof(raw_val)); if (ret) apfs_err(sb, "insertion failed for id 0x%llx, addr 0x%llx", extent_id, start); dstream->ds_sparse_bytes += end - start; out: apfs_free_query(query); return ret; } /** * apfs_zero_dstream_tail - Zero out stale bytes in a data stream's last block * @dstream: data stream info * * Returns 0 on success, or a negative error code in case of failure. */ static int apfs_zero_dstream_tail(struct apfs_dstream_info *dstream) { struct super_block *sb = dstream->ds_sb; struct inode *inode = NULL; struct page *page = NULL; void *fsdata = NULL; int valid_length; int err; /* No stale bytes if no actual content */ if (dstream->ds_size <= dstream->ds_sparse_bytes) return 0; /* No stale tail if the last block is fully used */ valid_length = dstream->ds_size & (sb->s_blocksize - 1); if (valid_length == 0) return 0; inode = dstream->ds_inode; if (!inode) { /* This should never happen, but be safe */ apfs_alert(sb, "attempt to zero the tail of xattr dstream 0x%llx", dstream->ds_id); return -EFSCORRUPTED; } /* This will take care of the CoW and zeroing */ err = __apfs_write_begin(NULL, inode->i_mapping, inode->i_size, 0, 0, &page, &fsdata); if (err) return err; return __apfs_write_end(NULL, inode->i_mapping, inode->i_size, 0, 0, page, fsdata); } /** * apfs_zero_bh_tail - Zero out stale bytes in a buffer head * @sb: filesystem superblock * @bh: buffer head to zero * @length: length of valid bytes to be left alone */ static void apfs_zero_bh_tail(struct super_block *sb, struct buffer_head *bh, u64 length) { ASSERT(buffer_trans(bh)); if (length < sb->s_blocksize) memset(bh->b_data + length, 0, sb->s_blocksize - length); } /** * apfs_range_in_snap - Check if a given block range overlaps a snapshot * @sb: filesystem superblock * @bno: first block in the range * @blkcnt: block count for the range * @in_snap: on return, the result * * Returns 0 on success, or a negative error code in case of failure. */ static int apfs_range_in_snap(struct super_block *sb, u64 bno, u64 blkcnt, bool *in_snap) { struct apfs_superblock *vsb_raw = APFS_SB(sb)->s_vsb_raw; struct apfs_node *extref_root = NULL; struct apfs_query *query = NULL; struct apfs_phys_extent pext = {0}; int ret; /* Avoid the tree queries when we don't even have snapshots */ if (vsb_raw->apfs_num_snapshots == 0) { *in_snap = false; return 0; } /* * Now check if the current physical extent tree has an entry for * these blocks */ extref_root = apfs_read_node(sb, le64_to_cpu(vsb_raw->apfs_extentref_tree_oid), APFS_OBJ_PHYSICAL, false /* write */); if (IS_ERR(extref_root)) { apfs_err(sb, "failed to read extref root 0x%llx", le64_to_cpu(vsb_raw->apfs_extentref_tree_oid)); return PTR_ERR(extref_root); } query = apfs_alloc_query(extref_root, NULL /* parent */); if (!query) { ret = -ENOMEM; goto out; } apfs_init_extent_key(bno, &query->key); query->flags = APFS_QUERY_EXTENTREF; ret = apfs_btree_query(sb, &query); if (ret && ret != -ENODATA) { apfs_err(sb, "query failed for paddr 0x%llx", bno); goto out; } if (ret == -ENODATA) { *in_snap = false; ret = 0; goto out; } ret = apfs_phys_ext_from_query(query, &pext); if (ret) { apfs_err(sb, "bad pext record for paddr 0x%llx", bno); goto out; } if (pext.bno <= bno && pext.bno + pext.blkcount >= bno + blkcnt) { if (pext.kind == APFS_KIND_NEW) { *in_snap = false; goto out; } } /* * I think the file extent could still be covered by two different * physical extents from the current tree, but it's easier to just * assume the worst here. */ *in_snap = true; out: apfs_free_query(query); apfs_node_free(extref_root); return ret; } /** * apfs_dstream_cache_in_snap - Check if the cached extent overlaps a snapshot * @dstream: the data stream to check * @in_snap: on return, the result * * Returns 0 on success, or a negative error code in case of failure. */ static int apfs_dstream_cache_in_snap(struct apfs_dstream_info *dstream, bool *in_snap) { struct super_block *sb = dstream->ds_sb; struct apfs_file_extent *cache = NULL; /* All changes to extents get flushed when a snaphot is created */ if (dstream->ds_ext_dirty) { *in_snap = false; return 0; } cache = &dstream->ds_cached_ext; return apfs_range_in_snap(sb, cache->phys_block_num, cache->len >> sb->s_blocksize_bits, in_snap); } /** * apfs_dstream_get_new_block - Like the get_block_t function, but for dstreams * @dstream: data stream info * @dsblock: logical dstream block to map * @bh_result: buffer head to map (NULL if none) * @bno: if not NULL, the new block number is returned here * * Returns 0 on success, or a negative error code in case of failure. */ static int apfs_dstream_get_new_block(struct apfs_dstream_info *dstream, u64 dsblock, struct buffer_head *bh_result, u64 *bno) { struct super_block *sb = dstream->ds_sb; struct apfs_superblock *vsb_raw = APFS_SB(sb)->s_vsb_raw; struct apfs_file_extent *cache = NULL; u64 phys_bno, logical_addr, cache_blks, dstream_blks; bool in_snap = true; int err; /* TODO: preallocate tail blocks */ logical_addr = dsblock << sb->s_blocksize_bits; err = apfs_spaceman_allocate_block(sb, &phys_bno, false /* backwards */); if (err) { apfs_err(sb, "block allocation failed"); return err; } apfs_assert_in_transaction(sb, &vsb_raw->apfs_o); le64_add_cpu(&vsb_raw->apfs_fs_alloc_count, 1); le64_add_cpu(&vsb_raw->apfs_total_blocks_alloced, 1); if (bno) *bno = phys_bno; if (bh_result) { apfs_map_bh(bh_result, sb, phys_bno); err = apfs_transaction_join(sb, bh_result); if (err) return err; if (!buffer_uptodate(bh_result)) { /* * Truly new buffers need to be marked as such, to get * zeroed; this also takes care of holes in sparse files */ set_buffer_new(bh_result); } else if (dstream->ds_size > logical_addr) { /* * The last block may have stale data left from a * truncation */ apfs_zero_bh_tail(sb, bh_result, dstream->ds_size - logical_addr); } } dstream_blks = apfs_size_to_blocks(sb, dstream->ds_size); if (dstream_blks < dsblock) { /* * This recurses into apfs_dstream_get_new_block() and dirties * the extent cache, so it must happen before flushing it. */ err = apfs_zero_dstream_tail(dstream); if (err) { apfs_err(sb, "failed to zero tail for dstream 0x%llx", dstream->ds_id); return err; } } err = apfs_dstream_cache_in_snap(dstream, &in_snap); if (err) return err; cache = &dstream->ds_cached_ext; cache_blks = apfs_size_to_blocks(sb, cache->len); /* TODO: allow dirty caches of several blocks in the middle of a file */ if (!in_snap && apfs_dstream_cache_is_tail(dstream) && logical_addr == cache->logical_addr + cache->len && phys_bno == cache->phys_block_num + cache_blks) { cache->len += sb->s_blocksize; dstream->ds_ext_dirty = true; return 0; } err = apfs_flush_extent_cache(dstream); if (err) { apfs_err(sb, "extent cache flush failed for dstream 0x%llx", dstream->ds_id); return err; } if (dstream_blks < dsblock) { /* * This puts new extents after the reported end of the file, so * it must happen after the flush to avoid conflict with those * extent operations. */ err = apfs_create_hole(dstream, dstream_blks, dsblock); if (err) { apfs_err(sb, "hole creation failed for dstream 0x%llx", dstream->ds_id); return err; } } cache->logical_addr = logical_addr; cache->phys_block_num = phys_bno; cache->len = sb->s_blocksize; dstream->ds_ext_dirty = true; return 0; } /** * apfs_dstream_get_new_bno - Allocate a new block inside a dstream * @dstream: data stream info * @dsblock: logical dstream block to allocate * @bno: on return, the new block number * * Returns 0 on success, or a negative error code in case of failure. */ int apfs_dstream_get_new_bno(struct apfs_dstream_info *dstream, u64 dsblock, u64 *bno) { return apfs_dstream_get_new_block(dstream, dsblock, NULL /* bh_result */, bno); } int apfs_get_new_block(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create) { struct apfs_inode_info *ai = APFS_I(inode); ASSERT(create); return apfs_dstream_get_new_block(&ai->i_dstream, iblock, bh_result, NULL /* bno */); } /** * apfs_shrink_dstream_last_extent - Shrink last extent of dstream being resized * @dstream: data stream info * @new_size: new size for the whole data stream * * Deletes, shrinks or zeroes the last extent, as needed for the truncation of * the data stream. * * Only works with the last extent, so it needs to be called repeatedly to * complete the truncation. Returns -EAGAIN in that case, or 0 when the process * is complete. Returns other negative error codes in case of failure. */ static int apfs_shrink_dstream_last_extent(struct apfs_dstream_info *dstream, loff_t new_size) { struct super_block *sb = dstream->ds_sb; struct apfs_sb_info *sbi = APFS_SB(sb); struct apfs_query *query; struct apfs_file_extent tail; u64 extent_id = dstream->ds_id; int ret = 0; query = apfs_alloc_query(sbi->s_cat_root, NULL /* parent */); if (!query) return -ENOMEM; apfs_init_file_extent_key(extent_id, -1, &query->key); query->flags = APFS_QUERY_CAT; ret = apfs_btree_query(sb, &query); if (ret && ret != -ENODATA) { apfs_err(sb, "query failed for last extent of id 0x%llx", extent_id); goto out; } if (!apfs_query_found_extent(query)) { /* No more extents, we deleted the whole file already? */ if (new_size) { apfs_err(sb, "missing extent for dstream 0x%llx", extent_id); ret = -EFSCORRUPTED; } else { ret = 0; } goto out; } ret = apfs_extent_from_query(query, &tail); if (ret) { apfs_err(sb, "bad tail extent record on dstream 0x%llx", extent_id); goto out; } if (tail.logical_addr + tail.len < new_size) { apfs_err(sb, "missing extent for dstream 0x%llx", extent_id); ret = -EFSCORRUPTED; /* Tail extent missing */ } else if (tail.logical_addr + tail.len == new_size) { ret = 0; /* Nothing more to be done */ } else if (tail.logical_addr >= new_size) { /* This whole extent needs to go */ ret = apfs_btree_remove(query); if (ret) { apfs_err(sb, "removal failed for id 0x%llx, addr 0x%llx", dstream->ds_id, tail.logical_addr); goto out; } if (apfs_ext_is_hole(&tail)) { dstream->ds_sparse_bytes -= tail.len; } else { ret = apfs_range_put_reference(sb, tail.phys_block_num, tail.len); if (ret) { apfs_err(sb, "failed to put range 0x%llx-0x%llx", tail.phys_block_num, tail.len); goto out; } } ret = apfs_crypto_adj_refcnt(sb, tail.crypto_id, -1); if (ret) { apfs_err(sb, "failed to take crypto id 0x%llx", tail.crypto_id); goto out; } ret = tail.logical_addr == new_size ? 0 : -EAGAIN; } else { /* * The file is being truncated in the middle of this extent. * TODO: preserve the physical tail to be overwritten later. */ new_size = apfs_size_to_blocks(sb, new_size) << sb->s_blocksize_bits; ret = apfs_shrink_extent_tail(query, dstream, new_size); if (ret) apfs_err(sb, "failed to shrink tail of dstream 0x%llx", extent_id); } out: apfs_free_query(query); return ret; } /** * apfs_shrink_dstream - Shrink a data stream's extents to a new length * @dstream: data stream info * @new_size: the new size * * Returns 0 on success, or a negative error code in case of failure. */ static int apfs_shrink_dstream(struct apfs_dstream_info *dstream, loff_t new_size) { int ret; do { ret = apfs_shrink_dstream_last_extent(dstream, new_size); } while (ret == -EAGAIN); return ret; } /** * apfs_truncate - Truncate a data stream's content * @dstream: data stream info * @new_size: the new size * * Returns 0 on success, or a negative error code in case of failure. */ int apfs_truncate(struct apfs_dstream_info *dstream, loff_t new_size) { struct super_block *sb = dstream->ds_sb; u64 old_blks, new_blks; struct apfs_file_extent *cache = &dstream->ds_cached_ext; int err; /* TODO: don't write the cached extent if it will be deleted */ err = apfs_flush_extent_cache(dstream); if (err) { apfs_err(sb, "extent cache flush failed for dstream 0x%llx", dstream->ds_id); return err; } dstream->ds_ext_dirty = false; /* TODO: keep the cache valid on truncation */ cache->len = 0; /* "<=", because a partial write may have left extents beyond the end */ if (new_size <= dstream->ds_size) return apfs_shrink_dstream(dstream, new_size); err = apfs_zero_dstream_tail(dstream); if (err) { apfs_err(sb, "failed to zero tail for dstream 0x%llx", dstream->ds_id); return err; } new_blks = apfs_size_to_blocks(sb, new_size); old_blks = apfs_size_to_blocks(sb, dstream->ds_size); return apfs_create_hole(dstream, old_blks, new_blks); } /** * apfs_dstream_delete_front - Deletes as many leading extents as possible * @sb: filesystem superblock * @ds_id: id for the dstream to delete * * Returns 0 on success, or a negative error code in case of failure, which may * be -ENODATA if there are no more extents, or -EAGAIN if the free queue is * getting too full. */ static int apfs_dstream_delete_front(struct super_block *sb, u64 ds_id) { struct apfs_sb_info *sbi = APFS_SB(sb); struct apfs_spaceman *sm = APFS_SM(sb); struct apfs_spaceman_phys *sm_raw = sm->sm_raw; struct apfs_spaceman_free_queue *fq = NULL; struct apfs_query *query = NULL; struct apfs_file_extent head; bool first_match = true; int ret; fq = &sm_raw->sm_fq[APFS_SFQ_MAIN]; if (le64_to_cpu(fq->sfq_count) > APFS_TRANS_MAIN_QUEUE_MAX) return -EAGAIN; query = apfs_alloc_query(sbi->s_cat_root, NULL /* parent */); if (!query) return -ENOMEM; apfs_init_file_extent_key(ds_id, 0, &query->key); query->flags = APFS_QUERY_CAT; next_extent: ret = apfs_btree_query(sb, &query); if (ret && ret != -ENODATA) { apfs_err(sb, "query failed for first extent of id 0x%llx", ds_id); goto out; } apfs_query_direct_forward(query); if (!apfs_query_found_extent(query)) { /* * After the original lookup, the query may not be set to the * first extent, but instead to the record that comes right * before. */ if (first_match) { first_match = false; goto next_extent; } ret = -ENODATA; goto out; } first_match = false; ret = apfs_extent_from_query(query, &head); if (ret) { apfs_err(sb, "bad head extent record on dstream 0x%llx", ds_id); goto out; } ret = apfs_btree_remove(query); if (ret) { apfs_err(sb, "removal failed for id 0x%llx, addr 0x%llx", ds_id, head.logical_addr); goto out; } /* * The official fsck doesn't complain about wrong sparse byte counts * for orphans, so I guess we don't need to update them here */ if (!apfs_ext_is_hole(&head)) { ret = apfs_range_put_reference(sb, head.phys_block_num, head.len); if (ret) { apfs_err(sb, "failed to put range 0x%llx-0x%llx", head.phys_block_num, head.len); goto out; } ret = apfs_crypto_adj_refcnt(sb, head.crypto_id, -1); if (ret) { apfs_err(sb, "failed to take crypto id 0x%llx", head.crypto_id); goto out; } } if (le64_to_cpu(fq->sfq_count) <= APFS_TRANS_MAIN_QUEUE_MAX) goto next_extent; ret = -EAGAIN; out: apfs_free_query(query); return ret; } /** * apfs_inode_delete_front - Deletes as many leading extents as possible * @inode: inode to delete * * Tries to delete all extents for @inode, in which case it returns 0. If the * free queue is getting too full, deletes as much as is reasonable and returns * -EAGAIN. May return other negative error codes as well. */ int apfs_inode_delete_front(struct inode *inode) { struct super_block *sb = inode->i_sb; struct apfs_dstream_info *dstream = NULL; struct apfs_inode_info *ai = APFS_I(inode); int ret; if (!ai->i_has_dstream) return 0; dstream = &ai->i_dstream; ret = apfs_flush_extent_cache(dstream); if (ret) { apfs_err(sb, "extent cache flush failed for dstream 0x%llx", dstream->ds_id); return ret; } ret = apfs_dstream_delete_front(sb, dstream->ds_id); if (ret == -ENODATA) return 0; return ret; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0) loff_t apfs_remap_file_range(struct file *src_file, loff_t off, struct file *dst_file, loff_t destoff, loff_t len, unsigned int remap_flags) #else int apfs_clone_file_range(struct file *src_file, loff_t off, struct file *dst_file, loff_t destoff, u64 len) #endif { struct inode *src_inode = file_inode(src_file); struct inode *dst_inode = file_inode(dst_file); struct apfs_inode_info *src_ai = APFS_I(src_inode); struct apfs_inode_info *dst_ai = APFS_I(dst_inode); struct apfs_dstream_info *src_ds = &src_ai->i_dstream; struct apfs_dstream_info *dst_ds = &dst_ai->i_dstream; struct super_block *sb = src_inode->i_sb; struct apfs_sb_info *sbi = APFS_SB(sb); const u64 xfield_flags = APFS_INODE_MAINTAIN_DIR_STATS | APFS_INODE_IS_SPARSE | APFS_INODE_HAS_PURGEABLE_FLAGS; int err; #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0) if (remap_flags & ~(REMAP_FILE_ADVISORY)) return -EINVAL; #endif if (src_inode == dst_inode) return -EINVAL; /* We only want to clone whole files, like in the official driver */ if (off != 0 || destoff != 0 || len != 0) return -EINVAL; /* * Clones here work in two steps: first the user creates an empty target * file, and then the user calls the ioctl, which replaces the file with * a clone. This is not atomic, of course. */ if (dst_ai->i_has_dstream || dst_ai->i_bsd_flags & APFS_INOBSD_COMPRESSED) { apfs_warn(sb, "clones can only replace freshly created files"); return -EOPNOTSUPP; } if (dst_ai->i_int_flags & xfield_flags) { apfs_warn(sb, "clone can't replace a file that has xfields"); return -EOPNOTSUPP; } if (!src_ai->i_has_dstream) { apfs_warn(sb, "can't clone a file with no dstream"); return -EOPNOTSUPP; } err = apfs_transaction_start(sb, APFS_TRANS_REG); if (err) return err; apfs_inode_join_transaction(sb, src_inode); apfs_inode_join_transaction(sb, dst_inode); err = apfs_flush_extent_cache(src_ds); if (err) { apfs_err(sb, "extent cache flush failed for dstream 0x%llx", src_ds->ds_id); goto fail; } err = apfs_dstream_adj_refcnt(src_ds, +1); if (err) { apfs_err(sb, "failed to take dstream id 0x%llx", src_ds->ds_id); goto fail; } src_ds->ds_shared = true; #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) dst_inode->i_mtime = dst_inode->i_ctime = current_time(dst_inode); #elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 7, 0) dst_inode->i_mtime = inode_set_ctime_current(dst_inode); #else inode_set_mtime_to_ts(dst_inode, inode_set_ctime_current(dst_inode)); #endif dst_inode->i_size = src_inode->i_size; dst_ai->i_key_class = src_ai->i_key_class; dst_ai->i_int_flags = src_ai->i_int_flags; dst_ai->i_bsd_flags = src_ai->i_bsd_flags; dst_ai->i_has_dstream = true; dst_ds->ds_sb = src_ds->ds_sb; dst_ds->ds_inode = dst_inode; dst_ds->ds_id = src_ds->ds_id; dst_ds->ds_size = src_ds->ds_size; dst_ds->ds_sparse_bytes = src_ds->ds_sparse_bytes; dst_ds->ds_cached_ext = src_ds->ds_cached_ext; dst_ds->ds_ext_dirty = false; dst_ds->ds_shared = true; dst_ai->i_int_flags |= APFS_INODE_WAS_EVER_CLONED | APFS_INODE_WAS_CLONED; src_ai->i_int_flags |= APFS_INODE_WAS_EVER_CLONED; /* * The sparse flag is the important one here: if we need it, it will get * set later by apfs_update_inode(), after the xfield gets created. */ dst_ai->i_int_flags &= ~xfield_flags; /* * Commit the transaction to make sure all buffers in the source inode * go through copy-on-write. This is a bit excessive, but I don't expect * clones to be created often enough for it to matter. */ sbi->s_nxi->nx_transaction.t_state |= APFS_NX_TRANS_FORCE_COMMIT; err = apfs_transaction_commit(sb); if (err) goto fail; return dst_ds->ds_size; fail: apfs_transaction_abort(sb); return err; } /** * apfs_extent_create_record - Create a logical extent record for a dstream id * @sb: filesystem superblock * @dstream_id: the dstream id * @extent: extent info for the record * * Returns 0 on success, or a negative error code in case of failure. */ static int apfs_extent_create_record(struct super_block *sb, u64 dstream_id, struct apfs_file_extent *extent) { struct apfs_sb_info *sbi = APFS_SB(sb); struct apfs_query *query = NULL; struct apfs_file_extent_val raw_val; struct apfs_file_extent_key raw_key; int ret = 0; query = apfs_alloc_query(sbi->s_cat_root, NULL /* parent */); if (!query) return -ENOMEM; apfs_init_file_extent_key(dstream_id, extent->logical_addr, &query->key); query->flags = APFS_QUERY_CAT | APFS_QUERY_EXACT; ret = apfs_btree_query(sb, &query); if (ret && ret != -ENODATA) { apfs_err(sb, "query failed for id 0x%llx, addr 0x%llx", dstream_id, extent->logical_addr); goto out; } apfs_key_set_hdr(APFS_TYPE_FILE_EXTENT, dstream_id, &raw_key); raw_key.logical_addr = cpu_to_le64(extent->logical_addr); raw_val.len_and_flags = cpu_to_le64(extent->len); raw_val.phys_block_num = cpu_to_le64(extent->phys_block_num); raw_val.crypto_id = cpu_to_le64(apfs_vol_is_encrypted(sb) ? dstream_id : 0); /* TODO */ ret = apfs_btree_insert(query, &raw_key, sizeof(raw_key), &raw_val, sizeof(raw_val)); if (ret) apfs_err(sb, "insertion failed for id 0x%llx, addr 0x%llx", dstream_id, extent->logical_addr); out: apfs_free_query(query); return ret; } /** * apfs_put_single_extent - Put a reference to a single extent * @sb: filesystem superblock * @paddr_end: first block after the extent to put * @paddr_min: don't put references before this block * * Puts a reference to the physical extent range that ends in @paddr_end. Sets * @paddr_end to the beginning of the extent, so that the caller can continue * with the previous one. Returns 0 on success, or a negative error code in * case of failure. * * TODO: unify this with apfs_take_single_extent(), they are almost the same. */ static int apfs_put_single_extent(struct super_block *sb, u64 *paddr_end, u64 paddr_min) { struct apfs_superblock *vsb_raw = APFS_SB(sb)->s_vsb_raw; struct apfs_node *extref_root = NULL; struct apfs_key key; struct apfs_query *query = NULL; struct apfs_phys_extent prev_ext; u64 prev_start, prev_end; bool cropped_head = false, cropped_tail = false; struct apfs_file_extent tmp = {0}; /* TODO: clean up all the fake extent interfaces? */ int ret; extref_root = apfs_read_node(sb, le64_to_cpu(vsb_raw->apfs_extentref_tree_oid), APFS_OBJ_PHYSICAL, true /* write */); if (IS_ERR(extref_root)) { apfs_err(sb, "failed to read extref root 0x%llx", le64_to_cpu(vsb_raw->apfs_extentref_tree_oid)); return PTR_ERR(extref_root); } apfs_assert_in_transaction(sb, &vsb_raw->apfs_o); vsb_raw->apfs_extentref_tree_oid = cpu_to_le64(extref_root->object.oid); apfs_init_extent_key(*paddr_end - 1, &key); restart: query = apfs_alloc_query(extref_root, NULL /* parent */); if (!query) { ret = -ENOMEM; goto out; } query->key = key; query->flags = APFS_QUERY_EXTENTREF; ret = apfs_btree_query(sb, &query); if (ret && ret != -ENODATA) { apfs_err(sb, "query failed for paddr 0x%llx", *paddr_end - 1); goto out; } if (ret == -ENODATA) { /* The whole range to put is part of a snapshot */ tmp.phys_block_num = paddr_min; tmp.len = (*paddr_end - paddr_min) << sb->s_blocksize_bits; ret = apfs_create_update_pext(query, &tmp, -1); *paddr_end = paddr_min; goto out; } ret = apfs_phys_ext_from_query(query, &prev_ext); if (ret) { apfs_err(sb, "bad pext record over paddr 0x%llx", *paddr_end - 1); goto out; } prev_start = prev_ext.bno; prev_end = prev_ext.bno + prev_ext.blkcount; if (prev_end < *paddr_end) { /* The extent to put is part of a snapshot */ tmp.phys_block_num = max(prev_end, paddr_min); tmp.len = (*paddr_end - tmp.phys_block_num) << sb->s_blocksize_bits; ret = apfs_create_update_pext(query, &tmp, -1); *paddr_end = tmp.phys_block_num; goto out; } if ((cropped_tail && prev_end > *paddr_end) || (cropped_head && prev_start < paddr_min)) { /* This should never happen, but be safe */ apfs_alert(sb, "recursion cropping physical extent 0x%llx-0x%llx", prev_start, prev_end); ret = -EFSCORRUPTED; goto out; } if (prev_end > *paddr_end) { ret = apfs_split_phys_ext(query, *paddr_end); if (ret) { apfs_err(sb, "failed to split pext at 0x%llx", *paddr_end); goto out; } /* The split may make the query invalid */ apfs_free_query(query); cropped_tail = true; goto restart; } if (prev_start < paddr_min) { ret = apfs_split_phys_ext(query, paddr_min); if (ret) { apfs_err(sb, "failed to split pext at 0x%llx", paddr_min); goto out; } /* The split may make the query invalid */ apfs_free_query(query); cropped_head = true; goto restart; } /* The extent to put already exists */ ret = apfs_put_phys_extent(&prev_ext, query); if (ret) apfs_err(sb, "failed to put pext at 0x%llx", prev_ext.bno); *paddr_end = prev_start; out: apfs_free_query(query); apfs_node_free(extref_root); return ret; } /** * apfs_range_put_reference - Put a reference to a physical range * @sb: filesystem superblock * @paddr: first block of the range * @length: length of the range (in bytes) * * Returns 0 on success, or a negative error code in case of failure. */ static int apfs_range_put_reference(struct super_block *sb, u64 paddr, u64 length) { u64 extent_end; int err; ASSERT(paddr); extent_end = paddr + (length >> sb->s_blocksize_bits); while (extent_end > paddr) { err = apfs_put_single_extent(sb, &extent_end, paddr); if (err) return err; } return 0; } /** * apfs_take_single_extent - Take a reference to a single extent * @sb: filesystem superblock * @paddr_end: first block after the extent to take * @paddr_min: don't take references before this block * * Takes a reference to the physical extent range that ends in @paddr_end. Sets * @paddr_end to the beginning of the extent, so that the caller can continue * with the previous one. Returns 0 on success, or a negative error code in * case of failure. */ static int apfs_take_single_extent(struct super_block *sb, u64 *paddr_end, u64 paddr_min) { struct apfs_superblock *vsb_raw = APFS_SB(sb)->s_vsb_raw; struct apfs_node *extref_root = NULL; struct apfs_key key; struct apfs_query *query = NULL; struct apfs_phys_extent prev_ext; u64 prev_start, prev_end; bool cropped_head = false, cropped_tail = false; struct apfs_file_extent tmp = {0}; /* TODO: clean up all the fake extent interfaces? */ int ret; extref_root = apfs_read_node(sb, le64_to_cpu(vsb_raw->apfs_extentref_tree_oid), APFS_OBJ_PHYSICAL, true /* write */); if (IS_ERR(extref_root)) { apfs_err(sb, "failed to read extref root 0x%llx", le64_to_cpu(vsb_raw->apfs_extentref_tree_oid)); return PTR_ERR(extref_root); } apfs_assert_in_transaction(sb, &vsb_raw->apfs_o); vsb_raw->apfs_extentref_tree_oid = cpu_to_le64(extref_root->object.oid); apfs_init_extent_key(*paddr_end - 1, &key); restart: query = apfs_alloc_query(extref_root, NULL /* parent */); if (!query) { ret = -ENOMEM; goto out; } query->key = key; query->flags = APFS_QUERY_EXTENTREF; ret = apfs_btree_query(sb, &query); if (ret && ret != -ENODATA) { apfs_err(sb, "query failed for paddr 0x%llx", *paddr_end - 1); goto out; } if (ret == -ENODATA) { /* The whole range to take is part of a snapshot */ tmp.phys_block_num = paddr_min; tmp.len = (*paddr_end - paddr_min) << sb->s_blocksize_bits; ret = apfs_create_update_pext(query, &tmp, +1); *paddr_end = paddr_min; goto out; } ret = apfs_phys_ext_from_query(query, &prev_ext); if (ret) { apfs_err(sb, "bad pext record over paddr 0x%llx", *paddr_end - 1); goto out; } prev_start = prev_ext.bno; prev_end = prev_ext.bno + prev_ext.blkcount; if (prev_end < *paddr_end) { /* The extent to take is part of a snapshot */ tmp.phys_block_num = max(prev_end, paddr_min); tmp.len = (*paddr_end - tmp.phys_block_num) << sb->s_blocksize_bits; ret = apfs_create_update_pext(query, &tmp, +1); *paddr_end = tmp.phys_block_num; goto out; } if ((cropped_tail && prev_end > *paddr_end) || (cropped_head && prev_start < paddr_min)) { /* This should never happen, but be safe */ apfs_alert(sb, "recursion cropping physical extent 0x%llx-0x%llx", prev_start, prev_end); ret = -EFSCORRUPTED; goto out; } if (prev_end > *paddr_end) { ret = apfs_split_phys_ext(query, *paddr_end); if (ret) { apfs_err(sb, "failed to split pext at 0x%llx", *paddr_end); goto out; } /* The split may make the query invalid */ apfs_free_query(query); cropped_tail = true; goto restart; } if (prev_start < paddr_min) { ret = apfs_split_phys_ext(query, paddr_min); if (ret) { apfs_err(sb, "failed to split pext at 0x%llx", paddr_min); goto out; } /* The split may make the query invalid */ apfs_free_query(query); cropped_head = true; goto restart; } /* The extent to take already exists */ ret = apfs_take_phys_extent(&prev_ext, query); if (ret) apfs_err(sb, "failed to take pext at 0x%llx", prev_ext.bno); *paddr_end = prev_start; out: apfs_free_query(query); apfs_node_free(extref_root); return ret; } /** * apfs_range_take_reference - Take a reference to a physical range * @sb: filesystem superblock * @paddr: first block of the range * @length: length of the range (in bytes) * * Returns 0 on success, or a negative error code in case of failure. */ static int apfs_range_take_reference(struct super_block *sb, u64 paddr, u64 length) { u64 extent_end; int err; ASSERT(paddr); extent_end = paddr + (length >> sb->s_blocksize_bits); while (extent_end > paddr) { err = apfs_take_single_extent(sb, &extent_end, paddr); if (err) return err; } return 0; } /** * apfs_clone_single_extent - Make a copy of an extent in a dstream to a new one * @dstream: old dstream * @new_id: id of the new dstream * @log_addr: logical address for the extent * * Duplicates the logical extent, and updates the references to the physical * extents as required. Sets @addr to the end of the extent, so that the caller * can continue in the same place. Returns 0 on success, or a negative error * code in case of failure. */ static int apfs_clone_single_extent(struct apfs_dstream_info *dstream, u64 new_id, u64 *log_addr) { struct super_block *sb = dstream->ds_sb; struct apfs_file_extent extent; int err; err = apfs_extent_read(dstream, *log_addr >> sb->s_blocksize_bits, &extent); if (err) { apfs_err(sb, "failed to read an extent to clone for dstream 0x%llx", dstream->ds_id); return err; } err = apfs_extent_create_record(sb, new_id, &extent); if (err) { apfs_err(sb, "failed to create extent record for clone of dstream 0x%llx", dstream->ds_id); return err; } if (!apfs_ext_is_hole(&extent)) { err = apfs_range_take_reference(sb, extent.phys_block_num, extent.len); if (err) { apfs_err(sb, "failed to take a reference to physical range 0x%llx-0x%llx", extent.phys_block_num, extent.len); return err; } } *log_addr += extent.len; return 0; } /** * apfs_clone_extents - Make a copy of all extents in a dstream to a new one * @dstream: old dstream * @new_id: id for the new dstream * * Returns 0 on success, or a negative error code in case of failure. */ int apfs_clone_extents(struct apfs_dstream_info *dstream, u64 new_id) { u64 next = 0; int err; while (next < dstream->ds_size) { err = apfs_clone_single_extent(dstream, new_id, &next); if (err) return err; } return 0; } /** * apfs_nonsparse_dstream_read - Read from a dstream without holes * @dstream: dstream to read * @buf: destination buffer * @count: exact number of bytes to read * @offset: dstream offset to read from * * Returns 0 on success or a negative error code in case of failure. */ int apfs_nonsparse_dstream_read(struct apfs_dstream_info *dstream, void *buf, size_t count, u64 offset) { struct super_block *sb = dstream->ds_sb; u64 logical_start_block, logical_end_block, log_bno, blkcnt, idx; struct buffer_head **bhs = NULL; int ret = 0; /* Save myself from thinking about overflow here */ if (count >= APFS_MAX_FILE_SIZE || offset >= APFS_MAX_FILE_SIZE) { apfs_err(sb, "dstream read overflow (0x%llx-0x%llx)", offset, (unsigned long long)count); return -EFBIG; } if (offset + count > dstream->ds_size) { apfs_err(sb, "reading past the end (0x%llx-0x%llx)", offset, (unsigned long long)count); /* No caller is expected to legitimately read out-of-bounds */ return -EFSCORRUPTED; } logical_start_block = offset >> sb->s_blocksize_bits; logical_end_block = (offset + count + sb->s_blocksize - 1) >> sb->s_blocksize_bits; blkcnt = logical_end_block - logical_start_block; bhs = kcalloc(blkcnt, sizeof(*bhs), GFP_KERNEL); if (!bhs) return -ENOMEM; for (log_bno = logical_start_block; log_bno < logical_end_block; log_bno++) { struct buffer_head *bh = NULL; u64 bno = 0; idx = log_bno - logical_start_block; ret = apfs_logic_to_phys_bno(dstream, log_bno, &bno); if (ret) goto out; if (bno == 0) { apfs_err(sb, "nonsparse dstream has a hole"); ret = -EFSCORRUPTED; goto out; } bhs[idx] = __apfs_getblk(sb, bno); if (!bhs[idx]) { apfs_err(sb, "failed to map block 0x%llx", bno); ret = -EIO; goto out; } bh = bhs[idx]; if (!buffer_uptodate(bh)) { get_bh(bh); lock_buffer(bh); apfs_submit_bh(REQ_OP_READ, 0, bh); } } for (log_bno = logical_start_block; log_bno < logical_end_block; log_bno++) { int off_in_block, left_in_block; idx = log_bno - logical_start_block; wait_on_buffer(bhs[idx]); if (!buffer_uptodate(bhs[idx])) { apfs_err(sb, "failed to read a block"); ret = -EIO; goto out; } if (log_bno == logical_start_block) off_in_block = offset & (sb->s_blocksize - 1); else off_in_block = 0; if (log_bno == logical_end_block - 1) left_in_block = count + offset - (log_bno << sb->s_blocksize_bits) - off_in_block; else left_in_block = sb->s_blocksize - off_in_block; memcpy(buf, bhs[idx]->b_data + off_in_block, left_in_block); buf += left_in_block; } out: if (bhs) { for (idx = 0; idx < blkcnt; idx++) brelse(bhs[idx]); kfree(bhs); } return ret; } /** * apfs_nonsparse_dstream_preread - Attempt to preread a dstream without holes * @dstream: dstream to preread * * Requests reads for all blocks of a dstream, but doesn't wait for the result. */ void apfs_nonsparse_dstream_preread(struct apfs_dstream_info *dstream) { struct super_block *sb = dstream->ds_sb; u64 logical_end_block, log_bno; logical_end_block = (dstream->ds_size + sb->s_blocksize - 1) >> sb->s_blocksize_bits; for (log_bno = 0; log_bno < logical_end_block; log_bno++) { struct buffer_head *bh = NULL; u64 bno = 0; int ret; ret = apfs_logic_to_phys_bno(dstream, log_bno, &bno); if (ret || bno == 0) return; bh = __apfs_getblk(sb, bno); if (!bh) return; if (!buffer_uptodate(bh)) { get_bh(bh); lock_buffer(bh); apfs_submit_bh(REQ_OP_READ, 0, bh); } brelse(bh); bh = NULL; } }