Commit f741a79e982cf56d7584435bad663553ffe6715f
Exists in
master
and in
7 other branches
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse: fuse: make fuse_dentry_revalidate() RCU aware fuse: make fuse_permission() RCU aware fuse: wakeup pollers on connection release/abort fuse: reduce size of struct fuse_request
Showing 5 changed files Side-by-side Diff
fs/fuse/cuse.c
... | ... | @@ -305,7 +305,7 @@ |
305 | 305 | static void cuse_process_init_reply(struct fuse_conn *fc, struct fuse_req *req) |
306 | 306 | { |
307 | 307 | struct cuse_conn *cc = fc_to_cc(fc); |
308 | - struct cuse_init_out *arg = &req->misc.cuse_init_out; | |
308 | + struct cuse_init_out *arg = req->out.args[0].value; | |
309 | 309 | struct page *page = req->pages[0]; |
310 | 310 | struct cuse_devinfo devinfo = { }; |
311 | 311 | struct device *dev; |
... | ... | @@ -384,6 +384,7 @@ |
384 | 384 | dev_set_uevent_suppress(dev, 0); |
385 | 385 | kobject_uevent(&dev->kobj, KOBJ_ADD); |
386 | 386 | out: |
387 | + kfree(arg); | |
387 | 388 | __free_page(page); |
388 | 389 | return; |
389 | 390 | |
... | ... | @@ -405,6 +406,7 @@ |
405 | 406 | struct page *page; |
406 | 407 | struct fuse_conn *fc = &cc->fc; |
407 | 408 | struct cuse_init_in *arg; |
409 | + void *outarg; | |
408 | 410 | |
409 | 411 | BUILD_BUG_ON(CUSE_INIT_INFO_MAX > PAGE_SIZE); |
410 | 412 | |
... | ... | @@ -419,6 +421,10 @@ |
419 | 421 | if (!page) |
420 | 422 | goto err_put_req; |
421 | 423 | |
424 | + outarg = kzalloc(sizeof(struct cuse_init_out), GFP_KERNEL); | |
425 | + if (!outarg) | |
426 | + goto err_free_page; | |
427 | + | |
422 | 428 | arg = &req->misc.cuse_init_in; |
423 | 429 | arg->major = FUSE_KERNEL_VERSION; |
424 | 430 | arg->minor = FUSE_KERNEL_MINOR_VERSION; |
... | ... | @@ -429,7 +435,7 @@ |
429 | 435 | req->in.args[0].value = arg; |
430 | 436 | req->out.numargs = 2; |
431 | 437 | req->out.args[0].size = sizeof(struct cuse_init_out); |
432 | - req->out.args[0].value = &req->misc.cuse_init_out; | |
438 | + req->out.args[0].value = outarg; | |
433 | 439 | req->out.args[1].size = CUSE_INIT_INFO_MAX; |
434 | 440 | req->out.argvar = 1; |
435 | 441 | req->out.argpages = 1; |
... | ... | @@ -440,6 +446,8 @@ |
440 | 446 | |
441 | 447 | return 0; |
442 | 448 | |
449 | +err_free_page: | |
450 | + __free_page(page); | |
443 | 451 | err_put_req: |
444 | 452 | fuse_put_request(fc, req); |
445 | 453 | err: |
fs/fuse/dev.c
... | ... | @@ -1910,6 +1910,21 @@ |
1910 | 1910 | kfree(dequeue_forget(fc, 1, NULL)); |
1911 | 1911 | } |
1912 | 1912 | |
1913 | +static void end_polls(struct fuse_conn *fc) | |
1914 | +{ | |
1915 | + struct rb_node *p; | |
1916 | + | |
1917 | + p = rb_first(&fc->polled_files); | |
1918 | + | |
1919 | + while (p) { | |
1920 | + struct fuse_file *ff; | |
1921 | + ff = rb_entry(p, struct fuse_file, polled_node); | |
1922 | + wake_up_interruptible_all(&ff->poll_wait); | |
1923 | + | |
1924 | + p = rb_next(p); | |
1925 | + } | |
1926 | +} | |
1927 | + | |
1913 | 1928 | /* |
1914 | 1929 | * Abort all requests. |
1915 | 1930 | * |
... | ... | @@ -1937,6 +1952,7 @@ |
1937 | 1952 | fc->blocked = 0; |
1938 | 1953 | end_io_requests(fc); |
1939 | 1954 | end_queued_requests(fc); |
1955 | + end_polls(fc); | |
1940 | 1956 | wake_up_all(&fc->waitq); |
1941 | 1957 | wake_up_all(&fc->blocked_waitq); |
1942 | 1958 | kill_fasync(&fc->fasync, SIGIO, POLL_IN); |
... | ... | @@ -1953,6 +1969,7 @@ |
1953 | 1969 | fc->connected = 0; |
1954 | 1970 | fc->blocked = 0; |
1955 | 1971 | end_queued_requests(fc); |
1972 | + end_polls(fc); | |
1956 | 1973 | wake_up_all(&fc->blocked_waitq); |
1957 | 1974 | spin_unlock(&fc->lock); |
1958 | 1975 | fuse_conn_put(fc); |
fs/fuse/dir.c
... | ... | @@ -158,10 +158,7 @@ |
158 | 158 | { |
159 | 159 | struct inode *inode; |
160 | 160 | |
161 | - if (nd && nd->flags & LOOKUP_RCU) | |
162 | - return -ECHILD; | |
163 | - | |
164 | - inode = entry->d_inode; | |
161 | + inode = ACCESS_ONCE(entry->d_inode); | |
165 | 162 | if (inode && is_bad_inode(inode)) |
166 | 163 | return 0; |
167 | 164 | else if (fuse_dentry_time(entry) < get_jiffies_64()) { |
... | ... | @@ -177,6 +174,9 @@ |
177 | 174 | if (!inode) |
178 | 175 | return 0; |
179 | 176 | |
177 | + if (nd->flags & LOOKUP_RCU) | |
178 | + return -ECHILD; | |
179 | + | |
180 | 180 | fc = get_fuse_conn(inode); |
181 | 181 | req = fuse_get_req(fc); |
182 | 182 | if (IS_ERR(req)) |
... | ... | @@ -970,6 +970,14 @@ |
970 | 970 | return err; |
971 | 971 | } |
972 | 972 | |
973 | +static int fuse_perm_getattr(struct inode *inode, int flags) | |
974 | +{ | |
975 | + if (flags & IPERM_FLAG_RCU) | |
976 | + return -ECHILD; | |
977 | + | |
978 | + return fuse_do_getattr(inode, NULL, NULL); | |
979 | +} | |
980 | + | |
973 | 981 | /* |
974 | 982 | * Check permission. The two basic access models of FUSE are: |
975 | 983 | * |
... | ... | @@ -989,9 +997,6 @@ |
989 | 997 | bool refreshed = false; |
990 | 998 | int err = 0; |
991 | 999 | |
992 | - if (flags & IPERM_FLAG_RCU) | |
993 | - return -ECHILD; | |
994 | - | |
995 | 1000 | if (!fuse_allow_task(fc, current)) |
996 | 1001 | return -EACCES; |
997 | 1002 | |
... | ... | @@ -1000,9 +1005,15 @@ |
1000 | 1005 | */ |
1001 | 1006 | if ((fc->flags & FUSE_DEFAULT_PERMISSIONS) || |
1002 | 1007 | ((mask & MAY_EXEC) && S_ISREG(inode->i_mode))) { |
1003 | - err = fuse_update_attributes(inode, NULL, NULL, &refreshed); | |
1004 | - if (err) | |
1005 | - return err; | |
1008 | + struct fuse_inode *fi = get_fuse_inode(inode); | |
1009 | + | |
1010 | + if (fi->i_time < get_jiffies_64()) { | |
1011 | + refreshed = true; | |
1012 | + | |
1013 | + err = fuse_perm_getattr(inode, flags); | |
1014 | + if (err) | |
1015 | + return err; | |
1016 | + } | |
1006 | 1017 | } |
1007 | 1018 | |
1008 | 1019 | if (fc->flags & FUSE_DEFAULT_PERMISSIONS) { |
... | ... | @@ -1012,7 +1023,7 @@ |
1012 | 1023 | attributes. This is also needed, because the root |
1013 | 1024 | node will at first have no permissions */ |
1014 | 1025 | if (err == -EACCES && !refreshed) { |
1015 | - err = fuse_do_getattr(inode, NULL, NULL); | |
1026 | + err = fuse_perm_getattr(inode, flags); | |
1016 | 1027 | if (!err) |
1017 | 1028 | err = generic_permission(inode, mask, |
1018 | 1029 | flags, NULL); |
1019 | 1030 | |
... | ... | @@ -1023,13 +1034,16 @@ |
1023 | 1034 | noticed immediately, only after the attribute |
1024 | 1035 | timeout has expired */ |
1025 | 1036 | } else if (mask & (MAY_ACCESS | MAY_CHDIR)) { |
1037 | + if (flags & IPERM_FLAG_RCU) | |
1038 | + return -ECHILD; | |
1039 | + | |
1026 | 1040 | err = fuse_access(inode, mask); |
1027 | 1041 | } else if ((mask & MAY_EXEC) && S_ISREG(inode->i_mode)) { |
1028 | 1042 | if (!(inode->i_mode & S_IXUGO)) { |
1029 | 1043 | if (refreshed) |
1030 | 1044 | return -EACCES; |
1031 | 1045 | |
1032 | - err = fuse_do_getattr(inode, NULL, NULL); | |
1046 | + err = fuse_perm_getattr(inode, flags); | |
1033 | 1047 | if (!err && !(inode->i_mode & S_IXUGO)) |
1034 | 1048 | return -EACCES; |
1035 | 1049 | } |
fs/fuse/file.c
fs/fuse/fuse_i.h