Commit 261aaba7 authored by Miklos Szeredi's avatar Miklos Szeredi

fuse: use iversion for readdir cache verification

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: default avatarMiklos Szeredi <mszeredi@redhat.com>
parent 7118883b
......@@ -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)) {
......
......@@ -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;
......
......@@ -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 @@ 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;
......@@ -457,7 +459,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;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment