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