6 Commits
Author SHA1 Message Date
Ernesto A. Fernández 3f9d529bcb 0.3.8
This release reworks ephemeral objects to support larger containers of
up to ~7.8 TiB, and to allow the creation of checkpoint mapping blocks.

Also the build is no longer broken for the new 6.8 kernel, thanks to a
patch by Woody Suwalski.

Signed-off-by: Ernesto A. Fernández <ernesto@corellium.com>
2024-03-11 00:32:25 -03:00
Woody Suwalski 60ef1e9991 Building on kernel 6.8
[eafer: moved bdev handle to the container superblock]
2024-03-10 20:58:42 -03:00
Ernesto A. Fernández 0313a0fee1 Rework handling of ephemeral objects
In pretty much all containers I've encountered so far, every objects has
a single block. There is an annoying exception though: containers with a
size between ~6.7 TiB and ~7.8 TiB have a multiblock spaceman. I have no
simple way to deal with this because my transactions currently handle
ephemeral objects very roughly, ignoring all the cpm metadata and just
updating each block in the expected header location.

Instead of trying to hack this in somehow, start doing the right thing
as explained in the official reference and keep all ephemeral objects in
memory at all times. So, when the first transaction starts, go through
the cpm metadata and read each object along with its size and oid. When
a new object gets created (or an old one gets deleted), all changes
happen in memory. On commit time, checksums are updated and all objects
get written to disk, but nothing neads to be read.

So this is all much cleaner, and I took this opportunity to support
multiple cpm blocks as well.

Signed-off-by: Ernesto A. Fernández <ernesto@corellium.com>
2024-03-01 21:12:56 -03:00
Ernesto A. Fernández 784dd43146 Fix typo in previous commit
It's annoying to have to make a new commit just for this, but I had
already pushed the bad one...

Signed-off-by: Ernesto A. Fernández <ernesto@corellium.com>
2024-02-22 16:01:46 -03:00
Ernesto A. Fernández 7df84d2173 Mention fusion drives in the README
Fusion drives are not yet supported, but the README does not mention
this. Fix it.

Signed-off-by: Ernesto A. Fernández <ernesto@corellium.com>
2024-02-20 20:04:21 -03:00
Ernesto A. Fernández fbf052e1e9 Check container features as soon as possible
Failure to mount fusion drives currently gets reported in a confusing
way:

  https://github.com/linux-apfs/linux-apfs-rw/issues/39#issuecomment-1947466697

The problem is that all feature checks are currently run at the same
time for both the container and the volume, after the volume superblock
has been mapped. Fusion drives never get that far, we can't even work
with the container's object map.

Split the feature checks into two functions, one for the container and
one for the volume. Leave the call for the volume in the same place, but
move the one for the container into apfs_map_main_super().

Signed-off-by: Ernesto A. Fernández <ernesto@corellium.com>
2024-02-20 19:46:42 -03:00
10 changed files with 749 additions and 731 deletions

No files matched your search

+2 -1
View File
@@ -22,7 +22,8 @@ This module is the result of reverse engineering and testing has been limited.
If you make use of the write support, expect data corruption. Please report any
issues that you find, but I can't promise a quick resolution at this stage.
Encryption is not yet implemented even in read-only mode.
Encryption is not yet implemented even in read-only mode, and neither are
fusion drives.
Build
=====
+33 -10
View File
@@ -64,6 +64,7 @@ struct apfs_object {
*/
struct buffer_head *o_bh;
char *data; /* The raw object */
bool ephemeral; /* Is this an ephemeral object? */
};
/* Constants used in managing the size of a node's table of contents */
@@ -121,8 +122,8 @@ static inline bool apfs_node_has_fixed_kv_size(struct apfs_node *node)
*/
struct apfs_spaceman {
struct apfs_spaceman_phys *sm_raw; /* On-disk spaceman structure */
struct buffer_head *sm_bh; /* Buffer head for @sm_raw */
struct apfs_nxsb_info *sm_nxi; /* Container superblock */
u32 sm_size; /* Size of @sm_raw in bytes */
u32 sm_blocks_per_chunk; /* Blocks covered by a bitmap block */
u32 sm_chunks_per_cib; /* Chunk count in a chunk-info block */
@@ -163,7 +164,6 @@ struct apfs_spaceman {
* Structure that keeps track of a container transaction.
*/
struct apfs_nx_transaction {
struct buffer_head *t_old_msb; /* Main superblock being replaced */
unsigned int t_state;
struct list_head t_inodes; /* List of inodes in the transaction */
@@ -204,6 +204,23 @@ struct apfs_max_ops {
int blks; /* Maximum extent blocks that may need changing */
};
/*
* List entry for an in-memory ephemeral object
*/
struct apfs_ephemeral_object_info {
u64 oid; /* Ephemeral object id */
u32 size; /* Size of the object in bytes */
void *object; /* In-memory address of the object */
};
/*
* We allocate a fixed space for the list of ephemeral objects. I don't
* actually know how big this should be allowed to get, but all the objects
* must be written down with each transaction commit, so probably not too big.
*/
#define APFS_EPHEMERAL_LIST_SIZE 32768
#define APFS_EPHEMERAL_LIST_LIMIT (APFS_EPHEMERAL_LIST_SIZE / sizeof(struct apfs_ephemeral_object_info))
/* Mount option flags for a container */
#define APFS_CHECK_NODES 1
#define APFS_READWRITE 2
@@ -213,10 +230,17 @@ struct apfs_max_ops {
*/
struct apfs_nxsb_info {
struct block_device *nx_bdev; /* Device for the container */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0)
struct bdev_handle *nx_bdev_handle;
#endif
struct apfs_nx_superblock *nx_raw; /* On-disk main sb */
struct apfs_object nx_object; /* Main superblock object */
u64 nx_bno; /* Current block number for the checkpoint superblock */
u64 nx_xid; /* Latest transaction id */
/* List of ephemeral objects in memory (except the superblock) */
struct apfs_ephemeral_object_info *nx_eph_list;
int nx_eph_count;
struct list_head vol_list; /* List of mounted volumes in container */
unsigned int nx_flags; /* Mount options shared by all volumes */
@@ -1017,12 +1041,12 @@ extern int apfs_make_empty_btree_root(struct super_block *sb, u32 subtype, u64 *
/* object.c */
extern int apfs_obj_verify_csum(struct super_block *sb, struct buffer_head *bh);
extern void apfs_obj_set_csum(struct super_block *sb,
struct apfs_obj_phys *obj);
extern int apfs_create_cpoint_map(struct super_block *sb, u64 oid, u64 bno);
extern int apfs_remove_cpoint_map(struct super_block *sb, u64 bno);
extern struct buffer_head *apfs_read_ephemeral_object(struct super_block *sb,
u64 oid);
extern void apfs_obj_set_csum(struct super_block *sb, struct apfs_obj_phys *obj);
extern int apfs_multiblock_verify_csum(char *object, u32 size);
extern void apfs_multiblock_set_csum(char *object, u32 size);
extern int apfs_create_cpm_block(struct super_block *sb, u64 bno, struct buffer_head **bh_p);
extern int apfs_create_cpoint_map(struct super_block *sb, struct apfs_checkpoint_map_phys *cpm, struct apfs_obj_phys *obj, u64 bno, u32 size);
extern struct apfs_ephemeral_object_info *apfs_ephemeral_object_lookup(struct super_block *sb, u64 oid);
extern struct buffer_head *apfs_read_object_block(struct super_block *sb, u64 bno, bool write, bool preserve);
extern u32 apfs_index_in_data_area(struct super_block *sb, u64 bno);
extern u64 apfs_data_index_to_bno(struct super_block *sb, u32 index);
@@ -1046,7 +1070,6 @@ extern int apfs_read_catalog(struct super_block *sb, bool write);
extern int apfs_sync_fs(struct super_block *sb, int wait);
/* transaction.c */
extern void apfs_cpoint_data_allocate(struct super_block *sb, u64 *bno);
extern int apfs_cpoint_data_free(struct super_block *sb, u64 bno);
extern int apfs_transaction_start(struct super_block *sb, struct apfs_max_ops maxops);
extern int apfs_transaction_commit(struct super_block *sb);
+13 -2
View File
@@ -589,11 +589,22 @@ int apfs_query_join_transaction(struct apfs_query *query)
struct super_block *sb = node->object.sb;
u64 oid = node->object.oid;
u32 storage = apfs_query_storage(query);
struct apfs_obj_phys *raw = NULL;
/*
* Ephemeral objects are checkpoint data, and all of their xids get
* updated on commit. There is no real need to do it here as well, but
* it's better for consistency with the other object types.
*/
if (storage == APFS_OBJ_EPHEMERAL) {
ASSERT(node->object.ephemeral);
raw = (void *)node->object.data;
raw->o_xid = cpu_to_le64(APFS_NXI(sb)->nx_xid);
return 0;
}
if (buffer_trans(node->object.o_bh)) /* Already in the transaction */
return 0;
/* Ephemeral objects are always checkpoint data */
ASSERT(storage != APFS_OBJ_EPHEMERAL);
/* Root nodes should join the transaction before the query is created */
ASSERT(!apfs_node_is_root(node));
+1 -1
View File
@@ -1,5 +1,5 @@
PACKAGE_NAME="linux-apfs-rw"
PACKAGE_VERSION="0.3.7"
PACKAGE_VERSION="0.3.8"
BUILT_MODULE_NAME[0]="apfs"
DEST_MODULE_LOCATION[0]="/extra"
+16 -1
View File
@@ -5,6 +5,9 @@
#include "apfs.h"
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0)
#include <linux/splice.h>
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 17, 0)
typedef int vm_fault_t;
#endif
@@ -152,6 +155,16 @@ int apfs_fsync(struct file *file, loff_t start, loff_t end, int datasync)
return apfs_sync_fs(sb, true /* wait */);
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0)
static ssize_t apfs_copy_file_range(struct file *src_file, loff_t src_off,
struct file *dst_file, loff_t dst_off,
size_t len, unsigned int flags)
{
return (splice_copy_file_range(src_file, src_off,
dst_file, dst_off, len));
}
#endif
const struct file_operations apfs_file_operations = {
.llseek = generic_file_llseek,
.read_iter = generic_file_read_iter,
@@ -161,7 +174,9 @@ const struct file_operations apfs_file_operations = {
.fsync = apfs_fsync,
.unlocked_ioctl = apfs_file_ioctl,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0)
.copy_file_range = apfs_copy_file_range,
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)
.copy_file_range = generic_copy_file_range,
#endif
+87 -52
View File
@@ -46,13 +46,13 @@ void apfs_node_free(struct apfs_node *node)
obj = &node->object;
if (obj->o_bh) {
obj->data = NULL;
brelse(obj->o_bh);
obj->o_bh = NULL;
} else {
} else if (!obj->ephemeral) {
/* Ephemeral data always remains in memory */
kfree(obj->data);
obj->data = NULL;
}
obj->data = NULL;
kfree(node);
}
@@ -75,9 +75,10 @@ struct apfs_node *apfs_read_node(struct super_block *sb, u64 oid, u32 storage,
struct apfs_sb_info *sbi = APFS_SB(sb);
struct apfs_nxsb_info *nxi = APFS_NXI(sb);
struct buffer_head *bh = NULL;
struct apfs_btree_node_phys *raw;
struct apfs_node *node;
struct apfs_nloc *free_head;
struct apfs_ephemeral_object_info *eph_info = NULL;
struct apfs_btree_node_phys *raw = NULL;
struct apfs_node *node = NULL;
struct apfs_nloc *free_head = NULL;
u64 bno;
int err;
@@ -95,6 +96,8 @@ struct apfs_node *apfs_read_node(struct super_block *sb, u64 oid, u32 storage,
apfs_err(sb, "object read failed for bno 0x%llx", bno);
return (void *)bh;
}
bno = bh->b_blocknr;
raw = (struct apfs_btree_node_phys *)bh->b_data;
break;
case APFS_OBJ_PHYSICAL:
bh = apfs_read_object_block(sb, oid, write, false /* preserve */);
@@ -102,21 +105,30 @@ struct apfs_node *apfs_read_node(struct super_block *sb, u64 oid, u32 storage,
apfs_err(sb, "object read failed for bno 0x%llx", oid);
return (void *)bh;
}
oid = bh->b_blocknr;
bno = oid = bh->b_blocknr;
raw = (struct apfs_btree_node_phys *)bh->b_data;
break;
case APFS_OBJ_EPHEMERAL:
/* Ephemeral objects are checkpoint data, so ignore 'write' */
bh = apfs_read_ephemeral_object(sb, oid);
if (IS_ERR(bh)) {
apfs_err(sb, "ephemeral read failed for oid 0x%llx", oid);
return (void *)bh;
/* Ephemeral objects are already in memory */
eph_info = apfs_ephemeral_object_lookup(sb, oid);
if (IS_ERR(eph_info)) {
apfs_err(sb, "no ephemeral node for oid 0x%llx", oid);
return (void *)eph_info;
}
if (eph_info->size != sb->s_blocksize) {
apfs_err(sb, "unsupported size for ephemeral node (%u)", eph_info->size);
return ERR_PTR(-EOPNOTSUPP);
}
bno = 0; /* In memory, so meaningless */
raw = eph_info->object;
/* Only for consistency, will happen again on commit */
if (write)
raw->btn_o.o_xid = cpu_to_le64(nxi->nx_xid);
break;
default:
apfs_alert(sb, "invalid storage type %u - bug!", storage);
return ERR_PTR(-EINVAL);
}
raw = (struct apfs_btree_node_phys *) bh->b_data;
node = kmalloc(sizeof(*node), GFP_KERNEL);
if (!node) {
@@ -138,19 +150,21 @@ struct apfs_node *apfs_read_node(struct super_block *sb, u64 oid, u32 storage,
node->val_free_list_len = le16_to_cpu(free_head->len);
node->object.sb = sb;
node->object.block_nr = bh->b_blocknr;
node->object.block_nr = bno;
node->object.oid = oid;
node->object.o_bh = bh;
node->object.data = bh->b_data;
node->object.data = (char *)raw;
node->object.ephemeral = !bh;
if (nxi->nx_flags & APFS_CHECK_NODES && !apfs_obj_verify_csum(sb, bh)) {
/* Ephemeral objects already got checked on mount */
if (!node->object.ephemeral && nxi->nx_flags & APFS_CHECK_NODES && !apfs_obj_verify_csum(sb, bh)) {
/* TODO: don't check this twice for virtual/physical objects */
apfs_err(sb, "bad checksum for node in block 0x%llx", (unsigned long long)bh->b_blocknr);
apfs_err(sb, "bad checksum for node in block 0x%llx", (unsigned long long)bno);
apfs_node_free(node);
return ERR_PTR(-EFSBADCRC);
}
if (!apfs_node_is_valid(sb, node)) {
apfs_err(sb, "bad node in block 0x%llx", (unsigned long long)bh->b_blocknr);
apfs_err(sb, "bad node in block 0x%llx", (unsigned long long)bno);
apfs_node_free(node);
return ERR_PTR(-EFSCORRUPTED);
}
@@ -332,9 +346,10 @@ static struct apfs_node *apfs_create_node(struct super_block *sb, u32 storage)
struct apfs_nxsb_info *nxi = APFS_NXI(sb);
struct apfs_nx_superblock *msb_raw = nxi->nx_raw;
struct apfs_superblock *vsb_raw = sbi->s_vsb_raw;
struct apfs_node *node;
struct buffer_head *bh;
struct apfs_btree_node_phys *raw;
struct apfs_ephemeral_object_info *eph_info = NULL;
struct apfs_node *node = NULL;
struct buffer_head *bh = NULL;
struct apfs_btree_node_phys *raw = NULL;
u64 bno, oid;
int err;
@@ -370,29 +385,38 @@ static struct apfs_node *apfs_create_node(struct super_block *sb, u32 storage)
oid = bno;
break;
case APFS_OBJ_EPHEMERAL:
apfs_cpoint_data_allocate(sb, &bno);
oid = le64_to_cpu(msb_raw->nx_next_oid);
le64_add_cpu(&msb_raw->nx_next_oid, 1);
err = apfs_create_cpoint_map(sb, oid, bno);
if (err) {
apfs_err(sb, "checkpoint map creation failed (0x%llx-0x%llx)", oid, bno);
return ERR_PTR(err);
if (nxi->nx_eph_count >= APFS_EPHEMERAL_LIST_LIMIT) {
apfs_err(sb, "creating too many ephemeral objects?");
return ERR_PTR(-EOPNOTSUPP);
}
eph_info = &nxi->nx_eph_list[nxi->nx_eph_count++];
eph_info->object = kzalloc(sb->s_blocksize, GFP_KERNEL);
if (!eph_info->object)
return ERR_PTR(-ENOMEM);
eph_info->size = sb->s_blocksize;
oid = eph_info->oid = le64_to_cpu(msb_raw->nx_next_oid);
le64_add_cpu(&msb_raw->nx_next_oid, 1);
break;
default:
apfs_alert(sb, "invalid storage type %u - bug!", storage);
return ERR_PTR(-EINVAL);
}
bh = apfs_getblk(sb, bno);
if (!bh)
return ERR_PTR(-EIO);
raw = (void *)bh->b_data;
err = apfs_transaction_join(sb, bh);
if (err)
goto fail;
set_buffer_csum(bh);
if (storage == APFS_OBJ_EPHEMERAL) {
bh = NULL;
bno = 0;
raw = eph_info->object;
} else {
bh = apfs_getblk(sb, bno);
if (!bh)
return ERR_PTR(-EIO);
bno = bh->b_blocknr;
raw = (void *)bh->b_data;
err = apfs_transaction_join(sb, bh);
if (err)
goto fail;
set_buffer_csum(bh);
}
/* Set most of the object header, but the subtype is up to the caller */
raw->btn_o.o_oid = cpu_to_le64(oid);
@@ -420,14 +444,20 @@ static struct apfs_node *apfs_create_node(struct super_block *sb, u32 storage)
}
node->object.sb = sb;
node->object.block_nr = bh->b_blocknr;
node->object.block_nr = bno;
node->object.oid = oid;
node->object.o_bh = bh;
node->object.data = bh->b_data;
node->object.data = (char *)raw;
node->object.ephemeral = !bh;
return node;
fail:
brelse(bh);
if (storage == APFS_OBJ_EPHEMERAL)
kfree(raw);
else
brelse(bh);
raw = NULL;
bh = NULL;
return ERR_PTR(err);
}
@@ -442,9 +472,11 @@ fail:
int apfs_delete_node(struct apfs_node *node, int type)
{
struct super_block *sb = node->object.sb;
struct apfs_nxsb_info *nxi = APFS_NXI(sb);
struct apfs_superblock *vsb_raw;
u64 oid = node->object.oid;
u64 bno = node->object.block_nr;
struct apfs_ephemeral_object_info *eph_info = NULL, *eph_info_end = NULL;
int err;
switch (type) {
@@ -479,16 +511,17 @@ int apfs_delete_node(struct apfs_node *node, int type)
le64_add_cpu(&vsb_raw->apfs_total_blocks_freed, 1);
return 0;
case APFS_QUERY_FREE_QUEUE:
err = apfs_cpoint_data_free(sb, bno);
if (err) {
apfs_err(sb, "failed to free checkpoint block 0x%llx", bno);
return err;
}
err = apfs_remove_cpoint_map(sb, bno);
if (err) {
apfs_err(sb, "checkpoint map removal failed (0x%llx)", bno);
return err;
eph_info_end = &nxi->nx_eph_list[nxi->nx_eph_count];
eph_info = apfs_ephemeral_object_lookup(sb, node->object.oid);
if (IS_ERR(eph_info)) {
apfs_alert(sb, "can't find ephemeral object to delete");
return PTR_ERR(eph_info);
}
kfree(eph_info->object);
eph_info->object = NULL;
memmove(eph_info, eph_info + 1, (char *)eph_info_end - (char *)(eph_info + 1));
eph_info_end->object = NULL;
--nxi->nx_eph_count;
return 0;
default:
apfs_alert(sb, "new query type must implement node deletion (%d)", type);
@@ -538,9 +571,10 @@ void apfs_update_node(struct apfs_node *node)
if (!free_head->len)
free_head->off = cpu_to_le16(APFS_BTOFF_INVALID);
(void)bh;
ASSERT(buffer_trans(bh));
ASSERT(buffer_csum(bh));
if (bh) {
ASSERT(buffer_trans(bh));
ASSERT(buffer_csum(bh));
}
}
/**
@@ -1193,6 +1227,7 @@ static int apfs_node_temp_dup(const struct apfs_node *original, struct apfs_node
*dup = *original;
dup->object.o_bh = NULL;
dup->object.data = NULL;
dup->object.ephemeral = false;
buffer = kmalloc(sb->s_blocksize, GFP_KERNEL);
if (!buffer) {
+99 -198
View File
@@ -37,131 +37,129 @@ static u64 apfs_fletcher64(void *addr, size_t len)
int apfs_obj_verify_csum(struct super_block *sb, struct buffer_head *bh)
{
struct apfs_obj_phys *obj = (struct apfs_obj_phys *)bh->b_data;
/* The checksum may be stale until the transaction is committed */
if (buffer_trans(bh))
return 1;
return apfs_multiblock_verify_csum(bh->b_data, sb->s_blocksize);
}
return (le64_to_cpu(obj->o_cksum) ==
apfs_fletcher64((char *) obj + APFS_MAX_CKSUM_SIZE,
sb->s_blocksize - APFS_MAX_CKSUM_SIZE));
/**
* apfs_multiblock_verify_csum - Verify an object's checksum
* @object: the object to verify
* @size: size of the object in bytes (may be multiple blocks)
*
* Returns 1 on success, 0 on failure.
*/
int apfs_multiblock_verify_csum(char *object, u32 size)
{
struct apfs_obj_phys *obj = (struct apfs_obj_phys *)object;
u64 actual_csum, header_csum;
header_csum = le64_to_cpu(obj->o_cksum);
actual_csum = apfs_fletcher64(object + APFS_MAX_CKSUM_SIZE, size - APFS_MAX_CKSUM_SIZE);
return header_csum == actual_csum;
}
/**
* apfs_obj_set_csum - Set the fletcher checksum in an object header
* @sb: superblock structure
* @obj: the object header
*
* The object must have a length of a single block.
*/
void apfs_obj_set_csum(struct super_block *sb, struct apfs_obj_phys *obj)
{
u64 cksum = apfs_fletcher64((char *)obj + APFS_MAX_CKSUM_SIZE,
sb->s_blocksize - APFS_MAX_CKSUM_SIZE);
apfs_multiblock_set_csum((char *)obj, sb->s_blocksize);
}
/**
* apfs_multiblock_set_csum - Set an object's checksum
* @object: the object to checksum
* @size: size of the object in bytes (may be multiple blocks)
*/
void apfs_multiblock_set_csum(char *object, u32 size)
{
struct apfs_obj_phys *obj = (struct apfs_obj_phys *)object;
u64 cksum;
cksum = apfs_fletcher64(object + APFS_MAX_CKSUM_SIZE, size - APFS_MAX_CKSUM_SIZE);
obj->o_cksum = cpu_to_le64(cksum);
}
/**
* apfs_cpm_lookup_oid - Search a checkpoint-mapping block for a given oid
* @sb: superblock structure
* @cpm: checkpoint-mapping block (on disk)
* @oid: the ephemeral object id to look up
* @bno: on return, the block number for the object
*
* Returns -EFSCORRUPTED in case of corruption, or -EAGAIN if @oid is not
* listed in @cpm; returns 0 on success.
*/
static int apfs_cpm_lookup_oid(struct super_block *sb,
struct apfs_checkpoint_map_phys *cpm,
u64 oid, u64 *bno)
{
u32 map_count = le32_to_cpu(cpm->cpm_count);
int i;
if (map_count > apfs_max_maps_per_block(sb)) {
apfs_err(sb, "block has too many maps (%d)", map_count);
return -EFSCORRUPTED;
}
for (i = 0; i < map_count; ++i) {
struct apfs_checkpoint_mapping *map = &cpm->cpm_map[i];
if (le64_to_cpu(map->cpm_oid) == oid) {
*bno = le64_to_cpu(map->cpm_paddr);
return 0;
}
}
return -EAGAIN; /* The mapping may still be in the next block */
}
/**
* apfs_read_cpm_block - Read the checkpoint mapping block
* @sb: super block structure
*
* Only a single cpm block is supported for now. Returns the buffer head for
* the block on success, or NULL in case of failure.
*/
static struct buffer_head *apfs_read_cpm_block(struct super_block *sb)
{
struct apfs_nx_superblock *raw_sb = APFS_NXI(sb)->nx_raw;
u64 desc_base = le64_to_cpu(raw_sb->nx_xp_desc_base);
u32 desc_index = le32_to_cpu(raw_sb->nx_xp_desc_index);
u32 desc_blks = le32_to_cpu(raw_sb->nx_xp_desc_blocks);
u32 desc_len = le32_to_cpu(raw_sb->nx_xp_desc_len);
u64 cpm_bno;
if (!desc_blks || desc_len < 2)
return NULL;
/* Last block in area is superblock; we want the last mapping block */
cpm_bno = desc_base + (desc_index + desc_len - 2) % desc_blks;
return apfs_sb_bread(sb, cpm_bno);
}
/**
* apfs_create_cpoint_map - Create a checkpoint mapping
* apfs_create_cpm_block - Create a new checkpoint-mapping block
* @sb: filesystem superblock
* @oid: ephemeral object id
* @bno: block number
* @bno: block number to use
* @bh_p: on return, the buffer head for the block
*
* Only mappings for free queue nodes are supported for now. Returns 0 on
* success or a negative error code in case of failure.
* Returns 0 on success or a negative error code in case of failure.
*/
int apfs_create_cpoint_map(struct super_block *sb, u64 oid, u64 bno)
int apfs_create_cpm_block(struct super_block *sb, u64 bno, struct buffer_head **bh_p)
{
struct buffer_head *bh;
struct apfs_checkpoint_map_phys *cpm;
struct apfs_checkpoint_mapping *map;
u32 cpm_count;
int err = 0;
struct apfs_nxsb_info *nxi = APFS_NXI(sb);
struct apfs_checkpoint_map_phys *cpm = NULL;
struct buffer_head *bh = NULL;
int err;
bh = apfs_read_cpm_block(sb);
if (!bh)
bh = apfs_getblk(sb, bno);
if (!bh) {
apfs_err(sb, "failed to map cpm block");
return -EIO;
cpm = (struct apfs_checkpoint_map_phys *)bh->b_data;
}
err = apfs_transaction_join(sb, bh);
if (err) {
brelse(bh);
return err;
}
set_buffer_csum(bh);
cpm = (void *)bh->b_data;
memset(cpm, 0, sb->s_blocksize);
cpm->cpm_o.o_oid = cpu_to_le64(bno);
cpm->cpm_o.o_xid = cpu_to_le64(nxi->nx_xid);
cpm->cpm_o.o_type = cpu_to_le32(APFS_OBJ_PHYSICAL | APFS_OBJECT_TYPE_CHECKPOINT_MAP);
cpm->cpm_o.o_subtype = cpu_to_le32(APFS_OBJECT_TYPE_INVALID);
/* For now: the caller will have to update these fields */
cpm->cpm_flags = cpu_to_le32(APFS_CHECKPOINT_MAP_LAST);
cpm->cpm_count = 0;
*bh_p = bh;
return 0;
}
/**
* apfs_create_cpoint_map - Create a checkpoint mapping for an object
* @sb: filesystem superblock
* @cpm: checkpoint mapping block to use
* @obj: header for the ephemeral object
* @bno: block number for the ephemeral object
* @size: size of the ephemeral object in bytes
*
* Returns 0 on success or a negative error code in case of failure, which may
* be -ENOSPC if @cpm is full.
*/
int apfs_create_cpoint_map(struct super_block *sb, struct apfs_checkpoint_map_phys *cpm, struct apfs_obj_phys *obj, u64 bno, u32 size)
{
struct apfs_checkpoint_mapping *map = NULL;
u32 cpm_count;
apfs_assert_in_transaction(sb, &cpm->cpm_o);
cpm_count = le32_to_cpu(cpm->cpm_count);
if (cpm_count >= apfs_max_maps_per_block(sb)) { /* TODO */
apfs_warn(sb, "creation of cpm blocks not yet supported");
err = -EOPNOTSUPP;
goto fail;
}
if (cpm_count >= apfs_max_maps_per_block(sb))
return -ENOSPC;
map = &cpm->cpm_map[cpm_count];
le32_add_cpu(&cpm->cpm_count, 1);
map->cpm_type = cpu_to_le32(APFS_OBJ_EPHEMERAL |
APFS_OBJECT_TYPE_BTREE_NODE);
map->cpm_subtype = cpu_to_le32(APFS_OBJECT_TYPE_SPACEMAN_FREE_QUEUE);
map->cpm_size = cpu_to_le32(sb->s_blocksize);
map->cpm_type = obj->o_type;
map->cpm_subtype = obj->o_subtype;
map->cpm_size = cpu_to_le32(size);
map->cpm_pad = 0;
map->cpm_fs_oid = 0;
map->cpm_oid = cpu_to_le64(oid);
map->cpm_oid = obj->o_oid;
map->cpm_paddr = cpu_to_le64(bno);
fail:
brelse(bh);
return err;
return 0;
}
/**
@@ -197,120 +195,23 @@ u64 apfs_data_index_to_bno(struct super_block *sb, u32 index)
}
/**
* apfs_remove_cpoint_map - Remove a checkpoint mapping
* @sb: filesystem superblock
* @bno: block number to delete
*
* Only mappings for free queue nodes are supported for now. Blocks that come
* after the deleted one are assumed to shift back one place. Returns 0 on
* success or a negative error code in case of failure.
*/
int apfs_remove_cpoint_map(struct super_block *sb, u64 bno)
{
struct buffer_head *bh;
struct apfs_checkpoint_map_phys *cpm;
struct apfs_checkpoint_mapping *map, *maps_start, *maps_end;
struct apfs_checkpoint_mapping *bno_map = NULL;
u32 cpm_count;
u32 bno_off;
int err = 0;
bh = apfs_read_cpm_block(sb);
if (!bh) {
apfs_err(sb, "failed to read cpm block");
return -EIO;
}
cpm = (struct apfs_checkpoint_map_phys *)bh->b_data;
apfs_assert_in_transaction(sb, &cpm->cpm_o);
/* TODO: multiple cpm blocks? */
cpm_count = le32_to_cpu(cpm->cpm_count);
if (cpm_count > apfs_max_maps_per_block(sb)) {
apfs_err(sb, "block has too many maps (%d)", cpm_count);
err = -EFSCORRUPTED;
goto fail;
}
maps_start = &cpm->cpm_map[0];
maps_end = &cpm->cpm_map[cpm_count];
bno_off = apfs_index_in_data_area(sb, bno);
for (map = maps_start; map < maps_end; ++map) {
u32 curr_off;
if (le64_to_cpu(map->cpm_paddr) == bno)
bno_map = map;
curr_off = apfs_index_in_data_area(sb, le64_to_cpu(map->cpm_paddr));
if (curr_off > bno_off)
map->cpm_paddr = cpu_to_le64(apfs_data_index_to_bno(sb, curr_off - 1));
}
if (!bno_map) {
apfs_err(sb, "no mapping for bno 0x%llx", bno);
err = -EFSCORRUPTED;
goto fail;
}
memmove(bno_map, bno_map + 1, (maps_end - bno_map - 1) * sizeof(*bno_map));
le32_add_cpu(&cpm->cpm_count, -1);
fail:
brelse(bh);
return err;
}
/**
* apfs_read_ephemeral_object - Find and map an ephemeral object
* apfs_ephemeral_object_lookup - Find an ephemeral object info in memory
* @sb: superblock structure
* @oid: ephemeral object id
*
* Returns the mapped buffer head for the object, or an error pointer in case
* Returns a pointer to the object info on success, or an error pointer in case
* of failure.
*/
struct buffer_head *apfs_read_ephemeral_object(struct super_block *sb, u64 oid)
struct apfs_ephemeral_object_info *apfs_ephemeral_object_lookup(struct super_block *sb, u64 oid)
{
struct apfs_nxsb_info *nxi = APFS_NXI(sb);
struct apfs_nx_superblock *raw_sb = nxi->nx_raw;
u64 desc_base = le64_to_cpu(raw_sb->nx_xp_desc_base);
u32 desc_index = le32_to_cpu(raw_sb->nx_xp_desc_index);
u32 desc_blks = le32_to_cpu(raw_sb->nx_xp_desc_blocks);
u32 desc_len = le32_to_cpu(raw_sb->nx_xp_desc_len);
u32 i;
struct apfs_ephemeral_object_info *list = NULL;
int i;
if (!desc_blks || !desc_len) {
apfs_err(sb, "no blocks in checkpoint descriptor area");
return ERR_PTR(-EFSCORRUPTED);
}
/* Last block in the area is superblock; the rest are mapping blocks */
for (i = 0; i < desc_len - 1; ++i) {
struct buffer_head *bh;
struct apfs_checkpoint_map_phys *cpm;
u64 cpm_bno = desc_base + (desc_index + i) % desc_blks;
u64 obj_bno;
int err;
bh = apfs_sb_bread(sb, cpm_bno);
if (!bh) {
apfs_err(sb, "failed to read cpm block");
return ERR_PTR(-EIO);
}
cpm = (struct apfs_checkpoint_map_phys *)bh->b_data;
err = apfs_cpm_lookup_oid(sb, cpm, oid, &obj_bno);
brelse(bh);
cpm = NULL;
if (err == -EAGAIN) /* Search the next mapping block */
continue;
if (err) {
apfs_err(sb, "cpm lookup failed for oid 0x%llx", oid);
return ERR_PTR(err);
}
bh = apfs_sb_bread(sb, obj_bno);
if (!bh) {
apfs_err(sb, "failed to read ephemeral block");
return ERR_PTR(-EIO);
}
return bh;
list = nxi->nx_eph_list;
for (i = 0; i < nxi->nx_eph_count; ++i) {
if (list[i].oid == oid)
return &list[i];
}
apfs_err(sb, "no mapping for oid 0x%llx", oid);
return ERR_PTR(-EFSCORRUPTED);
+18 -24
View File
@@ -86,9 +86,9 @@ static int apfs_read_spaceman_dev(struct super_block *sb,
spaceman->sm_free_count = le64_to_cpu(dev->sm_free_count);
spaceman->sm_addr_offset = le32_to_cpu(dev->sm_addr_offset);
/* Check that all the cib addresses fit in the spaceman block */
/* Check that all the cib addresses fit in the spaceman object */
if ((long long)spaceman->sm_addr_offset +
(long long)spaceman->sm_cib_count * sizeof(u64) > sb->s_blocksize) {
(long long)spaceman->sm_cib_count * sizeof(u64) > spaceman->sm_size) {
apfs_err(sb, "too many cibs (%u)", spaceman->sm_cib_count);
return -EFSCORRUPTED;
}
@@ -108,9 +108,9 @@ static __le16 *apfs_spaceman_get_16(struct super_block *sb, size_t off)
struct apfs_spaceman *spaceman = APFS_SM(sb);
struct apfs_spaceman_phys *sm_raw = spaceman->sm_raw;
if (off > sb->s_blocksize)
if (off > spaceman->sm_size)
return NULL;
if (off + sizeof(__le16) > sb->s_blocksize)
if (off + sizeof(__le16) > spaceman->sm_size)
return NULL;
return (void *)sm_raw + off;
}
@@ -127,9 +127,9 @@ static __le64 *apfs_spaceman_get_64(struct super_block *sb, size_t off)
struct apfs_spaceman *spaceman = APFS_SM(sb);
struct apfs_spaceman_phys *sm_raw = spaceman->sm_raw;
if (off > sb->s_blocksize)
if (off > spaceman->sm_size)
return NULL;
if (off + sizeof(__le64) > sb->s_blocksize)
if (off + sizeof(__le64) > spaceman->sm_size)
return NULL;
return (void *)sm_raw + off;
}
@@ -164,15 +164,15 @@ static int apfs_update_ip_bm_free_next(struct super_block *sb)
struct apfs_spaceman *spaceman = APFS_SM(sb);
struct apfs_spaceman_phys *raw = spaceman->sm_raw;
u32 free_next_off = le32_to_cpu(raw->sm_ip_bm_free_next_offset);
int bmap_count = le32_to_cpu(raw->sm_ip_bm_block_count);
int bmap_count = le32_to_cpu(raw->sm_ip_bm_block_count); /* TODO: this can overflow! */
__le16 *free_next;
int i;
if (free_next_off > sb->s_blocksize) {
if (free_next_off > spaceman->sm_size) {
apfs_err(sb, "offset out of bounds (%u)", free_next_off);
return -EFSCORRUPTED;
}
if (free_next_off + bmap_count * sizeof(*free_next) > sb->s_blocksize) {
if (free_next_off + bmap_count * sizeof(*free_next) > spaceman->sm_size) {
apfs_err(sb, "free next out of bounds (%u-%u)", free_next_off, bmap_count * (u32)sizeof(*free_next));
return -EFSCORRUPTED;
}
@@ -532,8 +532,6 @@ static int apfs_flush_free_queue(struct super_block *sb, unsigned int qid)
break;
}
set_buffer_csum(sm->sm_bh);
fail:
apfs_node_free(fq_root);
return err;
@@ -588,7 +586,7 @@ int apfs_read_spaceman(struct super_block *sb)
struct apfs_nxsb_info *nxi = APFS_NXI(sb);
struct apfs_nx_superblock *raw_sb = nxi->nx_raw;
struct apfs_spaceman *spaceman = NULL;
struct buffer_head *sm_bh;
struct apfs_ephemeral_object_info *sm_eph_info = NULL;
struct apfs_spaceman_phys *sm_raw;
u32 sm_flags;
u64 oid = le64_to_cpu(raw_sb->nx_spaceman_oid);
@@ -597,12 +595,13 @@ int apfs_read_spaceman(struct super_block *sb)
if (sb->s_flags & SB_RDONLY) /* The space manager won't be needed */
return 0;
sm_bh = apfs_read_ephemeral_object(sb, oid);
if (IS_ERR(sm_bh)) {
apfs_err(sb, "ephemeral read failed for oid 0x%llx", oid);
return PTR_ERR(sm_bh);
sm_eph_info = apfs_ephemeral_object_lookup(sb, oid);
if (IS_ERR(sm_eph_info)) {
apfs_err(sb, "no spaceman object for oid 0x%llx", oid);
return PTR_ERR(sm_eph_info);
}
sm_raw = (struct apfs_spaceman_phys *)sm_bh->b_data;
sm_raw = (struct apfs_spaceman_phys *)sm_eph_info->object;
sm_raw->sm_o.o_xid = cpu_to_le64(nxi->nx_xid);
spaceman = apfs_allocate_spaceman(sb, le32_to_cpu(sm_raw->sm_ip_bm_size_in_blocks));
if (IS_ERR(spaceman)) {
@@ -610,6 +609,8 @@ int apfs_read_spaceman(struct super_block *sb)
err = PTR_ERR(spaceman);
goto fail;
}
spaceman->sm_raw = sm_raw;
spaceman->sm_size = sm_eph_info->size;
spaceman->sm_free_cache_base = spaceman->sm_free_cache_blkcnt = 0;
@@ -634,8 +635,6 @@ int apfs_read_spaceman(struct super_block *sb)
goto fail;
}
spaceman->sm_bh = sm_bh;
spaceman->sm_raw = sm_raw;
err = apfs_rotate_ip_bitmaps(sb);
if (err) {
apfs_err(sb, "failed to rotate ip bitmaps");
@@ -654,8 +653,6 @@ int apfs_read_spaceman(struct super_block *sb)
return 0;
fail:
brelse(sm_bh);
spaceman->sm_bh = sm_bh = NULL;
spaceman->sm_raw = NULL;
return err;
}
@@ -857,7 +854,6 @@ int apfs_free_queue_insert_nocache(struct super_block *sb, u64 bno, u64 count)
if (!fq->sfq_oldest_xid)
fq->sfq_oldest_xid = cpu_to_le64(nxi->nx_xid);
le64_add_cpu(&fq->sfq_count, count);
set_buffer_csum(sm->sm_bh);
fail:
apfs_free_query(query);
@@ -1182,7 +1178,6 @@ int apfs_spaceman_allocate_block(struct super_block *sb, u64 *bno, bool backward
apfs_spaceman_write_cib_addr(sb, index, cib_bh->b_blocknr);
/* The free block count has changed */
apfs_write_spaceman(sm);
set_buffer_csum(sm->sm_bh);
}
brelse(cib_bh);
if (err == -ENOSPC) /* This cib is full */
@@ -1245,7 +1240,6 @@ static int apfs_main_free(struct super_block *sb, u64 bno)
apfs_spaceman_write_cib_addr(sb, cib_idx, cib_bh->b_blocknr);
/* The free block count has changed */
apfs_write_spaceman(sm);
set_buffer_csum(sm->sm_bh);
}
brelse(cib_bh);
if (err)
+91 -53
View File
@@ -152,14 +152,16 @@ out_unlock:
mutex_unlock(&nxs_mutex);
}
static int apfs_check_nx_features(struct super_block *sb);
/**
* apfs_map_main_super - Find the container superblock and map it into memory
* apfs_read_main_super - Find the container superblock and read it into memory
* @sb: superblock structure
*
* Returns a negative error code in case of failure. On success, returns 0
* and sets the nx_raw, nx_object and nx_xid fields of APFS_NXI(@sb).
* and sets the nx_raw and nx_xid fields of APFS_NXI(@sb).
*/
static int apfs_map_main_super(struct super_block *sb)
static int apfs_read_main_super(struct super_block *sb)
{
struct apfs_nxsb_info *nxi = APFS_NXI(sb);
struct buffer_head *bh;
@@ -228,20 +230,20 @@ static int apfs_map_main_super(struct super_block *sb)
desc_bh = NULL;
}
nxi->nx_raw = kmalloc(sb->s_blocksize, GFP_KERNEL);
if (!nxi->nx_raw) {
err = -ENOMEM;
goto fail;
}
memcpy(nxi->nx_raw, bh->b_data, sb->s_blocksize);
nxi->nx_bno = bno;
nxi->nx_xid = xid;
nxi->nx_raw = msb_raw;
nxi->nx_object.sb = sb; /* XXX: these "objects" never made any sense */
nxi->nx_object.block_nr = bno;
nxi->nx_object.oid = le64_to_cpu(msb_raw->nx_o.o_oid);
nxi->nx_object.o_bh = bh;
nxi->nx_object.data = bh->b_data;
/* For now we only support blocksize < PAGE_SIZE */
nxi->nx_blocksize = sb->s_blocksize;
nxi->nx_blocksize_bits = sb->s_blocksize_bits;
return 0;
err = apfs_check_nx_features(sb);
fail:
brelse(bh);
return err;
@@ -280,18 +282,19 @@ static void apfs_update_software_info(struct super_block *sb)
static struct file_system_type apfs_fs_type;
/**
* apfs_unmap_main_super - Clean up apfs_map_main_super()
* apfs_free_main_super - Clean up apfs_read_main_super()
* @sbi: in-memory superblock info
*
* It also cleans up after apfs_attach_nxi(), so the name is no longer accurate.
*/
static inline void apfs_unmap_main_super(struct apfs_sb_info *sbi)
static inline void apfs_free_main_super(struct apfs_sb_info *sbi)
{
struct apfs_nxsb_info *nxi = sbi->s_nxi;
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 5, 0)
fmode_t mode = FMODE_READ | FMODE_EXCL;
#endif
struct apfs_object *obj = NULL;
struct apfs_ephemeral_object_info *eph_list = NULL;
int i;
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 5, 0)
if (nxi->nx_flags & APFS_READWRITE)
@@ -304,13 +307,24 @@ static inline void apfs_unmap_main_super(struct apfs_sb_info *sbi)
if (--nxi->nx_refcnt)
goto out;
obj = &nxi->nx_object;
obj->data = NULL;
brelse(obj->o_bh);
obj->o_bh = NULL;
obj = NULL;
/* Clean up all the ephemeral objects in memory */
eph_list = nxi->nx_eph_list;
if (eph_list) {
for (i = 0; i < nxi->nx_eph_count; ++i) {
kfree(eph_list[i].object);
eph_list[i].object = NULL;
}
kfree(eph_list);
eph_list = nxi->nx_eph_list = NULL;
nxi->nx_eph_count = 0;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 5, 0)
kfree(nxi->nx_raw);
nxi->nx_raw = NULL;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0)
bdev_release(nxi->nx_bdev_handle);
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(6, 5, 0)
blkdev_put(nxi->nx_bdev, &apfs_fs_type);
#else
blkdev_put(nxi->nx_bdev, mode);
@@ -713,7 +727,7 @@ fail:
mutex_lock(&nxs_mutex);
apfs_put_omap(sbi->s_omap);
sbi->s_omap = NULL;
apfs_unmap_main_super(sbi);
apfs_free_main_super(sbi);
mutex_unlock(&nxs_mutex);
sb->s_fs_info = NULL;
@@ -1203,26 +1217,26 @@ out:
}
/**
* apfs_check_features - Check for unsupported features in the filesystem
* apfs_check_nx_features - Check for unsupported features in the container
* @sb: superblock structure
*
* Returns -EINVAL if unsupported incompatible features are found, otherwise
* returns 0.
*/
static int apfs_check_features(struct super_block *sb)
static int apfs_check_nx_features(struct super_block *sb)
{
struct apfs_nx_superblock *msb_raw = APFS_NXI(sb)->nx_raw;
struct apfs_superblock *vsb_raw = APFS_SB(sb)->s_vsb_raw;
struct apfs_nx_superblock *msb_raw = NULL;
u64 features;
ASSERT(msb_raw);
ASSERT(vsb_raw);
msb_raw = APFS_NXI(sb)->nx_raw;
if (!msb_raw) {
apfs_alert(sb, "feature checks are misplaced");
return -EINVAL;
}
features = le64_to_cpu(msb_raw->nx_incompatible_features);
if (features & ~APFS_NX_SUPPORTED_INCOMPAT_MASK) {
apfs_warn(sb,
"unknown incompatible container features (0x%llx)",
features);
apfs_warn(sb, "unknown incompatible container features (0x%llx)", features);
return -EINVAL;
}
if (features & APFS_NX_INCOMPAT_FUSION) {
@@ -1230,10 +1244,38 @@ static int apfs_check_features(struct super_block *sb)
return -EINVAL;
}
features = le64_to_cpu(msb_raw->nx_readonly_compatible_features);
if (features & ~APFS_NX_SUPPORTED_ROCOMPAT_MASK) {
apfs_warn(sb, "unknown read-only compatible container features (0x%llx)", features);
if (!sb_rdonly(sb)) {
apfs_warn(sb, "container can't be mounted read-write");
return -EINVAL;
}
}
return 0;
}
/**
* apfs_check_vol_features - Check for unsupported features in the volume
* @sb: superblock structure
*
* Returns -EINVAL if unsupported incompatible features are found, otherwise
* returns 0.
*/
static int apfs_check_vol_features(struct super_block *sb)
{
struct apfs_superblock *vsb_raw = NULL;
u64 features;
vsb_raw = APFS_SB(sb)->s_vsb_raw;
if (!vsb_raw) {
apfs_alert(sb, "feature checks are misplaced");
return -EINVAL;
}
features = le64_to_cpu(vsb_raw->apfs_incompatible_features);
if (features & ~APFS_SUPPORTED_INCOMPAT_MASK) {
apfs_warn(sb, "unknown incompatible volume features (0x%llx)",
features);
apfs_warn(sb, "unknown incompatible volume features (0x%llx)", features);
return -EINVAL;
}
if (features & APFS_INCOMPAT_DATALESS_SNAPS) {
@@ -1265,29 +1307,14 @@ static int apfs_check_features(struct super_block *sb)
if (!(features & APFS_FS_UNENCRYPTED))
apfs_warn(sb, "volume is encrypted, may not be read correctly");
features = le64_to_cpu(msb_raw->nx_readonly_compatible_features);
if (features & ~APFS_NX_SUPPORTED_ROCOMPAT_MASK) {
apfs_warn(sb,
"unknown read-only compatible container features (0x%llx)",
features);
if (!sb_rdonly(sb)) {
apfs_warn(sb, "container can't be mounted read-write");
return -EINVAL;
}
}
features = le64_to_cpu(vsb_raw->apfs_readonly_compatible_features);
if (features & ~APFS_SUPPORTED_ROCOMPAT_MASK) {
apfs_warn(sb,
"unknown read-only compatible volume features (0x%llx)",
features);
apfs_warn(sb, "unknown read-only compatible volume features (0x%llx)", features);
if (!sb_rdonly(sb)) {
apfs_warn(sb, "volume can't be mounted read-write");
return -EINVAL;
}
}
/* TODO: add checks for encryption, snapshots? */
return 0;
}
@@ -1385,7 +1412,7 @@ static int apfs_fill_super(struct super_block *sb, void *data, int silent)
if (err)
return err;
err = apfs_check_features(sb);
err = apfs_check_vol_features(sb);
if (err)
goto failed_omap;
@@ -1556,13 +1583,24 @@ static int apfs_attach_nxi(struct apfs_sb_info *sbi, const char *dev_name, fmode
nxi = apfs_nx_find_by_dev(dev);
if (!nxi) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0)
struct bdev_handle *handle;
#endif
struct block_device *bdev;
nxi = kzalloc(sizeof(*nxi), GFP_KERNEL);
if (!nxi)
return -ENOMEM;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 5, 0)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0)
handle = bdev_open_by_path(dev_name, mode, &apfs_fs_type, NULL);
if (IS_ERR(handle)) {
kfree(nxi);
return PTR_ERR(handle);
}
nxi->nx_bdev_handle = handle;
bdev = handle->bdev;
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(6, 5, 0)
bdev = blkdev_get_by_path(dev_name, mode, &apfs_fs_type, NULL);
#else
bdev = blkdev_get_by_path(dev_name, mode, &apfs_fs_type);
@@ -1635,13 +1673,13 @@ static struct dentry *apfs_mount(struct file_system_type *fs_type, int flags,
goto out_deactivate_super;
}
/* Only one superblock per volume */
apfs_unmap_main_super(sbi);
apfs_free_main_super(sbi);
kfree(sbi->s_snap_name);
sbi->s_snap_name = NULL;
kfree(sbi);
sbi = NULL;
} else {
error = apfs_map_main_super(sb);
error = apfs_read_main_super(sb);
if (error)
goto out_deactivate_super;
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 5, 0)
@@ -1660,7 +1698,7 @@ static struct dentry *apfs_mount(struct file_system_type *fs_type, int flags,
out_deactivate_super:
deactivate_locked_super(sb);
out_unmap_super:
apfs_unmap_main_super(sbi);
apfs_free_main_super(sbi);
out_free_sbi:
kfree(sbi->s_snap_name);
kfree(sbi);
+389 -389
View File
File diff suppressed because it is too large. Load diff