diff options
Diffstat (limited to 'drivers/android/binderfs.c')
| -rw-r--r-- | drivers/android/binderfs.c | 267 |
1 files changed, 124 insertions, 143 deletions
diff --git a/drivers/android/binderfs.c b/drivers/android/binderfs.c index e80ba93c62a9..b46bcb91072d 100644 --- a/drivers/android/binderfs.c +++ b/drivers/android/binderfs.c @@ -19,7 +19,6 @@ #include <linux/mutex.h> #include <linux/mount.h> #include <linux/fs_parser.h> -#include <linux/radix-tree.h> #include <linux/sched.h> #include <linux/seq_file.h> #include <linux/slab.h> @@ -30,7 +29,6 @@ #include <linux/uaccess.h> #include <linux/user_namespace.h> #include <linux/xarray.h> -#include <uapi/asm-generic/errno-base.h> #include <uapi/linux/android/binder.h> #include <uapi/linux/android/binderfs.h> @@ -39,7 +37,6 @@ #define FIRST_INODE 1 #define SECOND_INODE 2 #define INODE_OFFSET 3 -#define INTSTRLEN 21 #define BINDERFS_MAX_MINOR (1U << MINORBITS) /* Ensure that the initial ipc namespace always has devices available. */ #define BINDERFS_MAX_MINOR_CAPPED (BINDERFS_MAX_MINOR - 4) @@ -58,6 +55,13 @@ enum binderfs_stats_mode { binderfs_stats_mode_global, }; +struct binder_features { + bool oneway_spam_detection; + bool extended_error; + bool freeze_notification; + bool transaction_report; +}; + static const struct constant_table binderfs_param_stats[] = { { "global", binderfs_stats_mode_global }, {} @@ -69,6 +73,13 @@ static const struct fs_parameter_spec binderfs_fs_parameters[] = { {} }; +static struct binder_features binder_features = { + .oneway_spam_detection = true, + .extended_error = true, + .freeze_notification = true, + .transaction_report = true, +}; + static inline struct binderfs_info *BINDERFS_SB(const struct super_block *sb) { return sb->s_fs_info; @@ -85,7 +96,7 @@ bool is_binderfs_device(const struct inode *inode) /** * binderfs_binder_device_create - allocate inode from super block of a * binderfs mount - * @ref_inode: inode from wich the super block will be taken + * @ref_inode: inode from which the super block will be taken * @userp: buffer to copy information about new device for userspace to * @req: struct binderfs_device as copied from userspace * @@ -108,7 +119,6 @@ static int binderfs_binder_device_create(struct inode *ref_inode, struct dentry *dentry, *root; struct binder_device *device; char *name = NULL; - size_t name_len; struct inode *inode = NULL; struct super_block *sb = ref_inode->i_sb; struct binderfs_info *info = sb->s_fs_info; @@ -144,7 +154,7 @@ static int binderfs_binder_device_create(struct inode *ref_inode, goto err; inode->i_ino = minor + INODE_OFFSET; - inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode); + simple_inode_init_ts(inode); init_special_inode(inode, S_IFCHR | 0600, MKDEV(MAJOR(binderfs_dev), minor)); inode->i_fop = &binder_fops; @@ -152,9 +162,7 @@ static int binderfs_binder_device_create(struct inode *ref_inode, inode->i_gid = info->root_gid; req->name[BINDERFS_MAX_NAME] = '\0'; /* NUL-terminate */ - name_len = strlen(req->name); - /* Make sure to include terminating NUL byte */ - name = kmemdup(req->name, name_len + 1, GFP_KERNEL); + name = kstrdup(req->name, GFP_KERNEL); if (!name) goto err; @@ -175,28 +183,17 @@ static int binderfs_binder_device_create(struct inode *ref_inode, } root = sb->s_root; - inode_lock(d_inode(root)); - - /* look it up */ - dentry = lookup_one_len(name, root, name_len); + dentry = simple_start_creating(root, name); if (IS_ERR(dentry)) { - inode_unlock(d_inode(root)); ret = PTR_ERR(dentry); goto err; } - - if (d_really_is_positive(dentry)) { - /* already exists */ - dput(dentry); - inode_unlock(d_inode(root)); - ret = -EEXIST; - goto err; - } - inode->i_private = device; - d_instantiate(dentry, inode); + d_make_persistent(dentry, inode); fsnotify_create(root->d_inode, dentry); - inode_unlock(d_inode(root)); + simple_done_creating(dentry); + + binder_add_device(device); return 0; @@ -213,14 +210,17 @@ err: } /** - * binderfs_ctl_ioctl - handle binder device node allocation requests + * binder_ctl_ioctl - handle binder device node allocation requests + * @file: The file pointer for the binder-control device node. + * @cmd: The ioctl command. + * @arg: The ioctl argument. * * The request handler for the binder-control device. All requests operate on * the binderfs mount the binder-control device resides in: * - BINDER_CTL_ADD * Allocate a new binder device. * - * Return: 0 on success, negative errno on failure + * Return: %0 on success, negative errno on failure. */ static long binder_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg) @@ -263,6 +263,7 @@ static void binderfs_evict_inode(struct inode *inode) mutex_unlock(&binderfs_minors_mutex); if (refcount_dec_and_test(&device->ref)) { + binder_remove_device(device); kfree(device->context.name); kfree(device); } @@ -330,22 +331,10 @@ static int binderfs_show_options(struct seq_file *seq, struct dentry *root) return 0; } -static void binderfs_put_super(struct super_block *sb) -{ - struct binderfs_info *info = sb->s_fs_info; - - if (info && info->ipc_ns) - put_ipc_ns(info->ipc_ns); - - kfree(info); - sb->s_fs_info = NULL; -} - static const struct super_operations binderfs_super_ops = { .evict_inode = binderfs_evict_inode, .show_options = binderfs_show_options, .statfs = simple_statfs, - .put_super = binderfs_put_super, }; static inline bool is_binderfs_control_device(const struct dentry *dentry) @@ -355,7 +344,7 @@ static inline bool is_binderfs_control_device(const struct dentry *dentry) return info->control_dentry == dentry; } -static int binderfs_rename(struct user_namespace *mnt_userns, +static int binderfs_rename(struct mnt_idmap *idmap, struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry, unsigned int flags) @@ -364,7 +353,7 @@ static int binderfs_rename(struct user_namespace *mnt_userns, is_binderfs_control_device(new_dentry)) return -EPERM; - return simple_rename(&init_user_ns, old_dir, old_dentry, new_dir, + return simple_rename(idmap, old_dir, old_dentry, new_dir, new_dentry, flags); } @@ -411,12 +400,6 @@ static int binderfs_binder_ctl_create(struct super_block *sb) if (!device) return -ENOMEM; - /* If we have already created a binder-control node, return. */ - if (info->control_dentry) { - ret = 0; - goto out; - } - ret = -ENOMEM; inode = new_inode(sb); if (!inode) @@ -435,7 +418,7 @@ static int binderfs_binder_ctl_create(struct super_block *sb) } inode->i_ino = SECOND_INODE; - inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode); + simple_inode_init_ts(inode); init_special_inode(inode, S_IFCHR | 0600, MKDEV(MAJOR(binderfs_dev), minor)); inode->i_fop = &binder_ctl_fops; @@ -452,7 +435,8 @@ static int binderfs_binder_ctl_create(struct super_block *sb) inode->i_private = device; info->control_dentry = dentry; - d_add(dentry, inode); + d_make_persistent(dentry, inode); + dput(dentry); return 0; @@ -477,44 +461,11 @@ static struct inode *binderfs_make_inode(struct super_block *sb, int mode) if (ret) { ret->i_ino = iunique(sb, BINDERFS_MAX_MINOR + INODE_OFFSET); ret->i_mode = mode; - ret->i_atime = ret->i_mtime = ret->i_ctime = current_time(ret); + simple_inode_init_ts(ret); } return ret; } -static struct dentry *binderfs_create_dentry(struct dentry *parent, - const char *name) -{ - struct dentry *dentry; - - dentry = lookup_one_len(name, parent, strlen(name)); - if (IS_ERR(dentry)) - return dentry; - - /* Return error if the file/dir already exists. */ - if (d_really_is_positive(dentry)) { - dput(dentry); - return ERR_PTR(-EEXIST); - } - - return dentry; -} - -void binderfs_remove_file(struct dentry *dentry) -{ - struct inode *parent_inode; - - parent_inode = d_inode(dentry->d_parent); - inode_lock(parent_inode); - if (simple_positive(dentry)) { - dget(dentry); - simple_unlink(parent_inode, dentry); - d_delete(dentry); - dput(dentry); - } - inode_unlock(parent_inode); -} - struct dentry *binderfs_create_file(struct dentry *parent, const char *name, const struct file_operations *fops, void *data) @@ -524,28 +475,24 @@ struct dentry *binderfs_create_file(struct dentry *parent, const char *name, struct super_block *sb; parent_inode = d_inode(parent); - inode_lock(parent_inode); - dentry = binderfs_create_dentry(parent, name); + dentry = simple_start_creating(parent, name); if (IS_ERR(dentry)) - goto out; + return dentry; sb = parent_inode->i_sb; new_inode = binderfs_make_inode(sb, S_IFREG | 0444); if (!new_inode) { - dput(dentry); - dentry = ERR_PTR(-ENOMEM); - goto out; + simple_done_creating(dentry); + return ERR_PTR(-ENOMEM); } new_inode->i_fop = fops; new_inode->i_private = data; - d_instantiate(dentry, new_inode); + d_make_persistent(dentry, new_inode); fsnotify_create(parent_inode, dentry); - -out: - inode_unlock(parent_inode); - return dentry; + simple_done_creating(dentry); + return dentry; // borrowed } static struct dentry *binderfs_create_dir(struct dentry *parent, @@ -556,36 +503,78 @@ static struct dentry *binderfs_create_dir(struct dentry *parent, struct super_block *sb; parent_inode = d_inode(parent); - inode_lock(parent_inode); - dentry = binderfs_create_dentry(parent, name); + dentry = simple_start_creating(parent, name); if (IS_ERR(dentry)) - goto out; + return dentry; sb = parent_inode->i_sb; new_inode = binderfs_make_inode(sb, S_IFDIR | 0755); if (!new_inode) { - dput(dentry); - dentry = ERR_PTR(-ENOMEM); - goto out; + simple_done_creating(dentry); + return ERR_PTR(-ENOMEM); } new_inode->i_fop = &simple_dir_operations; new_inode->i_op = &simple_dir_inode_operations; set_nlink(new_inode, 2); - d_instantiate(dentry, new_inode); + d_make_persistent(dentry, new_inode); inc_nlink(parent_inode); fsnotify_mkdir(parent_inode, dentry); - -out: - inode_unlock(parent_inode); + simple_done_creating(dentry); return dentry; } +static int binder_features_show(struct seq_file *m, void *unused) +{ + bool *feature = m->private; + + seq_printf(m, "%d\n", *feature); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(binder_features); + +static int init_binder_features(struct super_block *sb) +{ + struct dentry *dentry, *dir; + + dir = binderfs_create_dir(sb->s_root, "features"); + if (IS_ERR(dir)) + return PTR_ERR(dir); + + dentry = binderfs_create_file(dir, "oneway_spam_detection", + &binder_features_fops, + &binder_features.oneway_spam_detection); + if (IS_ERR(dentry)) + return PTR_ERR(dentry); + + dentry = binderfs_create_file(dir, "extended_error", + &binder_features_fops, + &binder_features.extended_error); + if (IS_ERR(dentry)) + return PTR_ERR(dentry); + + dentry = binderfs_create_file(dir, "freeze_notification", + &binder_features_fops, + &binder_features.freeze_notification); + if (IS_ERR(dentry)) + return PTR_ERR(dentry); + + dentry = binderfs_create_file(dir, "transaction_report", + &binder_features_fops, + &binder_features.transaction_report); + if (IS_ERR(dentry)) + return PTR_ERR(dentry); + + return 0; +} + static int init_binder_logs(struct super_block *sb) { struct dentry *binder_logs_root_dir, *dentry, *proc_log_dir; + const struct binder_debugfs_entry *db_entry; struct binderfs_info *info; int ret = 0; @@ -596,43 +585,15 @@ static int init_binder_logs(struct super_block *sb) goto out; } - dentry = binderfs_create_file(binder_logs_root_dir, "stats", - &binder_stats_fops, NULL); - if (IS_ERR(dentry)) { - ret = PTR_ERR(dentry); - goto out; - } - - dentry = binderfs_create_file(binder_logs_root_dir, "state", - &binder_state_fops, NULL); - if (IS_ERR(dentry)) { - ret = PTR_ERR(dentry); - goto out; - } - - dentry = binderfs_create_file(binder_logs_root_dir, "transactions", - &binder_transactions_fops, NULL); - if (IS_ERR(dentry)) { - ret = PTR_ERR(dentry); - goto out; - } - - dentry = binderfs_create_file(binder_logs_root_dir, - "transaction_log", - &binder_transaction_log_fops, - &binder_transaction_log); - if (IS_ERR(dentry)) { - ret = PTR_ERR(dentry); - goto out; - } - - dentry = binderfs_create_file(binder_logs_root_dir, - "failed_transaction_log", - &binder_transaction_log_fops, - &binder_transaction_log_failed); - if (IS_ERR(dentry)) { - ret = PTR_ERR(dentry); - goto out; + binder_for_each_debugfs_entry(db_entry) { + dentry = binderfs_create_file(binder_logs_root_dir, + db_entry->name, + db_entry->fops, + db_entry->data); + if (IS_ERR(dentry)) { + ret = PTR_ERR(dentry); + goto out; + } } proc_log_dir = binderfs_create_dir(binder_logs_root_dir, "proc"); @@ -700,7 +661,7 @@ static int binderfs_fill_super(struct super_block *sb, struct fs_context *fc) inode->i_ino = FIRST_INODE; inode->i_fop = &simple_dir_operations; inode->i_mode = S_IFDIR | 0755; - inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode); + simple_inode_init_ts(inode); inode->i_op = &binderfs_dir_inode_operations; set_nlink(inode, 2); @@ -723,6 +684,10 @@ static int binderfs_fill_super(struct super_block *sb, struct fs_context *fc) name++; } + ret = init_binder_features(sb); + if (ret) + return ret; + if (info->mount_opts.stats_mode == binderfs_stats_mode_global) return init_binder_logs(sb); @@ -765,11 +730,27 @@ static int binderfs_init_fs_context(struct fs_context *fc) return 0; } +static void binderfs_kill_super(struct super_block *sb) +{ + struct binderfs_info *info = sb->s_fs_info; + + /* + * During inode eviction struct binderfs_info is needed. + * So first wipe the super_block then free struct binderfs_info. + */ + kill_anon_super(sb); + + if (info && info->ipc_ns) + put_ipc_ns(info->ipc_ns); + + kfree(info); +} + static struct file_system_type binder_fs_type = { .name = "binder", .init_fs_context = binderfs_init_fs_context, .parameters = binderfs_fs_parameters, - .kill_sb = kill_litter_super, + .kill_sb = binderfs_kill_super, .fs_flags = FS_USERNS_MOUNT, }; |
