Skip to content

Commit e501f29

Browse files
committed
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 ]
2 parents 3a5c374 + 5943026 commit e501f29

File tree

2 files changed

+35
-72
lines changed

2 files changed

+35
-72
lines changed

fs/cifs/cifsfs.c

Lines changed: 29 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include <linux/delay.h>
3636
#include <linux/kthread.h>
3737
#include <linux/freezer.h>
38+
#include <linux/namei.h>
3839
#include <net/ipv6.h>
3940
#include "cifsfs.h"
4041
#include "cifspdu.h"
@@ -542,14 +543,12 @@ static const struct super_operations cifs_super_ops = {
542543
static struct dentry *
543544
cifs_get_root(struct smb_vol *vol, struct super_block *sb)
544545
{
545-
int xid, rc;
546-
struct inode *inode;
547-
struct qstr name;
548-
struct dentry *dparent = NULL, *dchild = NULL, *alias;
546+
struct dentry *dentry;
549547
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
550-
unsigned int i, full_len, len;
551-
char *full_path = NULL, *pstart;
548+
char *full_path = NULL;
549+
char *s, *p;
552550
char sep;
551+
int xid;
553552

554553
full_path = cifs_build_path_to_root(vol, cifs_sb,
555554
cifs_sb_master_tcon(cifs_sb));
@@ -560,73 +559,32 @@ cifs_get_root(struct smb_vol *vol, struct super_block *sb)
560559

561560
xid = GetXid();
562561
sep = CIFS_DIR_SEP(cifs_sb);
563-
dparent = dget(sb->s_root);
564-
full_len = strlen(full_path);
565-
full_path[full_len] = sep;
566-
pstart = full_path + 1;
567-
568-
for (i = 1, len = 0; i <= full_len; i++) {
569-
if (full_path[i] != sep || !len) {
570-
len++;
571-
continue;
572-
}
573-
574-
full_path[i] = 0;
575-
cFYI(1, "get dentry for %s", pstart);
576-
577-
name.name = pstart;
578-
name.len = len;
579-
name.hash = full_name_hash(pstart, len);
580-
dchild = d_lookup(dparent, &name);
581-
if (dchild == NULL) {
582-
cFYI(1, "not exists");
583-
dchild = d_alloc(dparent, &name);
584-
if (dchild == NULL) {
585-
dput(dparent);
586-
dparent = ERR_PTR(-ENOMEM);
587-
goto out;
588-
}
589-
}
590-
591-
cFYI(1, "get inode");
592-
if (dchild->d_inode == NULL) {
593-
cFYI(1, "not exists");
594-
inode = NULL;
595-
if (cifs_sb_master_tcon(CIFS_SB(sb))->unix_ext)
596-
rc = cifs_get_inode_info_unix(&inode, full_path,
597-
sb, xid);
598-
else
599-
rc = cifs_get_inode_info(&inode, full_path,
600-
NULL, sb, xid, NULL);
601-
if (rc) {
602-
dput(dchild);
603-
dput(dparent);
604-
dparent = ERR_PTR(rc);
605-
goto out;
606-
}
607-
alias = d_materialise_unique(dchild, inode);
608-
if (alias != NULL) {
609-
dput(dchild);
610-
if (IS_ERR(alias)) {
611-
dput(dparent);
612-
dparent = ERR_PTR(-EINVAL); /* XXX */
613-
goto out;
614-
}
615-
dchild = alias;
616-
}
617-
}
618-
cFYI(1, "parent %p, child %p", dparent, dchild);
619-
620-
dput(dparent);
621-
dparent = dchild;
622-
len = 0;
623-
pstart = full_path + i + 1;
624-
full_path[i] = sep;
625-
}
626-
out:
562+
dentry = dget(sb->s_root);
563+
p = s = full_path;
564+
565+
do {
566+
struct inode *dir = dentry->d_inode;
567+
struct dentry *child;
568+
569+
/* skip separators */
570+
while (*s == sep)
571+
s++;
572+
if (!*s)
573+
break;
574+
p = s++;
575+
/* next separator */
576+
while (*s && *s != sep)
577+
s++;
578+
579+
mutex_lock(&dir->i_mutex);
580+
child = lookup_one_len(p, dentry, s - p);
581+
mutex_unlock(&dir->i_mutex);
582+
dput(dentry);
583+
dentry = child;
584+
} while (!IS_ERR(dentry));
627585
_FreeXid(xid);
628586
kfree(full_path);
629-
return dparent;
587+
return dentry;
630588
}
631589

632590
static int cifs_set_super(struct super_block *sb, void *data)

fs/namei.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -942,7 +942,6 @@ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
942942
* Don't forget we might have a non-mountpoint managed dentry
943943
* that wants to block transit.
944944
*/
945-
*inode = path->dentry->d_inode;
946945
if (unlikely(managed_dentry_might_block(path->dentry)))
947946
return false;
948947

@@ -955,6 +954,12 @@ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
955954
path->mnt = mounted;
956955
path->dentry = mounted->mnt_root;
957956
nd->seq = read_seqcount_begin(&path->dentry->d_seq);
957+
/*
958+
* Update the inode too. We don't need to re-check the
959+
* dentry sequence number here after this d_inode read,
960+
* because a mount-point is always pinned.
961+
*/
962+
*inode = path->dentry->d_inode;
958963
}
959964
return true;
960965
}

0 commit comments

Comments
 (0)