From patchwork Tue Mar 30 19:51:14 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jeff Layton X-Patchwork-Id: 53753 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.samba.org (fn.samba.org [216.83.154.106]) by ozlabs.org (Postfix) with ESMTP id E506BB7D1A for ; Thu, 27 May 2010 22:37:02 +1000 (EST) Received: from fn.samba.org (localhost [127.0.0.1]) by lists.samba.org (Postfix) with ESMTP id 7D23B46685; Thu, 27 May 2010 06:35:46 -0600 (MDT) X-Spam-Checker-Version: SpamAssassin 3.2.5 (2008-06-10) on fn.samba.org X-Spam-Level: X-Spam-Status: No, score=-9.9 required=3.8 tests=BAYES_00, RCVD_IN_DNSWL_HI, SPF_HELO_PASS,SPF_NEUTRAL autolearn=ham version=3.2.5 X-Original-To: linux-cifs-client@lists.samba.org Delivered-To: linux-cifs-client@lists.samba.org Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by lists.samba.org (Postfix) with ESMTP id 6A7D3AD26C for ; Tue, 30 Mar 2010 13:51:20 -0600 (MDT) Received: from int-mx04.intmail.prod.int.phx2.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.17]) by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id o2UJpI2l000653 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Tue, 30 Mar 2010 15:51:18 -0400 Received: from localhost.localdomain (vpn-10-89.rdu.redhat.com [10.11.10.89]) by int-mx04.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id o2UJp84U024778; Tue, 30 Mar 2010 15:51:17 -0400 From: Jeff Layton To: linux-cifs-client@lists.samba.org Date: Tue, 30 Mar 2010 15:51:14 -0400 Message-Id: <1269978677-6817-13-git-send-email-jlayton@samba.org> In-Reply-To: <1269978677-6817-1-git-send-email-jlayton@samba.org> References: <1269978677-6817-1-git-send-email-jlayton@samba.org> X-Scanned-By: MIMEDefang 2.67 on 10.5.11.17 X-Mailman-Approved-At: Thu, 27 May 2010 06:34:18 -0600 Cc: linux-fsdevel@vger.kernel.org Subject: [linux-cifs-client] [PATCH 12/15] cifs: build sessions and tcons on the fly X-BeenThere: linux-cifs-client@lists.samba.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: The Linux CIFS VFS client List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: linux-cifs-client-bounces@lists.samba.org Errors-To: linux-cifs-client-bounces@lists.samba.org From: Jeff Layton 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 --- 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 + #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;