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;
|
||||
}
|
||||
|
||||
@@ -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,6 +779,15 @@ struct apfs_xattr {
|
||||
bool has_dstream;
|
||||
};
|
||||
|
||||
struct apfs_compressed_data {
|
||||
bool has_dstream;
|
||||
u64 size;
|
||||
union {
|
||||
struct apfs_dstream_info *dstream;
|
||||
void *data;
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
* Report function name and line number for the message types that are likely
|
||||
* to signal a bug, to make things easier for reporters. Don't do this for the
|
||||
@@ -788,7 +802,7 @@ struct apfs_xattr {
|
||||
#define apfs_info(sb, fmt, ...) apfs_msg(sb, KERN_INFO, NULL, 0, fmt, ##__VA_ARGS__)
|
||||
|
||||
#ifdef CONFIG_APFS_DEBUG
|
||||
#define ASSERT(expr) BUG_ON(!(expr))
|
||||
#define ASSERT(expr) WARN_ON(!(expr))
|
||||
#define apfs_debug(sb, fmt, ...) apfs_msg(sb, KERN_DEBUG, __func__, __LINE__, fmt, ##__VA_ARGS__)
|
||||
#else
|
||||
#define ASSERT(expr) ((void)0)
|
||||
@@ -885,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 */
|
||||
@@ -916,7 +937,11 @@ extern int apfs_setattr(struct mnt_idmap *idmap,
|
||||
struct dentry *dentry, struct iattr *iattr);
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
||||
extern int apfs_update_time(struct inode *inode, struct timespec64 *time, int flags);
|
||||
#else
|
||||
extern int apfs_update_time(struct inode *inode, int flags);
|
||||
#endif
|
||||
long apfs_dir_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
|
||||
long apfs_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
|
||||
|
||||
@@ -1029,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);
|
||||
@@ -1042,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 */
|
||||
@@ -842,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 */
|
||||
@@ -956,7 +955,7 @@ 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);
|
||||
|
||||
+318
-355
File diff suppressed because it is too large.
Load diff
@@ -268,7 +268,7 @@ static int apfs_build_dentry_unhashed_key(struct qstr *qname, u64 parent_id,
|
||||
|
||||
apfs_key_set_hdr(APFS_TYPE_DIR_REC, parent_id, key);
|
||||
key->name_len = cpu_to_le16(namelen);
|
||||
strcpy(key->name, qname->name);
|
||||
strscpy(key->name, qname->name, namelen);
|
||||
|
||||
*key_p = key;
|
||||
return key_len;
|
||||
@@ -297,7 +297,7 @@ static int apfs_build_dentry_hashed_key(struct qstr *qname, u64 hash, u64 parent
|
||||
|
||||
apfs_key_set_hdr(APFS_TYPE_DIR_REC, parent_id, key);
|
||||
key->name_len_and_hash = cpu_to_le32(namelen | hash);
|
||||
strcpy(key->name, qname->name);
|
||||
strscpy(key->name, qname->name, namelen);
|
||||
|
||||
*key_p = key;
|
||||
return key_len;
|
||||
@@ -434,7 +434,7 @@ static int apfs_build_sibling_val(struct dentry *dentry,
|
||||
|
||||
val->parent_id = cpu_to_le64(apfs_ino(parent));
|
||||
val->name_len = cpu_to_le16(namelen);
|
||||
strcpy(val->name, qname->name);
|
||||
strscpy(val->name, qname->name, namelen);
|
||||
|
||||
*val_p = val;
|
||||
return val_len;
|
||||
@@ -601,7 +601,11 @@ static int apfs_create_dentry(struct dentry *dentry, struct inode *inode)
|
||||
}
|
||||
|
||||
/* Now update the parent inode */
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
||||
parent->i_mtime = parent->i_ctime = current_time(inode);
|
||||
#else
|
||||
parent->i_mtime = inode_set_ctime_current(parent);
|
||||
#endif
|
||||
++APFS_I(parent)->i_nchildren;
|
||||
apfs_inode_join_transaction(parent->i_sb, parent);
|
||||
return 0;
|
||||
@@ -841,7 +845,11 @@ static int __apfs_link(struct dentry *old_dentry, struct dentry *dentry)
|
||||
|
||||
/* First update the inode's link count */
|
||||
inc_nlink(inode);
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
||||
inode->i_ctime = current_time(inode);
|
||||
#else
|
||||
inode_set_ctime_current(inode);
|
||||
#endif
|
||||
apfs_inode_join_transaction(inode->i_sb, inode);
|
||||
|
||||
if (inode->i_nlink == 2) {
|
||||
@@ -1030,7 +1038,11 @@ static int apfs_delete_dentry(struct dentry *dentry)
|
||||
}
|
||||
|
||||
/* Now update the parent inode */
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
||||
parent->i_mtime = parent->i_ctime = current_time(parent);
|
||||
#else
|
||||
parent->i_mtime = inode_set_ctime_current(parent);
|
||||
#endif
|
||||
--APFS_I(parent)->i_nchildren;
|
||||
apfs_inode_join_transaction(sb, parent);
|
||||
return err;
|
||||
@@ -1089,7 +1101,7 @@ static int apfs_sibling_link_from_query(struct apfs_query *query,
|
||||
*name = kmalloc(namelen, GFP_KERNEL);
|
||||
if (!*name)
|
||||
return -ENOMEM;
|
||||
strcpy(*name, siblink->name);
|
||||
strscpy(*name, siblink->name, namelen);
|
||||
*parent = le64_to_cpu(siblink->parent_id);
|
||||
return 0;
|
||||
}
|
||||
@@ -1194,7 +1206,11 @@ static int apfs_create_orphan_link(struct inode *inode)
|
||||
}
|
||||
|
||||
/* Now update the child count for private-dir */
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
||||
priv_dir->i_mtime = priv_dir->i_ctime = current_time(priv_dir);
|
||||
#else
|
||||
priv_dir->i_mtime = inode_set_ctime_current(priv_dir);
|
||||
#endif
|
||||
++APFS_I(priv_dir)->i_nchildren;
|
||||
apfs_inode_join_transaction(sb, priv_dir);
|
||||
|
||||
@@ -1239,7 +1255,11 @@ int apfs_delete_orphan_link(struct inode *inode)
|
||||
}
|
||||
|
||||
/* Now update the child count for private-dir */
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
||||
priv_dir->i_mtime = priv_dir->i_ctime = current_time(priv_dir);
|
||||
#else
|
||||
priv_dir->i_mtime = inode_set_ctime_current(priv_dir);
|
||||
#endif
|
||||
--APFS_I(priv_dir)->i_nchildren;
|
||||
apfs_inode_join_transaction(sb, priv_dir);
|
||||
|
||||
@@ -1330,7 +1350,11 @@ static int __apfs_unlink(struct inode *dir, struct dentry *dentry)
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
||||
inode->i_ctime = dir->i_ctime;
|
||||
#else
|
||||
inode_set_ctime_to_ts(inode, inode_get_ctime(dir));
|
||||
#endif
|
||||
/* TODO: defer write of the primary name? */
|
||||
err = apfs_update_inode(inode, primary_name);
|
||||
if (err)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
PACKAGE_NAME="linux-apfs-rw"
|
||||
PACKAGE_VERSION="0.3.2"
|
||||
PACKAGE_VERSION="0.3.5"
|
||||
|
||||
BUILT_MODULE_NAME[0]="apfs"
|
||||
DEST_MODULE_LOCATION[0]="/extra"
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include <linux/buffer_head.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/blk_types.h>
|
||||
#include "apfs.h"
|
||||
|
||||
#define MAX(X, Y) ((X) <= (Y) ? (Y) : (X))
|
||||
@@ -46,7 +47,7 @@ int apfs_extent_from_query(struct apfs_query *query,
|
||||
char *raw = query->node->object.data;
|
||||
u64 ext_len;
|
||||
|
||||
if(!apfs_is_sealed(sb)) {
|
||||
if (!apfs_is_sealed(sb)) {
|
||||
struct apfs_file_extent_val *ext = NULL;
|
||||
struct apfs_file_extent_key *ext_key = NULL;
|
||||
|
||||
@@ -601,7 +602,7 @@ static int apfs_update_mid_extent(struct apfs_dstream_info *dstream, const struc
|
||||
raw_key.logical_addr = cpu_to_le64(extent->logical_addr);
|
||||
raw_val.len_and_flags = cpu_to_le64(extent->len);
|
||||
raw_val.phys_block_num = cpu_to_le64(extent->phys_block_num);
|
||||
if(apfs_vol_is_encrypted(sb))
|
||||
if (apfs_vol_is_encrypted(sb))
|
||||
new_crypto = extent_id;
|
||||
else
|
||||
new_crypto = 0;
|
||||
@@ -1636,7 +1637,11 @@ int apfs_truncate(struct apfs_dstream_info *dstream, loff_t new_size)
|
||||
return apfs_create_hole(dstream, old_blks, new_blks);
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
|
||||
loff_t apfs_remap_file_range(struct file *src_file, loff_t off, struct file *dst_file, loff_t destoff, loff_t len, unsigned int remap_flags)
|
||||
#else
|
||||
int apfs_clone_file_range(struct file *src_file, loff_t off, struct file *dst_file, loff_t destoff, u64 len)
|
||||
#endif
|
||||
{
|
||||
struct inode *src_inode = file_inode(src_file);
|
||||
struct inode *dst_inode = file_inode(dst_file);
|
||||
@@ -1651,8 +1656,10 @@ loff_t apfs_remap_file_range(struct file *src_file, loff_t off, struct file *dst
|
||||
const u64 xfield_flags = APFS_INODE_MAINTAIN_DIR_STATS | APFS_INODE_IS_SPARSE | APFS_INODE_HAS_PURGEABLE_FLAGS;
|
||||
int err;
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
|
||||
if (remap_flags & ~(REMAP_FILE_ADVISORY))
|
||||
return -EINVAL;
|
||||
#endif
|
||||
if (src_inode == dst_inode)
|
||||
return -EINVAL;
|
||||
|
||||
@@ -1697,7 +1704,11 @@ loff_t apfs_remap_file_range(struct file *src_file, loff_t off, struct file *dst
|
||||
}
|
||||
src_ds->ds_shared = true;
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
||||
dst_inode->i_mtime = dst_inode->i_ctime = current_time(dst_inode);
|
||||
#else
|
||||
dst_inode->i_mtime = inode_set_ctime_current(dst_inode);
|
||||
#endif
|
||||
dst_inode->i_size = src_inode->i_size;
|
||||
dst_ai->i_key_class = src_ai->i_key_class;
|
||||
dst_ai->i_int_flags = src_ai->i_int_flags;
|
||||
@@ -2124,3 +2135,139 @@ int apfs_clone_extents(struct apfs_dstream_info *dstream, u64 new_id)
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* apfs_nonsparse_dstream_read - Read from a dstream without holes
|
||||
* @dstream: dstream to read
|
||||
* @buf: destination buffer
|
||||
* @count: exact number of bytes to read
|
||||
* @offset: dstream offset to read from
|
||||
*
|
||||
* Returns 0 on success or a negative error code in case of failure.
|
||||
*/
|
||||
int apfs_nonsparse_dstream_read(struct apfs_dstream_info *dstream, void *buf, size_t count, u64 offset)
|
||||
{
|
||||
struct super_block *sb = dstream->ds_sb;
|
||||
u64 logical_start_block, logical_end_block, log_bno, blkcnt, idx;
|
||||
struct buffer_head **bhs = NULL;
|
||||
int ret = 0;
|
||||
|
||||
/* Save myself from thinking about overflow here */
|
||||
if (count >= APFS_MAX_FILE_SIZE || offset >= APFS_MAX_FILE_SIZE) {
|
||||
apfs_err(sb, "dstream read overflow (0x%llx-0x%llx)", offset, (unsigned long long)count);
|
||||
return -EFBIG;
|
||||
}
|
||||
|
||||
if (offset + count > dstream->ds_size) {
|
||||
apfs_err(sb, "reading past the end (0x%llx-0x%llx)", offset, (unsigned long long)count);
|
||||
/* No caller is expected to legitimately read out-of-bounds */
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
|
||||
logical_start_block = offset >> sb->s_blocksize_bits;
|
||||
logical_end_block = (offset + count + sb->s_blocksize - 1) >> sb->s_blocksize_bits;
|
||||
blkcnt = logical_end_block - logical_start_block;
|
||||
bhs = kcalloc(blkcnt, sizeof(*bhs), GFP_KERNEL);
|
||||
if (!bhs)
|
||||
return -ENOMEM;
|
||||
|
||||
for (log_bno = logical_start_block; log_bno < logical_end_block; log_bno++) {
|
||||
struct buffer_head *bh = NULL;
|
||||
u64 bno = 0;
|
||||
|
||||
idx = log_bno - logical_start_block;
|
||||
|
||||
ret = apfs_logic_to_phys_bno(dstream, log_bno, &bno);
|
||||
if (ret)
|
||||
goto out;
|
||||
if (bno == 0) {
|
||||
apfs_err(sb, "nonsparse dstream has a hole");
|
||||
ret = -EFSCORRUPTED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
bhs[idx] = __getblk_gfp(APFS_NXI(sb)->nx_bdev, bno, sb->s_blocksize, __GFP_MOVABLE);
|
||||
if (!bhs[idx]) {
|
||||
apfs_err(sb, "failed to map block 0x%llx", bno);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
bh = bhs[idx];
|
||||
if (!buffer_uptodate(bh)) {
|
||||
get_bh(bh);
|
||||
lock_buffer(bh);
|
||||
bh->b_end_io = end_buffer_read_sync;
|
||||
apfs_submit_bh(REQ_OP_READ, 0, bh);
|
||||
}
|
||||
}
|
||||
|
||||
for (log_bno = logical_start_block; log_bno < logical_end_block; log_bno++) {
|
||||
int off_in_block, left_in_block;
|
||||
|
||||
idx = log_bno - logical_start_block;
|
||||
wait_on_buffer(bhs[idx]);
|
||||
if (!buffer_uptodate(bhs[idx])) {
|
||||
apfs_err(sb, "failed to read a block");
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (log_bno == logical_start_block)
|
||||
off_in_block = offset & (sb->s_blocksize - 1);
|
||||
else
|
||||
off_in_block = 0;
|
||||
|
||||
if (log_bno == logical_end_block - 1)
|
||||
left_in_block = count + offset - (log_bno << sb->s_blocksize_bits) - off_in_block;
|
||||
else
|
||||
left_in_block = sb->s_blocksize - off_in_block;
|
||||
|
||||
memcpy(buf, bhs[idx]->b_data + off_in_block, left_in_block);
|
||||
buf += left_in_block;
|
||||
}
|
||||
|
||||
out:
|
||||
if (bhs) {
|
||||
for (idx = 0; idx < blkcnt; idx++)
|
||||
brelse(bhs[idx]);
|
||||
kfree(bhs);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* apfs_nonsparse_dstream_preread - Attempt to preread a dstream without holes
|
||||
* @dstream: dstream to preread
|
||||
*
|
||||
* Requests reads for all blocks of a dstream, but doesn't wait for the result.
|
||||
*/
|
||||
void apfs_nonsparse_dstream_preread(struct apfs_dstream_info *dstream)
|
||||
{
|
||||
struct super_block *sb = dstream->ds_sb;
|
||||
u64 logical_end_block, log_bno;
|
||||
|
||||
logical_end_block = (dstream->ds_size + sb->s_blocksize - 1) >> sb->s_blocksize_bits;
|
||||
|
||||
for (log_bno = 0; log_bno < logical_end_block; log_bno++) {
|
||||
struct buffer_head *bh = NULL;
|
||||
u64 bno = 0;
|
||||
int ret;
|
||||
|
||||
ret = apfs_logic_to_phys_bno(dstream, log_bno, &bno);
|
||||
if (ret || bno == 0)
|
||||
return;
|
||||
|
||||
bh = __getblk_gfp(APFS_NXI(sb)->nx_bdev, bno, sb->s_blocksize, __GFP_MOVABLE);
|
||||
if (!bh)
|
||||
return;
|
||||
if (!buffer_uptodate(bh)) {
|
||||
get_bh(bh);
|
||||
lock_buffer(bh);
|
||||
bh->b_end_io = end_buffer_read_sync;
|
||||
apfs_submit_bh(REQ_OP_READ, 0, bh);
|
||||
}
|
||||
brelse(bh);
|
||||
bh = NULL;
|
||||
}
|
||||
}
|
||||
@@ -99,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;
|
||||
}
|
||||
@@ -110,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;
|
||||
|
||||
@@ -145,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)
|
||||
|
||||
@@ -288,14 +288,16 @@ static int apfs_create_crypto_rec(struct inode *inode)
|
||||
goto out;
|
||||
|
||||
apfs_key_set_hdr(APFS_TYPE_CRYPTO_STATE, dstream->ds_id, &raw_key);
|
||||
if(sbi->s_dflt_pfk) {
|
||||
if (sbi->s_dflt_pfk) {
|
||||
struct apfs_crypto_state_val *raw_val = sbi->s_dflt_pfk;
|
||||
unsigned key_len = le16_to_cpu(raw_val->state.key_len);
|
||||
unsigned int key_len = le16_to_cpu(raw_val->state.key_len);
|
||||
|
||||
ret = apfs_btree_insert(query, &raw_key, sizeof(raw_key), raw_val, sizeof(*raw_val) + key_len);
|
||||
if (ret)
|
||||
apfs_err(sb, "insertion failed for id 0x%llx", dstream->ds_id);
|
||||
} else {
|
||||
struct apfs_crypto_state_val raw_val;
|
||||
|
||||
raw_val.refcnt = cpu_to_le32(1);
|
||||
raw_val.state.major_version = cpu_to_le16(APFS_WMCS_MAJOR_VERSION);
|
||||
raw_val.state.minor_version = cpu_to_le16(APFS_WMCS_MINOR_VERSION);
|
||||
@@ -318,7 +320,7 @@ out:
|
||||
* apfs_dflt_key_class - Returns default key class for files in volume
|
||||
* @sb: volume superblock
|
||||
*/
|
||||
static unsigned apfs_dflt_key_class(struct super_block *sb)
|
||||
static unsigned int apfs_dflt_key_class(struct super_block *sb)
|
||||
{
|
||||
struct apfs_sb_info *sbi = APFS_SB(sb);
|
||||
|
||||
@@ -399,7 +401,7 @@ static int apfs_crypto_set_key(struct super_block *sb, u64 crypto_id, struct apf
|
||||
struct apfs_crypto_state_val *raw_val;
|
||||
char *raw;
|
||||
int ret;
|
||||
unsigned pfk_len;
|
||||
unsigned int pfk_len;
|
||||
|
||||
if (!crypto_id)
|
||||
return 0;
|
||||
@@ -441,7 +443,7 @@ out:
|
||||
* @max_len: maximum allowed value of val->state.key_len
|
||||
*/
|
||||
static int apfs_crypto_get_key(struct super_block *sb, u64 crypto_id, struct apfs_crypto_state_val *val,
|
||||
unsigned max_len)
|
||||
unsigned int max_len)
|
||||
{
|
||||
struct apfs_sb_info *sbi = APFS_SB(sb);
|
||||
struct apfs_key key;
|
||||
@@ -449,7 +451,7 @@ static int apfs_crypto_get_key(struct super_block *sb, u64 crypto_id, struct apf
|
||||
struct apfs_crypto_state_val *raw_val;
|
||||
char *raw;
|
||||
int ret;
|
||||
unsigned pfk_len;
|
||||
unsigned int pfk_len;
|
||||
|
||||
if (!crypto_id)
|
||||
return -ENOENT;
|
||||
@@ -468,7 +470,7 @@ static int apfs_crypto_get_key(struct super_block *sb, u64 crypto_id, struct apf
|
||||
raw_val = (void *)raw + query->off;
|
||||
|
||||
pfk_len = le16_to_cpu(raw_val->state.key_len);
|
||||
if(pfk_len > max_len) {
|
||||
if (pfk_len > max_len) {
|
||||
ret = -ENOSPC;
|
||||
goto out;
|
||||
}
|
||||
@@ -501,7 +503,7 @@ int __apfs_write_begin(struct file *file, struct address_space *mapping, loff_t
|
||||
return err;
|
||||
}
|
||||
|
||||
if(apfs_vol_is_encrypted(sb)) {
|
||||
if (apfs_vol_is_encrypted(sb)) {
|
||||
err = apfs_create_crypto_rec(inode);
|
||||
if (err) {
|
||||
apfs_err(sb, "crypto creation failed for ino 0x%llx", apfs_ino(inode));
|
||||
@@ -679,6 +681,12 @@ static void apfs_noop_invalidate_folio(struct folio *folio, size_t offset, size_
|
||||
|
||||
/* bmap is not implemented to avoid issues with CoW on swapfiles */
|
||||
static const struct address_space_operations apfs_aops = {
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0)
|
||||
.dirty_folio = block_dirty_folio,
|
||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 14, 0)
|
||||
.set_page_dirty = __set_page_dirty_buffers,
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 0)
|
||||
.read_folio = apfs_read_folio,
|
||||
#else
|
||||
@@ -716,11 +724,13 @@ static void apfs_inode_set_ops(struct inode *inode, dev_t rdev, bool compressed)
|
||||
switch (inode->i_mode & S_IFMT) {
|
||||
case S_IFREG:
|
||||
inode->i_op = &apfs_file_inode_operations;
|
||||
if (compressed)
|
||||
if (compressed) {
|
||||
inode->i_fop = &apfs_compress_file_operations;
|
||||
else
|
||||
inode->i_mapping->a_ops = &apfs_compress_aops;
|
||||
} else {
|
||||
inode->i_fop = &apfs_file_operations;
|
||||
inode->i_mapping->a_ops = &apfs_aops;
|
||||
inode->i_mapping->a_ops = &apfs_aops;
|
||||
}
|
||||
break;
|
||||
case S_IFDIR:
|
||||
inode->i_op = &apfs_dir_inode_operations;
|
||||
@@ -790,7 +800,11 @@ static int apfs_inode_from_query(struct apfs_query *query, struct inode *inode)
|
||||
}
|
||||
|
||||
inode->i_atime = ns_to_timespec64(le64_to_cpu(inode_val->access_time));
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
||||
inode->i_ctime = ns_to_timespec64(le64_to_cpu(inode_val->change_time));
|
||||
#else
|
||||
inode_set_ctime_to_ts(inode, ns_to_timespec64(le64_to_cpu(inode_val->change_time)));
|
||||
#endif
|
||||
inode->i_mtime = ns_to_timespec64(le64_to_cpu(inode_val->mod_time));
|
||||
ai->i_crtime = ns_to_timespec64(le64_to_cpu(inode_val->create_time));
|
||||
|
||||
@@ -1073,8 +1087,10 @@ int apfs_getattr(struct mnt_idmap *idmap,
|
||||
generic_fillattr(inode, stat);
|
||||
#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 3, 0)
|
||||
generic_fillattr(mnt_userns, inode, stat);
|
||||
#else
|
||||
#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
||||
generic_fillattr(idmap, inode, stat);
|
||||
#else
|
||||
generic_fillattr(idmap, request_mask, inode, stat);
|
||||
#endif
|
||||
|
||||
stat->ino = apfs_ino(inode);
|
||||
@@ -1225,7 +1241,7 @@ static int apfs_create_dstream_xfield(struct inode *inode,
|
||||
|
||||
dstream_raw.size = cpu_to_le64(inode->i_size);
|
||||
dstream_raw.alloced_size = cpu_to_le64(apfs_alloced_size(dstream));
|
||||
if(apfs_vol_is_encrypted(inode->i_sb))
|
||||
if (apfs_vol_is_encrypted(inode->i_sb))
|
||||
dstream_raw.default_crypto_id = cpu_to_le64(dstream->ds_id);
|
||||
|
||||
/* TODO: can we assume that all inode records have an xfield blob? */
|
||||
@@ -1477,7 +1493,12 @@ int apfs_update_inode(struct inode *inode, char *new_name)
|
||||
inode_raw->group = cpu_to_le32(ai->i_saved_gid);
|
||||
|
||||
inode_raw->access_time = cpu_to_le64(timespec64_to_ns(&inode->i_atime));
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
||||
inode_raw->change_time = cpu_to_le64(timespec64_to_ns(&inode->i_ctime));
|
||||
#else
|
||||
struct timespec64 ictime = inode_get_ctime(inode);
|
||||
inode_raw->change_time = cpu_to_le64(timespec64_to_ns(&ictime));
|
||||
#endif
|
||||
inode_raw->mod_time = cpu_to_le64(timespec64_to_ns(&inode->i_mtime));
|
||||
inode_raw->create_time = cpu_to_le64(timespec64_to_ns(&ai->i_crtime));
|
||||
|
||||
@@ -1657,7 +1678,12 @@ struct inode *apfs_new_inode(struct inode *dir, umode_t mode, dev_t rdev)
|
||||
dstream->ds_shared = false;
|
||||
|
||||
now = current_time(inode);
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
||||
inode->i_atime = inode->i_mtime = inode->i_ctime = ai->i_crtime = now;
|
||||
#else
|
||||
inode_set_ctime_to_ts(inode, now);
|
||||
inode->i_atime = inode->i_mtime = ai->i_crtime = now;
|
||||
#endif
|
||||
vsb_raw->apfs_last_mod_time = cpu_to_le64(timespec64_to_ns(&now));
|
||||
|
||||
if (S_ISREG(mode))
|
||||
@@ -1751,7 +1777,11 @@ static int apfs_setsize(struct inode *inode, loff_t new_size)
|
||||
|
||||
if (new_size == inode->i_size)
|
||||
return 0;
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
||||
inode->i_mtime = inode->i_ctime = current_time(inode);
|
||||
#else
|
||||
inode->i_mtime = inode_set_ctime_current(inode);
|
||||
#endif
|
||||
|
||||
err = apfs_inode_create_dstream_rec(inode);
|
||||
if (err) {
|
||||
@@ -1837,7 +1867,11 @@ fail:
|
||||
}
|
||||
|
||||
/* TODO: this only seems to be necessary because ->write_inode() isn't firing */
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
||||
int apfs_update_time(struct inode *inode, struct timespec64 *time, int flags)
|
||||
#else
|
||||
int apfs_update_time(struct inode *inode, int flags)
|
||||
#endif
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct apfs_max_ops maxops;
|
||||
@@ -1851,9 +1885,11 @@ int apfs_update_time(struct inode *inode, struct timespec64 *time, int flags)
|
||||
return err;
|
||||
apfs_inode_join_transaction(sb, inode);
|
||||
|
||||
err = generic_update_time(inode, time, flags);
|
||||
if (err)
|
||||
goto fail;
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
||||
generic_update_time(inode, time, flags);
|
||||
#else
|
||||
generic_update_time(inode, flags);
|
||||
#endif
|
||||
|
||||
err = apfs_transaction_commit(sb);
|
||||
if (err)
|
||||
@@ -1873,7 +1909,7 @@ static int apfs_ioc_set_dflt_pfk(struct file *file, void __user *user_pfk)
|
||||
struct apfs_nxsb_info *nxi = APFS_NXI(sb);
|
||||
struct apfs_wrapped_crypto_state pfk_hdr;
|
||||
struct apfs_crypto_state_val *pfk;
|
||||
unsigned key_len;
|
||||
unsigned int key_len;
|
||||
|
||||
if (__copy_from_user(&pfk_hdr, user_pfk, sizeof(pfk_hdr)))
|
||||
return -EFAULT;
|
||||
@@ -1940,7 +1976,7 @@ static int apfs_ioc_set_pfk(struct file *file, void __user *user_pfk)
|
||||
struct apfs_inode_info *ai = APFS_I(inode);
|
||||
struct apfs_dstream_info *dstream = &ai->i_dstream;
|
||||
struct apfs_max_ops maxops;
|
||||
unsigned key_len, key_class;
|
||||
unsigned int key_len, key_class;
|
||||
int err;
|
||||
|
||||
if (__copy_from_user(&pfk_hdr, user_pfk, sizeof(pfk_hdr)))
|
||||
@@ -2007,7 +2043,7 @@ static int apfs_ioc_get_pfk(struct file *file, void __user *user_pfk)
|
||||
struct apfs_nxsb_info *nxi = APFS_NXI(sb);
|
||||
struct apfs_wrapped_crypto_state pfk_hdr;
|
||||
struct apfs_crypto_state_val *pfk;
|
||||
unsigned max_len, key_len;
|
||||
unsigned int max_len, key_len;
|
||||
struct apfs_dstream_info *dstream = &APFS_I(inode)->i_dstream;
|
||||
int err;
|
||||
|
||||
@@ -2274,7 +2310,11 @@ int apfs_fileattr_set(struct mnt_idmap *idmap, struct dentry *dentry, struct fil
|
||||
|
||||
apfs_inode_join_transaction(sb, inode);
|
||||
apfs_setflags(inode, fa->flags);
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
||||
inode->i_ctime = current_time(inode);
|
||||
#else
|
||||
inode_set_ctime_current(inode);
|
||||
#endif
|
||||
|
||||
err = apfs_transaction_commit(sb);
|
||||
if (err)
|
||||
|
||||
@@ -108,6 +108,9 @@ struct apfs_node *apfs_read_node(struct super_block *sb, u64 oid, u32 storage,
|
||||
return (void *)bh;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
apfs_alert(sb, "invalid storage type %u - bug!", storage);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
raw = (struct apfs_btree_node_phys *) bh->b_data;
|
||||
|
||||
@@ -138,12 +141,12 @@ struct apfs_node *apfs_read_node(struct super_block *sb, u64 oid, u32 storage,
|
||||
|
||||
if (nxi->nx_flags & APFS_CHECK_NODES && !apfs_obj_verify_csum(sb, bh)) {
|
||||
/* TODO: don't check this twice for virtual/physical objects */
|
||||
apfs_err(sb, "bad checksum for node in block 0x%llx", bh->b_blocknr);
|
||||
apfs_err(sb, "bad checksum for node in block 0x%llx", (unsigned long long)bh->b_blocknr);
|
||||
apfs_node_free(node);
|
||||
return ERR_PTR(-EFSBADCRC);
|
||||
}
|
||||
if (!apfs_node_is_valid(sb, node)) {
|
||||
apfs_err(sb, "bad node in block 0x%llx", bh->b_blocknr);
|
||||
apfs_err(sb, "bad node in block 0x%llx", (unsigned long long)bh->b_blocknr);
|
||||
apfs_node_free(node);
|
||||
return ERR_PTR(-EFSCORRUPTED);
|
||||
}
|
||||
@@ -374,8 +377,8 @@ static struct apfs_node *apfs_create_node(struct super_block *sb, u32 storage)
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ASSERT(false);
|
||||
return ERR_PTR(-EINVAL); /* Prevent compiler warnings */
|
||||
apfs_alert(sb, "invalid storage type %u - bug!", storage);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
bh = apfs_getblk(sb, bno);
|
||||
@@ -869,6 +872,7 @@ int apfs_node_query(struct super_block *sb, struct apfs_query *query)
|
||||
left = 0;
|
||||
do {
|
||||
struct apfs_key curr_key;
|
||||
|
||||
if (cmp > 0) {
|
||||
right = query->index - 1;
|
||||
if (right < left) {
|
||||
|
||||
@@ -383,7 +383,7 @@ 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", bh->b_blocknr);
|
||||
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);
|
||||
|
||||
+2
-2
@@ -116,7 +116,7 @@ static int apfs_create_snap_metadata_rec(struct inode *mntpoint, struct apfs_nod
|
||||
raw_val->extentref_tree_type = vsb_raw->apfs_extentref_tree_type;
|
||||
raw_val->flags = 0;
|
||||
raw_val->name_len = cpu_to_le16(name_len + 1); /* Count the null byte */
|
||||
strcpy(raw_val->name, name);
|
||||
strscpy(raw_val->name, name, name_len + 1);
|
||||
|
||||
apfs_assert_in_transaction(sb, &vsb_raw->apfs_o);
|
||||
raw_val->inum = vsb_raw->apfs_next_obj_id;
|
||||
@@ -173,7 +173,7 @@ static int apfs_create_snap_name_rec(struct apfs_node *snap_root, const char *na
|
||||
}
|
||||
apfs_key_set_hdr(APFS_TYPE_SNAP_NAME, APFS_SNAP_NAME_OBJ_ID, raw_key);
|
||||
raw_key->name_len = cpu_to_le16(name_len + 1); /* Count the null byte */
|
||||
strcpy(raw_key->name, name);
|
||||
strscpy(raw_key->name, name, name_len + 1);
|
||||
|
||||
raw_val.snap_xid = cpu_to_le64(APFS_NXI(sb)->nx_xid);
|
||||
|
||||
|
||||
+6
-8
@@ -361,7 +361,7 @@ static int apfs_flush_fq_rec(struct apfs_node *root, u64 xid, u64 *len)
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -406,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);
|
||||
@@ -829,7 +829,7 @@ static int apfs_chunk_alloc_free(struct super_block *sb,
|
||||
if (!ci->ci_bitmap_addr) {
|
||||
u64 bmap_bno;
|
||||
|
||||
if(!is_alloc) {
|
||||
if (!is_alloc) {
|
||||
apfs_err(sb, "attempt to free block in all-free chunk");
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
@@ -930,10 +930,9 @@ 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");
|
||||
@@ -943,11 +942,10 @@ static int apfs_chunk_alloc_free(struct super_block *sb,
|
||||
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;
|
||||
@@ -1102,7 +1100,7 @@ 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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
+10
-9
@@ -531,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);
|
||||
@@ -542,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 */
|
||||
@@ -554,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);
|
||||
}
|
||||
|
||||
@@ -686,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;
|
||||
|
||||
@@ -736,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;
|
||||
|
||||
@@ -751,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;
|
||||
@@ -761,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;
|
||||
}
|
||||
|
||||
@@ -845,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;
|
||||
@@ -923,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;
|
||||
|
||||
|
||||
@@ -112,9 +112,7 @@ static int apfs_xattr_extents_read(struct inode *parent,
|
||||
{
|
||||
struct super_block *sb = parent->i_sb;
|
||||
struct apfs_dstream_info *dstream;
|
||||
int length, blkcnt, i;
|
||||
struct buffer_head **bhs = NULL;
|
||||
int ret;
|
||||
int length, ret;
|
||||
|
||||
dstream = kzalloc(sizeof(*dstream), GFP_KERNEL);
|
||||
if (!dstream)
|
||||
@@ -146,59 +144,11 @@ static int apfs_xattr_extents_read(struct inode *parent,
|
||||
length = size;
|
||||
}
|
||||
|
||||
blkcnt = (length + sb->s_blocksize - 1) >> sb->s_blocksize_bits;
|
||||
bhs = kcalloc(blkcnt, sizeof(*bhs), GFP_KERNEL);
|
||||
for (i = 0; i < blkcnt; i++) {
|
||||
struct buffer_head *bh = NULL;
|
||||
u64 bno = 0;
|
||||
|
||||
ret = apfs_logic_to_phys_bno(dstream, i, &bno);
|
||||
if (ret)
|
||||
goto out;
|
||||
if (bno == 0) {
|
||||
/* No holes in xattr dstreams, I believe */
|
||||
apfs_err(sb, "xattr dstream has a hole");
|
||||
ret = -EFSCORRUPTED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
bhs[i] = __getblk_gfp(APFS_NXI(sb)->nx_bdev, bno, sb->s_blocksize, __GFP_MOVABLE);
|
||||
if (!bhs[i]) {
|
||||
apfs_err(sb, "failed to map block 0x%llx", bno);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
bh = bhs[i];
|
||||
if (!buffer_uptodate(bh)) {
|
||||
get_bh(bh);
|
||||
lock_buffer(bh);
|
||||
bh->b_end_io = end_buffer_read_sync;
|
||||
apfs_submit_bh(REQ_OP_READ, 0, bh);
|
||||
}
|
||||
}
|
||||
for (i = 0; i < blkcnt; i++) {
|
||||
int off, tocopy;
|
||||
|
||||
wait_on_buffer(bhs[i]);
|
||||
if (!buffer_uptodate(bhs[i])) {
|
||||
apfs_err(sb, "failed to read a block");
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
off = i << sb->s_blocksize_bits;
|
||||
tocopy = min(sb->s_blocksize, (unsigned long)(length - off));
|
||||
memcpy(buffer + off, bhs[i]->b_data, tocopy);
|
||||
}
|
||||
ret = length;
|
||||
ret = apfs_nonsparse_dstream_read(dstream, buffer, length, 0 /* offset */);
|
||||
if (ret == 0)
|
||||
ret = length;
|
||||
|
||||
out:
|
||||
if (bhs) {
|
||||
for (i = 0; i < blkcnt; i++)
|
||||
brelse(bhs[i]);
|
||||
kfree(bhs);
|
||||
}
|
||||
kfree(dstream);
|
||||
return ret;
|
||||
}
|
||||
@@ -233,6 +183,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
|
||||
@@ -465,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;
|
||||
|
||||
Reference in new issue
Block a user