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
|
||||
+1
-2
@@ -22,8 +22,7 @@ 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.
|
||||
|
||||
Many features are not yet implemented, including encryption and most forms of
|
||||
compression.
|
||||
Encryption is not yet implemented even in read-only mode.
|
||||
|
||||
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 */
|
||||
|
||||
@@ -124,7 +122,7 @@ 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_blocks_per_chunk; /* Blocks covered by a bitmap block */
|
||||
u32 sm_chunks_per_cib; /* Chunk count in a chunk-info block */
|
||||
@@ -133,6 +131,22 @@ struct apfs_spaceman {
|
||||
u32 sm_cib_count; /* Number of chunk-info blocks */
|
||||
u64 sm_free_count; /* Number of free blocks */
|
||||
u32 sm_addr_offset; /* Offset of cib addresses in @sm_raw */
|
||||
|
||||
/*
|
||||
* A range of freed blocks not yet put in the free queue. Extend this as
|
||||
* much as possible before creating an actual record.
|
||||
*/
|
||||
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
|
||||
@@ -212,7 +226,7 @@ struct apfs_nxsb_info {
|
||||
unsigned long nx_blocksize;
|
||||
unsigned char nx_blocksize_bits;
|
||||
|
||||
struct apfs_spaceman nx_spaceman;
|
||||
struct apfs_spaceman *nx_spaceman;
|
||||
struct apfs_nx_transaction nx_transaction;
|
||||
|
||||
/* For now, a single semaphore for every operation */
|
||||
@@ -276,6 +290,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 */
|
||||
|
||||
@@ -292,6 +308,7 @@ struct apfs_sb_info {
|
||||
struct apfs_crypto_state_val *s_dflt_pfk; /* default per-file key */
|
||||
|
||||
struct apfs_vol_transaction s_transaction;
|
||||
int s_trans_buffers_max;
|
||||
|
||||
struct inode *s_private_dir; /* Inode for the private directory */
|
||||
};
|
||||
@@ -312,8 +329,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;
|
||||
}
|
||||
|
||||
@@ -332,7 +351,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)
|
||||
@@ -490,7 +509,7 @@ static inline void apfs_init_sibling_link_key(u64 ino, u64 id,
|
||||
{
|
||||
key->id = ino;
|
||||
key->type = APFS_TYPE_SIBLING_LINK;
|
||||
key->number = id; /* Only guessing */
|
||||
key->number = id; /* Only guessing (TODO) */
|
||||
key->name = NULL;
|
||||
}
|
||||
|
||||
@@ -604,7 +623,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;
|
||||
@@ -639,7 +658,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;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -660,6 +681,7 @@ struct apfs_phys_extent {
|
||||
u64 blkcount;
|
||||
u64 len; /* In bytes */
|
||||
u32 refcnt;
|
||||
u8 kind;
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -667,12 +689,14 @@ struct apfs_phys_extent {
|
||||
*/
|
||||
struct apfs_dstream_info {
|
||||
struct super_block *ds_sb; /* Filesystem superblock */
|
||||
struct inode *ds_inode; /* NULL for xattr dstreams */
|
||||
u64 ds_id; /* ID of the extent records */
|
||||
u64 ds_size; /* Length of the stream */
|
||||
u64 ds_sparse_bytes;/* Hole byte count in stream */
|
||||
struct apfs_file_extent ds_cached_ext; /* Latest extent record */
|
||||
bool ds_ext_dirty; /* Is ds_cached_ext dirty? */
|
||||
spinlock_t ds_ext_lock; /* Protects ds_cached_ext */
|
||||
bool ds_shared; /* Has multiple references? */
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -764,17 +788,31 @@ struct apfs_xattr {
|
||||
bool has_dstream;
|
||||
};
|
||||
|
||||
#define apfs_emerg(sb, fmt, ...) apfs_msg(sb, KERN_EMERG, fmt, ##__VA_ARGS__)
|
||||
#define apfs_alert(sb, fmt, ...) apfs_msg(sb, KERN_ALERT, fmt, ##__VA_ARGS__)
|
||||
#define apfs_crit(sb, fmt, ...) apfs_msg(sb, KERN_CRIT, fmt, ##__VA_ARGS__)
|
||||
#define apfs_err(sb, fmt, ...) apfs_msg(sb, KERN_ERR, fmt, ##__VA_ARGS__)
|
||||
#define apfs_warn(sb, fmt, ...) apfs_msg(sb, KERN_WARNING, fmt, ##__VA_ARGS__)
|
||||
#define apfs_notice(sb, fmt, ...) apfs_msg(sb, KERN_NOTICE, fmt, ##__VA_ARGS__)
|
||||
#define apfs_info(sb, fmt, ...) apfs_msg(sb, KERN_INFO, fmt, ##__VA_ARGS__)
|
||||
struct apfs_compressed_data {
|
||||
bool has_dstream;
|
||||
u64 size;
|
||||
union {
|
||||
struct apfs_dstream_info *dstream;
|
||||
void *data;
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
* Report function name and line number for the message types that are likely
|
||||
* to signal a bug, to make things easier for reporters. Don't do this for the
|
||||
* common messages, there is no point and it makes the console look too busy.
|
||||
*/
|
||||
#define apfs_emerg(sb, fmt, ...) apfs_msg(sb, KERN_EMERG, __func__, __LINE__, fmt, ##__VA_ARGS__)
|
||||
#define apfs_alert(sb, fmt, ...) apfs_msg(sb, KERN_ALERT, __func__, __LINE__, fmt, ##__VA_ARGS__)
|
||||
#define apfs_crit(sb, fmt, ...) apfs_msg(sb, KERN_CRIT, __func__, __LINE__, fmt, ##__VA_ARGS__)
|
||||
#define apfs_err(sb, fmt, ...) apfs_msg(sb, KERN_ERR, __func__, __LINE__, fmt, ##__VA_ARGS__)
|
||||
#define apfs_warn(sb, fmt, ...) apfs_msg(sb, KERN_WARNING, NULL, 0, fmt, ##__VA_ARGS__)
|
||||
#define apfs_notice(sb, fmt, ...) apfs_msg(sb, KERN_NOTICE, NULL, 0, fmt, ##__VA_ARGS__)
|
||||
#define apfs_info(sb, fmt, ...) apfs_msg(sb, KERN_INFO, NULL, 0, fmt, ##__VA_ARGS__)
|
||||
|
||||
#ifdef CONFIG_APFS_DEBUG
|
||||
#define ASSERT(expr) BUG_ON(!(expr))
|
||||
#define apfs_debug(sb, fmt, ...) apfs_msg(sb, KERN_DEBUG, fmt, ##__VA_ARGS__)
|
||||
#define ASSERT(expr) WARN_ON(!(expr))
|
||||
#define apfs_debug(sb, fmt, ...) apfs_msg(sb, KERN_DEBUG, __func__, __LINE__, fmt, ##__VA_ARGS__)
|
||||
#else
|
||||
#define ASSERT(expr) ((void)0)
|
||||
#define apfs_debug(sb, fmt, ...) no_printk(fmt, ##__VA_ARGS__)
|
||||
@@ -793,16 +831,17 @@ do { \
|
||||
} while (0)
|
||||
|
||||
/* btree.c */
|
||||
extern struct apfs_node *apfs_query_root(const struct apfs_query *query);
|
||||
extern struct apfs_query *apfs_alloc_query(struct apfs_node *node,
|
||||
struct apfs_query *parent);
|
||||
extern void apfs_free_query(struct apfs_query *query);
|
||||
extern int apfs_btree_query(struct super_block *sb, struct apfs_query **query);
|
||||
extern struct apfs_node *apfs_omap_read_node(struct super_block *sb, u64 id);
|
||||
extern int apfs_omap_lookup_block(struct super_block *sb, struct apfs_omap *omap,
|
||||
u64 id, u64 *block, bool write);
|
||||
extern int apfs_omap_lookup_block(struct super_block *sb, struct apfs_omap *omap, u64 id, u64 *block, bool write);
|
||||
extern int apfs_omap_lookup_newest_block(struct super_block *sb, struct apfs_omap *omap, u64 id, u64 *block, bool write);
|
||||
extern int apfs_create_omap_rec(struct super_block *sb, u64 oid, u64 bno);
|
||||
extern int apfs_delete_omap_rec(struct super_block *sb, u64 oid);
|
||||
extern int apfs_query_join_transaction(struct apfs_query *query);
|
||||
extern int __apfs_btree_insert(struct apfs_query *query, void *key, int key_len, void *val, int val_len);
|
||||
extern int apfs_btree_insert(struct apfs_query *query, void *key, int key_len,
|
||||
void *val, int val_len);
|
||||
extern int apfs_btree_remove(struct apfs_query *query);
|
||||
@@ -828,7 +867,7 @@ extern int apfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
unsigned int flags);
|
||||
extern int apfs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
|
||||
bool excl);
|
||||
#else
|
||||
#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 3, 0)
|
||||
extern int apfs_mknod(struct user_namespace *mnt_userns, struct inode *dir,
|
||||
struct dentry *dentry, umode_t mode, dev_t rdev);
|
||||
extern int apfs_mkdir(struct user_namespace *mnt_userns, struct inode *dir,
|
||||
@@ -838,6 +877,16 @@ extern int apfs_rename(struct user_namespace *mnt_userns, struct inode *old_dir,
|
||||
struct dentry *new_dentry, unsigned int flags);
|
||||
extern int apfs_create(struct user_namespace *mnt_userns, struct inode *dir,
|
||||
struct dentry *dentry, umode_t mode, bool excl);
|
||||
#else
|
||||
extern int apfs_mknod(struct mnt_idmap *idmap, struct inode *dir,
|
||||
struct dentry *dentry, umode_t mode, dev_t rdev);
|
||||
extern int apfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
|
||||
struct dentry *dentry, umode_t mode);
|
||||
extern int apfs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
|
||||
struct dentry *old_dentry, struct inode *new_dir,
|
||||
struct dentry *new_dentry, unsigned int flags);
|
||||
extern int apfs_create(struct mnt_idmap *idmap, struct inode *dir,
|
||||
struct dentry *dentry, umode_t mode, bool excl);
|
||||
#endif
|
||||
|
||||
extern int apfs_link(struct dentry *old_dentry, struct inode *dir,
|
||||
@@ -861,8 +910,17 @@ 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);
|
||||
#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 */
|
||||
@@ -874,16 +932,27 @@ extern struct inode *apfs_new_inode(struct inode *dir, umode_t mode,
|
||||
dev_t rdev);
|
||||
extern int apfs_create_inode_rec(struct super_block *sb, struct inode *inode,
|
||||
struct dentry *dentry);
|
||||
extern int apfs_inode_create_exclusive_dstream(struct inode *inode);
|
||||
extern int APFS_CREATE_INODE_REC_MAXOPS(void);
|
||||
extern int __apfs_write_begin(struct file *file, struct address_space *mapping, loff_t pos, unsigned int len, unsigned int flags, struct page **pagep, void **fsdata);
|
||||
extern int __apfs_write_end(struct file *file, struct address_space *mapping, loff_t pos, unsigned int len, unsigned int copied, struct page *page, void *fsdata);
|
||||
extern int apfs_dstream_adj_refcnt(struct apfs_dstream_info *dstream, u32 delta);
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 12, 0)
|
||||
extern int apfs_setattr(struct dentry *dentry, struct iattr *iattr);
|
||||
#else
|
||||
#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 3, 0)
|
||||
extern int apfs_setattr(struct user_namespace *mnt_userns,
|
||||
struct dentry *dentry, struct iattr *iattr);
|
||||
#else
|
||||
extern int apfs_setattr(struct mnt_idmap *idmap,
|
||||
struct dentry *dentry, struct iattr *iattr);
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
||||
extern int apfs_update_time(struct inode *inode, struct timespec64 *time, int flags);
|
||||
#else
|
||||
extern int apfs_update_time(struct inode *inode, int flags);
|
||||
#endif
|
||||
long apfs_dir_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
|
||||
long apfs_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
|
||||
|
||||
@@ -893,16 +962,23 @@ extern int apfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
|
||||
#elif LINUX_VERSION_CODE < KERNEL_VERSION(5, 12, 0)
|
||||
extern int apfs_getattr(const struct path *path, struct kstat *stat,
|
||||
u32 request_mask, unsigned int query_flags);
|
||||
#else
|
||||
#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 3, 0)
|
||||
extern int apfs_getattr(struct user_namespace *mnt_userns,
|
||||
const struct path *path, struct kstat *stat, u32 request_mask,
|
||||
unsigned int query_flags);
|
||||
#else
|
||||
extern int apfs_getattr(struct mnt_idmap *idmap,
|
||||
const struct path *path, struct kstat *stat, u32 request_mask,
|
||||
unsigned int query_flags);
|
||||
#endif
|
||||
|
||||
extern int apfs_crypto_adj_refcnt(struct super_block *sb, u64 crypto_id, int delta);
|
||||
extern int APFS_CRYPTO_ADJ_REFCNT_MAXOPS(void);
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0)
|
||||
extern int apfs_fileattr_get(struct dentry *dentry, struct fileattr *fa);
|
||||
extern int apfs_fileattr_set(struct mnt_idmap *idmap, struct dentry *dentry, struct fileattr *fa);
|
||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0)
|
||||
extern int apfs_fileattr_get(struct dentry *dentry, struct fileattr *fa);
|
||||
extern int apfs_fileattr_set(struct user_namespace *mnt_userns, struct dentry *dentry, struct fileattr *fa);
|
||||
#endif
|
||||
@@ -919,14 +995,13 @@ extern int apfs_read_snap_meta_key(void *raw, int size, struct apfs_key *key);
|
||||
extern int apfs_read_omap_snap_key(void *raw, int size, struct apfs_key *key);
|
||||
|
||||
/* message.c */
|
||||
extern __printf(3, 4)
|
||||
void apfs_msg(struct super_block *sb, const char *prefix, const char *fmt, ...);
|
||||
extern __printf(5, 6) void apfs_msg(struct super_block *sb, const char *prefix, const char *func, int line, const char *fmt, ...);
|
||||
|
||||
/* node.c */
|
||||
extern struct apfs_node *apfs_read_node(struct super_block *sb, u64 oid,
|
||||
u32 storage, bool write);
|
||||
extern void apfs_update_node(struct apfs_node *node);
|
||||
extern int apfs_delete_node(struct apfs_query *query);
|
||||
extern int apfs_delete_node(struct apfs_node *node, int type);
|
||||
extern int apfs_node_query(struct super_block *sb, struct apfs_query *query);
|
||||
extern void apfs_node_query_first(struct apfs_query *query);
|
||||
extern int apfs_omap_map_from_query(struct apfs_query *query, struct apfs_omap_map *map);
|
||||
@@ -934,6 +1009,7 @@ extern int apfs_node_split(struct apfs_query *query);
|
||||
extern int apfs_node_locate_key(struct apfs_node *node, int index, int *off);
|
||||
extern void apfs_node_free(struct apfs_node *node);
|
||||
extern void apfs_node_free_range(struct apfs_node *node, u16 off, u16 len);
|
||||
extern bool apfs_node_has_room(struct apfs_node *node, int length, bool replace);
|
||||
extern int apfs_node_replace(struct apfs_query *query, void *key, int key_len, void *val, int val_len);
|
||||
extern int apfs_node_insert(struct apfs_query *query, void *key, int key_len, void *val, int val_len);
|
||||
extern int apfs_create_single_rec_node(struct apfs_query *query, void *key, int key_len, void *val, int val_len);
|
||||
@@ -957,6 +1033,7 @@ extern int apfs_switch_to_snapshot(struct super_block *sb);
|
||||
|
||||
/* spaceman.c */
|
||||
extern int apfs_read_spaceman(struct super_block *sb);
|
||||
extern int apfs_free_queue_insert_nocache(struct super_block *sb, u64 bno, u64 count);
|
||||
extern int apfs_free_queue_insert(struct super_block *sb, u64 bno, u64 count);
|
||||
extern int apfs_spaceman_allocate_block(struct super_block *sb, u64 *bno, bool backwards);
|
||||
|
||||
@@ -989,6 +1066,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);
|
||||
@@ -1002,6 +1082,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 */
|
||||
@@ -1022,6 +1103,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
|
||||
@@ -1049,7 +1142,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;
|
||||
|
||||
+19
-19
@@ -49,11 +49,11 @@
|
||||
#define APFS_OBJECT_TYPE_GBITMAP 0x00000019
|
||||
#define APFS_OBJECT_TYPE_GBITMAP_TREE 0x0000001a
|
||||
#define APFS_OBJECT_TYPE_GBITMAP_BLOCK 0x0000001b
|
||||
#define OBJECT_TYPE_ER_RECOVERY_BLOCK 0x0000001c
|
||||
#define OBJECT_TYPE_SNAP_META_EXT 0x0000001d
|
||||
#define OBJECT_TYPE_INTEGRITY_META 0x0000001e
|
||||
#define OBJECT_TYPE_FEXT_TREE 0x0000001f
|
||||
#define OBJECT_TYPE_RESERVED_20 0x00000020
|
||||
#define APFS_OBJECT_TYPE_ER_RECOVERY_BLOCK 0x0000001c
|
||||
#define APFS_OBJECT_TYPE_SNAP_META_EXT 0x0000001d
|
||||
#define APFS_OBJECT_TYPE_INTEGRITY_META 0x0000001e
|
||||
#define APFS_OBJECT_TYPE_FEXT_TREE 0x0000001f
|
||||
#define APFS_OBJECT_TYPE_RESERVED_20 0x00000020
|
||||
#define APFS_OBJECT_TYPE_INVALID 0x00000000
|
||||
#define APFS_OBJECT_TYPE_TEST 0x000000ff
|
||||
|
||||
@@ -306,7 +306,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 +503,7 @@ struct apfs_dir_stats_val {
|
||||
struct apfs_sibling_val {
|
||||
__le64 parent_id;
|
||||
__le16 name_len;
|
||||
u8 name[0];
|
||||
u8 name[];
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
@@ -622,7 +622,7 @@ enum {
|
||||
struct apfs_drec_key {
|
||||
struct apfs_key_header hdr;
|
||||
__le16 name_len;
|
||||
u8 name[0];
|
||||
u8 name[];
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
@@ -632,7 +632,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 +641,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 +660,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;
|
||||
|
||||
/*
|
||||
@@ -1278,7 +1278,7 @@ enum {
|
||||
struct apfs_xattr_val {
|
||||
__le16 flags;
|
||||
__le16 xdata_len;
|
||||
u8 xdata[0];
|
||||
u8 xdata[];
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
@@ -1372,7 +1372,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 +1435,7 @@ struct apfs_compress_rsrc_data {
|
||||
struct apfs_compress_rsrc_block {
|
||||
__le32 offs;
|
||||
__le32 size;
|
||||
} __packed block[0];
|
||||
} __packed block[];
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
@@ -1456,7 +1456,7 @@ struct apfs_snap_metadata_val {
|
||||
__le32 extentref_tree_type;
|
||||
__le32 flags;
|
||||
__le16 name_len;
|
||||
u8 name[0];
|
||||
u8 name[];
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
@@ -1475,7 +1475,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 +1503,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 +1520,7 @@ struct apfs_kb_locker {
|
||||
__le16 kl_nkeys;
|
||||
__le32 kl_nbytes;
|
||||
__le64 padding;
|
||||
struct apfs_keybag_entry kl_entries[0];
|
||||
struct apfs_keybag_entry kl_entries[];
|
||||
} __packed;
|
||||
|
||||
#endif /* _APFS_RAW_H */
|
||||
+327
-300
File diff suppressed because it is too large.
Load diff
@@ -1,7 +1,8 @@
|
||||
PACKAGE_NAME="linux-apfs-rw"
|
||||
PACKAGE_VERSION="0.3.0"
|
||||
PACKAGE_VERSION="0.3.7"
|
||||
|
||||
BUILT_MODULE_NAME[0]="apfs"
|
||||
DEST_MODULE_LOCATION[0]="/extra"
|
||||
|
||||
AUTOINSTALL="yes"
|
||||
PRE_BUILD="genver.sh"
|
||||
@@ -18,6 +18,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;
|
||||
@@ -41,6 +44,12 @@ static vm_fault_t apfs_page_mkwrite(struct vm_fault *vmf)
|
||||
goto out;
|
||||
apfs_inode_join_transaction(sb, inode);
|
||||
|
||||
err = apfs_inode_create_exclusive_dstream(inode);
|
||||
if (err) {
|
||||
apfs_err(sb, "dstream creation failed for ino 0x%llx", apfs_ino(inode));
|
||||
goto out_abort;
|
||||
}
|
||||
|
||||
lock_page(page);
|
||||
wait_for_stable_page(page);
|
||||
if (page->mapping != inode->i_mapping) {
|
||||
@@ -48,8 +57,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)
|
||||
@@ -73,8 +90,10 @@ static vm_fault_t apfs_page_mkwrite(struct vm_fault *vmf)
|
||||
unlock_page(page); /* XXX: race? */
|
||||
|
||||
err = block_page_mkwrite(vma, vmf, apfs_get_new_block);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "mkwrite failed for ino 0x%llx", apfs_ino(inode));
|
||||
goto out_abort;
|
||||
}
|
||||
set_page_dirty(page);
|
||||
|
||||
/* An immediate commit would leave the page unlocked */
|
||||
@@ -91,7 +110,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;
|
||||
}
|
||||
@@ -102,7 +125,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;
|
||||
|
||||
@@ -130,15 +153,43 @@ int apfs_fsync(struct file *file, loff_t start, loff_t end, int datasync)
|
||||
}
|
||||
|
||||
const struct file_operations apfs_file_operations = {
|
||||
.llseek = generic_file_llseek,
|
||||
.read_iter = generic_file_read_iter,
|
||||
.write_iter = generic_file_write_iter,
|
||||
.mmap = apfs_file_mmap,
|
||||
.open = generic_file_open,
|
||||
.fsync = apfs_fsync,
|
||||
.unlocked_ioctl = apfs_file_ioctl,
|
||||
.llseek = generic_file_llseek,
|
||||
.read_iter = generic_file_read_iter,
|
||||
.write_iter = generic_file_write_iter,
|
||||
.mmap = apfs_file_mmap,
|
||||
.open = generic_file_open,
|
||||
.fsync = apfs_fsync,
|
||||
.unlocked_ioctl = apfs_file_ioctl,
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)
|
||||
.copy_file_range = generic_copy_file_range,
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
|
||||
.remap_file_range = apfs_remap_file_range,
|
||||
#else
|
||||
.clone_file_range = apfs_clone_file_range,
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 5, 0)
|
||||
.splice_read = generic_file_splice_read,
|
||||
#else
|
||||
.splice_read = filemap_splice_read,
|
||||
#endif
|
||||
.splice_write = iter_file_splice_write,
|
||||
};
|
||||
|
||||
#if LINUX_VERSION_CODE == KERNEL_VERSION(5, 3, 0)
|
||||
/*
|
||||
* This is needed mainly to test clones with xfstests, so we only support the
|
||||
* kernel version I use during testing. TODO: support all kernel versions.
|
||||
*/
|
||||
int apfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, u64 start, u64 len)
|
||||
{
|
||||
return generic_block_fiemap(inode, fieinfo, start, len, apfs_get_block);
|
||||
}
|
||||
#endif
|
||||
|
||||
const struct inode_operations apfs_file_inode_operations = {
|
||||
.getattr = apfs_getattr,
|
||||
.listxattr = apfs_listxattr,
|
||||
@@ -148,4 +199,7 @@ const struct inode_operations apfs_file_inode_operations = {
|
||||
.fileattr_get = apfs_fileattr_get,
|
||||
.fileattr_set = apfs_fileattr_set,
|
||||
#endif
|
||||
#if LINUX_VERSION_CODE == KERNEL_VERSION(5, 3, 0)
|
||||
.fiemap = apfs_fiemap,
|
||||
#endif
|
||||
};
|
||||
@@ -0,0 +1,13 @@
|
||||
#!/bin/sh
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
# Script to generate the module version header
|
||||
#
|
||||
|
||||
if command -v git >/dev/null 2>&1 && [ -d .git ]; then
|
||||
GIT_COMMIT=$(git describe HEAD | tail -c 9)
|
||||
else
|
||||
GIT_COMMIT="$(grep PACKAGE_VERSION dkms.conf | cut -d '"' -f2)?"
|
||||
fi
|
||||
|
||||
printf '#define GIT_COMMIT\t"%s"\n' "$GIT_COMMIT" > version.h
|
||||
@@ -81,8 +81,10 @@ int apfs_keycmp(struct apfs_key *k1, struct apfs_key *k2)
|
||||
*/
|
||||
int apfs_read_cat_key(void *raw, int size, struct apfs_key *key, bool hashed)
|
||||
{
|
||||
if (size < sizeof(struct apfs_key_header))
|
||||
if (size < sizeof(struct apfs_key_header)) {
|
||||
apfs_err(NULL, "bad key length (%d)", size);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
key->id = apfs_cat_cnid((struct apfs_key_header *)raw);
|
||||
key->type = apfs_cat_type((struct apfs_key_header *)raw);
|
||||
|
||||
@@ -92,6 +94,7 @@ int apfs_read_cat_key(void *raw, int size, struct apfs_key *key, bool hashed)
|
||||
if (size < sizeof(struct apfs_drec_hashed_key) + 1 ||
|
||||
*((char *)raw + size - 1) != 0) {
|
||||
/* Filename must have NULL-termination */
|
||||
apfs_err(NULL, "invalid drec key (%d)", size);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
/* Name length is not used in key comparisons, only the hash */
|
||||
@@ -103,6 +106,7 @@ int apfs_read_cat_key(void *raw, int size, struct apfs_key *key, bool hashed)
|
||||
if (size < sizeof(struct apfs_drec_key) + 1 ||
|
||||
*((char *)raw + size - 1) != 0) {
|
||||
/* Filename must have NULL-termination */
|
||||
apfs_err(NULL, "invalid drec key (%d)", size);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
/* There's no hash */
|
||||
@@ -114,21 +118,26 @@ int apfs_read_cat_key(void *raw, int size, struct apfs_key *key, bool hashed)
|
||||
if (size < sizeof(struct apfs_xattr_key) + 1 ||
|
||||
*((char *)raw + size - 1) != 0) {
|
||||
/* xattr name must have NULL-termination */
|
||||
apfs_err(NULL, "invalid xattr key (%d)", size);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
key->number = 0;
|
||||
key->name = ((struct apfs_xattr_key *)raw)->name;
|
||||
break;
|
||||
case APFS_TYPE_FILE_EXTENT:
|
||||
if (size != sizeof(struct apfs_file_extent_key))
|
||||
if (size != sizeof(struct apfs_file_extent_key)) {
|
||||
apfs_err(NULL, "bad key length (%d)", size);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
key->number = le64_to_cpu(
|
||||
((struct apfs_file_extent_key *)raw)->logical_addr);
|
||||
key->name = NULL;
|
||||
break;
|
||||
case APFS_TYPE_SIBLING_LINK:
|
||||
if (size != sizeof(struct apfs_sibling_link_key))
|
||||
if (size != sizeof(struct apfs_sibling_link_key)) {
|
||||
apfs_err(NULL, "bad key length (%d)", size);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
key->number = le64_to_cpu(
|
||||
((struct apfs_sibling_link_key *)raw)->sibling_id);
|
||||
key->name = NULL;
|
||||
@@ -146,8 +155,10 @@ int apfs_read_fext_key(void *raw, int size, struct apfs_key *key)
|
||||
{
|
||||
struct apfs_fext_tree_key *raw_key;
|
||||
|
||||
if (size != sizeof(*raw_key))
|
||||
if (size != sizeof(*raw_key)) {
|
||||
apfs_err(NULL, "bad key length (%d)", size);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
raw_key = raw;
|
||||
|
||||
key->id = le64_to_cpu(raw_key->private_id);
|
||||
@@ -169,8 +180,10 @@ int apfs_read_free_queue_key(void *raw, int size, struct apfs_key *key)
|
||||
{
|
||||
struct apfs_spaceman_free_queue_key *raw_key;
|
||||
|
||||
if (size < sizeof(struct apfs_spaceman_free_queue_key))
|
||||
if (size < sizeof(struct apfs_spaceman_free_queue_key)) {
|
||||
apfs_err(NULL, "bad key length (%d)", size);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
raw_key = raw;
|
||||
|
||||
key->id = le64_to_cpu(raw_key->sfqk_xid);
|
||||
@@ -191,8 +204,10 @@ int apfs_read_free_queue_key(void *raw, int size, struct apfs_key *key)
|
||||
*/
|
||||
int apfs_read_omap_key(void *raw, int size, struct apfs_key *key)
|
||||
{
|
||||
if (size < sizeof(struct apfs_omap_key))
|
||||
if (size < sizeof(struct apfs_omap_key)) {
|
||||
apfs_err(NULL, "bad key length (%d)", size);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
|
||||
key->id = le64_to_cpu(((struct apfs_omap_key *)raw)->ok_oid);
|
||||
key->type = 0;
|
||||
@@ -212,8 +227,10 @@ int apfs_read_omap_key(void *raw, int size, struct apfs_key *key)
|
||||
*/
|
||||
int apfs_read_extentref_key(void *raw, int size, struct apfs_key *key)
|
||||
{
|
||||
if (size != sizeof(struct apfs_phys_ext_key))
|
||||
if (size != sizeof(struct apfs_phys_ext_key)) {
|
||||
apfs_err(NULL, "bad key length (%d)", size);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
key->id = apfs_cat_cnid((struct apfs_key_header *)raw);
|
||||
key->type = apfs_cat_type((struct apfs_key_header *)raw);
|
||||
key->number = 0;
|
||||
@@ -223,21 +240,26 @@ int apfs_read_extentref_key(void *raw, int size, struct apfs_key *key)
|
||||
|
||||
int apfs_read_snap_meta_key(void *raw, int size, struct apfs_key *key)
|
||||
{
|
||||
if (size < sizeof(struct apfs_key_header))
|
||||
if (size < sizeof(struct apfs_key_header)) {
|
||||
apfs_err(NULL, "bad key length (%d)", size);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
key->id = apfs_cat_cnid((struct apfs_key_header *)raw);
|
||||
key->type = apfs_cat_type((struct apfs_key_header *)raw);
|
||||
key->number = 0;
|
||||
|
||||
switch (key->type) {
|
||||
case APFS_TYPE_SNAP_METADATA:
|
||||
if (size != sizeof(struct apfs_snap_metadata_key))
|
||||
if (size != sizeof(struct apfs_snap_metadata_key)) {
|
||||
apfs_err(NULL, "bad key length (%d)", size);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
key->name = NULL;
|
||||
return 0;
|
||||
case APFS_TYPE_SNAP_NAME:
|
||||
if (size < sizeof(struct apfs_snap_name_key) + 1 || *((char *)raw + size - 1) != 0) {
|
||||
/* snapshot name must have NULL-termination */
|
||||
apfs_err(NULL, "invalid snap name key (%d)", size);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
key->name = ((struct apfs_snap_name_key *)raw)->name;
|
||||
@@ -251,8 +273,10 @@ int apfs_read_omap_snap_key(void *raw, int size, struct apfs_key *key)
|
||||
{
|
||||
__le64 *xid = NULL;
|
||||
|
||||
if (size != sizeof(*xid))
|
||||
if (size != sizeof(*xid)) {
|
||||
apfs_err(NULL, "bad key length (%d)", size);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
xid = raw;
|
||||
|
||||
key->id = le64_to_cpup(xid);
|
||||
|
||||
@@ -6,8 +6,9 @@
|
||||
#include <linux/fs.h>
|
||||
#include "apfs.h"
|
||||
|
||||
void apfs_msg(struct super_block *sb, const char *prefix, const char *fmt, ...)
|
||||
void apfs_msg(struct super_block *sb, const char *prefix, const char *func, int line, const char *fmt, ...)
|
||||
{
|
||||
char *sb_id = NULL;
|
||||
struct va_format vaf;
|
||||
va_list args;
|
||||
|
||||
@@ -16,7 +17,13 @@ void apfs_msg(struct super_block *sb, const char *prefix, const char *fmt, ...)
|
||||
vaf.fmt = fmt;
|
||||
vaf.va = &args;
|
||||
|
||||
printk("%sAPFS (%s): %pV\n", prefix, sb->s_id, &vaf);
|
||||
/* The superblock is not available to all callers */
|
||||
sb_id = sb ? sb->s_id : "?";
|
||||
|
||||
if (func)
|
||||
printk("%sAPFS (%s): %pV (%s:%d)\n", prefix, sb_id, &vaf, func, line);
|
||||
else
|
||||
printk("%sAPFS (%s): %pV\n", prefix, sb_id, &vaf);
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
@@ -18,13 +18,17 @@ static struct dentry *apfs_lookup(struct inode *dir, struct dentry *dentry,
|
||||
return ERR_PTR(-ENAMETOOLONG);
|
||||
|
||||
err = apfs_inode_by_name(dir, &dentry->d_name, &ino);
|
||||
if (err && err != -ENODATA)
|
||||
if (err && err != -ENODATA) {
|
||||
apfs_err(dir->i_sb, "inode lookup by name failed");
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
if (!err) {
|
||||
inode = apfs_iget(dir->i_sb, ino);
|
||||
if (IS_ERR(inode))
|
||||
if (IS_ERR(inode)) {
|
||||
apfs_err(dir->i_sb, "iget failed");
|
||||
return ERR_CAST(inode);
|
||||
}
|
||||
}
|
||||
|
||||
return d_splice_alias(inode, dentry);
|
||||
@@ -33,9 +37,12 @@ static struct dentry *apfs_lookup(struct inode *dir, struct dentry *dentry,
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 12, 0)
|
||||
static int apfs_symlink(struct inode *dir, struct dentry *dentry,
|
||||
const char *symname)
|
||||
#else
|
||||
#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 3, 0)
|
||||
static int apfs_symlink(struct user_namespace *mnt_userns, struct inode *dir,
|
||||
struct dentry *dentry, const char *symname)
|
||||
#else
|
||||
static int apfs_symlink(struct mnt_idmap *idmap, struct inode *dir,
|
||||
struct dentry *dentry, const char *symname)
|
||||
#endif
|
||||
{
|
||||
/* Symlink permissions don't mean anything and their value is fixed */
|
||||
|
||||
@@ -78,8 +78,10 @@ static int apfs_cpm_lookup_oid(struct super_block *sb,
|
||||
u32 map_count = le32_to_cpu(cpm->cpm_count);
|
||||
int i;
|
||||
|
||||
if (map_count > apfs_max_maps_per_block(sb))
|
||||
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];
|
||||
@@ -214,14 +216,17 @@ int apfs_remove_cpoint_map(struct super_block *sb, u64 bno)
|
||||
int err = 0;
|
||||
|
||||
bh = apfs_read_cpm_block(sb);
|
||||
if (!bh)
|
||||
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;
|
||||
}
|
||||
@@ -240,6 +245,7 @@ int apfs_remove_cpoint_map(struct super_block *sb, u64 bno)
|
||||
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;
|
||||
}
|
||||
@@ -269,8 +275,10 @@ struct buffer_head *apfs_read_ephemeral_object(struct super_block *sb, u64 oid)
|
||||
u32 desc_len = le32_to_cpu(raw_sb->nx_xp_desc_len);
|
||||
u32 i;
|
||||
|
||||
if (!desc_blks || !desc_len)
|
||||
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) {
|
||||
@@ -281,8 +289,10 @@ struct buffer_head *apfs_read_ephemeral_object(struct super_block *sb, u64 oid)
|
||||
int err;
|
||||
|
||||
bh = apfs_sb_bread(sb, cpm_bno);
|
||||
if (!bh)
|
||||
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);
|
||||
@@ -290,15 +300,20 @@ struct buffer_head *apfs_read_ephemeral_object(struct super_block *sb, u64 oid)
|
||||
cpm = NULL;
|
||||
if (err == -EAGAIN) /* Search the next mapping block */
|
||||
continue;
|
||||
if (err)
|
||||
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)
|
||||
if (!bh) {
|
||||
apfs_err(sb, "failed to read ephemeral block");
|
||||
return ERR_PTR(-EIO);
|
||||
}
|
||||
return bh;
|
||||
}
|
||||
return ERR_PTR(-EFSCORRUPTED); /* The mapping is missing */
|
||||
apfs_err(sb, "no mapping for oid 0x%llx", oid);
|
||||
return ERR_PTR(-EFSCORRUPTED);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -315,6 +330,7 @@ struct buffer_head *apfs_read_ephemeral_object(struct super_block *sb, u64 oid)
|
||||
struct buffer_head *apfs_read_object_block(struct super_block *sb, u64 bno, bool write, bool preserve)
|
||||
{
|
||||
struct apfs_nxsb_info *nxi = APFS_NXI(sb);
|
||||
struct apfs_superblock *vsb_raw = NULL;
|
||||
struct buffer_head *bh, *new_bh;
|
||||
struct apfs_obj_phys *obj;
|
||||
u32 type;
|
||||
@@ -324,13 +340,16 @@ struct buffer_head *apfs_read_object_block(struct super_block *sb, u64 bno, bool
|
||||
ASSERT(write || !preserve);
|
||||
|
||||
bh = apfs_sb_bread(sb, bno);
|
||||
if (!bh)
|
||||
if (!bh) {
|
||||
apfs_err(sb, "failed to read object block 0x%llx", bno);
|
||||
return ERR_PTR(-EIO);
|
||||
}
|
||||
|
||||
obj = (struct apfs_obj_phys *)bh->b_data;
|
||||
type = le32_to_cpu(obj->o_type);
|
||||
ASSERT(!(type & APFS_OBJ_EPHEMERAL));
|
||||
if (nxi->nx_flags & APFS_CHECK_NODES && !apfs_obj_verify_csum(sb, bh)) {
|
||||
apfs_err(sb, "bad checksum for object in block 0x%llx", bno);
|
||||
err = -EFSBADCRC;
|
||||
goto fail;
|
||||
}
|
||||
@@ -344,18 +363,34 @@ struct buffer_head *apfs_read_object_block(struct super_block *sb, u64 bno, bool
|
||||
return bh;
|
||||
|
||||
err = apfs_spaceman_allocate_block(sb, &new_bno, true /* backwards */);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "block allocation failed");
|
||||
goto fail;
|
||||
}
|
||||
new_bh = apfs_getblk(sb, new_bno);
|
||||
if (!new_bh) {
|
||||
apfs_err(sb, "failed to map block for CoW (0x%llx)", new_bno);
|
||||
err = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
memcpy(new_bh->b_data, bh->b_data, sb->s_blocksize);
|
||||
|
||||
/* Don't free the old copy if it's part of a snapshot */
|
||||
if (!preserve)
|
||||
/*
|
||||
* Don't free the old copy of the object if it's part of a snapshot.
|
||||
* Also increase the allocation count, except for the volume superblock
|
||||
* which is never counted there.
|
||||
*/
|
||||
if (!preserve) {
|
||||
err = apfs_free_queue_insert(sb, bh->b_blocknr, 1);
|
||||
if (err)
|
||||
apfs_err(sb, "free queue insertion failed for 0x%llx", (unsigned long long)bh->b_blocknr);
|
||||
} else if ((type & APFS_OBJECT_TYPE_MASK) != APFS_OBJECT_TYPE_FS) {
|
||||
vsb_raw = APFS_SB(sb)->s_vsb_raw;
|
||||
apfs_assert_in_transaction(sb, &vsb_raw->apfs_o);
|
||||
le64_add_cpu(&vsb_raw->apfs_fs_alloc_count, 1);
|
||||
le64_add_cpu(&vsb_raw->apfs_total_blocks_alloced, 1);
|
||||
}
|
||||
|
||||
brelse(bh);
|
||||
bh = new_bh;
|
||||
new_bh = NULL;
|
||||
|
||||
+116
-61
@@ -24,16 +24,18 @@ static int apfs_create_superblock_snapshot(struct super_block *sb, u64 *bno)
|
||||
int err;
|
||||
|
||||
err = apfs_spaceman_allocate_block(sb, bno, true /* backwards */);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "block allocation failed");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
snap_bh = apfs_getblk(sb, *bno);
|
||||
if (!snap_bh) {
|
||||
apfs_err(sb, "failed to map block for volume snap (0x%llx)", *bno);
|
||||
err = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
apfs_assert_in_transaction(sb, &vsb_raw->apfs_o);
|
||||
le64_add_cpu(&vsb_raw->apfs_fs_alloc_count, 1);
|
||||
|
||||
curr_bh = sbi->s_vobject.o_bh;
|
||||
memcpy(snap_bh->b_data, curr_bh->b_data, sb->s_blocksize);
|
||||
@@ -69,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;
|
||||
@@ -77,23 +78,24 @@ 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);
|
||||
if (err == 0) {
|
||||
apfs_err(sb, "record exists for xid 0x%llx", xid);
|
||||
err = -EFSCORRUPTED;
|
||||
goto fail;
|
||||
}
|
||||
if (err != -ENODATA)
|
||||
if (err != -ENODATA) {
|
||||
apfs_err(sb, "query failed for xid 0x%llx", xid);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
apfs_key_set_hdr(APFS_TYPE_SNAP_METADATA, xid, &raw_key);
|
||||
|
||||
@@ -108,13 +110,18 @@ static int apfs_create_snap_metadata_rec(struct inode *mntpoint, struct apfs_nod
|
||||
now = current_time(mntpoint);
|
||||
raw_val->create_time = cpu_to_le64(timespec64_to_ns(&now));
|
||||
raw_val->change_time = raw_val->create_time;
|
||||
raw_val->inum = 0; /* TODO: what is this? */
|
||||
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;
|
||||
le64_add_cpu(&vsb_raw->apfs_next_obj_id, 1);
|
||||
|
||||
err = apfs_btree_insert(query, &raw_key, sizeof(raw_key), raw_val, val_len);
|
||||
if (err)
|
||||
apfs_err(sb, "insertion failed for xid 0x%llx", xid);
|
||||
fail:
|
||||
kfree(raw_val);
|
||||
raw_val = NULL;
|
||||
@@ -127,20 +134,17 @@ static int apfs_create_snap_name_rec(struct apfs_node *snap_root, const char *na
|
||||
{
|
||||
struct super_block *sb = snap_root->object.sb;
|
||||
struct apfs_query *query = NULL;
|
||||
struct apfs_key key = {0};
|
||||
struct apfs_snap_name_key *raw_key = NULL;
|
||||
struct apfs_snap_name_val raw_val;
|
||||
int key_len;
|
||||
int err;
|
||||
|
||||
apfs_init_snap_name_key(name, &key);
|
||||
|
||||
query = apfs_alloc_query(snap_root, NULL /* parent */);
|
||||
if (!query) {
|
||||
err = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
query->key = &key;
|
||||
apfs_init_snap_name_key(name, &query->key);
|
||||
query->flags |= APFS_QUERY_SNAP_META | APFS_QUERY_EXACT;
|
||||
|
||||
err = apfs_btree_query(sb, &query);
|
||||
@@ -150,8 +154,10 @@ static int apfs_create_snap_name_rec(struct apfs_node *snap_root, const char *na
|
||||
err = -EEXIST;
|
||||
goto fail;
|
||||
}
|
||||
if (err != -ENODATA)
|
||||
if (err != -ENODATA) {
|
||||
apfs_err(sb, "query failed (%s)", name);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
key_len = sizeof(*raw_key) + name_len + 1;
|
||||
raw_key = kzalloc(key_len, GFP_KERNEL);
|
||||
@@ -161,11 +167,13 @@ 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);
|
||||
|
||||
err = apfs_btree_insert(query, raw_key, key_len, &raw_val, sizeof(raw_val));
|
||||
if (err)
|
||||
apfs_err(sb, "insertion failed (%s)", name);
|
||||
fail:
|
||||
kfree(raw_key);
|
||||
raw_key = NULL;
|
||||
@@ -174,30 +182,29 @@ fail:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int apfs_create_snap_meta_records(struct inode *mntpoint, const char *name, u64 sblock_oid)
|
||||
static int apfs_create_snap_meta_records(struct inode *mntpoint, const char *name, int name_len, u64 sblock_oid)
|
||||
{
|
||||
struct super_block *sb = mntpoint->i_sb;
|
||||
struct apfs_superblock *vsb_raw = APFS_SB(sb)->s_vsb_raw;
|
||||
struct apfs_node *snap_root = NULL;
|
||||
size_t name_len;
|
||||
int err;
|
||||
|
||||
snap_root = apfs_read_node(sb, le64_to_cpu(vsb_raw->apfs_snap_meta_tree_oid), APFS_OBJ_PHYSICAL, true /* write */);
|
||||
if (IS_ERR(snap_root))
|
||||
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);
|
||||
}
|
||||
apfs_assert_in_transaction(sb, &vsb_raw->apfs_o);
|
||||
vsb_raw->apfs_snap_meta_tree_oid = cpu_to_le64(snap_root->object.oid);
|
||||
|
||||
name_len = strlen(name);
|
||||
if (name_len > APFS_SNAP_MAX_NAMELEN) {
|
||||
err = -EFSCORRUPTED;
|
||||
err = apfs_create_snap_metadata_rec(mntpoint, snap_root, name, name_len, sblock_oid);
|
||||
if (err) {
|
||||
apfs_err(sb, "meta rec creation failed");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = apfs_create_snap_metadata_rec(mntpoint, snap_root, name, name_len, sblock_oid);
|
||||
if (err)
|
||||
goto fail;
|
||||
err = apfs_create_snap_name_rec(snap_root, name, name_len);
|
||||
if (err)
|
||||
apfs_err(sb, "name rec creation failed");
|
||||
|
||||
fail:
|
||||
apfs_node_free(snap_root);
|
||||
@@ -211,8 +218,10 @@ static int apfs_create_new_extentref_tree(struct super_block *sb)
|
||||
int err;
|
||||
|
||||
err = apfs_make_empty_btree_root(sb, APFS_OBJECT_TYPE_BLOCKREFTREE, &oid);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "failed to make empty root");
|
||||
return err;
|
||||
}
|
||||
|
||||
apfs_assert_in_transaction(sb, &vsb_raw->apfs_o);
|
||||
vsb_raw->apfs_extentref_tree_oid = cpu_to_le64(oid);
|
||||
@@ -231,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};
|
||||
@@ -240,35 +248,42 @@ static int apfs_update_omap_snap_tree(struct super_block *sb, __le64 *oid_p)
|
||||
/* An empty snapshots tree may not even have a root yet */
|
||||
if (!oid) {
|
||||
err = apfs_make_empty_btree_root(sb, APFS_OBJECT_TYPE_OMAP_SNAPSHOT, &oid);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "failed to make empty root");
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
root = apfs_read_node(sb, oid, APFS_OBJ_PHYSICAL, true /* write */);
|
||||
if (IS_ERR(root))
|
||||
if (IS_ERR(root)) {
|
||||
apfs_err(sb, "failed to read omap snap root 0x%llx", oid);
|
||||
return PTR_ERR(root);
|
||||
}
|
||||
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);
|
||||
if (err == 0) {
|
||||
apfs_err(sb, "record exists for xid 0x%llx", nxi->nx_xid);
|
||||
err = -EFSCORRUPTED;
|
||||
goto fail;
|
||||
}
|
||||
if (err != -ENODATA)
|
||||
if (err != -ENODATA) {
|
||||
apfs_err(sb, "query failed for xid 0x%llx", nxi->nx_xid);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
raw_key = cpu_to_le64(nxi->nx_xid);
|
||||
err = apfs_btree_insert(query, &raw_key, sizeof(raw_key), &raw_val, sizeof(raw_val));
|
||||
if (err)
|
||||
apfs_err(sb, "insertion failed for xid 0x%llx", nxi->nx_xid);
|
||||
*oid_p = cpu_to_le64(root->object.block_nr);
|
||||
|
||||
fail:
|
||||
@@ -298,14 +313,18 @@ static int apfs_update_omap_snapshots(struct super_block *sb)
|
||||
|
||||
omap_blk = le64_to_cpu(vsb_raw->apfs_omap_oid);
|
||||
bh = apfs_read_object_block(sb, omap_blk, true /* write */, false /* preserve */);
|
||||
if (IS_ERR(bh))
|
||||
if (IS_ERR(bh)) {
|
||||
apfs_err(sb, "CoW failed for bno 0x%llx", omap_blk);
|
||||
return PTR_ERR(bh);
|
||||
}
|
||||
omap = (struct apfs_omap_phys *)bh->b_data;
|
||||
|
||||
apfs_assert_in_transaction(sb, &omap->om_o);
|
||||
le32_add_cpu(&omap->om_snap_count, 1);
|
||||
omap->om_most_recent_snap = cpu_to_le64(xid);
|
||||
err = apfs_update_omap_snap_tree(sb, &omap->om_snapshot_tree_oid);
|
||||
if (err)
|
||||
apfs_err(sb, "omap snap tree update failed");
|
||||
|
||||
omap = NULL;
|
||||
brelse(bh);
|
||||
@@ -320,7 +339,7 @@ static int apfs_update_omap_snapshots(struct super_block *sb)
|
||||
*
|
||||
* Returns 0 on success, or a negative error code in case of failure.
|
||||
*/
|
||||
static int apfs_do_ioc_take_snapshot(struct inode *mntpoint, const char *name)
|
||||
static int apfs_do_ioc_take_snapshot(struct inode *mntpoint, const char *name, int name_len)
|
||||
{
|
||||
struct super_block *sb = mntpoint->i_sb;
|
||||
struct apfs_sb_info *sbi = APFS_SB(sb);
|
||||
@@ -341,24 +360,34 @@ static int apfs_do_ioc_take_snapshot(struct inode *mntpoint, const char *name)
|
||||
* changes to the layout after the snapshot is set up.
|
||||
*/
|
||||
err = apfs_transaction_flush_all_inodes(sb);
|
||||
if (err)
|
||||
return err;
|
||||
if (err) {
|
||||
apfs_err(sb, "failed to flush all inodes");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = apfs_create_superblock_snapshot(sb, &sblock_oid);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = apfs_create_snap_meta_records(mntpoint, name, sblock_oid);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "failed to snapshot superblock");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = apfs_create_snap_meta_records(mntpoint, name, name_len, sblock_oid);
|
||||
if (err) {
|
||||
apfs_err(sb, "failed to create snap meta records");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = apfs_create_new_extentref_tree(sb);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "failed to create new extref tree");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = apfs_update_omap_snapshots(sb);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "failed to update omap snapshots");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/*
|
||||
* The official reference allows old implementations to ignore extended
|
||||
@@ -395,6 +424,7 @@ int apfs_ioc_take_snapshot(struct file *file, void __user *user_arg)
|
||||
struct inode *inode = file_inode(file);
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct apfs_ioctl_snap_name *arg = NULL;
|
||||
size_t name_len;
|
||||
int err;
|
||||
|
||||
if (apfs_ino(inode) != APFS_ROOT_DIR_INO_NUM) {
|
||||
@@ -404,8 +434,10 @@ int apfs_ioc_take_snapshot(struct file *file, void __user *user_arg)
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 12, 0)
|
||||
if (!inode_owner_or_capable(inode))
|
||||
#else
|
||||
#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 3, 0)
|
||||
if (!inode_owner_or_capable(&init_user_ns, inode))
|
||||
#else
|
||||
if (!inode_owner_or_capable(&nop_mnt_idmap, inode))
|
||||
#endif
|
||||
return -EPERM;
|
||||
|
||||
@@ -424,7 +456,14 @@ int apfs_ioc_take_snapshot(struct file *file, void __user *user_arg)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = apfs_do_ioc_take_snapshot(inode, arg->name);
|
||||
name_len = strnlen(arg->name, sizeof(arg->name));
|
||||
if (name_len == sizeof(arg->name)) {
|
||||
apfs_warn(sb, "snapshot name is too long (%d)", (int)name_len);
|
||||
err = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = apfs_do_ioc_take_snapshot(inode, arg->name, name_len);
|
||||
fail:
|
||||
kfree(arg);
|
||||
arg = NULL;
|
||||
@@ -437,8 +476,10 @@ static int apfs_snap_xid_from_query(struct apfs_query *query, u64 *xid)
|
||||
char *raw = query->node->object.data;
|
||||
__le64 *val = NULL;
|
||||
|
||||
if (query->len != sizeof(*val))
|
||||
if (query->len != sizeof(*val)) {
|
||||
apfs_err(query->node->object.sb, "bad value length (%d)", query->len);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
val = (__le64 *)(raw + query->off);
|
||||
|
||||
*xid = le64_to_cpup(val);
|
||||
@@ -449,22 +490,24 @@ 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);
|
||||
if (err)
|
||||
if (err) {
|
||||
if (err != -ENODATA)
|
||||
apfs_err(sb, "query failed (%s)", name);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = apfs_snap_xid_from_query(query, xid);
|
||||
if (err)
|
||||
apfs_err(sb, "bad snap name record (%s)", name);
|
||||
fail:
|
||||
apfs_free_query(query);
|
||||
query = NULL;
|
||||
@@ -476,8 +519,10 @@ static int apfs_snap_sblock_from_query(struct apfs_query *query, u64 *sblock_oid
|
||||
char *raw = query->node->object.data;
|
||||
struct apfs_snap_metadata_val *val = NULL;
|
||||
|
||||
if (query->len < sizeof(*val))
|
||||
if (query->len < sizeof(*val)) {
|
||||
apfs_err(query->node->object.sb, "bad value length (%d)", query->len);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
val = (struct apfs_snap_metadata_val *)(raw + query->off);
|
||||
|
||||
*sblock_oid = le64_to_cpu(val->sblock_oid);
|
||||
@@ -488,22 +533,23 @@ 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);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "query failed for xid 0x%llx", xid);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = apfs_snap_sblock_from_query(query, sblock_oid);
|
||||
if (err)
|
||||
apfs_err(sb, "bad snap meta record for xid 0x%llx", xid);
|
||||
fail:
|
||||
apfs_free_query(query);
|
||||
query = NULL;
|
||||
@@ -531,17 +577,24 @@ int apfs_switch_to_snapshot(struct super_block *sb)
|
||||
ASSERT(sb->s_flags & SB_RDONLY);
|
||||
|
||||
name = sbi->s_snap_name;
|
||||
if (strlen(name) > APFS_SNAP_MAX_NAMELEN)
|
||||
if (strlen(name) > APFS_SNAP_MAX_NAMELEN) {
|
||||
apfs_warn(sb, "snapshot name is too long");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
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))
|
||||
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;
|
||||
|
||||
err = apfs_snapshot_name_to_xid(snap_root, name, &xid);
|
||||
if (err)
|
||||
if (err) {
|
||||
if (err == -ENODATA)
|
||||
apfs_info(sb, "no snapshot under that name (%s)", name);
|
||||
goto fail;
|
||||
}
|
||||
sbi->s_snap_xid = xid;
|
||||
|
||||
err = apfs_snapshot_xid_to_sblock(snap_root, xid, &sblock_oid);
|
||||
@@ -550,6 +603,8 @@ int apfs_switch_to_snapshot(struct super_block *sb)
|
||||
|
||||
apfs_unmap_volume_super(sb);
|
||||
err = apfs_map_volume_super_bno(sb, sblock_oid, nxi->nx_flags & APFS_CHECK_NODES);
|
||||
if (err)
|
||||
apfs_err(sb, "failed to map volume block 0x%llx", sblock_oid);
|
||||
|
||||
fail:
|
||||
apfs_node_free(snap_root);
|
||||
|
||||
+362
-95
File diff suppressed because it is too large.
Load diff
Loaded 20 of 24 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user