Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs-2.6
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs-2.6: vfs: fix race in rcu lookup of pruned dentry Fix cifs_get_root() [ Edited the last commit to get rid of a 'unused variable "seq"' warning due to Al editing the patch. - Linus ]
This commit is contained in:
commit
e501f29c72
@ -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 = {
|
|||||||
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)
|
|||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
for (i = 1, len = 0; i <= full_len; i++) {
|
do {
|
||||||
if (full_path[i] != sep || !len) {
|
struct inode *dir = dentry->d_inode;
|
||||||
len++;
|
struct dentry *child;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
full_path[i] = 0;
|
/* skip separators */
|
||||||
cFYI(1, "get dentry for %s", pstart);
|
while (*s == sep)
|
||||||
|
s++;
|
||||||
|
if (!*s)
|
||||||
|
break;
|
||||||
|
p = s++;
|
||||||
|
/* next separator */
|
||||||
|
while (*s && *s != sep)
|
||||||
|
s++;
|
||||||
|
|
||||||
name.name = pstart;
|
mutex_lock(&dir->i_mutex);
|
||||||
name.len = len;
|
child = lookup_one_len(p, dentry, s - p);
|
||||||
name.hash = full_name_hash(pstart, len);
|
mutex_unlock(&dir->i_mutex);
|
||||||
dchild = d_lookup(dparent, &name);
|
dput(dentry);
|
||||||
if (dchild == NULL) {
|
dentry = child;
|
||||||
cFYI(1, "not exists");
|
} while (!IS_ERR(dentry));
|
||||||
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:
|
|
||||||
_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)
|
||||||
|
@ -942,7 +942,6 @@ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
|
|||||||
* Don't forget we might have a non-mountpoint managed dentry
|
* Don't forget we might have a non-mountpoint managed dentry
|
||||||
* that wants to block transit.
|
* that wants to block transit.
|
||||||
*/
|
*/
|
||||||
*inode = path->dentry->d_inode;
|
|
||||||
if (unlikely(managed_dentry_might_block(path->dentry)))
|
if (unlikely(managed_dentry_might_block(path->dentry)))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -955,6 +954,12 @@ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
|
|||||||
path->mnt = mounted;
|
path->mnt = mounted;
|
||||||
path->dentry = mounted->mnt_root;
|
path->dentry = mounted->mnt_root;
|
||||||
nd->seq = read_seqcount_begin(&path->dentry->d_seq);
|
nd->seq = read_seqcount_begin(&path->dentry->d_seq);
|
||||||
|
/*
|
||||||
|
* Update the inode too. We don't need to re-check the
|
||||||
|
* dentry sequence number here after this d_inode read,
|
||||||
|
* because a mount-point is always pinned.
|
||||||
|
*/
|
||||||
|
*inode = path->dentry->d_inode;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user