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 | |
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')
-rw-r--r-- | fs/fuse/Makefile | 2 | ||||
-rw-r--r-- | fs/fuse/control.c | 218 | ||||
-rw-r--r-- | fs/fuse/dev.c | 2 | ||||
-rw-r--r-- | fs/fuse/fuse_i.h | 53 | ||||
-rw-r--r-- | fs/fuse/inode.c | 141 |
5 files changed, 310 insertions, 106 deletions
diff --git a/fs/fuse/Makefile b/fs/fuse/Makefile index c3e1f760cac9..72437065f6ad 100644 --- a/fs/fuse/Makefile +++ b/fs/fuse/Makefile | |||
@@ -4,4 +4,4 @@ | |||
4 | 4 | ||
5 | obj-$(CONFIG_FUSE_FS) += fuse.o | 5 | obj-$(CONFIG_FUSE_FS) += fuse.o |
6 | 6 | ||
7 | fuse-objs := dev.o dir.o file.o inode.o | 7 | fuse-objs := dev.o dir.o file.o inode.o control.o |
diff --git a/fs/fuse/control.c b/fs/fuse/control.c new file mode 100644 index 000000000000..a3bce3a77253 --- /dev/null +++ b/fs/fuse/control.c | |||
@@ -0,0 +1,218 @@ | |||
1 | /* | ||
2 | FUSE: Filesystem in Userspace | ||
3 | Copyright (C) 2001-2006 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 | |||
14 | #define FUSE_CTL_SUPER_MAGIC 0x65735543 | ||
15 | |||
16 | /* | ||
17 | * This is non-NULL when the single instance of the control filesystem | ||
18 | * exists. Protected by fuse_mutex | ||
19 | */ | ||
20 | static struct super_block *fuse_control_sb; | ||
21 | |||
22 | static struct fuse_conn *fuse_ctl_file_conn_get(struct file *file) | ||
23 | { | ||
24 | struct fuse_conn *fc; | ||
25 | mutex_lock(&fuse_mutex); | ||
26 | fc = file->f_dentry->d_inode->u.generic_ip; | ||
27 | if (fc) | ||
28 | fc = fuse_conn_get(fc); | ||
29 | mutex_unlock(&fuse_mutex); | ||
30 | return fc; | ||
31 | } | ||
32 | |||
33 | static ssize_t fuse_conn_abort_write(struct file *file, const char __user *buf, | ||
34 | size_t count, loff_t *ppos) | ||
35 | { | ||
36 | struct fuse_conn *fc = fuse_ctl_file_conn_get(file); | ||
37 | if (fc) { | ||
38 | fuse_abort_conn(fc); | ||
39 | fuse_conn_put(fc); | ||
40 | } | ||
41 | return count; | ||
42 | } | ||
43 | |||
44 | static ssize_t fuse_conn_waiting_read(struct file *file, char __user *buf, | ||
45 | size_t len, loff_t *ppos) | ||
46 | { | ||
47 | char tmp[32]; | ||
48 | size_t size; | ||
49 | |||
50 | if (!*ppos) { | ||
51 | struct fuse_conn *fc = fuse_ctl_file_conn_get(file); | ||
52 | if (!fc) | ||
53 | return 0; | ||
54 | |||
55 | file->private_data=(void *)(long)atomic_read(&fc->num_waiting); | ||
56 | fuse_conn_put(fc); | ||
57 | } | ||
58 | size = sprintf(tmp, "%ld\n", (long)file->private_data); | ||
59 | return simple_read_from_buffer(buf, len, ppos, tmp, size); | ||
60 | } | ||
61 | |||
62 | static const struct file_operations fuse_ctl_abort_ops = { | ||
63 | .open = nonseekable_open, | ||
64 | .write = fuse_conn_abort_write, | ||
65 | }; | ||
66 | |||
67 | static const struct file_operations fuse_ctl_waiting_ops = { | ||
68 | .open = nonseekable_open, | ||
69 | .read = fuse_conn_waiting_read, | ||
70 | }; | ||
71 | |||
72 | static struct dentry *fuse_ctl_add_dentry(struct dentry *parent, | ||
73 | struct fuse_conn *fc, | ||
74 | const char *name, | ||
75 | int mode, int nlink, | ||
76 | struct inode_operations *iop, | ||
77 | const struct file_operations *fop) | ||
78 | { | ||
79 | struct dentry *dentry; | ||
80 | struct inode *inode; | ||
81 | |||
82 | BUG_ON(fc->ctl_ndents >= FUSE_CTL_NUM_DENTRIES); | ||
83 | dentry = d_alloc_name(parent, name); | ||
84 | if (!dentry) | ||
85 | return NULL; | ||
86 | |||
87 | fc->ctl_dentry[fc->ctl_ndents++] = dentry; | ||
88 | inode = new_inode(fuse_control_sb); | ||
89 | if (!inode) | ||
90 | return NULL; | ||
91 | |||
92 | inode->i_mode = mode; | ||
93 | inode->i_uid = fc->user_id; | ||
94 | inode->i_gid = fc->group_id; | ||
95 | inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; | ||
96 | /* setting ->i_op to NULL is not allowed */ | ||
97 | if (iop) | ||
98 | inode->i_op = iop; | ||
99 | inode->i_fop = fop; | ||
100 | inode->i_nlink = nlink; | ||
101 | inode->u.generic_ip = fc; | ||
102 | d_add(dentry, inode); | ||
103 | return dentry; | ||
104 | } | ||
105 | |||
106 | /* | ||
107 | * Add a connection to the control filesystem (if it exists). Caller | ||
108 | * must host fuse_mutex | ||
109 | */ | ||
110 | int fuse_ctl_add_conn(struct fuse_conn *fc) | ||
111 | { | ||
112 | struct dentry *parent; | ||
113 | char name[32]; | ||
114 | |||
115 | if (!fuse_control_sb) | ||
116 | return 0; | ||
117 | |||
118 | parent = fuse_control_sb->s_root; | ||
119 | parent->d_inode->i_nlink++; | ||
120 | sprintf(name, "%llu", (unsigned long long) fc->id); | ||
121 | parent = fuse_ctl_add_dentry(parent, fc, name, S_IFDIR | 0500, 2, | ||
122 | &simple_dir_inode_operations, | ||
123 | &simple_dir_operations); | ||
124 | if (!parent) | ||
125 | goto err; | ||
126 | |||
127 | if (!fuse_ctl_add_dentry(parent, fc, "waiting", S_IFREG | 0400, 1, | ||
128 | NULL, &fuse_ctl_waiting_ops) || | ||
129 | !fuse_ctl_add_dentry(parent, fc, "abort", S_IFREG | 0200, 1, | ||
130 | NULL, &fuse_ctl_abort_ops)) | ||
131 | goto err; | ||
132 | |||
133 | return 0; | ||
134 | |||
135 | err: | ||
136 | fuse_ctl_remove_conn(fc); | ||
137 | return -ENOMEM; | ||
138 | } | ||
139 | |||
140 | /* | ||
141 | * Remove a connection from the control filesystem (if it exists). | ||
142 | * Caller must host fuse_mutex | ||
143 | */ | ||
144 | void fuse_ctl_remove_conn(struct fuse_conn *fc) | ||
145 | { | ||
146 | int i; | ||
147 | |||
148 | if (!fuse_control_sb) | ||
149 | return; | ||
150 | |||
151 | for (i = fc->ctl_ndents - 1; i >= 0; i--) { | ||
152 | struct dentry *dentry = fc->ctl_dentry[i]; | ||
153 | dentry->d_inode->u.generic_ip = NULL; | ||
154 | d_drop(dentry); | ||
155 | dput(dentry); | ||
156 | } | ||
157 | fuse_control_sb->s_root->d_inode->i_nlink--; | ||
158 | } | ||
159 | |||
160 | static int fuse_ctl_fill_super(struct super_block *sb, void *data, int silent) | ||
161 | { | ||
162 | struct tree_descr empty_descr = {""}; | ||
163 | struct fuse_conn *fc; | ||
164 | int err; | ||
165 | |||
166 | err = simple_fill_super(sb, FUSE_CTL_SUPER_MAGIC, &empty_descr); | ||
167 | if (err) | ||
168 | return err; | ||
169 | |||
170 | mutex_lock(&fuse_mutex); | ||
171 | BUG_ON(fuse_control_sb); | ||
172 | fuse_control_sb = sb; | ||
173 | list_for_each_entry(fc, &fuse_conn_list, entry) { | ||
174 | err = fuse_ctl_add_conn(fc); | ||
175 | if (err) { | ||
176 | fuse_control_sb = NULL; | ||
177 | mutex_unlock(&fuse_mutex); | ||
178 | return err; | ||
179 | } | ||
180 | } | ||
181 | mutex_unlock(&fuse_mutex); | ||
182 | |||
183 | return 0; | ||
184 | } | ||
185 | |||
186 | static int fuse_ctl_get_sb(struct file_system_type *fs_type, int flags, | ||
187 | const char *dev_name, void *raw_data, | ||
188 | struct vfsmount *mnt) | ||
189 | { | ||
190 | return get_sb_single(fs_type, flags, raw_data, | ||
191 | fuse_ctl_fill_super, mnt); | ||
192 | } | ||
193 | |||
194 | static void fuse_ctl_kill_sb(struct super_block *sb) | ||
195 | { | ||
196 | mutex_lock(&fuse_mutex); | ||
197 | fuse_control_sb = NULL; | ||
198 | mutex_unlock(&fuse_mutex); | ||
199 | |||
200 | kill_litter_super(sb); | ||
201 | } | ||
202 | |||
203 | static struct file_system_type fuse_ctl_fs_type = { | ||
204 | .owner = THIS_MODULE, | ||
205 | .name = "fusectl", | ||
206 | .get_sb = fuse_ctl_get_sb, | ||
207 | .kill_sb = fuse_ctl_kill_sb, | ||
208 | }; | ||
209 | |||
210 | int __init fuse_ctl_init(void) | ||
211 | { | ||
212 | return register_filesystem(&fuse_ctl_fs_type); | ||
213 | } | ||
214 | |||
215 | void fuse_ctl_cleanup(void) | ||
216 | { | ||
217 | unregister_filesystem(&fuse_ctl_fs_type); | ||
218 | } | ||
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index fec4779e2b55..fe3adf589174 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c | |||
@@ -833,7 +833,7 @@ static int fuse_dev_release(struct inode *inode, struct file *file) | |||
833 | end_requests(fc, &fc->processing); | 833 | end_requests(fc, &fc->processing); |
834 | spin_unlock(&fc->lock); | 834 | spin_unlock(&fc->lock); |
835 | fasync_helper(-1, file, 0, &fc->fasync); | 835 | fasync_helper(-1, file, 0, &fc->fasync); |
836 | kobject_put(&fc->kobj); | 836 | fuse_conn_put(fc); |
837 | } | 837 | } |
838 | 838 | ||
839 | return 0; | 839 | return 0; |
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 25f8581a770c..ac12b01f4446 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h | |||
@@ -14,6 +14,7 @@ | |||
14 | #include <linux/spinlock.h> | 14 | #include <linux/spinlock.h> |
15 | #include <linux/mm.h> | 15 | #include <linux/mm.h> |
16 | #include <linux/backing-dev.h> | 16 | #include <linux/backing-dev.h> |
17 | #include <linux/mutex.h> | ||
17 | 18 | ||
18 | /** Max number of pages that can be used in a single read request */ | 19 | /** Max number of pages that can be used in a single read request */ |
19 | #define FUSE_MAX_PAGES_PER_REQ 32 | 20 | #define FUSE_MAX_PAGES_PER_REQ 32 |
@@ -24,6 +25,9 @@ | |||
24 | /** It could be as large as PATH_MAX, but would that have any uses? */ | 25 | /** It could be as large as PATH_MAX, but would that have any uses? */ |
25 | #define FUSE_NAME_MAX 1024 | 26 | #define FUSE_NAME_MAX 1024 |
26 | 27 | ||
28 | /** Number of dentries for each connection in the control filesystem */ | ||
29 | #define FUSE_CTL_NUM_DENTRIES 3 | ||
30 | |||
27 | /** If the FUSE_DEFAULT_PERMISSIONS flag is given, the filesystem | 31 | /** If the FUSE_DEFAULT_PERMISSIONS flag is given, the filesystem |
28 | module will check permissions based on the file mode. Otherwise no | 32 | module will check permissions based on the file mode. Otherwise no |
29 | permission checking is done in the kernel */ | 33 | permission checking is done in the kernel */ |
@@ -33,6 +37,11 @@ | |||
33 | doing the mount will be allowed to access the filesystem */ | 37 | doing the mount will be allowed to access the filesystem */ |
34 | #define FUSE_ALLOW_OTHER (1 << 1) | 38 | #define FUSE_ALLOW_OTHER (1 << 1) |
35 | 39 | ||
40 | /** List of active connections */ | ||
41 | extern struct list_head fuse_conn_list; | ||
42 | |||
43 | /** Global mutex protecting fuse_conn_list and the control filesystem */ | ||
44 | extern struct mutex fuse_mutex; | ||
36 | 45 | ||
37 | /** FUSE inode */ | 46 | /** FUSE inode */ |
38 | struct fuse_inode { | 47 | struct fuse_inode { |
@@ -216,6 +225,9 @@ struct fuse_conn { | |||
216 | /** Lock protecting accessess to members of this structure */ | 225 | /** Lock protecting accessess to members of this structure */ |
217 | spinlock_t lock; | 226 | spinlock_t lock; |
218 | 227 | ||
228 | /** Refcount */ | ||
229 | atomic_t count; | ||
230 | |||
219 | /** The user id for this mount */ | 231 | /** The user id for this mount */ |
220 | uid_t user_id; | 232 | uid_t user_id; |
221 | 233 | ||
@@ -310,8 +322,17 @@ struct fuse_conn { | |||
310 | /** Backing dev info */ | 322 | /** Backing dev info */ |
311 | struct backing_dev_info bdi; | 323 | struct backing_dev_info bdi; |
312 | 324 | ||
313 | /** kobject */ | 325 | /** Entry on the fuse_conn_list */ |
314 | struct kobject kobj; | 326 | struct list_head entry; |
327 | |||
328 | /** Unique ID */ | ||
329 | u64 id; | ||
330 | |||
331 | /** Dentries in the control filesystem */ | ||
332 | struct dentry *ctl_dentry[FUSE_CTL_NUM_DENTRIES]; | ||
333 | |||
334 | /** number of dentries used in the above array */ | ||
335 | int ctl_ndents; | ||
315 | 336 | ||
316 | /** O_ASYNC requests */ | 337 | /** O_ASYNC requests */ |
317 | struct fasync_struct *fasync; | 338 | struct fasync_struct *fasync; |
@@ -327,11 +348,6 @@ static inline struct fuse_conn *get_fuse_conn(struct inode *inode) | |||
327 | return get_fuse_conn_super(inode->i_sb); | 348 | return get_fuse_conn_super(inode->i_sb); |
328 | } | 349 | } |
329 | 350 | ||
330 | static inline struct fuse_conn *get_fuse_conn_kobj(struct kobject *obj) | ||
331 | { | ||
332 | return container_of(obj, struct fuse_conn, kobj); | ||
333 | } | ||
334 | |||
335 | static inline struct fuse_inode *get_fuse_inode(struct inode *inode) | 351 | static inline struct fuse_inode *get_fuse_inode(struct inode *inode) |
336 | { | 352 | { |
337 | return container_of(inode, struct fuse_inode, inode); | 353 | return container_of(inode, struct fuse_inode, inode); |
@@ -422,6 +438,9 @@ int fuse_dev_init(void); | |||
422 | */ | 438 | */ |
423 | void fuse_dev_cleanup(void); | 439 | void fuse_dev_cleanup(void); |
424 | 440 | ||
441 | int fuse_ctl_init(void); | ||
442 | void fuse_ctl_cleanup(void); | ||
443 | |||
425 | /** | 444 | /** |
426 | * Allocate a request | 445 | * Allocate a request |
427 | */ | 446 | */ |
@@ -470,3 +489,23 @@ int fuse_do_getattr(struct inode *inode); | |||
470 | * Invalidate inode attributes | 489 | * Invalidate inode attributes |
471 | */ | 490 | */ |
472 | void fuse_invalidate_attr(struct inode *inode); | 491 | void fuse_invalidate_attr(struct inode *inode); |
492 | |||
493 | /** | ||
494 | * Acquire reference to fuse_conn | ||
495 | */ | ||
496 | struct fuse_conn *fuse_conn_get(struct fuse_conn *fc); | ||
497 | |||
498 | /** | ||
499 | * Release reference to fuse_conn | ||
500 | */ | ||
501 | void fuse_conn_put(struct fuse_conn *fc); | ||
502 | |||
503 | /** | ||
504 | * Add connection to control filesystem | ||
505 | */ | ||
506 | int fuse_ctl_add_conn(struct fuse_conn *fc); | ||
507 | |||
508 | /** | ||
509 | * Remove connection from control filesystem | ||
510 | */ | ||
511 | void fuse_ctl_remove_conn(struct fuse_conn *fc); | ||
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(); |