linux-fsdevel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] cifs: fix usage of d_materialise_unique in cifs_get_root
@ 2011-07-18 12:03 Jeff Layton
  2011-07-18 16:45 ` Al Viro
  0 siblings, 1 reply; 5+ messages in thread
From: Jeff Layton @ 2011-07-18 12:03 UTC (permalink / raw)
  To: smfrench; +Cc: linux-cifs, linux-fsdevel, piastryyy, viro

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 <piastryyy@gmail.com>
Reported-by: Al Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: Jeff Layton <jlayton@redhat.com>
---
 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


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH] cifs: fix usage of d_materialise_unique in cifs_get_root
  2011-07-18 12:03 [PATCH] cifs: fix usage of d_materialise_unique in cifs_get_root Jeff Layton
@ 2011-07-18 16:45 ` Al Viro
  2011-07-18 17:34   ` Al Viro
  0 siblings, 1 reply; 5+ messages in thread
From: Al Viro @ 2011-07-18 16:45 UTC (permalink / raw)
  To: Jeff Layton; +Cc: smfrench, linux-cifs, linux-fsdevel, piastryyy

On Mon, Jul 18, 2011 at 08:03:14AM -0400, Jeff Layton wrote:
> 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.

Huh?  First of all, lookup_one_len() doesn't return NULL on failure,
it returns ERR_PTR().  What's more, it already does d_alloc(), ->lookup(),
etc. and you don't need to bother with d_materialise_unique() and this
lookup-by-hand code in there.  Or with calculating hash - also done
by lookup_one_len(), TYVM...  If anything, I'd start with this as the first
approximation and probably looked into simplifying the loop a bit more -
lookup_one_len() doesn't need name component to be NUL-terminated...

diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 112fbd96..2d74619 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -574,51 +574,13 @@ cifs_get_root(struct smb_vol *vol, struct super_block *sb)
 		full_path[i] = 0;
 		cFYI(1, "get dentry for %s", pstart);
 
-		name.name = pstart;
-		name.len = len;
-		name.hash = full_name_hash(pstart, len);
-		dchild = d_lookup(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;
-			}
-		}
-
-		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);
-				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(-EINVAL); /* XXX */
-					goto out;
-				}
-				dchild = alias;
-			}
-		}
-		cFYI(1, "parent %p, child %p", dparent, dchild);
-
+		mutex_lock(&dparent->d_inode->i_mutex);
+		dchild = lookup_one_len(pstart, dparent->d_inode, len);
+		mutex_unlock(&dparent->d_inode->i_mutex);
 		dput(dparent);
 		dparent = dchild;
+		if (IS_ERR(dparent))
+			break;
 		len = 0;
 		pstart = full_path + i + 1;
 		full_path[i] = sep;

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH] cifs: fix usage of d_materialise_unique in cifs_get_root
  2011-07-18 16:45 ` Al Viro
@ 2011-07-18 17:34   ` Al Viro
       [not found]     ` <20110718173454.GC11013-3bDd1+5oDREiFSDQTTA3OLVCufUGDwFn@public.gmane.org>
  0 siblings, 1 reply; 5+ messages in thread
From: Al Viro @ 2011-07-18 17:34 UTC (permalink / raw)
  To: Jeff Layton; +Cc: smfrench, linux-cifs, linux-fsdevel, piastryyy

On Mon, Jul 18, 2011 at 05:45:02PM +0100, Al Viro wrote:
> lookup-by-hand code in there.  Or with calculating hash - also done
> by lookup_one_len(), TYVM...  If anything, I'd start with this as the first
> approximation and probably looked into simplifying the loop a bit more -
> lookup_one_len() doesn't need name component to be NUL-terminated...

Fix cifs_get_root()

Add missing ->i_mutex, convert to lookup_one_len() instead of
(broken) open-coded analog, cope with getting something like
a//b as relative pathname.  Simplify the hell out of it, while
we are there...

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
---
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 112fbd96..cbbb55e 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"
@@ -542,14 +543,12 @@ static const struct super_operations cifs_super_ops = {
 static struct dentry *
 cifs_get_root(struct smb_vol *vol, struct super_block *sb)
 {
-	int xid, rc;
-	struct inode *inode;
-	struct qstr name;
-	struct dentry *dparent = NULL, *dchild = NULL, *alias;
+	struct dentry *dentry;
 	struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
-	unsigned int i, full_len, len;
-	char *full_path = NULL, *pstart;
+	char *full_path = NULL;
+	char *s, *p;
 	char sep;
+	int xid;
 
 	full_path = cifs_build_path_to_root(vol, cifs_sb,
 					    cifs_sb_master_tcon(cifs_sb));
@@ -560,73 +559,32 @@ cifs_get_root(struct smb_vol *vol, struct super_block *sb)
 
 	xid = GetXid();
 	sep = CIFS_DIR_SEP(cifs_sb);
-	dparent = dget(sb->s_root);
-	full_len = strlen(full_path);
-	full_path[full_len] = sep;
-	pstart = full_path + 1;
-
-	for (i = 1, len = 0; i <= full_len; i++) {
-		if (full_path[i] != sep || !len) {
-			len++;
-			continue;
-		}
-
-		full_path[i] = 0;
-		cFYI(1, "get dentry for %s", pstart);
-
-		name.name = pstart;
-		name.len = len;
-		name.hash = full_name_hash(pstart, len);
-		dchild = d_lookup(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;
-			}
-		}
-
-		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);
-				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(-EINVAL); /* XXX */
-					goto out;
-				}
-				dchild = alias;
-			}
-		}
-		cFYI(1, "parent %p, child %p", dparent, dchild);
-
-		dput(dparent);
-		dparent = dchild;
-		len = 0;
-		pstart = full_path + i + 1;
-		full_path[i] = sep;
-	}
-out:
+	dentry = dget(sb->s_root);
+	p = s = full_path;
+
+	do {
+		struct inode *dir = dentry->d_inode;
+		struct dentry *child;
+
+		/* skip separators */
+		while (*s == sep)
+			s++;
+		if (!*s)
+			break;
+		p = s++;
+		/* next separator */
+		while (*s && *s != sep)
+			s++;
+
+		mutex_lock(&dir->i_mutex);
+		child = lookup_one_len(p, dentry, s - p);
+		mutex_unlock(&dir->i_mutex);
+		dput(dentry);
+		dentry = child;
+	} while (!IS_ERR(dentry));
 	_FreeXid(xid);
 	kfree(full_path);
-	return dparent;
+	return dentry;
 }
 
 static int cifs_set_super(struct super_block *sb, void *data)

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH] cifs: fix usage of d_materialise_unique in cifs_get_root
       [not found]     ` <20110718173454.GC11013-3bDd1+5oDREiFSDQTTA3OLVCufUGDwFn@public.gmane.org>
@ 2011-07-18 17:49       ` Jeff Layton
  2011-07-18 17:54         ` Al Viro
  0 siblings, 1 reply; 5+ messages in thread
From: Jeff Layton @ 2011-07-18 17:49 UTC (permalink / raw)
  To: Al Viro
  Cc: smfrench-Re5JQEeQqe8AvxtiuMwx3w,
	linux-cifs-u79uwXL29TY76Z2rM5mHXA,
	linux-fsdevel-u79uwXL29TY76Z2rM5mHXA,
	piastryyy-Re5JQEeQqe8AvxtiuMwx3w

On Mon, 18 Jul 2011 18:34:54 +0100
Al Viro <viro-3bDd1+5oDREiFSDQTTA3OLVCufUGDwFn@public.gmane.org> wrote:

> On Mon, Jul 18, 2011 at 05:45:02PM +0100, Al Viro wrote:
> > lookup-by-hand code in there.  Or with calculating hash - also done
> > by lookup_one_len(), TYVM...  If anything, I'd start with this as the first
> > approximation and probably looked into simplifying the loop a bit more -
> > lookup_one_len() doesn't need name component to be NUL-terminated...
> 
> Fix cifs_get_root()
> 
> Add missing ->i_mutex, convert to lookup_one_len() instead of
> (broken) open-coded analog, cope with getting something like
> a//b as relative pathname.  Simplify the hell out of it, while
> we are there...
> 
> Signed-off-by: Al Viro <viro-RmSDqhL/yNMiFSDQTTA3OLVCufUGDwFn@public.gmane.org>
> ---
> diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
> index 112fbd96..cbbb55e 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"
> @@ -542,14 +543,12 @@ static const struct super_operations cifs_super_ops = {
>  static struct dentry *
>  cifs_get_root(struct smb_vol *vol, struct super_block *sb)
>  {
> -	int xid, rc;
> -	struct inode *inode;
> -	struct qstr name;
> -	struct dentry *dparent = NULL, *dchild = NULL, *alias;
> +	struct dentry *dentry;
>  	struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
> -	unsigned int i, full_len, len;
> -	char *full_path = NULL, *pstart;
> +	char *full_path = NULL;
> +	char *s, *p;
>  	char sep;
> +	int xid;
>  
>  	full_path = cifs_build_path_to_root(vol, cifs_sb,
>  					    cifs_sb_master_tcon(cifs_sb));
> @@ -560,73 +559,32 @@ cifs_get_root(struct smb_vol *vol, struct super_block *sb)
>  
>  	xid = GetXid();
>  	sep = CIFS_DIR_SEP(cifs_sb);
> -	dparent = dget(sb->s_root);
> -	full_len = strlen(full_path);
> -	full_path[full_len] = sep;
> -	pstart = full_path + 1;
> -
> -	for (i = 1, len = 0; i <= full_len; i++) {
> -		if (full_path[i] != sep || !len) {
> -			len++;
> -			continue;
> -		}
> -
> -		full_path[i] = 0;
> -		cFYI(1, "get dentry for %s", pstart);
> -
> -		name.name = pstart;
> -		name.len = len;
> -		name.hash = full_name_hash(pstart, len);
> -		dchild = d_lookup(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;
> -			}
> -		}
> -
> -		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);
> -				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(-EINVAL); /* XXX */
> -					goto out;
> -				}
> -				dchild = alias;
> -			}
> -		}
> -		cFYI(1, "parent %p, child %p", dparent, dchild);
> -
> -		dput(dparent);
> -		dparent = dchild;
> -		len = 0;
> -		pstart = full_path + i + 1;
> -		full_path[i] = sep;
> -	}
> -out:
> +	dentry = dget(sb->s_root);
> +	p = s = full_path;
> +
> +	do {
> +		struct inode *dir = dentry->d_inode;
> +		struct dentry *child;
> +
> +		/* skip separators */
> +		while (*s == sep)
> +			s++;
> +		if (!*s)
> +			break;
> +		p = s++;
> +		/* next separator */
> +		while (*s && *s != sep)
> +			s++;
> +
> +		mutex_lock(&dir->i_mutex);
> +		child = lookup_one_len(p, dentry, s - p);
> +		mutex_unlock(&dir->i_mutex);
> +		dput(dentry);
> +		dentry = child;
> +	} while (!IS_ERR(dentry));
>  	_FreeXid(xid);
>  	kfree(full_path);
> -	return dparent;
> +	return dentry;
>  }
>  
>  static int cifs_set_super(struct super_block *sb, void *data)

Thanks, Al...

Looks good to me. I also gave it some basic smoke testing and it seems
to do the right thing.

Reviewed-by: Jeff Layton <jlayton-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] cifs: fix usage of d_materialise_unique in cifs_get_root
  2011-07-18 17:49       ` Jeff Layton
@ 2011-07-18 17:54         ` Al Viro
  0 siblings, 0 replies; 5+ messages in thread
From: Al Viro @ 2011-07-18 17:54 UTC (permalink / raw)
  To: Jeff Layton; +Cc: smfrench, linux-cifs, linux-fsdevel, piastryyy

On Mon, Jul 18, 2011 at 01:49:42PM -0400, Jeff Layton wrote:

> Thanks, Al...
> 
> Looks good to me. I also gave it some basic smoke testing and it seems
> to do the right thing.
> 
> Reviewed-by: Jeff Layton <jlayton@redhat.com>

OK, it's in #for-linus2

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2011-07-18 17:54 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-07-18 12:03 [PATCH] cifs: fix usage of d_materialise_unique in cifs_get_root Jeff Layton
2011-07-18 16:45 ` Al Viro
2011-07-18 17:34   ` Al Viro
     [not found]     ` <20110718173454.GC11013-3bDd1+5oDREiFSDQTTA3OLVCufUGDwFn@public.gmane.org>
2011-07-18 17:49       ` Jeff Layton
2011-07-18 17:54         ` Al Viro

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).