18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * CUSE: Character device in Userspace 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2008-2009 SUSE Linux Products GmbH 68c2ecf20Sopenharmony_ci * Copyright (C) 2008-2009 Tejun Heo <tj@kernel.org> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * CUSE enables character devices to be implemented from userland much 98c2ecf20Sopenharmony_ci * like FUSE allows filesystems. On initialization /dev/cuse is 108c2ecf20Sopenharmony_ci * created. By opening the file and replying to the CUSE_INIT request 118c2ecf20Sopenharmony_ci * userland CUSE server can create a character device. After that the 128c2ecf20Sopenharmony_ci * operation is very similar to FUSE. 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * A CUSE instance involves the following objects. 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * cuse_conn : contains fuse_conn and serves as bonding structure 178c2ecf20Sopenharmony_ci * channel : file handle connected to the userland CUSE server 188c2ecf20Sopenharmony_ci * cdev : the implemented character device 198c2ecf20Sopenharmony_ci * dev : generic device for cdev 208c2ecf20Sopenharmony_ci * 218c2ecf20Sopenharmony_ci * Note that 'channel' is what 'dev' is in FUSE. As CUSE deals with 228c2ecf20Sopenharmony_ci * devices, it's called 'channel' to reduce confusion. 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci * channel determines when the character device dies. When channel is 258c2ecf20Sopenharmony_ci * closed, everything begins to destruct. The cuse_conn is taken off 268c2ecf20Sopenharmony_ci * the lookup table preventing further access from cdev, cdev and 278c2ecf20Sopenharmony_ci * generic device are removed and the base reference of cuse_conn is 288c2ecf20Sopenharmony_ci * put. 298c2ecf20Sopenharmony_ci * 308c2ecf20Sopenharmony_ci * On each open, the matching cuse_conn is looked up and if found an 318c2ecf20Sopenharmony_ci * additional reference is taken which is released when the file is 328c2ecf20Sopenharmony_ci * closed. 338c2ecf20Sopenharmony_ci */ 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "CUSE: " fmt 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#include <linux/fuse.h> 388c2ecf20Sopenharmony_ci#include <linux/cdev.h> 398c2ecf20Sopenharmony_ci#include <linux/device.h> 408c2ecf20Sopenharmony_ci#include <linux/file.h> 418c2ecf20Sopenharmony_ci#include <linux/fs.h> 428c2ecf20Sopenharmony_ci#include <linux/kdev_t.h> 438c2ecf20Sopenharmony_ci#include <linux/kthread.h> 448c2ecf20Sopenharmony_ci#include <linux/list.h> 458c2ecf20Sopenharmony_ci#include <linux/magic.h> 468c2ecf20Sopenharmony_ci#include <linux/miscdevice.h> 478c2ecf20Sopenharmony_ci#include <linux/mutex.h> 488c2ecf20Sopenharmony_ci#include <linux/slab.h> 498c2ecf20Sopenharmony_ci#include <linux/stat.h> 508c2ecf20Sopenharmony_ci#include <linux/module.h> 518c2ecf20Sopenharmony_ci#include <linux/uio.h> 528c2ecf20Sopenharmony_ci#include <linux/user_namespace.h> 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci#include "fuse_i.h" 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci#define CUSE_CONNTBL_LEN 64 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistruct cuse_conn { 598c2ecf20Sopenharmony_ci struct list_head list; /* linked on cuse_conntbl */ 608c2ecf20Sopenharmony_ci struct fuse_mount fm; /* Dummy mount referencing fc */ 618c2ecf20Sopenharmony_ci struct fuse_conn fc; /* fuse connection */ 628c2ecf20Sopenharmony_ci struct cdev *cdev; /* associated character device */ 638c2ecf20Sopenharmony_ci struct device *dev; /* device representing @cdev */ 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci /* init parameters, set once during initialization */ 668c2ecf20Sopenharmony_ci bool unrestricted_ioctl; 678c2ecf20Sopenharmony_ci}; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(cuse_lock); /* protects registration */ 708c2ecf20Sopenharmony_cistatic struct list_head cuse_conntbl[CUSE_CONNTBL_LEN]; 718c2ecf20Sopenharmony_cistatic struct class *cuse_class; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic struct cuse_conn *fc_to_cc(struct fuse_conn *fc) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci return container_of(fc, struct cuse_conn, fc); 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic struct list_head *cuse_conntbl_head(dev_t devt) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci return &cuse_conntbl[(MAJOR(devt) + MINOR(devt)) % CUSE_CONNTBL_LEN]; 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci/************************************************************************** 858c2ecf20Sopenharmony_ci * CUSE frontend operations 868c2ecf20Sopenharmony_ci * 878c2ecf20Sopenharmony_ci * These are file operations for the character device. 888c2ecf20Sopenharmony_ci * 898c2ecf20Sopenharmony_ci * On open, CUSE opens a file from the FUSE mnt and stores it to 908c2ecf20Sopenharmony_ci * private_data of the open file. All other ops call FUSE ops on the 918c2ecf20Sopenharmony_ci * FUSE file. 928c2ecf20Sopenharmony_ci */ 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic ssize_t cuse_read_iter(struct kiocb *kiocb, struct iov_iter *to) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci struct fuse_io_priv io = FUSE_IO_PRIV_SYNC(kiocb); 978c2ecf20Sopenharmony_ci loff_t pos = 0; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci return fuse_direct_io(&io, to, &pos, FUSE_DIO_CUSE); 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic ssize_t cuse_write_iter(struct kiocb *kiocb, struct iov_iter *from) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci struct fuse_io_priv io = FUSE_IO_PRIV_SYNC(kiocb); 1058c2ecf20Sopenharmony_ci loff_t pos = 0; 1068c2ecf20Sopenharmony_ci /* 1078c2ecf20Sopenharmony_ci * No locking or generic_write_checks(), the server is 1088c2ecf20Sopenharmony_ci * responsible for locking and sanity checks. 1098c2ecf20Sopenharmony_ci */ 1108c2ecf20Sopenharmony_ci return fuse_direct_io(&io, from, &pos, 1118c2ecf20Sopenharmony_ci FUSE_DIO_WRITE | FUSE_DIO_CUSE); 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic int cuse_open(struct inode *inode, struct file *file) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci dev_t devt = inode->i_cdev->dev; 1178c2ecf20Sopenharmony_ci struct cuse_conn *cc = NULL, *pos; 1188c2ecf20Sopenharmony_ci int rc; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci /* look up and get the connection */ 1218c2ecf20Sopenharmony_ci mutex_lock(&cuse_lock); 1228c2ecf20Sopenharmony_ci list_for_each_entry(pos, cuse_conntbl_head(devt), list) 1238c2ecf20Sopenharmony_ci if (pos->dev->devt == devt) { 1248c2ecf20Sopenharmony_ci fuse_conn_get(&pos->fc); 1258c2ecf20Sopenharmony_ci cc = pos; 1268c2ecf20Sopenharmony_ci break; 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci mutex_unlock(&cuse_lock); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci /* dead? */ 1318c2ecf20Sopenharmony_ci if (!cc) 1328c2ecf20Sopenharmony_ci return -ENODEV; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci /* 1358c2ecf20Sopenharmony_ci * Generic permission check is already done against the chrdev 1368c2ecf20Sopenharmony_ci * file, proceed to open. 1378c2ecf20Sopenharmony_ci */ 1388c2ecf20Sopenharmony_ci rc = fuse_do_open(&cc->fm, 0, file, 0); 1398c2ecf20Sopenharmony_ci if (rc) 1408c2ecf20Sopenharmony_ci fuse_conn_put(&cc->fc); 1418c2ecf20Sopenharmony_ci return rc; 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic int cuse_release(struct inode *inode, struct file *file) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci struct fuse_file *ff = file->private_data; 1478c2ecf20Sopenharmony_ci struct fuse_mount *fm = ff->fm; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci fuse_sync_release(NULL, ff, file->f_flags); 1508c2ecf20Sopenharmony_ci fuse_conn_put(fm->fc); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci return 0; 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic long cuse_file_ioctl(struct file *file, unsigned int cmd, 1568c2ecf20Sopenharmony_ci unsigned long arg) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci struct fuse_file *ff = file->private_data; 1598c2ecf20Sopenharmony_ci struct cuse_conn *cc = fc_to_cc(ff->fm->fc); 1608c2ecf20Sopenharmony_ci unsigned int flags = 0; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci if (cc->unrestricted_ioctl) 1638c2ecf20Sopenharmony_ci flags |= FUSE_IOCTL_UNRESTRICTED; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci return fuse_do_ioctl(file, cmd, arg, flags); 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic long cuse_file_compat_ioctl(struct file *file, unsigned int cmd, 1698c2ecf20Sopenharmony_ci unsigned long arg) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci struct fuse_file *ff = file->private_data; 1728c2ecf20Sopenharmony_ci struct cuse_conn *cc = fc_to_cc(ff->fm->fc); 1738c2ecf20Sopenharmony_ci unsigned int flags = FUSE_IOCTL_COMPAT; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci if (cc->unrestricted_ioctl) 1768c2ecf20Sopenharmony_ci flags |= FUSE_IOCTL_UNRESTRICTED; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci return fuse_do_ioctl(file, cmd, arg, flags); 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic const struct file_operations cuse_frontend_fops = { 1828c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1838c2ecf20Sopenharmony_ci .read_iter = cuse_read_iter, 1848c2ecf20Sopenharmony_ci .write_iter = cuse_write_iter, 1858c2ecf20Sopenharmony_ci .open = cuse_open, 1868c2ecf20Sopenharmony_ci .release = cuse_release, 1878c2ecf20Sopenharmony_ci .unlocked_ioctl = cuse_file_ioctl, 1888c2ecf20Sopenharmony_ci .compat_ioctl = cuse_file_compat_ioctl, 1898c2ecf20Sopenharmony_ci .poll = fuse_file_poll, 1908c2ecf20Sopenharmony_ci .llseek = noop_llseek, 1918c2ecf20Sopenharmony_ci}; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci/************************************************************************** 1958c2ecf20Sopenharmony_ci * CUSE channel initialization and destruction 1968c2ecf20Sopenharmony_ci */ 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistruct cuse_devinfo { 1998c2ecf20Sopenharmony_ci const char *name; 2008c2ecf20Sopenharmony_ci}; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci/** 2038c2ecf20Sopenharmony_ci * cuse_parse_one - parse one key=value pair 2048c2ecf20Sopenharmony_ci * @pp: i/o parameter for the current position 2058c2ecf20Sopenharmony_ci * @end: points to one past the end of the packed string 2068c2ecf20Sopenharmony_ci * @keyp: out parameter for key 2078c2ecf20Sopenharmony_ci * @valp: out parameter for value 2088c2ecf20Sopenharmony_ci * 2098c2ecf20Sopenharmony_ci * *@pp points to packed strings - "key0=val0\0key1=val1\0" which ends 2108c2ecf20Sopenharmony_ci * at @end - 1. This function parses one pair and set *@keyp to the 2118c2ecf20Sopenharmony_ci * start of the key and *@valp to the start of the value. Note that 2128c2ecf20Sopenharmony_ci * the original string is modified such that the key string is 2138c2ecf20Sopenharmony_ci * terminated with '\0'. *@pp is updated to point to the next string. 2148c2ecf20Sopenharmony_ci * 2158c2ecf20Sopenharmony_ci * RETURNS: 2168c2ecf20Sopenharmony_ci * 1 on successful parse, 0 on EOF, -errno on failure. 2178c2ecf20Sopenharmony_ci */ 2188c2ecf20Sopenharmony_cistatic int cuse_parse_one(char **pp, char *end, char **keyp, char **valp) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci char *p = *pp; 2218c2ecf20Sopenharmony_ci char *key, *val; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci while (p < end && *p == '\0') 2248c2ecf20Sopenharmony_ci p++; 2258c2ecf20Sopenharmony_ci if (p == end) 2268c2ecf20Sopenharmony_ci return 0; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci if (end[-1] != '\0') { 2298c2ecf20Sopenharmony_ci pr_err("info not properly terminated\n"); 2308c2ecf20Sopenharmony_ci return -EINVAL; 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci key = val = p; 2348c2ecf20Sopenharmony_ci p += strlen(p); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci if (valp) { 2378c2ecf20Sopenharmony_ci strsep(&val, "="); 2388c2ecf20Sopenharmony_ci if (!val) 2398c2ecf20Sopenharmony_ci val = key + strlen(key); 2408c2ecf20Sopenharmony_ci key = strstrip(key); 2418c2ecf20Sopenharmony_ci val = strstrip(val); 2428c2ecf20Sopenharmony_ci } else 2438c2ecf20Sopenharmony_ci key = strstrip(key); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci if (!strlen(key)) { 2468c2ecf20Sopenharmony_ci pr_err("zero length info key specified\n"); 2478c2ecf20Sopenharmony_ci return -EINVAL; 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci *pp = p; 2518c2ecf20Sopenharmony_ci *keyp = key; 2528c2ecf20Sopenharmony_ci if (valp) 2538c2ecf20Sopenharmony_ci *valp = val; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci return 1; 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci/** 2598c2ecf20Sopenharmony_ci * cuse_parse_dev_info - parse device info 2608c2ecf20Sopenharmony_ci * @p: device info string 2618c2ecf20Sopenharmony_ci * @len: length of device info string 2628c2ecf20Sopenharmony_ci * @devinfo: out parameter for parsed device info 2638c2ecf20Sopenharmony_ci * 2648c2ecf20Sopenharmony_ci * Parse @p to extract device info and store it into @devinfo. String 2658c2ecf20Sopenharmony_ci * pointed to by @p is modified by parsing and @devinfo points into 2668c2ecf20Sopenharmony_ci * them, so @p shouldn't be freed while @devinfo is in use. 2678c2ecf20Sopenharmony_ci * 2688c2ecf20Sopenharmony_ci * RETURNS: 2698c2ecf20Sopenharmony_ci * 0 on success, -errno on failure. 2708c2ecf20Sopenharmony_ci */ 2718c2ecf20Sopenharmony_cistatic int cuse_parse_devinfo(char *p, size_t len, struct cuse_devinfo *devinfo) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci char *end = p + len; 2748c2ecf20Sopenharmony_ci char *key, *val; 2758c2ecf20Sopenharmony_ci int rc; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci while (true) { 2788c2ecf20Sopenharmony_ci rc = cuse_parse_one(&p, end, &key, &val); 2798c2ecf20Sopenharmony_ci if (rc < 0) 2808c2ecf20Sopenharmony_ci return rc; 2818c2ecf20Sopenharmony_ci if (!rc) 2828c2ecf20Sopenharmony_ci break; 2838c2ecf20Sopenharmony_ci if (strcmp(key, "DEVNAME") == 0) 2848c2ecf20Sopenharmony_ci devinfo->name = val; 2858c2ecf20Sopenharmony_ci else 2868c2ecf20Sopenharmony_ci pr_warn("unknown device info \"%s\"\n", key); 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci if (!devinfo->name || !strlen(devinfo->name)) { 2908c2ecf20Sopenharmony_ci pr_err("DEVNAME unspecified\n"); 2918c2ecf20Sopenharmony_ci return -EINVAL; 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci return 0; 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistatic void cuse_gendev_release(struct device *dev) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci kfree(dev); 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistruct cuse_init_args { 3038c2ecf20Sopenharmony_ci struct fuse_args_pages ap; 3048c2ecf20Sopenharmony_ci struct cuse_init_in in; 3058c2ecf20Sopenharmony_ci struct cuse_init_out out; 3068c2ecf20Sopenharmony_ci struct page *page; 3078c2ecf20Sopenharmony_ci struct fuse_page_desc desc; 3088c2ecf20Sopenharmony_ci}; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci/** 3118c2ecf20Sopenharmony_ci * cuse_process_init_reply - finish initializing CUSE channel 3128c2ecf20Sopenharmony_ci * 3138c2ecf20Sopenharmony_ci * This function creates the character device and sets up all the 3148c2ecf20Sopenharmony_ci * required data structures for it. Please read the comment at the 3158c2ecf20Sopenharmony_ci * top of this file for high level overview. 3168c2ecf20Sopenharmony_ci */ 3178c2ecf20Sopenharmony_cistatic void cuse_process_init_reply(struct fuse_mount *fm, 3188c2ecf20Sopenharmony_ci struct fuse_args *args, int error) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci struct fuse_conn *fc = fm->fc; 3218c2ecf20Sopenharmony_ci struct cuse_init_args *ia = container_of(args, typeof(*ia), ap.args); 3228c2ecf20Sopenharmony_ci struct fuse_args_pages *ap = &ia->ap; 3238c2ecf20Sopenharmony_ci struct cuse_conn *cc = fc_to_cc(fc), *pos; 3248c2ecf20Sopenharmony_ci struct cuse_init_out *arg = &ia->out; 3258c2ecf20Sopenharmony_ci struct page *page = ap->pages[0]; 3268c2ecf20Sopenharmony_ci struct cuse_devinfo devinfo = { }; 3278c2ecf20Sopenharmony_ci struct device *dev; 3288c2ecf20Sopenharmony_ci struct cdev *cdev; 3298c2ecf20Sopenharmony_ci dev_t devt; 3308c2ecf20Sopenharmony_ci int rc, i; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci if (error || arg->major != FUSE_KERNEL_VERSION || arg->minor < 11) 3338c2ecf20Sopenharmony_ci goto err; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci fc->minor = arg->minor; 3368c2ecf20Sopenharmony_ci fc->max_read = max_t(unsigned, arg->max_read, 4096); 3378c2ecf20Sopenharmony_ci fc->max_write = max_t(unsigned, arg->max_write, 4096); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci /* parse init reply */ 3408c2ecf20Sopenharmony_ci cc->unrestricted_ioctl = arg->flags & CUSE_UNRESTRICTED_IOCTL; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci rc = cuse_parse_devinfo(page_address(page), ap->args.out_args[1].size, 3438c2ecf20Sopenharmony_ci &devinfo); 3448c2ecf20Sopenharmony_ci if (rc) 3458c2ecf20Sopenharmony_ci goto err; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci /* determine and reserve devt */ 3488c2ecf20Sopenharmony_ci devt = MKDEV(arg->dev_major, arg->dev_minor); 3498c2ecf20Sopenharmony_ci if (!MAJOR(devt)) 3508c2ecf20Sopenharmony_ci rc = alloc_chrdev_region(&devt, MINOR(devt), 1, devinfo.name); 3518c2ecf20Sopenharmony_ci else 3528c2ecf20Sopenharmony_ci rc = register_chrdev_region(devt, 1, devinfo.name); 3538c2ecf20Sopenharmony_ci if (rc) { 3548c2ecf20Sopenharmony_ci pr_err("failed to register chrdev region\n"); 3558c2ecf20Sopenharmony_ci goto err; 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci /* devt determined, create device */ 3598c2ecf20Sopenharmony_ci rc = -ENOMEM; 3608c2ecf20Sopenharmony_ci dev = kzalloc(sizeof(*dev), GFP_KERNEL); 3618c2ecf20Sopenharmony_ci if (!dev) 3628c2ecf20Sopenharmony_ci goto err_region; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci device_initialize(dev); 3658c2ecf20Sopenharmony_ci dev_set_uevent_suppress(dev, 1); 3668c2ecf20Sopenharmony_ci dev->class = cuse_class; 3678c2ecf20Sopenharmony_ci dev->devt = devt; 3688c2ecf20Sopenharmony_ci dev->release = cuse_gendev_release; 3698c2ecf20Sopenharmony_ci dev_set_drvdata(dev, cc); 3708c2ecf20Sopenharmony_ci dev_set_name(dev, "%s", devinfo.name); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci mutex_lock(&cuse_lock); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci /* make sure the device-name is unique */ 3758c2ecf20Sopenharmony_ci for (i = 0; i < CUSE_CONNTBL_LEN; ++i) { 3768c2ecf20Sopenharmony_ci list_for_each_entry(pos, &cuse_conntbl[i], list) 3778c2ecf20Sopenharmony_ci if (!strcmp(dev_name(pos->dev), dev_name(dev))) 3788c2ecf20Sopenharmony_ci goto err_unlock; 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci rc = device_add(dev); 3828c2ecf20Sopenharmony_ci if (rc) 3838c2ecf20Sopenharmony_ci goto err_unlock; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci /* register cdev */ 3868c2ecf20Sopenharmony_ci rc = -ENOMEM; 3878c2ecf20Sopenharmony_ci cdev = cdev_alloc(); 3888c2ecf20Sopenharmony_ci if (!cdev) 3898c2ecf20Sopenharmony_ci goto err_unlock; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci cdev->owner = THIS_MODULE; 3928c2ecf20Sopenharmony_ci cdev->ops = &cuse_frontend_fops; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci rc = cdev_add(cdev, devt, 1); 3958c2ecf20Sopenharmony_ci if (rc) 3968c2ecf20Sopenharmony_ci goto err_cdev; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci cc->dev = dev; 3998c2ecf20Sopenharmony_ci cc->cdev = cdev; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci /* make the device available */ 4028c2ecf20Sopenharmony_ci list_add(&cc->list, cuse_conntbl_head(devt)); 4038c2ecf20Sopenharmony_ci mutex_unlock(&cuse_lock); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci /* announce device availability */ 4068c2ecf20Sopenharmony_ci dev_set_uevent_suppress(dev, 0); 4078c2ecf20Sopenharmony_ci kobject_uevent(&dev->kobj, KOBJ_ADD); 4088c2ecf20Sopenharmony_ciout: 4098c2ecf20Sopenharmony_ci kfree(ia); 4108c2ecf20Sopenharmony_ci __free_page(page); 4118c2ecf20Sopenharmony_ci return; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_cierr_cdev: 4148c2ecf20Sopenharmony_ci cdev_del(cdev); 4158c2ecf20Sopenharmony_cierr_unlock: 4168c2ecf20Sopenharmony_ci mutex_unlock(&cuse_lock); 4178c2ecf20Sopenharmony_ci put_device(dev); 4188c2ecf20Sopenharmony_cierr_region: 4198c2ecf20Sopenharmony_ci unregister_chrdev_region(devt, 1); 4208c2ecf20Sopenharmony_cierr: 4218c2ecf20Sopenharmony_ci fuse_abort_conn(fc); 4228c2ecf20Sopenharmony_ci goto out; 4238c2ecf20Sopenharmony_ci} 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_cistatic int cuse_send_init(struct cuse_conn *cc) 4268c2ecf20Sopenharmony_ci{ 4278c2ecf20Sopenharmony_ci int rc; 4288c2ecf20Sopenharmony_ci struct page *page; 4298c2ecf20Sopenharmony_ci struct fuse_mount *fm = &cc->fm; 4308c2ecf20Sopenharmony_ci struct cuse_init_args *ia; 4318c2ecf20Sopenharmony_ci struct fuse_args_pages *ap; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci BUILD_BUG_ON(CUSE_INIT_INFO_MAX > PAGE_SIZE); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci rc = -ENOMEM; 4368c2ecf20Sopenharmony_ci page = alloc_page(GFP_KERNEL | __GFP_ZERO); 4378c2ecf20Sopenharmony_ci if (!page) 4388c2ecf20Sopenharmony_ci goto err; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci ia = kzalloc(sizeof(*ia), GFP_KERNEL); 4418c2ecf20Sopenharmony_ci if (!ia) 4428c2ecf20Sopenharmony_ci goto err_free_page; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci ap = &ia->ap; 4458c2ecf20Sopenharmony_ci ia->in.major = FUSE_KERNEL_VERSION; 4468c2ecf20Sopenharmony_ci ia->in.minor = FUSE_KERNEL_MINOR_VERSION; 4478c2ecf20Sopenharmony_ci ia->in.flags |= CUSE_UNRESTRICTED_IOCTL; 4488c2ecf20Sopenharmony_ci ap->args.opcode = CUSE_INIT; 4498c2ecf20Sopenharmony_ci ap->args.in_numargs = 1; 4508c2ecf20Sopenharmony_ci ap->args.in_args[0].size = sizeof(ia->in); 4518c2ecf20Sopenharmony_ci ap->args.in_args[0].value = &ia->in; 4528c2ecf20Sopenharmony_ci ap->args.out_numargs = 2; 4538c2ecf20Sopenharmony_ci ap->args.out_args[0].size = sizeof(ia->out); 4548c2ecf20Sopenharmony_ci ap->args.out_args[0].value = &ia->out; 4558c2ecf20Sopenharmony_ci ap->args.out_args[1].size = CUSE_INIT_INFO_MAX; 4568c2ecf20Sopenharmony_ci ap->args.out_argvar = true; 4578c2ecf20Sopenharmony_ci ap->args.out_pages = true; 4588c2ecf20Sopenharmony_ci ap->num_pages = 1; 4598c2ecf20Sopenharmony_ci ap->pages = &ia->page; 4608c2ecf20Sopenharmony_ci ap->descs = &ia->desc; 4618c2ecf20Sopenharmony_ci ia->page = page; 4628c2ecf20Sopenharmony_ci ia->desc.length = ap->args.out_args[1].size; 4638c2ecf20Sopenharmony_ci ap->args.end = cuse_process_init_reply; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci rc = fuse_simple_background(fm, &ap->args, GFP_KERNEL); 4668c2ecf20Sopenharmony_ci if (rc) { 4678c2ecf20Sopenharmony_ci kfree(ia); 4688c2ecf20Sopenharmony_cierr_free_page: 4698c2ecf20Sopenharmony_ci __free_page(page); 4708c2ecf20Sopenharmony_ci } 4718c2ecf20Sopenharmony_cierr: 4728c2ecf20Sopenharmony_ci return rc; 4738c2ecf20Sopenharmony_ci} 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_cistatic void cuse_fc_release(struct fuse_conn *fc) 4768c2ecf20Sopenharmony_ci{ 4778c2ecf20Sopenharmony_ci struct cuse_conn *cc = fc_to_cc(fc); 4788c2ecf20Sopenharmony_ci kfree_rcu(cc, fc.rcu); 4798c2ecf20Sopenharmony_ci} 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci/** 4828c2ecf20Sopenharmony_ci * cuse_channel_open - open method for /dev/cuse 4838c2ecf20Sopenharmony_ci * @inode: inode for /dev/cuse 4848c2ecf20Sopenharmony_ci * @file: file struct being opened 4858c2ecf20Sopenharmony_ci * 4868c2ecf20Sopenharmony_ci * Userland CUSE server can create a CUSE device by opening /dev/cuse 4878c2ecf20Sopenharmony_ci * and replying to the initialization request kernel sends. This 4888c2ecf20Sopenharmony_ci * function is responsible for handling CUSE device initialization. 4898c2ecf20Sopenharmony_ci * Because the fd opened by this function is used during 4908c2ecf20Sopenharmony_ci * initialization, this function only creates cuse_conn and sends 4918c2ecf20Sopenharmony_ci * init. The rest is delegated to a kthread. 4928c2ecf20Sopenharmony_ci * 4938c2ecf20Sopenharmony_ci * RETURNS: 4948c2ecf20Sopenharmony_ci * 0 on success, -errno on failure. 4958c2ecf20Sopenharmony_ci */ 4968c2ecf20Sopenharmony_cistatic int cuse_channel_open(struct inode *inode, struct file *file) 4978c2ecf20Sopenharmony_ci{ 4988c2ecf20Sopenharmony_ci struct fuse_dev *fud; 4998c2ecf20Sopenharmony_ci struct cuse_conn *cc; 5008c2ecf20Sopenharmony_ci int rc; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci /* set up cuse_conn */ 5038c2ecf20Sopenharmony_ci cc = kzalloc(sizeof(*cc), GFP_KERNEL); 5048c2ecf20Sopenharmony_ci if (!cc) 5058c2ecf20Sopenharmony_ci return -ENOMEM; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci /* 5088c2ecf20Sopenharmony_ci * Limit the cuse channel to requests that can 5098c2ecf20Sopenharmony_ci * be represented in file->f_cred->user_ns. 5108c2ecf20Sopenharmony_ci */ 5118c2ecf20Sopenharmony_ci fuse_conn_init(&cc->fc, &cc->fm, file->f_cred->user_ns, 5128c2ecf20Sopenharmony_ci &fuse_dev_fiq_ops, NULL); 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci fud = fuse_dev_alloc_install(&cc->fc); 5158c2ecf20Sopenharmony_ci if (!fud) { 5168c2ecf20Sopenharmony_ci kfree(cc); 5178c2ecf20Sopenharmony_ci return -ENOMEM; 5188c2ecf20Sopenharmony_ci } 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&cc->list); 5218c2ecf20Sopenharmony_ci cc->fc.release = cuse_fc_release; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci cc->fc.initialized = 1; 5248c2ecf20Sopenharmony_ci rc = cuse_send_init(cc); 5258c2ecf20Sopenharmony_ci if (rc) { 5268c2ecf20Sopenharmony_ci fuse_dev_free(fud); 5278c2ecf20Sopenharmony_ci fuse_conn_put(&cc->fc); 5288c2ecf20Sopenharmony_ci return rc; 5298c2ecf20Sopenharmony_ci } 5308c2ecf20Sopenharmony_ci file->private_data = fud; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci return 0; 5338c2ecf20Sopenharmony_ci} 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci/** 5368c2ecf20Sopenharmony_ci * cuse_channel_release - release method for /dev/cuse 5378c2ecf20Sopenharmony_ci * @inode: inode for /dev/cuse 5388c2ecf20Sopenharmony_ci * @file: file struct being closed 5398c2ecf20Sopenharmony_ci * 5408c2ecf20Sopenharmony_ci * Disconnect the channel, deregister CUSE device and initiate 5418c2ecf20Sopenharmony_ci * destruction by putting the default reference. 5428c2ecf20Sopenharmony_ci * 5438c2ecf20Sopenharmony_ci * RETURNS: 5448c2ecf20Sopenharmony_ci * 0 on success, -errno on failure. 5458c2ecf20Sopenharmony_ci */ 5468c2ecf20Sopenharmony_cistatic int cuse_channel_release(struct inode *inode, struct file *file) 5478c2ecf20Sopenharmony_ci{ 5488c2ecf20Sopenharmony_ci struct fuse_dev *fud = file->private_data; 5498c2ecf20Sopenharmony_ci struct cuse_conn *cc = fc_to_cc(fud->fc); 5508c2ecf20Sopenharmony_ci int rc; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci /* remove from the conntbl, no more access from this point on */ 5538c2ecf20Sopenharmony_ci mutex_lock(&cuse_lock); 5548c2ecf20Sopenharmony_ci list_del_init(&cc->list); 5558c2ecf20Sopenharmony_ci mutex_unlock(&cuse_lock); 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci /* remove device */ 5588c2ecf20Sopenharmony_ci if (cc->dev) 5598c2ecf20Sopenharmony_ci device_unregister(cc->dev); 5608c2ecf20Sopenharmony_ci if (cc->cdev) { 5618c2ecf20Sopenharmony_ci unregister_chrdev_region(cc->cdev->dev, 1); 5628c2ecf20Sopenharmony_ci cdev_del(cc->cdev); 5638c2ecf20Sopenharmony_ci } 5648c2ecf20Sopenharmony_ci /* Base reference is now owned by "fud" */ 5658c2ecf20Sopenharmony_ci fuse_conn_put(&cc->fc); 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci rc = fuse_dev_release(inode, file); /* puts the base reference */ 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci return rc; 5708c2ecf20Sopenharmony_ci} 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_cistatic struct file_operations cuse_channel_fops; /* initialized during init */ 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci/************************************************************************** 5768c2ecf20Sopenharmony_ci * Misc stuff and module initializatiion 5778c2ecf20Sopenharmony_ci * 5788c2ecf20Sopenharmony_ci * CUSE exports the same set of attributes to sysfs as fusectl. 5798c2ecf20Sopenharmony_ci */ 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_cistatic ssize_t cuse_class_waiting_show(struct device *dev, 5828c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 5838c2ecf20Sopenharmony_ci{ 5848c2ecf20Sopenharmony_ci struct cuse_conn *cc = dev_get_drvdata(dev); 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", atomic_read(&cc->fc.num_waiting)); 5878c2ecf20Sopenharmony_ci} 5888c2ecf20Sopenharmony_cistatic DEVICE_ATTR(waiting, 0400, cuse_class_waiting_show, NULL); 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_cistatic ssize_t cuse_class_abort_store(struct device *dev, 5918c2ecf20Sopenharmony_ci struct device_attribute *attr, 5928c2ecf20Sopenharmony_ci const char *buf, size_t count) 5938c2ecf20Sopenharmony_ci{ 5948c2ecf20Sopenharmony_ci struct cuse_conn *cc = dev_get_drvdata(dev); 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci fuse_abort_conn(&cc->fc); 5978c2ecf20Sopenharmony_ci return count; 5988c2ecf20Sopenharmony_ci} 5998c2ecf20Sopenharmony_cistatic DEVICE_ATTR(abort, 0200, NULL, cuse_class_abort_store); 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_cistatic struct attribute *cuse_class_dev_attrs[] = { 6028c2ecf20Sopenharmony_ci &dev_attr_waiting.attr, 6038c2ecf20Sopenharmony_ci &dev_attr_abort.attr, 6048c2ecf20Sopenharmony_ci NULL, 6058c2ecf20Sopenharmony_ci}; 6068c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(cuse_class_dev); 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_cistatic struct miscdevice cuse_miscdev = { 6098c2ecf20Sopenharmony_ci .minor = CUSE_MINOR, 6108c2ecf20Sopenharmony_ci .name = "cuse", 6118c2ecf20Sopenharmony_ci .fops = &cuse_channel_fops, 6128c2ecf20Sopenharmony_ci}; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ciMODULE_ALIAS_MISCDEV(CUSE_MINOR); 6158c2ecf20Sopenharmony_ciMODULE_ALIAS("devname:cuse"); 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_cistatic int __init cuse_init(void) 6188c2ecf20Sopenharmony_ci{ 6198c2ecf20Sopenharmony_ci int i, rc; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci /* init conntbl */ 6228c2ecf20Sopenharmony_ci for (i = 0; i < CUSE_CONNTBL_LEN; i++) 6238c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&cuse_conntbl[i]); 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci /* inherit and extend fuse_dev_operations */ 6268c2ecf20Sopenharmony_ci cuse_channel_fops = fuse_dev_operations; 6278c2ecf20Sopenharmony_ci cuse_channel_fops.owner = THIS_MODULE; 6288c2ecf20Sopenharmony_ci cuse_channel_fops.open = cuse_channel_open; 6298c2ecf20Sopenharmony_ci cuse_channel_fops.release = cuse_channel_release; 6308c2ecf20Sopenharmony_ci /* CUSE is not prepared for FUSE_DEV_IOC_CLONE */ 6318c2ecf20Sopenharmony_ci cuse_channel_fops.unlocked_ioctl = NULL; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci cuse_class = class_create(THIS_MODULE, "cuse"); 6348c2ecf20Sopenharmony_ci if (IS_ERR(cuse_class)) 6358c2ecf20Sopenharmony_ci return PTR_ERR(cuse_class); 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci cuse_class->dev_groups = cuse_class_dev_groups; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci rc = misc_register(&cuse_miscdev); 6408c2ecf20Sopenharmony_ci if (rc) { 6418c2ecf20Sopenharmony_ci class_destroy(cuse_class); 6428c2ecf20Sopenharmony_ci return rc; 6438c2ecf20Sopenharmony_ci } 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci return 0; 6468c2ecf20Sopenharmony_ci} 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_cistatic void __exit cuse_exit(void) 6498c2ecf20Sopenharmony_ci{ 6508c2ecf20Sopenharmony_ci misc_deregister(&cuse_miscdev); 6518c2ecf20Sopenharmony_ci class_destroy(cuse_class); 6528c2ecf20Sopenharmony_ci} 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_cimodule_init(cuse_init); 6558c2ecf20Sopenharmony_cimodule_exit(cuse_exit); 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ciMODULE_AUTHOR("Tejun Heo <tj@kernel.org>"); 6588c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Character device in Userspace"); 6598c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 660