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,11 +139,20 @@ 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
|
||||
#define TRANSACTION_BUFFERS_MAX 65536
|
||||
#define TRANSACTION_STARTS_MAX 65536
|
||||
#define APFS_TRANS_MAIN_QUEUE_MAX 10000
|
||||
#define APFS_TRANS_BUFFERS_MAX 65536
|
||||
#define APFS_TRANS_STARTS_MAX 65536
|
||||
|
||||
/* Possible states for the container transaction structure */
|
||||
#define APFS_NX_TRANS_FORCE_COMMIT 1 /* Commit guaranteed */
|
||||
@@ -156,25 +164,17 @@ 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 delayed_work t_work; /* Work task for transaction commits */
|
||||
struct super_block *t_work_sb; /* sb that last queued a commit */
|
||||
|
||||
struct list_head t_inodes; /* List of inodes in the transaction */
|
||||
struct list_head t_buffers; /* List of buffers in the transaction */
|
||||
size_t t_buffers_count; /* Count of items on the list */
|
||||
int t_starts_count; /* Count of starts for transaction */
|
||||
};
|
||||
|
||||
/*
|
||||
* Structure that keeps track of a volume transaction.
|
||||
*/
|
||||
struct apfs_vol_transaction {
|
||||
struct buffer_head *t_old_vsb; /* Volume superblock being replaced */
|
||||
|
||||
struct apfs_node t_old_omap_root; /* Omap root node being replaced */
|
||||
struct apfs_node t_old_cat_root; /* Catalog root node being replaced */
|
||||
};
|
||||
|
||||
/* State bits for buffer heads in a transaction */
|
||||
#define BH_TRANS BH_PrivateStart /* Attached to a transaction */
|
||||
#define BH_CSUM (BH_PrivateStart + 1) /* Requires checksum update */
|
||||
@@ -197,6 +197,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 +223,19 @@ struct apfs_max_ops {
|
||||
*/
|
||||
struct apfs_nxsb_info {
|
||||
struct block_device *nx_bdev; /* Device for the container */
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 9, 0)
|
||||
struct file *nx_bdev_file;
|
||||
#elif 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,8 +245,9 @@ 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;
|
||||
int nx_trans_buffers_max;
|
||||
|
||||
/* For now, a single semaphore for every operation */
|
||||
struct rw_semaphore nx_big_sem;
|
||||
@@ -283,6 +310,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 */
|
||||
|
||||
@@ -298,9 +327,8 @@ struct apfs_sb_info {
|
||||
|
||||
struct apfs_crypto_state_val *s_dflt_pfk; /* default per-file key */
|
||||
|
||||
struct apfs_vol_transaction s_transaction;
|
||||
|
||||
struct inode *s_private_dir; /* Inode for the private directory */
|
||||
struct work_struct s_orphan_cleanup_work;
|
||||
};
|
||||
|
||||
static inline struct apfs_sb_info *APFS_SB(struct super_block *sb)
|
||||
@@ -319,8 +347,10 @@ static inline bool apfs_is_sealed(struct super_block *sb)
|
||||
* apfs_vol_is_encrypted - Check if a volume is encrypting files
|
||||
* @sb: superblock
|
||||
*/
|
||||
static inline bool apfs_vol_is_encrypted(struct super_block *sb) {
|
||||
static inline bool apfs_vol_is_encrypted(struct super_block *sb)
|
||||
{
|
||||
struct apfs_superblock *vsb_raw = APFS_SB(sb)->s_vsb_raw;
|
||||
|
||||
return (vsb_raw->apfs_fs_flags & cpu_to_le64(APFS_FS_UNENCRYPTED)) == 0;
|
||||
}
|
||||
|
||||
@@ -339,7 +369,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)
|
||||
@@ -497,7 +527,7 @@ static inline void apfs_init_sibling_link_key(u64 ino, u64 id,
|
||||
{
|
||||
key->id = ino;
|
||||
key->type = APFS_TYPE_SIBLING_LINK;
|
||||
key->number = id; /* Only guessing */
|
||||
key->number = id; /* Only guessing (TODO) */
|
||||
key->name = NULL;
|
||||
}
|
||||
|
||||
@@ -590,20 +620,21 @@ static inline u64 apfs_cat_cnid(struct apfs_key_header *key)
|
||||
}
|
||||
|
||||
/* Flags for the query structure */
|
||||
#define APFS_QUERY_TREE_MASK 00177 /* Which b-tree we query */
|
||||
#define APFS_QUERY_OMAP 00001 /* This is a b-tree object map query */
|
||||
#define APFS_QUERY_CAT 00002 /* This is a catalog tree query */
|
||||
#define APFS_QUERY_FREE_QUEUE 00004 /* This is a free queue query */
|
||||
#define APFS_QUERY_EXTENTREF 00010 /* This is an extent reference query */
|
||||
#define APFS_QUERY_FEXT 00020 /* This is a fext tree query */
|
||||
#define APFS_QUERY_SNAP_META 00040 /* This is a snapshot meta query */
|
||||
#define APFS_QUERY_OMAP_SNAP 00100 /* This is an omap snapshots query */
|
||||
#define APFS_QUERY_NEXT 00200 /* Find next of multiple matches */
|
||||
#define APFS_QUERY_EXACT 00400 /* Search for an exact match */
|
||||
#define APFS_QUERY_DONE 01000 /* The search at this level is over */
|
||||
#define APFS_QUERY_ANY_NAME 02000 /* Multiple search for any name */
|
||||
#define APFS_QUERY_ANY_NUMBER 04000 /* Multiple search for any number */
|
||||
#define APFS_QUERY_TREE_MASK 000177 /* Which b-tree we query */
|
||||
#define APFS_QUERY_OMAP 000001 /* This is a b-tree object map query */
|
||||
#define APFS_QUERY_CAT 000002 /* This is a catalog tree query */
|
||||
#define APFS_QUERY_FREE_QUEUE 000004 /* This is a free queue query */
|
||||
#define APFS_QUERY_EXTENTREF 000010 /* This is an extent reference query */
|
||||
#define APFS_QUERY_FEXT 000020 /* This is a fext tree query */
|
||||
#define APFS_QUERY_SNAP_META 000040 /* This is a snapshot meta query */
|
||||
#define APFS_QUERY_OMAP_SNAP 000100 /* This is an omap snapshots query */
|
||||
#define APFS_QUERY_NEXT 000200 /* Find next of multiple matches */
|
||||
#define APFS_QUERY_EXACT 000400 /* Search for an exact match */
|
||||
#define APFS_QUERY_DONE 001000 /* The search at this level is over */
|
||||
#define APFS_QUERY_ANY_NAME 002000 /* Multiple search for any name */
|
||||
#define APFS_QUERY_ANY_NUMBER 004000 /* Multiple search for any number */
|
||||
#define APFS_QUERY_MULTIPLE (APFS_QUERY_ANY_NAME | APFS_QUERY_ANY_NUMBER)
|
||||
#define APFS_QUERY_PREV 010000 /* Find previous record */
|
||||
|
||||
/*
|
||||
* Structure used to retrieve data from an APFS B-Tree. For now only used
|
||||
@@ -611,7 +642,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;
|
||||
@@ -646,7 +677,9 @@ static inline u32 apfs_query_storage(struct apfs_query *query)
|
||||
return APFS_OBJ_PHYSICAL;
|
||||
if (query->flags & APFS_QUERY_OMAP_SNAP)
|
||||
return APFS_OBJ_PHYSICAL;
|
||||
BUG();
|
||||
|
||||
/* Absurd, but don't panic: let the callers fail and report it */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -717,6 +750,8 @@ struct apfs_inode_info {
|
||||
bool i_has_dstream; /* Is there a dstream record? */
|
||||
struct apfs_dstream_info i_dstream; /* Dstream data, if any */
|
||||
|
||||
bool i_cleaned; /* Orphan data already deleted */
|
||||
|
||||
struct inode vfs_inode;
|
||||
};
|
||||
|
||||
@@ -774,17 +809,31 @@ struct apfs_xattr {
|
||||
bool has_dstream;
|
||||
};
|
||||
|
||||
#define apfs_emerg(sb, fmt, ...) apfs_msg(sb, KERN_EMERG, fmt, ##__VA_ARGS__)
|
||||
#define apfs_alert(sb, fmt, ...) apfs_msg(sb, KERN_ALERT, fmt, ##__VA_ARGS__)
|
||||
#define apfs_crit(sb, fmt, ...) apfs_msg(sb, KERN_CRIT, fmt, ##__VA_ARGS__)
|
||||
#define apfs_err(sb, fmt, ...) apfs_msg(sb, KERN_ERR, fmt, ##__VA_ARGS__)
|
||||
#define apfs_warn(sb, fmt, ...) apfs_msg(sb, KERN_WARNING, fmt, ##__VA_ARGS__)
|
||||
#define apfs_notice(sb, fmt, ...) apfs_msg(sb, KERN_NOTICE, fmt, ##__VA_ARGS__)
|
||||
#define apfs_info(sb, fmt, ...) apfs_msg(sb, KERN_INFO, fmt, ##__VA_ARGS__)
|
||||
struct apfs_compressed_data {
|
||||
bool has_dstream;
|
||||
u64 size;
|
||||
union {
|
||||
struct apfs_dstream_info *dstream;
|
||||
void *data;
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
* Report function name and line number for the message types that are likely
|
||||
* to signal a bug, to make things easier for reporters. Don't do this for the
|
||||
* common messages, there is no point and it makes the console look too busy.
|
||||
*/
|
||||
#define apfs_emerg(sb, fmt, ...) apfs_msg(sb, KERN_EMERG, __func__, __LINE__, fmt, ##__VA_ARGS__)
|
||||
#define apfs_alert(sb, fmt, ...) apfs_msg(sb, KERN_ALERT, __func__, __LINE__, fmt, ##__VA_ARGS__)
|
||||
#define apfs_crit(sb, fmt, ...) apfs_msg(sb, KERN_CRIT, __func__, __LINE__, fmt, ##__VA_ARGS__)
|
||||
#define apfs_err(sb, fmt, ...) apfs_msg(sb, KERN_ERR, __func__, __LINE__, fmt, ##__VA_ARGS__)
|
||||
#define apfs_warn(sb, fmt, ...) apfs_msg(sb, KERN_WARNING, NULL, 0, fmt, ##__VA_ARGS__)
|
||||
#define apfs_notice(sb, fmt, ...) apfs_msg(sb, KERN_NOTICE, NULL, 0, fmt, ##__VA_ARGS__)
|
||||
#define apfs_info(sb, fmt, ...) apfs_msg(sb, KERN_INFO, NULL, 0, fmt, ##__VA_ARGS__)
|
||||
|
||||
#ifdef CONFIG_APFS_DEBUG
|
||||
#define ASSERT(expr) BUG_ON(!(expr))
|
||||
#define apfs_debug(sb, fmt, ...) apfs_msg(sb, KERN_DEBUG, fmt, ##__VA_ARGS__)
|
||||
#define ASSERT(expr) WARN_ON(!(expr))
|
||||
#define apfs_debug(sb, fmt, ...) apfs_msg(sb, KERN_DEBUG, __func__, __LINE__, fmt, ##__VA_ARGS__)
|
||||
#else
|
||||
#define ASSERT(expr) ((void)0)
|
||||
#define apfs_debug(sb, fmt, ...) no_printk(fmt, ##__VA_ARGS__)
|
||||
@@ -803,22 +852,24 @@ 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 struct apfs_node *apfs_omap_read_node(struct super_block *sb, u64 id);
|
||||
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);
|
||||
extern void apfs_btree_change_node_count(struct apfs_query *query, int change);
|
||||
extern int apfs_btree_replace(struct apfs_query *query, void *key, int key_len,
|
||||
void *val, int val_len);
|
||||
extern void apfs_query_direct_forward(struct apfs_query *query);
|
||||
|
||||
/* compress.c */
|
||||
extern int apfs_compress_get_size(struct inode *inode, loff_t *size);
|
||||
@@ -838,7 +889,7 @@ extern int apfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
unsigned int flags);
|
||||
extern int apfs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
|
||||
bool excl);
|
||||
#else
|
||||
#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 3, 0)
|
||||
extern int apfs_mknod(struct user_namespace *mnt_userns, struct inode *dir,
|
||||
struct dentry *dentry, umode_t mode, dev_t rdev);
|
||||
extern int apfs_mkdir(struct user_namespace *mnt_userns, struct inode *dir,
|
||||
@@ -848,6 +899,16 @@ extern int apfs_rename(struct user_namespace *mnt_userns, struct inode *old_dir,
|
||||
struct dentry *new_dentry, unsigned int flags);
|
||||
extern int apfs_create(struct user_namespace *mnt_userns, struct inode *dir,
|
||||
struct dentry *dentry, umode_t mode, bool excl);
|
||||
#else
|
||||
extern int apfs_mknod(struct mnt_idmap *idmap, struct inode *dir,
|
||||
struct dentry *dentry, umode_t mode, dev_t rdev);
|
||||
extern int apfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
|
||||
struct dentry *dentry, umode_t mode);
|
||||
extern int apfs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
|
||||
struct dentry *old_dentry, struct inode *new_dir,
|
||||
struct dentry *new_dentry, unsigned int flags);
|
||||
extern int apfs_create(struct mnt_idmap *idmap, struct inode *dir,
|
||||
struct dentry *dentry, umode_t mode, bool excl);
|
||||
#endif
|
||||
|
||||
extern int apfs_link(struct dentry *old_dentry, struct inode *dir,
|
||||
@@ -856,6 +917,7 @@ extern int apfs_unlink(struct inode *dir, struct dentry *dentry);
|
||||
extern int apfs_rmdir(struct inode *dir, struct dentry *dentry);
|
||||
extern int apfs_delete_orphan_link(struct inode *inode);
|
||||
extern int APFS_DELETE_ORPHAN_LINK_MAXOPS(void);
|
||||
extern u64 apfs_any_orphan_ino(struct super_block *sb, u64 *ino_p);
|
||||
|
||||
/* extents.c */
|
||||
extern int apfs_extent_from_query(struct apfs_query *query,
|
||||
@@ -871,16 +933,25 @@ extern int apfs_get_new_block(struct inode *inode, sector_t iblock,
|
||||
struct buffer_head *bh_result, int create);
|
||||
extern int APFS_GET_NEW_BLOCK_MAXOPS(void);
|
||||
extern int apfs_truncate(struct apfs_dstream_info *dstream, loff_t new_size);
|
||||
extern int apfs_inode_delete_front(struct inode *inode);
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
|
||||
extern loff_t apfs_remap_file_range(struct file *src_file, loff_t off, struct file *dst_file, loff_t destoff, loff_t len, unsigned int remap_flags);
|
||||
#else
|
||||
extern int apfs_clone_file_range(struct file *src_file, loff_t off, struct file *dst_file, loff_t destoff, u64 len);
|
||||
#endif
|
||||
extern int apfs_clone_extents(struct apfs_dstream_info *dstream, u64 new_id);
|
||||
extern int apfs_nonsparse_dstream_read(struct apfs_dstream_info *dstream, void *buf, size_t count, u64 offset);
|
||||
extern void apfs_nonsparse_dstream_preread(struct apfs_dstream_info *dstream);
|
||||
|
||||
/* file.c */
|
||||
extern int apfs_file_mmap(struct file *file, struct vm_area_struct *vma);
|
||||
extern int apfs_fsync(struct file *file, loff_t start, loff_t end, int datasync);
|
||||
|
||||
/* inode.c */
|
||||
extern struct inode *apfs_iget(struct super_block *sb, u64 cnid);
|
||||
extern int apfs_update_inode(struct inode *inode, char *new_name);
|
||||
extern int APFS_UPDATE_INODE_MAXOPS(void);
|
||||
extern void apfs_orphan_cleanup_work(struct work_struct *work);
|
||||
extern void apfs_evict_inode(struct inode *inode);
|
||||
extern struct inode *apfs_new_inode(struct inode *dir, umode_t mode,
|
||||
dev_t rdev);
|
||||
@@ -894,12 +965,19 @@ extern int apfs_dstream_adj_refcnt(struct apfs_dstream_info *dstream, u32 delta)
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 12, 0)
|
||||
extern int apfs_setattr(struct dentry *dentry, struct iattr *iattr);
|
||||
#else
|
||||
#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 3, 0)
|
||||
extern int apfs_setattr(struct user_namespace *mnt_userns,
|
||||
struct dentry *dentry, struct iattr *iattr);
|
||||
#else
|
||||
extern int apfs_setattr(struct mnt_idmap *idmap,
|
||||
struct dentry *dentry, struct iattr *iattr);
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
||||
extern int apfs_update_time(struct inode *inode, struct timespec64 *time, int flags);
|
||||
#else
|
||||
extern int apfs_update_time(struct inode *inode, int flags);
|
||||
#endif
|
||||
long apfs_dir_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
|
||||
long apfs_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
|
||||
|
||||
@@ -909,16 +987,23 @@ extern int apfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
|
||||
#elif LINUX_VERSION_CODE < KERNEL_VERSION(5, 12, 0)
|
||||
extern int apfs_getattr(const struct path *path, struct kstat *stat,
|
||||
u32 request_mask, unsigned int query_flags);
|
||||
#else
|
||||
#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 3, 0)
|
||||
extern int apfs_getattr(struct user_namespace *mnt_userns,
|
||||
const struct path *path, struct kstat *stat, u32 request_mask,
|
||||
unsigned int query_flags);
|
||||
#else
|
||||
extern int apfs_getattr(struct mnt_idmap *idmap,
|
||||
const struct path *path, struct kstat *stat, u32 request_mask,
|
||||
unsigned int query_flags);
|
||||
#endif
|
||||
|
||||
extern int apfs_crypto_adj_refcnt(struct super_block *sb, u64 crypto_id, int delta);
|
||||
extern int APFS_CRYPTO_ADJ_REFCNT_MAXOPS(void);
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0)
|
||||
extern int apfs_fileattr_get(struct dentry *dentry, struct fileattr *fa);
|
||||
extern int apfs_fileattr_set(struct mnt_idmap *idmap, struct dentry *dentry, struct fileattr *fa);
|
||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0)
|
||||
extern int apfs_fileattr_get(struct dentry *dentry, struct fileattr *fa);
|
||||
extern int apfs_fileattr_set(struct user_namespace *mnt_userns, struct dentry *dentry, struct fileattr *fa);
|
||||
#endif
|
||||
@@ -935,14 +1020,13 @@ extern int apfs_read_snap_meta_key(void *raw, int size, struct apfs_key *key);
|
||||
extern int apfs_read_omap_snap_key(void *raw, int size, struct apfs_key *key);
|
||||
|
||||
/* message.c */
|
||||
extern __printf(3, 4)
|
||||
void apfs_msg(struct super_block *sb, const char *prefix, const char *fmt, ...);
|
||||
extern __printf(5, 6) void apfs_msg(struct super_block *sb, const char *prefix, const char *func, int line, const char *fmt, ...);
|
||||
|
||||
/* node.c */
|
||||
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);
|
||||
@@ -950,6 +1034,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);
|
||||
@@ -957,12 +1042,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);
|
||||
@@ -986,8 +1071,8 @@ 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 void apfs_transaction_init(struct apfs_nx_transaction *trans);
|
||||
extern int apfs_transaction_start(struct super_block *sb, struct apfs_max_ops maxops);
|
||||
extern int apfs_transaction_commit(struct super_block *sb);
|
||||
extern void apfs_inode_join_transaction(struct super_block *sb, struct inode *inode);
|
||||
@@ -1006,6 +1091,9 @@ extern int apfs_xattr_set(struct inode *inode, const char *name, const void *val
|
||||
size_t size, int flags);
|
||||
extern int APFS_XATTR_SET_MAXOPS(void);
|
||||
extern ssize_t apfs_listxattr(struct dentry *dentry, char *buffer, size_t size);
|
||||
extern int apfs_xattr_get_compressed_data(struct inode *inode, const char *name, struct apfs_compressed_data *cdata);
|
||||
extern void apfs_release_compressed_data(struct apfs_compressed_data *cdata);
|
||||
extern int apfs_compressed_data_read(struct apfs_compressed_data *cdata, void *buf, size_t count, u64 offset);
|
||||
|
||||
/* xfield.c */
|
||||
extern int apfs_find_xfield(u8 *xfields, int len, u8 xtype, char **xval);
|
||||
@@ -1019,6 +1107,7 @@ extern int apfs_insert_xfield(u8 *buffer, int buflen,
|
||||
*/
|
||||
|
||||
/* compress.c */
|
||||
extern const struct address_space_operations apfs_compress_aops;
|
||||
extern const struct file_operations apfs_compress_file_operations;
|
||||
|
||||
/* dir.c */
|
||||
@@ -1039,6 +1128,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
|
||||
@@ -1066,7 +1167,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;
|
||||
|
||||
+62
-26
@@ -268,6 +268,10 @@ struct apfs_phys_ext_val {
|
||||
#define APFS_FILE_EXTENT_LEN_MASK 0x00ffffffffffffffULL
|
||||
#define APFS_FILE_EXTENT_FLAG_MASK 0xff00000000000000ULL
|
||||
#define APFS_FILE_EXTENT_FLAG_SHIFT 56
|
||||
#define APFS_FILE_EXTENT_CRYPTO_FLAG 0x01 /* Made-up name */
|
||||
#define APFS_FILE_EXTENT_PREALLOCATED 0x02 /* Made-up name */
|
||||
#define APFS_VALID_FILE_EXTENT_FLAGS (APFS_FILE_EXTENT_CRYPTO_FLAG \
|
||||
| APFS_FILE_EXTENT_PREALLOCATED)
|
||||
|
||||
/*
|
||||
* Put a bound on maximum file size so that a growing truncation will always
|
||||
@@ -306,7 +310,7 @@ struct apfs_wrapped_crypto_state {
|
||||
__le32 key_os_version;
|
||||
__le16 key_revision;
|
||||
__le16 key_len;
|
||||
u8 persistent_key[0];
|
||||
u8 persistent_key[];
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
@@ -503,7 +507,7 @@ struct apfs_dir_stats_val {
|
||||
struct apfs_sibling_val {
|
||||
__le64 parent_id;
|
||||
__le16 name_len;
|
||||
u8 name[0];
|
||||
u8 name[];
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
@@ -622,7 +626,7 @@ enum {
|
||||
struct apfs_drec_key {
|
||||
struct apfs_key_header hdr;
|
||||
__le16 name_len;
|
||||
u8 name[0];
|
||||
u8 name[];
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
@@ -632,7 +636,7 @@ struct apfs_drec_key {
|
||||
struct apfs_drec_hashed_key {
|
||||
struct apfs_key_header hdr;
|
||||
__le32 name_len_and_hash;
|
||||
u8 name[0];
|
||||
u8 name[];
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
@@ -641,7 +645,7 @@ struct apfs_drec_hashed_key {
|
||||
struct apfs_xattr_key {
|
||||
struct apfs_key_header hdr;
|
||||
__le16 name_len;
|
||||
u8 name[0];
|
||||
u8 name[];
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
@@ -660,7 +664,7 @@ struct apfs_snap_metadata_key {
|
||||
struct apfs_snap_name_key {
|
||||
struct apfs_key_header hdr;
|
||||
__le16 name_len;
|
||||
u8 name[0];
|
||||
u8 name[];
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
@@ -1051,13 +1055,19 @@ struct apfs_checkpoint_map_phys {
|
||||
#define APFS_FS_SPILLEDOVER 0x00000010LL
|
||||
#define APFS_FS_RUN_SPILLOVER_CLEANER 0x00000020LL
|
||||
#define APFS_FS_ALWAYS_CHECK_EXTENTREF 0x00000040LL
|
||||
#define APFS_FS_PREVIOUSLY_SEALED 0x00000080LL /* Made-up name */
|
||||
#define APFS_FS_PFK 0x00000100LL /* Made-up name */
|
||||
#define APFS_FS_UNKNOWN_200 0x00000200LL
|
||||
#define APFS_FS_FLAGS_VALID_MASK (APFS_FS_UNENCRYPTED \
|
||||
| APFS_FS_EFFACEABLE \
|
||||
| APFS_FS_RESERVED_4 \
|
||||
| APFS_FS_ONEKEY \
|
||||
| APFS_FS_SPILLEDOVER \
|
||||
| APFS_FS_RUN_SPILLOVER_CLEANER \
|
||||
| APFS_FS_ALWAYS_CHECK_EXTENTREF)
|
||||
| APFS_FS_EFFACEABLE \
|
||||
| APFS_FS_RESERVED_4 \
|
||||
| APFS_FS_ONEKEY \
|
||||
| APFS_FS_SPILLEDOVER \
|
||||
| APFS_FS_RUN_SPILLOVER_CLEANER \
|
||||
| APFS_FS_ALWAYS_CHECK_EXTENTREF \
|
||||
| APFS_FS_PREVIOUSLY_SEALED \
|
||||
| APFS_FS_PFK \
|
||||
| APFS_FS_UNKNOWN_200)
|
||||
|
||||
#define APFS_FS_CRYPTOFLAGS (APFS_FS_UNENCRYPTED \
|
||||
| APFS_FS_EFFACEABLE \
|
||||
@@ -1124,7 +1134,9 @@ struct apfs_checkpoint_map_phys {
|
||||
#define APFS_INCOMPAT_NORMALIZATION_INSENSITIVE 0x00000008LL
|
||||
#define APFS_INCOMPAT_INCOMPLETE_RESTORE 0x00000010LL
|
||||
#define APFS_INCOMPAT_SEALED_VOLUME 0x00000020LL
|
||||
#define APFS_INCOMPAT_RESERVED_40 0x00000040LL
|
||||
#define APFS_INCOMPAT_PFK 0x00000040LL /* Made-up name */
|
||||
#define APFS_INCOMPAT_EXTENT_PREALLOC_FLAG 0x00000080LL /* Made-up name */
|
||||
#define APFS_INCOMPAT_SECONDARY_FSROOT 0x00000100LL /* Made-up name */
|
||||
|
||||
#define APFS_SUPPORTED_INCOMPAT_MASK (APFS_INCOMPAT_CASE_INSENSITIVE \
|
||||
| APFS_INCOMPAT_DATALESS_SNAPS \
|
||||
@@ -1132,7 +1144,9 @@ struct apfs_checkpoint_map_phys {
|
||||
| APFS_INCOMPAT_NORMALIZATION_INSENSITIVE \
|
||||
| APFS_INCOMPAT_INCOMPLETE_RESTORE \
|
||||
| APFS_INCOMPAT_SEALED_VOLUME \
|
||||
| APFS_INCOMPAT_RESERVED_40)
|
||||
| APFS_INCOMPAT_PFK \
|
||||
| APFS_INCOMPAT_EXTENT_PREALLOC_FLAG \
|
||||
| APFS_INCOMPAT_SECONDARY_FSROOT)
|
||||
|
||||
#define APFS_MODIFIED_NAMELEN 32
|
||||
|
||||
@@ -1162,6 +1176,18 @@ struct apfs_modified_by {
|
||||
#define APFS_CRYPTO_RESERVED_5 5
|
||||
#define APFS_UNASSIGNED_CRYPTO_ID (~0ULL)
|
||||
|
||||
/* Doc id index flags. I'm making up the names for now. */
|
||||
#define APFS_DOC_ID_HAS_PREV_TREE 0x00000001
|
||||
#define APFS_DOC_ID_UNKNOWN_02 0x00000002
|
||||
#define APFS_DOC_ID_UNKNOWN_04 0x00000004
|
||||
#define APFS_DOC_ID_UNKNOWN_08 0x00000008
|
||||
#define APFS_DOC_ID_UNKNOWN_10 0x00000010
|
||||
#define APFS_DOC_ID_VALID_FLAGS (APFS_DOC_ID_HAS_PREV_TREE \
|
||||
| APFS_DOC_ID_UNKNOWN_02 \
|
||||
| APFS_DOC_ID_UNKNOWN_04 \
|
||||
| APFS_DOC_ID_UNKNOWN_08 \
|
||||
| APFS_DOC_ID_UNKNOWN_10)
|
||||
|
||||
/*
|
||||
* Structure used to store the encryption state
|
||||
*/
|
||||
@@ -1241,15 +1267,24 @@ struct apfs_superblock {
|
||||
|
||||
__le64 apfs_snap_meta_ext_oid;
|
||||
|
||||
char apfs_volume_group_id[UUID_SIZE];
|
||||
/*3F0*/ char apfs_volume_group_id[UUID_SIZE];
|
||||
|
||||
__le64 apfs_integrity_meta_oid;
|
||||
/*400*/ __le64 apfs_integrity_meta_oid;
|
||||
|
||||
__le64 apfs_fext_tree_oid;
|
||||
__le32 apfs_fext_tree_type;
|
||||
/*410*/ __le32 apfs_fext_tree_type;
|
||||
|
||||
__le32 reserved_type;
|
||||
__le64 reserved_oid;
|
||||
|
||||
/*420*/ __le64 apfs_doc_id_index_xid;
|
||||
__le32 apfs_doc_id_index_flags;
|
||||
__le32 apfs_doc_id_tree_type;
|
||||
/*430*/ __le64 apfs_doc_id_tree_oid; /* Made-up name */
|
||||
__le64 apfs_prev_doc_id_tree_oid;
|
||||
__le64 apfs_doc_id_fixup_cursor;
|
||||
__le64 apfs_sec_root_tree_oid;
|
||||
/*450*/ __le32 apfs_sec_root_tree_type;
|
||||
} __packed;
|
||||
|
||||
/* Extended attributes constants */
|
||||
@@ -1268,9 +1303,10 @@ enum {
|
||||
APFS_XATTR_DATA_EMBEDDED = 0x00000002,
|
||||
APFS_XATTR_FILE_SYSTEM_OWNED = 0x00000004,
|
||||
APFS_XATTR_RESERVED_8 = 0x00000008,
|
||||
APFS_XATTR_UNKNOWN_10 = 0x00000010,
|
||||
};
|
||||
|
||||
#define APFS_XATTR_VALID_FLAGS 0x0000000f
|
||||
#define APFS_XATTR_VALID_FLAGS 0x0000001f
|
||||
|
||||
/*
|
||||
* Structure of the value of an extended attributes record
|
||||
@@ -1278,7 +1314,7 @@ enum {
|
||||
struct apfs_xattr_val {
|
||||
__le16 flags;
|
||||
__le16 xdata_len;
|
||||
u8 xdata[0];
|
||||
u8 xdata[];
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
@@ -1372,7 +1408,7 @@ struct apfs_file_info_key {
|
||||
struct apfs_file_data_hash_val {
|
||||
__le16 hashed_len;
|
||||
u8 hash_size;
|
||||
u8 hash[0];
|
||||
u8 hash[];
|
||||
} __packed;
|
||||
|
||||
#define APFS_FILE_INFO_DATA_HASH 1
|
||||
@@ -1435,7 +1471,7 @@ struct apfs_compress_rsrc_data {
|
||||
struct apfs_compress_rsrc_block {
|
||||
__le32 offs;
|
||||
__le32 size;
|
||||
} __packed block[0];
|
||||
} __packed block[];
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
@@ -1456,7 +1492,7 @@ struct apfs_snap_metadata_val {
|
||||
__le32 extentref_tree_type;
|
||||
__le32 flags;
|
||||
__le16 name_len;
|
||||
u8 name[0];
|
||||
u8 name[];
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
@@ -1475,7 +1511,7 @@ struct apfs_snap_meta_ext {
|
||||
__le32 sme_version;
|
||||
__le32 sme_flags;
|
||||
__le64 sme_snap_xid;
|
||||
char sme_uuid[UUID_SIZE];
|
||||
char sme_uuid[UUID_SIZE];
|
||||
__le64 sme_token;
|
||||
} __packed;
|
||||
|
||||
@@ -1503,11 +1539,11 @@ enum {
|
||||
* Structure of a single entry in the keybag
|
||||
*/
|
||||
struct apfs_keybag_entry {
|
||||
char ke_uuid[UUID_SIZE];
|
||||
char ke_uuid[UUID_SIZE];
|
||||
__le16 ke_tag;
|
||||
__le16 ke_keylen;
|
||||
__le32 padding;
|
||||
u8 ke_keydata[0];
|
||||
u8 ke_keydata[];
|
||||
} __packed;
|
||||
|
||||
#define APFS_KEYBAG_VERSION 2
|
||||
@@ -1520,7 +1556,7 @@ struct apfs_kb_locker {
|
||||
__le16 kl_nkeys;
|
||||
__le32 kl_nbytes;
|
||||
__le64 padding;
|
||||
struct apfs_keybag_entry kl_entries[0];
|
||||
struct apfs_keybag_entry kl_entries[];
|
||||
} __packed;
|
||||
|
||||
#endif /* _APFS_RAW_H */
|
||||
+327
-300
File diff suppressed because it is too large.
Load diff
@@ -1,7 +1,8 @@
|
||||
PACKAGE_NAME="linux-apfs-rw"
|
||||
PACKAGE_VERSION="0.3.1"
|
||||
PACKAGE_VERSION="0.3.10"
|
||||
|
||||
BUILT_MODULE_NAME[0]="apfs"
|
||||
DEST_MODULE_LOCATION[0]="/extra"
|
||||
|
||||
AUTOINSTALL="yes"
|
||||
PRE_BUILD="genver.sh"
|
||||
@@ -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;
|
||||
@@ -42,8 +48,10 @@ static vm_fault_t apfs_page_mkwrite(struct vm_fault *vmf)
|
||||
apfs_inode_join_transaction(sb, inode);
|
||||
|
||||
err = apfs_inode_create_exclusive_dstream(inode);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "dstream creation failed for ino 0x%llx", apfs_ino(inode));
|
||||
goto out_abort;
|
||||
}
|
||||
|
||||
lock_page(page);
|
||||
wait_for_stable_page(page);
|
||||
@@ -52,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)
|
||||
@@ -77,8 +93,10 @@ static vm_fault_t apfs_page_mkwrite(struct vm_fault *vmf)
|
||||
unlock_page(page); /* XXX: race? */
|
||||
|
||||
err = block_page_mkwrite(vma, vmf, apfs_get_new_block);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "mkwrite failed for ino 0x%llx", apfs_ino(inode));
|
||||
goto out_abort;
|
||||
}
|
||||
set_page_dirty(page);
|
||||
|
||||
/* An immediate commit would leave the page unlocked */
|
||||
@@ -95,7 +113,11 @@ out_abort:
|
||||
apfs_transaction_abort(sb);
|
||||
out:
|
||||
if (err)
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
||||
ret = block_page_mkwrite_return(err);
|
||||
#else
|
||||
ret = vmf_fs_error(err);
|
||||
#endif
|
||||
sb_end_pagefault(inode->i_sb);
|
||||
return ret;
|
||||
}
|
||||
@@ -106,7 +128,7 @@ static const struct vm_operations_struct apfs_file_vm_ops = {
|
||||
.page_mkwrite = apfs_page_mkwrite,
|
||||
};
|
||||
|
||||
static int apfs_file_mmap(struct file * file, struct vm_area_struct * vma)
|
||||
int apfs_file_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
{
|
||||
struct address_space *mapping = file->f_mapping;
|
||||
|
||||
@@ -133,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,
|
||||
@@ -141,8 +173,25 @@ const struct file_operations apfs_file_operations = {
|
||||
.open = generic_file_open,
|
||||
.fsync = apfs_fsync,
|
||||
.unlocked_ioctl = apfs_file_ioctl,
|
||||
|
||||
#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
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
|
||||
.remap_file_range = apfs_remap_file_range,
|
||||
#else
|
||||
.clone_file_range = apfs_clone_file_range,
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 5, 0)
|
||||
.splice_read = generic_file_splice_read,
|
||||
#else
|
||||
.splice_read = filemap_splice_read,
|
||||
#endif
|
||||
.splice_write = iter_file_splice_write,
|
||||
};
|
||||
|
||||
#if LINUX_VERSION_CODE == KERNEL_VERSION(5, 3, 0)
|
||||
|
||||
@@ -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
|
||||
@@ -81,8 +81,10 @@ int apfs_keycmp(struct apfs_key *k1, struct apfs_key *k2)
|
||||
*/
|
||||
int apfs_read_cat_key(void *raw, int size, struct apfs_key *key, bool hashed)
|
||||
{
|
||||
if (size < sizeof(struct apfs_key_header))
|
||||
if (size < sizeof(struct apfs_key_header)) {
|
||||
apfs_err(NULL, "bad key length (%d)", size);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
key->id = apfs_cat_cnid((struct apfs_key_header *)raw);
|
||||
key->type = apfs_cat_type((struct apfs_key_header *)raw);
|
||||
|
||||
@@ -92,6 +94,7 @@ int apfs_read_cat_key(void *raw, int size, struct apfs_key *key, bool hashed)
|
||||
if (size < sizeof(struct apfs_drec_hashed_key) + 1 ||
|
||||
*((char *)raw + size - 1) != 0) {
|
||||
/* Filename must have NULL-termination */
|
||||
apfs_err(NULL, "invalid drec key (%d)", size);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
/* Name length is not used in key comparisons, only the hash */
|
||||
@@ -103,6 +106,7 @@ int apfs_read_cat_key(void *raw, int size, struct apfs_key *key, bool hashed)
|
||||
if (size < sizeof(struct apfs_drec_key) + 1 ||
|
||||
*((char *)raw + size - 1) != 0) {
|
||||
/* Filename must have NULL-termination */
|
||||
apfs_err(NULL, "invalid drec key (%d)", size);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
/* There's no hash */
|
||||
@@ -114,21 +118,26 @@ int apfs_read_cat_key(void *raw, int size, struct apfs_key *key, bool hashed)
|
||||
if (size < sizeof(struct apfs_xattr_key) + 1 ||
|
||||
*((char *)raw + size - 1) != 0) {
|
||||
/* xattr name must have NULL-termination */
|
||||
apfs_err(NULL, "invalid xattr key (%d)", size);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
key->number = 0;
|
||||
key->name = ((struct apfs_xattr_key *)raw)->name;
|
||||
break;
|
||||
case APFS_TYPE_FILE_EXTENT:
|
||||
if (size != sizeof(struct apfs_file_extent_key))
|
||||
if (size != sizeof(struct apfs_file_extent_key)) {
|
||||
apfs_err(NULL, "bad key length (%d)", size);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
key->number = le64_to_cpu(
|
||||
((struct apfs_file_extent_key *)raw)->logical_addr);
|
||||
key->name = NULL;
|
||||
break;
|
||||
case APFS_TYPE_SIBLING_LINK:
|
||||
if (size != sizeof(struct apfs_sibling_link_key))
|
||||
if (size != sizeof(struct apfs_sibling_link_key)) {
|
||||
apfs_err(NULL, "bad key length (%d)", size);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
key->number = le64_to_cpu(
|
||||
((struct apfs_sibling_link_key *)raw)->sibling_id);
|
||||
key->name = NULL;
|
||||
@@ -146,8 +155,10 @@ int apfs_read_fext_key(void *raw, int size, struct apfs_key *key)
|
||||
{
|
||||
struct apfs_fext_tree_key *raw_key;
|
||||
|
||||
if (size != sizeof(*raw_key))
|
||||
if (size != sizeof(*raw_key)) {
|
||||
apfs_err(NULL, "bad key length (%d)", size);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
raw_key = raw;
|
||||
|
||||
key->id = le64_to_cpu(raw_key->private_id);
|
||||
@@ -169,8 +180,10 @@ int apfs_read_free_queue_key(void *raw, int size, struct apfs_key *key)
|
||||
{
|
||||
struct apfs_spaceman_free_queue_key *raw_key;
|
||||
|
||||
if (size < sizeof(struct apfs_spaceman_free_queue_key))
|
||||
if (size < sizeof(struct apfs_spaceman_free_queue_key)) {
|
||||
apfs_err(NULL, "bad key length (%d)", size);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
raw_key = raw;
|
||||
|
||||
key->id = le64_to_cpu(raw_key->sfqk_xid);
|
||||
@@ -191,8 +204,10 @@ int apfs_read_free_queue_key(void *raw, int size, struct apfs_key *key)
|
||||
*/
|
||||
int apfs_read_omap_key(void *raw, int size, struct apfs_key *key)
|
||||
{
|
||||
if (size < sizeof(struct apfs_omap_key))
|
||||
if (size < sizeof(struct apfs_omap_key)) {
|
||||
apfs_err(NULL, "bad key length (%d)", size);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
|
||||
key->id = le64_to_cpu(((struct apfs_omap_key *)raw)->ok_oid);
|
||||
key->type = 0;
|
||||
@@ -212,8 +227,10 @@ int apfs_read_omap_key(void *raw, int size, struct apfs_key *key)
|
||||
*/
|
||||
int apfs_read_extentref_key(void *raw, int size, struct apfs_key *key)
|
||||
{
|
||||
if (size != sizeof(struct apfs_phys_ext_key))
|
||||
if (size != sizeof(struct apfs_phys_ext_key)) {
|
||||
apfs_err(NULL, "bad key length (%d)", size);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
key->id = apfs_cat_cnid((struct apfs_key_header *)raw);
|
||||
key->type = apfs_cat_type((struct apfs_key_header *)raw);
|
||||
key->number = 0;
|
||||
@@ -223,21 +240,26 @@ int apfs_read_extentref_key(void *raw, int size, struct apfs_key *key)
|
||||
|
||||
int apfs_read_snap_meta_key(void *raw, int size, struct apfs_key *key)
|
||||
{
|
||||
if (size < sizeof(struct apfs_key_header))
|
||||
if (size < sizeof(struct apfs_key_header)) {
|
||||
apfs_err(NULL, "bad key length (%d)", size);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
key->id = apfs_cat_cnid((struct apfs_key_header *)raw);
|
||||
key->type = apfs_cat_type((struct apfs_key_header *)raw);
|
||||
key->number = 0;
|
||||
|
||||
switch (key->type) {
|
||||
case APFS_TYPE_SNAP_METADATA:
|
||||
if (size != sizeof(struct apfs_snap_metadata_key))
|
||||
if (size != sizeof(struct apfs_snap_metadata_key)) {
|
||||
apfs_err(NULL, "bad key length (%d)", size);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
key->name = NULL;
|
||||
return 0;
|
||||
case APFS_TYPE_SNAP_NAME:
|
||||
if (size < sizeof(struct apfs_snap_name_key) + 1 || *((char *)raw + size - 1) != 0) {
|
||||
/* snapshot name must have NULL-termination */
|
||||
apfs_err(NULL, "invalid snap name key (%d)", size);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
key->name = ((struct apfs_snap_name_key *)raw)->name;
|
||||
@@ -251,8 +273,10 @@ int apfs_read_omap_snap_key(void *raw, int size, struct apfs_key *key)
|
||||
{
|
||||
__le64 *xid = NULL;
|
||||
|
||||
if (size != sizeof(*xid))
|
||||
if (size != sizeof(*xid)) {
|
||||
apfs_err(NULL, "bad key length (%d)", size);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
xid = raw;
|
||||
|
||||
key->id = le64_to_cpup(xid);
|
||||
|
||||
@@ -6,8 +6,9 @@
|
||||
#include <linux/fs.h>
|
||||
#include "apfs.h"
|
||||
|
||||
void apfs_msg(struct super_block *sb, const char *prefix, const char *fmt, ...)
|
||||
void apfs_msg(struct super_block *sb, const char *prefix, const char *func, int line, const char *fmt, ...)
|
||||
{
|
||||
char *sb_id = NULL;
|
||||
struct va_format vaf;
|
||||
va_list args;
|
||||
|
||||
@@ -16,7 +17,13 @@ void apfs_msg(struct super_block *sb, const char *prefix, const char *fmt, ...)
|
||||
vaf.fmt = fmt;
|
||||
vaf.va = &args;
|
||||
|
||||
printk("%sAPFS (%s): %pV\n", prefix, sb->s_id, &vaf);
|
||||
/* The superblock is not available to all callers */
|
||||
sb_id = sb ? sb->s_id : "?";
|
||||
|
||||
if (func)
|
||||
printk("%sAPFS (%s): %pV (%s:%d)\n", prefix, sb_id, &vaf, func, line);
|
||||
else
|
||||
printk("%sAPFS (%s): %pV\n", prefix, sb_id, &vaf);
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
@@ -18,13 +18,17 @@ static struct dentry *apfs_lookup(struct inode *dir, struct dentry *dentry,
|
||||
return ERR_PTR(-ENAMETOOLONG);
|
||||
|
||||
err = apfs_inode_by_name(dir, &dentry->d_name, &ino);
|
||||
if (err && err != -ENODATA)
|
||||
if (err && err != -ENODATA) {
|
||||
apfs_err(dir->i_sb, "inode lookup by name failed");
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
if (!err) {
|
||||
inode = apfs_iget(dir->i_sb, ino);
|
||||
if (IS_ERR(inode))
|
||||
if (IS_ERR(inode)) {
|
||||
apfs_err(dir->i_sb, "iget failed");
|
||||
return ERR_CAST(inode);
|
||||
}
|
||||
}
|
||||
|
||||
return d_splice_alias(inode, dentry);
|
||||
@@ -33,9 +37,12 @@ static struct dentry *apfs_lookup(struct inode *dir, struct dentry *dentry,
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 12, 0)
|
||||
static int apfs_symlink(struct inode *dir, struct dentry *dentry,
|
||||
const char *symname)
|
||||
#else
|
||||
#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 3, 0)
|
||||
static int apfs_symlink(struct user_namespace *mnt_userns, struct inode *dir,
|
||||
struct dentry *dentry, const char *symname)
|
||||
#else
|
||||
static int apfs_symlink(struct mnt_idmap *idmap, struct inode *dir,
|
||||
struct dentry *dentry, const char *symname)
|
||||
#endif
|
||||
{
|
||||
/* Symlink permissions don't mean anything and their value is fixed */
|
||||
|
||||
@@ -37,129 +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))
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -195,110 +195,26 @@ 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)
|
||||
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)) {
|
||||
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) {
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
return ERR_PTR(err);
|
||||
|
||||
bh = apfs_sb_bread(sb, obj_bno);
|
||||
if (!bh)
|
||||
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];
|
||||
}
|
||||
return ERR_PTR(-EFSCORRUPTED); /* The mapping is missing */
|
||||
apfs_err(sb, "no mapping for oid 0x%llx", oid);
|
||||
return ERR_PTR(-EFSCORRUPTED);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -325,13 +241,16 @@ struct buffer_head *apfs_read_object_block(struct super_block *sb, u64 bno, bool
|
||||
ASSERT(write || !preserve);
|
||||
|
||||
bh = apfs_sb_bread(sb, bno);
|
||||
if (!bh)
|
||||
if (!bh) {
|
||||
apfs_err(sb, "failed to read object block 0x%llx", bno);
|
||||
return ERR_PTR(-EIO);
|
||||
}
|
||||
|
||||
obj = (struct apfs_obj_phys *)bh->b_data;
|
||||
type = le32_to_cpu(obj->o_type);
|
||||
ASSERT(!(type & APFS_OBJ_EPHEMERAL));
|
||||
if (nxi->nx_flags & APFS_CHECK_NODES && !apfs_obj_verify_csum(sb, bh)) {
|
||||
apfs_err(sb, "bad checksum for object in block 0x%llx", bno);
|
||||
err = -EFSBADCRC;
|
||||
goto fail;
|
||||
}
|
||||
@@ -345,10 +264,13 @@ struct buffer_head *apfs_read_object_block(struct super_block *sb, u64 bno, bool
|
||||
return bh;
|
||||
|
||||
err = apfs_spaceman_allocate_block(sb, &new_bno, true /* backwards */);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "block allocation failed");
|
||||
goto fail;
|
||||
}
|
||||
new_bh = apfs_getblk(sb, new_bno);
|
||||
if (!new_bh) {
|
||||
apfs_err(sb, "failed to map block for CoW (0x%llx)", new_bno);
|
||||
err = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
@@ -361,6 +283,8 @@ struct buffer_head *apfs_read_object_block(struct super_block *sb, u64 bno, bool
|
||||
*/
|
||||
if (!preserve) {
|
||||
err = apfs_free_queue_insert(sb, bh->b_blocknr, 1);
|
||||
if (err)
|
||||
apfs_err(sb, "free queue insertion failed for 0x%llx", (unsigned long long)bh->b_blocknr);
|
||||
} else if ((type & APFS_OBJECT_TYPE_MASK) != APFS_OBJECT_TYPE_FS) {
|
||||
vsb_raw = APFS_SB(sb)->s_vsb_raw;
|
||||
apfs_assert_in_transaction(sb, &vsb_raw->apfs_o);
|
||||
|
||||
+181
-59
File diff suppressed because it is too large.
Load diff
+425
-160
File diff suppressed because it is too large.
Load diff
Loaded 20 of 24 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user