Home Home > GIT Browse
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichal Marek <mmarek@suse.cz>2017-03-03 09:48:44 +0100
committerMichal Marek <mmarek@suse.cz>2017-03-03 09:59:24 +0100
commit271c9dd46fc36d5defb7ead7691ac832249a5fff (patch)
tree2ff532ab67a218b22f89c2ed3ddf7ead55269a88
parente0238e74146c7be1b968dfb921203ce17bef8b06 (diff)
parent0f482f4894ff42a3929962dd2ad0ae368db38e8d (diff)
Merge branch 'users/dmulder/SLE12-SP3/for-next' into SLE12-SP3
Pull cifs encryption support from David Mulder (fate#322075). Refreshed: patches.suse/cifs-0001-Prepare-for-encryption-support-first-part-.-Add-decr.patch Deleted (already part of stable): patches.suse/cifs-0002-CIFS-Fix-missing-nls-unload-in-smb2_reconnect.patch patches.suse/cifs-0003-CIFS-Fix-a-possible-memory-corruption-in-push-locks.patch patches.suse/cifs-0004-CIFS-Fix-a-possible-memory-corruption-during-reconne.patch
-rw-r--r--patches.suse/cifs-0001-Prepare-for-encryption-support-first-part-.-Add-decr.patch450
-rw-r--r--patches.suse/cifs-0001-SMB3-parsing-for-new-snapshot-timestamp-mount-parm.patch141
-rw-r--r--patches.suse/cifs-0001-cifs-Simplify-SMB2-and-SMB311-dependencies.patch48
-rw-r--r--patches.suse/cifs-0001-cifs-no-need-to-wank-with-copying-and-advancing-iove.patch144
-rw-r--r--patches.suse/cifs-0001-cifs_readv_receive-use-cifs_read_from_socket.patch44
-rw-r--r--patches.suse/cifs-0002-cifs-Only-select-the-required-crypto-modules.patch47
-rw-r--r--patches.suse/cifs-0002-cifs-don-t-bother-with-kmap-on-read_pages-side.patch239
-rw-r--r--patches.suse/cifs-0003-cifs-Add-soft-dependencies.patch42
-rw-r--r--patches.suse/cifs-0005-CIFS-Separate-SMB2-header-structure.patch1048
-rw-r--r--patches.suse/cifs-0006-CIFS-Make-SendReceive2-takes-resp-iov.patch880
-rw-r--r--patches.suse/cifs-0007-CIFS-Make-send_cancel-take-rqst-as-argument.patch164
-rw-r--r--patches.suse/cifs-0008-CIFS-Send-RFC1001-length-in-a-separate-iov.patch657
-rw-r--r--patches.suse/cifs-0009-CIFS-Separate-SMB2-sync-header-processing.patch127
-rw-r--r--patches.suse/cifs-0010-CIFS-Separate-RFC1001-length-processing-for-SMB2-rea.patch225
-rw-r--r--patches.suse/cifs-0011-CIFS-Add-capability-to-transform-requests-before-sen.patch633
-rw-r--r--patches.suse/cifs-0012-CIFS-Enable-encryption-during-session-setup-phase.patch94
-rw-r--r--patches.suse/cifs-0013-CIFS-Encrypt-SMB3-requests-before-sending.patch541
-rw-r--r--patches.suse/cifs-0014-CIFS-Add-transform-header-handling-callbacks.patch61
-rw-r--r--patches.suse/cifs-0015-CIFS-Add-mid-handle-callback.patch147
-rw-r--r--patches.suse/cifs-0016-CIFS-Add-copy-into-pages-callback-for-a-read-operati.patch146
-rw-r--r--patches.suse/cifs-0017-CIFS-Decrypt-and-process-small-encrypted-packets.patch362
-rw-r--r--patches.suse/cifs-0018-CIFS-Add-capability-to-decrypt-big-read-responses.patch277
-rw-r--r--patches.suse/cifs-0019-CIFS-Allow-to-switch-on-encryption-with-seal-mount-o.patch187
-rw-r--r--series.conf23
24 files changed, 6727 insertions, 0 deletions
diff --git a/patches.suse/cifs-0001-Prepare-for-encryption-support-first-part-.-Add-decr.patch b/patches.suse/cifs-0001-Prepare-for-encryption-support-first-part-.-Add-decr.patch
new file mode 100644
index 0000000000..9dc17c9c77
--- /dev/null
+++ b/patches.suse/cifs-0001-Prepare-for-encryption-support-first-part-.-Add-decr.patch
@@ -0,0 +1,450 @@
+From 373512ec5c105ed09e3738196dcb257dfab65cba Mon Sep 17 00:00:00 2001
+From: Steve French <smfrench@gmail.com>
+Date: Fri, 18 Dec 2015 13:05:30 -0600
+Subject: [PATCH] Prepare for encryption support (first part). Add decryption
+ and encryption key generation. Thanks to Metze for helping with this.
+Patch-mainline: v4.5-rc1
+Git-commit: 373512ec5c105ed09e3738196dcb257dfab65cba
+References: fate#322075
+
+Reviewed-by: Stefan Metzmacher <metze@samba.org>
+Signed-off-by: Steve French <steve.french@primarydata.com>
+Acked-by: Aurelien Aptel <aaptel@suse.com>
+Acked-by: David Mulder <dmulder@suse.com>
+
+---
+ fs/cifs/cifsglob.h | 8 ++-
+ fs/cifs/cifsproto.h | 5 +-
+ fs/cifs/connect.c | 2
+ fs/cifs/misc.c | 2
+ fs/cifs/smb2misc.c | 36 ++++++++++++++--
+ fs/cifs/smb2ops.c | 13 ++++--
+ fs/cifs/smb2pdu.c | 10 +---
+ fs/cifs/smb2pdu.h | 8 +--
+ fs/cifs/smb2proto.h | 3 -
+ fs/cifs/smb2transport.c | 102 +++++++++++++++++++++++++++++++++++++++++++++---
+ 10 files changed, 156 insertions(+), 33 deletions(-)
+
+--- a/fs/cifs/cifsglob.h
++++ b/fs/cifs/cifsglob.h
+@@ -225,7 +225,7 @@ struct smb_version_operations {
+ void (*print_stats)(struct seq_file *m, struct cifs_tcon *);
+ void (*dump_share_caps)(struct seq_file *, struct cifs_tcon *);
+ /* verify the message */
+- int (*check_message)(char *, unsigned int);
++ int (*check_message)(char *, unsigned int, struct TCP_Server_Info *);
+ bool (*is_oplock_break)(char *, struct TCP_Server_Info *);
+ void (*downgrade_oplock)(struct TCP_Server_Info *,
+ struct cifsInodeInfo *, bool);
+@@ -627,6 +627,7 @@ struct TCP_Server_Info {
+ #ifdef CONFIG_CIFS_SMB2
+ unsigned int max_read;
+ unsigned int max_write;
++ __u8 preauth_hash[512];
+ struct delayed_work reconnect; /* reconnect workqueue job */
+ struct mutex reconnect_mutex; /* prevent simultaneous reconnects */
+ #endif /* CONFIG_CIFS_SMB2 */
+@@ -811,7 +812,10 @@ struct cifs_ses {
+ bool need_reconnect:1; /* connection reset, uid now invalid */
+ #ifdef CONFIG_CIFS_SMB2
+ __u16 session_flags;
+- char smb3signingkey[SMB3_SIGN_KEY_SIZE]; /* for signing smb3 packets */
++ __u8 smb3signingkey[SMB3_SIGN_KEY_SIZE];
++ __u8 smb3encryptionkey[SMB3_SIGN_KEY_SIZE];
++ __u8 smb3decryptionkey[SMB3_SIGN_KEY_SIZE];
++ __u8 preauth_hash[512];
+ #endif /* CONFIG_CIFS_SMB2 */
+ };
+
+--- a/fs/cifs/cifsproto.h
++++ b/fs/cifs/cifsproto.h
+@@ -103,7 +103,7 @@ extern int SendReceiveBlockingLock(const
+ struct smb_hdr *out_buf,
+ int *bytes_returned);
+ extern int cifs_reconnect(struct TCP_Server_Info *server);
+-extern int checkSMB(char *buf, unsigned int length);
++extern int checkSMB(char *buf, unsigned int len, struct TCP_Server_Info *srvr);
+ extern bool is_valid_oplock_break(char *, struct TCP_Server_Info *);
+ extern bool backup_cred(struct cifs_sb_info *);
+ extern bool is_size_safe_to_change(struct cifsInodeInfo *, __u64 eof);
+@@ -443,7 +443,8 @@ extern int setup_ntlm_response(struct ci
+ extern int setup_ntlmv2_rsp(struct cifs_ses *, const struct nls_table *);
+ extern void cifs_crypto_shash_release(struct TCP_Server_Info *);
+ extern int calc_seckey(struct cifs_ses *);
+-extern int generate_smb3signingkey(struct cifs_ses *);
++extern int generate_smb30signingkey(struct cifs_ses *);
++extern int generate_smb311signingkey(struct cifs_ses *);
+
+ #ifdef CONFIG_CIFS_WEAK_PW_HASH
+ extern int calc_lanman_hash(const char *password, const char *cryptkey,
+--- a/fs/cifs/connect.c
++++ b/fs/cifs/connect.c
+@@ -833,7 +833,7 @@ standard_receive3(struct TCP_Server_Info
+ * 48 bytes is enough to display the header and a little bit
+ * into the payload for debugging purposes.
+ */
+- length = server->ops->check_message(buf, server->total_read);
++ length = server->ops->check_message(buf, server->total_read, server);
+ if (length != 0)
+ cifs_dump_mem("Bad SMB: ", buf,
+ min_t(unsigned int, server->total_read, 48));
+--- a/fs/cifs/misc.c
++++ b/fs/cifs/misc.c
+@@ -311,7 +311,7 @@ check_smb_hdr(struct smb_hdr *smb)
+ }
+
+ int
+-checkSMB(char *buf, unsigned int total_read)
++checkSMB(char *buf, unsigned int total_read, struct TCP_Server_Info *server)
+ {
+ struct smb_hdr *smb = (struct smb_hdr *)buf;
+ __u32 rfclen = be32_to_cpu(smb->smb_buf_length);
+--- a/fs/cifs/smb2misc.c
++++ b/fs/cifs/smb2misc.c
+@@ -38,7 +38,7 @@ check_smb2_hdr(struct smb2_hdr *hdr, __u
+ * Make sure that this really is an SMB, that it is a response,
+ * and that the message ids match.
+ */
+- if ((*(__le32 *)hdr->ProtocolId == SMB2_PROTO_NUMBER) &&
++ if ((hdr->ProtocolId == SMB2_PROTO_NUMBER) &&
+ (mid == wire_mid)) {
+ if (hdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR)
+ return 0;
+@@ -50,9 +50,9 @@ check_smb2_hdr(struct smb2_hdr *hdr, __u
+ cifs_dbg(VFS, "Received Request not response\n");
+ }
+ } else { /* bad signature or mid */
+- if (*(__le32 *)hdr->ProtocolId != SMB2_PROTO_NUMBER)
++ if (hdr->ProtocolId != SMB2_PROTO_NUMBER)
+ cifs_dbg(VFS, "Bad protocol string signature header %x\n",
+- *(unsigned int *) hdr->ProtocolId);
++ le32_to_cpu(hdr->ProtocolId));
+ if (mid != wire_mid)
+ cifs_dbg(VFS, "Mids do not match: %llu and %llu\n",
+ mid, wire_mid);
+@@ -93,11 +93,11 @@ static const __le16 smb2_rsp_struct_size
+ };
+
+ int
+-smb2_check_message(char *buf, unsigned int length)
++smb2_check_message(char *buf, unsigned int length, struct TCP_Server_Info *srvr)
+ {
+ struct smb2_hdr *hdr = (struct smb2_hdr *)buf;
+ struct smb2_pdu *pdu = (struct smb2_pdu *)hdr;
+- __u64 mid = le64_to_cpu(hdr->MessageId);
++ __u64 mid;
+ __u32 len = get_rfc1002_length(buf);
+ __u32 clc_len; /* calculated length */
+ int command;
+@@ -111,6 +111,30 @@ smb2_check_message(char *buf, unsigned i
+ * ie Validate the wct via smb2_struct_sizes table above
+ */
+
++ if (hdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM) {
++ struct smb2_transform_hdr *thdr =
++ (struct smb2_transform_hdr *)buf;
++ struct cifs_ses *ses = NULL;
++ struct list_head *tmp;
++
++ /* decrypt frame now that it is completely read in */
++ spin_lock(&cifs_tcp_ses_lock);
++ list_for_each(tmp, &srvr->smb_ses_list) {
++ ses = list_entry(tmp, struct cifs_ses, smb_ses_list);
++ if (ses->Suid == thdr->SessionId)
++ break;
++
++ ses = NULL;
++ }
++ spin_unlock(&cifs_tcp_ses_lock);
++ if (ses == NULL) {
++ cifs_dbg(VFS, "no decryption - session id not found\n");
++ return 1;
++ }
++ }
++
++
++ mid = le64_to_cpu(hdr->MessageId);
+ if (length < sizeof(struct smb2_pdu)) {
+ if ((length >= sizeof(struct smb2_hdr)) && (hdr->Status != 0)) {
+ pdu->StructureSize2 = 0;
+@@ -322,7 +346,7 @@ smb2_get_data_area_len(int *off, int *le
+
+ /* return pointer to beginning of data area, ie offset from SMB start */
+ if ((*off != 0) && (*len != 0))
+- return (char *)(&hdr->ProtocolId[0]) + *off;
++ return (char *)(&hdr->ProtocolId) + *off;
+ else
+ return NULL;
+ }
+--- a/fs/cifs/smb2ops.c
++++ b/fs/cifs/smb2ops.c
+@@ -182,6 +182,11 @@ smb2_find_mid(struct TCP_Server_Info *se
+ struct smb2_hdr *hdr = (struct smb2_hdr *)buf;
+ __u64 wire_mid = le64_to_cpu(hdr->MessageId);
+
++ if (hdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM) {
++ cifs_dbg(VFS, "encrypted frame parsing not supported yet");
++ return NULL;
++ }
++
+ spin_lock(&GlobalMid_Lock);
+ list_for_each_entry(mid, &server->pending_mid_q, qhead) {
+ if ((mid->mid == wire_mid) &&
+@@ -1720,7 +1725,7 @@ struct smb_version_operations smb30_oper
+ .get_lease_key = smb2_get_lease_key,
+ .set_lease_key = smb2_set_lease_key,
+ .new_lease_key = smb2_new_lease_key,
+- .generate_signingkey = generate_smb3signingkey,
++ .generate_signingkey = generate_smb30signingkey,
+ .calc_signature = smb3_calc_signature,
+ .set_integrity = smb3_set_integrity,
+ .is_read_op = smb21_is_read_op,
+@@ -1807,7 +1812,7 @@ struct smb_version_operations smb311_ope
+ .get_lease_key = smb2_get_lease_key,
+ .set_lease_key = smb2_set_lease_key,
+ .new_lease_key = smb2_new_lease_key,
+- .generate_signingkey = generate_smb3signingkey,
++ .generate_signingkey = generate_smb311signingkey,
+ .calc_signature = smb3_calc_signature,
+ .set_integrity = smb3_set_integrity,
+ .is_read_op = smb21_is_read_op,
+@@ -1866,7 +1871,7 @@ struct smb_version_values smb21_values =
+ struct smb_version_values smb30_values = {
+ .version_string = SMB30_VERSION_STRING,
+ .protocol_id = SMB30_PROT_ID,
+- .req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES,
++ .req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES | SMB2_GLOBAL_CAP_ENCRYPTION,
+ .large_lock_type = 0,
+ .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK,
+ .shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK,
+@@ -1886,7 +1891,7 @@ struct smb_version_values smb30_values =
+ struct smb_version_values smb302_values = {
+ .version_string = SMB302_VERSION_STRING,
+ .protocol_id = SMB302_PROT_ID,
+- .req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES,
++ .req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES | SMB2_GLOBAL_CAP_ENCRYPTION,
+ .large_lock_type = 0,
+ .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK,
+ .shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK,
+--- a/fs/cifs/smb2pdu.c
++++ b/fs/cifs/smb2pdu.c
+@@ -97,10 +97,7 @@ smb2_hdr_assemble(struct smb2_hdr *hdr,
+ hdr->smb2_buf_length = cpu_to_be32(parmsize + sizeof(struct smb2_hdr)
+ - 4 /* RFC 1001 length field itself not counted */);
+
+- hdr->ProtocolId[0] = 0xFE;
+- hdr->ProtocolId[1] = 'S';
+- hdr->ProtocolId[2] = 'M';
+- hdr->ProtocolId[3] = 'B';
++ hdr->ProtocolId = SMB2_PROTO_NUMBER;
+ hdr->StructureSize = cpu_to_le16(64);
+ hdr->Command = smb2_cmd;
+ if (tcon && tcon->ses && tcon->ses->server) {
+@@ -1590,7 +1587,8 @@ SMB2_ioctl(const unsigned int xid, struc
+ goto ioctl_exit;
+ }
+
+- memcpy(*out_data, rsp->hdr.ProtocolId + le32_to_cpu(rsp->OutputOffset),
++ memcpy(*out_data,
++ (char *)&rsp->hdr.ProtocolId + le32_to_cpu(rsp->OutputOffset),
+ *plen);
+ ioctl_exit:
+ free_rsp_buf(resp_buftype, rsp);
+@@ -2165,7 +2163,7 @@ SMB2_read(const unsigned int xid, struct
+ }
+
+ if (*buf) {
+- memcpy(*buf, (char *)rsp->hdr.ProtocolId + rsp->DataOffset,
++ memcpy(*buf, (char *)&rsp->hdr.ProtocolId + rsp->DataOffset,
+ *nbytes);
+ free_rsp_buf(resp_buftype, iov[0].iov_base);
+ } else if (resp_buftype != CIFS_NO_BUFFER) {
+--- a/fs/cifs/smb2pdu.h
++++ b/fs/cifs/smb2pdu.h
+@@ -86,6 +86,7 @@
+ #define MAX_SMB2_HDR_SIZE 0x78 /* 4 len + 64 hdr + (2*24 wct) + 2 bct + 2 pad */
+
+ #define SMB2_PROTO_NUMBER cpu_to_le32(0x424d53fe)
++#define SMB2_TRANSFORM_PROTO_NUM cpu_to_le32(0x424d53fd)
+
+ /*
+ * SMB2 Header Definition
+@@ -102,7 +103,7 @@ struct smb2_hdr {
+ __be32 smb2_buf_length; /* big endian on wire */
+ /* length is only two or three bytes - with
+ one or two byte type preceding it that MBZ */
+- __u8 ProtocolId[4]; /* 0xFE 'S' 'M' 'B' */
++ __le32 ProtocolId; /* 0xFE 'S' 'M' 'B' */
+ __le16 StructureSize; /* 64 */
+ __le16 CreditCharge; /* MBZ */
+ __le32 Status; /* Error from server */
+@@ -128,11 +129,10 @@ struct smb2_transform_hdr {
+ one or two byte type preceding it that MBZ */
+ __u8 ProtocolId[4]; /* 0xFD 'S' 'M' 'B' */
+ __u8 Signature[16];
+- __u8 Nonce[11];
+- __u8 Reserved[5];
++ __u8 Nonce[16];
+ __le32 OriginalMessageSize;
+ __u16 Reserved1;
+- __le16 EncryptionAlgorithm;
++ __le16 Flags; /* EncryptionAlgorithm */
+ __u64 SessionId;
+ } __packed;
+
+--- a/fs/cifs/smb2proto.h
++++ b/fs/cifs/smb2proto.h
+@@ -34,7 +34,8 @@ struct smb_rqst;
+ *****************************************************************
+ */
+ extern int map_smb2_to_linux_error(char *buf, bool log_err);
+-extern int smb2_check_message(char *buf, unsigned int length);
++extern int smb2_check_message(char *buf, unsigned int length,
++ struct TCP_Server_Info *server);
+ extern unsigned int smb2_calc_size(void *buf);
+ extern char *smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr);
+ extern __le16 *cifs_convert_path_to_utf16(const char *from,
+--- a/fs/cifs/smb2transport.c
++++ b/fs/cifs/smb2transport.c
+@@ -222,8 +222,8 @@ smb2_calc_signature(struct smb_rqst *rqs
+ return rc;
+ }
+
+-int
+-generate_smb3signingkey(struct cifs_ses *ses)
++static int generate_key(struct cifs_ses *ses, struct kvec label,
++ struct kvec context, __u8 *key, unsigned int key_size)
+ {
+ unsigned char zero = 0x0;
+ __u8 i[4] = {0, 0, 0, 1};
+@@ -233,7 +233,7 @@ generate_smb3signingkey(struct cifs_ses
+ unsigned char *hashptr = prfhash;
+
+ memset(prfhash, 0x0, SMB2_HMACSHA256_SIZE);
+- memset(ses->smb3signingkey, 0x0, SMB3_SIGNKEY_SIZE);
++ memset(key, 0x0, key_size);
+
+ rc = smb3_crypto_shash_allocate(ses->server);
+ if (rc) {
+@@ -262,7 +262,7 @@ generate_smb3signingkey(struct cifs_ses
+ }
+
+ rc = crypto_shash_update(&ses->server->secmech.sdeschmacsha256->shash,
+- "SMB2AESCMAC", 12);
++ label.iov_base, label.iov_len);
+ if (rc) {
+ cifs_dbg(VFS, "%s: Could not update with label\n", __func__);
+ goto smb3signkey_ret;
+@@ -276,7 +276,7 @@ generate_smb3signingkey(struct cifs_ses
+ }
+
+ rc = crypto_shash_update(&ses->server->secmech.sdeschmacsha256->shash,
+- "SmbSign", 8);
++ context.iov_base, context.iov_len);
+ if (rc) {
+ cifs_dbg(VFS, "%s: Could not update with context\n", __func__);
+ goto smb3signkey_ret;
+@@ -296,12 +296,102 @@ generate_smb3signingkey(struct cifs_ses
+ goto smb3signkey_ret;
+ }
+
+- memcpy(ses->smb3signingkey, hashptr, SMB3_SIGNKEY_SIZE);
++ memcpy(key, hashptr, key_size);
+
+ smb3signkey_ret:
+ return rc;
+ }
+
++struct derivation {
++ struct kvec label;
++ struct kvec context;
++};
++
++struct derivation_triplet {
++ struct derivation signing;
++ struct derivation encryption;
++ struct derivation decryption;
++};
++
++static int
++generate_smb3signingkey(struct cifs_ses *ses,
++ const struct derivation_triplet *ptriplet)
++{
++ int rc;
++
++ rc = generate_key(ses, ptriplet->signing.label,
++ ptriplet->signing.context, ses->smb3signingkey,
++ SMB3_SIGN_KEY_SIZE);
++ if (rc)
++ return rc;
++
++ rc = generate_key(ses, ptriplet->encryption.label,
++ ptriplet->encryption.context, ses->smb3encryptionkey,
++ SMB3_SIGN_KEY_SIZE);
++ if (rc)
++ return rc;
++
++ return generate_key(ses, ptriplet->decryption.label,
++ ptriplet->decryption.context,
++ ses->smb3decryptionkey, SMB3_SIGN_KEY_SIZE);
++}
++
++int
++generate_smb30signingkey(struct cifs_ses *ses)
++
++{
++ struct derivation_triplet triplet;
++ struct derivation *d;
++
++ d = &triplet.signing;
++ d->label.iov_base = "SMB2AESCMAC";
++ d->label.iov_len = 12;
++ d->context.iov_base = "SmbSign";
++ d->context.iov_len = 8;
++
++ d = &triplet.encryption;
++ d->label.iov_base = "SMB2AESCCM";
++ d->label.iov_len = 11;
++ d->context.iov_base = "ServerIn ";
++ d->context.iov_len = 10;
++
++ d = &triplet.decryption;
++ d->label.iov_base = "SMB2AESCCM";
++ d->label.iov_len = 11;
++ d->context.iov_base = "ServerOut";
++ d->context.iov_len = 10;
++
++ return generate_smb3signingkey(ses, &triplet);
++}
++
++int
++generate_smb311signingkey(struct cifs_ses *ses)
++
++{
++ struct derivation_triplet triplet;
++ struct derivation *d;
++
++ d = &triplet.signing;
++ d->label.iov_base = "SMB2AESCMAC";
++ d->label.iov_len = 12;
++ d->context.iov_base = "SmbSign";
++ d->context.iov_len = 8;
++
++ d = &triplet.encryption;
++ d->label.iov_base = "SMB2AESCCM";
++ d->label.iov_len = 11;
++ d->context.iov_base = "ServerIn ";
++ d->context.iov_len = 10;
++
++ d = &triplet.decryption;
++ d->label.iov_base = "SMB2AESCCM";
++ d->label.iov_len = 11;
++ d->context.iov_base = "ServerOut";
++ d->context.iov_len = 10;
++
++ return generate_smb3signingkey(ses, &triplet);
++}
++
+ int
+ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
+ {
diff --git a/patches.suse/cifs-0001-SMB3-parsing-for-new-snapshot-timestamp-mount-parm.patch b/patches.suse/cifs-0001-SMB3-parsing-for-new-snapshot-timestamp-mount-parm.patch
new file mode 100644
index 0000000000..43e3bc644e
--- /dev/null
+++ b/patches.suse/cifs-0001-SMB3-parsing-for-new-snapshot-timestamp-mount-parm.patch
@@ -0,0 +1,141 @@
+From 8b217fe7fcadd162944a88b14990b9723c27419f Mon Sep 17 00:00:00 2001
+From: Steve French <smfrench@gmail.com>
+Date: Fri, 11 Nov 2016 22:36:20 -0600
+Subject: [PATCH] SMB3: parsing for new snapshot timestamp mount parm
+Patch-mainline: v4.10-rc1
+Git-commit: 8b217fe7fcadd162944a88b14990b9723c27419f
+References: fate#322075
+
+New mount option "snapshot=<time>" to allow mounting an earlier
+version of the remote volume (if such a snapshot exists on
+the server).
+
+Note that eventually specifying a snapshot time of 1 will allow
+the user to mount the oldest snapshot. A subsequent patch
+add the processing for that and another for actually specifying
+the "time warp" create context on SMB2/SMB3 open.
+
+Check to make sure SMB2 negotiated, and ensure that
+we use a different tcon if mount same share twice
+but with different snaphshot times
+
+Signed-off-by: Steve French <smfrench@gmail.com>
+Reviewed-by: Pavel Shilovsky <pshilov@microsoft.com>
+Acked-by: Aurelien Aptel <aaptel@suse.com>
+Acked-by: David Mulder <dmulder@suse.com>
+
+---
+ fs/cifs/cifsglob.h | 2 ++
+ fs/cifs/connect.c | 38 +++++++++++++++++++++++++++++++++++---
+ 2 files changed, 37 insertions(+), 3 deletions(-)
+
+diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
+--- a/fs/cifs/cifsglob.h
++++ b/fs/cifs/cifsglob.h
+@@ -517,6 +517,7 @@ struct smb_vol {
+ struct sockaddr_storage dstaddr; /* destination address */
+ struct sockaddr_storage srcaddr; /* allow binding to a local IP */
+ struct nls_table *local_nls;
++ __u64 snapshot_time; /* needed for timewarp tokens */
+ };
+
+ #define CIFS_MOUNT_MASK (CIFS_MOUNT_NO_PERM | CIFS_MOUNT_SET_UID | \
+@@ -924,6 +925,7 @@ struct cifs_tcon {
+ __u32 maximal_access;
+ __u32 vol_serial_number;
+ __le64 vol_create_time;
++ __u64 snapshot_time; /* for timewarp tokens - timestamp of snapshot */
+ __u32 ss_flags; /* sector size flags */
+ __u32 perf_sector_size; /* best sector size for perf */
+ __u32 max_chunks;
+diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
+--- a/fs/cifs/connect.c
++++ b/fs/cifs/connect.c
+@@ -98,6 +98,7 @@ enum {
+ Opt_cruid, Opt_gid, Opt_file_mode,
+ Opt_dirmode, Opt_port,
+ Opt_rsize, Opt_wsize, Opt_actimeo,
++ Opt_snapshot,
+
+ /* Mount options which take string value */
+ Opt_user, Opt_pass, Opt_ip,
+@@ -191,6 +192,7 @@ static const match_table_t cifs_mount_op
+ { Opt_rsize, "rsize=%s" },
+ { Opt_wsize, "wsize=%s" },
+ { Opt_actimeo, "actimeo=%s" },
++ { Opt_snapshot, "snapshot=%s" },
+
+ { Opt_blank_user, "user=" },
+ { Opt_blank_user, "username=" },
+@@ -1587,6 +1589,14 @@ cifs_parse_mount_options(const char *mou
+ goto cifs_parse_mount_err;
+ }
+ break;
++ case Opt_snapshot:
++ if (get_option_ul(args, &option)) {
++ cifs_dbg(VFS, "%s: Invalid snapshot time\n",
++ __func__);
++ goto cifs_parse_mount_err;
++ }
++ vol->snapshot_time = option;
++ break;
+
+ /* String Arguments */
+
+@@ -2571,7 +2581,7 @@ static int match_tcon(struct cifs_tcon *
+ }
+
+ static struct cifs_tcon *
+-cifs_find_tcon(struct cifs_ses *ses, const char *unc)
++cifs_find_tcon(struct cifs_ses *ses, struct smb_vol *volume_info)
+ {
+ struct list_head *tmp;
+ struct cifs_tcon *tcon;
+@@ -2579,8 +2589,14 @@ cifs_find_tcon(struct cifs_ses *ses, con
+ 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, unc))
++ if (!match_tcon(tcon, volume_info->UNC))
+ continue;
++
++#ifdef CONFIG_CIFS_SMB2
++ if (tcon->snapshot_time != volume_info->snapshot_time)
++ continue;
++#endif /* CONFIG_CIFS_SMB2 */
++
+ ++tcon->tc_count;
+ spin_unlock(&cifs_tcp_ses_lock);
+ return tcon;
+@@ -2621,7 +2637,7 @@ cifs_get_tcon(struct cifs_ses *ses, stru
+ int rc, xid;
+ struct cifs_tcon *tcon;
+
+- tcon = cifs_find_tcon(ses, volume_info->UNC);
++ tcon = cifs_find_tcon(ses, volume_info);
+ if (tcon) {
+ cifs_dbg(FYI, "Found match on UNC path\n");
+ /* existing tcon already has a reference */
+@@ -2642,6 +2658,22 @@ cifs_get_tcon(struct cifs_ses *ses, stru
+ goto out_fail;
+ }
+
++ if (volume_info->snapshot_time) {
++#ifdef CONFIG_CIFS_SMB2
++ if (ses->server->vals->protocol_id == 0) {
++ cifs_dbg(VFS,
++ "Use SMB2 or later for snapshot mount option\n");
++ rc = -EOPNOTSUPP;
++ goto out_fail;
++ } else
++ tcon->snapshot_time = volume_info->snapshot_time;
++#else
++ cifs_dbg(VFS, "Snapshot mount option requires SMB2 support\n");
++ rc = -EOPNOTSUPP;
++ goto out_fail;
++#endif /* CONFIG_CIFS_SMB2 */
++ }
++
+ tcon->ses = ses;
+ if (volume_info->password) {
+ tcon->password = kstrdup(volume_info->password, GFP_KERNEL);
diff --git a/patches.suse/cifs-0001-cifs-Simplify-SMB2-and-SMB311-dependencies.patch b/patches.suse/cifs-0001-cifs-Simplify-SMB2-and-SMB311-dependencies.patch
new file mode 100644
index 0000000000..0844592406
--- /dev/null
+++ b/patches.suse/cifs-0001-cifs-Simplify-SMB2-and-SMB311-dependencies.patch
@@ -0,0 +1,48 @@
+From c1ecea87471bbb614f8121e00e5787f363140365 Mon Sep 17 00:00:00 2001
+From: Jean Delvare <jdelvare@suse.de>
+Date: Wed, 25 Jan 2017 16:07:29 +0100
+Subject: [PATCH] cifs: Simplify SMB2 and SMB311 dependencies
+Patch-mainline: v4.11
+Git-commit: c1ecea87471bbb614f8121e00e5787f363140365
+References: fate#322075
+
+* CIFS_SMB2 depends on CIFS, which depends on INET and selects NLS. So
+ these dependencies do not need to be repeated for CIFS_SMB2.
+* CIFS_SMB311 depends on CIFS_SMB2, which depends on INET. So this
+ dependency doesn't need to be repeated for CIFS_SMB311.
+
+Reviewed-by: Pavel Shilovsky <pshilov@microsoft.com>
+Signed-off-by: Jean Delvare <jdelvare@suse.de>
+Cc: Steve French <sfrench@samba.org>
+Acked-by: Aurelien Aptel <aaptel@suse.com>
+Acked-by: David Mulder <dmulder@suse.com>
+
+---
+ fs/cifs/Kconfig | 5 ++---
+ 1 file changed, 2 insertions(+), 3 deletions(-)
+
+diff --git a/fs/cifs/Kconfig b/fs/cifs/Kconfig
+--- a/fs/cifs/Kconfig
++++ b/fs/cifs/Kconfig
+@@ -169,8 +169,7 @@ 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
+@@ -194,7 +193,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.
+--
+2.10.2
+
diff --git a/patches.suse/cifs-0001-cifs-no-need-to-wank-with-copying-and-advancing-iove.patch b/patches.suse/cifs-0001-cifs-no-need-to-wank-with-copying-and-advancing-iove.patch
new file mode 100644
index 0000000000..2604d17c2d
--- /dev/null
+++ b/patches.suse/cifs-0001-cifs-no-need-to-wank-with-copying-and-advancing-iove.patch
@@ -0,0 +1,144 @@
+From 09aab880f7c5ac31e9db18a63353b980bcb52261 Mon Sep 17 00:00:00 2001
+From: Al Viro <viro@zeniv.linux.org.uk>
+Date: Fri, 13 Nov 2015 03:00:17 -0500
+Subject: cifs: no need to wank with copying and advancing iovec on
+ recvmsg side either
+Patch-mainline: v4.7-rc1
+Git-commit: 09aab880f7c5ac31e9db18a63353b980bcb52261
+References: fate#322075
+
+Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
+Acked-by: Aurelien Aptel <aaptel@suse.com>
+Acked-by: David Mulder <dmulder@suse.com>
+
+---
+ fs/cifs/cifsglob.h | 2 --
+ fs/cifs/connect.c | 72 ++++--------------------------------------------------
+ 2 files changed, 5 insertions(+), 69 deletions(-)
+
+diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
+--- a/fs/cifs/cifsglob.h
++++ b/fs/cifs/cifsglob.h
+@@ -622,8 +622,6 @@ struct TCP_Server_Info {
+ bool sec_mskerberos; /* supports legacy MS Kerberos */
+ bool large_buf; /* is current buffer large? */
+ struct delayed_work echo; /* echo ping workqueue job */
+- struct kvec *iov; /* reusable kvec array for receives */
+- unsigned int nr_iov; /* number of kvecs in array */
+ char *smallbuf; /* pointer to current "small" buffer */
+ char *bigbuf; /* pointer to current "big" buffer */
+ unsigned int total_read; /* total amount of data read in this pass */
+diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
+--- a/fs/cifs/connect.c
++++ b/fs/cifs/connect.c
+@@ -503,77 +503,20 @@ server_unresponsive(struct TCP_Server_In
+ return false;
+ }
+
+-/*
+- * kvec_array_init - clone a kvec array, and advance into it
+- * @new: pointer to memory for cloned array
+- * @iov: pointer to original array
+- * @nr_segs: number of members in original array
+- * @bytes: number of bytes to advance into the cloned array
+- *
+- * This function will copy the array provided in iov to a section of memory
+- * and advance the specified number of bytes into the new array. It returns
+- * the number of segments in the new array. "new" must be at least as big as
+- * the original iov array.
+- */
+-static unsigned int
+-kvec_array_init(struct kvec *new, struct kvec *iov, unsigned int nr_segs,
+- size_t bytes)
+-{
+- size_t base = 0;
+-
+- while (bytes || !iov->iov_len) {
+- int copy = min(bytes, iov->iov_len);
+-
+- bytes -= copy;
+- base += copy;
+- if (iov->iov_len == base) {
+- iov++;
+- nr_segs--;
+- base = 0;
+- }
+- }
+- memcpy(new, iov, sizeof(*iov) * nr_segs);
+- new->iov_base += base;
+- new->iov_len -= base;
+- return nr_segs;
+-}
+-
+-static struct kvec *
+-get_server_iovec(struct TCP_Server_Info *server, unsigned int nr_segs)
+-{
+- struct kvec *new_iov;
+-
+- if (server->iov && nr_segs <= server->nr_iov)
+- return server->iov;
+-
+- /* not big enough -- allocate a new one and release the old */
+- new_iov = kmalloc(sizeof(*new_iov) * nr_segs, GFP_NOFS);
+- if (new_iov) {
+- kfree(server->iov);
+- server->iov = new_iov;
+- server->nr_iov = nr_segs;
+- }
+- return new_iov;
+-}
+-
+ int
+ cifs_readv_from_socket(struct TCP_Server_Info *server, struct kvec *iov_orig,
+ unsigned int nr_segs, unsigned int to_read)
+ {
+ int length = 0;
+ int total_read;
+- unsigned int segs;
+ struct msghdr smb_msg;
+- struct kvec *iov;
+-
+- iov = get_server_iovec(server, nr_segs);
+- if (!iov)
+- return -ENOMEM;
+
+ smb_msg.msg_control = NULL;
+ smb_msg.msg_controllen = 0;
++ iov_iter_kvec(&smb_msg.msg_iter, READ | ITER_KVEC,
++ iov_orig, nr_segs, to_read);
+
+- for (total_read = 0; to_read; total_read += length, to_read -= length) {
++ for (total_read = 0; msg_data_left(&smb_msg); total_read += length) {
+ try_to_freeze();
+
+ if (server_unresponsive(server)) {
+@@ -581,10 +524,7 @@ cifs_readv_from_socket(struct TCP_Server
+ break;
+ }
+
+- segs = kvec_array_init(iov, iov_orig, nr_segs, total_read);
+-
+- length = kernel_recvmsg(server->ssocket, &smb_msg,
+- iov, segs, to_read, 0);
++ length = sock_recvmsg(server->ssocket, &smb_msg, msg_data_left(&smb_msg), 0);
+
+ if (server->tcpStatus == CifsExiting) {
+ total_read = -ESHUTDOWN;
+@@ -605,8 +545,7 @@ cifs_readv_from_socket(struct TCP_Server
+ length = 0;
+ continue;
+ } else if (length <= 0) {
+- cifs_dbg(FYI, "Received no data or error: expecting %d\n"
+- "got %d", to_read, length);
++ cifs_dbg(FYI, "Received no data or error: %d\n", length);
+ cifs_reconnect(server);
+ total_read = -ECONNABORTED;
+ break;
+@@ -785,7 +724,6 @@ static void clean_demultiplex_info(struc
+ }
+
+ kfree(server->hostname);
+- kfree(server->iov);
+ kfree(server);
+
+ length = atomic_dec_return(&tcpSesAllocCount);
diff --git a/patches.suse/cifs-0001-cifs_readv_receive-use-cifs_read_from_socket.patch b/patches.suse/cifs-0001-cifs_readv_receive-use-cifs_read_from_socket.patch
new file mode 100644
index 0000000000..2ceea38cea
--- /dev/null
+++ b/patches.suse/cifs-0001-cifs_readv_receive-use-cifs_read_from_socket.patch
@@ -0,0 +1,44 @@
+From a6137305a8c47fa92ab1a8efcfe76f0e9fa96ab7 Mon Sep 17 00:00:00 2001
+From: Al Viro <viro@zeniv.linux.org.uk>
+Date: Sat, 9 Jan 2016 19:37:16 -0500
+Subject: [PATCH] cifs_readv_receive: use cifs_read_from_socket()
+Patch-mainline: v4.7-rc1
+Git-commit: a6137305a8c47fa92ab1a8efcfe76f0e9fa96ab7
+References: fate#322075
+
+Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
+Acked-by: Aurelien Aptel <aaptel@suse.com>
+Acked-by: David Mulder <dmulder@suse.com>
+
+---
+ fs/cifs/cifssmb.c | 11 ++++-------
+ 1 file changed, 4 insertions(+), 7 deletions(-)
+
+diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
+--- a/fs/cifs/cifssmb.c
++++ b/fs/cifs/cifssmb.c
+@@ -1449,10 +1449,8 @@ cifs_readv_receive(struct TCP_Server_Inf
+ len = min_t(unsigned int, buflen, server->vals->read_rsp_size) -
+ HEADER_SIZE(server) + 1;
+
+- rdata->iov.iov_base = buf + HEADER_SIZE(server) - 1;
+- rdata->iov.iov_len = len;
+-
+- length = cifs_readv_from_socket(server, &rdata->iov, 1, len);
++ length = cifs_read_from_socket(server,
++ buf + HEADER_SIZE(server) - 1, len);
+ if (length < 0)
+ return length;
+ server->total_read += length;
+@@ -1504,9 +1502,8 @@ cifs_readv_receive(struct TCP_Server_Inf
+ len = data_offset - server->total_read;
+ if (len > 0) {
+ /* read any junk before data into the rest of smallbuf */
+- rdata->iov.iov_base = buf + server->total_read;
+- rdata->iov.iov_len = len;
+- length = cifs_readv_from_socket(server, &rdata->iov, 1, len);
++ length = cifs_read_from_socket(server,
++ buf + server->total_read, len);
+ if (length < 0)
+ return length;
+ server->total_read += length;
diff --git a/patches.suse/cifs-0002-cifs-Only-select-the-required-crypto-modules.patch b/patches.suse/cifs-0002-cifs-Only-select-the-required-crypto-modules.patch
new file mode 100644
index 0000000000..3ec3fbbddf
--- /dev/null
+++ b/patches.suse/cifs-0002-cifs-Only-select-the-required-crypto-modules.patch
@@ -0,0 +1,47 @@
+From 3692304bba6164be3810afd41b84ecb0e1e41db1 Mon Sep 17 00:00:00 2001
+From: Jean Delvare <jdelvare@suse.de>
+Date: Wed, 25 Jan 2017 16:08:17 +0100
+Subject: [PATCH] cifs: Only select the required crypto modules
+Patch-mainline: v4.11
+Git-commit: 3692304bba6164be3810afd41b84ecb0e1e41db1
+References: fate#322075
+
+The sha256 and cmac crypto modules are only needed for SMB2+, so move
+the select statements to config CIFS_SMB2. Also select CRYPTO_AES
+there as SMB2+ needs it.
+
+Reviewed-by: Pavel Shilovsky <pshilov@microsoft.com>
+Signed-off-by: Jean Delvare <jdelvare@suse.de>
+Cc: Steve French <sfrench@samba.org>
+Acked-by: Aurelien Aptel <aaptel@suse.com>
+Acked-by: David Mulder <dmulder@suse.com>
+
+---
+ fs/cifs/Kconfig | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/fs/cifs/Kconfig b/fs/cifs/Kconfig
+--- 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
+@@ -173,6 +171,9 @@ config CIFS_SMB2
+ select KEYS
+ select FSCACHE
+ select DNS_RESOLVER
++ select CRYPTO_AES
++ select CRYPTO_SHA256
++ select CRYPTO_CMAC
+
+ help
+ This enables support for the Server Message Block version 2
+--
+2.10.2
+
diff --git a/patches.suse/cifs-0002-cifs-don-t-bother-with-kmap-on-read_pages-side.patch b/patches.suse/cifs-0002-cifs-don-t-bother-with-kmap-on-read_pages-side.patch
new file mode 100644
index 0000000000..3c580e8e0e
--- /dev/null
+++ b/patches.suse/cifs-0002-cifs-don-t-bother-with-kmap-on-read_pages-side.patch
@@ -0,0 +1,239 @@
+From 71335664c38f03de10d7cf1d82705fe55a130b33 Mon Sep 17 00:00:00 2001
+From: Al Viro <viro@zeniv.linux.org.uk>
+Date: Sat, 9 Jan 2016 19:54:50 -0500
+Subject: cifs: don't bother with kmap on read_pages side
+Patch-mainline: v4.7-rc1
+Git-commit: 71335664c38f03de10d7cf1d82705fe55a130b33
+References: fate#322075
+
+just do ITER_BVEC recvmsg
+
+Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
+Acked-by: Aurelien Aptel <aaptel@suse.com>
+Acked-by: David Mulder <dmulder@suse.com>
+
+---
+ fs/cifs/cifsproto.h | 7 +++---
+ fs/cifs/connect.c | 65 ++++++++++++++++++++++++++++-------------------------
+ fs/cifs/file.c | 53 ++++++++++++++-----------------------------
+ 3 files changed, 55 insertions(+), 70 deletions(-)
+
+diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
+--- a/fs/cifs/cifsproto.h
++++ b/fs/cifs/cifsproto.h
+@@ -187,9 +187,8 @@ extern int set_cifs_acl(struct cifs_ntsd
+ extern void dequeue_mid(struct mid_q_entry *mid, bool malformed);
+ extern int cifs_read_from_socket(struct TCP_Server_Info *server, char *buf,
+ unsigned int to_read);
+-extern int cifs_readv_from_socket(struct TCP_Server_Info *server,
+- struct kvec *iov_orig, unsigned int nr_segs,
+- unsigned int to_read);
++extern int cifs_read_page_from_socket(struct TCP_Server_Info *server,
++ struct page *page, unsigned int to_read);
+ extern int cifs_setup_cifs_sb(struct smb_vol *pvolume_info,
+ struct cifs_sb_info *cifs_sb);
+ extern int cifs_match_super(struct super_block *, void *);
+diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
+--- a/fs/cifs/connect.c
++++ b/fs/cifs/connect.c
+@@ -503,39 +503,33 @@ server_unresponsive(struct TCP_Server_In
+ return false;
+ }
+
+-int
+-cifs_readv_from_socket(struct TCP_Server_Info *server, struct kvec *iov_orig,
+- unsigned int nr_segs, unsigned int to_read)
++static int
++cifs_readv_from_socket(struct TCP_Server_Info *server, struct msghdr *smb_msg)
+ {
+ int length = 0;
+ int total_read;
+- struct msghdr smb_msg;
++ smb_msg->msg_control = NULL;
++ smb_msg->msg_controllen = 0;
+
+- smb_msg.msg_control = NULL;
+- smb_msg.msg_controllen = 0;
+- iov_iter_kvec(&smb_msg.msg_iter, READ | ITER_KVEC,
+- iov_orig, nr_segs, to_read);
+-
+- for (total_read = 0; msg_data_left(&smb_msg); total_read += length) {
++ for (total_read = 0; msg_data_left(smb_msg); total_read += length) {
+ try_to_freeze();
+
+- if (server_unresponsive(server)) {
+- total_read = -ECONNABORTED;
+- break;
+- }
++ if (server_unresponsive(server))
++ return -ECONNABORTED;
+
+- length = sock_recvmsg(server->ssocket, &smb_msg, msg_data_left(&smb_msg), 0);
++ length = sock_recvmsg(server->ssocket, smb_msg, msg_data_left(smb_msg), 0);
+
+- if (server->tcpStatus == CifsExiting) {
+- total_read = -ESHUTDOWN;
+- break;
+- } else if (server->tcpStatus == CifsNeedReconnect) {
++ if (server->tcpStatus == CifsExiting)
++ return -ESHUTDOWN;
++
++ if (server->tcpStatus == CifsNeedReconnect) {
+ cifs_reconnect(server);
+- total_read = -ECONNABORTED;
+- break;
+- } else if (length == -ERESTARTSYS ||
+- length == -EAGAIN ||
+- length == -EINTR) {
++ return -ECONNABORTED;
++ }
++
++ if (length == -ERESTARTSYS ||
++ length == -EAGAIN ||
++ length == -EINTR) {
+ /*
+ * Minimum sleep to prevent looping, allowing socket
+ * to clear and app threads to set tcpStatus
+@@ -544,11 +538,12 @@ cifs_readv_from_socket(struct TCP_Server
+ usleep_range(1000, 2000);
+ length = 0;
+ continue;
+- } else if (length <= 0) {
++ }
++
++ if (length <= 0) {
+ cifs_dbg(FYI, "Received no data or error: %d\n", length);
+ cifs_reconnect(server);
+- total_read = -ECONNABORTED;
+- break;
++ return -ECONNABORTED;
+ }
+ }
+ return total_read;
+@@ -558,12 +553,21 @@ int
+ cifs_read_from_socket(struct TCP_Server_Info *server, char *buf,
+ unsigned int to_read)
+ {
+- struct kvec iov;
++ struct msghdr smb_msg;
++ struct kvec iov = {.iov_base = buf, .iov_len = to_read};
++ iov_iter_kvec(&smb_msg.msg_iter, READ | ITER_KVEC, &iov, 1, to_read);
+
+- iov.iov_base = buf;
+- iov.iov_len = to_read;
++ return cifs_readv_from_socket(server, &smb_msg);
++}
+
+- return cifs_readv_from_socket(server, &iov, 1, to_read);
++int
++cifs_read_page_from_socket(struct TCP_Server_Info *server, struct page *page,
++ unsigned int to_read)
++{
++ struct msghdr smb_msg;
++ struct bio_vec bv = {.bv_page = page, .bv_len = to_read};
++ iov_iter_bvec(&smb_msg.msg_iter, READ | ITER_BVEC, &bv, 1, to_read);
++ return cifs_readv_from_socket(server, &smb_msg);
+ }
+
+ static bool
+diff --git a/fs/cifs/file.c b/fs/cifs/file.c
+--- a/fs/cifs/file.c
++++ b/fs/cifs/file.c
+@@ -2873,39 +2873,31 @@ cifs_uncached_read_into_pages(struct TCP
+ int result = 0;
+ unsigned int i;
+ unsigned int nr_pages = rdata->nr_pages;
+- struct kvec iov;
+
+ rdata->got_bytes = 0;
+ rdata->tailsz = PAGE_SIZE;
+ for (i = 0; i < nr_pages; i++) {
+ struct page *page = rdata->pages[i];
++ size_t n;
+
+- if (len >= PAGE_SIZE) {
+- /* enough data to fill the page */
+- iov.iov_base = kmap(page);
+- iov.iov_len = PAGE_SIZE;
+- cifs_dbg(FYI, "%u: iov_base=%p iov_len=%zu\n",
+- i, iov.iov_base, iov.iov_len);
+- len -= PAGE_SIZE;
+- } else if (len > 0) {
+- /* enough for partial page, fill and zero the rest */
+- iov.iov_base = kmap(page);
+- iov.iov_len = len;
+- cifs_dbg(FYI, "%u: iov_base=%p iov_len=%zu\n",
+- i, iov.iov_base, iov.iov_len);
+- memset(iov.iov_base + len, '\0', PAGE_SIZE - len);
+- rdata->tailsz = len;
+- len = 0;
+- } else {
++ if (len <= 0) {
+ /* no need to hold page hostage */
+ rdata->pages[i] = NULL;
+ rdata->nr_pages--;
+ put_page(page);
+ continue;
+ }
+-
+- result = cifs_readv_from_socket(server, &iov, 1, iov.iov_len);
+- kunmap(page);
++ n = len;
++ if (len >= PAGE_SIZE) {
++ /* enough data to fill the page */
++ n = PAGE_SIZE;
++ len -= n;
++ } else {
++ zero_user(page, len, PAGE_SIZE - len);
++ rdata->tailsz = len;
++ len = 0;
++ }
++ result = cifs_read_page_from_socket(server, page, n);
+ if (result < 0)
+ break;
+
+@@ -3321,7 +3313,6 @@ cifs_readpages_read_into_pages(struct TC
+ u64 eof;
+ pgoff_t eof_index;
+ unsigned int nr_pages = rdata->nr_pages;
+- struct kvec iov;
+
+ /* determine the eof that the server (probably) has */
+ eof = CIFS_I(rdata->mapping->host)->server_eof;
+@@ -3332,23 +3323,14 @@ cifs_readpages_read_into_pages(struct TC
+ rdata->tailsz = PAGE_CACHE_SIZE;
+ for (i = 0; i < nr_pages; i++) {
+ struct page *page = rdata->pages[i];
++ size_t n = PAGE_CACHE_SIZE;
+
+ if (len >= PAGE_CACHE_SIZE) {
+- /* enough data to fill the page */
+- iov.iov_base = kmap(page);
+- iov.iov_len = PAGE_CACHE_SIZE;
+- cifs_dbg(FYI, "%u: idx=%lu iov_base=%p iov_len=%zu\n",
+- i, page->index, iov.iov_base, iov.iov_len);
+ len -= PAGE_CACHE_SIZE;
+ } else if (len > 0) {
+ /* enough for partial page, fill and zero the rest */
+- iov.iov_base = kmap(page);
+- iov.iov_len = len;
+- cifs_dbg(FYI, "%u: idx=%lu iov_base=%p iov_len=%zu\n",
+- i, page->index, iov.iov_base, iov.iov_len);
+- memset(iov.iov_base + len,
+- '\0', PAGE_CACHE_SIZE - len);
+- rdata->tailsz = len;
++ zero_user(page, len, PAGE_CACHE_SIZE - len);
++ n = rdata->tailsz = len;
+ len = 0;
+ } else if (page->index > eof_index) {
+ /*
+@@ -3378,8 +3360,7 @@ cifs_readpages_read_into_pages(struct TC
+ continue;
+ }
+
+- result = cifs_readv_from_socket(server, &iov, 1, iov.iov_len);
+- kunmap(page);
++ result = cifs_read_page_from_socket(server, page, n);
+ if (result < 0)
+ break;
+
diff --git a/patches.suse/cifs-0003-cifs-Add-soft-dependencies.patch b/patches.suse/cifs-0003-cifs-Add-soft-dependencies.patch
new file mode 100644
index 0000000000..baaca997d2
--- /dev/null
+++ b/patches.suse/cifs-0003-cifs-Add-soft-dependencies.patch
@@ -0,0 +1,42 @@
+From b9be76d585d48cb25af8db0d35e1ef9030fbe13a Mon Sep 17 00:00:00 2001
+From: Jean Delvare <jdelvare@suse.de>
+Date: Wed, 25 Jan 2017 16:09:10 +0100
+Subject: [PATCH] cifs: Add soft dependencies
+Patch-mainline: v4.11
+Git-commit: b9be76d585d48cb25af8db0d35e1ef9030fbe13a
+References: fate#322075
+
+List soft dependencies of cifs so that mkinitrd and dracut can include
+the required helper modules.
+
+Reviewed-by: Pavel Shilovsky <pshilov@microsoft.com>
+Signed-off-by: Jean Delvare <jdelvare@suse.de>
+Cc: Steve French <sfrench@samba.org>
+Acked-by: Aurelien Aptel <aaptel@suse.com>
+Acked-by: David Mulder <dmulder@suse.com>
+
+---
+ fs/cifs/cifsfs.c | 12 ++++++++++++
+ 1 file changed, 12 insertions(+)
+
+diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
+--- a/fs/cifs/cifsfs.c
++++ b/fs/cifs/cifsfs.c
+@@ -1305,5 +1305,17 @@ 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");
++#endif /* CONFIG_CIFS_SMB2 */
+ module_init(init_cifs)
+ module_exit(exit_cifs)
diff --git a/patches.suse/cifs-0005-CIFS-Separate-SMB2-header-structure.patch b/patches.suse/cifs-0005-CIFS-Separate-SMB2-header-structure.patch
new file mode 100644
index 0000000000..0f255f71f1
--- /dev/null
+++ b/patches.suse/cifs-0005-CIFS-Separate-SMB2-header-structure.patch
@@ -0,0 +1,1048 @@
+From 31473fc4f9653b73750d3792ffce6a6e1bdf0da7 Mon Sep 17 00:00:00 2001
+From: Pavel Shilovsky <pshilov@microsoft.com>
+Date: Mon, 24 Oct 2016 15:33:04 -0700
+Subject: [PATCH] CIFS: Separate SMB2 header structure
+Patch-mainline: v4.11
+Git-commit: 31473fc4f9653b73750d3792ffce6a6e1bdf0da7
+References: fate#322075
+
+In order to support compounding and encryption we need to separate
+RFC1001 length field and SMB2 header structure because the protocol
+treats them differently. This change will allow to simplify parsing
+of such complex SMB2 packets further.
+
+Signed-off-by: Pavel Shilovsky <pshilov@microsoft.com>
+Acked-by: Aurelien Aptel <aaptel@suse.com>
+Acked-by: David Mulder <dmulder@suse.com>
+
+---
+ fs/cifs/smb2glob.h | 5 ++
+ fs/cifs/smb2maperror.c | 5 +-
+ fs/cifs/smb2misc.c | 61 ++++++++++++----------
+ fs/cifs/smb2ops.c | 26 +++++-----
+ fs/cifs/smb2pdu.c | 132 ++++++++++++++++++++++++++----------------------
+ fs/cifs/smb2pdu.h | 12 +++--
+ fs/cifs/smb2transport.c | 81 +++++++++++++++--------------
+ 7 files changed, 174 insertions(+), 148 deletions(-)
+
+diff --git a/fs/cifs/smb2glob.h b/fs/cifs/smb2glob.h
+--- a/fs/cifs/smb2glob.h
++++ b/fs/cifs/smb2glob.h
+@@ -71,4 +71,9 @@
+ */
+ #define SMB2_MAX_CREDITS_AVAILABLE 32000
+
++static inline struct smb2_sync_hdr *get_sync_hdr(void *buf)
++{
++ return &(((struct smb2_hdr *)buf)->sync_hdr);
++}
++
+ #endif /* _SMB2_GLOB_H */
+diff --git a/fs/cifs/smb2maperror.c b/fs/cifs/smb2maperror.c
+--- a/fs/cifs/smb2maperror.c
++++ b/fs/cifs/smb2maperror.c
+@@ -26,6 +26,7 @@
+ #include "smb2pdu.h"
+ #include "smb2proto.h"
+ #include "smb2status.h"
++#include "smb2glob.h"
+
+ struct status_to_posix_error {
+ __le32 smb2_status;
+@@ -2449,10 +2450,10 @@ smb2_print_status(__le32 status)
+ int
+ map_smb2_to_linux_error(char *buf, bool log_err)
+ {
+- struct smb2_hdr *hdr = (struct smb2_hdr *)buf;
++ struct smb2_sync_hdr *shdr = get_sync_hdr(buf);
+ unsigned int i;
+ int rc = -EIO;
+- __le32 smb2err = hdr->Status;
++ __le32 smb2err = shdr->Status;
+
+ if (smb2err == 0)
+ return 0;
+diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c
+--- a/fs/cifs/smb2misc.c
++++ b/fs/cifs/smb2misc.c
+@@ -28,31 +28,32 @@
+ #include "cifs_debug.h"
+ #include "cifs_unicode.h"
+ #include "smb2status.h"
++#include "smb2glob.h"
+
+ static int
+-check_smb2_hdr(struct smb2_hdr *hdr, __u64 mid)
++check_smb2_hdr(struct smb2_sync_hdr *shdr, __u64 mid)
+ {
+- __u64 wire_mid = le64_to_cpu(hdr->MessageId);
++ __u64 wire_mid = le64_to_cpu(shdr->MessageId);
+
+ /*
+ * Make sure that this really is an SMB, that it is a response,
+ * and that the message ids match.
+ */
+- if ((hdr->ProtocolId == SMB2_PROTO_NUMBER) &&
++ if ((shdr->ProtocolId == SMB2_PROTO_NUMBER) &&
+ (mid == wire_mid)) {
+- if (hdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR)
++ if (shdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR)
+ return 0;
+ else {
+ /* only one valid case where server sends us request */
+- if (hdr->Command == SMB2_OPLOCK_BREAK)
++ if (shdr->Command == SMB2_OPLOCK_BREAK)
+ return 0;
+ else
+ cifs_dbg(VFS, "Received Request not response\n");
+ }
+ } else { /* bad signature or mid */
+- if (hdr->ProtocolId != SMB2_PROTO_NUMBER)
++ if (shdr->ProtocolId != SMB2_PROTO_NUMBER)
+ cifs_dbg(VFS, "Bad protocol string signature header %x\n",
+- le32_to_cpu(hdr->ProtocolId));
++ le32_to_cpu(shdr->ProtocolId));
+ if (mid != wire_mid)
+ cifs_dbg(VFS, "Mids do not match: %llu and %llu\n",
+ mid, wire_mid);
+@@ -95,8 +96,9 @@ static const __le16 smb2_rsp_struct_size
+ int
+ smb2_check_message(char *buf, unsigned int length, struct TCP_Server_Info *srvr)
+ {
+- struct smb2_hdr *hdr = (struct smb2_hdr *)buf;
+- struct smb2_pdu *pdu = (struct smb2_pdu *)hdr;
++ struct smb2_pdu *pdu = (struct smb2_pdu *)buf;
++ struct smb2_hdr *hdr = &pdu->hdr;
++ struct smb2_sync_hdr *shdr = get_sync_hdr(buf);
+ __u64 mid;
+ __u32 len = get_rfc1002_length(buf);
+ __u32 clc_len; /* calculated length */
+@@ -111,7 +113,7 @@ smb2_check_message(char *buf, unsigned i
+ * ie Validate the wct via smb2_struct_sizes table above
+ */
+
+- if (hdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM) {
++ if (shdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM) {
+ struct smb2_transform_hdr *thdr =
+ (struct smb2_transform_hdr *)buf;
+ struct cifs_ses *ses = NULL;
+@@ -133,10 +135,10 @@ smb2_check_message(char *buf, unsigned i
+ }
+ }
+
+-
+- mid = le64_to_cpu(hdr->MessageId);
++ mid = le64_to_cpu(shdr->MessageId);
+ if (length < sizeof(struct smb2_pdu)) {
+- if ((length >= sizeof(struct smb2_hdr)) && (hdr->Status != 0)) {
++ if ((length >= sizeof(struct smb2_hdr))
++ && (shdr->Status != 0)) {
+ pdu->StructureSize2 = 0;
+ /*
+ * As with SMB/CIFS, on some error cases servers may
+@@ -154,29 +156,30 @@ smb2_check_message(char *buf, unsigned i
+ return 1;
+ }
+
+- if (check_smb2_hdr(hdr, mid))
++ if (check_smb2_hdr(shdr, mid))
+ return 1;
+
+- if (hdr->StructureSize != SMB2_HEADER_STRUCTURE_SIZE) {
++ if (shdr->StructureSize != SMB2_HEADER_STRUCTURE_SIZE) {
+ cifs_dbg(VFS, "Illegal structure size %u\n",
+- le16_to_cpu(hdr->StructureSize));
++ le16_to_cpu(shdr->StructureSize));
+ return 1;
+ }
+
+- command = le16_to_cpu(hdr->Command);
++ command = le16_to_cpu(shdr->Command);
+ if (command >= NUMBER_OF_SMB2_COMMANDS) {
+ cifs_dbg(VFS, "Illegal SMB2 command %d\n", command);
+ return 1;
+ }
+
+ if (smb2_rsp_struct_sizes[command] != pdu->StructureSize2) {
+- if (command != SMB2_OPLOCK_BREAK_HE && (hdr->Status == 0 ||
++ if (command != SMB2_OPLOCK_BREAK_HE && (shdr->Status == 0 ||
+ pdu->StructureSize2 != SMB2_ERROR_STRUCTURE_SIZE2)) {
+ /* error packets have 9 byte structure size */
+ cifs_dbg(VFS, "Illegal response size %u for command %d\n",
+ le16_to_cpu(pdu->StructureSize2), command);
+ return 1;
+- } else if (command == SMB2_OPLOCK_BREAK_HE && (hdr->Status == 0)
++ } else if (command == SMB2_OPLOCK_BREAK_HE
++ && (shdr->Status == 0)
+ && (le16_to_cpu(pdu->StructureSize2) != 44)
+ && (le16_to_cpu(pdu->StructureSize2) != 36)) {
+ /* special case for SMB2.1 lease break message */
+@@ -199,7 +202,7 @@ smb2_check_message(char *buf, unsigned i
+ clc_len, 4 + len, mid);
+ /* create failed on symlink */
+ if (command == SMB2_CREATE_HE &&
+- hdr->Status == STATUS_STOPPED_ON_SYMLINK)
++ shdr->Status == STATUS_STOPPED_ON_SYMLINK)
+ return 0;
+ /* Windows 7 server returns 24 bytes more */
+ if (clc_len + 20 == len && command == SMB2_OPLOCK_BREAK_HE)
+@@ -261,11 +264,12 @@ static const bool has_smb2_data_area[NUM
+ char *
+ smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr)
+ {
++ struct smb2_sync_hdr *shdr = get_sync_hdr(hdr);
+ *off = 0;
+ *len = 0;
+
+ /* error responses do not have data area */
+- if (hdr->Status && hdr->Status != STATUS_MORE_PROCESSING_REQUIRED &&
++ if (shdr->Status && shdr->Status != STATUS_MORE_PROCESSING_REQUIRED &&
+ (((struct smb2_err_rsp *)hdr)->StructureSize) ==
+ SMB2_ERROR_STRUCTURE_SIZE2)
+ return NULL;
+@@ -275,7 +279,7 @@ smb2_get_data_area_len(int *off, int *le
+ * of the data buffer offset and data buffer length for the particular
+ * command.
+ */
+- switch (hdr->Command) {
++ switch (shdr->Command) {
+ case SMB2_NEGOTIATE:
+ *off = le16_to_cpu(
+ ((struct smb2_negotiate_rsp *)hdr)->SecurityBufferOffset);
+@@ -346,7 +350,7 @@ smb2_get_data_area_len(int *off, int *le
+
+ /* return pointer to beginning of data area, ie offset from SMB start */
+ if ((*off != 0) && (*len != 0))
+- return (char *)(&hdr->ProtocolId) + *off;
++ return (char *)shdr + *off;
+ else
+ return NULL;
+ }
+@@ -358,12 +362,13 @@ smb2_get_data_area_len(int *off, int *le
+ unsigned int
+ smb2_calc_size(void *buf)
+ {
+- struct smb2_hdr *hdr = (struct smb2_hdr *)buf;
+- struct smb2_pdu *pdu = (struct smb2_pdu *)hdr;
++ struct smb2_pdu *pdu = (struct smb2_pdu *)buf;
++ struct smb2_hdr *hdr = &pdu->hdr;
++ struct smb2_sync_hdr *shdr = get_sync_hdr(hdr);
+ int offset; /* the offset from the beginning of SMB to data area */
+ int data_length; /* the length of the variable length data area */
+ /* Structure Size has already been checked to make sure it is 64 */
+- int len = 4 + le16_to_cpu(pdu->hdr.StructureSize);
++ int len = 4 + le16_to_cpu(shdr->StructureSize);
+
+ /*
+ * StructureSize2, ie length of fixed parameter area has already
+@@ -371,7 +376,7 @@ smb2_calc_size(void *buf)
+ */
+ len += le16_to_cpu(pdu->StructureSize2);
+
+- if (has_smb2_data_area[le16_to_cpu(hdr->Command)] == false)
++ if (has_smb2_data_area[le16_to_cpu(shdr->Command)] == false)
+ goto calc_size_exit;
+
+ smb2_get_data_area_len(&offset, &data_length, hdr);
+@@ -582,7 +587,7 @@ smb2_is_valid_oplock_break(char *buffer,
+
+ cifs_dbg(FYI, "Checking for oplock break\n");
+
+- if (rsp->hdr.Command != SMB2_OPLOCK_BREAK)
++ if (rsp->hdr.sync_hdr.Command != SMB2_OPLOCK_BREAK)
+ return false;
+
+ if (rsp->StructureSize !=
+diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
+--- a/fs/cifs/smb2ops.c
++++ b/fs/cifs/smb2ops.c
+@@ -114,7 +114,9 @@ smb2_get_credits_field(struct TCP_Server
+ static unsigned int
+ smb2_get_credits(struct mid_q_entry *mid)
+ {
+- return le16_to_cpu(((struct smb2_hdr *)mid->resp_buf)->CreditRequest);
++ struct smb2_sync_hdr *shdr = get_sync_hdr(mid->resp_buf);
++
++ return le16_to_cpu(shdr->CreditRequest);
+ }
+
+ static int
+@@ -179,10 +181,10 @@ static struct mid_q_entry *
+ smb2_find_mid(struct TCP_Server_Info *server, char *buf)
+ {
+ struct mid_q_entry *mid;
+- struct smb2_hdr *hdr = (struct smb2_hdr *)buf;
+- __u64 wire_mid = le64_to_cpu(hdr->MessageId);
++ struct smb2_sync_hdr *shdr = get_sync_hdr(buf);
++ __u64 wire_mid = le64_to_cpu(shdr->MessageId);
+
+- if (hdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM) {
++ if (shdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM) {
+ cifs_dbg(VFS, "encrypted frame parsing not supported yet");
+ return NULL;
+ }
+@@ -191,7 +193,7 @@ smb2_find_mid(struct TCP_Server_Info *se
+ list_for_each_entry(mid, &server->pending_mid_q, qhead) {
+ if ((mid->mid == wire_mid) &&
+ (mid->mid_state == MID_REQUEST_SUBMITTED) &&
+- (mid->command == hdr->Command)) {
++ (mid->command == shdr->Command)) {
+ spin_unlock(&GlobalMid_Lock);
+ return mid;
+ }
+@@ -204,12 +206,12 @@ static void
+ smb2_dump_detail(void *buf)
+ {
+ #ifdef CONFIG_CIFS_DEBUG2
+- struct smb2_hdr *smb = (struct smb2_hdr *)buf;
++ struct smb2_sync_hdr *shdr = get_sync_hdr(buf);
+
+ cifs_dbg(VFS, "Cmd: %d Err: 0x%x Flags: 0x%x Mid: %llu Pid: %d\n",
+- smb->Command, smb->Status, smb->Flags, smb->MessageId,
+- smb->ProcessId);
+- cifs_dbg(VFS, "smb buf %p len %u\n", smb, smb2_calc_size(smb));
++ shdr->Command, shdr->Status, shdr->Flags, shdr->MessageId,
++ shdr->ProcessId);
++ cifs_dbg(VFS, "smb buf %p len %u\n", buf, smb2_calc_size(buf));
+ #endif
+ }
+
+@@ -953,14 +955,14 @@ smb2_close_dir(const unsigned int xid, s
+ static bool
+ smb2_is_status_pending(char *buf, struct TCP_Server_Info *server, int length)
+ {
+- struct smb2_hdr *hdr = (struct smb2_hdr *)buf;
++ struct smb2_sync_hdr *shdr = get_sync_hdr(buf);
+
+- if (hdr->Status != STATUS_PENDING)
++ if (shdr->Status != STATUS_PENDING)
+ return false;
+
+ if (!length) {
+ spin_lock(&server->req_lock);
+- server->credits += le16_to_cpu(hdr->CreditRequest);
++ server->credits += le16_to_cpu(shdr->CreditRequest);
+ spin_unlock(&server->req_lock);
+ wake_up(&server->request_q);
+ }
+diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
+--- a/fs/cifs/smb2pdu.c
++++ b/fs/cifs/smb2pdu.c
+@@ -83,6 +83,7 @@ smb2_hdr_assemble(struct smb2_hdr *hdr,
+ const struct cifs_tcon *tcon)
+ {
+ struct smb2_pdu *pdu = (struct smb2_pdu *)hdr;
++ struct smb2_sync_hdr *shdr = get_sync_hdr(hdr);
+ char *temp = (char *)hdr;
+ /* lookup word count ie StructureSize from table */
+ __u16 parmsize = smb2_req_struct_sizes[le16_to_cpu(smb2_cmd)];
+@@ -94,28 +95,28 @@ smb2_hdr_assemble(struct smb2_hdr *hdr,
+ memset(temp, 0, 256);
+
+ /* Note this is only network field converted to big endian */
+- hdr->smb2_buf_length = cpu_to_be32(parmsize + sizeof(struct smb2_hdr)
+- - 4 /* RFC 1001 length field itself not counted */);
++ hdr->smb2_buf_length =
++ cpu_to_be32(parmsize + sizeof(struct smb2_sync_hdr));
+
+- hdr->ProtocolId = SMB2_PROTO_NUMBER;
+- hdr->StructureSize = cpu_to_le16(64);
+- hdr->Command = smb2_cmd;
++ shdr->ProtocolId = SMB2_PROTO_NUMBER;
++ shdr->StructureSize = cpu_to_le16(64);
++ shdr->Command = smb2_cmd;
+ if (tcon && tcon->ses && tcon->ses->server) {
+ struct TCP_Server_Info *server = tcon->ses->server;
+
+ spin_lock(&server->req_lock);
+ /* Request up to 2 credits but don't go over the limit. */
+ if (server->credits >= SMB2_MAX_CREDITS_AVAILABLE)
+- hdr->CreditRequest = cpu_to_le16(0);
++ shdr->CreditRequest = cpu_to_le16(0);
+ else
+- hdr->CreditRequest = cpu_to_le16(
++ shdr->CreditRequest = cpu_to_le16(
+ min_t(int, SMB2_MAX_CREDITS_AVAILABLE -
+ server->credits, 2));
+ spin_unlock(&server->req_lock);
+ } else {
+- hdr->CreditRequest = cpu_to_le16(2);
++ shdr->CreditRequest = cpu_to_le16(2);
+ }
+- hdr->ProcessId = cpu_to_le32((__u16)current->tgid);
++ shdr->ProcessId = cpu_to_le32((__u16)current->tgid);
+
+ if (!tcon)
+ goto out;
+@@ -124,13 +125,13 @@ smb2_hdr_assemble(struct smb2_hdr *hdr,
+ /* See sections 2.2.4 and 3.2.4.1.5 of MS-SMB2 */
+ if ((tcon->ses) && (tcon->ses->server) &&
+ (tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU))
+- hdr->CreditCharge = cpu_to_le16(1);
++ shdr->CreditCharge = cpu_to_le16(1);
+ /* else CreditCharge MBZ */
+
+- hdr->TreeId = tcon->tid;
++ shdr->TreeId = tcon->tid;
+ /* Uid is not converted */
+ if (tcon->ses)
+- hdr->SessionId = tcon->ses->Suid;
++ shdr->SessionId = tcon->ses->Suid;
+
+ /*
+ * If we would set SMB2_FLAGS_DFS_OPERATIONS on open we also would have
+@@ -143,10 +144,10 @@ smb2_hdr_assemble(struct smb2_hdr *hdr,
+ * but it is safer to net set it for now.
+ */
+ /* if (tcon->share_flags & SHI1005_FLAGS_DFS)
+- hdr->Flags |= SMB2_FLAGS_DFS_OPERATIONS; */
++ shdr->Flags |= SMB2_FLAGS_DFS_OPERATIONS; */
+
+ if (tcon->ses && tcon->ses->server && tcon->ses->server->sign)
+- hdr->Flags |= SMB2_FLAGS_SIGNED;
++ shdr->Flags |= SMB2_FLAGS_SIGNED;
+ out:
+ pdu->StructureSize2 = cpu_to_le16(parmsize);
+ return;
+@@ -408,7 +409,7 @@ SMB2_negotiate(const unsigned int xid, s
+ if (rc)
+ return rc;
+
+- req->hdr.SessionId = 0;
++ req->hdr.sync_hdr.SessionId = 0;
+
+ req->Dialects[0] = cpu_to_le16(ses->server->vals->protocol_id);
+
+@@ -641,14 +642,15 @@ ssetup_ntlmssp_authenticate:
+ if (rc)
+ return rc;
+
+- req->hdr.SessionId = 0; /* First session, not a reauthenticate */
++ /* First session, not a reauthenticate */
++ req->hdr.sync_hdr.SessionId = 0;
+
+ /* if reconnect, we need to send previous sess id, otherwise it is 0 */
+ req->PreviousSessionId = previous_session;
+
+ req->Flags = 0; /* MBZ */
+ /* to enable echos and oplocks */
+- req->hdr.CreditRequest = cpu_to_le16(3);
++ req->hdr.sync_hdr.CreditRequest = cpu_to_le16(3);
+
+ /* only one of SMB2 signing flags may be set in SMB2 request */
+ if (server->sign)
+@@ -731,7 +733,7 @@ ssetup_ntlmssp_authenticate:
+ iov[1].iov_base = security_blob;
+ iov[1].iov_len = blob_length;
+ } else if (phase == NtLmAuthenticate) {
+- req->hdr.SessionId = ses->Suid;
++ req->hdr.sync_hdr.SessionId = ses->Suid;
+ rc = build_ntlmssp_auth_blob(&ntlmssp_blob, &blob_length, ses,
+ nls_cp);
+ if (rc) {
+@@ -774,9 +776,9 @@ ssetup_ntlmssp_authenticate:
+
+ kfree(security_blob);
+ rsp = (struct smb2_sess_setup_rsp *)iov[0].iov_base;
+- ses->Suid = rsp->hdr.SessionId;
++ ses->Suid = rsp->hdr.sync_hdr.SessionId;
+ if (resp_buftype != CIFS_NO_BUFFER &&
+- rsp->hdr.Status == STATUS_MORE_PROCESSING_REQUIRED) {
++ rsp->hdr.sync_hdr.Status == STATUS_MORE_PROCESSING_REQUIRED) {
+ if (phase != NtLmNegotiate) {
+ cifs_dbg(VFS, "Unexpected more processing error\n");
+ goto ssetup_exit;
+@@ -880,9 +882,9 @@ SMB2_logoff(const unsigned int xid, stru
+ return rc;
+
+ /* since no tcon, smb2_init can not do this, so do here */
+- req->hdr.SessionId = ses->Suid;
++ req->hdr.sync_hdr.SessionId = ses->Suid;
+ if (server->sign)
+- req->hdr.Flags |= SMB2_FLAGS_SIGNED;
++ req->hdr.sync_hdr.Flags |= SMB2_FLAGS_SIGNED;
+
+ rc = SendReceiveNoRsp(xid, ses, (char *) &req->hdr, 0);
+ /*
+@@ -957,7 +959,7 @@ SMB2_tcon(const unsigned int xid, struct
+
+ if (tcon == NULL) {
+ /* since no tcon, smb2_init can not do this, so do here */
+- req->hdr.SessionId = ses->Suid;
++ req->hdr.sync_hdr.SessionId = ses->Suid;
+ /* if (ses->server->sec_mode & SECMODE_SIGN_REQUIRED)
+ req->hdr.Flags |= SMB2_FLAGS_SIGNED; */
+ }
+@@ -987,7 +989,7 @@ SMB2_tcon(const unsigned int xid, struct
+ }
+
+ if (tcon == NULL) {
+- ses->ipc_tid = rsp->hdr.TreeId;
++ ses->ipc_tid = rsp->hdr.sync_hdr.TreeId;
+ goto tcon_exit;
+ }
+
+@@ -1010,7 +1012,7 @@ SMB2_tcon(const unsigned int xid, struct
+ tcon->maximal_access = le32_to_cpu(rsp->MaximalAccess);
+ tcon->tidStatus = CifsGood;
+ tcon->need_reconnect = false;
+- tcon->tid = rsp->hdr.TreeId;
++ tcon->tid = rsp->hdr.sync_hdr.TreeId;
+ strlcpy(tcon->treeName, tree, sizeof(tcon->treeName));
+
+ if ((rsp->Capabilities & SMB2_SHARE_CAP_DFS) &&
+@@ -1027,7 +1029,7 @@ tcon_exit:
+ return rc;
+
+ tcon_error_exit:
+- if (rsp->hdr.Status == STATUS_BAD_NETWORK_NAME) {
++ if (rsp->hdr.sync_hdr.Status == STATUS_BAD_NETWORK_NAME) {
+ cifs_dbg(VFS, "BAD_NETWORK_NAME: %s\n", tree);
+ if (tcon)
+ tcon->bad_network_name = true;
+@@ -1463,6 +1465,7 @@ SMB2_ioctl(const unsigned int xid, struc
+ {
+ struct smb2_ioctl_req *req;
+ struct smb2_ioctl_rsp *rsp;
++ struct smb2_sync_hdr *shdr;
+ struct TCP_Server_Info *server;
+ struct cifs_ses *ses;
+ struct kvec iov[2];
+@@ -1587,9 +1590,8 @@ SMB2_ioctl(const unsigned int xid, struc
+ goto ioctl_exit;
+ }
+
+- memcpy(*out_data,
+- (char *)&rsp->hdr.ProtocolId + le32_to_cpu(rsp->OutputOffset),
+- *plen);
++ shdr = get_sync_hdr(rsp);
++ memcpy(*out_data, (char *)shdr + le32_to_cpu(rsp->OutputOffset), *plen);
+ ioctl_exit:
+ free_rsp_buf(resp_buftype, rsp);
+ return rc;
+@@ -1808,11 +1810,11 @@ static void
+ smb2_echo_callback(struct mid_q_entry *mid)
+ {
+ struct TCP_Server_Info *server = mid->callback_data;
+- struct smb2_echo_rsp *smb2 = (struct smb2_echo_rsp *)mid->resp_buf;
++ struct smb2_echo_rsp *rsp = (struct smb2_echo_rsp *)mid->resp_buf;
+ unsigned int credits_received = 1;
+
+ if (mid->mid_state == MID_RESPONSE_RECEIVED)
+- credits_received = le16_to_cpu(smb2->hdr.CreditRequest);
++ credits_received = le16_to_cpu(rsp->hdr.sync_hdr.CreditRequest);
+
+ mutex_lock(&server->srv_mutex);
+ DeleteMidQEntry(mid);
+@@ -1889,7 +1891,7 @@ SMB2_echo(struct TCP_Server_Info *server
+ if (rc)
+ return rc;
+
+- req->hdr.CreditRequest = cpu_to_le16(1);
++ req->hdr.sync_hdr.CreditRequest = cpu_to_le16(1);
+
+ iov.iov_base = (char *)req;
+ /* 4 for rfc1002 length field */
+@@ -1952,6 +1954,7 @@ smb2_new_read_req(struct kvec *iov, stru
+ {
+ int rc = -EACCES;
+ struct smb2_read_req *req = NULL;
++ struct smb2_sync_hdr *shdr;
+
+ rc = small_smb2_init(SMB2_READ, io_parms->tcon, (void **) &req);
+ if (rc)
+@@ -1959,7 +1962,8 @@ smb2_new_read_req(struct kvec *iov, stru
+ if (io_parms->tcon->ses->server == NULL)
+ return -ECONNABORTED;
+
+- req->hdr.ProcessId = cpu_to_le32(io_parms->pid);
++ shdr = get_sync_hdr(req);
++ shdr->ProcessId = cpu_to_le32(io_parms->pid);
+
+ req->PersistentFileId = io_parms->persistent_fid;
+ req->VolatileFileId = io_parms->volatile_fid;
+@@ -1973,18 +1977,18 @@ smb2_new_read_req(struct kvec *iov, stru
+ if (request_type & CHAINED_REQUEST) {
+ if (!(request_type & END_OF_CHAIN)) {
+ /* 4 for rfc1002 length field */
+- req->hdr.NextCommand =
++ shdr->NextCommand =
+ cpu_to_le32(get_rfc1002_length(req) + 4);
+ } else /* END_OF_CHAIN */
+- req->hdr.NextCommand = 0;
++ shdr->NextCommand = 0;
+ if (request_type & RELATED_REQUEST) {
+- req->hdr.Flags |= SMB2_FLAGS_RELATED_OPERATIONS;
++ shdr->Flags |= SMB2_FLAGS_RELATED_OPERATIONS;
+ /*
+ * Related requests use info from previous read request
+ * in chain.
+ */
+- req->hdr.SessionId = 0xFFFFFFFF;
+- req->hdr.TreeId = 0xFFFFFFFF;
++ shdr->SessionId = 0xFFFFFFFF;
++ shdr->TreeId = 0xFFFFFFFF;
+ req->PersistentFileId = 0xFFFFFFFF;
+ req->VolatileFileId = 0xFFFFFFFF;
+ }
+@@ -2006,7 +2010,7 @@ smb2_readv_callback(struct mid_q_entry *
+ 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 smb2_hdr *buf = (struct smb2_hdr *)rdata->iov.iov_base;
++ struct smb2_sync_hdr *shdr = get_sync_hdr(rdata->iov.iov_base);
+ unsigned int credits_received = 1;
+ struct smb_rqst rqst = { .rq_iov = &rdata->iov,
+ .rq_nvec = 1,
+@@ -2021,7 +2025,7 @@ smb2_readv_callback(struct mid_q_entry *
+
+ switch (mid->mid_state) {
+ case MID_RESPONSE_RECEIVED:
+- credits_received = le16_to_cpu(buf->CreditRequest);
++ credits_received = le16_to_cpu(shdr->CreditRequest);
+ /* result already set, check signature */
+ if (server->sign) {
+ int rc;
+@@ -2065,7 +2069,8 @@ int
+ smb2_async_readv(struct cifs_readdata *rdata)
+ {
+ int rc, flags = 0;
+- struct smb2_hdr *buf;
++ char *buf;
++ struct smb2_sync_hdr *shdr;
+ struct cifs_io_parms io_parms;
+ struct smb_rqst rqst = { .rq_iov = &rdata->iov,
+ .rq_nvec = 1 };
+@@ -2096,17 +2101,18 @@ smb2_async_readv(struct cifs_readdata *r
+ return rc;
+ }
+
+- buf = (struct smb2_hdr *)rdata->iov.iov_base;
++ buf = rdata->iov.iov_base;
++ shdr = get_sync_hdr(buf);
+ /* 4 for rfc1002 length field */
+ rdata->iov.iov_len = get_rfc1002_length(rdata->iov.iov_base) + 4;
+
+ if (rdata->credits) {
+- buf->CreditCharge = cpu_to_le16(DIV_ROUND_UP(rdata->bytes,
++ shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(rdata->bytes,
+ SMB2_MAX_BUFFER_SIZE));
+- buf->CreditRequest = buf->CreditCharge;
++ shdr->CreditRequest = shdr->CreditCharge;
+ spin_lock(&server->req_lock);
+ server->credits += rdata->credits -
+- le16_to_cpu(buf->CreditCharge);
++ le16_to_cpu(shdr->CreditCharge);
+ spin_unlock(&server->req_lock);
+ wake_up(&server->request_q);
+ flags = CIFS_HAS_CREDITS;
+@@ -2131,6 +2137,7 @@ SMB2_read(const unsigned int xid, struct
+ {
+ int resp_buftype, rc = -EACCES;
+ struct smb2_read_rsp *rsp = NULL;
++ struct smb2_sync_hdr *shdr;
+ struct kvec iov[1];
+
+ *nbytes = 0;
+@@ -2142,8 +2149,9 @@ SMB2_read(const unsigned int xid, struct
+ &resp_buftype, CIFS_LOG_ERROR);
+
+ rsp = (struct smb2_read_rsp *)iov[0].iov_base;
++ shdr = get_sync_hdr(rsp);
+
+- if (rsp->hdr.Status == STATUS_END_OF_FILE) {
++ if (shdr->Status == STATUS_END_OF_FILE) {
+ free_rsp_buf(resp_buftype, iov[0].iov_base);
+ return 0;
+ }
+@@ -2163,8 +2171,7 @@ SMB2_read(const unsigned int xid, struct
+ }
+
+ if (*buf) {
+- memcpy(*buf, (char *)&rsp->hdr.ProtocolId + rsp->DataOffset,
+- *nbytes);
++ memcpy(*buf, (char *)shdr + rsp->DataOffset, *nbytes);
+ free_rsp_buf(resp_buftype, iov[0].iov_base);
+ } else if (resp_buftype != CIFS_NO_BUFFER) {
+ *buf = iov[0].iov_base;
+@@ -2192,7 +2199,7 @@ smb2_writev_callback(struct mid_q_entry
+
+ switch (mid->mid_state) {
+ case MID_RESPONSE_RECEIVED:
+- credits_received = le16_to_cpu(rsp->hdr.CreditRequest);
++ credits_received = le16_to_cpu(rsp->hdr.sync_hdr.CreditRequest);
+ wdata->result = smb2_check_receive(mid, tcon->ses->server, 0);
+ if (wdata->result != 0)
+ break;
+@@ -2238,6 +2245,7 @@ smb2_async_writev(struct cifs_writedata
+ {
+ int rc = -EACCES, flags = 0;
+ struct smb2_write_req *req = NULL;
++ struct smb2_sync_hdr *shdr;
+ struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink);
+ struct TCP_Server_Info *server = tcon->ses->server;
+ struct kvec iov;
+@@ -2256,7 +2264,8 @@ smb2_async_writev(struct cifs_writedata
+ goto async_writev_out;
+ }
+
+- req->hdr.ProcessId = cpu_to_le32(wdata->cfile->pid);
++ shdr = get_sync_hdr(req);
++ shdr->ProcessId = cpu_to_le32(wdata->cfile->pid);
+
+ req->PersistentFileId = wdata->cfile->fid.persistent_fid;
+ req->VolatileFileId = wdata->cfile->fid.volatile_fid;
+@@ -2288,12 +2297,12 @@ smb2_async_writev(struct cifs_writedata
+ inc_rfc1001_len(&req->hdr, wdata->bytes - 1 /* Buffer */);
+
+ if (wdata->credits) {
+- req->hdr.CreditCharge = cpu_to_le16(DIV_ROUND_UP(wdata->bytes,
++ shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(wdata->bytes,
+ SMB2_MAX_BUFFER_SIZE));
+- req->hdr.CreditRequest = req->hdr.CreditCharge;
++ shdr->CreditRequest = shdr->CreditCharge;
+ spin_lock(&server->req_lock);
+ server->credits += wdata->credits -
+- le16_to_cpu(req->hdr.CreditCharge);
++ le16_to_cpu(shdr->CreditCharge);
+ spin_unlock(&server->req_lock);
+ wake_up(&server->request_q);
+ flags = CIFS_HAS_CREDITS;
+@@ -2339,7 +2348,7 @@ SMB2_write(const unsigned int xid, struc
+ if (io_parms->tcon->ses->server == NULL)
+ return -ECONNABORTED;
+
+- req->hdr.ProcessId = cpu_to_le32(io_parms->pid);
++ req->hdr.sync_hdr.ProcessId = cpu_to_le32(io_parms->pid);
+
+ req->PersistentFileId = io_parms->persistent_fid;
+ req->VolatileFileId = io_parms->volatile_fid;
+@@ -2493,7 +2502,8 @@ SMB2_query_directory(const unsigned int
+ rsp = (struct smb2_query_directory_rsp *)iov[0].iov_base;
+
+ if (rc) {
+- if (rc == -ENODATA && rsp->hdr.Status == STATUS_NO_MORE_FILES) {
++ if (rc == -ENODATA &&
++ rsp->hdr.sync_hdr.Status == STATUS_NO_MORE_FILES) {
+ srch_inf->endOfSearch = true;
+ rc = 0;
+ }
+@@ -2573,7 +2583,7 @@ send_set_info(const unsigned int xid, st
+ return rc;
+ }
+
+- req->hdr.ProcessId = cpu_to_le32(pid);
++ req->hdr.sync_hdr.ProcessId = cpu_to_le32(pid);
+
+ req->InfoType = SMB2_O_INFO_FILE;
+ req->FileInfoClass = info_class;
+@@ -2739,7 +2749,7 @@ SMB2_oplock_break(const unsigned int xid
+ req->VolatileFid = volatile_fid;
+ req->PersistentFid = persistent_fid;
+ req->OplockLevel = oplock_level;
+- req->hdr.CreditRequest = cpu_to_le16(1);
++ req->hdr.sync_hdr.CreditRequest = cpu_to_le16(1);
+
+ rc = SendReceiveNoRsp(xid, tcon->ses, (char *) req, CIFS_OBREAK_OP);
+ /* SMB2 buffer freed by function above */
+@@ -2913,7 +2923,7 @@ smb2_lockv(const unsigned int xid, struc
+ if (rc)
+ return rc;
+
+- req->hdr.ProcessId = cpu_to_le32(pid);
++ req->hdr.sync_hdr.ProcessId = cpu_to_le32(pid);
+ req->LockCount = cpu_to_le16(num_lock);
+
+ req->PersistentFileId = persist_fid;
+@@ -2968,7 +2978,7 @@ SMB2_lease_break(const unsigned int xid,
+ if (rc)
+ return rc;
+
+- req->hdr.CreditRequest = cpu_to_le16(1);
++ req->hdr.sync_hdr.CreditRequest = cpu_to_le16(1);
+ req->StructureSize = cpu_to_le16(36);
+ inc_rfc1001_len(req, 12);
+
+diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h
+--- a/fs/cifs/smb2pdu.h
++++ b/fs/cifs/smb2pdu.h
+@@ -99,10 +99,7 @@
+
+ #define SMB2_HEADER_STRUCTURE_SIZE cpu_to_le16(64)
+
+-struct smb2_hdr {
+- __be32 smb2_buf_length; /* big endian on wire */
+- /* length is only two or three bytes - with
+- one or two byte type preceding it that MBZ */
++struct smb2_sync_hdr {
+ __le32 ProtocolId; /* 0xFE 'S' 'M' 'B' */
+ __le16 StructureSize; /* 64 */
+ __le16 CreditCharge; /* MBZ */
+@@ -118,6 +115,13 @@ struct smb2_hdr {
+ __u8 Signature[16];
+ } __packed;
+
++struct smb2_hdr {
++ __be32 smb2_buf_length; /* big endian on wire */
++ /* length is only two or three bytes - with */
++ /* one or two byte type preceding it that MBZ */
++ struct smb2_sync_hdr sync_hdr;
++} __packed;
++
+ struct smb2_pdu {
+ struct smb2_hdr hdr;
+ __le16 StructureSize2; /* size of wct area (varies, request specific) */
+diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c
+--- a/fs/cifs/smb2transport.c
++++ b/fs/cifs/smb2transport.c
+@@ -115,13 +115,13 @@ smb3_crypto_shash_allocate(struct TCP_Se
+ }
+
+ static struct cifs_ses *
+-smb2_find_smb_ses(struct smb2_hdr *smb2hdr, struct TCP_Server_Info *server)
++smb2_find_smb_ses(struct smb2_sync_hdr *shdr, struct TCP_Server_Info *server)
+ {
+ struct cifs_ses *ses;
+
+ spin_lock(&cifs_tcp_ses_lock);
+ list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
+- if (ses->Suid != smb2hdr->SessionId)
++ if (ses->Suid != shdr->SessionId)
+ continue;
+ spin_unlock(&cifs_tcp_ses_lock);
+ return ses;
+@@ -131,7 +131,6 @@ smb2_find_smb_ses(struct smb2_hdr *smb2h
+ return NULL;
+ }
+
+-
+ int
+ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
+ {
+@@ -140,17 +139,17 @@ smb2_calc_signature(struct smb_rqst *rqs
+ unsigned char *sigptr = smb2_signature;
+ struct kvec *iov = rqst->rq_iov;
+ int n_vec = rqst->rq_nvec;
+- struct smb2_hdr *smb2_pdu = (struct smb2_hdr *)iov[0].iov_base;
++ struct smb2_sync_hdr *shdr = get_sync_hdr(iov[0].iov_base);
+ struct cifs_ses *ses;
+
+- ses = smb2_find_smb_ses(smb2_pdu, server);
++ ses = smb2_find_smb_ses(shdr, server);
+ if (!ses) {
+ cifs_dbg(VFS, "%s: Could not find session\n", __func__);
+ return 0;
+ }
+
+ memset(smb2_signature, 0x0, SMB2_HMACSHA256_SIZE);
+- memset(smb2_pdu->Signature, 0x0, SMB2_SIGNATURE_SIZE);
++ memset(shdr->Signature, 0x0, SMB2_SIGNATURE_SIZE);
+
+ rc = smb2_crypto_shash_allocate(server);
+ if (rc) {
+@@ -217,7 +216,7 @@ smb2_calc_signature(struct smb_rqst *rqs
+ if (rc)
+ cifs_dbg(VFS, "%s: Could not generate sha256 hash\n", __func__);
+
+- memcpy(smb2_pdu->Signature, sigptr, SMB2_SIGNATURE_SIZE);
++ memcpy(shdr->Signature, sigptr, SMB2_SIGNATURE_SIZE);
+
+ return rc;
+ }
+@@ -401,17 +400,17 @@ smb3_calc_signature(struct smb_rqst *rqs
+ unsigned char *sigptr = smb3_signature;
+ struct kvec *iov = rqst->rq_iov;
+ int n_vec = rqst->rq_nvec;
+- struct smb2_hdr *smb2_pdu = (struct smb2_hdr *)iov[0].iov_base;
++ struct smb2_sync_hdr *shdr = get_sync_hdr(iov[0].iov_base);
+ struct cifs_ses *ses;
+
+- ses = smb2_find_smb_ses(smb2_pdu, server);
++ ses = smb2_find_smb_ses(shdr, server);
+ if (!ses) {
+ cifs_dbg(VFS, "%s: Could not find session\n", __func__);
+ return 0;
+ }
+
+ memset(smb3_signature, 0x0, SMB2_CMACAES_SIZE);
+- memset(smb2_pdu->Signature, 0x0, SMB2_SIGNATURE_SIZE);
++ memset(shdr->Signature, 0x0, SMB2_SIGNATURE_SIZE);
+
+ rc = crypto_shash_setkey(server->secmech.cmacaes,
+ ses->smb3signingkey, SMB2_CMACAES_SIZE);
+@@ -478,7 +477,7 @@ smb3_calc_signature(struct smb_rqst *rqs
+ if (rc)
+ cifs_dbg(VFS, "%s: Could not generate cmac aes\n", __func__);
+
+- memcpy(smb2_pdu->Signature, sigptr, SMB2_SIGNATURE_SIZE);
++ memcpy(shdr->Signature, sigptr, SMB2_SIGNATURE_SIZE);
+
+ return rc;
+ }
+@@ -488,14 +487,14 @@ static int
+ smb2_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server)
+ {
+ int rc = 0;
+- struct smb2_hdr *smb2_pdu = rqst->rq_iov[0].iov_base;
++ struct smb2_sync_hdr *shdr = get_sync_hdr(rqst->rq_iov[0].iov_base);
+
+- if (!(smb2_pdu->Flags & SMB2_FLAGS_SIGNED) ||
++ if (!(shdr->Flags & SMB2_FLAGS_SIGNED) ||
+ server->tcpStatus == CifsNeedNegotiate)
+ return rc;
+
+ if (!server->session_estab) {
+- strncpy(smb2_pdu->Signature, "BSRSPYL", 8);
++ strncpy(shdr->Signature, "BSRSPYL", 8);
+ return rc;
+ }
+
+@@ -509,11 +508,11 @@ smb2_verify_signature(struct smb_rqst *r
+ {
+ unsigned int rc;
+ char server_response_sig[16];
+- struct smb2_hdr *smb2_pdu = (struct smb2_hdr *)rqst->rq_iov[0].iov_base;
++ struct smb2_sync_hdr *shdr = get_sync_hdr(rqst->rq_iov[0].iov_base);
+
+- if ((smb2_pdu->Command == SMB2_NEGOTIATE) ||
+- (smb2_pdu->Command == SMB2_SESSION_SETUP) ||
+- (smb2_pdu->Command == SMB2_OPLOCK_BREAK) ||
++ if ((shdr->Command == SMB2_NEGOTIATE) ||
++ (shdr->Command == SMB2_SESSION_SETUP) ||
++ (shdr->Command == SMB2_OPLOCK_BREAK) ||
+ (!server->session_estab))
+ return 0;
+
+@@ -523,17 +522,17 @@ smb2_verify_signature(struct smb_rqst *r
+ */
+
+ /* Do not need to verify session setups with signature "BSRSPYL " */
+- if (memcmp(smb2_pdu->Signature, "BSRSPYL ", 8) == 0)
++ if (memcmp(shdr->Signature, "BSRSPYL ", 8) == 0)
+ cifs_dbg(FYI, "dummy signature received for smb command 0x%x\n",
+- smb2_pdu->Command);
++ shdr->Command);
+
+ /*
+ * Save off the origiginal signature so we can modify the smb and check
+ * our calculated signature against what the server sent.
+ */
+- memcpy(server_response_sig, smb2_pdu->Signature, SMB2_SIGNATURE_SIZE);
++ memcpy(server_response_sig, shdr->Signature, SMB2_SIGNATURE_SIZE);
+
+- memset(smb2_pdu->Signature, 0, SMB2_SIGNATURE_SIZE);
++ memset(shdr->Signature, 0, SMB2_SIGNATURE_SIZE);
+
+ mutex_lock(&server->srv_mutex);
+ rc = server->ops->calc_signature(rqst, server);
+@@ -542,8 +541,7 @@ smb2_verify_signature(struct smb_rqst *r
+ if (rc)
+ return rc;
+
+- if (memcmp(server_response_sig, smb2_pdu->Signature,
+- SMB2_SIGNATURE_SIZE))
++ if (memcmp(server_response_sig, shdr->Signature, SMB2_SIGNATURE_SIZE))
+ return -EACCES;
+ else
+ return 0;
+@@ -554,18 +552,19 @@ smb2_verify_signature(struct smb_rqst *r
+ * and when srv_mutex is held.
+ */
+ static inline void
+-smb2_seq_num_into_buf(struct TCP_Server_Info *server, struct smb2_hdr *hdr)
++smb2_seq_num_into_buf(struct TCP_Server_Info *server,
++ struct smb2_sync_hdr *shdr)
+ {
+- unsigned int i, num = le16_to_cpu(hdr->CreditCharge);
++ unsigned int i, num = le16_to_cpu(shdr->CreditCharge);
+
+- hdr->MessageId = get_next_mid64(server);
++ shdr->MessageId = get_next_mid64(server);
+ /* skip message numbers according to CreditCharge field */
+ for (i = 1; i < num; i++)
+ get_next_mid(server);
+ }
+
+ static struct mid_q_entry *
+-smb2_mid_entry_alloc(const struct smb2_hdr *smb_buffer,
++smb2_mid_entry_alloc(const struct smb2_sync_hdr *shdr,
+ struct TCP_Server_Info *server)
+ {
+ struct mid_q_entry *temp;
+@@ -580,9 +579,9 @@ smb2_mid_entry_alloc(const struct smb2_h
+ return temp;
+ else {
+ memset(temp, 0, sizeof(struct mid_q_entry));
+- temp->mid = le64_to_cpu(smb_buffer->MessageId);
++ temp->mid = le64_to_cpu(shdr->MessageId);
+ temp->pid = current->pid;
+- temp->command = smb_buffer->Command; /* Always LE */
++ temp->command = shdr->Command; /* Always LE */
+ temp->when_alloc = jiffies;
+ temp->server = server;
+
+@@ -600,7 +599,7 @@ smb2_mid_entry_alloc(const struct smb2_h
+ }
+
+ static int
+-smb2_get_mid_entry(struct cifs_ses *ses, struct smb2_hdr *buf,
++smb2_get_mid_entry(struct cifs_ses *ses, struct smb2_sync_hdr *shdr,
+ struct mid_q_entry **mid)
+ {
+ if (ses->server->tcpStatus == CifsExiting)
+@@ -612,19 +611,19 @@ smb2_get_mid_entry(struct cifs_ses *ses,
+ }
+
+ if (ses->status == CifsNew) {
+- if ((buf->Command != SMB2_SESSION_SETUP) &&
+- (buf->Command != SMB2_NEGOTIATE))
++ if ((shdr->Command != SMB2_SESSION_SETUP) &&
++ (shdr->Command != SMB2_NEGOTIATE))
+ return -EAGAIN;
+ /* else ok - we are setting up session */
+ }
+
+ if (ses->status == CifsExiting) {
+- if (buf->Command != SMB2_LOGOFF)
++ if (shdr->Command != SMB2_LOGOFF)
+ return -EAGAIN;
+ /* else ok - we are shutting down the session */
+ }
+
+- *mid = smb2_mid_entry_alloc(buf, ses->server);
++ *mid = smb2_mid_entry_alloc(shdr, ses->server);
+ if (*mid == NULL)
+ return -ENOMEM;
+ spin_lock(&GlobalMid_Lock);
+@@ -663,12 +662,12 @@ struct mid_q_entry *
+ smb2_setup_request(struct cifs_ses *ses, struct smb_rqst *rqst)
+ {
+ int rc;
+- struct smb2_hdr *hdr = (struct smb2_hdr *)rqst->rq_iov[0].iov_base;
++ struct smb2_sync_hdr *shdr = get_sync_hdr(rqst->rq_iov[0].iov_base);
+ struct mid_q_entry *mid;
+
+- smb2_seq_num_into_buf(ses->server, hdr);
++ smb2_seq_num_into_buf(ses->server, shdr);
+
+- rc = smb2_get_mid_entry(ses, hdr, &mid);
++ rc = smb2_get_mid_entry(ses, shdr, &mid);
+ if (rc)
+ return ERR_PTR(rc);
+ rc = smb2_sign_rqst(rqst, ses->server);
+@@ -683,12 +682,12 @@ struct mid_q_entry *
+ smb2_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst)
+ {
+ int rc;
+- struct smb2_hdr *hdr = (struct smb2_hdr *)rqst->rq_iov[0].iov_base;
++ struct smb2_sync_hdr *shdr = get_sync_hdr(rqst->rq_iov[0].iov_base);
+ struct mid_q_entry *mid;
+
+- smb2_seq_num_into_buf(server, hdr);
++ smb2_seq_num_into_buf(server, shdr);
+
+- mid = smb2_mid_entry_alloc(hdr, server);
++ mid = smb2_mid_entry_alloc(shdr, server);
+ if (mid == NULL)
+ return ERR_PTR(-ENOMEM);
+
diff --git a/patches.suse/cifs-0006-CIFS-Make-SendReceive2-takes-resp-iov.patch b/patches.suse/cifs-0006-CIFS-Make-SendReceive2-takes-resp-iov.patch
new file mode 100644
index 0000000000..59be476816
--- /dev/null
+++ b/patches.suse/cifs-0006-CIFS-Make-SendReceive2-takes-resp-iov.patch
@@ -0,0 +1,880 @@
+From da502f7df03d2d0b416775f92ae022f3f82bedd5 Mon Sep 17 00:00:00 2001
+From: Pavel Shilovsky <pshilov@microsoft.com>
+Date: Tue, 25 Oct 2016 11:38:47 -0700
+Subject: [PATCH] CIFS: Make SendReceive2() takes resp iov
+Patch-mainline: v4.11
+Git-commit: da502f7df03d2d0b416775f92ae022f3f82bedd5
+References: fate#322075
+
+Now SendReceive2 frees the first iov and returns a response buffer
+in it that increases a code complexity. Simplify this by making
+a caller responsible for freeing request buffer itself and returning
+a response buffer in a separate iov.
+
+Signed-off-by: Pavel Shilovsky <pshilov@microsoft.com>
+Acked-by: Aurelien Aptel <aaptel@suse.com>
+Acked-by: David Mulder <dmulder@suse.com>
+
+---
+ fs/cifs/cifsproto.h | 3 +-
+ fs/cifs/cifssmb.c | 70 ++++++++++++++++------------
+ fs/cifs/sess.c | 5 +-
+ fs/cifs/smb2pdu.c | 128 +++++++++++++++++++++++++++++++++-------------------
+ fs/cifs/transport.c | 30 ++++--------
+ 5 files changed, 136 insertions(+), 100 deletions(-)
+
+diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
+--- a/fs/cifs/cifsproto.h
++++ b/fs/cifs/cifsproto.h
+@@ -96,7 +96,8 @@ extern int cifs_wait_mtu_credits(struct
+ 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 ,
+diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
+--- a/fs/cifs/cifssmb.c
++++ b/fs/cifs/cifssmb.c
+@@ -673,6 +673,7 @@ CIFSSMBTDis(const unsigned int xid, stru
+ 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);
+
+@@ -772,6 +773,7 @@ CIFSSMBLogoff(const unsigned int xid, st
+
+ pSMB->AndXCommand = 0xFF;
+ rc = SendReceiveNoRsp(xid, ses, (char *) pSMB, 0);
++ cifs_small_buf_release(pSMB);
+ session_already_dead:
+ mutex_unlock(&ses->session_mutex);
+
+@@ -1671,6 +1673,7 @@ CIFSSMBRead(const unsigned int xid, stru
+ 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;
+@@ -1720,10 +1723,11 @@ CIFSSMBRead(const unsigned int xid, stru
+
+ 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 {
+@@ -1751,12 +1755,11 @@ CIFSSMBRead(const unsigned int xid, stru
+ }
+ }
+
+-/* 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)
+@@ -2192,6 +2195,7 @@ CIFSSMBWrite2(const unsigned int xid, st
+ __u64 offset = io_parms->offset;
+ struct cifs_tcon *tcon = io_parms->tcon;
+ unsigned int count = io_parms->length;
++ struct kvec rsp_iov;
+
+ *nbytes = 0;
+
+@@ -2250,8 +2254,9 @@ CIFSSMBWrite2(const unsigned int xid, st
+ 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);
+@@ -2259,7 +2264,7 @@ CIFSSMBWrite2(const unsigned int xid, st
+ /* 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);
+@@ -2273,8 +2278,7 @@ CIFSSMBWrite2(const unsigned int xid, st
+ *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 */
+@@ -2289,6 +2293,7 @@ int cifs_lockv(const unsigned int xid, s
+ int rc = 0;
+ LOCK_REQ *pSMB = NULL;
+ struct kvec iov[2];
++ struct kvec rsp_iov;
+ int resp_buf_type;
+ __u16 count;
+
+@@ -2317,7 +2322,9 @@ int cifs_lockv(const unsigned int xid, s
+ 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);
+
+@@ -2378,14 +2385,12 @@ CIFSSMBLock(const unsigned int xid, stru
+ 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);
+@@ -2411,6 +2416,7 @@ CIFSSMBPosixLock(const unsigned int xid,
+ 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");
+
+@@ -2472,11 +2478,10 @@ CIFSSMBPosixLock(const unsigned int xid,
+ 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);
+@@ -2516,10 +2521,7 @@ CIFSSMBPosixLock(const unsigned int xid,
+ }
+
+ 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 */
+@@ -2546,6 +2548,7 @@ CIFSSMBClose(const unsigned int xid, str
+ 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) {
+@@ -2575,6 +2578,7 @@ CIFSSMBFlush(const unsigned int xid, str
+ 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);
+@@ -3828,6 +3832,7 @@ CIFSSMBGetCIFSACL(const unsigned int xid
+ int buf_type = 0;
+ QUERY_SEC_DESC_REQ *pSMB;
+ struct kvec iov[1];
++ struct kvec rsp_iov;
+
+ cifs_dbg(FYI, "GetCifsACL\n");
+
+@@ -3851,7 +3856,8 @@ CIFSSMBGetCIFSACL(const unsigned int xid
+ 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);
+@@ -3863,11 +3869,11 @@ CIFSSMBGetCIFSACL(const unsigned int xid
+ 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);
+@@ -3904,8 +3910,7 @@ CIFSSMBGetCIFSACL(const unsigned int xid
+ }
+ }
+ 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;
+ }
+
+@@ -4674,6 +4679,7 @@ CIFSFindClose(const unsigned int xid, st
+ 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);
+
+@@ -5695,6 +5701,7 @@ CIFSSMBSetFileSize(const unsigned int xi
+ 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);
+@@ -5766,6 +5773,7 @@ CIFSSMBSetFileInfo(const unsigned int xi
+ 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);
+@@ -5826,6 +5834,7 @@ CIFSSMBSetFileDisposition(const unsigned
+ 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);
+
+@@ -6065,6 +6074,7 @@ CIFSSMBUnixSetFileInfo(const unsigned in
+ 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/sess.c b/fs/cifs/sess.c
+--- a/fs/cifs/sess.c
++++ b/fs/cifs/sess.c
+@@ -652,6 +652,7 @@ sess_sendreceive(struct sess_data *sess_
+ int rc;
+ struct smb_hdr *smb_buf = (struct smb_hdr *) sess_data->iov[0].iov_base;
+ __u16 count;
++ struct kvec rsp_iov = { NULL, 0 };
+
+ count = sess_data->iov[1].iov_len + sess_data->iov[2].iov_len;
+ smb_buf->smb_buf_length =
+@@ -661,7 +662,9 @@ sess_sendreceive(struct sess_data *sess_
+ rc = SendReceive2(sess_data->xid, sess_data->ses,
+ sess_data->iov, 3 /* num_iovecs */,
+ &sess_data->buf0_type,
+- CIFS_LOG_ERROR);
++ CIFS_LOG_ERROR, &rsp_iov);
++ cifs_small_buf_release(sess_data->iov[0].iov_base);
++ memcpy(&sess_data->iov[0], &rsp_iov, sizeof(struct kvec));
+
+ return rc;
+ }
+diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
+--- a/fs/cifs/smb2pdu.c
++++ b/fs/cifs/smb2pdu.c
+@@ -391,6 +391,7 @@ SMB2_negotiate(const unsigned int xid, s
+ struct smb2_negotiate_req *req;
+ struct smb2_negotiate_rsp *rsp;
+ struct kvec iov[1];
++ struct kvec rsp_iov;
+ int rc = 0;
+ int resp_buftype;
+ struct TCP_Server_Info *server = ses->server;
+@@ -439,9 +440,9 @@ SMB2_negotiate(const unsigned int xid, s
+ /* 4 for rfc1002 length field */
+ iov[0].iov_len = get_rfc1002_length(req) + 4;
+
+- rc = SendReceive2(xid, ses, iov, 1, &resp_buftype, flags);
+-
+- rsp = (struct smb2_negotiate_rsp *)iov[0].iov_base;
++ rc = SendReceive2(xid, ses, iov, 1, &resp_buftype, flags, &rsp_iov);
++ cifs_small_buf_release(req);
++ rsp = (struct smb2_negotiate_rsp *)rsp_iov.iov_base;
+ /*
+ * No tcon so can't do
+ * cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]);
+@@ -606,6 +607,7 @@ SMB2_sess_setup(const unsigned int xid,
+ unsigned char *ntlmssp_blob = NULL;
+ bool use_spnego = false; /* else use raw ntlmssp */
+ u64 previous_session = ses->Suid;
++ struct kvec rsp_iov = { NULL, 0 };
+
+ cifs_dbg(FYI, "Session Setup\n");
+
+@@ -772,7 +774,9 @@ ssetup_ntlmssp_authenticate:
+ /* BB add code to build os and lm fields */
+
+ rc = SendReceive2(xid, ses, iov, 2, &resp_buftype,
+- CIFS_LOG_ERROR | CIFS_NEG_OP);
++ CIFS_LOG_ERROR | CIFS_NEG_OP, &rsp_iov);
++ cifs_small_buf_release(iov[0].iov_base);
++ memcpy(&iov[0], &rsp_iov, sizeof(struct kvec));
+
+ kfree(security_blob);
+ rsp = (struct smb2_sess_setup_rsp *)iov[0].iov_base;
+@@ -886,7 +890,8 @@ SMB2_logoff(const unsigned int xid, stru
+ if (server->sign)
+ req->hdr.sync_hdr.Flags |= SMB2_FLAGS_SIGNED;
+
+- rc = SendReceiveNoRsp(xid, ses, (char *) &req->hdr, 0);
++ rc = SendReceiveNoRsp(xid, ses, (char *) req, 0);
++ cifs_small_buf_release(req);
+ /*
+ * No tcon so can't do
+ * cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]);
+@@ -918,6 +923,7 @@ SMB2_tcon(const unsigned int xid, struct
+ struct smb2_tree_connect_req *req;
+ struct smb2_tree_connect_rsp *rsp = NULL;
+ struct kvec iov[2];
++ struct kvec rsp_iov;
+ int rc = 0;
+ int resp_buftype;
+ int unc_path_len;
+@@ -977,8 +983,9 @@ SMB2_tcon(const unsigned int xid, struct
+
+ inc_rfc1001_len(req, unc_path_len - 1 /* pad */);
+
+- rc = SendReceive2(xid, ses, iov, 2, &resp_buftype, 0);
+- rsp = (struct smb2_tree_connect_rsp *)iov[0].iov_base;
++ rc = SendReceive2(xid, ses, iov, 2, &resp_buftype, 0, &rsp_iov);
++ cifs_small_buf_release(req);
++ rsp = (struct smb2_tree_connect_rsp *)rsp_iov.iov_base;
+
+ if (rc != 0) {
+ if (tcon) {
+@@ -1059,7 +1066,8 @@ SMB2_tdis(const unsigned int xid, struct
+ if (rc)
+ return rc;
+
+- rc = SendReceiveNoRsp(xid, ses, (char *)&req->hdr, 0);
++ rc = SendReceiveNoRsp(xid, ses, (char *)req, 0);
++ cifs_small_buf_release(req);
+ if (rc)
+ cifs_stats_fail_inc(tcon, SMB2_TREE_DISCONNECT_HE);
+
+@@ -1321,12 +1329,13 @@ SMB2_open(const unsigned int xid, struct
+ struct cifs_tcon *tcon = oparms->tcon;
+ struct cifs_ses *ses = tcon->ses;
+ struct kvec iov[4];
++ struct kvec rsp_iov;
+ int resp_buftype;
+ int uni_path_len;
+ __le16 *copy_path = NULL;
+ int copy_size;
+ int rc = 0;
+- unsigned int num_iovecs = 2;
++ unsigned int n_iov = 2;
+ __u32 file_attributes = 0;
+ char *dhc_buf = NULL, *lc_buf = NULL;
+
+@@ -1391,25 +1400,25 @@ SMB2_open(const unsigned int xid, struct
+ *oplock == SMB2_OPLOCK_LEVEL_NONE)
+ req->RequestedOplockLevel = *oplock;
+ else {
+- rc = add_lease_context(server, iov, &num_iovecs, oplock);
++ rc = add_lease_context(server, iov, &n_iov, oplock);
+ if (rc) {
+ cifs_small_buf_release(req);
+ kfree(copy_path);
+ return rc;
+ }
+- lc_buf = iov[num_iovecs-1].iov_base;
++ lc_buf = iov[n_iov-1].iov_base;
+ }
+
+ if (*oplock == SMB2_OPLOCK_LEVEL_BATCH) {
+ /* need to set Next field of lease context if we request it */
+ if (server->capabilities & SMB2_GLOBAL_CAP_LEASING) {
+ struct create_context *ccontext =
+- (struct create_context *)iov[num_iovecs-1].iov_base;
++ (struct create_context *)iov[n_iov-1].iov_base;
+ ccontext->Next =
+ cpu_to_le32(server->vals->create_lease_size);
+ }
+
+- rc = add_durable_context(iov, &num_iovecs, oparms,
++ rc = add_durable_context(iov, &n_iov, oparms,
+ tcon->use_persistent);
+ if (rc) {
+ cifs_small_buf_release(req);
+@@ -1417,11 +1426,12 @@ SMB2_open(const unsigned int xid, struct
+ kfree(lc_buf);
+ return rc;
+ }
+- dhc_buf = iov[num_iovecs-1].iov_base;
++ dhc_buf = iov[n_iov-1].iov_base;
+ }
+
+- rc = SendReceive2(xid, ses, iov, num_iovecs, &resp_buftype, 0);
+- rsp = (struct smb2_create_rsp *)iov[0].iov_base;
++ rc = SendReceive2(xid, ses, iov, n_iov, &resp_buftype, 0, &rsp_iov);
++ cifs_small_buf_release(req);
++ rsp = (struct smb2_create_rsp *)rsp_iov.iov_base;
+
+ if (rc != 0) {
+ cifs_stats_fail_inc(tcon, SMB2_CREATE_HE);
+@@ -1469,8 +1479,9 @@ SMB2_ioctl(const unsigned int xid, struc
+ struct TCP_Server_Info *server;
+ struct cifs_ses *ses;
+ struct kvec iov[2];
++ struct kvec rsp_iov;
+ int resp_buftype;
+- int num_iovecs;
++ int n_iov;
+ int rc = 0;
+
+ cifs_dbg(FYI, "SMB2 IOCTL\n");
+@@ -1507,9 +1518,9 @@ SMB2_ioctl(const unsigned int xid, struc
+ cpu_to_le32(offsetof(struct smb2_ioctl_req, Buffer) - 4);
+ iov[1].iov_base = in_data;
+ iov[1].iov_len = indatalen;
+- num_iovecs = 2;
++ n_iov = 2;
+ } else
+- num_iovecs = 1;
++ n_iov = 1;
+
+ req->OutputOffset = 0;
+ req->OutputCount = 0; /* MBZ */
+@@ -1546,8 +1557,9 @@ SMB2_ioctl(const unsigned int xid, struc
+ iov[0].iov_len = get_rfc1002_length(req) + 4;
+
+
+- rc = SendReceive2(xid, ses, iov, num_iovecs, &resp_buftype, 0);
+- rsp = (struct smb2_ioctl_rsp *)iov[0].iov_base;
++ rc = SendReceive2(xid, ses, iov, n_iov, &resp_buftype, 0, &rsp_iov);
++ cifs_small_buf_release(req);
++ rsp = (struct smb2_ioctl_rsp *)rsp_iov.iov_base;
+
+ if ((rc != 0) && (rc != -EINVAL)) {
+ cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE);
+@@ -1631,6 +1643,7 @@ SMB2_close(const unsigned int xid, struc
+ struct TCP_Server_Info *server;
+ struct cifs_ses *ses = tcon->ses;
+ struct kvec iov[1];
++ struct kvec rsp_iov;
+ int resp_buftype;
+ int rc = 0;
+
+@@ -1652,8 +1665,9 @@ SMB2_close(const unsigned int xid, struc
+ /* 4 for rfc1002 length field */
+ iov[0].iov_len = get_rfc1002_length(req) + 4;
+
+- rc = SendReceive2(xid, ses, iov, 1, &resp_buftype, 0);
+- rsp = (struct smb2_close_rsp *)iov[0].iov_base;
++ rc = SendReceive2(xid, ses, iov, 1, &resp_buftype, 0, &rsp_iov);
++ cifs_small_buf_release(req);
++ rsp = (struct smb2_close_rsp *)rsp_iov.iov_base;
+
+ if (rc != 0) {
+ cifs_stats_fail_inc(tcon, SMB2_CLOSE_HE);
+@@ -1732,6 +1746,7 @@ query_info(const unsigned int xid, struc
+ struct smb2_query_info_req *req;
+ struct smb2_query_info_rsp *rsp = NULL;
+ struct kvec iov[2];
++ struct kvec rsp_iov;
+ int rc = 0;
+ int resp_buftype;
+ struct TCP_Server_Info *server;
+@@ -1761,8 +1776,9 @@ query_info(const unsigned int xid, struc
+ /* 4 for rfc1002 length field */
+ iov[0].iov_len = get_rfc1002_length(req) + 4;
+
+- rc = SendReceive2(xid, ses, iov, 1, &resp_buftype, 0);
+- rsp = (struct smb2_query_info_rsp *)iov[0].iov_base;
++ rc = SendReceive2(xid, ses, iov, 1, &resp_buftype, 0, &rsp_iov);
++ cifs_small_buf_release(req);
++ rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base;
+
+ if (rc) {
+ cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE);
+@@ -1914,6 +1930,7 @@ SMB2_flush(const unsigned int xid, struc
+ struct TCP_Server_Info *server;
+ struct cifs_ses *ses = tcon->ses;
+ struct kvec iov[1];
++ struct kvec rsp_iov;
+ int resp_buftype;
+ int rc = 0;
+
+@@ -1935,12 +1952,13 @@ SMB2_flush(const unsigned int xid, struc
+ /* 4 for rfc1002 length field */
+ iov[0].iov_len = get_rfc1002_length(req) + 4;
+
+- rc = SendReceive2(xid, ses, iov, 1, &resp_buftype, 0);
++ rc = SendReceive2(xid, ses, iov, 1, &resp_buftype, 0, &rsp_iov);
++ cifs_small_buf_release(req);
+
+ if (rc != 0)
+ cifs_stats_fail_inc(tcon, SMB2_FLUSH_HE);
+
+- free_rsp_buf(resp_buftype, iov[0].iov_base);
++ free_rsp_buf(resp_buftype, rsp_iov.iov_base);
+ return rc;
+ }
+
+@@ -2139,6 +2157,7 @@ SMB2_read(const unsigned int xid, struct
+ struct smb2_read_rsp *rsp = NULL;
+ struct smb2_sync_hdr *shdr;
+ struct kvec iov[1];
++ struct kvec rsp_iov;
+
+ *nbytes = 0;
+ rc = smb2_new_read_req(iov, io_parms, 0, 0);
+@@ -2146,13 +2165,14 @@ SMB2_read(const unsigned int xid, struct
+ return rc;
+
+ rc = SendReceive2(xid, io_parms->tcon->ses, iov, 1,
+- &resp_buftype, CIFS_LOG_ERROR);
++ &resp_buftype, CIFS_LOG_ERROR, &rsp_iov);
++ cifs_small_buf_release(iov[0].iov_base);
+
+- rsp = (struct smb2_read_rsp *)iov[0].iov_base;
++ rsp = (struct smb2_read_rsp *)rsp_iov.iov_base;
+ shdr = get_sync_hdr(rsp);
+
+ if (shdr->Status == STATUS_END_OF_FILE) {
+- free_rsp_buf(resp_buftype, iov[0].iov_base);
++ free_rsp_buf(resp_buftype, rsp_iov.iov_base);
+ return 0;
+ }
+
+@@ -2172,9 +2192,9 @@ SMB2_read(const unsigned int xid, struct
+
+ if (*buf) {
+ memcpy(*buf, (char *)shdr + rsp->DataOffset, *nbytes);
+- free_rsp_buf(resp_buftype, iov[0].iov_base);
++ free_rsp_buf(resp_buftype, rsp_iov.iov_base);
+ } else if (resp_buftype != CIFS_NO_BUFFER) {
+- *buf = iov[0].iov_base;
++ *buf = rsp_iov.iov_base;
+ if (resp_buftype == CIFS_SMALL_BUFFER)
+ *buf_type = CIFS_SMALL_BUFFER;
+ else if (resp_buftype == CIFS_LARGE_BUFFER)
+@@ -2336,6 +2356,8 @@ SMB2_write(const unsigned int xid, struc
+ struct smb2_write_req *req = NULL;
+ struct smb2_write_rsp *rsp = NULL;
+ int resp_buftype;
++ struct kvec rsp_iov;
++
+ *nbytes = 0;
+
+ if (n_vec < 1)
+@@ -2370,8 +2392,9 @@ SMB2_write(const unsigned int xid, struc
+ inc_rfc1001_len(req, io_parms->length - 1 /* Buffer */);
+
+ rc = SendReceive2(xid, io_parms->tcon->ses, iov, n_vec + 1,
+- &resp_buftype, 0);
+- rsp = (struct smb2_write_rsp *)iov[0].iov_base;
++ &resp_buftype, 0, &rsp_iov);
++ cifs_small_buf_release(req);
++ rsp = (struct smb2_write_rsp *)rsp_iov.iov_base;
+
+ if (rc) {
+ cifs_stats_fail_inc(io_parms->tcon, SMB2_WRITE_HE);
+@@ -2434,6 +2457,7 @@ SMB2_query_directory(const unsigned int
+ struct smb2_query_directory_req *req;
+ struct smb2_query_directory_rsp *rsp = NULL;
+ struct kvec iov[2];
++ struct kvec rsp_iov;
+ int rc = 0;
+ int len;
+ int resp_buftype = CIFS_NO_BUFFER;
+@@ -2498,8 +2522,9 @@ SMB2_query_directory(const unsigned int
+
+ inc_rfc1001_len(req, len - 1 /* Buffer */);
+
+- rc = SendReceive2(xid, ses, iov, 2, &resp_buftype, 0);
+- rsp = (struct smb2_query_directory_rsp *)iov[0].iov_base;
++ rc = SendReceive2(xid, ses, iov, 2, &resp_buftype, 0, &rsp_iov);
++ cifs_small_buf_release(req);
++ rsp = (struct smb2_query_directory_rsp *)rsp_iov.iov_base;
+
+ if (rc) {
+ if (rc == -ENODATA &&
+@@ -2559,6 +2584,7 @@ send_set_info(const unsigned int xid, st
+ struct smb2_set_info_req *req;
+ struct smb2_set_info_rsp *rsp = NULL;
+ struct kvec *iov;
++ struct kvec rsp_iov;
+ int rc = 0;
+ int resp_buftype;
+ unsigned int i;
+@@ -2610,8 +2636,9 @@ send_set_info(const unsigned int xid, st
+ iov[i].iov_len = size[i];
+ }
+
+- rc = SendReceive2(xid, ses, iov, num, &resp_buftype, 0);
+- rsp = (struct smb2_set_info_rsp *)iov[0].iov_base;
++ rc = SendReceive2(xid, ses, iov, num, &resp_buftype, 0, &rsp_iov);
++ cifs_small_buf_release(req);
++ rsp = (struct smb2_set_info_rsp *)rsp_iov.iov_base;
+
+ if (rc != 0)
+ cifs_stats_fail_inc(tcon, SMB2_SET_INFO_HE);
+@@ -2752,7 +2779,7 @@ SMB2_oplock_break(const unsigned int xid
+ req->hdr.sync_hdr.CreditRequest = cpu_to_le16(1);
+
+ rc = SendReceiveNoRsp(xid, tcon->ses, (char *) req, CIFS_OBREAK_OP);
+- /* SMB2 buffer freed by function above */
++ cifs_small_buf_release(req);
+
+ if (rc) {
+ cifs_stats_fail_inc(tcon, SMB2_OPLOCK_BREAK_HE);
+@@ -2812,6 +2839,7 @@ SMB2_QFS_info(const unsigned int xid, st
+ {
+ struct smb2_query_info_rsp *rsp = NULL;
+ struct kvec iov;
++ struct kvec rsp_iov;
+ int rc = 0;
+ int resp_buftype;
+ struct cifs_ses *ses = tcon->ses;
+@@ -2823,12 +2851,13 @@ SMB2_QFS_info(const unsigned int xid, st
+ if (rc)
+ return rc;
+
+- rc = SendReceive2(xid, ses, &iov, 1, &resp_buftype, 0);
++ rc = SendReceive2(xid, ses, &iov, 1, &resp_buftype, 0, &rsp_iov);
++ cifs_small_buf_release(iov.iov_base);
+ if (rc) {
+ cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE);
+ goto qfsinf_exit;
+ }
+- rsp = (struct smb2_query_info_rsp *)iov.iov_base;
++ rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base;
+
+ info = (struct smb2_fs_full_size_info *)(4 /* RFC1001 len */ +
+ le16_to_cpu(rsp->OutputBufferOffset) + (char *)&rsp->hdr);
+@@ -2839,7 +2868,7 @@ SMB2_QFS_info(const unsigned int xid, st
+ copy_fs_info_to_kstatfs(info, fsdata);
+
+ qfsinf_exit:
+- free_rsp_buf(resp_buftype, iov.iov_base);
++ free_rsp_buf(resp_buftype, rsp_iov.iov_base);
+ return rc;
+ }
+
+@@ -2849,6 +2878,7 @@ SMB2_QFS_attr(const unsigned int xid, st
+ {
+ struct smb2_query_info_rsp *rsp = NULL;
+ struct kvec iov;
++ struct kvec rsp_iov;
+ int rc = 0;
+ int resp_buftype, max_len, min_len;
+ struct cifs_ses *ses = tcon->ses;
+@@ -2873,12 +2903,13 @@ SMB2_QFS_attr(const unsigned int xid, st
+ if (rc)
+ return rc;
+
+- rc = SendReceive2(xid, ses, &iov, 1, &resp_buftype, 0);
++ rc = SendReceive2(xid, ses, &iov, 1, &resp_buftype, 0, &rsp_iov);
++ cifs_small_buf_release(iov.iov_base);
+ if (rc) {
+ cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE);
+ goto qfsattr_exit;
+ }
+- rsp = (struct smb2_query_info_rsp *)iov.iov_base;
++ rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base;
+
+ rsp_len = le32_to_cpu(rsp->OutputBufferLength);
+ offset = le16_to_cpu(rsp->OutputBufferOffset);
+@@ -2902,7 +2933,7 @@ SMB2_QFS_attr(const unsigned int xid, st
+ }
+
+ qfsattr_exit:
+- free_rsp_buf(resp_buftype, iov.iov_base);
++ free_rsp_buf(resp_buftype, rsp_iov.iov_base);
+ return rc;
+ }
+
+@@ -2914,6 +2945,7 @@ smb2_lockv(const unsigned int xid, struc
+ int rc = 0;
+ struct smb2_lock_req *req = NULL;
+ struct kvec iov[2];
++ struct kvec rsp_iov;
+ int resp_buf_type;
+ unsigned int count;
+
+@@ -2939,7 +2971,9 @@ smb2_lockv(const unsigned int xid, struc
+ iov[1].iov_len = count;
+
+ 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(req);
+ if (rc) {
+ cifs_dbg(FYI, "Send error in smb2_lockv = %d\n", rc);
+ cifs_stats_fail_inc(tcon, SMB2_LOCK_HE);
+@@ -2986,7 +3020,7 @@ SMB2_lease_break(const unsigned int xid,
+ req->LeaseState = lease_state;
+
+ rc = SendReceiveNoRsp(xid, tcon->ses, (char *) req, CIFS_OBREAK_OP);
+- /* SMB2 buffer freed by function above */
++ cifs_small_buf_release(req);
+
+ if (rc) {
+ cifs_stats_fail_inc(tcon, SMB2_OPLOCK_BREAK_HE);
+diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c
+--- a/fs/cifs/transport.c
++++ b/fs/cifs/transport.c
+@@ -605,12 +605,13 @@ SendReceiveNoRsp(const unsigned int xid,
+ {
+ int rc;
+ struct kvec iov[1];
++ struct kvec rsp_iov;
+ int resp_buf_type;
+
+ iov[0].iov_base = in_buf;
+ iov[0].iov_len = get_rfc1002_length(in_buf) + 4;
+ flags |= CIFS_NO_RESP;
+- rc = SendReceive2(xid, ses, iov, 1, &resp_buf_type, flags);
++ rc = SendReceive2(xid, ses, iov, 1, &resp_buf_type, flags, &rsp_iov);
+ cifs_dbg(NOISY, "SendRcvNoRsp flags %d rc %d\n", flags, rc);
+
+ return rc;
+@@ -709,7 +710,7 @@ cifs_setup_request(struct cifs_ses *ses,
+ int
+ SendReceive2(const unsigned int xid, struct cifs_ses *ses,
+ struct kvec *iov, int n_vec, int *resp_buf_type /* ret */,
+- const int flags)
++ const int flags, struct kvec *resp_iov)
+ {
+ int rc = 0;
+ int timeout, optype;
+@@ -725,15 +726,12 @@ SendReceive2(const unsigned int xid, str
+ *resp_buf_type = CIFS_NO_BUFFER; /* no response buf yet */
+
+ if ((ses == NULL) || (ses->server == NULL)) {
+- cifs_small_buf_release(buf);
+ cifs_dbg(VFS, "Null session\n");
+ return -EIO;
+ }
+
+- if (ses->server->tcpStatus == CifsExiting) {
+- cifs_small_buf_release(buf);
++ if (ses->server->tcpStatus == CifsExiting)
+ return -ENOENT;
+- }
+
+ /*
+ * Ensure that we do not send more than 50 overlapping requests
+@@ -742,10 +740,8 @@ SendReceive2(const unsigned int xid, str
+ */
+
+ rc = wait_for_free_request(ses->server, timeout, optype);
+- if (rc) {
+- cifs_small_buf_release(buf);
++ if (rc)
+ return rc;
+- }
+
+ /*
+ * Make sure that we sign in the same order that we send on this socket
+@@ -758,7 +754,6 @@ SendReceive2(const unsigned int xid, str
+ midQ = ses->server->ops->setup_request(ses, &rqst);
+ if (IS_ERR(midQ)) {
+ mutex_unlock(&ses->server->srv_mutex);
+- cifs_small_buf_release(buf);
+ /* Update # of requests on wire to server */
+ add_credits(ses->server, 1, optype);
+ return PTR_ERR(midQ);
+@@ -774,15 +769,11 @@ SendReceive2(const unsigned int xid, str
+ ses->server->sequence_number -= 2;
+ mutex_unlock(&ses->server->srv_mutex);
+
+- if (rc < 0) {
+- cifs_small_buf_release(buf);
++ if (rc < 0)
+ goto out;
+- }
+
+- if (timeout == CIFS_ASYNC_OP) {
+- cifs_small_buf_release(buf);
++ if (timeout == CIFS_ASYNC_OP)
+ goto out;
+- }
+
+ rc = wait_for_response(ses->server, midQ);
+ if (rc != 0) {
+@@ -791,15 +782,12 @@ SendReceive2(const unsigned int xid, str
+ if (midQ->mid_state == MID_REQUEST_SUBMITTED) {
+ midQ->callback = DeleteMidQEntry;
+ spin_unlock(&GlobalMid_Lock);
+- cifs_small_buf_release(buf);
+ add_credits(ses->server, 1, optype);
+ return rc;
+ }
+ spin_unlock(&GlobalMid_Lock);
+ }
+
+- cifs_small_buf_release(buf);
+-
+ rc = cifs_sync_mid_result(midQ, ses->server);
+ if (rc != 0) {
+ add_credits(ses->server, 1, optype);
+@@ -813,8 +801,8 @@ SendReceive2(const unsigned int xid, str
+ }
+
+ buf = (char *)midQ->resp_buf;
+- iov[0].iov_base = buf;
+- iov[0].iov_len = get_rfc1002_length(buf) + 4;
++ resp_iov->iov_base = buf;
++ resp_iov->iov_len = get_rfc1002_length(buf) + 4;
+ if (midQ->large_buf)
+ *resp_buf_type = CIFS_LARGE_BUFFER;
+ else
diff --git a/patches.suse/cifs-0007-CIFS-Make-send_cancel-take-rqst-as-argument.patch b/patches.suse/cifs-0007-CIFS-Make-send_cancel-take-rqst-as-argument.patch
new file mode 100644
index 0000000000..3441b49d87
--- /dev/null
+++ b/patches.suse/cifs-0007-CIFS-Make-send_cancel-take-rqst-as-argument.patch
@@ -0,0 +1,164 @@
+From fb2036d817584df42504910fe104f68517e8990e Mon Sep 17 00:00:00 2001
+From: Pavel Shilovsky <pshilov@microsoft.com>
+Date: Wed, 23 Nov 2016 15:08:14 -0800
+Subject: [PATCH] CIFS: Make send_cancel take rqst as argument
+Patch-mainline: v4.11
+Git-commit: fb2036d817584df42504910fe104f68517e8990e
+References: fate#322075
+
+Signed-off-by: Pavel Shilovsky <pshilov@microsoft.com>
+Acked-by: Aurelien Aptel <aaptel@suse.com>
+Acked-by: David Mulder <dmulder@suse.com>
+
+---
+ fs/cifs/cifsglob.h | 2 +-
+ fs/cifs/smb1ops.c | 4 ++--
+ fs/cifs/transport.c | 33 +++++++++++++++++++--------------
+ 3 files changed, 22 insertions(+), 17 deletions(-)
+
+diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
+--- a/fs/cifs/cifsglob.h
++++ b/fs/cifs/cifsglob.h
+@@ -194,7 +194,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 */
+diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c
+--- a/fs/cifs/smb1ops.c
++++ b/fs/cifs/smb1ops.c
+@@ -36,11 +36,11 @@
+ * SMB_COM_NT_CANCEL request and then sends it.
+ */
+ static int
+-send_nt_cancel(struct TCP_Server_Info *server, void *buf,
++send_nt_cancel(struct TCP_Server_Info *server, struct smb_rqst *rqst,
+ struct mid_q_entry *mid)
+ {
+ int rc = 0;
+- struct smb_hdr *in_buf = (struct smb_hdr *)buf;
++ struct smb_hdr *in_buf = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
+
+ /* -4 for RFC1001 length and +2 for BCC field */
+ in_buf->smb_buf_length = cpu_to_be32(sizeof(struct smb_hdr) - 4 + 2);
+diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c
+--- a/fs/cifs/transport.c
++++ b/fs/cifs/transport.c
+@@ -654,10 +654,11 @@ cifs_sync_mid_result(struct mid_q_entry
+ }
+
+ static inline int
+-send_cancel(struct TCP_Server_Info *server, void *buf, struct mid_q_entry *mid)
++send_cancel(struct TCP_Server_Info *server, struct smb_rqst *rqst,
++ struct mid_q_entry *mid)
+ {
+ return server->ops->send_cancel ?
+- server->ops->send_cancel(server, buf, mid) : 0;
++ server->ops->send_cancel(server, rqst, mid) : 0;
+ }
+
+ int
+@@ -777,7 +778,7 @@ SendReceive2(const unsigned int xid, str
+
+ rc = wait_for_response(ses->server, midQ);
+ if (rc != 0) {
+- send_cancel(ses->server, buf, midQ);
++ send_cancel(ses->server, &rqst, midQ);
+ spin_lock(&GlobalMid_Lock);
+ if (midQ->mid_state == MID_REQUEST_SUBMITTED) {
+ midQ->callback = DeleteMidQEntry;
+@@ -830,6 +831,9 @@ SendReceive(const unsigned int xid, stru
+ {
+ int rc = 0;
+ struct mid_q_entry *midQ;
++ unsigned int len = be32_to_cpu(in_buf->smb_buf_length);
++ struct kvec iov = { .iov_base = in_buf, .iov_len = len };
++ struct smb_rqst rqst = { .rq_iov = &iov, .rq_nvec = 1 };
+
+ if (ses == NULL) {
+ cifs_dbg(VFS, "Null smb session\n");
+@@ -847,10 +851,9 @@ SendReceive(const unsigned int xid, stru
+ to the same server. We may make this configurable later or
+ use ses->maxReq */
+
+- if (be32_to_cpu(in_buf->smb_buf_length) > CIFSMaxBufSize +
+- MAX_CIFS_HDR_SIZE - 4) {
++ if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
+ cifs_dbg(VFS, "Illegal length, greater than maximum frame, %d\n",
+- be32_to_cpu(in_buf->smb_buf_length));
++ len);
+ return -EIO;
+ }
+
+@@ -881,7 +884,7 @@ SendReceive(const unsigned int xid, stru
+ midQ->mid_state = MID_REQUEST_SUBMITTED;
+
+ cifs_in_send_inc(ses->server);
+- rc = smb_send(ses->server, in_buf, be32_to_cpu(in_buf->smb_buf_length));
++ rc = smb_send(ses->server, in_buf, len);
+ cifs_in_send_dec(ses->server);
+ cifs_save_when_sent(midQ);
+
+@@ -898,7 +901,7 @@ SendReceive(const unsigned int xid, stru
+
+ rc = wait_for_response(ses->server, midQ);
+ if (rc != 0) {
+- send_cancel(ses->server, in_buf, midQ);
++ send_cancel(ses->server, &rqst, midQ);
+ spin_lock(&GlobalMid_Lock);
+ if (midQ->mid_state == MID_REQUEST_SUBMITTED) {
+ /* no longer considered to be "in-flight" */
+@@ -967,6 +970,9 @@ SendReceiveBlockingLock(const unsigned i
+ int rstart = 0;
+ struct mid_q_entry *midQ;
+ struct cifs_ses *ses;
++ unsigned int len = be32_to_cpu(in_buf->smb_buf_length);
++ struct kvec iov = { .iov_base = in_buf, .iov_len = len };
++ struct smb_rqst rqst = { .rq_iov = &iov, .rq_nvec = 1 };
+
+ if (tcon == NULL || tcon->ses == NULL) {
+ cifs_dbg(VFS, "Null smb session\n");
+@@ -986,10 +992,9 @@ SendReceiveBlockingLock(const unsigned i
+ to the same server. We may make this configurable later or
+ use ses->maxReq */
+
+- if (be32_to_cpu(in_buf->smb_buf_length) > CIFSMaxBufSize +
+- MAX_CIFS_HDR_SIZE - 4) {
++ if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
+ cifs_dbg(VFS, "Illegal length, greater than maximum frame, %d\n",
+- be32_to_cpu(in_buf->smb_buf_length));
++ len);
+ return -EIO;
+ }
+
+@@ -1018,7 +1023,7 @@ SendReceiveBlockingLock(const unsigned i
+
+ midQ->mid_state = MID_REQUEST_SUBMITTED;
+ cifs_in_send_inc(ses->server);
+- rc = smb_send(ses->server, in_buf, be32_to_cpu(in_buf->smb_buf_length));
++ rc = smb_send(ses->server, in_buf, len);
+ cifs_in_send_dec(ses->server);
+ cifs_save_when_sent(midQ);
+
+@@ -1047,7 +1052,7 @@ SendReceiveBlockingLock(const unsigned i
+ if (in_buf->Command == SMB_COM_TRANSACTION2) {
+ /* POSIX lock. We send a NT_CANCEL SMB to cause the
+ blocking lock to return. */
+- rc = send_cancel(ses->server, in_buf, midQ);
++ rc = send_cancel(ses->server, &rqst, midQ);
+ if (rc) {
+ cifs_delete_mid(midQ);
+ return rc;
+@@ -1068,7 +1073,7 @@ SendReceiveBlockingLock(const unsigned i
+
+ rc = wait_for_response(ses->server, midQ);
+ if (rc) {
+- send_cancel(ses->server, in_buf, midQ);
++ send_cancel(ses->server, &rqst, midQ);
+ spin_lock(&GlobalMid_Lock);
+ if (midQ->mid_state == MID_REQUEST_SUBMITTED) {
+ /* no longer considered to be "in-flight" */
diff --git a/patches.suse/cifs-0008-CIFS-Send-RFC1001-length-in-a-separate-iov.patch b/patches.suse/cifs-0008-CIFS-Send-RFC1001-length-in-a-separate-iov.patch
new file mode 100644
index 0000000000..1d22a0a3d3
--- /dev/null
+++ b/patches.suse/cifs-0008-CIFS-Send-RFC1001-length-in-a-separate-iov.patch
@@ -0,0 +1,657 @@
+From 738f9de5cdb9175c19d24cfdf90b4543fc3b47bf Mon Sep 17 00:00:00 2001
+From: Pavel Shilovsky <pshilov@microsoft.com>
+Date: Wed, 23 Nov 2016 15:14:57 -0800
+Subject: [PATCH] CIFS: Send RFC1001 length in a separate iov
+Patch-mainline: v4.11
+Git-commit: 738f9de5cdb9175c19d24cfdf90b4543fc3b47bf
+References: fate#322075
+
+In order to simplify further encryption support we need to separate
+RFC1001 length and SMB2 header when sending a request. Put the length
+field in iov[0] and the rest of the packet into following iovs.
+
+Signed-off-by: Pavel Shilovsky <pshilov@microsoft.com>
+Acked-by: Aurelien Aptel <aaptel@suse.com>
+Acked-by: David Mulder <dmulder@suse.com>
+
+---
+ fs/cifs/cifsencrypt.c | 38 +++++++++++++---------
+ fs/cifs/cifsglob.h | 2 +-
+ fs/cifs/cifssmb.c | 51 +++++++++++++++++------------
+ fs/cifs/smb2pdu.c | 64 ++++++++++++++++++++++--------------
+ fs/cifs/smb2transport.c | 28 +++++++++-------
+ fs/cifs/transport.c | 86 +++++++++++++++++++++++++++++++++++++------------
+ 6 files changed, 174 insertions(+), 95 deletions(-)
+
+diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c
+--- a/fs/cifs/cifsencrypt.c
++++ b/fs/cifs/cifsencrypt.c
+@@ -104,26 +104,21 @@ static int cifs_calc_signature(struct sm
+ return rc;
+ }
+
+- 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(&server->secmech.sdescmd5->shash,
+- iov[i].iov_base + 4, iov[i].iov_len - 4);
+- } else {
+- rc =
+- crypto_shash_update(&server->secmech.sdescmd5->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(&server->secmech.sdescmd5->shash,
++ iov[i].iov_base, iov[i].iov_len);
+ if (rc) {
+ cifs_dbg(VFS, "%s: Could not update with payload\n",
+ __func__);
+@@ -156,6 +151,10 @@ int cifs_sign_rqst(struct smb_rqst *rqst
+ 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;
+
+@@ -197,12 +196,14 @@ int cifs_sign_smbv(struct kvec *iov, int
+ 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);
+ }
+
+@@ -215,6 +216,10 @@ int cifs_verify_signature(struct smb_rqs
+ 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;
+
+diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
+--- a/fs/cifs/cifsglob.h
++++ b/fs/cifs/cifsglob.h
+@@ -1094,7 +1094,7 @@ struct cifs_readdata {
+ int (*read_into_pages)(struct TCP_Server_Info *server,
+ struct cifs_readdata *rdata,
+ unsigned int len);
+- struct kvec iov;
++ struct kvec iov[2];
+ unsigned int pagesz;
+ unsigned int tailsz;
+ unsigned int credits;
+diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
+--- a/fs/cifs/cifssmb.c
++++ b/fs/cifs/cifssmb.c
+@@ -708,9 +708,9 @@ CIFSSMBEcho(struct TCP_Server_Info *serv
+ {
+ 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");
+
+@@ -725,8 +725,11 @@ CIFSSMBEcho(struct TCP_Server_Info *serv
+ 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;
++
++ 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,
+ server, CIFS_ASYNC_OP | CIFS_ECHO_OP);
+@@ -1510,10 +1513,12 @@ cifs_readv_receive(struct TCP_Server_Inf
+ }
+
+ /* 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);
+@@ -1546,8 +1551,8 @@ cifs_readv_callback(struct mid_q_entry *
+ 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,
+@@ -1602,8 +1607,8 @@ cifs_async_readv(struct cifs_readdata *r
+ 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);
+@@ -1643,8 +1648,10 @@ cifs_async_readv(struct cifs_readdata *r
+ }
+
+ /* 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,
+@@ -2103,7 +2110,7 @@ cifs_async_writev(struct cifs_writedata
+ 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) {
+@@ -2136,11 +2143,13 @@ cifs_async_writev(struct cifs_writedata
+ 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;
+@@ -2161,7 +2170,7 @@ cifs_async_writev(struct cifs_writedata
+ (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);
+diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
+--- a/fs/cifs/smb2pdu.c
++++ b/fs/cifs/smb2pdu.c
+@@ -1891,9 +1891,9 @@ SMB2_echo(struct TCP_Server_Info *server
+ {
+ struct smb2_echo_req *req;
+ 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");
+
+@@ -1909,9 +1909,11 @@ SMB2_echo(struct TCP_Server_Info *server
+
+ req->hdr.sync_hdr.CreditRequest = cpu_to_le16(1);
+
+- iov.iov_base = (char *)req;
+ /* 4 for rfc1002 length field */
+- iov.iov_len = get_rfc1002_length(req) + 4;
++ iov[0].iov_len = 4;
++ iov[0].iov_base = (char *)req;
++ iov[1].iov_len = get_rfc1002_length(req);
++ iov[1].iov_base = (char *)req + 4;
+
+ rc = cifs_call_async(server, &rqst, NULL, smb2_echo_callback, server,
+ CIFS_ECHO_OP);
+@@ -1967,8 +1969,9 @@ SMB2_flush(const unsigned int xid, struc
+ * have the end_of_chain boolean set to true.
+ */
+ static int
+-smb2_new_read_req(struct kvec *iov, struct cifs_io_parms *io_parms,
+- unsigned int remaining_bytes, int request_type)
++smb2_new_read_req(void **buf, unsigned int *total_len,
++ struct cifs_io_parms *io_parms, unsigned int remaining_bytes,
++ int request_type)
+ {
+ int rc = -EACCES;
+ struct smb2_read_req *req = NULL;
+@@ -2016,9 +2019,9 @@ smb2_new_read_req(struct kvec *iov, stru
+ else
+ req->RemainingBytes = 0;
+
+- iov[0].iov_base = (char *)req;
++ *buf = req;
+ /* 4 for rfc1002 length field */
+- iov[0].iov_len = get_rfc1002_length(req) + 4;
++ *total_len = get_rfc1002_length(req) + 4;
+ return rc;
+ }
+
+@@ -2028,10 +2031,11 @@ smb2_readv_callback(struct mid_q_entry *
+ 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 smb2_sync_hdr *shdr = get_sync_hdr(rdata->iov.iov_base);
++ struct smb2_sync_hdr *shdr =
++ (struct smb2_sync_hdr *)rdata->iov[1].iov_base;
+ unsigned int credits_received = 1;
+- 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,
+@@ -2082,7 +2086,7 @@ smb2_readv_callback(struct mid_q_entry *
+ add_credits(server, credits_received, 0);
+ }
+
+-/* smb2_async_readv - send an async write, and set up mid to handle result */
++/* smb2_async_readv - send an async read, and set up mid to handle result */
+ int
+ smb2_async_readv(struct cifs_readdata *rdata)
+ {
+@@ -2090,9 +2094,10 @@ smb2_async_readv(struct cifs_readdata *r
+ char *buf;
+ struct smb2_sync_hdr *shdr;
+ struct cifs_io_parms io_parms;
+- struct smb_rqst rqst = { .rq_iov = &rdata->iov,
+- .rq_nvec = 1 };
++ struct smb_rqst rqst = { .rq_iov = rdata->iov,
++ .rq_nvec = 2 };
+ struct TCP_Server_Info *server;
++ unsigned int total_len;
+
+ cifs_dbg(FYI, "%s: offset=%llu bytes=%u\n",
+ __func__, rdata->offset, rdata->bytes);
+@@ -2106,7 +2111,7 @@ smb2_async_readv(struct cifs_readdata *r
+
+ server = io_parms.tcon->ses->server;
+
+- rc = smb2_new_read_req(&rdata->iov, &io_parms, 0, 0);
++ rc = smb2_new_read_req((void **) &buf, &total_len, &io_parms, 0, 0);
+ if (rc) {
+ if (rc == -EAGAIN && rdata->credits) {
+ /* credits was reset by reconnect */
+@@ -2119,10 +2124,12 @@ smb2_async_readv(struct cifs_readdata *r
+ return rc;
+ }
+
+- buf = rdata->iov.iov_base;
+ shdr = get_sync_hdr(buf);
+ /* 4 for rfc1002 length field */
+- rdata->iov.iov_len = get_rfc1002_length(rdata->iov.iov_base) + 4;
++ rdata->iov[0].iov_len = 4;
++ rdata->iov[0].iov_base = buf;
++ rdata->iov[1].iov_len = total_len - 4;
++ rdata->iov[1].iov_base = buf + 4;
+
+ if (rdata->credits) {
+ shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(rdata->bytes,
+@@ -2158,12 +2165,17 @@ SMB2_read(const unsigned int xid, struct
+ struct smb2_sync_hdr *shdr;
+ struct kvec iov[1];
+ struct kvec rsp_iov;
++ unsigned int total_len;
++ char *req;
+
+ *nbytes = 0;
+- rc = smb2_new_read_req(iov, io_parms, 0, 0);
++ rc = smb2_new_read_req((void **)&req, &total_len, io_parms, 0, 0);
+ if (rc)
+ return rc;
+
++ iov[0].iov_base = buf;
++ iov[0].iov_len = total_len;
++
+ rc = SendReceive2(xid, io_parms->tcon->ses, iov, 1,
+ &resp_buftype, CIFS_LOG_ERROR, &rsp_iov);
+ cifs_small_buf_release(iov[0].iov_base);
+@@ -2268,8 +2280,8 @@ smb2_async_writev(struct cifs_writedata
+ struct smb2_sync_hdr *shdr;
+ struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink);
+ struct TCP_Server_Info *server = tcon->ses->server;
+- struct kvec iov;
+- struct smb_rqst rqst;
++ struct kvec iov[2];
++ struct smb_rqst rqst = { };
+
+ rc = small_smb2_init(SMB2_WRITE, tcon, (void **) &req);
+ if (rc) {
+@@ -2299,11 +2311,13 @@ smb2_async_writev(struct cifs_writedata
+ req->RemainingBytes = 0;
+
+ /* 4 for rfc1002 length field and 1 for Buffer */
+- iov.iov_len = get_rfc1002_length(req) + 4 - 1;
+- iov.iov_base = req;
++ iov[0].iov_len = 4;
++ iov[0].iov_base = req;
++ iov[1].iov_len = get_rfc1002_length(req) - 1;
++ iov[1].iov_base = (char *)req + 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;
+diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c
+--- a/fs/cifs/smb2transport.c
++++ b/fs/cifs/smb2transport.c
+@@ -139,7 +139,7 @@ smb2_calc_signature(struct smb_rqst *rqs
+ unsigned char *sigptr = smb2_signature;
+ struct kvec *iov = rqst->rq_iov;
+ int n_vec = rqst->rq_nvec;
+- struct smb2_sync_hdr *shdr = get_sync_hdr(iov[0].iov_base);
++ struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)iov[1].iov_base;
+ struct cifs_ses *ses;
+
+ ses = smb2_find_smb_ses(shdr, server);
+@@ -400,7 +400,7 @@ smb3_calc_signature(struct smb_rqst *rqs
+ unsigned char *sigptr = smb3_signature;
+ struct kvec *iov = rqst->rq_iov;
+ int n_vec = rqst->rq_nvec;
+- struct smb2_sync_hdr *shdr = get_sync_hdr(iov[0].iov_base);
++ struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)iov[1].iov_base;
+ struct cifs_ses *ses;
+
+ ses = smb2_find_smb_ses(shdr, server);
+@@ -487,7 +487,8 @@ static int
+ smb2_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server)
+ {
+ int rc = 0;
+- struct smb2_sync_hdr *shdr = get_sync_hdr(rqst->rq_iov[0].iov_base);
++ struct smb2_sync_hdr *shdr =
++ (struct smb2_sync_hdr *)rqst->rq_iov[1].iov_base;
+
+ if (!(shdr->Flags & SMB2_FLAGS_SIGNED) ||
+ server->tcpStatus == CifsNeedNegotiate)
+@@ -508,7 +509,8 @@ smb2_verify_signature(struct smb_rqst *r
+ {
+ unsigned int rc;
+ char server_response_sig[16];
+- struct smb2_sync_hdr *shdr = get_sync_hdr(rqst->rq_iov[0].iov_base);
++ struct smb2_sync_hdr *shdr =
++ (struct smb2_sync_hdr *)rqst->rq_iov[1].iov_base;
+
+ if ((shdr->Command == SMB2_NEGOTIATE) ||
+ (shdr->Command == SMB2_SESSION_SETUP) ||
+@@ -637,12 +639,14 @@ smb2_check_receive(struct mid_q_entry *m
+ bool log_error)
+ {
+ unsigned int len = get_rfc1002_length(mid->resp_buf);
+- struct kvec iov;
+- struct smb_rqst rqst = { .rq_iov = &iov,
+- .rq_nvec = 1 };
+-
+- iov.iov_base = (char *)mid->resp_buf;
+- iov.iov_len = get_rfc1002_length(mid->resp_buf) + 4;
++ struct kvec iov[2];
++ struct smb_rqst rqst = { .rq_iov = iov,
++ .rq_nvec = 2 };
++
++ iov[0].iov_base = (char *)mid->resp_buf;
++ iov[0].iov_len = 4;
++ iov[1].iov_base = (char *)mid->resp_buf + 4;
++ iov[1].iov_len = len;
+
+ dump_smb(mid->resp_buf, min_t(u32, 80, len));
+ /* convert the length into a more usable form */
+@@ -662,7 +666,8 @@ struct mid_q_entry *
+ smb2_setup_request(struct cifs_ses *ses, struct smb_rqst *rqst)
+ {
+ int rc;
+- struct smb2_sync_hdr *shdr = get_sync_hdr(rqst->rq_iov[0].iov_base);
++ struct smb2_sync_hdr *shdr =
++ (struct smb2_sync_hdr *)rqst->rq_iov[1].iov_base;
+ struct mid_q_entry *mid;
+
+ smb2_seq_num_into_buf(ses->server, shdr);
+@@ -682,7 +687,8 @@ struct mid_q_entry *
+ smb2_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst)
+ {
+ int rc;
+- struct smb2_sync_hdr *shdr = get_sync_hdr(rqst->rq_iov[0].iov_base);
++ struct smb2_sync_hdr *shdr =
++ (struct smb2_sync_hdr *)rqst->rq_iov[1].iov_base;
+ struct mid_q_entry *mid;
+
+ smb2_seq_num_into_buf(server, shdr);
+diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c
+--- a/fs/cifs/transport.c
++++ b/fs/cifs/transport.c
+@@ -314,8 +314,12 @@ smb_send_rqst(struct TCP_Server_Info *se
+ return -EIO;
+ }
+
++ if (n_vec < 2)
++ return -EIO;
++
+ cifs_dbg(FYI, "Sending smb: smb_len=%u\n", smb_buf_length);
+ dump_smb(iov[0].iov_base, iov[0].iov_len);
++ dump_smb(iov[1].iov_base, iov[1].iov_len);
+
+ /* cork the socket */
+ kernel_setsockopt(ssocket, SOL_TCP, TCP_CORK,
+@@ -379,12 +383,14 @@ int
+ smb_send(struct TCP_Server_Info *server, struct smb_hdr *smb_buffer,
+ unsigned int smb_buf_length)
+ {
+- struct kvec iov;
++ struct kvec iov[2];
+
+- iov.iov_base = smb_buffer;
+- iov.iov_len = smb_buf_length + 4;
++ iov[0].iov_base = smb_buffer;
++ iov[0].iov_len = 4;
++ iov[1].iov_base = (char *)smb_buffer + 4;
++ iov[1].iov_len = smb_buf_length;
+
+- return smb_sendv(server, &iov, 1);
++ return smb_sendv(server, iov, 2);
+ }
+
+ static int
+@@ -512,6 +518,10 @@ cifs_setup_async_request(struct TCP_Serv
+ struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
+ struct mid_q_entry *mid;
+
++ if (rqst->rq_iov[0].iov_len != 4 ||
++ rqst->rq_iov[0].iov_base + 4 != rqst->rq_iov[1].iov_base)
++ return ERR_PTR(-EIO);
++
+ /* enable signing if server requires it */
+ if (server->sign)
+ hdr->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
+@@ -535,8 +545,8 @@ cifs_setup_async_request(struct TCP_Serv
+ */
+ 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_receive_t *receive, mid_callback_t *callback, void *cbdata,
++ const int flags)
+ {
+ int rc, timeout, optype;
+ struct mid_q_entry *mid;
+@@ -671,13 +681,15 @@ cifs_check_receive(struct mid_q_entry *m
+
+ /* convert the length into a more usable form */
+ if (server->sign) {
+- struct kvec iov;
++ struct kvec iov[2];
+ int rc = 0;
+- struct smb_rqst rqst = { .rq_iov = &iov,
+- .rq_nvec = 1 };
++ struct smb_rqst rqst = { .rq_iov = iov,
++ .rq_nvec = 2 };
+
+- iov.iov_base = mid->resp_buf;
+- iov.iov_len = len;
++ iov[0].iov_base = mid->resp_buf;
++ iov[0].iov_len = 4;
++ iov[1].iov_base = (char *)mid->resp_buf + 4;
++ iov[1].iov_len = len - 4;
+ /* FIXME: add code to kill session */
+ rc = cifs_verify_signature(&rqst, server,
+ mid->sequence_number);
+@@ -697,6 +709,10 @@ cifs_setup_request(struct cifs_ses *ses,
+ struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
+ struct mid_q_entry *mid;
+
++ if (rqst->rq_iov[0].iov_len != 4 ||
++ rqst->rq_iov[0].iov_base + 4 != rqst->rq_iov[1].iov_base)
++ return ERR_PTR(-EIO);
++
+ rc = allocate_mid(ses, hdr, &mid);
+ if (rc)
+ return ERR_PTR(rc);
+@@ -708,18 +724,16 @@ cifs_setup_request(struct cifs_ses *ses,
+ return mid;
+ }
+
+-int
+-SendReceive2(const unsigned int xid, struct cifs_ses *ses,
+- struct kvec *iov, int n_vec, int *resp_buf_type /* ret */,
+- const int flags, struct kvec *resp_iov)
++static 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)
+ {
+ int rc = 0;
+ int timeout, optype;
+ struct mid_q_entry *midQ;
+- char *buf = iov[0].iov_base;
+ unsigned int credits = 1;
+- struct smb_rqst rqst = { .rq_iov = iov,
+- .rq_nvec = n_vec };
++ char *buf;
+
+ timeout = flags & CIFS_TIMEOUT_MASK;
+ optype = flags & CIFS_OP_MASK;
+@@ -752,7 +766,7 @@ SendReceive2(const unsigned int xid, str
+
+ mutex_lock(&ses->server->srv_mutex);
+
+- midQ = ses->server->ops->setup_request(ses, &rqst);
++ midQ = ses->server->ops->setup_request(ses, rqst);
+ if (IS_ERR(midQ)) {
+ mutex_unlock(&ses->server->srv_mutex);
+ /* Update # of requests on wire to server */
+@@ -762,7 +776,7 @@ SendReceive2(const unsigned int xid, str
+
+ midQ->mid_state = MID_REQUEST_SUBMITTED;
+ cifs_in_send_inc(ses->server);
+- rc = smb_sendv(ses->server, iov, n_vec);
++ rc = smb_send_rqst(ses->server, rqst);
+ cifs_in_send_dec(ses->server);
+ cifs_save_when_sent(midQ);
+
+@@ -778,7 +792,7 @@ SendReceive2(const unsigned int xid, str
+
+ rc = wait_for_response(ses->server, midQ);
+ if (rc != 0) {
+- send_cancel(ses->server, &rqst, midQ);
++ send_cancel(ses->server, rqst, midQ);
+ spin_lock(&GlobalMid_Lock);
+ if (midQ->mid_state == MID_REQUEST_SUBMITTED) {
+ midQ->callback = DeleteMidQEntry;
+@@ -824,6 +838,36 @@ out:
+ return rc;
+ }
+
++int
++SendReceive2(const unsigned int xid, struct cifs_ses *ses,
++ struct kvec *iov, int n_vec, int *resp_buf_type /* ret */,
++ const int flags, struct kvec *resp_iov)
++{
++ struct smb_rqst rqst;
++ struct kvec *new_iov;
++ int rc;
++
++ new_iov = kmalloc(sizeof(struct kvec) * (n_vec + 1), GFP_KERNEL);
++ if (!new_iov)
++ return -ENOMEM;
++
++ /* 1st iov is a RFC1001 length followed by the rest of the packet */
++ memcpy(new_iov + 1, iov, (sizeof(struct kvec) * n_vec));
++
++ new_iov[0].iov_base = new_iov[1].iov_base;
++ new_iov[0].iov_len = 4;
++ new_iov[1].iov_base += 4;
++ new_iov[1].iov_len -= 4;
++
++ memset(&rqst, 0, sizeof(struct smb_rqst));
++ rqst.rq_iov = new_iov;
++ rqst.rq_nvec = n_vec + 1;
++
++ rc = cifs_send_recv(xid, ses, &rqst, resp_buf_type, flags, resp_iov);
++ kfree(new_iov);
++ return rc;
++}
++
+ int
+ SendReceive(const unsigned int xid, struct cifs_ses *ses,
+ struct smb_hdr *in_buf, struct smb_hdr *out_buf,
diff --git a/patches.suse/cifs-0009-CIFS-Separate-SMB2-sync-header-processing.patch b/patches.suse/cifs-0009-CIFS-Separate-SMB2-sync-header-processing.patch
new file mode 100644
index 0000000000..6f66202088
--- /dev/null
+++ b/patches.suse/cifs-0009-CIFS-Separate-SMB2-sync-header-processing.patch
@@ -0,0 +1,127 @@
+From cb200bd6264a80c04e09e8635fa4f3901cabdaef Mon Sep 17 00:00:00 2001
+From: Pavel Shilovsky <pshilov@microsoft.com>
+Date: Mon, 24 Oct 2016 16:59:57 -0700
+Subject: [PATCH] CIFS: Separate SMB2 sync header processing
+Patch-mainline: v4.11
+Git-commit: cb200bd6264a80c04e09e8635fa4f3901cabdaef
+References: fate#322075
+
+Do not process RFC1001 length in smb2_hdr_assemble() because
+it is not a part of SMB2 header. This allows to cleanup the code
+and adds a possibility combine several SMB2 packets into one
+for compounding.
+
+Signed-off-by: Pavel Shilovsky <pshilov@microsoft.com>
+Acked-by: Aurelien Aptel <aaptel@suse.com>
+Acked-by: David Mulder <dmulder@suse.com>
+
+---
+ fs/cifs/smb2pdu.c | 50 ++++++++++++++++++++++++++++++--------------------
+ fs/cifs/smb2pdu.h | 5 +++++
+ 2 files changed, 35 insertions(+), 20 deletions(-)
+
+diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
+--- a/fs/cifs/smb2pdu.c
++++ b/fs/cifs/smb2pdu.c
+@@ -79,25 +79,9 @@ static const int smb2_req_struct_sizes[N
+
+
+ static void
+-smb2_hdr_assemble(struct smb2_hdr *hdr, __le16 smb2_cmd /* command */ ,
++smb2_hdr_assemble(struct smb2_sync_hdr *shdr, __le16 smb2_cmd,
+ const struct cifs_tcon *tcon)
+ {
+- struct smb2_pdu *pdu = (struct smb2_pdu *)hdr;
+- struct smb2_sync_hdr *shdr = get_sync_hdr(hdr);
+- char *temp = (char *)hdr;
+- /* lookup word count ie StructureSize from table */
+- __u16 parmsize = smb2_req_struct_sizes[le16_to_cpu(smb2_cmd)];
+-
+- /*
+- * smaller than SMALL_BUFFER_SIZE but bigger than fixed area of
+- * largest operations (Create)
+- */
+- memset(temp, 0, 256);
+-
+- /* Note this is only network field converted to big endian */
+- hdr->smb2_buf_length =
+- cpu_to_be32(parmsize + sizeof(struct smb2_sync_hdr));
+-
+ shdr->ProtocolId = SMB2_PROTO_NUMBER;
+ shdr->StructureSize = cpu_to_le16(64);
+ shdr->Command = smb2_cmd;
+@@ -149,7 +133,6 @@ smb2_hdr_assemble(struct smb2_hdr *hdr,
+ if (tcon->ses && tcon->ses->server && tcon->ses->server->sign)
+ shdr->Flags |= SMB2_FLAGS_SIGNED;
+ out:
+- pdu->StructureSize2 = cpu_to_le16(parmsize);
+ return;
+ }
+
+@@ -282,6 +265,26 @@ out:
+ return rc;
+ }
+
++static void
++fill_small_buf(__le16 smb2_command, struct cifs_tcon *tcon, void *buf,
++ unsigned int *total_len)
++{
++ struct smb2_sync_pdu *spdu = (struct smb2_sync_pdu *)buf;
++ /* lookup word count ie StructureSize from table */
++ __u16 parmsize = smb2_req_struct_sizes[le16_to_cpu(smb2_command)];
++
++ /*
++ * smaller than SMALL_BUFFER_SIZE but bigger than fixed area of
++ * largest operations (Create)
++ */
++ memset(buf, 0, 256);
++
++ smb2_hdr_assemble(&spdu->sync_hdr, smb2_command, tcon);
++ spdu->StructureSize2 = cpu_to_le16(parmsize);
++
++ *total_len = parmsize + sizeof(struct smb2_sync_hdr);
++}
++
+ /*
+ * Allocate and return pointer to an SMB request hdr, and set basic
+ * SMB information in the SMB header. If the return code is zero, this
+@@ -291,7 +294,9 @@ static int
+ small_smb2_init(__le16 smb2_command, struct cifs_tcon *tcon,
+ void **request_buf)
+ {
+- int rc = 0;
++ int rc;
++ unsigned int total_len;
++ struct smb2_pdu *pdu;
+
+ rc = smb2_reconnect(smb2_command, tcon);
+ if (rc)
+@@ -304,7 +309,12 @@ small_smb2_init(__le16 smb2_command, str
+ return -ENOMEM;
+ }
+
+- smb2_hdr_assemble((struct smb2_hdr *) *request_buf, smb2_command, tcon);
++ pdu = (struct smb2_pdu *)(*request_buf);
++
++ fill_small_buf(smb2_command, tcon, get_sync_hdr(pdu), &total_len);
++
++ /* Note this is only network field converted to big endian */
++ pdu->hdr.smb2_buf_length = cpu_to_be32(total_len);
+
+ if (tcon != NULL) {
+ #ifdef CONFIG_CIFS_STATS2
+diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h
+--- a/fs/cifs/smb2pdu.h
++++ b/fs/cifs/smb2pdu.h
+@@ -115,6 +115,11 @@ struct smb2_sync_hdr {
+ __u8 Signature[16];
+ } __packed;
+
++struct smb2_sync_pdu {
++ struct smb2_sync_hdr sync_hdr;
++ __le16 StructureSize2; /* size of wct area (varies, request specific) */
++} __packed;
++
+ struct smb2_hdr {
+ __be32 smb2_buf_length; /* big endian on wire */
+ /* length is only two or three bytes - with */
diff --git a/patches.suse/cifs-0010-CIFS-Separate-RFC1001-length-processing-for-SMB2-rea.patch b/patches.suse/cifs-0010-CIFS-Separate-RFC1001-length-processing-for-SMB2-rea.patch
new file mode 100644
index 0000000000..32f38a9c99
--- /dev/null
+++ b/patches.suse/cifs-0010-CIFS-Separate-RFC1001-length-processing-for-SMB2-rea.patch
@@ -0,0 +1,225 @@
+From b8f57ee8aad414a3122bff72d7968a94baacb9b6 Mon Sep 17 00:00:00 2001
+From: Pavel Shilovsky <pshilov@microsoft.com>
+Date: Wed, 23 Nov 2016 15:31:54 -0800
+Subject: [PATCH] CIFS: Separate RFC1001 length processing for SMB2 read
+Patch-mainline: v4.11
+Git-commit: b8f57ee8aad414a3122bff72d7968a94baacb9b6
+References: fate#322075
+
+Allocate and initialize SMB2 read request without RFC1001 length
+field to directly call cifs_send_recv() rather than SendReceive2()
+in a read codepath.
+
+Signed-off-by: Pavel Shilovsky <pshilov@microsoft.com>
+Acked-by: Aurelien Aptel <aaptel@suse.com>
+Acked-by: David Mulder <dmulder@suse.com>
+
+---
+ fs/cifs/cifsproto.h | 3 ++
+ fs/cifs/smb2pdu.c | 89 ++++++++++++++++++++++++++++++++++++++++-------------
+ fs/cifs/smb2pdu.h | 5 +--
+ fs/cifs/transport.c | 2 +-
+ 4 files changed, 74 insertions(+), 25 deletions(-)
+
+diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
+--- a/fs/cifs/cifsproto.h
++++ b/fs/cifs/cifsproto.h
+@@ -79,6 +79,9 @@ extern int cifs_call_async(struct TCP_Se
+ struct smb_rqst *rqst,
+ mid_receive_t *receive, mid_callback_t *callback,
+ 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 */ ,
+diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
+--- a/fs/cifs/smb2pdu.c
++++ b/fs/cifs/smb2pdu.c
+@@ -285,10 +285,46 @@ fill_small_buf(__le16 smb2_command, stru
+ *total_len = parmsize + sizeof(struct smb2_sync_hdr);
+ }
+
++/* init request without RFC1001 length at the beginning */
++static int
++smb2_plain_req_init(__le16 smb2_command, struct cifs_tcon *tcon,
++ void **request_buf, unsigned int *total_len)
++{
++ int rc;
++ struct smb2_sync_hdr *shdr;
++
++ rc = smb2_reconnect(smb2_command, tcon);
++ if (rc)
++ return rc;
++
++ /* BB eventually switch this to SMB2 specific small buf size */
++ *request_buf = cifs_small_buf_get();
++ if (*request_buf == NULL) {
++ /* BB should we add a retry in here if not a writepage? */
++ return -ENOMEM;
++ }
++
++ shdr = (struct smb2_sync_hdr *)(*request_buf);
++
++ fill_small_buf(smb2_command, tcon, shdr, total_len);
++
++ if (tcon != NULL) {
++#ifdef CONFIG_CIFS_STATS2
++ uint16_t com_code = le16_to_cpu(smb2_command);
++
++ cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_sent[com_code]);
++#endif
++ cifs_stats_inc(&tcon->num_smbs_sent);
++ }
++
++ return rc;
++}
++
+ /*
+ * Allocate and return pointer to an SMB request hdr, and set basic
+ * SMB information in the SMB header. If the return code is zero, this
+- * function must have filled in request_buf pointer.
++ * function must have filled in request_buf pointer. The returned buffer
++ * has RFC1001 length at the beginning.
+ */
+ static int
+ small_smb2_init(__le16 smb2_command, struct cifs_tcon *tcon,
+@@ -1984,16 +2020,17 @@ smb2_new_read_req(void **buf, unsigned i
+ int request_type)
+ {
+ int rc = -EACCES;
+- struct smb2_read_req *req = NULL;
++ struct smb2_read_plain_req *req = NULL;
+ struct smb2_sync_hdr *shdr;
+
+- rc = small_smb2_init(SMB2_READ, io_parms->tcon, (void **) &req);
++ rc = smb2_plain_req_init(SMB2_READ, io_parms->tcon, (void **) &req,
++ total_len);
+ if (rc)
+ return rc;
+ if (io_parms->tcon->ses->server == NULL)
+ return -ECONNABORTED;
+
+- shdr = get_sync_hdr(req);
++ shdr = &req->sync_hdr;
+ shdr->ProcessId = cpu_to_le32(io_parms->pid);
+
+ req->PersistentFileId = io_parms->persistent_fid;
+@@ -2007,9 +2044,9 @@ smb2_new_read_req(void **buf, unsigned i
+
+ if (request_type & CHAINED_REQUEST) {
+ if (!(request_type & END_OF_CHAIN)) {
+- /* 4 for rfc1002 length field */
+- shdr->NextCommand =
+- cpu_to_le32(get_rfc1002_length(req) + 4);
++ /* next 8-byte aligned request */
++ *total_len = DIV_ROUND_UP(*total_len, 8) * 8;
++ shdr->NextCommand = cpu_to_le32(*total_len);
+ } else /* END_OF_CHAIN */
+ shdr->NextCommand = 0;
+ if (request_type & RELATED_REQUEST) {
+@@ -2030,8 +2067,6 @@ smb2_new_read_req(void **buf, unsigned i
+ req->RemainingBytes = 0;
+
+ *buf = req;
+- /* 4 for rfc1002 length field */
+- *total_len = get_rfc1002_length(req) + 4;
+ return rc;
+ }
+
+@@ -2108,6 +2143,7 @@ smb2_async_readv(struct cifs_readdata *r
+ .rq_nvec = 2 };
+ struct TCP_Server_Info *server;
+ unsigned int total_len;
++ __be32 req_len;
+
+ cifs_dbg(FYI, "%s: offset=%llu bytes=%u\n",
+ __func__, rdata->offset, rdata->bytes);
+@@ -2134,12 +2170,14 @@ smb2_async_readv(struct cifs_readdata *r
+ return rc;
+ }
+
+- shdr = get_sync_hdr(buf);
+- /* 4 for rfc1002 length field */
+- rdata->iov[0].iov_len = 4;
+- rdata->iov[0].iov_base = buf;
+- rdata->iov[1].iov_len = total_len - 4;
+- rdata->iov[1].iov_base = buf + 4;
++ req_len = cpu_to_be32(total_len);
++
++ rdata->iov[0].iov_base = &req_len;
++ rdata->iov[0].iov_len = sizeof(__be32);
++ rdata->iov[1].iov_base = buf;
++ rdata->iov[1].iov_len = total_len;
++
++ shdr = (struct smb2_sync_hdr *)buf;
+
+ if (rdata->credits) {
+ shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(rdata->bytes,
+@@ -2171,24 +2209,31 @@ SMB2_read(const unsigned int xid, struct
+ unsigned int *nbytes, char **buf, int *buf_type)
+ {
+ int resp_buftype, rc = -EACCES;
++ struct smb2_read_plain_req *req = NULL;
+ struct smb2_read_rsp *rsp = NULL;
+ struct smb2_sync_hdr *shdr;
+- struct kvec iov[1];
++ struct kvec iov[2];
+ struct kvec rsp_iov;
+ unsigned int total_len;
+- char *req;
++ __be32 req_len;
++ struct smb_rqst rqst = { .rq_iov = iov,
++ .rq_nvec = 2 };
+
+ *nbytes = 0;
+ rc = smb2_new_read_req((void **)&req, &total_len, io_parms, 0, 0);
+ if (rc)
+ return rc;
+
+- iov[0].iov_base = buf;
+- iov[0].iov_len = total_len;
++ req_len = cpu_to_be32(total_len);
+
+- rc = SendReceive2(xid, io_parms->tcon->ses, iov, 1,
+- &resp_buftype, CIFS_LOG_ERROR, &rsp_iov);
+- cifs_small_buf_release(iov[0].iov_base);
++ iov[0].iov_base = &req_len;
++ iov[0].iov_len = sizeof(__be32);
++ iov[1].iov_base = req;
++ iov[1].iov_len = total_len;
++
++ rc = cifs_send_recv(xid, io_parms->tcon->ses, &rqst, &resp_buftype,
++ CIFS_LOG_ERROR, &rsp_iov);
++ cifs_small_buf_release(req);
+
+ rsp = (struct smb2_read_rsp *)rsp_iov.iov_base;
+ shdr = get_sync_hdr(rsp);
+diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h
+--- a/fs/cifs/smb2pdu.h
++++ b/fs/cifs/smb2pdu.h
+@@ -821,8 +821,9 @@ struct smb2_flush_rsp {
+ #define SMB2_CHANNEL_RDMA_V1 0x00000001 /* SMB3 or later */
+ #define SMB2_CHANNEL_RDMA_V1_INVALIDATE 0x00000001 /* SMB3.02 or later */
+
+-struct smb2_read_req {
+- struct smb2_hdr hdr;
++/* SMB2 read request without RFC1001 length at the beginning */
++struct smb2_read_plain_req {
++ struct smb2_sync_hdr sync_hdr;
+ __le16 StructureSize; /* Must be 49 */
+ __u8 Padding; /* offset from start of SMB2 header to place read */
+ __u8 Flags; /* MBZ unless SMB3.02 or later */
+diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c
+--- a/fs/cifs/transport.c
++++ b/fs/cifs/transport.c
+@@ -724,7 +724,7 @@ cifs_setup_request(struct cifs_ses *ses,
+ return mid;
+ }
+
+-static int
++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)
diff --git a/patches.suse/cifs-0011-CIFS-Add-capability-to-transform-requests-before-sen.patch b/patches.suse/cifs-0011-CIFS-Add-capability-to-transform-requests-before-sen.patch
new file mode 100644
index 0000000000..26d3371b23
--- /dev/null
+++ b/patches.suse/cifs-0011-CIFS-Add-capability-to-transform-requests-before-sen.patch
@@ -0,0 +1,633 @@
+From 7fb8986e7449d0a5cebd84d059927afa423fbf85 Mon Sep 17 00:00:00 2001
+From: Pavel Shilovsky <pshilov@microsoft.com>
+Date: Mon, 31 Oct 2016 13:49:30 -0700
+Subject: [PATCH] CIFS: Add capability to transform requests before sending
+Patch-mainline: v4.11
+Git-commit: 7fb8986e7449d0a5cebd84d059927afa423fbf85
+References: fate#322075
+
+This will allow us to do protocol specific tranformations of packets
+before sending to the server. For SMB3 it can be used to support
+encryption.
+
+Signed-off-by: Pavel Shilovsky <pshilov@microsoft.com>
+Acked-by: Aurelien Aptel <aaptel@suse.com>
+Acked-by: David Mulder <dmulder@suse.com>
+
+---
+ fs/cifs/cifsglob.h | 7 +++
+ fs/cifs/smb2pdu.c | 129 ++++++++++++++++++++++++++++++++++++++++++----------
+ fs/cifs/transport.c | 33 ++++++++++----
+ 3 files changed, 136 insertions(+), 33 deletions(-)
+
+diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
+--- a/fs/cifs/cifsglob.h
++++ b/fs/cifs/cifsglob.h
+@@ -417,6 +417,11 @@ 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 *);
+ };
+
+ struct smb_version_values {
+@@ -1450,7 +1455,9 @@ static inline void free_dfs_info_array(s
+ #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/smb2pdu.c b/fs/cifs/smb2pdu.c
+--- a/fs/cifs/smb2pdu.c
++++ b/fs/cifs/smb2pdu.c
+@@ -77,6 +77,13 @@ static const int smb2_req_struct_sizes[N
+ /* SMB2_OPLOCK_BREAK */ 24 /* BB this is 36 for LEASE_BREAK variant */
+ };
+
++static int encryption_required(const struct cifs_tcon *tcon)
++{
++ if ((tcon->ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA) ||
++ (tcon->share_flags & SHI1005_FLAGS_ENCRYPT_DATA))
++ return 1;
++ return 0;
++}
+
+ static void
+ smb2_hdr_assemble(struct smb2_sync_hdr *shdr, __le16 smb2_cmd,
+@@ -130,7 +137,8 @@ smb2_hdr_assemble(struct smb2_sync_hdr *
+ /* if (tcon->share_flags & SHI1005_FLAGS_DFS)
+ shdr->Flags |= SMB2_FLAGS_DFS_OPERATIONS; */
+
+- if (tcon->ses && tcon->ses->server && tcon->ses->server->sign)
++ if (tcon->ses && tcon->ses->server && tcon->ses->server->sign &&
++ !encryption_required(tcon))
+ shdr->Flags |= SMB2_FLAGS_SIGNED;
+ out:
+ return;
+@@ -415,7 +423,6 @@ static void assemble_neg_contexts(struct
+ }
+ #endif /* SMB311 */
+
+-
+ /*
+ *
+ * SMB2 Worker functions follow:
+@@ -915,6 +922,7 @@ SMB2_logoff(const unsigned int xid, stru
+ struct smb2_logoff_req *req; /* response is also trivial struct */
+ int rc = 0;
+ struct TCP_Server_Info *server;
++ int flags = 0;
+
+ cifs_dbg(FYI, "disconnect session %p\n", ses);
+
+@@ -933,10 +941,13 @@ SMB2_logoff(const unsigned int xid, stru
+
+ /* since no tcon, smb2_init can not do this, so do here */
+ req->hdr.sync_hdr.SessionId = ses->Suid;
+- if (server->sign)
++
++ if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA)
++ flags |= CIFS_TRANSFORM_REQ;
++ else if (server->sign)
+ req->hdr.sync_hdr.Flags |= SMB2_FLAGS_SIGNED;
+
+- rc = SendReceiveNoRsp(xid, ses, (char *) req, 0);
++ rc = SendReceiveNoRsp(xid, ses, (char *) req, flags);
+ cifs_small_buf_release(req);
+ /*
+ * No tcon so can't do
+@@ -975,6 +986,7 @@ SMB2_tcon(const unsigned int xid, struct
+ int unc_path_len;
+ struct TCP_Server_Info *server;
+ __le16 *unc_path = NULL;
++ int flags = 0;
+
+ cifs_dbg(FYI, "TCON\n");
+
+@@ -1009,6 +1021,9 @@ SMB2_tcon(const unsigned int xid, struct
+ return rc;
+ }
+
++ if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA)
++ flags |= CIFS_TRANSFORM_REQ;
++
+ if (tcon == NULL) {
+ /* since no tcon, smb2_init can not do this, so do here */
+ req->hdr.sync_hdr.SessionId = ses->Suid;
+@@ -1029,7 +1044,7 @@ SMB2_tcon(const unsigned int xid, struct
+
+ inc_rfc1001_len(req, unc_path_len - 1 /* pad */);
+
+- rc = SendReceive2(xid, ses, iov, 2, &resp_buftype, 0, &rsp_iov);
++ rc = SendReceive2(xid, ses, iov, 2, &resp_buftype, flags, &rsp_iov);
+ cifs_small_buf_release(req);
+ rsp = (struct smb2_tree_connect_rsp *)rsp_iov.iov_base;
+
+@@ -1097,6 +1112,7 @@ SMB2_tdis(const unsigned int xid, struct
+ int rc = 0;
+ struct TCP_Server_Info *server;
+ struct cifs_ses *ses = tcon->ses;
++ int flags = 0;
+
+ cifs_dbg(FYI, "Tree Disconnect\n");
+
+@@ -1112,7 +1128,10 @@ SMB2_tdis(const unsigned int xid, struct
+ if (rc)
+ return rc;
+
+- rc = SendReceiveNoRsp(xid, ses, (char *)req, 0);
++ if (encryption_required(tcon))
++ flags |= CIFS_TRANSFORM_REQ;
++
++ rc = SendReceiveNoRsp(xid, ses, (char *)req, flags);
+ cifs_small_buf_release(req);
+ if (rc)
+ cifs_stats_fail_inc(tcon, SMB2_TREE_DISCONNECT_HE);
+@@ -1384,6 +1403,7 @@ SMB2_open(const unsigned int xid, struct
+ unsigned int n_iov = 2;
+ __u32 file_attributes = 0;
+ char *dhc_buf = NULL, *lc_buf = NULL;
++ int flags = 0;
+
+ cifs_dbg(FYI, "create/open\n");
+
+@@ -1396,6 +1416,9 @@ SMB2_open(const unsigned int xid, struct
+ if (rc)
+ return rc;
+
++ if (encryption_required(tcon))
++ flags |= CIFS_TRANSFORM_REQ;
++
+ if (oparms->create_options & CREATE_OPTION_READONLY)
+ file_attributes |= ATTR_READONLY;
+ if (oparms->create_options & CREATE_OPTION_SPECIAL)
+@@ -1475,7 +1498,7 @@ SMB2_open(const unsigned int xid, struct
+ dhc_buf = iov[n_iov-1].iov_base;
+ }
+
+- rc = SendReceive2(xid, ses, iov, n_iov, &resp_buftype, 0, &rsp_iov);
++ rc = SendReceive2(xid, ses, iov, n_iov, &resp_buftype, flags, &rsp_iov);
+ cifs_small_buf_release(req);
+ rsp = (struct smb2_create_rsp *)rsp_iov.iov_base;
+
+@@ -1529,6 +1552,7 @@ SMB2_ioctl(const unsigned int xid, struc
+ int resp_buftype;
+ int n_iov;
+ int rc = 0;
++ int flags = 0;
+
+ cifs_dbg(FYI, "SMB2 IOCTL\n");
+
+@@ -1553,6 +1577,9 @@ SMB2_ioctl(const unsigned int xid, struc
+ if (rc)
+ return rc;
+
++ if (encryption_required(tcon))
++ flags |= CIFS_TRANSFORM_REQ;
++
+ req->CtlCode = cpu_to_le32(opcode);
+ req->PersistentFileId = persistent_fid;
+ req->VolatileFileId = volatile_fid;
+@@ -1603,7 +1630,7 @@ SMB2_ioctl(const unsigned int xid, struc
+ iov[0].iov_len = get_rfc1002_length(req) + 4;
+
+
+- rc = SendReceive2(xid, ses, iov, n_iov, &resp_buftype, 0, &rsp_iov);
++ rc = SendReceive2(xid, ses, iov, n_iov, &resp_buftype, flags, &rsp_iov);
+ cifs_small_buf_release(req);
+ rsp = (struct smb2_ioctl_rsp *)rsp_iov.iov_base;
+
+@@ -1692,6 +1719,7 @@ SMB2_close(const unsigned int xid, struc
+ struct kvec rsp_iov;
+ int resp_buftype;
+ int rc = 0;
++ int flags = 0;
+
+ cifs_dbg(FYI, "Close\n");
+
+@@ -1704,6 +1732,9 @@ SMB2_close(const unsigned int xid, struc
+ if (rc)
+ return rc;
+
++ if (encryption_required(tcon))
++ flags |= CIFS_TRANSFORM_REQ;
++
+ req->PersistentFileId = persistent_fid;
+ req->VolatileFileId = volatile_fid;
+
+@@ -1711,7 +1742,7 @@ SMB2_close(const unsigned int xid, struc
+ /* 4 for rfc1002 length field */
+ iov[0].iov_len = get_rfc1002_length(req) + 4;
+
+- rc = SendReceive2(xid, ses, iov, 1, &resp_buftype, 0, &rsp_iov);
++ rc = SendReceive2(xid, ses, iov, 1, &resp_buftype, flags, &rsp_iov);
+ cifs_small_buf_release(req);
+ rsp = (struct smb2_close_rsp *)rsp_iov.iov_base;
+
+@@ -1797,6 +1828,7 @@ query_info(const unsigned int xid, struc
+ int resp_buftype;
+ struct TCP_Server_Info *server;
+ struct cifs_ses *ses = tcon->ses;
++ int flags = 0;
+
+ cifs_dbg(FYI, "Query Info\n");
+
+@@ -1809,6 +1841,9 @@ query_info(const unsigned int xid, struc
+ if (rc)
+ return rc;
+
++ if (encryption_required(tcon))
++ flags |= CIFS_TRANSFORM_REQ;
++
+ req->InfoType = SMB2_O_INFO_FILE;
+ req->FileInfoClass = info_class;
+ req->PersistentFileId = persistent_fid;
+@@ -1822,7 +1857,7 @@ query_info(const unsigned int xid, struc
+ /* 4 for rfc1002 length field */
+ iov[0].iov_len = get_rfc1002_length(req) + 4;
+
+- rc = SendReceive2(xid, ses, iov, 1, &resp_buftype, 0, &rsp_iov);
++ rc = SendReceive2(xid, ses, iov, 1, &resp_buftype, flags, &rsp_iov);
+ cifs_small_buf_release(req);
+ rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base;
+
+@@ -1981,6 +2016,7 @@ SMB2_flush(const unsigned int xid, struc
+ struct kvec rsp_iov;
+ int resp_buftype;
+ int rc = 0;
++ int flags = 0;
+
+ cifs_dbg(FYI, "Flush\n");
+
+@@ -1993,6 +2029,9 @@ SMB2_flush(const unsigned int xid, struc
+ if (rc)
+ return rc;
+
++ if (encryption_required(tcon))
++ flags |= CIFS_TRANSFORM_REQ;
++
+ req->PersistentFileId = persistent_fid;
+ req->VolatileFileId = volatile_fid;
+
+@@ -2000,7 +2039,7 @@ SMB2_flush(const unsigned int xid, struc
+ /* 4 for rfc1002 length field */
+ iov[0].iov_len = get_rfc1002_length(req) + 4;
+
+- rc = SendReceive2(xid, ses, iov, 1, &resp_buftype, 0, &rsp_iov);
++ rc = SendReceive2(xid, ses, iov, 1, &resp_buftype, flags, &rsp_iov);
+ cifs_small_buf_release(req);
+
+ if (rc != 0)
+@@ -2170,6 +2209,9 @@ smb2_async_readv(struct cifs_readdata *r
+ return rc;
+ }
+
++ if (encryption_required(io_parms.tcon))
++ flags |= CIFS_TRANSFORM_REQ;
++
+ req_len = cpu_to_be32(total_len);
+
+ rdata->iov[0].iov_base = &req_len;
+@@ -2188,7 +2230,7 @@ smb2_async_readv(struct cifs_readdata *r
+ le16_to_cpu(shdr->CreditCharge);
+ spin_unlock(&server->req_lock);
+ wake_up(&server->request_q);
+- flags = CIFS_HAS_CREDITS;
++ flags |= CIFS_HAS_CREDITS;
+ }
+
+ kref_get(&rdata->refcount);
+@@ -2218,12 +2260,17 @@ SMB2_read(const unsigned int xid, struct
+ __be32 req_len;
+ struct smb_rqst rqst = { .rq_iov = iov,
+ .rq_nvec = 2 };
++ int flags = CIFS_LOG_ERROR;
++ struct cifs_ses *ses = io_parms->tcon->ses;
+
+ *nbytes = 0;
+ rc = smb2_new_read_req((void **)&req, &total_len, io_parms, 0, 0);
+ if (rc)
+ return rc;
+
++ if (encryption_required(io_parms->tcon))
++ flags |= CIFS_TRANSFORM_REQ;
++
+ req_len = cpu_to_be32(total_len);
+
+ iov[0].iov_base = &req_len;
+@@ -2231,8 +2278,7 @@ SMB2_read(const unsigned int xid, struct
+ iov[1].iov_base = req;
+ iov[1].iov_len = total_len;
+
+- rc = cifs_send_recv(xid, io_parms->tcon->ses, &rqst, &resp_buftype,
+- CIFS_LOG_ERROR, &rsp_iov);
++ rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov);
+ cifs_small_buf_release(req);
+
+ rsp = (struct smb2_read_rsp *)rsp_iov.iov_base;
+@@ -2351,6 +2397,9 @@ smb2_async_writev(struct cifs_writedata
+ goto async_writev_out;
+ }
+
++ if (encryption_required(tcon))
++ flags |= CIFS_TRANSFORM_REQ;
++
+ shdr = get_sync_hdr(req);
+ shdr->ProcessId = cpu_to_le32(wdata->cfile->pid);
+
+@@ -2394,7 +2443,7 @@ smb2_async_writev(struct cifs_writedata
+ le16_to_cpu(shdr->CreditCharge);
+ spin_unlock(&server->req_lock);
+ wake_up(&server->request_q);
+- flags = CIFS_HAS_CREDITS;
++ flags |= CIFS_HAS_CREDITS;
+ }
+
+ kref_get(&wdata->refcount);
+@@ -2426,6 +2475,7 @@ SMB2_write(const unsigned int xid, struc
+ struct smb2_write_rsp *rsp = NULL;
+ int resp_buftype;
+ struct kvec rsp_iov;
++ int flags = 0;
+
+ *nbytes = 0;
+
+@@ -2439,6 +2489,9 @@ SMB2_write(const unsigned int xid, struc
+ if (io_parms->tcon->ses->server == NULL)
+ return -ECONNABORTED;
+
++ if (encryption_required(io_parms->tcon))
++ flags |= CIFS_TRANSFORM_REQ;
++
+ req->hdr.sync_hdr.ProcessId = cpu_to_le32(io_parms->pid);
+
+ req->PersistentFileId = io_parms->persistent_fid;
+@@ -2461,7 +2514,7 @@ SMB2_write(const unsigned int xid, struc
+ inc_rfc1001_len(req, io_parms->length - 1 /* Buffer */);
+
+ rc = SendReceive2(xid, io_parms->tcon->ses, iov, n_vec + 1,
+- &resp_buftype, 0, &rsp_iov);
++ &resp_buftype, flags, &rsp_iov);
+ cifs_small_buf_release(req);
+ rsp = (struct smb2_write_rsp *)rsp_iov.iov_base;
+
+@@ -2537,6 +2590,7 @@ SMB2_query_directory(const unsigned int
+ char *end_of_smb;
+ unsigned int output_size = CIFSMaxBufSize;
+ size_t info_buf_size;
++ int flags = 0;
+
+ if (ses && (ses->server))
+ server = ses->server;
+@@ -2547,6 +2601,9 @@ SMB2_query_directory(const unsigned int
+ if (rc)
+ return rc;
+
++ if (encryption_required(tcon))
++ flags |= CIFS_TRANSFORM_REQ;
++
+ switch (srch_inf->info_level) {
+ case SMB_FIND_FILE_DIRECTORY_INFO:
+ req->FileInformationClass = FILE_DIRECTORY_INFORMATION;
+@@ -2591,7 +2648,7 @@ SMB2_query_directory(const unsigned int
+
+ inc_rfc1001_len(req, len - 1 /* Buffer */);
+
+- rc = SendReceive2(xid, ses, iov, 2, &resp_buftype, 0, &rsp_iov);
++ rc = SendReceive2(xid, ses, iov, 2, &resp_buftype, flags, &rsp_iov);
+ cifs_small_buf_release(req);
+ rsp = (struct smb2_query_directory_rsp *)rsp_iov.iov_base;
+
+@@ -2659,6 +2716,7 @@ send_set_info(const unsigned int xid, st
+ unsigned int i;
+ struct TCP_Server_Info *server;
+ struct cifs_ses *ses = tcon->ses;
++ int flags = 0;
+
+ if (ses && (ses->server))
+ server = ses->server;
+@@ -2678,6 +2736,9 @@ send_set_info(const unsigned int xid, st
+ return rc;
+ }
+
++ if (encryption_required(tcon))
++ flags |= CIFS_TRANSFORM_REQ;
++
+ req->hdr.sync_hdr.ProcessId = cpu_to_le32(pid);
+
+ req->InfoType = SMB2_O_INFO_FILE;
+@@ -2705,7 +2766,7 @@ send_set_info(const unsigned int xid, st
+ iov[i].iov_len = size[i];
+ }
+
+- rc = SendReceive2(xid, ses, iov, num, &resp_buftype, 0, &rsp_iov);
++ rc = SendReceive2(xid, ses, iov, num, &resp_buftype, flags, &rsp_iov);
+ cifs_small_buf_release(req);
+ rsp = (struct smb2_set_info_rsp *)rsp_iov.iov_base;
+
+@@ -2835,19 +2896,22 @@ SMB2_oplock_break(const unsigned int xid
+ {
+ int rc;
+ struct smb2_oplock_break *req = NULL;
++ int flags = CIFS_OBREAK_OP;
+
+ cifs_dbg(FYI, "SMB2_oplock_break\n");
+ rc = small_smb2_init(SMB2_OPLOCK_BREAK, tcon, (void **) &req);
+-
+ if (rc)
+ return rc;
+
++ if (encryption_required(tcon))
++ flags |= CIFS_TRANSFORM_REQ;
++
+ req->VolatileFid = volatile_fid;
+ req->PersistentFid = persistent_fid;
+ req->OplockLevel = oplock_level;
+ req->hdr.sync_hdr.CreditRequest = cpu_to_le16(1);
+
+- rc = SendReceiveNoRsp(xid, tcon->ses, (char *) req, CIFS_OBREAK_OP);
++ rc = SendReceiveNoRsp(xid, tcon->ses, (char *) req, flags);
+ cifs_small_buf_release(req);
+
+ if (rc) {
+@@ -2913,6 +2977,7 @@ SMB2_QFS_info(const unsigned int xid, st
+ int resp_buftype;
+ struct cifs_ses *ses = tcon->ses;
+ struct smb2_fs_full_size_info *info = NULL;
++ int flags = 0;
+
+ rc = build_qfs_info_req(&iov, tcon, FS_FULL_SIZE_INFORMATION,
+ sizeof(struct smb2_fs_full_size_info),
+@@ -2920,7 +2985,10 @@ SMB2_QFS_info(const unsigned int xid, st
+ if (rc)
+ return rc;
+
+- rc = SendReceive2(xid, ses, &iov, 1, &resp_buftype, 0, &rsp_iov);
++ if (encryption_required(tcon))
++ flags |= CIFS_TRANSFORM_REQ;
++
++ rc = SendReceive2(xid, ses, &iov, 1, &resp_buftype, flags, &rsp_iov);
+ cifs_small_buf_release(iov.iov_base);
+ if (rc) {
+ cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE);
+@@ -2952,6 +3020,7 @@ SMB2_QFS_attr(const unsigned int xid, st
+ int resp_buftype, max_len, min_len;
+ struct cifs_ses *ses = tcon->ses;
+ unsigned int rsp_len, offset;
++ int flags = 0;
+
+ if (level == FS_DEVICE_INFORMATION) {
+ max_len = sizeof(FILE_SYSTEM_DEVICE_INFO);
+@@ -2972,7 +3041,10 @@ SMB2_QFS_attr(const unsigned int xid, st
+ if (rc)
+ return rc;
+
+- rc = SendReceive2(xid, ses, &iov, 1, &resp_buftype, 0, &rsp_iov);
++ if (encryption_required(tcon))
++ flags |= CIFS_TRANSFORM_REQ;
++
++ rc = SendReceive2(xid, ses, &iov, 1, &resp_buftype, flags, &rsp_iov);
+ cifs_small_buf_release(iov.iov_base);
+ if (rc) {
+ cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE);
+@@ -3017,6 +3089,7 @@ smb2_lockv(const unsigned int xid, struc
+ struct kvec rsp_iov;
+ int resp_buf_type;
+ unsigned int count;
++ int flags = CIFS_NO_RESP;
+
+ cifs_dbg(FYI, "smb2_lockv num lock %d\n", num_lock);
+
+@@ -3024,6 +3097,9 @@ smb2_lockv(const unsigned int xid, struc
+ if (rc)
+ return rc;
+
++ if (encryption_required(tcon))
++ flags |= CIFS_TRANSFORM_REQ;
++
+ req->hdr.sync_hdr.ProcessId = cpu_to_le32(pid);
+ req->LockCount = cpu_to_le16(num_lock);
+
+@@ -3040,7 +3116,7 @@ smb2_lockv(const unsigned int xid, struc
+ iov[1].iov_len = count;
+
+ 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, flags,
+ &rsp_iov);
+ cifs_small_buf_release(req);
+ if (rc) {
+@@ -3074,13 +3150,16 @@ SMB2_lease_break(const unsigned int xid,
+ {
+ int rc;
+ struct smb2_lease_ack *req = NULL;
++ int flags = CIFS_OBREAK_OP;
+
+ cifs_dbg(FYI, "SMB2_lease_break\n");
+ rc = small_smb2_init(SMB2_OPLOCK_BREAK, tcon, (void **) &req);
+-
+ if (rc)
+ return rc;
+
++ if (encryption_required(tcon))
++ flags |= CIFS_TRANSFORM_REQ;
++
+ req->hdr.sync_hdr.CreditRequest = cpu_to_le16(1);
+ req->StructureSize = cpu_to_le16(36);
+ inc_rfc1001_len(req, 12);
+@@ -3088,7 +3167,7 @@ SMB2_lease_break(const unsigned int xid,
+ memcpy(req->LeaseKey, lease_key, 16);
+ req->LeaseState = lease_state;
+
+- rc = SendReceiveNoRsp(xid, tcon->ses, (char *) req, CIFS_OBREAK_OP);
++ rc = SendReceiveNoRsp(xid, tcon->ses, (char *) req, flags);
+ cifs_small_buf_release(req);
+
+ if (rc) {
+diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c
+--- a/fs/cifs/transport.c
++++ b/fs/cifs/transport.c
+@@ -291,7 +291,7 @@ rqst_len(struct smb_rqst *rqst)
+ }
+
+ static int
+-smb_send_rqst(struct TCP_Server_Info *server, struct smb_rqst *rqst)
++__smb_send_rqst(struct TCP_Server_Info *server, struct smb_rqst *rqst)
+ {
+ int rc;
+ struct kvec *iov = rqst->rq_iov;
+@@ -371,12 +371,27 @@ uncork:
+ }
+
+ static int
+-smb_sendv(struct TCP_Server_Info *server, struct kvec *iov, int n_vec)
++smb_send_rqst(struct TCP_Server_Info *server, struct smb_rqst *rqst, int flags)
+ {
+- struct smb_rqst rqst = { .rq_iov = iov,
+- .rq_nvec = n_vec };
++ struct smb_rqst cur_rqst;
++ int rc;
++
++ if (!(flags & CIFS_TRANSFORM_REQ))
++ return __smb_send_rqst(server, rqst);
++
++ if (!server->ops->init_transform_rq ||
++ !server->ops->free_transform_rq) {
++ cifs_dbg(VFS, "Encryption requested but transform callbacks are missed\n");
++ return -EIO;
++ }
+
+- return smb_send_rqst(server, &rqst);
++ rc = server->ops->init_transform_rq(server, &cur_rqst, rqst);
++ if (rc)
++ return rc;
++
++ rc = __smb_send_rqst(server, &cur_rqst);
++ server->ops->free_transform_rq(&cur_rqst);
++ return rc;
+ }
+
+ int
+@@ -384,13 +399,15 @@ smb_send(struct TCP_Server_Info *server,
+ unsigned int smb_buf_length)
+ {
+ struct kvec iov[2];
++ struct smb_rqst rqst = { .rq_iov = iov,
++ .rq_nvec = 2 };
+
+ iov[0].iov_base = smb_buffer;
+ iov[0].iov_len = 4;
+ iov[1].iov_base = (char *)smb_buffer + 4;
+ iov[1].iov_len = smb_buf_length;
+
+- return smb_sendv(server, iov, 2);
++ return __smb_send_rqst(server, &rqst);
+ }
+
+ static int
+@@ -582,7 +599,7 @@ cifs_call_async(struct TCP_Server_Info *
+
+
+ cifs_in_send_inc(server);
+- rc = smb_send_rqst(server, rqst);
++ rc = smb_send_rqst(server, rqst, flags);
+ cifs_in_send_dec(server);
+ cifs_save_when_sent(mid);
+
+@@ -776,7 +793,7 @@ cifs_send_recv(const unsigned int xid, s
+
+ midQ->mid_state = MID_REQUEST_SUBMITTED;
+ cifs_in_send_inc(ses->server);
+- rc = smb_send_rqst(ses->server, rqst);
++ rc = smb_send_rqst(ses->server, rqst, flags);
+ cifs_in_send_dec(ses->server);
+ cifs_save_when_sent(midQ);
+
diff --git a/patches.suse/cifs-0012-CIFS-Enable-encryption-during-session-setup-phase.patch b/patches.suse/cifs-0012-CIFS-Enable-encryption-during-session-setup-phase.patch
new file mode 100644
index 0000000000..207918f7fa
--- /dev/null
+++ b/patches.suse/cifs-0012-CIFS-Enable-encryption-during-session-setup-phase.patch
@@ -0,0 +1,94 @@
+From cabfb3680f78981d26c078a26e5c748531257ebb Mon Sep 17 00:00:00 2001
+From: Pavel Shilovsky <pshilov@microsoft.com>
+Date: Mon, 7 Nov 2016 18:20:50 -0800
+Subject: [PATCH] CIFS: Enable encryption during session setup phase
+Patch-mainline: v4.11
+Git-commit: cabfb3680f78981d26c078a26e5c748531257ebb
+References: fate#322075
+
+In order to allow encryption on SMB connection we need to exchange
+a session key and generate encryption and decryption keys.
+
+Signed-off-by: Pavel Shilovsky <pshilov@microsoft.com>
+Acked-by: Aurelien Aptel <aaptel@suse.com>
+Acked-by: David Mulder <dmulder@suse.com>
+
+---
+ fs/cifs/sess.c | 22 ++++++++++------------
+ fs/cifs/smb2pdu.c | 12 ++----------
+ 2 files changed, 12 insertions(+), 22 deletions(-)
+
+diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c
+--- a/fs/cifs/sess.c
++++ b/fs/cifs/sess.c
+@@ -344,13 +344,12 @@ void build_ntlmssp_negotiate_blob(unsign
+ /* BB is NTLMV2 session security format easier to use here? */
+ flags = NTLMSSP_NEGOTIATE_56 | NTLMSSP_REQUEST_TARGET |
+ NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE |
+- NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_EXTENDED_SEC;
+- if (ses->server->sign) {
++ NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_EXTENDED_SEC |
++ NTLMSSP_NEGOTIATE_SEAL;
++ if (ses->server->sign)
+ flags |= NTLMSSP_NEGOTIATE_SIGN;
+- if (!ses->server->session_estab ||
+- ses->ntlmssp->sesskey_per_smbsess)
+- flags |= NTLMSSP_NEGOTIATE_KEY_XCH;
+- }
++ if (!ses->server->session_estab || ses->ntlmssp->sesskey_per_smbsess)
++ flags |= NTLMSSP_NEGOTIATE_KEY_XCH;
+
+ sec_blob->NegotiateFlags = cpu_to_le32(flags);
+
+@@ -407,13 +406,12 @@ int build_ntlmssp_auth_blob(unsigned cha
+ flags = NTLMSSP_NEGOTIATE_56 |
+ NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_TARGET_INFO |
+ NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE |
+- NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_EXTENDED_SEC;
+- if (ses->server->sign) {
++ NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_EXTENDED_SEC |
++ NTLMSSP_NEGOTIATE_SEAL;
++ if (ses->server->sign)
+ flags |= NTLMSSP_NEGOTIATE_SIGN;
+- if (!ses->server->session_estab ||
+- ses->ntlmssp->sesskey_per_smbsess)
+- flags |= NTLMSSP_NEGOTIATE_KEY_XCH;
+- }
++ if (!ses->server->session_estab || ses->ntlmssp->sesskey_per_smbsess)
++ flags |= NTLMSSP_NEGOTIATE_KEY_XCH;
+
+ tmp = *pbuffer + sizeof(AUTHENTICATE_MESSAGE);
+ sec_blob->NegotiateFlags = cpu_to_le32(flags);
+diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
+--- a/fs/cifs/smb2pdu.c
++++ b/fs/cifs/smb2pdu.c
+@@ -878,15 +878,13 @@ ssetup_exit:
+
+ if (!rc) {
+ mutex_lock(&server->srv_mutex);
+- if (server->sign && server->ops->generate_signingkey) {
++ if (server->ops->generate_signingkey) {
+ rc = server->ops->generate_signingkey(ses);
+- kfree(ses->auth_key.response);
+- ses->auth_key.response = NULL;
+ if (rc) {
+ cifs_dbg(FYI,
+ "SMB3 session key generation failed\n");
+ mutex_unlock(&server->srv_mutex);
+- goto keygen_exit;
++ return rc;
+ }
+ }
+ if (!server->session_estab) {
+@@ -902,11 +900,6 @@ ssetup_exit:
+ spin_unlock(&GlobalMid_Lock);
+ }
+
+-keygen_exit:
+- if (!server->sign) {
+- kfree(ses->auth_key.response);
+- ses->auth_key.response = NULL;
+- }
+ if (spnego_key) {
+ key_invalidate(spnego_key);
+ key_put(spnego_key);
diff --git a/patches.suse/cifs-0013-CIFS-Encrypt-SMB3-requests-before-sending.patch b/patches.suse/cifs-0013-CIFS-Encrypt-SMB3-requests-before-sending.patch
new file mode 100644
index 0000000000..5e15a5f692
--- /dev/null
+++ b/patches.suse/cifs-0013-CIFS-Encrypt-SMB3-requests-before-sending.patch
@@ -0,0 +1,541 @@
+From 026e93dc0a3eefb0be060bcb9ecd8d7a7fd5c398 Mon Sep 17 00:00:00 2001
+From: Pavel Shilovsky <pshilov@microsoft.com>
+Date: Thu, 3 Nov 2016 16:47:37 -0700
+Subject: [PATCH] CIFS: Encrypt SMB3 requests before sending
+Patch-mainline: v4.11
+Git-commit: 026e93dc0a3eefb0be060bcb9ecd8d7a7fd5c398
+References: fate#322075
+
+This change allows to encrypt packets if it is required by a server
+for SMB sessions or tree connections.
+
+Signed-off-by: Pavel Shilovsky <pshilov@microsoft.com>
+Acked-by: Aurelien Aptel <aaptel@suse.com>
+Acked-by: David Mulder <dmulder@suse.com>
+
+---
+ fs/cifs/Kconfig | 2 +
+ fs/cifs/cifsencrypt.c | 13 ++-
+ fs/cifs/cifsfs.c | 2 +
+ fs/cifs/cifsglob.h | 2 +
+ fs/cifs/cifsproto.h | 2 +-
+ fs/cifs/connect.c | 4 +-
+ fs/cifs/smb2ops.c | 256 ++++++++++++++++++++++++++++++++++++++++++++++++
+ fs/cifs/smb2pdu.h | 5 +-
+ fs/cifs/smb2proto.h | 3 +
+ fs/cifs/smb2transport.c | 41 +++++++-
+ 10 files changed, 320 insertions(+), 10 deletions(-)
+
+diff --git a/fs/cifs/Kconfig b/fs/cifs/Kconfig
+--- a/fs/cifs/Kconfig
++++ b/fs/cifs/Kconfig
+@@ -174,6 +174,8 @@ config CIFS_SMB2
+ 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
+diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c
+--- a/fs/cifs/cifsencrypt.c
++++ b/fs/cifs/cifsencrypt.c
+@@ -33,6 +33,7 @@
+ #include <linux/ctype.h>
+ #include <linux/random.h>
+ #include <linux/highmem.h>
++#include <crypto/aead.h>
+
+ static int
+ cifs_crypto_shash_md5_allocate(struct TCP_Server_Info *server)
+@@ -843,7 +844,7 @@ calc_seckey(struct cifs_ses *ses)
+ }
+
+ 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);
+@@ -865,6 +866,16 @@ cifs_crypto_shash_release(struct TCP_Ser
+ 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
+--- a/fs/cifs/cifsfs.c
++++ b/fs/cifs/cifsfs.c
+@@ -1316,6 +1316,8 @@ MODULE_SOFTDEP("pre: nls");
+ 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
+--- a/fs/cifs/cifsglob.h
++++ b/fs/cifs/cifsglob.h
+@@ -122,6 +122,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 */
+diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
+--- a/fs/cifs/cifsproto.h
++++ b/fs/cifs/cifsproto.h
+@@ -445,7 +445,7 @@ extern int SMBNTencrypt(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/connect.c b/fs/cifs/connect.c
+--- a/fs/cifs/connect.c
++++ b/fs/cifs/connect.c
+@@ -2151,7 +2151,7 @@ cifs_put_tcp_session(struct TCP_Server_I
+ 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);
+@@ -2264,7 +2264,7 @@ cifs_get_tcp_session(struct smb_vol *vol
+ 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));
+
+diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
+--- a/fs/cifs/smb2ops.c
++++ b/fs/cifs/smb2ops.c
+@@ -20,6 +20,8 @@
+ #include <linux/pagemap.h>
+ #include <linux/vfs.h>
+ #include <linux/falloc.h>
++#include <linux/scatterlist.h>
++#include <crypto/aead.h>
+ #include "cifsglob.h"
+ #include "smb2pdu.h"
+ #include "smb2proto.h"
+@@ -1498,6 +1500,256 @@ smb2_dir_needs_close(struct cifsFileInfo
+ return !cfile->invalidHandle;
+ }
+
++static void
++fill_transform_hdr(struct smb2_transform_hdr *tr_hdr, struct smb_rqst *old_rq)
++{
++ struct smb2_sync_hdr *shdr =
++ (struct smb2_sync_hdr *)old_rq->rq_iov[1].iov_base;
++ unsigned int orig_len = get_rfc1002_length(old_rq->rq_iov[0].iov_base);
++
++ memset(tr_hdr, 0, sizeof(struct smb2_transform_hdr));
++ tr_hdr->ProtocolId = SMB2_TRANSFORM_PROTO_NUM;
++ tr_hdr->OriginalMessageSize = cpu_to_le32(orig_len);
++ tr_hdr->Flags = cpu_to_le16(0x01);
++ get_random_bytes(&tr_hdr->Nonce, SMB3_AES128CMM_NONCE);
++ memcpy(&tr_hdr->SessionId, &shdr->SessionId, 8);
++ inc_rfc1001_len(tr_hdr, sizeof(struct smb2_transform_hdr) - 4);
++ inc_rfc1001_len(tr_hdr, orig_len);
++}
++
++static struct scatterlist *
++init_sg(struct smb_rqst *rqst, u8 *sign)
++{
++ unsigned int sg_len = rqst->rq_nvec + rqst->rq_npages + 1;
++ unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 24;
++ struct scatterlist *sg;
++ unsigned int i;
++ unsigned int j;
++
++ sg = kmalloc_array(sg_len, sizeof(struct scatterlist), GFP_KERNEL);
++ if (!sg)
++ return NULL;
++
++ sg_init_table(sg, sg_len);
++ sg_set_buf(&sg[0], rqst->rq_iov[0].iov_base + 24, assoc_data_len);
++ for (i = 1; i < rqst->rq_nvec; i++)
++ sg_set_buf(&sg[i], rqst->rq_iov[i].iov_base,
++ rqst->rq_iov[i].iov_len);
++ for (j = 0; i < sg_len - 1; i++, j++) {
++ unsigned int len = (j < rqst->rq_npages - 1) ? rqst->rq_pagesz
++ : rqst->rq_tailsz;
++ sg_set_page(&sg[i], rqst->rq_pages[j], len, 0);
++ }
++ sg_set_buf(&sg[sg_len - 1], sign, SMB2_SIGNATURE_SIZE);
++ return sg;
++}
++
++struct cifs_crypt_result {
++ int err;
++ struct completion completion;
++};
++
++static void cifs_crypt_complete(struct crypto_async_request *req, int err)
++{
++ struct cifs_crypt_result *res = req->data;
++
++ if (err == -EINPROGRESS)
++ return;
++
++ res->err = err;
++ complete(&res->completion);
++}
++
++/*
++ * Encrypt or decrypt @rqst message. @rqst has the following format:
++ * iov[0] - transform header (associate data),
++ * iov[1-N] and pages - data to encrypt.
++ * On success return encrypted data in iov[1-N] and pages, leave iov[0]
++ * untouched.
++ */
++static int
++crypt_message(struct TCP_Server_Info *server, struct smb_rqst *rqst, int enc)
++{
++ struct smb2_transform_hdr *tr_hdr =
++ (struct smb2_transform_hdr *)rqst->rq_iov[0].iov_base;
++ unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 24;
++ struct cifs_ses *ses;
++ int rc = 0;
++ struct scatterlist *sg;
++ u8 sign[SMB2_SIGNATURE_SIZE] = {};
++ struct aead_request *req;
++ char *iv;
++ unsigned int iv_len;
++ struct cifs_crypt_result result = {0, };
++ struct crypto_aead *tfm;
++ unsigned int crypt_len = le32_to_cpu(tr_hdr->OriginalMessageSize);
++
++ init_completion(&result.completion);
++
++ ses = smb2_find_smb_ses(server, tr_hdr->SessionId);
++ if (!ses) {
++ cifs_dbg(VFS, "%s: Could not find session\n", __func__);
++ return 0;
++ }
++
++ rc = smb3_crypto_aead_allocate(server);
++ if (rc) {
++ cifs_dbg(VFS, "%s: crypto alloc failed\n", __func__);
++ return rc;
++ }
++
++ tfm = enc ? server->secmech.ccmaesencrypt :
++ server->secmech.ccmaesdecrypt;
++ rc = crypto_aead_setkey(tfm, enc ? ses->smb3encryptionkey :
++ ses->smb3decryptionkey, SMB3_SIGN_KEY_SIZE);
++ if (rc) {
++ cifs_dbg(VFS, "%s: Failed to set aead key %d\n", __func__, rc);
++ return rc;
++ }
++
++ rc = crypto_aead_setauthsize(tfm, SMB2_SIGNATURE_SIZE);
++ if (rc) {
++ cifs_dbg(VFS, "%s: Failed to set authsize %d\n", __func__, rc);
++ return rc;
++ }
++
++ req = aead_request_alloc(tfm, GFP_KERNEL);
++ if (!req) {
++ cifs_dbg(VFS, "%s: Failed to alloc aead request", __func__);
++ return -ENOMEM;
++ }
++
++ if (!enc) {
++ memcpy(sign, &tr_hdr->Signature, SMB2_SIGNATURE_SIZE);
++ crypt_len += SMB2_SIGNATURE_SIZE;
++ }
++
++ sg = init_sg(rqst, sign);
++ if (!sg) {
++ cifs_dbg(VFS, "%s: Failed to init sg %d", __func__, rc);
++ goto free_req;
++ }
++
++ iv_len = crypto_aead_ivsize(tfm);
++ iv = kzalloc(iv_len, GFP_KERNEL);
++ if (!iv) {
++ cifs_dbg(VFS, "%s: Failed to alloc IV", __func__);
++ goto free_sg;
++ }
++ iv[0] = 3;
++ memcpy(iv + 1, (char *)tr_hdr->Nonce, SMB3_AES128CMM_NONCE);
++
++ aead_request_set_crypt(req, sg, sg, crypt_len, iv);
++ aead_request_set_ad(req, assoc_data_len);
++
++ aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
++ cifs_crypt_complete, &result);
++
++ rc = enc ? crypto_aead_encrypt(req) : crypto_aead_decrypt(req);
++
++ if (rc == -EINPROGRESS || rc == -EBUSY) {
++ wait_for_completion(&result.completion);
++ rc = result.err;
++ }
++
++ if (!rc && enc)
++ memcpy(&tr_hdr->Signature, sign, SMB2_SIGNATURE_SIZE);
++
++ kfree(iv);
++free_sg:
++ kfree(sg);
++free_req:
++ kfree(req);
++ return rc;
++}
++
++static int
++smb3_init_transform_rq(struct TCP_Server_Info *server, struct smb_rqst *new_rq,
++ struct smb_rqst *old_rq)
++{
++ struct kvec *iov;
++ struct page **pages;
++ struct smb2_transform_hdr *tr_hdr;
++ unsigned int npages = old_rq->rq_npages;
++ int i;
++ int rc = -ENOMEM;
++
++ pages = kmalloc_array(npages, sizeof(struct page *), GFP_KERNEL);
++ if (!pages)
++ return rc;
++
++ new_rq->rq_pages = pages;
++ new_rq->rq_npages = old_rq->rq_npages;
++ new_rq->rq_pagesz = old_rq->rq_pagesz;
++ new_rq->rq_tailsz = old_rq->rq_tailsz;
++
++ for (i = 0; i < npages; i++) {
++ pages[i] = alloc_page(GFP_KERNEL|__GFP_HIGHMEM);
++ if (!pages[i])
++ goto err_free_pages;
++ }
++
++ iov = kmalloc_array(old_rq->rq_nvec, sizeof(struct kvec), GFP_KERNEL);
++ if (!iov)
++ goto err_free_pages;
++
++ /* copy all iovs from the old except the 1st one (rfc1002 length) */
++ memcpy(&iov[1], &old_rq->rq_iov[1],
++ sizeof(struct kvec) * (old_rq->rq_nvec - 1));
++ new_rq->rq_iov = iov;
++ new_rq->rq_nvec = old_rq->rq_nvec;
++
++ tr_hdr = kmalloc(sizeof(struct smb2_transform_hdr), GFP_KERNEL);
++ if (!tr_hdr)
++ goto err_free_iov;
++
++ /* fill the 1st iov with a transform header */
++ fill_transform_hdr(tr_hdr, old_rq);
++ new_rq->rq_iov[0].iov_base = tr_hdr;
++ new_rq->rq_iov[0].iov_len = sizeof(struct smb2_transform_hdr);
++
++ /* copy pages form the old */
++ for (i = 0; i < npages; i++) {
++ char *dst = kmap(new_rq->rq_pages[i]);
++ char *src = kmap(old_rq->rq_pages[i]);
++ unsigned int len = (i < npages - 1) ? new_rq->rq_pagesz :
++ new_rq->rq_tailsz;
++ memcpy(dst, src, len);
++ kunmap(new_rq->rq_pages[i]);
++ kunmap(old_rq->rq_pages[i]);
++ }
++
++ rc = crypt_message(server, new_rq, 1);
++ cifs_dbg(FYI, "encrypt message returned %d", rc);
++ if (rc)
++ goto err_free_tr_hdr;
++
++ return rc;
++
++err_free_tr_hdr:
++ kfree(tr_hdr);
++err_free_iov:
++ kfree(iov);
++err_free_pages:
++ for (i = i - 1; i >= 0; i--)
++ put_page(pages[i]);
++ kfree(pages);
++ return rc;
++}
++
++static void
++smb3_free_transform_rq(struct smb_rqst *rqst)
++{
++ int i = rqst->rq_npages - 1;
++
++ for (; i >= 0; i--)
++ put_page(rqst->rq_pages[i]);
++ kfree(rqst->rq_pages);
++ /* free transform header */
++ kfree(rqst->rq_iov[0].iov_base);
++ kfree(rqst->rq_iov);
++}
++
+ struct smb_version_operations smb20_operations = {
+ .compare_fids = smb2_compare_fids,
+ .setup_request = smb2_setup_request,
+@@ -1740,6 +1992,8 @@ struct smb_version_operations smb30_oper
+ .wp_retry_size = smb2_wp_retry_size,
+ .dir_needs_close = smb2_dir_needs_close,
+ .fallocate = smb3_fallocate,
++ .init_transform_rq = smb3_init_transform_rq,
++ .free_transform_rq = smb3_free_transform_rq,
+ };
+
+ #ifdef CONFIG_CIFS_SMB311
+@@ -1827,6 +2081,8 @@ struct smb_version_operations smb311_ope
+ .wp_retry_size = smb2_wp_retry_size,
+ .dir_needs_close = smb2_dir_needs_close,
+ .fallocate = smb3_fallocate,
++ .init_transform_rq = smb3_init_transform_rq,
++ .free_transform_rq = smb3_free_transform_rq,
+ };
+ #endif /* CIFS_SMB311 */
+
+diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h
+--- a/fs/cifs/smb2pdu.h
++++ b/fs/cifs/smb2pdu.h
+@@ -132,11 +132,14 @@ struct smb2_pdu {
+ __le16 StructureSize2; /* size of wct area (varies, request specific) */
+ } __packed;
+
++#define SMB3_AES128CMM_NONCE 11
++#define SMB3_AES128GCM_NONCE 12
++
+ struct smb2_transform_hdr {
+ __be32 smb2_buf_length; /* big endian on wire */
+ /* length is only two or three bytes - with
+ one or two byte type preceding it that MBZ */
+- __u8 ProtocolId[4]; /* 0xFD 'S' 'M' 'B' */
++ __le32 ProtocolId; /* 0xFD 'S' 'M' 'B' */
+ __u8 Signature[16];
+ __u8 Nonce[16];
+ __le32 OriginalMessageSize;
+diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h
+--- a/fs/cifs/smb2proto.h
++++ b/fs/cifs/smb2proto.h
+@@ -56,6 +56,8 @@ extern void smb2_echo_request(struct wor
+ extern __le32 smb2_get_lease_state(struct cifsInodeInfo *cinode);
+ extern bool smb2_is_valid_oplock_break(char *buffer,
+ struct TCP_Server_Info *srv);
++extern struct cifs_ses *smb2_find_smb_ses(struct TCP_Server_Info *server,
++ __u64 ses_id);
+
+ extern void move_smb2_info_to_cifs(FILE_ALL_INFO *dst,
+ struct smb2_file_all_info *src);
+@@ -97,6 +99,7 @@ extern int smb2_unlock_range(struct cifs
+ struct file_lock *flock, const unsigned int xid);
+ extern int smb2_push_mandatory_locks(struct cifsFileInfo *cfile);
+ extern void smb2_reconnect_server(struct work_struct *work);
++extern int smb3_crypto_aead_allocate(struct TCP_Server_Info *server);
+
+ /*
+ * SMB2 Worker functions - most of protocol specific implementation details
+diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c
+--- a/fs/cifs/smb2transport.c
++++ b/fs/cifs/smb2transport.c
+@@ -31,6 +31,7 @@
+ #include <asm/processor.h>
+ #include <linux/mempool.h>
+ #include <linux/highmem.h>
++#include <crypto/aead.h>
+ #include "smb2pdu.h"
+ #include "cifsglob.h"
+ #include "cifsproto.h"
+@@ -114,14 +115,14 @@ smb3_crypto_shash_allocate(struct TCP_Se
+ return 0;
+ }
+
+-static struct cifs_ses *
+-smb2_find_smb_ses(struct smb2_sync_hdr *shdr, struct TCP_Server_Info *server)
++struct cifs_ses *
++smb2_find_smb_ses(struct TCP_Server_Info *server, __u64 ses_id)
+ {
+ struct cifs_ses *ses;
+
+ spin_lock(&cifs_tcp_ses_lock);
+ list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
+- if (ses->Suid != shdr->SessionId)
++ if (ses->Suid != ses_id)
+ continue;
+ spin_unlock(&cifs_tcp_ses_lock);
+ return ses;
+@@ -142,7 +143,7 @@ smb2_calc_signature(struct smb_rqst *rqs
+ struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)iov[1].iov_base;
+ struct cifs_ses *ses;
+
+- ses = smb2_find_smb_ses(shdr, server);
++ ses = smb2_find_smb_ses(server, shdr->SessionId);
+ if (!ses) {
+ cifs_dbg(VFS, "%s: Could not find session\n", __func__);
+ return 0;
+@@ -403,7 +404,7 @@ smb3_calc_signature(struct smb_rqst *rqs
+ struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)iov[1].iov_base;
+ struct cifs_ses *ses;
+
+- ses = smb2_find_smb_ses(shdr, server);
++ ses = smb2_find_smb_ses(server, shdr->SessionId);
+ if (!ses) {
+ cifs_dbg(VFS, "%s: Could not find session\n", __func__);
+ return 0;
+@@ -705,3 +706,33 @@ smb2_setup_async_request(struct TCP_Serv
+
+ return mid;
+ }
++
++int
++smb3_crypto_aead_allocate(struct TCP_Server_Info *server)
++{
++ struct crypto_aead *tfm;
++
++ if (!server->secmech.ccmaesencrypt) {
++ tfm = crypto_alloc_aead("ccm(aes)", 0, 0);
++ if (IS_ERR(tfm)) {
++ cifs_dbg(VFS, "%s: Failed to alloc encrypt aead\n",
++ __func__);
++ return PTR_ERR(tfm);
++ }
++ server->secmech.ccmaesencrypt = tfm;
++ }
++
++ if (!server->secmech.ccmaesdecrypt) {
++ tfm = crypto_alloc_aead("ccm(aes)", 0, 0);
++ if (IS_ERR(tfm)) {
++ crypto_free_aead(server->secmech.ccmaesencrypt);
++ server->secmech.ccmaesencrypt = NULL;
++ cifs_dbg(VFS, "%s: Failed to alloc decrypt aead\n",
++ __func__);
++ return PTR_ERR(tfm);
++ }
++ server->secmech.ccmaesdecrypt = tfm;
++ }
++
++ return 0;
++}
diff --git a/patches.suse/cifs-0014-CIFS-Add-transform-header-handling-callbacks.patch b/patches.suse/cifs-0014-CIFS-Add-transform-header-handling-callbacks.patch
new file mode 100644
index 0000000000..7d424b6232
--- /dev/null
+++ b/patches.suse/cifs-0014-CIFS-Add-transform-header-handling-callbacks.patch
@@ -0,0 +1,61 @@
+From 9bb17e0916a03ab901fb684e874d77a1e96b3d1e Mon Sep 17 00:00:00 2001
+From: Pavel Shilovsky <pshilov@microsoft.com>
+Date: Thu, 17 Nov 2016 15:24:34 -0800
+Subject: [PATCH] CIFS: Add transform header handling callbacks
+Patch-mainline: v4.11
+Git-commit: 9bb17e0916a03ab901fb684e874d77a1e96b3d1e
+References: fate#322075
+
+We need to recognize and parse transformed packets in demultiplex
+thread to find a corresponsing mid and process it further.
+
+Signed-off-by: Pavel Shilovsky <pshilov@microsoft.com>
+Acked-by: Aurelien Aptel <aaptel@suse.com>
+Acked-by: David Mulder <dmulder@suse.com>
+
+---
+ fs/cifs/cifsglob.h | 3 +++
+ fs/cifs/connect.c | 17 ++++++++++++-----
+ 2 files changed, 15 insertions(+), 5 deletions(-)
+
+diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
+--- a/fs/cifs/cifsglob.h
++++ b/fs/cifs/cifsglob.h
+@@ -424,6 +424,9 @@ struct smb_version_operations {
+ 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 {
+diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
+--- a/fs/cifs/connect.c
++++ b/fs/cifs/connect.c
+@@ -909,12 +909,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;
diff --git a/patches.suse/cifs-0015-CIFS-Add-mid-handle-callback.patch b/patches.suse/cifs-0015-CIFS-Add-mid-handle-callback.patch
new file mode 100644
index 0000000000..44d81aec2a
--- /dev/null
+++ b/patches.suse/cifs-0015-CIFS-Add-mid-handle-callback.patch
@@ -0,0 +1,147 @@
+From 9b7c18a2d4b798963ea80f6769701dcc4c24b55e Mon Sep 17 00:00:00 2001
+From: Pavel Shilovsky <pshilov@microsoft.com>
+Date: Wed, 16 Nov 2016 14:06:17 -0800
+Subject: [PATCH] CIFS: Add mid handle callback
+Patch-mainline: v4.11
+Git-commit: 9b7c18a2d4b798963ea80f6769701dcc4c24b55e
+References: fate#322075
+
+We need to process read responses differently because the data
+should go directly into preallocated pages. This can be done
+by specifying a mid handle callback.
+
+Signed-off-by: Pavel Shilovsky <pshilov@microsoft.com>
+Acked-by: Aurelien Aptel <aaptel@suse.com>
+Acked-by: David Mulder <dmulder@suse.com>
+
+---
+ fs/cifs/cifsglob.h | 8 ++++++++
+ fs/cifs/cifsproto.h | 2 +-
+ fs/cifs/cifssmb.c | 6 +++---
+ fs/cifs/smb2pdu.c | 10 +++++-----
+ fs/cifs/transport.c | 5 +++--
+ 5 files changed, 20 insertions(+), 11 deletions(-)
+
+diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
+--- a/fs/cifs/cifsglob.h
++++ b/fs/cifs/cifsglob.h
+@@ -1287,6 +1287,13 @@ typedef int (mid_receive_t)(struct TCP_S
+ */
+ 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 */
+@@ -1301,6 +1308,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 */
+diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
+--- a/fs/cifs/cifsproto.h
++++ b/fs/cifs/cifsproto.h
+@@ -78,7 +78,7 @@ extern void cifs_wake_up_task(struct mid
+ 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);
+diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
+--- a/fs/cifs/cifssmb.c
++++ b/fs/cifs/cifssmb.c
+@@ -731,7 +731,7 @@ CIFSSMBEcho(struct TCP_Server_Info *serv
+ 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,
++ 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);
+@@ -1655,7 +1655,7 @@ cifs_async_readv(struct cifs_readdata *r
+
+ 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);
+@@ -2175,7 +2175,7 @@ cifs_async_writev(struct cifs_writedata
+
+ 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);
+diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
+--- a/fs/cifs/smb2pdu.c
++++ b/fs/cifs/smb2pdu.c
+@@ -1989,8 +1989,8 @@ SMB2_echo(struct TCP_Server_Info *server
+ iov[1].iov_len = get_rfc1002_length(req);
+ iov[1].iov_base = (char *)req + 4;
+
+- rc = cifs_call_async(server, &rqst, NULL, smb2_echo_callback, server,
+- CIFS_ECHO_OP);
++ rc = cifs_call_async(server, &rqst, NULL, smb2_echo_callback, NULL,
++ server, CIFS_ECHO_OP);
+ if (rc)
+ cifs_dbg(FYI, "Echo request failed: %d\n", rc);
+
+@@ -2229,7 +2229,7 @@ smb2_async_readv(struct cifs_readdata *r
+ kref_get(&rdata->refcount);
+ rc = cifs_call_async(io_parms.tcon->ses->server, &rqst,
+ cifs_readv_receive, smb2_readv_callback,
+- rdata, flags);
++ NULL, rdata, flags);
+ if (rc) {
+ kref_put(&rdata->refcount, cifs_readdata_release);
+ cifs_stats_fail_inc(io_parms.tcon, SMB2_READ_HE);
+@@ -2440,8 +2440,8 @@ smb2_async_writev(struct cifs_writedata
+ }
+
+ kref_get(&wdata->refcount);
+- rc = cifs_call_async(server, &rqst, NULL, smb2_writev_callback, wdata,
+- flags);
++ rc = cifs_call_async(server, &rqst, NULL, smb2_writev_callback, NULL,
++ wdata, flags);
+
+ if (rc) {
+ kref_put(&wdata->refcount, release);
+diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c
+--- a/fs/cifs/transport.c
++++ b/fs/cifs/transport.c
+@@ -562,8 +562,8 @@ cifs_setup_async_request(struct TCP_Serv
+ */
+ 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_receive_t *receive, mid_callback_t *callback,
++ mid_handle_t *handle, void *cbdata, const int flags)
+ {
+ int rc, timeout, optype;
+ struct mid_q_entry *mid;
+@@ -590,6 +590,7 @@ cifs_call_async(struct TCP_Server_Info *
+ mid->receive = receive;
+ mid->callback = callback;
+ mid->callback_data = cbdata;
++ mid->handle = handle;
+ mid->mid_state = MID_REQUEST_SUBMITTED;
+
+ /* put it on the pending_mid_q */
diff --git a/patches.suse/cifs-0016-CIFS-Add-copy-into-pages-callback-for-a-read-operati.patch b/patches.suse/cifs-0016-CIFS-Add-copy-into-pages-callback-for-a-read-operati.patch
new file mode 100644
index 0000000000..4a9595268d
--- /dev/null
+++ b/patches.suse/cifs-0016-CIFS-Add-copy-into-pages-callback-for-a-read-operati.patch
@@ -0,0 +1,146 @@
+From d70b9104b1ca586f73aaf59426756cec3325a40e Mon Sep 17 00:00:00 2001
+From: Pavel Shilovsky <pshilov@microsoft.com>
+Date: Thu, 17 Nov 2016 16:20:18 -0800
+Subject: [PATCH] CIFS: Add copy into pages callback for a read operation
+Patch-mainline: v4.11
+Git-commit: d70b9104b1ca586f73aaf59426756cec3325a40e
+References: fate#322075
+
+Since we have two different types of reads (pagecache and direct)
+we need to process such responses differently after decryption of
+a packet. The change allows to specify a callback that copies a read
+payload data into preallocated pages.
+
+Signed-off-by: Pavel Shilovsky <pshilov@microsoft.com>
+Acked-by: Aurelien Aptel <aaptel@suse.com>
+Acked-by: David Mulder <dmulder@suse.com>
+
+---
+ fs/cifs/cifsglob.h | 3 +++
+ fs/cifs/file.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++------
+ 2 files changed, 49 insertions(+), 6 deletions(-)
+
+diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
+--- a/fs/cifs/cifsglob.h
++++ b/fs/cifs/cifsglob.h
+@@ -1102,6 +1102,9 @@ struct cifs_readdata {
+ int (*read_into_pages)(struct TCP_Server_Info *server,
+ struct cifs_readdata *rdata,
+ unsigned int len);
++ 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;
+diff --git a/fs/cifs/file.c b/fs/cifs/file.c
+--- a/fs/cifs/file.c
++++ b/fs/cifs/file.c
+@@ -2867,8 +2867,9 @@ cifs_uncached_readv_complete(struct work
+ }
+
+ static int
+-cifs_uncached_read_into_pages(struct TCP_Server_Info *server,
+- struct cifs_readdata *rdata, unsigned int len)
++uncached_fill_pages(struct TCP_Server_Info *server,
++ struct cifs_readdata *rdata, struct iov_iter *iter,
++ unsigned int len)
+ {
+ int result = 0;
+ unsigned int i;
+@@ -2897,7 +2898,10 @@ cifs_uncached_read_into_pages(struct TCP
+ rdata->tailsz = len;
+ len = 0;
+ }
+- result = cifs_read_page_from_socket(server, page, n);
++ if (iter)
++ result = copy_page_from_iter(page, 0, n, iter);
++ else
++ result = cifs_read_page_from_socket(server, page, n);
+ if (result < 0)
+ break;
+
+@@ -2909,6 +2913,21 @@ cifs_uncached_read_into_pages(struct TCP
+ }
+
+ static int
++cifs_uncached_read_into_pages(struct TCP_Server_Info *server,
++ struct cifs_readdata *rdata, unsigned int len)
++{
++ return uncached_fill_pages(server, rdata, NULL, len);
++}
++
++static int
++cifs_uncached_copy_into_pages(struct TCP_Server_Info *server,
++ struct cifs_readdata *rdata,
++ struct iov_iter *iter)
++{
++ return uncached_fill_pages(server, rdata, iter, iter->count);
++}
++
++static int
+ cifs_send_async_read(loff_t offset, size_t len, struct cifsFileInfo *open_file,
+ struct cifs_sb_info *cifs_sb, struct list_head *rdata_list)
+ {
+@@ -2955,6 +2974,7 @@ cifs_send_async_read(loff_t offset, size
+ rdata->pid = pid;
+ rdata->pagesz = PAGE_SIZE;
+ rdata->read_into_pages = cifs_uncached_read_into_pages;
++ rdata->copy_into_pages = cifs_uncached_copy_into_pages;
+ rdata->credits = credits;
+
+ if (!rdata->cfile->invalidHandle ||
+@@ -3305,8 +3325,9 @@ cifs_readv_complete(struct work_struct *
+ }
+
+ static int
+-cifs_readpages_read_into_pages(struct TCP_Server_Info *server,
+- struct cifs_readdata *rdata, unsigned int len)
++readpages_fill_pages(struct TCP_Server_Info *server,
++ struct cifs_readdata *rdata, struct iov_iter *iter,
++ unsigned int len)
+ {
+ int result = 0;
+ unsigned int i;
+@@ -3360,7 +3381,10 @@ cifs_readpages_read_into_pages(struct TC
+ continue;
+ }
+
+- result = cifs_read_page_from_socket(server, page, n);
++ if (iter)
++ result = copy_page_from_iter(page, 0, n, iter);
++ else
++ result = cifs_read_page_from_socket(server, page, n);
+ if (result < 0)
+ break;
+
+@@ -3372,6 +3396,21 @@ cifs_readpages_read_into_pages(struct TC
+ }
+
+ static int
++cifs_readpages_read_into_pages(struct TCP_Server_Info *server,
++ struct cifs_readdata *rdata, unsigned int len)
++{
++ return readpages_fill_pages(server, rdata, NULL, len);
++}
++
++static int
++cifs_readpages_copy_into_pages(struct TCP_Server_Info *server,
++ struct cifs_readdata *rdata,
++ struct iov_iter *iter)
++{
++ return readpages_fill_pages(server, rdata, iter, iter->count);
++}
++
++static int
+ readpages_get_pages(struct address_space *mapping, struct list_head *page_list,
+ unsigned int rsize, struct list_head *tmplist,
+ unsigned int *nr_pages, loff_t *offset, unsigned int *bytes)
+@@ -3525,6 +3564,7 @@ static int cifs_readpages(struct file *f
+ rdata->pid = pid;
+ rdata->pagesz = PAGE_CACHE_SIZE;
+ rdata->read_into_pages = cifs_readpages_read_into_pages;
++ rdata->copy_into_pages = cifs_readpages_copy_into_pages;
+ rdata->credits = credits;
+
+ list_for_each_entry_safe(page, tpage, &tmplist, lru) {
diff --git a/patches.suse/cifs-0017-CIFS-Decrypt-and-process-small-encrypted-packets.patch b/patches.suse/cifs-0017-CIFS-Decrypt-and-process-small-encrypted-packets.patch
new file mode 100644
index 0000000000..ff3ec3cf3f
--- /dev/null
+++ b/patches.suse/cifs-0017-CIFS-Decrypt-and-process-small-encrypted-packets.patch
@@ -0,0 +1,362 @@
+From 4326ed2f6a16ae9d33e4209b540dc9a371aba840 Mon Sep 17 00:00:00 2001
+From: Pavel Shilovsky <pshilov@microsoft.com>
+Date: Thu, 17 Nov 2016 15:24:46 -0800
+Subject: [PATCH] CIFS: Decrypt and process small encrypted packets
+Patch-mainline: v4.11
+Git-commit: 4326ed2f6a16ae9d33e4209b540dc9a371aba840
+References: fate#322075
+
+Allow to decrypt transformed packets, find a corresponding mid
+and process as usual further.
+
+Signed-off-by: Pavel Shilovsky <pshilov@microsoft.com>
+Acked-by: Aurelien Aptel <aaptel@suse.com>
+Acked-by: David Mulder <dmulder@suse.com>
+
+---
+ fs/cifs/cifsglob.h | 1 +
+ fs/cifs/cifsproto.h | 2 +
+ fs/cifs/connect.c | 9 ++
+ fs/cifs/smb2ops.c | 226 ++++++++++++++++++++++++++++++++++++++++++++++++
+ fs/cifs/smb2pdu.c | 4 +-
+ fs/cifs/smb2proto.h | 2 +
+ fs/cifs/smb2transport.c | 2 +-
+ 7 files changed, 243 insertions(+), 3 deletions(-)
+
+diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
+--- a/fs/cifs/cifsglob.h
++++ b/fs/cifs/cifsglob.h
+@@ -1317,6 +1317,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
+diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
+--- a/fs/cifs/cifsproto.h
++++ b/fs/cifs/cifsproto.h
+@@ -75,6 +75,8 @@ extern struct mid_q_entry *AllocMidQEntr
+ 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_call_async(struct TCP_Server_Info *server,
+ struct smb_rqst *rqst,
+ mid_receive_t *receive, mid_callback_t *callback,
+diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
+--- a/fs/cifs/connect.c
++++ b/fs/cifs/connect.c
+@@ -766,6 +766,15 @@ standard_receive3(struct TCP_Server_Info
+
+ 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
+diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
+--- a/fs/cifs/smb2ops.c
++++ b/fs/cifs/smb2ops.c
+@@ -1750,6 +1750,228 @@ smb3_free_transform_rq(struct smb_rqst *
+ kfree(rqst->rq_iov);
+ }
+
++static int
++smb3_is_transform_hdr(void *buf)
++{
++ struct smb2_transform_hdr *trhdr = buf;
++
++ return trhdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM;
++}
++
++static int
++decrypt_raw_data(struct TCP_Server_Info *server, char *buf,
++ unsigned int buf_data_size, struct page **pages,
++ unsigned int npages, unsigned int page_data_size)
++{
++ struct kvec iov[2];
++ struct smb_rqst rqst = {NULL};
++ struct smb2_hdr *hdr;
++ int rc;
++
++ iov[0].iov_base = buf;
++ iov[0].iov_len = sizeof(struct smb2_transform_hdr);
++ iov[1].iov_base = buf + sizeof(struct smb2_transform_hdr);
++ iov[1].iov_len = buf_data_size;
++
++ rqst.rq_iov = iov;
++ rqst.rq_nvec = 2;
++ rqst.rq_pages = pages;
++ rqst.rq_npages = npages;
++ rqst.rq_pagesz = PAGE_SIZE;
++ rqst.rq_tailsz = (page_data_size % PAGE_SIZE) ? : PAGE_SIZE;
++
++ rc = crypt_message(server, &rqst, 0);
++ cifs_dbg(FYI, "decrypt message returned %d\n", rc);
++
++ if (rc)
++ return rc;
++
++ memmove(buf + 4, iov[1].iov_base, buf_data_size);
++ hdr = (struct smb2_hdr *)buf;
++ hdr->smb2_buf_length = cpu_to_be32(buf_data_size + page_data_size);
++ server->total_read = buf_data_size + page_data_size + 4;
++
++ return rc;
++}
++
++static int
++handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
++ char *buf, unsigned int buf_len, struct page **pages,
++ unsigned int npages, unsigned int page_data_size)
++{
++ unsigned int data_offset;
++ unsigned int data_len;
++ struct cifs_readdata *rdata = mid->callback_data;
++ struct smb2_sync_hdr *shdr = get_sync_hdr(buf);
++ struct bio_vec *bvec = NULL;
++ struct iov_iter iter;
++ struct kvec iov;
++ int length;
++
++ if (shdr->Command != SMB2_READ) {
++ cifs_dbg(VFS, "only big read responses are supported\n");
++ return -ENOTSUPP;
++ }
++
++ if (server->ops->is_status_pending &&
++ server->ops->is_status_pending(buf, server, 0))
++ return -1;
++
++ rdata->result = server->ops->map_error(buf, false);
++ if (rdata->result != 0) {
++ cifs_dbg(FYI, "%s: server returned error %d\n",
++ __func__, rdata->result);
++ dequeue_mid(mid, rdata->result);
++ return 0;
++ }
++
++ data_offset = server->ops->read_data_offset(buf) + 4;
++ data_len = server->ops->read_data_length(buf);
++
++ if (data_offset < server->vals->read_rsp_size) {
++ /*
++ * win2k8 sometimes sends an offset of 0 when the read
++ * is beyond the EOF. Treat it as if the data starts just after
++ * the header.
++ */
++ cifs_dbg(FYI, "%s: data offset (%u) inside read response header\n",
++ __func__, data_offset);
++ data_offset = server->vals->read_rsp_size;
++ } else if (data_offset > MAX_CIFS_SMALL_BUFFER_SIZE) {
++ /* data_offset is beyond the end of smallbuf */
++ cifs_dbg(FYI, "%s: data offset (%u) beyond end of smallbuf\n",
++ __func__, data_offset);
++ rdata->result = -EIO;
++ dequeue_mid(mid, rdata->result);
++ return 0;
++ }
++
++ if (buf_len <= data_offset) {
++ /* read response payload is in pages */
++ /* BB add code to init iter with pages */
++ } else if (buf_len >= data_offset + data_len) {
++ /* read response payload is in buf */
++ WARN_ONCE(npages > 0, "read data can be either in buf or in pages");
++ iov.iov_base = buf + data_offset;
++ iov.iov_len = data_len;
++ iov_iter_kvec(&iter, WRITE | ITER_KVEC, &iov, 1, data_len);
++ } else {
++ /* read response payload cannot be in both buf and pages */
++ WARN_ONCE(1, "buf can not contain only a part of read data");
++ rdata->result = -EIO;
++ dequeue_mid(mid, rdata->result);
++ return 0;
++ }
++
++ /* set up first iov for signature check */
++ 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->vals->read_rsp_size - 4;
++ cifs_dbg(FYI, "0: iov_base=%p iov_len=%zu\n",
++ rdata->iov[0].iov_base, server->vals->read_rsp_size);
++
++ length = rdata->copy_into_pages(server, rdata, &iter);
++
++ kfree(bvec);
++
++ if (length < 0)
++ return length;
++
++ dequeue_mid(mid, false);
++ return length;
++}
++
++static int
++receive_encrypted_standard(struct TCP_Server_Info *server,
++ struct mid_q_entry **mid)
++{
++ int length;
++ char *buf = server->smallbuf;
++ unsigned int pdu_length = get_rfc1002_length(buf);
++ unsigned int buf_size;
++ struct mid_q_entry *mid_entry;
++
++ /* switch to large buffer if too big for a small one */
++ if (pdu_length + 4 > MAX_CIFS_SMALL_BUFFER_SIZE) {
++ server->large_buf = true;
++ memcpy(server->bigbuf, buf, server->total_read);
++ buf = server->bigbuf;
++ }
++
++ /* now read the rest */
++ length = cifs_read_from_socket(server, buf + HEADER_SIZE(server) - 1,
++ pdu_length - HEADER_SIZE(server) + 1 + 4);
++ if (length < 0)
++ return length;
++ server->total_read += length;
++
++ buf_size = pdu_length + 4 - sizeof(struct smb2_transform_hdr);
++ length = decrypt_raw_data(server, buf, buf_size, NULL, 0, 0);
++ if (length)
++ return length;
++
++ mid_entry = smb2_find_mid(server, buf);
++ if (mid_entry == NULL)
++ cifs_dbg(FYI, "mid not found\n");
++ else {
++ cifs_dbg(FYI, "mid found\n");
++ mid_entry->decrypted = true;
++ }
++
++ *mid = mid_entry;
++
++ if (mid_entry && mid_entry->handle)
++ return mid_entry->handle(server, mid_entry);
++
++ return cifs_handle_standard(server, mid_entry);
++}
++
++static int
++smb3_receive_transform(struct TCP_Server_Info *server, struct mid_q_entry **mid)
++{
++ char *buf = server->smallbuf;
++ unsigned int pdu_length = get_rfc1002_length(buf);
++ struct smb2_transform_hdr *tr_hdr = (struct smb2_transform_hdr *)buf;
++ unsigned int orig_len = le32_to_cpu(tr_hdr->OriginalMessageSize);
++
++ if (pdu_length + 4 < sizeof(struct smb2_transform_hdr) +
++ sizeof(struct smb2_sync_hdr)) {
++ cifs_dbg(VFS, "Transform message is too small (%u)\n",
++ pdu_length);
++ cifs_reconnect(server);
++ wake_up(&server->response_q);
++ return -ECONNABORTED;
++ }
++
++ if (pdu_length + 4 < orig_len + sizeof(struct smb2_transform_hdr)) {
++ cifs_dbg(VFS, "Transform message is broken\n");
++ cifs_reconnect(server);
++ wake_up(&server->response_q);
++ return -ECONNABORTED;
++ }
++
++ if (pdu_length + 4 > CIFSMaxBufSize + MAX_HEADER_SIZE(server)) {
++ cifs_dbg(VFS, "Decoding responses of big size (%u) is not supported\n",
++ pdu_length);
++ /* BB add code to allocate and fill highmem pages here */
++ cifs_reconnect(server);
++ wake_up(&server->response_q);
++ return -ECONNABORTED;
++ }
++
++ return receive_encrypted_standard(server, mid);
++}
++
++int
++smb3_handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid)
++{
++ char *buf = server->large_buf ? server->bigbuf : server->smallbuf;
++
++ return handle_read_data(server, mid, buf, get_rfc1002_length(buf) + 4,
++ NULL, 0, 0);
++}
++
+ struct smb_version_operations smb20_operations = {
+ .compare_fids = smb2_compare_fids,
+ .setup_request = smb2_setup_request,
+@@ -1994,6 +2216,8 @@ struct smb_version_operations smb30_oper
+ .fallocate = smb3_fallocate,
+ .init_transform_rq = smb3_init_transform_rq,
+ .free_transform_rq = smb3_free_transform_rq,
++ .is_transform_hdr = smb3_is_transform_hdr,
++ .receive_transform = smb3_receive_transform,
+ };
+
+ #ifdef CONFIG_CIFS_SMB311
+@@ -2083,6 +2307,8 @@ struct smb_version_operations smb311_ope
+ .fallocate = smb3_fallocate,
+ .init_transform_rq = smb3_init_transform_rq,
+ .free_transform_rq = smb3_free_transform_rq,
++ .is_transform_hdr = smb3_is_transform_hdr,
++ .receive_transform = smb3_receive_transform,
+ };
+ #endif /* CIFS_SMB311 */
+
+diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
+--- a/fs/cifs/smb2pdu.c
++++ b/fs/cifs/smb2pdu.c
+@@ -2126,7 +2126,7 @@ smb2_readv_callback(struct mid_q_entry *
+ case MID_RESPONSE_RECEIVED:
+ credits_received = le16_to_cpu(shdr->CreditRequest);
+ /* result already set, check signature */
+- if (server->sign) {
++ if (server->sign && !mid->decrypted) {
+ int rc;
+
+ rc = smb2_verify_signature(&rqst, server);
+@@ -2229,7 +2229,7 @@ smb2_async_readv(struct cifs_readdata *r
+ kref_get(&rdata->refcount);
+ rc = cifs_call_async(io_parms.tcon->ses->server, &rqst,
+ cifs_readv_receive, smb2_readv_callback,
+- NULL, rdata, flags);
++ smb3_handle_read_data, rdata, flags);
+ if (rc) {
+ kref_put(&rdata->refcount, cifs_readdata_release);
+ cifs_stats_fail_inc(io_parms.tcon, SMB2_READ_HE);
+diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h
+--- a/fs/cifs/smb2proto.h
++++ b/fs/cifs/smb2proto.h
+@@ -58,6 +58,8 @@ extern bool smb2_is_valid_oplock_break(c
+ struct TCP_Server_Info *srv);
+ extern struct cifs_ses *smb2_find_smb_ses(struct TCP_Server_Info *server,
+ __u64 ses_id);
++extern int smb3_handle_read_data(struct TCP_Server_Info *server,
++ struct mid_q_entry *mid);
+
+ extern void move_smb2_info_to_cifs(FILE_ALL_INFO *dst,
+ struct smb2_file_all_info *src);
+diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c
+--- a/fs/cifs/smb2transport.c
++++ b/fs/cifs/smb2transport.c
+@@ -651,7 +651,7 @@ smb2_check_receive(struct mid_q_entry *m
+
+ dump_smb(mid->resp_buf, min_t(u32, 80, len));
+ /* convert the length into a more usable form */
+- if (len > 24 && server->sign) {
++ if (len > 24 && server->sign && !mid->decrypted) {
+ int rc;
+
+ rc = smb2_verify_signature(&rqst, server);
diff --git a/patches.suse/cifs-0018-CIFS-Add-capability-to-decrypt-big-read-responses.patch b/patches.suse/cifs-0018-CIFS-Add-capability-to-decrypt-big-read-responses.patch
new file mode 100644
index 0000000000..fbda7fddbd
--- /dev/null
+++ b/patches.suse/cifs-0018-CIFS-Add-capability-to-decrypt-big-read-responses.patch
@@ -0,0 +1,277 @@
+From c42a6abe3012832a68a371dabe17c2ced97e62ad Mon Sep 17 00:00:00 2001
+From: Pavel Shilovsky <pshilov@microsoft.com>
+Date: Thu, 17 Nov 2016 16:20:23 -0800
+Subject: [PATCH] CIFS: Add capability to decrypt big read responses
+Patch-mainline: v4.11
+Git-commit: c42a6abe3012832a68a371dabe17c2ced97e62ad
+References: fate#322075
+
+Allow to decrypt transformed packets that are bigger than the big
+buffer size. In particular it is used for read responses that can
+only exceed the big buffer size.
+
+Signed-off-by: Pavel Shilovsky <pshilov@microsoft.com>
+Acked-by: Aurelien Aptel <aaptel@suse.com>
+Acked-by: David Mulder <dmulder@suse.com>
+
+---
+ fs/cifs/cifsproto.h | 1 +
+ fs/cifs/cifssmb.c | 8 +--
+ fs/cifs/smb2ops.c | 173 +++++++++++++++++++++++++++++++++++++++++++++++++---
+ 3 files changed, 169 insertions(+), 13 deletions(-)
+
+diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
+--- a/fs/cifs/cifsproto.h
++++ b/fs/cifs/cifsproto.h
+@@ -77,6 +77,7 @@ extern void cifs_delete_mid(struct mid_q
+ 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,
+diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
+--- a/fs/cifs/cifssmb.c
++++ b/fs/cifs/cifssmb.c
+@@ -1400,8 +1400,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;
+@@ -1427,7 +1427,7 @@ cifs_readv_discard(struct TCP_Server_Inf
+ 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;
+ }
+@@ -1460,7 +1460,7 @@ cifs_readv_receive(struct TCP_Server_Inf
+
+ 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;
+ }
+
+diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
+--- a/fs/cifs/smb2ops.c
++++ b/fs/cifs/smb2ops.c
+@@ -1795,12 +1795,72 @@ decrypt_raw_data(struct TCP_Server_Info
+ }
+
+ static int
++read_data_into_pages(struct TCP_Server_Info *server, struct page **pages,
++ unsigned int npages, unsigned int len)
++{
++ int i;
++ int length;
++
++ for (i = 0; i < npages; i++) {
++ struct page *page = pages[i];
++ size_t n;
++
++ n = len;
++ if (len >= PAGE_SIZE) {
++ /* enough data to fill the page */
++ n = PAGE_SIZE;
++ len -= n;
++ } else {
++ zero_user(page, len, PAGE_SIZE - len);
++ len = 0;
++ }
++ length = cifs_read_page_from_socket(server, page, n);
++ if (length < 0)
++ return length;
++ server->total_read += length;
++ }
++
++ return 0;
++}
++
++static int
++init_read_bvec(struct page **pages, unsigned int npages, unsigned int data_size,
++ unsigned int cur_off, struct bio_vec **page_vec)
++{
++ struct bio_vec *bvec;
++ int i;
++
++ bvec = kcalloc(npages, sizeof(struct bio_vec), GFP_KERNEL);
++ if (!bvec)
++ return -ENOMEM;
++
++ for (i = 0; i < npages; i++) {
++ bvec[i].bv_page = pages[i];
++ bvec[i].bv_offset = (i == 0) ? cur_off : 0;
++ bvec[i].bv_len = min_t(unsigned int, PAGE_SIZE, data_size);
++ data_size -= bvec[i].bv_len;
++ }
++
++ if (data_size != 0) {
++ cifs_dbg(VFS, "%s: something went wrong\n", __func__);
++ kfree(bvec);
++ return -EIO;
++ }
++
++ *page_vec = bvec;
++ return 0;
++}
++
++static int
+ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
+ char *buf, unsigned int buf_len, struct page **pages,
+ unsigned int npages, unsigned int page_data_size)
+ {
+ unsigned int data_offset;
+ unsigned int data_len;
++ unsigned int cur_off;
++ unsigned int cur_page_idx;
++ unsigned int pad_len;
+ struct cifs_readdata *rdata = mid->callback_data;
+ struct smb2_sync_hdr *shdr = get_sync_hdr(buf);
+ struct bio_vec *bvec = NULL;
+@@ -1846,9 +1906,37 @@ handle_read_data(struct TCP_Server_Info
+ return 0;
+ }
+
++ pad_len = data_offset - server->vals->read_rsp_size;
++
+ if (buf_len <= data_offset) {
+ /* read response payload is in pages */
+- /* BB add code to init iter with pages */
++ cur_page_idx = pad_len / PAGE_SIZE;
++ cur_off = pad_len % PAGE_SIZE;
++
++ if (cur_page_idx != 0) {
++ /* data offset is beyond the 1st page of response */
++ cifs_dbg(FYI, "%s: data offset (%u) beyond 1st page of response\n",
++ __func__, data_offset);
++ rdata->result = -EIO;
++ dequeue_mid(mid, rdata->result);
++ return 0;
++ }
++
++ if (data_len > page_data_size - pad_len) {
++ /* data_len is corrupt -- discard frame */
++ rdata->result = -EIO;
++ dequeue_mid(mid, rdata->result);
++ return 0;
++ }
++
++ rdata->result = init_read_bvec(pages, npages, page_data_size,
++ cur_off, &bvec);
++ if (rdata->result != 0) {
++ dequeue_mid(mid, rdata->result);
++ return 0;
++ }
++
++ iov_iter_bvec(&iter, WRITE | ITER_BVEC, bvec, npages, data_len);
+ } else if (buf_len >= data_offset + data_len) {
+ /* read response payload is in buf */
+ WARN_ONCE(npages > 0, "read data can be either in buf or in pages");
+@@ -1883,6 +1971,79 @@ handle_read_data(struct TCP_Server_Info
+ }
+
+ static int
++receive_encrypted_read(struct TCP_Server_Info *server, struct mid_q_entry **mid)
++{
++ char *buf = server->smallbuf;
++ struct smb2_transform_hdr *tr_hdr = (struct smb2_transform_hdr *)buf;
++ unsigned int npages;
++ struct page **pages;
++ unsigned int len;
++ unsigned int buflen = get_rfc1002_length(buf) + 4;
++ int rc;
++ int i = 0;
++
++ len = min_t(unsigned int, buflen, server->vals->read_rsp_size - 4 +
++ sizeof(struct smb2_transform_hdr)) - HEADER_SIZE(server) + 1;
++
++ rc = cifs_read_from_socket(server, buf + HEADER_SIZE(server) - 1, len);
++ if (rc < 0)
++ return rc;
++ server->total_read += rc;
++
++ len = le32_to_cpu(tr_hdr->OriginalMessageSize) + 4 -
++ server->vals->read_rsp_size;
++ npages = DIV_ROUND_UP(len, PAGE_SIZE);
++
++ pages = kmalloc_array(npages, sizeof(struct page *), GFP_KERNEL);
++ if (!pages) {
++ rc = -ENOMEM;
++ goto discard_data;
++ }
++
++ for (; i < npages; i++) {
++ pages[i] = alloc_page(GFP_KERNEL|__GFP_HIGHMEM);
++ if (!pages[i]) {
++ rc = -ENOMEM;
++ goto discard_data;
++ }
++ }
++
++ /* read read data into pages */
++ rc = read_data_into_pages(server, pages, npages, len);
++ if (rc)
++ goto free_pages;
++
++ rc = cifs_discard_remaining_data(server);
++ if (rc)
++ goto free_pages;
++
++ rc = decrypt_raw_data(server, buf, server->vals->read_rsp_size - 4,
++ pages, npages, len);
++ if (rc)
++ goto free_pages;
++
++ *mid = smb2_find_mid(server, buf);
++ if (*mid == NULL)
++ cifs_dbg(FYI, "mid not found\n");
++ else {
++ cifs_dbg(FYI, "mid found\n");
++ (*mid)->decrypted = true;
++ rc = handle_read_data(server, *mid, buf,
++ server->vals->read_rsp_size,
++ pages, npages, len);
++ }
++
++free_pages:
++ for (i = i - 1; i >= 0; i--)
++ put_page(pages[i]);
++ kfree(pages);
++ return rc;
++discard_data:
++ cifs_discard_remaining_data(server);
++ goto free_pages;
++}
++
++static int
+ receive_encrypted_standard(struct TCP_Server_Info *server,
+ struct mid_q_entry **mid)
+ {
+@@ -1951,14 +2112,8 @@ smb3_receive_transform(struct TCP_Server
+ return -ECONNABORTED;
+ }
+
+- if (pdu_length + 4 > CIFSMaxBufSize + MAX_HEADER_SIZE(server)) {
+- cifs_dbg(VFS, "Decoding responses of big size (%u) is not supported\n",
+- pdu_length);
+- /* BB add code to allocate and fill highmem pages here */
+- cifs_reconnect(server);
+- wake_up(&server->response_q);
+- return -ECONNABORTED;
+- }
++ if (pdu_length + 4 > CIFSMaxBufSize + MAX_HEADER_SIZE(server))
++ return receive_encrypted_read(server, mid);
+
+ return receive_encrypted_standard(server, mid);
+ }
diff --git a/patches.suse/cifs-0019-CIFS-Allow-to-switch-on-encryption-with-seal-mount-o.patch b/patches.suse/cifs-0019-CIFS-Allow-to-switch-on-encryption-with-seal-mount-o.patch
new file mode 100644
index 0000000000..9885241ae9
--- /dev/null
+++ b/patches.suse/cifs-0019-CIFS-Allow-to-switch-on-encryption-with-seal-mount-o.patch
@@ -0,0 +1,187 @@
+From ae6f8dd4d0c87bfb72da9d9b56342adf53e69c31 Mon Sep 17 00:00:00 2001
+From: Pavel Shilovsky <pshilov@microsoft.com>
+Date: Thu, 17 Nov 2016 13:59:23 -0800
+Subject: [PATCH] CIFS: Allow to switch on encryption with seal mount option
+Patch-mainline: v4.11
+Git-commit: ae6f8dd4d0c87bfb72da9d9b56342adf53e69c31
+References: fate#322075
+
+This allows users to inforce encryption for SMB3 shares if a server
+supports it.
+
+Signed-off-by: Pavel Shilovsky <pshilov@microsoft.com>
+Acked-by: Aurelien Aptel <aaptel@suse.com>
+Acked-by: David Mulder <dmulder@suse.com>
+
+---
+ fs/cifs/connect.c | 41 ++++++++++++++++++++++++++++-------------
+ fs/cifs/smb2pdu.c | 33 +++++++++++++++------------------
+ 2 files changed, 43 insertions(+), 31 deletions(-)
+
+diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
+--- a/fs/cifs/connect.c
++++ b/fs/cifs/connect.c
+@@ -2571,12 +2571,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;
+ }
+
+@@ -2589,14 +2595,8 @@ cifs_find_tcon(struct cifs_ses *ses, str
+ 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
+- if (tcon->snapshot_time != volume_info->snapshot_time)
++ if (!match_tcon(tcon, volume_info))
+ continue;
+-#endif /* CONFIG_CIFS_SMB2 */
+-
+ ++tcon->tc_count;
+ spin_unlock(&cifs_tcp_ses_lock);
+ return tcon;
+@@ -2642,8 +2642,6 @@ cifs_get_tcon(struct cifs_ses *ses, stru
+ cifs_dbg(FYI, "Found match on UNC path\n");
+ /* existing tcon already has a reference */
+ cifs_put_smb_ses(ses);
+- if (tcon->seal != volume_info->seal)
+- cifs_dbg(VFS, "transport encryption setting conflicts with existing tid\n");
+ return tcon;
+ }
+
+@@ -2699,7 +2697,6 @@ cifs_get_tcon(struct cifs_ses *ses, stru
+ tcon->Flags &= ~SMB_SHARE_IS_IN_DFS;
+ cifs_dbg(FYI, "DFS disabled (%d)\n", tcon->Flags);
+ }
+- tcon->seal = volume_info->seal;
+ tcon->use_persistent = false;
+ /* check if SMB2 or later, CIFS does not support persistent handles */
+ if (volume_info->persistent) {
+@@ -2736,6 +2733,24 @@ cifs_get_tcon(struct cifs_ses *ses, stru
+ tcon->use_resilient = true;
+ }
+
++ if (volume_info->seal) {
++ if (ses->server->vals->protocol_id == 0) {
++ cifs_dbg(VFS,
++ "SMB3 or later required for encryption\n");
++ rc = -EOPNOTSUPP;
++ goto out_fail;
++#ifdef CONFIG_CIFS_SMB2
++ } else if (tcon->ses->server->capabilities &
++ SMB2_GLOBAL_CAP_ENCRYPTION)
++ tcon->seal = true;
++ else {
++ cifs_dbg(VFS, "Encryption is not supported on share\n");
++ rc = -EOPNOTSUPP;
++ goto out_fail;
++#endif /* CONFIG_CIFS_SMB2 */
++ }
++ }
++
+ /*
+ * We can have only one retry value for a connection to a share so for
+ * resources mounted more than once to the same server share the last
+@@ -2867,7 +2882,7 @@ cifs_match_super(struct super_block *sb,
+
+ if (!match_server(tcp_srv, volume_info) ||
+ !match_session(ses, volume_info) ||
+- !match_tcon(tcon, volume_info->UNC) ||
++ !match_tcon(tcon, volume_info) ||
+ !match_prepath(sb, mnt_data)) {
+ rc = 0;
+ goto out;
+diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
+--- a/fs/cifs/smb2pdu.c
++++ b/fs/cifs/smb2pdu.c
+@@ -79,9 +79,14 @@ static const int smb2_req_struct_sizes[N
+
+ static int encryption_required(const struct cifs_tcon *tcon)
+ {
++ if (!tcon)
++ return 0;
+ if ((tcon->ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA) ||
+ (tcon->share_flags & SHI1005_FLAGS_ENCRYPT_DATA))
+ return 1;
++ if (tcon->seal &&
++ (tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION))
++ return 1;
+ return 0;
+ }
+
+@@ -867,8 +872,6 @@ ssetup_ntlmssp_authenticate:
+ goto ssetup_exit;
+
+ ses->session_flags = le16_to_cpu(rsp->SessionFlags);
+- if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA)
+- cifs_dbg(VFS, "SMB3 encryption not supported yet\n");
+ ssetup_exit:
+ free_rsp_buf(resp_buftype, rsp);
+
+@@ -991,12 +994,6 @@ SMB2_tcon(const unsigned int xid, struct
+ if (tcon && tcon->bad_network_name)
+ return -ENOENT;
+
+- if ((tcon && tcon->seal) &&
+- ((ses->server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION) == 0)) {
+- cifs_dbg(VFS, "encryption requested but no server support");
+- return -EOPNOTSUPP;
+- }
+-
+ unc_path = kmalloc(MAX_SHARENAME_LENGTH * 2, GFP_KERNEL);
+ if (unc_path == NULL)
+ return -ENOMEM;
+@@ -1014,15 +1011,16 @@ SMB2_tcon(const unsigned int xid, struct
+ return rc;
+ }
+
+- if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA)
+- flags |= CIFS_TRANSFORM_REQ;
+-
+ if (tcon == NULL) {
++ if ((ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA))
++ flags |= CIFS_TRANSFORM_REQ;
++
+ /* since no tcon, smb2_init can not do this, so do here */
+ req->hdr.sync_hdr.SessionId = ses->Suid;
+ /* if (ses->server->sec_mode & SECMODE_SIGN_REQUIRED)
+ req->hdr.Flags |= SMB2_FLAGS_SIGNED; */
+- }
++ } else if (encryption_required(tcon))
++ flags |= CIFS_TRANSFORM_REQ;
+
+ iov[0].iov_base = (char *)req;
+ /* 4 for rfc1002 length field and 1 for pad */
+@@ -1079,9 +1077,12 @@ SMB2_tcon(const unsigned int xid, struct
+ if ((rsp->Capabilities & SMB2_SHARE_CAP_DFS) &&
+ ((tcon->share_flags & SHI1005_FLAGS_DFS) == 0))
+ cifs_dbg(VFS, "DFS capability contradicts DFS flag\n");
++
++ if (tcon->seal &&
++ !(tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION))
++ cifs_dbg(VFS, "Encryption is requested but not supported\n");
++
+ init_copy_chunk_defaults(tcon);
+- if (tcon->share_flags & SHI1005_FLAGS_ENCRYPT_DATA)
+- cifs_dbg(VFS, "Encrypted shares not supported");
+ if (tcon->ses->server->ops->validate_negotiate)
+ rc = tcon->ses->server->ops->validate_negotiate(xid, tcon);
+ tcon_exit:
diff --git a/series.conf b/series.conf
index 5c3036dfaa..08d17f6ad7 100644
--- a/series.conf
+++ b/series.conf
@@ -2729,6 +2729,29 @@
patches.fixes/cifs-compare-prepath-sb
patches.fixes/cifs-move-prepath-check
patches.fixes/cifs-optional-treename-prefix
+ patches.suse/cifs-0001-Prepare-for-encryption-support-first-part-.-Add-decr.patch
+ patches.suse/cifs-0001-cifs-Simplify-SMB2-and-SMB311-dependencies.patch
+ patches.suse/cifs-0002-cifs-Only-select-the-required-crypto-modules.patch
+ patches.suse/cifs-0003-cifs-Add-soft-dependencies.patch
+ patches.suse/cifs-0005-CIFS-Separate-SMB2-header-structure.patch
+ patches.suse/cifs-0006-CIFS-Make-SendReceive2-takes-resp-iov.patch
+ patches.suse/cifs-0007-CIFS-Make-send_cancel-take-rqst-as-argument.patch
+ patches.suse/cifs-0001-cifs_readv_receive-use-cifs_read_from_socket.patch
+ patches.suse/cifs-0008-CIFS-Send-RFC1001-length-in-a-separate-iov.patch
+ patches.suse/cifs-0009-CIFS-Separate-SMB2-sync-header-processing.patch
+ patches.suse/cifs-0010-CIFS-Separate-RFC1001-length-processing-for-SMB2-rea.patch
+ patches.suse/cifs-0011-CIFS-Add-capability-to-transform-requests-before-sen.patch
+ patches.suse/cifs-0012-CIFS-Enable-encryption-during-session-setup-phase.patch
+ patches.suse/cifs-0013-CIFS-Encrypt-SMB3-requests-before-sending.patch
+ patches.suse/cifs-0014-CIFS-Add-transform-header-handling-callbacks.patch
+ patches.suse/cifs-0015-CIFS-Add-mid-handle-callback.patch
+ patches.suse/cifs-0001-cifs-no-need-to-wank-with-copying-and-advancing-iove.patch
+ patches.suse/cifs-0002-cifs-don-t-bother-with-kmap-on-read_pages-side.patch
+ patches.suse/cifs-0016-CIFS-Add-copy-into-pages-callback-for-a-read-operati.patch
+ patches.suse/cifs-0017-CIFS-Decrypt-and-process-small-encrypted-packets.patch
+ patches.suse/cifs-0018-CIFS-Add-capability-to-decrypt-big-read-responses.patch
+ patches.suse/cifs-0001-SMB3-parsing-for-new-snapshot-timestamp-mount-parm.patch
+ patches.suse/cifs-0019-CIFS-Allow-to-switch-on-encryption-with-seal-mount-o.patch
########################################################
# ext2/ext3