fs/fuse/dir.c | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-)
We shall send forget request if lookup/create/open success but returned
outarg.attr is invalid, because FUSEdaemon had increase the lookup count
Reported-by: Xie Yongji <xieyongji@bytedance.com>
Signed-off-by: Zhang Tianci <zhangtianci.1997@bytedance.com>
---
Changes in v2:
- Fix wrong usage of goto label. [Bernd]
- Link to v1: https://lore.kernel.org/lkml/20260202120023.74357-1-zhangtianci.1997@bytedance.com/
fs/fuse/dir.c | 36 ++++++++++++++++++++++++++----------
1 file changed, 26 insertions(+), 10 deletions(-)
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 4b6b3d2758ff2..be25934b86105 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -578,8 +578,10 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr *name
goto out_put_forget;
err = -EIO;
- if (fuse_invalid_attr(&outarg->attr))
- goto out_put_forget;
+ if (fuse_invalid_attr(&outarg->attr)) {
+ fuse_queue_forget(fm->fc, forget, outarg->nodeid, 1);
+ goto out;
+ }
if (outarg->nodeid == FUSE_ROOT_ID && outarg->generation != 0) {
pr_warn_once("root generation should be zero\n");
outarg->generation = 0;
@@ -878,14 +880,24 @@ static int fuse_create_open(struct mnt_idmap *idmap, struct inode *dir,
if (err)
goto out_free_ff;
- err = -EIO;
- if (!S_ISREG(outentry.attr.mode) || invalid_nodeid(outentry.nodeid) ||
- fuse_invalid_attr(&outentry.attr))
- goto out_free_ff;
-
ff->fh = outopenp->fh;
ff->nodeid = outentry.nodeid;
ff->open_flags = outopenp->open_flags;
+
+ err = -EIO;
+ if (invalid_nodeid(outentry.nodeid)) {
+ flags &= ~(O_CREAT | O_EXCL | O_TRUNC);
+ fuse_sync_release(NULL, ff, flags);
+ goto out_put_forget_req;
+ }
+
+ if (!S_ISREG(outentry.attr.mode) || fuse_invalid_attr(&outentry.attr)) {
+ flags &= ~(O_CREAT | O_EXCL | O_TRUNC);
+ fuse_sync_release(NULL, ff, flags);
+ fuse_queue_forget(fm->fc, forget, outentry.nodeid, 1);
+ goto out_err;
+ }
+
inode = fuse_iget(dir->i_sb, outentry.nodeid, outentry.generation,
&outentry.attr, ATTR_TIMEOUT(&outentry), 0, 0);
if (!inode) {
@@ -1007,11 +1019,14 @@ static struct dentry *create_new_entry(struct mnt_idmap *idmap, struct fuse_moun
goto out_put_forget_req;
err = -EIO;
- if (invalid_nodeid(outarg.nodeid) || fuse_invalid_attr(&outarg.attr))
+ if (invalid_nodeid(outarg.nodeid))
goto out_put_forget_req;
- if ((outarg.attr.mode ^ mode) & S_IFMT)
- goto out_put_forget_req;
+ if (fuse_invalid_attr(&outarg.attr) ||
+ ((outarg.attr.mode ^ mode) & S_IFMT)) {
+ fuse_queue_forget(fm->fc, forget, outarg.nodeid, 1);
+ goto out_err;
+ }
inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation,
&outarg.attr, ATTR_TIMEOUT(&outarg), 0, 0);
@@ -1040,6 +1055,7 @@ static struct dentry *create_new_entry(struct mnt_idmap *idmap, struct fuse_moun
if (err == -EEXIST)
fuse_invalidate_entry(entry);
kfree(forget);
+out_err:
return ERR_PTR(err);
}
--
2.39.5
On Thu, 26 Feb 2026 at 13:50, Zhang Tianci <zhangtianci.1997@bytedance.com> wrote: > > We shall send forget request if lookup/create/open success but returned > outarg.attr is invalid, because FUSEdaemon had increase the lookup count These are cases when the fuse daemon is broken anyway. Keeping it working does not make much sense, and adds complexity to the kernel. So NAK unless there's a use case which explains why this would be a good thing. Thanks, Miklos
© 2016 - 2026 Red Hat, Inc.