Skip to content

Commit 105c2db

Browse files
author
Trond Myklebust
committed
NFSv4: Fix nfs_clear_verifier_delegated() for delegated directories
If the client returns a directory delegation, then look up all the child dentries, and clear their 'verifier delegated' bit, unless subject to a file delegation. Similarly, if a file delegation is being returned, check if there is a directory delegation before clearing a 'verifier delegated' bit. Reported-by: Christoph Hellwig <hch@lst.de> Fixes: 156b094 ("NFS: Request a directory delegation on ACCESS, CREATE, and UNLINK") Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
1 parent 6f9bda2 commit 105c2db

1 file changed

Lines changed: 49 additions & 8 deletions

File tree

fs/nfs/dir.c

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1440,7 +1440,8 @@ static void nfs_set_verifier_locked(struct dentry *dentry, unsigned long verf)
14401440

14411441
if (!dir || !nfs_verify_change_attribute(dir, verf))
14421442
return;
1443-
if (inode && NFS_PROTO(inode)->have_delegation(inode, FMODE_READ, 0))
1443+
if (NFS_PROTO(dir)->have_delegation(dir, FMODE_READ, 0) ||
1444+
(inode && NFS_PROTO(inode)->have_delegation(inode, FMODE_READ, 0)))
14441445
nfs_set_verifier_delegated(&verf);
14451446
dentry->d_time = verf;
14461447
}
@@ -1465,6 +1466,49 @@ void nfs_set_verifier(struct dentry *dentry, unsigned long verf)
14651466
EXPORT_SYMBOL_GPL(nfs_set_verifier);
14661467

14671468
#if IS_ENABLED(CONFIG_NFS_V4)
1469+
static void nfs_clear_verifier_file(struct inode *inode)
1470+
{
1471+
struct dentry *alias;
1472+
struct inode *dir;
1473+
1474+
hlist_for_each_entry(alias, &inode->i_dentry, d_u.d_alias) {
1475+
spin_lock(&alias->d_lock);
1476+
dir = d_inode_rcu(alias->d_parent);
1477+
if (!dir ||
1478+
!NFS_PROTO(dir)->have_delegation(dir, FMODE_READ, 0))
1479+
nfs_unset_verifier_delegated(&alias->d_time);
1480+
spin_unlock(&alias->d_lock);
1481+
}
1482+
}
1483+
1484+
static void nfs_clear_verifier_directory(struct inode *dir)
1485+
{
1486+
struct dentry *this_parent;
1487+
struct dentry *dentry;
1488+
struct inode *inode;
1489+
1490+
if (hlist_empty(&dir->i_dentry))
1491+
return;
1492+
this_parent =
1493+
hlist_entry(dir->i_dentry.first, struct dentry, d_u.d_alias);
1494+
1495+
spin_lock(&this_parent->d_lock);
1496+
nfs_unset_verifier_delegated(&this_parent->d_time);
1497+
dentry = d_first_child(this_parent);
1498+
hlist_for_each_entry_from(dentry, d_sib) {
1499+
if (unlikely(dentry->d_flags & DCACHE_DENTRY_CURSOR))
1500+
continue;
1501+
inode = d_inode_rcu(dentry);
1502+
if (inode &&
1503+
NFS_PROTO(inode)->have_delegation(inode, FMODE_READ, 0))
1504+
continue;
1505+
spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
1506+
nfs_unset_verifier_delegated(&dentry->d_time);
1507+
spin_unlock(&dentry->d_lock);
1508+
}
1509+
spin_unlock(&this_parent->d_lock);
1510+
}
1511+
14681512
/**
14691513
* nfs_clear_verifier_delegated - clear the dir verifier delegation tag
14701514
* @inode: pointer to inode
@@ -1477,16 +1521,13 @@ EXPORT_SYMBOL_GPL(nfs_set_verifier);
14771521
*/
14781522
void nfs_clear_verifier_delegated(struct inode *inode)
14791523
{
1480-
struct dentry *alias;
1481-
14821524
if (!inode)
14831525
return;
14841526
spin_lock(&inode->i_lock);
1485-
hlist_for_each_entry(alias, &inode->i_dentry, d_u.d_alias) {
1486-
spin_lock(&alias->d_lock);
1487-
nfs_unset_verifier_delegated(&alias->d_time);
1488-
spin_unlock(&alias->d_lock);
1489-
}
1527+
if (S_ISREG(inode->i_mode))
1528+
nfs_clear_verifier_file(inode);
1529+
else if (S_ISDIR(inode->i_mode))
1530+
nfs_clear_verifier_directory(inode);
14901531
spin_unlock(&inode->i_lock);
14911532
}
14921533
EXPORT_SYMBOL_GPL(nfs_clear_verifier_delegated);

0 commit comments

Comments
 (0)