.. | .. |
---|
1 | | -/* SPDX-License-Identifier: GPL-2.0 */ |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0 |
---|
2 | 2 | |
---|
3 | 3 | #include <linux/compiler_types.h> |
---|
4 | 4 | #include <linux/errno.h> |
---|
.. | .. |
---|
18 | 18 | #include <linux/module.h> |
---|
19 | 19 | #include <linux/mutex.h> |
---|
20 | 20 | #include <linux/mount.h> |
---|
21 | | -#include <linux/parser.h> |
---|
| 21 | +#include <linux/fs_parser.h> |
---|
22 | 22 | #include <linux/radix-tree.h> |
---|
23 | 23 | #include <linux/sched.h> |
---|
24 | 24 | #include <linux/seq_file.h> |
---|
.. | .. |
---|
48 | 48 | static DEFINE_MUTEX(binderfs_minors_mutex); |
---|
49 | 49 | static DEFINE_IDA(binderfs_minors); |
---|
50 | 50 | |
---|
51 | | -enum { |
---|
| 51 | +enum binderfs_param { |
---|
52 | 52 | Opt_max, |
---|
53 | 53 | Opt_stats_mode, |
---|
54 | | - Opt_err |
---|
55 | 54 | }; |
---|
56 | 55 | |
---|
57 | 56 | enum binderfs_stats_mode { |
---|
58 | | - STATS_NONE, |
---|
59 | | - STATS_GLOBAL, |
---|
| 57 | + binderfs_stats_mode_unset, |
---|
| 58 | + binderfs_stats_mode_global, |
---|
60 | 59 | }; |
---|
61 | 60 | |
---|
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 | + {} |
---|
66 | 64 | }; |
---|
67 | 65 | |
---|
68 | | -static inline struct binderfs_info *BINDERFS_I(const struct inode *inode) |
---|
| 66 | +static 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) |
---|
69 | 73 | { |
---|
70 | | - return inode->i_sb->s_fs_info; |
---|
| 74 | + return sb->s_fs_info; |
---|
71 | 75 | } |
---|
72 | 76 | |
---|
73 | 77 | bool is_binderfs_device(const struct inode *inode) |
---|
.. | .. |
---|
246 | 250 | static void binderfs_evict_inode(struct inode *inode) |
---|
247 | 251 | { |
---|
248 | 252 | 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); |
---|
250 | 254 | |
---|
251 | 255 | clear_inode(inode); |
---|
252 | 256 | |
---|
.. | .. |
---|
264 | 268 | } |
---|
265 | 269 | } |
---|
266 | 270 | |
---|
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) |
---|
273 | 273 | { |
---|
274 | | - char *p, *stats; |
---|
275 | | - opts->max = BINDERFS_MAX_MINOR; |
---|
276 | | - opts->stats_mode = STATS_NONE; |
---|
| 274 | + int opt; |
---|
| 275 | + struct binderfs_mount_opts *ctx = fc->fs_private; |
---|
| 276 | + struct fs_parse_result result; |
---|
277 | 277 | |
---|
278 | | - while ((p = strsep(&data, ",")) != NULL) { |
---|
279 | | - substring_t args[MAX_OPT_ARGS]; |
---|
280 | | - int token; |
---|
281 | | - int max_devices; |
---|
| 278 | + opt = fs_parse(fc, binderfs_fs_parameters, param, &result); |
---|
| 279 | + if (opt < 0) |
---|
| 280 | + return opt; |
---|
282 | 281 | |
---|
283 | | - if (!*p) |
---|
284 | | - continue; |
---|
| 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); |
---|
285 | 286 | |
---|
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; |
---|
| 287 | + ctx->max = result.uint_32; |
---|
| 288 | + break; |
---|
| 289 | + case Opt_stats_mode: |
---|
| 290 | + if (!capable(CAP_SYS_ADMIN)) |
---|
| 291 | + return -EPERM; |
---|
293 | 292 | |
---|
294 | | - opts->max = max_devices; |
---|
295 | | - break; |
---|
296 | | - case Opt_stats_mode: |
---|
297 | | - if (!capable(CAP_SYS_ADMIN)) |
---|
298 | | - return -EINVAL; |
---|
299 | | - |
---|
300 | | - stats = match_strdup(&args[0]); |
---|
301 | | - if (!stats) |
---|
302 | | - return -ENOMEM; |
---|
303 | | - |
---|
304 | | - if (strcmp(stats, "global") != 0) { |
---|
305 | | - kfree(stats); |
---|
306 | | - return -EINVAL; |
---|
307 | | - } |
---|
308 | | - |
---|
309 | | - opts->stats_mode = STATS_GLOBAL; |
---|
310 | | - kfree(stats); |
---|
311 | | - break; |
---|
312 | | - default: |
---|
313 | | - pr_err("Invalid mount options\n"); |
---|
314 | | - return -EINVAL; |
---|
315 | | - } |
---|
| 293 | + ctx->stats_mode = result.uint_32; |
---|
| 294 | + break; |
---|
| 295 | + default: |
---|
| 296 | + return invalfc(fc, "Unsupported parameter '%s'", param->key); |
---|
316 | 297 | } |
---|
317 | 298 | |
---|
318 | 299 | return 0; |
---|
319 | 300 | } |
---|
320 | 301 | |
---|
321 | | -static int binderfs_remount(struct super_block *sb, int *flags, char *data) |
---|
| 302 | +static int binderfs_fs_context_reconfigure(struct fs_context *fc) |
---|
322 | 303 | { |
---|
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); |
---|
325 | 306 | |
---|
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; |
---|
| 307 | + if (info->mount_opts.stats_mode != ctx->stats_mode) |
---|
| 308 | + return invalfc(fc, "Binderfs stats mode cannot be changed during a remount"); |
---|
330 | 309 | |
---|
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 | | - } |
---|
336 | | - |
---|
| 310 | + info->mount_opts.stats_mode = ctx->stats_mode; |
---|
| 311 | + info->mount_opts.max = ctx->max; |
---|
337 | 312 | return 0; |
---|
338 | 313 | } |
---|
339 | 314 | |
---|
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) |
---|
341 | 316 | { |
---|
342 | | - struct binderfs_info *info; |
---|
| 317 | + struct binderfs_info *info = BINDERFS_SB(root->d_sb); |
---|
343 | 318 | |
---|
344 | | - info = root->d_sb->s_fs_info; |
---|
345 | 319 | if (info->mount_opts.max <= BINDERFS_MAX_MINOR) |
---|
346 | 320 | 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: |
---|
348 | 326 | seq_printf(seq, ",stats=global"); |
---|
| 327 | + break; |
---|
| 328 | + } |
---|
349 | 329 | |
---|
350 | 330 | return 0; |
---|
| 331 | +} |
---|
| 332 | + |
---|
| 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; |
---|
351 | 342 | } |
---|
352 | 343 | |
---|
353 | 344 | static const struct super_operations binderfs_super_ops = { |
---|
354 | 345 | .evict_inode = binderfs_evict_inode, |
---|
355 | | - .remount_fs = binderfs_remount, |
---|
356 | | - .show_options = binderfs_show_mount_opts, |
---|
| 346 | + .show_options = binderfs_show_options, |
---|
357 | 347 | .statfs = simple_statfs, |
---|
| 348 | + .put_super = binderfs_put_super, |
---|
358 | 349 | }; |
---|
359 | 350 | |
---|
360 | 351 | static inline bool is_binderfs_control_device(const struct dentry *dentry) |
---|
361 | 352 | { |
---|
362 | 353 | struct binderfs_info *info = dentry->d_sb->s_fs_info; |
---|
| 354 | + |
---|
363 | 355 | return info->control_dentry == dentry; |
---|
364 | 356 | } |
---|
365 | 357 | |
---|
.. | .. |
---|
592 | 584 | static int init_binder_logs(struct super_block *sb) |
---|
593 | 585 | { |
---|
594 | 586 | struct dentry *binder_logs_root_dir, *dentry, *proc_log_dir; |
---|
| 587 | + const struct binder_debugfs_entry *db_entry; |
---|
595 | 588 | struct binderfs_info *info; |
---|
596 | 589 | int ret = 0; |
---|
597 | 590 | |
---|
.. | .. |
---|
602 | 595 | goto out; |
---|
603 | 596 | } |
---|
604 | 597 | |
---|
605 | | - dentry = binderfs_create_file(binder_logs_root_dir, "stats", |
---|
606 | | - &binder_stats_fops, NULL); |
---|
607 | | - if (IS_ERR(dentry)) { |
---|
608 | | - ret = PTR_ERR(dentry); |
---|
609 | | - goto out; |
---|
610 | | - } |
---|
611 | | - |
---|
612 | | - dentry = binderfs_create_file(binder_logs_root_dir, "state", |
---|
613 | | - &binder_state_fops, NULL); |
---|
614 | | - if (IS_ERR(dentry)) { |
---|
615 | | - ret = PTR_ERR(dentry); |
---|
616 | | - goto out; |
---|
617 | | - } |
---|
618 | | - |
---|
619 | | - dentry = binderfs_create_file(binder_logs_root_dir, "transactions", |
---|
620 | | - &binder_transactions_fops, NULL); |
---|
621 | | - if (IS_ERR(dentry)) { |
---|
622 | | - ret = PTR_ERR(dentry); |
---|
623 | | - goto out; |
---|
624 | | - } |
---|
625 | | - |
---|
626 | | - dentry = binderfs_create_file(binder_logs_root_dir, |
---|
627 | | - "transaction_log", |
---|
628 | | - &binder_transaction_log_fops, |
---|
629 | | - &binder_transaction_log); |
---|
630 | | - if (IS_ERR(dentry)) { |
---|
631 | | - ret = PTR_ERR(dentry); |
---|
632 | | - goto out; |
---|
633 | | - } |
---|
634 | | - |
---|
635 | | - dentry = binderfs_create_file(binder_logs_root_dir, |
---|
636 | | - "failed_transaction_log", |
---|
637 | | - &binder_transaction_log_fops, |
---|
638 | | - &binder_transaction_log_failed); |
---|
639 | | - if (IS_ERR(dentry)) { |
---|
640 | | - ret = PTR_ERR(dentry); |
---|
641 | | - goto out; |
---|
| 598 | + binder_for_each_debugfs_entry(db_entry) { |
---|
| 599 | + dentry = binderfs_create_file(binder_logs_root_dir, |
---|
| 600 | + db_entry->name, |
---|
| 601 | + db_entry->fops, |
---|
| 602 | + db_entry->data); |
---|
| 603 | + if (IS_ERR(dentry)) { |
---|
| 604 | + ret = PTR_ERR(dentry); |
---|
| 605 | + goto out; |
---|
| 606 | + } |
---|
642 | 607 | } |
---|
643 | 608 | |
---|
644 | 609 | proc_log_dir = binderfs_create_dir(binder_logs_root_dir, "proc"); |
---|
.. | .. |
---|
653 | 618 | return ret; |
---|
654 | 619 | } |
---|
655 | 620 | |
---|
656 | | -static int binderfs_fill_super(struct super_block *sb, void *data, int silent) |
---|
| 621 | +static int binderfs_fill_super(struct super_block *sb, struct fs_context *fc) |
---|
657 | 622 | { |
---|
658 | 623 | int ret; |
---|
659 | 624 | struct binderfs_info *info; |
---|
| 625 | + struct binderfs_mount_opts *ctx = fc->fs_private; |
---|
660 | 626 | struct inode *inode = NULL; |
---|
661 | | - struct binderfs_device device_info = { 0 }; |
---|
| 627 | + struct binderfs_device device_info = {}; |
---|
662 | 628 | const char *name; |
---|
663 | 629 | size_t len; |
---|
664 | 630 | |
---|
.. | .. |
---|
689 | 655 | |
---|
690 | 656 | info->ipc_ns = get_ipc_ns(current->nsproxy->ipc_ns); |
---|
691 | 657 | |
---|
692 | | - ret = binderfs_parse_mount_opts(data, &info->mount_opts); |
---|
693 | | - if (ret) |
---|
694 | | - return ret; |
---|
695 | | - |
---|
696 | 658 | info->root_gid = make_kgid(sb->s_user_ns, 0); |
---|
697 | 659 | if (!gid_valid(info->root_gid)) |
---|
698 | 660 | info->root_gid = GLOBAL_ROOT_GID; |
---|
699 | 661 | info->root_uid = make_kuid(sb->s_user_ns, 0); |
---|
700 | 662 | if (!uid_valid(info->root_uid)) |
---|
701 | 663 | info->root_uid = GLOBAL_ROOT_UID; |
---|
| 664 | + info->mount_opts.max = ctx->max; |
---|
| 665 | + info->mount_opts.stats_mode = ctx->stats_mode; |
---|
702 | 666 | |
---|
703 | 667 | inode = new_inode(sb); |
---|
704 | 668 | if (!inode) |
---|
.. | .. |
---|
730 | 694 | name++; |
---|
731 | 695 | } |
---|
732 | 696 | |
---|
733 | | - if (info->mount_opts.stats_mode == STATS_GLOBAL) |
---|
| 697 | + if (info->mount_opts.stats_mode == binderfs_stats_mode_global) |
---|
734 | 698 | return init_binder_logs(sb); |
---|
735 | 699 | |
---|
736 | 700 | return 0; |
---|
737 | 701 | } |
---|
738 | 702 | |
---|
739 | | -static struct dentry *binderfs_mount(struct file_system_type *fs_type, |
---|
740 | | - int flags, const char *dev_name, |
---|
741 | | - void *data) |
---|
| 703 | +static int binderfs_fs_context_get_tree(struct fs_context *fc) |
---|
742 | 704 | { |
---|
743 | | - return mount_nodev(fs_type, flags, data, binderfs_fill_super); |
---|
| 705 | + return get_tree_nodev(fc, binderfs_fill_super); |
---|
744 | 706 | } |
---|
745 | 707 | |
---|
746 | | -static void binderfs_kill_super(struct super_block *sb) |
---|
| 708 | +static void binderfs_fs_context_free(struct fs_context *fc) |
---|
747 | 709 | { |
---|
748 | | - struct binderfs_info *info = sb->s_fs_info; |
---|
| 710 | + struct binderfs_mount_opts *ctx = fc->fs_private; |
---|
749 | 711 | |
---|
750 | | - kill_litter_super(sb); |
---|
| 712 | + kfree(ctx); |
---|
| 713 | +} |
---|
751 | 714 | |
---|
752 | | - if (info && info->ipc_ns) |
---|
753 | | - put_ipc_ns(info->ipc_ns); |
---|
| 715 | +static const struct fs_context_operations binderfs_fs_context_ops = { |
---|
| 716 | + .free = binderfs_fs_context_free, |
---|
| 717 | + .get_tree = binderfs_fs_context_get_tree, |
---|
| 718 | + .parse_param = binderfs_fs_context_parse_param, |
---|
| 719 | + .reconfigure = binderfs_fs_context_reconfigure, |
---|
| 720 | +}; |
---|
754 | 721 | |
---|
755 | | - kfree(info); |
---|
| 722 | +static int binderfs_init_fs_context(struct fs_context *fc) |
---|
| 723 | +{ |
---|
| 724 | + struct binderfs_mount_opts *ctx; |
---|
| 725 | + |
---|
| 726 | + ctx = kzalloc(sizeof(struct binderfs_mount_opts), GFP_KERNEL); |
---|
| 727 | + if (!ctx) |
---|
| 728 | + return -ENOMEM; |
---|
| 729 | + |
---|
| 730 | + ctx->max = BINDERFS_MAX_MINOR; |
---|
| 731 | + ctx->stats_mode = binderfs_stats_mode_unset; |
---|
| 732 | + |
---|
| 733 | + fc->fs_private = ctx; |
---|
| 734 | + fc->ops = &binderfs_fs_context_ops; |
---|
| 735 | + |
---|
| 736 | + return 0; |
---|
756 | 737 | } |
---|
757 | 738 | |
---|
758 | 739 | static struct file_system_type binder_fs_type = { |
---|
759 | | - .name = "binder", |
---|
760 | | - .mount = binderfs_mount, |
---|
761 | | - .kill_sb = binderfs_kill_super, |
---|
762 | | - .fs_flags = FS_USERNS_MOUNT, |
---|
| 740 | + .name = "binder", |
---|
| 741 | + .init_fs_context = binderfs_init_fs_context, |
---|
| 742 | + .parameters = binderfs_fs_parameters, |
---|
| 743 | + .kill_sb = kill_litter_super, |
---|
| 744 | + .fs_flags = FS_USERNS_MOUNT, |
---|
763 | 745 | }; |
---|
764 | 746 | |
---|
765 | 747 | int __init init_binderfs(void) |
---|