From 2b149f11978b44199954710d32c0eecf6c9efd9c Mon Sep 17 00:00:00 2001 From: Shirish Pargaonkar Date: Sat, 18 Sep 2010 22:02:18 -0500 Subject: cifs NTLMv2/NTLMSSP ntlmv2 within ntlmssp autentication code Attribue Value (AV) pairs or Target Info (TI) pairs are part of ntlmv2 authentication. Structure ntlmv2_resp had only definition for two av pairs. So removed it, and now allocation of av pairs is dynamic. For servers like Windows 7/2008, av pairs sent by server in challege packet (type 2 in the ntlmssp exchange/negotiation) can vary. Server sends them during ntlmssp negotiation. So when ntlmssp is used as an authentication mechanism, type 2 challenge packet from server has this information. Pluck it and use the entire blob for authenticaiton purpose. If user has not specified, extract (netbios) domain name from the av pairs which is used to calculate ntlmv2 hash. Servers like Windows 7 are particular about the AV pair blob. Servers like Windows 2003, are not very strict about the contents of av pair blob used during ntlmv2 authentication. So when security mechanism such as ntlmv2 is used (not ntlmv2 in ntlmssp), there is no negotiation and so genereate a minimal blob that gets used in ntlmv2 authentication as well as gets sent. Fields tilen and tilbob are session specific. AV pair values are defined. To calculate ntlmv2 response we need ti/av pair blob. For sec mech like ntlmssp, the blob is plucked from type 2 response from the server. From this blob, netbios name of the domain is retrieved, if user has not already provided, to be included in the Target String as part of ntlmv2 hash calculations. For sec mech like ntlmv2, create a minimal, two av pair blob. The allocated blob is freed in case of error. In case there is no error, this blob is used in calculating ntlmv2 response (in CalcNTLMv2_response) and is also copied on the response to the server, and then freed. The type 3 ntlmssp response is prepared on a buffer, 5 * sizeof of struct _AUTHENTICATE_MESSAGE, an empirical value large enough to hold _AUTHENTICATE_MESSAGE plus a blob with max possible 10 values as part of ntlmv2 response and lmv2 keys and domain, user, workstation names etc. Also, kerberos gets selected as a default mechanism if server supports it, over the other security mechanisms. Signed-off-by: Shirish Pargaonkar Signed-off-by: Steve French --- fs/cifs/connect.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'fs/cifs/connect.c') diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 88c84a38bccb..c99760a523bf 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -1740,6 +1740,8 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info) if (ses == NULL) goto get_ses_fail; + ses->tilen = 0; + ses->tiblob = NULL; /* new SMB session uses our server ref */ ses->server = server; if (server->addr.sockAddr6.sin6_family == AF_INET6) -- cgit v1.2.2 From 3eb9a8893a76cf1cda3b41c3212eb2cfe83eae0e Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Wed, 1 Sep 2010 17:06:02 -0700 Subject: cifs: Allow binding to local IP address. When using multi-homed machines, it's nice to be able to specify the local IP to use for outbound connections. This patch gives cifs the ability to bind to a particular IP address. Usage: mount -t cifs -o srcaddr=192.168.1.50,user=foo, ... Usage: mount -t cifs -o srcaddr=2002::100:1,user=foo, ... Acked-by: Jeff Layton Acked-by: Dr. David Holder Signed-off-by: Ben Greear Signed-off-by: Steve French --- fs/cifs/connect.c | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 88 insertions(+), 2 deletions(-) (limited to 'fs/cifs/connect.c') diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index c99760a523bf..fa884520fb84 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -105,6 +105,7 @@ struct smb_vol { bool sockopt_tcp_nodelay:1; unsigned short int port; char *prepath; + struct sockaddr_storage srcaddr; /* allow binding to a local IP */ struct nls_table *local_nls; }; @@ -1046,6 +1047,22 @@ cifs_parse_mount_options(char *options, const char *devname, "long\n"); return 1; } + } else if (strnicmp(data, "srcaddr", 7) == 0) { + vol->srcaddr.ss_family = AF_UNSPEC; + + if (!value || !*value) { + printk(KERN_WARNING "CIFS: srcaddr value" + " not specified.\n"); + return 1; /* needs_arg; */ + } + i = cifs_convert_address((struct sockaddr *)&vol->srcaddr, + value, strlen(value)); + if (i < 0) { + printk(KERN_WARNING "CIFS: Could not parse" + " srcaddr: %s\n", + value); + return 1; + } } else if (strnicmp(data, "prefixpath", 10) == 0) { if (!value || !*value) { printk(KERN_WARNING @@ -1374,8 +1391,36 @@ cifs_parse_mount_options(char *options, const char *devname, return 0; } +/** Returns true if srcaddr isn't specified and rhs isn't + * specified, or if srcaddr is specified and + * matches the IP address of the rhs argument. + */ +static bool +srcip_matches(struct sockaddr *srcaddr, struct sockaddr *rhs) +{ + switch (srcaddr->sa_family) { + case AF_UNSPEC: + return (rhs->sa_family == AF_UNSPEC); + case AF_INET: { + struct sockaddr_in *saddr4 = (struct sockaddr_in *)srcaddr; + struct sockaddr_in *vaddr4 = (struct sockaddr_in *)rhs; + return (saddr4->sin_addr.s_addr == vaddr4->sin_addr.s_addr); + } + case AF_INET6: { + struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *)srcaddr; + struct sockaddr_in6 *vaddr6 = (struct sockaddr_in6 *)&rhs; + return ipv6_addr_equal(&saddr6->sin6_addr, &vaddr6->sin6_addr); + } + default: + WARN_ON(1); + return false; /* don't expect to be here */ + } +} + + static bool -match_address(struct TCP_Server_Info *server, struct sockaddr *addr) +match_address(struct TCP_Server_Info *server, struct sockaddr *addr, + struct sockaddr *srcaddr) { struct sockaddr_in *addr4 = (struct sockaddr_in *)addr; struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr; @@ -1402,6 +1447,9 @@ match_address(struct TCP_Server_Info *server, struct sockaddr *addr) break; } + if (!srcip_matches(srcaddr, (struct sockaddr *)&server->srcaddr)) + return false; + return true; } @@ -1469,7 +1517,8 @@ cifs_find_tcp_session(struct sockaddr *addr, struct smb_vol *vol) if (server->tcpStatus == CifsNew) continue; - if (!match_address(server, addr)) + if (!match_address(server, addr, + (struct sockaddr *)&vol->srcaddr)) continue; if (!match_security(server, vol)) @@ -1584,6 +1633,8 @@ cifs_get_tcp_session(struct smb_vol *volume_info) * no need to spinlock this init of tcpStatus or srv_count */ tcp_ses->tcpStatus = CifsNew; + memcpy(&tcp_ses->srcaddr, &volume_info->srcaddr, + sizeof(tcp_ses->srcaddr)); ++tcp_ses->srv_count; if (addr.ss_family == AF_INET6) { @@ -1999,6 +2050,33 @@ static void rfc1002mangle(char *target, char *source, unsigned int length) } +static int +bind_socket(struct TCP_Server_Info *server) +{ + int rc = 0; + if (server->srcaddr.ss_family != AF_UNSPEC) { + /* Bind to the specified local IP address */ + struct socket *socket = server->ssocket; + rc = socket->ops->bind(socket, + (struct sockaddr *) &server->srcaddr, + sizeof(server->srcaddr)); + if (rc < 0) { + struct sockaddr_in *saddr4; + struct sockaddr_in6 *saddr6; + saddr4 = (struct sockaddr_in *)&server->srcaddr; + saddr6 = (struct sockaddr_in6 *)&server->srcaddr; + if (saddr6->sin6_family == AF_INET6) + cERROR(1, "cifs: " + "Failed to bind to: %pI6c, error: %d\n", + &saddr6->sin6_addr, rc); + else + cERROR(1, "cifs: " + "Failed to bind to: %pI4, error: %d\n", + &saddr4->sin_addr.s_addr, rc); + } + } + return rc; +} static int ipv4_connect(struct TCP_Server_Info *server) @@ -2024,6 +2102,10 @@ ipv4_connect(struct TCP_Server_Info *server) cifs_reclassify_socket4(socket); } + rc = bind_socket(server); + if (rc < 0) + return rc; + /* user overrode default port */ if (server->addr.sockAddr.sin_port) { rc = socket->ops->connect(socket, (struct sockaddr *) @@ -2186,6 +2268,10 @@ ipv6_connect(struct TCP_Server_Info *server) cifs_reclassify_socket6(socket); } + rc = bind_socket(server); + if (rc < 0) + return rc; + /* user overrode default port */ if (server->addr.sockAddr6.sin6_port) { rc = socket->ops->connect(socket, -- cgit v1.2.2 From 736a33205969c16f81d747db14ff4c0f133609a6 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 30 Jul 2010 14:56:00 +0200 Subject: cifs: add "mfsymlinks" mount option This is the start for an implementation of "Minshall+French Symlinks" (see http://wiki.samba.org/index.php/UNIX_Extensions#Minshall.2BFrench_symlinks). Signed-off-by: Stefan Metzmacher Signed-off-by: Steve French --- fs/cifs/connect.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'fs/cifs/connect.c') diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index fa884520fb84..435b912f5de5 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -100,6 +100,7 @@ struct smb_vol { bool noautotune:1; bool nostrictsync:1; /* do not force expensive SMBflush on every sync */ bool fsc:1; /* enable fscache */ + bool mfsymlinks:1; /* use Minshall+French Symlinks */ unsigned int rsize; unsigned int wsize; bool sockopt_tcp_nodelay:1; @@ -1342,6 +1343,8 @@ cifs_parse_mount_options(char *options, const char *devname, "/proc/fs/cifs/LookupCacheEnabled to 0\n"); } else if (strnicmp(data, "fsc", 3) == 0) { vol->fsc = true; + } else if (strnicmp(data, "mfsymlinks", 10) == 0) { + vol->mfsymlinks = true; } else printk(KERN_WARNING "CIFS: Unknown mount option %s\n", data); @@ -2554,6 +2557,14 @@ static void setup_cifs_sb(struct smb_vol *pvolume_info, cFYI(1, "mounting share using direct i/o"); cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO; } + if (pvolume_info->mfsymlinks) { + if (pvolume_info->sfu_emul) { + cERROR(1, "mount option mfsymlinks ignored if sfu " + "mount option is used"); + } else { + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MF_SYMLINKS; + } + } if ((pvolume_info->cifs_acl) && (pvolume_info->dynperm)) cERROR(1, "mount option dynperm ignored if cifsacl " -- cgit v1.2.2 From ab9db8b737210bec365593a04dd1c534220bb311 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 21 Sep 2010 08:14:46 -0700 Subject: cifs: allow matching of tcp sessions in CifsNew state With commit 7332f2a6217ee6925f83ef0e725013067ed316ba, cifsd will no longer exit when the socket abends and the tcpStatus is CifsNew. With that change, there's no reason to avoid matching an existing session in this state. Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/connect.c | 9 --------- 1 file changed, 9 deletions(-) (limited to 'fs/cifs/connect.c') diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 435b912f5de5..271038b6ec0e 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -1511,15 +1511,6 @@ cifs_find_tcp_session(struct sockaddr *addr, struct smb_vol *vol) write_lock(&cifs_tcp_ses_lock); list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { - /* - * the demux thread can exit on its own while still in CifsNew - * so don't accept any sockets in that state. Since the - * tcpStatus never changes back to CifsNew it's safe to check - * for this without a lock. - */ - if (server->tcpStatus == CifsNew) - continue; - if (!match_address(server, addr, (struct sockaddr *)&vol->srcaddr)) continue; -- cgit v1.2.2 From d3bf5221d3274b5015ad18a55060b074cca8d2f0 Mon Sep 17 00:00:00 2001 From: Steve French Date: Wed, 22 Sep 2010 19:15:36 +0000 Subject: [CIFS] Fix ordering of cleanup on module init failure If registering fs cache failed, we weren't cleaning up proc. Acked-by: Jeff Layton CC: Suresh Jayaraman Signed-off-by: Steve French --- fs/cifs/connect.c | 1 - 1 file changed, 1 deletion(-) (limited to 'fs/cifs/connect.c') diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 271038b6ec0e..230410e0a453 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -47,7 +47,6 @@ #include "ntlmssp.h" #include "nterr.h" #include "rfc1002pdu.h" -#include "cn_cifs.h" #include "fscache.h" #define CIFS_PORT 445 -- cgit v1.2.2 From a6e8a8455c94565c53e1a1756d2ab9d9e3a902b8 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Mon, 20 Sep 2010 16:01:33 -0700 Subject: cifs: add function to get a tcon from cifs_sb When we convert cifs to do multiple sessions per mount, we'll need more than one tcon per superblock. At that point "cifs_sb->tcon" will make no sense. Add a new accessor function that gets a tcon given a cifs_sb. For now, it just returns cifs_sb->tcon. Later it'll do more. Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/connect.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs/cifs/connect.c') diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 230410e0a453..c42d37fb5b7c 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -3026,8 +3026,8 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb) int rc = 0; char *tmp; - if (cifs_sb->tcon) - cifs_put_tcon(cifs_sb->tcon); + if (cifs_sb_tcon(cifs_sb)) + cifs_put_tcon(cifs_sb_tcon(cifs_sb)); cifs_sb->tcon = NULL; tmp = cifs_sb->prepath; -- cgit v1.2.2 From f6acb9d0596889a774e142ed76cb05b90d9763d2 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Mon, 20 Sep 2010 16:01:34 -0700 Subject: cifs: temporarily rename cifs_sb->tcon to ptcon to catch stragglers Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/connect.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs/cifs/connect.c') diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index c42d37fb5b7c..b4bacea54626 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -2727,7 +2727,7 @@ try_mount_again: goto remote_path_check; } - cifs_sb->tcon = tcon; + cifs_sb->ptcon = tcon; /* do not care if following two calls succeed - informational */ if (!tcon->ipc) { @@ -3029,7 +3029,7 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb) if (cifs_sb_tcon(cifs_sb)) cifs_put_tcon(cifs_sb_tcon(cifs_sb)); - cifs_sb->tcon = NULL; + cifs_sb->ptcon = NULL; tmp = cifs_sb->prepath; cifs_sb->prepathlen = 0; cifs_sb->prepath = NULL; -- cgit v1.2.2 From 0d424ad0a4b8c08e45928bccfa5b4b240097b01b Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Mon, 20 Sep 2010 16:01:35 -0700 Subject: cifs: add cifs_sb_master_tcon and convert some callers to use it At mount time, we'll always need to create a tcon that will serve as a template for others that are associated with the mount. This tcon is known as the "master" tcon. In some cases, we'll need to use that tcon regardless of who's accessing the mount. Add an accessor function for the master tcon and go ahead and switch the appropriate places to use it. Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/connect.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs/cifs/connect.c') diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index b4bacea54626..f6a3091c2874 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -3025,9 +3025,9 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb) { int rc = 0; char *tmp; + struct cifsTconInfo *tcon = cifs_sb_master_tcon(cifs_sb); - if (cifs_sb_tcon(cifs_sb)) - cifs_put_tcon(cifs_sb_tcon(cifs_sb)); + cifs_put_tcon(tcon); cifs_sb->ptcon = NULL; tmp = cifs_sb->prepath; -- cgit v1.2.2 From 9d002df492b14c690425d9785530371b6c1ccbca Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 6 Oct 2010 19:51:11 -0400 Subject: cifs: add routines to build sessions and tcons on the fly This patch is rather large, but it's a bit difficult to do piecemeal... For non-multiuser mounts, everything will basically work as it does today. A call to cifs_sb_tlink will return the "master" tcon link. 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 Signed-off-by: Steve French --- fs/cifs/connect.c | 268 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 261 insertions(+), 7 deletions(-) (limited to 'fs/cifs/connect.c') diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index f6a3091c2874..3156a9de947d 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -109,6 +109,9 @@ struct smb_vol { struct nls_table *local_nls; }; +#define TLINK_ERROR_EXPIRE (1 * HZ) + + static int ipv4_connect(struct TCP_Server_Info *server); static int ipv6_connect(struct TCP_Server_Info *server); @@ -1959,6 +1962,23 @@ out_fail: return ERR_PTR(rc); } +void +cifs_put_tlink(struct tcon_link *tlink) +{ + if (!tlink || IS_ERR(tlink)) + return; + + if (!atomic_dec_and_test(&tlink->tl_count) || + test_bit(TCON_LINK_IN_TREE, &tlink->tl_flags)) { + tlink->tl_time = jiffies; + return; + } + + if (!IS_ERR(tlink_tcon(tlink))) + cifs_put_tcon(tlink_tcon(tlink)); + kfree(tlink); + return; +} int get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, const char *old_path, @@ -2641,6 +2661,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; @@ -2652,6 +2673,7 @@ try_mount_again: pSesInfo = NULL; srvTcp = NULL; full_path = NULL; + tlink = NULL; xid = GetXid(); @@ -2727,8 +2749,6 @@ try_mount_again: goto remote_path_check; } - cifs_sb->ptcon = tcon; - /* do not care if following two calls succeed - informational */ if (!tcon->ipc) { CIFSSMBQFSDeviceInfo(xid, tcon); @@ -2837,6 +2857,35 @@ remote_path_check: #endif } + if (rc) + goto mount_fail_check; + + /* now, hang the tcon off of the superblock */ + tlink = kzalloc(sizeof *tlink, GFP_KERNEL); + if (tlink == NULL) { + rc = -ENOMEM; + goto mount_fail_check; + } + + tlink->tl_index = pSesInfo->linux_uid; + tlink->tl_tcon = tcon; + tlink->tl_time = jiffies; + set_bit(TCON_LINK_MASTER, &tlink->tl_flags); + set_bit(TCON_LINK_IN_TREE, &tlink->tl_flags); + + rc = radix_tree_preload(GFP_KERNEL); + if (rc == -ENOMEM) { + kfree(tlink); + goto mount_fail_check; + } + + spin_lock(&cifs_sb->tlink_tree_lock); + radix_tree_insert(&cifs_sb->tlink_tree, pSesInfo->linux_uid, tlink); + radix_tree_tag_set(&cifs_sb->tlink_tree, pSesInfo->linux_uid, + CIFS_TLINK_MASTER_TAG); + spin_unlock(&cifs_sb->tlink_tree_lock); + radix_tree_preload_end(); + mount_fail_check: /* on error free sesinfo and tcon struct if needed */ if (rc) { @@ -3023,19 +3072,37 @@ 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 cifsTconInfo *tcon = cifs_sb_master_tcon(cifs_sb); + struct tcon_link *tlink[8]; + unsigned long index = 0; + + do { + spin_lock(&cifs_sb->tlink_tree_lock); + ret = radix_tree_gang_lookup(&cifs_sb->tlink_tree, + (void **)tlink, index, + ARRAY_SIZE(tlink)); + /* increment index for next pass */ + if (ret > 0) + index = tlink[ret - 1]->tl_index + 1; + for (i = 0; i < ret; i++) { + cifs_get_tlink(tlink[i]); + clear_bit(TCON_LINK_IN_TREE, &tlink[i]->tl_flags); + radix_tree_delete(&cifs_sb->tlink_tree, + tlink[i]->tl_index); + } + spin_unlock(&cifs_sb->tlink_tree_lock); - cifs_put_tcon(tcon); + for (i = 0; i < ret; i++) + cifs_put_tlink(tlink[i]); + } while (ret != 0); - cifs_sb->ptcon = NULL; tmp = cifs_sb->prepath; cifs_sb->prepathlen = 0; cifs_sb->prepath = NULL; kfree(tmp); - return rc; + return 0; } int cifs_negotiate_protocol(unsigned int xid, struct cifsSesInfo *ses) @@ -3096,3 +3163,190 @@ int cifs_setup_session(unsigned int xid, struct cifsSesInfo *ses, 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 smb_vol *vol_info; + char username[MAX_USERNAME_SIZE + 1]; + + vol_info = kzalloc(sizeof(*vol_info), GFP_KERNEL); + if (vol_info == NULL) { + tcon = ERR_PTR(-ENOMEM); + goto out; + } + + snprintf(username, MAX_USERNAME_SIZE, "krb50x%x", fsuid); + vol_info->username = username; + vol_info->local_nls = cifs_sb->local_nls; + vol_info->linux_uid = fsuid; + vol_info->cred_uid = fsuid; + 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; + + /* FIXME: allow for other secFlg settings */ + vol_info->secFlg = CIFSSEC_MUST_KRB5; + + /* get a reference for the same TCP session */ + write_lock(&cifs_tcp_ses_lock); + ++master_tcon->ses->server->srv_count; + write_unlock(&cifs_tcp_ses_lock); + + ses = cifs_get_smb_ses(master_tcon->ses->server, vol_info); + if (IS_ERR(ses)) { + tcon = (struct cifsTconInfo *)ses; + cifs_put_tcp_session(master_tcon->ses->server); + 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); + + return tcon; +} + +static struct tcon_link * +cifs_sb_master_tlink(struct cifs_sb_info *cifs_sb) +{ + struct tcon_link *tlink; + unsigned int ret; + + spin_lock(&cifs_sb->tlink_tree_lock); + ret = radix_tree_gang_lookup_tag(&cifs_sb->tlink_tree, (void **)&tlink, + 0, 1, CIFS_TLINK_MASTER_TAG); + spin_unlock(&cifs_sb->tlink_tree_lock); + + /* the master tcon should always be present */ + if (ret == 0) + BUG(); + + return tlink; +} + +struct cifsTconInfo * +cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb) +{ + return tlink_tcon(cifs_sb_master_tlink(cifs_sb)); +} + +static int +cifs_sb_tcon_pending_wait(void *unused) +{ + schedule(); + return signal_pending(current) ? -ERESTARTSYS : 0; +} + +/* + * Find or construct an appropriate tcon given a cifs_sb and the fsuid of the + * current task. + * + * If the superblock doesn't refer to a multiuser mount, then just return + * the master tcon for the mount. + * + * First, search the radix tree for an existing tcon for this fsuid. If one + * exists, then check to see if it's pending construction. If it is then wait + * for construction to complete. Once it's no longer pending, check to see if + * it failed and either return an error or retry construction, depending on + * the timeout. + * + * If one doesn't exist then insert a new tcon_link struct into the tree and + * try to construct a new one. + */ +struct tcon_link * +cifs_sb_tlink(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_MULTIUSER)) + return cifs_get_tlink(cifs_sb_master_tlink(cifs_sb)); + + spin_lock(&cifs_sb->tlink_tree_lock); + tlink = radix_tree_lookup(&cifs_sb->tlink_tree, fsuid); + if (tlink) + cifs_get_tlink(tlink); + spin_unlock(&cifs_sb->tlink_tree_lock); + + if (tlink == NULL) { + newtlink = kzalloc(sizeof(*tlink), GFP_KERNEL); + if (newtlink == NULL) + return ERR_PTR(-ENOMEM); + newtlink->tl_index = fsuid; + newtlink->tl_tcon = ERR_PTR(-EACCES); + set_bit(TCON_LINK_PENDING, &newtlink->tl_flags); + set_bit(TCON_LINK_IN_TREE, &newtlink->tl_flags); + cifs_get_tlink(newtlink); + + ret = radix_tree_preload(GFP_KERNEL); + if (ret != 0) { + kfree(newtlink); + return ERR_PTR(ret); + } + + spin_lock(&cifs_sb->tlink_tree_lock); + /* was one inserted after previous search? */ + tlink = radix_tree_lookup(&cifs_sb->tlink_tree, fsuid); + if (tlink) { + cifs_get_tlink(tlink); + spin_unlock(&cifs_sb->tlink_tree_lock); + radix_tree_preload_end(); + kfree(newtlink); + goto wait_for_construction; + } + ret = radix_tree_insert(&cifs_sb->tlink_tree, fsuid, newtlink); + spin_unlock(&cifs_sb->tlink_tree_lock); + radix_tree_preload_end(); + if (ret) { + kfree(newtlink); + return ERR_PTR(ret); + } + tlink = newtlink; + } else { +wait_for_construction: + ret = wait_on_bit(&tlink->tl_flags, TCON_LINK_PENDING, + cifs_sb_tcon_pending_wait, + TASK_INTERRUPTIBLE); + if (ret) { + cifs_put_tlink(tlink); + return ERR_PTR(ret); + } + + /* if it's good, return it */ + if (!IS_ERR(tlink->tl_tcon)) + return tlink; + + /* return error if we tried this already recently */ + if (time_before(jiffies, tlink->tl_time + TLINK_ERROR_EXPIRE)) { + cifs_put_tlink(tlink); + return ERR_PTR(-EACCES); + } + + if (test_and_set_bit(TCON_LINK_PENDING, &tlink->tl_flags)) + goto wait_for_construction; + } + + tlink->tl_tcon = cifs_construct_tcon(cifs_sb, fsuid); + clear_bit(TCON_LINK_PENDING, &tlink->tl_flags); + wake_up_bit(&tlink->tl_flags, TCON_LINK_PENDING); + + if (IS_ERR(tlink->tl_tcon)) { + cifs_put_tlink(tlink); + return ERR_PTR(-EACCES); + } + + return tlink; +} -- cgit v1.2.2 From 0eb8a132c449c755b7a3f18f33365b2040c47347 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 6 Oct 2010 19:51:12 -0400 Subject: cifs: add "multiuser" mount option This allows someone to declare a mount as a multiuser mount. Multiuser mounts also imply "noperm" since we want to allow the server to handle permission checking. It also (for now) requires Kerberos authentication. Eventually, we could expand this to other authtypes, but that requires a scheme to allow per-user credential stashing in some form. Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/connect.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'fs/cifs/connect.c') diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 3156a9de947d..e65f72d1f23b 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -100,6 +100,7 @@ struct smb_vol { bool nostrictsync:1; /* do not force expensive SMBflush on every sync */ bool fsc:1; /* enable fscache */ bool mfsymlinks:1; /* use Minshall+French Symlinks */ + bool multiuser:1; unsigned int rsize; unsigned int wsize; bool sockopt_tcp_nodelay:1; @@ -1347,6 +1348,8 @@ cifs_parse_mount_options(char *options, const char *devname, vol->fsc = true; } else if (strnicmp(data, "mfsymlinks", 10) == 0) { vol->mfsymlinks = true; + } else if (strnicmp(data, "multiuser", 8) == 0) { + vol->multiuser = true; } else printk(KERN_WARNING "CIFS: Unknown mount option %s\n", data); @@ -1378,6 +1381,13 @@ cifs_parse_mount_options(char *options, const char *devname, return 1; } } + + if (vol->multiuser && !(vol->secFlg & CIFSSEC_MAY_KRB5)) { + cERROR(1, "Multiuser mounts currently require krb5 " + "authentication!"); + return 1; + } + if (vol->UNCip == NULL) vol->UNCip = &vol->UNC[2]; @@ -2563,6 +2573,9 @@ static void setup_cifs_sb(struct smb_vol *pvolume_info, cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DYNPERM; if (pvolume_info->fsc) cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_FSCACHE; + if (pvolume_info->multiuser) + cifs_sb->mnt_cifs_flags |= (CIFS_MOUNT_MULTIUSER | + CIFS_MOUNT_NO_PERM); if (pvolume_info->direct_io) { cFYI(1, "mounting share using direct i/o"); cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO; -- cgit v1.2.2 From 2de970ff69bbcc5a4b7440df669a595b2b1acd73 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 6 Oct 2010 19:51:12 -0400 Subject: cifs: implement recurring workqueue job to prune old tcons Create a workqueue job that cleans out unused tlinks. For now, it uses a hardcoded expire time of 10 minutes. When it's done, the work rearms itself. On umount, the work is cancelled before tearing down the tlink tree. Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/connect.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) (limited to 'fs/cifs/connect.c') diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index e65f72d1f23b..1092e9e839c2 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -110,11 +110,13 @@ struct smb_vol { struct nls_table *local_nls; }; +/* FIXME: should these be tunable? */ #define TLINK_ERROR_EXPIRE (1 * HZ) - +#define TLINK_IDLE_EXPIRE (600 * HZ) static int ipv4_connect(struct TCP_Server_Info *server); static int ipv6_connect(struct TCP_Server_Info *server); +static void cifs_prune_tlinks(struct work_struct *work); /* * cifs tcp session reconnection @@ -2494,6 +2496,8 @@ convert_delimiter(char *path, char delim) static void setup_cifs_sb(struct smb_vol *pvolume_info, struct cifs_sb_info *cifs_sb) { + INIT_DELAYED_WORK(&cifs_sb->prune_tlinks, cifs_prune_tlinks); + if (pvolume_info->rsize > CIFSMaxBufSize) { cERROR(1, "rsize %d too large, using MaxBufSize", pvolume_info->rsize); @@ -2899,6 +2903,9 @@ remote_path_check: spin_unlock(&cifs_sb->tlink_tree_lock); radix_tree_preload_end(); + queue_delayed_work(system_nrt_wq, &cifs_sb->prune_tlinks, + TLINK_IDLE_EXPIRE); + mount_fail_check: /* on error free sesinfo and tcon struct if needed */ if (rc) { @@ -3090,6 +3097,8 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb) struct tcon_link *tlink[8]; unsigned long index = 0; + cancel_delayed_work_sync(&cifs_sb->prune_tlinks); + do { spin_lock(&cifs_sb->tlink_tree_lock); ret = radix_tree_gang_lookup(&cifs_sb->tlink_tree, @@ -3363,3 +3372,50 @@ wait_for_construction: return tlink; } + +/* + * periodic workqueue job that scans tcon_tree for a superblock and closes + * out tcons. + */ +static void +cifs_prune_tlinks(struct work_struct *work) +{ + struct cifs_sb_info *cifs_sb = container_of(work, struct cifs_sb_info, + prune_tlinks.work); + struct tcon_link *tlink[8]; + unsigned long now = jiffies; + unsigned long index = 0; + int i, ret; + + do { + spin_lock(&cifs_sb->tlink_tree_lock); + ret = radix_tree_gang_lookup(&cifs_sb->tlink_tree, + (void **)tlink, index, + ARRAY_SIZE(tlink)); + /* increment index for next pass */ + if (ret > 0) + index = tlink[ret - 1]->tl_index + 1; + for (i = 0; i < ret; i++) { + if (test_bit(TCON_LINK_MASTER, &tlink[i]->tl_flags) || + atomic_read(&tlink[i]->tl_count) != 0 || + time_after(tlink[i]->tl_time + TLINK_IDLE_EXPIRE, + now)) { + tlink[i] = NULL; + continue; + } + cifs_get_tlink(tlink[i]); + clear_bit(TCON_LINK_IN_TREE, &tlink[i]->tl_flags); + radix_tree_delete(&cifs_sb->tlink_tree, + tlink[i]->tl_index); + } + spin_unlock(&cifs_sb->tlink_tree_lock); + + for (i = 0; i < ret; i++) { + if (tlink[i] != NULL) + cifs_put_tlink(tlink[i]); + } + } while (ret != 0); + + queue_delayed_work(system_nrt_wq, &cifs_sb->prune_tlinks, + TLINK_IDLE_EXPIRE); +} -- cgit v1.2.2 From d2445556137c38ae15d3191174bfd235630ed7cd Mon Sep 17 00:00:00 2001 From: Steve French Date: Fri, 8 Oct 2010 03:38:46 +0000 Subject: [CIFS] Remove build warning Signed-off-by: Steve French --- fs/cifs/connect.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/cifs/connect.c') diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 1092e9e839c2..4944fc84d5ef 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -3185,7 +3185,7 @@ int cifs_setup_session(unsigned int xid, struct cifsSesInfo *ses, return rc; } -struct cifsTconInfo * +static struct cifsTconInfo * cifs_construct_tcon(struct cifs_sb_info *cifs_sb, uid_t fsuid) { struct cifsTconInfo *master_tcon = cifs_sb_master_tcon(cifs_sb); -- cgit v1.2.2 From 5d0d28824c76409f0d1a645bf0ae81318c8ffa42 Mon Sep 17 00:00:00 2001 From: Shirish Pargaonkar Date: Wed, 13 Oct 2010 18:15:00 -0500 Subject: NTLM authentication and signing - Calculate auth response per smb session Start calculation auth response within a session. Move/Add pertinet data structures like session key, server challenge and ntlmv2_hash in a session structure. We should do the calculations within a session before copying session key and response over to server data structures because a session setup can fail. Only after a very first smb session succeeds, it copies/makes its session key, session key of smb connection. This key stays with the smb connection throughout its life. Signed-off-by: Shirish Pargaonkar Reviewed-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/connect.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'fs/cifs/connect.c') diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 4944fc84d5ef..019f00380d12 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -173,6 +173,8 @@ cifs_reconnect(struct TCP_Server_Info *server) sock_release(server->ssocket); server->ssocket = NULL; } + server->sequence_number = 0; + server->session_estab = false; spin_lock(&GlobalMid_Lock); list_for_each(tmp, &server->pending_mid_q) { @@ -205,7 +207,6 @@ cifs_reconnect(struct TCP_Server_Info *server) spin_lock(&GlobalMid_Lock); if (server->tcpStatus != CifsExiting) server->tcpStatus = CifsGood; - server->sequence_number = 0; spin_unlock(&GlobalMid_Lock); /* atomic_set(&server->inFlight,0);*/ wake_up(&server->response_q); @@ -1631,6 +1632,7 @@ cifs_get_tcp_session(struct smb_vol *volume_info) volume_info->source_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL); memcpy(tcp_ses->server_RFC1001_name, volume_info->target_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL); + tcp_ses->session_estab = false; tcp_ses->sequence_number = 0; INIT_LIST_HEAD(&tcp_ses->tcp_ses_list); INIT_LIST_HEAD(&tcp_ses->smb_ses_list); @@ -2983,14 +2985,13 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, #ifdef CONFIG_CIFS_WEAK_PW_HASH if ((global_secflags & CIFSSEC_MAY_LANMAN) && (ses->server->secType == LANMAN)) - calc_lanman_hash(tcon->password, ses->server->cryptKey, + calc_lanman_hash(tcon->password, ses->cryptKey, ses->server->secMode & SECMODE_PW_ENCRYPT ? true : false, bcc_ptr); else #endif /* CIFS_WEAK_PW_HASH */ - SMBNTencrypt(tcon->password, ses->server->cryptKey, - bcc_ptr); + SMBNTencrypt(tcon->password, ses->cryptKey, bcc_ptr); bcc_ptr += CIFS_SESS_KEY_SIZE; if (ses->capabilities & CAP_UNICODE) { @@ -3175,6 +3176,15 @@ int cifs_setup_session(unsigned int xid, struct cifsSesInfo *ses, if (rc) { cERROR(1, "Send error in SessSetup = %d", rc); } else { + mutex_lock(&ses->server->srv_mutex); + if (!server->session_estab) { + memcpy(&server->session_key.data, + &ses->auth_key.data, ses->auth_key.len); + server->session_key.len = ses->auth_key.len; + ses->server->session_estab = true; + } + mutex_unlock(&server->srv_mutex); + cFYI(1, "CIFS Session Established successfully"); spin_lock(&GlobalMid_Lock); ses->status = CifsGood; -- cgit v1.2.2 From 3f9bcca7820a6711307b6499952b13cfcfc31dd6 Mon Sep 17 00:00:00 2001 From: Suresh Jayaraman Date: Mon, 18 Oct 2010 23:29:37 +0530 Subject: cifs: convert cifs_tcp_ses_lock from a rwlock to a spinlock cifs_tcp_ses_lock is a rwlock with protects the cifs_tcp_ses_list, server->smb_ses_list and the ses->tcon_list. It also protects a few ref counters in server, ses and tcon. In most cases the critical section doesn't seem to be large, in a few cases where it is slightly large, there seem to be really no benefit from concurrent access. I briefly considered RCU mechanism but it appears to me that there is no real need. Replace it with a spinlock and get rid of the last rwlock in the cifs code. Signed-off-by: Suresh Jayaraman Signed-off-by: Steve French --- fs/cifs/connect.c | 70 +++++++++++++++++++++++++++---------------------------- 1 file changed, 35 insertions(+), 35 deletions(-) (limited to 'fs/cifs/connect.c') diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 019f00380d12..7e73176acb58 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -150,7 +150,7 @@ cifs_reconnect(struct TCP_Server_Info *server) /* before reconnecting the tcp session, mark the smb session (uid) and the tid bad so they are not used until reconnected */ - read_lock(&cifs_tcp_ses_lock); + spin_lock(&cifs_tcp_ses_lock); list_for_each(tmp, &server->smb_ses_list) { ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list); ses->need_reconnect = true; @@ -160,7 +160,7 @@ cifs_reconnect(struct TCP_Server_Info *server) tcon->need_reconnect = true; } } - read_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); /* do not want to be sending data on a socket we are freeing */ mutex_lock(&server->srv_mutex); if (server->ssocket) { @@ -637,9 +637,9 @@ multi_t2_fnd: } /* end while !EXITING */ /* take it off the list, if it's not already */ - write_lock(&cifs_tcp_ses_lock); + spin_lock(&cifs_tcp_ses_lock); list_del_init(&server->tcp_ses_list); - write_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); spin_lock(&GlobalMid_Lock); server->tcpStatus = CifsExiting; @@ -677,7 +677,7 @@ multi_t2_fnd: * BB: we shouldn't have to do any of this. It shouldn't be * possible to exit from the thread with active SMB sessions */ - read_lock(&cifs_tcp_ses_lock); + spin_lock(&cifs_tcp_ses_lock); if (list_empty(&server->pending_mid_q)) { /* loop through server session structures attached to this and mark them dead */ @@ -687,7 +687,7 @@ multi_t2_fnd: ses->status = CifsExiting; ses->server = NULL; } - read_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); } else { /* although we can not zero the server struct pointer yet, since there are active requests which may depnd on them, @@ -710,7 +710,7 @@ multi_t2_fnd: } } spin_unlock(&GlobalMid_Lock); - read_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); /* 1/8th of sec is more than enough time for them to exit */ msleep(125); } @@ -733,12 +733,12 @@ multi_t2_fnd: if a crazy root user tried to kill cifsd kernel thread explicitly this might happen) */ /* BB: This shouldn't be necessary, see above */ - read_lock(&cifs_tcp_ses_lock); + spin_lock(&cifs_tcp_ses_lock); list_for_each(tmp, &server->smb_ses_list) { ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list); ses->server = NULL; } - read_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); kfree(server->hostname); task_to_wake = xchg(&server->tsk, NULL); @@ -1524,7 +1524,7 @@ cifs_find_tcp_session(struct sockaddr *addr, struct smb_vol *vol) { struct TCP_Server_Info *server; - write_lock(&cifs_tcp_ses_lock); + spin_lock(&cifs_tcp_ses_lock); list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { if (!match_address(server, addr, (struct sockaddr *)&vol->srcaddr)) @@ -1534,11 +1534,11 @@ cifs_find_tcp_session(struct sockaddr *addr, struct smb_vol *vol) continue; ++server->srv_count; - write_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); cFYI(1, "Existing tcp session with server found"); return server; } - write_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); return NULL; } @@ -1547,14 +1547,14 @@ cifs_put_tcp_session(struct TCP_Server_Info *server) { struct task_struct *task; - write_lock(&cifs_tcp_ses_lock); + spin_lock(&cifs_tcp_ses_lock); if (--server->srv_count > 0) { - write_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); return; } list_del_init(&server->tcp_ses_list); - write_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); spin_lock(&GlobalMid_Lock); server->tcpStatus = CifsExiting; @@ -1679,9 +1679,9 @@ cifs_get_tcp_session(struct smb_vol *volume_info) } /* thread spawned, put it on the list */ - write_lock(&cifs_tcp_ses_lock); + spin_lock(&cifs_tcp_ses_lock); list_add(&tcp_ses->tcp_ses_list, &cifs_tcp_ses_list); - write_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); cifs_fscache_get_client_cookie(tcp_ses); @@ -1703,7 +1703,7 @@ cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb_vol *vol) { struct cifsSesInfo *ses; - write_lock(&cifs_tcp_ses_lock); + spin_lock(&cifs_tcp_ses_lock); list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { switch (server->secType) { case Kerberos: @@ -1723,10 +1723,10 @@ cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb_vol *vol) continue; } ++ses->ses_count; - write_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); return ses; } - write_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); return NULL; } @@ -1737,14 +1737,14 @@ cifs_put_smb_ses(struct cifsSesInfo *ses) struct TCP_Server_Info *server = ses->server; cFYI(1, "%s: ses_count=%d\n", __func__, ses->ses_count); - write_lock(&cifs_tcp_ses_lock); + spin_lock(&cifs_tcp_ses_lock); if (--ses->ses_count > 0) { - write_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); return; } list_del_init(&ses->smb_ses_list); - write_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); if (ses->status == CifsGood) { xid = GetXid(); @@ -1841,9 +1841,9 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info) goto get_ses_fail; /* success, put it on the list */ - write_lock(&cifs_tcp_ses_lock); + spin_lock(&cifs_tcp_ses_lock); list_add(&ses->smb_ses_list, &server->smb_ses_list); - write_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); FreeXid(xid); return ses; @@ -1860,7 +1860,7 @@ cifs_find_tcon(struct cifsSesInfo *ses, const char *unc) struct list_head *tmp; struct cifsTconInfo *tcon; - write_lock(&cifs_tcp_ses_lock); + spin_lock(&cifs_tcp_ses_lock); list_for_each(tmp, &ses->tcon_list) { tcon = list_entry(tmp, struct cifsTconInfo, tcon_list); if (tcon->tidStatus == CifsExiting) @@ -1869,10 +1869,10 @@ cifs_find_tcon(struct cifsSesInfo *ses, const char *unc) continue; ++tcon->tc_count; - write_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); return tcon; } - write_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); return NULL; } @@ -1883,14 +1883,14 @@ cifs_put_tcon(struct cifsTconInfo *tcon) struct cifsSesInfo *ses = tcon->ses; cFYI(1, "%s: tc_count=%d\n", __func__, tcon->tc_count); - write_lock(&cifs_tcp_ses_lock); + spin_lock(&cifs_tcp_ses_lock); if (--tcon->tc_count > 0) { - write_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); return; } list_del_init(&tcon->tcon_list); - write_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); xid = GetXid(); CIFSSMBTDis(xid, tcon); @@ -1963,9 +1963,9 @@ cifs_get_tcon(struct cifsSesInfo *ses, struct smb_vol *volume_info) tcon->nocase = volume_info->nocase; tcon->local_lease = volume_info->local_lease; - write_lock(&cifs_tcp_ses_lock); + spin_lock(&cifs_tcp_ses_lock); list_add(&tcon->tcon_list, &ses->tcon_list); - write_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); cifs_fscache_get_super_cookie(tcon); @@ -3225,9 +3225,9 @@ cifs_construct_tcon(struct cifs_sb_info *cifs_sb, uid_t fsuid) vol_info->secFlg = CIFSSEC_MUST_KRB5; /* get a reference for the same TCP session */ - write_lock(&cifs_tcp_ses_lock); + spin_lock(&cifs_tcp_ses_lock); ++master_tcon->ses->server->srv_count; - write_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); ses = cifs_get_smb_ses(master_tcon->ses->server, vol_info); if (IS_ERR(ses)) { -- cgit v1.2.2