mirror of
https://github.com/linux-apfs/linux-apfs-rw.git
synced 2026-08-18 02:19:59 -07:00
Compare commits
No files matched your search
@@ -9,3 +9,4 @@
|
||||
*.a
|
||||
*.mod
|
||||
tags
|
||||
version.h
|
||||
@@ -14,8 +14,10 @@ apfs-y := btree.o compress.o dir.o extents.o file.o inode.o key.o libzbitmap.o \
|
||||
spaceman.o super.o symlink.o transaction.o unicode.o xattr.o xfield.o
|
||||
|
||||
default:
|
||||
./genver.sh
|
||||
make -C $(KERNEL_DIR) M=$(PWD)
|
||||
install:
|
||||
make -C $(KERNEL_DIR) M=$(PWD) modules_install
|
||||
clean:
|
||||
rm -f version.h
|
||||
make -C $(KERNEL_DIR) M=$(PWD) clean
|
||||
+2
-1
@@ -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
|
||||
=====
|
||||
|
||||
@@ -13,8 +13,6 @@
|
||||
#include <linux/version.h>
|
||||
#include "apfs_raw.h"
|
||||
|
||||
#define APFS_MODULE_ID_STRING "linux-apfs by EA Fernández"
|
||||
|
||||
#define EFSBADCRC EBADMSG /* Bad CRC detected */
|
||||
#define EFSCORRUPTED EUCLEAN /* Filesystem is corrupted */
|
||||
|
||||
@@ -66,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 */
|
||||
@@ -123,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 buffer_head *sm_ip; /* Current internal pool */
|
||||
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 */
|
||||
@@ -140,6 +139,15 @@ struct apfs_spaceman {
|
||||
*/
|
||||
u64 sm_free_cache_base;
|
||||
u64 sm_free_cache_blkcnt;
|
||||
|
||||
/* Shift to match an ip block with its bitmap in the array */
|
||||
int sm_ip_bmaps_shift;
|
||||
/* Mask to find an ip block's offset inside its ip bitmap */
|
||||
u32 sm_ip_bmaps_mask;
|
||||
/* Number of ip bitmaps */
|
||||
u32 sm_ip_bmaps_count;
|
||||
/* List of ip bitmaps, in order */
|
||||
struct buffer_head *sm_ip_bmaps[];
|
||||
};
|
||||
|
||||
#define TRANSACTION_MAIN_QUEUE_MAX 4096
|
||||
@@ -156,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 */
|
||||
@@ -197,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
|
||||
@@ -206,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 */
|
||||
@@ -219,7 +250,7 @@ struct apfs_nxsb_info {
|
||||
unsigned long nx_blocksize;
|
||||
unsigned char nx_blocksize_bits;
|
||||
|
||||
struct apfs_spaceman nx_spaceman;
|
||||
struct apfs_spaceman *nx_spaceman;
|
||||
struct apfs_nx_transaction nx_transaction;
|
||||
|
||||
/* For now, a single semaphore for every operation */
|
||||
@@ -283,6 +314,8 @@ struct apfs_sb_info {
|
||||
struct list_head list; /* List of mounted volumes in container */
|
||||
struct apfs_superblock *s_vsb_raw; /* On-disk volume sb */
|
||||
|
||||
dev_t s_anon_dev; /* Anonymous device for this volume-snapshot */
|
||||
|
||||
char *s_snap_name; /* Label for the mounted snapshot */
|
||||
u64 s_snap_xid; /* Transaction id for mounted snapshot */
|
||||
|
||||
@@ -342,7 +375,7 @@ static inline struct apfs_nxsb_info *APFS_NXI(struct super_block *sb)
|
||||
*/
|
||||
static inline struct apfs_spaceman *APFS_SM(struct super_block *sb)
|
||||
{
|
||||
return &APFS_NXI(sb)->nx_spaceman;
|
||||
return APFS_NXI(sb)->nx_spaceman;
|
||||
}
|
||||
|
||||
static inline bool apfs_is_case_insensitive(struct super_block *sb)
|
||||
@@ -614,7 +647,7 @@ static inline u64 apfs_cat_cnid(struct apfs_key_header *key)
|
||||
*/
|
||||
struct apfs_query {
|
||||
struct apfs_node *node; /* Node being searched */
|
||||
struct apfs_key *key; /* What the query is looking for */
|
||||
struct apfs_key key; /* What the query is looking for */
|
||||
|
||||
struct apfs_query *parent; /* Query for parent node */
|
||||
unsigned int flags;
|
||||
@@ -822,15 +855,17 @@ do { \
|
||||
} while (0)
|
||||
|
||||
/* btree.c */
|
||||
extern struct apfs_node *apfs_query_root(const struct apfs_query *query);
|
||||
extern struct apfs_query *apfs_alloc_query(struct apfs_node *node,
|
||||
struct apfs_query *parent);
|
||||
extern void apfs_free_query(struct apfs_query *query);
|
||||
extern int apfs_btree_query(struct super_block *sb, struct apfs_query **query);
|
||||
extern int apfs_omap_lookup_block(struct super_block *sb, struct apfs_omap *omap,
|
||||
u64 id, u64 *block, bool write);
|
||||
extern int apfs_omap_lookup_block(struct super_block *sb, struct apfs_omap *omap, u64 id, u64 *block, bool write);
|
||||
extern int apfs_omap_lookup_newest_block(struct super_block *sb, struct apfs_omap *omap, u64 id, u64 *block, bool write);
|
||||
extern int apfs_create_omap_rec(struct super_block *sb, u64 oid, u64 bno);
|
||||
extern int apfs_delete_omap_rec(struct super_block *sb, u64 oid);
|
||||
extern int apfs_query_join_transaction(struct apfs_query *query);
|
||||
extern int __apfs_btree_insert(struct apfs_query *query, void *key, int key_len, void *val, int val_len);
|
||||
extern int apfs_btree_insert(struct apfs_query *query, void *key, int key_len,
|
||||
void *val, int val_len);
|
||||
extern int apfs_btree_remove(struct apfs_query *query);
|
||||
@@ -990,7 +1025,7 @@ extern __printf(5, 6) void apfs_msg(struct super_block *sb, const char *prefix,
|
||||
extern struct apfs_node *apfs_read_node(struct super_block *sb, u64 oid,
|
||||
u32 storage, bool write);
|
||||
extern void apfs_update_node(struct apfs_node *node);
|
||||
extern int apfs_delete_node(struct apfs_query *query);
|
||||
extern int apfs_delete_node(struct apfs_node *node, int type);
|
||||
extern int apfs_node_query(struct super_block *sb, struct apfs_query *query);
|
||||
extern void apfs_node_query_first(struct apfs_query *query);
|
||||
extern int apfs_omap_map_from_query(struct apfs_query *query, struct apfs_omap_map *map);
|
||||
@@ -998,6 +1033,7 @@ extern int apfs_node_split(struct apfs_query *query);
|
||||
extern int apfs_node_locate_key(struct apfs_node *node, int index, int *off);
|
||||
extern void apfs_node_free(struct apfs_node *node);
|
||||
extern void apfs_node_free_range(struct apfs_node *node, u16 off, u16 len);
|
||||
extern bool apfs_node_has_room(struct apfs_node *node, int length, bool replace);
|
||||
extern int apfs_node_replace(struct apfs_query *query, void *key, int key_len, void *val, int val_len);
|
||||
extern int apfs_node_insert(struct apfs_query *query, void *key, int key_len, void *val, int val_len);
|
||||
extern int apfs_create_single_rec_node(struct apfs_query *query, void *key, int key_len, void *val, int val_len);
|
||||
@@ -1005,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);
|
||||
@@ -1034,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);
|
||||
@@ -1091,6 +1126,18 @@ extern const struct inode_operations apfs_symlink_inode_operations;
|
||||
/* xattr.c */
|
||||
extern const struct xattr_handler *apfs_xattr_handlers[];
|
||||
|
||||
/**
|
||||
* apfs_assert_query_is_valid - Assert that all of a query's ancestors are set
|
||||
* @query: the query to check
|
||||
*
|
||||
* A query may lose some of its ancestors during a node split, but nothing
|
||||
* should be done to such a query until it gets refreshed.
|
||||
*/
|
||||
static inline void apfs_assert_query_is_valid(const struct apfs_query *query)
|
||||
{
|
||||
ASSERT(apfs_node_is_root(apfs_query_root(query)));
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: the following are modified variants of buffer head functions that will
|
||||
* work with the shared block device for the container. The correct approach
|
||||
@@ -1118,7 +1165,11 @@ apfs_getblk(struct super_block *sb, sector_t block)
|
||||
{
|
||||
struct buffer_head *bh;
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 7, 0)
|
||||
bh = __getblk_gfp(APFS_NXI(sb)->nx_bdev, block, sb->s_blocksize, __GFP_MOVABLE);
|
||||
#else
|
||||
bh = bdev_getblk(APFS_NXI(sb)->nx_bdev, block, sb->s_blocksize, __GFP_MOVABLE);
|
||||
#endif
|
||||
if (bh)
|
||||
set_buffer_uptodate(bh);
|
||||
return bh;
|
||||
|
||||
@@ -99,24 +99,25 @@ static struct apfs_query *apfs_dentry_lookup(struct inode *dir,
|
||||
{
|
||||
struct super_block *sb = dir->i_sb;
|
||||
struct apfs_sb_info *sbi = APFS_SB(sb);
|
||||
struct apfs_key key;
|
||||
struct apfs_query *query;
|
||||
u64 cnid = apfs_ino(dir);
|
||||
bool hashed = apfs_is_normalization_insensitive(sb);
|
||||
int err;
|
||||
|
||||
apfs_init_drec_key(sb, cnid, child->name, child->len, &key);
|
||||
|
||||
query = apfs_alloc_query(sbi->s_cat_root, NULL /* parent */);
|
||||
if (!query)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
query->key = &key;
|
||||
apfs_init_drec_key(sb, cnid, child->name, child->len, &query->key);
|
||||
|
||||
/*
|
||||
* Distinct filenames in the same directory may (rarely) share the same
|
||||
* hash. The query code cannot handle that because their order in the
|
||||
* b-tree would depend on their unnormalized original names. Just get
|
||||
* hash. The query code cannot handle that because their order in the
|
||||
* b-tree would depend on their unnormalized original names. Just get
|
||||
* all the candidates and check them one by one.
|
||||
*
|
||||
* This is very wasteful for normalization-sensitive filesystems: there
|
||||
* are no hashes so we just check every single file in the directory for
|
||||
* no reason. This would be easy to avoid but does it matter? (TODO)
|
||||
*/
|
||||
query->flags |= APFS_QUERY_CAT | APFS_QUERY_ANY_NAME | APFS_QUERY_EXACT;
|
||||
do {
|
||||
@@ -128,6 +129,14 @@ static struct apfs_query *apfs_dentry_lookup(struct inode *dir,
|
||||
goto fail;
|
||||
} while (unlikely(apfs_filename_cmp(sb, child->name, child->len, drec->name, drec->name_len)));
|
||||
|
||||
/*
|
||||
* We may need to refresh the query later, but the refresh code doesn't
|
||||
* know how to deal with hash collisions. Instead set the key to the
|
||||
* unnormalized name and pretend that this was never a multiple query
|
||||
* in the first place.
|
||||
*/
|
||||
query->key.name = drec->name;
|
||||
query->flags &= ~(APFS_QUERY_MULTIPLE | APFS_QUERY_DONE | APFS_QUERY_NEXT);
|
||||
return query;
|
||||
|
||||
fail:
|
||||
@@ -173,7 +182,6 @@ static int apfs_readdir(struct file *file, struct dir_context *ctx)
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct apfs_sb_info *sbi = APFS_SB(sb);
|
||||
struct apfs_nxsb_info *nxi = APFS_NXI(sb);
|
||||
struct apfs_key key;
|
||||
struct apfs_query *query;
|
||||
u64 cnid = apfs_ino(inode);
|
||||
loff_t pos;
|
||||
@@ -193,8 +201,7 @@ static int apfs_readdir(struct file *file, struct dir_context *ctx)
|
||||
}
|
||||
|
||||
/* We want all the children for the cnid, regardless of the name */
|
||||
apfs_init_drec_key(sb, cnid, NULL /* name */, 0 /* name_len */, &key);
|
||||
query->key = &key;
|
||||
apfs_init_drec_key(sb, cnid, NULL /* name */, 0 /* name_len */, &query->key);
|
||||
query->flags = APFS_QUERY_CAT | APFS_QUERY_MULTIPLE | APFS_QUERY_EXACT;
|
||||
|
||||
pos = ctx->pos - 2;
|
||||
@@ -361,7 +368,6 @@ static int apfs_create_dentry_rec(struct inode *inode, struct qstr *qname,
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct apfs_sb_info *sbi = APFS_SB(sb);
|
||||
struct apfs_key key;
|
||||
struct apfs_query *query;
|
||||
void *raw_key = NULL;
|
||||
struct apfs_drec_val *raw_val = NULL;
|
||||
@@ -369,21 +375,20 @@ static int apfs_create_dentry_rec(struct inode *inode, struct qstr *qname,
|
||||
bool hashed = apfs_is_normalization_insensitive(sb);
|
||||
int ret;
|
||||
|
||||
apfs_init_drec_key(sb, parent_id, qname->name, qname->len, &key);
|
||||
query = apfs_alloc_query(sbi->s_cat_root, NULL /* parent */);
|
||||
if (!query)
|
||||
return -ENOMEM;
|
||||
query->key = &key;
|
||||
apfs_init_drec_key(sb, parent_id, qname->name, qname->len, &query->key);
|
||||
query->flags |= APFS_QUERY_CAT;
|
||||
|
||||
ret = apfs_btree_query(sb, &query);
|
||||
if (ret && ret != -ENODATA) {
|
||||
apfs_err(sb, "query failed in dir 0x%llx (hash 0x%llx)", parent_id, key.number);
|
||||
apfs_err(sb, "query failed in dir 0x%llx (hash 0x%llx)", parent_id, query->key.number);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (hashed)
|
||||
key_len = apfs_build_dentry_hashed_key(qname, key.number, parent_id,
|
||||
key_len = apfs_build_dentry_hashed_key(qname, query->key.number, parent_id,
|
||||
(struct apfs_drec_hashed_key **)&raw_key);
|
||||
else
|
||||
key_len = apfs_build_dentry_unhashed_key(qname, parent_id,
|
||||
@@ -401,7 +406,7 @@ static int apfs_create_dentry_rec(struct inode *inode, struct qstr *qname,
|
||||
/* TODO: deal with hash collisions */
|
||||
ret = apfs_btree_insert(query, raw_key, key_len, raw_val, val_len);
|
||||
if (ret)
|
||||
apfs_err(sb, "insertion failed in dir 0x%llx (hash 0x%llx)", parent_id, key.number);
|
||||
apfs_err(sb, "insertion failed in dir 0x%llx (hash 0x%llx)", parent_id, query->key.number);
|
||||
|
||||
fail:
|
||||
kfree(raw_val);
|
||||
@@ -453,18 +458,16 @@ static int apfs_create_sibling_link_rec(struct dentry *dentry,
|
||||
{
|
||||
struct super_block *sb = dentry->d_sb;
|
||||
struct apfs_sb_info *sbi = APFS_SB(sb);
|
||||
struct apfs_key key;
|
||||
struct apfs_query *query = NULL;
|
||||
struct apfs_sibling_link_key raw_key;
|
||||
struct apfs_sibling_val *raw_val;
|
||||
int val_len;
|
||||
int ret;
|
||||
|
||||
apfs_init_sibling_link_key(apfs_ino(inode), sibling_id, &key);
|
||||
query = apfs_alloc_query(sbi->s_cat_root, NULL /* parent */);
|
||||
if (!query)
|
||||
return -ENOMEM;
|
||||
query->key = &key;
|
||||
apfs_init_sibling_link_key(apfs_ino(inode), sibling_id, &query->key);
|
||||
query->flags |= APFS_QUERY_CAT;
|
||||
|
||||
ret = apfs_btree_query(sb, &query);
|
||||
@@ -503,17 +506,15 @@ static int apfs_create_sibling_map_rec(struct dentry *dentry,
|
||||
{
|
||||
struct super_block *sb = dentry->d_sb;
|
||||
struct apfs_sb_info *sbi = APFS_SB(sb);
|
||||
struct apfs_key key;
|
||||
struct apfs_query *query = NULL;
|
||||
struct apfs_sibling_map_key raw_key;
|
||||
struct apfs_sibling_map_val raw_val;
|
||||
int ret;
|
||||
|
||||
apfs_init_sibling_map_key(sibling_id, &key);
|
||||
query = apfs_alloc_query(sbi->s_cat_root, NULL /* parent */);
|
||||
if (!query)
|
||||
return -ENOMEM;
|
||||
query->key = &key;
|
||||
apfs_init_sibling_map_key(sibling_id, &query->key);
|
||||
query->flags |= APFS_QUERY_CAT;
|
||||
|
||||
ret = apfs_btree_query(sb, &query);
|
||||
@@ -603,8 +604,10 @@ static int apfs_create_dentry(struct dentry *dentry, struct inode *inode)
|
||||
/* Now update the parent inode */
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
||||
parent->i_mtime = parent->i_ctime = current_time(inode);
|
||||
#else
|
||||
#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 7, 0)
|
||||
parent->i_mtime = inode_set_ctime_current(parent);
|
||||
#else
|
||||
inode_set_mtime_to_ts(parent, inode_set_ctime_current(parent));
|
||||
#endif
|
||||
++APFS_I(parent)->i_nchildren;
|
||||
apfs_inode_join_transaction(parent->i_sb, parent);
|
||||
@@ -923,17 +926,15 @@ static int apfs_delete_sibling_link_rec(struct dentry *dentry, u64 sibling_id)
|
||||
struct super_block *sb = dentry->d_sb;
|
||||
struct apfs_sb_info *sbi = APFS_SB(sb);
|
||||
struct inode *inode = d_inode(dentry);
|
||||
struct apfs_key key;
|
||||
struct apfs_query *query = NULL;
|
||||
int ret;
|
||||
|
||||
ASSERT(sibling_id);
|
||||
|
||||
apfs_init_sibling_link_key(apfs_ino(inode), sibling_id, &key);
|
||||
query = apfs_alloc_query(sbi->s_cat_root, NULL /* parent */);
|
||||
if (!query)
|
||||
return -ENOMEM;
|
||||
query->key = &key;
|
||||
apfs_init_sibling_link_key(apfs_ino(inode), sibling_id, &query->key);
|
||||
query->flags |= APFS_QUERY_CAT | APFS_QUERY_EXACT;
|
||||
|
||||
ret = apfs_btree_query(sb, &query);
|
||||
@@ -966,17 +967,15 @@ static int apfs_delete_sibling_map_rec(struct dentry *dentry, u64 sibling_id)
|
||||
{
|
||||
struct super_block *sb = dentry->d_sb;
|
||||
struct apfs_sb_info *sbi = APFS_SB(sb);
|
||||
struct apfs_key key;
|
||||
struct apfs_query *query = NULL;
|
||||
int ret;
|
||||
|
||||
ASSERT(sibling_id);
|
||||
|
||||
apfs_init_sibling_map_key(sibling_id, &key);
|
||||
query = apfs_alloc_query(sbi->s_cat_root, NULL /* parent */);
|
||||
if (!query)
|
||||
return -ENOMEM;
|
||||
query->key = &key;
|
||||
apfs_init_sibling_map_key(sibling_id, &query->key);
|
||||
query->flags |= APFS_QUERY_CAT | APFS_QUERY_EXACT;
|
||||
|
||||
ret = apfs_btree_query(sb, &query);
|
||||
@@ -1040,8 +1039,10 @@ static int apfs_delete_dentry(struct dentry *dentry)
|
||||
/* Now update the parent inode */
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
||||
parent->i_mtime = parent->i_ctime = current_time(parent);
|
||||
#else
|
||||
#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 7, 0)
|
||||
parent->i_mtime = inode_set_ctime_current(parent);
|
||||
#else
|
||||
inode_set_mtime_to_ts(parent, inode_set_ctime_current(parent));
|
||||
#endif
|
||||
--APFS_I(parent)->i_nchildren;
|
||||
apfs_inode_join_transaction(sb, parent);
|
||||
@@ -1119,18 +1120,14 @@ static int apfs_find_primary_link(struct inode *inode, char **name, u64 *parent)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct apfs_sb_info *sbi = APFS_SB(sb);
|
||||
struct apfs_key key;
|
||||
struct apfs_query *query;
|
||||
int err;
|
||||
|
||||
query = apfs_alloc_query(sbi->s_cat_root, NULL /* parent */);
|
||||
if (!query)
|
||||
return -ENOMEM;
|
||||
|
||||
apfs_init_sibling_link_key(apfs_ino(inode), 0 /* sibling_id */, &key);
|
||||
query->key = &key;
|
||||
query->flags |= APFS_QUERY_CAT | APFS_QUERY_ANY_NUMBER |
|
||||
APFS_QUERY_EXACT;
|
||||
apfs_init_sibling_link_key(apfs_ino(inode), 0 /* sibling_id */, &query->key);
|
||||
query->flags |= APFS_QUERY_CAT | APFS_QUERY_ANY_NUMBER | APFS_QUERY_EXACT;
|
||||
|
||||
/* The primary link is the one with the lowest sibling id */
|
||||
*name = NULL;
|
||||
@@ -1208,8 +1205,10 @@ static int apfs_create_orphan_link(struct inode *inode)
|
||||
/* Now update the child count for private-dir */
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
||||
priv_dir->i_mtime = priv_dir->i_ctime = current_time(priv_dir);
|
||||
#else
|
||||
#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 7, 0)
|
||||
priv_dir->i_mtime = inode_set_ctime_current(priv_dir);
|
||||
#else
|
||||
inode_set_mtime_to_ts(priv_dir, inode_set_ctime_current(priv_dir));
|
||||
#endif
|
||||
++APFS_I(priv_dir)->i_nchildren;
|
||||
apfs_inode_join_transaction(sb, priv_dir);
|
||||
@@ -1257,8 +1256,10 @@ int apfs_delete_orphan_link(struct inode *inode)
|
||||
/* Now update the child count for private-dir */
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
||||
priv_dir->i_mtime = priv_dir->i_ctime = current_time(priv_dir);
|
||||
#else
|
||||
#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 7, 0)
|
||||
priv_dir->i_mtime = inode_set_ctime_current(priv_dir);
|
||||
#else
|
||||
inode_set_mtime_to_ts(priv_dir, inode_set_ctime_current(priv_dir));
|
||||
#endif
|
||||
--APFS_I(priv_dir)->i_nchildren;
|
||||
apfs_inode_join_transaction(sb, priv_dir);
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
PACKAGE_NAME="linux-apfs-rw"
|
||||
PACKAGE_VERSION="0.3.5"
|
||||
PACKAGE_VERSION="0.3.8"
|
||||
|
||||
BUILT_MODULE_NAME[0]="apfs"
|
||||
DEST_MODULE_LOCATION[0]="/extra"
|
||||
|
||||
AUTOINSTALL="yes"
|
||||
PRE_BUILD="genver.sh"
|
||||
@@ -139,7 +139,7 @@ static int apfs_extent_read(struct apfs_dstream_info *dstream, sector_t dsblock,
|
||||
ret = -ENOMEM;
|
||||
goto done;
|
||||
}
|
||||
query->key = &key;
|
||||
query->key = key;
|
||||
query->flags = apfs_is_sealed(sb) ? APFS_QUERY_FEXT : APFS_QUERY_CAT;
|
||||
|
||||
ret = apfs_btree_query(sb, &query);
|
||||
@@ -413,7 +413,6 @@ static int apfs_update_tail_extent(struct apfs_dstream_info *dstream, const stru
|
||||
{
|
||||
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;
|
||||
@@ -431,13 +430,11 @@ static int apfs_update_tail_extent(struct apfs_dstream_info *dstream, const stru
|
||||
new_crypto = 0;
|
||||
raw_val.crypto_id = cpu_to_le64(new_crypto);
|
||||
|
||||
/* We want the last extent record */
|
||||
apfs_init_file_extent_key(extent_id, -1, &key);
|
||||
|
||||
query = apfs_alloc_query(sbi->s_cat_root, NULL /* parent */);
|
||||
if (!query)
|
||||
return -ENOMEM;
|
||||
query->key = &key;
|
||||
/* 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);
|
||||
@@ -614,7 +611,7 @@ search_and_insert:
|
||||
query = apfs_alloc_query(sbi->s_cat_root, NULL /* parent */);
|
||||
if (!query)
|
||||
return -ENOMEM;
|
||||
query->key = &key;
|
||||
query->key = key;
|
||||
query->flags = APFS_QUERY_CAT;
|
||||
|
||||
ret = apfs_btree_query(sb, &query);
|
||||
@@ -801,7 +798,6 @@ static int apfs_insert_phys_extent(struct apfs_dstream_info *dstream, const stru
|
||||
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_key key;
|
||||
struct apfs_query *query = NULL;
|
||||
struct apfs_phys_extent pext;
|
||||
u64 blkcnt = extent->len >> sb->s_blocksize_bits;
|
||||
@@ -830,8 +826,7 @@ static int apfs_insert_phys_extent(struct apfs_dstream_info *dstream, const stru
|
||||
* one.
|
||||
*/
|
||||
last_bno = extent->phys_block_num + blkcnt - 1;
|
||||
apfs_init_extent_key(last_bno, &key);
|
||||
query->key = &key;
|
||||
apfs_init_extent_key(last_bno, &query->key);
|
||||
query->flags = APFS_QUERY_EXTENTREF;
|
||||
|
||||
ret = apfs_btree_query(sb, &query);
|
||||
@@ -1153,7 +1148,6 @@ static int apfs_create_hole(struct apfs_dstream_info *dstream, u64 start, u64 en
|
||||
{
|
||||
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;
|
||||
@@ -1173,11 +1167,10 @@ static int apfs_create_hole(struct apfs_dstream_info *dstream, u64 start, u64 en
|
||||
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);
|
||||
|
||||
apfs_init_file_extent_key(extent_id, start, &key);
|
||||
query = apfs_alloc_query(sbi->s_cat_root, NULL /* parent */);
|
||||
if (!query)
|
||||
return -ENOMEM;
|
||||
query->key = &key;
|
||||
apfs_init_file_extent_key(extent_id, start, &query->key);
|
||||
query->flags = APFS_QUERY_CAT;
|
||||
|
||||
ret = apfs_btree_query(sb, &query);
|
||||
@@ -1260,7 +1253,6 @@ static int apfs_range_in_snap(struct super_block *sb, u64 bno, u64 blkcnt, bool
|
||||
{
|
||||
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 pext = {0};
|
||||
int ret;
|
||||
@@ -1287,8 +1279,7 @@ static int apfs_range_in_snap(struct super_block *sb, u64 bno, u64 blkcnt, bool
|
||||
goto out;
|
||||
}
|
||||
|
||||
apfs_init_extent_key(bno, &key);
|
||||
query->key = &key;
|
||||
apfs_init_extent_key(bno, &query->key);
|
||||
query->flags = APFS_QUERY_EXTENTREF;
|
||||
|
||||
ret = apfs_btree_query(sb, &query);
|
||||
@@ -1500,18 +1491,15 @@ static int apfs_shrink_dstream_last_extent(struct apfs_dstream_info *dstream, lo
|
||||
{
|
||||
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 tail;
|
||||
u64 extent_id = dstream->ds_id;
|
||||
int ret = 0;
|
||||
|
||||
apfs_init_file_extent_key(extent_id, -1, &key);
|
||||
|
||||
query = apfs_alloc_query(sbi->s_cat_root, NULL /* parent */);
|
||||
if (!query)
|
||||
return -ENOMEM;
|
||||
query->key = &key;
|
||||
apfs_init_file_extent_key(extent_id, -1, &query->key);
|
||||
query->flags = APFS_QUERY_CAT;
|
||||
|
||||
ret = apfs_btree_query(sb, &query);
|
||||
@@ -1706,8 +1694,10 @@ int apfs_clone_file_range(struct file *src_file, loff_t off, struct file *dst_fi
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
||||
dst_inode->i_mtime = dst_inode->i_ctime = current_time(dst_inode);
|
||||
#else
|
||||
#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;
|
||||
@@ -1760,18 +1750,15 @@ fail:
|
||||
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_key key;
|
||||
struct apfs_query *query = NULL;
|
||||
struct apfs_file_extent_val raw_val;
|
||||
struct apfs_file_extent_key raw_key;
|
||||
int ret = 0;
|
||||
|
||||
apfs_init_file_extent_key(dstream_id, extent->logical_addr, &key);
|
||||
|
||||
query = apfs_alloc_query(sbi->s_cat_root, NULL /* parent */);
|
||||
if (!query)
|
||||
return -ENOMEM;
|
||||
query->key = &key;
|
||||
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);
|
||||
@@ -1835,7 +1822,7 @@ restart:
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
query->key = &key;
|
||||
query->key = key;
|
||||
query->flags = APFS_QUERY_EXTENTREF;
|
||||
|
||||
ret = apfs_btree_query(sb, &query);
|
||||
@@ -1975,7 +1962,7 @@ restart:
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
query->key = &key;
|
||||
query->key = key;
|
||||
query->flags = APFS_QUERY_EXTENTREF;
|
||||
|
||||
ret = apfs_btree_query(sb, &query);
|
||||
@@ -2186,7 +2173,11 @@ int apfs_nonsparse_dstream_read(struct apfs_dstream_info *dstream, void *buf, si
|
||||
goto out;
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 7, 0)
|
||||
bhs[idx] = __getblk_gfp(APFS_NXI(sb)->nx_bdev, bno, sb->s_blocksize, __GFP_MOVABLE);
|
||||
#else
|
||||
bhs[idx] = bdev_getblk(APFS_NXI(sb)->nx_bdev, bno, sb->s_blocksize, __GFP_MOVABLE);
|
||||
#endif
|
||||
if (!bhs[idx]) {
|
||||
apfs_err(sb, "failed to map block 0x%llx", bno);
|
||||
ret = -EIO;
|
||||
@@ -2258,7 +2249,11 @@ void apfs_nonsparse_dstream_preread(struct apfs_dstream_info *dstream)
|
||||
if (ret || bno == 0)
|
||||
return;
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 7, 0)
|
||||
bh = __getblk_gfp(APFS_NXI(sb)->nx_bdev, bno, sb->s_blocksize, __GFP_MOVABLE);
|
||||
#else
|
||||
bh = bdev_getblk(APFS_NXI(sb)->nx_bdev, bno, sb->s_blocksize, __GFP_MOVABLE);
|
||||
#endif
|
||||
if (!bh)
|
||||
return;
|
||||
if (!buffer_uptodate(bh)) {
|
||||
|
||||
@@ -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
|
||||
@@ -18,6 +21,9 @@ static vm_fault_t apfs_page_mkwrite(struct vm_fault *vmf)
|
||||
struct vm_area_struct *vma = vmf->vma;
|
||||
#endif
|
||||
struct page *page = vmf->page;
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0)
|
||||
struct folio *folio;
|
||||
#endif
|
||||
struct inode *inode = file_inode(vma->vm_file);
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct buffer_head *bh, *head;
|
||||
@@ -54,8 +60,16 @@ static vm_fault_t apfs_page_mkwrite(struct vm_fault *vmf)
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
if (!page_has_buffers(page))
|
||||
if (!page_has_buffers(page)) {
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 7, 0)
|
||||
create_empty_buffers(page, sb->s_blocksize, 0);
|
||||
#else
|
||||
folio = page_folio(page);
|
||||
bh = folio_buffers(folio);
|
||||
if (!bh)
|
||||
bh = create_empty_buffers(folio, sb->s_blocksize, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
size = i_size_read(inode);
|
||||
if (page->index == size >> PAGE_SHIFT)
|
||||
@@ -141,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,
|
||||
@@ -150,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
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
#!/bin/sh
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
# Script to generate the module version header
|
||||
#
|
||||
|
||||
if command -v git >/dev/null 2>&1 && [ -d .git ]; then
|
||||
GIT_COMMIT=$(git describe HEAD | tail -c 9)
|
||||
else
|
||||
GIT_COMMIT="$(grep PACKAGE_VERSION dkms.conf | cut -d '"' -f2)?"
|
||||
fi
|
||||
|
||||
printf '#define GIT_COMMIT\t"%s"\n' "$GIT_COMMIT" > version.h
|
||||
@@ -64,17 +64,15 @@ static int apfs_create_dstream_rec(struct apfs_dstream_info *dstream)
|
||||
{
|
||||
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_dstream_id_key raw_key;
|
||||
struct apfs_dstream_id_val raw_val;
|
||||
int ret;
|
||||
|
||||
apfs_init_dstream_id_key(dstream->ds_id, &key);
|
||||
query = apfs_alloc_query(sbi->s_cat_root, NULL /* parent */);
|
||||
if (!query)
|
||||
return -ENOMEM;
|
||||
query->key = &key;
|
||||
apfs_init_dstream_id_key(dstream->ds_id, &query->key);
|
||||
query->flags |= APFS_QUERY_CAT | APFS_QUERY_EXACT;
|
||||
|
||||
ret = apfs_btree_query(sb, &query);
|
||||
@@ -190,7 +188,6 @@ int apfs_dstream_adj_refcnt(struct apfs_dstream_info *dstream, u32 delta)
|
||||
{
|
||||
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_dstream_id_val raw_val;
|
||||
void *raw = NULL;
|
||||
@@ -199,11 +196,10 @@ int apfs_dstream_adj_refcnt(struct apfs_dstream_info *dstream, u32 delta)
|
||||
|
||||
ASSERT(APFS_I(dstream->ds_inode)->i_has_dstream);
|
||||
|
||||
apfs_init_dstream_id_key(dstream->ds_id, &key);
|
||||
query = apfs_alloc_query(sbi->s_cat_root, NULL /* parent */);
|
||||
if (!query)
|
||||
return -ENOMEM;
|
||||
query->key = &key;
|
||||
apfs_init_dstream_id_key(dstream->ds_id, &query->key);
|
||||
query->flags |= APFS_QUERY_CAT | APFS_QUERY_EXACT;
|
||||
|
||||
ret = apfs_btree_query(sb, &query);
|
||||
@@ -268,7 +264,6 @@ static int apfs_create_crypto_rec(struct inode *inode)
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct apfs_sb_info *sbi = APFS_SB(sb);
|
||||
struct apfs_dstream_info *dstream = &APFS_I(inode)->i_dstream;
|
||||
struct apfs_key key;
|
||||
struct apfs_query *query;
|
||||
struct apfs_crypto_state_key raw_key;
|
||||
int ret;
|
||||
@@ -276,11 +271,10 @@ static int apfs_create_crypto_rec(struct inode *inode)
|
||||
if (inode->i_size || inode->i_blocks) /* Already has a dstream */
|
||||
return 0;
|
||||
|
||||
apfs_init_crypto_state_key(dstream->ds_id, &key);
|
||||
query = apfs_alloc_query(sbi->s_cat_root, NULL /* parent */);
|
||||
if (!query)
|
||||
return -ENOMEM;
|
||||
query->key = &key;
|
||||
apfs_init_crypto_state_key(dstream->ds_id, &query->key);
|
||||
query->flags |= APFS_QUERY_CAT | APFS_QUERY_EXACT;
|
||||
|
||||
ret = apfs_btree_query(sb, &query);
|
||||
@@ -343,7 +337,6 @@ static unsigned int apfs_dflt_key_class(struct super_block *sb)
|
||||
int apfs_crypto_adj_refcnt(struct super_block *sb, u64 crypto_id, int delta)
|
||||
{
|
||||
struct apfs_sb_info *sbi = APFS_SB(sb);
|
||||
struct apfs_key key;
|
||||
struct apfs_query *query;
|
||||
struct apfs_crypto_state_val *raw_val;
|
||||
char *raw;
|
||||
@@ -352,11 +345,10 @@ int apfs_crypto_adj_refcnt(struct super_block *sb, u64 crypto_id, int delta)
|
||||
if (!crypto_id)
|
||||
return 0;
|
||||
|
||||
apfs_init_crypto_state_key(crypto_id, &key);
|
||||
query = apfs_alloc_query(sbi->s_cat_root, NULL /* parent */);
|
||||
if (!query)
|
||||
return -ENOMEM;
|
||||
query->key = &key;
|
||||
apfs_init_crypto_state_key(crypto_id, &query->key);
|
||||
query->flags |= APFS_QUERY_CAT | APFS_QUERY_EXACT;
|
||||
|
||||
ret = apfs_btree_query(sb, &query);
|
||||
@@ -396,7 +388,6 @@ int APFS_CRYPTO_ADJ_REFCNT_MAXOPS(void)
|
||||
static int apfs_crypto_set_key(struct super_block *sb, u64 crypto_id, struct apfs_crypto_state_val *new_val)
|
||||
{
|
||||
struct apfs_sb_info *sbi = APFS_SB(sb);
|
||||
struct apfs_key key;
|
||||
struct apfs_query *query;
|
||||
struct apfs_crypto_state_val *raw_val;
|
||||
char *raw;
|
||||
@@ -408,11 +399,10 @@ static int apfs_crypto_set_key(struct super_block *sb, u64 crypto_id, struct apf
|
||||
|
||||
pfk_len = le16_to_cpu(new_val->state.key_len);
|
||||
|
||||
apfs_init_crypto_state_key(crypto_id, &key);
|
||||
query = apfs_alloc_query(sbi->s_cat_root, NULL /* parent */);
|
||||
if (!query)
|
||||
return -ENOMEM;
|
||||
query->key = &key;
|
||||
apfs_init_crypto_state_key(crypto_id, &query->key);
|
||||
query->flags |= APFS_QUERY_CAT | APFS_QUERY_EXACT;
|
||||
|
||||
ret = apfs_btree_query(sb, &query);
|
||||
@@ -446,7 +436,6 @@ static int apfs_crypto_get_key(struct super_block *sb, u64 crypto_id, struct apf
|
||||
unsigned int max_len)
|
||||
{
|
||||
struct apfs_sb_info *sbi = APFS_SB(sb);
|
||||
struct apfs_key key;
|
||||
struct apfs_query *query;
|
||||
struct apfs_crypto_state_val *raw_val;
|
||||
char *raw;
|
||||
@@ -456,11 +445,10 @@ static int apfs_crypto_get_key(struct super_block *sb, u64 crypto_id, struct apf
|
||||
if (!crypto_id)
|
||||
return -ENOENT;
|
||||
|
||||
apfs_init_crypto_state_key(crypto_id, &key);
|
||||
query = apfs_alloc_query(sbi->s_cat_root, NULL /* parent */);
|
||||
if (!query)
|
||||
return -ENOMEM;
|
||||
query->key = &key;
|
||||
apfs_init_crypto_state_key(crypto_id, &query->key);
|
||||
query->flags |= APFS_QUERY_CAT | APFS_QUERY_EXACT;
|
||||
|
||||
ret = apfs_btree_query(sb, &query);
|
||||
@@ -488,6 +476,9 @@ int __apfs_write_begin(struct file *file, struct address_space *mapping, loff_t
|
||||
struct apfs_dstream_info *dstream = &APFS_I(inode)->i_dstream;
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct page *page;
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0)
|
||||
struct folio *folio;
|
||||
#endif
|
||||
struct buffer_head *bh, *head;
|
||||
unsigned int blocksize, block_start, block_end, from, to;
|
||||
pgoff_t index = pos >> PAGE_SHIFT;
|
||||
@@ -520,8 +511,16 @@ int __apfs_write_begin(struct file *file, struct address_space *mapping, loff_t
|
||||
#endif
|
||||
if (!page)
|
||||
return -ENOMEM;
|
||||
if (!page_has_buffers(page))
|
||||
if (!page_has_buffers(page)) {
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 7, 0)
|
||||
create_empty_buffers(page, sb->s_blocksize, 0);
|
||||
#else
|
||||
folio = page_folio(page);
|
||||
bh = folio_buffers(folio);
|
||||
if (!bh)
|
||||
bh = create_empty_buffers(folio, sb->s_blocksize, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* CoW moves existing blocks, so read them but mark them as unmapped */
|
||||
head = page_buffers(page);
|
||||
@@ -799,13 +798,19 @@ static int apfs_inode_from_query(struct apfs_query *query, struct inode *inode)
|
||||
ai->i_nchildren = le32_to_cpu(inode_val->nchildren);
|
||||
}
|
||||
|
||||
inode->i_atime = ns_to_timespec64(le64_to_cpu(inode_val->access_time));
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
||||
inode->i_ctime = ns_to_timespec64(le64_to_cpu(inode_val->change_time));
|
||||
#else
|
||||
inode_set_ctime_to_ts(inode, ns_to_timespec64(le64_to_cpu(inode_val->change_time)));
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 7, 0)
|
||||
inode->i_atime = ns_to_timespec64(le64_to_cpu(inode_val->access_time));
|
||||
inode->i_mtime = ns_to_timespec64(le64_to_cpu(inode_val->mod_time));
|
||||
#else
|
||||
inode_set_atime_to_ts(inode, ns_to_timespec64(le64_to_cpu(inode_val->access_time)));
|
||||
inode_set_mtime_to_ts(inode, ns_to_timespec64(le64_to_cpu(inode_val->mod_time)));
|
||||
#endif
|
||||
ai->i_crtime = ns_to_timespec64(le64_to_cpu(inode_val->create_time));
|
||||
|
||||
dstream->ds_size = inode->i_size = inode->i_blocks = 0;
|
||||
@@ -868,16 +873,13 @@ static struct apfs_query *apfs_inode_lookup(const struct inode *inode)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct apfs_sb_info *sbi = APFS_SB(sb);
|
||||
struct apfs_key key;
|
||||
struct apfs_query *query;
|
||||
int ret;
|
||||
|
||||
apfs_init_inode_key(apfs_ino(inode), &key);
|
||||
|
||||
query = apfs_alloc_query(sbi->s_cat_root, NULL /* parent */);
|
||||
if (!query)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
query->key = &key;
|
||||
apfs_init_inode_key(apfs_ino(inode), &query->key);
|
||||
query->flags |= APFS_QUERY_CAT | APFS_QUERY_EXACT;
|
||||
|
||||
ret = apfs_btree_query(sb, &query);
|
||||
@@ -938,7 +940,6 @@ static int apfs_check_dstream_refcnt(struct inode *inode)
|
||||
struct apfs_dstream_info *dstream = &ai->i_dstream;
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct apfs_sb_info *sbi = APFS_SB(sb);
|
||||
struct apfs_key key;
|
||||
struct apfs_query *query = NULL;
|
||||
struct apfs_dstream_id_val raw_val;
|
||||
void *raw = NULL;
|
||||
@@ -950,11 +951,10 @@ static int apfs_check_dstream_refcnt(struct inode *inode)
|
||||
return 0;
|
||||
}
|
||||
|
||||
apfs_init_dstream_id_key(dstream->ds_id, &key);
|
||||
query = apfs_alloc_query(sbi->s_cat_root, NULL /* parent */);
|
||||
if (!query)
|
||||
return -ENOMEM;
|
||||
query->key = &key;
|
||||
apfs_init_dstream_id_key(dstream->ds_id, &query->key);
|
||||
query->flags |= APFS_QUERY_CAT | APFS_QUERY_EXACT;
|
||||
|
||||
ret = apfs_btree_query(sb, &query);
|
||||
@@ -1044,6 +1044,7 @@ int apfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
|
||||
struct inode *inode = d_inode(dentry);
|
||||
|
||||
generic_fillattr(inode, stat);
|
||||
stat->dev = APFS_SB(inode->i_sb)->s_anon_dev;
|
||||
stat->ino = apfs_ino(inode);
|
||||
return 0;
|
||||
}
|
||||
@@ -1093,6 +1094,7 @@ int apfs_getattr(struct mnt_idmap *idmap,
|
||||
generic_fillattr(idmap, request_mask, inode, stat);
|
||||
#endif
|
||||
|
||||
stat->dev = APFS_SB(inode->i_sb)->s_anon_dev;
|
||||
stat->ino = apfs_ino(inode);
|
||||
return 0;
|
||||
}
|
||||
@@ -1112,6 +1114,9 @@ static int apfs_build_inode_val(struct inode *inode, struct qstr *qname,
|
||||
{
|
||||
struct apfs_inode_val *val;
|
||||
struct apfs_x_field xkey;
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0)
|
||||
struct timespec64 ts;
|
||||
#endif
|
||||
int total_xlen, val_len;
|
||||
bool is_device = S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode);
|
||||
__le32 rdev;
|
||||
@@ -1130,7 +1135,12 @@ static int apfs_build_inode_val(struct inode *inode, struct qstr *qname,
|
||||
val->parent_id = cpu_to_le64(APFS_I(inode)->i_parent_id);
|
||||
val->private_id = cpu_to_le64(apfs_ino(inode));
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 7, 0)
|
||||
val->mod_time = cpu_to_le64(timespec64_to_ns(&inode->i_mtime));
|
||||
#else
|
||||
ts = inode_get_mtime(inode);
|
||||
val->mod_time = cpu_to_le64(timespec64_to_ns(&ts));
|
||||
#endif
|
||||
val->create_time = val->change_time = val->access_time = val->mod_time;
|
||||
|
||||
if (S_ISDIR(inode->i_mode))
|
||||
@@ -1492,14 +1502,22 @@ int apfs_update_inode(struct inode *inode, char *new_name)
|
||||
if (gid_valid(sbi->s_gid))
|
||||
inode_raw->group = cpu_to_le32(ai->i_saved_gid);
|
||||
|
||||
inode_raw->access_time = cpu_to_le64(timespec64_to_ns(&inode->i_atime));
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
||||
inode_raw->change_time = cpu_to_le64(timespec64_to_ns(&inode->i_ctime));
|
||||
#else
|
||||
struct timespec64 ictime = inode_get_ctime(inode);
|
||||
inode_raw->change_time = cpu_to_le64(timespec64_to_ns(&ictime));
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 7, 0)
|
||||
inode_raw->access_time = cpu_to_le64(timespec64_to_ns(&inode->i_atime));
|
||||
inode_raw->mod_time = cpu_to_le64(timespec64_to_ns(&inode->i_mtime));
|
||||
#else
|
||||
struct timespec64 ts = inode_get_mtime(inode);
|
||||
inode_raw->mod_time = cpu_to_le64(timespec64_to_ns(&ts));
|
||||
ts = inode_get_atime(inode);
|
||||
inode_raw->access_time = cpu_to_le64(timespec64_to_ns(&ts));
|
||||
#endif
|
||||
inode_raw->create_time = cpu_to_le64(timespec64_to_ns(&ai->i_crtime));
|
||||
|
||||
if (S_ISDIR(inode->i_mode)) {
|
||||
@@ -1680,9 +1698,11 @@ struct inode *apfs_new_inode(struct inode *dir, umode_t mode, dev_t rdev)
|
||||
now = current_time(inode);
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
||||
inode->i_atime = inode->i_mtime = inode->i_ctime = ai->i_crtime = now;
|
||||
#else
|
||||
#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 7, 0)
|
||||
inode_set_ctime_to_ts(inode, now);
|
||||
inode->i_atime = inode->i_mtime = ai->i_crtime = now;
|
||||
#else
|
||||
ai->i_crtime = simple_inode_init_ts(inode);
|
||||
#endif
|
||||
vsb_raw->apfs_last_mod_time = cpu_to_le64(timespec64_to_ns(&now));
|
||||
|
||||
@@ -1720,18 +1740,16 @@ int apfs_create_inode_rec(struct super_block *sb, struct inode *inode,
|
||||
struct dentry *dentry)
|
||||
{
|
||||
struct apfs_sb_info *sbi = APFS_SB(sb);
|
||||
struct apfs_key key;
|
||||
struct apfs_query *query;
|
||||
struct apfs_inode_key raw_key;
|
||||
struct apfs_inode_val *raw_val;
|
||||
int val_len;
|
||||
int ret;
|
||||
|
||||
apfs_init_inode_key(apfs_ino(inode), &key);
|
||||
query = apfs_alloc_query(sbi->s_cat_root, NULL /* parent */);
|
||||
if (!query)
|
||||
return -ENOMEM;
|
||||
query->key = &key;
|
||||
apfs_init_inode_key(apfs_ino(inode), &query->key);
|
||||
query->flags |= APFS_QUERY_CAT;
|
||||
|
||||
ret = apfs_btree_query(sb, &query);
|
||||
@@ -1779,8 +1797,10 @@ static int apfs_setsize(struct inode *inode, loff_t new_size)
|
||||
return 0;
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
||||
inode->i_mtime = inode->i_ctime = current_time(inode);
|
||||
#else
|
||||
#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 7, 0)
|
||||
inode->i_mtime = inode_set_ctime_current(inode);
|
||||
#else
|
||||
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
|
||||
#endif
|
||||
|
||||
err = apfs_inode_create_dstream_rec(inode);
|
||||
|
||||
@@ -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);
|
||||
|
||||
+5
-20
@@ -71,7 +71,6 @@ static int apfs_create_snap_metadata_rec(struct inode *mntpoint, struct apfs_nod
|
||||
struct apfs_sb_info *sbi = APFS_SB(sb);
|
||||
struct apfs_superblock *vsb_raw = sbi->s_vsb_raw;
|
||||
struct apfs_query *query = NULL;
|
||||
struct apfs_key key = {0};
|
||||
struct apfs_snap_metadata_key raw_key;
|
||||
struct apfs_snap_metadata_val *raw_val = NULL;
|
||||
int val_len;
|
||||
@@ -79,14 +78,12 @@ static int apfs_create_snap_metadata_rec(struct inode *mntpoint, struct apfs_nod
|
||||
u64 xid = APFS_NXI(sb)->nx_xid;
|
||||
int err;
|
||||
|
||||
apfs_init_snap_metadata_key(xid, &key);
|
||||
|
||||
query = apfs_alloc_query(snap_root, NULL /* parent */);
|
||||
if (!query) {
|
||||
err = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
query->key = &key;
|
||||
apfs_init_snap_metadata_key(xid, &query->key);
|
||||
query->flags |= APFS_QUERY_SNAP_META | APFS_QUERY_EXACT;
|
||||
|
||||
err = apfs_btree_query(sb, &query);
|
||||
@@ -137,20 +134,17 @@ static int apfs_create_snap_name_rec(struct apfs_node *snap_root, const char *na
|
||||
{
|
||||
struct super_block *sb = snap_root->object.sb;
|
||||
struct apfs_query *query = NULL;
|
||||
struct apfs_key key = {0};
|
||||
struct apfs_snap_name_key *raw_key = NULL;
|
||||
struct apfs_snap_name_val raw_val;
|
||||
int key_len;
|
||||
int err;
|
||||
|
||||
apfs_init_snap_name_key(name, &key);
|
||||
|
||||
query = apfs_alloc_query(snap_root, NULL /* parent */);
|
||||
if (!query) {
|
||||
err = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
query->key = &key;
|
||||
apfs_init_snap_name_key(name, &query->key);
|
||||
query->flags |= APFS_QUERY_SNAP_META | APFS_QUERY_EXACT;
|
||||
|
||||
err = apfs_btree_query(sb, &query);
|
||||
@@ -246,7 +240,6 @@ static int apfs_update_omap_snap_tree(struct super_block *sb, __le64 *oid_p)
|
||||
struct apfs_nxsb_info *nxi = APFS_NXI(sb);
|
||||
struct apfs_node *root = NULL;
|
||||
u64 oid = le64_to_cpup(oid_p);
|
||||
struct apfs_key key = {0};
|
||||
struct apfs_query *query = NULL;
|
||||
__le64 raw_key;
|
||||
struct apfs_omap_snapshot raw_val = {0};
|
||||
@@ -268,14 +261,12 @@ static int apfs_update_omap_snap_tree(struct super_block *sb, __le64 *oid_p)
|
||||
}
|
||||
oid = 0;
|
||||
|
||||
apfs_init_omap_snap_key(nxi->nx_xid, &key);
|
||||
|
||||
query = apfs_alloc_query(root, NULL /* parent */);
|
||||
if (!query) {
|
||||
err = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
query->key = &key;
|
||||
apfs_init_omap_snap_key(nxi->nx_xid, &query->key);
|
||||
query->flags = APFS_QUERY_OMAP_SNAP | APFS_QUERY_EXACT;
|
||||
|
||||
err = apfs_btree_query(sb, &query);
|
||||
@@ -499,15 +490,12 @@ static int apfs_snapshot_name_to_xid(struct apfs_node *snap_root, const char *na
|
||||
{
|
||||
struct super_block *sb = snap_root->object.sb;
|
||||
struct apfs_query *query = NULL;
|
||||
struct apfs_key key = {0};
|
||||
int err;
|
||||
|
||||
apfs_init_snap_name_key(name, &key);
|
||||
|
||||
query = apfs_alloc_query(snap_root, NULL /* parent */);
|
||||
if (!query)
|
||||
return -ENOMEM;
|
||||
query->key = &key;
|
||||
apfs_init_snap_name_key(name, &query->key);
|
||||
query->flags |= APFS_QUERY_SNAP_META | APFS_QUERY_EXACT;
|
||||
|
||||
err = apfs_btree_query(sb, &query);
|
||||
@@ -545,15 +533,12 @@ static int apfs_snapshot_xid_to_sblock(struct apfs_node *snap_root, u64 xid, u64
|
||||
{
|
||||
struct super_block *sb = snap_root->object.sb;
|
||||
struct apfs_query *query = NULL;
|
||||
struct apfs_key key = {0};
|
||||
int err;
|
||||
|
||||
apfs_init_snap_metadata_key(xid, &key);
|
||||
|
||||
query = apfs_alloc_query(snap_root, NULL /* parent */);
|
||||
if (!query)
|
||||
return -ENOMEM;
|
||||
query->key = &key;
|
||||
apfs_init_snap_metadata_key(xid, &query->key);
|
||||
query->flags |= APFS_QUERY_SNAP_META | APFS_QUERY_EXACT;
|
||||
|
||||
err = apfs_btree_query(sb, &query);
|
||||
|
||||
+204
-88
@@ -5,6 +5,7 @@
|
||||
|
||||
#include <linux/buffer_head.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/slab.h>
|
||||
#include "apfs.h"
|
||||
|
||||
/**
|
||||
@@ -85,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;
|
||||
}
|
||||
@@ -95,6 +96,25 @@ static int apfs_read_spaceman_dev(struct super_block *sb,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* apfs_spaceman_get_16 - Get a 16-bit value from an offset in the spaceman
|
||||
* @sb: superblock structure
|
||||
* @off: offset for the value
|
||||
*
|
||||
* Returns a pointer to the value, or NULL if it doesn't fit.
|
||||
*/
|
||||
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 > spaceman->sm_size)
|
||||
return NULL;
|
||||
if (off + sizeof(__le16) > spaceman->sm_size)
|
||||
return NULL;
|
||||
return (void *)sm_raw + off;
|
||||
}
|
||||
|
||||
/**
|
||||
* apfs_spaceman_get_64 - Get a 64-bit value from an offset in the spaceman
|
||||
* @sb: superblock structure
|
||||
@@ -107,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;
|
||||
}
|
||||
@@ -124,7 +144,7 @@ static bool apfs_ip_bm_is_free(struct apfs_spaceman_phys *sm, u16 index)
|
||||
u16 free_head = le16_to_cpu(sm->sm_ip_bm_free_head);
|
||||
u16 free_tail = le16_to_cpu(sm->sm_ip_bm_free_tail);
|
||||
u16 free_len, index_in_free;
|
||||
u16 bmap_count = 16;
|
||||
u16 bmap_count = le32_to_cpu(sm->sm_ip_bm_block_count);
|
||||
|
||||
free_len = (bmap_count + free_tail - free_head) % bmap_count;
|
||||
index_in_free = (bmap_count + index - free_head) % bmap_count;
|
||||
@@ -144,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 = 16;
|
||||
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;
|
||||
}
|
||||
@@ -168,65 +188,61 @@ static int apfs_update_ip_bm_free_next(struct super_block *sb)
|
||||
}
|
||||
|
||||
/**
|
||||
* apfs_rotate_ip_bitmaps - Allocate a new ip bitmap from the circular buffer
|
||||
* @sb: superblock structure
|
||||
* apfs_rotate_single_ip_bitmap - Reallocate an ip bmap in the circular buffer
|
||||
* @sb: filesystem superblock
|
||||
* @idx: index of the ip bitmap to reallocate
|
||||
*
|
||||
* Returns 0 on success or a negative error code in case of failure.
|
||||
*/
|
||||
static int apfs_rotate_ip_bitmaps(struct super_block *sb)
|
||||
static int apfs_rotate_single_ip_bitmap(struct super_block *sb, u32 idx)
|
||||
{
|
||||
struct apfs_nxsb_info *nxi = APFS_NXI(sb);
|
||||
struct apfs_spaceman *spaceman = APFS_SM(sb);
|
||||
struct apfs_spaceman_phys *sm_raw = spaceman->sm_raw;
|
||||
u64 bmap_base = le64_to_cpu(sm_raw->sm_ip_bm_base);
|
||||
u32 bmap_length = le32_to_cpu(sm_raw->sm_ip_bm_block_count);
|
||||
u16 free_head, free_tail;
|
||||
__le64 *curr_bmap_off, *xid;
|
||||
struct buffer_head *old_bh = NULL, *new_bh = NULL;
|
||||
int err = 0;
|
||||
|
||||
apfs_assert_in_transaction(sb, &sm_raw->sm_o);
|
||||
|
||||
if (le32_to_cpu(sm_raw->sm_ip_bm_size_in_blocks) != 1) {
|
||||
apfs_warn(sb, "Multiblock ip bitmaps not supported");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
xid = apfs_spaceman_get_64(sb, le32_to_cpu(sm_raw->sm_ip_bm_xid_offset));
|
||||
if (!xid) {
|
||||
apfs_err(sb, "xid out of bounds (%u)", le32_to_cpu(sm_raw->sm_ip_bm_xid_offset));
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
*xid = cpu_to_le64(nxi->nx_xid);
|
||||
u64 ring_base;
|
||||
u32 ring_length;
|
||||
u32 xid_off, ip_bitmap_off;
|
||||
u64 ip_bitmap_bno;
|
||||
u16 free_head;
|
||||
__le64 *xid_p = NULL;
|
||||
__le16 *ip_bitmap_p = NULL;
|
||||
int err;
|
||||
|
||||
ring_base = le64_to_cpu(sm_raw->sm_ip_bm_base);
|
||||
ring_length = le32_to_cpu(sm_raw->sm_ip_bm_block_count);
|
||||
free_head = le16_to_cpu(sm_raw->sm_ip_bm_free_head);
|
||||
free_tail = le16_to_cpu(sm_raw->sm_ip_bm_free_tail);
|
||||
|
||||
curr_bmap_off = apfs_spaceman_get_64(sb, le32_to_cpu(sm_raw->sm_ip_bitmap_offset));
|
||||
if (!curr_bmap_off) {
|
||||
apfs_err(sb, "bmap offset out of bounds (%u)", le32_to_cpu(sm_raw->sm_ip_bitmap_offset));
|
||||
xid_off = le32_to_cpu(sm_raw->sm_ip_bm_xid_offset) + idx * sizeof(*xid_p);
|
||||
xid_p = apfs_spaceman_get_64(sb, xid_off);
|
||||
if (!xid_p) {
|
||||
apfs_err(sb, "xid out of bounds (%u)", xid_off);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
old_bh = apfs_sb_bread(sb, bmap_base + le64_to_cpup(curr_bmap_off));
|
||||
*xid_p = cpu_to_le64(nxi->nx_xid);
|
||||
|
||||
ip_bitmap_off = le32_to_cpu(sm_raw->sm_ip_bitmap_offset) + idx * sizeof(*ip_bitmap_p);
|
||||
ip_bitmap_p = apfs_spaceman_get_16(sb, ip_bitmap_off);
|
||||
if (!ip_bitmap_p) {
|
||||
apfs_err(sb, "bmap offset out of bounds (%u)", ip_bitmap_off);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
|
||||
ip_bitmap_bno = ring_base + le16_to_cpup(ip_bitmap_p);
|
||||
old_bh = apfs_sb_bread(sb, ip_bitmap_bno);
|
||||
if (!old_bh) {
|
||||
apfs_err(sb, "failed to read current ip bitmap (0x%llx)", bmap_base + le64_to_cpup(curr_bmap_off));
|
||||
apfs_err(sb, "failed to read current ip bitmap (0x%llx)", ip_bitmap_bno);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
*curr_bmap_off = cpu_to_le64(free_head);
|
||||
free_head = (free_head + 1) % bmap_length;
|
||||
free_tail = (free_tail + 1) % bmap_length;
|
||||
*ip_bitmap_p = cpu_to_le16(free_head);
|
||||
free_head = (free_head + 1) % ring_length;
|
||||
sm_raw->sm_ip_bm_free_head = cpu_to_le16(free_head);
|
||||
sm_raw->sm_ip_bm_free_tail = cpu_to_le16(free_tail);
|
||||
err = apfs_update_ip_bm_free_next(sb);
|
||||
if (err) {
|
||||
apfs_err(sb, "failed to update bitmap ring");
|
||||
goto out;
|
||||
}
|
||||
|
||||
new_bh = apfs_getblk(sb, bmap_base + le64_to_cpup(curr_bmap_off));
|
||||
ip_bitmap_bno = ring_base + le16_to_cpup(ip_bitmap_p);
|
||||
new_bh = apfs_getblk(sb, ip_bitmap_bno);
|
||||
if (!new_bh) {
|
||||
apfs_err(sb, "failed to map block for CoW (0x%llx)", bmap_base + le64_to_cpup(curr_bmap_off));
|
||||
apfs_err(sb, "failed to map block for CoW (0x%llx)", ip_bitmap_bno);
|
||||
err = -EIO;
|
||||
goto out;
|
||||
}
|
||||
@@ -234,7 +250,7 @@ static int apfs_rotate_ip_bitmaps(struct super_block *sb)
|
||||
err = apfs_transaction_join(sb, new_bh);
|
||||
if (err)
|
||||
goto out;
|
||||
spaceman->sm_ip = new_bh;
|
||||
spaceman->sm_ip_bmaps[idx] = new_bh;
|
||||
|
||||
out:
|
||||
brelse(old_bh);
|
||||
@@ -243,6 +259,64 @@ out:
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* apfs_rotate_ip_bitmaps - Allocate new ip bitmaps from the circular buffer
|
||||
* @sb: superblock structure
|
||||
*
|
||||
* Allocates bitmaps for the whole internal pool at once, meaning that each
|
||||
* transaction is forced to allocate one bitmap for every ~1.32 TiB of container
|
||||
* size, even if they won't be needed. This seems very reasonable to me, but the
|
||||
* official implementation avoids it and they may have a good reason.
|
||||
*
|
||||
* Returns 0 on success or a negative error code in case of failure.
|
||||
*/
|
||||
static int apfs_rotate_ip_bitmaps(struct super_block *sb)
|
||||
{
|
||||
struct apfs_spaceman *spaceman = APFS_SM(sb);
|
||||
struct apfs_spaceman_phys *sm_raw = spaceman->sm_raw;
|
||||
u32 ring_length = le32_to_cpu(sm_raw->sm_ip_bm_block_count);
|
||||
u32 bmaps_count = spaceman->sm_ip_bmaps_count;
|
||||
u16 free_head, free_tail, free_len;
|
||||
int err;
|
||||
u32 i;
|
||||
|
||||
apfs_assert_in_transaction(sb, &sm_raw->sm_o);
|
||||
|
||||
free_head = le16_to_cpu(sm_raw->sm_ip_bm_free_head);
|
||||
free_tail = le16_to_cpu(sm_raw->sm_ip_bm_free_tail);
|
||||
|
||||
/*
|
||||
* Check that we have enough room before doing anything. If we run out
|
||||
* I may need to compact the ring using the blocks marked as 0xFFFF in
|
||||
* ip_bm_free_next (TODO).
|
||||
*/
|
||||
free_len = (ring_length + free_tail - free_head) % ring_length;
|
||||
if (free_len < bmaps_count) {
|
||||
apfs_alert(sb, "full ip bitmap ring (%u < %u)", free_len, bmaps_count);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
for (i = 0; i < bmaps_count; ++i) {
|
||||
err = apfs_rotate_single_ip_bitmap(sb, i);
|
||||
if (err) {
|
||||
apfs_err(sb, "failed to rotate ip bitmap %u", i);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
/* All bitmaps have been reallocated, so just free the same number */
|
||||
free_tail = (free_tail + bmaps_count) % ring_length;
|
||||
sm_raw->sm_ip_bm_free_tail = cpu_to_le16(free_tail);
|
||||
|
||||
err = apfs_update_ip_bm_free_next(sb);
|
||||
if (err) {
|
||||
apfs_err(sb, "failed to update bitmap ring");
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Free queue record data
|
||||
*/
|
||||
@@ -309,10 +383,11 @@ static int apfs_ip_mark_free(struct super_block *sb, u64 bno)
|
||||
{
|
||||
struct apfs_spaceman *sm = APFS_SM(sb);
|
||||
struct apfs_spaceman_phys *sm_raw = sm->sm_raw;
|
||||
char *bitmap = sm->sm_ip->b_data;
|
||||
struct buffer_head *bmap_bh = NULL;
|
||||
|
||||
bno -= le64_to_cpu(sm_raw->sm_ip_base);
|
||||
__clear_bit_le(bno, bitmap);
|
||||
bmap_bh = sm->sm_ip_bmaps[bno >> sm->sm_ip_bmaps_shift];
|
||||
__clear_bit_le(bno & sm->sm_ip_bmaps_mask, bmap_bh->b_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -337,15 +412,13 @@ static int apfs_flush_fq_rec(struct apfs_node *root, u64 xid, u64 *len)
|
||||
struct apfs_spaceman *sm = APFS_SM(sb);
|
||||
struct apfs_query *query = NULL;
|
||||
struct apfs_fq_rec fqrec = {0};
|
||||
struct apfs_key key;
|
||||
u64 bno;
|
||||
int err;
|
||||
|
||||
query = apfs_alloc_query(root, NULL /* parent */);
|
||||
if (!query)
|
||||
return -ENOMEM;
|
||||
apfs_init_free_queue_key(xid, 0 /* paddr */, &key);
|
||||
query->key = &key;
|
||||
apfs_init_free_queue_key(xid, 0 /* paddr */, &query->key);
|
||||
query->flags |= APFS_QUERY_FREE_QUEUE | APFS_QUERY_ANY_NUMBER | APFS_QUERY_EXACT;
|
||||
|
||||
err = apfs_btree_query(sb, &query);
|
||||
@@ -459,13 +532,47 @@ 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* apfs_allocate_spaceman - Allocate an in-memory spaceman struct, if needed
|
||||
* @sb: superblock structure
|
||||
* @bmap_cnt: internal pool bitmap count
|
||||
*
|
||||
* Returns the spaceman and sets it in the superblock info. Also sets the fixed
|
||||
* information about the ip bitmap count. On failure, returns an error pointer.
|
||||
*/
|
||||
static struct apfs_spaceman *apfs_allocate_spaceman(struct super_block *sb, u32 bmap_cnt)
|
||||
{
|
||||
struct apfs_nxsb_info *nxi = APFS_NXI(sb);
|
||||
struct apfs_spaceman *spaceman = NULL;
|
||||
int blk_bitcnt = sb->s_blocksize * 8;
|
||||
size_t sm_size;
|
||||
|
||||
if (nxi->nx_spaceman)
|
||||
return nxi->nx_spaceman;
|
||||
|
||||
/* We don't expect filesystems this big, it would be like 260 TiB */
|
||||
if (bmap_cnt > 200) {
|
||||
apfs_err(sb, "too many ip bitmap blocks (%u)", bmap_cnt);
|
||||
return ERR_PTR(-EFSCORRUPTED);
|
||||
}
|
||||
sm_size = sizeof(*spaceman) + bmap_cnt * sizeof(spaceman->sm_ip_bmaps[0]);
|
||||
|
||||
spaceman = nxi->nx_spaceman = kzalloc(sm_size, GFP_KERNEL);
|
||||
if (!spaceman)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
spaceman->sm_nxi = nxi;
|
||||
|
||||
spaceman->sm_ip_bmaps_count = bmap_cnt;
|
||||
spaceman->sm_ip_bmaps_mask = blk_bitcnt - 1;
|
||||
spaceman->sm_ip_bmaps_shift = order_base_2(blk_bitcnt);
|
||||
return spaceman;
|
||||
}
|
||||
|
||||
/**
|
||||
* apfs_read_spaceman - Find and read the space manager
|
||||
* @sb: superblock structure
|
||||
@@ -478,8 +585,8 @@ 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 = APFS_SM(sb);
|
||||
struct buffer_head *sm_bh;
|
||||
struct apfs_spaceman *spaceman = NULL;
|
||||
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);
|
||||
@@ -488,14 +595,24 @@ int apfs_read_spaceman(struct super_block *sb)
|
||||
if (sb->s_flags & SB_RDONLY) /* The space manager won't be needed */
|
||||
return 0;
|
||||
|
||||
spaceman->sm_free_cache_base = spaceman->sm_free_cache_blkcnt = 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)) {
|
||||
apfs_err(sb, "failed to allocate spaceman");
|
||||
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;
|
||||
|
||||
sm_flags = le32_to_cpu(sm_raw->sm_flags);
|
||||
/* Undocumented feature, but it's too common to refuse to mount */
|
||||
@@ -518,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");
|
||||
@@ -538,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;
|
||||
}
|
||||
@@ -557,7 +670,7 @@ static void apfs_write_spaceman(struct apfs_spaceman *sm)
|
||||
struct apfs_spaceman_device *dev_raw = &sm_raw->sm_dev[APFS_SD_MAIN];
|
||||
struct apfs_nxsb_info *nxi;
|
||||
|
||||
nxi = container_of(sm, struct apfs_nxsb_info, nx_spaceman);
|
||||
nxi = sm->sm_nxi;
|
||||
ASSERT(le64_to_cpu(sm_raw->sm_o.o_xid) == nxi->nx_xid);
|
||||
|
||||
dev_raw->sm_free_count = cpu_to_le64(sm->sm_free_count);
|
||||
@@ -573,18 +686,26 @@ static u64 apfs_ip_find_free(struct super_block *sb)
|
||||
{
|
||||
struct apfs_spaceman *sm = APFS_SM(sb);
|
||||
struct apfs_spaceman_phys *sm_raw = sm->sm_raw;
|
||||
u64 bitcount = le64_to_cpu(sm_raw->sm_ip_block_count);
|
||||
char *bitmap = sm->sm_ip->b_data;
|
||||
u64 offset;
|
||||
int blk_bitcnt = sb->s_blocksize * 8;
|
||||
u64 full_bitcnt = le64_to_cpu(sm_raw->sm_ip_block_count);
|
||||
u32 i;
|
||||
|
||||
if (bitcount > sb->s_blocksize * 8)
|
||||
return 0;
|
||||
offset = find_next_zero_bit_le(bitmap, bitcount, 0 /* offset */);
|
||||
if (offset >= bitcount) {
|
||||
apfs_warn(sb, "internal pool seems full");
|
||||
return 0;
|
||||
for (i = 0; i < sm->sm_ip_bmaps_count; ++i) {
|
||||
char *bitmap = sm->sm_ip_bmaps[i]->b_data;
|
||||
u64 off_in_bmap_blk, off_in_ip;
|
||||
|
||||
off_in_bmap_blk = find_next_zero_bit_le(bitmap, blk_bitcnt, 0 /* offset */);
|
||||
if (off_in_bmap_blk >= blk_bitcnt) /* No space in this chunk */
|
||||
continue;
|
||||
|
||||
/* We found something, confirm that it's not outside the ip */
|
||||
off_in_ip = (i << sm->sm_ip_bmaps_shift) + off_in_bmap_blk;
|
||||
if (off_in_ip >= full_bitcnt)
|
||||
break;
|
||||
return le64_to_cpu(sm_raw->sm_ip_base) + off_in_ip;
|
||||
}
|
||||
return le64_to_cpu(sm_raw->sm_ip_base) + offset;
|
||||
apfs_err(sb, "internal pool seems full");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -615,10 +736,11 @@ static void apfs_ip_mark_used(struct super_block *sb, u64 bno)
|
||||
{
|
||||
struct apfs_spaceman *sm = APFS_SM(sb);
|
||||
struct apfs_spaceman_phys *sm_raw = sm->sm_raw;
|
||||
char *bitmap = sm->sm_ip->b_data;
|
||||
struct buffer_head *bmap_bh = NULL;
|
||||
|
||||
bno -= le64_to_cpu(sm_raw->sm_ip_base);
|
||||
__set_bit_le(bno, bitmap);
|
||||
bmap_bh = sm->sm_ip_bmaps[bno >> sm->sm_ip_bmaps_shift];
|
||||
__set_bit_le(bno & sm->sm_ip_bmaps_mask, bmap_bh->b_data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -671,7 +793,6 @@ int apfs_free_queue_insert_nocache(struct super_block *sb, u64 bno, u64 count)
|
||||
struct apfs_query *query = NULL;
|
||||
struct apfs_spaceman_free_queue_key raw_key;
|
||||
__le64 raw_val;
|
||||
struct apfs_key key;
|
||||
u64 node_count;
|
||||
u16 node_limit;
|
||||
int err;
|
||||
@@ -693,9 +814,7 @@ int apfs_free_queue_insert_nocache(struct super_block *sb, u64 bno, u64 count)
|
||||
err = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
apfs_init_free_queue_key(nxi->nx_xid, bno, &key);
|
||||
query->key = &key;
|
||||
apfs_init_free_queue_key(nxi->nx_xid, bno, &query->key);
|
||||
query->flags |= APFS_QUERY_FREE_QUEUE;
|
||||
|
||||
err = apfs_btree_query(sb, &query);
|
||||
@@ -735,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);
|
||||
@@ -1060,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 */
|
||||
@@ -1123,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)
|
||||
|
||||
@@ -14,6 +14,9 @@
|
||||
#include <linux/statfs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include "apfs.h"
|
||||
#include "version.h"
|
||||
|
||||
#define APFS_MODULE_ID_STRING "linux-apfs by eafer (" GIT_COMMIT ")"
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0) /* iversion came in 4.16 */
|
||||
#include <linux/iversion.h>
|
||||
@@ -110,11 +113,8 @@ static struct buffer_head *apfs_read_super_copy(struct super_block *sb)
|
||||
apfs_err(sb, "not an apfs filesystem");
|
||||
goto fail;
|
||||
}
|
||||
if (!apfs_obj_verify_csum(sb, bh)) {
|
||||
apfs_err(sb, "inconsistent container superblock");
|
||||
err = -EFSBADCRC;
|
||||
goto fail;
|
||||
}
|
||||
if (!apfs_obj_verify_csum(sb, bh))
|
||||
apfs_notice(sb, "backup superblock seems corrupted");
|
||||
return bh;
|
||||
|
||||
fail:
|
||||
@@ -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,19 +307,32 @@ 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);
|
||||
#endif
|
||||
|
||||
list_del(&nxi->nx_list);
|
||||
kfree(nxi->nx_spaceman);
|
||||
nxi->nx_spaceman = NULL;
|
||||
kfree(nxi);
|
||||
out:
|
||||
sbi->s_nxi = NULL;
|
||||
@@ -711,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;
|
||||
@@ -879,10 +895,11 @@ static int apfs_count_used_blocks(struct super_block *sb, u64 *count)
|
||||
vol_id = le64_to_cpu(msb_raw->nx_fs_oid[i]);
|
||||
if (vol_id == 0) /* All volumes have been checked */
|
||||
break;
|
||||
err = apfs_omap_lookup_block(sb, omap, vol_id, &vol_bno,
|
||||
false /* write */);
|
||||
if (err)
|
||||
err = apfs_omap_lookup_newest_block(sb, omap, vol_id, &vol_bno, false /* write */);
|
||||
if (err) {
|
||||
apfs_err(sb, "omap lookup failed for vol id 0x%llx", vol_id);
|
||||
break;
|
||||
}
|
||||
|
||||
bh = apfs_sb_bread(sb, vol_bno);
|
||||
if (!bh) {
|
||||
@@ -958,6 +975,8 @@ static int apfs_show_options(struct seq_file *seq, struct dentry *root)
|
||||
|
||||
if (sbi->s_vol_nr != 0)
|
||||
seq_printf(seq, ",vol=%u", sbi->s_vol_nr);
|
||||
if (sbi->s_snap_name)
|
||||
seq_printf(seq, ",snap=%s", sbi->s_snap_name);
|
||||
if (uid_valid(sbi->s_uid))
|
||||
seq_printf(seq, ",uid=%u", from_kuid(&init_user_ns,
|
||||
sbi->s_uid));
|
||||
@@ -1198,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) {
|
||||
@@ -1225,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) {
|
||||
@@ -1260,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;
|
||||
}
|
||||
|
||||
@@ -1380,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;
|
||||
|
||||
@@ -1475,17 +1507,34 @@ static int apfs_test_super(struct super_block *sb, void *data)
|
||||
}
|
||||
|
||||
/**
|
||||
* apfs_set_super - Assign a fake bdev and an info struct to a superblock
|
||||
* apfs_set_super - Assign the device and an info struct to a superblock
|
||||
* @sb: superblock structure to set
|
||||
* @data: superblock info for the volume being mounted
|
||||
*/
|
||||
static int apfs_set_super(struct super_block *sb, void *data)
|
||||
{
|
||||
int err = set_anon_super(sb, data);
|
||||
struct apfs_sb_info *sbi = data;
|
||||
struct apfs_nxsb_info *nxi = sbi->s_nxi;
|
||||
int err;
|
||||
|
||||
if (!err)
|
||||
sb->s_fs_info = data;
|
||||
return err;
|
||||
/*
|
||||
* This fake device number will be unique to this volume-snapshot
|
||||
* combination. It gets reported by stat(), so that userland tools can
|
||||
* use it to tell different mountpoints apart.
|
||||
*/
|
||||
err = get_anon_bdev(&sbi->s_anon_dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* This is the actual device number, shared by all volumes and
|
||||
* snapshots. It gets reported by the mountinfo file, and it seems that
|
||||
* udisks uses it to decide if a device is mounted, so it must be set.
|
||||
*/
|
||||
sb->s_dev = nxi->nx_bdev->bd_dev;
|
||||
|
||||
sb->s_fs_info = sbi;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1534,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);
|
||||
@@ -1613,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)
|
||||
@@ -1638,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);
|
||||
@@ -1647,11 +1707,19 @@ out_unlock:
|
||||
return ERR_PTR(error);
|
||||
}
|
||||
|
||||
static void apfs_kill_sb(struct super_block *sb)
|
||||
{
|
||||
dev_t anon_dev = APFS_SB(sb)->s_anon_dev;
|
||||
|
||||
generic_shutdown_super(sb);
|
||||
free_anon_bdev(anon_dev);
|
||||
}
|
||||
|
||||
static struct file_system_type apfs_fs_type = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "apfs",
|
||||
.mount = apfs_mount,
|
||||
.kill_sb = kill_anon_super,
|
||||
.kill_sb = apfs_kill_sb,
|
||||
.fs_flags = FS_REQUIRES_DEV,
|
||||
};
|
||||
MODULE_ALIAS_FS("apfs");
|
||||
@@ -1677,6 +1745,7 @@ static void __exit exit_apfs_fs(void)
|
||||
|
||||
MODULE_AUTHOR("Ernesto A. Fernández");
|
||||
MODULE_DESCRIPTION("Apple File System");
|
||||
MODULE_VERSION(GIT_COMMIT);
|
||||
MODULE_LICENSE("GPL");
|
||||
module_init(init_apfs_fs)
|
||||
module_exit(exit_apfs_fs)
|
||||
+444
-433
File diff suppressed because it is too large.
Load diff
@@ -195,18 +195,16 @@ int apfs_xattr_get_compressed_data(struct inode *inode, const char *name, struct
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct apfs_sb_info *sbi = APFS_SB(sb);
|
||||
struct apfs_key key;
|
||||
struct apfs_query *query;
|
||||
struct apfs_xattr xattr;
|
||||
u64 cnid = apfs_ino(inode);
|
||||
int ret;
|
||||
|
||||
apfs_init_xattr_key(cnid, name, &key);
|
||||
|
||||
query = apfs_alloc_query(sbi->s_cat_root, NULL /* parent */);
|
||||
if (!query)
|
||||
return -ENOMEM;
|
||||
query->key = &key;
|
||||
apfs_init_xattr_key(cnid, name, &query->key);
|
||||
query->flags |= APFS_QUERY_CAT | APFS_QUERY_EXACT;
|
||||
|
||||
ret = apfs_btree_query(sb, &query);
|
||||
@@ -330,18 +328,15 @@ int ____apfs_xattr_get(struct inode *inode, const char *name, void *buffer,
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct apfs_sb_info *sbi = APFS_SB(sb);
|
||||
struct apfs_key key;
|
||||
struct apfs_query *query;
|
||||
struct apfs_xattr xattr;
|
||||
u64 cnid = apfs_ino(inode);
|
||||
int ret;
|
||||
|
||||
apfs_init_xattr_key(cnid, name, &key);
|
||||
|
||||
query = apfs_alloc_query(sbi->s_cat_root, NULL /* parent */);
|
||||
if (!query)
|
||||
return -ENOMEM;
|
||||
query->key = &key;
|
||||
apfs_init_xattr_key(cnid, name, &query->key);
|
||||
query->flags |= APFS_QUERY_CAT | APFS_QUERY_EXACT;
|
||||
|
||||
ret = apfs_btree_query(sb, &query);
|
||||
@@ -461,16 +456,13 @@ static int apfs_delete_any_xattr(struct inode *inode)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct apfs_sb_info *sbi = APFS_SB(sb);
|
||||
struct apfs_key key;
|
||||
struct apfs_query *query;
|
||||
int ret;
|
||||
|
||||
query = apfs_alloc_query(sbi->s_cat_root, NULL /* parent */);
|
||||
if (!query)
|
||||
return -ENOMEM;
|
||||
|
||||
apfs_init_xattr_key(apfs_ino(inode), NULL /* name */, &key);
|
||||
query->key = &key;
|
||||
apfs_init_xattr_key(apfs_ino(inode), NULL /* name */, &query->key);
|
||||
query->flags = APFS_QUERY_CAT | APFS_QUERY_ANY_NAME | APFS_QUERY_EXACT;
|
||||
|
||||
ret = apfs_btree_query(sb, &query);
|
||||
@@ -715,7 +707,6 @@ int apfs_xattr_set(struct inode *inode, const char *name, const void *value,
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct apfs_sb_info *sbi = APFS_SB(sb);
|
||||
struct apfs_key key;
|
||||
struct apfs_query *query = NULL;
|
||||
u64 cnid = apfs_ino(inode);
|
||||
int key_len, val_len;
|
||||
@@ -733,14 +724,12 @@ int apfs_xattr_set(struct inode *inode, const char *name, const void *value,
|
||||
}
|
||||
}
|
||||
|
||||
apfs_init_xattr_key(cnid, name, &key);
|
||||
|
||||
query = apfs_alloc_query(sbi->s_cat_root, NULL /* parent */);
|
||||
if (!query) {
|
||||
ret = -ENOMEM;
|
||||
goto done;
|
||||
}
|
||||
query->key = &key;
|
||||
apfs_init_xattr_key(cnid, name, &query->key);
|
||||
query->flags = APFS_QUERY_CAT | APFS_QUERY_EXACT;
|
||||
|
||||
ret = apfs_btree_query(sb, &query);
|
||||
@@ -874,7 +863,6 @@ ssize_t apfs_listxattr(struct dentry *dentry, char *buffer, size_t size)
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct apfs_sb_info *sbi = APFS_SB(sb);
|
||||
struct apfs_nxsb_info *nxi = APFS_NXI(sb);
|
||||
struct apfs_key key;
|
||||
struct apfs_query *query;
|
||||
u64 cnid = apfs_ino(inode);
|
||||
size_t free = size;
|
||||
@@ -889,8 +877,7 @@ ssize_t apfs_listxattr(struct dentry *dentry, char *buffer, size_t size)
|
||||
}
|
||||
|
||||
/* We want all the xattrs for the cnid, regardless of the name */
|
||||
apfs_init_xattr_key(cnid, NULL /* name */, &key);
|
||||
query->key = &key;
|
||||
apfs_init_xattr_key(cnid, NULL /* name */, &query->key);
|
||||
query->flags = APFS_QUERY_CAT | APFS_QUERY_MULTIPLE | APFS_QUERY_EXACT;
|
||||
|
||||
while (1) {
|
||||
|
||||
Reference in new issue
Block a user