LCOV - code coverage report
Current view: top level - source3/libsmb - clilist.c (source / functions) Hit Total Coverage
Test: coverage report for v4-17-test 1498b464 Lines: 249 566 44.0 %
Date: 2024-06-13 04:01:37 Functions: 12 18 66.7 %

          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       15614 : static NTSTATUS is_bad_name(bool windows_names, const char *name)
      32             : {
      33       15614 :         const char *bad_name_p = NULL;
      34             : 
      35       15614 :         bad_name_p = strchr(name, '/');
      36       15614 :         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       15614 :         if (windows_names) {
      44       15614 :                 bad_name_p = strchr(name, '\\');
      45       15614 :                 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       15614 :         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       11851 : NTSTATUS is_bad_finfo_name(const struct cli_state *cli,
      62             :                         const struct file_info *finfo)
      63             : {
      64       11851 :         NTSTATUS status = NT_STATUS_OK;
      65       11851 :         bool windows_names = true;
      66             : 
      67       11851 :         if (cli->requested_posix_capabilities & CIFS_UNIX_POSIX_PATHNAMES_CAP) {
      68           0 :                 windows_names = false;
      69             :         }
      70       11851 :         if (finfo->name != NULL) {
      71       11851 :                 status = is_bad_name(windows_names, finfo->name);
      72       11851 :                 if (!NT_STATUS_IS_OK(status)) {
      73           0 :                         DBG_ERR("bad finfo->name\n");
      74           0 :                         return status;
      75             :                 }
      76             :         }
      77       11851 :         if (finfo->short_name != NULL) {
      78        3763 :                 status = is_bad_name(windows_names, finfo->short_name);
      79        3763 :                 if (!NT_STATUS_IS_OK(status)) {
      80           0 :                         DBG_ERR("bad finfo->short_name\n");
      81           0 :                         return status;
      82             :                 }
      83             :         }
      84       11851 :         return NT_STATUS_OK;
      85             : }
      86             : 
      87             : /****************************************************************************
      88             :  Calculate a safe next_entry_offset.
      89             : ****************************************************************************/
      90             : 
      91           6 : static size_t calc_next_entry_offset(const char *base, const char *pdata_end)
      92             : {
      93           6 :         size_t next_entry_offset = (size_t)IVAL(base,0);
      94             : 
      95          12 :         if (next_entry_offset == 0 ||
      96          12 :                         base + next_entry_offset < base ||
      97           6 :                         base + next_entry_offset > pdata_end) {
      98           0 :                 next_entry_offset = pdata_end - base;
      99             :         }
     100           6 :         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           6 : 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           6 :         const char *base = p;
     125             : 
     126           6 :         data_blob_free(p_last_name_raw);
     127             : 
     128           6 :         if (p_resume_key) {
     129           6 :                 *p_resume_key = 0;
     130             :         }
     131           6 :         ZERO_STRUCTP(finfo);
     132             : 
     133           6 :         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           6 :                 case SMB_FIND_FILE_BOTH_DIRECTORY_INFO: /* NT uses this, but also accepts 2 */
     235             :                 {
     236             :                         size_t namelen, slen;
     237             : 
     238           6 :                         if (pdata_end - base < 94) {
     239           0 :                                 return pdata_end - base;
     240             :                         }
     241             : 
     242           6 :                         p += 4; /* next entry offset */
     243             : 
     244           6 :                         if (p_resume_key) {
     245           6 :                                 *p_resume_key = IVAL(p,0);
     246             :                         }
     247           6 :                         p += 4; /* fileindex */
     248             : 
     249             :                         /* Offset zero is "create time", not "change time". */
     250           6 :                         p += 8;
     251           6 :                         finfo->atime_ts = interpret_long_date(p);
     252           6 :                         p += 8;
     253           6 :                         finfo->mtime_ts = interpret_long_date(p);
     254           6 :                         p += 8;
     255           6 :                         finfo->ctime_ts = interpret_long_date(p);
     256           6 :                         p += 8;
     257           6 :                         finfo->size = IVAL2_TO_SMB_BIG_UINT(p,0);
     258           6 :                         p += 8;
     259           6 :                         p += 8; /* alloc size */
     260           6 :                         finfo->attr = IVAL(p,0);
     261           6 :                         p += 4;
     262           6 :                         namelen = IVAL(p,0);
     263           6 :                         p += 4;
     264           6 :                         p += 4; /* EA size */
     265           6 :                         slen = CVAL(p, 0);
     266           6 :                         if (slen > 24) {
     267             :                                 /* Bad short name length. */
     268           0 :                                 return pdata_end - base;
     269             :                         }
     270           6 :                         p += 2;
     271           6 :                         ret = pull_string_talloc(ctx,
     272             :                                                  base_ptr,
     273             :                                                  recv_flags2,
     274             :                                                  &finfo->short_name,
     275             :                                                  p,
     276             :                                                  slen,
     277             :                                                  STR_UNICODE);
     278           6 :                         if (ret == (size_t)-1) {
     279           0 :                                 return pdata_end - base;
     280             :                         }
     281           6 :                         p += 24; /* short name? */
     282           6 :                         if (p + namelen < p || p + namelen > pdata_end) {
     283           0 :                                 return pdata_end - base;
     284             :                         }
     285           6 :                         ret = pull_string_talloc(ctx,
     286             :                                                  base_ptr,
     287             :                                                  recv_flags2,
     288             :                                                  &finfo->name,
     289             :                                                  p,
     290             :                                                  namelen,
     291             :                                                  0);
     292           6 :                         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           6 :                         if (p_last_name_raw) {
     302           6 :                                 *p_last_name_raw = data_blob(NULL, namelen+2);
     303           6 :                                 memcpy(p_last_name_raw->data, p, namelen);
     304           6 :                                 SSVAL(p_last_name_raw->data, namelen, 0);
     305             :                         }
     306           6 :                         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 :         if (state->dirlist == NULL) {
     541           0 :                 *pfinfo = NULL;
     542           0 :                 return NT_STATUS_OK;
     543             :         }
     544             : 
     545           0 :         num_received = talloc_array_length(state->dirlist) / DIR_STRUCT_SIZE;
     546             : 
     547           0 :         finfo = talloc_array(mem_ctx, struct file_info, num_received);
     548           0 :         if (finfo == NULL) {
     549           0 :                 return NT_STATUS_NO_MEMORY;
     550             :         }
     551             : 
     552           0 :         for (i=0; i<num_received; i++) {
     553           0 :                 if (!interpret_short_filename(
     554             :                             finfo, state->cli,
     555           0 :                             (char *)state->dirlist + i * DIR_STRUCT_SIZE,
     556           0 :                             &finfo[i])) {
     557           0 :                         TALLOC_FREE(finfo);
     558           0 :                         return NT_STATUS_NO_MEMORY;
     559             :                 }
     560           0 :                 if (finfo->name == NULL) {
     561           0 :                         TALLOC_FREE(finfo);
     562           0 :                         return NT_STATUS_INVALID_NETWORK_RESPONSE;
     563             :                 }
     564           0 :                 status = is_bad_finfo_name(state->cli, finfo);
     565           0 :                 if (!NT_STATUS_IS_OK(status)) {
     566           0 :                         smbXcli_conn_disconnect(state->cli->conn, status);
     567           0 :                         TALLOC_FREE(finfo);
     568           0 :                         return status;
     569             :                 }
     570             :         }
     571           0 :         TALLOC_FREE(state->dirlist);
     572           0 :         *pfinfo = finfo;
     573           0 :         return NT_STATUS_OK;
     574             : }
     575             : 
     576           0 : NTSTATUS cli_list_old(struct cli_state *cli, const char *mask,
     577             :                       uint32_t attribute,
     578             :                       NTSTATUS (*fn)(struct file_info *,
     579             :                                  const char *, void *), void *state)
     580             : {
     581           0 :         TALLOC_CTX *frame = talloc_stackframe();
     582             :         struct tevent_context *ev;
     583             :         struct tevent_req *req;
     584           0 :         NTSTATUS status = NT_STATUS_NO_MEMORY;
     585           0 :         struct file_info *finfo = NULL;
     586             :         size_t i, num_finfo;
     587             : 
     588           0 :         if (smbXcli_conn_has_async_calls(cli->conn)) {
     589             :                 /*
     590             :                  * Can't use sync call while an async call is in flight
     591             :                  */
     592           0 :                 status = NT_STATUS_INVALID_PARAMETER;
     593           0 :                 goto fail;
     594             :         }
     595           0 :         ev = samba_tevent_context_init(frame);
     596           0 :         if (ev == NULL) {
     597           0 :                 goto fail;
     598             :         }
     599           0 :         req = cli_list_old_send(frame, ev, cli, mask, attribute);
     600           0 :         if (req == NULL) {
     601           0 :                 goto fail;
     602             :         }
     603           0 :         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
     604           0 :                 goto fail;
     605             :         }
     606           0 :         status = cli_list_old_recv(req, frame, &finfo);
     607           0 :         if (!NT_STATUS_IS_OK(status)) {
     608           0 :                 goto fail;
     609             :         }
     610           0 :         num_finfo = talloc_array_length(finfo);
     611           0 :         for (i=0; i<num_finfo; i++) {
     612           0 :                 status = fn(&finfo[i], mask, state);
     613           0 :                 if (!NT_STATUS_IS_OK(status)) {
     614           0 :                         goto fail;
     615             :                 }
     616             :         }
     617           0 :  fail:
     618           0 :         TALLOC_FREE(frame);
     619           0 :         return status;
     620             : }
     621             : 
     622             : struct cli_list_trans_state {
     623             :         struct tevent_context *ev;
     624             :         struct cli_state *cli;
     625             :         char *mask;
     626             :         uint32_t attribute;
     627             :         uint16_t info_level;
     628             : 
     629             :         int loop_count;
     630             :         int total_received;
     631             :         uint16_t max_matches;
     632             :         bool first;
     633             : 
     634             :         int ff_eos;
     635             :         int ff_dir_handle;
     636             : 
     637             :         uint16_t setup[1];
     638             :         uint8_t *param;
     639             : 
     640             :         struct file_info *finfo;
     641             : };
     642             : 
     643             : static void cli_list_trans_done(struct tevent_req *subreq);
     644             : 
     645           6 : static struct tevent_req *cli_list_trans_send(TALLOC_CTX *mem_ctx,
     646             :                                               struct tevent_context *ev,
     647             :                                               struct cli_state *cli,
     648             :                                               const char *mask,
     649             :                                               uint32_t attribute,
     650             :                                               uint16_t info_level)
     651             : {
     652             :         struct tevent_req *req, *subreq;
     653             :         struct cli_list_trans_state *state;
     654             :         size_t param_len;
     655           6 :         uint16_t additional_flags2 = 0;
     656             : 
     657           6 :         req = tevent_req_create(mem_ctx, &state,
     658             :                                 struct cli_list_trans_state);
     659           6 :         if (req == NULL) {
     660           0 :                 return NULL;
     661             :         }
     662           6 :         state->ev = ev;
     663           6 :         state->cli = cli;
     664           6 :         state->mask = talloc_strdup(state, mask);
     665           6 :         if (tevent_req_nomem(state->mask, req)) {
     666           0 :                 return tevent_req_post(req, ev);
     667             :         }
     668           6 :         state->attribute = attribute;
     669           6 :         state->info_level = info_level;
     670           6 :         state->loop_count = 0;
     671           6 :         state->first = true;
     672             : 
     673           6 :         state->max_matches = 1366; /* Match W2k */
     674             : 
     675           6 :         SSVAL(&state->setup[0], 0, TRANSACT2_FINDFIRST);
     676             : 
     677           6 :         state->param = talloc_array(state, uint8_t, 12);
     678           6 :         if (tevent_req_nomem(state->param, req)) {
     679           0 :                 return tevent_req_post(req, ev);
     680             :         }
     681             : 
     682           6 :         SSVAL(state->param, 0, state->attribute);
     683           6 :         SSVAL(state->param, 2, state->max_matches);
     684           6 :         SSVAL(state->param, 4,
     685             :               FLAG_TRANS2_FIND_REQUIRE_RESUME
     686             :               |FLAG_TRANS2_FIND_CLOSE_IF_END
     687             :               |(cli->backup_intent ? FLAG_TRANS2_FIND_BACKUP_INTENT : 0));
     688           6 :         SSVAL(state->param, 6, state->info_level);
     689           6 :         SIVAL(state->param, 8, 0);
     690             : 
     691          12 :         state->param = trans2_bytes_push_str(state->param, smbXcli_conn_use_unicode(cli->conn),
     692          12 :                                              state->mask, strlen(state->mask)+1,
     693             :                                              NULL);
     694           6 :         if (tevent_req_nomem(state->param, req)) {
     695           0 :                 return tevent_req_post(req, ev);
     696             :         }
     697             : 
     698           6 :         if (clistr_is_previous_version_path(state->mask, NULL, NULL, NULL)) {
     699           0 :                 additional_flags2 = FLAGS2_REPARSE_PATH;
     700             :         }
     701             : 
     702           6 :         param_len = talloc_get_size(state->param);
     703             : 
     704          18 :         subreq = cli_trans_send(state, state->ev, state->cli, additional_flags2,
     705             :                                 SMBtrans2, NULL, -1, 0, 0,
     706           6 :                                 state->setup, 1, 0,
     707           6 :                                 state->param, param_len, 10,
     708             :                                 NULL, 0, CLI_BUFFER_SIZE);
     709           6 :         if (tevent_req_nomem(subreq, req)) {
     710           0 :                 return tevent_req_post(req, ev);
     711             :         }
     712           6 :         tevent_req_set_callback(subreq, cli_list_trans_done, req);
     713           6 :         return req;
     714             : }
     715             : 
     716           6 : static void cli_list_trans_done(struct tevent_req *subreq)
     717             : {
     718           6 :         struct tevent_req *req = tevent_req_callback_data(
     719             :                 subreq, struct tevent_req);
     720           6 :         struct cli_list_trans_state *state = tevent_req_data(
     721             :                 req, struct cli_list_trans_state);
     722             :         NTSTATUS status;
     723             :         uint8_t *param;
     724             :         uint32_t num_param;
     725             :         uint8_t *data;
     726             :         char *data_end;
     727             :         uint32_t num_data;
     728             :         uint32_t min_param;
     729             :         struct file_info *tmp;
     730             :         size_t old_num_finfo;
     731             :         uint16_t recv_flags2;
     732             :         int ff_searchcount;
     733             :         bool ff_eos;
     734             :         char *p, *p2;
     735           6 :         uint32_t resume_key = 0;
     736             :         int i;
     737             :         DATA_BLOB last_name_raw;
     738           6 :         struct file_info *finfo = NULL;
     739             :         size_t param_len;
     740           6 :         uint16_t additional_flags2 = 0;
     741             : 
     742           6 :         min_param = (state->first ? 6 : 4);
     743             : 
     744           6 :         status = cli_trans_recv(subreq, talloc_tos(), &recv_flags2,
     745             :                                 NULL, 0, NULL,
     746             :                                 &param, min_param, &num_param,
     747             :                                 &data, 0, &num_data);
     748           6 :         TALLOC_FREE(subreq);
     749           6 :         if (!NT_STATUS_IS_OK(status)) {
     750             :                 /*
     751             :                  * TODO: retry, OS/2 nofiles
     752             :                  */
     753           4 :                 tevent_req_nterror(req, status);
     754           4 :                 return;
     755             :         }
     756             : 
     757           2 :         if (state->first) {
     758           2 :                 state->ff_dir_handle = SVAL(param, 0);
     759           2 :                 ff_searchcount = SVAL(param, 2);
     760           2 :                 ff_eos = SVAL(param, 4) != 0;
     761             :         } else {
     762           0 :                 ff_searchcount = SVAL(param, 0);
     763           0 :                 ff_eos = SVAL(param, 2) != 0;
     764             :         }
     765             : 
     766           2 :         old_num_finfo = talloc_array_length(state->finfo);
     767             : 
     768           2 :         tmp = talloc_realloc(state, state->finfo, struct file_info,
     769             :                                    old_num_finfo + ff_searchcount);
     770           2 :         if (tevent_req_nomem(tmp, req)) {
     771           0 :                 return;
     772             :         }
     773           2 :         state->finfo = tmp;
     774             : 
     775           2 :         p2 = p = (char *)data;
     776           2 :         data_end = (char *)data + num_data;
     777           2 :         last_name_raw = data_blob_null;
     778             : 
     779           8 :         for (i=0; i<ff_searchcount; i++) {
     780           6 :                 if (p2 >= data_end) {
     781           0 :                         ff_eos = true;
     782           0 :                         break;
     783             :                 }
     784           6 :                 if ((state->info_level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO)
     785           6 :                     && (i == ff_searchcount-1)) {
     786             :                         /* Last entry - fixup the last offset length. */
     787           2 :                         SIVAL(p2, 0, PTR_DIFF((data + num_data), p2));
     788             :                 }
     789             : 
     790           6 :                 data_blob_free(&last_name_raw);
     791             : 
     792           6 :                 finfo = &state->finfo[old_num_finfo + i];
     793             : 
     794          18 :                 p2 += interpret_long_filename(
     795           6 :                         state->finfo, /* Stick fname to the array as such */
     796           6 :                         state->cli, state->info_level,
     797             :                         (char *)data, recv_flags2, p2,
     798             :                         data_end, finfo, &resume_key, &last_name_raw);
     799             : 
     800           6 :                 if (finfo->name == NULL) {
     801           0 :                         DEBUG(1, ("cli_list: Error: unable to parse name from "
     802             :                                   "info level %d\n", state->info_level));
     803           0 :                         tevent_req_nterror(req,
     804             :                                 NT_STATUS_INVALID_NETWORK_RESPONSE);
     805           0 :                         return;
     806             :                 }
     807             : 
     808           6 :                 status = is_bad_finfo_name(state->cli, finfo);
     809           6 :                 if (!NT_STATUS_IS_OK(status)) {
     810           0 :                         smbXcli_conn_disconnect(state->cli->conn, status);
     811           0 :                         tevent_req_nterror(req, status);
     812           0 :                         return;
     813             :                 }
     814             : 
     815           6 :                 if (!state->first && (state->mask[0] != '\0') &&
     816           0 :                     strcsequal(finfo->name, state->mask)) {
     817           0 :                         DEBUG(1, ("Error: Looping in FIND_NEXT as name %s has "
     818             :                                   "already been seen?\n", finfo->name));
     819           0 :                         ff_eos = true;
     820           0 :                         break;
     821             :                 }
     822             :         }
     823             : 
     824           2 :         if (ff_searchcount == 0) {
     825           0 :                 ff_eos = true;
     826             :         }
     827             : 
     828           2 :         TALLOC_FREE(param);
     829           2 :         TALLOC_FREE(data);
     830             : 
     831             :         /*
     832             :          * Shrink state->finfo to the real length we received
     833             :          */
     834           2 :         tmp = talloc_realloc(state, state->finfo, struct file_info,
     835             :                                    old_num_finfo + i);
     836           2 :         if (tevent_req_nomem(tmp, req)) {
     837           0 :                 return;
     838             :         }
     839           2 :         state->finfo = tmp;
     840             : 
     841           2 :         state->first = false;
     842             : 
     843           2 :         if (ff_eos) {
     844           2 :                 data_blob_free(&last_name_raw);
     845           2 :                 tevent_req_done(req);
     846           2 :                 return;
     847             :         }
     848             : 
     849           0 :         TALLOC_FREE(state->mask);
     850           0 :         state->mask = talloc_strdup(state, finfo->name);
     851           0 :         if (tevent_req_nomem(state->mask, req)) {
     852           0 :                 return;
     853             :         }
     854             : 
     855           0 :         SSVAL(&state->setup[0], 0, TRANSACT2_FINDNEXT);
     856             : 
     857           0 :         param = talloc_realloc(state, state->param, uint8_t, 12);
     858           0 :         if (tevent_req_nomem(param, req)) {
     859           0 :                 return;
     860             :         }
     861           0 :         state->param = param;
     862             : 
     863           0 :         SSVAL(param, 0, state->ff_dir_handle);
     864           0 :         SSVAL(param, 2, state->max_matches); /* max count */
     865           0 :         SSVAL(param, 4, state->info_level);
     866             :         /*
     867             :          * For W2K servers serving out FAT filesystems we *must* set
     868             :          * the resume key. If it's not FAT then it's returned as zero.
     869             :          */
     870           0 :         SIVAL(param, 6, resume_key); /* ff_resume_key */
     871             :         /*
     872             :          * NB. *DON'T* use continue here. If you do it seems that W2K
     873             :          * and bretheren can miss filenames. Use last filename
     874             :          * continue instead. JRA
     875             :          */
     876           0 :         SSVAL(param, 10, (FLAG_TRANS2_FIND_REQUIRE_RESUME
     877             :                           |FLAG_TRANS2_FIND_CLOSE_IF_END
     878             :                           |(state->cli->backup_intent ? FLAG_TRANS2_FIND_BACKUP_INTENT : 0)));
     879           0 :         if (last_name_raw.length) {
     880           0 :                 state->param = trans2_bytes_push_bytes(state->param,
     881           0 :                                                        last_name_raw.data,
     882             :                                                        last_name_raw.length);
     883           0 :                 if (tevent_req_nomem(state->param, req)) {
     884           0 :                         return;
     885             :                 }
     886           0 :                 data_blob_free(&last_name_raw);
     887             :         } else {
     888           0 :                 state->param = trans2_bytes_push_str(state->param,
     889           0 :                                                      smbXcli_conn_use_unicode(state->cli->conn),
     890           0 :                                                      state->mask,
     891           0 :                                                      strlen(state->mask)+1,
     892             :                                                      NULL);
     893           0 :                 if (tevent_req_nomem(state->param, req)) {
     894           0 :                         return;
     895             :                 }
     896             :         }
     897           0 :         param_len = talloc_get_size(state->param);
     898             : 
     899           0 :         if (clistr_is_previous_version_path(state->mask, NULL, NULL, NULL)) {
     900           0 :                 additional_flags2 = FLAGS2_REPARSE_PATH;
     901             :         }
     902             : 
     903           0 :         subreq = cli_trans_send(state, state->ev, state->cli, additional_flags2,
     904             :                                 SMBtrans2, NULL, -1, 0, 0,
     905           0 :                                 state->setup, 1, 0,
     906             :                                 state->param, param_len, 10,
     907             :                                 NULL, 0, CLI_BUFFER_SIZE);
     908           0 :         if (tevent_req_nomem(subreq, req)) {
     909           0 :                 return;
     910             :         }
     911           0 :         tevent_req_set_callback(subreq, cli_list_trans_done, req);
     912             : }
     913             : 
     914           8 : static NTSTATUS cli_list_trans_recv(struct tevent_req *req,
     915             :                                     TALLOC_CTX *mem_ctx,
     916             :                                     struct file_info **finfo)
     917             : {
     918           8 :         struct cli_list_trans_state *state = tevent_req_data(
     919             :                 req, struct cli_list_trans_state);
     920             :         NTSTATUS status;
     921             : 
     922           8 :         if (tevent_req_is_nterror(req, &status)) {
     923           4 :                 return status;
     924             :         }
     925           4 :         *finfo = talloc_move(mem_ctx, &state->finfo);
     926           4 :         return NT_STATUS_OK;
     927             : }
     928             : 
     929           0 : NTSTATUS cli_list_trans(struct cli_state *cli, const char *mask,
     930             :                         uint32_t attribute, int info_level,
     931             :                         NTSTATUS (*fn)(
     932             :                                 struct file_info *finfo,
     933             :                                 const char *mask,
     934             :                                 void *private_data),
     935             :                         void *private_data)
     936             : {
     937           0 :         TALLOC_CTX *frame = talloc_stackframe();
     938             :         struct tevent_context *ev;
     939             :         struct tevent_req *req;
     940             :         int i, num_finfo;
     941           0 :         struct file_info *finfo = NULL;
     942           0 :         NTSTATUS status = NT_STATUS_NO_MEMORY;
     943             : 
     944           0 :         if (smbXcli_conn_has_async_calls(cli->conn)) {
     945             :                 /*
     946             :                  * Can't use sync call while an async call is in flight
     947             :                  */
     948           0 :                 status = NT_STATUS_INVALID_PARAMETER;
     949           0 :                 goto fail;
     950             :         }
     951           0 :         ev = samba_tevent_context_init(frame);
     952           0 :         if (ev == NULL) {
     953           0 :                 goto fail;
     954             :         }
     955           0 :         req = cli_list_trans_send(frame, ev, cli, mask, attribute, info_level);
     956           0 :         if (req == NULL) {
     957           0 :                 goto fail;
     958             :         }
     959           0 :         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
     960           0 :                 goto fail;
     961             :         }
     962           0 :         status = cli_list_trans_recv(req, frame, &finfo);
     963           0 :         if (!NT_STATUS_IS_OK(status)) {
     964           0 :                 goto fail;
     965             :         }
     966           0 :         num_finfo = talloc_array_length(finfo);
     967           0 :         for (i=0; i<num_finfo; i++) {
     968           0 :                 status = fn(&finfo[i], mask, private_data);
     969           0 :                 if (!NT_STATUS_IS_OK(status)) {
     970           0 :                         goto fail;
     971             :                 }
     972             :         }
     973           0 :  fail:
     974           0 :         TALLOC_FREE(frame);
     975           0 :         return status;
     976             : }
     977             : 
     978             : struct cli_list_state {
     979             :         struct tevent_context *ev;
     980             :         struct tevent_req *subreq;
     981             :         NTSTATUS (*recv_fn)(struct tevent_req *req, TALLOC_CTX *mem_ctx,
     982             :                             struct file_info **finfo);
     983             :         struct file_info *finfo;
     984             :         size_t num_received;
     985             : };
     986             : 
     987             : static void cli_list_done(struct tevent_req *subreq);
     988             : 
     989        2509 : struct tevent_req *cli_list_send(TALLOC_CTX *mem_ctx,
     990             :                                  struct tevent_context *ev,
     991             :                                  struct cli_state *cli,
     992             :                                  const char *mask,
     993             :                                  uint32_t attribute,
     994             :                                  uint16_t info_level)
     995             : {
     996        2509 :         struct tevent_req *req = NULL;
     997             :         struct cli_list_state *state;
     998        2509 :         enum protocol_types proto = smbXcli_conn_protocol(cli->conn);
     999             : 
    1000        2509 :         req = tevent_req_create(mem_ctx, &state, struct cli_list_state);
    1001        2509 :         if (req == NULL) {
    1002           0 :                 return NULL;
    1003             :         }
    1004        2509 :         state->ev = ev;
    1005             : 
    1006        2509 :         if (proto >= PROTOCOL_SMB2_02) {
    1007        2503 :                 state->subreq = cli_smb2_list_send(state, ev, cli, mask);
    1008        2503 :                 state->recv_fn = cli_smb2_list_recv;
    1009           6 :         } else if (proto >= PROTOCOL_LANMAN2) {
    1010           6 :                 state->subreq = cli_list_trans_send(
    1011             :                         state, ev, cli, mask, attribute, info_level);
    1012           6 :                 state->recv_fn = cli_list_trans_recv;
    1013             :         } else {
    1014           0 :                 state->subreq = cli_list_old_send(
    1015             :                         state, ev, cli, mask, attribute);
    1016           0 :                 state->recv_fn = cli_list_old_recv;
    1017             :         }
    1018        2509 :         if (tevent_req_nomem(state->subreq, req)) {
    1019           0 :                 return tevent_req_post(req, ev);
    1020             :         }
    1021        2509 :         tevent_req_set_callback(state->subreq, cli_list_done, req);
    1022        2509 :         return req;
    1023             : }
    1024             : 
    1025        7431 : static void cli_list_done(struct tevent_req *subreq)
    1026             : {
    1027        7431 :         struct tevent_req *req = tevent_req_callback_data(
    1028             :                 subreq, struct tevent_req);
    1029        7431 :         struct cli_list_state *state = tevent_req_data(
    1030             :                 req, struct cli_list_state);
    1031             :         NTSTATUS status;
    1032             : 
    1033        7431 :         SMB_ASSERT(subreq == state->subreq);
    1034             : 
    1035             :         /*
    1036             :          * We don't want to be called by the lowerlevel routines
    1037             :          * from within state->recv_fn()
    1038             :          */
    1039        7431 :         tevent_req_set_callback(subreq, NULL, NULL);
    1040             : 
    1041        7431 :         status = state->recv_fn(subreq, state, &state->finfo);
    1042        7431 :         if (NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) {
    1043             :                 /* We'll get back here */
    1044        2479 :                 tevent_req_set_callback(subreq, cli_list_done, req);
    1045        2479 :                 return;
    1046             :         }
    1047             : 
    1048        4952 :         if (tevent_req_nterror(req, status)) {
    1049        2507 :                 return;
    1050             :         }
    1051        2445 :         tevent_req_notify_callback(req);
    1052             : }
    1053             : 
    1054       16805 : NTSTATUS cli_list_recv(
    1055             :         struct tevent_req *req,
    1056             :         TALLOC_CTX *mem_ctx,
    1057             :         struct file_info **pfinfo)
    1058             : {
    1059       16805 :         struct cli_list_state *state = tevent_req_data(
    1060             :                 req, struct cli_list_state);
    1061             :         size_t num_results;
    1062       16805 :         struct file_info *finfo = NULL;
    1063             :         NTSTATUS status;
    1064             :         bool in_progress;
    1065             : 
    1066       16805 :         in_progress = tevent_req_is_in_progress(req);
    1067             : 
    1068       16805 :         if (!in_progress) {
    1069        2509 :                 if (!tevent_req_is_nterror(req, &status)) {
    1070           0 :                         status = NT_STATUS_NO_MORE_FILES;
    1071             :                 }
    1072        2509 :                 return status;
    1073             :         }
    1074             : 
    1075       14296 :         if (state->finfo == NULL) {
    1076       11847 :                 status = state->recv_fn(state->subreq, state, &state->finfo);
    1077             : 
    1078       11847 :                 if (NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) {
    1079        2443 :                         tevent_req_set_callback(
    1080             :                                 state->subreq, cli_list_done, req);
    1081        2443 :                         return NT_STATUS_RETRY;
    1082             :                 }
    1083             : 
    1084        9404 :                 if (NT_STATUS_IS_OK(status) && (state->finfo == NULL)) {
    1085           2 :                         status = NT_STATUS_NO_MORE_FILES;
    1086             :                 }
    1087             : 
    1088        9404 :                 if (tevent_req_nterror(req, status)) {
    1089           2 :                         return status;
    1090             :                 }
    1091             : 
    1092        9402 :                 state->num_received = 0;
    1093             :         }
    1094             : 
    1095       11851 :         num_results = talloc_array_length(state->finfo);
    1096             : 
    1097       11851 :         if (num_results == 1) {
    1098       11845 :                 finfo = talloc_move(mem_ctx, &state->finfo);
    1099             :         } else {
    1100           6 :                 struct file_info *src_finfo =
    1101           6 :                         &state->finfo[state->num_received];
    1102             : 
    1103           6 :                 finfo = talloc(mem_ctx, struct file_info);
    1104           6 :                 if (finfo == NULL) {
    1105           0 :                         return NT_STATUS_NO_MEMORY;
    1106             :                 }
    1107           6 :                 *finfo = *src_finfo;
    1108           6 :                 finfo->name = talloc_move(finfo, &src_finfo->name);
    1109           6 :                 finfo->short_name = talloc_move(finfo, &src_finfo->short_name);
    1110             :         }
    1111             : 
    1112       11851 :         state->num_received += 1;
    1113             : 
    1114       11851 :         if (state->num_received == num_results) {
    1115       11847 :                 TALLOC_FREE(state->finfo);
    1116             :         }
    1117             : 
    1118       11851 :         tevent_req_defer_callback(req, state->ev);
    1119       11851 :         tevent_req_notify_callback(req);
    1120             : 
    1121       11851 :         *pfinfo = finfo;
    1122       11851 :         return NT_STATUS_OK;
    1123             : }
    1124             : 
    1125             : struct cli_list_sync_state {
    1126             :         const char *mask;
    1127             :         uint32_t attribute;
    1128             :         NTSTATUS (*fn)(struct file_info *finfo,
    1129             :                        const char *mask,
    1130             :                        void *private_data);
    1131             :         void *private_data;
    1132             :         NTSTATUS status;
    1133             :         bool processed_file;
    1134             : };
    1135             : 
    1136        8632 : static void cli_list_sync_cb(struct tevent_req *subreq)
    1137             : {
    1138        4688 :         struct cli_list_sync_state *state =
    1139        3944 :                 tevent_req_callback_data_void(subreq);
    1140             :         struct file_info *finfo;
    1141             :         bool ok;
    1142             : 
    1143        8632 :         state->status = cli_list_recv(subreq, talloc_tos(), &finfo);
    1144             :         /* No TALLOC_FREE(subreq), we get here more than once */
    1145             : 
    1146        8632 :         if (NT_STATUS_EQUAL(state->status, NT_STATUS_RETRY)) {
    1147             :                 /*
    1148             :                  * The lowlevel SMB call was rearmed, we'll get back
    1149             :                  * here when it's done.
    1150             :                  */
    1151         912 :                 state->status = NT_STATUS_OK;
    1152        1906 :                 return;
    1153             :         }
    1154             : 
    1155        7720 :         if (!NT_STATUS_IS_OK(state->status)) {
    1156         980 :                 return;
    1157             :         }
    1158             : 
    1159        6740 :         ok = dir_check_ftype(finfo->attr, state->attribute);
    1160        6740 :         if (!ok) {
    1161             :                 /*
    1162             :                  * Only process if attributes match.  On SMB1 server
    1163             :                  * does this, so on SMB2 we need to emulate in the
    1164             :                  * client.
    1165             :                  *
    1166             :                  * https://bugzilla.samba.org/show_bug.cgi?id=10260
    1167             :                  */
    1168           0 :                 return;
    1169             :         }
    1170             : 
    1171        6740 :         state->status = state->fn(finfo, state->mask, state->private_data);
    1172             : 
    1173        6740 :         state->processed_file = true;
    1174             : 
    1175        6740 :         TALLOC_FREE(finfo);
    1176             : }
    1177             : 
    1178         978 : NTSTATUS cli_list(struct cli_state *cli,
    1179             :                   const char *mask,
    1180             :                   uint32_t attribute,
    1181             :                   NTSTATUS (*fn)(struct file_info *finfo,
    1182             :                                  const char *mask,
    1183             :                                  void *private_data),
    1184             :                   void *private_data)
    1185             : {
    1186         978 :         TALLOC_CTX *frame = NULL;
    1187         978 :         struct cli_list_sync_state state = {
    1188             :                 .mask = mask,
    1189             :                 .attribute = attribute,
    1190             :                 .fn = fn,
    1191             :                 .private_data = private_data,
    1192             :         };
    1193             :         struct tevent_context *ev;
    1194             :         struct tevent_req *req;
    1195         978 :         NTSTATUS status = NT_STATUS_NO_MEMORY;
    1196             :         uint16_t info_level;
    1197             : 
    1198         978 :         frame = talloc_stackframe();
    1199             : 
    1200         978 :         if (smbXcli_conn_has_async_calls(cli->conn)) {
    1201             :                 /*
    1202             :                  * Can't use sync call while an async call is in flight
    1203             :                  */
    1204           0 :                 status = NT_STATUS_INVALID_PARAMETER;
    1205           0 :                 goto fail;
    1206             :         }
    1207         978 :         ev = samba_tevent_context_init(frame);
    1208         978 :         if (ev == NULL) {
    1209           0 :                 goto fail;
    1210             :         }
    1211             : 
    1212         978 :         info_level = (smb1cli_conn_capabilities(cli->conn) & CAP_NT_SMBS)
    1213             :                 ? SMB_FIND_FILE_BOTH_DIRECTORY_INFO : SMB_FIND_INFO_STANDARD;
    1214             : 
    1215         978 :         req = cli_list_send(frame, ev, cli, mask, attribute, info_level);
    1216         978 :         if (req == NULL) {
    1217           0 :                 goto fail;
    1218             :         }
    1219         978 :         tevent_req_set_callback(req, cli_list_sync_cb, &state);
    1220             : 
    1221         978 :         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
    1222           0 :                 goto fail;
    1223             :         }
    1224             : 
    1225         978 :         status = state.status;
    1226             : 
    1227         978 :         if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_FILES)) {
    1228         950 :                 status = NT_STATUS_OK;
    1229             :         }
    1230             : 
    1231        1476 :         if (NT_STATUS_IS_OK(status) && !state.processed_file) {
    1232          36 :                 status = NT_STATUS_NO_SUCH_FILE;
    1233             :         }
    1234             : 
    1235        1456 :  fail:
    1236         978 :         TALLOC_FREE(frame);
    1237         978 :         return status;
    1238             : }

Generated by: LCOV version 1.13