- modify dentry revalidation algorithm to check inode/connection generations. If them are not equal then perform revalidation. remark: during forced dentry revalidation we are sending FUSE_LOOKUP request to the userspace daemon and if it return the same inode after lookup then we can "upgrade" inode connection generation without invalidating it. - don't send FUSE_FSYNC, FUSE_RELEASE, etc requests to the userspace daemon about stale inodes (this can confuse libfuse) Cc: Miklos Szeredi <mszeredi@xxxxxxxxxx> Cc: Al Viro <viro@xxxxxxxxxxxxxxxxxx> Cc: Amir Goldstein <amir73il@xxxxxxxxx> Cc: Stéphane Graber <stgraber@xxxxxxxxxx> Cc: Seth Forshee <sforshee@xxxxxxxxxx> Cc: Christian Brauner <brauner@xxxxxxxxxx> Cc: Andrei Vagin <avagin@xxxxxxxxx> Cc: Pavel Tikhomirov <ptikhomirov@xxxxxxxxxxxxx> Cc: linux-fsdevel@xxxxxxxxxxxxxxx Cc: linux-kernel@xxxxxxxxxxxxxxx Cc: criu@xxxxxxxxxx Signed-off-by: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@xxxxxxxxxxxxx> --- fs/fuse/dir.c | 4 +++- fs/fuse/file.c | 6 ++++-- fs/fuse/fuse_i.h | 3 ++- fs/fuse/inode.c | 2 +- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 63625c29d6ef..5ac7d88e5dfc 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -213,7 +213,8 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags) inode = d_inode_rcu(entry); if (inode && fuse_is_bad(inode)) goto invalid; - else if (time_before64(fuse_dentry_time(entry), get_jiffies_64()) || + else if ((inode && fuse_stale_inode_conn(inode)) || + 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); @@ -255,6 +256,7 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags) } spin_lock(&fi->lock); fi->nlookup++; + fi->conn_gen = READ_ONCE(get_fuse_conn(inode)->conn_gen); spin_unlock(&fi->lock); } kfree(forget); diff --git a/fs/fuse/file.c b/fs/fuse/file.c index d5b30faff0b9..be9086a1868d 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -110,7 +110,8 @@ static void fuse_file_put(struct fuse_file *ff, bool sync, bool isdir) if (refcount_dec_and_test(&ff->count)) { struct fuse_args *args = &ff->release_args->args; - if (isdir ? ff->fm->fc->flags.no_opendir : ff->fm->fc->flags.no_open) { + if (fuse_stale_ff(ff) || + (isdir ? ff->fm->fc->flags.no_opendir : ff->fm->fc->flags.no_open)) { /* Do nothing when client does not implement 'open' */ fuse_release_end(ff->fm, args, 0); } else if (sync) { @@ -597,9 +598,10 @@ static int fuse_fsync(struct file *file, loff_t start, loff_t end, { struct inode *inode = file->f_mapping->host; struct fuse_conn *fc = get_fuse_conn(inode); + struct fuse_file *ff = file->private_data; int err; - if (fuse_is_bad(inode)) + if (fuse_stale_ff(ff) || fuse_is_bad(inode)) return -EIO; inode_lock(inode); diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 4f4a6f912c7c..0643de31674d 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -954,7 +954,8 @@ static inline bool fuse_stale_inode(const struct inode *inode, int generation, struct fuse_attr *attr) { return inode->i_generation != generation || - inode_wrong_type(inode, attr->mode); + inode_wrong_type(inode, attr->mode) || + fuse_stale_inode_conn(inode); } static inline void fuse_make_bad(struct inode *inode) diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index c3109e016494..f9dc8971274d 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -124,7 +124,7 @@ static void fuse_evict_inode(struct inode *inode) fuse_dax_inode_cleanup(inode); if (fi->nlookup) { fuse_queue_forget(fc, fi->forget, fi->nodeid, - fi->nlookup, false); + fi->nlookup, fuse_stale_inode_conn(inode)); fi->forget = NULL; } } -- 2.34.1