Files
Ernesto A. Fernández f0e88ca368 Switch to bh_submit() for kernel 7.2
The build is broken for the 7.2 release candidate. The reason is that
submit_bh() is gone now, replaced with a new bh_submit() that also takes
the I/O end handler as an argument. Adjust apfs_submit_bh() to these
changes.

Signed-off-by: Ernesto A. Fernández <ernesto@corellium.com>
2026-07-14 19:38:12 -03:00

2373 lines
71 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2018 Ernesto A. Fernández <ernesto.mnd.fernandez@gmail.com>
*/
#include <linux/buffer_head.h>
#include <linux/slab.h>
#include <linux/blk_types.h>
#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;
}
}