diff options
author | Trond Myklebust <trond.myklebust@hammerspace.com> | 2018-09-28 12:42:51 -0400 |
---|---|---|
committer | Trond Myklebust <trond.myklebust@hammerspace.com> | 2018-09-30 15:35:18 -0400 |
commit | c7944ebb9ce9461079659e9e6ec5baaf73724b3b (patch) | |
tree | 2f519efaf6144cb3f34952fce4c1f21d95259269 /fs/nfs/dir.c | |
parent | 5ceb9d7fdaaf6d8ced6cd7861cf1deb9cd93fa47 (diff) |
NFSv4: Fix lookup revalidate of regular files
If we're revalidating an existing dentry in order to open a file, we need
to ensure that we check the directory has not changed before we optimise
away the lookup.
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
Diffstat (limited to 'fs/nfs/dir.c')
-rw-r--r-- | fs/nfs/dir.c | 79 |
1 files changed, 39 insertions, 40 deletions
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 2e6a253e4104..71b2e390becf 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c | |||
@@ -1231,7 +1231,8 @@ out_bad: | |||
1231 | } | 1231 | } |
1232 | 1232 | ||
1233 | static int | 1233 | static int |
1234 | nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags) | 1234 | __nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags, |
1235 | int (*reval)(struct inode *, struct dentry *, unsigned int)) | ||
1235 | { | 1236 | { |
1236 | struct dentry *parent; | 1237 | struct dentry *parent; |
1237 | struct inode *dir; | 1238 | struct inode *dir; |
@@ -1242,17 +1243,22 @@ nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags) | |||
1242 | dir = d_inode_rcu(parent); | 1243 | dir = d_inode_rcu(parent); |
1243 | if (!dir) | 1244 | if (!dir) |
1244 | return -ECHILD; | 1245 | return -ECHILD; |
1245 | ret = nfs_do_lookup_revalidate(dir, dentry, flags); | 1246 | ret = reval(dir, dentry, flags); |
1246 | if (parent != READ_ONCE(dentry->d_parent)) | 1247 | if (parent != READ_ONCE(dentry->d_parent)) |
1247 | return -ECHILD; | 1248 | return -ECHILD; |
1248 | } else { | 1249 | } else { |
1249 | parent = dget_parent(dentry); | 1250 | parent = dget_parent(dentry); |
1250 | ret = nfs_do_lookup_revalidate(d_inode(parent), dentry, flags); | 1251 | ret = reval(d_inode(parent), dentry, flags); |
1251 | dput(parent); | 1252 | dput(parent); |
1252 | } | 1253 | } |
1253 | return ret; | 1254 | return ret; |
1254 | } | 1255 | } |
1255 | 1256 | ||
1257 | static int nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags) | ||
1258 | { | ||
1259 | return __nfs_lookup_revalidate(dentry, flags, nfs_do_lookup_revalidate); | ||
1260 | } | ||
1261 | |||
1256 | /* | 1262 | /* |
1257 | * A weaker form of d_revalidate for revalidating just the d_inode(dentry) | 1263 | * A weaker form of d_revalidate for revalidating just the d_inode(dentry) |
1258 | * when we don't really care about the dentry name. This is called when a | 1264 | * when we don't really care about the dentry name. This is called when a |
@@ -1609,62 +1615,55 @@ no_open: | |||
1609 | } | 1615 | } |
1610 | EXPORT_SYMBOL_GPL(nfs_atomic_open); | 1616 | EXPORT_SYMBOL_GPL(nfs_atomic_open); |
1611 | 1617 | ||
1612 | static int nfs4_lookup_revalidate(struct dentry *dentry, unsigned int flags) | 1618 | static int |
1619 | nfs4_do_lookup_revalidate(struct inode *dir, struct dentry *dentry, | ||
1620 | unsigned int flags) | ||
1613 | { | 1621 | { |
1614 | struct inode *inode; | 1622 | struct inode *inode; |
1615 | int ret = 0; | ||
1616 | 1623 | ||
1617 | if (!(flags & LOOKUP_OPEN) || (flags & LOOKUP_DIRECTORY)) | 1624 | if (!(flags & LOOKUP_OPEN) || (flags & LOOKUP_DIRECTORY)) |
1618 | goto no_open; | 1625 | goto full_reval; |
1619 | if (d_mountpoint(dentry)) | 1626 | if (d_mountpoint(dentry)) |
1620 | goto no_open; | 1627 | goto full_reval; |
1621 | if (NFS_SB(dentry->d_sb)->caps & NFS_CAP_ATOMIC_OPEN_V1) | ||
1622 | goto no_open; | ||
1623 | 1628 | ||
1624 | inode = d_inode(dentry); | 1629 | inode = d_inode(dentry); |
1625 | 1630 | ||
1626 | /* We can't create new files in nfs_open_revalidate(), so we | 1631 | /* We can't create new files in nfs_open_revalidate(), so we |
1627 | * optimize away revalidation of negative dentries. | 1632 | * optimize away revalidation of negative dentries. |
1628 | */ | 1633 | */ |
1629 | if (inode == NULL) { | 1634 | if (inode == NULL) |
1630 | struct dentry *parent; | 1635 | goto full_reval; |
1631 | struct inode *dir; | 1636 | |
1632 | 1637 | if (NFS_PROTO(dir)->have_delegation(inode, FMODE_READ)) | |
1633 | if (flags & LOOKUP_RCU) { | 1638 | return nfs_lookup_revalidate_delegated(dir, dentry, inode); |
1634 | parent = READ_ONCE(dentry->d_parent); | ||
1635 | dir = d_inode_rcu(parent); | ||
1636 | if (!dir) | ||
1637 | return -ECHILD; | ||
1638 | } else { | ||
1639 | parent = dget_parent(dentry); | ||
1640 | dir = d_inode(parent); | ||
1641 | } | ||
1642 | if (!nfs_neg_need_reval(dir, dentry, flags)) | ||
1643 | ret = 1; | ||
1644 | else if (flags & LOOKUP_RCU) | ||
1645 | ret = -ECHILD; | ||
1646 | if (!(flags & LOOKUP_RCU)) | ||
1647 | dput(parent); | ||
1648 | else if (parent != READ_ONCE(dentry->d_parent)) | ||
1649 | return -ECHILD; | ||
1650 | goto out; | ||
1651 | } | ||
1652 | 1639 | ||
1653 | /* NFS only supports OPEN on regular files */ | 1640 | /* NFS only supports OPEN on regular files */ |
1654 | if (!S_ISREG(inode->i_mode)) | 1641 | if (!S_ISREG(inode->i_mode)) |
1655 | goto no_open; | 1642 | goto full_reval; |
1643 | |||
1656 | /* We cannot do exclusive creation on a positive dentry */ | 1644 | /* We cannot do exclusive creation on a positive dentry */ |
1657 | if (flags & LOOKUP_EXCL) | 1645 | if (flags & (LOOKUP_EXCL | LOOKUP_REVAL)) |
1658 | goto no_open; | 1646 | goto reval_dentry; |
1647 | |||
1648 | /* Check if the directory changed */ | ||
1649 | if (!nfs_check_verifier(dir, dentry, flags & LOOKUP_RCU)) | ||
1650 | goto reval_dentry; | ||
1659 | 1651 | ||
1660 | /* Let f_op->open() actually open (and revalidate) the file */ | 1652 | /* Let f_op->open() actually open (and revalidate) the file */ |
1661 | ret = 1; | 1653 | return 1; |
1654 | reval_dentry: | ||
1655 | if (flags & LOOKUP_RCU) | ||
1656 | return -ECHILD; | ||
1657 | return nfs_lookup_revalidate_dentry(dir, dentry, inode);; | ||
1662 | 1658 | ||
1663 | out: | 1659 | full_reval: |
1664 | return ret; | 1660 | return nfs_do_lookup_revalidate(dir, dentry, flags); |
1661 | } | ||
1665 | 1662 | ||
1666 | no_open: | 1663 | static int nfs4_lookup_revalidate(struct dentry *dentry, unsigned int flags) |
1667 | return nfs_lookup_revalidate(dentry, flags); | 1664 | { |
1665 | return __nfs_lookup_revalidate(dentry, flags, | ||
1666 | nfs4_do_lookup_revalidate); | ||
1668 | } | 1667 | } |
1669 | 1668 | ||
1670 | #endif /* CONFIG_NFSV4 */ | 1669 | #endif /* CONFIG_NFSV4 */ |