On 8/12/21 8:09 PM, Jeffle Xu wrote:
From: Miklos Szeredi <mszeredi(a)redhat.com>
commit 261aaba72fdba17b74a3a434d9f925b43d90e958 upstream
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(a)redhat.com>
Signed-off-by: Jeffle Xu <jefflexu(a)linux.alibaba.com>
Reviewed-by: Joseph Qi <joseph.qi(a)linux.alibaba.com>
---
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 7d76b615ee11..27814ce8b52a 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)
@@ -105,6 +106,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.
@@ -463,7 +470,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) {
fi = get_fuse_inode(inode);
@@ -578,7 +585,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:
@@ -688,7 +695,7 @@ static int fuse_unlink(struct inode *dir, struct dentry *entry)
drop_nlink(inode);
spin_unlock(&fi->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)
@@ -710,7 +717,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);
@@ -749,9 +756,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)) {
@@ -986,7 +993,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 6f1e69a9d0ce..086b0c4c4a59 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -158,6 +158,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 6e86bea9c4bf..656afd035e21 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>
#include <linux/pagemap.h>
#include <linux/highmem.h>
@@ -449,6 +450,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;
@@ -459,7 +461,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;
}