diff options
author | Miklos Szeredi <miklos@szeredi.hu> | 2005-09-09 16:10:31 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2005-09-09 17:03:45 -0400 |
commit | 1e9a4ed9396e9c31139721b639550ffb1df17065 (patch) | |
tree | 213566cf1294f5dd8f6ff62ceb3557b5f5b6c59c /fs/fuse | |
parent | b6aeadeda22a9aa322fdfcd3f4c69ccf0da5cbdd (diff) |
[PATCH] FUSE - mount options
This patch adds miscellaneous mount options to the FUSE filesystem.
The following mount options are added:
o default_permissions: check permissions with generic_permission()
o allow_other: allow other users to access files
o allow_root: allow root to access files
o kernel_cache: don't invalidate page cache on open
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/dev.c | 78 | ||||
-rw-r--r-- | fs/fuse/dir.c | 35 | ||||
-rw-r--r-- | fs/fuse/file.c | 2 | ||||
-rw-r--r-- | fs/fuse/fuse_i.h | 45 | ||||
-rw-r--r-- | fs/fuse/inode.c | 77 |
5 files changed, 170 insertions, 67 deletions
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index e8f3170946f1..ca6fc0e96d7c 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c | |||
@@ -26,7 +26,7 @@ static inline struct fuse_conn *fuse_get_conn(struct file *file) | |||
26 | struct fuse_conn *fc; | 26 | struct fuse_conn *fc; |
27 | spin_lock(&fuse_lock); | 27 | spin_lock(&fuse_lock); |
28 | fc = file->private_data; | 28 | fc = file->private_data; |
29 | if (fc && !fc->sb) | 29 | if (fc && !fc->mounted) |
30 | fc = NULL; | 30 | fc = NULL; |
31 | spin_unlock(&fuse_lock); | 31 | spin_unlock(&fuse_lock); |
32 | return fc; | 32 | return fc; |
@@ -148,6 +148,17 @@ void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req) | |||
148 | fuse_putback_request(fc, req); | 148 | fuse_putback_request(fc, req); |
149 | } | 149 | } |
150 | 150 | ||
151 | void fuse_release_background(struct fuse_req *req) | ||
152 | { | ||
153 | iput(req->inode); | ||
154 | iput(req->inode2); | ||
155 | if (req->file) | ||
156 | fput(req->file); | ||
157 | spin_lock(&fuse_lock); | ||
158 | list_del(&req->bg_entry); | ||
159 | spin_unlock(&fuse_lock); | ||
160 | } | ||
161 | |||
151 | /* | 162 | /* |
152 | * This function is called when a request is finished. Either a reply | 163 | * This function is called when a request is finished. Either a reply |
153 | * has arrived or it was interrupted (and not yet sent) or some error | 164 | * has arrived or it was interrupted (and not yet sent) or some error |
@@ -166,12 +177,10 @@ static void request_end(struct fuse_conn *fc, struct fuse_req *req) | |||
166 | putback = atomic_dec_and_test(&req->count); | 177 | putback = atomic_dec_and_test(&req->count); |
167 | spin_unlock(&fuse_lock); | 178 | spin_unlock(&fuse_lock); |
168 | if (req->background) { | 179 | if (req->background) { |
169 | if (req->inode) | 180 | down_read(&fc->sbput_sem); |
170 | iput(req->inode); | 181 | if (fc->mounted) |
171 | if (req->inode2) | 182 | fuse_release_background(req); |
172 | iput(req->inode2); | 183 | up_read(&fc->sbput_sem); |
173 | if (req->file) | ||
174 | fput(req->file); | ||
175 | } | 184 | } |
176 | wake_up(&req->waitq); | 185 | wake_up(&req->waitq); |
177 | if (req->in.h.opcode == FUSE_INIT) { | 186 | if (req->in.h.opcode == FUSE_INIT) { |
@@ -191,11 +200,39 @@ static void request_end(struct fuse_conn *fc, struct fuse_req *req) | |||
191 | fuse_putback_request(fc, req); | 200 | fuse_putback_request(fc, req); |
192 | } | 201 | } |
193 | 202 | ||
194 | static void background_request(struct fuse_req *req) | 203 | /* |
204 | * Unfortunately request interruption not just solves the deadlock | ||
205 | * problem, it causes problems too. These stem from the fact, that an | ||
206 | * interrupted request is continued to be processed in userspace, | ||
207 | * while all the locks and object references (inode and file) held | ||
208 | * during the operation are released. | ||
209 | * | ||
210 | * To release the locks is exactly why there's a need to interrupt the | ||
211 | * request, so there's not a lot that can be done about this, except | ||
212 | * introduce additional locking in userspace. | ||
213 | * | ||
214 | * More important is to keep inode and file references until userspace | ||
215 | * has replied, otherwise FORGET and RELEASE could be sent while the | ||
216 | * inode/file is still used by the filesystem. | ||
217 | * | ||
218 | * For this reason the concept of "background" request is introduced. | ||
219 | * An interrupted request is backgrounded if it has been already sent | ||
220 | * to userspace. Backgrounding involves getting an extra reference to | ||
221 | * inode(s) or file used in the request, and adding the request to | ||
222 | * fc->background list. When a reply is received for a background | ||
223 | * request, the object references are released, and the request is | ||
224 | * removed from the list. If the filesystem is unmounted while there | ||
225 | * are still background requests, the list is walked and references | ||
226 | * are released as if a reply was received. | ||
227 | * | ||
228 | * There's one more use for a background request. The RELEASE message is | ||
229 | * always sent as background, since it doesn't return an error or | ||
230 | * data. | ||
231 | */ | ||
232 | static void background_request(struct fuse_conn *fc, struct fuse_req *req) | ||
195 | { | 233 | { |
196 | /* Need to get hold of the inode(s) and/or file used in the | ||
197 | request, so FORGET and RELEASE are not sent too early */ | ||
198 | req->background = 1; | 234 | req->background = 1; |
235 | list_add(&req->bg_entry, &fc->background); | ||
199 | if (req->inode) | 236 | if (req->inode) |
200 | req->inode = igrab(req->inode); | 237 | req->inode = igrab(req->inode); |
201 | if (req->inode2) | 238 | if (req->inode2) |
@@ -215,7 +252,8 @@ static int request_wait_answer_nonint(struct fuse_req *req) | |||
215 | } | 252 | } |
216 | 253 | ||
217 | /* Called with fuse_lock held. Releases, and then reacquires it. */ | 254 | /* Called with fuse_lock held. Releases, and then reacquires it. */ |
218 | static void request_wait_answer(struct fuse_req *req, int interruptible) | 255 | static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req, |
256 | int interruptible) | ||
219 | { | 257 | { |
220 | int intr; | 258 | int intr; |
221 | 259 | ||
@@ -255,7 +293,7 @@ static void request_wait_answer(struct fuse_req *req, int interruptible) | |||
255 | list_del(&req->list); | 293 | list_del(&req->list); |
256 | __fuse_put_request(req); | 294 | __fuse_put_request(req); |
257 | } else if (!req->finished && req->sent) | 295 | } else if (!req->finished && req->sent) |
258 | background_request(req); | 296 | background_request(fc, req); |
259 | } | 297 | } |
260 | 298 | ||
261 | static unsigned len_args(unsigned numargs, struct fuse_arg *args) | 299 | static unsigned len_args(unsigned numargs, struct fuse_arg *args) |
@@ -297,7 +335,7 @@ static void request_send_wait(struct fuse_conn *fc, struct fuse_req *req, | |||
297 | { | 335 | { |
298 | req->isreply = 1; | 336 | req->isreply = 1; |
299 | spin_lock(&fuse_lock); | 337 | spin_lock(&fuse_lock); |
300 | if (!fc->file) | 338 | if (!fc->connected) |
301 | req->out.h.error = -ENOTCONN; | 339 | req->out.h.error = -ENOTCONN; |
302 | else if (fc->conn_error) | 340 | else if (fc->conn_error) |
303 | req->out.h.error = -ECONNREFUSED; | 341 | req->out.h.error = -ECONNREFUSED; |
@@ -307,7 +345,7 @@ static void request_send_wait(struct fuse_conn *fc, struct fuse_req *req, | |||
307 | after request_end() */ | 345 | after request_end() */ |
308 | __fuse_get_request(req); | 346 | __fuse_get_request(req); |
309 | 347 | ||
310 | request_wait_answer(req, interruptible); | 348 | request_wait_answer(fc, req, interruptible); |
311 | } | 349 | } |
312 | spin_unlock(&fuse_lock); | 350 | spin_unlock(&fuse_lock); |
313 | } | 351 | } |
@@ -330,7 +368,7 @@ void request_send_nonint(struct fuse_conn *fc, struct fuse_req *req) | |||
330 | static void request_send_nowait(struct fuse_conn *fc, struct fuse_req *req) | 368 | static void request_send_nowait(struct fuse_conn *fc, struct fuse_req *req) |
331 | { | 369 | { |
332 | spin_lock(&fuse_lock); | 370 | spin_lock(&fuse_lock); |
333 | if (fc->file) { | 371 | if (fc->connected) { |
334 | queue_request(fc, req); | 372 | queue_request(fc, req); |
335 | spin_unlock(&fuse_lock); | 373 | spin_unlock(&fuse_lock); |
336 | } else { | 374 | } else { |
@@ -348,7 +386,9 @@ void request_send_noreply(struct fuse_conn *fc, struct fuse_req *req) | |||
348 | void request_send_background(struct fuse_conn *fc, struct fuse_req *req) | 386 | void request_send_background(struct fuse_conn *fc, struct fuse_req *req) |
349 | { | 387 | { |
350 | req->isreply = 1; | 388 | req->isreply = 1; |
351 | background_request(req); | 389 | spin_lock(&fuse_lock); |
390 | background_request(fc, req); | ||
391 | spin_unlock(&fuse_lock); | ||
352 | request_send_nowait(fc, req); | 392 | request_send_nowait(fc, req); |
353 | } | 393 | } |
354 | 394 | ||
@@ -583,7 +623,7 @@ static void request_wait(struct fuse_conn *fc) | |||
583 | DECLARE_WAITQUEUE(wait, current); | 623 | DECLARE_WAITQUEUE(wait, current); |
584 | 624 | ||
585 | add_wait_queue_exclusive(&fc->waitq, &wait); | 625 | add_wait_queue_exclusive(&fc->waitq, &wait); |
586 | while (fc->sb && list_empty(&fc->pending)) { | 626 | while (fc->mounted && list_empty(&fc->pending)) { |
587 | set_current_state(TASK_INTERRUPTIBLE); | 627 | set_current_state(TASK_INTERRUPTIBLE); |
588 | if (signal_pending(current)) | 628 | if (signal_pending(current)) |
589 | break; | 629 | break; |
@@ -622,7 +662,7 @@ static ssize_t fuse_dev_readv(struct file *file, const struct iovec *iov, | |||
622 | goto err_unlock; | 662 | goto err_unlock; |
623 | request_wait(fc); | 663 | request_wait(fc); |
624 | err = -ENODEV; | 664 | err = -ENODEV; |
625 | if (!fc->sb) | 665 | if (!fc->mounted) |
626 | goto err_unlock; | 666 | goto err_unlock; |
627 | err = -ERESTARTSYS; | 667 | err = -ERESTARTSYS; |
628 | if (list_empty(&fc->pending)) | 668 | if (list_empty(&fc->pending)) |
@@ -839,7 +879,7 @@ static int fuse_dev_release(struct inode *inode, struct file *file) | |||
839 | spin_lock(&fuse_lock); | 879 | spin_lock(&fuse_lock); |
840 | fc = file->private_data; | 880 | fc = file->private_data; |
841 | if (fc) { | 881 | if (fc) { |
842 | fc->file = NULL; | 882 | fc->connected = 0; |
843 | end_requests(fc, &fc->pending); | 883 | end_requests(fc, &fc->pending); |
844 | end_requests(fc, &fc->processing); | 884 | end_requests(fc, &fc->processing); |
845 | fuse_release_conn(fc); | 885 | fuse_release_conn(fc); |
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 8adc1eed164b..0950455914dd 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c | |||
@@ -418,7 +418,8 @@ static int fuse_revalidate(struct dentry *entry) | |||
418 | struct fuse_conn *fc = get_fuse_conn(inode); | 418 | struct fuse_conn *fc = get_fuse_conn(inode); |
419 | 419 | ||
420 | if (get_node_id(inode) == FUSE_ROOT_ID) { | 420 | if (get_node_id(inode) == FUSE_ROOT_ID) { |
421 | if (current->fsuid != fc->user_id) | 421 | if (!(fc->flags & FUSE_ALLOW_OTHER) && |
422 | current->fsuid != fc->user_id) | ||
422 | return -EACCES; | 423 | return -EACCES; |
423 | } else if (time_before_eq(jiffies, fi->i_time)) | 424 | } else if (time_before_eq(jiffies, fi->i_time)) |
424 | return 0; | 425 | return 0; |
@@ -430,9 +431,31 @@ static int fuse_permission(struct inode *inode, int mask, struct nameidata *nd) | |||
430 | { | 431 | { |
431 | struct fuse_conn *fc = get_fuse_conn(inode); | 432 | struct fuse_conn *fc = get_fuse_conn(inode); |
432 | 433 | ||
433 | if (current->fsuid != fc->user_id) | 434 | if (!(fc->flags & FUSE_ALLOW_OTHER) && current->fsuid != fc->user_id) |
434 | return -EACCES; | 435 | return -EACCES; |
435 | else { | 436 | else if (fc->flags & FUSE_DEFAULT_PERMISSIONS) { |
437 | int err = generic_permission(inode, mask, NULL); | ||
438 | |||
439 | /* If permission is denied, try to refresh file | ||
440 | attributes. This is also needed, because the root | ||
441 | node will at first have no permissions */ | ||
442 | if (err == -EACCES) { | ||
443 | err = fuse_do_getattr(inode); | ||
444 | if (!err) | ||
445 | err = generic_permission(inode, mask, NULL); | ||
446 | } | ||
447 | |||
448 | /* FIXME: Need some mechanism to revoke permissions: | ||
449 | currently if the filesystem suddenly changes the | ||
450 | file mode, we will not be informed about it, and | ||
451 | continue to allow access to the file/directory. | ||
452 | |||
453 | This is actually not so grave, since the user can | ||
454 | simply keep access to the file/directory anyway by | ||
455 | keeping it open... */ | ||
456 | |||
457 | return err; | ||
458 | } else { | ||
436 | int mode = inode->i_mode; | 459 | int mode = inode->i_mode; |
437 | if ((mask & MAY_WRITE) && IS_RDONLY(inode) && | 460 | if ((mask & MAY_WRITE) && IS_RDONLY(inode) && |
438 | (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) | 461 | (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) |
@@ -636,6 +659,12 @@ static int fuse_setattr(struct dentry *entry, struct iattr *attr) | |||
636 | int err; | 659 | int err; |
637 | int is_truncate = 0; | 660 | int is_truncate = 0; |
638 | 661 | ||
662 | if (fc->flags & FUSE_DEFAULT_PERMISSIONS) { | ||
663 | err = inode_change_ok(inode, attr); | ||
664 | if (err) | ||
665 | return err; | ||
666 | } | ||
667 | |||
639 | if (attr->ia_valid & ATTR_SIZE) { | 668 | if (attr->ia_valid & ATTR_SIZE) { |
640 | unsigned long limit; | 669 | unsigned long limit; |
641 | is_truncate = 1; | 670 | is_truncate = 1; |
diff --git a/fs/fuse/file.c b/fs/fuse/file.c index de8c9c702461..96ea302db184 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c | |||
@@ -70,7 +70,7 @@ static int fuse_open(struct inode *inode, struct file *file) | |||
70 | else | 70 | else |
71 | request_send(fc, req); | 71 | request_send(fc, req); |
72 | err = req->out.h.error; | 72 | err = req->out.h.error; |
73 | if (!err) | 73 | if (!err && !(fc->flags & FUSE_KERNEL_CACHE)) |
74 | invalidate_inode_pages(inode->i_mapping); | 74 | invalidate_inode_pages(inode->i_mapping); |
75 | if (err) { | 75 | if (err) { |
76 | fuse_request_free(ff->release_req); | 76 | fuse_request_free(ff->release_req); |
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index b4aa8f7bc2c1..c8e6c87496e0 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h | |||
@@ -21,6 +21,19 @@ | |||
21 | /** If more requests are outstanding, then the operation will block */ | 21 | /** If more requests are outstanding, then the operation will block */ |
22 | #define FUSE_MAX_OUTSTANDING 10 | 22 | #define FUSE_MAX_OUTSTANDING 10 |
23 | 23 | ||
24 | /** If the FUSE_DEFAULT_PERMISSIONS flag is given, the filesystem | ||
25 | module will check permissions based on the file mode. Otherwise no | ||
26 | permission checking is done in the kernel */ | ||
27 | #define FUSE_DEFAULT_PERMISSIONS (1 << 0) | ||
28 | |||
29 | /** If the FUSE_ALLOW_OTHER flag is given, then not only the user | ||
30 | doing the mount will be allowed to access the filesystem */ | ||
31 | #define FUSE_ALLOW_OTHER (1 << 1) | ||
32 | |||
33 | /** If the FUSE_KERNEL_CACHE flag is given, then cached data will not | ||
34 | be flushed on open */ | ||
35 | #define FUSE_KERNEL_CACHE (1 << 2) | ||
36 | |||
24 | /** FUSE inode */ | 37 | /** FUSE inode */ |
25 | struct fuse_inode { | 38 | struct fuse_inode { |
26 | /** Inode data */ | 39 | /** Inode data */ |
@@ -109,6 +122,9 @@ struct fuse_req { | |||
109 | lists in fuse_conn */ | 122 | lists in fuse_conn */ |
110 | struct list_head list; | 123 | struct list_head list; |
111 | 124 | ||
125 | /** Entry on the background list */ | ||
126 | struct list_head bg_entry; | ||
127 | |||
112 | /** refcount */ | 128 | /** refcount */ |
113 | atomic_t count; | 129 | atomic_t count; |
114 | 130 | ||
@@ -176,15 +192,15 @@ struct fuse_req { | |||
176 | * unmounted. | 192 | * unmounted. |
177 | */ | 193 | */ |
178 | struct fuse_conn { | 194 | struct fuse_conn { |
179 | /** The superblock of the mounted filesystem */ | 195 | /** Reference count */ |
180 | struct super_block *sb; | 196 | int count; |
181 | |||
182 | /** The opened client device */ | ||
183 | struct file *file; | ||
184 | 197 | ||
185 | /** The user id for this mount */ | 198 | /** The user id for this mount */ |
186 | uid_t user_id; | 199 | uid_t user_id; |
187 | 200 | ||
201 | /** The fuse mount flags for this mount */ | ||
202 | unsigned flags; | ||
203 | |||
188 | /** Readers of the connection are waiting on this */ | 204 | /** Readers of the connection are waiting on this */ |
189 | wait_queue_head_t waitq; | 205 | wait_queue_head_t waitq; |
190 | 206 | ||
@@ -194,6 +210,10 @@ struct fuse_conn { | |||
194 | /** The list of requests being processed */ | 210 | /** The list of requests being processed */ |
195 | struct list_head processing; | 211 | struct list_head processing; |
196 | 212 | ||
213 | /** Requests put in the background (RELEASE or any other | ||
214 | interrupted request) */ | ||
215 | struct list_head background; | ||
216 | |||
197 | /** Controls the maximum number of outstanding requests */ | 217 | /** Controls the maximum number of outstanding requests */ |
198 | struct semaphore outstanding_sem; | 218 | struct semaphore outstanding_sem; |
199 | 219 | ||
@@ -201,12 +221,21 @@ struct fuse_conn { | |||
201 | outstanding_sem would go negative */ | 221 | outstanding_sem would go negative */ |
202 | unsigned outstanding_debt; | 222 | unsigned outstanding_debt; |
203 | 223 | ||
224 | /** RW semaphore for exclusion with fuse_put_super() */ | ||
225 | struct rw_semaphore sbput_sem; | ||
226 | |||
204 | /** The list of unused requests */ | 227 | /** The list of unused requests */ |
205 | struct list_head unused_list; | 228 | struct list_head unused_list; |
206 | 229 | ||
207 | /** The next unique request id */ | 230 | /** The next unique request id */ |
208 | u64 reqctr; | 231 | u64 reqctr; |
209 | 232 | ||
233 | /** Mount is active */ | ||
234 | unsigned mounted : 1; | ||
235 | |||
236 | /** Connection established */ | ||
237 | unsigned connected : 1; | ||
238 | |||
210 | /** Connection failed (version mismatch) */ | 239 | /** Connection failed (version mismatch) */ |
211 | unsigned conn_error : 1; | 240 | unsigned conn_error : 1; |
212 | 241 | ||
@@ -261,6 +290,7 @@ extern struct file_operations fuse_dev_operations; | |||
261 | * - the private_data field of the device file | 290 | * - the private_data field of the device file |
262 | * - the s_fs_info field of the super block | 291 | * - the s_fs_info field of the super block |
263 | * - unused_list, pending, processing lists in fuse_conn | 292 | * - unused_list, pending, processing lists in fuse_conn |
293 | * - background list in fuse_conn | ||
264 | * - the unique request ID counter reqctr in fuse_conn | 294 | * - the unique request ID counter reqctr in fuse_conn |
265 | * - the sb (super_block) field in fuse_conn | 295 | * - the sb (super_block) field in fuse_conn |
266 | * - the file (device file) field in fuse_conn | 296 | * - the file (device file) field in fuse_conn |
@@ -372,6 +402,11 @@ void request_send_noreply(struct fuse_conn *fc, struct fuse_req *req); | |||
372 | void request_send_background(struct fuse_conn *fc, struct fuse_req *req); | 402 | void request_send_background(struct fuse_conn *fc, struct fuse_req *req); |
373 | 403 | ||
374 | /** | 404 | /** |
405 | * Release inodes and file assiciated with background request | ||
406 | */ | ||
407 | void fuse_release_background(struct fuse_req *req); | ||
408 | |||
409 | /** | ||
375 | * Get the attributes of a file | 410 | * Get the attributes of a file |
376 | */ | 411 | */ |
377 | int fuse_do_getattr(struct inode *inode); | 412 | int fuse_do_getattr(struct inode *inode); |
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index f229d6962643..458c62ca0fec 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c | |||
@@ -15,7 +15,6 @@ | |||
15 | #include <linux/seq_file.h> | 15 | #include <linux/seq_file.h> |
16 | #include <linux/init.h> | 16 | #include <linux/init.h> |
17 | #include <linux/module.h> | 17 | #include <linux/module.h> |
18 | #include <linux/moduleparam.h> | ||
19 | #include <linux/parser.h> | 18 | #include <linux/parser.h> |
20 | #include <linux/statfs.h> | 19 | #include <linux/statfs.h> |
21 | 20 | ||
@@ -25,11 +24,6 @@ MODULE_LICENSE("GPL"); | |||
25 | 24 | ||
26 | spinlock_t fuse_lock; | 25 | spinlock_t fuse_lock; |
27 | static kmem_cache_t *fuse_inode_cachep; | 26 | static kmem_cache_t *fuse_inode_cachep; |
28 | static int mount_count; | ||
29 | |||
30 | static int mount_max = 1000; | ||
31 | module_param(mount_max, int, 0644); | ||
32 | MODULE_PARM_DESC(mount_max, "Maximum number of FUSE mounts allowed, if -1 then unlimited (default: 1000)"); | ||
33 | 27 | ||
34 | #define FUSE_SUPER_MAGIC 0x65735546 | 28 | #define FUSE_SUPER_MAGIC 0x65735546 |
35 | 29 | ||
@@ -37,6 +31,7 @@ struct fuse_mount_data { | |||
37 | int fd; | 31 | int fd; |
38 | unsigned rootmode; | 32 | unsigned rootmode; |
39 | unsigned user_id; | 33 | unsigned user_id; |
34 | unsigned flags; | ||
40 | }; | 35 | }; |
41 | 36 | ||
42 | static struct inode *fuse_alloc_inode(struct super_block *sb) | 37 | static struct inode *fuse_alloc_inode(struct super_block *sb) |
@@ -89,8 +84,8 @@ void fuse_send_forget(struct fuse_conn *fc, struct fuse_req *req, | |||
89 | 84 | ||
90 | static void fuse_clear_inode(struct inode *inode) | 85 | static void fuse_clear_inode(struct inode *inode) |
91 | { | 86 | { |
92 | struct fuse_conn *fc = get_fuse_conn(inode); | 87 | if (inode->i_sb->s_flags & MS_ACTIVE) { |
93 | if (fc) { | 88 | struct fuse_conn *fc = get_fuse_conn(inode); |
94 | struct fuse_inode *fi = get_fuse_inode(inode); | 89 | struct fuse_inode *fi = get_fuse_inode(inode); |
95 | fuse_send_forget(fc, fi->forget_req, fi->nodeid, fi->nlookup); | 90 | fuse_send_forget(fc, fi->forget_req, fi->nodeid, fi->nlookup); |
96 | fi->forget_req = NULL; | 91 | fi->forget_req = NULL; |
@@ -195,14 +190,19 @@ static void fuse_put_super(struct super_block *sb) | |||
195 | { | 190 | { |
196 | struct fuse_conn *fc = get_fuse_conn_super(sb); | 191 | struct fuse_conn *fc = get_fuse_conn_super(sb); |
197 | 192 | ||
193 | down_write(&fc->sbput_sem); | ||
194 | while (!list_empty(&fc->background)) | ||
195 | fuse_release_background(list_entry(fc->background.next, | ||
196 | struct fuse_req, bg_entry)); | ||
197 | |||
198 | spin_lock(&fuse_lock); | 198 | spin_lock(&fuse_lock); |
199 | mount_count --; | 199 | fc->mounted = 0; |
200 | fc->sb = NULL; | ||
201 | fc->user_id = 0; | 200 | fc->user_id = 0; |
201 | fc->flags = 0; | ||
202 | /* Flush all readers on this fs */ | 202 | /* Flush all readers on this fs */ |
203 | wake_up_all(&fc->waitq); | 203 | wake_up_all(&fc->waitq); |
204 | up_write(&fc->sbput_sem); | ||
204 | fuse_release_conn(fc); | 205 | fuse_release_conn(fc); |
205 | *get_fuse_conn_super_p(sb) = NULL; | ||
206 | spin_unlock(&fuse_lock); | 206 | spin_unlock(&fuse_lock); |
207 | } | 207 | } |
208 | 208 | ||
@@ -249,7 +249,6 @@ enum { | |||
249 | OPT_USER_ID, | 249 | OPT_USER_ID, |
250 | OPT_DEFAULT_PERMISSIONS, | 250 | OPT_DEFAULT_PERMISSIONS, |
251 | OPT_ALLOW_OTHER, | 251 | OPT_ALLOW_OTHER, |
252 | OPT_ALLOW_ROOT, | ||
253 | OPT_KERNEL_CACHE, | 252 | OPT_KERNEL_CACHE, |
254 | OPT_ERR | 253 | OPT_ERR |
255 | }; | 254 | }; |
@@ -260,7 +259,6 @@ static match_table_t tokens = { | |||
260 | {OPT_USER_ID, "user_id=%u"}, | 259 | {OPT_USER_ID, "user_id=%u"}, |
261 | {OPT_DEFAULT_PERMISSIONS, "default_permissions"}, | 260 | {OPT_DEFAULT_PERMISSIONS, "default_permissions"}, |
262 | {OPT_ALLOW_OTHER, "allow_other"}, | 261 | {OPT_ALLOW_OTHER, "allow_other"}, |
263 | {OPT_ALLOW_ROOT, "allow_root"}, | ||
264 | {OPT_KERNEL_CACHE, "kernel_cache"}, | 262 | {OPT_KERNEL_CACHE, "kernel_cache"}, |
265 | {OPT_ERR, NULL} | 263 | {OPT_ERR, NULL} |
266 | }; | 264 | }; |
@@ -298,6 +296,18 @@ static int parse_fuse_opt(char *opt, struct fuse_mount_data *d) | |||
298 | d->user_id = value; | 296 | d->user_id = value; |
299 | break; | 297 | break; |
300 | 298 | ||
299 | case OPT_DEFAULT_PERMISSIONS: | ||
300 | d->flags |= FUSE_DEFAULT_PERMISSIONS; | ||
301 | break; | ||
302 | |||
303 | case OPT_ALLOW_OTHER: | ||
304 | d->flags |= FUSE_ALLOW_OTHER; | ||
305 | break; | ||
306 | |||
307 | case OPT_KERNEL_CACHE: | ||
308 | d->flags |= FUSE_KERNEL_CACHE; | ||
309 | break; | ||
310 | |||
301 | default: | 311 | default: |
302 | return 0; | 312 | return 0; |
303 | } | 313 | } |
@@ -313,6 +323,12 @@ static int fuse_show_options(struct seq_file *m, struct vfsmount *mnt) | |||
313 | struct fuse_conn *fc = get_fuse_conn_super(mnt->mnt_sb); | 323 | struct fuse_conn *fc = get_fuse_conn_super(mnt->mnt_sb); |
314 | 324 | ||
315 | seq_printf(m, ",user_id=%u", fc->user_id); | 325 | seq_printf(m, ",user_id=%u", fc->user_id); |
326 | if (fc->flags & FUSE_DEFAULT_PERMISSIONS) | ||
327 | seq_puts(m, ",default_permissions"); | ||
328 | if (fc->flags & FUSE_ALLOW_OTHER) | ||
329 | seq_puts(m, ",allow_other"); | ||
330 | if (fc->flags & FUSE_KERNEL_CACHE) | ||
331 | seq_puts(m, ",kernel_cache"); | ||
316 | return 0; | 332 | return 0; |
317 | } | 333 | } |
318 | 334 | ||
@@ -330,7 +346,8 @@ static void free_conn(struct fuse_conn *fc) | |||
330 | /* Must be called with the fuse lock held */ | 346 | /* Must be called with the fuse lock held */ |
331 | void fuse_release_conn(struct fuse_conn *fc) | 347 | void fuse_release_conn(struct fuse_conn *fc) |
332 | { | 348 | { |
333 | if (!fc->sb && !fc->file) | 349 | fc->count--; |
350 | if (!fc->count) | ||
334 | free_conn(fc); | 351 | free_conn(fc); |
335 | } | 352 | } |
336 | 353 | ||
@@ -342,14 +359,13 @@ static struct fuse_conn *new_conn(void) | |||
342 | if (fc != NULL) { | 359 | if (fc != NULL) { |
343 | int i; | 360 | int i; |
344 | memset(fc, 0, sizeof(*fc)); | 361 | memset(fc, 0, sizeof(*fc)); |
345 | fc->sb = NULL; | ||
346 | fc->file = NULL; | ||
347 | fc->user_id = 0; | ||
348 | init_waitqueue_head(&fc->waitq); | 362 | init_waitqueue_head(&fc->waitq); |
349 | INIT_LIST_HEAD(&fc->pending); | 363 | INIT_LIST_HEAD(&fc->pending); |
350 | INIT_LIST_HEAD(&fc->processing); | 364 | INIT_LIST_HEAD(&fc->processing); |
351 | INIT_LIST_HEAD(&fc->unused_list); | 365 | INIT_LIST_HEAD(&fc->unused_list); |
366 | INIT_LIST_HEAD(&fc->background); | ||
352 | sema_init(&fc->outstanding_sem, 0); | 367 | sema_init(&fc->outstanding_sem, 0); |
368 | init_rwsem(&fc->sbput_sem); | ||
353 | for (i = 0; i < FUSE_MAX_OUTSTANDING; i++) { | 369 | for (i = 0; i < FUSE_MAX_OUTSTANDING; i++) { |
354 | struct fuse_req *req = fuse_request_alloc(); | 370 | struct fuse_req *req = fuse_request_alloc(); |
355 | if (!req) { | 371 | if (!req) { |
@@ -380,8 +396,10 @@ static struct fuse_conn *get_conn(struct file *file, struct super_block *sb) | |||
380 | fc = ERR_PTR(-EINVAL); | 396 | fc = ERR_PTR(-EINVAL); |
381 | } else { | 397 | } else { |
382 | file->private_data = fc; | 398 | file->private_data = fc; |
383 | fc->sb = sb; | 399 | *get_fuse_conn_super_p(sb) = fc; |
384 | fc->file = file; | 400 | fc->mounted = 1; |
401 | fc->connected = 1; | ||
402 | fc->count = 2; | ||
385 | } | 403 | } |
386 | spin_unlock(&fuse_lock); | 404 | spin_unlock(&fuse_lock); |
387 | return fc; | 405 | return fc; |
@@ -407,17 +425,6 @@ static struct super_operations fuse_super_operations = { | |||
407 | .show_options = fuse_show_options, | 425 | .show_options = fuse_show_options, |
408 | }; | 426 | }; |
409 | 427 | ||
410 | static int inc_mount_count(void) | ||
411 | { | ||
412 | int success = 0; | ||
413 | spin_lock(&fuse_lock); | ||
414 | mount_count ++; | ||
415 | if (mount_max == -1 || mount_count <= mount_max) | ||
416 | success = 1; | ||
417 | spin_unlock(&fuse_lock); | ||
418 | return success; | ||
419 | } | ||
420 | |||
421 | static int fuse_fill_super(struct super_block *sb, void *data, int silent) | 428 | static int fuse_fill_super(struct super_block *sb, void *data, int silent) |
422 | { | 429 | { |
423 | struct fuse_conn *fc; | 430 | struct fuse_conn *fc; |
@@ -444,14 +451,9 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) | |||
444 | if (IS_ERR(fc)) | 451 | if (IS_ERR(fc)) |
445 | return PTR_ERR(fc); | 452 | return PTR_ERR(fc); |
446 | 453 | ||
454 | fc->flags = d.flags; | ||
447 | fc->user_id = d.user_id; | 455 | fc->user_id = d.user_id; |
448 | 456 | ||
449 | *get_fuse_conn_super_p(sb) = fc; | ||
450 | |||
451 | err = -ENFILE; | ||
452 | if (!inc_mount_count() && current->uid != 0) | ||
453 | goto err; | ||
454 | |||
455 | err = -ENOMEM; | 457 | err = -ENOMEM; |
456 | root = get_root_inode(sb, d.rootmode); | 458 | root = get_root_inode(sb, d.rootmode); |
457 | if (root == NULL) | 459 | if (root == NULL) |
@@ -467,11 +469,8 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) | |||
467 | 469 | ||
468 | err: | 470 | err: |
469 | spin_lock(&fuse_lock); | 471 | spin_lock(&fuse_lock); |
470 | mount_count --; | ||
471 | fc->sb = NULL; | ||
472 | fuse_release_conn(fc); | 472 | fuse_release_conn(fc); |
473 | spin_unlock(&fuse_lock); | 473 | spin_unlock(&fuse_lock); |
474 | *get_fuse_conn_super_p(sb) = NULL; | ||
475 | return err; | 474 | return err; |
476 | } | 475 | } |
477 | 476 | ||