NFSv4: Fix lookup revalidate of regular files [Linux 4.19.64]

This Linux kernel change "NFSv4: Fix lookup revalidate of regular files" is included in the Linux 4.19.64 release. This change is authored by Trond Myklebust <trond.myklebust [at] hammerspace.com> on Fri Sep 28 12:42:51 2018 -0400. The commit for this change in Linux stable tree is 9e441c7 (patch) which is from upstream commit c7944eb. The same Linux upstream change may have been applied to various maintained Linux releases and you can find all Linux releases containing changes from upstream c7944eb.

NFSv4: Fix lookup revalidate of regular files

commit c7944ebb9ce9461079659e9e6ec5baaf73724b3b upstream.

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 <[email protected]>
Signed-off-by: Qian Lu <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>

There are 79 lines of Linux source code added/deleted in this change. Code changes to Linux kernel are as follows.

 fs/nfs/dir.c | 79 ++++++++++++++++++++++++++++++------------------------------
 1 file changed, 39 insertions(+), 40 deletions(-)

diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 2e6a253..71b2e39 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -1231,7 +1231,8 @@ int nfs_neg_need_reval(struct inode *dir, struct dentry *dentry,
 }

 static int
-nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags)
+__nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags,
+           int (*reval)(struct inode *, struct dentry *, unsigned int))
 {
    struct dentry *parent;
    struct inode *dir;
@@ -1242,17 +1243,22 @@ int nfs_neg_need_reval(struct inode *dir, struct dentry *dentry,
        dir = d_inode_rcu(parent);
        if (!dir)
            return -ECHILD;
-       ret = nfs_do_lookup_revalidate(dir, dentry, flags);
+       ret = reval(dir, dentry, flags);
        if (parent != READ_ONCE(dentry->d_parent))
            return -ECHILD;
    } else {
        parent = dget_parent(dentry);
-       ret = nfs_do_lookup_revalidate(d_inode(parent), dentry, flags);
+       ret = reval(d_inode(parent), dentry, flags);
        dput(parent);
    }
    return ret;
 }

+static int nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags)
+{
+   return __nfs_lookup_revalidate(dentry, flags, nfs_do_lookup_revalidate);
+}
+
 /*
  * A weaker form of d_revalidate for revalidating just the d_inode(dentry)
  * when we don't really care about the dentry name. This is called when a
@@ -1609,62 +1615,55 @@ int nfs_atomic_open(struct inode *dir, struct dentry *dentry,
 }
 EXPORT_SYMBOL_GPL(nfs_atomic_open);

-static int nfs4_lookup_revalidate(struct dentry *dentry, unsigned int flags)
+static int
+nfs4_do_lookup_revalidate(struct inode *dir, struct dentry *dentry,
+             unsigned int flags)
 {
    struct inode *inode;
-   int ret = 0;

    if (!(flags & LOOKUP_OPEN) || (flags & LOOKUP_DIRECTORY))
-       goto no_open;
+       goto full_reval;
    if (d_mountpoint(dentry))
-       goto no_open;
-   if (NFS_SB(dentry->d_sb)->caps & NFS_CAP_ATOMIC_OPEN_V1)
-       goto no_open;
+       goto full_reval;

    inode = d_inode(dentry);

    /* We can't create new files in nfs_open_revalidate(), so we
     * optimize away revalidation of negative dentries.
     */
-   if (inode == NULL) {
-       struct dentry *parent;
-       struct inode *dir;
-
-       if (flags & LOOKUP_RCU) {
-           parent = READ_ONCE(dentry->d_parent);
-           dir = d_inode_rcu(parent);
-           if (!dir)
-               return -ECHILD;
-       } else {
-           parent = dget_parent(dentry);
-           dir = d_inode(parent);
-       }
-       if (!nfs_neg_need_reval(dir, dentry, flags))
-           ret = 1;
-       else if (flags & LOOKUP_RCU)
-           ret = -ECHILD;
-       if (!(flags & LOOKUP_RCU))
-           dput(parent);
-       else if (parent != READ_ONCE(dentry->d_parent))
-           return -ECHILD;
-       goto out;
-   }
+   if (inode == NULL)
+       goto full_reval;
+
+   if (NFS_PROTO(dir)->have_delegation(inode, FMODE_READ))
+       return nfs_lookup_revalidate_delegated(dir, dentry, inode);

    /* NFS only supports OPEN on regular files */
    if (!S_ISREG(inode->i_mode))
-       goto no_open;
+       goto full_reval;
+
    /* We cannot do exclusive creation on a positive dentry */
-   if (flags & LOOKUP_EXCL)
-       goto no_open;
+   if (flags & (LOOKUP_EXCL | LOOKUP_REVAL))
+       goto reval_dentry;
+
+   /* Check if the directory changed */
+   if (!nfs_check_verifier(dir, dentry, flags & LOOKUP_RCU))
+       goto reval_dentry;

    /* Let f_op->open() actually open (and revalidate) the file */
-   ret = 1;
+   return 1;
+reval_dentry:
+   if (flags & LOOKUP_RCU)
+       return -ECHILD;
+   return nfs_lookup_revalidate_dentry(dir, dentry, inode);;

-out:
-   return ret;
+full_reval:
+   return nfs_do_lookup_revalidate(dir, dentry, flags);
+}

-no_open:
-   return nfs_lookup_revalidate(dentry, flags);
+static int nfs4_lookup_revalidate(struct dentry *dentry, unsigned int flags)
+{
+   return __nfs_lookup_revalidate(dentry, flags,
+           nfs4_do_lookup_revalidate);
 }

 #endif /* CONFIG_NFSV4 */

Leave a Reply

Your email address will not be published. Required fields are marked *