exportfs_decode_fh(): negative pinned may become positive without the parent locked
authorAl Viro <viro@zeniv.linux.org.uk>
Sat, 9 Nov 2019 03:08:29 +0000 (22:08 -0500)
committerAl Viro <viro@zeniv.linux.org.uk>
Sun, 10 Nov 2019 16:56:05 +0000 (11:56 -0500)
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
fs/exportfs/expfs.c

index 09bc687..2dd55b1 100644 (file)
@@ -519,26 +519,33 @@ struct dentry *exportfs_decode_fh(struct vfsmount *mnt, struct fid *fid,
                 * inode is actually connected to the parent.
                 */
                err = exportfs_get_name(mnt, target_dir, nbuf, result);
-               if (!err) {
-                       inode_lock(target_dir->d_inode);
-                       nresult = lookup_one_len(nbuf, target_dir,
-                                                strlen(nbuf));
-                       inode_unlock(target_dir->d_inode);
-                       if (!IS_ERR(nresult)) {
-                               if (nresult->d_inode) {
-                                       dput(result);
-                                       result = nresult;
-                               } else
-                                       dput(nresult);
-                       }
+               if (err) {
+                       dput(target_dir);
+                       goto err_result;
                }
 
+               inode_lock(target_dir->d_inode);
+               nresult = lookup_one_len(nbuf, target_dir, strlen(nbuf));
+               if (!IS_ERR(nresult)) {
+                       if (unlikely(nresult->d_inode != result->d_inode)) {
+                               dput(nresult);
+                               nresult = ERR_PTR(-ESTALE);
+                       }
+               }
+               inode_unlock(target_dir->d_inode);
                /*
                 * At this point we are done with the parent, but it's pinned
                 * by the child dentry anyway.
                 */
                dput(target_dir);
 
+               if (IS_ERR(nresult)) {
+                       err = PTR_ERR(nresult);
+                       goto err_result;
+               }
+               dput(result);
+               result = nresult;
+
                /*
                 * And finally make sure the dentry is actually acceptable
                 * to NFSD.