diff options
author | Miklos Szeredi <miklos@szeredi.hu> | 2006-06-25 08:48:51 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-06-25 13:01:19 -0400 |
commit | bafa96541b250a7051e3fbc5de6e8369daf8ffec (patch) | |
tree | 9b758c424fcda2d263c71f25358bb65a0abc15d4 /fs/fuse/inode.c | |
parent | 51eb01e73599efb88c6c20b1c226d20309a75450 (diff) |
[PATCH] fuse: add control filesystem
Add a control filesystem to fuse, replacing the attributes currently exported
through sysfs. An empty directory '/sys/fs/fuse/connections' is still created
in sysfs, and mounting the control filesystem here provides backward
compatibility.
Advantages of the control filesystem over the previous solution:
- allows the object directory and the attributes to be owned by the
filesystem owner, hence letting unpriviled users abort the
filesystem connection
- does not suffer from module unload race
[akpm@osdl.org: fix this fs for recent dhowells depredations]
[akpm@osdl.org: fix 64-bit printk warnings]
Signed-off-by: Miklos Szeredi <miklos@szeredi.hu>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'fs/fuse/inode.c')
-rw-r--r-- | fs/fuse/inode.c | 141 |
1 files changed, 44 insertions, 97 deletions
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 0225729977c4..13a7e8ab7a78 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c | |||
@@ -22,13 +22,8 @@ MODULE_DESCRIPTION("Filesystem in Userspace"); | |||
22 | MODULE_LICENSE("GPL"); | 22 | MODULE_LICENSE("GPL"); |
23 | 23 | ||
24 | static kmem_cache_t *fuse_inode_cachep; | 24 | static kmem_cache_t *fuse_inode_cachep; |
25 | static struct subsystem connections_subsys; | 25 | struct list_head fuse_conn_list; |
26 | 26 | DEFINE_MUTEX(fuse_mutex); | |
27 | struct fuse_conn_attr { | ||
28 | struct attribute attr; | ||
29 | ssize_t (*show)(struct fuse_conn *, char *); | ||
30 | ssize_t (*store)(struct fuse_conn *, const char *, size_t); | ||
31 | }; | ||
32 | 27 | ||
33 | #define FUSE_SUPER_MAGIC 0x65735546 | 28 | #define FUSE_SUPER_MAGIC 0x65735546 |
34 | 29 | ||
@@ -211,8 +206,11 @@ static void fuse_put_super(struct super_block *sb) | |||
211 | kill_fasync(&fc->fasync, SIGIO, POLL_IN); | 206 | kill_fasync(&fc->fasync, SIGIO, POLL_IN); |
212 | wake_up_all(&fc->waitq); | 207 | wake_up_all(&fc->waitq); |
213 | wake_up_all(&fc->blocked_waitq); | 208 | wake_up_all(&fc->blocked_waitq); |
214 | kobject_del(&fc->kobj); | 209 | mutex_lock(&fuse_mutex); |
215 | kobject_put(&fc->kobj); | 210 | list_del(&fc->entry); |
211 | fuse_ctl_remove_conn(fc); | ||
212 | mutex_unlock(&fuse_mutex); | ||
213 | fuse_conn_put(fc); | ||
216 | } | 214 | } |
217 | 215 | ||
218 | static void convert_fuse_statfs(struct kstatfs *stbuf, struct fuse_kstatfs *attr) | 216 | static void convert_fuse_statfs(struct kstatfs *stbuf, struct fuse_kstatfs *attr) |
@@ -362,11 +360,6 @@ static int fuse_show_options(struct seq_file *m, struct vfsmount *mnt) | |||
362 | return 0; | 360 | return 0; |
363 | } | 361 | } |
364 | 362 | ||
365 | static void fuse_conn_release(struct kobject *kobj) | ||
366 | { | ||
367 | kfree(get_fuse_conn_kobj(kobj)); | ||
368 | } | ||
369 | |||
370 | static struct fuse_conn *new_conn(void) | 363 | static struct fuse_conn *new_conn(void) |
371 | { | 364 | { |
372 | struct fuse_conn *fc; | 365 | struct fuse_conn *fc; |
@@ -374,13 +367,12 @@ static struct fuse_conn *new_conn(void) | |||
374 | fc = kzalloc(sizeof(*fc), GFP_KERNEL); | 367 | fc = kzalloc(sizeof(*fc), GFP_KERNEL); |
375 | if (fc) { | 368 | if (fc) { |
376 | spin_lock_init(&fc->lock); | 369 | spin_lock_init(&fc->lock); |
370 | atomic_set(&fc->count, 1); | ||
377 | init_waitqueue_head(&fc->waitq); | 371 | init_waitqueue_head(&fc->waitq); |
378 | init_waitqueue_head(&fc->blocked_waitq); | 372 | init_waitqueue_head(&fc->blocked_waitq); |
379 | INIT_LIST_HEAD(&fc->pending); | 373 | INIT_LIST_HEAD(&fc->pending); |
380 | INIT_LIST_HEAD(&fc->processing); | 374 | INIT_LIST_HEAD(&fc->processing); |
381 | INIT_LIST_HEAD(&fc->io); | 375 | INIT_LIST_HEAD(&fc->io); |
382 | kobj_set_kset_s(fc, connections_subsys); | ||
383 | kobject_init(&fc->kobj); | ||
384 | atomic_set(&fc->num_waiting, 0); | 376 | atomic_set(&fc->num_waiting, 0); |
385 | fc->bdi.ra_pages = (VM_MAX_READAHEAD * 1024) / PAGE_CACHE_SIZE; | 377 | fc->bdi.ra_pages = (VM_MAX_READAHEAD * 1024) / PAGE_CACHE_SIZE; |
386 | fc->bdi.unplug_io_fn = default_unplug_io_fn; | 378 | fc->bdi.unplug_io_fn = default_unplug_io_fn; |
@@ -390,6 +382,18 @@ static struct fuse_conn *new_conn(void) | |||
390 | return fc; | 382 | return fc; |
391 | } | 383 | } |
392 | 384 | ||
385 | void fuse_conn_put(struct fuse_conn *fc) | ||
386 | { | ||
387 | if (atomic_dec_and_test(&fc->count)) | ||
388 | kfree(fc); | ||
389 | } | ||
390 | |||
391 | struct fuse_conn *fuse_conn_get(struct fuse_conn *fc) | ||
392 | { | ||
393 | atomic_inc(&fc->count); | ||
394 | return fc; | ||
395 | } | ||
396 | |||
393 | static struct inode *get_root_inode(struct super_block *sb, unsigned mode) | 397 | static struct inode *get_root_inode(struct super_block *sb, unsigned mode) |
394 | { | 398 | { |
395 | struct fuse_attr attr; | 399 | struct fuse_attr attr; |
@@ -459,10 +463,9 @@ static void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req) | |||
459 | request_send_background(fc, req); | 463 | request_send_background(fc, req); |
460 | } | 464 | } |
461 | 465 | ||
462 | static unsigned long long conn_id(void) | 466 | static u64 conn_id(void) |
463 | { | 467 | { |
464 | /* BKL is held for ->get_sb() */ | 468 | static u64 ctr = 1; |
465 | static unsigned long long ctr = 1; | ||
466 | return ctr++; | 469 | return ctr++; |
467 | } | 470 | } |
468 | 471 | ||
@@ -519,24 +522,21 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) | |||
519 | if (!init_req) | 522 | if (!init_req) |
520 | goto err_put_root; | 523 | goto err_put_root; |
521 | 524 | ||
522 | err = kobject_set_name(&fc->kobj, "%llu", conn_id()); | 525 | mutex_lock(&fuse_mutex); |
523 | if (err) | ||
524 | goto err_free_req; | ||
525 | |||
526 | err = kobject_add(&fc->kobj); | ||
527 | if (err) | ||
528 | goto err_free_req; | ||
529 | |||
530 | /* Setting file->private_data can't race with other mount() | ||
531 | instances, since BKL is held for ->get_sb() */ | ||
532 | err = -EINVAL; | 526 | err = -EINVAL; |
533 | if (file->private_data) | 527 | if (file->private_data) |
534 | goto err_kobject_del; | 528 | goto err_unlock; |
535 | 529 | ||
530 | fc->id = conn_id(); | ||
531 | err = fuse_ctl_add_conn(fc); | ||
532 | if (err) | ||
533 | goto err_unlock; | ||
534 | |||
535 | list_add_tail(&fc->entry, &fuse_conn_list); | ||
536 | sb->s_root = root_dentry; | 536 | sb->s_root = root_dentry; |
537 | fc->connected = 1; | 537 | fc->connected = 1; |
538 | kobject_get(&fc->kobj); | 538 | file->private_data = fuse_conn_get(fc); |
539 | file->private_data = fc; | 539 | mutex_unlock(&fuse_mutex); |
540 | /* | 540 | /* |
541 | * atomic_dec_and_test() in fput() provides the necessary | 541 | * atomic_dec_and_test() in fput() provides the necessary |
542 | * memory barrier for file->private_data to be visible on all | 542 | * memory barrier for file->private_data to be visible on all |
@@ -548,15 +548,14 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) | |||
548 | 548 | ||
549 | return 0; | 549 | return 0; |
550 | 550 | ||
551 | err_kobject_del: | 551 | err_unlock: |
552 | kobject_del(&fc->kobj); | 552 | mutex_unlock(&fuse_mutex); |
553 | err_free_req: | ||
554 | fuse_request_free(init_req); | 553 | fuse_request_free(init_req); |
555 | err_put_root: | 554 | err_put_root: |
556 | dput(root_dentry); | 555 | dput(root_dentry); |
557 | err: | 556 | err: |
558 | fput(file); | 557 | fput(file); |
559 | kobject_put(&fc->kobj); | 558 | fuse_conn_put(fc); |
560 | return err; | 559 | return err; |
561 | } | 560 | } |
562 | 561 | ||
@@ -574,68 +573,8 @@ static struct file_system_type fuse_fs_type = { | |||
574 | .kill_sb = kill_anon_super, | 573 | .kill_sb = kill_anon_super, |
575 | }; | 574 | }; |
576 | 575 | ||
577 | static ssize_t fuse_conn_waiting_show(struct fuse_conn *fc, char *page) | ||
578 | { | ||
579 | return sprintf(page, "%i\n", atomic_read(&fc->num_waiting)); | ||
580 | } | ||
581 | |||
582 | static ssize_t fuse_conn_abort_store(struct fuse_conn *fc, const char *page, | ||
583 | size_t count) | ||
584 | { | ||
585 | fuse_abort_conn(fc); | ||
586 | return count; | ||
587 | } | ||
588 | |||
589 | static struct fuse_conn_attr fuse_conn_waiting = | ||
590 | __ATTR(waiting, 0400, fuse_conn_waiting_show, NULL); | ||
591 | static struct fuse_conn_attr fuse_conn_abort = | ||
592 | __ATTR(abort, 0600, NULL, fuse_conn_abort_store); | ||
593 | |||
594 | static struct attribute *fuse_conn_attrs[] = { | ||
595 | &fuse_conn_waiting.attr, | ||
596 | &fuse_conn_abort.attr, | ||
597 | NULL, | ||
598 | }; | ||
599 | |||
600 | static ssize_t fuse_conn_attr_show(struct kobject *kobj, | ||
601 | struct attribute *attr, | ||
602 | char *page) | ||
603 | { | ||
604 | struct fuse_conn_attr *fca = | ||
605 | container_of(attr, struct fuse_conn_attr, attr); | ||
606 | |||
607 | if (fca->show) | ||
608 | return fca->show(get_fuse_conn_kobj(kobj), page); | ||
609 | else | ||
610 | return -EACCES; | ||
611 | } | ||
612 | |||
613 | static ssize_t fuse_conn_attr_store(struct kobject *kobj, | ||
614 | struct attribute *attr, | ||
615 | const char *page, size_t count) | ||
616 | { | ||
617 | struct fuse_conn_attr *fca = | ||
618 | container_of(attr, struct fuse_conn_attr, attr); | ||
619 | |||
620 | if (fca->store) | ||
621 | return fca->store(get_fuse_conn_kobj(kobj), page, count); | ||
622 | else | ||
623 | return -EACCES; | ||
624 | } | ||
625 | |||
626 | static struct sysfs_ops fuse_conn_sysfs_ops = { | ||
627 | .show = &fuse_conn_attr_show, | ||
628 | .store = &fuse_conn_attr_store, | ||
629 | }; | ||
630 | |||
631 | static struct kobj_type ktype_fuse_conn = { | ||
632 | .release = fuse_conn_release, | ||
633 | .sysfs_ops = &fuse_conn_sysfs_ops, | ||
634 | .default_attrs = fuse_conn_attrs, | ||
635 | }; | ||
636 | |||
637 | static decl_subsys(fuse, NULL, NULL); | 576 | static decl_subsys(fuse, NULL, NULL); |
638 | static decl_subsys(connections, &ktype_fuse_conn, NULL); | 577 | static decl_subsys(connections, NULL, NULL); |
639 | 578 | ||
640 | static void fuse_inode_init_once(void *foo, kmem_cache_t *cachep, | 579 | static void fuse_inode_init_once(void *foo, kmem_cache_t *cachep, |
641 | unsigned long flags) | 580 | unsigned long flags) |
@@ -709,6 +648,7 @@ static int __init fuse_init(void) | |||
709 | printk("fuse init (API version %i.%i)\n", | 648 | printk("fuse init (API version %i.%i)\n", |
710 | FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION); | 649 | FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION); |
711 | 650 | ||
651 | INIT_LIST_HEAD(&fuse_conn_list); | ||
712 | res = fuse_fs_init(); | 652 | res = fuse_fs_init(); |
713 | if (res) | 653 | if (res) |
714 | goto err; | 654 | goto err; |
@@ -721,8 +661,14 @@ static int __init fuse_init(void) | |||
721 | if (res) | 661 | if (res) |
722 | goto err_dev_cleanup; | 662 | goto err_dev_cleanup; |
723 | 663 | ||
664 | res = fuse_ctl_init(); | ||
665 | if (res) | ||
666 | goto err_sysfs_cleanup; | ||
667 | |||
724 | return 0; | 668 | return 0; |
725 | 669 | ||
670 | err_sysfs_cleanup: | ||
671 | fuse_sysfs_cleanup(); | ||
726 | err_dev_cleanup: | 672 | err_dev_cleanup: |
727 | fuse_dev_cleanup(); | 673 | fuse_dev_cleanup(); |
728 | err_fs_cleanup: | 674 | err_fs_cleanup: |
@@ -735,6 +681,7 @@ static void __exit fuse_exit(void) | |||
735 | { | 681 | { |
736 | printk(KERN_DEBUG "fuse exit\n"); | 682 | printk(KERN_DEBUG "fuse exit\n"); |
737 | 683 | ||
684 | fuse_ctl_cleanup(); | ||
738 | fuse_sysfs_cleanup(); | 685 | fuse_sysfs_cleanup(); |
739 | fuse_fs_cleanup(); | 686 | fuse_fs_cleanup(); |
740 | fuse_dev_cleanup(); | 687 | fuse_dev_cleanup(); |