OSDN Git Service

cifs: update multiplex loop to handle compounded responses
authorRonnie Sahlberg <lsahlber@redhat.com>
Fri, 1 Jun 2018 00:53:08 +0000 (10:53 +1000)
committerSteve French <smfrench@gmail.com>
Sat, 2 Jun 2018 23:36:26 +0000 (18:36 -0500)
Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
Signed-off-by: Steve French <smfrench@gmail.com>
fs/cifs/cifsglob.h
fs/cifs/connect.c
fs/cifs/smb2misc.c
fs/cifs/smb2ops.c

index 76a579a..5105ac5 100644 (file)
@@ -458,7 +458,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 {
index b68c5b9..adc97d0 100644 (file)
@@ -856,6 +856,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;
@@ -893,17 +894,18 @@ cifs_demultiplex_thread(void *p)
                 * so we can now interpret the length field.
                 */
                pdu_length = get_rfc1002_length(buf);
-               server->pdu_size = pdu_length;
 
                cifs_dbg(FYI, "RFC1002 header 0x%x\n", pdu_length);
                if (!is_smb_response(server, buf[0]))
                        continue;
+next_pdu:
+               server->pdu_size = pdu_length;
 
                /* make sure we have enough to get to the MID */
-               if (pdu_length < HEADER_SIZE(server) - 1 -
+               if (server->pdu_size < HEADER_SIZE(server) - 1 -
                    server->vals->header_preamble_size) {
                        cifs_dbg(VFS, "SMB response too short (%u bytes)\n",
-                                pdu_length);
+                                server->pdu_size);
                        cifs_reconnect(server);
                        wake_up(&server->response_q);
                        continue;
@@ -918,6 +920,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->pdu_size = next_offset;
+               }
+
                if (server->ops->is_transform_hdr &&
                    server->ops->receive_transform &&
                    server->ops->is_transform_hdr(buf)) {
@@ -963,7 +971,15 @@ cifs_demultiplex_thread(void *p)
                                server->ops->dump_detail(buf, server);
                        cifs_dump_mids(server);
 #endif /* CIFS_DEBUG2 */
-
+               }
+               if (pdu_length > server->pdu_size) {
+                       if (!allocate_buffers(server))
+                               continue;
+                       pdu_length -= server->pdu_size;
+                       server->total_read = 0;
+                       server->large_buf = false;
+                       buf = server->smallbuf;
+                       goto next_pdu;
                }
        } /* end while !EXITING */
 
index ea964f7..cb5728e 100644 (file)
@@ -244,6 +244,7 @@ smb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *srvr)
                 * MacOS server pads after SMB2.1 write response with 3 bytes
                 * of junk. Other servers match RFC1001 len to actual
                 * SMB2/SMB3 frame length (header + smb2 response specific data)
+                * Some windows servers do too when compounding is used.
                 * Log the server error (once), but allow it and continue
                 * since the frame is parseable.
                 */
index 36e7495..43ec82a 100644 (file)
@@ -2807,6 +2807,19 @@ 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;
+
+       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,
@@ -2898,6 +2911,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 = {
@@ -2992,6 +3006,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 = {
@@ -3096,6 +3111,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
@@ -3196,6 +3212,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 */