Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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 <tkjos@google.com>
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
Link: https://lore.kernel.org/r/20200313153427.141789-1-christian.brauner@ubuntu.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Christian Brauner and committed by
Greg Kroah-Hartman
095cf502 b52cc1bb

+103 -95
+103 -95
drivers/android/binderfs.c
··· 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 + 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) ··· 250 246 static void binderfs_evict_inode(struct inode *inode) 251 247 { 252 248 struct binder_device *device = inode->i_private; 253 - struct binderfs_info *info = BINDERFS_I(inode); 249 + struct binderfs_info *info = BINDERFS_SB(inode->i_sb); 254 250 255 251 clear_inode(inode); 256 252 ··· 268 264 } 269 265 } 270 266 271 - /** 272 - * binderfs_parse_mount_opts - parse binderfs mount options 273 - * @data: options to set (can be NULL in which case defaults are used) 274 - */ 275 - static int binderfs_parse_mount_opts(char *data, 276 - struct binderfs_mount_opts *opts) 267 + static int binderfs_fs_context_parse_param(struct fs_context *fc, 268 + struct fs_parameter *param) 277 269 { 278 - char *p, *stats; 279 - opts->max = BINDERFS_MAX_MINOR; 280 - opts->stats_mode = STATS_NONE; 270 + int opt; 271 + struct binderfs_mount_opts *ctx = fc->fs_private; 272 + struct fs_parse_result result; 281 273 282 - while ((p = strsep(&data, ",")) != NULL) { 283 - substring_t args[MAX_OPT_ARGS]; 284 - int token; 285 - int max_devices; 274 + opt = fs_parse(fc, binderfs_fs_parameters, param, &result); 275 + if (opt < 0) 276 + return opt; 286 277 287 - if (!*p) 288 - continue; 278 + switch (opt) { 279 + case Opt_max: 280 + if (result.uint_32 > BINDERFS_MAX_MINOR) 281 + return invalfc(fc, "Bad value for '%s'", param->key); 289 282 290 - token = match_token(p, tokens, args); 291 - switch (token) { 292 - case Opt_max: 293 - if (match_int(&args[0], &max_devices) || 294 - (max_devices < 0 || 295 - (max_devices > BINDERFS_MAX_MINOR))) 296 - return -EINVAL; 283 + ctx->max = result.uint_32; 284 + break; 285 + case Opt_stats_mode: 286 + if (!capable(CAP_SYS_ADMIN)) 287 + return -EPERM; 297 288 298 - opts->max = max_devices; 299 - break; 300 - case Opt_stats_mode: 301 - if (!capable(CAP_SYS_ADMIN)) 302 - return -EINVAL; 303 - 304 - stats = match_strdup(&args[0]); 305 - if (!stats) 306 - return -ENOMEM; 307 - 308 - if (strcmp(stats, "global") != 0) { 309 - kfree(stats); 310 - return -EINVAL; 311 - } 312 - 313 - opts->stats_mode = STATS_GLOBAL; 314 - kfree(stats); 315 - break; 316 - default: 317 - pr_err("Invalid mount options\n"); 318 - return -EINVAL; 319 - } 289 + ctx->stats_mode = result.uint_32; 290 + break; 291 + default: 292 + return invalfc(fc, "Unsupported parameter '%s'", param->key); 320 293 } 321 294 322 295 return 0; 323 296 } 324 297 325 - static int binderfs_remount(struct super_block *sb, int *flags, char *data) 298 + static int binderfs_fs_context_reconfigure(struct fs_context *fc) 326 299 { 327 - int prev_stats_mode, ret; 328 - struct binderfs_info *info = sb->s_fs_info; 300 + struct binderfs_mount_opts *ctx = fc->fs_private; 301 + struct binderfs_info *info = BINDERFS_SB(fc->root->d_sb); 329 302 330 - prev_stats_mode = info->mount_opts.stats_mode; 331 - ret = binderfs_parse_mount_opts(data, &info->mount_opts); 332 - if (ret) 333 - return ret; 303 + if (info->mount_opts.stats_mode != ctx->stats_mode) 304 + return invalfc(fc, "Binderfs stats mode cannot be changed during a remount"); 334 305 335 - if (prev_stats_mode != info->mount_opts.stats_mode) { 336 - pr_err("Binderfs stats mode cannot be changed during a remount\n"); 337 - info->mount_opts.stats_mode = prev_stats_mode; 338 - return -EINVAL; 339 - } 340 - 306 + info->mount_opts.stats_mode = ctx->stats_mode; 307 + info->mount_opts.max = ctx->max; 341 308 return 0; 342 309 } 343 310 344 - static int binderfs_show_mount_opts(struct seq_file *seq, struct dentry *root) 311 + static int binderfs_show_options(struct seq_file *seq, struct dentry *root) 345 312 { 346 - struct binderfs_info *info; 313 + struct binderfs_info *info = BINDERFS_SB(root->d_sb); 347 314 348 - info = root->d_sb->s_fs_info; 349 315 if (info->mount_opts.max <= BINDERFS_MAX_MINOR) 350 316 seq_printf(seq, ",max=%d", info->mount_opts.max); 351 - if (info->mount_opts.stats_mode == STATS_GLOBAL) 317 + 318 + switch (info->mount_opts.stats_mode) { 319 + case binderfs_stats_mode_unset: 320 + break; 321 + case binderfs_stats_mode_global: 352 322 seq_printf(seq, ",stats=global"); 323 + break; 324 + } 353 325 354 326 return 0; 327 + } 328 + 329 + static void binderfs_put_super(struct super_block *sb) 330 + { 331 + struct binderfs_info *info = sb->s_fs_info; 332 + 333 + if (info && info->ipc_ns) 334 + put_ipc_ns(info->ipc_ns); 335 + 336 + kfree(info); 337 + sb->s_fs_info = NULL; 355 338 } 356 339 357 340 static const struct super_operations binderfs_super_ops = { 358 341 .evict_inode = binderfs_evict_inode, 359 - .remount_fs = binderfs_remount, 360 - .show_options = binderfs_show_mount_opts, 342 + .show_options = binderfs_show_options, 361 343 .statfs = simple_statfs, 344 + .put_super = binderfs_put_super, 362 345 }; 363 346 364 347 static inline bool is_binderfs_control_device(const struct dentry *dentry) ··· 643 652 return ret; 644 653 } 645 654 646 - static int binderfs_fill_super(struct super_block *sb, void *data, int silent) 655 + static int binderfs_fill_super(struct super_block *sb, struct fs_context *fc) 647 656 { 648 657 int ret; 649 658 struct binderfs_info *info; 659 + struct binderfs_mount_opts *ctx = fc->fs_private; 650 660 struct inode *inode = NULL; 651 661 struct binderfs_device device_info = { 0 }; 652 662 const char *name; ··· 680 688 681 689 info->ipc_ns = get_ipc_ns(current->nsproxy->ipc_ns); 682 690 683 - ret = binderfs_parse_mount_opts(data, &info->mount_opts); 684 - if (ret) 685 - return ret; 686 - 687 691 info->root_gid = make_kgid(sb->s_user_ns, 0); 688 692 if (!gid_valid(info->root_gid)) 689 693 info->root_gid = GLOBAL_ROOT_GID; 690 694 info->root_uid = make_kuid(sb->s_user_ns, 0); 691 695 if (!uid_valid(info->root_uid)) 692 696 info->root_uid = GLOBAL_ROOT_UID; 697 + info->mount_opts.max = ctx->max; 698 + info->mount_opts.stats_mode = ctx->stats_mode; 693 699 694 700 inode = new_inode(sb); 695 701 if (!inode) ··· 719 729 name++; 720 730 } 721 731 722 - if (info->mount_opts.stats_mode == STATS_GLOBAL) 732 + if (info->mount_opts.stats_mode == binderfs_stats_mode_global) 723 733 return init_binder_logs(sb); 724 734 725 735 return 0; 726 736 } 727 737 728 - static struct dentry *binderfs_mount(struct file_system_type *fs_type, 729 - int flags, const char *dev_name, 730 - void *data) 738 + static int binderfs_fs_context_get_tree(struct fs_context *fc) 731 739 { 732 - return mount_nodev(fs_type, flags, data, binderfs_fill_super); 740 + return get_tree_nodev(fc, binderfs_fill_super); 733 741 } 734 742 735 - static void binderfs_kill_super(struct super_block *sb) 743 + static void binderfs_fs_context_free(struct fs_context *fc) 736 744 { 737 - struct binderfs_info *info = sb->s_fs_info; 745 + struct binderfs_mount_opts *ctx = fc->fs_private; 738 746 739 - kill_litter_super(sb); 747 + kfree(ctx); 748 + } 740 749 741 - if (info && info->ipc_ns) 742 - put_ipc_ns(info->ipc_ns); 750 + static const struct fs_context_operations binderfs_fs_context_ops = { 751 + .free = binderfs_fs_context_free, 752 + .get_tree = binderfs_fs_context_get_tree, 753 + .parse_param = binderfs_fs_context_parse_param, 754 + .reconfigure = binderfs_fs_context_reconfigure, 755 + }; 743 756 744 - kfree(info); 757 + static int binderfs_init_fs_context(struct fs_context *fc) 758 + { 759 + struct binderfs_mount_opts *ctx = fc->fs_private; 760 + 761 + ctx = kzalloc(sizeof(struct binderfs_mount_opts), GFP_KERNEL); 762 + if (!ctx) 763 + return -ENOMEM; 764 + 765 + ctx->max = BINDERFS_MAX_MINOR; 766 + ctx->stats_mode = binderfs_stats_mode_unset; 767 + 768 + fc->fs_private = ctx; 769 + fc->ops = &binderfs_fs_context_ops; 770 + 771 + return 0; 745 772 } 746 773 747 774 static struct file_system_type binder_fs_type = { 748 - .name = "binder", 749 - .mount = binderfs_mount, 750 - .kill_sb = binderfs_kill_super, 751 - .fs_flags = FS_USERNS_MOUNT, 775 + .name = "binder", 776 + .init_fs_context = binderfs_init_fs_context, 777 + .parameters = binderfs_fs_parameters, 778 + .kill_sb = kill_litter_super, 779 + .fs_flags = FS_USERNS_MOUNT, 752 780 }; 753 781 754 782 int __init init_binderfs(void)