| 1 | |
| 2 | |
| 3 | * CUSE: Character device in Userspace
|
| 4 | *
|
| 5 | * Copyright (C) 2008-2009 SUSE Linux Products GmbH
|
| 6 | * Copyright (C) 2008-2009 Tejun Heo <[email protected]>
|
| 7 | *
|
| 8 | * CUSE enables character devices to be implemented from userland much
|
| 9 | * like FUSE allows filesystems. On initialization /dev/cuse is
|
| 10 | * created. By opening the file and replying to the CUSE_INIT request
|
| 11 | * userland CUSE server can create a character device. After that the
|
| 12 | * operation is very similar to FUSE.
|
| 13 | *
|
| 14 | * A CUSE instance involves the following objects.
|
| 15 | *
|
| 16 | * cuse_conn : contains fuse_conn and serves as bonding structure
|
| 17 | * channel : file handle connected to the userland CUSE server
|
| 18 | * cdev : the implemented character device
|
| 19 | * dev : generic device for cdev
|
| 20 | *
|
| 21 | * Note that 'channel' is what 'dev' is in FUSE. As CUSE deals with
|
| 22 | * devices, it's called 'channel' to reduce confusion.
|
| 23 | *
|
| 24 | * channel determines when the character device dies. When channel is
|
| 25 | * closed, everything begins to destruct. The cuse_conn is taken off
|
| 26 | * the lookup table preventing further access from cdev, cdev and
|
| 27 | * generic device are removed and the base reference of cuse_conn is
|
| 28 | * put.
|
| 29 | *
|
| 30 | * On each open, the matching cuse_conn is looked up and if found an
|
| 31 | * additional reference is taken which is released when the file is
|
| 32 | * closed.
|
| 33 |
|
| 34 |
|
| 35 | #define pr_fmt(fmt) "CUSE: " fmt
|
| 36 |
|
| 37 | #include <linux/fuse.h>
|
| 38 | #include <linux/cdev.h>
|
| 39 | #include <linux/device.h>
|
| 40 | #include <linux/file.h>
|
| 41 | #include <linux/fs.h>
|
| 42 | #include <linux/kdev_t.h>
|
| 43 | #include <linux/kthread.h>
|
| 44 | #include <linux/list.h>
|
| 45 | #include <linux/magic.h>
|
| 46 | #include <linux/miscdevice.h>
|
| 47 | #include <linux/mutex.h>
|
| 48 | #include <linux/slab.h>
|
| 49 | #include <linux/stat.h>
|
| 50 | #include <linux/module.h>
|
| 51 | #include <linux/uio.h>
|
| 52 | #include <linux/user_namespace.h>
|
| 53 |
|
| 54 | #include "dev.h"
|
| 55 | #include "fuse_i.h"
|
| 56 | #include "fuse_dev_i.h"
|
| 57 |
|
| 58 | #define CUSE_CONNTBL_LEN 64
|
| 59 |
|
| 60 | struct cuse_conn {
|
| 61 | struct list_head list;
|
| 62 | struct fuse_mount fm;
|
| 63 | struct fuse_conn fc;
|
| 64 | struct cdev *cdev;
|
| 65 | struct device *dev;
|
| 66 |
|
| 67 |
|
| 68 | bool unrestricted_ioctl;
|
| 69 | };
|
| 70 |
|
| 71 | static DEFINE_MUTEX(cuse_lock);
|
| 72 | static struct list_head cuse_conntbl[CUSE_CONNTBL_LEN];
|
| 73 | static struct class *cuse_class;
|
| 74 |
|
| 75 | static struct cuse_conn *fc_to_cc(struct fuse_conn *fc)
|
| 76 | {
|
| 77 | return container_of(fc, struct cuse_conn, fc);
|
| 78 | }
|
| 79 |
|
| 80 | static struct list_head *cuse_conntbl_head(dev_t devt)
|
| 81 | {
|
| 82 | return &cuse_conntbl[(MAJOR(devt) + MINOR(devt)) % CUSE_CONNTBL_LEN];
|
| 83 | }
|
| 84 |
|
| 85 |
|
| 86 | |
| 87 | * CUSE frontend operations
|
| 88 | *
|
| 89 | * These are file operations for the character device.
|
| 90 | *
|
| 91 | * On open, CUSE opens a file from the FUSE mnt and stores it to
|
| 92 | * private_data of the open file. All other ops call FUSE ops on the
|
| 93 | * FUSE file.
|
| 94 |
|
| 95 |
|
| 96 | static ssize_t cuse_read_iter(struct kiocb *kiocb, struct iov_iter *to)
|
| 97 | {
|
| 98 | struct fuse_io_priv io = FUSE_IO_PRIV_SYNC(kiocb);
|
| 99 | loff_t pos = 0;
|
| 100 |
|
| 101 | return fuse_direct_io(&io, to, &pos, FUSE_DIO_CUSE);
|
| 102 | }
|
| 103 |
|
| 104 | static ssize_t cuse_write_iter(struct kiocb *kiocb, struct iov_iter *from)
|
| 105 | {
|
| 106 | struct fuse_io_priv io = FUSE_IO_PRIV_SYNC(kiocb);
|
| 107 | loff_t pos = 0;
|
| 108 | |
| 109 | * No locking or generic_write_checks(), the server is
|
| 110 | * responsible for locking and sanity checks.
|
| 111 |
|
| 112 | return fuse_direct_io(&io, from, &pos,
|
| 113 | FUSE_DIO_WRITE | FUSE_DIO_CUSE);
|
| 114 | }
|
| 115 |
|
| 116 | static int cuse_open(struct inode *inode, struct file *file)
|
| 117 | {
|
| 118 | dev_t devt = inode->i_cdev->dev;
|
| 119 | struct cuse_conn *cc = NULL, *pos;
|
| 120 | int rc;
|
| 121 |
|
| 122 |
|
| 123 | mutex_lock(&cuse_lock);
|
| 124 | list_for_each_entry(pos, cuse_conntbl_head(devt), list)
|
| 125 | if (pos->dev->devt == devt) {
|
| 126 | fuse_conn_get(&pos->fc);
|
| 127 | cc = pos;
|
| 128 | break;
|
| 129 | }
|
| 130 | mutex_unlock(&cuse_lock);
|
| 131 |
|
| 132 |
|
| 133 | if (!cc)
|
| 134 | return -ENODEV;
|
| 135 |
|
| 136 | |
| 137 | * Generic permission check is already done against the chrdev
|
| 138 | * file, proceed to open.
|
| 139 |
|
| 140 | rc = fuse_do_open(&cc->fm, 0, file, 0);
|
| 141 | if (rc)
|
| 142 | fuse_conn_put(&cc->fc);
|
| 143 | return rc;
|
| 144 | }
|
| 145 |
|
| 146 | static int cuse_release(struct inode *inode, struct file *file)
|
| 147 | {
|
| 148 | struct fuse_file *ff = file->private_data;
|
| 149 | struct fuse_mount *fm = ff->fm;
|
| 150 |
|
| 151 | fuse_sync_release(NULL, ff, file->f_flags);
|
| 152 | fuse_conn_put(fm->fc);
|
| 153 |
|
| 154 | return 0;
|
| 155 | }
|
| 156 |
|
| 157 | static long cuse_file_ioctl(struct file *file, unsigned int cmd,
|
| 158 | unsigned long arg)
|
| 159 | {
|
| 160 | struct fuse_file *ff = file->private_data;
|
| 161 | struct cuse_conn *cc = fc_to_cc(ff->fm->fc);
|
| 162 | unsigned int flags = 0;
|
| 163 |
|
| 164 | if (cc->unrestricted_ioctl)
|
| 165 | flags |= FUSE_IOCTL_UNRESTRICTED;
|
| 166 |
|
| 167 | return fuse_do_ioctl(file, cmd, arg, flags);
|
| 168 | }
|
| 169 |
|
| 170 | static long cuse_file_compat_ioctl(struct file *file, unsigned int cmd,
|
| 171 | unsigned long arg)
|
| 172 | {
|
| 173 | struct fuse_file *ff = file->private_data;
|
| 174 | struct cuse_conn *cc = fc_to_cc(ff->fm->fc);
|
| 175 | unsigned int flags = FUSE_IOCTL_COMPAT;
|
| 176 |
|
| 177 | if (cc->unrestricted_ioctl)
|
| 178 | flags |= FUSE_IOCTL_UNRESTRICTED;
|
| 179 |
|
| 180 | return fuse_do_ioctl(file, cmd, arg, flags);
|
| 181 | }
|
| 182 |
|
| 183 | static const struct file_operations cuse_frontend_fops = {
|
| 184 | .owner = THIS_MODULE,
|
| 185 | .read_iter = cuse_read_iter,
|
| 186 | .write_iter = cuse_write_iter,
|
| 187 | .open = cuse_open,
|
| 188 | .release = cuse_release,
|
| 189 | .unlocked_ioctl = cuse_file_ioctl,
|
| 190 | .compat_ioctl = cuse_file_compat_ioctl,
|
| 191 | .poll = fuse_file_poll,
|
| 192 | .llseek = noop_llseek,
|
| 193 | };
|
| 194 |
|
| 195 |
|
| 196 | |
| 197 | * CUSE channel initialization and destruction
|
| 198 |
|
| 199 |
|
| 200 | struct cuse_devinfo {
|
| 201 | const char *name;
|
| 202 | };
|
| 203 |
|
| 204 | |
| 205 | * cuse_parse_one - parse one key=value pair
|
| 206 | * @pp: i/o parameter for the current position
|
| 207 | * @end: points to one past the end of the packed string
|
| 208 | * @keyp: out parameter for key
|
| 209 | * @valp: out parameter for value
|
| 210 | *
|
| 211 | * *@pp points to packed strings - "key0=val0\0key1=val1\0" which ends
|
| 212 | * at @end - 1. This function parses one pair and set *@keyp to the
|
| 213 | * start of the key and *@valp to the start of the value. Note that
|
| 214 | * the original string is modified such that the key string is
|
| 215 | * terminated with '\0'. *@pp is updated to point to the next string.
|
| 216 | *
|
| 217 | * RETURNS:
|
| 218 | * 1 on successful parse, 0 on EOF, -errno on failure.
|
| 219 |
|
| 220 | static int cuse_parse_one(char **pp, char *end, char **keyp, char **valp)
|
| 221 | {
|
| 222 | char *p = *pp;
|
| 223 | char *key, *val;
|
| 224 |
|
| 225 | while (p < end && *p == '\0')
|
| 226 | p++;
|
| 227 | if (p == end)
|
| 228 | return 0;
|
| 229 |
|
| 230 | if (end[-1] != '\0') {
|
| 231 | pr_err("info not properly terminated\n");
|
| 232 | return -EINVAL;
|
| 233 | }
|
| 234 |
|
| 235 | key = val = p;
|
| 236 | p += strlen(p);
|
| 237 |
|
| 238 | if (valp) {
|
| 239 | strsep(&val, "=");
|
| 240 | if (!val)
|
| 241 | val = key + strlen(key);
|
| 242 | key = strstrip(key);
|
| 243 | val = strstrip(val);
|
| 244 | } else
|
| 245 | key = strstrip(key);
|
| 246 |
|
| 247 | if (!strlen(key)) {
|
| 248 | pr_err("zero length info key specified\n");
|
| 249 | return -EINVAL;
|
| 250 | }
|
| 251 |
|
| 252 | *pp = p;
|
| 253 | *keyp = key;
|
| 254 | if (valp)
|
| 255 | *valp = val;
|
| 256 |
|
| 257 | return 1;
|
| 258 | }
|
| 259 |
|
| 260 | |
| 261 | * cuse_parse_devinfo - parse device info
|
| 262 | * @p: device info string
|
| 263 | * @len: length of device info string
|
| 264 | * @devinfo: out parameter for parsed device info
|
| 265 | *
|
| 266 | * Parse @p to extract device info and store it into @devinfo. String
|
| 267 | * pointed to by @p is modified by parsing and @devinfo points into
|
| 268 | * them, so @p shouldn't be freed while @devinfo is in use.
|
| 269 | *
|
| 270 | * RETURNS:
|
| 271 | * 0 on success, -errno on failure.
|
| 272 |
|
| 273 | static int cuse_parse_devinfo(char *p, size_t len, struct cuse_devinfo *devinfo)
|
| 274 | {
|
| 275 | char *end = p + len;
|
| 276 | char *key, *val;
|
| 277 | int rc;
|
| 278 |
|
| 279 | while (true) {
|
| 280 | rc = cuse_parse_one(&p, end, &key, &val);
|
| 281 | if (rc < 0)
|
| 282 | return rc;
|
| 283 | if (!rc)
|
| 284 | break;
|
| 285 | if (strcmp(key, "DEVNAME") == 0)
|
| 286 | devinfo->name = val;
|
| 287 | else
|
| 288 | pr_warn("unknown device info \"%s\"\n", key);
|
| 289 | }
|
| 290 |
|
| 291 | if (!devinfo->name || !strlen(devinfo->name)) {
|
| 292 | pr_err("DEVNAME unspecified\n");
|
| 293 | return -EINVAL;
|
| 294 | }
|
| 295 |
|
| 296 | return 0;
|
| 297 | }
|
| 298 |
|
| 299 | static void cuse_gendev_release(struct device *dev)
|
| 300 | {
|
| 301 | kfree(dev);
|
| 302 | }
|
| 303 |
|
| 304 | struct cuse_init_args {
|
| 305 | struct fuse_args_pages ap;
|
| 306 | struct cuse_init_in in;
|
| 307 | struct cuse_init_out out;
|
| 308 | struct folio *folio;
|
| 309 | struct fuse_folio_desc desc;
|
| 310 | struct fuse_conn *fc;
|
| 311 | };
|
| 312 |
|
| 313 | |
| 314 | * cuse_process_init_reply - finish initializing CUSE channel
|
| 315 | *
|
| 316 | * @fm: The fuse mount information containing the CUSE connection.
|
| 317 | * @args: The arguments passed to the init reply.
|
| 318 | * @error: The error code signifying if any error occurred during the process.
|
| 319 | *
|
| 320 | * This function creates the character device and sets up all the
|
| 321 | * required data structures for it. Please read the comment at the
|
| 322 | * top of this file for high level overview.
|
| 323 |
|
| 324 | static void cuse_process_init_reply(struct fuse_args *args, int error)
|
| 325 | {
|
| 326 | struct cuse_init_args *ia = container_of(args, typeof(*ia), ap.args);
|
| 327 | struct fuse_conn *fc = ia->fc;
|
| 328 | struct fuse_args_pages *ap = &ia->ap;
|
| 329 | struct cuse_conn *cc = fc_to_cc(fc), *pos;
|
| 330 | struct cuse_init_out *arg = &ia->out;
|
| 331 | struct folio *folio = ap->folios[0];
|
| 332 | struct cuse_devinfo devinfo = { };
|
| 333 | struct device *dev;
|
| 334 | struct cdev *cdev;
|
| 335 | dev_t devt;
|
| 336 | int rc, i;
|
| 337 |
|
| 338 | if (error || arg->major != FUSE_KERNEL_VERSION || arg->minor < 11)
|
| 339 | goto err;
|
| 340 |
|
| 341 | fc->minor = arg->minor;
|
| 342 | fc->max_read = max_t(unsigned, arg->max_read, 4096);
|
| 343 | fc->max_write = max_t(unsigned, arg->max_write, 4096);
|
| 344 |
|
| 345 |
|
| 346 | cc->unrestricted_ioctl = arg->flags & CUSE_UNRESTRICTED_IOCTL;
|
| 347 |
|
| 348 | rc = cuse_parse_devinfo(folio_address(folio), ap->args.out_args[1].size,
|
| 349 | &devinfo);
|
| 350 | if (rc)
|
| 351 | goto err;
|
| 352 |
|
| 353 |
|
| 354 | devt = MKDEV(arg->dev_major, arg->dev_minor);
|
| 355 | if (!MAJOR(devt))
|
| 356 | rc = alloc_chrdev_region(&devt, MINOR(devt), 1, devinfo.name);
|
| 357 | else
|
| 358 | rc = register_chrdev_region(devt, 1, devinfo.name);
|
| 359 | if (rc) {
|
| 360 | pr_err("failed to register chrdev region\n");
|
| 361 | goto err;
|
| 362 | }
|
| 363 |
|
| 364 |
|
| 365 | rc = -ENOMEM;
|
| 366 | dev = kzalloc_obj(*dev);
|
| 367 | if (!dev)
|
| 368 | goto err_region;
|
| 369 |
|
| 370 | device_initialize(dev);
|
| 371 | dev_set_uevent_suppress(dev, 1);
|
| 372 | dev->class = cuse_class;
|
| 373 | dev->devt = devt;
|
| 374 | dev->release = cuse_gendev_release;
|
| 375 | dev_set_drvdata(dev, cc);
|
| 376 | dev_set_name(dev, "%s", devinfo.name);
|
| 377 |
|
| 378 | mutex_lock(&cuse_lock);
|
| 379 |
|
| 380 |
|
| 381 | for (i = 0; i < CUSE_CONNTBL_LEN; ++i) {
|
| 382 | list_for_each_entry(pos, &cuse_conntbl[i], list)
|
| 383 | if (!strcmp(dev_name(pos->dev), dev_name(dev)))
|
| 384 | goto err_unlock;
|
| 385 | }
|
| 386 |
|
| 387 | rc = device_add(dev);
|
| 388 | if (rc)
|
| 389 | goto err_unlock;
|
| 390 |
|
| 391 |
|
| 392 | rc = -ENOMEM;
|
| 393 | cdev = cdev_alloc();
|
| 394 | if (!cdev)
|
| 395 | goto err_dev;
|
| 396 |
|
| 397 | cdev->owner = THIS_MODULE;
|
| 398 | cdev->ops = &cuse_frontend_fops;
|
| 399 |
|
| 400 | rc = cdev_add(cdev, devt, 1);
|
| 401 | if (rc)
|
| 402 | goto err_cdev;
|
| 403 |
|
| 404 | cc->dev = dev;
|
| 405 | cc->cdev = cdev;
|
| 406 |
|
| 407 |
|
| 408 | list_add(&cc->list, cuse_conntbl_head(devt));
|
| 409 | mutex_unlock(&cuse_lock);
|
| 410 |
|
| 411 |
|
| 412 | dev_set_uevent_suppress(dev, 0);
|
| 413 | kobject_uevent(&dev->kobj, KOBJ_ADD);
|
| 414 | out:
|
| 415 | kfree(ia);
|
| 416 | folio_put(folio);
|
| 417 | return;
|
| 418 |
|
| 419 | err_cdev:
|
| 420 | cdev_del(cdev);
|
| 421 | err_dev:
|
| 422 | device_del(dev);
|
| 423 | err_unlock:
|
| 424 | mutex_unlock(&cuse_lock);
|
| 425 | put_device(dev);
|
| 426 | err_region:
|
| 427 | unregister_chrdev_region(devt, 1);
|
| 428 | err:
|
| 429 | fuse_chan_abort(fc->chan, false);
|
| 430 | goto out;
|
| 431 | }
|
| 432 |
|
| 433 | static int cuse_send_init(struct cuse_conn *cc)
|
| 434 | {
|
| 435 | int rc;
|
| 436 | struct folio *folio;
|
| 437 | struct fuse_mount *fm = &cc->fm;
|
| 438 | struct cuse_init_args *ia;
|
| 439 | struct fuse_args_pages *ap;
|
| 440 |
|
| 441 | BUILD_BUG_ON(CUSE_INIT_INFO_MAX > PAGE_SIZE);
|
| 442 |
|
| 443 | rc = -ENOMEM;
|
| 444 |
|
| 445 | folio = folio_alloc(GFP_KERNEL | __GFP_ZERO, 0);
|
| 446 | if (!folio)
|
| 447 | goto err;
|
| 448 |
|
| 449 | ia = kzalloc_obj(*ia);
|
| 450 | if (!ia)
|
| 451 | goto err_free_folio;
|
| 452 |
|
| 453 | ap = &ia->ap;
|
| 454 | ia->in.major = FUSE_KERNEL_VERSION;
|
| 455 | ia->in.minor = FUSE_KERNEL_MINOR_VERSION;
|
| 456 | ia->in.flags |= CUSE_UNRESTRICTED_IOCTL;
|
| 457 | ap->args.opcode = CUSE_INIT;
|
| 458 | ap->args.in_numargs = 1;
|
| 459 | ap->args.in_args[0].size = sizeof(ia->in);
|
| 460 | ap->args.in_args[0].value = &ia->in;
|
| 461 | ap->args.out_numargs = 2;
|
| 462 | ap->args.out_args[0].size = sizeof(ia->out);
|
| 463 | ap->args.out_args[0].value = &ia->out;
|
| 464 | ap->args.out_args[1].size = CUSE_INIT_INFO_MAX;
|
| 465 | ap->args.out_argvar = true;
|
| 466 | ap->args.out_pages = true;
|
| 467 | ap->num_folios = 1;
|
| 468 | ap->folios = &ia->folio;
|
| 469 | ap->descs = &ia->desc;
|
| 470 | ia->folio = folio;
|
| 471 | ia->desc.length = ap->args.out_args[1].size;
|
| 472 | ia->fc = &cc->fc;
|
| 473 | ap->args.end = cuse_process_init_reply;
|
| 474 |
|
| 475 | rc = fuse_simple_background(fm, &ap->args, GFP_KERNEL);
|
| 476 | if (rc) {
|
| 477 | kfree(ia);
|
| 478 | err_free_folio:
|
| 479 | folio_put(folio);
|
| 480 | }
|
| 481 | err:
|
| 482 | return rc;
|
| 483 | }
|
| 484 |
|
| 485 | static void cuse_fc_release(struct fuse_conn *fc)
|
| 486 | {
|
| 487 | kfree(fc_to_cc(fc));
|
| 488 | }
|
| 489 |
|
| 490 | |
| 491 | * cuse_channel_open - open method for /dev/cuse
|
| 492 | * @inode: inode for /dev/cuse
|
| 493 | * @file: file struct being opened
|
| 494 | *
|
| 495 | * Userland CUSE server can create a CUSE device by opening /dev/cuse
|
| 496 | * and replying to the initialization request kernel sends. This
|
| 497 | * function is responsible for handling CUSE device initialization.
|
| 498 | * Because the fd opened by this function is used during
|
| 499 | * initialization, this function only creates cuse_conn and sends
|
| 500 | * init. The rest is delegated to a kthread.
|
| 501 | *
|
| 502 | * RETURNS:
|
| 503 | * 0 on success, -errno on failure.
|
| 504 |
|
| 505 | static int cuse_channel_open(struct inode *inode, struct file *file)
|
| 506 | {
|
| 507 | struct fuse_dev *fud;
|
| 508 | struct cuse_conn *cc;
|
| 509 | struct fuse_chan *fch __free(fuse_chan_free) = fuse_dev_chan_new();
|
| 510 | int rc;
|
| 511 |
|
| 512 | if (!fch)
|
| 513 | return -ENOMEM;
|
| 514 |
|
| 515 |
|
| 516 | cc = kzalloc_obj(*cc);
|
| 517 | if (!cc)
|
| 518 | return -ENOMEM;
|
| 519 |
|
| 520 | |
| 521 | * Limit the cuse channel to requests that can
|
| 522 | * be represented in file->f_cred->user_ns.
|
| 523 |
|
| 524 | fuse_conn_init(&cc->fc, &cc->fm, file->f_cred->user_ns, no_free_ptr(fch));
|
| 525 | cc->fc.release = cuse_fc_release;
|
| 526 | fud = fuse_dev_alloc_install(cc->fc.chan);
|
| 527 | fuse_conn_put(&cc->fc);
|
| 528 | if (!fud)
|
| 529 | return -ENOMEM;
|
| 530 |
|
| 531 | INIT_LIST_HEAD(&cc->list);
|
| 532 |
|
| 533 |
|
| 534 | smp_store_release(&cc->fc.chan->initialized, 1);
|
| 535 | rc = cuse_send_init(cc);
|
| 536 | if (rc) {
|
| 537 | fuse_dev_put(fud);
|
| 538 | return rc;
|
| 539 | }
|
| 540 | file->private_data = fud;
|
| 541 |
|
| 542 | return 0;
|
| 543 | }
|
| 544 |
|
| 545 | |
| 546 | * cuse_channel_release - release method for /dev/cuse
|
| 547 | * @inode: inode for /dev/cuse
|
| 548 | * @file: file struct being closed
|
| 549 | *
|
| 550 | * Disconnect the channel, deregister CUSE device and initiate
|
| 551 | * destruction by putting the default reference.
|
| 552 | *
|
| 553 | * RETURNS:
|
| 554 | * 0 on success, -errno on failure.
|
| 555 |
|
| 556 | static int cuse_channel_release(struct inode *inode, struct file *file)
|
| 557 | {
|
| 558 | struct fuse_dev *fud = __fuse_get_dev(file);
|
| 559 | struct cuse_conn *cc = fc_to_cc(fud->chan->conn);
|
| 560 |
|
| 561 |
|
| 562 | mutex_lock(&cuse_lock);
|
| 563 | list_del_init(&cc->list);
|
| 564 | mutex_unlock(&cuse_lock);
|
| 565 |
|
| 566 |
|
| 567 | if (cc->dev)
|
| 568 | device_unregister(cc->dev);
|
| 569 | if (cc->cdev) {
|
| 570 | unregister_chrdev_region(cc->cdev->dev, 1);
|
| 571 | cdev_del(cc->cdev);
|
| 572 | }
|
| 573 |
|
| 574 | return fuse_dev_release(inode, file);
|
| 575 | }
|
| 576 |
|
| 577 | static struct file_operations cuse_channel_fops;
|
| 578 |
|
| 579 |
|
| 580 | |
| 581 | * Misc stuff and module initializatiion
|
| 582 | *
|
| 583 | * CUSE exports the same set of attributes to sysfs as fusectl.
|
| 584 |
|
| 585 |
|
| 586 | static ssize_t cuse_class_waiting_show(struct device *dev,
|
| 587 | struct device_attribute *attr, char *buf)
|
| 588 | {
|
| 589 | struct cuse_conn *cc = dev_get_drvdata(dev);
|
| 590 |
|
| 591 | return sprintf(buf, "%d\n", atomic_read(&cc->fc.chan->num_waiting));
|
| 592 | }
|
| 593 | static DEVICE_ATTR(waiting, 0400, cuse_class_waiting_show, NULL);
|
| 594 |
|
| 595 | static ssize_t cuse_class_abort_store(struct device *dev,
|
| 596 | struct device_attribute *attr,
|
| 597 | const char *buf, size_t count)
|
| 598 | {
|
| 599 | struct cuse_conn *cc = dev_get_drvdata(dev);
|
| 600 |
|
| 601 | fuse_chan_abort(cc->fc.chan, false);
|
| 602 | return count;
|
| 603 | }
|
| 604 | static DEVICE_ATTR(abort, 0200, NULL, cuse_class_abort_store);
|
| 605 |
|
| 606 | static struct attribute *cuse_class_dev_attrs[] = {
|
| 607 | &dev_attr_waiting.attr,
|
| 608 | &dev_attr_abort.attr,
|
| 609 | NULL,
|
| 610 | };
|
| 611 | ATTRIBUTE_GROUPS(cuse_class_dev);
|
| 612 |
|
| 613 | static struct miscdevice cuse_miscdev = {
|
| 614 | .minor = CUSE_MINOR,
|
| 615 | .name = "cuse",
|
| 616 | .fops = &cuse_channel_fops,
|
| 617 | };
|
| 618 |
|
| 619 | MODULE_ALIAS_MISCDEV(CUSE_MINOR);
|
| 620 | MODULE_ALIAS("devname:cuse");
|
| 621 |
|
| 622 | static int __init cuse_init(void)
|
| 623 | {
|
| 624 | int i, rc;
|
| 625 |
|
| 626 |
|
| 627 | for (i = 0; i < CUSE_CONNTBL_LEN; i++)
|
| 628 | INIT_LIST_HEAD(&cuse_conntbl[i]);
|
| 629 |
|
| 630 |
|
| 631 | cuse_channel_fops = fuse_dev_operations;
|
| 632 | cuse_channel_fops.owner = THIS_MODULE;
|
| 633 | cuse_channel_fops.open = cuse_channel_open;
|
| 634 | cuse_channel_fops.release = cuse_channel_release;
|
| 635 |
|
| 636 | cuse_channel_fops.unlocked_ioctl = NULL;
|
| 637 |
|
| 638 | cuse_class = class_create("cuse");
|
| 639 | if (IS_ERR(cuse_class))
|
| 640 | return PTR_ERR(cuse_class);
|
| 641 |
|
| 642 | cuse_class->dev_groups = cuse_class_dev_groups;
|
| 643 |
|
| 644 | rc = misc_register(&cuse_miscdev);
|
| 645 | if (rc) {
|
| 646 | class_destroy(cuse_class);
|
| 647 | return rc;
|
| 648 | }
|
| 649 |
|
| 650 | return 0;
|
| 651 | }
|
| 652 |
|
| 653 | static void __exit cuse_exit(void)
|
| 654 | {
|
| 655 | misc_deregister(&cuse_miscdev);
|
| 656 | class_destroy(cuse_class);
|
| 657 | |
| 658 | * Wait for pending call_rcu() callbacks that call back into
|
| 659 | * this module via fc->release (cuse_fc_release).
|
| 660 |
|
| 661 | rcu_barrier();
|
| 662 | }
|
| 663 |
|
| 664 | module_init(cuse_init);
|
| 665 | module_exit(cuse_exit);
|
| 666 |
|
| 667 | MODULE_AUTHOR("Tejun Heo <[email protected]>");
|
| 668 | MODULE_DESCRIPTION("Character device in Userspace");
|
| 669 | MODULE_LICENSE("GPL");
|