Unverified Commit 432f7c78 authored by Eric Sandeen's avatar Eric Sandeen Committed by Christian Brauner
Browse files

hfsplus: convert hfsplus to use the new mount api



Convert the hfsplus filesystem to use the new mount API.
Tested by comparing random mount & remount options before and after
the change.

Signed-off-by: default avatarEric Sandeen <sandeen@redhat.com>
Link: https://lore.kernel.org/r/20240916172735.866916-6-sandeen@redhat.com


Reviewed-by: default avatarJan Kara <jack@suse.cz>
Signed-off-by: default avatarChristian Brauner <brauner@kernel.org>
parent ffcd06b6
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@
#include <linux/mutex.h>
#include <linux/buffer_head.h>
#include <linux/blkdev.h>
#include <linux/fs_context.h>
#include "hfsplus_raw.h"

#define DBG_BNODE_REFS	0x00000001
@@ -496,8 +497,7 @@ long hfsplus_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);

/* options.c */
void hfsplus_fill_defaults(struct hfsplus_sb_info *opts);
int hfsplus_parse_options_remount(char *input, int *force);
int hfsplus_parse_options(char *input, struct hfsplus_sb_info *sbi);
int hfsplus_parse_param(struct fs_context *fc, struct fs_parameter *param);
int hfsplus_show_options(struct seq_file *seq, struct dentry *root);

/* part_tbl.c */
+94 −169
Original line number Diff line number Diff line
@@ -12,7 +12,8 @@
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/parser.h>
#include <linux/fs_context.h>
#include <linux/fs_parser.h>
#include <linux/nls.h>
#include <linux/mount.h>
#include <linux/seq_file.h>
@@ -23,26 +24,23 @@ enum {
	opt_creator, opt_type,
	opt_umask, opt_uid, opt_gid,
	opt_part, opt_session, opt_nls,
	opt_nodecompose, opt_decompose,
	opt_barrier, opt_nobarrier,
	opt_force, opt_err
	opt_decompose, opt_barrier,
	opt_force,
};

static const match_table_t tokens = {
	{ opt_creator, "creator=%s" },
	{ opt_type, "type=%s" },
	{ opt_umask, "umask=%o" },
	{ opt_uid, "uid=%u" },
	{ opt_gid, "gid=%u" },
	{ opt_part, "part=%u" },
	{ opt_session, "session=%u" },
	{ opt_nls, "nls=%s" },
	{ opt_decompose, "decompose" },
	{ opt_nodecompose, "nodecompose" },
	{ opt_barrier, "barrier" },
	{ opt_nobarrier, "nobarrier" },
	{ opt_force, "force" },
	{ opt_err, NULL }
static const struct fs_parameter_spec hfs_param_spec[] = {
	fsparam_string	("creator",	opt_creator),
	fsparam_string	("type",	opt_type),
	fsparam_u32oct	("umask",	opt_umask),
	fsparam_u32	("uid",		opt_uid),
	fsparam_u32	("gid",		opt_gid),
	fsparam_u32	("part",	opt_part),
	fsparam_u32	("session",	opt_session),
	fsparam_string	("nls",		opt_nls),
	fsparam_flag_no	("decompose",	opt_decompose),
	fsparam_flag_no	("barrier",	opt_barrier),
	fsparam_flag	("force",	opt_force),
	{}
};

/* Initialize an options object to reasonable defaults */
@@ -60,164 +58,91 @@ void hfsplus_fill_defaults(struct hfsplus_sb_info *opts)
	opts->session = -1;
}

/* convert a "four byte character" to a 32 bit int with error checks */
static inline int match_fourchar(substring_t *arg, u32 *result)
/* Parse options from mount. Returns nonzero errno on failure */
int hfsplus_parse_param(struct fs_context *fc, struct fs_parameter *param)
{
	if (arg->to - arg->from != 4)
		return -EINVAL;
	memcpy(result, arg->from, 4);
	return 0;
}

int hfsplus_parse_options_remount(char *input, int *force)
{
	char *p;
	substring_t args[MAX_OPT_ARGS];
	int token;

	if (!input)
		return 1;

	while ((p = strsep(&input, ",")) != NULL) {
		if (!*p)
			continue;

		token = match_token(p, tokens, args);
		switch (token) {
		case opt_force:
			*force = 1;
			break;
		default:
			break;
		}
	}
	struct hfsplus_sb_info *sbi = fc->s_fs_info;
	struct fs_parse_result result;
	int opt;

	return 1;
}

/* Parse options from mount. Returns 0 on failure */
/* input is the options passed to mount() as a string */
int hfsplus_parse_options(char *input, struct hfsplus_sb_info *sbi)
{
	char *p;
	substring_t args[MAX_OPT_ARGS];
	int tmp, token;

	if (!input)
		goto done;
	/*
	 * Only the force option is examined during remount, all others
	 * are ignored.
	 */
	if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE &&
	    strncmp(param->key, "force", 5))
		return 0;

	while ((p = strsep(&input, ",")) != NULL) {
		if (!*p)
			continue;
	opt = fs_parse(fc, hfs_param_spec, param, &result);
	if (opt < 0)
		return opt;

		token = match_token(p, tokens, args);
		switch (token) {
	switch (opt) {
	case opt_creator:
			if (match_fourchar(&args[0], &sbi->creator)) {
		if (strlen(param->string) != 4) {
			pr_err("creator requires a 4 character value\n");
				return 0;
			return -EINVAL;
		}
		memcpy(&sbi->creator, param->string, 4);
		break;
	case opt_type:
			if (match_fourchar(&args[0], &sbi->type)) {
		if (strlen(param->string) != 4) {
			pr_err("type requires a 4 character value\n");
				return 0;
			return -EINVAL;
		}
		memcpy(&sbi->type, param->string, 4);
		break;
	case opt_umask:
			if (match_octal(&args[0], &tmp)) {
				pr_err("umask requires a value\n");
				return 0;
			}
			sbi->umask = (umode_t)tmp;
		sbi->umask = (umode_t)result.uint_32;
		break;
	case opt_uid:
			if (match_int(&args[0], &tmp)) {
				pr_err("uid requires an argument\n");
				return 0;
			}
			sbi->uid = make_kuid(current_user_ns(), (uid_t)tmp);
			if (!uid_valid(sbi->uid)) {
				pr_err("invalid uid specified\n");
				return 0;
			} else {
		sbi->uid = result.uid;
		set_bit(HFSPLUS_SB_UID, &sbi->flags);
			}
		break;
	case opt_gid:
			if (match_int(&args[0], &tmp)) {
				pr_err("gid requires an argument\n");
				return 0;
			}
			sbi->gid = make_kgid(current_user_ns(), (gid_t)tmp);
			if (!gid_valid(sbi->gid)) {
				pr_err("invalid gid specified\n");
				return 0;
			} else {
		sbi->gid = result.gid;
		set_bit(HFSPLUS_SB_GID, &sbi->flags);
			}
		break;
	case opt_part:
			if (match_int(&args[0], &sbi->part)) {
				pr_err("part requires an argument\n");
				return 0;
			}
		sbi->part = result.uint_32;
		break;
	case opt_session:
			if (match_int(&args[0], &sbi->session)) {
				pr_err("session requires an argument\n");
				return 0;
			}
		sbi->session = result.uint_32;
		break;
	case opt_nls:
		if (sbi->nls) {
			pr_err("unable to change nls mapping\n");
				return 0;
			return -EINVAL;
		}
			p = match_strdup(&args[0]);
			if (p)
				sbi->nls = load_nls(p);
		sbi->nls = load_nls(param->string);
		if (!sbi->nls) {
			pr_err("unable to load nls mapping \"%s\"\n",
				       p);
				kfree(p);
				return 0;
			       param->string);
			return -EINVAL;
		}
			kfree(p);
		break;
	case opt_decompose:
			clear_bit(HFSPLUS_SB_NODECOMPOSE, &sbi->flags);
			break;
		case opt_nodecompose:
		if (result.negated)
			set_bit(HFSPLUS_SB_NODECOMPOSE, &sbi->flags);
		else
			clear_bit(HFSPLUS_SB_NODECOMPOSE, &sbi->flags);
		break;
	case opt_barrier:
			clear_bit(HFSPLUS_SB_NOBARRIER, &sbi->flags);
			break;
		case opt_nobarrier:
		if (result.negated)
			set_bit(HFSPLUS_SB_NOBARRIER, &sbi->flags);
		else
			clear_bit(HFSPLUS_SB_NOBARRIER, &sbi->flags);
		break;
	case opt_force:
		set_bit(HFSPLUS_SB_FORCE, &sbi->flags);
		break;
	default:
			return 0;
		}
		return -EINVAL;
	}

done:
	if (!sbi->nls) {
		/* try utf8 first, as this is the old default behaviour */
		sbi->nls = load_nls("utf8");
		if (!sbi->nls)
			sbi->nls = load_nls_default();
		if (!sbi->nls)
	return 0;
}

	return 1;
}

int hfsplus_show_options(struct seq_file *seq, struct dentry *root)
{
	struct hfsplus_sb_info *sbi = HFSPLUS_SB(root->d_sb);
+53 −31
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@
#include <linux/blkdev.h>
#include <linux/backing-dev.h>
#include <linux/fs.h>
#include <linux/fs_context.h>
#include <linux/slab.h>
#include <linux/vfs.h>
#include <linux/nls.h>
@@ -332,34 +333,33 @@ static int hfsplus_statfs(struct dentry *dentry, struct kstatfs *buf)
	return 0;
}

static int hfsplus_remount(struct super_block *sb, int *flags, char *data)
static int hfsplus_reconfigure(struct fs_context *fc)
{
	struct super_block *sb = fc->root->d_sb;

	sync_filesystem(sb);
	if ((bool)(*flags & SB_RDONLY) == sb_rdonly(sb))
	if ((bool)(fc->sb_flags & SB_RDONLY) == sb_rdonly(sb))
		return 0;
	if (!(*flags & SB_RDONLY)) {
		struct hfsplus_vh *vhdr = HFSPLUS_SB(sb)->s_vhdr;
		int force = 0;

		if (!hfsplus_parse_options_remount(data, &force))
			return -EINVAL;
	if (!(fc->sb_flags & SB_RDONLY)) {
		struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
		struct hfsplus_vh *vhdr = sbi->s_vhdr;

		if (!(vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_UNMNT))) {
			pr_warn("filesystem was not cleanly unmounted, running fsck.hfsplus is recommended.  leaving read-only.\n");
			sb->s_flags |= SB_RDONLY;
			*flags |= SB_RDONLY;
		} else if (force) {
			fc->sb_flags |= SB_RDONLY;
		} else if (test_bit(HFSPLUS_SB_FORCE, &sbi->flags)) {
			/* nothing */
		} else if (vhdr->attributes &
				cpu_to_be32(HFSPLUS_VOL_SOFTLOCK)) {
			pr_warn("filesystem is marked locked, leaving read-only.\n");
			sb->s_flags |= SB_RDONLY;
			*flags |= SB_RDONLY;
			fc->sb_flags |= SB_RDONLY;
		} else if (vhdr->attributes &
				cpu_to_be32(HFSPLUS_VOL_JOURNALED)) {
			pr_warn("filesystem is marked journaled, leaving read-only.\n");
			sb->s_flags |= SB_RDONLY;
			*flags |= SB_RDONLY;
			fc->sb_flags |= SB_RDONLY;
		}
	}
	return 0;
@@ -373,38 +373,33 @@ static const struct super_operations hfsplus_sops = {
	.put_super	= hfsplus_put_super,
	.sync_fs	= hfsplus_sync_fs,
	.statfs		= hfsplus_statfs,
	.remount_fs	= hfsplus_remount,
	.show_options	= hfsplus_show_options,
};

static int hfsplus_fill_super(struct super_block *sb, void *data, int silent)
static int hfsplus_fill_super(struct super_block *sb, struct fs_context *fc)
{
	struct hfsplus_vh *vhdr;
	struct hfsplus_sb_info *sbi;
	struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
	hfsplus_cat_entry entry;
	struct hfs_find_data fd;
	struct inode *root, *inode;
	struct qstr str;
	struct nls_table *nls = NULL;
	struct nls_table *nls;
	u64 last_fs_block, last_fs_page;
	int silent = fc->sb_flags & SB_SILENT;
	int err;

	err = -ENOMEM;
	sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
	if (!sbi)
		goto out;

	sb->s_fs_info = sbi;
	mutex_init(&sbi->alloc_mutex);
	mutex_init(&sbi->vh_mutex);
	spin_lock_init(&sbi->work_lock);
	INIT_DELAYED_WORK(&sbi->sync_work, delayed_sync_fs);
	hfsplus_fill_defaults(sbi);

	err = -EINVAL;
	if (!hfsplus_parse_options(data, sbi)) {
		pr_err("unable to parse mount options\n");
		goto out_unload_nls;
	if (!sbi->nls) {
		/* try utf8 first, as this is the old default behaviour */
		sbi->nls = load_nls("utf8");
		if (!sbi->nls)
			sbi->nls = load_nls_default();
	}

	/* temporarily use utf8 to correctly find the hidden dir below */
@@ -616,7 +611,6 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent)
	unload_nls(sbi->nls);
	unload_nls(nls);
	kfree(sbi);
out:
	return err;
}

@@ -641,18 +635,46 @@ static void hfsplus_free_inode(struct inode *inode)

#define HFSPLUS_INODE_SIZE	sizeof(struct hfsplus_inode_info)

static struct dentry *hfsplus_mount(struct file_system_type *fs_type,
			  int flags, const char *dev_name, void *data)
static int hfsplus_get_tree(struct fs_context *fc)
{
	return get_tree_bdev(fc, hfsplus_fill_super);
}

static void hfsplus_free_fc(struct fs_context *fc)
{
	kfree(fc->s_fs_info);
}

static const struct fs_context_operations hfsplus_context_ops = {
	.parse_param	= hfsplus_parse_param,
	.get_tree	= hfsplus_get_tree,
	.reconfigure	= hfsplus_reconfigure,
	.free		= hfsplus_free_fc,
};

static int hfsplus_init_fs_context(struct fs_context *fc)
{
	return mount_bdev(fs_type, flags, dev_name, data, hfsplus_fill_super);
	struct hfsplus_sb_info *sbi;

	sbi = kzalloc(sizeof(struct hfsplus_sb_info), GFP_KERNEL);
	if (!sbi)
		return -ENOMEM;

	if (fc->purpose != FS_CONTEXT_FOR_RECONFIGURE)
		hfsplus_fill_defaults(sbi);

	fc->s_fs_info = sbi;
	fc->ops = &hfsplus_context_ops;

	return 0;
}

static struct file_system_type hfsplus_fs_type = {
	.owner		= THIS_MODULE,
	.name		= "hfsplus",
	.mount		= hfsplus_mount,
	.kill_sb	= kill_block_super,
	.fs_flags	= FS_REQUIRES_DEV,
	.init_fs_context = hfsplus_init_fs_context,
};
MODULE_ALIAS_FS("hfsplus");