fuse: use iversion for readdir cache verification
authorMiklos Szeredi <mszeredi@redhat.com>
Mon, 1 Oct 2018 08:07:05 +0000 (10:07 +0200)
committerMiklos Szeredi <mszeredi@redhat.com>
Mon, 1 Oct 2018 08:07:05 +0000 (10:07 +0200)
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 <mszeredi@redhat.com>
fs/fuse/dir.c
fs/fuse/fuse_i.h
fs/fuse/readdir.c

index 3a333b0ea9ad954d0145428b02cfffecf617b743..6800fdc3e73094ea9c9472c40e376d2f674a8522 100644 (file)
@@ -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)) {
index 3deb013a289eb033e29525a9147f2301be4f4ce3..d9d1ea78efa6e442dde3ac8362b412f70ada75fd 100644 (file)
@@ -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;
index dafd6543cca29e1b909c140622f8f612497be0ed..ab18b78f475553eba469c6ce71b151e69c6a2dd8 100644 (file)
@@ -8,6 +8,7 @@
 
 
 #include "fuse_i.h"
+#include <linux/iversion.h>
 #include <linux/posix_acl.h>
 #include <linux/pagemap.h>
 #include <linux/highmem.h>
@@ -447,6 +448,7 @@ retry_locked:
                /* 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;
@@ -457,7 +459,8 @@ retry_locked:
         * 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;
                }