LCOV - code coverage report
Current view: top level - source3/libsmb - clilist.c (source / functions) Hit Total Coverage
Test: coverage report for abartlet/fix-coverage dd10fb34 Lines: 254 562 45.2 %
Date: 2021-09-23 10:06:22 Functions: 12 19 63.2 %

          Line data    Source code
       1             : /*
       2             :    Unix SMB/CIFS implementation.
       3             :    client directory list routines
       4             :    Copyright (C) Andrew Tridgell 1994-1998
       5             : 
       6             :    This program is free software; you can redistribute it and/or modify
       7             :    it under the terms of the GNU General Public License as published by
       8             :    the Free Software Foundation; either version 3 of the License, or
       9             :    (at your option) any later version.
      10             : 
      11             :    This program is distributed in the hope that it will be useful,
      12             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             :    GNU General Public License for more details.
      15             : 
      16             :    You should have received a copy of the GNU General Public License
      17             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      18             : */
      19             : 
      20             : #include "includes.h"
      21             : #include "libsmb/libsmb.h"
      22             : #include "../lib/util/tevent_ntstatus.h"
      23             : #include "async_smb.h"
      24             : #include "trans2.h"
      25             : #include "../libcli/smb/smbXcli_base.h"
      26             : 
      27             : /****************************************************************************
      28             :  Check if a returned directory name is safe.
      29             : ****************************************************************************/
      30             : 
      31       56166 : static NTSTATUS is_bad_name(bool windows_names, const char *name)
      32             : {
      33       56166 :         const char *bad_name_p = NULL;
      34             : 
      35       56166 :         bad_name_p = strchr(name, '/');
      36       56166 :         if (bad_name_p != NULL) {
      37             :                 /*
      38             :                  * Windows and POSIX names can't have '/'.
      39             :                  * Server is attacking us.
      40             :                  */
      41           0 :                 return NT_STATUS_INVALID_NETWORK_RESPONSE;
      42             :         }
      43       56166 :         if (windows_names) {
      44       55960 :                 bad_name_p = strchr(name, '\\');
      45       55960 :                 if (bad_name_p != NULL) {
      46             :                         /*
      47             :                          * Windows names can't have '\\'.
      48             :                          * Server is attacking us.
      49             :                          */
      50           0 :                         return NT_STATUS_INVALID_NETWORK_RESPONSE;
      51             :                 }
      52             :         }
      53       56166 :         return NT_STATUS_OK;
      54             : }
      55             : 
      56             : /****************************************************************************
      57             :  Check if a returned directory name is safe. Disconnect if server is
      58             :  sending bad names.
      59             : ****************************************************************************/
      60             : 
      61       41099 : NTSTATUS is_bad_finfo_name(const struct cli_state *cli,
      62             :                         const struct file_info *finfo)
      63             : {
      64       41099 :         NTSTATUS status = NT_STATUS_OK;
      65       41099 :         bool windows_names = true;
      66             : 
      67       41099 :         if (cli->requested_posix_capabilities & CIFS_UNIX_POSIX_PATHNAMES_CAP) {
      68         206 :                 windows_names = false;
      69             :         }
      70       41099 :         if (finfo->name != NULL) {
      71       41099 :                 status = is_bad_name(windows_names, finfo->name);
      72       41099 :                 if (!NT_STATUS_IS_OK(status)) {
      73           0 :                         DBG_ERR("bad finfo->name\n");
      74           0 :                         return status;
      75             :                 }
      76             :         }
      77       41099 :         if (finfo->short_name != NULL) {
      78       15067 :                 status = is_bad_name(windows_names, finfo->short_name);
      79       15067 :                 if (!NT_STATUS_IS_OK(status)) {
      80           0 :                         DBG_ERR("bad finfo->short_name\n");
      81           0 :                         return status;
      82             :                 }
      83             :         }
      84       41099 :         return NT_STATUS_OK;
      85             : }
      86             : 
      87             : /****************************************************************************
      88             :  Calculate a safe next_entry_offset.
      89             : ****************************************************************************/
      90             : 
      91        7103 : static size_t calc_next_entry_offset(const char *base, const char *pdata_end)
      92             : {
      93        7103 :         size_t next_entry_offset = (size_t)IVAL(base,0);
      94             : 
      95       14206 :         if (next_entry_offset == 0 ||
      96       14206 :                         base + next_entry_offset < base ||
      97        7103 :                         base + next_entry_offset > pdata_end) {
      98           0 :                 next_entry_offset = pdata_end - base;
      99             :         }
     100        7103 :         return next_entry_offset;
     101             : }
     102             : 
     103             : /****************************************************************************
     104             :  Interpret a long filename structure - this is mostly guesses at the moment.
     105             :  The length of the structure is returned
     106             :  The structure of a long filename depends on the info level.
     107             :  SMB_FIND_FILE_BOTH_DIRECTORY_INFO is used
     108             :  by NT and SMB_FIND_EA_SIZE is used by OS/2
     109             : ****************************************************************************/
     110             : 
     111        7103 : static size_t interpret_long_filename(TALLOC_CTX *ctx,
     112             :                                         struct cli_state *cli,
     113             :                                         int level,
     114             :                                         const char *base_ptr,
     115             :                                         uint16_t recv_flags2,
     116             :                                         const char *p,
     117             :                                         const char *pdata_end,
     118             :                                         struct file_info *finfo,
     119             :                                         uint32_t *p_resume_key,
     120             :                                         DATA_BLOB *p_last_name_raw)
     121             : {
     122             :         int len;
     123             :         size_t ret;
     124        7103 :         const char *base = p;
     125             : 
     126        7103 :         data_blob_free(p_last_name_raw);
     127             : 
     128        7103 :         if (p_resume_key) {
     129        7103 :                 *p_resume_key = 0;
     130             :         }
     131        7103 :         ZERO_STRUCTP(finfo);
     132             : 
     133        7103 :         switch (level) {
     134           0 :                 case SMB_FIND_INFO_STANDARD: /* OS/2 understands this */
     135             :                         /* these dates are converted to GMT by
     136             :                            make_unix_date */
     137           0 :                         if (pdata_end - base < 27) {
     138           0 :                                 return pdata_end - base;
     139             :                         }
     140             :                         /*
     141             :                          * What we're returning here as ctime_ts is
     142             :                          * actually the server create time.
     143             :                          */
     144           0 :                         finfo->btime_ts = convert_time_t_to_timespec(
     145           0 :                                 make_unix_date2(p+4,
     146             :                                         smb1cli_conn_server_time_zone(
     147             :                                                 cli->conn)));
     148           0 :                         finfo->ctime_ts = convert_time_t_to_timespec(
     149           0 :                                 make_unix_date2(p+4, smb1cli_conn_server_time_zone(cli->conn)));
     150           0 :                         finfo->atime_ts = convert_time_t_to_timespec(
     151           0 :                                 make_unix_date2(p+8, smb1cli_conn_server_time_zone(cli->conn)));
     152           0 :                         finfo->mtime_ts = convert_time_t_to_timespec(
     153           0 :                                 make_unix_date2(p+12, smb1cli_conn_server_time_zone(cli->conn)));
     154           0 :                         finfo->size = IVAL(p,16);
     155           0 :                         finfo->attr = SVAL(p,24);
     156           0 :                         len = CVAL(p, 26);
     157           0 :                         p += 27;
     158           0 :                         if (recv_flags2 & FLAGS2_UNICODE_STRINGS) {
     159           0 :                                 p += ucs2_align(base_ptr, p, STR_UNICODE);
     160             :                         }
     161             : 
     162             :                         /* We can safely use len here (which is required by OS/2)
     163             :                          * and the NAS-BASIC server instead of +2 or +1 as the
     164             :                          * STR_TERMINATE flag below is
     165             :                          * actually used as the length calculation.
     166             :                          * The len is merely an upper bound.
     167             :                          * Due to the explicit 2 byte null termination
     168             :                          * in cli_receive_trans/cli_receive_nt_trans
     169             :                          * we know this is safe. JRA + kukks
     170             :                          */
     171             : 
     172           0 :                         if (p + len > pdata_end) {
     173           0 :                                 return pdata_end - base;
     174             :                         }
     175             : 
     176             :                         /* the len+2 below looks strange but it is
     177             :                            important to cope with the differences
     178             :                            between win2000 and win9x for this call
     179             :                            (tridge) */
     180           0 :                         ret = pull_string_talloc(ctx,
     181             :                                                  base_ptr,
     182             :                                                  recv_flags2,
     183             :                                                  &finfo->name,
     184             :                                                  p,
     185           0 :                                                  len+2,
     186             :                                                  STR_TERMINATE);
     187           0 :                         if (ret == (size_t)-1) {
     188           0 :                                 return pdata_end - base;
     189             :                         }
     190           0 :                         p += ret;
     191           0 :                         return PTR_DIFF(p, base);
     192             : 
     193           0 :                 case SMB_FIND_EA_SIZE: /* this is what OS/2 uses mostly */
     194             :                         /* these dates are converted to GMT by
     195             :                            make_unix_date */
     196           0 :                         if (pdata_end - base < 31) {
     197           0 :                                 return pdata_end - base;
     198             :                         }
     199             :                         /*
     200             :                          * What we're returning here as ctime_ts is
     201             :                          * actually the server create time.
     202             :                          */
     203           0 :                         finfo->btime_ts = convert_time_t_to_timespec(
     204           0 :                                 make_unix_date2(p+4,
     205             :                                         smb1cli_conn_server_time_zone(
     206             :                                                 cli->conn)));
     207           0 :                         finfo->ctime_ts = convert_time_t_to_timespec(
     208           0 :                                 make_unix_date2(p+4, smb1cli_conn_server_time_zone(cli->conn)));
     209           0 :                         finfo->atime_ts = convert_time_t_to_timespec(
     210           0 :                                 make_unix_date2(p+8, smb1cli_conn_server_time_zone(cli->conn)));
     211           0 :                         finfo->mtime_ts = convert_time_t_to_timespec(
     212           0 :                                 make_unix_date2(p+12, smb1cli_conn_server_time_zone(cli->conn)));
     213           0 :                         finfo->size = IVAL(p,16);
     214           0 :                         finfo->attr = SVAL(p,24);
     215           0 :                         len = CVAL(p, 30);
     216           0 :                         p += 31;
     217             :                         /* check for unisys! */
     218           0 :                         if (p + len + 1 > pdata_end) {
     219           0 :                                 return pdata_end - base;
     220             :                         }
     221           0 :                         ret = pull_string_talloc(ctx,
     222             :                                                  base_ptr,
     223             :                                                  recv_flags2,
     224             :                                                  &finfo->name,
     225             :                                                  p,
     226             :                                                  len,
     227             :                                                  STR_NOALIGN);
     228           0 :                         if (ret == (size_t)-1) {
     229           0 :                                 return pdata_end - base;
     230             :                         }
     231           0 :                         p += ret;
     232           0 :                         return PTR_DIFF(p, base) + 1;
     233             : 
     234        7103 :                 case SMB_FIND_FILE_BOTH_DIRECTORY_INFO: /* NT uses this, but also accepts 2 */
     235             :                 {
     236             :                         size_t namelen, slen;
     237             : 
     238        7103 :                         if (pdata_end - base < 94) {
     239           0 :                                 return pdata_end - base;
     240             :                         }
     241             : 
     242        7103 :                         p += 4; /* next entry offset */
     243             : 
     244        7103 :                         if (p_resume_key) {
     245        7103 :                                 *p_resume_key = IVAL(p,0);
     246             :                         }
     247        7103 :                         p += 4; /* fileindex */
     248             : 
     249             :                         /* Offset zero is "create time", not "change time". */
     250        7103 :                         p += 8;
     251        7103 :                         finfo->atime_ts = interpret_long_date(p);
     252        7103 :                         p += 8;
     253        7103 :                         finfo->mtime_ts = interpret_long_date(p);
     254        7103 :                         p += 8;
     255        7103 :                         finfo->ctime_ts = interpret_long_date(p);
     256        7103 :                         p += 8;
     257        7103 :                         finfo->size = IVAL2_TO_SMB_BIG_UINT(p,0);
     258        7103 :                         p += 8;
     259        7103 :                         p += 8; /* alloc size */
     260        7103 :                         finfo->attr = IVAL(p,0);
     261        7103 :                         p += 4;
     262        7103 :                         namelen = IVAL(p,0);
     263        7103 :                         p += 4;
     264        7103 :                         p += 4; /* EA size */
     265        7103 :                         slen = CVAL(p, 0);
     266        7103 :                         if (slen > 24) {
     267             :                                 /* Bad short name length. */
     268           0 :                                 return pdata_end - base;
     269             :                         }
     270        7103 :                         p += 2;
     271        7103 :                         ret = pull_string_talloc(ctx,
     272             :                                                  base_ptr,
     273             :                                                  recv_flags2,
     274             :                                                  &finfo->short_name,
     275             :                                                  p,
     276             :                                                  slen,
     277             :                                                  STR_UNICODE);
     278        7103 :                         if (ret == (size_t)-1) {
     279           0 :                                 return pdata_end - base;
     280             :                         }
     281        7103 :                         p += 24; /* short name? */
     282        7103 :                         if (p + namelen < p || p + namelen > pdata_end) {
     283           0 :                                 return pdata_end - base;
     284             :                         }
     285        7103 :                         ret = pull_string_talloc(ctx,
     286             :                                                  base_ptr,
     287             :                                                  recv_flags2,
     288             :                                                  &finfo->name,
     289             :                                                  p,
     290             :                                                  namelen,
     291             :                                                  0);
     292        7103 :                         if (ret == (size_t)-1) {
     293           0 :                                 return pdata_end - base;
     294             :                         }
     295             : 
     296             :                         /* To be robust in the face of unicode conversion failures
     297             :                            we need to copy the raw bytes of the last name seen here.
     298             :                            Namelen doesn't include the terminating unicode null, so
     299             :                            copy it here. */
     300             : 
     301        7103 :                         if (p_last_name_raw) {
     302        7103 :                                 *p_last_name_raw = data_blob(NULL, namelen+2);
     303        7103 :                                 memcpy(p_last_name_raw->data, p, namelen);
     304        7103 :                                 SSVAL(p_last_name_raw->data, namelen, 0);
     305             :                         }
     306        7103 :                         return calc_next_entry_offset(base, pdata_end);
     307             :                 }
     308             :         }
     309             : 
     310           0 :         DEBUG(1,("Unknown long filename format %d\n",level));
     311           0 :         return calc_next_entry_offset(base, pdata_end);
     312             : }
     313             : 
     314             : /****************************************************************************
     315             :  Interpret a short filename structure.
     316             :  The length of the structure is returned.
     317             : ****************************************************************************/
     318             : 
     319           0 : static bool interpret_short_filename(TALLOC_CTX *ctx,
     320             :                                 struct cli_state *cli,
     321             :                                 char *p,
     322             :                                 struct file_info *finfo)
     323             : {
     324             :         size_t ret;
     325           0 :         ZERO_STRUCTP(finfo);
     326             : 
     327           0 :         finfo->attr = CVAL(p,21);
     328             : 
     329             :         /* We don't get birth time. */
     330           0 :         finfo->btime_ts.tv_sec = 0;
     331           0 :         finfo->btime_ts.tv_nsec = 0;
     332             :         /* this date is converted to GMT by make_unix_date */
     333           0 :         finfo->ctime_ts.tv_sec = make_unix_date(p+22, smb1cli_conn_server_time_zone(cli->conn));
     334           0 :         finfo->ctime_ts.tv_nsec = 0;
     335           0 :         finfo->mtime_ts.tv_sec = finfo->atime_ts.tv_sec = finfo->ctime_ts.tv_sec;
     336           0 :         finfo->mtime_ts.tv_nsec = finfo->atime_ts.tv_nsec = 0;
     337           0 :         finfo->size = IVAL(p,26);
     338           0 :         ret = pull_string_talloc(ctx,
     339             :                                  NULL,
     340             :                                  0,
     341             :                                  &finfo->name,
     342           0 :                                  p+30,
     343             :                                  12,
     344             :                                  STR_ASCII);
     345           0 :         if (ret == (size_t)-1) {
     346           0 :                 return false;
     347             :         }
     348             : 
     349           0 :         if (finfo->name) {
     350           0 :                 finfo->short_name = talloc_strdup(ctx, finfo->name);
     351           0 :                 if (finfo->short_name == NULL) {
     352           0 :                         return false;
     353             :                 }
     354             :         }
     355           0 :         return true;
     356             : }
     357             : 
     358             : struct cli_list_old_state {
     359             :         struct tevent_context *ev;
     360             :         struct cli_state *cli;
     361             :         uint16_t vwv[2];
     362             :         char *mask;
     363             :         int num_asked;
     364             :         uint32_t attribute;
     365             :         uint8_t search_status[23];
     366             :         bool first;
     367             :         bool done;
     368             :         uint8_t *dirlist;
     369             : };
     370             : 
     371             : static void cli_list_old_done(struct tevent_req *subreq);
     372             : 
     373           0 : static struct tevent_req *cli_list_old_send(TALLOC_CTX *mem_ctx,
     374             :                                             struct tevent_context *ev,
     375             :                                             struct cli_state *cli,
     376             :                                             const char *mask,
     377             :                                             uint32_t attribute)
     378             : {
     379             :         struct tevent_req *req, *subreq;
     380             :         struct cli_list_old_state *state;
     381             :         uint8_t *bytes;
     382             :         static const uint16_t zero = 0;
     383             :         uint32_t usable_space;
     384             : 
     385           0 :         req = tevent_req_create(mem_ctx, &state, struct cli_list_old_state);
     386           0 :         if (req == NULL) {
     387           0 :                 return NULL;
     388             :         }
     389           0 :         state->ev = ev;
     390           0 :         state->cli = cli;
     391           0 :         state->attribute = attribute;
     392           0 :         state->first = true;
     393           0 :         state->mask = talloc_strdup(state, mask);
     394           0 :         if (tevent_req_nomem(state->mask, req)) {
     395           0 :                 return tevent_req_post(req, ev);
     396             :         }
     397           0 :         usable_space = cli_state_available_size(cli, 100);
     398           0 :         state->num_asked = usable_space / DIR_STRUCT_SIZE;
     399             : 
     400           0 :         SSVAL(state->vwv + 0, 0, state->num_asked);
     401           0 :         SSVAL(state->vwv + 1, 0, state->attribute);
     402             : 
     403           0 :         bytes = talloc_array(state, uint8_t, 1);
     404           0 :         if (tevent_req_nomem(bytes, req)) {
     405           0 :                 return tevent_req_post(req, ev);
     406             :         }
     407           0 :         bytes[0] = 4;
     408           0 :         bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), mask,
     409           0 :                                    strlen(mask)+1, NULL);
     410             : 
     411           0 :         bytes = smb_bytes_push_bytes(bytes, 5, (const uint8_t *)&zero, 2);
     412           0 :         if (tevent_req_nomem(bytes, req)) {
     413           0 :                 return tevent_req_post(req, ev);
     414             :         }
     415             : 
     416           0 :         subreq = cli_smb_send(state, state->ev, state->cli, SMBsearch, 0, 0,
     417           0 :                         2, state->vwv, talloc_get_size(bytes), bytes);
     418           0 :         if (tevent_req_nomem(subreq, req)) {
     419           0 :                 return tevent_req_post(req, ev);
     420             :         }
     421           0 :         tevent_req_set_callback(subreq, cli_list_old_done, req);
     422           0 :         return req;
     423             : }
     424             : 
     425           0 : static void cli_list_old_done(struct tevent_req *subreq)
     426             : {
     427           0 :         struct tevent_req *req = tevent_req_callback_data(
     428             :                 subreq, struct tevent_req);
     429           0 :         struct cli_list_old_state *state = tevent_req_data(
     430             :                 req, struct cli_list_old_state);
     431             :         NTSTATUS status;
     432             :         uint8_t cmd;
     433             :         uint8_t wct;
     434             :         uint16_t *vwv;
     435             :         uint32_t num_bytes;
     436             :         uint8_t *bytes;
     437             :         uint16_t received;
     438             :         size_t dirlist_len;
     439             :         uint8_t *tmp;
     440             : 
     441           0 :         status = cli_smb_recv(subreq, state, NULL, 0, &wct, &vwv, &num_bytes,
     442             :                               &bytes);
     443           0 :         if (!NT_STATUS_IS_OK(status)
     444           0 :             && !NT_STATUS_EQUAL(status, NT_STATUS_DOS(ERRDOS, ERRnofiles))
     445           0 :             && !NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
     446           0 :                 TALLOC_FREE(subreq);
     447           0 :                 tevent_req_nterror(req, status);
     448           0 :                 return;
     449             :         }
     450           0 :         if (NT_STATUS_EQUAL(status, NT_STATUS_DOS(ERRDOS, ERRnofiles))
     451           0 :             || NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
     452           0 :                 received = 0;
     453             :         } else {
     454           0 :                 if (wct < 1) {
     455           0 :                         TALLOC_FREE(subreq);
     456           0 :                         tevent_req_nterror(
     457             :                                 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
     458           0 :                         return;
     459             :                 }
     460           0 :                 received = SVAL(vwv + 0, 0);
     461             :         }
     462             : 
     463           0 :         if (received > 0) {
     464             :                 /*
     465             :                  * I don't think this can wrap. received is
     466             :                  * initialized from a 16-bit value.
     467             :                  */
     468           0 :                 if (num_bytes < ((uint32_t)received * DIR_STRUCT_SIZE + 3)) {
     469           0 :                         TALLOC_FREE(subreq);
     470           0 :                         tevent_req_nterror(
     471             :                                 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
     472           0 :                         return;
     473             :                 }
     474             : 
     475           0 :                 dirlist_len = talloc_get_size(state->dirlist);
     476             : 
     477           0 :                 tmp = talloc_realloc(
     478             :                         state, state->dirlist, uint8_t,
     479             :                         dirlist_len + received * DIR_STRUCT_SIZE);
     480           0 :                 if (tevent_req_nomem(tmp, req)) {
     481           0 :                         return;
     482             :                 }
     483           0 :                 state->dirlist = tmp;
     484           0 :                 memcpy(state->dirlist + dirlist_len, bytes + 3,
     485           0 :                        received * DIR_STRUCT_SIZE);
     486             : 
     487           0 :                 SSVAL(state->search_status, 0, 21);
     488           0 :                 memcpy(state->search_status + 2,
     489           0 :                        bytes + 3 + (received-1)*DIR_STRUCT_SIZE, 21);
     490           0 :                 cmd = SMBsearch;
     491             :         } else {
     492           0 :                 if (state->first || state->done) {
     493           0 :                         tevent_req_done(req);
     494           0 :                         return;
     495             :                 }
     496           0 :                 state->done = true;
     497           0 :                 state->num_asked = 0;
     498           0 :                 cmd = SMBfclose;
     499             :         }
     500           0 :         TALLOC_FREE(subreq);
     501             : 
     502           0 :         state->first = false;
     503             : 
     504           0 :         SSVAL(state->vwv + 0, 0, state->num_asked);
     505           0 :         SSVAL(state->vwv + 1, 0, state->attribute);
     506             : 
     507           0 :         bytes = talloc_array(state, uint8_t, 1);
     508           0 :         if (tevent_req_nomem(bytes, req)) {
     509           0 :                 return;
     510             :         }
     511           0 :         bytes[0] = 4;
     512           0 :         bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(state->cli->conn), "",
     513             :                                    1, NULL);
     514           0 :         bytes = smb_bytes_push_bytes(bytes, 5, state->search_status,
     515             :                                      sizeof(state->search_status));
     516           0 :         if (tevent_req_nomem(bytes, req)) {
     517           0 :                 return;
     518             :         }
     519           0 :         subreq = cli_smb_send(state, state->ev, state->cli, cmd, 0, 0,
     520           0 :                               2, state->vwv, talloc_get_size(bytes), bytes);
     521           0 :         if (tevent_req_nomem(subreq, req)) {
     522           0 :                 return;
     523             :         }
     524           0 :         tevent_req_set_callback(subreq, cli_list_old_done, req);
     525             : }
     526             : 
     527           0 : static NTSTATUS cli_list_old_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
     528             :                                   struct file_info **pfinfo)
     529             : {
     530           0 :         struct cli_list_old_state *state = tevent_req_data(
     531             :                 req, struct cli_list_old_state);
     532             :         NTSTATUS status;
     533             :         size_t i, num_received;
     534             :         struct file_info *finfo;
     535             : 
     536           0 :         if (tevent_req_is_nterror(req, &status)) {
     537           0 :                 return status;
     538             :         }
     539             : 
     540           0 :         num_received = talloc_array_length(state->dirlist) / DIR_STRUCT_SIZE;
     541             : 
     542           0 :         finfo = talloc_array(mem_ctx, struct file_info, num_received);
     543           0 :         if (finfo == NULL) {
     544           0 :                 return NT_STATUS_NO_MEMORY;
     545             :         }
     546             : 
     547           0 :         for (i=0; i<num_received; i++) {
     548           0 :                 if (!interpret_short_filename(
     549             :                             finfo, state->cli,
     550           0 :                             (char *)state->dirlist + i * DIR_STRUCT_SIZE,
     551           0 :                             &finfo[i])) {
     552           0 :                         TALLOC_FREE(finfo);
     553           0 :                         return NT_STATUS_NO_MEMORY;
     554             :                 }
     555           0 :                 if (finfo->name == NULL) {
     556           0 :                         TALLOC_FREE(finfo);
     557           0 :                         return NT_STATUS_INVALID_NETWORK_RESPONSE;
     558             :                 }
     559           0 :                 status = is_bad_finfo_name(state->cli, finfo);
     560           0 :                 if (!NT_STATUS_IS_OK(status)) {
     561           0 :                         smbXcli_conn_disconnect(state->cli->conn, status);
     562           0 :                         TALLOC_FREE(finfo);
     563           0 :                         return status;
     564             :                 }
     565             :         }
     566           0 :         *pfinfo = finfo;
     567           0 :         return NT_STATUS_OK;
     568             : }
     569             : 
     570           0 : NTSTATUS cli_list_old(struct cli_state *cli, const char *mask,
     571             :                       uint32_t attribute,
     572             :                       NTSTATUS (*fn)(struct file_info *,
     573             :                                  const char *, void *), void *state)
     574             : {
     575           0 :         TALLOC_CTX *frame = talloc_stackframe();
     576             :         struct tevent_context *ev;
     577             :         struct tevent_req *req;
     578           0 :         NTSTATUS status = NT_STATUS_NO_MEMORY;
     579           0 :         struct file_info *finfo = NULL;
     580             :         size_t i, num_finfo;
     581             : 
     582           0 :         if (smbXcli_conn_has_async_calls(cli->conn)) {
     583             :                 /*
     584             :                  * Can't use sync call while an async call is in flight
     585             :                  */
     586           0 :                 status = NT_STATUS_INVALID_PARAMETER;
     587           0 :                 goto fail;
     588             :         }
     589           0 :         ev = samba_tevent_context_init(frame);
     590           0 :         if (ev == NULL) {
     591           0 :                 goto fail;
     592             :         }
     593           0 :         req = cli_list_old_send(frame, ev, cli, mask, attribute);
     594           0 :         if (req == NULL) {
     595           0 :                 goto fail;
     596             :         }
     597           0 :         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
     598           0 :                 goto fail;
     599             :         }
     600           0 :         status = cli_list_old_recv(req, frame, &finfo);
     601           0 :         if (!NT_STATUS_IS_OK(status)) {
     602           0 :                 goto fail;
     603             :         }
     604           0 :         num_finfo = talloc_array_length(finfo);
     605           0 :         for (i=0; i<num_finfo; i++) {
     606           0 :                 status = fn(&finfo[i], mask, state);
     607           0 :                 if (!NT_STATUS_IS_OK(status)) {
     608           0 :                         goto fail;
     609             :                 }
     610             :         }
     611           0 :  fail:
     612           0 :         TALLOC_FREE(frame);
     613           0 :         return status;
     614             : }
     615             : 
     616             : struct cli_list_trans_state {
     617             :         struct tevent_context *ev;
     618             :         struct cli_state *cli;
     619             :         char *mask;
     620             :         uint32_t attribute;
     621             :         uint16_t info_level;
     622             : 
     623             :         int loop_count;
     624             :         int total_received;
     625             :         uint16_t max_matches;
     626             :         bool first;
     627             : 
     628             :         int ff_eos;
     629             :         int ff_dir_handle;
     630             : 
     631             :         uint16_t setup[1];
     632             :         uint8_t *param;
     633             : 
     634             :         struct file_info *finfo;
     635             : };
     636             : 
     637             : static void cli_list_trans_done(struct tevent_req *subreq);
     638             : 
     639        1377 : static struct tevent_req *cli_list_trans_send(TALLOC_CTX *mem_ctx,
     640             :                                               struct tevent_context *ev,
     641             :                                               struct cli_state *cli,
     642             :                                               const char *mask,
     643             :                                               uint32_t attribute,
     644             :                                               uint16_t info_level)
     645             : {
     646             :         struct tevent_req *req, *subreq;
     647             :         struct cli_list_trans_state *state;
     648             :         size_t param_len;
     649        1377 :         uint16_t additional_flags2 = 0;
     650             : 
     651        1377 :         req = tevent_req_create(mem_ctx, &state,
     652             :                                 struct cli_list_trans_state);
     653        1377 :         if (req == NULL) {
     654           0 :                 return NULL;
     655             :         }
     656        1377 :         state->ev = ev;
     657        1377 :         state->cli = cli;
     658        1377 :         state->mask = talloc_strdup(state, mask);
     659        1377 :         if (tevent_req_nomem(state->mask, req)) {
     660           0 :                 return tevent_req_post(req, ev);
     661             :         }
     662        1377 :         state->attribute = attribute;
     663        1377 :         state->info_level = info_level;
     664        1377 :         state->loop_count = 0;
     665        1377 :         state->first = true;
     666             : 
     667        1377 :         state->max_matches = 1366; /* Match W2k */
     668             : 
     669        1377 :         SSVAL(&state->setup[0], 0, TRANSACT2_FINDFIRST);
     670             : 
     671        1377 :         state->param = talloc_array(state, uint8_t, 12);
     672        1377 :         if (tevent_req_nomem(state->param, req)) {
     673           0 :                 return tevent_req_post(req, ev);
     674             :         }
     675             : 
     676        1377 :         SSVAL(state->param, 0, state->attribute);
     677        1377 :         SSVAL(state->param, 2, state->max_matches);
     678        1377 :         SSVAL(state->param, 4,
     679             :               FLAG_TRANS2_FIND_REQUIRE_RESUME
     680             :               |FLAG_TRANS2_FIND_CLOSE_IF_END
     681             :               |(cli->backup_intent ? FLAG_TRANS2_FIND_BACKUP_INTENT : 0));
     682        1377 :         SSVAL(state->param, 6, state->info_level);
     683        1377 :         SIVAL(state->param, 8, 0);
     684             : 
     685        2754 :         state->param = trans2_bytes_push_str(state->param, smbXcli_conn_use_unicode(cli->conn),
     686        2754 :                                              state->mask, strlen(state->mask)+1,
     687             :                                              NULL);
     688        1377 :         if (tevent_req_nomem(state->param, req)) {
     689           0 :                 return tevent_req_post(req, ev);
     690             :         }
     691             : 
     692        1377 :         if (clistr_is_previous_version_path(state->mask, NULL, NULL, NULL)) {
     693          68 :                 additional_flags2 = FLAGS2_REPARSE_PATH;
     694             :         }
     695             : 
     696        1377 :         param_len = talloc_get_size(state->param);
     697             : 
     698        4131 :         subreq = cli_trans_send(state, state->ev, state->cli, additional_flags2,
     699             :                                 SMBtrans2, NULL, -1, 0, 0,
     700        1377 :                                 state->setup, 1, 0,
     701        1377 :                                 state->param, param_len, 10,
     702             :                                 NULL, 0, CLI_BUFFER_SIZE);
     703        1377 :         if (tevent_req_nomem(subreq, req)) {
     704           0 :                 return tevent_req_post(req, ev);
     705             :         }
     706        1377 :         tevent_req_set_callback(subreq, cli_list_trans_done, req);
     707        1377 :         return req;
     708             : }
     709             : 
     710        1377 : static void cli_list_trans_done(struct tevent_req *subreq)
     711             : {
     712        1377 :         struct tevent_req *req = tevent_req_callback_data(
     713             :                 subreq, struct tevent_req);
     714        1377 :         struct cli_list_trans_state *state = tevent_req_data(
     715             :                 req, struct cli_list_trans_state);
     716             :         NTSTATUS status;
     717             :         uint8_t *param;
     718             :         uint32_t num_param;
     719             :         uint8_t *data;
     720             :         char *data_end;
     721             :         uint32_t num_data;
     722             :         uint32_t min_param;
     723             :         struct file_info *tmp;
     724             :         size_t old_num_finfo;
     725             :         uint16_t recv_flags2;
     726             :         int ff_searchcount;
     727             :         bool ff_eos;
     728             :         char *p, *p2;
     729        1377 :         uint32_t resume_key = 0;
     730             :         int i;
     731             :         DATA_BLOB last_name_raw;
     732        1377 :         struct file_info *finfo = NULL;
     733             :         size_t param_len;
     734        1377 :         uint16_t additional_flags2 = 0;
     735             : 
     736        1377 :         min_param = (state->first ? 6 : 4);
     737             : 
     738        1377 :         status = cli_trans_recv(subreq, talloc_tos(), &recv_flags2,
     739             :                                 NULL, 0, NULL,
     740             :                                 &param, min_param, &num_param,
     741             :                                 &data, 0, &num_data);
     742        1377 :         TALLOC_FREE(subreq);
     743        1377 :         if (!NT_STATUS_IS_OK(status)) {
     744             :                 /*
     745             :                  * TODO: retry, OS/2 nofiles
     746             :                  */
     747          79 :                 tevent_req_nterror(req, status);
     748          79 :                 return;
     749             :         }
     750             : 
     751        1298 :         if (state->first) {
     752        1298 :                 state->ff_dir_handle = SVAL(param, 0);
     753        1298 :                 ff_searchcount = SVAL(param, 2);
     754        1298 :                 ff_eos = SVAL(param, 4) != 0;
     755             :         } else {
     756           0 :                 ff_searchcount = SVAL(param, 0);
     757           0 :                 ff_eos = SVAL(param, 2) != 0;
     758             :         }
     759             : 
     760        1298 :         old_num_finfo = talloc_array_length(state->finfo);
     761             : 
     762        1298 :         tmp = talloc_realloc(state, state->finfo, struct file_info,
     763             :                                    old_num_finfo + ff_searchcount);
     764        1298 :         if (tevent_req_nomem(tmp, req)) {
     765           0 :                 return;
     766             :         }
     767        1298 :         state->finfo = tmp;
     768             : 
     769        1298 :         p2 = p = (char *)data;
     770        1298 :         data_end = (char *)data + num_data;
     771        1298 :         last_name_raw = data_blob_null;
     772             : 
     773        8399 :         for (i=0; i<ff_searchcount; i++) {
     774        7103 :                 if (p2 >= data_end) {
     775           0 :                         ff_eos = true;
     776           0 :                         break;
     777             :                 }
     778        7103 :                 if ((state->info_level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO)
     779        7103 :                     && (i == ff_searchcount-1)) {
     780             :                         /* Last entry - fixup the last offset length. */
     781        1298 :                         SIVAL(p2, 0, PTR_DIFF((data + num_data), p2));
     782             :                 }
     783             : 
     784        7103 :                 data_blob_free(&last_name_raw);
     785             : 
     786        7103 :                 finfo = &state->finfo[old_num_finfo + i];
     787             : 
     788       21309 :                 p2 += interpret_long_filename(
     789        7103 :                         state->finfo, /* Stick fname to the array as such */
     790        7103 :                         state->cli, state->info_level,
     791             :                         (char *)data, recv_flags2, p2,
     792             :                         data_end, finfo, &resume_key, &last_name_raw);
     793             : 
     794        7103 :                 if (finfo->name == NULL) {
     795           2 :                         DEBUG(1, ("cli_list: Error: unable to parse name from "
     796             :                                   "info level %d\n", state->info_level));
     797           2 :                         tevent_req_nterror(req,
     798             :                                 NT_STATUS_INVALID_NETWORK_RESPONSE);
     799           2 :                         return;
     800             :                 }
     801             : 
     802        7101 :                 status = is_bad_finfo_name(state->cli, finfo);
     803        7101 :                 if (!NT_STATUS_IS_OK(status)) {
     804           0 :                         smbXcli_conn_disconnect(state->cli->conn, status);
     805           0 :                         tevent_req_nterror(req, status);
     806           0 :                         return;
     807             :                 }
     808             : 
     809        7101 :                 if (!state->first && (state->mask[0] != '\0') &&
     810           0 :                     strcsequal(finfo->name, state->mask)) {
     811           0 :                         DEBUG(1, ("Error: Looping in FIND_NEXT as name %s has "
     812             :                                   "already been seen?\n", finfo->name));
     813           0 :                         ff_eos = true;
     814           0 :                         break;
     815             :                 }
     816             :         }
     817             : 
     818        1296 :         if (ff_searchcount == 0) {
     819           0 :                 ff_eos = true;
     820             :         }
     821             : 
     822        1296 :         TALLOC_FREE(param);
     823        1296 :         TALLOC_FREE(data);
     824             : 
     825             :         /*
     826             :          * Shrink state->finfo to the real length we received
     827             :          */
     828        1296 :         tmp = talloc_realloc(state, state->finfo, struct file_info,
     829             :                                    old_num_finfo + i);
     830        1296 :         if (tevent_req_nomem(tmp, req)) {
     831           0 :                 return;
     832             :         }
     833        1296 :         state->finfo = tmp;
     834             : 
     835        1296 :         state->first = false;
     836             : 
     837        1296 :         if (ff_eos) {
     838        1296 :                 data_blob_free(&last_name_raw);
     839        1296 :                 tevent_req_done(req);
     840        1296 :                 return;
     841             :         }
     842             : 
     843           0 :         TALLOC_FREE(state->mask);
     844           0 :         state->mask = talloc_strdup(state, finfo->name);
     845           0 :         if (tevent_req_nomem(state->mask, req)) {
     846           0 :                 return;
     847             :         }
     848             : 
     849           0 :         SSVAL(&state->setup[0], 0, TRANSACT2_FINDNEXT);
     850             : 
     851           0 :         param = talloc_realloc(state, state->param, uint8_t, 12);
     852           0 :         if (tevent_req_nomem(param, req)) {
     853           0 :                 return;
     854             :         }
     855           0 :         state->param = param;
     856             : 
     857           0 :         SSVAL(param, 0, state->ff_dir_handle);
     858           0 :         SSVAL(param, 2, state->max_matches); /* max count */
     859           0 :         SSVAL(param, 4, state->info_level);
     860             :         /*
     861             :          * For W2K servers serving out FAT filesystems we *must* set
     862             :          * the resume key. If it's not FAT then it's returned as zero.
     863             :          */
     864           0 :         SIVAL(param, 6, resume_key); /* ff_resume_key */
     865             :         /*
     866             :          * NB. *DON'T* use continue here. If you do it seems that W2K
     867             :          * and bretheren can miss filenames. Use last filename
     868             :          * continue instead. JRA
     869             :          */
     870           0 :         SSVAL(param, 10, (FLAG_TRANS2_FIND_REQUIRE_RESUME
     871             :                           |FLAG_TRANS2_FIND_CLOSE_IF_END
     872             :                           |(state->cli->backup_intent ? FLAG_TRANS2_FIND_BACKUP_INTENT : 0)));
     873           0 :         if (last_name_raw.length) {
     874           0 :                 state->param = trans2_bytes_push_bytes(state->param,
     875           0 :                                                        last_name_raw.data,
     876             :                                                        last_name_raw.length);
     877           0 :                 if (tevent_req_nomem(state->param, req)) {
     878           0 :                         return;
     879             :                 }
     880           0 :                 data_blob_free(&last_name_raw);
     881             :         } else {
     882           0 :                 state->param = trans2_bytes_push_str(state->param,
     883           0 :                                                      smbXcli_conn_use_unicode(state->cli->conn),
     884           0 :                                                      state->mask,
     885           0 :                                                      strlen(state->mask)+1,
     886             :                                                      NULL);
     887           0 :                 if (tevent_req_nomem(state->param, req)) {
     888           0 :                         return;
     889             :                 }
     890             :         }
     891           0 :         param_len = talloc_get_size(state->param);
     892             : 
     893           0 :         if (clistr_is_previous_version_path(state->mask, NULL, NULL, NULL)) {
     894           0 :                 additional_flags2 = FLAGS2_REPARSE_PATH;
     895             :         }
     896             : 
     897           0 :         subreq = cli_trans_send(state, state->ev, state->cli, additional_flags2,
     898             :                                 SMBtrans2, NULL, -1, 0, 0,
     899           0 :                                 state->setup, 1, 0,
     900             :                                 state->param, param_len, 10,
     901             :                                 NULL, 0, CLI_BUFFER_SIZE);
     902           0 :         if (tevent_req_nomem(subreq, req)) {
     903           0 :                 return;
     904             :         }
     905           0 :         tevent_req_set_callback(subreq, cli_list_trans_done, req);
     906             : }
     907             : 
     908        2673 : static NTSTATUS cli_list_trans_recv(struct tevent_req *req,
     909             :                                     TALLOC_CTX *mem_ctx,
     910             :                                     struct file_info **finfo)
     911             : {
     912        2673 :         struct cli_list_trans_state *state = tevent_req_data(
     913             :                 req, struct cli_list_trans_state);
     914             :         NTSTATUS status;
     915             : 
     916        2673 :         if (tevent_req_is_nterror(req, &status)) {
     917          81 :                 return status;
     918             :         }
     919        2592 :         *finfo = talloc_move(mem_ctx, &state->finfo);
     920        2592 :         return NT_STATUS_OK;
     921             : }
     922             : 
     923           0 : NTSTATUS cli_list_trans(struct cli_state *cli, const char *mask,
     924             :                         uint32_t attribute, int info_level,
     925             :                         NTSTATUS (*fn)(
     926             :                                 struct file_info *finfo,
     927             :                                 const char *mask,
     928             :                                 void *private_data),
     929             :                         void *private_data)
     930             : {
     931           0 :         TALLOC_CTX *frame = talloc_stackframe();
     932             :         struct tevent_context *ev;
     933             :         struct tevent_req *req;
     934             :         int i, num_finfo;
     935           0 :         struct file_info *finfo = NULL;
     936           0 :         NTSTATUS status = NT_STATUS_NO_MEMORY;
     937             : 
     938           0 :         if (smbXcli_conn_has_async_calls(cli->conn)) {
     939             :                 /*
     940             :                  * Can't use sync call while an async call is in flight
     941             :                  */
     942           0 :                 status = NT_STATUS_INVALID_PARAMETER;
     943           0 :                 goto fail;
     944             :         }
     945           0 :         ev = samba_tevent_context_init(frame);
     946           0 :         if (ev == NULL) {
     947           0 :                 goto fail;
     948             :         }
     949           0 :         req = cli_list_trans_send(frame, ev, cli, mask, attribute, info_level);
     950           0 :         if (req == NULL) {
     951           0 :                 goto fail;
     952             :         }
     953           0 :         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
     954           0 :                 goto fail;
     955             :         }
     956           0 :         status = cli_list_trans_recv(req, frame, &finfo);
     957           0 :         if (!NT_STATUS_IS_OK(status)) {
     958           0 :                 goto fail;
     959             :         }
     960           0 :         num_finfo = talloc_array_length(finfo);
     961           0 :         for (i=0; i<num_finfo; i++) {
     962           0 :                 status = fn(&finfo[i], mask, private_data);
     963           0 :                 if (!NT_STATUS_IS_OK(status)) {
     964           0 :                         goto fail;
     965             :                 }
     966             :         }
     967           0 :  fail:
     968           0 :         TALLOC_FREE(frame);
     969           0 :         return status;
     970             : }
     971             : 
     972             : struct cli_list_state {
     973             :         struct tevent_context *ev;
     974             :         struct tevent_req *subreq;
     975             :         NTSTATUS (*recv_fn)(struct tevent_req *req, TALLOC_CTX *mem_ctx,
     976             :                             struct file_info **finfo);
     977             :         struct file_info *finfo;
     978             :         size_t num_received;
     979             : };
     980             : 
     981             : static void cli_list_done(struct tevent_req *subreq);
     982             : 
     983        7201 : struct tevent_req *cli_list_send(TALLOC_CTX *mem_ctx,
     984             :                                  struct tevent_context *ev,
     985             :                                  struct cli_state *cli,
     986             :                                  const char *mask,
     987             :                                  uint32_t attribute,
     988             :                                  uint16_t info_level)
     989             : {
     990        7201 :         struct tevent_req *req = NULL;
     991             :         struct cli_list_state *state;
     992        7201 :         enum protocol_types proto = smbXcli_conn_protocol(cli->conn);
     993             : 
     994        7201 :         req = tevent_req_create(mem_ctx, &state, struct cli_list_state);
     995        7201 :         if (req == NULL) {
     996           0 :                 return NULL;
     997             :         }
     998        7201 :         state->ev = ev;
     999             : 
    1000        7201 :         if (proto >= PROTOCOL_SMB2_02) {
    1001        5824 :                 state->subreq = cli_smb2_list_send(state, ev, cli, mask);
    1002        5824 :                 state->recv_fn = cli_smb2_list_recv;
    1003        1377 :         } else if (proto >= PROTOCOL_LANMAN2) {
    1004        1377 :                 state->subreq = cli_list_trans_send(
    1005             :                         state, ev, cli, mask, attribute, info_level);
    1006        1377 :                 state->recv_fn = cli_list_trans_recv;
    1007             :         } else {
    1008           0 :                 state->subreq = cli_list_old_send(
    1009             :                         state, ev, cli, mask, attribute);
    1010           0 :                 state->recv_fn = cli_list_old_recv;
    1011             :         }
    1012        7201 :         if (tevent_req_nomem(state->subreq, req)) {
    1013           0 :                 return tevent_req_post(req, ev);
    1014             :         }
    1015        7201 :         tevent_req_set_callback(state->subreq, cli_list_done, req);
    1016        7201 :         return req;
    1017             : }
    1018             : 
    1019       18572 : static void cli_list_done(struct tevent_req *subreq)
    1020             : {
    1021       18572 :         struct tevent_req *req = tevent_req_callback_data(
    1022             :                 subreq, struct tevent_req);
    1023       18572 :         struct cli_list_state *state = tevent_req_data(
    1024             :                 req, struct cli_list_state);
    1025             :         NTSTATUS status;
    1026             : 
    1027       18572 :         SMB_ASSERT(subreq == state->subreq);
    1028             : 
    1029             :         /*
    1030             :          * We don't want to be called by the lowerlevel routines
    1031             :          * from within state->recv_fn()
    1032             :          */
    1033       18572 :         tevent_req_set_callback(subreq, NULL, NULL);
    1034             : 
    1035       18572 :         status = state->recv_fn(subreq, state, &state->finfo);
    1036       18572 :         if (NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) {
    1037             :                 /* We'll get back here */
    1038        5764 :                 tevent_req_set_callback(subreq, cli_list_done, req);
    1039        5764 :                 return;
    1040             :         }
    1041             : 
    1042       12808 :         if (tevent_req_nterror(req, status)) {
    1043        5903 :                 return;
    1044             :         }
    1045        6905 :         tevent_req_notify_callback(req);
    1046             : }
    1047             : 
    1048       55201 : NTSTATUS cli_list_recv(
    1049             :         struct tevent_req *req,
    1050             :         TALLOC_CTX *mem_ctx,
    1051             :         struct file_info **pfinfo)
    1052             : {
    1053       55201 :         struct cli_list_state *state = tevent_req_data(
    1054             :                 req, struct cli_list_state);
    1055             :         size_t num_results;
    1056       55201 :         struct file_info *finfo = NULL;
    1057             :         NTSTATUS status;
    1058             :         bool in_progress;
    1059             : 
    1060       55201 :         in_progress = tevent_req_is_in_progress(req);
    1061             : 
    1062       55201 :         if (!in_progress) {
    1063        7201 :                 if (!tevent_req_is_nterror(req, &status)) {
    1064           0 :                         status = NT_STATUS_NO_MORE_FILES;
    1065             :                 }
    1066        7201 :                 return status;
    1067             :         }
    1068             : 
    1069       48000 :         if (state->finfo == NULL) {
    1070       35294 :                 status = state->recv_fn(state->subreq, state, &state->finfo);
    1071             : 
    1072       35294 :                 if (NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) {
    1073        5607 :                         tevent_req_set_callback(
    1074             :                                 state->subreq, cli_list_done, req);
    1075        5607 :                         return NT_STATUS_RETRY;
    1076             :                 }
    1077             : 
    1078       29687 :                 if (NT_STATUS_IS_OK(status) && (state->finfo == NULL)) {
    1079        1296 :                         status = NT_STATUS_NO_MORE_FILES;
    1080             :                 }
    1081             : 
    1082       29687 :                 if (tevent_req_nterror(req, status)) {
    1083        1298 :                         return status;
    1084             :                 }
    1085             : 
    1086       28389 :                 state->num_received = 0;
    1087             :         }
    1088             : 
    1089       41095 :         num_results = talloc_array_length(state->finfo);
    1090             : 
    1091       41095 :         if (num_results == 1) {
    1092       34604 :                 finfo = talloc_move(mem_ctx, &state->finfo);
    1093             :         } else {
    1094        6491 :                 struct file_info *src_finfo =
    1095        6491 :                         &state->finfo[state->num_received];
    1096             : 
    1097        6491 :                 finfo = talloc(mem_ctx, struct file_info);
    1098        6491 :                 if (finfo == NULL) {
    1099           0 :                         return NT_STATUS_NO_MEMORY;
    1100             :                 }
    1101        6491 :                 *finfo = *src_finfo;
    1102        6491 :                 finfo->name = talloc_move(finfo, &src_finfo->name);
    1103        6491 :                 finfo->short_name = talloc_move(finfo, &src_finfo->short_name);
    1104             :         }
    1105             : 
    1106       41095 :         state->num_received += 1;
    1107             : 
    1108       41095 :         if (state->num_received == num_results) {
    1109       35294 :                 TALLOC_FREE(state->finfo);
    1110             :         }
    1111             : 
    1112       41095 :         tevent_req_defer_callback(req, state->ev);
    1113       41095 :         tevent_req_notify_callback(req);
    1114             : 
    1115       41095 :         *pfinfo = finfo;
    1116       41095 :         return NT_STATUS_OK;
    1117             : }
    1118             : 
    1119             : struct cli_list_sync_state {
    1120             :         const char *mask;
    1121             :         uint32_t attribute;
    1122             :         NTSTATUS (*fn)(struct file_info *finfo,
    1123             :                        const char *mask,
    1124             :                        void *private_data);
    1125             :         void *private_data;
    1126             :         NTSTATUS status;
    1127             :         bool processed_file;
    1128             : };
    1129             : 
    1130       42000 : static void cli_list_sync_cb(struct tevent_req *subreq)
    1131             : {
    1132       37137 :         struct cli_list_sync_state *state =
    1133        4863 :                 tevent_req_callback_data_void(subreq);
    1134             :         struct file_info *finfo;
    1135             :         bool ok;
    1136             : 
    1137       42000 :         state->status = cli_list_recv(subreq, talloc_tos(), &finfo);
    1138             :         /* No TALLOC_FREE(subreq), we get here more than once */
    1139             : 
    1140       42000 :         if (NT_STATUS_EQUAL(state->status, NT_STATUS_RETRY)) {
    1141             :                 /*
    1142             :                  * The lowlevel SMB call was rearmed, we'll get back
    1143             :                  * here when it's done.
    1144             :                  */
    1145        3132 :                 state->status = NT_STATUS_OK;
    1146       11736 :                 return;
    1147             :         }
    1148             : 
    1149       38868 :         if (!NT_STATUS_IS_OK(state->status)) {
    1150        6024 :                 return;
    1151             :         }
    1152             : 
    1153       32844 :         ok = dir_check_ftype(finfo->attr, state->attribute);
    1154       32844 :         if (!ok) {
    1155             :                 /*
    1156             :                  * Only process if attributes match.  On SMB1 server
    1157             :                  * does this, so on SMB2 we need to emulate in the
    1158             :                  * client.
    1159             :                  *
    1160             :                  * https://bugzilla.samba.org/show_bug.cgi?id=10260
    1161             :                  */
    1162           0 :                 return;
    1163             :         }
    1164             : 
    1165       32844 :         state->status = state->fn(finfo, state->mask, state->private_data);
    1166             : 
    1167       32844 :         state->processed_file = true;
    1168             : 
    1169       32844 :         TALLOC_FREE(finfo);
    1170             : }
    1171             : 
    1172        4726 : NTSTATUS cli_list(struct cli_state *cli,
    1173             :                   const char *mask,
    1174             :                   uint32_t attribute,
    1175             :                   NTSTATUS (*fn)(struct file_info *finfo,
    1176             :                                  const char *mask,
    1177             :                                  void *private_data),
    1178             :                   void *private_data)
    1179             : {
    1180        4726 :         TALLOC_CTX *frame = NULL;
    1181        4726 :         struct cli_list_sync_state state = {
    1182             :                 .mask = mask,
    1183             :                 .attribute = attribute,
    1184             :                 .fn = fn,
    1185             :                 .private_data = private_data,
    1186             :         };
    1187             :         struct tevent_context *ev;
    1188             :         struct tevent_req *req;
    1189        4726 :         NTSTATUS status = NT_STATUS_NO_MEMORY;
    1190             :         uint16_t info_level;
    1191             : 
    1192        4726 :         frame = talloc_stackframe();
    1193             : 
    1194        4726 :         if (smbXcli_conn_has_async_calls(cli->conn)) {
    1195             :                 /*
    1196             :                  * Can't use sync call while an async call is in flight
    1197             :                  */
    1198           0 :                 status = NT_STATUS_INVALID_PARAMETER;
    1199           0 :                 goto fail;
    1200             :         }
    1201        4726 :         ev = samba_tevent_context_init(frame);
    1202        4726 :         if (ev == NULL) {
    1203           0 :                 goto fail;
    1204             :         }
    1205             : 
    1206        4726 :         info_level = (smb1cli_conn_capabilities(cli->conn) & CAP_NT_SMBS)
    1207             :                 ? SMB_FIND_FILE_BOTH_DIRECTORY_INFO : SMB_FIND_INFO_STANDARD;
    1208             : 
    1209        4726 :         req = cli_list_send(frame, ev, cli, mask, attribute, info_level);
    1210        4726 :         if (req == NULL) {
    1211           0 :                 goto fail;
    1212             :         }
    1213        4726 :         tevent_req_set_callback(req, cli_list_sync_cb, &state);
    1214             : 
    1215        4726 :         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
    1216           0 :                 goto fail;
    1217             :         }
    1218             : 
    1219        4726 :         status = state.status;
    1220             : 
    1221        4726 :         if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_FILES)) {
    1222        4583 :                 status = NT_STATUS_OK;
    1223             :         }
    1224             : 
    1225        9028 :         if (NT_STATUS_IS_OK(status) && !state.processed_file) {
    1226         155 :                 status = NT_STATUS_NO_SUCH_FILE;
    1227             :         }
    1228             : 
    1229        9008 :  fail:
    1230        4726 :         TALLOC_FREE(frame);
    1231        4726 :         return status;
    1232             : }

Generated by: LCOV version 1.13