]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
fuse: fix readdirplus Oops in fuse_dentry_revalidate
authorMiklos Szeredi <mszeredi@suse.cz>
Mon, 3 Jun 2013 12:40:22 +0000 (14:40 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 7 Jun 2013 19:53:35 +0000 (12:53 -0700)
commit 28420dad233520811c0e0860e7fb4975ed863fc4 upstream.

Fix bug introduced by commit 4582a4ab2a "FUSE: Adapt readdirplus to application
usage patterns".

We need to check for a positive dentry; negative dentries are not added by
readdirplus.  Secondly we need to advise the use of readdirplus on the *parent*,
otherwise the whole thing is useless.  Thirdly all this is only relevant if
"readdirplus_auto" mode is selected by the filesystem.

We advise the use of readdirplus only if the dentry was still valid.  If we had
to redo the lookup then there was no use in doing the -plus version.

Reported-by: Bernd Schubert <bernd.schubert@itwm.fraunhofer.de>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
CC: Feng Shuo <steve.shuo.feng@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/fuse/dir.c
fs/fuse/inode.c

index ff15522481d4261c042b87107f90108dcd304d87..185c47987be05f7a72f2e3ff8c31fbd8e921a54b 100644 (file)
@@ -180,6 +180,8 @@ u64 fuse_get_attr_version(struct fuse_conn *fc)
 static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
 {
        struct inode *inode;
+       struct dentry *parent;
+       struct fuse_conn *fc;
 
        inode = ACCESS_ONCE(entry->d_inode);
        if (inode && is_bad_inode(inode))
@@ -187,10 +189,8 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
        else if (fuse_dentry_time(entry) < get_jiffies_64()) {
                int err;
                struct fuse_entry_out outarg;
-               struct fuse_conn *fc;
                struct fuse_req *req;
                struct fuse_forget_link *forget;
-               struct dentry *parent;
                u64 attr_version;
 
                /* For negative dentries, always do a fresh lookup */
@@ -241,8 +241,14 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
                                       entry_attr_timeout(&outarg),
                                       attr_version);
                fuse_change_entry_timeout(entry, &outarg);
+       } else if (inode) {
+               fc = get_fuse_conn(inode);
+               if (fc->readdirplus_auto) {
+                       parent = dget_parent(entry);
+                       fuse_advise_use_readdirplus(parent->d_inode);
+                       dput(parent);
+               }
        }
-       fuse_advise_use_readdirplus(inode);
        return 1;
 }
 
index 137185c3884fbbb9dfcab6115c78952d7aec7f08..a215d22fb3ab3409a36c9563327d730f2d1878c9 100644 (file)
@@ -864,10 +864,11 @@ static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req)
                                fc->dont_mask = 1;
                        if (arg->flags & FUSE_AUTO_INVAL_DATA)
                                fc->auto_inval_data = 1;
-                       if (arg->flags & FUSE_DO_READDIRPLUS)
+                       if (arg->flags & FUSE_DO_READDIRPLUS) {
                                fc->do_readdirplus = 1;
-                       if (arg->flags & FUSE_READDIRPLUS_AUTO)
-                               fc->readdirplus_auto = 1;
+                               if (arg->flags & FUSE_READDIRPLUS_AUTO)
+                                       fc->readdirplus_auto = 1;
+                       }
                } else {
                        ra_pages = fc->max_read / PAGE_CACHE_SIZE;
                        fc->no_lock = 1;