If this refactoring seems cumbersome, it's because the goal is to move the lookup parts of fuse_dentry_revalidate into a common function. This function will be used elsewhere in a separate commit. In the meantime, the new function fuse_dentry_revalidate_lookup is responsible for just the lookup and validation portions of the revalidate dance. The fuse_dentry_revalidate function retains the responsibility for invalidating and mutating any state associated with the origial fuse_inode and dentry. Cc: stable@xxxxxxxxxxxxxxx Fixes: 1866d779d5d2 ("fuse: Allow fuse_fill_super_common() for submounts") Signed-off-by: Krister Johansen <kjlx@xxxxxxxxxxxxxxxxxx> --- fs/fuse/dir.c | 85 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 58 insertions(+), 27 deletions(-) diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 04a1c62342dc..da5b6079d88c 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -183,6 +183,57 @@ static void fuse_lookup_init(struct fuse_conn *fc, struct fuse_args *args, args->out_args[0].value = outarg; } +static int fuse_dentry_revalidate_lookup(struct fuse_mount *fm, + struct dentry *entry, + struct inode *inode, + struct fuse_entry_out *outarg, + bool *lookedup) +{ + struct dentry *parent; + struct fuse_forget_link *forget; + FUSE_ARGS(args); + int ret; + + forget = fuse_alloc_forget(); + ret = -ENOMEM; + if (!forget) + goto out; + + parent = dget_parent(entry); + fuse_lookup_init(fm->fc, &args, get_node_id(d_inode(parent)), + &entry->d_name, outarg); + ret = fuse_simple_request(fm, &args); + dput(parent); + + /* Zero nodeid is same as -ENOENT */ + if (!ret && !outarg->nodeid) + ret = -ENOENT; + if (!ret) { + if (outarg->nodeid != get_node_id(inode) || + (bool) IS_AUTOMOUNT(inode) != (bool) (outarg->attr.flags & FUSE_ATTR_SUBMOUNT)) { + fuse_queue_forget(fm->fc, forget, + outarg->nodeid, 1); + goto invalid; + } + *lookedup = true; + } + kfree(forget); + if (ret == -ENOMEM || ret == -EINTR) + goto out; + if (ret || fuse_invalid_attr(&outarg->attr) || + fuse_stale_inode(inode, outarg->generation, &outarg->attr)) { + goto invalid; + } + + ret = 1; +out: + return ret; + +invalid: + ret = 0; + goto out; +} + /* * Check whether the dentry is still valid * @@ -206,9 +257,8 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags) else if (time_before64(fuse_dentry_time(entry), get_jiffies_64()) || (flags & (LOOKUP_EXCL | LOOKUP_REVAL | LOOKUP_RENAME_TARGET))) { struct fuse_entry_out outarg; - FUSE_ARGS(args); - struct fuse_forget_link *forget; u64 attr_version; + bool lookedup = false; /* For negative dentries, always do a fresh lookup */ if (!inode) @@ -220,38 +270,19 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags) fm = get_fuse_mount(inode); - forget = fuse_alloc_forget(); - ret = -ENOMEM; - if (!forget) - goto out; - attr_version = fuse_get_attr_version(fm->fc); - parent = dget_parent(entry); - fuse_lookup_init(fm->fc, &args, get_node_id(d_inode(parent)), - &entry->d_name, &outarg); - ret = fuse_simple_request(fm, &args); - dput(parent); - /* Zero nodeid is same as -ENOENT */ - if (!ret && !outarg.nodeid) - ret = -ENOENT; - if (!ret) { + ret = fuse_dentry_revalidate_lookup(fm, entry, inode, &outarg, + &lookedup); + if (ret == -ENOMEM || ret == -EINTR) + goto out; + if (lookedup) { fi = get_fuse_inode(inode); - if (outarg.nodeid != get_node_id(inode) || - (bool) IS_AUTOMOUNT(inode) != (bool) (outarg.attr.flags & FUSE_ATTR_SUBMOUNT)) { - fuse_queue_forget(fm->fc, forget, - outarg.nodeid, 1); - goto invalid; - } spin_lock(&fi->lock); fi->nlookup++; spin_unlock(&fi->lock); } - kfree(forget); - if (ret == -ENOMEM || ret == -EINTR) - goto out; - if (ret || fuse_invalid_attr(&outarg.attr) || - fuse_stale_inode(inode, outarg.generation, &outarg.attr)) + if (ret <= 0) goto invalid; forget_all_cached_acls(inode); -- 2.25.1