Skip to content

Commit bd4928e

Browse files
author
Trond Myklebust
committed
NFS: Avoid changing nlink when file removes and attribute updates race
If a file removal races with another operation that updates its attributes, then skip the change to nlink, and just mark the attributes as being stale. Reported-by: Aiden Lambert <alambert48@gatech.edu> Fixes: 59a707b ("NFS: Ensure we revalidate the inode correctly after remove or rename") Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
1 parent 6a23ae0 commit bd4928e

1 file changed

Lines changed: 13 additions & 6 deletions

File tree

fs/nfs/dir.c

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1894,13 +1894,15 @@ static int nfs_dentry_delete(const struct dentry *dentry)
18941894
}
18951895

18961896
/* Ensure that we revalidate inode->i_nlink */
1897-
static void nfs_drop_nlink(struct inode *inode)
1897+
static void nfs_drop_nlink(struct inode *inode, unsigned long gencount)
18981898
{
1899+
struct nfs_inode *nfsi = NFS_I(inode);
1900+
18991901
spin_lock(&inode->i_lock);
19001902
/* drop the inode if we're reasonably sure this is the last link */
1901-
if (inode->i_nlink > 0)
1903+
if (inode->i_nlink > 0 && gencount == nfsi->attr_gencount)
19021904
drop_nlink(inode);
1903-
NFS_I(inode)->attr_gencount = nfs_inc_attr_generation_counter();
1905+
nfsi->attr_gencount = nfs_inc_attr_generation_counter();
19041906
nfs_set_cache_invalid(
19051907
inode, NFS_INO_INVALID_CHANGE | NFS_INO_INVALID_CTIME |
19061908
NFS_INO_INVALID_NLINK);
@@ -1914,8 +1916,9 @@ static void nfs_drop_nlink(struct inode *inode)
19141916
static void nfs_dentry_iput(struct dentry *dentry, struct inode *inode)
19151917
{
19161918
if (dentry->d_flags & DCACHE_NFSFS_RENAMED) {
1919+
unsigned long gencount = READ_ONCE(NFS_I(inode)->attr_gencount);
19171920
nfs_complete_unlink(dentry, inode);
1918-
nfs_drop_nlink(inode);
1921+
nfs_drop_nlink(inode, gencount);
19191922
}
19201923
iput(inode);
19211924
}
@@ -2507,9 +2510,11 @@ static int nfs_safe_remove(struct dentry *dentry)
25072510

25082511
trace_nfs_remove_enter(dir, dentry);
25092512
if (inode != NULL) {
2513+
unsigned long gencount = READ_ONCE(NFS_I(inode)->attr_gencount);
2514+
25102515
error = NFS_PROTO(dir)->remove(dir, dentry);
25112516
if (error == 0)
2512-
nfs_drop_nlink(inode);
2517+
nfs_drop_nlink(inode, gencount);
25132518
} else
25142519
error = NFS_PROTO(dir)->remove(dir, dentry);
25152520
if (error == -ENOENT)
@@ -2709,6 +2714,7 @@ int nfs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
27092714
{
27102715
struct inode *old_inode = d_inode(old_dentry);
27112716
struct inode *new_inode = d_inode(new_dentry);
2717+
unsigned long new_gencount = 0;
27122718
struct dentry *dentry = NULL;
27132719
struct rpc_task *task;
27142720
bool must_unblock = false;
@@ -2761,6 +2767,7 @@ int nfs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
27612767
} else {
27622768
block_revalidate(new_dentry);
27632769
must_unblock = true;
2770+
new_gencount = NFS_I(new_inode)->attr_gencount;
27642771
spin_unlock(&new_dentry->d_lock);
27652772
}
27662773

@@ -2800,7 +2807,7 @@ int nfs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
28002807
new_dir, new_dentry, error);
28012808
if (!error) {
28022809
if (new_inode != NULL)
2803-
nfs_drop_nlink(new_inode);
2810+
nfs_drop_nlink(new_inode, new_gencount);
28042811
/*
28052812
* The d_move() should be here instead of in an async RPC completion
28062813
* handler because we need the proper locks to move the dentry. If

0 commit comments

Comments
 (0)