]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
NFS: Fix inode corruption in nfs_prime_dcache()
authorTrond Myklebust <trond.myklebust@primarydata.com>
Thu, 22 Sep 2016 17:38:52 +0000 (13:38 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 28 Oct 2016 07:45:25 +0000 (03:45 -0400)
commit 7dc72d5f7a0ec97a53e126c46e2cbd2560757955 upstream.

Due to inode number reuse in filesystems, we can end up corrupting the
inode on our client if we apply the file attributes without ensuring that
the filehandle matches.
Typical symptoms include spurious "mode changed" reports in the syslog.

We still do want to ensure that we don't invalidate the dentry if the
inode number matches, but we don't have a filehandle.

Fixes: fa9233699cc1 ("NFS: Don't require a filehandle to refresh...")
Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
Tested-by: Oleg Drokin <green@linuxhacker.ru>
Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/nfs/dir.c

index 177fefb26c18d344a30dfb85f75db61c7a6e44ec..6bc5a68e39f1c0a0bf56e08e88c6da65ce4862f1 100644 (file)
@@ -435,11 +435,11 @@ int nfs_same_file(struct dentry *dentry, struct nfs_entry *entry)
                return 0;
 
        nfsi = NFS_I(inode);
-       if (entry->fattr->fileid == nfsi->fileid)
-               return 1;
-       if (nfs_compare_fh(entry->fh, &nfsi->fh) == 0)
-               return 1;
-       return 0;
+       if (entry->fattr->fileid != nfsi->fileid)
+               return 0;
+       if (entry->fh->size && nfs_compare_fh(entry->fh, &nfsi->fh) != 0)
+               return 0;
+       return 1;
 }
 
 static
@@ -517,6 +517,8 @@ again:
                                        &entry->fattr->fsid))
                        goto out;
                if (nfs_same_file(dentry, entry)) {
+                       if (!entry->fh->size)
+                               goto out;
                        nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
                        status = nfs_refresh_inode(d_inode(dentry), entry->fattr);
                        if (!status)
@@ -529,6 +531,10 @@ again:
                        goto again;
                }
        }
+       if (!entry->fh->size) {
+               d_lookup_done(dentry);
+               goto out;
+       }
 
        inode = nfs_fhget(dentry->d_sb, entry->fh, entry->fattr, entry->label);
        alias = d_splice_alias(inode, dentry);