|
|
|
@@ -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)
|
|
|
|
@@ -1868,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))
|
|
|
|
@@ -1981,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;
|
|
|
|
@@ -1997,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;
|
|
|
|
@@ -2009,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)
|
|
|
|
@@ -2022,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
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@@ -2091,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,
|
|
|
|
|
};
|
|
|
|
|