| .. | .. |
|---|
| 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) |
|---|