@@ -449,7 +449,7 @@ struct smb_version_operations {
struct mid_q_entry **);
enum securityEnum (*select_sectype)(struct TCP_Server_Info *,
enum securityEnum);
-
+ int (*next_header)(char *);
};
struct smb_version_values {
@@ -840,6 +840,7 @@ cifs_demultiplex_thread(void *p)
int length;
struct TCP_Server_Info *server = p;
unsigned int pdu_length;
+ unsigned int next_offset;
char *buf = NULL;
struct task_struct *task_to_wake = NULL;
struct mid_q_entry *mid_entry;
@@ -877,17 +878,18 @@ cifs_demultiplex_thread(void *p)
* so we can now interpret the length field.
*/
pdu_length = get_rfc1002_length(buf);
+next_pdu:
server->total_size = pdu_length;
- cifs_dbg(FYI, "RFC1002 header 0x%x\n", pdu_length);
+ cifs_dbg(FYI, "RFC1002 header 0x%x\n", server->total_size);
if (!is_smb_response(server, buf[0]))
continue;
/* make sure we have enough to get to the MID */
- if (pdu_length < HEADER_SIZE(server) - 1 -
+ if (server->total_size < HEADER_SIZE(server) - 1 -
server->vals->header_preamble_size) {
cifs_dbg(VFS, "SMB response too short (%u bytes)\n",
- pdu_length);
+ server->total_size);
cifs_reconnect(server);
wake_up(&server->response_q);
continue;
@@ -902,6 +904,12 @@ cifs_demultiplex_thread(void *p)
continue;
server->total_read += length;
+ if (server->ops->next_header) {
+ next_offset = server->ops->next_header(buf);
+ if (next_offset)
+ server->total_size = next_offset;
+ }
+
if (server->ops->is_transform_hdr &&
server->ops->receive_transform &&
server->ops->is_transform_hdr(buf))
@@ -947,6 +955,10 @@ cifs_demultiplex_thread(void *p)
cifs_dump_mids(server);
#endif /* CIFS_DEBUG2 */
+ if (pdu_length > server->total_size) {
+ pdu_length -= server->total_size;
+ goto next_pdu;
+ }
}
} /* end while !EXITING */
@@ -125,8 +125,8 @@ smb2_get_credits(struct mid_q_entry *mid)
char *buf = mid->resp_buf;
struct smb2_sync_hdr *shdr;
- if ( *(__u32 *)buf == SMB2_PROTO_NUMBER ||
- *(__u32 *)buf == SMB2_TRANSFORM_PROTO_NUM)
+ if ( *(__le32 *)buf == SMB2_PROTO_NUMBER ||
+ *(__le32 *)buf == SMB2_TRANSFORM_PROTO_NUM)
shdr = (struct smb2_sync_hdr *)buf;
else
shdr = (struct smb2_sync_hdr *)(buf + 4);
@@ -2718,6 +2718,24 @@ smb3_handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid)
NULL, 0, 0);
}
+static int
+smb2_next_header(char *buf)
+{
+ struct smb2_sync_hdr *hdr = (struct smb2_sync_hdr *)buf;
+ struct smb2_transform_hdr *t_hdr = (struct smb2_transform_hdr *)buf;
+
+ /* FIXME: compounding.
+ * How does compounding work with smb3 encryption?
+ * For now, assume that we will have one transform header for each
+ * smb2 header (and assume that smb2->NextOffset is 0 for all of them.)
+ */
+ if (hdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM)
+ return sizeof(struct smb2_transform_hdr) +
+ le32_to_cpu(t_hdr->OriginalMessageSize);
+
+ return le32_to_cpu(hdr->NextCommand);
+}
+
struct smb_version_operations smb20_operations = {
.compare_fids = smb2_compare_fids,
.setup_request = smb2_setup_request,
@@ -2809,6 +2827,7 @@ struct smb_version_operations smb20_operations = {
.get_acl_by_fid = get_smb2_acl_by_fid,
.set_acl = set_smb2_acl,
#endif /* CIFS_ACL */
+ .next_header = smb2_next_header,
};
struct smb_version_operations smb21_operations = {
@@ -2903,6 +2922,7 @@ struct smb_version_operations smb21_operations = {
.get_acl_by_fid = get_smb2_acl_by_fid,
.set_acl = set_smb2_acl,
#endif /* CIFS_ACL */
+ .next_header = smb2_next_header,
};
struct smb_version_operations smb30_operations = {
@@ -3007,6 +3027,7 @@ struct smb_version_operations smb30_operations = {
.get_acl_by_fid = get_smb2_acl_by_fid,
.set_acl = set_smb2_acl,
#endif /* CIFS_ACL */
+ .next_header = smb2_next_header,
};
#ifdef CONFIG_CIFS_SMB311
@@ -3107,6 +3128,7 @@ struct smb_version_operations smb311_operations = {
.query_all_EAs = smb2_query_eas,
.set_EA = smb2_set_ea,
#endif /* CIFS_XATTR */
+ .next_header = smb2_next_header,
};
#endif /* CIFS_SMB311 */
Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com> --- fs/cifs/cifsglob.h | 2 +- fs/cifs/connect.c | 18 +++++++++++++++--- fs/cifs/smb2ops.c | 26 ++++++++++++++++++++++++-- 3 files changed, 40 insertions(+), 6 deletions(-)