Skip to content

Commit 095cf50

Browse files
Christian Braunergregkh
authored andcommitted
binderfs: port to new mount api
When I first wrote binderfs the new mount api had not yet landed. Now that it has been around for a little while and a bunch of filesystems have already been ported we should do so too. When Al sent his mount-api-conversion pr he requested that binderfs (and a few others) be ported separately. It's time we port binderfs. We can make use of the new option parser, get nicer infrastructure and it will be easier if we ever add any new mount options. This survives testing with the binderfs selftests: for i in `seq 1 1000`; do ./binderfs_test; done including the new stress tests I sent out for review today: TAP version 13 1..1 # selftests: filesystems/binderfs: binderfs_test # [==========] Running 3 tests from 1 test cases. # [ RUN ] global.binderfs_stress # [ XFAIL! ] Tests are not run as root. Skipping privileged tests # [==========] Running 3 tests from 1 test cases. # [ RUN ] global.binderfs_stress # [ OK ] global.binderfs_stress # [ RUN ] global.binderfs_test_privileged # [ OK ] global.binderfs_test_privileged # [ RUN ] global.binderfs_test_unprivileged # # Allocated new binder device with major 243, minor 4, and name my-binder # # Detected binder version: 8 # [==========] Running 3 tests from 1 test cases. # [ RUN ] global.binderfs_stress # [ OK ] global.binderfs_stress # [ RUN ] global.binderfs_test_privileged # [ OK ] global.binderfs_test_privileged # [ RUN ] global.binderfs_test_unprivileged # [ OK ] global.binderfs_test_unprivileged # [==========] 3 / 3 tests passed. # [ PASSED ] ok 1 selftests: filesystems/binderfs: binderfs_test Cc: Todd Kjos <[email protected]> Signed-off-by: Christian Brauner <[email protected]> Reviewed-by: Kees Cook <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent b52cc1b commit 095cf50

File tree

1 file changed

+104
-96
lines changed

1 file changed

+104
-96
lines changed

drivers/android/binderfs.c

Lines changed: 104 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
#include <linux/module.h>
1919
#include <linux/mutex.h>
2020
#include <linux/mount.h>
21-
#include <linux/parser.h>
21+
#include <linux/fs_parser.h>
2222
#include <linux/radix-tree.h>
2323
#include <linux/sched.h>
2424
#include <linux/seq_file.h>
@@ -48,26 +48,30 @@ static dev_t binderfs_dev;
4848
static DEFINE_MUTEX(binderfs_minors_mutex);
4949
static DEFINE_IDA(binderfs_minors);
5050

51-
enum {
51+
enum binderfs_param {
5252
Opt_max,
5353
Opt_stats_mode,
54-
Opt_err
5554
};
5655

5756
enum binderfs_stats_mode {
58-
STATS_NONE,
59-
STATS_GLOBAL,
57+
binderfs_stats_mode_unset,
58+
binderfs_stats_mode_global,
6059
};
6160

62-
static const match_table_t tokens = {
63-
{ Opt_max, "max=%d" },
64-
{ Opt_stats_mode, "stats=%s" },
65-
{ Opt_err, NULL }
61+
static const struct constant_table binderfs_param_stats[] = {
62+
{ "global", binderfs_stats_mode_global },
63+
{}
6664
};
6765

68-
static inline struct binderfs_info *BINDERFS_I(const struct inode *inode)
66+
const struct fs_parameter_spec binderfs_fs_parameters[] = {
67+
fsparam_u32("max", Opt_max),
68+
fsparam_enum("stats", Opt_stats_mode, binderfs_param_stats),
69+
{}
70+
};
71+
72+
static inline struct binderfs_info *BINDERFS_SB(const struct super_block *sb)
6973
{
70-
return inode->i_sb->s_fs_info;
74+
return sb->s_fs_info;
7175
}
7276

7377
bool is_binderfs_device(const struct inode *inode)
@@ -246,7 +250,7 @@ static long binder_ctl_ioctl(struct file *file, unsigned int cmd,
246250
static void binderfs_evict_inode(struct inode *inode)
247251
{
248252
struct binder_device *device = inode->i_private;
249-
struct binderfs_info *info = BINDERFS_I(inode);
253+
struct binderfs_info *info = BINDERFS_SB(inode->i_sb);
250254

251255
clear_inode(inode);
252256

@@ -264,97 +268,84 @@ static void binderfs_evict_inode(struct inode *inode)
264268
}
265269
}
266270

267-
/**
268-
* binderfs_parse_mount_opts - parse binderfs mount options
269-
* @data: options to set (can be NULL in which case defaults are used)
270-
*/
271-
static int binderfs_parse_mount_opts(char *data,
272-
struct binderfs_mount_opts *opts)
271+
static int binderfs_fs_context_parse_param(struct fs_context *fc,
272+
struct fs_parameter *param)
273273
{
274-
char *p, *stats;
275-
opts->max = BINDERFS_MAX_MINOR;
276-
opts->stats_mode = STATS_NONE;
277-
278-
while ((p = strsep(&data, ",")) != NULL) {
279-
substring_t args[MAX_OPT_ARGS];
280-
int token;
281-
int max_devices;
282-
283-
if (!*p)
284-
continue;
285-
286-
token = match_token(p, tokens, args);
287-
switch (token) {
288-
case Opt_max:
289-
if (match_int(&args[0], &max_devices) ||
290-
(max_devices < 0 ||
291-
(max_devices > BINDERFS_MAX_MINOR)))
292-
return -EINVAL;
293-
294-
opts->max = max_devices;
295-
break;
296-
case Opt_stats_mode:
297-
if (!capable(CAP_SYS_ADMIN))
298-
return -EINVAL;
274+
int opt;
275+
struct binderfs_mount_opts *ctx = fc->fs_private;
276+
struct fs_parse_result result;
299277

300-
stats = match_strdup(&args[0]);
301-
if (!stats)
302-
return -ENOMEM;
278+
opt = fs_parse(fc, binderfs_fs_parameters, param, &result);
279+
if (opt < 0)
280+
return opt;
303281

304-
if (strcmp(stats, "global") != 0) {
305-
kfree(stats);
306-
return -EINVAL;
307-
}
282+
switch (opt) {
283+
case Opt_max:
284+
if (result.uint_32 > BINDERFS_MAX_MINOR)
285+
return invalfc(fc, "Bad value for '%s'", param->key);
308286

309-
opts->stats_mode = STATS_GLOBAL;
310-
kfree(stats);
311-
break;
312-
default:
313-
pr_err("Invalid mount options\n");
314-
return -EINVAL;
315-
}
287+
ctx->max = result.uint_32;
288+
break;
289+
case Opt_stats_mode:
290+
if (!capable(CAP_SYS_ADMIN))
291+
return -EPERM;
292+
293+
ctx->stats_mode = result.uint_32;
294+
break;
295+
default:
296+
return invalfc(fc, "Unsupported parameter '%s'", param->key);
316297
}
317298

318299
return 0;
319300
}
320301

321-
static int binderfs_remount(struct super_block *sb, int *flags, char *data)
302+
static int binderfs_fs_context_reconfigure(struct fs_context *fc)
322303
{
323-
int prev_stats_mode, ret;
324-
struct binderfs_info *info = sb->s_fs_info;
304+
struct binderfs_mount_opts *ctx = fc->fs_private;
305+
struct binderfs_info *info = BINDERFS_SB(fc->root->d_sb);
325306

326-
prev_stats_mode = info->mount_opts.stats_mode;
327-
ret = binderfs_parse_mount_opts(data, &info->mount_opts);
328-
if (ret)
329-
return ret;
330-
331-
if (prev_stats_mode != info->mount_opts.stats_mode) {
332-
pr_err("Binderfs stats mode cannot be changed during a remount\n");
333-
info->mount_opts.stats_mode = prev_stats_mode;
334-
return -EINVAL;
335-
}
307+
if (info->mount_opts.stats_mode != ctx->stats_mode)
308+
return invalfc(fc, "Binderfs stats mode cannot be changed during a remount");
336309

310+
info->mount_opts.stats_mode = ctx->stats_mode;
311+
info->mount_opts.max = ctx->max;
337312
return 0;
338313
}
339314

340-
static int binderfs_show_mount_opts(struct seq_file *seq, struct dentry *root)
315+
static int binderfs_show_options(struct seq_file *seq, struct dentry *root)
341316
{
342-
struct binderfs_info *info;
317+
struct binderfs_info *info = BINDERFS_SB(root->d_sb);
343318

344-
info = root->d_sb->s_fs_info;
345319
if (info->mount_opts.max <= BINDERFS_MAX_MINOR)
346320
seq_printf(seq, ",max=%d", info->mount_opts.max);
347-
if (info->mount_opts.stats_mode == STATS_GLOBAL)
321+
322+
switch (info->mount_opts.stats_mode) {
323+
case binderfs_stats_mode_unset:
324+
break;
325+
case binderfs_stats_mode_global:
348326
seq_printf(seq, ",stats=global");
327+
break;
328+
}
349329

350330
return 0;
351331
}
352332

333+
static void binderfs_put_super(struct super_block *sb)
334+
{
335+
struct binderfs_info *info = sb->s_fs_info;
336+
337+
if (info && info->ipc_ns)
338+
put_ipc_ns(info->ipc_ns);
339+
340+
kfree(info);
341+
sb->s_fs_info = NULL;
342+
}
343+
353344
static const struct super_operations binderfs_super_ops = {
354345
.evict_inode = binderfs_evict_inode,
355-
.remount_fs = binderfs_remount,
356-
.show_options = binderfs_show_mount_opts,
346+
.show_options = binderfs_show_options,
357347
.statfs = simple_statfs,
348+
.put_super = binderfs_put_super,
358349
};
359350

360351
static inline bool is_binderfs_control_device(const struct dentry *dentry)
@@ -652,10 +643,11 @@ static int init_binder_logs(struct super_block *sb)
652643
return ret;
653644
}
654645

655-
static int binderfs_fill_super(struct super_block *sb, void *data, int silent)
646+
static int binderfs_fill_super(struct super_block *sb, struct fs_context *fc)
656647
{
657648
int ret;
658649
struct binderfs_info *info;
650+
struct binderfs_mount_opts *ctx = fc->fs_private;
659651
struct inode *inode = NULL;
660652
struct binderfs_device device_info = { 0 };
661653
const char *name;
@@ -688,16 +680,14 @@ static int binderfs_fill_super(struct super_block *sb, void *data, int silent)
688680

689681
info->ipc_ns = get_ipc_ns(current->nsproxy->ipc_ns);
690682

691-
ret = binderfs_parse_mount_opts(data, &info->mount_opts);
692-
if (ret)
693-
return ret;
694-
695683
info->root_gid = make_kgid(sb->s_user_ns, 0);
696684
if (!gid_valid(info->root_gid))
697685
info->root_gid = GLOBAL_ROOT_GID;
698686
info->root_uid = make_kuid(sb->s_user_ns, 0);
699687
if (!uid_valid(info->root_uid))
700688
info->root_uid = GLOBAL_ROOT_UID;
689+
info->mount_opts.max = ctx->max;
690+
info->mount_opts.stats_mode = ctx->stats_mode;
701691

702692
inode = new_inode(sb);
703693
if (!inode)
@@ -729,36 +719,54 @@ static int binderfs_fill_super(struct super_block *sb, void *data, int silent)
729719
name++;
730720
}
731721

732-
if (info->mount_opts.stats_mode == STATS_GLOBAL)
722+
if (info->mount_opts.stats_mode == binderfs_stats_mode_global)
733723
return init_binder_logs(sb);
734724

735725
return 0;
736726
}
737727

738-
static struct dentry *binderfs_mount(struct file_system_type *fs_type,
739-
int flags, const char *dev_name,
740-
void *data)
728+
static int binderfs_fs_context_get_tree(struct fs_context *fc)
741729
{
742-
return mount_nodev(fs_type, flags, data, binderfs_fill_super);
730+
return get_tree_nodev(fc, binderfs_fill_super);
743731
}
744732

745-
static void binderfs_kill_super(struct super_block *sb)
733+
static void binderfs_fs_context_free(struct fs_context *fc)
746734
{
747-
struct binderfs_info *info = sb->s_fs_info;
735+
struct binderfs_mount_opts *ctx = fc->fs_private;
748736

749-
kill_litter_super(sb);
737+
kfree(ctx);
738+
}
750739

751-
if (info && info->ipc_ns)
752-
put_ipc_ns(info->ipc_ns);
740+
static const struct fs_context_operations binderfs_fs_context_ops = {
741+
.free = binderfs_fs_context_free,
742+
.get_tree = binderfs_fs_context_get_tree,
743+
.parse_param = binderfs_fs_context_parse_param,
744+
.reconfigure = binderfs_fs_context_reconfigure,
745+
};
753746

754-
kfree(info);
747+
static int binderfs_init_fs_context(struct fs_context *fc)
748+
{
749+
struct binderfs_mount_opts *ctx = fc->fs_private;
750+
751+
ctx = kzalloc(sizeof(struct binderfs_mount_opts), GFP_KERNEL);
752+
if (!ctx)
753+
return -ENOMEM;
754+
755+
ctx->max = BINDERFS_MAX_MINOR;
756+
ctx->stats_mode = binderfs_stats_mode_unset;
757+
758+
fc->fs_private = ctx;
759+
fc->ops = &binderfs_fs_context_ops;
760+
761+
return 0;
755762
}
756763

757764
static struct file_system_type binder_fs_type = {
758-
.name = "binder",
759-
.mount = binderfs_mount,
760-
.kill_sb = binderfs_kill_super,
761-
.fs_flags = FS_USERNS_MOUNT,
765+
.name = "binder",
766+
.init_fs_context = binderfs_init_fs_context,
767+
.parameters = binderfs_fs_parameters,
768+
.kill_sb = kill_litter_super,
769+
.fs_flags = FS_USERNS_MOUNT,
762770
};
763771

764772
int __init init_binderfs(void)

0 commit comments

Comments
 (0)