mirror of
https://github.com/linux-apfs/linux-apfs-rw.git
synced 2026-08-18 02:19:59 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f95bcc94c9 | ||
|
|
911bbe1743 | ||
|
|
6d54029be9 | ||
|
|
88b36a788e | ||
|
|
ef79bb1bee | ||
|
|
67eb48bb34 | ||
|
|
9aadac614c | ||
|
|
1158bd03a6 | ||
|
|
351c7cf826 | ||
|
|
61672f2caf | ||
|
|
0459ba0998 | ||
|
|
1db8a6d92a | ||
|
|
a3bca211bd | ||
|
|
de721cd995 | ||
|
|
0daf28cef7 | ||
|
|
c76aecff25 | ||
|
|
714202fefa | ||
|
|
44db9dc02e | ||
|
|
aefae5433f |
No files matched your search
@@ -14,9 +14,9 @@ apfs-y := btree.o compress.o dir.o extents.o file.o inode.o key.o libzbitmap.o \
|
||||
spaceman.o super.o symlink.o transaction.o unicode.o xattr.o xfield.o
|
||||
|
||||
# If you want mounts to be writable by default, run the build as:
|
||||
# make CONFIG=-DCONFIG_APFS_RW_ALWAYS
|
||||
# make APFS_CONFIG=-DCONFIG_APFS_RW_ALWAYS
|
||||
# This is risky and not generally recommended.
|
||||
ccflags-y += $(CONFIG)
|
||||
ccflags-y += $(APFS_CONFIG)
|
||||
|
||||
default:
|
||||
./genver.sh
|
||||
|
||||
@@ -380,6 +380,7 @@ struct apfs_sb_info {
|
||||
unsigned int s_vol_nr; /* Index of the volume in the sb list */
|
||||
kuid_t s_uid; /* uid to override on-disk uid */
|
||||
kgid_t s_gid; /* gid to override on-disk gid */
|
||||
unsigned int s_mount_opt;
|
||||
|
||||
struct apfs_crypto_state_val *s_dflt_pfk; /* default per-file key */
|
||||
|
||||
@@ -1021,8 +1022,13 @@ extern struct inode *apfs_new_inode(struct inode *dir, umode_t mode,
|
||||
extern int apfs_create_inode_rec(struct super_block *sb, struct inode *inode,
|
||||
struct dentry *dentry);
|
||||
extern int apfs_inode_create_exclusive_dstream(struct inode *inode);
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 17, 0)
|
||||
extern int __apfs_write_begin(const struct kiocb *iocb, struct address_space *mapping, loff_t pos, unsigned int len, unsigned int flags, struct page **pagep, void **fsdata);
|
||||
extern int __apfs_write_end(const struct kiocb *iocb, struct address_space *mapping, loff_t pos, unsigned int len, unsigned int copied, struct page *page, void *fsdata);
|
||||
#else
|
||||
extern int __apfs_write_begin(struct file *file, struct address_space *mapping, loff_t pos, unsigned int len, unsigned int flags, struct page **pagep, void **fsdata);
|
||||
extern int __apfs_write_end(struct file *file, struct address_space *mapping, loff_t pos, unsigned int len, unsigned int copied, struct page *page, void *fsdata);
|
||||
#endif
|
||||
extern int apfs_dstream_adj_refcnt(struct apfs_dstream_info *dstream, u32 delta);
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 12, 0)
|
||||
@@ -1035,7 +1041,9 @@ extern int apfs_setattr(struct mnt_idmap *idmap,
|
||||
struct dentry *dentry, struct iattr *iattr);
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(7, 0, 0)
|
||||
extern int apfs_update_time(struct inode *inode, enum fs_update_time time, unsigned int flags);
|
||||
#elif 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);
|
||||
@@ -1061,6 +1069,10 @@ extern int apfs_getattr(struct mnt_idmap *idmap,
|
||||
|
||||
extern int apfs_crypto_adj_refcnt(struct super_block *sb, u64 crypto_id, int delta);
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 17, 0)
|
||||
#define fileattr file_kattr
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0) || RHEL_VERSION_GE(9, 6)
|
||||
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);
|
||||
@@ -1207,6 +1219,33 @@ static inline void apfs_assert_query_is_valid(const struct apfs_query *query)
|
||||
#define page_has_buffers(page) folio_buffers(page_folio(page))
|
||||
#endif
|
||||
|
||||
static inline int apfs_inode_state_read_once(struct inode *inode)
|
||||
{
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 19, 0)
|
||||
return inode_state_read_once(inode);
|
||||
#else
|
||||
return inode->i_state;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void apfs_inode_state_set_raw(struct inode *inode, int flags)
|
||||
{
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 19, 0)
|
||||
inode_state_set_raw(inode, flags);
|
||||
#else
|
||||
inode->i_state |= flags;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void apfs_inode_state_clear_raw(struct inode *inode, int flags)
|
||||
{
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 19, 0)
|
||||
inode_state_clear_raw(inode, flags);
|
||||
#else
|
||||
inode->i_state &= ~flags;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: the following are modified variants of buffer head functions that will
|
||||
* work with the shared block device for the container. The correct approach
|
||||
|
||||
@@ -261,7 +261,7 @@ const struct file_operations apfs_dir_operations = {
|
||||
*
|
||||
* Returns the length of the key, or a negative error code in case of failure.
|
||||
*/
|
||||
static int apfs_build_dentry_unhashed_key(struct qstr *qname, u64 parent_id,
|
||||
static int apfs_build_dentry_unhashed_key(const struct qstr *qname, u64 parent_id,
|
||||
struct apfs_drec_key **key_p)
|
||||
{
|
||||
struct apfs_drec_key *key;
|
||||
@@ -290,7 +290,7 @@ static int apfs_build_dentry_unhashed_key(struct qstr *qname, u64 parent_id,
|
||||
*
|
||||
* Returns the length of the key, or a negative error code in case of failure.
|
||||
*/
|
||||
static int apfs_build_dentry_hashed_key(struct qstr *qname, u64 hash, u64 parent_id,
|
||||
static int apfs_build_dentry_hashed_key(const struct qstr *qname, u64 hash, u64 parent_id,
|
||||
struct apfs_drec_hashed_key **key_p)
|
||||
{
|
||||
struct apfs_drec_hashed_key *key;
|
||||
@@ -363,7 +363,7 @@ static int apfs_build_dentry_val(struct inode *inode, u64 sibling_id,
|
||||
*
|
||||
* Returns 0 on success or a negative error code in case of failure.
|
||||
*/
|
||||
static int apfs_create_dentry_rec(struct inode *inode, struct qstr *qname,
|
||||
static int apfs_create_dentry_rec(struct inode *inode, const struct qstr *qname,
|
||||
u64 parent_id, u64 sibling_id)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
@@ -426,7 +426,7 @@ static int apfs_build_sibling_val(struct dentry *dentry,
|
||||
struct apfs_sibling_val **val_p)
|
||||
{
|
||||
struct apfs_sibling_val *val;
|
||||
struct qstr *qname = &dentry->d_name;
|
||||
const struct qstr *qname = &dentry->d_name;
|
||||
u16 namelen = qname->len + 1; /* We count the null-termination */
|
||||
struct inode *parent = d_inode(dentry->d_parent);
|
||||
int val_len;
|
||||
@@ -1255,9 +1255,10 @@ static void __apfs_undo_unlink(struct dentry *dentry)
|
||||
{
|
||||
struct inode *inode = d_inode(dentry);
|
||||
|
||||
inode->i_state |= I_LINKABLE; /* Silence warning about nlink 0->1 */
|
||||
/* Silence warning about nlink 0->1 */
|
||||
apfs_inode_state_set_raw(inode, I_LINKABLE);
|
||||
inc_nlink(inode);
|
||||
inode->i_state &= ~I_LINKABLE;
|
||||
apfs_inode_state_clear_raw(inode, I_LINKABLE);
|
||||
|
||||
apfs_undo_delete_dentry(dentry);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
PACKAGE_NAME="linux-apfs-rw"
|
||||
PACKAGE_VERSION="0.3.15"
|
||||
PACKAGE_VERSION="0.3.19"
|
||||
|
||||
BUILT_MODULE_NAME[0]="apfs"
|
||||
DEST_MODULE_LOCATION[0]="/extra"
|
||||
|
||||
@@ -470,7 +470,11 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 17, 0)
|
||||
int __apfs_write_begin(const struct kiocb *file, struct address_space *mapping, loff_t pos, unsigned int len, unsigned int flags, struct page **pagep, void **fsdata)
|
||||
#else
|
||||
int __apfs_write_begin(struct file *file, struct address_space *mapping, loff_t pos, unsigned int len, unsigned int flags, struct page **pagep, void **fsdata)
|
||||
#endif
|
||||
{
|
||||
struct inode *inode = mapping->host;
|
||||
struct apfs_dstream_info *dstream = &APFS_I(inode)->i_dstream;
|
||||
@@ -584,7 +588,11 @@ out_put_page:
|
||||
return err;
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 17, 0)
|
||||
static int apfs_write_begin(const struct kiocb *file, struct address_space *mapping,
|
||||
loff_t pos, unsigned int len,
|
||||
struct folio **foliop, void **fsdata)
|
||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0)
|
||||
static int apfs_write_begin(struct file *file, struct address_space *mapping,
|
||||
loff_t pos, unsigned int len,
|
||||
struct folio **foliop, void **fsdata)
|
||||
@@ -629,7 +637,11 @@ fail:
|
||||
return err;
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 17, 0)
|
||||
int __apfs_write_end(const struct kiocb *file, struct address_space *mapping, loff_t pos, unsigned int len, unsigned int copied, struct page *page, void *fsdata)
|
||||
#else
|
||||
int __apfs_write_end(struct file *file, struct address_space *mapping, loff_t pos, unsigned int len, unsigned int copied, struct page *page, void *fsdata)
|
||||
#endif
|
||||
{
|
||||
struct inode *inode = mapping->host;
|
||||
struct apfs_dstream_info *dstream = &APFS_I(inode)->i_dstream;
|
||||
@@ -652,7 +664,11 @@ int __apfs_write_end(struct file *file, struct address_space *mapping, loff_t po
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 17, 0)
|
||||
static int apfs_write_end(const struct kiocb *file, struct address_space *mapping,
|
||||
loff_t pos, unsigned int len, unsigned int copied,
|
||||
struct folio *folio, void *fsdata)
|
||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0)
|
||||
static int apfs_write_end(struct file *file, struct address_space *mapping,
|
||||
loff_t pos, unsigned int len, unsigned int copied,
|
||||
struct folio *folio, void *fsdata)
|
||||
@@ -1021,7 +1037,7 @@ struct inode *apfs_iget(struct super_block *sb, u64 cnid)
|
||||
inode = apfs_iget_locked(sb, cnid);
|
||||
if (!inode)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
if (!(inode->i_state & I_NEW))
|
||||
if (!(apfs_inode_state_read_once(inode) & I_NEW))
|
||||
return inode;
|
||||
|
||||
down_read(&nxi->nx_big_sem);
|
||||
@@ -1133,7 +1149,7 @@ int apfs_getattr(struct mnt_idmap *idmap,
|
||||
*
|
||||
* Returns the length of the value, or a negative error code in case of failure.
|
||||
*/
|
||||
static int apfs_build_inode_val(struct inode *inode, struct qstr *qname,
|
||||
static int apfs_build_inode_val(struct inode *inode, const struct qstr *qname,
|
||||
struct apfs_inode_val **val_p)
|
||||
{
|
||||
struct apfs_inode_val *val;
|
||||
@@ -2093,7 +2109,9 @@ fail:
|
||||
}
|
||||
|
||||
/* TODO: this only seems to be necessary because ->write_inode() isn't firing */
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(7, 0, 0)
|
||||
int apfs_update_time(struct inode *inode, enum fs_update_time time, unsigned int flags)
|
||||
#elif 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)
|
||||
@@ -2107,7 +2125,9 @@ int apfs_update_time(struct inode *inode, int flags)
|
||||
return err;
|
||||
apfs_inode_join_transaction(sb, inode);
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) && !RHEL_VERSION_GE(9, 6)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(7, 0, 0)
|
||||
generic_update_time(inode, time, flags);
|
||||
#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) && !RHEL_VERSION_GE(9, 6)
|
||||
generic_update_time(inode, time, flags);
|
||||
#else
|
||||
generic_update_time(inode, flags);
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
#include <linux/seq_file.h>
|
||||
#include "apfs.h"
|
||||
#include "version.h"
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(7, 0, 0)
|
||||
#include <linux/fs_context.h>
|
||||
#endif
|
||||
|
||||
#define APFS_MODULE_ID_STRING "linux-apfs by eafer (" GIT_COMMIT ")"
|
||||
|
||||
@@ -465,6 +468,9 @@ static inline void apfs_free_main_super(struct apfs_sb_info *sbi)
|
||||
u32 bmap_idx;
|
||||
int i;
|
||||
|
||||
if (!nxi)
|
||||
return;
|
||||
|
||||
mutex_lock(&nxs_mutex);
|
||||
|
||||
list_del(&sbi->list);
|
||||
@@ -1187,7 +1193,9 @@ static const struct super_operations apfs_sops = {
|
||||
.put_super = apfs_put_super,
|
||||
.sync_fs = apfs_sync_fs,
|
||||
.statfs = apfs_statfs,
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(7, 0, 0)
|
||||
.remount_fs = apfs_remount,
|
||||
#endif
|
||||
.show_options = apfs_show_options,
|
||||
};
|
||||
|
||||
@@ -1227,30 +1235,53 @@ static void apfs_set_nx_flags(struct super_block *sb, unsigned int flags)
|
||||
mutex_unlock(&nxs_mutex);
|
||||
}
|
||||
|
||||
static void parse_options_set_flags(struct super_block *sb, struct apfs_sb_info *sbi,
|
||||
unsigned int nx_flags)
|
||||
{
|
||||
struct apfs_nxsb_info *nxi = sbi->s_nxi;
|
||||
|
||||
apfs_set_nx_flags(sb, nx_flags);
|
||||
if (!(sb->s_flags & SB_RDONLY)) {
|
||||
if (nxi->nx_flags & APFS_READWRITE) {
|
||||
apfs_notice(sb, "experimental write support is enabled");
|
||||
} else {
|
||||
apfs_warn(sb, "experimental writes disabled to avoid data loss");
|
||||
apfs_warn(sb, "if you really want them, check the README");
|
||||
sb->s_flags |= SB_RDONLY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Many of the parse_options() functions in other file systems return 0
|
||||
* on error. This one returns an error code, and 0 on success.
|
||||
*
|
||||
* Note that even on failure, the caller is responsible for freeing all
|
||||
* superblock fields.
|
||||
*/
|
||||
static int parse_options(struct super_block *sb, char *options)
|
||||
static int parse_options(struct apfs_sb_info *sbi, char *options)
|
||||
{
|
||||
struct apfs_sb_info *sbi = APFS_SB(sb);
|
||||
struct apfs_nxsb_info *nxi = sbi->s_nxi;
|
||||
char *p;
|
||||
substring_t args[MAX_OPT_ARGS];
|
||||
int option;
|
||||
int err = 0;
|
||||
unsigned int nx_flags;
|
||||
|
||||
/* Set default values before parsing */
|
||||
nx_flags = 0;
|
||||
sbi->s_vol_nr = 0;
|
||||
sbi->s_snap_name = NULL;
|
||||
sbi->s_tier2_path = NULL;
|
||||
sbi->s_mount_opt = 0;
|
||||
|
||||
#ifdef CONFIG_APFS_RW_ALWAYS
|
||||
/* Still risky, but some packagers want writable mounts by default */
|
||||
nx_flags |= APFS_READWRITE;
|
||||
sbi->s_mount_opt |= APFS_READWRITE;
|
||||
#endif
|
||||
|
||||
sbi->s_uid = INVALID_UID;
|
||||
sbi->s_gid = INVALID_GID;
|
||||
|
||||
if (!options)
|
||||
goto out;
|
||||
return 0;
|
||||
|
||||
while ((p = strsep(&options, ",")) != NULL) {
|
||||
int token;
|
||||
@@ -1264,14 +1295,14 @@ static int parse_options(struct super_block *sb, char *options)
|
||||
* Write support is not safe yet, so keep it disabled
|
||||
* unless the user requests it explicitly.
|
||||
*/
|
||||
nx_flags |= APFS_READWRITE;
|
||||
sbi->s_mount_opt |= APFS_READWRITE;
|
||||
break;
|
||||
case Opt_cknodes:
|
||||
/*
|
||||
* Right now, node checksums are too costly to enable
|
||||
* by default. TODO: try to improve this.
|
||||
*/
|
||||
nx_flags |= APFS_CHECK_NODES;
|
||||
sbi->s_mount_opt |= APFS_CHECK_NODES;
|
||||
break;
|
||||
case Opt_uid:
|
||||
err = match_int(&args[0], &option);
|
||||
@@ -1279,7 +1310,7 @@ static int parse_options(struct super_block *sb, char *options)
|
||||
return err;
|
||||
sbi->s_uid = make_kuid(current_user_ns(), option);
|
||||
if (!uid_valid(sbi->s_uid)) {
|
||||
apfs_err(sb, "invalid uid");
|
||||
apfs_err(NULL, "invalid uid");
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
@@ -1289,36 +1320,34 @@ static int parse_options(struct super_block *sb, char *options)
|
||||
return err;
|
||||
sbi->s_gid = make_kgid(current_user_ns(), option);
|
||||
if (!gid_valid(sbi->s_gid)) {
|
||||
apfs_err(sb, "invalid gid");
|
||||
apfs_err(NULL, "invalid gid");
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
case Opt_vol:
|
||||
err = match_int(&args[0], &sbi->s_vol_nr);
|
||||
if (err) {
|
||||
apfs_err(NULL, "invalid volume number");
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
case Opt_snap:
|
||||
kfree(sbi->s_snap_name);
|
||||
sbi->s_snap_name = match_strdup(&args[0]);
|
||||
if (!sbi->s_snap_name)
|
||||
return -ENOMEM;
|
||||
break;
|
||||
case Opt_tier2:
|
||||
/* Already read early on mount */
|
||||
kfree(sbi->s_tier2_path);
|
||||
sbi->s_tier2_path = match_strdup(&args[0]);
|
||||
if (!sbi->s_tier2_path)
|
||||
return -ENOMEM;
|
||||
break;
|
||||
default:
|
||||
/*
|
||||
* We should have already checked the mount options in
|
||||
* apfs_preparse_options(), so this is a bug.
|
||||
*/
|
||||
apfs_alert(sb, "invalid mount option %s", p);
|
||||
apfs_warn(NULL, "invalid mount option %s", p);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
apfs_set_nx_flags(sb, nx_flags);
|
||||
if (!(sb->s_flags & SB_RDONLY)) {
|
||||
if (nxi->nx_flags & APFS_READWRITE) {
|
||||
apfs_notice(sb, "experimental write support is enabled");
|
||||
} else {
|
||||
apfs_warn(sb, "experimental writes disabled to avoid data loss");
|
||||
apfs_warn(sb, "if you really want them, check the README");
|
||||
sb->s_flags |= SB_RDONLY;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1550,12 +1579,7 @@ static int apfs_fill_super(struct super_block *sb, void *data, int silent)
|
||||
err = apfs_setup_bdi(sb);
|
||||
if (err)
|
||||
goto failed_volume;
|
||||
|
||||
sbi->s_uid = INVALID_UID;
|
||||
sbi->s_gid = INVALID_GID;
|
||||
err = parse_options(sb, data);
|
||||
if (err)
|
||||
goto failed_volume;
|
||||
parse_options_set_flags(sb, sbi, sbi->s_mount_opt);
|
||||
|
||||
err = apfs_map_volume_super(sb, false /* write */);
|
||||
if (err)
|
||||
@@ -1585,7 +1609,11 @@ static int apfs_fill_super(struct super_block *sb, void *data, int silent)
|
||||
goto failed_cat;
|
||||
|
||||
sb->s_op = &apfs_sops;
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 17, 0)
|
||||
set_default_d_op(sb, &apfs_dentry_operations);
|
||||
#else
|
||||
sb->s_d_op = &apfs_dentry_operations;
|
||||
#endif
|
||||
sb->s_xattr = apfs_xattr_handlers;
|
||||
sb->s_maxbytes = MAX_LFS_FILESIZE;
|
||||
sb->s_time_gran = 1; /* Nanosecond granularity */
|
||||
@@ -1864,108 +1892,51 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* apfs_preparse_options - Parse the options used to identify a superblock
|
||||
* @sbi: superblock info
|
||||
* @options: options string to parse
|
||||
*
|
||||
* Returns 0 on success, or a negative error code in case of failure. Even on
|
||||
* failure, the caller is responsible for freeing all superblock fields.
|
||||
*/
|
||||
static int apfs_preparse_options(struct apfs_sb_info *sbi, char *options)
|
||||
{
|
||||
char *tofree = NULL;
|
||||
char *p;
|
||||
substring_t args[MAX_OPT_ARGS];
|
||||
int err = 0;
|
||||
|
||||
/* Set default values before parsing */
|
||||
sbi->s_vol_nr = 0;
|
||||
sbi->s_snap_name = NULL;
|
||||
sbi->s_tier2_path = NULL;
|
||||
|
||||
if (!options)
|
||||
return 0;
|
||||
|
||||
/* Later parse_options() will need the unmodified options string */
|
||||
options = kstrdup(options, GFP_KERNEL);
|
||||
if (!options)
|
||||
return -ENOMEM;
|
||||
tofree = options;
|
||||
|
||||
while ((p = strsep(&options, ",")) != NULL) {
|
||||
int token;
|
||||
|
||||
if (!*p)
|
||||
continue;
|
||||
token = match_token(p, tokens, args);
|
||||
switch (token) {
|
||||
case Opt_vol:
|
||||
err = match_int(&args[0], &sbi->s_vol_nr);
|
||||
if (err)
|
||||
goto out;
|
||||
break;
|
||||
case Opt_snap:
|
||||
kfree(sbi->s_snap_name);
|
||||
sbi->s_snap_name = match_strdup(&args[0]);
|
||||
if (!sbi->s_snap_name) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case Opt_tier2:
|
||||
kfree(sbi->s_tier2_path);
|
||||
sbi->s_tier2_path = match_strdup(&args[0]);
|
||||
if (!sbi->s_tier2_path) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case Opt_readwrite:
|
||||
case Opt_cknodes:
|
||||
case Opt_uid:
|
||||
case Opt_gid:
|
||||
/* Not needed for sget(), will be read later */
|
||||
break;
|
||||
default:
|
||||
apfs_warn(NULL, "invalid mount option %s", p);
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
err = 0;
|
||||
out:
|
||||
kfree(tofree);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is a copy of mount_bdev() that allows multiple mounts.
|
||||
*/
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(7, 0, 0)
|
||||
static int apfs_test_super_fc(struct super_block *sb, struct fs_context *fc);
|
||||
static int apfs_set_super_fc(struct super_block *sb, struct fs_context *fc);
|
||||
static int apfs_get_tree(struct fs_context *fc)
|
||||
#else
|
||||
static struct dentry *apfs_mount(struct file_system_type *fs_type, int flags,
|
||||
const char *dev_name, void *data)
|
||||
#endif
|
||||
{
|
||||
struct super_block *sb;
|
||||
struct apfs_sb_info *sbi;
|
||||
struct apfs_blkdev_info *bd_info = NULL, *tier2_info = NULL;
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 5, 0) || RHEL_VERSION_GE(9, 4)
|
||||
int error = 0;
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(7, 0, 0)
|
||||
blk_mode_t mode = sb_open_mode(fc->sb_flags);
|
||||
void *data = fc->fs_private;
|
||||
const char *dev_name = fc->source;
|
||||
|
||||
sbi = fc->s_fs_info;
|
||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(6, 5, 0) || RHEL_VERSION_GE(9, 4)
|
||||
blk_mode_t mode = sb_open_mode(flags);
|
||||
#else
|
||||
fmode_t mode = FMODE_READ | FMODE_EXCL;
|
||||
#endif
|
||||
int error = 0;
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(7, 0, 0)
|
||||
sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
|
||||
if (!sbi)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
/* Set up the fields that sget() will need to id the superblock */
|
||||
error = apfs_preparse_options(sbi, data);
|
||||
error = parse_options(sbi, data);
|
||||
if (error)
|
||||
goto out_free_sbi;
|
||||
#endif
|
||||
|
||||
/* Make sure that snapshots are mounted read-only */
|
||||
if (sbi->s_snap_name)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(7, 0, 0)
|
||||
fc->sb_flags |= SB_RDONLY;
|
||||
#else
|
||||
flags |= SB_RDONLY;
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 5, 0) && !RHEL_VERSION_GE(9, 4)
|
||||
if (!(flags & SB_RDONLY))
|
||||
@@ -1977,7 +1948,12 @@ static struct dentry *apfs_mount(struct file_system_type *fs_type, int flags,
|
||||
goto out_free_sbi;
|
||||
|
||||
/* TODO: lockfs stuff? Btrfs doesn't seem to care */
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(7, 0, 0)
|
||||
fc->sb_flags |= SB_NOSEC;
|
||||
sb = sget_fc(fc, apfs_test_super_fc, apfs_set_super_fc);
|
||||
#else
|
||||
sb = sget(fs_type, apfs_test_super, apfs_set_super, flags | SB_NOSEC, sbi);
|
||||
#endif
|
||||
if (IS_ERR(sb)) {
|
||||
error = PTR_ERR(sb);
|
||||
goto out_unmap_super;
|
||||
@@ -1993,7 +1969,11 @@ static struct dentry *apfs_mount(struct file_system_type *fs_type, int flags,
|
||||
WARN_ON(sb->s_dev != bd_info->blki_bdev->bd_dev);
|
||||
|
||||
if (sb->s_root) {
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(7, 0, 0)
|
||||
if ((fc->sb_flags ^ sb->s_flags) & SB_RDONLY) {
|
||||
#else
|
||||
if ((flags ^ sb->s_flags) & SB_RDONLY) {
|
||||
#endif
|
||||
error = -EBUSY;
|
||||
deactivate_locked_super(sb);
|
||||
goto out_unmap_super;
|
||||
@@ -2005,8 +1985,14 @@ static struct dentry *apfs_mount(struct file_system_type *fs_type, int flags,
|
||||
kfree(sbi->s_tier2_path);
|
||||
sbi->s_tier2_path = NULL;
|
||||
kfree(sbi);
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(7, 0, 0)
|
||||
fc->s_fs_info = NULL;
|
||||
#endif
|
||||
sbi = NULL;
|
||||
} else {
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(7, 0, 0)
|
||||
fc->s_fs_info = NULL;
|
||||
#endif
|
||||
if (!sbi->s_snap_name && !tier2_info)
|
||||
snprintf(sb->s_id, sizeof(sb->s_id), "%pg:%u", bd_info->blki_bdev, sbi->s_vol_nr);
|
||||
else if (!tier2_info)
|
||||
@@ -2018,28 +2004,47 @@ static struct dentry *apfs_mount(struct file_system_type *fs_type, int flags,
|
||||
error = apfs_read_main_super(sb);
|
||||
if (error) {
|
||||
deactivate_locked_super(sb);
|
||||
return ERR_PTR(error);
|
||||
goto out_return_error;
|
||||
}
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 5, 0) && !RHEL_VERSION_GE(9, 4)
|
||||
sb->s_mode = mode;
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(7, 0, 0)
|
||||
error = apfs_fill_super(sb, data, fc->sb_flags & SB_SILENT ? 1 : 0);
|
||||
#else
|
||||
error = apfs_fill_super(sb, data, flags & SB_SILENT ? 1 : 0);
|
||||
#endif
|
||||
if (error) {
|
||||
deactivate_locked_super(sb);
|
||||
return ERR_PTR(error);
|
||||
goto out_return_error;
|
||||
}
|
||||
sb->s_flags |= SB_ACTIVE;
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(7, 0, 0)
|
||||
fc->root = dget(sb->s_root);
|
||||
return 0;
|
||||
#else
|
||||
return dget(sb->s_root);
|
||||
#endif
|
||||
|
||||
out_unmap_super:
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(7, 0, 0)
|
||||
apfs_free_main_super(sbi);
|
||||
#endif
|
||||
out_free_sbi:
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(7, 0, 0)
|
||||
kfree(sbi->s_snap_name);
|
||||
kfree(sbi->s_tier2_path);
|
||||
kfree(sbi);
|
||||
#endif
|
||||
out_return_error:
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(7, 0, 0)
|
||||
return error;
|
||||
#else
|
||||
return ERR_PTR(error);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2087,10 +2092,72 @@ static void apfs_kill_sb(struct super_block *sb)
|
||||
apfs_free_sb_info(sb);
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(7, 0, 0)
|
||||
static int apfs_test_super_fc(struct super_block *sb, struct fs_context *fc)
|
||||
{
|
||||
return apfs_test_super(sb, fc->s_fs_info);
|
||||
}
|
||||
|
||||
static int apfs_set_super_fc(struct super_block *sb, struct fs_context *fc)
|
||||
{
|
||||
return apfs_set_super(sb, fc->s_fs_info);
|
||||
}
|
||||
|
||||
static int apfs_reconfigure(struct fs_context *fc)
|
||||
{
|
||||
struct super_block *sb = fc->root->d_sb;
|
||||
int flags = fc->sb_flags;
|
||||
|
||||
return apfs_remount(sb, &flags, fc->fs_private);
|
||||
}
|
||||
|
||||
static void apfs_free_fc(struct fs_context *fc)
|
||||
{
|
||||
struct apfs_sb_info *sbi = fc->s_fs_info;
|
||||
|
||||
if (sbi) {
|
||||
apfs_free_main_super(sbi);
|
||||
kfree(sbi->s_snap_name);
|
||||
kfree(sbi->s_tier2_path);
|
||||
kfree(sbi);
|
||||
}
|
||||
}
|
||||
|
||||
static int apfs_parse_monolithic(struct fs_context *fc, void *data)
|
||||
{
|
||||
return parse_options(fc->s_fs_info, data);
|
||||
}
|
||||
|
||||
static const struct fs_context_operations apfs_context_ops = {
|
||||
.get_tree = apfs_get_tree,
|
||||
.reconfigure = apfs_reconfigure,
|
||||
.free = apfs_free_fc,
|
||||
.parse_monolithic = apfs_parse_monolithic,
|
||||
};
|
||||
|
||||
static int apfs_init_fs_context(struct fs_context *fc)
|
||||
{
|
||||
struct apfs_sb_info *sbi;
|
||||
|
||||
/* Setup happens here now instead of the top of apfs_mount */
|
||||
sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
|
||||
if (!sbi)
|
||||
return -ENOMEM;
|
||||
|
||||
fc->s_fs_info = sbi;
|
||||
fc->ops = &apfs_context_ops;
|
||||
return 0;
|
||||
}
|
||||
#endif /* LINUX_VERSION_CODE >= 7.0.0 */
|
||||
|
||||
static struct file_system_type apfs_fs_type = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "apfs",
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(7, 0, 0)
|
||||
.mount = apfs_mount,
|
||||
#else
|
||||
.init_fs_context = apfs_init_fs_context,
|
||||
#endif
|
||||
.kill_sb = apfs_kill_sb,
|
||||
.fs_flags = FS_REQUIRES_DEV,
|
||||
};
|
||||
|
||||
+1
-1
@@ -468,7 +468,7 @@ int apfs_transaction_flush_all_inodes(struct super_block *sb)
|
||||
curr_err = apfs_update_inode(inode, NULL /* new_name */);
|
||||
if (curr_err)
|
||||
err = curr_err;
|
||||
inode->i_state &= ~I_DIRTY_ALL;
|
||||
apfs_inode_state_clear_raw(inode, I_DIRTY_ALL);
|
||||
|
||||
/*
|
||||
* The same inode may get dirtied again as soon as we release
|
||||
|
||||
Reference in new issue
Block a user