[linux-cifs-client] [PATCH 12/15] cifs: build sessions and tcons on the fly

Jeff Layton jlayton at samba.org
Tue Mar 30 13:51:42 MDT 2010


From: Jeff Layton <jlayton at redhat.com>

This patch is rather large, but it's a bit difficult to do piecemeal...

First, callers of cifs_sb_tcon will need to expect the possibility
of a IS_ERR() return from that function. The bulk of this patch is
fixing the callers to do so.

Turn the tcon pointer in the cifs_sb into a radix tree that uses the
fsuid of the process as a key. The value is a new "tcon_link" struct
that contains info about a tcon that's under construction.

When a new process needs a tcon, it'll call cifs_sb_tcon. That will
then look up the tcon_link in the radix tree. If it exists and is
valid, it's returned.

If it doesn't exist, then we stuff a new tcon_link into the tree and
mark it as pending and then go and try to build the session/tcon.
If that works, the tcon pointer in the tcon_link is updated and the
pending flag is cleared.

If the construction fails, then we set the tcon pointer to an ERR_PTR
and clear the pending flag.

If the radix tree is searched and the tcon_link is marked pending
then we go to sleep and wait for the pending flag to be cleared.

Signed-off-by: Jeff Layton <jlayton at redhat.com>
---
 fs/cifs/cifs_dfs_ref.c |   10 ++-
 fs/cifs/cifs_fs_sb.h   |    7 ++-
 fs/cifs/cifsacl.c      |   32 ++++++--
 fs/cifs/cifsfs.c       |   69 ++++++----------
 fs/cifs/cifsglob.h     |    7 +--
 fs/cifs/cifsproto.h    |    5 +-
 fs/cifs/connect.c      |  208 ++++++++++++++++++++++++++++++++++++++++++++++--
 fs/cifs/dir.c          |   47 ++++++-----
 fs/cifs/file.c         |   16 ++--
 fs/cifs/inode.c        |  120 ++++++++++++++++++++++------
 fs/cifs/link.c         |   13 +++-
 fs/cifs/misc.c         |    2 +-
 fs/cifs/readdir.c      |   12 ++-
 fs/cifs/xattr.c        |   23 +++++-
 14 files changed, 437 insertions(+), 134 deletions(-)

diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c
index 923a9e7..e2d22ff 100644
--- a/fs/cifs/cifs_dfs_ref.c
+++ b/fs/cifs/cifs_dfs_ref.c
@@ -307,6 +307,7 @@ cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd)
 	struct dfs_info3_param *referrals = NULL;
 	unsigned int num_referrals = 0;
 	struct cifs_sb_info *cifs_sb;
+	struct cifsTconInfo *tcon;
 	struct cifsSesInfo *ses;
 	char *full_path = NULL;
 	int xid, i;
@@ -322,13 +323,14 @@ cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd)
 	nd->path.dentry = dget(dentry);
 
 	cifs_sb = CIFS_SB(dentry->d_inode->i_sb);
-	ses = cifs_sb_tcon(cifs_sb)->ses;
-
-	if (!ses) {
-		rc = -EINVAL;
+	tcon = cifs_sb_tcon(cifs_sb);
+	if (IS_ERR(tcon)) {
+		rc = PTR_ERR(tcon);
 		goto out_err;
 	}
 
+	ses = tcon->ses;
+
 	/*
 	 * The MSDFS spec states that paths in DFS referral requests and
 	 * responses must be prefixed by a single '\' character instead of
diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h
index 037dbab..27fc7d9 100644
--- a/fs/cifs/cifs_fs_sb.h
+++ b/fs/cifs/cifs_fs_sb.h
@@ -15,6 +15,8 @@
  *   the GNU Lesser General Public License for more details.
  *
  */
+#include <linux/radix-tree.h>
+
 #ifndef _CIFS_FS_SB_H
 #define _CIFS_FS_SB_H
 
@@ -36,7 +38,10 @@
 #define CIFS_MOUNT_MULTISES	0x8000 /* multisession mount */
 
 struct cifs_sb_info {
-	struct cifsTconInfo *tcon;	/* primary mount */
+	struct radix_tree_root tcon_tree;
+#define CIFS_TCON_MASTER_TAG		0	/* tcon is "master" (mount) tcon */
+#define CIFS_TCON_PENDING_TAG		1	/* tcon is under construction */
+	spinlock_t tcon_tree_lock;
 	struct nls_table *local_nls;
 	unsigned int rsize;
 	unsigned int wsize;
diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c
index 5e4ea43..1497ea5 100644
--- a/fs/cifs/cifsacl.c
+++ b/fs/cifs/cifsacl.c
@@ -555,10 +555,14 @@ static struct cifs_ntsd *get_cifs_acl_by_fid(struct cifs_sb_info *cifs_sb,
 		__u16 fid, u32 *pacllen)
 {
 	struct cifs_ntsd *pntsd = NULL;
+	struct cifsTconInfo *tcon = cifs_sb_tcon(cifs_sb);
 	int xid, rc;
 
+	if (IS_ERR(tcon))
+		return (struct cifs_ntsd *) tcon;
+
 	xid = GetXid();
-	rc = CIFSSMBGetCIFSACL(xid, cifs_sb_tcon(cifs_sb), fid, &pntsd, pacllen);
+	rc = CIFSSMBGetCIFSACL(xid, tcon, fid, &pntsd, pacllen);
 	FreeXid(xid);
 
 
@@ -570,13 +574,17 @@ static struct cifs_ntsd *get_cifs_acl_by_path(struct cifs_sb_info *cifs_sb,
 		const char *path, u32 *pacllen)
 {
 	struct cifs_ntsd *pntsd = NULL;
+	struct cifsTconInfo *tcon = cifs_sb_tcon(cifs_sb);
 	int oplock = 0;
 	int xid, rc;
 	__u16 fid;
 
+	if (IS_ERR(tcon))
+		return (struct cifs_ntsd *) tcon;
+
 	xid = GetXid();
 
-	rc = CIFSSMBOpen(xid, cifs_sb_tcon(cifs_sb), path, FILE_OPEN, READ_CONTROL, 0,
+	rc = CIFSSMBOpen(xid, tcon, path, FILE_OPEN, READ_CONTROL, 0,
 			 &fid, &oplock, NULL, cifs_sb->local_nls,
 			 cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
 	if (rc) {
@@ -584,10 +592,10 @@ static struct cifs_ntsd *get_cifs_acl_by_path(struct cifs_sb_info *cifs_sb,
 		goto out;
 	}
 
-	rc = CIFSSMBGetCIFSACL(xid, cifs_sb_tcon(cifs_sb), fid, &pntsd, pacllen);
+	rc = CIFSSMBGetCIFSACL(xid, tcon, fid, &pntsd, pacllen);
 	cFYI(1, ("GetCIFSACL rc = %d ACL len %d", rc, *pacllen));
 
-	CIFSSMBClose(xid, cifs_sb_tcon(cifs_sb), fid);
+	CIFSSMBClose(xid, tcon, fid);
  out:
 	FreeXid(xid);
 	return pntsd;
@@ -615,9 +623,13 @@ static int set_cifs_acl_by_fid(struct cifs_sb_info *cifs_sb, __u16 fid,
 		struct cifs_ntsd *pnntsd, u32 acllen)
 {
 	int xid, rc;
+	struct cifsTconInfo *tcon = cifs_sb_tcon(cifs_sb);
+
+	if (IS_ERR(tcon))
+		return PTR_ERR(tcon);
 
 	xid = GetXid();
-	rc = CIFSSMBSetCIFSACL(xid, cifs_sb_tcon(cifs_sb), fid, pnntsd, acllen);
+	rc = CIFSSMBSetCIFSACL(xid, tcon, fid, pnntsd, acllen);
 	FreeXid(xid);
 
 	cFYI(DBG2, ("SetCIFSACL rc = %d", rc));
@@ -630,10 +642,14 @@ static int set_cifs_acl_by_path(struct cifs_sb_info *cifs_sb, const char *path,
 	int oplock = 0;
 	int xid, rc;
 	__u16 fid;
+	struct cifsTconInfo *tcon = cifs_sb_tcon(cifs_sb);
+
+	if (IS_ERR(tcon))
+		return PTR_ERR(tcon);
 
 	xid = GetXid();
 
-	rc = CIFSSMBOpen(xid, cifs_sb_tcon(cifs_sb), path, FILE_OPEN, WRITE_DAC, 0,
+	rc = CIFSSMBOpen(xid, tcon, path, FILE_OPEN, WRITE_DAC, 0,
 			 &fid, &oplock, NULL, cifs_sb->local_nls,
 			 cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
 	if (rc) {
@@ -641,10 +657,10 @@ static int set_cifs_acl_by_path(struct cifs_sb_info *cifs_sb, const char *path,
 		goto out;
 	}
 
-	rc = CIFSSMBSetCIFSACL(xid, cifs_sb_tcon(cifs_sb), fid, pnntsd, acllen);
+	rc = CIFSSMBSetCIFSACL(xid, tcon, fid, pnntsd, acllen);
 	cFYI(DBG2, ("SetCIFSACL rc = %d", rc));
 
-	CIFSSMBClose(xid, cifs_sb_tcon(cifs_sb), fid);
+	CIFSSMBClose(xid, tcon, fid);
  out:
 	FreeXid(xid);
 	return rc;
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 53ecc94..84755d5 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -102,6 +102,8 @@ cifs_read_super(struct super_block *sb, void *data,
 	cifs_sb = CIFS_SB(sb);
 	if (cifs_sb == NULL)
 		return -ENOMEM;
+	INIT_RADIX_TREE(&cifs_sb->tcon_tree, GFP_KERNEL);
+	spin_lock_init(&cifs_sb->tcon_tree_lock);
 
 #ifdef CONFIG_CIFS_DFS_UPCALL
 	/* copy mount params to sb for use in submounts */
@@ -228,6 +230,9 @@ cifs_statfs(struct dentry *dentry, struct kstatfs *buf)
 	int rc = -EOPNOTSUPP;
 	int xid;
 
+	if (IS_ERR(tcon))
+		return PTR_ERR(tcon);
+
 	xid = GetXid();
 
 	buf->f_type = CIFS_MAGIC_NUMBER;
@@ -359,7 +364,7 @@ static int
 cifs_show_options(struct seq_file *s, struct vfsmount *m)
 {
 	struct cifs_sb_info *cifs_sb = CIFS_SB(m->mnt_sb);
-	struct cifsTconInfo *tcon = cifs_sb_tcon(cifs_sb);
+	struct cifsTconInfo *tcon = cifs_sb_master_tcon(cifs_sb);
 
 	seq_printf(s, ",unc=%s", tcon->treeName);
 
@@ -433,20 +438,13 @@ int cifs_xquota_set(struct super_block *sb, int quota_type, qid_t qid,
 	int xid;
 	int rc = 0;
 	struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
-	struct cifsTconInfo *pTcon;
-
-	if (cifs_sb)
-		pTcon = cifs_sb_tcon(cifs_sb);
-	else
-		return -EIO;
+	struct cifsTconInfo *tcon = cifs_sb_tcon(cifs_sb);
 
+	if (IS_ERR(tcon))
+		return PTR_ERR(tcon);
 
 	xid = GetXid();
-	if (pTcon) {
-		cFYI(1, ("set type: 0x%x id: %d", quota_type, qid));
-	} else
-		rc = -EIO;
-
+	cFYI(1, ("set type: 0x%x id: %d", quota_type, qid));
 	FreeXid(xid);
 	return rc;
 }
@@ -457,19 +455,13 @@ int cifs_xquota_get(struct super_block *sb, int quota_type, qid_t qid,
 	int xid;
 	int rc = 0;
 	struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
-	struct cifsTconInfo *pTcon;
+	struct cifsTconInfo *tcon = cifs_sb_tcon(cifs_sb);
 
-	if (cifs_sb)
-		pTcon = cifs_sb_tcon(cifs_sb);
-	else
-		return -EIO;
+	if (IS_ERR(tcon))
+		return PTR_ERR(tcon);
 
 	xid = GetXid();
-	if (pTcon) {
-		cFYI(1, ("set type: 0x%x id: %d", quota_type, qid));
-	} else
-		rc = -EIO;
-
+	cFYI(1, ("set type: 0x%x id: %d", quota_type, qid));
 	FreeXid(xid);
 	return rc;
 }
@@ -479,19 +471,13 @@ int cifs_xstate_set(struct super_block *sb, unsigned int flags, int operation)
 	int xid;
 	int rc = 0;
 	struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
-	struct cifsTconInfo *pTcon;
+	struct cifsTconInfo *tcon = cifs_sb_tcon(cifs_sb);
 
-	if (cifs_sb)
-		pTcon = cifs_sb_tcon(cifs_sb);
-	else
-		return -EIO;
+	if (IS_ERR(tcon))
+		return PTR_ERR(tcon);
 
 	xid = GetXid();
-	if (pTcon) {
-		cFYI(1, ("flags: 0x%x operation: 0x%x", flags, operation));
-	} else
-		rc = -EIO;
-
+	cFYI(1, ("flags: 0x%x operation: 0x%x", flags, operation));
 	FreeXid(xid);
 	return rc;
 }
@@ -501,19 +487,13 @@ int cifs_xstate_get(struct super_block *sb, struct fs_quota_stat *qstats)
 	int xid;
 	int rc = 0;
 	struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
-	struct cifsTconInfo *pTcon;
+	struct cifsTconInfo *tcon = cifs_sb_tcon(cifs_sb);
 
-	if (cifs_sb)
-		pTcon = cifs_sb_tcon(cifs_sb);
-	else
-		return -EIO;
+	if (IS_ERR(tcon))
+		return PTR_ERR(tcon);
 
 	xid = GetXid();
-	if (pTcon) {
-		cFYI(1, ("pqstats %p", qstats));
-	} else
-		rc = -EIO;
-
+	cFYI(1, ("pqstats %p", qstats));
 	FreeXid(xid);
 	return rc;
 }
@@ -534,9 +514,8 @@ static void cifs_umount_begin(struct super_block *sb)
 	if (cifs_sb == NULL)
 		return;
 
-	tcon = cifs_sb_tcon(cifs_sb);
-	if (tcon == NULL)
-		return;
+	/* FIXME: handle multisession case properly */
+	tcon = cifs_sb_master_tcon(cifs_sb);
 
 	read_lock(&cifs_tcp_ses_lock);
 	if ((tcon->tc_count > 1) || (tcon->tidStatus == CifsExiting)) {
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index b47a66b..49c08e0 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -221,6 +221,7 @@ struct cifsSesInfo {
 	char *serverDomain;	/* security realm of server */
 	int Suid;		/* remote smb uid  */
 	uid_t linux_uid;        /* local Linux uid */
+	uid_t linux_fsuid;	/* fsuid of owner */
 	int capabilities;
 	char serverName[SERVER_NAME_LEN_WITH_NULL * 2];	/* BB make bigger for
 				TCP names - will ipv6 and sctp addresses fit? */
@@ -408,12 +409,6 @@ CIFS_SB(struct super_block *sb)
 	return sb->s_fs_info;
 }
 
-static inline struct cifsTconInfo *
-cifs_sb_tcon(struct cifs_sb_info *cifs_sb)
-{
-	return cifs_sb->tcon;
-}
-
 static inline char CIFS_DIR_SEP(const struct cifs_sb_info *cifs_sb)
 {
 	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS)
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index 4b048f1..d8d7fa8 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -93,7 +93,8 @@ extern struct timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time,
 
 extern struct cifsFileInfo *cifs_new_fileinfo(struct inode *newinode,
 				__u16 fileHandle, struct file *file,
-				struct vfsmount *mnt, unsigned int oflags);
+				struct vfsmount *mnt, struct cifsTconInfo *tcon,
+				unsigned int oflags);
 extern int cifs_posix_open(char *full_path, struct inode **pinode,
 			   struct vfsmount *mnt, int mode, int oflags,
 			   __u32 *poplock, __u16 *pnetfid, int xid);
@@ -118,6 +119,8 @@ extern void cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb,
 			      const char *path, const __u16 *pfid);
 extern int mode_to_acl(struct inode *inode, const char *path, __u64);
 
+extern struct cifsTconInfo *cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb);
+extern struct cifsTconInfo *cifs_sb_tcon(struct cifs_sb_info *cifs_sb);
 extern int cifs_mount(struct super_block *, struct cifs_sb_info *, char *,
 			const char *);
 extern int cifs_umount(struct super_block *, struct cifs_sb_info *);
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index 3e1efc6..b213a9b 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -104,6 +104,15 @@ struct smb_vol {
 	struct nls_table *local_nls;
 };
 
+#define TLINK_ERROR_EXPIRE	(1 * HZ)
+
+#define TCON_LINK_PENDING	1
+struct tcon_link {
+	unsigned long flags;
+	unsigned long time;
+	struct cifsTconInfo *tcon;
+};
+
 static int ipv4_connect(struct TCP_Server_Info *server);
 static int ipv6_connect(struct TCP_Server_Info *server);
 
@@ -1699,6 +1708,7 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info)
 			strcpy(ses->domainName, volume_info->domainname);
 	}
 	ses->linux_uid = volume_info->linux_uid;
+	ses->linux_fsuid = current_fsuid();
 	ses->overrideSecFlg = volume_info->secFlg;
 
 	mutex_lock(&ses->session_mutex);
@@ -2480,6 +2490,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
 	struct TCP_Server_Info *srvTcp;
 	char   *full_path;
 	char *mount_data = mount_data_global;
+	struct tcon_link *tlink;
 #ifdef CONFIG_CIFS_DFS_UPCALL
 	struct dfs_info3_param *referrals = NULL;
 	unsigned int num_referrals = 0;
@@ -2491,6 +2502,7 @@ try_mount_again:
 	pSesInfo = NULL;
 	srvTcp = NULL;
 	full_path = NULL;
+	tlink = NULL;
 
 	xid = GetXid();
 
@@ -2566,8 +2578,6 @@ try_mount_again:
 		goto remote_path_check;
 	}
 
-	cifs_sb->tcon = tcon;
-
 	/* do not care if following two calls succeed - informational */
 	if (!tcon->ipc) {
 		CIFSSMBQFSDeviceInfo(xid, tcon);
@@ -2676,6 +2686,29 @@ remote_path_check:
 #endif
 	}
 
+	/* now, hang the tcon off of the superblock */
+	tlink = kzalloc(sizeof *tlink, GFP_KERNEL);
+	if (tlink == NULL) {
+		rc = -ENOMEM;
+		goto mount_fail_check;
+	}
+
+	tlink->tcon = tcon;
+	tlink->time = jiffies;
+
+	rc = radix_tree_preload(GFP_KERNEL);
+	if (rc == -ENOMEM) {
+		kfree(tlink);
+		goto mount_fail_check;
+	}
+
+	spin_lock(&cifs_sb->tcon_tree_lock);
+	radix_tree_insert(&cifs_sb->tcon_tree, pSesInfo->linux_fsuid, tlink);
+	radix_tree_tag_set(&cifs_sb->tcon_tree, pSesInfo->linux_fsuid,
+			   CIFS_TCON_MASTER_TAG);
+	spin_unlock(&cifs_sb->tcon_tree_lock);
+	radix_tree_preload_end();
+
 mount_fail_check:
 	/* on error free sesinfo and tcon struct if needed */
 	if (rc) {
@@ -2862,19 +2895,35 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses,
 int
 cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb)
 {
-	int rc = 0;
+	int i, ret;
 	char *tmp;
+	struct tcon_link *tlink[8];
+	unsigned long index = 0;
+
+	while (1) {
+		spin_lock(&cifs_sb->tcon_tree_lock);
+		ret = radix_tree_gang_lookup(&cifs_sb->tcon_tree,
+					     (void **)tlink, index,
+					     ARRAY_SIZE(tlink));
+		for (i = 0; i < ret; i++) {
+			index = (unsigned long)tlink[i]->tcon->ses->linux_fsuid;
+			radix_tree_delete(&cifs_sb->tcon_tree, index);
+		}
+		spin_unlock(&cifs_sb->tcon_tree_lock);
 
-	if (cifs_sb_tcon(cifs_sb))
-		cifs_put_tcon(cifs_sb_tcon(cifs_sb));
+		for (i = 0; i < ret; i++)
+			cifs_put_tcon(tlink[i]->tcon);
+
+		if (!ret)
+			break;
+	}
 
-	cifs_sb->tcon = NULL;
 	tmp = cifs_sb->prepath;
 	cifs_sb->prepathlen = 0;
 	cifs_sb->prepath = NULL;
 	kfree(tmp);
 
-	return rc;
+	return 0;
 }
 
 int cifs_setup_session(unsigned int xid, struct cifsSesInfo *pSesInfo,
@@ -2931,3 +2980,148 @@ ss_err_exit:
 	return rc;
 }
 
+struct cifsTconInfo *
+cifs_construct_tcon(struct cifs_sb_info *cifs_sb, uid_t fsuid)
+{
+	struct cifsTconInfo *master_tcon = cifs_sb_master_tcon(cifs_sb);
+	struct cifsSesInfo *ses;
+	struct cifsTconInfo *tcon = NULL;
+	struct TCP_Server_Info *server = master_tcon->ses->server;
+	struct smb_vol *vol_info;
+
+	vol_info = kzalloc(sizeof(*vol_info), GFP_KERNEL);
+	if (vol_info == NULL) {
+		tcon = ERR_PTR(-ENOMEM);
+		goto out;
+	}
+
+	vol_info->username = kzalloc(MAX_USERNAME_SIZE + 1, GFP_KERNEL);
+	if (vol_info->username == NULL) {
+		tcon = ERR_PTR(-ENOMEM);
+		goto out;
+	}
+
+	snprintf(vol_info->username, MAX_USERNAME_SIZE, "krb5user:0x%x", fsuid);
+	vol_info->local_nls = cifs_sb->local_nls;
+	vol_info->linux_uid = current_uid();
+
+	/* for multisession mounts, force krb5 for now */
+	vol_info->secFlg = server->secMode &
+				(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
+	vol_info->secFlg |= (CIFSSEC_MAY_KRB5 | CIFSSEC_MUST_KRB5);
+
+	vol_info->UNC = master_tcon->treeName;
+	vol_info->retry = master_tcon->retry;
+	vol_info->nocase = master_tcon->nocase;
+	vol_info->local_lease = master_tcon->local_lease;
+	vol_info->no_linux_ext = !master_tcon->unix_ext;
+
+	ses = cifs_get_smb_ses(master_tcon->ses->server, vol_info);
+	if (IS_ERR(ses)) {
+		tcon = (struct cifsTconInfo *) ses;
+		goto out;
+	}
+
+	tcon = cifs_get_tcon(ses, vol_info);
+	if (IS_ERR(tcon)) {
+		cifs_put_smb_ses(ses);
+		goto out;
+	}
+
+	if (ses->capabilities & CAP_UNIX)
+		reset_cifs_unix_caps(0, tcon, NULL, vol_info);
+out:
+	kfree(vol_info->username);
+	kfree(vol_info);
+
+	return tcon;
+}
+
+struct cifsTconInfo *
+cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb)
+{
+	struct tcon_link *tlink;
+	unsigned int ret;
+
+	spin_lock(&cifs_sb->tcon_tree_lock);
+	ret = radix_tree_gang_lookup_tag(&cifs_sb->tcon_tree, (void **) &tlink,
+					0, 1, CIFS_TCON_MASTER_TAG);
+	spin_unlock(&cifs_sb->tcon_tree_lock);
+
+	/* the master tcon should always be present */
+	if (ret == 0)
+		BUG();
+
+	return tlink->tcon;
+}
+
+static int
+cifs_sb_tcon_pending_wait(void *unused)
+{
+	schedule();
+	return signal_pending(current) ? -ERESTARTSYS : 0;
+}
+
+struct cifsTconInfo *
+cifs_sb_tcon(struct cifs_sb_info *cifs_sb)
+{
+	int ret;
+	unsigned long fsuid = (unsigned long) current_fsuid();
+	struct tcon_link *tlink, *newtlink;
+
+	if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTISES))
+		return cifs_sb_master_tcon(cifs_sb);
+
+	spin_lock(&cifs_sb->tcon_tree_lock);
+	tlink = radix_tree_lookup(&cifs_sb->tcon_tree, fsuid);
+	spin_unlock(&cifs_sb->tcon_tree_lock);
+
+	if (tlink == NULL) {
+		newtlink = kzalloc(sizeof(*tlink), GFP_KERNEL);
+		if (newtlink == NULL)
+			return ERR_PTR(-ENOMEM);
+		newtlink->time = jiffies;
+		newtlink->tcon = ERR_PTR(-EACCES);
+		set_bit(TCON_LINK_PENDING, &newtlink->flags);
+
+		ret = radix_tree_preload(GFP_KERNEL);
+		if (ret != 0) {
+			kfree(newtlink);
+			return ERR_PTR(ret);
+		}
+
+		spin_lock(&cifs_sb->tcon_tree_lock);
+		tlink = radix_tree_lookup(&cifs_sb->tcon_tree, fsuid);
+		if (!tlink) {
+			tlink = newtlink;
+			newtlink = NULL;
+			ret = radix_tree_insert(&cifs_sb->tcon_tree, fsuid,
+						tlink);
+		}
+		spin_unlock(&cifs_sb->tcon_tree_lock);
+		radix_tree_preload_end();
+		kfree(newtlink);
+		if (ret)
+			return ERR_PTR(ret);
+	} else {
+		ret = wait_on_bit(&tlink->flags, TCON_LINK_PENDING,
+				  cifs_sb_tcon_pending_wait,
+				  TASK_INTERRUPTIBLE);
+		if (ret)
+			return ERR_PTR(ret);
+
+		spin_lock(&cifs_sb->tcon_tree_lock);
+		if (!IS_ERR(tlink->tcon) ||
+		    time_before(jiffies, tlink->time + TLINK_ERROR_EXPIRE)) {
+			spin_unlock(&cifs_sb->tcon_tree_lock);
+			return tlink->tcon;
+		}
+		set_bit(TCON_LINK_PENDING, &tlink->flags);
+		spin_unlock(&cifs_sb->tcon_tree_lock);
+	}
+
+	tlink->tcon = cifs_construct_tcon(cifs_sb, fsuid);
+	clear_bit(TCON_LINK_PENDING, &tlink->flags);
+	wake_up_bit(&tlink->flags, TCON_LINK_PENDING);
+	return tlink->tcon;
+}
diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c
index 3467626..680c87a 100644
--- a/fs/cifs/dir.c
+++ b/fs/cifs/dir.c
@@ -53,18 +53,19 @@ build_path_from_dentry(struct dentry *direntry)
 	int dfsplen;
 	char *full_path;
 	char dirsep;
-	struct cifs_sb_info *cifs_sb;
+	struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb);
+	struct cifsTconInfo *tcon = cifs_sb_master_tcon(cifs_sb);
 
+	/* FIXME: this is racy -- need to take proper references */
 	if (direntry == NULL)
 		return NULL;  /* not much we can do if dentry is freed and
 		we need to reopen the file after it was closed implicitly
 		when the server crashed */
 
-	cifs_sb = CIFS_SB(direntry->d_sb);
 	dirsep = CIFS_DIR_SEP(cifs_sb);
 	pplen = cifs_sb->prepathlen;
-	if (cifs_sb_tcon(cifs_sb) && (cifs_sb_tcon(cifs_sb)->Flags & SMB_SHARE_IS_IN_DFS))
-		dfsplen = strnlen(cifs_sb_tcon(cifs_sb)->treeName, MAX_TREE_SIZE + 1);
+	if (tcon && (tcon->Flags & SMB_SHARE_IS_IN_DFS))
+		dfsplen = strnlen(tcon->treeName, MAX_TREE_SIZE + 1);
 	else
 		dfsplen = 0;
 cifs_bp_rename_retry:
@@ -117,7 +118,7 @@ cifs_bp_rename_retry:
 	/* BB test paths to Windows with '/' in the midst of prepath */
 
 	if (dfsplen) {
-		strncpy(full_path, cifs_sb_tcon(cifs_sb)->treeName, dfsplen);
+		strncpy(full_path, tcon->treeName, dfsplen);
 		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) {
 			int i;
 			for (i = 0; i < dfsplen; i++) {
@@ -126,7 +127,7 @@ cifs_bp_rename_retry:
 			}
 		}
 	}
-	strncpy(full_path + dfsplen, CIFS_SB(direntry->d_sb)->prepath, pplen);
+	strncpy(full_path + dfsplen, cifs_sb->prepath, pplen);
 	return full_path;
 }
 
@@ -138,7 +139,6 @@ cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle, struct file *file,
 	int oplock = 0;
 	struct cifsFileInfo *pCifsFile;
 	struct cifsInodeInfo *pCifsInode;
-	struct cifs_sb_info *cifs_sb = CIFS_SB(mnt->mnt_sb);
 
 	pCifsFile = kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL);
 	if (pCifsFile == NULL)
@@ -163,7 +163,7 @@ cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle, struct file *file,
 	slow_work_init(&pCifsFile->oplock_break, &cifs_oplock_break_ops);
 
 	write_lock(&GlobalSMBSeslock);
-	list_add(&pCifsFile->tlist, &cifs_sb_tcon(cifs_sb)->openFileList);
+	list_add(&pCifsFile->tlist, &tcon->openFileList);
 	pCifsInode = CIFS_I(newinode);
 	if (pCifsInode) {
 		/* if readable file instance put first in list*/
@@ -193,14 +193,17 @@ int cifs_posix_open(char *full_path, struct inode **pinode,
 	FILE_UNIX_BASIC_INFO *presp_data;
 	__u32 posix_flags = 0;
 	struct cifs_sb_info *cifs_sb = CIFS_SB(mnt->mnt_sb);
-	struct cifs_fattr fattr;
 	struct cifsTconInfo *tcon = cifs_sb_tcon(cifs_sb);
+	struct cifs_fattr fattr;
 
 	if (IS_ERR(tcon))
 		return PTR_ERR(tcon);
 
 	cFYI(1, ("posix open %s", full_path));
 
+	if (IS_ERR(tcon))
+		return PTR_ERR(tcon);
+
 	presp_data = kzalloc(sizeof(FILE_UNIX_BASIC_INFO), GFP_KERNEL);
 	if (presp_data == NULL)
 		return -ENOMEM;
@@ -297,17 +300,17 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
 	 */
 	int desiredAccess = GENERIC_READ | GENERIC_WRITE;
 	__u16 fileHandle;
-	struct cifs_sb_info *cifs_sb;
-	struct cifsTconInfo *tcon;
+	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+	struct cifsTconInfo *tcon = cifs_sb_tcon(cifs_sb);
 	char *full_path = NULL;
 	FILE_ALL_INFO *buf = NULL;
 	struct inode *newinode = NULL;
 	int disposition = FILE_OVERWRITE_IF;
 
-	xid = GetXid();
+	if (IS_ERR(tcon))
+		return PTR_ERR(tcon);
 
-	cifs_sb = CIFS_SB(inode->i_sb);
-	tcon = cifs_sb_tcon(cifs_sb);
+	xid = GetXid();
 
 	if (IS_ERR(tcon)) {
 		FreeXid(xid);
@@ -390,7 +393,7 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
 	if (!tcon->unix_ext && (mode & S_IWUGO) == 0)
 		create_options |= CREATE_OPTION_READONLY;
 
-	if (cifs_sb_tcon(cifs_sb)->ses->capabilities & CAP_NT_SMBS)
+	if (tcon->ses->capabilities & CAP_NT_SMBS)
 		rc = CIFSSMBOpen(xid, tcon, full_path, disposition,
 			 desiredAccess, create_options,
 			 &fileHandle, &oplock, buf, cifs_sb->local_nls,
@@ -491,18 +494,18 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode,
 {
 	int rc = -EPERM;
 	int xid;
-	struct cifs_sb_info *cifs_sb;
-	struct cifsTconInfo *pTcon;
+	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+	struct cifsTconInfo *pTcon = cifs_sb_tcon(cifs_sb);
 	char *full_path = NULL;
 	struct inode *newinode = NULL;
 
 	if (!old_valid_dev(device_number))
 		return -EINVAL;
 
-	xid = GetXid();
+	if (IS_ERR(pTcon))
+		return PTR_ERR(pTcon);
 
-	cifs_sb = CIFS_SB(inode->i_sb);
-	pTcon = cifs_sb_tcon(cifs_sb);
+	xid = GetXid();
 
 	full_path = build_path_from_dentry(direntry);
 	if (full_path == NULL)
@@ -635,6 +638,10 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
 
 	cifs_sb = CIFS_SB(parent_dir_inode->i_sb);
 	pTcon = cifs_sb_tcon(cifs_sb);
+	if (IS_ERR(pTcon)) {
+		FreeXid(xid);
+		return (struct dentry *) pTcon;
+	}
 
 	/*
 	 * Don't allow the separator character in a path component.
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index 308520e..4078e95 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -264,9 +264,9 @@ int cifs_open(struct inode *inode, struct file *file)
 	__u16 netfid;
 	FILE_ALL_INFO *buf = NULL;
 
-	xid = GetXid();
-
 	cifs_sb = CIFS_SB(inode->i_sb);
+
+	xid = GetXid();
 	tcon = cifs_sb_tcon(cifs_sb);
 	if (IS_ERR(tcon)) {
 		FreeXid(xid);
@@ -373,7 +373,7 @@ int cifs_open(struct inode *inode, struct file *file)
 		goto out;
 	}
 
-	if (cifs_sb_tcon(cifs_sb)->ses->capabilities & CAP_NT_SMBS)
+	if (tcon->ses->capabilities & CAP_NT_SMBS)
 		rc = CIFSSMBOpen(xid, tcon, full_path, disposition,
 			 desiredAccess, CREATE_NOT_DIR, &netfid, &oplock, buf,
 			 cifs_sb->local_nls, cifs_sb->mnt_cifs_flags
@@ -1377,6 +1377,7 @@ static int cifs_writepages(struct address_space *mapping,
 	int xid, long_op;
 
 	cifs_sb = CIFS_SB(mapping->host->i_sb);
+	tcon = cifs_sb_master_tcon(cifs_sb);
 
 	/*
 	 * If wsize is smaller that the page cache size, default to writing
@@ -1386,11 +1387,10 @@ static int cifs_writepages(struct address_space *mapping,
 		return generic_writepages(mapping, wbc);
 
 	/* WTF? */
-	if ((cifs_sb_tcon(cifs_sb)->ses) && (cifs_sb_tcon(cifs_sb)->ses->server))
-		if (cifs_sb_tcon(cifs_sb)->ses->server->secMode &
-				(SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
-			if (!experimEnabled)
-				return generic_writepages(mapping, wbc);
+	if (tcon->ses->server->secMode &
+	    (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
+		if (!experimEnabled)
+			return generic_writepages(mapping, wbc);
 
 	iov = kmalloc(32 * sizeof(struct kvec), GFP_KERNEL);
 	if (iov == NULL)
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
index e09bf9f..e8cb95d 100644
--- a/fs/cifs/inode.c
+++ b/fs/cifs/inode.c
@@ -50,7 +50,7 @@ static void cifs_set_ops(struct inode *inode, const bool is_dfs_referral)
 
 
 		/* check if server can support readpages */
-		if (cifs_sb_tcon(cifs_sb)->ses->server->maxBuf <
+		if (cifs_sb_master_tcon(cifs_sb)->ses->server->maxBuf <
 				PAGE_CACHE_SIZE + MAX_CIFS_HDR_SIZE)
 			inode->i_data.a_ops = &cifs_addr_ops_smallbuf;
 		else
@@ -300,12 +300,14 @@ int cifs_get_inode_info_unix(struct inode **pinode,
 	int rc;
 	FILE_UNIX_BASIC_INFO find_data;
 	struct cifs_fattr fattr;
-	struct cifsTconInfo *tcon;
 	struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
+	struct cifsTconInfo *tcon = cifs_sb_tcon(cifs_sb);
 
-	tcon = cifs_sb_tcon(cifs_sb);
 	cFYI(1, ("Getting info on %s", full_path));
 
+	if (IS_ERR(tcon))
+		return PTR_ERR(tcon);
+
 	/* could have done a find first instead but this returns more info */
 	rc = CIFSSMBUnixQPathInfo(xid, tcon, full_path, &find_data,
 				  cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
@@ -340,11 +342,14 @@ cifs_sfu_type(struct cifs_fattr *fattr, const unsigned char *path,
 	int rc;
 	int oplock = 0;
 	__u16 netfid;
-	struct cifsTconInfo *pTcon = cifs_sb_tcon(cifs_sb);
+	struct cifsTconInfo *tcon = cifs_sb_tcon(cifs_sb);
 	char buf[24];
 	unsigned int bytes_read;
 	char *pbuf;
 
+	if (IS_ERR(tcon))
+		return PTR_ERR(tcon);
+
 	pbuf = buf;
 
 	fattr->cf_mode &= ~S_IFMT;
@@ -359,7 +364,7 @@ cifs_sfu_type(struct cifs_fattr *fattr, const unsigned char *path,
 		return -EINVAL;	 /* EOPNOTSUPP? */
 	}
 
-	rc = CIFSSMBOpen(xid, pTcon, path, FILE_OPEN, GENERIC_READ,
+	rc = CIFSSMBOpen(xid, tcon, path, FILE_OPEN, GENERIC_READ,
 			 CREATE_NOT_DIR, &netfid, &oplock, NULL,
 			 cifs_sb->local_nls,
 			 cifs_sb->mnt_cifs_flags &
@@ -367,7 +372,7 @@ cifs_sfu_type(struct cifs_fattr *fattr, const unsigned char *path,
 	if (rc == 0) {
 		int buf_type = CIFS_NO_BUFFER;
 			/* Read header */
-		rc = CIFSSMBRead(xid, pTcon, netfid,
+		rc = CIFSSMBRead(xid, tcon, netfid,
 				 24 /* length */, 0 /* offset */,
 				 &bytes_read, &pbuf, &buf_type);
 		if ((rc == 0) && (bytes_read >= 8)) {
@@ -409,7 +414,7 @@ cifs_sfu_type(struct cifs_fattr *fattr, const unsigned char *path,
 			fattr->cf_dtype = DT_REG;
 			rc = -EOPNOTSUPP; /* or some unknown SFU type */
 		}
-		CIFSSMBClose(xid, pTcon, netfid);
+		CIFSSMBClose(xid, tcon, netfid);
 	}
 	return rc;
 }
@@ -428,8 +433,12 @@ static int cifs_sfu_mode(struct cifs_fattr *fattr, const unsigned char *path,
 	ssize_t rc;
 	char ea_value[4];
 	__u32 mode;
+	struct cifsTconInfo *tcon = cifs_sb_tcon(cifs_sb);
+
+	if (IS_ERR(tcon))
+		return PTR_ERR(tcon);
 
-	rc = CIFSSMBQAllEAs(xid, cifs_sb_tcon(cifs_sb), path, "SETFILEBITS",
+	rc = CIFSSMBQAllEAs(xid, tcon, path, "SETFILEBITS",
 			    ea_value, 4 /* size of buf */, cifs_sb->local_nls,
 			    cifs_sb->mnt_cifs_flags &
 				CIFS_MOUNT_MAP_SPECIAL_CHR);
@@ -455,6 +464,8 @@ static void
 cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info,
 		       struct cifs_sb_info *cifs_sb, bool adjust_tz)
 {
+	struct cifsTconInfo *tcon = cifs_sb_master_tcon(cifs_sb);
+
 	memset(fattr, 0, sizeof(*fattr));
 	fattr->cf_cifsattrs = le32_to_cpu(info->Attributes);
 	if (info->DeletePending)
@@ -469,8 +480,8 @@ cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info,
 	fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime);
 
 	if (adjust_tz) {
-		fattr->cf_ctime.tv_sec += cifs_sb_tcon(cifs_sb)->ses->server->timeAdj;
-		fattr->cf_mtime.tv_sec += cifs_sb_tcon(cifs_sb)->ses->server->timeAdj;
+		fattr->cf_ctime.tv_sec += tcon->ses->server->timeAdj;
+		fattr->cf_mtime.tv_sec += tcon->ses->server->timeAdj;
 	}
 
 	fattr->cf_eof = le64_to_cpu(info->EndOfFile);
@@ -540,15 +551,17 @@ int cifs_get_inode_info(struct inode **pinode,
 	struct super_block *sb, int xid, const __u16 *pfid)
 {
 	int rc = 0, tmprc;
-	struct cifsTconInfo *pTcon;
 	struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
+	struct cifsTconInfo *pTcon = cifs_sb_tcon(cifs_sb);
 	char *buf = NULL;
 	bool adjustTZ = false;
 	struct cifs_fattr fattr;
 
-	pTcon = cifs_sb_tcon(cifs_sb);
 	cFYI(1, ("Getting info on %s", full_path));
 
+	if (IS_ERR(pTcon))
+		return PTR_ERR(pTcon);
+
 	if ((pfindData == NULL) && (*pinode != NULL)) {
 		if (CIFS_I(*pinode)->clientCanCacheRead) {
 			cFYI(1, ("No need to revalidate cached inode sizes"));
@@ -669,6 +682,7 @@ char *cifs_build_path_to_root(struct cifs_sb_info *cifs_sb)
 	int pplen = cifs_sb->prepathlen;
 	int dfsplen;
 	char *full_path = NULL;
+	struct cifsTconInfo *tcon = cifs_sb_master_tcon(cifs_sb);
 
 	/* if no prefix path, simply set path to the root of share to "" */
 	if (pplen == 0) {
@@ -678,8 +692,8 @@ char *cifs_build_path_to_root(struct cifs_sb_info *cifs_sb)
 		return full_path;
 	}
 
-	if (cifs_sb_tcon(cifs_sb) && (cifs_sb_tcon(cifs_sb)->Flags & SMB_SHARE_IS_IN_DFS))
-		dfsplen = strnlen(cifs_sb_tcon(cifs_sb)->treeName, MAX_TREE_SIZE + 1);
+	if (tcon->Flags & SMB_SHARE_IS_IN_DFS)
+		dfsplen = strnlen(tcon->treeName, MAX_TREE_SIZE + 1);
 	else
 		dfsplen = 0;
 
@@ -688,7 +702,7 @@ char *cifs_build_path_to_root(struct cifs_sb_info *cifs_sb)
 		return full_path;
 
 	if (dfsplen) {
-		strncpy(full_path, cifs_sb_tcon(cifs_sb)->treeName, dfsplen);
+		strncpy(full_path, tcon->treeName, dfsplen);
 		/* switch slash direction in prepath depending on whether
 		 * windows or posix style path names
 		 */
@@ -757,18 +771,21 @@ cifs_iget(struct super_block *sb, struct cifs_fattr *fattr)
 struct inode *cifs_root_iget(struct super_block *sb, unsigned long ino)
 {
 	int xid;
-	struct cifs_sb_info *cifs_sb;
+	struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
+	struct cifsTconInfo *tcon = cifs_sb_tcon(cifs_sb);
 	struct inode *inode = NULL;
 	long rc;
 	char *full_path;
 
-	cifs_sb = CIFS_SB(sb);
+	if (IS_ERR(tcon))
+		return (struct inode *) tcon;
+
 	full_path = cifs_build_path_to_root(cifs_sb);
 	if (full_path == NULL)
 		return ERR_PTR(-ENOMEM);
 
 	xid = GetXid();
-	if (cifs_sb_tcon(cifs_sb)->unix_ext)
+	if (tcon->unix_ext)
 		rc = cifs_get_inode_info_unix(&inode, full_path, sb, xid);
 	else
 		rc = cifs_get_inode_info(&inode, full_path, NULL, sb,
@@ -777,7 +794,7 @@ struct inode *cifs_root_iget(struct super_block *sb, unsigned long ino)
 	if (!inode)
 		return ERR_PTR(-ENOMEM);
 
-	if (rc && cifs_sb_tcon(cifs_sb)->ipc) {
+	if (rc && tcon->ipc) {
 		cFYI(1, ("ipc connection - fake read inode"));
 		inode->i_mode |= S_IFDIR;
 		inode->i_nlink = 2;
@@ -860,7 +877,11 @@ cifs_set_file_info(struct inode *inode, struct iattr *attrs, int xid,
 		goto set_via_filehandle;
 	}
 
-	pTcon = open_file->tcon;
+	pTcon = cifs_sb_tcon(cifs_sb);
+	if (IS_ERR(pTcon)) {
+		rc = PTR_ERR(pTcon);
+		goto out;
+	}
 
 	/*
 	 * NT4 apparently returns success on this call, but it doesn't
@@ -926,6 +947,11 @@ cifs_rename_pending_delete(char *full_path, struct dentry *dentry, int xid)
 	__u32 dosattr, origattr;
 	FILE_BASIC_INFO *info_buf = NULL;
 
+	if (IS_ERR(tcon)) {
+		rc = PTR_ERR(tcon);
+		goto out;
+	}
+
 	rc = CIFSSMBOpen(xid, tcon, full_path, FILE_OPEN,
 			 DELETE|FILE_WRITE_ATTRIBUTES, CREATE_NOT_DIR,
 			 &netfid, &oplock, NULL, cifs_sb->local_nls,
@@ -1040,6 +1066,11 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
 	cFYI(1, ("cifs_unlink, dir=0x%p, dentry=0x%p", dir, dentry));
 
 	xid = GetXid();
+	if (IS_ERR(tcon)) {
+		rc = PTR_ERR(tcon);
+		FreeXid(xid);
+		return rc;
+	}
 
 	/* Unlink can be called from rename so we can not take the
 	 * sb->s_vfs_rename_mutex here */
@@ -1136,6 +1167,11 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode)
 
 	cifs_sb = CIFS_SB(inode->i_sb);
 	pTcon = cifs_sb_tcon(cifs_sb);
+	if (IS_ERR(pTcon)) {
+		rc = PTR_ERR(pTcon);
+		FreeXid(xid);
+		return rc;
+	}
 
 	full_path = build_path_from_dentry(direntry);
 	if (full_path == NULL) {
@@ -1316,6 +1352,11 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry)
 
 	cifs_sb = CIFS_SB(inode->i_sb);
 	pTcon = cifs_sb_tcon(cifs_sb);
+	if (IS_ERR(pTcon)) {
+		rc = PTR_ERR(pTcon);
+		FreeXid(xid);
+		return rc;
+	}
 
 	full_path = build_path_from_dentry(direntry);
 	if (full_path == NULL) {
@@ -1360,6 +1401,9 @@ cifs_do_rename(int xid, struct dentry *from_dentry, const char *fromPath,
 	__u16 srcfid;
 	int oplock, rc;
 
+	if (IS_ERR(pTcon))
+		return PTR_ERR(pTcon);
+
 	/* try path-based rename first */
 	rc = CIFSSMBRename(xid, pTcon, fromPath, toPath, cifs_sb->local_nls,
 			   cifs_sb->mnt_cifs_flags &
@@ -1398,14 +1442,21 @@ int cifs_rename(struct inode *source_dir, struct dentry *source_dentry,
 	char *toName = NULL;
 	struct cifs_sb_info *cifs_sb_source;
 	struct cifs_sb_info *cifs_sb_target;
-	struct cifsTconInfo *tcon;
+	struct cifsTconInfo *source_tcon, *target_tcon;
 	FILE_UNIX_BASIC_INFO *info_buf_source = NULL;
 	FILE_UNIX_BASIC_INFO *info_buf_target;
 	int xid, rc, tmprc;
 
 	cifs_sb_target = CIFS_SB(target_dir->i_sb);
 	cifs_sb_source = CIFS_SB(source_dir->i_sb);
-	tcon = cifs_sb_tcon(cifs_sb_source);
+	source_tcon = cifs_sb_tcon(cifs_sb_source);
+	target_tcon = cifs_sb_tcon(cifs_sb_target);
+
+	if (IS_ERR(source_tcon))
+		return PTR_ERR(source_tcon);
+
+	if (IS_ERR(target_tcon))
+		return PTR_ERR(target_tcon);
 
 	xid = GetXid();
 
@@ -1413,7 +1464,7 @@ int cifs_rename(struct inode *source_dir, struct dentry *source_dentry,
 	 * BB: this might be allowed if same server, but different share.
 	 * Consider adding support for this
 	 */
-	if (tcon != cifs_sb_tcon(cifs_sb_target)) {
+	if (source_tcon != target_tcon) {
 		rc = -EXDEV;
 		goto cifs_rename_exit;
 	}
@@ -1437,7 +1488,7 @@ int cifs_rename(struct inode *source_dir, struct dentry *source_dentry,
 	rc = cifs_do_rename(xid, source_dentry, fromName,
 			    target_dentry, toName);
 
-	if (rc == -EEXIST && tcon->unix_ext) {
+	if (rc == -EEXIST && source_tcon->unix_ext) {
 		/*
 		 * Are src and dst hardlinks of same inode? We can
 		 * only tell with unix extensions enabled
@@ -1451,7 +1502,7 @@ int cifs_rename(struct inode *source_dir, struct dentry *source_dentry,
 		}
 
 		info_buf_target = info_buf_source + 1;
-		tmprc = CIFSSMBUnixQPathInfo(xid, tcon, fromName,
+		tmprc = CIFSSMBUnixQPathInfo(xid, source_tcon, fromName,
 					info_buf_source,
 					cifs_sb_source->local_nls,
 					cifs_sb_source->mnt_cifs_flags &
@@ -1459,7 +1510,7 @@ int cifs_rename(struct inode *source_dir, struct dentry *source_dentry,
 		if (tmprc != 0)
 			goto unlink_target;
 
-		tmprc = CIFSSMBUnixQPathInfo(xid, tcon,
+		tmprc = CIFSSMBUnixQPathInfo(xid, source_tcon,
 					toName, info_buf_target,
 					cifs_sb_target->local_nls,
 					/* remap based on source sb */
@@ -1562,12 +1613,19 @@ int cifs_revalidate_dentry(struct dentry *dentry)
 	char *full_path = NULL;
 	struct inode *inode = dentry->d_inode;
 	struct super_block *sb = dentry->d_sb;
+	struct cifsTconInfo *tcon;
 
 	if (inode == NULL)
 		return -ENOENT;
 
 	xid = GetXid();
 
+	tcon = cifs_sb_tcon(CIFS_SB(sb));
+	if (IS_ERR(tcon)) {
+		rc = PTR_ERR(tcon);
+		goto check_inval;
+	}
+
 	if (!cifs_inode_needs_reval(inode))
 		goto check_inval;
 
@@ -1690,6 +1748,9 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs,
 		if (pTcon == NULL)
 			pTcon = cifs_sb_tcon(cifs_sb);
 
+		if (IS_ERR(pTcon))
+			return PTR_ERR(pTcon);
+
 		/* Set file size by pathname rather than by handle
 		   either because no valid, writeable file handle for
 		   it was found or because there was an error setting
@@ -1837,6 +1898,10 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)
 		cifsFileInfo_put(open_file);
 	} else {
 		pTcon = cifs_sb_tcon(cifs_sb);
+		if (IS_ERR(pTcon)) {
+			rc = PTR_ERR(pTcon);
+			goto out;
+		}
 		rc = CIFSSMBUnixSetPathInfo(xid, pTcon, full_path, args,
 				    cifs_sb->local_nls,
 				    cifs_sb->mnt_cifs_flags &
@@ -2009,6 +2074,9 @@ cifs_setattr(struct dentry *direntry, struct iattr *attrs)
 	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
 	struct cifsTconInfo *pTcon = cifs_sb_tcon(cifs_sb);
 
+	if (IS_ERR(pTcon))
+		return PTR_ERR(pTcon);
+
 	if (pTcon->unix_ext)
 		return cifs_setattr_unix(direntry, attrs);
 
diff --git a/fs/cifs/link.c b/fs/cifs/link.c
index c99e6fc..e6227a3 100644
--- a/fs/cifs/link.c
+++ b/fs/cifs/link.c
@@ -44,6 +44,8 @@ cifs_hardlink(struct dentry *old_file, struct inode *inode,
 
 	cifs_sb_target = CIFS_SB(inode->i_sb);
 	pTcon = cifs_sb_tcon(cifs_sb_target);
+	if (IS_ERR(pTcon))
+		return PTR_ERR(pTcon);
 
 /* No need to check for cross device links since server will do that
    BB note DFS case in future though (when we may have to check) */
@@ -116,6 +118,11 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd)
 
 	xid = GetXid();
 
+	if (IS_ERR(tcon)) {
+		rc = PTR_ERR(tcon);
+		goto out;
+	}
+
 	/*
 	 * For now, we just handle symlinks with unix extensions enabled.
 	 * Eventually we should handle NTFS reparse points, and MacOS
@@ -168,9 +175,13 @@ cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname)
 
 	cifs_sb = CIFS_SB(inode->i_sb);
 	pTcon = cifs_sb_tcon(cifs_sb);
+	if (IS_ERR(pTcon)) {
+		rc = PTR_ERR(pTcon);
+		FreeXid(xid);
+		return rc;
+	}
 
 	full_path = build_path_from_dentry(direntry);
-
 	if (full_path == NULL) {
 		rc = -ENOMEM;
 		FreeXid(xid);
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index 80edc23..51ca940 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -726,6 +726,6 @@ cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb)
 			   "properly. Hardlinks will not be recognized on this "
 			   "mount. Consider mounting with the \"noserverino\" "
 			   "option to silence this message.",
-			   cifs_sb_tcon(cifs_sb)->treeName));
+			   cifs_sb_master_tcon(cifs_sb)->treeName));
 	}
 }
diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c
index 491745f..e26504b 100644
--- a/fs/cifs/readdir.c
+++ b/fs/cifs/readdir.c
@@ -101,7 +101,7 @@ cifs_readdir_lookup(struct dentry *parent, struct qstr *name,
 		return NULL;
 	}
 
-	if (cifs_sb_tcon(CIFS_SB(sb))->nocase)
+	if (cifs_sb_master_tcon(CIFS_SB(sb))->nocase)
 		dentry->d_op = &cifs_ci_dentry_ops;
 	else
 		dentry->d_op = &cifs_dentry_ops;
@@ -170,7 +170,7 @@ static void
 cifs_std_info_to_fattr(struct cifs_fattr *fattr, FIND_FILE_STANDARD_INFO *info,
 		       struct cifs_sb_info *cifs_sb)
 {
-	int offset = cifs_sb_tcon(cifs_sb)->ses->server->timeAdj;
+	int offset = cifs_sb_master_tcon(cifs_sb)->ses->server->timeAdj;
 
 	memset(fattr, 0, sizeof(*fattr));
 	fattr->cf_atime = cnvrtDosUnixTm(info->LastAccessDate,
@@ -231,6 +231,10 @@ static int initiate_cifs_search(const int xid, struct file *file)
 	if (cifs_sb == NULL)
 		return -EINVAL;
 
+	pTcon = cifs_sb_tcon(cifs_sb);
+	if (IS_ERR(pTcon))
+		return PTR_ERR(pTcon);
+
 	if (file->private_data == NULL)
 		file->private_data =
 			kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL);
@@ -240,11 +244,9 @@ static int initiate_cifs_search(const int xid, struct file *file)
 	cifsFile = file->private_data;
 	cifsFile->invalidHandle = true;
 	cifsFile->srch_inf.endOfSearch = false;
-	cifsFile->tcon = cifs_sb_tcon(cifs_sb);
+	cifsFile->tcon = pTcon;
 
 	pTcon = cifsFile->tcon;
-	if (pTcon == NULL)
-		return -EINVAL;
 
 	full_path = build_path_from_dentry(file->f_path.dentry);
 
diff --git a/fs/cifs/xattr.c b/fs/cifs/xattr.c
index 7efdb5a..d37dcdc 100644
--- a/fs/cifs/xattr.c
+++ b/fs/cifs/xattr.c
@@ -61,6 +61,11 @@ int cifs_removexattr(struct dentry *direntry, const char *ea_name)
 
 	cifs_sb = CIFS_SB(sb);
 	pTcon = cifs_sb_tcon(cifs_sb);
+	if (IS_ERR(pTcon)) {
+		rc = PTR_ERR(pTcon);
+		FreeXid(xid);
+		return rc;
+	}
 
 	full_path = build_path_from_dentry(direntry);
 	if (full_path == NULL) {
@@ -116,6 +121,11 @@ int cifs_setxattr(struct dentry *direntry, const char *ea_name,
 
 	cifs_sb = CIFS_SB(sb);
 	pTcon = cifs_sb_tcon(cifs_sb);
+	if (IS_ERR(pTcon)) {
+		rc = PTR_ERR(pTcon);
+		FreeXid(xid);
+		return rc;
+	}
 
 	full_path = build_path_from_dentry(direntry);
 	if (full_path == NULL) {
@@ -224,6 +234,11 @@ ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name,
 
 	cifs_sb = CIFS_SB(sb);
 	pTcon = cifs_sb_tcon(cifs_sb);
+	if (IS_ERR(pTcon)) {
+		rc = PTR_ERR(pTcon);
+		FreeXid(xid);
+		return rc;
+	}
 
 	full_path = build_path_from_dentry(direntry);
 	if (full_path == NULL) {
@@ -345,13 +360,19 @@ ssize_t cifs_listxattr(struct dentry *direntry, char *data, size_t buf_size)
 		return -EIO;
 
 	cifs_sb = CIFS_SB(sb);
-	pTcon = cifs_sb_tcon(cifs_sb);
 
 	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
 		return -EOPNOTSUPP;
 
 	xid = GetXid();
 
+	pTcon = cifs_sb_tcon(cifs_sb);
+	if (IS_ERR(pTcon)) {
+		rc = PTR_ERR(pTcon);
+		FreeXid(xid);
+		return rc;
+	}
+
 	full_path = build_path_from_dentry(direntry);
 	if (full_path == NULL) {
 		rc = -ENOMEM;
-- 
1.6.6.1



More information about the linux-cifs-client mailing list