Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e501f29c authored by Linus Torvalds's avatar Linus Torvalds
Browse files
* '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 ]
parents 3a5c3743 59430262
Loading
Loading
Loading
Loading
+29 −71
Original line number Original line Diff line number Diff line
@@ -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++) {
		if (full_path[i] != sep || !len) {
			len++;
			continue;
		}


		full_path[i] = 0;
	do {
		cFYI(1, "get dentry for %s", pstart);
		struct inode *dir = dentry->d_inode;

		struct dentry *child;
		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");
		/* skip separators */
		if (dchild->d_inode == NULL) {
		while (*s == sep)
			cFYI(1, "not exists");
			s++;
			inode = NULL;
		if (!*s)
			if (cifs_sb_master_tcon(CIFS_SB(sb))->unix_ext)
			break;
				rc = cifs_get_inode_info_unix(&inode, full_path,
		p = s++;
							      sb, xid);
		/* next separator */
			else
		while (*s && *s != sep)
				rc = cifs_get_inode_info(&inode, full_path,
			s++;
							 NULL, sb, xid, NULL);

			if (rc) {
		mutex_lock(&dir->i_mutex);
				dput(dchild);
		child = lookup_one_len(p, dentry, s - p);
				dput(dparent);
		mutex_unlock(&dir->i_mutex);
				dparent = ERR_PTR(rc);
		dput(dentry);
				goto out;
		dentry = child;
			}
	} while (!IS_ERR(dentry));
			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)
+6 −1
Original line number Original line Diff line number Diff line
@@ -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;
}
}