Browse Source

Trans2: implement FIND_NEXT request to handle folders with large number of files

Signed-off-by: Jean-Baptiste Kempf <jb@videolan.org>
Sylver Bruneau 9 years ago
parent
commit
46d8d43778
3 changed files with 185 additions and 7 deletions
  1. 2 3
      src/smb_defs.h
  2. 20 0
      src/smb_packets.h
  3. 163 4
      src/smb_trans2.c

+ 2 - 3
src/smb_defs.h

@@ -78,6 +78,7 @@
 // SMB TRANS2 SubCommands
 //-----------------------------------------------------------------------------/
 #define SMB_TR2_FIND_FIRST      0x0001
+#define SMB_TR2_FIND_NEXT       0x0002
 #define SMB_TR2_QUERY_PATH      0x0005
 
 
@@ -169,8 +170,6 @@
 #define SMB_FIND2_FLAG_CLOSE_EOS  (1 << 1)  // Close after End Of Search ?
 #define SMB_FIND2_FLAG_RESUME     (1 << 2)  // Send resume keys ?
 #define SMB_FIND2_FLAG_CONTINUE   (1 << 3)  // not set == new search
-#define SMB_FIND2_FLAG_BACKUP     (1 << 3)  // Backup intent ?
-#define SMB_FIND2_FLAG_DEFAULT    (SMB_FIND2_FLAG_CLOSE_EOS | \
-                                   SMB_FIND2_FLAG_RESUME)
+#define SMB_FIND2_FLAG_BACKUP     (1 << 4)  // Backup intent ?
 
 #endif

+ 20 - 0
src/smb_packets.h

@@ -341,6 +341,17 @@ typedef struct
     uint8_t       pattern[];          // The queried pattern "\\folder\\*"
 } __attribute__((packed))   smb_tr2_findfirst2;
 
+//// -> Trans2|FindNext2
+typedef struct
+{
+    uint16_t      sid;                // Search handle
+    uint16_t      count;              // Search count
+    uint16_t      interest;           // What kind of info do we want ?
+    uint32_t      resume_key;         // Value returned by previous find2 call
+    uint16_t      flags;
+    uint8_t       pattern[];          // The queried pattern "\\folder\\*"
+} __attribute__((packed))   smb_tr2_findnext2;
+
 //// -> Trans2|QueryPathInfo
 typedef struct
 {
@@ -381,6 +392,15 @@ typedef struct
     uint16_t      padding;
 } __attribute__((packed))   smb_tr2_findfirst2_params;
 
+//// <- Trans2|FindNext2Params
+typedef struct
+{
+    uint16_t      count;
+    uint16_t      eos;
+    uint16_t      ea_error_offset;
+    uint16_t      last_name_offset;
+} __attribute__((packed))   smb_tr2_findnext2_params;
+
 //// <- Trans2|FindFirst2FileInfo
 typedef struct
 {

+ 163 - 4
src/smb_trans2.c

@@ -131,6 +131,26 @@ static void smb_find_first_parse(smb_message *msg, smb_file **files_p)
     return;
 }
 
+static void smb_find_next_parse(smb_message *msg, smb_file **files_p)
+{
+    smb_trans2_resp       *tr2;
+    smb_tr2_findnext2_params  *params;
+    smb_tr2_find2_entry   *iter;
+    uint8_t               *eod;
+    size_t                count;
+
+    assert(msg != NULL);
+
+    // Let's parse the answer we got from server
+    tr2     = (smb_trans2_resp *)msg->packet->payload;
+    params  = (smb_tr2_findnext2_params *)tr2->payload;
+    iter    = (smb_tr2_find2_entry *)(tr2->payload + sizeof(smb_tr2_findnext2_params));
+    eod     = msg->packet->payload + msg->payload_size;
+    count   = params->count;
+    smb_tr2_find2_parse_entries(files_p, iter, count, eod);
+    return;
+}
+
 static smb_message  *smb_trans2_find_first (smb_session *s, smb_tid tid, const char *pattern)
 {
     smb_message           *msg;
@@ -181,7 +201,7 @@ static smb_message  *smb_trans2_find_first (smb_session *s, smb_tid tid, const c
     SMB_MSG_INIT_PKT(find);
     find.attrs     = SMB_FIND2_ATTR_DEFAULT;
     find.count     = 1366;     // ??
-    find.flags     = SMB_FIND2_FLAG_DEFAULT | SMB_FIND2_FLAG_CLOSE;
+    find.flags     = SMB_FIND2_FLAG_CLOSE_EOS | SMB_FIND2_FLAG_RESUME;
     find.interest  = 0x0104;   // 'Find file both directory info'
     SMB_MSG_PUT_PKT(msg, find);
     smb_message_append(msg, utf_pattern, utf_pattern_len);
@@ -202,17 +222,156 @@ static smb_message  *smb_trans2_find_first (smb_session *s, smb_tid tid, const c
     return msg;
 }
 
+static smb_message  *smb_trans2_find_next (smb_session *s, smb_tid tid, uint16_t resume_key, uint16_t sid, const char *pattern)
+{
+    smb_message           *msg_find_next2 = NULL;
+    smb_trans2_req        tr2_find_next2;
+    smb_tr2_findnext2     find_next2;
+    size_t                utf_pattern_len, tr2_bct, tr2_param_count;
+    char                  *utf_pattern;
+    int                   res;
+    unsigned int          padding = 0;
+
+    assert(s != NULL && pattern != NULL && tid);
+
+    utf_pattern_len = smb_to_utf16(pattern, strlen(pattern) + 1, &utf_pattern);
+    if (utf_pattern_len == 0)
+        return (NULL);
+
+    tr2_bct = sizeof(smb_tr2_findnext2) + utf_pattern_len;
+    tr2_param_count = tr2_bct;
+    tr2_bct += 3;
+    // Adds padding at the end if necessary.
+    while ((tr2_bct % 4) != 3)
+    {
+        padding++;
+        tr2_bct++;
+    }
+
+    msg_find_next2 = smb_message_new(SMB_CMD_TRANS2);
+    msg_find_next2->packet->header.tid = tid;
+
+    SMB_MSG_INIT_PKT(tr2_find_next2);
+    tr2_find_next2.wct                = 0x0f;
+    tr2_find_next2.total_param_count  = tr2_param_count;
+    tr2_find_next2.total_data_count   = 0x0000;
+    tr2_find_next2.max_param_count    = 10; // ?? Why not the same or 12 ?
+    tr2_find_next2.max_data_count     = 0xffff;
+    //max_setup_count
+    //reserved
+    //flags
+    //timeout
+    //reserve2
+    tr2_find_next2.param_count        = tr2_param_count;
+    tr2_find_next2.param_offset       = 68; // Offset of find_next_params in packet;
+    tr2_find_next2.data_count         = 0;
+    tr2_find_next2.data_offset        = 88; // Offset of pattern in packet
+    tr2_find_next2.setup_count        = 1;
+    //reserve3
+    tr2_find_next2.cmd                = SMB_TR2_FIND_NEXT;
+    tr2_find_next2.bct = tr2_bct; //3 == padding
+    SMB_MSG_PUT_PKT(msg_find_next2, tr2_find_next2);
+
+    SMB_MSG_INIT_PKT(find_next2);
+    find_next2.sid        = sid;
+    find_next2.count      = 255;
+    find_next2.interest   = 0x0104;   // 'Find file both directory info'
+    find_next2.flags      = SMB_FIND2_FLAG_CLOSE_EOS|SMB_FIND2_FLAG_CONTINUE;
+    find_next2.resume_key = resume_key;
+    SMB_MSG_PUT_PKT(msg_find_next2, find_next2);
+    smb_message_append(msg_find_next2, utf_pattern, utf_pattern_len);
+    while (padding--)
+        smb_message_put8(msg_find_next2, 0);
+
+    res = smb_session_send_msg(s, msg_find_next2);
+    smb_message_destroy(msg_find_next2);
+    free(utf_pattern);
+
+    if (!res)
+    {
+        BDSM_dbg("Unable to query pattern: %s\n", pattern);
+        return (NULL);
+    }
+
+    msg_find_next2 = smb_tr2_recv(s);
+    return msg_find_next2;
+}
+
 smb_file  *smb_find(smb_session *s, smb_tid tid, const char *pattern)
 {
-    smb_file              *files = NULL;
-    smb_message           *msg;
+    smb_file                  *files = NULL;
+    smb_message               *msg;
+    smb_trans2_resp           *tr2_resp;
+    smb_tr2_findfirst2_params *findfirst2_params;
+    smb_tr2_findnext2_params  *findnext2_params;
+    bool                      end_of_search;
+    uint16_t                  sid;
+    uint16_t                  resume_key;
+    uint16_t                  error_offset;
 
     assert(s != NULL && pattern != NULL && tid);
 
+    // Send FIND_FIRST request
     msg = smb_trans2_find_first(s,tid,pattern);
-    if (msg != NULL)
+    if (msg)
     {
         smb_find_first_parse(msg,&files);
+        if (files)
+        {
+            // Check if we shall send a FIND_NEXT request
+            tr2_resp          = (smb_trans2_resp *)msg->packet->payload;
+            findfirst2_params = (smb_tr2_findfirst2_params *)tr2_resp->payload;
+
+            sid               = findfirst2_params->id;
+            end_of_search     = findfirst2_params->eos;
+            resume_key        = findfirst2_params->last_name_offset;
+            error_offset      = findfirst2_params->ea_error_offset;
+
+            smb_message_destroy(msg);
+
+            // Send FIND_NEXT queries until the find is finished
+            // or until an error occurs
+            while ((!end_of_search) && (error_offset == 0))
+            {
+                msg = smb_trans2_find_next(s, tid, resume_key, sid, pattern);
+
+                if (msg)
+                {
+                    // Update info for next FIND_NEXT query
+                    tr2_resp         = (smb_trans2_resp *)msg->packet->payload;
+                    findnext2_params = (smb_tr2_findnext2_params *)tr2_resp->payload;
+                    end_of_search    = findnext2_params->eos;
+                    resume_key       = findnext2_params->last_name_offset;
+                    error_offset     = findnext2_params->ea_error_offset;
+
+                    // parse the result for files
+                    smb_find_next_parse(msg, &files);
+                    smb_message_destroy(msg);
+
+                    if (!files)
+                    {
+                        BDSM_dbg("Error during FIND_NEXT answer parsing\n");
+                        end_of_search = true;
+                    }
+                }
+                else
+                {
+                    BDSM_dbg("Error during FIND_NEXT request\n");
+                    smb_stat_list_destroy(files);
+                    end_of_search = true;
+                }
+            }
+        }
+        else
+        {
+            BDSM_dbg("Error during FIND_FIRST answer parsing\n");
+            smb_message_destroy(msg);
+        }
+    }
+    else
+    {
+        BDSM_dbg("Error during FIND_FIRST request\n");
+        smb_stat_list_destroy(files);
         smb_message_destroy(msg);
     }