xref: /openbmc/linux/fs/fuse/control.c (revision 078cd827)
1bafa9654SMiklos Szeredi /*
2bafa9654SMiklos Szeredi   FUSE: Filesystem in Userspace
31729a16cSMiklos Szeredi   Copyright (C) 2001-2008  Miklos Szeredi <miklos@szeredi.hu>
4bafa9654SMiklos Szeredi 
5bafa9654SMiklos Szeredi   This program can be distributed under the terms of the GNU GPL.
6bafa9654SMiklos Szeredi   See the file COPYING.
7bafa9654SMiklos Szeredi */
8bafa9654SMiklos Szeredi 
9bafa9654SMiklos Szeredi #include "fuse_i.h"
10bafa9654SMiklos Szeredi 
11bafa9654SMiklos Szeredi #include <linux/init.h>
12bafa9654SMiklos Szeredi #include <linux/module.h>
13bafa9654SMiklos Szeredi 
14bafa9654SMiklos Szeredi #define FUSE_CTL_SUPER_MAGIC 0x65735543
15bafa9654SMiklos Szeredi 
16bafa9654SMiklos Szeredi /*
17bafa9654SMiklos Szeredi  * This is non-NULL when the single instance of the control filesystem
18bafa9654SMiklos Szeredi  * exists.  Protected by fuse_mutex
19bafa9654SMiklos Szeredi  */
20bafa9654SMiklos Szeredi static struct super_block *fuse_control_sb;
21bafa9654SMiklos Szeredi 
22bafa9654SMiklos Szeredi static struct fuse_conn *fuse_ctl_file_conn_get(struct file *file)
23bafa9654SMiklos Szeredi {
24bafa9654SMiklos Szeredi 	struct fuse_conn *fc;
25bafa9654SMiklos Szeredi 	mutex_lock(&fuse_mutex);
26496ad9aaSAl Viro 	fc = file_inode(file)->i_private;
27bafa9654SMiklos Szeredi 	if (fc)
28bafa9654SMiklos Szeredi 		fc = fuse_conn_get(fc);
29bafa9654SMiklos Szeredi 	mutex_unlock(&fuse_mutex);
30bafa9654SMiklos Szeredi 	return fc;
31bafa9654SMiklos Szeredi }
32bafa9654SMiklos Szeredi 
33bafa9654SMiklos Szeredi static ssize_t fuse_conn_abort_write(struct file *file, const char __user *buf,
34bafa9654SMiklos Szeredi 				     size_t count, loff_t *ppos)
35bafa9654SMiklos Szeredi {
36bafa9654SMiklos Szeredi 	struct fuse_conn *fc = fuse_ctl_file_conn_get(file);
37bafa9654SMiklos Szeredi 	if (fc) {
38bafa9654SMiklos Szeredi 		fuse_abort_conn(fc);
39bafa9654SMiklos Szeredi 		fuse_conn_put(fc);
40bafa9654SMiklos Szeredi 	}
41bafa9654SMiklos Szeredi 	return count;
42bafa9654SMiklos Szeredi }
43bafa9654SMiklos Szeredi 
44bafa9654SMiklos Szeredi static ssize_t fuse_conn_waiting_read(struct file *file, char __user *buf,
45bafa9654SMiklos Szeredi 				      size_t len, loff_t *ppos)
46bafa9654SMiklos Szeredi {
47bafa9654SMiklos Szeredi 	char tmp[32];
48bafa9654SMiklos Szeredi 	size_t size;
49bafa9654SMiklos Szeredi 
50bafa9654SMiklos Szeredi 	if (!*ppos) {
511729a16cSMiklos Szeredi 		long value;
52bafa9654SMiklos Szeredi 		struct fuse_conn *fc = fuse_ctl_file_conn_get(file);
53bafa9654SMiklos Szeredi 		if (!fc)
54bafa9654SMiklos Szeredi 			return 0;
55bafa9654SMiklos Szeredi 
561729a16cSMiklos Szeredi 		value = atomic_read(&fc->num_waiting);
571729a16cSMiklos Szeredi 		file->private_data = (void *)value;
58bafa9654SMiklos Szeredi 		fuse_conn_put(fc);
59bafa9654SMiklos Szeredi 	}
60bafa9654SMiklos Szeredi 	size = sprintf(tmp, "%ld\n", (long)file->private_data);
61bafa9654SMiklos Szeredi 	return simple_read_from_buffer(buf, len, ppos, tmp, size);
62bafa9654SMiklos Szeredi }
63bafa9654SMiklos Szeredi 
6479a9d994SCsaba Henk static ssize_t fuse_conn_limit_read(struct file *file, char __user *buf,
6579a9d994SCsaba Henk 				    size_t len, loff_t *ppos, unsigned val)
6679a9d994SCsaba Henk {
6779a9d994SCsaba Henk 	char tmp[32];
6879a9d994SCsaba Henk 	size_t size = sprintf(tmp, "%u\n", val);
6979a9d994SCsaba Henk 
7079a9d994SCsaba Henk 	return simple_read_from_buffer(buf, len, ppos, tmp, size);
7179a9d994SCsaba Henk }
7279a9d994SCsaba Henk 
7379a9d994SCsaba Henk static ssize_t fuse_conn_limit_write(struct file *file, const char __user *buf,
7479a9d994SCsaba Henk 				     size_t count, loff_t *ppos, unsigned *val,
7579a9d994SCsaba Henk 				     unsigned global_limit)
7679a9d994SCsaba Henk {
7779a9d994SCsaba Henk 	unsigned long t;
7879a9d994SCsaba Henk 	unsigned limit = (1 << 16) - 1;
7979a9d994SCsaba Henk 	int err;
8079a9d994SCsaba Henk 
81e2690695SPeter Huewe 	if (*ppos)
8279a9d994SCsaba Henk 		return -EINVAL;
8379a9d994SCsaba Henk 
84e2690695SPeter Huewe 	err = kstrtoul_from_user(buf, count, 0, &t);
8579a9d994SCsaba Henk 	if (err)
8679a9d994SCsaba Henk 		return err;
8779a9d994SCsaba Henk 
8879a9d994SCsaba Henk 	if (!capable(CAP_SYS_ADMIN))
8979a9d994SCsaba Henk 		limit = min(limit, global_limit);
9079a9d994SCsaba Henk 
9179a9d994SCsaba Henk 	if (t > limit)
9279a9d994SCsaba Henk 		return -EINVAL;
9379a9d994SCsaba Henk 
9479a9d994SCsaba Henk 	*val = t;
9579a9d994SCsaba Henk 
9679a9d994SCsaba Henk 	return count;
9779a9d994SCsaba Henk }
9879a9d994SCsaba Henk 
9979a9d994SCsaba Henk static ssize_t fuse_conn_max_background_read(struct file *file,
10079a9d994SCsaba Henk 					     char __user *buf, size_t len,
10179a9d994SCsaba Henk 					     loff_t *ppos)
10279a9d994SCsaba Henk {
10379a9d994SCsaba Henk 	struct fuse_conn *fc;
10479a9d994SCsaba Henk 	unsigned val;
10579a9d994SCsaba Henk 
10679a9d994SCsaba Henk 	fc = fuse_ctl_file_conn_get(file);
10779a9d994SCsaba Henk 	if (!fc)
10879a9d994SCsaba Henk 		return 0;
10979a9d994SCsaba Henk 
11079a9d994SCsaba Henk 	val = fc->max_background;
11179a9d994SCsaba Henk 	fuse_conn_put(fc);
11279a9d994SCsaba Henk 
11379a9d994SCsaba Henk 	return fuse_conn_limit_read(file, buf, len, ppos, val);
11479a9d994SCsaba Henk }
11579a9d994SCsaba Henk 
11679a9d994SCsaba Henk static ssize_t fuse_conn_max_background_write(struct file *file,
11779a9d994SCsaba Henk 					      const char __user *buf,
11879a9d994SCsaba Henk 					      size_t count, loff_t *ppos)
11979a9d994SCsaba Henk {
120381bf7caSDaniel Mack 	unsigned uninitialized_var(val);
12179a9d994SCsaba Henk 	ssize_t ret;
12279a9d994SCsaba Henk 
12379a9d994SCsaba Henk 	ret = fuse_conn_limit_write(file, buf, count, ppos, &val,
12479a9d994SCsaba Henk 				    max_user_bgreq);
12579a9d994SCsaba Henk 	if (ret > 0) {
12679a9d994SCsaba Henk 		struct fuse_conn *fc = fuse_ctl_file_conn_get(file);
12779a9d994SCsaba Henk 		if (fc) {
12879a9d994SCsaba Henk 			fc->max_background = val;
12979a9d994SCsaba Henk 			fuse_conn_put(fc);
13079a9d994SCsaba Henk 		}
13179a9d994SCsaba Henk 	}
13279a9d994SCsaba Henk 
13379a9d994SCsaba Henk 	return ret;
13479a9d994SCsaba Henk }
13579a9d994SCsaba Henk 
13679a9d994SCsaba Henk static ssize_t fuse_conn_congestion_threshold_read(struct file *file,
13779a9d994SCsaba Henk 						   char __user *buf, size_t len,
13879a9d994SCsaba Henk 						   loff_t *ppos)
13979a9d994SCsaba Henk {
14079a9d994SCsaba Henk 	struct fuse_conn *fc;
14179a9d994SCsaba Henk 	unsigned val;
14279a9d994SCsaba Henk 
14379a9d994SCsaba Henk 	fc = fuse_ctl_file_conn_get(file);
14479a9d994SCsaba Henk 	if (!fc)
14579a9d994SCsaba Henk 		return 0;
14679a9d994SCsaba Henk 
14779a9d994SCsaba Henk 	val = fc->congestion_threshold;
14879a9d994SCsaba Henk 	fuse_conn_put(fc);
14979a9d994SCsaba Henk 
15079a9d994SCsaba Henk 	return fuse_conn_limit_read(file, buf, len, ppos, val);
15179a9d994SCsaba Henk }
15279a9d994SCsaba Henk 
15379a9d994SCsaba Henk static ssize_t fuse_conn_congestion_threshold_write(struct file *file,
15479a9d994SCsaba Henk 						    const char __user *buf,
15579a9d994SCsaba Henk 						    size_t count, loff_t *ppos)
15679a9d994SCsaba Henk {
157381bf7caSDaniel Mack 	unsigned uninitialized_var(val);
15879a9d994SCsaba Henk 	ssize_t ret;
15979a9d994SCsaba Henk 
16079a9d994SCsaba Henk 	ret = fuse_conn_limit_write(file, buf, count, ppos, &val,
16179a9d994SCsaba Henk 				    max_user_congthresh);
16279a9d994SCsaba Henk 	if (ret > 0) {
16379a9d994SCsaba Henk 		struct fuse_conn *fc = fuse_ctl_file_conn_get(file);
16479a9d994SCsaba Henk 		if (fc) {
16579a9d994SCsaba Henk 			fc->congestion_threshold = val;
16679a9d994SCsaba Henk 			fuse_conn_put(fc);
16779a9d994SCsaba Henk 		}
16879a9d994SCsaba Henk 	}
16979a9d994SCsaba Henk 
17079a9d994SCsaba Henk 	return ret;
17179a9d994SCsaba Henk }
17279a9d994SCsaba Henk 
173bafa9654SMiklos Szeredi static const struct file_operations fuse_ctl_abort_ops = {
174bafa9654SMiklos Szeredi 	.open = nonseekable_open,
175bafa9654SMiklos Szeredi 	.write = fuse_conn_abort_write,
1766038f373SArnd Bergmann 	.llseek = no_llseek,
177bafa9654SMiklos Szeredi };
178bafa9654SMiklos Szeredi 
179bafa9654SMiklos Szeredi static const struct file_operations fuse_ctl_waiting_ops = {
180bafa9654SMiklos Szeredi 	.open = nonseekable_open,
181bafa9654SMiklos Szeredi 	.read = fuse_conn_waiting_read,
1826038f373SArnd Bergmann 	.llseek = no_llseek,
183bafa9654SMiklos Szeredi };
184bafa9654SMiklos Szeredi 
18579a9d994SCsaba Henk static const struct file_operations fuse_conn_max_background_ops = {
18679a9d994SCsaba Henk 	.open = nonseekable_open,
18779a9d994SCsaba Henk 	.read = fuse_conn_max_background_read,
18879a9d994SCsaba Henk 	.write = fuse_conn_max_background_write,
1896038f373SArnd Bergmann 	.llseek = no_llseek,
19079a9d994SCsaba Henk };
19179a9d994SCsaba Henk 
19279a9d994SCsaba Henk static const struct file_operations fuse_conn_congestion_threshold_ops = {
19379a9d994SCsaba Henk 	.open = nonseekable_open,
19479a9d994SCsaba Henk 	.read = fuse_conn_congestion_threshold_read,
19579a9d994SCsaba Henk 	.write = fuse_conn_congestion_threshold_write,
1966038f373SArnd Bergmann 	.llseek = no_llseek,
19779a9d994SCsaba Henk };
19879a9d994SCsaba Henk 
199bafa9654SMiklos Szeredi static struct dentry *fuse_ctl_add_dentry(struct dentry *parent,
200bafa9654SMiklos Szeredi 					  struct fuse_conn *fc,
201bafa9654SMiklos Szeredi 					  const char *name,
202bafa9654SMiklos Szeredi 					  int mode, int nlink,
203754661f1SArjan van de Ven 					  const struct inode_operations *iop,
204bafa9654SMiklos Szeredi 					  const struct file_operations *fop)
205bafa9654SMiklos Szeredi {
206bafa9654SMiklos Szeredi 	struct dentry *dentry;
207bafa9654SMiklos Szeredi 	struct inode *inode;
208bafa9654SMiklos Szeredi 
209bafa9654SMiklos Szeredi 	BUG_ON(fc->ctl_ndents >= FUSE_CTL_NUM_DENTRIES);
210bafa9654SMiklos Szeredi 	dentry = d_alloc_name(parent, name);
211bafa9654SMiklos Szeredi 	if (!dentry)
212bafa9654SMiklos Szeredi 		return NULL;
213bafa9654SMiklos Szeredi 
214bafa9654SMiklos Szeredi 	fc->ctl_dentry[fc->ctl_ndents++] = dentry;
215bafa9654SMiklos Szeredi 	inode = new_inode(fuse_control_sb);
216bafa9654SMiklos Szeredi 	if (!inode)
217bafa9654SMiklos Szeredi 		return NULL;
218bafa9654SMiklos Szeredi 
21985fe4025SChristoph Hellwig 	inode->i_ino = get_next_ino();
220bafa9654SMiklos Szeredi 	inode->i_mode = mode;
221bafa9654SMiklos Szeredi 	inode->i_uid = fc->user_id;
222bafa9654SMiklos Szeredi 	inode->i_gid = fc->group_id;
223078cd827SDeepa Dinamani 	inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode);
224bafa9654SMiklos Szeredi 	/* setting ->i_op to NULL is not allowed */
225bafa9654SMiklos Szeredi 	if (iop)
226bafa9654SMiklos Szeredi 		inode->i_op = iop;
227bafa9654SMiklos Szeredi 	inode->i_fop = fop;
228bfe86848SMiklos Szeredi 	set_nlink(inode, nlink);
2298e18e294STheodore Ts'o 	inode->i_private = fc;
230bafa9654SMiklos Szeredi 	d_add(dentry, inode);
231bafa9654SMiklos Szeredi 	return dentry;
232bafa9654SMiklos Szeredi }
233bafa9654SMiklos Szeredi 
234bafa9654SMiklos Szeredi /*
235bafa9654SMiklos Szeredi  * Add a connection to the control filesystem (if it exists).  Caller
236873302c7SMiklos Szeredi  * must hold fuse_mutex
237bafa9654SMiklos Szeredi  */
238bafa9654SMiklos Szeredi int fuse_ctl_add_conn(struct fuse_conn *fc)
239bafa9654SMiklos Szeredi {
240bafa9654SMiklos Szeredi 	struct dentry *parent;
241bafa9654SMiklos Szeredi 	char name[32];
242bafa9654SMiklos Szeredi 
243bafa9654SMiklos Szeredi 	if (!fuse_control_sb)
244bafa9654SMiklos Szeredi 		return 0;
245bafa9654SMiklos Szeredi 
246bafa9654SMiklos Szeredi 	parent = fuse_control_sb->s_root;
2472b0143b5SDavid Howells 	inc_nlink(d_inode(parent));
248b6f2fcbcSMiklos Szeredi 	sprintf(name, "%u", fc->dev);
249bafa9654SMiklos Szeredi 	parent = fuse_ctl_add_dentry(parent, fc, name, S_IFDIR | 0500, 2,
250bafa9654SMiklos Szeredi 				     &simple_dir_inode_operations,
251bafa9654SMiklos Szeredi 				     &simple_dir_operations);
252bafa9654SMiklos Szeredi 	if (!parent)
253bafa9654SMiklos Szeredi 		goto err;
254bafa9654SMiklos Szeredi 
255bafa9654SMiklos Szeredi 	if (!fuse_ctl_add_dentry(parent, fc, "waiting", S_IFREG | 0400, 1,
256bafa9654SMiklos Szeredi 				 NULL, &fuse_ctl_waiting_ops) ||
257bafa9654SMiklos Szeredi 	    !fuse_ctl_add_dentry(parent, fc, "abort", S_IFREG | 0200, 1,
25879a9d994SCsaba Henk 				 NULL, &fuse_ctl_abort_ops) ||
25979a9d994SCsaba Henk 	    !fuse_ctl_add_dentry(parent, fc, "max_background", S_IFREG | 0600,
26079a9d994SCsaba Henk 				 1, NULL, &fuse_conn_max_background_ops) ||
26179a9d994SCsaba Henk 	    !fuse_ctl_add_dentry(parent, fc, "congestion_threshold",
26279a9d994SCsaba Henk 				 S_IFREG | 0600, 1, NULL,
26379a9d994SCsaba Henk 				 &fuse_conn_congestion_threshold_ops))
264bafa9654SMiklos Szeredi 		goto err;
265bafa9654SMiklos Szeredi 
266bafa9654SMiklos Szeredi 	return 0;
267bafa9654SMiklos Szeredi 
268bafa9654SMiklos Szeredi  err:
269bafa9654SMiklos Szeredi 	fuse_ctl_remove_conn(fc);
270bafa9654SMiklos Szeredi 	return -ENOMEM;
271bafa9654SMiklos Szeredi }
272bafa9654SMiklos Szeredi 
273bafa9654SMiklos Szeredi /*
274bafa9654SMiklos Szeredi  * Remove a connection from the control filesystem (if it exists).
275873302c7SMiklos Szeredi  * Caller must hold fuse_mutex
276bafa9654SMiklos Szeredi  */
277bafa9654SMiklos Szeredi void fuse_ctl_remove_conn(struct fuse_conn *fc)
278bafa9654SMiklos Szeredi {
279bafa9654SMiklos Szeredi 	int i;
280bafa9654SMiklos Szeredi 
281bafa9654SMiklos Szeredi 	if (!fuse_control_sb)
282bafa9654SMiklos Szeredi 		return;
283bafa9654SMiklos Szeredi 
284bafa9654SMiklos Szeredi 	for (i = fc->ctl_ndents - 1; i >= 0; i--) {
285bafa9654SMiklos Szeredi 		struct dentry *dentry = fc->ctl_dentry[i];
2862b0143b5SDavid Howells 		d_inode(dentry)->i_private = NULL;
287bafa9654SMiklos Szeredi 		d_drop(dentry);
288bafa9654SMiklos Szeredi 		dput(dentry);
289bafa9654SMiklos Szeredi 	}
2902b0143b5SDavid Howells 	drop_nlink(d_inode(fuse_control_sb->s_root));
291bafa9654SMiklos Szeredi }
292bafa9654SMiklos Szeredi 
293bafa9654SMiklos Szeredi static int fuse_ctl_fill_super(struct super_block *sb, void *data, int silent)
294bafa9654SMiklos Szeredi {
295bafa9654SMiklos Szeredi 	struct tree_descr empty_descr = {""};
296bafa9654SMiklos Szeredi 	struct fuse_conn *fc;
297bafa9654SMiklos Szeredi 	int err;
298bafa9654SMiklos Szeredi 
299bafa9654SMiklos Szeredi 	err = simple_fill_super(sb, FUSE_CTL_SUPER_MAGIC, &empty_descr);
300bafa9654SMiklos Szeredi 	if (err)
301bafa9654SMiklos Szeredi 		return err;
302bafa9654SMiklos Szeredi 
303bafa9654SMiklos Szeredi 	mutex_lock(&fuse_mutex);
304bafa9654SMiklos Szeredi 	BUG_ON(fuse_control_sb);
305bafa9654SMiklos Szeredi 	fuse_control_sb = sb;
306bafa9654SMiklos Szeredi 	list_for_each_entry(fc, &fuse_conn_list, entry) {
307bafa9654SMiklos Szeredi 		err = fuse_ctl_add_conn(fc);
308bafa9654SMiklos Szeredi 		if (err) {
309bafa9654SMiklos Szeredi 			fuse_control_sb = NULL;
310bafa9654SMiklos Szeredi 			mutex_unlock(&fuse_mutex);
311bafa9654SMiklos Szeredi 			return err;
312bafa9654SMiklos Szeredi 		}
313bafa9654SMiklos Szeredi 	}
314bafa9654SMiklos Szeredi 	mutex_unlock(&fuse_mutex);
315bafa9654SMiklos Szeredi 
316bafa9654SMiklos Szeredi 	return 0;
317bafa9654SMiklos Szeredi }
318bafa9654SMiklos Szeredi 
319fc14f2feSAl Viro static struct dentry *fuse_ctl_mount(struct file_system_type *fs_type,
320fc14f2feSAl Viro 			int flags, const char *dev_name, void *raw_data)
321bafa9654SMiklos Szeredi {
322fc14f2feSAl Viro 	return mount_single(fs_type, flags, raw_data, fuse_ctl_fill_super);
323bafa9654SMiklos Szeredi }
324bafa9654SMiklos Szeredi 
325bafa9654SMiklos Szeredi static void fuse_ctl_kill_sb(struct super_block *sb)
326bafa9654SMiklos Szeredi {
327ff795447SMiklos Szeredi 	struct fuse_conn *fc;
328ff795447SMiklos Szeredi 
329bafa9654SMiklos Szeredi 	mutex_lock(&fuse_mutex);
330bafa9654SMiklos Szeredi 	fuse_control_sb = NULL;
331ff795447SMiklos Szeredi 	list_for_each_entry(fc, &fuse_conn_list, entry)
332ff795447SMiklos Szeredi 		fc->ctl_ndents = 0;
333bafa9654SMiklos Szeredi 	mutex_unlock(&fuse_mutex);
334bafa9654SMiklos Szeredi 
335bafa9654SMiklos Szeredi 	kill_litter_super(sb);
336bafa9654SMiklos Szeredi }
337bafa9654SMiklos Szeredi 
338bafa9654SMiklos Szeredi static struct file_system_type fuse_ctl_fs_type = {
339bafa9654SMiklos Szeredi 	.owner		= THIS_MODULE,
340bafa9654SMiklos Szeredi 	.name		= "fusectl",
341fc14f2feSAl Viro 	.mount		= fuse_ctl_mount,
342bafa9654SMiklos Szeredi 	.kill_sb	= fuse_ctl_kill_sb,
343bafa9654SMiklos Szeredi };
3447f78e035SEric W. Biederman MODULE_ALIAS_FS("fusectl");
345bafa9654SMiklos Szeredi 
346bafa9654SMiklos Szeredi int __init fuse_ctl_init(void)
347bafa9654SMiklos Szeredi {
348bafa9654SMiklos Szeredi 	return register_filesystem(&fuse_ctl_fs_type);
349bafa9654SMiklos Szeredi }
350bafa9654SMiklos Szeredi 
3517736e8ccSFabian Frederick void __exit fuse_ctl_cleanup(void)
352bafa9654SMiklos Szeredi {
353bafa9654SMiklos Szeredi 	unregister_filesystem(&fuse_ctl_fs_type);
354bafa9654SMiklos Szeredi }
355