It currently calls d_lookup to get a dentry and then passes that to
d_materialise_unique. This is wrong as d_materialise_unique is intended
to introduce a new dentry into the tree. It also uses d_lookup when
lookup_one_len would generally be a better choice since it does
permission checks.

Also, fix the dentry hash calculation to work with nocase mounts.

Cc: Pavel Shilovsky <[email protected]>
Reported-by: Al Viro <[email protected]>
Signed-off-by: Jeff Layton <[email protected]>
---
 fs/cifs/cifsfs.c |   73 +++++++++++++++++++++++++++++++-----------------------
 1 files changed, 42 insertions(+), 31 deletions(-)

diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 9dd4375..fdb96eb 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -35,6 +35,7 @@
 #include <linux/delay.h>
 #include <linux/kthread.h>
 #include <linux/freezer.h>
+#include <linux/namei.h>
 #include <net/ipv6.h>
 #include "cifsfs.h"
 #include "cifspdu.h"
@@ -556,47 +557,57 @@ cifs_get_root(struct smb_vol *vol, struct super_block *sb)
                full_path[i] = 0;
                cFYI(1, "get dentry for %s", pstart);
 
+               dchild = lookup_one_len(pstart, dparent, len);
+               if (dchild != NULL) {
+                       if (dchild->d_inode != NULL)
+                               goto next_component;
+                       cFYI(1, "dentry is negative");
+                       dput(dchild);
+               } else {
+                       cFYI(1, "dentry does not exist");
+               }
+
                name.name = pstart;
                name.len = len;
-               name.hash = full_name_hash(pstart, len);
-               dchild = d_lookup(dparent, &name);
+               if (dparent->d_op && dparent->d_op->d_hash)
+                       dparent->d_op->d_hash(dparent, dparent->d_inode, &name);
+               else
+                       name.hash = full_name_hash(pstart, len);
+
+               dchild = d_alloc(dparent, &name);
                if (dchild == NULL) {
-                       cFYI(1, "not exists");
-                       dchild = d_alloc(dparent, &name);
-                       if (dchild == NULL) {
-                               dput(dparent);
-                               dparent = ERR_PTR(-ENOMEM);
-                               goto out;
-                       }
+                       dput(dparent);
+                       dparent = ERR_PTR(-ENOMEM);
+                       goto out;
                }
 
                cFYI(1, "get inode");
-               if (dchild->d_inode == NULL) {
-                       cFYI(1, "not exists");
-                       inode = NULL;
-                       if (cifs_sb_master_tcon(CIFS_SB(sb))->unix_ext)
-                               rc = cifs_get_inode_info_unix(&inode, full_path,
-                                                             sb, xid);
-                       else
-                               rc = cifs_get_inode_info(&inode, full_path,
-                                                        NULL, sb, xid, NULL);
-                       if (rc) {
-                               dput(dchild);
+               inode = NULL;
+               if (cifs_sb_master_tcon(CIFS_SB(sb))->unix_ext)
+                       rc = cifs_get_inode_info_unix(&inode, full_path,
+                                                     sb, xid);
+               else
+                       rc = cifs_get_inode_info(&inode, full_path,
+                                                NULL, sb, xid, NULL);
+               if (rc) {
+                       dput(dchild);
+                       dput(dparent);
+                       dparent = ERR_PTR(rc);
+                       goto out;
+               }
+
+               alias = d_materialise_unique(dchild, inode);
+               if (alias != NULL) {
+                       dput(dchild);
+                       if (IS_ERR(alias)) {
                                dput(dparent);
-                               dparent = ERR_PTR(rc);
+                               dparent = ERR_CAST(alias);
                                goto out;
                        }
-                       alias = d_materialise_unique(dchild, inode);
-                       if (alias != NULL) {
-                               dput(dchild);
-                               if (IS_ERR(alias)) {
-                                       dput(dparent);
-                                       dparent = ERR_PTR(-EINVAL); /* XXX */
-                                       goto out;
-                               }
-                               dchild = alias;
-                       }
+                       dchild = alias;
                }
+
+next_component:
                cFYI(1, "parent %p, child %p", dparent, dchild);
 
                dput(dparent);
-- 
1.7.6

--
To unsubscribe from this list: send the line "unsubscribe linux-cifs" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to