GIT 2a97468024fb5b6eccee2a67a7796485c829343a git+ssh://master.kernel.org/pub/scm/linux/kernel/git/sfrench/cifs-2.6.git commit Author: Petr Tesarik Date: Tue Nov 20 02:24:08 2007 +0000 [CIFS] Fix spurious reconnect on 2nd peek from read of SMB length When retrying kernel_recvmsg() because of a short read, check returned length against the remaining length, not against total length. This avoids unneeded session reconnects which would otherwise occur when kernel_recvmsg() finally returns zero when asked to read zero bytes. Signed-off-by: Petr Tesarik Signed-off-by: Jeff Layton Signed-off-by: Steve French commit f7a44eadd5a03b8455c7caab402ce96811c6903d Author: Steve French Date: Sat Nov 17 00:01:51 2007 +0000 [CIFS] remove build warning CC: Jeff Layton Signed-off-by: Steve French commit 2442421b176420eca7cb68c575fc221332f488d8 Author: Steve French Date: Fri Nov 16 23:37:35 2007 +0000 [CIFS] Have CIFS_SessSetup build correct SPNEGO SessionSetup request Have CIFS_SessSetup call cifs_get_spnego_key when Kerberos is negotiated. Use the info in the key payload to build a session setup request packet. Also clean up how the request buffer in the function is freed on error. With appropriate user space helper (in samba/source/client). Kerberos support (secure session establishment can be done now via Kerberos, previously users would have to use NTLMv2 instead for more secure session setup). Signed-off-by: Jeff Layton Signed-off-by: Steve French commit 8840dee9dc53883883c321d2811e9f87700d9350 Author: Steve French Date: Fri Nov 16 23:05:52 2007 +0000 [CIFS] minor checkpatch cleanup Signed-off-by: Steve French commit d6c2e4d02b72d8ae63784bdc57cfa285128de211 Author: Jeff Layton Date: Fri Nov 16 22:23:17 2007 +0000 [CIFS] have cifs_get_spnego_key get the hostname from TCP_Server_Info Signed-off-by: Jeff Layton Signed-off-by: Steve French commit c359cf3c61c6ea9f4f461a8bd22023a15d75d9b5 Author: Jeff Layton Date: Fri Nov 16 22:22:06 2007 +0000 [CIFS] add hostname field to TCP_Server_Info struct ...and populate it with the hostname portion of the UNC string. Signed-off-by: Jeff Layton Signed-off-by: Steve French commit 70fe7dc05596a405ee6a83265f675a544e32f7d8 Author: Jeff Layton Date: Fri Nov 16 22:21:07 2007 +0000 [CIFS] clean up error handling in cifs_mount Move all of the kfree's sprinkled in the middle of the function to the end, and have the code set rc and just goto there on error. Also zero out the password string before freeing it. Looks like this should also fix a potential memory leak of the prepath string if an error occurs near the end of the function. Signed-off-by: Jeff Layton Signed-off-by: Steve French commit 68bf728a225b7f2045bb501854d6e7695b9b015d Author: Steve French Date: Fri Nov 16 18:32:52 2007 +0000 [CIFS] add ver= prefix to upcall format version Acked-by: Jeff Layton Acked-by: Igor Mammedov Signed-off-by: Steve French commit 133672efbc1085f9af990bdc145e1822ea93bcf3 Author: Steve French Date: Tue Nov 13 22:41:37 2007 +0000 [CIFS] Fix buffer overflow if server sends corrupt response to small request In SendReceive() function in transport.c - it memcpy's message payload into a buffer passed via out_buf param. The function assumes that all buffers are of size (CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) , unfortunately it is also called with smaller (MAX_CIFS_SMALL_BUFFER_SIZE) buffers. There are eight callers (SMB worker functions) which are primarily affected by this change: TreeDisconnect, uLogoff, Close, findClose, SetFileSize, SetFileTimes, Lock and PosixLock CC: Dave Kleikamp CC: Przemyslaw Wegrzyn Acked-by: Jeff Layton Signed-off-by: Steve French fs/cifs/CHANGES | 1 + fs/cifs/TODO | 2 +- fs/cifs/cifs_spnego.c | 20 +++++--- fs/cifs/cifs_spnego.h | 1 + fs/cifs/cifsglob.h | 13 +++++ fs/cifs/cifsproto.h | 17 +++--- fs/cifs/cifssmb.c | 97 +++++++++++++--------------------- fs/cifs/connect.c | 141 ++++++++++++++++++++++++++----------------------- fs/cifs/file.c | 20 ++++---- fs/cifs/sess.c | 93 ++++++++++++++++++++++++++------ fs/cifs/transport.c | 91 ++++++++++++++++++++++++------- 11 files changed, 303 insertions(+), 193 deletions(-) diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES index 64dd222..e31aa74 100644 --- a/fs/cifs/CHANGES +++ b/fs/cifs/CHANGES @@ -1,6 +1,7 @@ Version 1.52 ------------ Fix oops on second mount to server when null auth is used. +Enable experimental Kerberos support Version 1.51 ------------ diff --git a/fs/cifs/TODO b/fs/cifs/TODO index 29d4b27..a8852c2 100644 --- a/fs/cifs/TODO +++ b/fs/cifs/TODO @@ -16,7 +16,7 @@ SecurityDescriptors c) Better pam/winbind integration (e.g. to handle uid mapping better) -d) Kerberos/SPNEGO session setup support - (started) +d) Verify that Kerberos signing works e) Cleanup now unneeded SessSetup code in fs/cifs/connect.c and add back in NTLMSSP code if any servers diff --git a/fs/cifs/cifs_spnego.c b/fs/cifs/cifs_spnego.c index ad54a3a..1529d2b 100644 --- a/fs/cifs/cifs_spnego.c +++ b/fs/cifs/cifs_spnego.c @@ -66,20 +66,26 @@ struct key_type cifs_spnego_key_type = { .describe = user_describe, }; +#define MAX_VER_STR_LEN 9 /* length of longest version string e.g. + strlen(";ver=0xFF") */ +#define MAX_MECH_STR_LEN 13 /* length of longest security mechanism name, eg + in future could have strlen(";sec=ntlmsspi") */ +#define MAX_IPV6_ADDR_LEN 42 /* eg FEDC:BA98:7654:3210:FEDC:BA98:7654:3210/60 */ /* get a key struct with a SPNEGO security blob, suitable for session setup */ struct key * -cifs_get_spnego_key(struct cifsSesInfo *sesInfo, const char *hostname) +cifs_get_spnego_key(struct cifsSesInfo *sesInfo) { struct TCP_Server_Info *server = sesInfo->server; char *description, *dp; size_t desc_len; struct key *spnego_key; + const char *hostname = server->hostname; - - /* version + ;ip{4|6}= + address + ;host=hostname + - ;sec= + ;uid= + NULL */ - desc_len = 4 + 5 + 32 + 1 + 5 + strlen(hostname) + - strlen(";sec=krb5") + 7 + sizeof(uid_t)*2 + 1; + /* BB: come up with better scheme for determining length */ + /* length of fields (with semicolons): ver=0xyz ipv4= ipaddress host= + hostname sec=mechanism uid=0x uid */ + desc_len = MAX_VER_STR_LEN + 5 + MAX_IPV6_ADDR_LEN + 1 + 6 + + strlen(hostname) + MAX_MECH_STR_LEN + 8 + (sizeof(uid_t) * 2); spnego_key = ERR_PTR(-ENOMEM); description = kzalloc(desc_len, GFP_KERNEL); if (description == NULL) @@ -88,7 +94,7 @@ cifs_get_spnego_key(struct cifsSesInfo *sesInfo, const char *hostname) dp = description; /* start with version and hostname portion of UNC string */ spnego_key = ERR_PTR(-EINVAL); - sprintf(dp, "0x%2.2x;host=%s;", CIFS_SPNEGO_UPCALL_VERSION, + sprintf(dp, "ver=0x%x;host=%s;", CIFS_SPNEGO_UPCALL_VERSION, hostname); dp = description + strlen(description); diff --git a/fs/cifs/cifs_spnego.h b/fs/cifs/cifs_spnego.h index f443f3b..05a34b1 100644 --- a/fs/cifs/cifs_spnego.h +++ b/fs/cifs/cifs_spnego.h @@ -41,6 +41,7 @@ struct cifs_spnego_msg { #ifdef __KERNEL__ extern struct key_type cifs_spnego_key_type; +extern struct key *cifs_get_spnego_key(struct cifsSesInfo *sesInfo); #endif /* KERNEL */ #endif /* _CIFS_SPNEGO_H */ diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 87f51f2..1fde219 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -110,6 +110,7 @@ struct mac_key { unsigned int len; union { char ntlm[CIFS_SESS_KEY_SIZE + 16]; + char krb5[CIFS_SESS_KEY_SIZE + 16]; /* BB: length correct? */ struct { char key[16]; struct ntlmv2_resp resp; @@ -139,6 +140,7 @@ struct TCP_Server_Info { /* 15 character server name + 0x20 16th byte indicating type = srv */ char server_RFC1001_name[SERVER_NAME_LEN_WITH_NULL]; char unicode_server_Name[SERVER_NAME_LEN_WITH_NULL * 2]; + char *hostname; /* hostname portion of UNC string */ struct socket *ssocket; union { struct sockaddr_in sockAddr; @@ -471,6 +473,17 @@ struct dir_notify_req { #define CIFS_LARGE_BUFFER 2 #define CIFS_IOVEC 4 /* array of response buffers */ +/* Type of Request to SendReceive2 */ +#define CIFS_STD_OP 0 /* normal request timeout */ +#define CIFS_LONG_OP 1 /* long op (up to 45 sec, oplock time) */ +#define CIFS_VLONG_OP 2 /* sloow op - can take up to 180 seconds */ +#define CIFS_BLOCKING_OP 4 /* operation can block */ +#define CIFS_ASYNC_OP 8 /* do not wait for response */ +#define CIFS_TIMEOUT_MASK 0x00F /* only one of 5 above set in req */ +#define CIFS_LOG_ERROR 0x010 /* log NT STATUS if non-zero */ +#define CIFS_LARGE_BUF_OP 0x020 /* large request buffer */ +#define CIFS_NO_RESP 0x040 /* no response buffer required */ + /* Security Flags: indicate type of session setup needed */ #define CIFSSEC_MAY_SIGN 0x00001 #define CIFSSEC_MAY_NTLM 0x00002 diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index dd1d7c2..8350eec 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -48,10 +48,11 @@ extern int SendReceive(const unsigned int /* xid */ , struct cifsSesInfo *, struct smb_hdr * /* input */ , struct smb_hdr * /* out */ , int * /* bytes returned */ , const int long_op); +extern int SendReceiveNoRsp(const unsigned int xid, struct cifsSesInfo *ses, + struct smb_hdr *in_buf, int flags); extern int SendReceive2(const unsigned int /* xid */ , struct cifsSesInfo *, struct kvec *, int /* nvec to send */, - int * /* type of buf returned */ , const int long_op, - const int logError /* whether to log status code*/ ); + int * /* type of buf returned */ , const int flags); extern int SendReceiveBlockingLock(const unsigned int /* xid */ , struct cifsTconInfo *, struct smb_hdr * /* input */ , @@ -76,8 +77,6 @@ extern void header_assemble(struct smb_hdr *, char /* command */ , extern int small_smb_init_no_tc(const int smb_cmd, const int wct, struct cifsSesInfo *ses, void **request_buf); -extern struct key *cifs_get_spnego_key(struct cifsSesInfo *sesInfo, - const char *hostname); extern int CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, const int stage, const struct nls_table *nls_cp); @@ -248,15 +247,15 @@ extern int CIFSSMBQueryReparseLinkInfo(const int xid, extern int CIFSSMBOpen(const int xid, struct cifsTconInfo *tcon, const char *fileName, const int disposition, const int access_flags, const int omode, - __u16 * netfid, int *pOplock, FILE_ALL_INFO *, + __u16 *netfid, int *pOplock, FILE_ALL_INFO *, const struct nls_table *nls_codepage, int remap); extern int SMBLegacyOpen(const int xid, struct cifsTconInfo *tcon, const char *fileName, const int disposition, const int access_flags, const int omode, - __u16 * netfid, int *pOplock, FILE_ALL_INFO *, + __u16 *netfid, int *pOplock, FILE_ALL_INFO *, const struct nls_table *nls_codepage, int remap); extern int CIFSPOSIXCreate(const int xid, struct cifsTconInfo *tcon, - u32 posix_flags, __u64 mode, __u16 * netfid, + u32 posix_flags, __u64 mode, __u16 *netfid, FILE_UNIX_BASIC_INFO *pRetData, __u32 *pOplock, const char *name, const struct nls_table *nls_codepage, int remap); @@ -277,7 +276,7 @@ extern int CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon, const __u64 offset, unsigned int *nbytes, struct kvec *iov, const int nvec, const int long_op); extern int CIFSGetSrvInodeNumber(const int xid, struct cifsTconInfo *tcon, - const unsigned char *searchName, __u64 * inode_number, + const unsigned char *searchName, __u64 *inode_number, const struct nls_table *nls_codepage, int remap_special_chars); extern int cifs_convertUCSpath(char *target, const __le16 *source, int maxlen, @@ -352,5 +351,5 @@ extern int CIFSSMBSetPosixACL(const int xid, struct cifsTconInfo *tcon, const char *local_acl, const int buflen, const int acl_type, const struct nls_table *nls_codepage, int remap_special_chars); extern int CIFSGetExtAttr(const int xid, struct cifsTconInfo *tcon, - const int netfid, __u64 * pExtAttrBits, __u64 *pMask); + const int netfid, __u64 *pExtAttrBits, __u64 *pMask); #endif /* _CIFSPROTO_H */ diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 59d7b7c..9e8a6be 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -698,9 +698,7 @@ int CIFSSMBTDis(const int xid, struct cifsTconInfo *tcon) { struct smb_hdr *smb_buffer; - struct smb_hdr *smb_buffer_response; /* BB removeme BB */ int rc = 0; - int length; cFYI(1, ("In tree disconnect")); /* @@ -737,16 +735,12 @@ CIFSSMBTDis(const int xid, struct cifsTconInfo *tcon) if (rc) { up(&tcon->tconSem); return rc; - } else { - smb_buffer_response = smb_buffer; /* BB removeme BB */ } - rc = SendReceive(xid, tcon->ses, smb_buffer, smb_buffer_response, - &length, 0); + + rc = SendReceiveNoRsp(xid, tcon->ses, smb_buffer, 0); if (rc) cFYI(1, ("Tree disconnect failed %d", rc)); - if (smb_buffer) - cifs_small_buf_release(smb_buffer); up(&tcon->tconSem); /* No need to return error on this operation if tid invalidated and @@ -760,10 +754,8 @@ CIFSSMBTDis(const int xid, struct cifsTconInfo *tcon) int CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses) { - struct smb_hdr *smb_buffer_response; LOGOFF_ANDX_REQ *pSMB; int rc = 0; - int length; cFYI(1, ("In SMBLogoff for session disconnect")); if (ses) @@ -782,8 +774,6 @@ CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses) return rc; } - smb_buffer_response = (struct smb_hdr *)pSMB; /* BB removeme BB */ - if (ses->server) { pSMB->hdr.Mid = GetNextMid(ses->server); @@ -795,8 +785,7 @@ CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses) pSMB->hdr.Uid = ses->Suid; pSMB->AndXCommand = 0xFF; - rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB, - smb_buffer_response, &length, 0); + rc = SendReceiveNoRsp(xid, ses, (struct smb_hdr *) pSMB, 0); if (ses->server) { atomic_dec(&ses->server->socketUseCount); if (atomic_read(&ses->server->socketUseCount) == 0) { @@ -807,7 +796,6 @@ CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses) } } up(&ses->sesSem); - cifs_small_buf_release(pSMB); /* if session dead then we do not need to do ulogoff, since server closed smb session, no sense reporting @@ -1255,7 +1243,7 @@ OldOpenRetry: pSMB->ByteCount = cpu_to_le16(count); /* long_op set to 1 to allow for oplock break timeouts */ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 1); + (struct smb_hdr *)pSMBr, &bytes_returned, CIFS_LONG_OP); cifs_stats_inc(&tcon->num_opens); if (rc) { cFYI(1, ("Error in Open = %d", rc)); @@ -1368,7 +1356,7 @@ openRetry: pSMB->ByteCount = cpu_to_le16(count); /* long_op set to 1 to allow for oplock break timeouts */ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 1); + (struct smb_hdr *)pSMBr, &bytes_returned, CIFS_LONG_OP); cifs_stats_inc(&tcon->num_opens); if (rc) { cFYI(1, ("Error in Open = %d", rc)); @@ -1446,7 +1434,7 @@ CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, const int netfid, iov[0].iov_base = (char *)pSMB; iov[0].iov_len = pSMB->hdr.smb_buf_length + 4; rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovecs */, - &resp_buf_type, 0 /* not long op */, 1 /* log err */ ); + &resp_buf_type, CIFS_STD_OP | CIFS_LOG_ERROR); cifs_stats_inc(&tcon->num_reads); pSMBr = (READ_RSP *)iov[0].iov_base; if (rc) { @@ -1665,7 +1653,7 @@ CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon, rc = SendReceive2(xid, tcon->ses, iov, n_vec + 1, &resp_buf_type, - long_op, 0 /* do not log STATUS code */ ); + long_op); cifs_stats_inc(&tcon->num_writes); if (rc) { cFYI(1, ("Send error Write2 = %d", rc)); @@ -1707,7 +1695,7 @@ CIFSSMBLock(const int xid, struct cifsTconInfo *tcon, int timeout = 0; __u16 count; - cFYI(1, ("In CIFSSMBLock - timeout %d numLock %d", waitFlag, numLock)); + cFYI(1, ("CIFSSMBLock timeout %d numLock %d", waitFlag, numLock)); rc = small_smb_init(SMB_COM_LOCKING_ANDX, 8, tcon, (void **) &pSMB); if (rc) @@ -1716,10 +1704,10 @@ CIFSSMBLock(const int xid, struct cifsTconInfo *tcon, pSMBr = (LOCK_RSP *)pSMB; /* BB removeme BB */ if (lockType == LOCKING_ANDX_OPLOCK_RELEASE) { - timeout = -1; /* no response expected */ + timeout = CIFS_ASYNC_OP; /* no response expected */ pSMB->Timeout = 0; } else if (waitFlag == TRUE) { - timeout = 3; /* blocking operation, no timeout */ + timeout = CIFS_BLOCKING_OP; /* blocking operation, no timeout */ pSMB->Timeout = cpu_to_le32(-1);/* blocking - do not time out */ } else { pSMB->Timeout = 0; @@ -1749,15 +1737,16 @@ CIFSSMBLock(const int xid, struct cifsTconInfo *tcon, if (waitFlag) { rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned); + cifs_small_buf_release(pSMB); } else { - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, timeout); + rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *)pSMB, + timeout); + /* SMB buffer freed by function above */ } cifs_stats_inc(&tcon->num_locks); if (rc) { cFYI(1, ("Send error in Lock = %d", rc)); } - cifs_small_buf_release(pSMB); /* Note: On -EAGAIN error only caller can retry on handle based calls since file handle passed in no longer valid */ @@ -1776,7 +1765,9 @@ CIFSSMBPosixLock(const int xid, struct cifsTconInfo *tcon, int rc = 0; int timeout = 0; int bytes_returned = 0; + int resp_buf_type = 0; __u16 params, param_offset, offset, byte_count, count; + struct kvec iov[1]; cFYI(1, ("Posix Lock")); @@ -1818,7 +1809,7 @@ CIFSSMBPosixLock(const int xid, struct cifsTconInfo *tcon, parm_data->lock_type = cpu_to_le16(lock_type); if (waitFlag) { - timeout = 3; /* blocking operation, no timeout */ + timeout = CIFS_BLOCKING_OP; /* blocking operation, no timeout */ parm_data->lock_flags = cpu_to_le16(1); pSMB->Timeout = cpu_to_le32(-1); } else @@ -1838,8 +1829,13 @@ CIFSSMBPosixLock(const int xid, struct cifsTconInfo *tcon, rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned); } else { - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, timeout); + iov[0].iov_base = (char *)pSMB; + iov[0].iov_len = pSMB->hdr.smb_buf_length + 4; + rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovecs */, + &resp_buf_type, timeout); + pSMB = NULL; /* request buf already freed by SendReceive2. Do + not try to free it twice below on exit */ + pSMBr = (struct smb_com_transaction2_sfi_rsp *)iov[0].iov_base; } if (rc) { @@ -1874,6 +1870,11 @@ plk_err_exit: if (pSMB) cifs_small_buf_release(pSMB); + if (resp_buf_type == CIFS_SMALL_BUFFER) + cifs_small_buf_release(iov[0].iov_base); + else if (resp_buf_type == CIFS_LARGE_BUFFER) + cifs_buf_release(iov[0].iov_base); + /* Note: On -EAGAIN error only caller can retry on handle based calls since file handle passed in no longer valid */ @@ -1886,8 +1887,6 @@ CIFSSMBClose(const int xid, struct cifsTconInfo *tcon, int smb_file_id) { int rc = 0; CLOSE_REQ *pSMB = NULL; - CLOSE_RSP *pSMBr = NULL; - int bytes_returned; cFYI(1, ("In CIFSSMBClose")); /* do not retry on dead session on close */ @@ -1897,13 +1896,10 @@ CIFSSMBClose(const int xid, struct cifsTconInfo *tcon, int smb_file_id) if (rc) return rc; - pSMBr = (CLOSE_RSP *)pSMB; /* BB removeme BB */ - pSMB->FileID = (__u16) smb_file_id; pSMB->LastWriteTime = 0xFFFFFFFF; pSMB->ByteCount = 0; - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); + rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0); cifs_stats_inc(&tcon->num_closes); if (rc) { if (rc != -EINTR) { @@ -1912,8 +1908,6 @@ CIFSSMBClose(const int xid, struct cifsTconInfo *tcon, int smb_file_id) } } - cifs_small_buf_release(pSMB); - /* Since session is dead, file will be closed on server already */ if (rc == -EAGAIN) rc = 0; @@ -3102,7 +3096,7 @@ CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid, iov[0].iov_len = pSMB->hdr.smb_buf_length + 4; rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovec */, &buf_type, - 0 /* not long op */, 0 /* do not log STATUS codes */ ); + CIFS_STD_OP); cifs_stats_inc(&tcon->num_acl_get); if (rc) { cFYI(1, ("Send error in QuerySecDesc = %d", rc)); @@ -3763,8 +3757,6 @@ CIFSFindClose(const int xid, struct cifsTconInfo *tcon, { int rc = 0; FINDCLOSE_REQ *pSMB = NULL; - CLOSE_RSP *pSMBr = NULL; /* BB removeme BB */ - int bytes_returned; cFYI(1, ("In CIFSSMBFindClose")); rc = small_smb_init(SMB_COM_FIND_CLOSE2, 1, tcon, (void **)&pSMB); @@ -3776,16 +3768,13 @@ CIFSFindClose(const int xid, struct cifsTconInfo *tcon, if (rc) return rc; - pSMBr = (CLOSE_RSP *)pSMB; /* BB removeme BB */ pSMB->FileID = searchHandle; pSMB->ByteCount = 0; - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); + rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0); if (rc) { cERROR(1, ("Send error in FindClose = %d", rc)); } cifs_stats_inc(&tcon->num_fclose); - cifs_small_buf_release(pSMB); /* Since session is dead, search handle closed on server already */ if (rc == -EAGAIN) @@ -4707,11 +4696,9 @@ CIFSSMBSetFileSize(const int xid, struct cifsTconInfo *tcon, __u64 size, __u16 fid, __u32 pid_of_opener, int SetAllocation) { struct smb_com_transaction2_sfi_req *pSMB = NULL; - struct smb_com_transaction2_sfi_rsp *pSMBr = NULL; char *data_offset; struct file_end_of_file_info *parm_data; int rc = 0; - int bytes_returned = 0; __u16 params, param_offset, offset, byte_count, count; cFYI(1, ("SetFileSize (via SetFileInfo) %lld", @@ -4721,8 +4708,6 @@ CIFSSMBSetFileSize(const int xid, struct cifsTconInfo *tcon, __u64 size, if (rc) return rc; - pSMBr = (struct smb_com_transaction2_sfi_rsp *)pSMB; - pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener); pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16)); @@ -4773,17 +4758,13 @@ CIFSSMBSetFileSize(const int xid, struct cifsTconInfo *tcon, __u64 size, pSMB->Reserved4 = 0; pSMB->hdr.smb_buf_length += byte_count; pSMB->ByteCount = cpu_to_le16(byte_count); - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); + rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0); if (rc) { cFYI(1, ("Send error in SetFileInfo (SetFileSize) = %d", rc)); } - if (pSMB) - cifs_small_buf_release(pSMB); - /* Note: On -EAGAIN error only caller can retry on handle based calls since file handle passed in no longer valid */ @@ -4801,10 +4782,8 @@ CIFSSMBSetFileTimes(const int xid, struct cifsTconInfo *tcon, const FILE_BASIC_INFO *data, __u16 fid) { struct smb_com_transaction2_sfi_req *pSMB = NULL; - struct smb_com_transaction2_sfi_rsp *pSMBr = NULL; char *data_offset; int rc = 0; - int bytes_returned = 0; __u16 params, param_offset, offset, byte_count, count; cFYI(1, ("Set Times (via SetFileInfo)")); @@ -4813,8 +4792,6 @@ CIFSSMBSetFileTimes(const int xid, struct cifsTconInfo *tcon, if (rc) return rc; - pSMBr = (struct smb_com_transaction2_sfi_rsp *)pSMB; - /* At this point there is no need to override the current pid with the pid of the opener, but that could change if we someday use an existing handle (rather than opening one on the fly) */ @@ -4854,14 +4831,11 @@ CIFSSMBSetFileTimes(const int xid, struct cifsTconInfo *tcon, pSMB->hdr.smb_buf_length += byte_count; pSMB->ByteCount = cpu_to_le16(byte_count); memcpy(data_offset, data, sizeof(FILE_BASIC_INFO)); - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); + rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0); if (rc) { cFYI(1, ("Send error in Set Time (SetFileInfo) = %d", rc)); } - cifs_small_buf_release(pSMB); - /* Note: On -EAGAIN error only caller can retry on handle based calls since file handle passed in no longer valid */ @@ -5152,7 +5126,8 @@ int CIFSSMBNotify(const int xid, struct cifsTconInfo *tcon, pSMB->ByteCount = 0; rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, -1); + (struct smb_hdr *)pSMBr, &bytes_returned, + CIFS_ASYNC_OP); if (rc) { cFYI(1, ("Error in Notify = %d", rc)); } else { diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index c52a76f..fd9147c 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -438,9 +438,9 @@ incomplete_rcv: csocket = server->ssocket; wake_up(&server->response_q); continue; - } else if (length < 4) { - cFYI(1, ("less than four bytes received (%d bytes)", - length)); + } else if (length < pdu_length) { + cFYI(1, ("requested %d bytes but only got %d bytes", + pdu_length, length)); pdu_length -= length; msleep(1); goto incomplete_rcv; @@ -752,6 +752,7 @@ multi_t2_fnd: } write_unlock(&GlobalSMBSeslock); + kfree(server->hostname); kfree(server); if (length > 0) mempool_resize(cifs_req_poolp, length + cifs_min_rcv, @@ -760,6 +761,34 @@ multi_t2_fnd: return 0; } +/* extract the host portion of the UNC string */ +static char * +extract_hostname(const char *unc) +{ + const char *src; + char *dst, *delim; + unsigned int len; + + /* skip double chars at beginning of string */ + /* BB: check validity of these bytes? */ + src = unc + 2; + + /* delimiter between hostname and sharename is always '\\' now */ + delim = strchr(src, '\\'); + if (!delim) + return ERR_PTR(-EINVAL); + + len = delim - src; + dst = kmalloc((len + 1), GFP_KERNEL); + if (dst == NULL) + return ERR_PTR(-ENOMEM); + + memcpy(dst, src, len); + dst[len] = '\0'; + + return dst; +} + static int cifs_parse_mount_options(char *options, const char *devname, struct smb_vol *vol) @@ -1781,11 +1810,8 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, memset(&volume_info, 0, sizeof(struct smb_vol)); if (cifs_parse_mount_options(mount_data, devname, &volume_info)) { - kfree(volume_info.UNC); - kfree(volume_info.password); - kfree(volume_info.prepath); - FreeXid(xid); - return -EINVAL; + rc = -EINVAL; + goto out; } if (volume_info.nullauth) { @@ -1798,11 +1824,8 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, cifserror("No username specified"); /* In userspace mount helper we can get user name from alternate locations such as env variables and files on disk */ - kfree(volume_info.UNC); - kfree(volume_info.password); - kfree(volume_info.prepath); - FreeXid(xid); - return -EINVAL; + rc = -EINVAL; + goto out; } if (volume_info.UNCip && volume_info.UNC) { @@ -1821,11 +1844,8 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, if (rc <= 0) { /* we failed translating address */ - kfree(volume_info.UNC); - kfree(volume_info.password); - kfree(volume_info.prepath); - FreeXid(xid); - return -EINVAL; + rc = -EINVAL; + goto out; } cFYI(1, ("UNC: %s ip: %s", volume_info.UNC, volume_info.UNCip)); @@ -1835,20 +1855,14 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, /* BB using ip addr as server name to connect to the DFS root below */ cERROR(1, ("Connecting to DFS root not implemented yet")); - kfree(volume_info.UNC); - kfree(volume_info.password); - kfree(volume_info.prepath); - FreeXid(xid); - return -EINVAL; + rc = -EINVAL; + goto out; } else /* which servers DFS root would we conect to */ { cERROR(1, ("CIFS mount error: No UNC path (e.g. -o " "unc=//192.168.1.100/public) specified")); - kfree(volume_info.UNC); - kfree(volume_info.password); - kfree(volume_info.prepath); - FreeXid(xid); - return -EINVAL; + rc = -EINVAL; + goto out; } /* this is needed for ASCII cp to Unicode converts */ @@ -1860,11 +1874,8 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, if (cifs_sb->local_nls == NULL) { cERROR(1, ("CIFS mount error: iocharset %s not found", volume_info.iocharset)); - kfree(volume_info.UNC); - kfree(volume_info.password); - kfree(volume_info.prepath); - FreeXid(xid); - return -ELIBACC; + rc = -ELIBACC; + goto out; } } @@ -1878,11 +1889,8 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, &sin_server6.sin6_addr, volume_info.username, &srvTcp); } else { - kfree(volume_info.UNC); - kfree(volume_info.password); - kfree(volume_info.prepath); - FreeXid(xid); - return -EINVAL; + rc = -EINVAL; + goto out; } if (srvTcp) { @@ -1906,22 +1914,14 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, "Aborting operation")); if (csocket != NULL) sock_release(csocket); - kfree(volume_info.UNC); - kfree(volume_info.password); - kfree(volume_info.prepath); - FreeXid(xid); - return rc; + goto out; } srvTcp = kzalloc(sizeof(struct TCP_Server_Info), GFP_KERNEL); if (!srvTcp) { rc = -ENOMEM; sock_release(csocket); - kfree(volume_info.UNC); - kfree(volume_info.password); - kfree(volume_info.prepath); - FreeXid(xid); - return rc; + goto out; } else { memcpy(&srvTcp->addr.sockAddr, &sin_server, sizeof(struct sockaddr_in)); @@ -1929,6 +1929,12 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, /* BB Add code for ipv6 case too */ srvTcp->ssocket = csocket; srvTcp->protocolType = IPV4; + srvTcp->hostname = extract_hostname(volume_info.UNC); + if (IS_ERR(srvTcp->hostname)) { + rc = PTR_ERR(srvTcp->hostname); + sock_release(csocket); + goto out; + } init_waitqueue_head(&srvTcp->response_q); init_waitqueue_head(&srvTcp->request_q); INIT_LIST_HEAD(&srvTcp->pending_mid_q); @@ -1938,16 +1944,13 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, srvTcp->tcpStatus = CifsNew; init_MUTEX(&srvTcp->tcpSem); srvTcp->tsk = kthread_run((void *)(void *)cifs_demultiplex_thread, srvTcp, "cifsd"); - if ( IS_ERR(srvTcp->tsk) ) { + if (IS_ERR(srvTcp->tsk)) { rc = PTR_ERR(srvTcp->tsk); cERROR(1, ("error %d create cifsd thread", rc)); srvTcp->tsk = NULL; sock_release(csocket); - kfree(volume_info.UNC); - kfree(volume_info.password); - kfree(volume_info.prepath); - FreeXid(xid); - return rc; + kfree(srvTcp->hostname); + goto out; } wait_for_completion(&cifsd_complete); rc = 0; @@ -1962,8 +1965,6 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, if (existingCifsSes) { pSesInfo = existingCifsSes; cFYI(1, ("Existing smb sess found")); - kfree(volume_info.password); - /* volume_info.UNC freed at end of function */ } else if (!rc) { cFYI(1, ("Existing smb sess not found")); pSesInfo = sesInfoAlloc(); @@ -1977,8 +1978,11 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, if (!rc) { /* volume_info.password freed at unmount */ - if (volume_info.password) + if (volume_info.password) { pSesInfo->password = volume_info.password; + /* set to NULL to prevent freeing on exit */ + volume_info.password = NULL; + } if (volume_info.username) strncpy(pSesInfo->userName, volume_info.username, @@ -2000,8 +2004,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, up(&pSesInfo->sesSem); if (!rc) atomic_inc(&srvTcp->socketUseCount); - } else - kfree(volume_info.password); + } } /* search for existing tcon to this server share */ @@ -2106,9 +2109,8 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, "", cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); - kfree(volume_info.UNC); - FreeXid(xid); - return -ENODEV; + rc = -ENODEV; + goto out; } else { /* BB Do we need to wrap sesSem around * this TCon call and Unix SetFS as @@ -2231,6 +2233,12 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, (in which case it is not needed anymore) but when new sesion is created the password ptr is put in the new session structure (in which case the password will be freed at unmount time) */ +out: + /* zero out password before freeing */ + if (volume_info.password != NULL) { + memset(volume_info.password, 0, strlen(volume_info.password)); + kfree(volume_info.password); + } kfree(volume_info.UNC); kfree(volume_info.prepath); FreeXid(xid); @@ -2374,7 +2382,7 @@ CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses, pSMB->req_no_secext.ByteCount = cpu_to_le16(count); rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response, - &bytes_returned, 1); + &bytes_returned, CIFS_LONG_OP); if (rc) { /* rc = map_smb_to_linux_error(smb_buffer_response); now done in SendReceive */ } else if ((smb_buffer_response->WordCount == 3) @@ -2678,7 +2686,7 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid, pSMB->req.ByteCount = cpu_to_le16(count); rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response, - &bytes_returned, 1); + &bytes_returned, CIFS_LONG_OP); if (smb_buffer_response->Status.CifsError == cpu_to_le32(NT_STATUS_MORE_PROCESSING_REQUIRED)) @@ -3105,7 +3113,7 @@ CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses, pSMB->req.ByteCount = cpu_to_le16(count); rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response, - &bytes_returned, 1); + &bytes_returned, CIFS_LONG_OP); if (rc) { /* rc = map_smb_to_linux_error(smb_buffer_response) done in SendReceive now */ } else if ((smb_buffer_response->WordCount == 3) || @@ -3381,7 +3389,8 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, pSMB->hdr.smb_buf_length += count; pSMB->ByteCount = cpu_to_le16(count); - rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response, &length, 0); + rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response, &length, + CIFS_STD_OP); /* if (rc) rc = map_smb_to_linux_error(smb_buffer_response); */ /* above now done in SendReceive */ diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 68ad4ca..8025641 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -835,9 +835,9 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data, xid = GetXid(); if (*poffset > file->f_path.dentry->d_inode->i_size) - long_op = 2; /* writes past end of file can take a long time */ + long_op = CIFS_VLONG_OP; /* writes past EOF take long time */ else - long_op = 1; + long_op = CIFS_LONG_OP; for (total_written = 0; write_size > total_written; total_written += bytes_written) { @@ -884,7 +884,7 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data, } } else *poffset += bytes_written; - long_op = FALSE; /* subsequent writes fast - + long_op = CIFS_STD_OP; /* subsequent writes fast - 15 seconds is plenty */ } @@ -934,9 +934,9 @@ static ssize_t cifs_write(struct file *file, const char *write_data, xid = GetXid(); if (*poffset > file->f_path.dentry->d_inode->i_size) - long_op = 2; /* writes past end of file can take a long time */ + long_op = CIFS_VLONG_OP; /* writes past EOF can be slow */ else - long_op = 1; + long_op = CIFS_LONG_OP; for (total_written = 0; write_size > total_written; total_written += bytes_written) { @@ -1002,7 +1002,7 @@ static ssize_t cifs_write(struct file *file, const char *write_data, } } else *poffset += bytes_written; - long_op = FALSE; /* subsequent writes fast - + long_op = CIFS_STD_OP; /* subsequent writes fast - 15 seconds is plenty */ } @@ -1087,11 +1087,11 @@ refind_writable: read_unlock(&GlobalSMBSeslock); return open_file; } - + read_unlock(&GlobalSMBSeslock); /* Had to unlock since following call can block */ rc = cifs_reopen_file(open_file->pfile, FALSE); - if (!rc) { + if (!rc) { if (!open_file->closePend) return open_file; else { /* start over in case this was deleted */ @@ -1114,7 +1114,7 @@ refind_writable: /* can not use this handle, no write pending on this one after all */ atomic_dec(&open_file->wrtPending); - + if (open_file->closePend) /* list could have changed */ goto refind_writable; /* else we simply continue to the next entry. Thus @@ -1360,7 +1360,7 @@ retry: open_file->netfid, bytes_to_write, offset, &bytes_written, iov, n_iov, - 1); + CIFS_LONG_OP); atomic_dec(&open_file->wrtPending); if (rc || bytes_written < bytes_to_write) { cERROR(1, ("Write2 ret %d, wrote %d", diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index 899dc60..d0cb469 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -29,6 +29,7 @@ #include "ntlmssp.h" #include "nterr.h" #include +#include "cifs_spnego.h" extern void SMBNTencrypt(unsigned char *passwd, unsigned char *c8, unsigned char *p24); @@ -340,11 +341,12 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, SESSION_SETUP_ANDX *pSMB; __u32 capabilities; int count; - int resp_buf_type = 0; - struct kvec iov[2]; + int resp_buf_type; + struct kvec iov[3]; enum securityEnum type; __u16 action; int bytes_remaining; + struct key *spnego_key = NULL; if (ses == NULL) return -EINVAL; @@ -377,24 +379,32 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, capabilities = cifs_ssetup_hdr(ses, pSMB); - /* we will send the SMB in two pieces, - a fixed length beginning part, and a - second part which will include the strings - and rest of bcc area, in order to avoid having - to do a large buffer 17K allocation */ + /* we will send the SMB in three pieces: + a fixed length beginning part, an optional + SPNEGO blob (which can be zero length), and a + last part which will include the strings + and rest of bcc area. This allows us to avoid + a large buffer 17K allocation */ iov[0].iov_base = (char *)pSMB; iov[0].iov_len = smb_buf->smb_buf_length + 4; + /* setting this here allows the code at the end of the function + to free the request buffer if there's an error */ + resp_buf_type = CIFS_SMALL_BUFFER; + /* 2000 big enough to fit max user, domain, NOS name etc. */ str_area = kmalloc(2000, GFP_KERNEL); if (str_area == NULL) { - cifs_small_buf_release(smb_buf); - return -ENOMEM; + rc = -ENOMEM; + goto ssetup_exit; } bcc_ptr = str_area; ses->flags &= ~CIFS_SES_LANMAN; + iov[1].iov_base = NULL; + iov[1].iov_len = 0; + if (type == LANMAN) { #ifdef CONFIG_CIFS_WEAK_PW_HASH char lnm_session_key[CIFS_SESS_KEY_SIZE]; @@ -463,8 +473,8 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, struct ntlmv2_resp */ if (v2_sess_key == NULL) { - cifs_small_buf_release(smb_buf); - return -ENOMEM; + rc = -ENOMEM; + goto ssetup_exit; } pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities); @@ -499,22 +509,67 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, unicode_ssetup_strings(&bcc_ptr, ses, nls_cp); } else ascii_ssetup_strings(&bcc_ptr, ses, nls_cp); - } else /* NTLMSSP or SPNEGO */ { + } else if (type == Kerberos) { +#ifdef CONFIG_CIFS_UPCALL + struct cifs_spnego_msg *msg; + spnego_key = cifs_get_spnego_key(ses); + if (IS_ERR(spnego_key)) { + rc = PTR_ERR(spnego_key); + spnego_key = NULL; + goto ssetup_exit; + } + + msg = spnego_key->payload.data; + /* bail out if key is too long */ + if (msg->sesskey_len > + sizeof(ses->server->mac_signing_key.data.krb5)) { + cERROR(1, ("Kerberos signing key too long (%u bytes)", + msg->sesskey_len)); + rc = -EOVERFLOW; + goto ssetup_exit; + } + ses->server->mac_signing_key.len = msg->sesskey_len; + memcpy(ses->server->mac_signing_key.data.krb5, msg->data, + msg->sesskey_len); pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC; capabilities |= CAP_EXTENDED_SECURITY; pSMB->req.Capabilities = cpu_to_le32(capabilities); - /* BB set password lengths */ + iov[1].iov_base = msg->data + msg->sesskey_len; + iov[1].iov_len = msg->secblob_len; + pSMB->req.SecurityBlobLength = cpu_to_le16(iov[1].iov_len); + + if (ses->capabilities & CAP_UNICODE) { + /* unicode strings must be word aligned */ + if (iov[0].iov_len % 2) { + *bcc_ptr = 0; + bcc_ptr++; + } + unicode_oslm_strings(&bcc_ptr, nls_cp); + unicode_domain_string(&bcc_ptr, ses, nls_cp); + } else + /* BB: is this right? */ + ascii_ssetup_strings(&bcc_ptr, ses, nls_cp); +#else /* ! CONFIG_CIFS_UPCALL */ + cERROR(1, ("Kerberos negotiated but upcall support disabled!")); + rc = -ENOSYS; + goto ssetup_exit; +#endif /* CONFIG_CIFS_UPCALL */ + } else { + cERROR(1, ("secType %d not supported!", type)); + rc = -ENOSYS; + goto ssetup_exit; } - count = (long) bcc_ptr - (long) str_area; + iov[2].iov_base = str_area; + iov[2].iov_len = (long) bcc_ptr - (long) str_area; + + count = iov[1].iov_len + iov[2].iov_len; smb_buf->smb_buf_length += count; BCC_LE(smb_buf) = cpu_to_le16(count); - iov[1].iov_base = str_area; - iov[1].iov_len = count; - rc = SendReceive2(xid, ses, iov, 2 /* num_iovecs */, &resp_buf_type, - 0 /* not long op */, 1 /* log NT STATUS if any */ ); + rc = SendReceive2(xid, ses, iov, 3 /* num_iovecs */, &resp_buf_type, + CIFS_STD_OP /* not long */ | CIFS_LOG_ERROR); /* SMB request buf freed in SendReceive2 */ cFYI(1, ("ssetup rc from sendrecv2 is %d", rc)); @@ -560,6 +615,8 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, ses, nls_cp); ssetup_exit: + if (spnego_key) + key_put(spnego_key); kfree(str_area); if (resp_buf_type == CIFS_SMALL_BUFFER) { cFYI(1, ("ssetup freeing small buf %p", iov[0].iov_base)); diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index 7ed32b3..50b623a 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -308,7 +308,7 @@ smb_send2(struct socket *ssocket, struct kvec *iov, int n_vec, static int wait_for_free_request(struct cifsSesInfo *ses, const int long_op) { - if (long_op == -1) { + if (long_op == CIFS_ASYNC_OP) { /* oplock breaks must not be held up */ atomic_inc(&ses->server->inFlight); } else { @@ -337,7 +337,7 @@ static int wait_for_free_request(struct cifsSesInfo *ses, const int long_op) as they are allowed to block on server */ /* update # of requests on the wire to server */ - if (long_op < 3) + if (long_op != CIFS_BLOCKING_OP) atomic_inc(&ses->server->inFlight); spin_unlock(&GlobalMid_Lock); break; @@ -415,17 +415,48 @@ static int wait_for_response(struct cifsSesInfo *ses, } } + +/* + * + * Send an SMB Request. No response info (other than return code) + * needs to be parsed. + * + * flags indicate the type of request buffer and how long to wait + * and whether to log NT STATUS code (error) before mapping it to POSIX error + * + */ +int +SendReceiveNoRsp(const unsigned int xid, struct cifsSesInfo *ses, + struct smb_hdr *in_buf, int flags) +{ + int rc; + struct kvec iov[1]; + int resp_buf_type; + + iov[0].iov_base = (char *)in_buf; + iov[0].iov_len = in_buf->smb_buf_length + 4; + flags |= CIFS_NO_RESP; + rc = SendReceive2(xid, ses, iov, 1, &resp_buf_type, flags); +#ifdef CONFIG_CIFS_DEBUG2 + cFYI(1, ("SendRcvNoR flags %d rc %d", flags, rc)); +#endif + return rc; +} + int SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, struct kvec *iov, int n_vec, int *pRespBufType /* ret */, - const int long_op, const int logError) + const int flags) { int rc = 0; + int long_op; unsigned int receive_len; unsigned long timeout; struct mid_q_entry *midQ; struct smb_hdr *in_buf = iov[0].iov_base; + long_op = flags & CIFS_TIMEOUT_MASK; + *pRespBufType = CIFS_NO_BUFFER; /* no response buf yet */ if ((ses == NULL) || (ses->server == NULL)) { @@ -483,15 +514,22 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, if (rc < 0) goto out; - if (long_op == -1) - goto out; - else if (long_op == 2) /* writes past end of file can take loong time */ + if (long_op == CIFS_STD_OP) + timeout = 15 * HZ; + else if (long_op == CIFS_VLONG_OP) /* e.g. slow writes past EOF */ timeout = 180 * HZ; - else if (long_op == 1) + else if (long_op == CIFS_LONG_OP) timeout = 45 * HZ; /* should be greater than servers oplock break timeout (about 43 seconds) */ - else - timeout = 15 * HZ; + else if (long_op == CIFS_ASYNC_OP) + goto out; + else if (long_op == CIFS_BLOCKING_OP) + timeout = 0x7FFFFFFF; /* large, but not so large as to wrap */ + else { + cERROR(1, ("unknown timeout flag %d", long_op)); + rc = -EIO; + goto out; + } /* wait for 15 seconds or until woken up due to response arriving or due to last connection to this server being unmounted */ @@ -566,7 +604,8 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, } /* BB special case reconnect tid and uid here? */ - rc = map_smb_to_linux_error(midQ->resp_buf, logError); + rc = map_smb_to_linux_error(midQ->resp_buf, + flags & CIFS_LOG_ERROR); /* convert ByteCount if necessary */ if (receive_len >= sizeof(struct smb_hdr) - 4 @@ -574,8 +613,10 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, (2 * midQ->resp_buf->WordCount) + 2 /* bcc */ ) BCC(midQ->resp_buf) = le16_to_cpu(BCC_LE(midQ->resp_buf)); - midQ->resp_buf = NULL; /* mark it so will not be freed - by DeleteMidQEntry */ + if ((flags & CIFS_NO_RESP) == 0) + midQ->resp_buf = NULL; /* mark it so buf will + not be freed by + DeleteMidQEntry */ } else { rc = -EIO; cFYI(1, ("Bad MID state?")); @@ -663,17 +704,25 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses, if (rc < 0) goto out; - if (long_op == -1) + if (long_op == CIFS_STD_OP) + timeout = 15 * HZ; + /* wait for 15 seconds or until woken up due to response arriving or + due to last connection to this server being unmounted */ + else if (long_op == CIFS_ASYNC_OP) goto out; - else if (long_op == 2) /* writes past end of file can take loong time */ + else if (long_op == CIFS_VLONG_OP) /* writes past EOF can be slow */ timeout = 180 * HZ; - else if (long_op == 1) + else if (long_op == CIFS_LONG_OP) timeout = 45 * HZ; /* should be greater than servers oplock break timeout (about 43 seconds) */ - else - timeout = 15 * HZ; - /* wait for 15 seconds or until woken up due to response arriving or - due to last connection to this server being unmounted */ + else if (long_op == CIFS_BLOCKING_OP) + timeout = 0x7FFFFFFF; /* large but no so large as to wrap */ + else { + cERROR(1, ("unknown timeout flag %d", long_op)); + rc = -EIO; + goto out; + } + if (signal_pending(current)) { /* if signal pending do not hold up user for full smb timeout but we still give response a chance to complete */ @@ -812,7 +861,7 @@ send_lock_cancel(const unsigned int xid, struct cifsTconInfo *tcon, pSMB->hdr.Mid = GetNextMid(ses->server); return SendReceive(xid, ses, in_buf, out_buf, - &bytes_returned, 0); + &bytes_returned, CIFS_STD_OP); } int @@ -844,7 +893,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon, to the same server. We may make this configurable later or use ses->maxReq */ - rc = wait_for_free_request(ses, 3); + rc = wait_for_free_request(ses, CIFS_BLOCKING_OP); if (rc) return rc;