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
@@ -299,6 +299,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 */
|
||||
};
|
||||
@@ -319,8 +320,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;
|
||||
}
|
||||
|
||||
@@ -497,7 +500,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;
|
||||
}
|
||||
|
||||
@@ -646,7 +649,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;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -774,17 +779,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__)
|
||||
@@ -807,7 +826,6 @@ 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_create_omap_rec(struct super_block *sb, u64 oid, u64 bno);
|
||||
@@ -838,7 +856,7 @@ extern int apfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
unsigned int flags);
|
||||
extern int apfs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
|
||||
bool excl);
|
||||
#else
|
||||
#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 3, 0)
|
||||
extern int apfs_mknod(struct user_namespace *mnt_userns, struct inode *dir,
|
||||
struct dentry *dentry, umode_t mode, dev_t rdev);
|
||||
extern int apfs_mkdir(struct user_namespace *mnt_userns, struct inode *dir,
|
||||
@@ -848,6 +866,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,
|
||||
@@ -871,10 +899,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 */
|
||||
@@ -894,12 +929,19 @@ extern int apfs_dstream_adj_refcnt(struct apfs_dstream_info *dstream, u32 delta)
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 12, 0)
|
||||
extern int apfs_setattr(struct dentry *dentry, struct iattr *iattr);
|
||||
#else
|
||||
#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 3, 0)
|
||||
extern int apfs_setattr(struct user_namespace *mnt_userns,
|
||||
struct dentry *dentry, struct iattr *iattr);
|
||||
#else
|
||||
extern int apfs_setattr(struct mnt_idmap *idmap,
|
||||
struct dentry *dentry, struct iattr *iattr);
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
||||
extern int apfs_update_time(struct inode *inode, struct timespec64 *time, int flags);
|
||||
#else
|
||||
extern int apfs_update_time(struct inode *inode, int flags);
|
||||
#endif
|
||||
long apfs_dir_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
|
||||
long apfs_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
|
||||
|
||||
@@ -909,16 +951,23 @@ extern int apfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
|
||||
#elif LINUX_VERSION_CODE < KERNEL_VERSION(5, 12, 0)
|
||||
extern int apfs_getattr(const struct path *path, struct kstat *stat,
|
||||
u32 request_mask, unsigned int query_flags);
|
||||
#else
|
||||
#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 3, 0)
|
||||
extern int apfs_getattr(struct user_namespace *mnt_userns,
|
||||
const struct path *path, struct kstat *stat, u32 request_mask,
|
||||
unsigned int query_flags);
|
||||
#else
|
||||
extern int apfs_getattr(struct mnt_idmap *idmap,
|
||||
const struct path *path, struct kstat *stat, u32 request_mask,
|
||||
unsigned int query_flags);
|
||||
#endif
|
||||
|
||||
extern int apfs_crypto_adj_refcnt(struct super_block *sb, u64 crypto_id, int delta);
|
||||
extern int APFS_CRYPTO_ADJ_REFCNT_MAXOPS(void);
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0)
|
||||
extern int apfs_fileattr_get(struct dentry *dentry, struct fileattr *fa);
|
||||
extern int apfs_fileattr_set(struct mnt_idmap *idmap, struct dentry *dentry, struct fileattr *fa);
|
||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0)
|
||||
extern int apfs_fileattr_get(struct dentry *dentry, struct fileattr *fa);
|
||||
extern int apfs_fileattr_set(struct user_namespace *mnt_userns, struct dentry *dentry, struct fileattr *fa);
|
||||
#endif
|
||||
@@ -935,8 +984,7 @@ 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,
|
||||
@@ -1006,6 +1054,9 @@ extern int apfs_xattr_set(struct inode *inode, const char *name, const void *val
|
||||
size_t size, int flags);
|
||||
extern int APFS_XATTR_SET_MAXOPS(void);
|
||||
extern ssize_t apfs_listxattr(struct dentry *dentry, char *buffer, size_t size);
|
||||
extern int apfs_xattr_get_compressed_data(struct inode *inode, const char *name, struct apfs_compressed_data *cdata);
|
||||
extern void apfs_release_compressed_data(struct apfs_compressed_data *cdata);
|
||||
extern int apfs_compressed_data_read(struct apfs_compressed_data *cdata, void *buf, size_t count, u64 offset);
|
||||
|
||||
/* xfield.c */
|
||||
extern int apfs_find_xfield(u8 *xfields, int len, u8 xtype, char **xval);
|
||||
@@ -1019,6 +1070,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 */
|
||||
|
||||
+14
-14
@@ -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 */
|
||||
@@ -35,13 +35,17 @@ static int apfs_child_from_query(struct apfs_query *query, u64 *child)
|
||||
if (query->flags & APFS_QUERY_CAT && apfs_is_sealed(sb)) {
|
||||
struct apfs_btn_index_node_val *index_val = NULL;
|
||||
|
||||
if (query->len != sizeof(*index_val))
|
||||
if (query->len != sizeof(*index_val)) {
|
||||
apfs_err(sb, "bad sealed index value length (%d)", query->len);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
index_val = (struct apfs_btn_index_node_val *)(raw + query->off);
|
||||
*child = le64_to_cpu(index_val->binv_child_oid) + apfs_catalog_base_oid(query);
|
||||
} else {
|
||||
if (query->len != 8) /* The data on a nonleaf node is the child id */
|
||||
if (query->len != 8) { /* The data on a nonleaf node is the child id */
|
||||
apfs_err(sb, "bad index value length (%d)", query->len);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
*child = le64_to_cpup((__le64 *)(raw + query->off));
|
||||
}
|
||||
return 0;
|
||||
@@ -190,8 +194,11 @@ int apfs_omap_lookup_block(struct super_block *sb, struct apfs_omap *omap,
|
||||
query->flags |= APFS_QUERY_OMAP;
|
||||
|
||||
ret = apfs_btree_query(sb, &query);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
if (ret != -ENODATA)
|
||||
apfs_err(sb, "query failed for oid 0x%llx, xid 0x%llx", id, apfs_mounted_xid(sb));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = apfs_omap_map_from_query(query, &map);
|
||||
if (ret) {
|
||||
@@ -211,6 +218,7 @@ int apfs_omap_lookup_block(struct super_block *sb, struct apfs_omap *omap,
|
||||
|
||||
new_bh = apfs_read_object_block(sb, *block, write, preserve);
|
||||
if (IS_ERR(new_bh)) {
|
||||
apfs_err(sb, "CoW failed for oid 0x%llx, xid 0x%llx", id, apfs_mounted_xid(sb));
|
||||
ret = PTR_ERR(new_bh);
|
||||
goto fail;
|
||||
}
|
||||
@@ -225,6 +233,8 @@ int apfs_omap_lookup_block(struct super_block *sb, struct apfs_omap *omap,
|
||||
ret = apfs_btree_insert(query, &key, sizeof(key), &val, sizeof(val));
|
||||
else
|
||||
ret = apfs_btree_replace(query, &key, sizeof(key), &val, sizeof(val));
|
||||
if (ret)
|
||||
apfs_err(sb, "CoW omap update failed (oid 0x%llx, xid 0x%llx)", id, apfs_mounted_xid(sb));
|
||||
|
||||
*block = new_bh->b_blocknr;
|
||||
brelse(new_bh);
|
||||
@@ -265,8 +275,10 @@ int apfs_create_omap_rec(struct super_block *sb, u64 oid, u64 bno)
|
||||
query->flags |= APFS_QUERY_OMAP;
|
||||
|
||||
ret = apfs_btree_query(sb, &query);
|
||||
if (ret && ret != -ENODATA)
|
||||
if (ret && ret != -ENODATA) {
|
||||
apfs_err(sb, "query failed for oid 0x%llx, bno 0x%llx", oid, bno);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
raw_key.ok_oid = cpu_to_le64(oid);
|
||||
raw_key.ok_xid = cpu_to_le64(nxi->nx_xid);
|
||||
@@ -276,8 +288,10 @@ int apfs_create_omap_rec(struct super_block *sb, u64 oid, u64 bno)
|
||||
|
||||
ret = apfs_btree_insert(query, &raw_key, sizeof(raw_key),
|
||||
&raw_val, sizeof(raw_val));
|
||||
if (ret)
|
||||
if (ret) {
|
||||
apfs_err(sb, "insertion failed for oid 0x%llx, bno 0x%llx", oid, bno);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
apfs_omap_cache_save(omap, oid, bno);
|
||||
|
||||
@@ -311,13 +325,23 @@ int apfs_delete_omap_rec(struct super_block *sb, u64 oid)
|
||||
query->flags |= APFS_QUERY_OMAP;
|
||||
|
||||
ret = apfs_btree_query(sb, &query);
|
||||
if (ret == -ENODATA)
|
||||
if (ret == -ENODATA) {
|
||||
apfs_err(sb, "nonexistent record (oid 0x%llx)", oid);
|
||||
ret = -EFSCORRUPTED;
|
||||
if (!ret)
|
||||
ret = apfs_btree_remove(query);
|
||||
if (!ret)
|
||||
apfs_omap_cache_delete(omap, oid);
|
||||
goto fail;
|
||||
}
|
||||
if (ret) {
|
||||
apfs_err(sb, "query failed (oid 0x%llx)", oid);
|
||||
goto fail;
|
||||
}
|
||||
ret = apfs_btree_remove(query);
|
||||
if (ret) {
|
||||
apfs_err(sb, "removal failed (oid 0x%llx)", oid);
|
||||
goto fail;
|
||||
}
|
||||
apfs_omap_cache_delete(omap, oid);
|
||||
|
||||
fail:
|
||||
apfs_free_query(query);
|
||||
return ret;
|
||||
}
|
||||
@@ -409,8 +433,10 @@ static int apfs_query_set_before_first(struct super_block *sb, struct apfs_query
|
||||
|
||||
/* Now go a level deeper */
|
||||
node = apfs_read_node(sb, child_id, storage, false /* write */);
|
||||
if (IS_ERR(node))
|
||||
if (IS_ERR(node)) {
|
||||
apfs_err(sb, "failed to read child 0x%llx of node 0x%llx", child_id, (*query)->node->object.oid);
|
||||
return PTR_ERR(node);
|
||||
}
|
||||
|
||||
parent = *query;
|
||||
*query = apfs_alloc_query(node, parent);
|
||||
@@ -422,7 +448,7 @@ static int apfs_query_set_before_first(struct super_block *sb, struct apfs_query
|
||||
node = NULL;
|
||||
}
|
||||
|
||||
apfs_alert(sb, "b-tree is corrupted");
|
||||
apfs_err(sb, "btree is too high");
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
|
||||
@@ -455,7 +481,7 @@ next_node:
|
||||
* forever if the filesystem is damaged. 12 should be more
|
||||
* than enough to map every block.
|
||||
*/
|
||||
apfs_alert(sb, "b-tree is corrupted");
|
||||
apfs_err(sb, "btree is too high");
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
|
||||
@@ -466,8 +492,10 @@ next_node:
|
||||
* let the query give up at the root node.
|
||||
*/
|
||||
err = apfs_query_set_before_first(sb, query);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "failed to set before the first record");
|
||||
return err;
|
||||
}
|
||||
return -ENODATA;
|
||||
} else if (err == -EAGAIN) {
|
||||
if (!(*query)->parent) /* We are at the root of the tree */
|
||||
@@ -494,8 +522,10 @@ next_node:
|
||||
|
||||
/* Now go a level deeper and search the child */
|
||||
node = apfs_read_node(sb, child_id, storage, false /* write */);
|
||||
if (IS_ERR(node))
|
||||
if (IS_ERR(node)) {
|
||||
apfs_err(sb, "failed to read node 0x%llx", child_id);
|
||||
return PTR_ERR(node);
|
||||
}
|
||||
|
||||
if (node->object.oid != child_id)
|
||||
apfs_debug(sb, "corrupt b-tree");
|
||||
@@ -515,28 +545,6 @@ next_node:
|
||||
goto next_node;
|
||||
}
|
||||
|
||||
/**
|
||||
* apfs_omap_read_node - Find and read a node from a b-tree
|
||||
* @sb: filesystem superblock
|
||||
* @id: id for the seeked node
|
||||
*
|
||||
* Returns NULL is case of failure, otherwise a pointer to the resulting
|
||||
* apfs_node structure.
|
||||
*/
|
||||
struct apfs_node *apfs_omap_read_node(struct super_block *sb, u64 id)
|
||||
{
|
||||
struct apfs_node *result;
|
||||
|
||||
result = apfs_read_node(sb, id, APFS_OBJ_VIRTUAL, false /* write */);
|
||||
if (IS_ERR(result))
|
||||
return result;
|
||||
|
||||
if (result->object.oid != id)
|
||||
apfs_debug(sb, "corrupt b-tree");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* apfs_query_join_transaction - Add the found node to the current transaction
|
||||
* @query: query that found the node
|
||||
@@ -556,8 +564,10 @@ int apfs_query_join_transaction(struct apfs_query *query)
|
||||
ASSERT(!apfs_node_is_root(node));
|
||||
|
||||
node = apfs_read_node(sb, oid, storage, true /* write */);
|
||||
if (IS_ERR(node))
|
||||
if (IS_ERR(node)) {
|
||||
apfs_err(sb, "Cow failed for node 0x%llx", oid);
|
||||
return PTR_ERR(node);
|
||||
}
|
||||
apfs_node_free(query->node);
|
||||
query->node = node;
|
||||
|
||||
@@ -665,18 +675,20 @@ static int apfs_query_refresh(struct apfs_query *old_query)
|
||||
* which are only expected when large inline xattr values are involved.
|
||||
*/
|
||||
if ((old_query->flags & APFS_QUERY_TREE_MASK) != APFS_QUERY_CAT) {
|
||||
apfs_warn(sb, "attempt to refresh a non-catalog query");
|
||||
apfs_err(sb, "non-catalog query");
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
if (!apfs_node_is_leaf(node)) {
|
||||
apfs_warn(sb, "attempt to refresh a non-leaf query");
|
||||
apfs_err(sb, "non-leaf query");
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
|
||||
/* Build a new query that points exactly to the same key */
|
||||
err = apfs_read_cat_key(raw + old_query->key_off, old_query->key_len, &new_key, hashed);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "failed to read the key");
|
||||
return err;
|
||||
}
|
||||
new_query = apfs_alloc_query(APFS_SB(sb)->s_cat_root, NULL /* parent */);
|
||||
if (!new_query)
|
||||
return -ENOMEM;
|
||||
@@ -684,8 +696,10 @@ static int apfs_query_refresh(struct apfs_query *old_query)
|
||||
new_query->flags = APFS_QUERY_CAT | APFS_QUERY_EXACT;
|
||||
|
||||
err = apfs_btree_query(sb, &new_query);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "failed to rerun");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Set the original query flags and key on the new query */
|
||||
for (ancestor = new_query; ancestor; ancestor = ancestor->parent) {
|
||||
@@ -739,6 +753,7 @@ int apfs_btree_insert(struct apfs_query *query, void *key, int key_len,
|
||||
void *val, int val_len)
|
||||
{
|
||||
struct apfs_node *node = query->node;
|
||||
struct super_block *sb = node->object.sb;
|
||||
struct apfs_btree_node_phys *node_raw;
|
||||
int err;
|
||||
|
||||
@@ -748,8 +763,10 @@ int apfs_btree_insert(struct apfs_query *query, void *key, int key_len,
|
||||
key_len, val_len);
|
||||
|
||||
err = apfs_query_join_transaction(query);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "query join failed");
|
||||
return err;
|
||||
}
|
||||
|
||||
again:
|
||||
node = query->node;
|
||||
@@ -760,18 +777,23 @@ again:
|
||||
if (err == -ENOSPC) {
|
||||
if (!query->parent && !apfs_node_is_root(node)) {
|
||||
err = apfs_query_refresh(query);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "query refresh failed");
|
||||
return err;
|
||||
}
|
||||
if (node->records == 1) {
|
||||
/* The new record just won't fit in the node */
|
||||
return apfs_create_single_rec_node(query, key, key_len, val, val_len);
|
||||
}
|
||||
}
|
||||
err = apfs_node_split(query);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "node split failed");
|
||||
return err;
|
||||
}
|
||||
goto again;
|
||||
} else if (err) {
|
||||
apfs_err(sb, "node record insertion failed");
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -779,6 +801,8 @@ again:
|
||||
if (query->parent && query->index == 0) {
|
||||
err = apfs_btree_replace(query->parent, key, key_len,
|
||||
NULL /* val */, 0 /* val_len */);
|
||||
if (err)
|
||||
apfs_err(sb, "parent update failed");
|
||||
}
|
||||
return err;
|
||||
}
|
||||
@@ -792,6 +816,7 @@ again:
|
||||
int apfs_btree_remove(struct apfs_query *query)
|
||||
{
|
||||
struct apfs_node *node = query->node;
|
||||
struct super_block *sb = node->object.sb;
|
||||
struct apfs_btree_node_phys *node_raw;
|
||||
int later_entries = node->records - query->index - 1;
|
||||
int err;
|
||||
@@ -804,8 +829,10 @@ int apfs_btree_remove(struct apfs_query *query)
|
||||
apfs_btree_change_node_count(query, -1 /* change */);
|
||||
|
||||
err = apfs_query_join_transaction(query);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "query join failed");
|
||||
return err;
|
||||
}
|
||||
|
||||
node = query->node;
|
||||
node_raw = (void *)query->node->object.data;
|
||||
@@ -815,11 +842,10 @@ int apfs_btree_remove(struct apfs_query *query)
|
||||
if (query->parent) {
|
||||
/* Just get rid of the node */
|
||||
return apfs_delete_node(query);
|
||||
} else {
|
||||
/* All descendants are gone, root is the whole tree */
|
||||
node_raw->btn_level = 0;
|
||||
node->flags |= APFS_BTNODE_LEAF;
|
||||
}
|
||||
/* All descendants are gone, root is the whole tree */
|
||||
node_raw->btn_level = 0;
|
||||
node->flags |= APFS_BTNODE_LEAF;
|
||||
}
|
||||
|
||||
/* The first key in a node must match the parent record's */
|
||||
@@ -834,8 +860,10 @@ int apfs_btree_remove(struct apfs_query *query)
|
||||
|
||||
err = apfs_btree_replace(query->parent, key, first_key_len,
|
||||
NULL /* val */, 0 /* val_len */);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "parent update failed");
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove the entry from the table of contents */
|
||||
@@ -902,8 +930,10 @@ int apfs_btree_replace(struct apfs_query *query, void *key, int key_len,
|
||||
}
|
||||
|
||||
err = apfs_query_join_transaction(query);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "query join failed");
|
||||
return err;
|
||||
}
|
||||
|
||||
again:
|
||||
node = query->node;
|
||||
@@ -914,8 +944,10 @@ again:
|
||||
if (key && query->parent && query->index == 0) {
|
||||
err = apfs_btree_replace(query->parent, key, key_len,
|
||||
NULL /* val */, 0 /* val_len */);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "parent update failed");
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
err = apfs_node_replace(query, key, key_len, val, val_len);
|
||||
@@ -923,16 +955,20 @@ again:
|
||||
if (!query->parent && !apfs_node_is_root(node)) {
|
||||
if (node->records == 1) {
|
||||
/* Node is defragmented, ENOSPC is absurd */
|
||||
WARN_ON(1);
|
||||
apfs_alert(sb, "absurd ENOSPC in empty node");
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
err = apfs_query_refresh(query);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "query refresh failed");
|
||||
return err;
|
||||
}
|
||||
}
|
||||
err = apfs_node_split(query);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "node split failed");
|
||||
return err;
|
||||
}
|
||||
goto again;
|
||||
}
|
||||
return err;
|
||||
|
||||
+327
-300
File diff suppressed because it is too large.
Load diff
@@ -1,5 +1,5 @@
|
||||
PACKAGE_NAME="linux-apfs-rw"
|
||||
PACKAGE_VERSION="0.3.1"
|
||||
PACKAGE_VERSION="0.3.5"
|
||||
|
||||
BUILT_MODULE_NAME[0]="apfs"
|
||||
DEST_MODULE_LOCATION[0]="/extra"
|
||||
|
||||
@@ -42,8 +42,10 @@ static vm_fault_t apfs_page_mkwrite(struct vm_fault *vmf)
|
||||
apfs_inode_join_transaction(sb, inode);
|
||||
|
||||
err = apfs_inode_create_exclusive_dstream(inode);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "dstream creation failed for ino 0x%llx", apfs_ino(inode));
|
||||
goto out_abort;
|
||||
}
|
||||
|
||||
lock_page(page);
|
||||
wait_for_stable_page(page);
|
||||
@@ -77,8 +79,10 @@ static vm_fault_t apfs_page_mkwrite(struct vm_fault *vmf)
|
||||
unlock_page(page); /* XXX: race? */
|
||||
|
||||
err = block_page_mkwrite(vma, vmf, apfs_get_new_block);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "mkwrite failed for ino 0x%llx", apfs_ino(inode));
|
||||
goto out_abort;
|
||||
}
|
||||
set_page_dirty(page);
|
||||
|
||||
/* An immediate commit would leave the page unlocked */
|
||||
@@ -95,7 +99,11 @@ out_abort:
|
||||
apfs_transaction_abort(sb);
|
||||
out:
|
||||
if (err)
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
||||
ret = block_page_mkwrite_return(err);
|
||||
#else
|
||||
ret = vmf_fs_error(err);
|
||||
#endif
|
||||
sb_end_pagefault(inode->i_sb);
|
||||
return ret;
|
||||
}
|
||||
@@ -106,7 +114,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;
|
||||
|
||||
@@ -141,8 +149,23 @@ const struct file_operations apfs_file_operations = {
|
||||
.open = generic_file_open,
|
||||
.fsync = apfs_fsync,
|
||||
.unlocked_ioctl = apfs_file_ioctl,
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -325,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;
|
||||
}
|
||||
@@ -345,10 +363,13 @@ struct buffer_head *apfs_read_object_block(struct super_block *sb, u64 bno, bool
|
||||
return bh;
|
||||
|
||||
err = apfs_spaceman_allocate_block(sb, &new_bno, true /* backwards */);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "block allocation failed");
|
||||
goto fail;
|
||||
}
|
||||
new_bh = apfs_getblk(sb, new_bno);
|
||||
if (!new_bh) {
|
||||
apfs_err(sb, "failed to map block for CoW (0x%llx)", new_bno);
|
||||
err = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
@@ -361,6 +382,8 @@ struct buffer_head *apfs_read_object_block(struct super_block *sb, u64 bno, bool
|
||||
*/
|
||||
if (!preserve) {
|
||||
err = apfs_free_queue_insert(sb, bh->b_blocknr, 1);
|
||||
if (err)
|
||||
apfs_err(sb, "free queue insertion failed for 0x%llx", (unsigned long long)bh->b_blocknr);
|
||||
} else if ((type & APFS_OBJECT_TYPE_MASK) != APFS_OBJECT_TYPE_FS) {
|
||||
vsb_raw = APFS_SB(sb)->s_vsb_raw;
|
||||
apfs_assert_in_transaction(sb, &vsb_raw->apfs_o);
|
||||
|
||||
+104
-36
@@ -24,11 +24,14 @@ 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;
|
||||
}
|
||||
@@ -88,11 +91,14 @@ static int apfs_create_snap_metadata_rec(struct inode *mntpoint, struct apfs_nod
|
||||
|
||||
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);
|
||||
|
||||
@@ -110,13 +116,15 @@ 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;
|
||||
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;
|
||||
@@ -152,8 +160,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);
|
||||
@@ -163,11 +173,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;
|
||||
@@ -176,30 +188,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);
|
||||
@@ -213,8 +224,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);
|
||||
@@ -242,13 +255,17 @@ 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);
|
||||
@@ -263,14 +280,19 @@ static int apfs_update_omap_snap_tree(struct super_block *sb, __le64 *oid_p)
|
||||
|
||||
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:
|
||||
@@ -300,14 +322,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);
|
||||
@@ -322,7 +348,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);
|
||||
@@ -343,24 +369,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)
|
||||
if (err) {
|
||||
apfs_err(sb, "failed to flush all inodes");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = apfs_create_superblock_snapshot(sb, &sblock_oid);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "failed to snapshot superblock");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = apfs_create_snap_meta_records(mntpoint, name, sblock_oid);
|
||||
if (err)
|
||||
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
|
||||
@@ -397,6 +433,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) {
|
||||
@@ -406,8 +443,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;
|
||||
|
||||
@@ -426,7 +465,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;
|
||||
@@ -439,8 +485,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);
|
||||
@@ -463,10 +511,15 @@ static int apfs_snapshot_name_to_xid(struct apfs_node *snap_root, const char *na
|
||||
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;
|
||||
@@ -478,8 +531,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);
|
||||
@@ -502,10 +557,14 @@ static int apfs_snapshot_xid_to_sblock(struct apfs_node *snap_root, u64 xid, u64
|
||||
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;
|
||||
@@ -533,17 +592,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);
|
||||
@@ -552,6 +618,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);
|
||||
|
||||
+132
-39
@@ -87,8 +87,10 @@ static int apfs_read_spaceman_dev(struct super_block *sb,
|
||||
|
||||
/* Check that all the cib addresses fit in the spaceman block */
|
||||
if ((long long)spaceman->sm_addr_offset +
|
||||
(long long)spaceman->sm_cib_count * sizeof(u64) > sb->s_blocksize)
|
||||
(long long)spaceman->sm_cib_count * sizeof(u64) > sb->s_blocksize) {
|
||||
apfs_err(sb, "too many cibs (%u)", spaceman->sm_cib_count);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -146,10 +148,14 @@ static int apfs_update_ip_bm_free_next(struct super_block *sb)
|
||||
__le16 *free_next;
|
||||
int i;
|
||||
|
||||
if (free_next_off > sb->s_blocksize)
|
||||
if (free_next_off > sb->s_blocksize) {
|
||||
apfs_err(sb, "offset out of bounds (%u)", free_next_off);
|
||||
return -EFSCORRUPTED;
|
||||
if (free_next_off + bmap_count * sizeof(*free_next) > sb->s_blocksize)
|
||||
}
|
||||
if (free_next_off + bmap_count * sizeof(*free_next) > sb->s_blocksize) {
|
||||
apfs_err(sb, "free next out of bounds (%u-%u)", free_next_off, bmap_count * (u32)sizeof(*free_next));
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
free_next = (void *)raw + free_next_off;
|
||||
|
||||
for (i = 0; i < bmap_count; ++i) {
|
||||
@@ -187,19 +193,25 @@ static int apfs_rotate_ip_bitmaps(struct super_block *sb)
|
||||
}
|
||||
|
||||
xid = apfs_spaceman_get_64(sb, le32_to_cpu(sm_raw->sm_ip_bm_xid_offset));
|
||||
if (!xid)
|
||||
if (!xid) {
|
||||
apfs_err(sb, "xid out of bounds (%u)", le32_to_cpu(sm_raw->sm_ip_bm_xid_offset));
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
*xid = cpu_to_le64(nxi->nx_xid);
|
||||
|
||||
free_head = le16_to_cpu(sm_raw->sm_ip_bm_free_head);
|
||||
free_tail = le16_to_cpu(sm_raw->sm_ip_bm_free_tail);
|
||||
|
||||
curr_bmap_off = apfs_spaceman_get_64(sb, le32_to_cpu(sm_raw->sm_ip_bitmap_offset));
|
||||
if (!curr_bmap_off)
|
||||
if (!curr_bmap_off) {
|
||||
apfs_err(sb, "bmap offset out of bounds (%u)", le32_to_cpu(sm_raw->sm_ip_bitmap_offset));
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
old_bh = apfs_sb_bread(sb, bmap_base + le64_to_cpup(curr_bmap_off));
|
||||
if (!old_bh)
|
||||
if (!old_bh) {
|
||||
apfs_err(sb, "failed to read current ip bitmap (0x%llx)", bmap_base + le64_to_cpup(curr_bmap_off));
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
*curr_bmap_off = cpu_to_le64(free_head);
|
||||
free_head = (free_head + 1) % bmap_length;
|
||||
@@ -207,11 +219,14 @@ static int apfs_rotate_ip_bitmaps(struct super_block *sb)
|
||||
sm_raw->sm_ip_bm_free_head = cpu_to_le16(free_head);
|
||||
sm_raw->sm_ip_bm_free_tail = cpu_to_le16(free_tail);
|
||||
err = apfs_update_ip_bm_free_next(sb);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "failed to update bitmap ring");
|
||||
goto out;
|
||||
}
|
||||
|
||||
new_bh = apfs_getblk(sb, bmap_base + le64_to_cpup(curr_bmap_off));
|
||||
if (!new_bh) {
|
||||
apfs_err(sb, "failed to map block for CoW (0x%llx)", bmap_base + le64_to_cpup(curr_bmap_off));
|
||||
err = -EIO;
|
||||
goto out;
|
||||
}
|
||||
@@ -251,8 +266,10 @@ static int apfs_fq_rec_from_query(struct apfs_query *query, struct apfs_fq_rec *
|
||||
char *raw = query->node->object.data;
|
||||
struct apfs_spaceman_free_queue_key *key;
|
||||
|
||||
if (query->key_len != sizeof(*key))
|
||||
if (query->key_len != sizeof(*key)) {
|
||||
apfs_err(query->node->object.sb, "bad key length (%d)", query->key_len);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
key = (struct apfs_spaceman_free_queue_key *)(raw + query->key_off);
|
||||
|
||||
fqrec->xid = le64_to_cpu(key->sfqk_xid);
|
||||
@@ -265,6 +282,7 @@ static int apfs_fq_rec_from_query(struct apfs_query *query, struct apfs_fq_rec *
|
||||
fqrec->len = le64_to_cpup((__le64 *)(raw + query->off));
|
||||
return 0;
|
||||
}
|
||||
apfs_err(query->node->object.sb, "bad value length (%d)", query->len);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
|
||||
@@ -331,23 +349,32 @@ static int apfs_flush_fq_rec(struct apfs_node *root, u64 xid, u64 *len)
|
||||
query->flags |= APFS_QUERY_FREE_QUEUE | APFS_QUERY_ANY_NUMBER | APFS_QUERY_EXACT;
|
||||
|
||||
err = apfs_btree_query(sb, &query);
|
||||
if (err)
|
||||
if (err) {
|
||||
if (err != -ENODATA)
|
||||
apfs_err(sb, "query failed for xid 0x%llx, paddr 0x%llx", xid, 0ULL);
|
||||
goto fail;
|
||||
}
|
||||
err = apfs_fq_rec_from_query(query, &fqrec);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "bad free queue rec for xid 0x%llx", xid);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for (bno = fqrec.bno; bno < fqrec.bno + fqrec.len; ++bno) {
|
||||
if(apfs_block_in_ip(sm, bno))
|
||||
if (apfs_block_in_ip(sm, bno))
|
||||
err = apfs_ip_mark_free(sb, bno);
|
||||
else
|
||||
err = apfs_main_free(sb, bno);
|
||||
if(err)
|
||||
if (err) {
|
||||
apfs_err(sb, "freeing block 0x%llx failed (%d)", (unsigned long long)bno, err);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
err = apfs_btree_remove(query);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "removal failed for xid 0x%llx", xid);
|
||||
goto fail;
|
||||
}
|
||||
*len = fqrec.len;
|
||||
|
||||
fail:
|
||||
@@ -379,7 +406,7 @@ static u64 apfs_free_queue_oldest_xid(struct apfs_node *root)
|
||||
*
|
||||
* Returns 0 on success or a negative error code in case of failure.
|
||||
*/
|
||||
static int apfs_flush_free_queue(struct super_block *sb, unsigned qid)
|
||||
static int apfs_flush_free_queue(struct super_block *sb, unsigned int qid)
|
||||
{
|
||||
struct apfs_nxsb_info *nxi = APFS_NXI(sb);
|
||||
struct apfs_spaceman *sm = APFS_SM(sb);
|
||||
@@ -391,8 +418,10 @@ static int apfs_flush_free_queue(struct super_block *sb, unsigned qid)
|
||||
|
||||
fq_root = apfs_read_node(sb, le64_to_cpu(fq->sfq_tree_oid),
|
||||
APFS_OBJ_EPHEMERAL, true /* write */);
|
||||
if (IS_ERR(fq_root))
|
||||
if (IS_ERR(fq_root)) {
|
||||
apfs_err(sb, "failed to read fq root 0x%llx", le64_to_cpu(fq->sfq_tree_oid));
|
||||
return PTR_ERR(fq_root);
|
||||
}
|
||||
|
||||
/* Preserve a few transactions */
|
||||
while (oldest + 4 < nxi->nx_xid) {
|
||||
@@ -407,6 +436,7 @@ static int apfs_flush_free_queue(struct super_block *sb, unsigned qid)
|
||||
err = 0;
|
||||
break;
|
||||
} else if (err) {
|
||||
apfs_err(sb, "failed to flush fq");
|
||||
goto fail;
|
||||
} else {
|
||||
le64_add_cpu(&fq->sfq_count, -count);
|
||||
@@ -461,8 +491,10 @@ int apfs_read_spaceman(struct super_block *sb)
|
||||
spaceman->sm_free_cache_base = spaceman->sm_free_cache_blkcnt = 0;
|
||||
|
||||
sm_bh = apfs_read_ephemeral_object(sb, oid);
|
||||
if (IS_ERR(sm_bh))
|
||||
if (IS_ERR(sm_bh)) {
|
||||
apfs_err(sb, "ephemeral read failed for oid 0x%llx", oid);
|
||||
return PTR_ERR(sm_bh);
|
||||
}
|
||||
sm_raw = (struct apfs_spaceman_phys *)sm_bh->b_data;
|
||||
|
||||
sm_flags = le32_to_cpu(sm_raw->sm_flags);
|
||||
@@ -472,13 +504,16 @@ int apfs_read_spaceman(struct super_block *sb)
|
||||
|
||||
/* Only read the main device; fusion drives are not yet supported */
|
||||
err = apfs_read_spaceman_dev(sb, &sm_raw->sm_dev[APFS_SD_MAIN]);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "failed to read main device");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
spaceman->sm_blocks_per_chunk =
|
||||
le32_to_cpu(sm_raw->sm_blocks_per_chunk);
|
||||
spaceman->sm_chunks_per_cib = le32_to_cpu(sm_raw->sm_chunks_per_cib);
|
||||
if (spaceman->sm_chunks_per_cib > apfs_max_chunks_per_cib(sb)) {
|
||||
apfs_err(sb, "too many chunks per cib (%u)", spaceman->sm_chunks_per_cib);
|
||||
err = -EFSCORRUPTED;
|
||||
goto fail;
|
||||
}
|
||||
@@ -486,14 +521,20 @@ int apfs_read_spaceman(struct super_block *sb)
|
||||
spaceman->sm_bh = sm_bh;
|
||||
spaceman->sm_raw = sm_raw;
|
||||
err = apfs_rotate_ip_bitmaps(sb);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "failed to rotate ip bitmaps");
|
||||
goto fail;
|
||||
}
|
||||
err = apfs_flush_free_queue(sb, APFS_SFQ_IP);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "failed to flush ip fq");
|
||||
goto fail;
|
||||
}
|
||||
err = apfs_flush_free_queue(sb, APFS_SFQ_MAIN);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "failed to flush main fq");
|
||||
goto fail;
|
||||
}
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
@@ -625,11 +666,14 @@ int apfs_free_queue_insert_nocache(struct super_block *sb, u64 bno, u64 count)
|
||||
struct apfs_spaceman *sm = APFS_SM(sb);
|
||||
struct apfs_spaceman_phys *sm_raw = sm->sm_raw;
|
||||
struct apfs_spaceman_free_queue *fq;
|
||||
struct apfs_node *fq_root;
|
||||
struct apfs_node *fq_root = NULL;
|
||||
struct apfs_btree_info *fq_info = NULL;
|
||||
struct apfs_query *query = NULL;
|
||||
struct apfs_spaceman_free_queue_key raw_key;
|
||||
__le64 raw_val;
|
||||
struct apfs_key key;
|
||||
u64 node_count;
|
||||
u16 node_limit;
|
||||
int err;
|
||||
|
||||
if (apfs_block_in_ip(sm, bno))
|
||||
@@ -639,8 +683,10 @@ int apfs_free_queue_insert_nocache(struct super_block *sb, u64 bno, u64 count)
|
||||
|
||||
fq_root = apfs_read_node(sb, le64_to_cpu(fq->sfq_tree_oid),
|
||||
APFS_OBJ_EPHEMERAL, true /* write */);
|
||||
if (IS_ERR(fq_root))
|
||||
if (IS_ERR(fq_root)) {
|
||||
apfs_err(sb, "failed to read fq root 0x%llx", le64_to_cpu(fq->sfq_tree_oid));
|
||||
return PTR_ERR(fq_root);
|
||||
}
|
||||
|
||||
query = apfs_alloc_query(fq_root, NULL /* parent */);
|
||||
if (!query) {
|
||||
@@ -653,8 +699,10 @@ int apfs_free_queue_insert_nocache(struct super_block *sb, u64 bno, u64 count)
|
||||
query->flags |= APFS_QUERY_FREE_QUEUE;
|
||||
|
||||
err = apfs_btree_query(sb, &query);
|
||||
if (err && err != -ENODATA)
|
||||
if (err && err != -ENODATA) {
|
||||
apfs_err(sb, "query failed for xid 0x%llx, paddr 0x%llx", nxi->nx_xid, bno);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
raw_key.sfqk_xid = cpu_to_le64(nxi->nx_xid);
|
||||
raw_key.sfqk_paddr = cpu_to_le64(bno);
|
||||
@@ -665,8 +713,24 @@ int apfs_free_queue_insert_nocache(struct super_block *sb, u64 bno, u64 count)
|
||||
raw_val = cpu_to_le64(count);
|
||||
err = apfs_btree_insert(query, &raw_key, sizeof(raw_key), &raw_val, sizeof(raw_val));
|
||||
}
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "insertion failed for xid 0x%llx, paddr 0x%llx", nxi->nx_xid, bno);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
fq_info = (void *)fq_root->object.data + sb->s_blocksize - sizeof(*fq_info);
|
||||
node_count = le64_to_cpu(fq_info->bt_node_count);
|
||||
node_limit = le16_to_cpu(fq->sfq_tree_node_limit);
|
||||
if (node_count > node_limit) {
|
||||
/*
|
||||
* Ideally this should never happen, but at this point I can't
|
||||
* be certain of that (TODO). If it does happen, it's best to
|
||||
* abort and avoid corruption.
|
||||
*/
|
||||
apfs_alert(sb, "free queue has too many nodes (%llu > %u)", node_count, node_limit);
|
||||
err = -EFSCORRUPTED;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!fq->sfq_oldest_xid)
|
||||
fq->sfq_oldest_xid = cpu_to_le64(nxi->nx_xid);
|
||||
@@ -719,8 +783,10 @@ int apfs_free_queue_insert(struct super_block *sb, u64 bno, u64 count)
|
||||
|
||||
/* Failed to extend the cache, so flush it and replace it */
|
||||
err = apfs_free_queue_insert_nocache(sb, sm->sm_free_cache_base, sm->sm_free_cache_blkcnt);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "fq cache flush failed (0x%llx-0x%llx)", sm->sm_free_cache_base, sm->sm_free_cache_blkcnt);
|
||||
return err;
|
||||
}
|
||||
sm->sm_free_cache_base = bno;
|
||||
sm->sm_free_cache_blkcnt = count;
|
||||
return 0;
|
||||
@@ -763,20 +829,25 @@ static int apfs_chunk_alloc_free(struct super_block *sb,
|
||||
if (!ci->ci_bitmap_addr) {
|
||||
u64 bmap_bno;
|
||||
|
||||
/* Freeing a block in an all-free chunk? */
|
||||
if(!is_alloc)
|
||||
if (!is_alloc) {
|
||||
apfs_err(sb, "attempt to free block in all-free chunk");
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
|
||||
/* All blocks in this chunk are free */
|
||||
bmap_bno = apfs_ip_find_free(sb);
|
||||
if (!bmap_bno)
|
||||
if (!bmap_bno) {
|
||||
apfs_err(sb, "no free blocks in ip");
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
bmap_bh = apfs_sb_bread(sb, bmap_bno);
|
||||
} else {
|
||||
bmap_bh = apfs_sb_bread(sb, le64_to_cpu(ci->ci_bitmap_addr));
|
||||
}
|
||||
if (!bmap_bh)
|
||||
if (!bmap_bh) {
|
||||
apfs_err(sb, "failed to read bitmap block");
|
||||
return -EIO;
|
||||
}
|
||||
bmap = bmap_bh->b_data;
|
||||
if (!ci->ci_bitmap_addr) {
|
||||
memset(bmap, 0, sb->s_blocksize);
|
||||
@@ -790,12 +861,14 @@ static int apfs_chunk_alloc_free(struct super_block *sb,
|
||||
|
||||
new_bmap_bno = apfs_ip_find_free(sb);
|
||||
if (!new_bmap_bno) {
|
||||
apfs_err(sb, "no free blocks in ip");
|
||||
err = -EFSCORRUPTED;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
new_bmap_bh = apfs_getblk(sb, new_bmap_bno);
|
||||
if (!new_bmap_bh) {
|
||||
apfs_err(sb, "failed to map new bmap block (0x%llx)", new_bmap_bno);
|
||||
err = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
@@ -803,8 +876,10 @@ static int apfs_chunk_alloc_free(struct super_block *sb,
|
||||
err = apfs_free_queue_insert(sb, bmap_bh->b_blocknr, 1);
|
||||
brelse(bmap_bh);
|
||||
bmap_bh = new_bmap_bh;
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "free queue insertion failed");
|
||||
goto fail;
|
||||
}
|
||||
bmap = bmap_bh->b_data;
|
||||
}
|
||||
apfs_ip_mark_used(sb, bmap_bh->b_blocknr);
|
||||
@@ -816,12 +891,14 @@ static int apfs_chunk_alloc_free(struct super_block *sb,
|
||||
|
||||
new_cib_bno = apfs_ip_find_free(sb);
|
||||
if (!new_cib_bno) {
|
||||
apfs_err(sb, "no free blocks in ip");
|
||||
err = -EFSCORRUPTED;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
new_cib_bh = apfs_getblk(sb, new_cib_bno);
|
||||
if (!new_cib_bh) {
|
||||
apfs_err(sb, "failed to map new cib block (0x%llx)", new_cib_bno);
|
||||
err = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
@@ -829,8 +906,10 @@ static int apfs_chunk_alloc_free(struct super_block *sb,
|
||||
err = apfs_free_queue_insert(sb, (*cib_bh)->b_blocknr, 1);
|
||||
brelse(*cib_bh);
|
||||
*cib_bh = new_cib_bh;
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "free queue insertion failed");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = apfs_transaction_join(sb, *cib_bh);
|
||||
if (err)
|
||||
@@ -851,22 +930,22 @@ static int apfs_chunk_alloc_free(struct super_block *sb,
|
||||
ci->ci_bitmap_addr = cpu_to_le64(bmap_bh->b_blocknr);
|
||||
ASSERT(buffer_trans(*cib_bh));
|
||||
set_buffer_csum(*cib_bh);
|
||||
mark_buffer_dirty(*cib_bh);
|
||||
|
||||
/* Finally, allocate / free the actual block that was requested */
|
||||
if(is_alloc) {
|
||||
if (is_alloc) {
|
||||
*bno = apfs_chunk_find_free(sb, bmap, le64_to_cpu(ci->ci_addr));
|
||||
if (!*bno) {
|
||||
apfs_err(sb, "no free blocks in chunk");
|
||||
err = -EFSCORRUPTED;
|
||||
goto fail;
|
||||
}
|
||||
apfs_chunk_mark_used(sb, bmap, *bno);
|
||||
sm->sm_free_count -= 1;
|
||||
} else {
|
||||
if(!apfs_chunk_mark_free(sb, bmap, *bno)) {
|
||||
if (!apfs_chunk_mark_free(sb, bmap, *bno)) {
|
||||
apfs_err(sb, "block already marked as free (0x%llx)", *bno);
|
||||
le32_add_cpu(&ci->ci_free_count, -1);
|
||||
set_buffer_csum(*cib_bh);
|
||||
mark_buffer_dirty(*cib_bh);
|
||||
err = -EFSCORRUPTED;
|
||||
} else
|
||||
sm->sm_free_count += 1;
|
||||
@@ -924,8 +1003,10 @@ static int apfs_cib_allocate_block(struct super_block *sb,
|
||||
|
||||
/* Avoid out-of-bounds operations on corrupted cibs */
|
||||
chunk_count = le32_to_cpu(cib->cib_chunk_info_count);
|
||||
if (chunk_count > sm->sm_chunks_per_cib)
|
||||
if (chunk_count > sm->sm_chunks_per_cib) {
|
||||
apfs_err(sb, "too many chunks in cib (%u)", chunk_count);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
|
||||
for (i = 0; i < chunk_count; ++i) {
|
||||
int index;
|
||||
@@ -936,6 +1017,8 @@ static int apfs_cib_allocate_block(struct super_block *sb,
|
||||
err = apfs_chunk_allocate_block(sb, cib_bh, index, bno);
|
||||
if (err == -ENOSPC) /* This chunk is full */
|
||||
continue;
|
||||
if (err)
|
||||
apfs_err(sb, "error during allocation");
|
||||
return err;
|
||||
}
|
||||
return -ENOSPC;
|
||||
@@ -966,8 +1049,10 @@ int apfs_spaceman_allocate_block(struct super_block *sb, u64 *bno, bool backward
|
||||
|
||||
cib_bno = apfs_spaceman_read_cib_addr(sb, index);
|
||||
cib_bh = apfs_sb_bread(sb, cib_bno);
|
||||
if (!cib_bh)
|
||||
if (!cib_bh) {
|
||||
apfs_err(sb, "failed to read cib");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
err = apfs_cib_allocate_block(sb, &cib_bh, bno, backwards);
|
||||
if (!err) {
|
||||
@@ -980,6 +1065,8 @@ int apfs_spaceman_allocate_block(struct super_block *sb, u64 *bno, bool backward
|
||||
brelse(cib_bh);
|
||||
if (err == -ENOSPC) /* This cib is full */
|
||||
continue;
|
||||
if (err)
|
||||
apfs_err(sb, "error during allocation");
|
||||
return err;
|
||||
}
|
||||
return -ENOSPC;
|
||||
@@ -1013,8 +1100,10 @@ static int apfs_main_free(struct super_block *sb, u64 bno)
|
||||
u64 cib_bno;
|
||||
int err;
|
||||
|
||||
if(!sm_raw->sm_blocks_per_chunk || !sm_raw->sm_chunks_per_cib)
|
||||
if (!sm_raw->sm_blocks_per_chunk || !sm_raw->sm_chunks_per_cib) {
|
||||
apfs_err(sb, "block or chunk count not set");
|
||||
return -EINVAL;
|
||||
}
|
||||
/* TODO: use bitshifts instead of do_div() */
|
||||
chunk_idx = bno;
|
||||
do_div(chunk_idx, sm->sm_blocks_per_chunk);
|
||||
@@ -1023,8 +1112,10 @@ static int apfs_main_free(struct super_block *sb, u64 bno)
|
||||
|
||||
cib_bno = apfs_spaceman_read_cib_addr(sb, cib_idx);
|
||||
cib_bh = apfs_sb_bread(sb, cib_bno);
|
||||
if (!cib_bh)
|
||||
if (!cib_bh) {
|
||||
apfs_err(sb, "failed to read cib");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
err = apfs_chunk_free(sb, &cib_bh, chunk_idx, bno);
|
||||
if (!err) {
|
||||
@@ -1035,6 +1126,8 @@ static int apfs_main_free(struct super_block *sb, u64 bno)
|
||||
set_buffer_csum(sm->sm_bh);
|
||||
}
|
||||
brelse(cib_bh);
|
||||
if (err)
|
||||
apfs_err(sb, "error during free");
|
||||
|
||||
return err;
|
||||
}
|
||||
@@ -272,11 +272,13 @@ static void apfs_update_software_info(struct super_block *sb)
|
||||
memmove(mod_by + 1, mod_by, (APFS_MAX_HIST - 1) * sizeof(*mod_by));
|
||||
|
||||
memset(mod_by->id, 0, sizeof(mod_by->id));
|
||||
strcpy(mod_by->id, APFS_MODULE_ID_STRING);
|
||||
strscpy(mod_by->id, APFS_MODULE_ID_STRING, sizeof(mod_by->id));
|
||||
mod_by->timestamp = cpu_to_le64(ktime_get_real_ns());
|
||||
mod_by->last_xid = cpu_to_le64(APFS_NXI(sb)->nx_xid);
|
||||
}
|
||||
|
||||
static struct file_system_type apfs_fs_type;
|
||||
|
||||
/**
|
||||
* apfs_unmap_main_super - Clean up apfs_map_main_super()
|
||||
* @sbi: in-memory superblock info
|
||||
@@ -286,11 +288,15 @@ static void apfs_update_software_info(struct super_block *sb)
|
||||
static inline void apfs_unmap_main_super(struct apfs_sb_info *sbi)
|
||||
{
|
||||
struct apfs_nxsb_info *nxi = sbi->s_nxi;
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 5, 0)
|
||||
fmode_t mode = FMODE_READ | FMODE_EXCL;
|
||||
#endif
|
||||
struct apfs_object *obj = NULL;
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 5, 0)
|
||||
if (nxi->nx_flags & APFS_READWRITE)
|
||||
mode |= FMODE_WRITE;
|
||||
#endif
|
||||
|
||||
lockdep_assert_held(&nxs_mutex);
|
||||
|
||||
@@ -304,7 +310,12 @@ static inline void apfs_unmap_main_super(struct apfs_sb_info *sbi)
|
||||
obj->o_bh = NULL;
|
||||
obj = NULL;
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 5, 0)
|
||||
blkdev_put(nxi->nx_bdev, &apfs_fs_type);
|
||||
#else
|
||||
blkdev_put(nxi->nx_bdev, mode);
|
||||
#endif
|
||||
|
||||
list_del(&nxi->nx_list);
|
||||
kfree(nxi);
|
||||
out:
|
||||
@@ -959,12 +970,15 @@ static int apfs_show_options(struct seq_file *seq, struct dentry *root)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* TODO: don't ignore @wait */
|
||||
int apfs_sync_fs(struct super_block *sb, int wait)
|
||||
{
|
||||
struct apfs_max_ops maxops = {0};
|
||||
int err;
|
||||
|
||||
/* TODO: actually start the commit and return without waiting? */
|
||||
if (wait == 0)
|
||||
return 0;
|
||||
|
||||
err = apfs_transaction_start(sb, maxops);
|
||||
if (err)
|
||||
return err;
|
||||
@@ -1320,6 +1334,27 @@ static int apfs_setup_bdi(struct super_block *sb)
|
||||
|
||||
#endif
|
||||
|
||||
static void apfs_set_trans_buffer_limit(struct super_block *sb)
|
||||
{
|
||||
struct apfs_sb_info *sbi = APFS_SB(sb);
|
||||
unsigned long memsize_in_blocks;
|
||||
struct sysinfo info = {0};
|
||||
|
||||
si_meminfo(&info);
|
||||
memsize_in_blocks = info.totalram << (PAGE_SHIFT - sb->s_blocksize_bits);
|
||||
|
||||
/*
|
||||
* Buffer heads are not reclaimed while they are part of the current
|
||||
* transaction, so systems with little memory will crash if we don't
|
||||
* commit often enough. This hack should make that happen in general,
|
||||
* but I still need to get the reclaim to work eventually (TODO).
|
||||
*/
|
||||
if (memsize_in_blocks >= 16 * TRANSACTION_BUFFERS_MAX)
|
||||
sbi->s_trans_buffers_max = TRANSACTION_BUFFERS_MAX;
|
||||
else
|
||||
sbi->s_trans_buffers_max = memsize_in_blocks / 16;
|
||||
}
|
||||
|
||||
static int apfs_fill_super(struct super_block *sb, void *data, int silent)
|
||||
{
|
||||
struct apfs_sb_info *sbi = APFS_SB(sb);
|
||||
@@ -1333,6 +1368,8 @@ static int apfs_fill_super(struct super_block *sb, void *data, int silent)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
apfs_set_trans_buffer_limit(sb);
|
||||
|
||||
sbi->s_uid = INVALID_UID;
|
||||
sbi->s_gid = INVALID_GID;
|
||||
err = parse_options(sb, data);
|
||||
@@ -1445,13 +1482,12 @@ static int apfs_test_super(struct super_block *sb, void *data)
|
||||
static int apfs_set_super(struct super_block *sb, void *data)
|
||||
{
|
||||
int err = set_anon_super(sb, data);
|
||||
|
||||
if (!err)
|
||||
sb->s_fs_info = data;
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct file_system_type apfs_fs_type;
|
||||
|
||||
/*
|
||||
* Wrapper for lookup_bdev() that supports older kernels.
|
||||
*/
|
||||
@@ -1480,7 +1516,11 @@ static int apfs_lookup_bdev(const char *pathname, dev_t *dev)
|
||||
*
|
||||
* Returns 0 on success, or a negative error code in case of failure.
|
||||
*/
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 5, 0)
|
||||
static int apfs_attach_nxi(struct apfs_sb_info *sbi, const char *dev_name, blk_mode_t mode)
|
||||
#else
|
||||
static int apfs_attach_nxi(struct apfs_sb_info *sbi, const char *dev_name, fmode_t mode)
|
||||
#endif
|
||||
{
|
||||
struct apfs_nxsb_info *nxi;
|
||||
dev_t dev = 0;
|
||||
@@ -1500,7 +1540,11 @@ static int apfs_attach_nxi(struct apfs_sb_info *sbi, const char *dev_name, fmode
|
||||
if (!nxi)
|
||||
return -ENOMEM;
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 5, 0)
|
||||
bdev = blkdev_get_by_path(dev_name, mode, &apfs_fs_type, NULL);
|
||||
#else
|
||||
bdev = blkdev_get_by_path(dev_name, mode, &apfs_fs_type);
|
||||
#endif
|
||||
if (IS_ERR(bdev)) {
|
||||
kfree(nxi);
|
||||
return PTR_ERR(bdev);
|
||||
@@ -1527,7 +1571,11 @@ static struct dentry *apfs_mount(struct file_system_type *fs_type, int flags,
|
||||
struct apfs_nxsb_info *nxi;
|
||||
struct super_block *sb;
|
||||
struct apfs_sb_info *sbi;
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 5, 0)
|
||||
blk_mode_t mode = sb_open_mode(flags);
|
||||
#else
|
||||
fmode_t mode = FMODE_READ | FMODE_EXCL;
|
||||
#endif
|
||||
int error = 0;
|
||||
|
||||
mutex_lock(&nxs_mutex);
|
||||
@@ -1544,8 +1592,10 @@ static struct dentry *apfs_mount(struct file_system_type *fs_type, int flags,
|
||||
if (sbi->s_snap_name)
|
||||
flags |= SB_RDONLY;
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 5, 0)
|
||||
if (!(flags & SB_RDONLY))
|
||||
mode |= FMODE_WRITE;
|
||||
#endif
|
||||
|
||||
error = apfs_attach_nxi(sbi, dev_name, mode);
|
||||
if (error)
|
||||
@@ -1572,7 +1622,9 @@ static struct dentry *apfs_mount(struct file_system_type *fs_type, int flags,
|
||||
error = apfs_map_main_super(sb);
|
||||
if (error)
|
||||
goto out_deactivate_super;
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 5, 0)
|
||||
sb->s_mode = mode;
|
||||
#endif
|
||||
snprintf(sb->s_id, sizeof(sb->s_id), "%xg", sb->s_dev);
|
||||
error = apfs_fill_super(sb, data, flags & SB_SILENT ? 1 : 0);
|
||||
if (error)
|
||||
|
||||
@@ -35,6 +35,7 @@ static const char *apfs_get_link(struct dentry *dentry, struct inode *inode,
|
||||
size = __apfs_xattr_get(inode, APFS_XATTR_NAME_SYMLINK,
|
||||
NULL /* buffer */, 0 /* size */);
|
||||
if (size < 0) { /* TODO: return a better error code */
|
||||
apfs_err(sb, "symlink size read failed");
|
||||
err = size;
|
||||
goto fail;
|
||||
}
|
||||
@@ -47,13 +48,13 @@ static const char *apfs_get_link(struct dentry *dentry, struct inode *inode,
|
||||
|
||||
size = __apfs_xattr_get(inode, APFS_XATTR_NAME_SYMLINK, target, size);
|
||||
if (size < 0) {
|
||||
apfs_err(sb, "symlink read failed");
|
||||
err = size;
|
||||
goto fail;
|
||||
}
|
||||
if (size == 0 || *(target + size - 1) != 0) {
|
||||
/* Target path must be NULL-terminated */
|
||||
apfs_alert(sb, "bad link target in inode 0x%llx",
|
||||
apfs_ino(inode));
|
||||
apfs_err(sb, "bad link target in inode 0x%llx", apfs_ino(inode));
|
||||
err = -EFSCORRUPTED;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
+67
-28
@@ -95,13 +95,17 @@ static int apfs_cpoint_init_desc(struct super_block *sb)
|
||||
u32 new_sb_index;
|
||||
int err;
|
||||
|
||||
if (!desc_blks || !desc_len)
|
||||
if (!desc_blks || !desc_len) {
|
||||
apfs_err(sb, "bad checkpoint descriptor area");
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
|
||||
err = apfs_cpoint_init_area(sb, desc_base, desc_blks,
|
||||
desc_next, desc_len);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "failed to init area");
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Now update the superblock with the new checkpoint */
|
||||
|
||||
@@ -149,13 +153,17 @@ static int apfs_cpoint_init_data(struct super_block *sb)
|
||||
u32 data_len = le32_to_cpu(raw_sb->nx_xp_data_len);
|
||||
int err;
|
||||
|
||||
if (!data_blks || !data_len)
|
||||
if (!data_blks || !data_len) {
|
||||
apfs_err(sb, "bad checkpoint data area");
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
|
||||
err = apfs_cpoint_init_area(sb, data_base, data_blks,
|
||||
data_next, data_len);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "failed to init area");
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Apparently the previous checkpoint gets invalidated right away */
|
||||
apfs_assert_in_transaction(sb, &raw_sb->nx_o);
|
||||
@@ -219,14 +227,17 @@ static int apfs_update_mapping_blocks(struct super_block *sb)
|
||||
int j;
|
||||
|
||||
bh = apfs_sb_bread(sb, desc_base + desc_curr);
|
||||
if (!bh)
|
||||
if (!bh) {
|
||||
apfs_err(sb, "failed to read cpm block");
|
||||
return -EINVAL;
|
||||
}
|
||||
ASSERT(buffer_trans(bh));
|
||||
cpm = (struct apfs_checkpoint_map_phys *)bh->b_data;
|
||||
apfs_assert_in_transaction(sb, &cpm->cpm_o);
|
||||
|
||||
map_count = le32_to_cpu(cpm->cpm_count);
|
||||
if (map_count > apfs_max_maps_per_block(sb)) {
|
||||
apfs_err(sb, "block has too many maps (%d)", map_count);
|
||||
brelse(bh);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
@@ -290,6 +301,7 @@ int apfs_cpoint_data_free(struct super_block *sb, u64 bno)
|
||||
new_bh = apfs_getblk(sb, apfs_data_index_to_bno(sb, i));
|
||||
old_bh = apfs_sb_bread(sb, apfs_data_index_to_bno(sb, i + 1));
|
||||
if (!new_bh || !old_bh) {
|
||||
apfs_err(sb, "failed to map blocks in data area");
|
||||
brelse(new_bh);
|
||||
brelse(old_bh);
|
||||
return -EIO;
|
||||
@@ -327,12 +339,18 @@ static int apfs_checkpoint_start(struct super_block *sb)
|
||||
ASSERT(!(sb->s_flags & SB_RDONLY));
|
||||
|
||||
err = apfs_cpoint_init_desc(sb);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "failed to init descriptor area");
|
||||
return err;
|
||||
}
|
||||
err = apfs_cpoint_init_data(sb);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "failed to init data area");
|
||||
return err;
|
||||
}
|
||||
err = apfs_update_mapping_blocks(sb);
|
||||
if (err)
|
||||
apfs_err(sb, "failed to update checkpoint mappings");
|
||||
|
||||
return err;
|
||||
}
|
||||
@@ -444,12 +462,16 @@ int apfs_transaction_start(struct super_block *sb, struct apfs_max_ops maxops)
|
||||
nx_trans->t_starts_count = 0;
|
||||
|
||||
err = apfs_checkpoint_start(sb);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "failed to start a new checkpoint");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = apfs_read_spaceman(sb);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "failed to read the spaceman");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* Don't start transactions unless we are sure they fit in disk */
|
||||
@@ -457,8 +479,10 @@ int apfs_transaction_start(struct super_block *sb, struct apfs_max_ops maxops)
|
||||
/* Commit what we have so far to flush the queues */
|
||||
nx_trans->t_state |= APFS_NX_TRANS_FORCE_COMMIT;
|
||||
err = apfs_transaction_commit(sb);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "commit failed");
|
||||
goto fail;
|
||||
}
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
@@ -473,16 +497,22 @@ int apfs_transaction_start(struct super_block *sb, struct apfs_max_ops maxops)
|
||||
get_bh(vol_trans->t_old_omap_root.object.o_bh);
|
||||
|
||||
err = apfs_map_volume_super(sb, true /* write */);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "CoW failed for volume super");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* TODO: don't copy these nodes for transactions that don't use them */
|
||||
err = apfs_read_omap(sb, true /* write */);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "CoW failed for omap");
|
||||
goto fail;
|
||||
}
|
||||
err = apfs_read_catalog(sb, true /* write */);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "Cow failed for catalog");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
nx_trans->t_starts_count++;
|
||||
@@ -501,7 +531,7 @@ fail:
|
||||
static void apfs_end_buffer_write_sync(struct buffer_head *bh, int uptodate)
|
||||
{
|
||||
struct page *page = NULL;
|
||||
bool must_unlock, is_metadata;
|
||||
bool is_metadata;
|
||||
|
||||
page = bh->b_page;
|
||||
get_page(page);
|
||||
@@ -512,7 +542,7 @@ static void apfs_end_buffer_write_sync(struct buffer_head *bh, int uptodate)
|
||||
bh = NULL;
|
||||
|
||||
/* Future writes to mmapped areas should fault for CoW */
|
||||
must_unlock = trylock_page(page);
|
||||
lock_page(page);
|
||||
page_mkclean(page);
|
||||
|
||||
/* XXX: otherwise, the page cache fills up and crashes the machine */
|
||||
@@ -524,8 +554,7 @@ static void apfs_end_buffer_write_sync(struct buffer_head *bh, int uptodate)
|
||||
#endif
|
||||
}
|
||||
|
||||
if (must_unlock)
|
||||
unlock_page(page);
|
||||
unlock_page(page);
|
||||
put_page(page);
|
||||
}
|
||||
|
||||
@@ -577,8 +606,10 @@ int apfs_transaction_flush_all_inodes(struct super_block *sb)
|
||||
nx_trans->t_state = 0;
|
||||
|
||||
/* Transaction aborted by ->evict_inode(), error code is lost */
|
||||
if (sb->s_flags & SB_RDONLY)
|
||||
if (sb->s_flags & SB_RDONLY) {
|
||||
apfs_err(sb, "abort during inode eviction");
|
||||
return -EROFS;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
@@ -602,8 +633,10 @@ static int apfs_transaction_commit_nx(struct super_block *sb)
|
||||
|
||||
/* Before committing the bhs, write all inode metadata to them */
|
||||
err = apfs_transaction_flush_all_inodes(sb);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "failed to flush all inodes");
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now that nothing else will be freed, flush the last update to the
|
||||
@@ -611,8 +644,10 @@ static int apfs_transaction_commit_nx(struct super_block *sb)
|
||||
*/
|
||||
if (sm->sm_free_cache_base) {
|
||||
err = apfs_free_queue_insert_nocache(sb, sm->sm_free_cache_base, sm->sm_free_cache_blkcnt);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "fq cache flush failed (0x%llx-0x%llx)", sm->sm_free_cache_base, sm->sm_free_cache_blkcnt);
|
||||
return err;
|
||||
}
|
||||
sm->sm_free_cache_base = sm->sm_free_cache_blkcnt = 0;
|
||||
}
|
||||
|
||||
@@ -639,8 +674,10 @@ static int apfs_transaction_commit_nx(struct super_block *sb)
|
||||
apfs_submit_bh(REQ_OP_WRITE, REQ_SYNC, bh);
|
||||
}
|
||||
err = apfs_checkpoint_end(sb);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "failed to end the checkpoint");
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Success: forget the old container and volume superblocks */
|
||||
brelse(nx_trans->t_old_msb);
|
||||
@@ -648,6 +685,7 @@ static int apfs_transaction_commit_nx(struct super_block *sb)
|
||||
|
||||
list_for_each_entry(sbi, &nxi->vol_list, list) {
|
||||
struct apfs_vol_transaction *vol_trans = &sbi->s_transaction;
|
||||
|
||||
if (!vol_trans->t_old_vsb)
|
||||
continue;
|
||||
|
||||
@@ -698,7 +736,7 @@ static bool apfs_transaction_need_commit(struct super_block *sb)
|
||||
struct apfs_spaceman_phys *sm_raw = sm->sm_raw;
|
||||
struct apfs_spaceman_free_queue *fq_ip = &sm_raw->sm_fq[APFS_SFQ_IP];
|
||||
struct apfs_spaceman_free_queue *fq_main = &sm_raw->sm_fq[APFS_SFQ_MAIN];
|
||||
int buffers_max = TRANSACTION_BUFFERS_MAX;
|
||||
int buffers_max = APFS_SB(sb)->s_trans_buffers_max;
|
||||
int starts_max = TRANSACTION_STARTS_MAX;
|
||||
int mq_max = TRANSACTION_MAIN_QUEUE_MAX;
|
||||
|
||||
@@ -713,7 +751,7 @@ static bool apfs_transaction_need_commit(struct super_block *sb)
|
||||
mq_max += 20;
|
||||
}
|
||||
|
||||
if(nx_trans->t_buffers_count > buffers_max)
|
||||
if (nx_trans->t_buffers_count > buffers_max)
|
||||
return true;
|
||||
if (nx_trans->t_starts_count > starts_max)
|
||||
return true;
|
||||
@@ -723,11 +761,11 @@ static bool apfs_transaction_need_commit(struct super_block *sb)
|
||||
* exactly 3 times. Don't allow large transactions if we can't
|
||||
* be sure the bitmap changes will all fit.
|
||||
*/
|
||||
if(le64_to_cpu(fq_ip->sfq_count) * 3 > le64_to_cpu(sm_raw->sm_ip_block_count))
|
||||
if (le64_to_cpu(fq_ip->sfq_count) * 3 > le64_to_cpu(sm_raw->sm_ip_block_count))
|
||||
return true;
|
||||
|
||||
/* Don't let the main queue get too full either */
|
||||
if(le64_to_cpu(fq_main->sfq_count) > mq_max)
|
||||
if (le64_to_cpu(fq_main->sfq_count) > mq_max)
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -750,7 +788,7 @@ int apfs_transaction_commit(struct super_block *sb)
|
||||
if (apfs_transaction_need_commit(sb)) {
|
||||
err = apfs_transaction_commit_nx(sb);
|
||||
if (err) {
|
||||
apfs_warn(sb, "transaction commit failed");
|
||||
apfs_err(sb, "transaction commit failed");
|
||||
return err;
|
||||
}
|
||||
}
|
||||
@@ -807,7 +845,7 @@ int apfs_transaction_join(struct super_block *sb, struct buffer_head *bh)
|
||||
get_bh(bh);
|
||||
bhi->bh = bh;
|
||||
list_add(&bhi->list, &nx_trans->t_buffers);
|
||||
nx_trans->t_buffers_count ++;
|
||||
nx_trans->t_buffers_count++;
|
||||
|
||||
set_buffer_trans(bh);
|
||||
bh->b_private = bhi;
|
||||
@@ -858,7 +896,7 @@ void apfs_transaction_abort(struct super_block *sb)
|
||||
|
||||
ASSERT(nx_trans->t_old_msb);
|
||||
nx_trans->t_state = 0;
|
||||
apfs_warn(sb, "aborting transaction");
|
||||
apfs_err(sb, "aborting transaction");
|
||||
|
||||
--nxi->nx_xid;
|
||||
list_for_each_entry_safe(bhi, tmp, &nx_trans->t_buffers, list) {
|
||||
@@ -885,6 +923,7 @@ void apfs_transaction_abort(struct super_block *sb)
|
||||
|
||||
list_for_each_entry(sbi, &nxi->vol_list, list) {
|
||||
struct apfs_vol_transaction *vol_trans = &sbi->s_transaction;
|
||||
|
||||
if (!vol_trans->t_old_vsb)
|
||||
continue;
|
||||
|
||||
|
||||
@@ -23,32 +23,43 @@
|
||||
static int apfs_xattr_from_query(struct apfs_query *query,
|
||||
struct apfs_xattr *xattr)
|
||||
{
|
||||
struct super_block *sb = query->node->object.sb;
|
||||
struct apfs_xattr_val *xattr_val;
|
||||
struct apfs_xattr_key *xattr_key;
|
||||
char *raw = query->node->object.data;
|
||||
int datalen = query->len - sizeof(*xattr_val);
|
||||
int namelen = query->key_len - sizeof(*xattr_key);
|
||||
|
||||
if (namelen < 1 || datalen < 0)
|
||||
if (namelen < 1 || datalen < 0) {
|
||||
apfs_err(sb, "bad length of name (%d) or data (%d)", namelen, datalen);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
|
||||
xattr_val = (struct apfs_xattr_val *)(raw + query->off);
|
||||
xattr_key = (struct apfs_xattr_key *)(raw + query->key_off);
|
||||
|
||||
if (namelen != le16_to_cpu(xattr_key->name_len))
|
||||
if (namelen != le16_to_cpu(xattr_key->name_len)) {
|
||||
apfs_err(sb, "inconsistent name length (%d vs %d)", namelen, le16_to_cpu(xattr_key->name_len));
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
|
||||
/* The xattr name must be NULL-terminated */
|
||||
if (xattr_key->name[namelen - 1] != 0)
|
||||
if (xattr_key->name[namelen - 1] != 0) {
|
||||
apfs_err(sb, "null termination missing");
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
|
||||
xattr->has_dstream = le16_to_cpu(xattr_val->flags) &
|
||||
APFS_XATTR_DATA_STREAM;
|
||||
|
||||
if (xattr->has_dstream && datalen != sizeof(struct apfs_xattr_dstream))
|
||||
if (xattr->has_dstream && datalen != sizeof(struct apfs_xattr_dstream)) {
|
||||
apfs_err(sb, "bad data length (%d)", datalen);
|
||||
return -EFSCORRUPTED;
|
||||
if (!xattr->has_dstream && datalen != le16_to_cpu(xattr_val->xdata_len))
|
||||
}
|
||||
if (!xattr->has_dstream && datalen != le16_to_cpu(xattr_val->xdata_len)) {
|
||||
apfs_err(sb, "inconsistent data length (%d vs %d)", datalen, le16_to_cpu(xattr_val->xdata_len));
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
|
||||
xattr->name = xattr_key->name;
|
||||
xattr->name_len = namelen - 1; /* Don't count the NULL termination */
|
||||
@@ -101,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)
|
||||
@@ -113,7 +122,9 @@ static int apfs_xattr_extents_read(struct inode *parent,
|
||||
length = dstream->ds_size;
|
||||
if (length < 0 || length < dstream->ds_size) {
|
||||
/* TODO: avoid overflow here for huge compressed files */
|
||||
return -E2BIG;
|
||||
apfs_warn(sb, "xattr is too big to read on linux (0x%llx)", dstream->ds_size);
|
||||
ret = -E2BIG;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!buffer) {
|
||||
@@ -133,56 +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 */
|
||||
ret = -EFSCORRUPTED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
bhs[i] = __getblk_gfp(APFS_NXI(sb)->nx_bdev, bno, sb->s_blocksize, __GFP_MOVABLE);
|
||||
if (!bhs[i]) {
|
||||
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])) {
|
||||
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;
|
||||
}
|
||||
@@ -217,6 +183,125 @@ 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_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;
|
||||
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
|
||||
@@ -260,12 +345,15 @@ int ____apfs_xattr_get(struct inode *inode, const char *name, void *buffer,
|
||||
query->flags |= APFS_QUERY_CAT | APFS_QUERY_EXACT;
|
||||
|
||||
ret = apfs_btree_query(sb, &query);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
if (ret != -ENODATA)
|
||||
apfs_err(sb, "query failed for id 0x%llx (%s)", cnid, name);
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = apfs_xattr_from_query(query, &xattr);
|
||||
if (ret) {
|
||||
apfs_alert(sb, "bad xattr record in inode 0x%llx", cnid);
|
||||
apfs_err(sb, "bad xattr record in inode 0x%llx", cnid);
|
||||
goto done;
|
||||
}
|
||||
|
||||
@@ -300,8 +388,10 @@ static int apfs_xattr_get(struct inode *inode, const char *name, void *buffer, s
|
||||
down_read(&nxi->nx_big_sem);
|
||||
ret = __apfs_xattr_get(inode, name, buffer, size);
|
||||
up_read(&nxi->nx_big_sem);
|
||||
if (ret > XATTR_SIZE_MAX)
|
||||
if (ret > XATTR_SIZE_MAX) {
|
||||
apfs_warn(inode->i_sb, "xattr is too big to read on linux (%d)", ret);
|
||||
return -E2BIG;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -327,8 +417,10 @@ static int apfs_delete_xattr(struct apfs_query *query)
|
||||
int err;
|
||||
|
||||
err = apfs_xattr_from_query(query, &xattr);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "bad xattr record");
|
||||
return err;
|
||||
}
|
||||
|
||||
if (!xattr.has_dstream)
|
||||
return apfs_btree_remove(query);
|
||||
@@ -344,9 +436,13 @@ static int apfs_delete_xattr(struct apfs_query *query)
|
||||
* really need to add some assertions (TODO).
|
||||
*/
|
||||
err = apfs_btree_remove(query);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "removal failed");
|
||||
goto fail;
|
||||
}
|
||||
err = apfs_truncate(dstream, 0);
|
||||
if (err)
|
||||
apfs_err(sb, "truncation failed for dstream 0x%llx", dstream->ds_id);
|
||||
|
||||
fail:
|
||||
kfree(dstream);
|
||||
@@ -381,12 +477,16 @@ static int apfs_delete_any_xattr(struct inode *inode)
|
||||
if (ret) {
|
||||
if (ret == -ENODATA)
|
||||
ret = 0; /* No more xattrs, we are done */
|
||||
else
|
||||
apfs_err(sb, "query failed for ino 0x%llx", apfs_ino(inode));
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = apfs_delete_xattr(query);
|
||||
if (!ret)
|
||||
ret = -EAGAIN;
|
||||
else
|
||||
apfs_err(sb, "xattr deletion failed");
|
||||
|
||||
out:
|
||||
apfs_free_query(query);
|
||||
@@ -434,7 +534,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;
|
||||
@@ -531,10 +631,13 @@ static struct apfs_dstream_info *apfs_create_xattr_dstream(struct super_block *s
|
||||
u64 bno;
|
||||
|
||||
err = apfs_dstream_get_new_bno(dstream, i, &bno);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "failed to get new block in dstream 0x%llx", dstream->ds_id);
|
||||
goto fail;
|
||||
}
|
||||
bh = apfs_sb_bread(sb, bno);
|
||||
if (!bh) {
|
||||
apfs_err(sb, "failed to read new block");
|
||||
err = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
@@ -556,8 +659,10 @@ static struct apfs_dstream_info *apfs_create_xattr_dstream(struct super_block *s
|
||||
}
|
||||
|
||||
err = apfs_flush_extent_cache(dstream);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "extent cache flush failed for dstream 0x%llx", dstream->ds_id);
|
||||
goto fail;
|
||||
}
|
||||
return dstream;
|
||||
|
||||
fail:
|
||||
@@ -580,8 +685,10 @@ static int apfs_xattr_dstream_from_query(struct apfs_query *query, struct apfs_d
|
||||
int err;
|
||||
|
||||
err = apfs_xattr_from_query(query, &xattr);
|
||||
if (err)
|
||||
if (err) {
|
||||
apfs_err(sb, "bad xattr record");
|
||||
return err;
|
||||
}
|
||||
|
||||
if (xattr.has_dstream) {
|
||||
dstream = kzalloc(sizeof(*dstream), GFP_KERNEL);
|
||||
@@ -620,8 +727,10 @@ int apfs_xattr_set(struct inode *inode, const char *name, const void *value,
|
||||
|
||||
if (size > APFS_XATTR_MAX_EMBEDDED_SIZE) {
|
||||
dstream = apfs_create_xattr_dstream(sb, value, size);
|
||||
if (IS_ERR(dstream))
|
||||
if (IS_ERR(dstream)) {
|
||||
apfs_err(sb, "failed to set xattr dstream for ino 0x%llx", apfs_ino(inode));
|
||||
return PTR_ERR(dstream);
|
||||
}
|
||||
}
|
||||
|
||||
apfs_init_xattr_key(cnid, name, &key);
|
||||
@@ -636,21 +745,27 @@ int apfs_xattr_set(struct inode *inode, const char *name, const void *value,
|
||||
|
||||
ret = apfs_btree_query(sb, &query);
|
||||
if (ret) {
|
||||
if (ret != -ENODATA)
|
||||
if (ret != -ENODATA) {
|
||||
apfs_err(sb, "query failed for id 0x%llx (%s)", cnid, name);
|
||||
goto done;
|
||||
else if (flags & XATTR_REPLACE)
|
||||
} else if (flags & XATTR_REPLACE) {
|
||||
goto done;
|
||||
}
|
||||
} else if (flags & XATTR_CREATE) {
|
||||
ret = -EEXIST;
|
||||
goto done;
|
||||
} else if (!value) {
|
||||
ret = apfs_delete_xattr(query);
|
||||
if (ret)
|
||||
apfs_err(sb, "xattr deletion failed");
|
||||
goto done;
|
||||
} else {
|
||||
/* Remember the old dstream to clean it up later */
|
||||
ret = apfs_xattr_dstream_from_query(query, &old_dstream);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
apfs_err(sb, "failed to get the old dstream");
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
key_len = apfs_build_xattr_key(name, cnid, &raw_key);
|
||||
@@ -676,11 +791,16 @@ int apfs_xattr_set(struct inode *inode, const char *name, const void *value,
|
||||
ret = apfs_btree_insert(query, raw_key, key_len, raw_val, val_len);
|
||||
else
|
||||
ret = apfs_btree_replace(query, raw_key, key_len, raw_val, val_len);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
apfs_err(sb, "insertion/update failed for id 0x%llx (%s)", cnid, name);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (old_dstream)
|
||||
if (old_dstream) {
|
||||
ret = apfs_truncate(old_dstream, 0);
|
||||
if (ret)
|
||||
apfs_err(sb, "truncation failed for dstream 0x%llx", old_dstream->ds_id);
|
||||
}
|
||||
|
||||
done:
|
||||
kfree(dstream);
|
||||
@@ -699,11 +819,16 @@ int APFS_XATTR_SET_MAXOPS(void)
|
||||
static int apfs_xattr_osx_set(const struct xattr_handler *handler,
|
||||
struct dentry *unused, struct inode *inode, const char *name,
|
||||
const void *value, size_t size, int flags)
|
||||
#else
|
||||
#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 3, 0)
|
||||
static int apfs_xattr_osx_set(const struct xattr_handler *handler,
|
||||
struct user_namespace *mnt_userns, struct dentry *unused,
|
||||
struct inode *inode, const char *name, const void *value,
|
||||
size_t size, int flags)
|
||||
#else
|
||||
static int apfs_xattr_osx_set(const struct xattr_handler *handler,
|
||||
struct mnt_idmap *idmap, struct dentry *unused,
|
||||
struct inode *inode, const char *name, const void *value,
|
||||
size_t size, int flags)
|
||||
#endif
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
@@ -776,12 +901,14 @@ ssize_t apfs_listxattr(struct dentry *dentry, char *buffer, size_t size)
|
||||
ret = size - free;
|
||||
break;
|
||||
}
|
||||
if (ret)
|
||||
if (ret) {
|
||||
apfs_err(sb, "query failed for id 0x%llx", cnid);
|
||||
break;
|
||||
}
|
||||
|
||||
ret = apfs_xattr_from_query(query, &xattr);
|
||||
if (ret) {
|
||||
apfs_alert(sb, "bad xattr key in inode %llx", cnid);
|
||||
apfs_err(sb, "bad xattr key in inode %llx", cnid);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user