Skip to content

Commit

Permalink
NFS: Fix up directory verifier races
Browse files Browse the repository at this point in the history
In order to avoid having our dentry revalidation race with an update
of the directory on the server, we need to store the verifier before
the RPC calls to LOOKUP and READDIR.

Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
Reviewed-by: Benjamin Coddington <bcodding@gmail.com>
Tested-by: Benjamin Coddington <bcodding@gmail.com>
Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
  • Loading branch information
trondmy authored and amschuma-ntap committed Feb 10, 2020
1 parent bb6d3fb commit a1147b8
Showing 1 changed file with 14 additions and 7 deletions.
21 changes: 14 additions & 7 deletions fs/nfs/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ typedef struct {
loff_t current_index;
decode_dirent_t decode;

unsigned long dir_verifier;
unsigned long timestamp;
unsigned long gencount;
unsigned int cache_entry_index;
Expand Down Expand Up @@ -353,6 +354,7 @@ int nfs_readdir_xdr_filler(struct page **pages, nfs_readdir_descriptor_t *desc,
again:
timestamp = jiffies;
gencount = nfs_inc_attr_generation_counter();
desc->dir_verifier = nfs_save_change_attribute(inode);
error = NFS_PROTO(inode)->readdir(file_dentry(file), cred, entry->cookie, pages,
NFS_SERVER(inode)->dtsize, desc->plus);
if (error < 0) {
Expand Down Expand Up @@ -455,13 +457,13 @@ void nfs_force_use_readdirplus(struct inode *dir)
}

static
void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry)
void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry,
unsigned long dir_verifier)
{
struct qstr filename = QSTR_INIT(entry->name, entry->len);
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
struct dentry *dentry;
struct dentry *alias;
struct inode *dir = d_inode(parent);
struct inode *inode;
int status;

Expand Down Expand Up @@ -500,7 +502,7 @@ void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry)
if (nfs_same_file(dentry, entry)) {
if (!entry->fh->size)
goto out;
nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
nfs_set_verifier(dentry, dir_verifier);
status = nfs_refresh_inode(d_inode(dentry), entry->fattr);
if (!status)
nfs_setsecurity(d_inode(dentry), entry->fattr, entry->label);
Expand All @@ -526,7 +528,7 @@ void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry)
dput(dentry);
dentry = alias;
}
nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
nfs_set_verifier(dentry, dir_verifier);
out:
dput(dentry);
}
Expand Down Expand Up @@ -564,7 +566,8 @@ int nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *en
count++;

if (desc->plus)
nfs_prime_dcache(file_dentry(desc->file), entry);
nfs_prime_dcache(file_dentry(desc->file), entry,
desc->dir_verifier);

status = nfs_readdir_add_to_array(entry, page);
if (status != 0)
Expand Down Expand Up @@ -1159,6 +1162,7 @@ nfs_lookup_revalidate_dentry(struct inode *dir, struct dentry *dentry,
struct nfs_fh *fhandle;
struct nfs_fattr *fattr;
struct nfs4_label *label;
unsigned long dir_verifier;
int ret;

ret = -ENOMEM;
Expand All @@ -1168,6 +1172,7 @@ nfs_lookup_revalidate_dentry(struct inode *dir, struct dentry *dentry,
if (fhandle == NULL || fattr == NULL || IS_ERR(label))
goto out;

dir_verifier = nfs_save_change_attribute(dir);
ret = NFS_PROTO(dir)->lookup(dir, dentry, fhandle, fattr, label);
if (ret < 0) {
switch (ret) {
Expand All @@ -1188,7 +1193,7 @@ nfs_lookup_revalidate_dentry(struct inode *dir, struct dentry *dentry,
goto out;

nfs_setsecurity(inode, fattr, label);
nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
nfs_set_verifier(dentry, dir_verifier);

/* set a readdirplus hint that we had a cache miss */
nfs_force_use_readdirplus(dir);
Expand Down Expand Up @@ -1415,6 +1420,7 @@ struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, unsigned in
struct nfs_fh *fhandle = NULL;
struct nfs_fattr *fattr = NULL;
struct nfs4_label *label = NULL;
unsigned long dir_verifier;
int error;

dfprintk(VFS, "NFS: lookup(%pd2)\n", dentry);
Expand All @@ -1440,6 +1446,7 @@ struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, unsigned in
if (IS_ERR(label))
goto out;

dir_verifier = nfs_save_change_attribute(dir);
trace_nfs_lookup_enter(dir, dentry, flags);
error = NFS_PROTO(dir)->lookup(dir, dentry, fhandle, fattr, label);
if (error == -ENOENT)
Expand All @@ -1463,7 +1470,7 @@ struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, unsigned in
goto out_label;
dentry = res;
}
nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
nfs_set_verifier(dentry, dir_verifier);
out_label:
trace_nfs_lookup_exit(dir, dentry, flags, error);
nfs4_label_free(label);
Expand Down

0 comments on commit a1147b8

Please sign in to comment.