cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

control.c (8654B)


      1/*
      2  FUSE: Filesystem in Userspace
      3  Copyright (C) 2001-2008  Miklos Szeredi <miklos@szeredi.hu>
      4
      5  This program can be distributed under the terms of the GNU GPL.
      6  See the file COPYING.
      7*/
      8
      9#include "fuse_i.h"
     10
     11#include <linux/init.h>
     12#include <linux/module.h>
     13#include <linux/fs_context.h>
     14
     15#define FUSE_CTL_SUPER_MAGIC 0x65735543
     16
     17/*
     18 * This is non-NULL when the single instance of the control filesystem
     19 * exists.  Protected by fuse_mutex
     20 */
     21static struct super_block *fuse_control_sb;
     22
     23static struct fuse_conn *fuse_ctl_file_conn_get(struct file *file)
     24{
     25	struct fuse_conn *fc;
     26	mutex_lock(&fuse_mutex);
     27	fc = file_inode(file)->i_private;
     28	if (fc)
     29		fc = fuse_conn_get(fc);
     30	mutex_unlock(&fuse_mutex);
     31	return fc;
     32}
     33
     34static ssize_t fuse_conn_abort_write(struct file *file, const char __user *buf,
     35				     size_t count, loff_t *ppos)
     36{
     37	struct fuse_conn *fc = fuse_ctl_file_conn_get(file);
     38	if (fc) {
     39		if (fc->abort_err)
     40			fc->aborted = true;
     41		fuse_abort_conn(fc);
     42		fuse_conn_put(fc);
     43	}
     44	return count;
     45}
     46
     47static ssize_t fuse_conn_waiting_read(struct file *file, char __user *buf,
     48				      size_t len, loff_t *ppos)
     49{
     50	char tmp[32];
     51	size_t size;
     52
     53	if (!*ppos) {
     54		long value;
     55		struct fuse_conn *fc = fuse_ctl_file_conn_get(file);
     56		if (!fc)
     57			return 0;
     58
     59		value = atomic_read(&fc->num_waiting);
     60		file->private_data = (void *)value;
     61		fuse_conn_put(fc);
     62	}
     63	size = sprintf(tmp, "%ld\n", (long)file->private_data);
     64	return simple_read_from_buffer(buf, len, ppos, tmp, size);
     65}
     66
     67static ssize_t fuse_conn_limit_read(struct file *file, char __user *buf,
     68				    size_t len, loff_t *ppos, unsigned val)
     69{
     70	char tmp[32];
     71	size_t size = sprintf(tmp, "%u\n", val);
     72
     73	return simple_read_from_buffer(buf, len, ppos, tmp, size);
     74}
     75
     76static ssize_t fuse_conn_limit_write(struct file *file, const char __user *buf,
     77				     size_t count, loff_t *ppos, unsigned *val,
     78				     unsigned global_limit)
     79{
     80	unsigned long t;
     81	unsigned limit = (1 << 16) - 1;
     82	int err;
     83
     84	if (*ppos)
     85		return -EINVAL;
     86
     87	err = kstrtoul_from_user(buf, count, 0, &t);
     88	if (err)
     89		return err;
     90
     91	if (!capable(CAP_SYS_ADMIN))
     92		limit = min(limit, global_limit);
     93
     94	if (t > limit)
     95		return -EINVAL;
     96
     97	*val = t;
     98
     99	return count;
    100}
    101
    102static ssize_t fuse_conn_max_background_read(struct file *file,
    103					     char __user *buf, size_t len,
    104					     loff_t *ppos)
    105{
    106	struct fuse_conn *fc;
    107	unsigned val;
    108
    109	fc = fuse_ctl_file_conn_get(file);
    110	if (!fc)
    111		return 0;
    112
    113	val = READ_ONCE(fc->max_background);
    114	fuse_conn_put(fc);
    115
    116	return fuse_conn_limit_read(file, buf, len, ppos, val);
    117}
    118
    119static ssize_t fuse_conn_max_background_write(struct file *file,
    120					      const char __user *buf,
    121					      size_t count, loff_t *ppos)
    122{
    123	unsigned val;
    124	ssize_t ret;
    125
    126	ret = fuse_conn_limit_write(file, buf, count, ppos, &val,
    127				    max_user_bgreq);
    128	if (ret > 0) {
    129		struct fuse_conn *fc = fuse_ctl_file_conn_get(file);
    130		if (fc) {
    131			spin_lock(&fc->bg_lock);
    132			fc->max_background = val;
    133			fc->blocked = fc->num_background >= fc->max_background;
    134			if (!fc->blocked)
    135				wake_up(&fc->blocked_waitq);
    136			spin_unlock(&fc->bg_lock);
    137			fuse_conn_put(fc);
    138		}
    139	}
    140
    141	return ret;
    142}
    143
    144static ssize_t fuse_conn_congestion_threshold_read(struct file *file,
    145						   char __user *buf, size_t len,
    146						   loff_t *ppos)
    147{
    148	struct fuse_conn *fc;
    149	unsigned val;
    150
    151	fc = fuse_ctl_file_conn_get(file);
    152	if (!fc)
    153		return 0;
    154
    155	val = READ_ONCE(fc->congestion_threshold);
    156	fuse_conn_put(fc);
    157
    158	return fuse_conn_limit_read(file, buf, len, ppos, val);
    159}
    160
    161static ssize_t fuse_conn_congestion_threshold_write(struct file *file,
    162						    const char __user *buf,
    163						    size_t count, loff_t *ppos)
    164{
    165	unsigned val;
    166	struct fuse_conn *fc;
    167	ssize_t ret;
    168
    169	ret = fuse_conn_limit_write(file, buf, count, ppos, &val,
    170				    max_user_congthresh);
    171	if (ret <= 0)
    172		goto out;
    173	fc = fuse_ctl_file_conn_get(file);
    174	if (!fc)
    175		goto out;
    176
    177	down_read(&fc->killsb);
    178	spin_lock(&fc->bg_lock);
    179	fc->congestion_threshold = val;
    180	spin_unlock(&fc->bg_lock);
    181	up_read(&fc->killsb);
    182	fuse_conn_put(fc);
    183out:
    184	return ret;
    185}
    186
    187static const struct file_operations fuse_ctl_abort_ops = {
    188	.open = nonseekable_open,
    189	.write = fuse_conn_abort_write,
    190	.llseek = no_llseek,
    191};
    192
    193static const struct file_operations fuse_ctl_waiting_ops = {
    194	.open = nonseekable_open,
    195	.read = fuse_conn_waiting_read,
    196	.llseek = no_llseek,
    197};
    198
    199static const struct file_operations fuse_conn_max_background_ops = {
    200	.open = nonseekable_open,
    201	.read = fuse_conn_max_background_read,
    202	.write = fuse_conn_max_background_write,
    203	.llseek = no_llseek,
    204};
    205
    206static const struct file_operations fuse_conn_congestion_threshold_ops = {
    207	.open = nonseekable_open,
    208	.read = fuse_conn_congestion_threshold_read,
    209	.write = fuse_conn_congestion_threshold_write,
    210	.llseek = no_llseek,
    211};
    212
    213static struct dentry *fuse_ctl_add_dentry(struct dentry *parent,
    214					  struct fuse_conn *fc,
    215					  const char *name,
    216					  int mode, int nlink,
    217					  const struct inode_operations *iop,
    218					  const struct file_operations *fop)
    219{
    220	struct dentry *dentry;
    221	struct inode *inode;
    222
    223	BUG_ON(fc->ctl_ndents >= FUSE_CTL_NUM_DENTRIES);
    224	dentry = d_alloc_name(parent, name);
    225	if (!dentry)
    226		return NULL;
    227
    228	inode = new_inode(fuse_control_sb);
    229	if (!inode) {
    230		dput(dentry);
    231		return NULL;
    232	}
    233
    234	inode->i_ino = get_next_ino();
    235	inode->i_mode = mode;
    236	inode->i_uid = fc->user_id;
    237	inode->i_gid = fc->group_id;
    238	inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode);
    239	/* setting ->i_op to NULL is not allowed */
    240	if (iop)
    241		inode->i_op = iop;
    242	inode->i_fop = fop;
    243	set_nlink(inode, nlink);
    244	inode->i_private = fc;
    245	d_add(dentry, inode);
    246
    247	fc->ctl_dentry[fc->ctl_ndents++] = dentry;
    248
    249	return dentry;
    250}
    251
    252/*
    253 * Add a connection to the control filesystem (if it exists).  Caller
    254 * must hold fuse_mutex
    255 */
    256int fuse_ctl_add_conn(struct fuse_conn *fc)
    257{
    258	struct dentry *parent;
    259	char name[32];
    260
    261	if (!fuse_control_sb)
    262		return 0;
    263
    264	parent = fuse_control_sb->s_root;
    265	inc_nlink(d_inode(parent));
    266	sprintf(name, "%u", fc->dev);
    267	parent = fuse_ctl_add_dentry(parent, fc, name, S_IFDIR | 0500, 2,
    268				     &simple_dir_inode_operations,
    269				     &simple_dir_operations);
    270	if (!parent)
    271		goto err;
    272
    273	if (!fuse_ctl_add_dentry(parent, fc, "waiting", S_IFREG | 0400, 1,
    274				 NULL, &fuse_ctl_waiting_ops) ||
    275	    !fuse_ctl_add_dentry(parent, fc, "abort", S_IFREG | 0200, 1,
    276				 NULL, &fuse_ctl_abort_ops) ||
    277	    !fuse_ctl_add_dentry(parent, fc, "max_background", S_IFREG | 0600,
    278				 1, NULL, &fuse_conn_max_background_ops) ||
    279	    !fuse_ctl_add_dentry(parent, fc, "congestion_threshold",
    280				 S_IFREG | 0600, 1, NULL,
    281				 &fuse_conn_congestion_threshold_ops))
    282		goto err;
    283
    284	return 0;
    285
    286 err:
    287	fuse_ctl_remove_conn(fc);
    288	return -ENOMEM;
    289}
    290
    291/*
    292 * Remove a connection from the control filesystem (if it exists).
    293 * Caller must hold fuse_mutex
    294 */
    295void fuse_ctl_remove_conn(struct fuse_conn *fc)
    296{
    297	int i;
    298
    299	if (!fuse_control_sb)
    300		return;
    301
    302	for (i = fc->ctl_ndents - 1; i >= 0; i--) {
    303		struct dentry *dentry = fc->ctl_dentry[i];
    304		d_inode(dentry)->i_private = NULL;
    305		if (!i) {
    306			/* Get rid of submounts: */
    307			d_invalidate(dentry);
    308		}
    309		dput(dentry);
    310	}
    311	drop_nlink(d_inode(fuse_control_sb->s_root));
    312}
    313
    314static int fuse_ctl_fill_super(struct super_block *sb, struct fs_context *fsc)
    315{
    316	static const struct tree_descr empty_descr = {""};
    317	struct fuse_conn *fc;
    318	int err;
    319
    320	err = simple_fill_super(sb, FUSE_CTL_SUPER_MAGIC, &empty_descr);
    321	if (err)
    322		return err;
    323
    324	mutex_lock(&fuse_mutex);
    325	BUG_ON(fuse_control_sb);
    326	fuse_control_sb = sb;
    327	list_for_each_entry(fc, &fuse_conn_list, entry) {
    328		err = fuse_ctl_add_conn(fc);
    329		if (err) {
    330			fuse_control_sb = NULL;
    331			mutex_unlock(&fuse_mutex);
    332			return err;
    333		}
    334	}
    335	mutex_unlock(&fuse_mutex);
    336
    337	return 0;
    338}
    339
    340static int fuse_ctl_get_tree(struct fs_context *fsc)
    341{
    342	return get_tree_single(fsc, fuse_ctl_fill_super);
    343}
    344
    345static const struct fs_context_operations fuse_ctl_context_ops = {
    346	.get_tree	= fuse_ctl_get_tree,
    347};
    348
    349static int fuse_ctl_init_fs_context(struct fs_context *fsc)
    350{
    351	fsc->ops = &fuse_ctl_context_ops;
    352	return 0;
    353}
    354
    355static void fuse_ctl_kill_sb(struct super_block *sb)
    356{
    357	struct fuse_conn *fc;
    358
    359	mutex_lock(&fuse_mutex);
    360	fuse_control_sb = NULL;
    361	list_for_each_entry(fc, &fuse_conn_list, entry)
    362		fc->ctl_ndents = 0;
    363	mutex_unlock(&fuse_mutex);
    364
    365	kill_litter_super(sb);
    366}
    367
    368static struct file_system_type fuse_ctl_fs_type = {
    369	.owner		= THIS_MODULE,
    370	.name		= "fusectl",
    371	.init_fs_context = fuse_ctl_init_fs_context,
    372	.kill_sb	= fuse_ctl_kill_sb,
    373};
    374MODULE_ALIAS_FS("fusectl");
    375
    376int __init fuse_ctl_init(void)
    377{
    378	return register_filesystem(&fuse_ctl_fs_type);
    379}
    380
    381void __exit fuse_ctl_cleanup(void)
    382{
    383	unregister_filesystem(&fuse_ctl_fs_type);
    384}