diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-02-20 18:38:47 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-02-20 18:38:47 -0800 |
commit | 2bfe01eff4307409b95859e860261d0907149b61 (patch) | |
tree | 51fd41be8e8df6057c3dde1eafdaa476b9200bea /fs | |
parent | cab7076a185e1e27f6879325e4da762424c3f1c9 (diff) | |
parent | ae6f8dd4d0c87bfb72da9d9b56342adf53e69c31 (diff) |
Merge branch 'for-next' of git://git.samba.org/sfrench/cifs-2.6
Pull CIFS/SMB3 updates from Steve French:
"Includes support for a critical SMB3 security feature: per-share
encryption from Pavel, and a cleanup from Jean Delvare.
Will have another cifs/smb3 merge next week"
* 'for-next' of git://git.samba.org/sfrench/cifs-2.6:
CIFS: Allow to switch on encryption with seal mount option
CIFS: Add capability to decrypt big read responses
CIFS: Decrypt and process small encrypted packets
CIFS: Add copy into pages callback for a read operation
CIFS: Add mid handle callback
CIFS: Add transform header handling callbacks
CIFS: Encrypt SMB3 requests before sending
CIFS: Enable encryption during session setup phase
CIFS: Add capability to transform requests before sending
CIFS: Separate RFC1001 length processing for SMB2 read
CIFS: Separate SMB2 sync header processing
CIFS: Send RFC1001 length in a separate iov
CIFS: Make send_cancel take rqst as argument
CIFS: Make SendReceive2() takes resp iov
CIFS: Separate SMB2 header structure
CIFS: Fix splice read for non-cached files
cifs: Add soft dependencies
cifs: Only select the required crypto modules
cifs: Simplify SMB2 and SMB311 dependencies
Diffstat (limited to 'fs')
-rw-r--r-- | fs/cifs/Kconfig | 12 | ||||
-rw-r--r-- | fs/cifs/cifsencrypt.c | 51 | ||||
-rw-r--r-- | fs/cifs/cifsfs.c | 14 | ||||
-rw-r--r-- | fs/cifs/cifsglob.h | 28 | ||||
-rw-r--r-- | fs/cifs/cifsproto.h | 13 | ||||
-rw-r--r-- | fs/cifs/cifssmb.c | 135 | ||||
-rw-r--r-- | fs/cifs/connect.c | 71 | ||||
-rw-r--r-- | fs/cifs/file.c | 62 | ||||
-rw-r--r-- | fs/cifs/sess.c | 27 | ||||
-rw-r--r-- | fs/cifs/smb1ops.c | 4 | ||||
-rw-r--r-- | fs/cifs/smb2glob.h | 5 | ||||
-rw-r--r-- | fs/cifs/smb2maperror.c | 5 | ||||
-rw-r--r-- | fs/cifs/smb2misc.c | 61 | ||||
-rw-r--r-- | fs/cifs/smb2ops.c | 663 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.c | 575 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.h | 27 | ||||
-rw-r--r-- | fs/cifs/smb2proto.h | 5 | ||||
-rw-r--r-- | fs/cifs/smb2transport.c | 132 | ||||
-rw-r--r-- | fs/cifs/transport.c | 171 |
19 files changed, 1582 insertions, 479 deletions
diff --git a/fs/cifs/Kconfig b/fs/cifs/Kconfig index e7b478b49985..034f00f21390 100644 --- a/fs/cifs/Kconfig +++ b/fs/cifs/Kconfig @@ -9,8 +9,6 @@ config CIFS select CRYPTO_ARC4 select CRYPTO_ECB select CRYPTO_DES - select CRYPTO_SHA256 - select CRYPTO_CMAC help This is the client VFS module for the Common Internet File System (CIFS) protocol which is the successor to the Server Message Block @@ -169,11 +167,15 @@ config CIFS_NFSD_EXPORT config CIFS_SMB2 bool "SMB2 and SMB3 network file system support" - depends on CIFS && INET - select NLS + depends on CIFS select KEYS select FSCACHE select DNS_RESOLVER + select CRYPTO_AES + select CRYPTO_SHA256 + select CRYPTO_CMAC + select CRYPTO_AEAD2 + select CRYPTO_CCM help This enables support for the Server Message Block version 2 @@ -194,7 +196,7 @@ config CIFS_SMB2 config CIFS_SMB311 bool "SMB3.1.1 network file system support (Experimental)" - depends on CIFS_SMB2 && INET + depends on CIFS_SMB2 help This enables experimental support for the newest, SMB3.1.1, dialect. diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c index 66bd7fa9b7a6..058ac9b36f04 100644 --- a/fs/cifs/cifsencrypt.c +++ b/fs/cifs/cifsencrypt.c @@ -34,6 +34,7 @@ #include <linux/random.h> #include <linux/highmem.h> #include <crypto/skcipher.h> +#include <crypto/aead.h> static int cifs_crypto_shash_md5_allocate(struct TCP_Server_Info *server) @@ -75,24 +76,20 @@ int __cifs_calc_signature(struct smb_rqst *rqst, struct kvec *iov = rqst->rq_iov; int n_vec = rqst->rq_nvec; - for (i = 0; i < n_vec; i++) { + if (n_vec < 2 || iov[0].iov_len != 4) + return -EIO; + + for (i = 1; i < n_vec; i++) { if (iov[i].iov_len == 0) continue; if (iov[i].iov_base == NULL) { cifs_dbg(VFS, "null iovec entry\n"); return -EIO; } - /* The first entry includes a length field (which does not get - signed that occupies the first 4 bytes before the header */ - if (i == 0) { - if (iov[0].iov_len <= 8) /* cmd field at offset 9 */ - break; /* nothing to sign or corrupt header */ - rc = crypto_shash_update(shash, - iov[i].iov_base + 4, iov[i].iov_len - 4); - } else { - rc = crypto_shash_update(shash, - iov[i].iov_base, iov[i].iov_len); - } + if (i == 1 && iov[1].iov_len <= 4) + break; /* nothing to sign or corrupt header */ + rc = crypto_shash_update(shash, + iov[i].iov_base, iov[i].iov_len); if (rc) { cifs_dbg(VFS, "%s: Could not update with payload\n", __func__); @@ -168,6 +165,10 @@ int cifs_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server, char smb_signature[20]; struct smb_hdr *cifs_pdu = (struct smb_hdr *)rqst->rq_iov[0].iov_base; + if (rqst->rq_iov[0].iov_len != 4 || + rqst->rq_iov[0].iov_base + 4 != rqst->rq_iov[1].iov_base) + return -EIO; + if ((cifs_pdu == NULL) || (server == NULL)) return -EINVAL; @@ -209,12 +210,14 @@ int cifs_sign_smbv(struct kvec *iov, int n_vec, struct TCP_Server_Info *server, int cifs_sign_smb(struct smb_hdr *cifs_pdu, struct TCP_Server_Info *server, __u32 *pexpected_response_sequence_number) { - struct kvec iov; + struct kvec iov[2]; - iov.iov_base = cifs_pdu; - iov.iov_len = be32_to_cpu(cifs_pdu->smb_buf_length) + 4; + iov[0].iov_base = cifs_pdu; + iov[0].iov_len = 4; + iov[1].iov_base = (char *)cifs_pdu + 4; + iov[1].iov_len = be32_to_cpu(cifs_pdu->smb_buf_length); - return cifs_sign_smbv(&iov, 1, server, + return cifs_sign_smbv(iov, 2, server, pexpected_response_sequence_number); } @@ -227,6 +230,10 @@ int cifs_verify_signature(struct smb_rqst *rqst, char what_we_think_sig_should_be[20]; struct smb_hdr *cifs_pdu = (struct smb_hdr *)rqst->rq_iov[0].iov_base; + if (rqst->rq_iov[0].iov_len != 4 || + rqst->rq_iov[0].iov_base + 4 != rqst->rq_iov[1].iov_base) + return -EIO; + if (cifs_pdu == NULL || server == NULL) return -EINVAL; @@ -868,7 +875,7 @@ out: } void -cifs_crypto_shash_release(struct TCP_Server_Info *server) +cifs_crypto_secmech_release(struct TCP_Server_Info *server) { if (server->secmech.cmacaes) { crypto_free_shash(server->secmech.cmacaes); @@ -890,6 +897,16 @@ cifs_crypto_shash_release(struct TCP_Server_Info *server) server->secmech.hmacmd5 = NULL; } + if (server->secmech.ccmaesencrypt) { + crypto_free_aead(server->secmech.ccmaesencrypt); + server->secmech.ccmaesencrypt = NULL; + } + + if (server->secmech.ccmaesdecrypt) { + crypto_free_aead(server->secmech.ccmaesdecrypt); + server->secmech.ccmaesdecrypt = NULL; + } + kfree(server->secmech.sdesccmacaes); server->secmech.sdesccmacaes = NULL; kfree(server->secmech.sdeschmacsha256); diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 70f4e65fced2..15e1db8738ae 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -1365,5 +1365,19 @@ MODULE_DESCRIPTION ("VFS to access servers complying with the SNIA CIFS Specification " "e.g. Samba and Windows"); MODULE_VERSION(CIFS_VERSION); +MODULE_SOFTDEP("pre: arc4"); +MODULE_SOFTDEP("pre: des"); +MODULE_SOFTDEP("pre: ecb"); +MODULE_SOFTDEP("pre: hmac"); +MODULE_SOFTDEP("pre: md4"); +MODULE_SOFTDEP("pre: md5"); +MODULE_SOFTDEP("pre: nls"); +#ifdef CONFIG_CIFS_SMB2 +MODULE_SOFTDEP("pre: aes"); +MODULE_SOFTDEP("pre: cmac"); +MODULE_SOFTDEP("pre: sha256"); +MODULE_SOFTDEP("pre: aead2"); +MODULE_SOFTDEP("pre: ccm"); +#endif /* CONFIG_CIFS_SMB2 */ module_init(init_cifs) module_exit(exit_cifs) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 7ea8a3393936..1a90bb3e2986 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -136,6 +136,8 @@ struct cifs_secmech { struct sdesc *sdescmd5; /* ctxt to generate cifs/smb signature */ struct sdesc *sdeschmacsha256; /* ctxt to generate smb2 signature */ struct sdesc *sdesccmacaes; /* ctxt to generate smb3 signature */ + struct crypto_aead *ccmaesencrypt; /* smb3 encryption aead */ + struct crypto_aead *ccmaesdecrypt; /* smb3 decryption aead */ }; /* per smb session structure/fields */ @@ -208,7 +210,7 @@ struct cifsInodeInfo; struct cifs_open_parms; struct smb_version_operations { - int (*send_cancel)(struct TCP_Server_Info *, void *, + int (*send_cancel)(struct TCP_Server_Info *, struct smb_rqst *, struct mid_q_entry *); bool (*compare_fids)(struct cifsFileInfo *, struct cifsFileInfo *); /* setup request: allocate mid, sign message */ @@ -433,6 +435,14 @@ struct smb_version_operations { bool (*dir_needs_close)(struct cifsFileInfo *); long (*fallocate)(struct file *, struct cifs_tcon *, int, loff_t, loff_t); + /* init transform request - used for encryption for now */ + int (*init_transform_rq)(struct TCP_Server_Info *, struct smb_rqst *, + struct smb_rqst *); + /* free transform request */ + void (*free_transform_rq)(struct smb_rqst *); + int (*is_transform_hdr)(void *buf); + int (*receive_transform)(struct TCP_Server_Info *, + struct mid_q_entry **); }; struct smb_version_values { @@ -1119,7 +1129,10 @@ struct cifs_readdata { int (*read_into_pages)(struct TCP_Server_Info *server, struct cifs_readdata *rdata, unsigned int len); - struct kvec iov; + int (*copy_into_pages)(struct TCP_Server_Info *server, + struct cifs_readdata *rdata, + struct iov_iter *iter); + struct kvec iov[2]; unsigned int pagesz; unsigned int tailsz; unsigned int credits; @@ -1302,6 +1315,13 @@ typedef int (mid_receive_t)(struct TCP_Server_Info *server, */ typedef void (mid_callback_t)(struct mid_q_entry *mid); +/* + * This is the protopyte for mid handle function. This is called once the mid + * has been recognized after decryption of the message. + */ +typedef int (mid_handle_t)(struct TCP_Server_Info *server, + struct mid_q_entry *mid); + /* one of these for every pending CIFS request to the server */ struct mid_q_entry { struct list_head qhead; /* mids waiting on reply from this server */ @@ -1316,6 +1336,7 @@ struct mid_q_entry { #endif mid_receive_t *receive; /* call receive callback */ mid_callback_t *callback; /* call completion callback */ + mid_handle_t *handle; /* call handle mid callback */ void *callback_data; /* general purpose pointer for callback */ void *resp_buf; /* pointer to received SMB header */ int mid_state; /* wish this were enum but can not pass to wait_event */ @@ -1323,6 +1344,7 @@ struct mid_q_entry { bool large_buf:1; /* if valid response, is pointer to large buf */ bool multiRsp:1; /* multiple trans2 responses for one request */ bool multiEnd:1; /* both received */ + bool decrypted:1; /* decrypted entry */ }; /* Make code in transport.c a little cleaner by moving @@ -1475,7 +1497,9 @@ static inline void free_dfs_info_array(struct dfs_info3_param *param, #define CIFS_OBREAK_OP 0x0100 /* oplock break request */ #define CIFS_NEG_OP 0x0200 /* negotiate request */ #define CIFS_OP_MASK 0x0380 /* mask request type */ + #define CIFS_HAS_CREDITS 0x0400 /* already has credits */ +#define CIFS_TRANSFORM_REQ 0x0800 /* transform request before sending */ /* Security Flags: indicate type of session setup needed */ #define CIFSSEC_MAY_SIGN 0x00001 diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index c7b3c841e660..406d2c10ba78 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -75,10 +75,16 @@ extern struct mid_q_entry *AllocMidQEntry(const struct smb_hdr *smb_buffer, extern void DeleteMidQEntry(struct mid_q_entry *midEntry); extern void cifs_delete_mid(struct mid_q_entry *mid); extern void cifs_wake_up_task(struct mid_q_entry *mid); +extern int cifs_handle_standard(struct TCP_Server_Info *server, + struct mid_q_entry *mid); +extern int cifs_discard_remaining_data(struct TCP_Server_Info *server); extern int cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst, mid_receive_t *receive, mid_callback_t *callback, - void *cbdata, const int flags); + mid_handle_t *handle, void *cbdata, const int flags); +extern int cifs_send_recv(const unsigned int xid, struct cifs_ses *ses, + struct smb_rqst *rqst, int *resp_buf_type, + const int flags, struct kvec *resp_iov); extern int SendReceive(const unsigned int /* xid */ , struct cifs_ses *, struct smb_hdr * /* input */ , struct smb_hdr * /* out */ , @@ -96,7 +102,8 @@ extern int cifs_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int *credits); extern int SendReceive2(const unsigned int /* xid */ , struct cifs_ses *, struct kvec *, int /* nvec to send */, - int * /* type of buf returned */ , const int flags); + int * /* type of buf returned */, const int flags, + struct kvec * /* resp vec */); extern int SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *ptcon, struct smb_hdr *in_buf , @@ -441,7 +448,7 @@ extern int SMBNTencrypt(unsigned char *, unsigned char *, unsigned char *, const struct nls_table *); extern int setup_ntlm_response(struct cifs_ses *, const struct nls_table *); extern int setup_ntlmv2_rsp(struct cifs_ses *, const struct nls_table *); -extern void cifs_crypto_shash_release(struct TCP_Server_Info *); +extern void cifs_crypto_secmech_release(struct TCP_Server_Info *server); extern int calc_seckey(struct cifs_ses *); extern int generate_smb30signingkey(struct cifs_ses *); extern int generate_smb311signingkey(struct cifs_ses *); diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index b47261858e6d..f5099fb8a22f 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -673,6 +673,7 @@ CIFSSMBTDis(const unsigned int xid, struct cifs_tcon *tcon) return rc; rc = SendReceiveNoRsp(xid, tcon->ses, (char *)smb_buffer, 0); + cifs_small_buf_release(smb_buffer); if (rc) cifs_dbg(FYI, "Tree disconnect failed %d\n", rc); @@ -707,9 +708,9 @@ CIFSSMBEcho(struct TCP_Server_Info *server) { ECHO_REQ *smb; int rc = 0; - struct kvec iov; - struct smb_rqst rqst = { .rq_iov = &iov, - .rq_nvec = 1 }; + struct kvec iov[2]; + struct smb_rqst rqst = { .rq_iov = iov, + .rq_nvec = 2 }; cifs_dbg(FYI, "In echo request\n"); @@ -724,10 +725,13 @@ CIFSSMBEcho(struct TCP_Server_Info *server) put_bcc(1, &smb->hdr); smb->Data[0] = 'a'; inc_rfc1001_len(smb, 3); - iov.iov_base = smb; - iov.iov_len = be32_to_cpu(smb->hdr.smb_buf_length) + 4; - rc = cifs_call_async(server, &rqst, NULL, cifs_echo_callback, + iov[0].iov_len = 4; + iov[0].iov_base = smb; + iov[1].iov_len = get_rfc1002_length(smb); + iov[1].iov_base = (char *)smb + 4; + + rc = cifs_call_async(server, &rqst, NULL, cifs_echo_callback, NULL, server, CIFS_ASYNC_OP | CIFS_ECHO_OP); if (rc) cifs_dbg(FYI, "Echo request failed: %d\n", rc); @@ -772,6 +776,7 @@ CIFSSMBLogoff(const unsigned int xid, struct cifs_ses *ses) pSMB->AndXCommand = 0xFF; rc = SendReceiveNoRsp(xid, ses, (char *) pSMB, 0); + cifs_small_buf_release(pSMB); session_already_dead: mutex_unlock(&ses->session_mutex); @@ -1394,8 +1399,8 @@ openRetry: * Discard any remaining data in the current SMB. To do this, we borrow the * current bigbuf. */ -static int -discard_remaining_data(struct TCP_Server_Info *server) +int +cifs_discard_remaining_data(struct TCP_Server_Info *server) { unsigned int rfclen = get_rfc1002_length(server->smallbuf); int remaining = rfclen + 4 - server->total_read; @@ -1421,7 +1426,7 @@ cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid) int length; struct cifs_readdata *rdata = mid->callback_data; - length = discard_remaining_data(server); + length = cifs_discard_remaining_data(server); dequeue_mid(mid, rdata->result); return length; } @@ -1454,7 +1459,7 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) if (server->ops->is_status_pending && server->ops->is_status_pending(buf, server, 0)) { - discard_remaining_data(server); + cifs_discard_remaining_data(server); return -1; } @@ -1507,10 +1512,12 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) } /* set up first iov for signature check */ - rdata->iov.iov_base = buf; - rdata->iov.iov_len = server->total_read; - cifs_dbg(FYI, "0: iov_base=%p iov_len=%zu\n", - rdata->iov.iov_base, rdata->iov.iov_len); + rdata->iov[0].iov_base = buf; + rdata->iov[0].iov_len = 4; + rdata->iov[1].iov_base = buf + 4; + rdata->iov[1].iov_len = server->total_read - 4; + cifs_dbg(FYI, "0: iov_base=%p iov_len=%u\n", + rdata->iov[0].iov_base, server->total_read); /* how much data is in the response? */ data_len = server->ops->read_data_length(buf); @@ -1543,8 +1550,8 @@ cifs_readv_callback(struct mid_q_entry *mid) struct cifs_readdata *rdata = mid->callback_data; struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink); struct TCP_Server_Info *server = tcon->ses->server; - struct smb_rqst rqst = { .rq_iov = &rdata->iov, - .rq_nvec = 1, + struct smb_rqst rqst = { .rq_iov = rdata->iov, + .rq_nvec = 2, .rq_pages = rdata->pages, .rq_npages = rdata->nr_pages, .rq_pagesz = rdata->pagesz, @@ -1599,8 +1606,8 @@ cifs_async_readv(struct cifs_readdata *rdata) READ_REQ *smb = NULL; int wct; struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink); - struct smb_rqst rqst = { .rq_iov = &rdata->iov, - .rq_nvec = 1 }; + struct smb_rqst rqst = { .rq_iov = rdata->iov, + .rq_nvec = 2 }; cifs_dbg(FYI, "%s: offset=%llu bytes=%u\n", __func__, rdata->offset, rdata->bytes); @@ -1640,12 +1647,14 @@ cifs_async_readv(struct cifs_readdata *rdata) } /* 4 for RFC1001 length + 1 for BCC */ - rdata->iov.iov_base = smb; - rdata->iov.iov_len = be32_to_cpu(smb->hdr.smb_buf_length) + 4; + rdata->iov[0].iov_base = smb; + rdata->iov[0].iov_len = 4; + rdata->iov[1].iov_base = (char *)smb + 4; + rdata->iov[1].iov_len = get_rfc1002_length(smb); kref_get(&rdata->refcount); rc = cifs_call_async(tcon->ses->server, &rqst, cifs_readv_receive, - cifs_readv_callback, rdata, 0); + cifs_readv_callback, NULL, rdata, 0); if (rc == 0) cifs_stats_inc(&tcon->stats.cifs_stats.num_reads); @@ -1667,6 +1676,7 @@ CIFSSMBRead(const unsigned int xid, struct cifs_io_parms *io_parms, int wct; int resp_buf_type = 0; struct kvec iov[1]; + struct kvec rsp_iov; __u32 pid = io_parms->pid; __u16 netfid = io_parms->netfid; __u64 offset = io_parms->offset; @@ -1716,10 +1726,11 @@ CIFSSMBRead(const unsigned int xid, struct cifs_io_parms *io_parms, iov[0].iov_base = (char *)pSMB; iov[0].iov_len = be32_to_cpu(pSMB->hdr.smb_buf_length) + 4; - rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovecs */, - &resp_buf_type, CIFS_LOG_ERROR); + rc = SendReceive2(xid, tcon->ses, iov, 1, &resp_buf_type, + CIFS_LOG_ERROR, &rsp_iov); + cifs_small_buf_release(pSMB); cifs_stats_inc(&tcon->stats.cifs_stats.num_reads); - pSMBr = (READ_RSP *)iov[0].iov_base; + pSMBr = (READ_RSP *)rsp_iov.iov_base; if (rc) { cifs_dbg(VFS, "Send error in read = %d\n", rc); } else { @@ -1747,12 +1758,11 @@ CIFSSMBRead(const unsigned int xid, struct cifs_io_parms *io_parms, } } -/* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */ if (*buf) { - free_rsp_buf(resp_buf_type, iov[0].iov_base); + free_rsp_buf(resp_buf_type, rsp_iov.iov_base); } else if (resp_buf_type != CIFS_NO_BUFFER) { /* return buffer to caller to free */ - *buf = iov[0].iov_base; + *buf = rsp_iov.iov_base; if (resp_buf_type == CIFS_SMALL_BUFFER) *pbuf_type = CIFS_SMALL_BUFFER; else if (resp_buf_type == CIFS_LARGE_BUFFER) @@ -2093,7 +2103,7 @@ cifs_async_writev(struct cifs_writedata *wdata, WRITE_REQ *smb = NULL; int wct; struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink); - struct kvec iov; + struct kvec iov[2]; struct smb_rqst rqst = { }; if (tcon->ses->capabilities & CAP_LARGE_FILES) { @@ -2126,11 +2136,13 @@ cifs_async_writev(struct cifs_writedata *wdata, cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4); /* 4 for RFC1001 length + 1 for BCC */ - iov.iov_len = be32_to_cpu(smb->hdr.smb_buf_length) + 4 + 1; - iov.iov_base = smb; + iov[0].iov_len = 4; + iov[0].iov_base = smb; + iov[1].iov_len = get_rfc1002_length(smb) + 1; + iov[1].iov_base = (char *)smb + 4; - rqst.rq_iov = &iov; - rqst.rq_nvec = 1; + rqst.rq_iov = iov; + rqst.rq_nvec = 2; rqst.rq_pages = wdata->pages; rqst.rq_npages = wdata->nr_pages; rqst.rq_pagesz = wdata->pagesz; @@ -2151,12 +2163,12 @@ cifs_async_writev(struct cifs_writedata *wdata, (struct smb_com_writex_req *)smb; inc_rfc1001_len(&smbw->hdr, wdata->bytes + 5); put_bcc(wdata->bytes + 5, &smbw->hdr); - iov.iov_len += 4; /* pad bigger by four bytes */ + iov[1].iov_len += 4; /* pad bigger by four bytes */ } kref_get(&wdata->refcount); rc = cifs_call_async(tcon->ses->server, &rqst, NULL, - cifs_writev_callback, wdata, 0); + cifs_writev_callback, NULL, wdata, 0); if (rc == 0) cifs_stats_inc(&tcon->stats.cifs_stats.num_writes); @@ -2182,6 +2194,7 @@ CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms, __u64 offset = io_parms->offset; struct cifs_tcon *tcon = io_parms->tcon; unsigned int count = io_parms->length; + struct kvec rsp_iov; *nbytes = 0; @@ -2240,8 +2253,9 @@ CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms, else /* wct == 12 pad bigger by four bytes */ iov[0].iov_len = smb_hdr_len + 8; - - rc = SendReceive2(xid, tcon->ses, iov, n_vec + 1, &resp_buf_type, 0); + rc = SendReceive2(xid, tcon->ses, iov, n_vec + 1, &resp_buf_type, 0, + &rsp_iov); + cifs_small_buf_release(pSMB); cifs_stats_inc(&tcon->stats.cifs_stats.num_writes); if (rc) { cifs_dbg(FYI, "Send error Write2 = %d\n", rc); @@ -2249,7 +2263,7 @@ CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms, /* presumably this can not happen, but best to be safe */ rc = -EIO; } else { - WRITE_RSP *pSMBr = (WRITE_RSP *)iov[0].iov_base; + WRITE_RSP *pSMBr = (WRITE_RSP *)rsp_iov.iov_base; *nbytes = le16_to_cpu(pSMBr->CountHigh); *nbytes = (*nbytes) << 16; *nbytes += le16_to_cpu(pSMBr->Count); @@ -2263,8 +2277,7 @@ CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms, *nbytes &= 0xFFFF; } -/* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */ - free_rsp_buf(resp_buf_type, iov[0].iov_base); + free_rsp_buf(resp_buf_type, rsp_iov.iov_base); /* Note: On -EAGAIN error only caller can retry on handle based calls since file handle passed in no longer valid */ @@ -2279,6 +2292,7 @@ int cifs_lockv(const unsigned int xid, struct cifs_tcon *tcon, int rc = 0; LOCK_REQ *pSMB = NULL; struct kvec iov[2]; + struct kvec rsp_iov; int resp_buf_type; __u16 count; @@ -2307,7 +2321,9 @@ int cifs_lockv(const unsigned int xid, struct cifs_tcon *tcon, iov[1].iov_len = (num_unlock + num_lock) * sizeof(LOCKING_ANDX_RANGE); cifs_stats_inc(&tcon->stats.cifs_stats.num_locks); - rc = SendReceive2(xid, tcon->ses, iov, 2, &resp_buf_type, CIFS_NO_RESP); + rc = SendReceive2(xid, tcon->ses, iov, 2, &resp_buf_type, CIFS_NO_RESP, + &rsp_iov); + cifs_small_buf_release(pSMB); if (rc) cifs_dbg(FYI, "Send error in cifs_lockv = %d\n", rc); @@ -2368,14 +2384,12 @@ CIFSSMBLock(const unsigned int xid, struct cifs_tcon *tcon, inc_rfc1001_len(pSMB, count); pSMB->ByteCount = cpu_to_le16(count); - if (waitFlag) { + if (waitFlag) rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMB, &bytes_returned); - cifs_small_buf_release(pSMB); - } else { + else rc = SendReceiveNoRsp(xid, tcon->ses, (char *)pSMB, flags); - /* SMB buffer freed by function above */ - } + cifs_small_buf_release(pSMB); cifs_stats_inc(&tcon->stats.cifs_stats.num_locks); if (rc) cifs_dbg(FYI, "Send error in Lock = %d\n", rc); @@ -2401,6 +2415,7 @@ CIFSSMBPosixLock(const unsigned int xid, struct cifs_tcon *tcon, int resp_buf_type = 0; __u16 params, param_offset, offset, byte_count, count; struct kvec iov[1]; + struct kvec rsp_iov; cifs_dbg(FYI, "Posix Lock\n"); @@ -2462,11 +2477,10 @@ CIFSSMBPosixLock(const unsigned int xid, struct cifs_tcon *tcon, iov[0].iov_base = (char *)pSMB; iov[0].iov_len = be32_to_cpu(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; + &resp_buf_type, timeout, &rsp_iov); + pSMBr = (struct smb_com_transaction2_sfi_rsp *)rsp_iov.iov_base; } + cifs_small_buf_release(pSMB); if (rc) { cifs_dbg(FYI, "Send error in Posix Lock = %d\n", rc); @@ -2506,10 +2520,7 @@ CIFSSMBPosixLock(const unsigned int xid, struct cifs_tcon *tcon, } plk_err_exit: - if (pSMB) - cifs_small_buf_release(pSMB); - - free_rsp_buf(resp_buf_type, iov[0].iov_base); + free_rsp_buf(resp_buf_type, rsp_iov.iov_base); /* Note: On -EAGAIN error only caller can retry on handle based calls since file handle passed in no longer valid */ @@ -2536,6 +2547,7 @@ CIFSSMBClose(const unsigned int xid, struct cifs_tcon *tcon, int smb_file_id) pSMB->LastWriteTime = 0xFFFFFFFF; pSMB->ByteCount = 0; rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0); + cifs_small_buf_release(pSMB); cifs_stats_inc(&tcon->stats.cifs_stats.num_closes); if (rc) { if (rc != -EINTR) { @@ -2565,6 +2577,7 @@ CIFSSMBFlush(const unsigned int xid, struct cifs_tcon *tcon, int smb_file_id) pSMB->FileID = (__u16) smb_file_id; pSMB->ByteCount = 0; rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0); + cifs_small_buf_release(pSMB); cifs_stats_inc(&tcon->stats.cifs_stats.num_flushes); if (rc) cifs_dbg(VFS, "Send error in Flush = %d\n", rc); @@ -3820,6 +3833,7 @@ CIFSSMBGetCIFSACL(const unsigned int xid, struct cifs_tcon *tcon, __u16 fid, int buf_type = 0; QUERY_SEC_DESC_REQ *pSMB; struct kvec iov[1]; + struct kvec rsp_iov; cifs_dbg(FYI, "GetCifsACL\n"); @@ -3843,7 +3857,8 @@ CIFSSMBGetCIFSACL(const unsigned int xid, struct cifs_tcon *tcon, __u16 fid, iov[0].iov_len = be32_to_cpu(pSMB->hdr.smb_buf_length) + 4; rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovec */, &buf_type, - 0); + 0, &rsp_iov); + cifs_small_buf_release(pSMB); cifs_stats_inc(&tcon->stats.cifs_stats.num_acl_get); if (rc) { cifs_dbg(FYI, "Send error in QuerySecDesc = %d\n", rc); @@ -3855,11 +3870,11 @@ CIFSSMBGetCIFSACL(const unsigned int xid, struct cifs_tcon *tcon, __u16 fid, char *pdata; /* validate_nttransact */ - rc = validate_ntransact(iov[0].iov_base, (char **)&parm, + rc = validate_ntransact(rsp_iov.iov_base, (char **)&parm, &pdata, &parm_len, pbuflen); if (rc) goto qsec_out; - pSMBr = (struct smb_com_ntransact_rsp *)iov[0].iov_base; + pSMBr = (struct smb_com_ntransact_rsp *)rsp_iov.iov_base; cifs_dbg(FYI, "smb %p parm %p data %p\n", pSMBr, parm, *acl_inf); @@ -3896,8 +3911,7 @@ CIFSSMBGetCIFSACL(const unsigned int xid, struct cifs_tcon *tcon, __u16 fid, } } qsec_out: - free_rsp_buf(buf_type, iov[0].iov_base); -/* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */ + free_rsp_buf(buf_type, rsp_iov.iov_base); return rc; } @@ -4666,6 +4680,7 @@ CIFSFindClose(const unsigned int xid, struct cifs_tcon *tcon, pSMB->FileID = searchHandle; pSMB->ByteCount = 0; rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0); + cifs_small_buf_release(pSMB); if (rc) cifs_dbg(VFS, "Send error in FindClose = %d\n", rc); @@ -5687,6 +5702,7 @@ CIFSSMBSetFileSize(const unsigned int xid, struct cifs_tcon *tcon, inc_rfc1001_len(pSMB, byte_count); pSMB->ByteCount = cpu_to_le16(byte_count); rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0); + cifs_small_buf_release(pSMB); if (rc) { cifs_dbg(FYI, "Send error in SetFileInfo (SetFileSize) = %d\n", rc); @@ -5758,6 +5774,7 @@ CIFSSMBSetFileInfo(const unsigned int xid, struct cifs_tcon *tcon, pSMB->ByteCount = cpu_to_le16(byte_count); memcpy(data_offset, data, sizeof(FILE_BASIC_INFO)); rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0); + cifs_small_buf_release(pSMB); if (rc) cifs_dbg(FYI, "Send error in Set Time (SetFileInfo) = %d\n", rc); @@ -5818,6 +5835,7 @@ CIFSSMBSetFileDisposition(const unsigned int xid, struct cifs_tcon *tcon, pSMB->ByteCount = cpu_to_le16(byte_count); *data_offset = delete_file ? 1 : 0; rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0); + cifs_small_buf_release(pSMB); if (rc) cifs_dbg(FYI, "Send error in SetFileDisposition = %d\n", rc); @@ -6057,6 +6075,7 @@ CIFSSMBUnixSetFileInfo(const unsigned int xid, struct cifs_tcon *tcon, cifs_fill_unix_set_info((FILE_UNIX_BASIC_INFO *)data_offset, args); rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0); + cifs_small_buf_release(pSMB); if (rc) cifs_dbg(FYI, "Send error in Set Time (SetFileInfo) = %d\n", rc); diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 35ae49ed1f76..777ad9f4fc3c 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -787,6 +787,15 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid) dump_smb(buf, server->total_read); + return cifs_handle_standard(server, mid); +} + +int +cifs_handle_standard(struct TCP_Server_Info *server, struct mid_q_entry *mid) +{ + char *buf = server->large_buf ? server->bigbuf : server->smallbuf; + int length; + /* * We know that we received enough to get to the MID as we * checked the pdu_length earlier. Now check to see @@ -872,12 +881,19 @@ cifs_demultiplex_thread(void *p) continue; server->total_read += length; - mid_entry = server->ops->find_mid(server, buf); + if (server->ops->is_transform_hdr && + server->ops->receive_transform && + server->ops->is_transform_hdr(buf)) { + length = server->ops->receive_transform(server, + &mid_entry); + } else { + mid_entry = server->ops->find_mid(server, buf); - if (!mid_entry || !mid_entry->receive) - length = standard_receive3(server, mid_entry); - else - length = mid_entry->receive(server, mid_entry); + if (!mid_entry || !mid_entry->receive) + length = standard_receive3(server, mid_entry); + else + length = mid_entry->receive(server, mid_entry); + } if (length < 0) continue; @@ -2154,7 +2170,7 @@ cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect) server->tcpStatus = CifsExiting; spin_unlock(&GlobalMid_Lock); - cifs_crypto_shash_release(server); + cifs_crypto_secmech_release(server); cifs_fscache_release_client_cookie(server); kfree(server->session_key.response); @@ -2273,7 +2289,7 @@ cifs_get_tcp_session(struct smb_vol *volume_info) return tcp_ses; out_err_crypto_release: - cifs_crypto_shash_release(tcp_ses); + cifs_crypto_secmech_release(tcp_ses); put_net(cifs_net_ns(tcp_ses)); @@ -2614,12 +2630,18 @@ get_ses_fail: return ERR_PTR(rc); } -static int match_tcon(struct cifs_tcon *tcon, const char *unc) +static int match_tcon(struct cifs_tcon *tcon, struct smb_vol *volume_info) { if (tcon->tidStatus == CifsExiting) return 0; - if (strncmp(tcon->treeName, unc, MAX_TREE_SIZE)) + if (strncmp(tcon->treeName, volume_info->UNC, MAX_TREE_SIZE)) return 0; + if (tcon->seal != volume_info->seal) + return 0; +#ifdef CONFIG_CIFS_SMB2 + if (tcon->snapshot_time != volume_info->snapshot_time) + return 0; +#endif /* CONFIG_CIFS_SMB2 */ return 1; } @@ -2632,14 +2654,8 @@ cifs_find_tcon(struct cifs_ses *ses, struct smb_vol *volume_info) spin_lock(&cifs_tcp_ses_lock); list_for_each(tmp, &ses->tcon_list) { tcon = list_entry(tmp, struct cifs_tcon, tcon_list); - if (!match_tcon(tcon, volume_info->UNC)) - continue; - -#ifdef CONFIG_CIFS_SMB2 |