162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  linux/fs/hfsplus/options.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2001
662306a36Sopenharmony_ci * Brad Boyer (flar@allandria.com)
762306a36Sopenharmony_ci * (C) 2003 Ardis Technologies <roman@ardistech.com>
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Option parsing
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/string.h>
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/sched.h>
1562306a36Sopenharmony_ci#include <linux/parser.h>
1662306a36Sopenharmony_ci#include <linux/nls.h>
1762306a36Sopenharmony_ci#include <linux/mount.h>
1862306a36Sopenharmony_ci#include <linux/seq_file.h>
1962306a36Sopenharmony_ci#include <linux/slab.h>
2062306a36Sopenharmony_ci#include "hfsplus_fs.h"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cienum {
2362306a36Sopenharmony_ci	opt_creator, opt_type,
2462306a36Sopenharmony_ci	opt_umask, opt_uid, opt_gid,
2562306a36Sopenharmony_ci	opt_part, opt_session, opt_nls,
2662306a36Sopenharmony_ci	opt_nodecompose, opt_decompose,
2762306a36Sopenharmony_ci	opt_barrier, opt_nobarrier,
2862306a36Sopenharmony_ci	opt_force, opt_err
2962306a36Sopenharmony_ci};
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic const match_table_t tokens = {
3262306a36Sopenharmony_ci	{ opt_creator, "creator=%s" },
3362306a36Sopenharmony_ci	{ opt_type, "type=%s" },
3462306a36Sopenharmony_ci	{ opt_umask, "umask=%o" },
3562306a36Sopenharmony_ci	{ opt_uid, "uid=%u" },
3662306a36Sopenharmony_ci	{ opt_gid, "gid=%u" },
3762306a36Sopenharmony_ci	{ opt_part, "part=%u" },
3862306a36Sopenharmony_ci	{ opt_session, "session=%u" },
3962306a36Sopenharmony_ci	{ opt_nls, "nls=%s" },
4062306a36Sopenharmony_ci	{ opt_decompose, "decompose" },
4162306a36Sopenharmony_ci	{ opt_nodecompose, "nodecompose" },
4262306a36Sopenharmony_ci	{ opt_barrier, "barrier" },
4362306a36Sopenharmony_ci	{ opt_nobarrier, "nobarrier" },
4462306a36Sopenharmony_ci	{ opt_force, "force" },
4562306a36Sopenharmony_ci	{ opt_err, NULL }
4662306a36Sopenharmony_ci};
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci/* Initialize an options object to reasonable defaults */
4962306a36Sopenharmony_civoid hfsplus_fill_defaults(struct hfsplus_sb_info *opts)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	if (!opts)
5262306a36Sopenharmony_ci		return;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	opts->creator = HFSPLUS_DEF_CR_TYPE;
5562306a36Sopenharmony_ci	opts->type = HFSPLUS_DEF_CR_TYPE;
5662306a36Sopenharmony_ci	opts->umask = current_umask();
5762306a36Sopenharmony_ci	opts->uid = current_uid();
5862306a36Sopenharmony_ci	opts->gid = current_gid();
5962306a36Sopenharmony_ci	opts->part = -1;
6062306a36Sopenharmony_ci	opts->session = -1;
6162306a36Sopenharmony_ci}
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci/* convert a "four byte character" to a 32 bit int with error checks */
6462306a36Sopenharmony_cistatic inline int match_fourchar(substring_t *arg, u32 *result)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	if (arg->to - arg->from != 4)
6762306a36Sopenharmony_ci		return -EINVAL;
6862306a36Sopenharmony_ci	memcpy(result, arg->from, 4);
6962306a36Sopenharmony_ci	return 0;
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ciint hfsplus_parse_options_remount(char *input, int *force)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	char *p;
7562306a36Sopenharmony_ci	substring_t args[MAX_OPT_ARGS];
7662306a36Sopenharmony_ci	int token;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	if (!input)
7962306a36Sopenharmony_ci		return 1;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	while ((p = strsep(&input, ",")) != NULL) {
8262306a36Sopenharmony_ci		if (!*p)
8362306a36Sopenharmony_ci			continue;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci		token = match_token(p, tokens, args);
8662306a36Sopenharmony_ci		switch (token) {
8762306a36Sopenharmony_ci		case opt_force:
8862306a36Sopenharmony_ci			*force = 1;
8962306a36Sopenharmony_ci			break;
9062306a36Sopenharmony_ci		default:
9162306a36Sopenharmony_ci			break;
9262306a36Sopenharmony_ci		}
9362306a36Sopenharmony_ci	}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	return 1;
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci/* Parse options from mount. Returns 0 on failure */
9962306a36Sopenharmony_ci/* input is the options passed to mount() as a string */
10062306a36Sopenharmony_ciint hfsplus_parse_options(char *input, struct hfsplus_sb_info *sbi)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	char *p;
10362306a36Sopenharmony_ci	substring_t args[MAX_OPT_ARGS];
10462306a36Sopenharmony_ci	int tmp, token;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	if (!input)
10762306a36Sopenharmony_ci		goto done;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	while ((p = strsep(&input, ",")) != NULL) {
11062306a36Sopenharmony_ci		if (!*p)
11162306a36Sopenharmony_ci			continue;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci		token = match_token(p, tokens, args);
11462306a36Sopenharmony_ci		switch (token) {
11562306a36Sopenharmony_ci		case opt_creator:
11662306a36Sopenharmony_ci			if (match_fourchar(&args[0], &sbi->creator)) {
11762306a36Sopenharmony_ci				pr_err("creator requires a 4 character value\n");
11862306a36Sopenharmony_ci				return 0;
11962306a36Sopenharmony_ci			}
12062306a36Sopenharmony_ci			break;
12162306a36Sopenharmony_ci		case opt_type:
12262306a36Sopenharmony_ci			if (match_fourchar(&args[0], &sbi->type)) {
12362306a36Sopenharmony_ci				pr_err("type requires a 4 character value\n");
12462306a36Sopenharmony_ci				return 0;
12562306a36Sopenharmony_ci			}
12662306a36Sopenharmony_ci			break;
12762306a36Sopenharmony_ci		case opt_umask:
12862306a36Sopenharmony_ci			if (match_octal(&args[0], &tmp)) {
12962306a36Sopenharmony_ci				pr_err("umask requires a value\n");
13062306a36Sopenharmony_ci				return 0;
13162306a36Sopenharmony_ci			}
13262306a36Sopenharmony_ci			sbi->umask = (umode_t)tmp;
13362306a36Sopenharmony_ci			break;
13462306a36Sopenharmony_ci		case opt_uid:
13562306a36Sopenharmony_ci			if (match_int(&args[0], &tmp)) {
13662306a36Sopenharmony_ci				pr_err("uid requires an argument\n");
13762306a36Sopenharmony_ci				return 0;
13862306a36Sopenharmony_ci			}
13962306a36Sopenharmony_ci			sbi->uid = make_kuid(current_user_ns(), (uid_t)tmp);
14062306a36Sopenharmony_ci			if (!uid_valid(sbi->uid)) {
14162306a36Sopenharmony_ci				pr_err("invalid uid specified\n");
14262306a36Sopenharmony_ci				return 0;
14362306a36Sopenharmony_ci			} else {
14462306a36Sopenharmony_ci				set_bit(HFSPLUS_SB_UID, &sbi->flags);
14562306a36Sopenharmony_ci			}
14662306a36Sopenharmony_ci			break;
14762306a36Sopenharmony_ci		case opt_gid:
14862306a36Sopenharmony_ci			if (match_int(&args[0], &tmp)) {
14962306a36Sopenharmony_ci				pr_err("gid requires an argument\n");
15062306a36Sopenharmony_ci				return 0;
15162306a36Sopenharmony_ci			}
15262306a36Sopenharmony_ci			sbi->gid = make_kgid(current_user_ns(), (gid_t)tmp);
15362306a36Sopenharmony_ci			if (!gid_valid(sbi->gid)) {
15462306a36Sopenharmony_ci				pr_err("invalid gid specified\n");
15562306a36Sopenharmony_ci				return 0;
15662306a36Sopenharmony_ci			} else {
15762306a36Sopenharmony_ci				set_bit(HFSPLUS_SB_GID, &sbi->flags);
15862306a36Sopenharmony_ci			}
15962306a36Sopenharmony_ci			break;
16062306a36Sopenharmony_ci		case opt_part:
16162306a36Sopenharmony_ci			if (match_int(&args[0], &sbi->part)) {
16262306a36Sopenharmony_ci				pr_err("part requires an argument\n");
16362306a36Sopenharmony_ci				return 0;
16462306a36Sopenharmony_ci			}
16562306a36Sopenharmony_ci			break;
16662306a36Sopenharmony_ci		case opt_session:
16762306a36Sopenharmony_ci			if (match_int(&args[0], &sbi->session)) {
16862306a36Sopenharmony_ci				pr_err("session requires an argument\n");
16962306a36Sopenharmony_ci				return 0;
17062306a36Sopenharmony_ci			}
17162306a36Sopenharmony_ci			break;
17262306a36Sopenharmony_ci		case opt_nls:
17362306a36Sopenharmony_ci			if (sbi->nls) {
17462306a36Sopenharmony_ci				pr_err("unable to change nls mapping\n");
17562306a36Sopenharmony_ci				return 0;
17662306a36Sopenharmony_ci			}
17762306a36Sopenharmony_ci			p = match_strdup(&args[0]);
17862306a36Sopenharmony_ci			if (p)
17962306a36Sopenharmony_ci				sbi->nls = load_nls(p);
18062306a36Sopenharmony_ci			if (!sbi->nls) {
18162306a36Sopenharmony_ci				pr_err("unable to load nls mapping \"%s\"\n",
18262306a36Sopenharmony_ci				       p);
18362306a36Sopenharmony_ci				kfree(p);
18462306a36Sopenharmony_ci				return 0;
18562306a36Sopenharmony_ci			}
18662306a36Sopenharmony_ci			kfree(p);
18762306a36Sopenharmony_ci			break;
18862306a36Sopenharmony_ci		case opt_decompose:
18962306a36Sopenharmony_ci			clear_bit(HFSPLUS_SB_NODECOMPOSE, &sbi->flags);
19062306a36Sopenharmony_ci			break;
19162306a36Sopenharmony_ci		case opt_nodecompose:
19262306a36Sopenharmony_ci			set_bit(HFSPLUS_SB_NODECOMPOSE, &sbi->flags);
19362306a36Sopenharmony_ci			break;
19462306a36Sopenharmony_ci		case opt_barrier:
19562306a36Sopenharmony_ci			clear_bit(HFSPLUS_SB_NOBARRIER, &sbi->flags);
19662306a36Sopenharmony_ci			break;
19762306a36Sopenharmony_ci		case opt_nobarrier:
19862306a36Sopenharmony_ci			set_bit(HFSPLUS_SB_NOBARRIER, &sbi->flags);
19962306a36Sopenharmony_ci			break;
20062306a36Sopenharmony_ci		case opt_force:
20162306a36Sopenharmony_ci			set_bit(HFSPLUS_SB_FORCE, &sbi->flags);
20262306a36Sopenharmony_ci			break;
20362306a36Sopenharmony_ci		default:
20462306a36Sopenharmony_ci			return 0;
20562306a36Sopenharmony_ci		}
20662306a36Sopenharmony_ci	}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_cidone:
20962306a36Sopenharmony_ci	if (!sbi->nls) {
21062306a36Sopenharmony_ci		/* try utf8 first, as this is the old default behaviour */
21162306a36Sopenharmony_ci		sbi->nls = load_nls("utf8");
21262306a36Sopenharmony_ci		if (!sbi->nls)
21362306a36Sopenharmony_ci			sbi->nls = load_nls_default();
21462306a36Sopenharmony_ci		if (!sbi->nls)
21562306a36Sopenharmony_ci			return 0;
21662306a36Sopenharmony_ci	}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	return 1;
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ciint hfsplus_show_options(struct seq_file *seq, struct dentry *root)
22262306a36Sopenharmony_ci{
22362306a36Sopenharmony_ci	struct hfsplus_sb_info *sbi = HFSPLUS_SB(root->d_sb);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	if (sbi->creator != HFSPLUS_DEF_CR_TYPE)
22662306a36Sopenharmony_ci		seq_show_option_n(seq, "creator", (char *)&sbi->creator, 4);
22762306a36Sopenharmony_ci	if (sbi->type != HFSPLUS_DEF_CR_TYPE)
22862306a36Sopenharmony_ci		seq_show_option_n(seq, "type", (char *)&sbi->type, 4);
22962306a36Sopenharmony_ci	seq_printf(seq, ",umask=%o,uid=%u,gid=%u", sbi->umask,
23062306a36Sopenharmony_ci			from_kuid_munged(&init_user_ns, sbi->uid),
23162306a36Sopenharmony_ci			from_kgid_munged(&init_user_ns, sbi->gid));
23262306a36Sopenharmony_ci	if (sbi->part >= 0)
23362306a36Sopenharmony_ci		seq_printf(seq, ",part=%u", sbi->part);
23462306a36Sopenharmony_ci	if (sbi->session >= 0)
23562306a36Sopenharmony_ci		seq_printf(seq, ",session=%u", sbi->session);
23662306a36Sopenharmony_ci	if (sbi->nls)
23762306a36Sopenharmony_ci		seq_printf(seq, ",nls=%s", sbi->nls->charset);
23862306a36Sopenharmony_ci	if (test_bit(HFSPLUS_SB_NODECOMPOSE, &sbi->flags))
23962306a36Sopenharmony_ci		seq_puts(seq, ",nodecompose");
24062306a36Sopenharmony_ci	if (test_bit(HFSPLUS_SB_NOBARRIER, &sbi->flags))
24162306a36Sopenharmony_ci		seq_puts(seq, ",nobarrier");
24262306a36Sopenharmony_ci	return 0;
24362306a36Sopenharmony_ci}
244