Skip to content

Commit

Permalink
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel…
Browse files Browse the repository at this point in the history
…/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 ]
  • Loading branch information
torvalds committed Jul 20, 2011
2 parents 3a5c374 + 5943026 commit e501f29
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 72 deletions.
100 changes: 29 additions & 71 deletions fs/cifs/cifsfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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));
Expand All @@ -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)
Expand Down
7 changes: 6 additions & 1 deletion fs/namei.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
* that wants to block transit.
*/
*inode = path->dentry->d_inode;
if (unlikely(managed_dentry_might_block(path->dentry)))
return false;

Expand All @@ -955,6 +954,12 @@ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
path->mnt = mounted;
path->dentry = mounted->mnt_root;
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;
}
Expand Down

0 comments on commit e501f29

Please sign in to comment.