Use the internal iversion counter to make sure modifications of the
directory through this filesystem are not missed by the mtime check (due to
mtime granularity).

Signed-off-by: Miklos Szeredi <[email protected]>
---
 fs/fuse/dir.c     | 21 ++++++++++++++-------
 fs/fuse/fuse_i.h  |  3 +++
 fs/fuse/readdir.c |  5 ++++-
 3 files changed, 21 insertions(+), 8 deletions(-)

diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 3a333b0ea9ad..6800fdc3e730 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -14,6 +14,7 @@
 #include <linux/namei.h>
 #include <linux/slab.h>
 #include <linux/xattr.h>
+#include <linux/iversion.h>
 #include <linux/posix_acl.h>
 
 static void fuse_advise_use_readdirplus(struct inode *dir)
@@ -89,6 +90,12 @@ void fuse_invalidate_attr(struct inode *inode)
        get_fuse_inode(inode)->i_time = 0;
 }
 
+static void fuse_dir_changed(struct inode *dir)
+{
+       fuse_invalidate_attr(dir);
+       inode_maybe_inc_iversion(dir, false);
+}
+
 /**
  * Mark the attributes as stale due to an atime change.  Avoid the invalidate 
if
  * atime is not used.
@@ -447,7 +454,7 @@ static int fuse_create_open(struct inode *dir, struct 
dentry *entry,
        kfree(forget);
        d_instantiate(entry, inode);
        fuse_change_entry_timeout(entry, &outentry);
-       fuse_invalidate_attr(dir);
+       fuse_dir_changed(dir);
        err = finish_open(file, entry, generic_file_open);
        if (err) {
                fuse_sync_release(ff, flags);
@@ -561,7 +568,7 @@ static int create_new_entry(struct fuse_conn *fc, struct 
fuse_args *args,
        } else {
                fuse_change_entry_timeout(entry, &outarg);
        }
-       fuse_invalidate_attr(dir);
+       fuse_dir_changed(dir);
        return 0;
 
  out_put_forget_req:
@@ -671,7 +678,7 @@ static int fuse_unlink(struct inode *dir, struct dentry 
*entry)
                        drop_nlink(inode);
                spin_unlock(&fc->lock);
                fuse_invalidate_attr(inode);
-               fuse_invalidate_attr(dir);
+               fuse_dir_changed(dir);
                fuse_invalidate_entry_cache(entry);
                fuse_update_ctime(inode);
        } else if (err == -EINTR)
@@ -693,7 +700,7 @@ static int fuse_rmdir(struct inode *dir, struct dentry 
*entry)
        err = fuse_simple_request(fc, &args);
        if (!err) {
                clear_nlink(d_inode(entry));
-               fuse_invalidate_attr(dir);
+               fuse_dir_changed(dir);
                fuse_invalidate_entry_cache(entry);
        } else if (err == -EINTR)
                fuse_invalidate_entry(entry);
@@ -732,9 +739,9 @@ static int fuse_rename_common(struct inode *olddir, struct 
dentry *oldent,
                        fuse_update_ctime(d_inode(newent));
                }
 
-               fuse_invalidate_attr(olddir);
+               fuse_dir_changed(olddir);
                if (olddir != newdir)
-                       fuse_invalidate_attr(newdir);
+                       fuse_dir_changed(newdir);
 
                /* newent will end up negative */
                if (!(flags & RENAME_EXCHANGE) && d_really_is_positive(newent)) 
{
@@ -967,7 +974,7 @@ int fuse_reverse_inval_entry(struct super_block *sb, u64 
parent_nodeid,
        if (!entry)
                goto unlock;
 
-       fuse_invalidate_attr(parent);
+       fuse_dir_changed(parent);
        fuse_invalidate_entry(entry);
 
        if (child_nodeid != 0 && d_really_is_positive(entry)) {
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index e498f9edf01f..1e8d20e86760 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -120,6 +120,9 @@ struct fuse_inode {
                /** modification time of directory when cache was started */
                struct timespec64 mtime;
 
+               /** iversion of directory when cache was started */
+               u64 iversion;
+
                /** protects above fields */
                spinlock_t lock;
        } rdc;
diff --git a/fs/fuse/readdir.c b/fs/fuse/readdir.c
index f96525cef518..d91ae7449b73 100644
--- a/fs/fuse/readdir.c
+++ b/fs/fuse/readdir.c
@@ -8,6 +8,7 @@
 
 
 #include "fuse_i.h"
+#include <linux/iversion.h>
 #include <linux/posix_acl.h>
 
 static bool fuse_use_readdirplus(struct inode *dir, struct dir_context *ctx)
@@ -445,6 +446,7 @@ static int fuse_readdir_cached(struct file *file, struct 
dir_context *ctx)
                /* Starting cache? Set cache mtime. */
                if (!ctx->pos && !fi->rdc.size) {
                        fi->rdc.mtime = inode->i_mtime;
+                       fi->rdc.iversion = inode_query_iversion(inode);
                }
                spin_unlock(&fi->rdc.lock);
                return UNCACHED;
@@ -455,7 +457,8 @@ static int fuse_readdir_cached(struct file *file, struct 
dir_context *ctx)
         * changed, and reset the cache if so.
         */
        if (!ctx->pos) {
-               if (!timespec64_equal(&fi->rdc.mtime, &inode->i_mtime)) {
+               if (inode_peek_iversion(inode) != fi->rdc.iversion ||
+                   !timespec64_equal(&fi->rdc.mtime, &inode->i_mtime)) {
                        fuse_rdc_reset(inode);
                        goto retry_locked;
                }
-- 
2.14.3

Reply via email to