18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci  FUSE: Filesystem in Userspace
38c2ecf20Sopenharmony_ci  Copyright (C) 2001-2008  Miklos Szeredi <miklos@szeredi.hu>
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci  This program can be distributed under the terms of the GNU GPL.
68c2ecf20Sopenharmony_ci  See the file COPYING.
78c2ecf20Sopenharmony_ci*/
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include "fuse_i.h"
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/init.h>
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/fs_context.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#define FUSE_CTL_SUPER_MAGIC 0x65735543
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci/*
188c2ecf20Sopenharmony_ci * This is non-NULL when the single instance of the control filesystem
198c2ecf20Sopenharmony_ci * exists.  Protected by fuse_mutex
208c2ecf20Sopenharmony_ci */
218c2ecf20Sopenharmony_cistatic struct super_block *fuse_control_sb;
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_cistatic struct fuse_conn *fuse_ctl_file_conn_get(struct file *file)
248c2ecf20Sopenharmony_ci{
258c2ecf20Sopenharmony_ci	struct fuse_conn *fc;
268c2ecf20Sopenharmony_ci	mutex_lock(&fuse_mutex);
278c2ecf20Sopenharmony_ci	fc = file_inode(file)->i_private;
288c2ecf20Sopenharmony_ci	if (fc)
298c2ecf20Sopenharmony_ci		fc = fuse_conn_get(fc);
308c2ecf20Sopenharmony_ci	mutex_unlock(&fuse_mutex);
318c2ecf20Sopenharmony_ci	return fc;
328c2ecf20Sopenharmony_ci}
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_cistatic ssize_t fuse_conn_abort_write(struct file *file, const char __user *buf,
358c2ecf20Sopenharmony_ci				     size_t count, loff_t *ppos)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	struct fuse_conn *fc = fuse_ctl_file_conn_get(file);
388c2ecf20Sopenharmony_ci	if (fc) {
398c2ecf20Sopenharmony_ci		if (fc->abort_err)
408c2ecf20Sopenharmony_ci			fc->aborted = true;
418c2ecf20Sopenharmony_ci		fuse_abort_conn(fc);
428c2ecf20Sopenharmony_ci		fuse_conn_put(fc);
438c2ecf20Sopenharmony_ci	}
448c2ecf20Sopenharmony_ci	return count;
458c2ecf20Sopenharmony_ci}
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistatic ssize_t fuse_conn_waiting_read(struct file *file, char __user *buf,
488c2ecf20Sopenharmony_ci				      size_t len, loff_t *ppos)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	char tmp[32];
518c2ecf20Sopenharmony_ci	size_t size;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	if (!*ppos) {
548c2ecf20Sopenharmony_ci		long value;
558c2ecf20Sopenharmony_ci		struct fuse_conn *fc = fuse_ctl_file_conn_get(file);
568c2ecf20Sopenharmony_ci		if (!fc)
578c2ecf20Sopenharmony_ci			return 0;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci		value = atomic_read(&fc->num_waiting);
608c2ecf20Sopenharmony_ci		file->private_data = (void *)value;
618c2ecf20Sopenharmony_ci		fuse_conn_put(fc);
628c2ecf20Sopenharmony_ci	}
638c2ecf20Sopenharmony_ci	size = sprintf(tmp, "%ld\n", (long)file->private_data);
648c2ecf20Sopenharmony_ci	return simple_read_from_buffer(buf, len, ppos, tmp, size);
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic ssize_t fuse_conn_limit_read(struct file *file, char __user *buf,
688c2ecf20Sopenharmony_ci				    size_t len, loff_t *ppos, unsigned val)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	char tmp[32];
718c2ecf20Sopenharmony_ci	size_t size = sprintf(tmp, "%u\n", val);
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	return simple_read_from_buffer(buf, len, ppos, tmp, size);
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic ssize_t fuse_conn_limit_write(struct file *file, const char __user *buf,
778c2ecf20Sopenharmony_ci				     size_t count, loff_t *ppos, unsigned *val,
788c2ecf20Sopenharmony_ci				     unsigned global_limit)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	unsigned long t;
818c2ecf20Sopenharmony_ci	unsigned limit = (1 << 16) - 1;
828c2ecf20Sopenharmony_ci	int err;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	if (*ppos)
858c2ecf20Sopenharmony_ci		return -EINVAL;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	err = kstrtoul_from_user(buf, count, 0, &t);
888c2ecf20Sopenharmony_ci	if (err)
898c2ecf20Sopenharmony_ci		return err;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
928c2ecf20Sopenharmony_ci		limit = min(limit, global_limit);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	if (t > limit)
958c2ecf20Sopenharmony_ci		return -EINVAL;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	*val = t;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	return count;
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cistatic ssize_t fuse_conn_max_background_read(struct file *file,
1038c2ecf20Sopenharmony_ci					     char __user *buf, size_t len,
1048c2ecf20Sopenharmony_ci					     loff_t *ppos)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	struct fuse_conn *fc;
1078c2ecf20Sopenharmony_ci	unsigned val;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	fc = fuse_ctl_file_conn_get(file);
1108c2ecf20Sopenharmony_ci	if (!fc)
1118c2ecf20Sopenharmony_ci		return 0;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	val = READ_ONCE(fc->max_background);
1148c2ecf20Sopenharmony_ci	fuse_conn_put(fc);
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	return fuse_conn_limit_read(file, buf, len, ppos, val);
1178c2ecf20Sopenharmony_ci}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cistatic ssize_t fuse_conn_max_background_write(struct file *file,
1208c2ecf20Sopenharmony_ci					      const char __user *buf,
1218c2ecf20Sopenharmony_ci					      size_t count, loff_t *ppos)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	unsigned val;
1248c2ecf20Sopenharmony_ci	ssize_t ret;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	ret = fuse_conn_limit_write(file, buf, count, ppos, &val,
1278c2ecf20Sopenharmony_ci				    max_user_bgreq);
1288c2ecf20Sopenharmony_ci	if (ret > 0) {
1298c2ecf20Sopenharmony_ci		struct fuse_conn *fc = fuse_ctl_file_conn_get(file);
1308c2ecf20Sopenharmony_ci		if (fc) {
1318c2ecf20Sopenharmony_ci			spin_lock(&fc->bg_lock);
1328c2ecf20Sopenharmony_ci			fc->max_background = val;
1338c2ecf20Sopenharmony_ci			fc->blocked = fc->num_background >= fc->max_background;
1348c2ecf20Sopenharmony_ci			if (!fc->blocked)
1358c2ecf20Sopenharmony_ci				wake_up(&fc->blocked_waitq);
1368c2ecf20Sopenharmony_ci			spin_unlock(&fc->bg_lock);
1378c2ecf20Sopenharmony_ci			fuse_conn_put(fc);
1388c2ecf20Sopenharmony_ci		}
1398c2ecf20Sopenharmony_ci	}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	return ret;
1428c2ecf20Sopenharmony_ci}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_cistatic ssize_t fuse_conn_congestion_threshold_read(struct file *file,
1458c2ecf20Sopenharmony_ci						   char __user *buf, size_t len,
1468c2ecf20Sopenharmony_ci						   loff_t *ppos)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	struct fuse_conn *fc;
1498c2ecf20Sopenharmony_ci	unsigned val;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	fc = fuse_ctl_file_conn_get(file);
1528c2ecf20Sopenharmony_ci	if (!fc)
1538c2ecf20Sopenharmony_ci		return 0;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	val = READ_ONCE(fc->congestion_threshold);
1568c2ecf20Sopenharmony_ci	fuse_conn_put(fc);
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	return fuse_conn_limit_read(file, buf, len, ppos, val);
1598c2ecf20Sopenharmony_ci}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_cistatic ssize_t fuse_conn_congestion_threshold_write(struct file *file,
1628c2ecf20Sopenharmony_ci						    const char __user *buf,
1638c2ecf20Sopenharmony_ci						    size_t count, loff_t *ppos)
1648c2ecf20Sopenharmony_ci{
1658c2ecf20Sopenharmony_ci	unsigned val;
1668c2ecf20Sopenharmony_ci	struct fuse_conn *fc;
1678c2ecf20Sopenharmony_ci	struct fuse_mount *fm;
1688c2ecf20Sopenharmony_ci	ssize_t ret;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	ret = fuse_conn_limit_write(file, buf, count, ppos, &val,
1718c2ecf20Sopenharmony_ci				    max_user_congthresh);
1728c2ecf20Sopenharmony_ci	if (ret <= 0)
1738c2ecf20Sopenharmony_ci		goto out;
1748c2ecf20Sopenharmony_ci	fc = fuse_ctl_file_conn_get(file);
1758c2ecf20Sopenharmony_ci	if (!fc)
1768c2ecf20Sopenharmony_ci		goto out;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	down_read(&fc->killsb);
1798c2ecf20Sopenharmony_ci	spin_lock(&fc->bg_lock);
1808c2ecf20Sopenharmony_ci	fc->congestion_threshold = val;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	/*
1838c2ecf20Sopenharmony_ci	 * Get any fuse_mount belonging to this fuse_conn; s_bdi is
1848c2ecf20Sopenharmony_ci	 * shared between all of them
1858c2ecf20Sopenharmony_ci	 */
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	if (!list_empty(&fc->mounts)) {
1888c2ecf20Sopenharmony_ci		fm = list_first_entry(&fc->mounts, struct fuse_mount, fc_entry);
1898c2ecf20Sopenharmony_ci		if (fc->num_background < fc->congestion_threshold) {
1908c2ecf20Sopenharmony_ci			clear_bdi_congested(fm->sb->s_bdi, BLK_RW_SYNC);
1918c2ecf20Sopenharmony_ci			clear_bdi_congested(fm->sb->s_bdi, BLK_RW_ASYNC);
1928c2ecf20Sopenharmony_ci		} else {
1938c2ecf20Sopenharmony_ci			set_bdi_congested(fm->sb->s_bdi, BLK_RW_SYNC);
1948c2ecf20Sopenharmony_ci			set_bdi_congested(fm->sb->s_bdi, BLK_RW_ASYNC);
1958c2ecf20Sopenharmony_ci		}
1968c2ecf20Sopenharmony_ci	}
1978c2ecf20Sopenharmony_ci	spin_unlock(&fc->bg_lock);
1988c2ecf20Sopenharmony_ci	up_read(&fc->killsb);
1998c2ecf20Sopenharmony_ci	fuse_conn_put(fc);
2008c2ecf20Sopenharmony_ciout:
2018c2ecf20Sopenharmony_ci	return ret;
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_cistatic const struct file_operations fuse_ctl_abort_ops = {
2058c2ecf20Sopenharmony_ci	.open = nonseekable_open,
2068c2ecf20Sopenharmony_ci	.write = fuse_conn_abort_write,
2078c2ecf20Sopenharmony_ci	.llseek = no_llseek,
2088c2ecf20Sopenharmony_ci};
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_cistatic const struct file_operations fuse_ctl_waiting_ops = {
2118c2ecf20Sopenharmony_ci	.open = nonseekable_open,
2128c2ecf20Sopenharmony_ci	.read = fuse_conn_waiting_read,
2138c2ecf20Sopenharmony_ci	.llseek = no_llseek,
2148c2ecf20Sopenharmony_ci};
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_cistatic const struct file_operations fuse_conn_max_background_ops = {
2178c2ecf20Sopenharmony_ci	.open = nonseekable_open,
2188c2ecf20Sopenharmony_ci	.read = fuse_conn_max_background_read,
2198c2ecf20Sopenharmony_ci	.write = fuse_conn_max_background_write,
2208c2ecf20Sopenharmony_ci	.llseek = no_llseek,
2218c2ecf20Sopenharmony_ci};
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_cistatic const struct file_operations fuse_conn_congestion_threshold_ops = {
2248c2ecf20Sopenharmony_ci	.open = nonseekable_open,
2258c2ecf20Sopenharmony_ci	.read = fuse_conn_congestion_threshold_read,
2268c2ecf20Sopenharmony_ci	.write = fuse_conn_congestion_threshold_write,
2278c2ecf20Sopenharmony_ci	.llseek = no_llseek,
2288c2ecf20Sopenharmony_ci};
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_cistatic struct dentry *fuse_ctl_add_dentry(struct dentry *parent,
2318c2ecf20Sopenharmony_ci					  struct fuse_conn *fc,
2328c2ecf20Sopenharmony_ci					  const char *name,
2338c2ecf20Sopenharmony_ci					  int mode, int nlink,
2348c2ecf20Sopenharmony_ci					  const struct inode_operations *iop,
2358c2ecf20Sopenharmony_ci					  const struct file_operations *fop)
2368c2ecf20Sopenharmony_ci{
2378c2ecf20Sopenharmony_ci	struct dentry *dentry;
2388c2ecf20Sopenharmony_ci	struct inode *inode;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	BUG_ON(fc->ctl_ndents >= FUSE_CTL_NUM_DENTRIES);
2418c2ecf20Sopenharmony_ci	dentry = d_alloc_name(parent, name);
2428c2ecf20Sopenharmony_ci	if (!dentry)
2438c2ecf20Sopenharmony_ci		return NULL;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	inode = new_inode(fuse_control_sb);
2468c2ecf20Sopenharmony_ci	if (!inode) {
2478c2ecf20Sopenharmony_ci		dput(dentry);
2488c2ecf20Sopenharmony_ci		return NULL;
2498c2ecf20Sopenharmony_ci	}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	inode->i_ino = get_next_ino();
2528c2ecf20Sopenharmony_ci	inode->i_mode = mode;
2538c2ecf20Sopenharmony_ci	inode->i_uid = fc->user_id;
2548c2ecf20Sopenharmony_ci	inode->i_gid = fc->group_id;
2558c2ecf20Sopenharmony_ci	inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode);
2568c2ecf20Sopenharmony_ci	/* setting ->i_op to NULL is not allowed */
2578c2ecf20Sopenharmony_ci	if (iop)
2588c2ecf20Sopenharmony_ci		inode->i_op = iop;
2598c2ecf20Sopenharmony_ci	inode->i_fop = fop;
2608c2ecf20Sopenharmony_ci	set_nlink(inode, nlink);
2618c2ecf20Sopenharmony_ci	inode->i_private = fc;
2628c2ecf20Sopenharmony_ci	d_add(dentry, inode);
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	fc->ctl_dentry[fc->ctl_ndents++] = dentry;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	return dentry;
2678c2ecf20Sopenharmony_ci}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci/*
2708c2ecf20Sopenharmony_ci * Add a connection to the control filesystem (if it exists).  Caller
2718c2ecf20Sopenharmony_ci * must hold fuse_mutex
2728c2ecf20Sopenharmony_ci */
2738c2ecf20Sopenharmony_ciint fuse_ctl_add_conn(struct fuse_conn *fc)
2748c2ecf20Sopenharmony_ci{
2758c2ecf20Sopenharmony_ci	struct dentry *parent;
2768c2ecf20Sopenharmony_ci	char name[32];
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	if (!fuse_control_sb || fc->no_control)
2798c2ecf20Sopenharmony_ci		return 0;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	parent = fuse_control_sb->s_root;
2828c2ecf20Sopenharmony_ci	inc_nlink(d_inode(parent));
2838c2ecf20Sopenharmony_ci	sprintf(name, "%u", fc->dev);
2848c2ecf20Sopenharmony_ci	parent = fuse_ctl_add_dentry(parent, fc, name, S_IFDIR | 0500, 2,
2858c2ecf20Sopenharmony_ci				     &simple_dir_inode_operations,
2868c2ecf20Sopenharmony_ci				     &simple_dir_operations);
2878c2ecf20Sopenharmony_ci	if (!parent)
2888c2ecf20Sopenharmony_ci		goto err;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	if (!fuse_ctl_add_dentry(parent, fc, "waiting", S_IFREG | 0400, 1,
2918c2ecf20Sopenharmony_ci				 NULL, &fuse_ctl_waiting_ops) ||
2928c2ecf20Sopenharmony_ci	    !fuse_ctl_add_dentry(parent, fc, "abort", S_IFREG | 0200, 1,
2938c2ecf20Sopenharmony_ci				 NULL, &fuse_ctl_abort_ops) ||
2948c2ecf20Sopenharmony_ci	    !fuse_ctl_add_dentry(parent, fc, "max_background", S_IFREG | 0600,
2958c2ecf20Sopenharmony_ci				 1, NULL, &fuse_conn_max_background_ops) ||
2968c2ecf20Sopenharmony_ci	    !fuse_ctl_add_dentry(parent, fc, "congestion_threshold",
2978c2ecf20Sopenharmony_ci				 S_IFREG | 0600, 1, NULL,
2988c2ecf20Sopenharmony_ci				 &fuse_conn_congestion_threshold_ops))
2998c2ecf20Sopenharmony_ci		goto err;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	return 0;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci err:
3048c2ecf20Sopenharmony_ci	fuse_ctl_remove_conn(fc);
3058c2ecf20Sopenharmony_ci	return -ENOMEM;
3068c2ecf20Sopenharmony_ci}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci/*
3098c2ecf20Sopenharmony_ci * Remove a connection from the control filesystem (if it exists).
3108c2ecf20Sopenharmony_ci * Caller must hold fuse_mutex
3118c2ecf20Sopenharmony_ci */
3128c2ecf20Sopenharmony_civoid fuse_ctl_remove_conn(struct fuse_conn *fc)
3138c2ecf20Sopenharmony_ci{
3148c2ecf20Sopenharmony_ci	int i;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	if (!fuse_control_sb || fc->no_control)
3178c2ecf20Sopenharmony_ci		return;
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	for (i = fc->ctl_ndents - 1; i >= 0; i--) {
3208c2ecf20Sopenharmony_ci		struct dentry *dentry = fc->ctl_dentry[i];
3218c2ecf20Sopenharmony_ci		d_inode(dentry)->i_private = NULL;
3228c2ecf20Sopenharmony_ci		if (!i) {
3238c2ecf20Sopenharmony_ci			/* Get rid of submounts: */
3248c2ecf20Sopenharmony_ci			d_invalidate(dentry);
3258c2ecf20Sopenharmony_ci		}
3268c2ecf20Sopenharmony_ci		dput(dentry);
3278c2ecf20Sopenharmony_ci	}
3288c2ecf20Sopenharmony_ci	drop_nlink(d_inode(fuse_control_sb->s_root));
3298c2ecf20Sopenharmony_ci}
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_cistatic int fuse_ctl_fill_super(struct super_block *sb, struct fs_context *fctx)
3328c2ecf20Sopenharmony_ci{
3338c2ecf20Sopenharmony_ci	static const struct tree_descr empty_descr = {""};
3348c2ecf20Sopenharmony_ci	struct fuse_conn *fc;
3358c2ecf20Sopenharmony_ci	int err;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	err = simple_fill_super(sb, FUSE_CTL_SUPER_MAGIC, &empty_descr);
3388c2ecf20Sopenharmony_ci	if (err)
3398c2ecf20Sopenharmony_ci		return err;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	mutex_lock(&fuse_mutex);
3428c2ecf20Sopenharmony_ci	BUG_ON(fuse_control_sb);
3438c2ecf20Sopenharmony_ci	fuse_control_sb = sb;
3448c2ecf20Sopenharmony_ci	list_for_each_entry(fc, &fuse_conn_list, entry) {
3458c2ecf20Sopenharmony_ci		err = fuse_ctl_add_conn(fc);
3468c2ecf20Sopenharmony_ci		if (err) {
3478c2ecf20Sopenharmony_ci			fuse_control_sb = NULL;
3488c2ecf20Sopenharmony_ci			mutex_unlock(&fuse_mutex);
3498c2ecf20Sopenharmony_ci			return err;
3508c2ecf20Sopenharmony_ci		}
3518c2ecf20Sopenharmony_ci	}
3528c2ecf20Sopenharmony_ci	mutex_unlock(&fuse_mutex);
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	return 0;
3558c2ecf20Sopenharmony_ci}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_cistatic int fuse_ctl_get_tree(struct fs_context *fc)
3588c2ecf20Sopenharmony_ci{
3598c2ecf20Sopenharmony_ci	return get_tree_single(fc, fuse_ctl_fill_super);
3608c2ecf20Sopenharmony_ci}
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_cistatic const struct fs_context_operations fuse_ctl_context_ops = {
3638c2ecf20Sopenharmony_ci	.get_tree	= fuse_ctl_get_tree,
3648c2ecf20Sopenharmony_ci};
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_cistatic int fuse_ctl_init_fs_context(struct fs_context *fc)
3678c2ecf20Sopenharmony_ci{
3688c2ecf20Sopenharmony_ci	fc->ops = &fuse_ctl_context_ops;
3698c2ecf20Sopenharmony_ci	return 0;
3708c2ecf20Sopenharmony_ci}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_cistatic void fuse_ctl_kill_sb(struct super_block *sb)
3738c2ecf20Sopenharmony_ci{
3748c2ecf20Sopenharmony_ci	struct fuse_conn *fc;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	mutex_lock(&fuse_mutex);
3778c2ecf20Sopenharmony_ci	fuse_control_sb = NULL;
3788c2ecf20Sopenharmony_ci	list_for_each_entry(fc, &fuse_conn_list, entry)
3798c2ecf20Sopenharmony_ci		fc->ctl_ndents = 0;
3808c2ecf20Sopenharmony_ci	mutex_unlock(&fuse_mutex);
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	kill_litter_super(sb);
3838c2ecf20Sopenharmony_ci}
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_cistatic struct file_system_type fuse_ctl_fs_type = {
3868c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
3878c2ecf20Sopenharmony_ci	.name		= "fusectl",
3888c2ecf20Sopenharmony_ci	.init_fs_context = fuse_ctl_init_fs_context,
3898c2ecf20Sopenharmony_ci	.kill_sb	= fuse_ctl_kill_sb,
3908c2ecf20Sopenharmony_ci};
3918c2ecf20Sopenharmony_ciMODULE_ALIAS_FS("fusectl");
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ciint __init fuse_ctl_init(void)
3948c2ecf20Sopenharmony_ci{
3958c2ecf20Sopenharmony_ci	return register_filesystem(&fuse_ctl_fs_type);
3968c2ecf20Sopenharmony_ci}
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_civoid __exit fuse_ctl_cleanup(void)
3998c2ecf20Sopenharmony_ci{
4008c2ecf20Sopenharmony_ci	unregister_filesystem(&fuse_ctl_fs_type);
4018c2ecf20Sopenharmony_ci}
402