Commit fec11dd9 authored by Al Viro's avatar Al Viro

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: default avatarAl Viro <viro@zeniv.linux.org.uk>
Reviewed-by: default avatarJeff Layton <jlayton@redhat.com>
parent 0916a5e4
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/kthread.h> #include <linux/kthread.h>
#include <linux/freezer.h> #include <linux/freezer.h>
#include <linux/namei.h>
#include <net/ipv6.h> #include <net/ipv6.h>
#include "cifsfs.h" #include "cifsfs.h"
#include "cifspdu.h" #include "cifspdu.h"
...@@ -542,14 +543,12 @@ static const struct super_operations cifs_super_ops = { ...@@ -542,14 +543,12 @@ static const struct super_operations cifs_super_ops = {
static struct dentry * static struct dentry *
cifs_get_root(struct smb_vol *vol, struct super_block *sb) cifs_get_root(struct smb_vol *vol, struct super_block *sb)
{ {
int xid, rc; struct dentry *dentry;
struct inode *inode;
struct qstr name;
struct dentry *dparent = NULL, *dchild = NULL, *alias;
struct cifs_sb_info *cifs_sb = CIFS_SB(sb); struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
unsigned int i, full_len, len; char *full_path = NULL;
char *full_path = NULL, *pstart; char *s, *p;
char sep; char sep;
int xid;
full_path = cifs_build_path_to_root(vol, cifs_sb, full_path = cifs_build_path_to_root(vol, cifs_sb,
cifs_sb_master_tcon(cifs_sb)); cifs_sb_master_tcon(cifs_sb));
...@@ -560,73 +559,32 @@ cifs_get_root(struct smb_vol *vol, struct super_block *sb) ...@@ -560,73 +559,32 @@ cifs_get_root(struct smb_vol *vol, struct super_block *sb)
xid = GetXid(); xid = GetXid();
sep = CIFS_DIR_SEP(cifs_sb); sep = CIFS_DIR_SEP(cifs_sb);
dparent = dget(sb->s_root); dentry = dget(sb->s_root);
full_len = strlen(full_path); p = s = full_path;
full_path[full_len] = sep;
pstart = full_path + 1; do {
struct inode *dir = dentry->d_inode;
for (i = 1, len = 0; i <= full_len; i++) { struct dentry *child;
if (full_path[i] != sep || !len) {
len++; /* skip separators */
continue; while (*s == sep)
} s++;
if (!*s)
full_path[i] = 0; break;
cFYI(1, "get dentry for %s", pstart); p = s++;
/* next separator */
name.name = pstart; while (*s && *s != sep)
name.len = len; s++;
name.hash = full_name_hash(pstart, len);
dchild = d_lookup(dparent, &name); mutex_lock(&dir->i_mutex);
if (dchild == NULL) { child = lookup_one_len(p, dentry, s - p);
cFYI(1, "not exists"); mutex_unlock(&dir->i_mutex);
dchild = d_alloc(dparent, &name); dput(dentry);
if (dchild == NULL) { dentry = child;
dput(dparent); } while (!IS_ERR(dentry));
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:
_FreeXid(xid); _FreeXid(xid);
kfree(full_path); kfree(full_path);
return dparent; return dentry;
} }
static int cifs_set_super(struct super_block *sb, void *data) static int cifs_set_super(struct super_block *sb, void *data)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment