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

Jeff Layton <[email protected]>
Newsgroups gmane.linux.file-systems,gmane.linux.file-systems.cifs
Message-ID <[email protected]>
From: Jeff Layton <[email protected]>

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 <[email protected]>
---
 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

--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.