xref: /openbmc/linux/drivers/android/binderfs.c (revision 211b64e4)
13ad20fe3SChristian Brauner /* SPDX-License-Identifier: GPL-2.0 */
23ad20fe3SChristian Brauner 
33ad20fe3SChristian Brauner #include <linux/compiler_types.h>
43ad20fe3SChristian Brauner #include <linux/errno.h>
53ad20fe3SChristian Brauner #include <linux/fs.h>
63ad20fe3SChristian Brauner #include <linux/fsnotify.h>
73ad20fe3SChristian Brauner #include <linux/gfp.h>
83ad20fe3SChristian Brauner #include <linux/idr.h>
93ad20fe3SChristian Brauner #include <linux/init.h>
103ad20fe3SChristian Brauner #include <linux/ipc_namespace.h>
113ad20fe3SChristian Brauner #include <linux/kdev_t.h>
123ad20fe3SChristian Brauner #include <linux/kernel.h>
133ad20fe3SChristian Brauner #include <linux/list.h>
1401b3f1fcSChristian Brauner #include <linux/namei.h>
153ad20fe3SChristian Brauner #include <linux/magic.h>
163ad20fe3SChristian Brauner #include <linux/major.h>
173ad20fe3SChristian Brauner #include <linux/miscdevice.h>
183ad20fe3SChristian Brauner #include <linux/module.h>
193ad20fe3SChristian Brauner #include <linux/mutex.h>
203ad20fe3SChristian Brauner #include <linux/mount.h>
213ad20fe3SChristian Brauner #include <linux/parser.h>
223ad20fe3SChristian Brauner #include <linux/radix-tree.h>
233ad20fe3SChristian Brauner #include <linux/sched.h>
24849d540dSChristian Brauner #include <linux/seq_file.h>
253ad20fe3SChristian Brauner #include <linux/slab.h>
263ad20fe3SChristian Brauner #include <linux/spinlock_types.h>
273ad20fe3SChristian Brauner #include <linux/stddef.h>
283ad20fe3SChristian Brauner #include <linux/string.h>
293ad20fe3SChristian Brauner #include <linux/types.h>
303ad20fe3SChristian Brauner #include <linux/uaccess.h>
313ad20fe3SChristian Brauner #include <linux/user_namespace.h>
323ad20fe3SChristian Brauner #include <linux/xarray.h>
333ad20fe3SChristian Brauner #include <uapi/asm-generic/errno-base.h>
343ad20fe3SChristian Brauner #include <uapi/linux/android/binder.h>
35c13295adSChristian Brauner #include <uapi/linux/android/binderfs.h>
363ad20fe3SChristian Brauner 
373ad20fe3SChristian Brauner #include "binder_internal.h"
383ad20fe3SChristian Brauner 
393ad20fe3SChristian Brauner #define FIRST_INODE 1
403ad20fe3SChristian Brauner #define SECOND_INODE 2
413ad20fe3SChristian Brauner #define INODE_OFFSET 3
423ad20fe3SChristian Brauner #define INTSTRLEN 21
433ad20fe3SChristian Brauner #define BINDERFS_MAX_MINOR (1U << MINORBITS)
4436bdf3caSChristian Brauner /* Ensure that the initial ipc namespace always has devices available. */
4536bdf3caSChristian Brauner #define BINDERFS_MAX_MINOR_CAPPED (BINDERFS_MAX_MINOR - 4)
463ad20fe3SChristian Brauner 
473ad20fe3SChristian Brauner static dev_t binderfs_dev;
483ad20fe3SChristian Brauner static DEFINE_MUTEX(binderfs_minors_mutex);
493ad20fe3SChristian Brauner static DEFINE_IDA(binderfs_minors);
503ad20fe3SChristian Brauner 
51849d540dSChristian Brauner enum {
52849d540dSChristian Brauner 	Opt_max,
53f0083451SHridya Valsaraju 	Opt_stats_mode,
54849d540dSChristian Brauner 	Opt_err
55849d540dSChristian Brauner };
56849d540dSChristian Brauner 
57f0083451SHridya Valsaraju enum binderfs_stats_mode {
58f0083451SHridya Valsaraju 	STATS_NONE,
59f0083451SHridya Valsaraju 	STATS_GLOBAL,
60f0083451SHridya Valsaraju };
61f0083451SHridya Valsaraju 
62849d540dSChristian Brauner static const match_table_t tokens = {
63849d540dSChristian Brauner 	{ Opt_max, "max=%d" },
64f0083451SHridya Valsaraju 	{ Opt_stats_mode, "stats=%s" },
65849d540dSChristian Brauner 	{ Opt_err, NULL     }
66849d540dSChristian Brauner };
67849d540dSChristian Brauner 
683ad20fe3SChristian Brauner static inline struct binderfs_info *BINDERFS_I(const struct inode *inode)
693ad20fe3SChristian Brauner {
703ad20fe3SChristian Brauner 	return inode->i_sb->s_fs_info;
713ad20fe3SChristian Brauner }
723ad20fe3SChristian Brauner 
733ad20fe3SChristian Brauner bool is_binderfs_device(const struct inode *inode)
743ad20fe3SChristian Brauner {
753ad20fe3SChristian Brauner 	if (inode->i_sb->s_magic == BINDERFS_SUPER_MAGIC)
763ad20fe3SChristian Brauner 		return true;
773ad20fe3SChristian Brauner 
783ad20fe3SChristian Brauner 	return false;
793ad20fe3SChristian Brauner }
803ad20fe3SChristian Brauner 
813ad20fe3SChristian Brauner /**
823ad20fe3SChristian Brauner  * binderfs_binder_device_create - allocate inode from super block of a
833ad20fe3SChristian Brauner  *                                 binderfs mount
843ad20fe3SChristian Brauner  * @ref_inode: inode from wich the super block will be taken
853ad20fe3SChristian Brauner  * @userp:     buffer to copy information about new device for userspace to
863ad20fe3SChristian Brauner  * @req:       struct binderfs_device as copied from userspace
873ad20fe3SChristian Brauner  *
8801b3f1fcSChristian Brauner  * This function allocates a new binder_device and reserves a new minor
893ad20fe3SChristian Brauner  * number for it.
903ad20fe3SChristian Brauner  * Minor numbers are limited and tracked globally in binderfs_minors. The
913ad20fe3SChristian Brauner  * function will stash a struct binder_device for the specific binder
923ad20fe3SChristian Brauner  * device in i_private of the inode.
933ad20fe3SChristian Brauner  * It will go on to allocate a new inode from the super block of the
943ad20fe3SChristian Brauner  * filesystem mount, stash a struct binder_device in its i_private field
953ad20fe3SChristian Brauner  * and attach a dentry to that inode.
963ad20fe3SChristian Brauner  *
973ad20fe3SChristian Brauner  * Return: 0 on success, negative errno on failure
983ad20fe3SChristian Brauner  */
993ad20fe3SChristian Brauner static int binderfs_binder_device_create(struct inode *ref_inode,
1003ad20fe3SChristian Brauner 					 struct binderfs_device __user *userp,
1013ad20fe3SChristian Brauner 					 struct binderfs_device *req)
1023ad20fe3SChristian Brauner {
1033ad20fe3SChristian Brauner 	int minor, ret;
10401b3f1fcSChristian Brauner 	struct dentry *dentry, *root;
1053ad20fe3SChristian Brauner 	struct binder_device *device;
1063ad20fe3SChristian Brauner 	char *name = NULL;
10701b3f1fcSChristian Brauner 	size_t name_len;
1083ad20fe3SChristian Brauner 	struct inode *inode = NULL;
1093ad20fe3SChristian Brauner 	struct super_block *sb = ref_inode->i_sb;
1103ad20fe3SChristian Brauner 	struct binderfs_info *info = sb->s_fs_info;
1117fefaaddSChristian Brauner #if defined(CONFIG_IPC_NS)
11236bdf3caSChristian Brauner 	bool use_reserve = (info->ipc_ns == &init_ipc_ns);
1137fefaaddSChristian Brauner #else
1147fefaaddSChristian Brauner 	bool use_reserve = true;
1157fefaaddSChristian Brauner #endif
1163ad20fe3SChristian Brauner 
1173ad20fe3SChristian Brauner 	/* Reserve new minor number for the new device. */
1183ad20fe3SChristian Brauner 	mutex_lock(&binderfs_minors_mutex);
119849d540dSChristian Brauner 	if (++info->device_count <= info->mount_opts.max)
12036bdf3caSChristian Brauner 		minor = ida_alloc_max(&binderfs_minors,
12136bdf3caSChristian Brauner 				      use_reserve ? BINDERFS_MAX_MINOR :
12236bdf3caSChristian Brauner 						    BINDERFS_MAX_MINOR_CAPPED,
123849d540dSChristian Brauner 				      GFP_KERNEL);
124849d540dSChristian Brauner 	else
125849d540dSChristian Brauner 		minor = -ENOSPC;
126849d540dSChristian Brauner 	if (minor < 0) {
127849d540dSChristian Brauner 		--info->device_count;
1283ad20fe3SChristian Brauner 		mutex_unlock(&binderfs_minors_mutex);
1293ad20fe3SChristian Brauner 		return minor;
130849d540dSChristian Brauner 	}
131849d540dSChristian Brauner 	mutex_unlock(&binderfs_minors_mutex);
1323ad20fe3SChristian Brauner 
1333ad20fe3SChristian Brauner 	ret = -ENOMEM;
1343ad20fe3SChristian Brauner 	device = kzalloc(sizeof(*device), GFP_KERNEL);
1353ad20fe3SChristian Brauner 	if (!device)
1363ad20fe3SChristian Brauner 		goto err;
1373ad20fe3SChristian Brauner 
1383ad20fe3SChristian Brauner 	inode = new_inode(sb);
1393ad20fe3SChristian Brauner 	if (!inode)
1403ad20fe3SChristian Brauner 		goto err;
1413ad20fe3SChristian Brauner 
1423ad20fe3SChristian Brauner 	inode->i_ino = minor + INODE_OFFSET;
1433ad20fe3SChristian Brauner 	inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode);
1443ad20fe3SChristian Brauner 	init_special_inode(inode, S_IFCHR | 0600,
1453ad20fe3SChristian Brauner 			   MKDEV(MAJOR(binderfs_dev), minor));
1463ad20fe3SChristian Brauner 	inode->i_fop = &binder_fops;
1473ad20fe3SChristian Brauner 	inode->i_uid = info->root_uid;
1483ad20fe3SChristian Brauner 	inode->i_gid = info->root_gid;
1493ad20fe3SChristian Brauner 
15001b3f1fcSChristian Brauner 	req->name[BINDERFS_MAX_NAME] = '\0'; /* NUL-terminate */
15101b3f1fcSChristian Brauner 	name_len = strlen(req->name);
15201b3f1fcSChristian Brauner 	/* Make sure to include terminating NUL byte */
15301b3f1fcSChristian Brauner 	name = kmemdup(req->name, name_len + 1, GFP_KERNEL);
1543ad20fe3SChristian Brauner 	if (!name)
1553ad20fe3SChristian Brauner 		goto err;
1563ad20fe3SChristian Brauner 
157f0fe2c0fSChristian Brauner 	refcount_set(&device->ref, 1);
1583ad20fe3SChristian Brauner 	device->binderfs_inode = inode;
1593ad20fe3SChristian Brauner 	device->context.binder_context_mgr_uid = INVALID_UID;
1603ad20fe3SChristian Brauner 	device->context.name = name;
1613ad20fe3SChristian Brauner 	device->miscdev.name = name;
1623ad20fe3SChristian Brauner 	device->miscdev.minor = minor;
1633ad20fe3SChristian Brauner 	mutex_init(&device->context.context_mgr_node_lock);
1643ad20fe3SChristian Brauner 
1653ad20fe3SChristian Brauner 	req->major = MAJOR(binderfs_dev);
1663ad20fe3SChristian Brauner 	req->minor = minor;
1673ad20fe3SChristian Brauner 
168ca2864c6SHridya Valsaraju 	if (userp && copy_to_user(userp, req, sizeof(*req))) {
1693ad20fe3SChristian Brauner 		ret = -EFAULT;
1703ad20fe3SChristian Brauner 		goto err;
1713ad20fe3SChristian Brauner 	}
1723ad20fe3SChristian Brauner 
1733ad20fe3SChristian Brauner 	root = sb->s_root;
1743ad20fe3SChristian Brauner 	inode_lock(d_inode(root));
17501b3f1fcSChristian Brauner 
17601b3f1fcSChristian Brauner 	/* look it up */
17701b3f1fcSChristian Brauner 	dentry = lookup_one_len(name, root, name_len);
17801b3f1fcSChristian Brauner 	if (IS_ERR(dentry)) {
1793ad20fe3SChristian Brauner 		inode_unlock(d_inode(root));
18001b3f1fcSChristian Brauner 		ret = PTR_ERR(dentry);
1813ad20fe3SChristian Brauner 		goto err;
1823ad20fe3SChristian Brauner 	}
1833ad20fe3SChristian Brauner 
18401b3f1fcSChristian Brauner 	if (d_really_is_positive(dentry)) {
18501b3f1fcSChristian Brauner 		/* already exists */
1863ad20fe3SChristian Brauner 		dput(dentry);
1873ad20fe3SChristian Brauner 		inode_unlock(d_inode(root));
1883ad20fe3SChristian Brauner 		ret = -EEXIST;
1893ad20fe3SChristian Brauner 		goto err;
1903ad20fe3SChristian Brauner 	}
1913ad20fe3SChristian Brauner 
1923ad20fe3SChristian Brauner 	inode->i_private = device;
19301684db9SChristian Brauner 	d_instantiate(dentry, inode);
1943ad20fe3SChristian Brauner 	fsnotify_create(root->d_inode, dentry);
1953ad20fe3SChristian Brauner 	inode_unlock(d_inode(root));
1963ad20fe3SChristian Brauner 
1973ad20fe3SChristian Brauner 	return 0;
1983ad20fe3SChristian Brauner 
1993ad20fe3SChristian Brauner err:
2003ad20fe3SChristian Brauner 	kfree(name);
2013ad20fe3SChristian Brauner 	kfree(device);
2023ad20fe3SChristian Brauner 	mutex_lock(&binderfs_minors_mutex);
203849d540dSChristian Brauner 	--info->device_count;
2043ad20fe3SChristian Brauner 	ida_free(&binderfs_minors, minor);
2053ad20fe3SChristian Brauner 	mutex_unlock(&binderfs_minors_mutex);
2063ad20fe3SChristian Brauner 	iput(inode);
2073ad20fe3SChristian Brauner 
2083ad20fe3SChristian Brauner 	return ret;
2093ad20fe3SChristian Brauner }
2103ad20fe3SChristian Brauner 
2113ad20fe3SChristian Brauner /**
2123ad20fe3SChristian Brauner  * binderfs_ctl_ioctl - handle binder device node allocation requests
2133ad20fe3SChristian Brauner  *
2143ad20fe3SChristian Brauner  * The request handler for the binder-control device. All requests operate on
2153ad20fe3SChristian Brauner  * the binderfs mount the binder-control device resides in:
2163ad20fe3SChristian Brauner  * - BINDER_CTL_ADD
2173ad20fe3SChristian Brauner  *   Allocate a new binder device.
2183ad20fe3SChristian Brauner  *
2193ad20fe3SChristian Brauner  * Return: 0 on success, negative errno on failure
2203ad20fe3SChristian Brauner  */
2213ad20fe3SChristian Brauner static long binder_ctl_ioctl(struct file *file, unsigned int cmd,
2223ad20fe3SChristian Brauner 			     unsigned long arg)
2233ad20fe3SChristian Brauner {
2243ad20fe3SChristian Brauner 	int ret = -EINVAL;
2253ad20fe3SChristian Brauner 	struct inode *inode = file_inode(file);
2263ad20fe3SChristian Brauner 	struct binderfs_device __user *device = (struct binderfs_device __user *)arg;
2273ad20fe3SChristian Brauner 	struct binderfs_device device_req;
2283ad20fe3SChristian Brauner 
2293ad20fe3SChristian Brauner 	switch (cmd) {
2303ad20fe3SChristian Brauner 	case BINDER_CTL_ADD:
2313ad20fe3SChristian Brauner 		ret = copy_from_user(&device_req, device, sizeof(device_req));
2323ad20fe3SChristian Brauner 		if (ret) {
2333ad20fe3SChristian Brauner 			ret = -EFAULT;
2343ad20fe3SChristian Brauner 			break;
2353ad20fe3SChristian Brauner 		}
2363ad20fe3SChristian Brauner 
2373ad20fe3SChristian Brauner 		ret = binderfs_binder_device_create(inode, device, &device_req);
2383ad20fe3SChristian Brauner 		break;
2393ad20fe3SChristian Brauner 	default:
2403ad20fe3SChristian Brauner 		break;
2413ad20fe3SChristian Brauner 	}
2423ad20fe3SChristian Brauner 
2433ad20fe3SChristian Brauner 	return ret;
2443ad20fe3SChristian Brauner }
2453ad20fe3SChristian Brauner 
2463ad20fe3SChristian Brauner static void binderfs_evict_inode(struct inode *inode)
2473ad20fe3SChristian Brauner {
2483ad20fe3SChristian Brauner 	struct binder_device *device = inode->i_private;
249849d540dSChristian Brauner 	struct binderfs_info *info = BINDERFS_I(inode);
2503ad20fe3SChristian Brauner 
2513ad20fe3SChristian Brauner 	clear_inode(inode);
2523ad20fe3SChristian Brauner 
2530e13e452SHridya Valsaraju 	if (!S_ISCHR(inode->i_mode) || !device)
2543ad20fe3SChristian Brauner 		return;
2553ad20fe3SChristian Brauner 
2563ad20fe3SChristian Brauner 	mutex_lock(&binderfs_minors_mutex);
257849d540dSChristian Brauner 	--info->device_count;
2583ad20fe3SChristian Brauner 	ida_free(&binderfs_minors, device->miscdev.minor);
2593ad20fe3SChristian Brauner 	mutex_unlock(&binderfs_minors_mutex);
2603ad20fe3SChristian Brauner 
261f0fe2c0fSChristian Brauner 	if (refcount_dec_and_test(&device->ref)) {
2623ad20fe3SChristian Brauner 		kfree(device->context.name);
2633ad20fe3SChristian Brauner 		kfree(device);
2643ad20fe3SChristian Brauner 	}
265f0fe2c0fSChristian Brauner }
2663ad20fe3SChristian Brauner 
267849d540dSChristian Brauner /**
268849d540dSChristian Brauner  * binderfs_parse_mount_opts - parse binderfs mount options
269849d540dSChristian Brauner  * @data: options to set (can be NULL in which case defaults are used)
270849d540dSChristian Brauner  */
271849d540dSChristian Brauner static int binderfs_parse_mount_opts(char *data,
272849d540dSChristian Brauner 				     struct binderfs_mount_opts *opts)
273849d540dSChristian Brauner {
274f0083451SHridya Valsaraju 	char *p, *stats;
275849d540dSChristian Brauner 	opts->max = BINDERFS_MAX_MINOR;
276f0083451SHridya Valsaraju 	opts->stats_mode = STATS_NONE;
277849d540dSChristian Brauner 
278849d540dSChristian Brauner 	while ((p = strsep(&data, ",")) != NULL) {
279849d540dSChristian Brauner 		substring_t args[MAX_OPT_ARGS];
280849d540dSChristian Brauner 		int token;
281849d540dSChristian Brauner 		int max_devices;
282849d540dSChristian Brauner 
283849d540dSChristian Brauner 		if (!*p)
284849d540dSChristian Brauner 			continue;
285849d540dSChristian Brauner 
286849d540dSChristian Brauner 		token = match_token(p, tokens, args);
287849d540dSChristian Brauner 		switch (token) {
288849d540dSChristian Brauner 		case Opt_max:
289849d540dSChristian Brauner 			if (match_int(&args[0], &max_devices) ||
290849d540dSChristian Brauner 			    (max_devices < 0 ||
291849d540dSChristian Brauner 			     (max_devices > BINDERFS_MAX_MINOR)))
292849d540dSChristian Brauner 				return -EINVAL;
293849d540dSChristian Brauner 
294849d540dSChristian Brauner 			opts->max = max_devices;
295849d540dSChristian Brauner 			break;
296f0083451SHridya Valsaraju 		case Opt_stats_mode:
297f0083451SHridya Valsaraju 			if (!capable(CAP_SYS_ADMIN))
298f0083451SHridya Valsaraju 				return -EINVAL;
299f0083451SHridya Valsaraju 
300f0083451SHridya Valsaraju 			stats = match_strdup(&args[0]);
301f0083451SHridya Valsaraju 			if (!stats)
302f0083451SHridya Valsaraju 				return -ENOMEM;
303f0083451SHridya Valsaraju 
304f0083451SHridya Valsaraju 			if (strcmp(stats, "global") != 0) {
305f0083451SHridya Valsaraju 				kfree(stats);
306f0083451SHridya Valsaraju 				return -EINVAL;
307f0083451SHridya Valsaraju 			}
308f0083451SHridya Valsaraju 
309f0083451SHridya Valsaraju 			opts->stats_mode = STATS_GLOBAL;
310f0083451SHridya Valsaraju 			kfree(stats);
311f0083451SHridya Valsaraju 			break;
312849d540dSChristian Brauner 		default:
313849d540dSChristian Brauner 			pr_err("Invalid mount options\n");
314849d540dSChristian Brauner 			return -EINVAL;
315849d540dSChristian Brauner 		}
316849d540dSChristian Brauner 	}
317849d540dSChristian Brauner 
318849d540dSChristian Brauner 	return 0;
319849d540dSChristian Brauner }
320849d540dSChristian Brauner 
321849d540dSChristian Brauner static int binderfs_remount(struct super_block *sb, int *flags, char *data)
322849d540dSChristian Brauner {
323f0083451SHridya Valsaraju 	int prev_stats_mode, ret;
324849d540dSChristian Brauner 	struct binderfs_info *info = sb->s_fs_info;
325f0083451SHridya Valsaraju 
326f0083451SHridya Valsaraju 	prev_stats_mode = info->mount_opts.stats_mode;
327f0083451SHridya Valsaraju 	ret = binderfs_parse_mount_opts(data, &info->mount_opts);
328f0083451SHridya Valsaraju 	if (ret)
329f0083451SHridya Valsaraju 		return ret;
330f0083451SHridya Valsaraju 
331f0083451SHridya Valsaraju 	if (prev_stats_mode != info->mount_opts.stats_mode) {
332f0083451SHridya Valsaraju 		pr_err("Binderfs stats mode cannot be changed during a remount\n");
333f0083451SHridya Valsaraju 		info->mount_opts.stats_mode = prev_stats_mode;
334f0083451SHridya Valsaraju 		return -EINVAL;
335f0083451SHridya Valsaraju 	}
336f0083451SHridya Valsaraju 
337f0083451SHridya Valsaraju 	return 0;
338849d540dSChristian Brauner }
339849d540dSChristian Brauner 
340849d540dSChristian Brauner static int binderfs_show_mount_opts(struct seq_file *seq, struct dentry *root)
341849d540dSChristian Brauner {
342849d540dSChristian Brauner 	struct binderfs_info *info;
343849d540dSChristian Brauner 
344849d540dSChristian Brauner 	info = root->d_sb->s_fs_info;
345849d540dSChristian Brauner 	if (info->mount_opts.max <= BINDERFS_MAX_MINOR)
346849d540dSChristian Brauner 		seq_printf(seq, ",max=%d", info->mount_opts.max);
347f0083451SHridya Valsaraju 	if (info->mount_opts.stats_mode == STATS_GLOBAL)
348f0083451SHridya Valsaraju 		seq_printf(seq, ",stats=global");
349849d540dSChristian Brauner 
350849d540dSChristian Brauner 	return 0;
351849d540dSChristian Brauner }
352849d540dSChristian Brauner 
3533ad20fe3SChristian Brauner static const struct super_operations binderfs_super_ops = {
3543ad20fe3SChristian Brauner 	.evict_inode    = binderfs_evict_inode,
355849d540dSChristian Brauner 	.remount_fs	= binderfs_remount,
356849d540dSChristian Brauner 	.show_options	= binderfs_show_mount_opts,
357849d540dSChristian Brauner 	.statfs         = simple_statfs,
3583ad20fe3SChristian Brauner };
3593ad20fe3SChristian Brauner 
360e98e6fa1SChristian Brauner static inline bool is_binderfs_control_device(const struct dentry *dentry)
361e98e6fa1SChristian Brauner {
362e98e6fa1SChristian Brauner 	struct binderfs_info *info = dentry->d_sb->s_fs_info;
363e98e6fa1SChristian Brauner 	return info->control_dentry == dentry;
364e98e6fa1SChristian Brauner }
365e98e6fa1SChristian Brauner 
3663ad20fe3SChristian Brauner static int binderfs_rename(struct inode *old_dir, struct dentry *old_dentry,
3673ad20fe3SChristian Brauner 			   struct inode *new_dir, struct dentry *new_dentry,
3683ad20fe3SChristian Brauner 			   unsigned int flags)
3693ad20fe3SChristian Brauner {
370e98e6fa1SChristian Brauner 	if (is_binderfs_control_device(old_dentry) ||
371e98e6fa1SChristian Brauner 	    is_binderfs_control_device(new_dentry))
3723ad20fe3SChristian Brauner 		return -EPERM;
3733ad20fe3SChristian Brauner 
374e98e6fa1SChristian Brauner 	return simple_rename(old_dir, old_dentry, new_dir, new_dentry, flags);
3753ad20fe3SChristian Brauner }
3763ad20fe3SChristian Brauner 
3773ad20fe3SChristian Brauner static int binderfs_unlink(struct inode *dir, struct dentry *dentry)
3783ad20fe3SChristian Brauner {
379e98e6fa1SChristian Brauner 	if (is_binderfs_control_device(dentry))
3803ad20fe3SChristian Brauner 		return -EPERM;
3813ad20fe3SChristian Brauner 
3823ad20fe3SChristian Brauner 	return simple_unlink(dir, dentry);
3833ad20fe3SChristian Brauner }
3843ad20fe3SChristian Brauner 
3853ad20fe3SChristian Brauner static const struct file_operations binder_ctl_fops = {
3863ad20fe3SChristian Brauner 	.owner		= THIS_MODULE,
3873ad20fe3SChristian Brauner 	.open		= nonseekable_open,
3883ad20fe3SChristian Brauner 	.unlocked_ioctl	= binder_ctl_ioctl,
3893ad20fe3SChristian Brauner 	.compat_ioctl	= binder_ctl_ioctl,
3903ad20fe3SChristian Brauner 	.llseek		= noop_llseek,
3913ad20fe3SChristian Brauner };
3923ad20fe3SChristian Brauner 
3933ad20fe3SChristian Brauner /**
3943ad20fe3SChristian Brauner  * binderfs_binder_ctl_create - create a new binder-control device
3953ad20fe3SChristian Brauner  * @sb: super block of the binderfs mount
3963ad20fe3SChristian Brauner  *
3973ad20fe3SChristian Brauner  * This function creates a new binder-control device node in the binderfs mount
3983ad20fe3SChristian Brauner  * referred to by @sb.
3993ad20fe3SChristian Brauner  *
4003ad20fe3SChristian Brauner  * Return: 0 on success, negative errno on failure
4013ad20fe3SChristian Brauner  */
4023ad20fe3SChristian Brauner static int binderfs_binder_ctl_create(struct super_block *sb)
4033ad20fe3SChristian Brauner {
4043ad20fe3SChristian Brauner 	int minor, ret;
4053ad20fe3SChristian Brauner 	struct dentry *dentry;
4063ad20fe3SChristian Brauner 	struct binder_device *device;
4073ad20fe3SChristian Brauner 	struct inode *inode = NULL;
4083ad20fe3SChristian Brauner 	struct dentry *root = sb->s_root;
4093ad20fe3SChristian Brauner 	struct binderfs_info *info = sb->s_fs_info;
410da8ddba5SChristian Brauner #if defined(CONFIG_IPC_NS)
411da8ddba5SChristian Brauner 	bool use_reserve = (info->ipc_ns == &init_ipc_ns);
412da8ddba5SChristian Brauner #else
413da8ddba5SChristian Brauner 	bool use_reserve = true;
414da8ddba5SChristian Brauner #endif
4153ad20fe3SChristian Brauner 
4163ad20fe3SChristian Brauner 	device = kzalloc(sizeof(*device), GFP_KERNEL);
4173ad20fe3SChristian Brauner 	if (!device)
4183ad20fe3SChristian Brauner 		return -ENOMEM;
4193ad20fe3SChristian Brauner 
4203ad20fe3SChristian Brauner 	/* If we have already created a binder-control node, return. */
4213ad20fe3SChristian Brauner 	if (info->control_dentry) {
4223ad20fe3SChristian Brauner 		ret = 0;
4233ad20fe3SChristian Brauner 		goto out;
4243ad20fe3SChristian Brauner 	}
4253ad20fe3SChristian Brauner 
4263ad20fe3SChristian Brauner 	ret = -ENOMEM;
4273ad20fe3SChristian Brauner 	inode = new_inode(sb);
4283ad20fe3SChristian Brauner 	if (!inode)
4293ad20fe3SChristian Brauner 		goto out;
4303ad20fe3SChristian Brauner 
4313ad20fe3SChristian Brauner 	/* Reserve a new minor number for the new device. */
4323ad20fe3SChristian Brauner 	mutex_lock(&binderfs_minors_mutex);
433da8ddba5SChristian Brauner 	minor = ida_alloc_max(&binderfs_minors,
434da8ddba5SChristian Brauner 			      use_reserve ? BINDERFS_MAX_MINOR :
435da8ddba5SChristian Brauner 					    BINDERFS_MAX_MINOR_CAPPED,
436da8ddba5SChristian Brauner 			      GFP_KERNEL);
4373ad20fe3SChristian Brauner 	mutex_unlock(&binderfs_minors_mutex);
4383ad20fe3SChristian Brauner 	if (minor < 0) {
4393ad20fe3SChristian Brauner 		ret = minor;
4403ad20fe3SChristian Brauner 		goto out;
4413ad20fe3SChristian Brauner 	}
4423ad20fe3SChristian Brauner 
4433ad20fe3SChristian Brauner 	inode->i_ino = SECOND_INODE;
4443ad20fe3SChristian Brauner 	inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode);
4453ad20fe3SChristian Brauner 	init_special_inode(inode, S_IFCHR | 0600,
4463ad20fe3SChristian Brauner 			   MKDEV(MAJOR(binderfs_dev), minor));
4473ad20fe3SChristian Brauner 	inode->i_fop = &binder_ctl_fops;
4483ad20fe3SChristian Brauner 	inode->i_uid = info->root_uid;
4493ad20fe3SChristian Brauner 	inode->i_gid = info->root_gid;
4503ad20fe3SChristian Brauner 
451211b64e4SChristian Brauner 	refcount_set(&device->ref, 1);
4523ad20fe3SChristian Brauner 	device->binderfs_inode = inode;
4533ad20fe3SChristian Brauner 	device->miscdev.minor = minor;
4543ad20fe3SChristian Brauner 
4553ad20fe3SChristian Brauner 	dentry = d_alloc_name(root, "binder-control");
4563ad20fe3SChristian Brauner 	if (!dentry)
4573ad20fe3SChristian Brauner 		goto out;
4583ad20fe3SChristian Brauner 
4593ad20fe3SChristian Brauner 	inode->i_private = device;
4603ad20fe3SChristian Brauner 	info->control_dentry = dentry;
4613ad20fe3SChristian Brauner 	d_add(dentry, inode);
4623ad20fe3SChristian Brauner 
4633ad20fe3SChristian Brauner 	return 0;
4643ad20fe3SChristian Brauner 
4653ad20fe3SChristian Brauner out:
4663ad20fe3SChristian Brauner 	kfree(device);
4673ad20fe3SChristian Brauner 	iput(inode);
4683ad20fe3SChristian Brauner 
4693ad20fe3SChristian Brauner 	return ret;
4703ad20fe3SChristian Brauner }
4713ad20fe3SChristian Brauner 
4723ad20fe3SChristian Brauner static const struct inode_operations binderfs_dir_inode_operations = {
4733ad20fe3SChristian Brauner 	.lookup = simple_lookup,
4743ad20fe3SChristian Brauner 	.rename = binderfs_rename,
4753ad20fe3SChristian Brauner 	.unlink = binderfs_unlink,
4763ad20fe3SChristian Brauner };
4773ad20fe3SChristian Brauner 
4780e13e452SHridya Valsaraju static struct inode *binderfs_make_inode(struct super_block *sb, int mode)
4790e13e452SHridya Valsaraju {
4800e13e452SHridya Valsaraju 	struct inode *ret;
4810e13e452SHridya Valsaraju 
4820e13e452SHridya Valsaraju 	ret = new_inode(sb);
4830e13e452SHridya Valsaraju 	if (ret) {
4840e13e452SHridya Valsaraju 		ret->i_ino = iunique(sb, BINDERFS_MAX_MINOR + INODE_OFFSET);
4850e13e452SHridya Valsaraju 		ret->i_mode = mode;
4860e13e452SHridya Valsaraju 		ret->i_atime = ret->i_mtime = ret->i_ctime = current_time(ret);
4870e13e452SHridya Valsaraju 	}
4880e13e452SHridya Valsaraju 	return ret;
4890e13e452SHridya Valsaraju }
4900e13e452SHridya Valsaraju 
4910e13e452SHridya Valsaraju static struct dentry *binderfs_create_dentry(struct dentry *parent,
4920e13e452SHridya Valsaraju 					     const char *name)
4930e13e452SHridya Valsaraju {
4940e13e452SHridya Valsaraju 	struct dentry *dentry;
4950e13e452SHridya Valsaraju 
4960e13e452SHridya Valsaraju 	dentry = lookup_one_len(name, parent, strlen(name));
4970e13e452SHridya Valsaraju 	if (IS_ERR(dentry))
4980e13e452SHridya Valsaraju 		return dentry;
4990e13e452SHridya Valsaraju 
5000e13e452SHridya Valsaraju 	/* Return error if the file/dir already exists. */
5010e13e452SHridya Valsaraju 	if (d_really_is_positive(dentry)) {
5020e13e452SHridya Valsaraju 		dput(dentry);
5030e13e452SHridya Valsaraju 		return ERR_PTR(-EEXIST);
5040e13e452SHridya Valsaraju 	}
5050e13e452SHridya Valsaraju 
5060e13e452SHridya Valsaraju 	return dentry;
5070e13e452SHridya Valsaraju }
5080e13e452SHridya Valsaraju 
5094feb80faSHridya Valsaraju void binderfs_remove_file(struct dentry *dentry)
5104feb80faSHridya Valsaraju {
5114feb80faSHridya Valsaraju 	struct inode *parent_inode;
5124feb80faSHridya Valsaraju 
5134feb80faSHridya Valsaraju 	parent_inode = d_inode(dentry->d_parent);
5144feb80faSHridya Valsaraju 	inode_lock(parent_inode);
5154feb80faSHridya Valsaraju 	if (simple_positive(dentry)) {
5164feb80faSHridya Valsaraju 		dget(dentry);
5174feb80faSHridya Valsaraju 		simple_unlink(parent_inode, dentry);
5184feb80faSHridya Valsaraju 		d_delete(dentry);
5194feb80faSHridya Valsaraju 		dput(dentry);
5204feb80faSHridya Valsaraju 	}
5214feb80faSHridya Valsaraju 	inode_unlock(parent_inode);
5224feb80faSHridya Valsaraju }
5234feb80faSHridya Valsaraju 
5244feb80faSHridya Valsaraju struct dentry *binderfs_create_file(struct dentry *parent, const char *name,
5250e13e452SHridya Valsaraju 				    const struct file_operations *fops,
5260e13e452SHridya Valsaraju 				    void *data)
5270e13e452SHridya Valsaraju {
5280e13e452SHridya Valsaraju 	struct dentry *dentry;
5290e13e452SHridya Valsaraju 	struct inode *new_inode, *parent_inode;
5300e13e452SHridya Valsaraju 	struct super_block *sb;
5310e13e452SHridya Valsaraju 
5320e13e452SHridya Valsaraju 	parent_inode = d_inode(parent);
5330e13e452SHridya Valsaraju 	inode_lock(parent_inode);
5340e13e452SHridya Valsaraju 
5350e13e452SHridya Valsaraju 	dentry = binderfs_create_dentry(parent, name);
5360e13e452SHridya Valsaraju 	if (IS_ERR(dentry))
5370e13e452SHridya Valsaraju 		goto out;
5380e13e452SHridya Valsaraju 
5390e13e452SHridya Valsaraju 	sb = parent_inode->i_sb;
5400e13e452SHridya Valsaraju 	new_inode = binderfs_make_inode(sb, S_IFREG | 0444);
5410e13e452SHridya Valsaraju 	if (!new_inode) {
5420e13e452SHridya Valsaraju 		dput(dentry);
5430e13e452SHridya Valsaraju 		dentry = ERR_PTR(-ENOMEM);
5440e13e452SHridya Valsaraju 		goto out;
5450e13e452SHridya Valsaraju 	}
5460e13e452SHridya Valsaraju 
5470e13e452SHridya Valsaraju 	new_inode->i_fop = fops;
5480e13e452SHridya Valsaraju 	new_inode->i_private = data;
5490e13e452SHridya Valsaraju 	d_instantiate(dentry, new_inode);
5500e13e452SHridya Valsaraju 	fsnotify_create(parent_inode, dentry);
5510e13e452SHridya Valsaraju 
5520e13e452SHridya Valsaraju out:
5530e13e452SHridya Valsaraju 	inode_unlock(parent_inode);
5540e13e452SHridya Valsaraju 	return dentry;
5550e13e452SHridya Valsaraju }
5560e13e452SHridya Valsaraju 
5570e13e452SHridya Valsaraju static struct dentry *binderfs_create_dir(struct dentry *parent,
5580e13e452SHridya Valsaraju 					  const char *name)
5590e13e452SHridya Valsaraju {
5600e13e452SHridya Valsaraju 	struct dentry *dentry;
5610e13e452SHridya Valsaraju 	struct inode *new_inode, *parent_inode;
5620e13e452SHridya Valsaraju 	struct super_block *sb;
5630e13e452SHridya Valsaraju 
5640e13e452SHridya Valsaraju 	parent_inode = d_inode(parent);
5650e13e452SHridya Valsaraju 	inode_lock(parent_inode);
5660e13e452SHridya Valsaraju 
5670e13e452SHridya Valsaraju 	dentry = binderfs_create_dentry(parent, name);
5680e13e452SHridya Valsaraju 	if (IS_ERR(dentry))
5690e13e452SHridya Valsaraju 		goto out;
5700e13e452SHridya Valsaraju 
5710e13e452SHridya Valsaraju 	sb = parent_inode->i_sb;
5720e13e452SHridya Valsaraju 	new_inode = binderfs_make_inode(sb, S_IFDIR | 0755);
5730e13e452SHridya Valsaraju 	if (!new_inode) {
5740e13e452SHridya Valsaraju 		dput(dentry);
5750e13e452SHridya Valsaraju 		dentry = ERR_PTR(-ENOMEM);
5760e13e452SHridya Valsaraju 		goto out;
5770e13e452SHridya Valsaraju 	}
5780e13e452SHridya Valsaraju 
5790e13e452SHridya Valsaraju 	new_inode->i_fop = &simple_dir_operations;
5800e13e452SHridya Valsaraju 	new_inode->i_op = &simple_dir_inode_operations;
5810e13e452SHridya Valsaraju 
5820e13e452SHridya Valsaraju 	set_nlink(new_inode, 2);
5830e13e452SHridya Valsaraju 	d_instantiate(dentry, new_inode);
5840e13e452SHridya Valsaraju 	inc_nlink(parent_inode);
5850e13e452SHridya Valsaraju 	fsnotify_mkdir(parent_inode, dentry);
5860e13e452SHridya Valsaraju 
5870e13e452SHridya Valsaraju out:
5880e13e452SHridya Valsaraju 	inode_unlock(parent_inode);
5890e13e452SHridya Valsaraju 	return dentry;
5900e13e452SHridya Valsaraju }
5910e13e452SHridya Valsaraju 
5920e13e452SHridya Valsaraju static int init_binder_logs(struct super_block *sb)
5930e13e452SHridya Valsaraju {
5944feb80faSHridya Valsaraju 	struct dentry *binder_logs_root_dir, *dentry, *proc_log_dir;
5954feb80faSHridya Valsaraju 	struct binderfs_info *info;
5960e13e452SHridya Valsaraju 	int ret = 0;
5970e13e452SHridya Valsaraju 
5980e13e452SHridya Valsaraju 	binder_logs_root_dir = binderfs_create_dir(sb->s_root,
5990e13e452SHridya Valsaraju 						   "binder_logs");
6000e13e452SHridya Valsaraju 	if (IS_ERR(binder_logs_root_dir)) {
6010e13e452SHridya Valsaraju 		ret = PTR_ERR(binder_logs_root_dir);
6020e13e452SHridya Valsaraju 		goto out;
6030e13e452SHridya Valsaraju 	}
6040e13e452SHridya Valsaraju 
6050e13e452SHridya Valsaraju 	dentry = binderfs_create_file(binder_logs_root_dir, "stats",
6060e13e452SHridya Valsaraju 				      &binder_stats_fops, NULL);
6070e13e452SHridya Valsaraju 	if (IS_ERR(dentry)) {
6080e13e452SHridya Valsaraju 		ret = PTR_ERR(dentry);
6090e13e452SHridya Valsaraju 		goto out;
6100e13e452SHridya Valsaraju 	}
6110e13e452SHridya Valsaraju 
6120e13e452SHridya Valsaraju 	dentry = binderfs_create_file(binder_logs_root_dir, "state",
6130e13e452SHridya Valsaraju 				      &binder_state_fops, NULL);
6140e13e452SHridya Valsaraju 	if (IS_ERR(dentry)) {
6150e13e452SHridya Valsaraju 		ret = PTR_ERR(dentry);
6160e13e452SHridya Valsaraju 		goto out;
6170e13e452SHridya Valsaraju 	}
6180e13e452SHridya Valsaraju 
6190e13e452SHridya Valsaraju 	dentry = binderfs_create_file(binder_logs_root_dir, "transactions",
6200e13e452SHridya Valsaraju 				      &binder_transactions_fops, NULL);
62103e2e07eSHridya Valsaraju 	if (IS_ERR(dentry)) {
62203e2e07eSHridya Valsaraju 		ret = PTR_ERR(dentry);
62303e2e07eSHridya Valsaraju 		goto out;
62403e2e07eSHridya Valsaraju 	}
62503e2e07eSHridya Valsaraju 
62603e2e07eSHridya Valsaraju 	dentry = binderfs_create_file(binder_logs_root_dir,
62703e2e07eSHridya Valsaraju 				      "transaction_log",
62803e2e07eSHridya Valsaraju 				      &binder_transaction_log_fops,
62903e2e07eSHridya Valsaraju 				      &binder_transaction_log);
63003e2e07eSHridya Valsaraju 	if (IS_ERR(dentry)) {
63103e2e07eSHridya Valsaraju 		ret = PTR_ERR(dentry);
63203e2e07eSHridya Valsaraju 		goto out;
63303e2e07eSHridya Valsaraju 	}
63403e2e07eSHridya Valsaraju 
63503e2e07eSHridya Valsaraju 	dentry = binderfs_create_file(binder_logs_root_dir,
63603e2e07eSHridya Valsaraju 				      "failed_transaction_log",
63703e2e07eSHridya Valsaraju 				      &binder_transaction_log_fops,
63803e2e07eSHridya Valsaraju 				      &binder_transaction_log_failed);
6394feb80faSHridya Valsaraju 	if (IS_ERR(dentry)) {
6400e13e452SHridya Valsaraju 		ret = PTR_ERR(dentry);
6414feb80faSHridya Valsaraju 		goto out;
6424feb80faSHridya Valsaraju 	}
6434feb80faSHridya Valsaraju 
6444feb80faSHridya Valsaraju 	proc_log_dir = binderfs_create_dir(binder_logs_root_dir, "proc");
6454feb80faSHridya Valsaraju 	if (IS_ERR(proc_log_dir)) {
6464feb80faSHridya Valsaraju 		ret = PTR_ERR(proc_log_dir);
6474feb80faSHridya Valsaraju 		goto out;
6484feb80faSHridya Valsaraju 	}
6494feb80faSHridya Valsaraju 	info = sb->s_fs_info;
6504feb80faSHridya Valsaraju 	info->proc_log_dir = proc_log_dir;
6510e13e452SHridya Valsaraju 
6520e13e452SHridya Valsaraju out:
6530e13e452SHridya Valsaraju 	return ret;
6540e13e452SHridya Valsaraju }
6550e13e452SHridya Valsaraju 
6563ad20fe3SChristian Brauner static int binderfs_fill_super(struct super_block *sb, void *data, int silent)
6573ad20fe3SChristian Brauner {
65836975fc3SChristian Brauner 	int ret;
6593ad20fe3SChristian Brauner 	struct binderfs_info *info;
6603ad20fe3SChristian Brauner 	struct inode *inode = NULL;
661ca2864c6SHridya Valsaraju 	struct binderfs_device device_info = { 0 };
662ca2864c6SHridya Valsaraju 	const char *name;
663ca2864c6SHridya Valsaraju 	size_t len;
6643ad20fe3SChristian Brauner 
6653ad20fe3SChristian Brauner 	sb->s_blocksize = PAGE_SIZE;
6663ad20fe3SChristian Brauner 	sb->s_blocksize_bits = PAGE_SHIFT;
6673ad20fe3SChristian Brauner 
6683ad20fe3SChristian Brauner 	/*
6693ad20fe3SChristian Brauner 	 * The binderfs filesystem can be mounted by userns root in a
6703ad20fe3SChristian Brauner 	 * non-initial userns. By default such mounts have the SB_I_NODEV flag
6713ad20fe3SChristian Brauner 	 * set in s_iflags to prevent security issues where userns root can
6723ad20fe3SChristian Brauner 	 * just create random device nodes via mknod() since it owns the
6733ad20fe3SChristian Brauner 	 * filesystem mount. But binderfs does not allow to create any files
6743ad20fe3SChristian Brauner 	 * including devices nodes. The only way to create binder devices nodes
6753ad20fe3SChristian Brauner 	 * is through the binder-control device which userns root is explicitly
6763ad20fe3SChristian Brauner 	 * allowed to do. So removing the SB_I_NODEV flag from s_iflags is both
6773ad20fe3SChristian Brauner 	 * necessary and safe.
6783ad20fe3SChristian Brauner 	 */
6793ad20fe3SChristian Brauner 	sb->s_iflags &= ~SB_I_NODEV;
6803ad20fe3SChristian Brauner 	sb->s_iflags |= SB_I_NOEXEC;
6813ad20fe3SChristian Brauner 	sb->s_magic = BINDERFS_SUPER_MAGIC;
6823ad20fe3SChristian Brauner 	sb->s_op = &binderfs_super_ops;
6833ad20fe3SChristian Brauner 	sb->s_time_gran = 1;
6843ad20fe3SChristian Brauner 
68536975fc3SChristian Brauner 	sb->s_fs_info = kzalloc(sizeof(struct binderfs_info), GFP_KERNEL);
68636975fc3SChristian Brauner 	if (!sb->s_fs_info)
68736975fc3SChristian Brauner 		return -ENOMEM;
68836975fc3SChristian Brauner 	info = sb->s_fs_info;
68936975fc3SChristian Brauner 
69036975fc3SChristian Brauner 	info->ipc_ns = get_ipc_ns(current->nsproxy->ipc_ns);
6913ad20fe3SChristian Brauner 
692849d540dSChristian Brauner 	ret = binderfs_parse_mount_opts(data, &info->mount_opts);
693849d540dSChristian Brauner 	if (ret)
69436975fc3SChristian Brauner 		return ret;
695849d540dSChristian Brauner 
6963ad20fe3SChristian Brauner 	info->root_gid = make_kgid(sb->s_user_ns, 0);
6973ad20fe3SChristian Brauner 	if (!gid_valid(info->root_gid))
6983ad20fe3SChristian Brauner 		info->root_gid = GLOBAL_ROOT_GID;
6993ad20fe3SChristian Brauner 	info->root_uid = make_kuid(sb->s_user_ns, 0);
7003ad20fe3SChristian Brauner 	if (!uid_valid(info->root_uid))
7013ad20fe3SChristian Brauner 		info->root_uid = GLOBAL_ROOT_UID;
7023ad20fe3SChristian Brauner 
7033ad20fe3SChristian Brauner 	inode = new_inode(sb);
7043ad20fe3SChristian Brauner 	if (!inode)
70536975fc3SChristian Brauner 		return -ENOMEM;
7063ad20fe3SChristian Brauner 
7073ad20fe3SChristian Brauner 	inode->i_ino = FIRST_INODE;
7083ad20fe3SChristian Brauner 	inode->i_fop = &simple_dir_operations;
7093ad20fe3SChristian Brauner 	inode->i_mode = S_IFDIR | 0755;
7103ad20fe3SChristian Brauner 	inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode);
7113ad20fe3SChristian Brauner 	inode->i_op = &binderfs_dir_inode_operations;
7123ad20fe3SChristian Brauner 	set_nlink(inode, 2);
7133ad20fe3SChristian Brauner 
7143ad20fe3SChristian Brauner 	sb->s_root = d_make_root(inode);
7153ad20fe3SChristian Brauner 	if (!sb->s_root)
71636975fc3SChristian Brauner 		return -ENOMEM;
7173ad20fe3SChristian Brauner 
718ca2864c6SHridya Valsaraju 	ret = binderfs_binder_ctl_create(sb);
719ca2864c6SHridya Valsaraju 	if (ret)
720ca2864c6SHridya Valsaraju 		return ret;
721ca2864c6SHridya Valsaraju 
722ca2864c6SHridya Valsaraju 	name = binder_devices_param;
723ca2864c6SHridya Valsaraju 	for (len = strcspn(name, ","); len > 0; len = strcspn(name, ",")) {
724ca2864c6SHridya Valsaraju 		strscpy(device_info.name, name, len + 1);
725ca2864c6SHridya Valsaraju 		ret = binderfs_binder_device_create(inode, NULL, &device_info);
726ca2864c6SHridya Valsaraju 		if (ret)
727ca2864c6SHridya Valsaraju 			return ret;
728ca2864c6SHridya Valsaraju 		name += len;
729ca2864c6SHridya Valsaraju 		if (*name == ',')
730ca2864c6SHridya Valsaraju 			name++;
731ca2864c6SHridya Valsaraju 	}
732ca2864c6SHridya Valsaraju 
7330e13e452SHridya Valsaraju 	if (info->mount_opts.stats_mode == STATS_GLOBAL)
7340e13e452SHridya Valsaraju 		return init_binder_logs(sb);
7350e13e452SHridya Valsaraju 
736ca2864c6SHridya Valsaraju 	return 0;
7373ad20fe3SChristian Brauner }
7383ad20fe3SChristian Brauner 
7393ad20fe3SChristian Brauner static struct dentry *binderfs_mount(struct file_system_type *fs_type,
7403ad20fe3SChristian Brauner 				     int flags, const char *dev_name,
7413ad20fe3SChristian Brauner 				     void *data)
7423ad20fe3SChristian Brauner {
743b6c770d7SChristian Brauner 	return mount_nodev(fs_type, flags, data, binderfs_fill_super);
7443ad20fe3SChristian Brauner }
7453ad20fe3SChristian Brauner 
7463ad20fe3SChristian Brauner static void binderfs_kill_super(struct super_block *sb)
7473ad20fe3SChristian Brauner {
7483ad20fe3SChristian Brauner 	struct binderfs_info *info = sb->s_fs_info;
7493ad20fe3SChristian Brauner 
75041984795SChristian Brauner 	kill_litter_super(sb);
75141984795SChristian Brauner 
7523ad20fe3SChristian Brauner 	if (info && info->ipc_ns)
7533ad20fe3SChristian Brauner 		put_ipc_ns(info->ipc_ns);
7543ad20fe3SChristian Brauner 
7553ad20fe3SChristian Brauner 	kfree(info);
7563ad20fe3SChristian Brauner }
7573ad20fe3SChristian Brauner 
7583ad20fe3SChristian Brauner static struct file_system_type binder_fs_type = {
7593ad20fe3SChristian Brauner 	.name		= "binder",
7603ad20fe3SChristian Brauner 	.mount		= binderfs_mount,
7613ad20fe3SChristian Brauner 	.kill_sb	= binderfs_kill_super,
7623ad20fe3SChristian Brauner 	.fs_flags	= FS_USERNS_MOUNT,
7633ad20fe3SChristian Brauner };
7643ad20fe3SChristian Brauner 
7655b9633afSChristian Brauner int __init init_binderfs(void)
7663ad20fe3SChristian Brauner {
7673ad20fe3SChristian Brauner 	int ret;
768028fb582SHridya Valsaraju 	const char *name;
769028fb582SHridya Valsaraju 	size_t len;
770028fb582SHridya Valsaraju 
771028fb582SHridya Valsaraju 	/* Verify that the default binderfs device names are valid. */
772028fb582SHridya Valsaraju 	name = binder_devices_param;
773028fb582SHridya Valsaraju 	for (len = strcspn(name, ","); len > 0; len = strcspn(name, ",")) {
774028fb582SHridya Valsaraju 		if (len > BINDERFS_MAX_NAME)
775028fb582SHridya Valsaraju 			return -E2BIG;
776028fb582SHridya Valsaraju 		name += len;
777028fb582SHridya Valsaraju 		if (*name == ',')
778028fb582SHridya Valsaraju 			name++;
779028fb582SHridya Valsaraju 	}
7803ad20fe3SChristian Brauner 
7813ad20fe3SChristian Brauner 	/* Allocate new major number for binderfs. */
7823ad20fe3SChristian Brauner 	ret = alloc_chrdev_region(&binderfs_dev, 0, BINDERFS_MAX_MINOR,
7833ad20fe3SChristian Brauner 				  "binder");
7843ad20fe3SChristian Brauner 	if (ret)
7853ad20fe3SChristian Brauner 		return ret;
7863ad20fe3SChristian Brauner 
7873ad20fe3SChristian Brauner 	ret = register_filesystem(&binder_fs_type);
7883ad20fe3SChristian Brauner 	if (ret) {
7893ad20fe3SChristian Brauner 		unregister_chrdev_region(binderfs_dev, BINDERFS_MAX_MINOR);
7903ad20fe3SChristian Brauner 		return ret;
7913ad20fe3SChristian Brauner 	}
7923ad20fe3SChristian Brauner 
7933ad20fe3SChristian Brauner 	return ret;
7943ad20fe3SChristian Brauner }
795