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)
|
||||
@@ -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,6 +809,15 @@ struct apfs_xattr {
|
||||
bool has_dstream;
|
||||
};
|
||||
|
||||
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
|
||||
@@ -788,7 +832,7 @@ struct apfs_xattr {
|
||||
#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 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)
|
||||
@@ -808,21 +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 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);
|
||||
@@ -870,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,
|
||||
@@ -885,20 +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);
|
||||
@@ -920,7 +973,11 @@ 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);
|
||||
|
||||
@@ -969,7 +1026,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);
|
||||
@@ -977,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);
|
||||
@@ -984,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);
|
||||
@@ -1013,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);
|
||||
@@ -1033,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);
|
||||
@@ -1046,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 */
|
||||
@@ -1066,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
|
||||
@@ -1093,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 */
|
||||
+318
-355
File diff suppressed because it is too large.
Load diff
@@ -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;
|
||||
@@ -268,7 +275,7 @@ static int apfs_build_dentry_unhashed_key(struct qstr *qname, u64 parent_id,
|
||||
|
||||
apfs_key_set_hdr(APFS_TYPE_DIR_REC, parent_id, key);
|
||||
key->name_len = cpu_to_le16(namelen);
|
||||
strcpy(key->name, qname->name);
|
||||
strscpy(key->name, qname->name, namelen);
|
||||
|
||||
*key_p = key;
|
||||
return key_len;
|
||||
@@ -297,7 +304,7 @@ static int apfs_build_dentry_hashed_key(struct qstr *qname, u64 hash, u64 parent
|
||||
|
||||
apfs_key_set_hdr(APFS_TYPE_DIR_REC, parent_id, key);
|
||||
key->name_len_and_hash = cpu_to_le32(namelen | hash);
|
||||
strcpy(key->name, qname->name);
|
||||
strscpy(key->name, qname->name, namelen);
|
||||
|
||||
*key_p = key;
|
||||
return key_len;
|
||||
@@ -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);
|
||||
@@ -434,7 +439,7 @@ static int apfs_build_sibling_val(struct dentry *dentry,
|
||||
|
||||
val->parent_id = cpu_to_le64(apfs_ino(parent));
|
||||
val->name_len = cpu_to_le16(namelen);
|
||||
strcpy(val->name, qname->name);
|
||||
strscpy(val->name, qname->name, namelen);
|
||||
|
||||
*val_p = val;
|
||||
return val_len;
|
||||
@@ -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);
|
||||
@@ -601,7 +602,13 @@ 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);
|
||||
#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);
|
||||
return 0;
|
||||
@@ -841,7 +848,11 @@ static int __apfs_link(struct dentry *old_dentry, struct dentry *dentry)
|
||||
|
||||
/* First update the inode's link count */
|
||||
inc_nlink(inode);
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
||||
inode->i_ctime = current_time(inode);
|
||||
#else
|
||||
inode_set_ctime_current(inode);
|
||||
#endif
|
||||
apfs_inode_join_transaction(inode->i_sb, inode);
|
||||
|
||||
if (inode->i_nlink == 2) {
|
||||
@@ -915,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);
|
||||
@@ -958,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);
|
||||
@@ -1030,7 +1037,13 @@ 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);
|
||||
#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);
|
||||
return err;
|
||||
@@ -1089,7 +1102,7 @@ static int apfs_sibling_link_from_query(struct apfs_query *query,
|
||||
*name = kmalloc(namelen, GFP_KERNEL);
|
||||
if (!*name)
|
||||
return -ENOMEM;
|
||||
strcpy(*name, siblink->name);
|
||||
strscpy(*name, siblink->name, namelen);
|
||||
*parent = le64_to_cpu(siblink->parent_id);
|
||||
return 0;
|
||||
}
|
||||
@@ -1107,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;
|
||||
@@ -1149,13 +1158,13 @@ fail:
|
||||
|
||||
/**
|
||||
* apfs_orphan_name - Get the name for an orphan inode's invisible link
|
||||
* @inode: the vfs inode
|
||||
* @qname: on return, the name assigned to the link
|
||||
* @ino: the inode number
|
||||
* @qname: on return, the name assigned to the link
|
||||
*
|
||||
* Returns 0 on success; the caller must remember to free @qname->name after
|
||||
* use. Returns a negative error code in case of failure.
|
||||
*/
|
||||
static int apfs_orphan_name(struct inode *inode, struct qstr *qname)
|
||||
static int apfs_orphan_name(u64 ino, struct qstr *qname)
|
||||
{
|
||||
int max_len;
|
||||
char *name;
|
||||
@@ -1165,7 +1174,7 @@ static int apfs_orphan_name(struct inode *inode, struct qstr *qname)
|
||||
name = kmalloc(max_len, GFP_KERNEL);
|
||||
if (!name)
|
||||
return -ENOMEM;
|
||||
qname->len = snprintf(name, max_len, "0x%llx-dead", apfs_ino(inode));
|
||||
qname->len = snprintf(name, max_len, "0x%llx-dead", ino);
|
||||
qname->name = name;
|
||||
return 0;
|
||||
}
|
||||
@@ -1184,7 +1193,7 @@ static int apfs_create_orphan_link(struct inode *inode)
|
||||
struct qstr qname;
|
||||
int err = 0;
|
||||
|
||||
err = apfs_orphan_name(inode, &qname);
|
||||
err = apfs_orphan_name(apfs_ino(inode), &qname);
|
||||
if (err)
|
||||
return err;
|
||||
err = apfs_create_dentry_rec(inode, &qname, apfs_ino(priv_dir), 0 /* sibling_id */);
|
||||
@@ -1194,7 +1203,13 @@ 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);
|
||||
#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);
|
||||
|
||||
@@ -1221,7 +1236,7 @@ int apfs_delete_orphan_link(struct inode *inode)
|
||||
struct apfs_drec drec;
|
||||
int err;
|
||||
|
||||
err = apfs_orphan_name(inode, &qname);
|
||||
err = apfs_orphan_name(apfs_ino(inode), &qname);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -1239,7 +1254,13 @@ 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);
|
||||
#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);
|
||||
|
||||
@@ -1330,7 +1351,11 @@ static int __apfs_unlink(struct inode *dir, struct dentry *dentry)
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
||||
inode->i_ctime = dir->i_ctime;
|
||||
#else
|
||||
inode_set_ctime_to_ts(inode, inode_get_ctime(dir));
|
||||
#endif
|
||||
/* TODO: defer write of the primary name? */
|
||||
err = apfs_update_inode(inode, primary_name);
|
||||
if (err)
|
||||
@@ -1456,3 +1481,64 @@ out_abort:
|
||||
apfs_transaction_abort(sb);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* apfs_any_orphan_ino - Find the inode number for any orphaned regular file
|
||||
* @sb: filesytem superblock
|
||||
* @ino_p: on return, the inode number found
|
||||
*
|
||||
* Returns 0 on success, or a negative error code in case of failure, which may
|
||||
* be -ENODATA if there are no orphan files.
|
||||
*/
|
||||
u64 apfs_any_orphan_ino(struct super_block *sb, u64 *ino_p)
|
||||
{
|
||||
struct apfs_sb_info *sbi = APFS_SB(sb);
|
||||
struct apfs_query *query = NULL;
|
||||
struct apfs_drec drec = {0};
|
||||
struct qstr qname = {0};
|
||||
bool hashed = apfs_is_normalization_insensitive(sb);
|
||||
bool found = false;
|
||||
int err;
|
||||
|
||||
query = apfs_alloc_query(sbi->s_cat_root, NULL /* parent */);
|
||||
if (!query)
|
||||
return -ENOMEM;
|
||||
apfs_init_drec_key(sb, APFS_PRIV_DIR_INO_NUM, NULL /* name */, 0 /* name_len */, &query->key);
|
||||
query->flags = APFS_QUERY_CAT | APFS_QUERY_MULTIPLE | APFS_QUERY_EXACT;
|
||||
|
||||
while (!found) {
|
||||
err = apfs_btree_query(sb, &query);
|
||||
if (err) {
|
||||
if (err == -ENODATA)
|
||||
goto out;
|
||||
apfs_err(sb, "drec query failed for private dir");
|
||||
goto out;
|
||||
}
|
||||
err = apfs_drec_from_query(query, &drec, hashed);
|
||||
if (err) {
|
||||
apfs_alert(sb, "bad dentry record in private dir");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* These files are deleted immediately by ->evict_inode() */
|
||||
if (drec.type != DT_REG)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Confirm that this is an orphan file, because the official
|
||||
* reference allows other uses for the private directory.
|
||||
*/
|
||||
err = apfs_orphan_name(drec.ino, &qname);
|
||||
if (err)
|
||||
goto out;
|
||||
found = strcmp(drec.name, qname.name) == 0;
|
||||
kfree(qname.name);
|
||||
qname.name = NULL;
|
||||
}
|
||||
*ino_p = drec.ino;
|
||||
|
||||
out:
|
||||
apfs_free_query(query);
|
||||
query = NULL;
|
||||
return err;
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
PACKAGE_NAME="linux-apfs-rw"
|
||||
PACKAGE_VERSION="0.3.3"
|
||||
PACKAGE_VERSION="0.3.10"
|
||||
|
||||
BUILT_MODULE_NAME[0]="apfs"
|
||||
DEST_MODULE_LOCATION[0]="/extra"
|
||||
|
||||
AUTOINSTALL="yes"
|
||||
PRE_BUILD="genver.sh"
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include <linux/buffer_head.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/blk_types.h>
|
||||
#include "apfs.h"
|
||||
|
||||
#define MAX(X, Y) ((X) <= (Y) ? (Y) : (X))
|
||||
@@ -46,7 +47,7 @@ int apfs_extent_from_query(struct apfs_query *query,
|
||||
char *raw = query->node->object.data;
|
||||
u64 ext_len;
|
||||
|
||||
if(!apfs_is_sealed(sb)) {
|
||||
if (!apfs_is_sealed(sb)) {
|
||||
struct apfs_file_extent_val *ext = NULL;
|
||||
struct apfs_file_extent_key *ext_key = NULL;
|
||||
|
||||
@@ -138,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);
|
||||
@@ -386,7 +387,7 @@ static int apfs_shrink_extent_tail(struct apfs_query *query, struct apfs_dstream
|
||||
}
|
||||
|
||||
/**
|
||||
* apfs_query_found_extent - Is this query pointing to an extent record?
|
||||
* apfs_query_found_extent - Did this query find an extent with the right id?
|
||||
* @query: the (successful) query that found the record
|
||||
*/
|
||||
static inline bool apfs_query_found_extent(struct apfs_query *query)
|
||||
@@ -397,7 +398,12 @@ static inline bool apfs_query_found_extent(struct apfs_query *query)
|
||||
if (query->key_len < sizeof(*hdr))
|
||||
return false;
|
||||
hdr = raw + query->key_off;
|
||||
return apfs_cat_type(hdr) == APFS_TYPE_FILE_EXTENT;
|
||||
|
||||
if (apfs_cat_type(hdr) != APFS_TYPE_FILE_EXTENT)
|
||||
return false;
|
||||
if (apfs_cat_cnid(hdr) != query->key.id)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -412,7 +418,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;
|
||||
@@ -430,13 +435,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);
|
||||
@@ -601,7 +604,7 @@ static int apfs_update_mid_extent(struct apfs_dstream_info *dstream, const struc
|
||||
raw_key.logical_addr = cpu_to_le64(extent->logical_addr);
|
||||
raw_val.len_and_flags = cpu_to_le64(extent->len);
|
||||
raw_val.phys_block_num = cpu_to_le64(extent->phys_block_num);
|
||||
if(apfs_vol_is_encrypted(sb))
|
||||
if (apfs_vol_is_encrypted(sb))
|
||||
new_crypto = extent_id;
|
||||
else
|
||||
new_crypto = 0;
|
||||
@@ -613,7 +616,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);
|
||||
@@ -800,7 +803,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;
|
||||
@@ -829,8 +831,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);
|
||||
@@ -1152,7 +1153,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;
|
||||
@@ -1172,11 +1172,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);
|
||||
@@ -1259,7 +1258,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;
|
||||
@@ -1286,8 +1284,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);
|
||||
@@ -1499,18 +1496,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);
|
||||
@@ -1636,6 +1630,125 @@ int apfs_truncate(struct apfs_dstream_info *dstream, loff_t new_size)
|
||||
return apfs_create_hole(dstream, old_blks, new_blks);
|
||||
}
|
||||
|
||||
/**
|
||||
* apfs_dstream_delete_front - Deletes as many leading extents as possible
|
||||
* @sb: filesystem superblock
|
||||
* @ds_id: id for the dstream to delete
|
||||
*
|
||||
* Returns 0 on success, or a negative error code in case of failure, which may
|
||||
* be -ENODATA if there are no more extents, or -EAGAIN if the free queue is
|
||||
* getting too full.
|
||||
*/
|
||||
static int apfs_dstream_delete_front(struct super_block *sb, u64 ds_id)
|
||||
{
|
||||
struct apfs_sb_info *sbi = APFS_SB(sb);
|
||||
struct apfs_spaceman *sm = APFS_SM(sb);
|
||||
struct apfs_spaceman_phys *sm_raw = sm->sm_raw;
|
||||
struct apfs_spaceman_free_queue *fq = NULL;
|
||||
struct apfs_query *query = NULL;
|
||||
struct apfs_file_extent head;
|
||||
bool first_match = true;
|
||||
int ret;
|
||||
|
||||
fq = &sm_raw->sm_fq[APFS_SFQ_MAIN];
|
||||
if (le64_to_cpu(fq->sfq_count) > APFS_TRANS_MAIN_QUEUE_MAX)
|
||||
return -EAGAIN;
|
||||
|
||||
query = apfs_alloc_query(sbi->s_cat_root, NULL /* parent */);
|
||||
if (!query)
|
||||
return -ENOMEM;
|
||||
apfs_init_file_extent_key(ds_id, 0, &query->key);
|
||||
query->flags = APFS_QUERY_CAT;
|
||||
|
||||
next_extent:
|
||||
ret = apfs_btree_query(sb, &query);
|
||||
if (ret && ret != -ENODATA) {
|
||||
apfs_err(sb, "query failed for first extent of id 0x%llx", ds_id);
|
||||
goto out;
|
||||
}
|
||||
apfs_query_direct_forward(query);
|
||||
if (!apfs_query_found_extent(query)) {
|
||||
/*
|
||||
* After the original lookup, the query may not be set to the
|
||||
* first extent, but instead to the record that comes right
|
||||
* before.
|
||||
*/
|
||||
if (first_match) {
|
||||
first_match = false;
|
||||
goto next_extent;
|
||||
}
|
||||
ret = -ENODATA;
|
||||
goto out;
|
||||
}
|
||||
first_match = false;
|
||||
|
||||
ret = apfs_extent_from_query(query, &head);
|
||||
if (ret) {
|
||||
apfs_err(sb, "bad head extent record on dstream 0x%llx", ds_id);
|
||||
goto out;
|
||||
}
|
||||
ret = apfs_btree_remove(query);
|
||||
if (ret) {
|
||||
apfs_err(sb, "removal failed for id 0x%llx, addr 0x%llx", ds_id, head.logical_addr);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* The official fsck doesn't complain about wrong sparse byte counts
|
||||
* for orphans, so I guess we don't need to update them here
|
||||
*/
|
||||
if (!apfs_ext_is_hole(&head)) {
|
||||
ret = apfs_range_put_reference(sb, head.phys_block_num, head.len);
|
||||
if (ret) {
|
||||
apfs_err(sb, "failed to put range 0x%llx-0x%llx", head.phys_block_num, head.len);
|
||||
goto out;
|
||||
}
|
||||
ret = apfs_crypto_adj_refcnt(sb, head.crypto_id, -1);
|
||||
if (ret) {
|
||||
apfs_err(sb, "failed to take crypto id 0x%llx", head.crypto_id);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (le64_to_cpu(fq->sfq_count) <= APFS_TRANS_MAIN_QUEUE_MAX)
|
||||
goto next_extent;
|
||||
ret = -EAGAIN;
|
||||
out:
|
||||
apfs_free_query(query);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* apfs_inode_delete_front - Deletes as many leading extents as possible
|
||||
* @inode: inode to delete
|
||||
*
|
||||
* Tries to delete all extents for @inode, in which case it returns 0. If the
|
||||
* free queue is getting too full, deletes as much as is reasonable and returns
|
||||
* -EAGAIN. May return other negative error codes as well.
|
||||
*/
|
||||
int apfs_inode_delete_front(struct inode *inode)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct apfs_dstream_info *dstream = NULL;
|
||||
struct apfs_inode_info *ai = APFS_I(inode);
|
||||
int ret;
|
||||
|
||||
if (!ai->i_has_dstream)
|
||||
return 0;
|
||||
|
||||
dstream = &ai->i_dstream;
|
||||
ret = apfs_flush_extent_cache(dstream);
|
||||
if (ret) {
|
||||
apfs_err(sb, "extent cache flush failed for dstream 0x%llx", dstream->ds_id);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = apfs_dstream_delete_front(sb, dstream->ds_id);
|
||||
if (ret == -ENODATA)
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
|
||||
loff_t apfs_remap_file_range(struct file *src_file, loff_t off, struct file *dst_file, loff_t destoff, loff_t len, unsigned int remap_flags)
|
||||
#else
|
||||
@@ -1703,7 +1816,13 @@ int apfs_clone_file_range(struct file *src_file, loff_t off, struct file *dst_fi
|
||||
}
|
||||
src_ds->ds_shared = true;
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
||||
dst_inode->i_mtime = dst_inode->i_ctime = current_time(dst_inode);
|
||||
#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 7, 0)
|
||||
dst_inode->i_mtime = inode_set_ctime_current(dst_inode);
|
||||
#else
|
||||
inode_set_mtime_to_ts(dst_inode, inode_set_ctime_current(dst_inode));
|
||||
#endif
|
||||
dst_inode->i_size = src_inode->i_size;
|
||||
dst_ai->i_key_class = src_ai->i_key_class;
|
||||
dst_ai->i_int_flags = src_ai->i_int_flags;
|
||||
@@ -1755,18 +1874,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);
|
||||
@@ -1830,7 +1946,7 @@ restart:
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
query->key = &key;
|
||||
query->key = key;
|
||||
query->flags = APFS_QUERY_EXTENTREF;
|
||||
|
||||
ret = apfs_btree_query(sb, &query);
|
||||
@@ -1970,7 +2086,7 @@ restart:
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
query->key = &key;
|
||||
query->key = key;
|
||||
query->flags = APFS_QUERY_EXTENTREF;
|
||||
|
||||
ret = apfs_btree_query(sb, &query);
|
||||
@@ -2130,3 +2246,147 @@ int apfs_clone_extents(struct apfs_dstream_info *dstream, u64 new_id)
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* apfs_nonsparse_dstream_read - Read from a dstream without holes
|
||||
* @dstream: dstream to read
|
||||
* @buf: destination buffer
|
||||
* @count: exact number of bytes to read
|
||||
* @offset: dstream offset to read from
|
||||
*
|
||||
* Returns 0 on success or a negative error code in case of failure.
|
||||
*/
|
||||
int apfs_nonsparse_dstream_read(struct apfs_dstream_info *dstream, void *buf, size_t count, u64 offset)
|
||||
{
|
||||
struct super_block *sb = dstream->ds_sb;
|
||||
u64 logical_start_block, logical_end_block, log_bno, blkcnt, idx;
|
||||
struct buffer_head **bhs = NULL;
|
||||
int ret = 0;
|
||||
|
||||
/* Save myself from thinking about overflow here */
|
||||
if (count >= APFS_MAX_FILE_SIZE || offset >= APFS_MAX_FILE_SIZE) {
|
||||
apfs_err(sb, "dstream read overflow (0x%llx-0x%llx)", offset, (unsigned long long)count);
|
||||
return -EFBIG;
|
||||
}
|
||||
|
||||
if (offset + count > dstream->ds_size) {
|
||||
apfs_err(sb, "reading past the end (0x%llx-0x%llx)", offset, (unsigned long long)count);
|
||||
/* No caller is expected to legitimately read out-of-bounds */
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
|
||||
logical_start_block = offset >> sb->s_blocksize_bits;
|
||||
logical_end_block = (offset + count + sb->s_blocksize - 1) >> sb->s_blocksize_bits;
|
||||
blkcnt = logical_end_block - logical_start_block;
|
||||
bhs = kcalloc(blkcnt, sizeof(*bhs), GFP_KERNEL);
|
||||
if (!bhs)
|
||||
return -ENOMEM;
|
||||
|
||||
for (log_bno = logical_start_block; log_bno < logical_end_block; log_bno++) {
|
||||
struct buffer_head *bh = NULL;
|
||||
u64 bno = 0;
|
||||
|
||||
idx = log_bno - logical_start_block;
|
||||
|
||||
ret = apfs_logic_to_phys_bno(dstream, log_bno, &bno);
|
||||
if (ret)
|
||||
goto out;
|
||||
if (bno == 0) {
|
||||
apfs_err(sb, "nonsparse dstream has a hole");
|
||||
ret = -EFSCORRUPTED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
#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;
|
||||
goto out;
|
||||
}
|
||||
|
||||
bh = bhs[idx];
|
||||
if (!buffer_uptodate(bh)) {
|
||||
get_bh(bh);
|
||||
lock_buffer(bh);
|
||||
bh->b_end_io = end_buffer_read_sync;
|
||||
apfs_submit_bh(REQ_OP_READ, 0, bh);
|
||||
}
|
||||
}
|
||||
|
||||
for (log_bno = logical_start_block; log_bno < logical_end_block; log_bno++) {
|
||||
int off_in_block, left_in_block;
|
||||
|
||||
idx = log_bno - logical_start_block;
|
||||
wait_on_buffer(bhs[idx]);
|
||||
if (!buffer_uptodate(bhs[idx])) {
|
||||
apfs_err(sb, "failed to read a block");
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (log_bno == logical_start_block)
|
||||
off_in_block = offset & (sb->s_blocksize - 1);
|
||||
else
|
||||
off_in_block = 0;
|
||||
|
||||
if (log_bno == logical_end_block - 1)
|
||||
left_in_block = count + offset - (log_bno << sb->s_blocksize_bits) - off_in_block;
|
||||
else
|
||||
left_in_block = sb->s_blocksize - off_in_block;
|
||||
|
||||
memcpy(buf, bhs[idx]->b_data + off_in_block, left_in_block);
|
||||
buf += left_in_block;
|
||||
}
|
||||
|
||||
out:
|
||||
if (bhs) {
|
||||
for (idx = 0; idx < blkcnt; idx++)
|
||||
brelse(bhs[idx]);
|
||||
kfree(bhs);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* apfs_nonsparse_dstream_preread - Attempt to preread a dstream without holes
|
||||
* @dstream: dstream to preread
|
||||
*
|
||||
* Requests reads for all blocks of a dstream, but doesn't wait for the result.
|
||||
*/
|
||||
void apfs_nonsparse_dstream_preread(struct apfs_dstream_info *dstream)
|
||||
{
|
||||
struct super_block *sb = dstream->ds_sb;
|
||||
u64 logical_end_block, log_bno;
|
||||
|
||||
logical_end_block = (dstream->ds_size + sb->s_blocksize - 1) >> sb->s_blocksize_bits;
|
||||
|
||||
for (log_bno = 0; log_bno < logical_end_block; log_bno++) {
|
||||
struct buffer_head *bh = NULL;
|
||||
u64 bno = 0;
|
||||
int ret;
|
||||
|
||||
ret = apfs_logic_to_phys_bno(dstream, log_bno, &bno);
|
||||
if (ret || bno == 0)
|
||||
return;
|
||||
|
||||
#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)) {
|
||||
get_bh(bh);
|
||||
lock_buffer(bh);
|
||||
bh->b_end_io = end_buffer_read_sync;
|
||||
apfs_submit_bh(REQ_OP_READ, 0, bh);
|
||||
}
|
||||
brelse(bh);
|
||||
bh = NULL;
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
@@ -99,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;
|
||||
}
|
||||
@@ -110,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;
|
||||
|
||||
@@ -137,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,
|
||||
@@ -146,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
|
||||
|
||||
@@ -155,6 +185,13 @@ const struct file_operations apfs_file_operations = {
|
||||
#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
|
||||
@@ -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);
|
||||
|
||||
+79
-25
@@ -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);
|
||||
@@ -116,7 +113,7 @@ static int apfs_create_snap_metadata_rec(struct inode *mntpoint, struct apfs_nod
|
||||
raw_val->extentref_tree_type = vsb_raw->apfs_extentref_tree_type;
|
||||
raw_val->flags = 0;
|
||||
raw_val->name_len = cpu_to_le16(name_len + 1); /* Count the null byte */
|
||||
strcpy(raw_val->name, name);
|
||||
strscpy(raw_val->name, name, name_len + 1);
|
||||
|
||||
apfs_assert_in_transaction(sb, &vsb_raw->apfs_o);
|
||||
raw_val->inum = vsb_raw->apfs_next_obj_id;
|
||||
@@ -137,27 +134,24 @@ 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);
|
||||
if (err == 0) {
|
||||
/* TODO: avoid transaction abort here */
|
||||
apfs_info(sb, "a snapshot with that name already exists");
|
||||
err = -EEXIST;
|
||||
/* This should never happen here, the caller has checked */
|
||||
apfs_alert(sb, "snapshot name record already exists");
|
||||
err = -EFSCORRUPTED;
|
||||
goto fail;
|
||||
}
|
||||
if (err != -ENODATA) {
|
||||
@@ -173,7 +167,7 @@ static int apfs_create_snap_name_rec(struct apfs_node *snap_root, const char *na
|
||||
}
|
||||
apfs_key_set_hdr(APFS_TYPE_SNAP_NAME, APFS_SNAP_NAME_OBJ_ID, raw_key);
|
||||
raw_key->name_len = cpu_to_le16(name_len + 1); /* Count the null byte */
|
||||
strcpy(raw_key->name, name);
|
||||
strscpy(raw_key->name, name, name_len + 1);
|
||||
|
||||
raw_val.snap_xid = cpu_to_le64(APFS_NXI(sb)->nx_xid);
|
||||
|
||||
@@ -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);
|
||||
@@ -341,6 +332,56 @@ static int apfs_update_omap_snapshots(struct super_block *sb)
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* apfs_snapshot_name_check - Check if a snapshot with the given name exists
|
||||
* @sb: filesystem superblock
|
||||
* @name: name to check
|
||||
* @name_len: length of @name
|
||||
* @eexist: on return, whether the name exists or not
|
||||
*
|
||||
* Returns 0 on success, or a negative error code in case of failure.
|
||||
*/
|
||||
static int apfs_snapshot_name_check(struct super_block *sb, const char *name, int name_len, bool *eexist)
|
||||
{
|
||||
struct apfs_sb_info *sbi = APFS_SB(sb);
|
||||
struct apfs_superblock *vsb_raw = sbi->s_vsb_raw;
|
||||
struct apfs_node *snap_root = NULL;
|
||||
struct apfs_query *query = NULL;
|
||||
int err;
|
||||
|
||||
snap_root = apfs_read_node(sb, le64_to_cpu(vsb_raw->apfs_snap_meta_tree_oid), APFS_OBJ_PHYSICAL, false /* write */);
|
||||
if (IS_ERR(snap_root)) {
|
||||
apfs_err(sb, "failed to read snap meta root 0x%llx", le64_to_cpu(vsb_raw->apfs_snap_meta_tree_oid));
|
||||
return PTR_ERR(snap_root);
|
||||
}
|
||||
vsb_raw = NULL;
|
||||
|
||||
query = apfs_alloc_query(snap_root, NULL /* parent */);
|
||||
if (!query) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
apfs_init_snap_name_key(name, &query->key);
|
||||
query->flags |= APFS_QUERY_SNAP_META | APFS_QUERY_EXACT;
|
||||
|
||||
err = apfs_btree_query(sb, &query);
|
||||
if (err == 0) {
|
||||
*eexist = true;
|
||||
} else if (err == -ENODATA) {
|
||||
*eexist = false;
|
||||
err = 0;
|
||||
} else {
|
||||
apfs_err(sb, "query failed (%s)", name);
|
||||
}
|
||||
|
||||
out:
|
||||
apfs_free_query(query);
|
||||
query = NULL;
|
||||
apfs_node_free(snap_root);
|
||||
snap_root = NULL;
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* apfs_do_ioc_takesnapshot - Actual work for apfs_ioc_take_snapshot()
|
||||
* @mntpoint: inode of the mount point to snapshot
|
||||
@@ -357,12 +398,31 @@ static int apfs_do_ioc_take_snapshot(struct inode *mntpoint, const char *name, i
|
||||
/* TODO: remember to update the maxops in the future */
|
||||
struct apfs_max_ops maxops = {0};
|
||||
u64 sblock_oid;
|
||||
bool eexist = false;
|
||||
int err;
|
||||
|
||||
err = apfs_transaction_start(sb, maxops);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* This check can fail in normal operation, so run it before making any
|
||||
* changes and exit the transaction cleanly if necessary. The name
|
||||
* lookup will have to be repeated later, but it's ok because I don't
|
||||
* expect snapshot creation to be a bottleneck for anyone.
|
||||
*/
|
||||
err = apfs_snapshot_name_check(sb, name, name_len, &eexist);
|
||||
if (err) {
|
||||
apfs_err(sb, "snapshot name existence check failed");
|
||||
goto fail;
|
||||
}
|
||||
if (eexist) {
|
||||
err = apfs_transaction_commit(sb);
|
||||
if (err)
|
||||
goto fail;
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
/*
|
||||
* Flush the extent caches to the extenref tree before it gets moved to
|
||||
* the snapshot. It seems safer in general to avoid big unpredictable
|
||||
@@ -499,15 +559,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 +602,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);
|
||||
|
||||
+294
-124
File diff suppressed because it is too large.
Load diff
+509
-500
File diff suppressed because it is too large.
Load diff
@@ -112,9 +112,7 @@ static int apfs_xattr_extents_read(struct inode *parent,
|
||||
{
|
||||
struct super_block *sb = parent->i_sb;
|
||||
struct apfs_dstream_info *dstream;
|
||||
int length, blkcnt, i;
|
||||
struct buffer_head **bhs = NULL;
|
||||
int ret;
|
||||
int length, ret;
|
||||
|
||||
dstream = kzalloc(sizeof(*dstream), GFP_KERNEL);
|
||||
if (!dstream)
|
||||
@@ -146,59 +144,11 @@ static int apfs_xattr_extents_read(struct inode *parent,
|
||||
length = size;
|
||||
}
|
||||
|
||||
blkcnt = (length + sb->s_blocksize - 1) >> sb->s_blocksize_bits;
|
||||
bhs = kcalloc(blkcnt, sizeof(*bhs), GFP_KERNEL);
|
||||
for (i = 0; i < blkcnt; i++) {
|
||||
struct buffer_head *bh = NULL;
|
||||
u64 bno = 0;
|
||||
|
||||
ret = apfs_logic_to_phys_bno(dstream, i, &bno);
|
||||
if (ret)
|
||||
goto out;
|
||||
if (bno == 0) {
|
||||
/* No holes in xattr dstreams, I believe */
|
||||
apfs_err(sb, "xattr dstream has a hole");
|
||||
ret = -EFSCORRUPTED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
bhs[i] = __getblk_gfp(APFS_NXI(sb)->nx_bdev, bno, sb->s_blocksize, __GFP_MOVABLE);
|
||||
if (!bhs[i]) {
|
||||
apfs_err(sb, "failed to map block 0x%llx", bno);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
bh = bhs[i];
|
||||
if (!buffer_uptodate(bh)) {
|
||||
get_bh(bh);
|
||||
lock_buffer(bh);
|
||||
bh->b_end_io = end_buffer_read_sync;
|
||||
apfs_submit_bh(REQ_OP_READ, 0, bh);
|
||||
}
|
||||
}
|
||||
for (i = 0; i < blkcnt; i++) {
|
||||
int off, tocopy;
|
||||
|
||||
wait_on_buffer(bhs[i]);
|
||||
if (!buffer_uptodate(bhs[i])) {
|
||||
apfs_err(sb, "failed to read a block");
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
off = i << sb->s_blocksize_bits;
|
||||
tocopy = min(sb->s_blocksize, (unsigned long)(length - off));
|
||||
memcpy(buffer + off, bhs[i]->b_data, tocopy);
|
||||
}
|
||||
ret = length;
|
||||
ret = apfs_nonsparse_dstream_read(dstream, buffer, length, 0 /* offset */);
|
||||
if (ret == 0)
|
||||
ret = length;
|
||||
|
||||
out:
|
||||
if (bhs) {
|
||||
for (i = 0; i < blkcnt; i++)
|
||||
brelse(bhs[i]);
|
||||
kfree(bhs);
|
||||
}
|
||||
kfree(dstream);
|
||||
return ret;
|
||||
}
|
||||
@@ -233,6 +183,123 @@ static int apfs_xattr_inline_read(struct apfs_xattr *xattr, void *buffer, size_t
|
||||
return length;
|
||||
}
|
||||
|
||||
/**
|
||||
* apfs_xattr_get_compressed_data - Get the compressed data in a named attribute
|
||||
* @inode: inode the attribute belongs to
|
||||
* @name: name of the attribute
|
||||
* @cdata: compressed data struct to set on return
|
||||
*
|
||||
* Returns 0 on success or a negative error code in case of failure.
|
||||
*/
|
||||
int apfs_xattr_get_compressed_data(struct inode *inode, const char *name, struct apfs_compressed_data *cdata)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct apfs_sb_info *sbi = APFS_SB(sb);
|
||||
struct apfs_query *query;
|
||||
struct apfs_xattr xattr;
|
||||
u64 cnid = apfs_ino(inode);
|
||||
int ret;
|
||||
|
||||
|
||||
query = apfs_alloc_query(sbi->s_cat_root, NULL /* parent */);
|
||||
if (!query)
|
||||
return -ENOMEM;
|
||||
apfs_init_xattr_key(cnid, name, &query->key);
|
||||
query->flags |= APFS_QUERY_CAT | APFS_QUERY_EXACT;
|
||||
|
||||
ret = apfs_btree_query(sb, &query);
|
||||
if (ret) {
|
||||
apfs_err(sb, "query failed for id 0x%llx (%s)", cnid, name);
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = apfs_xattr_from_query(query, &xattr);
|
||||
if (ret) {
|
||||
apfs_err(sb, "bad xattr record in inode 0x%llx", cnid);
|
||||
goto done;
|
||||
}
|
||||
|
||||
cdata->has_dstream = xattr.has_dstream;
|
||||
if (cdata->has_dstream) {
|
||||
struct apfs_dstream_info *dstream = NULL;
|
||||
|
||||
dstream = kzalloc(sizeof(*dstream), GFP_KERNEL);
|
||||
if (!dstream) {
|
||||
ret = -ENOMEM;
|
||||
goto done;
|
||||
}
|
||||
apfs_dstream_from_xattr(sb, &xattr, dstream);
|
||||
|
||||
cdata->dstream = dstream;
|
||||
cdata->size = dstream->ds_size;
|
||||
} else {
|
||||
void *data = NULL;
|
||||
int len;
|
||||
|
||||
len = xattr.xdata_len;
|
||||
if (len > APFS_XATTR_MAX_EMBEDDED_SIZE) {
|
||||
apfs_err(sb, "inline xattr too big");
|
||||
ret = -EFSCORRUPTED;
|
||||
goto done;
|
||||
}
|
||||
data = kzalloc(len, GFP_KERNEL);
|
||||
if (!data) {
|
||||
ret = -ENOMEM;
|
||||
goto done;
|
||||
}
|
||||
memcpy(data, xattr.xdata, len);
|
||||
|
||||
cdata->data = data;
|
||||
cdata->size = len;
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
done:
|
||||
apfs_free_query(query);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* apfs_release_compressed_data - Clean up a compressed data struct
|
||||
* @cdata: struct to clean up (but not free)
|
||||
*/
|
||||
void apfs_release_compressed_data(struct apfs_compressed_data *cdata)
|
||||
{
|
||||
if (!cdata)
|
||||
return;
|
||||
|
||||
if (cdata->has_dstream) {
|
||||
kfree(cdata->dstream);
|
||||
cdata->dstream = NULL;
|
||||
} else {
|
||||
kfree(cdata->data);
|
||||
cdata->data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* apfs_compressed_data_read - Read from a compressed data struct
|
||||
* @cdata: compressed data struct
|
||||
* @buf: destination buffer
|
||||
* @count: exact number of bytes to read
|
||||
* @offset: dstream offset to read from
|
||||
*
|
||||
* Returns 0 on success or a negative error code in case of failure.
|
||||
*/
|
||||
int apfs_compressed_data_read(struct apfs_compressed_data *cdata, void *buf, size_t count, u64 offset)
|
||||
{
|
||||
if (cdata->has_dstream)
|
||||
return apfs_nonsparse_dstream_read(cdata->dstream, buf, count, offset);
|
||||
|
||||
if (offset > cdata->size || count > cdata->size - offset) {
|
||||
apfs_err(NULL, "reading past the end (0x%llx-0x%llx)", offset, (unsigned long long)count);
|
||||
/* No caller is expected to legitimately read out-of-bounds */
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
memcpy(buf, cdata->data + offset, count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* __apfs_xattr_get - Find and read a named attribute
|
||||
* @inode: inode the attribute belongs to
|
||||
@@ -261,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);
|
||||
@@ -392,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);
|
||||
@@ -465,7 +526,7 @@ static int apfs_build_xattr_key(const char *name, u64 ino, struct apfs_xattr_key
|
||||
|
||||
apfs_key_set_hdr(APFS_TYPE_XATTR, ino, key);
|
||||
key->name_len = cpu_to_le16(namelen);
|
||||
strcpy(key->name, name);
|
||||
strscpy(key->name, name, namelen);
|
||||
|
||||
*key_p = key;
|
||||
return key_len;
|
||||
@@ -646,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;
|
||||
@@ -664,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);
|
||||
@@ -805,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;
|
||||
@@ -820,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