LCOV - code coverage report
Current view: top level - source3/libsmb - clilist.c (source / functions) Hit Total Coverage
Test: coverage report for recycleplus df22b230 Lines: 253 576 43.9 %
Date: 2024-02-14 10:14:15 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       16160 : static NTSTATUS is_bad_name(bool windows_names, const char *name)
      32             : {
      33       16160 :         const char *bad_name_p = NULL;
      34             : 
      35       16160 :         bad_name_p = strchr(name, '/');
      36       16160 :         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       16160 :         if (windows_names) {
      44       16160 :                 bad_name_p = strchr(name, '\\');
      45       16160 :                 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       16160 :         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       12129 : NTSTATUS is_bad_finfo_name(const struct cli_state *cli,
      62             :                         const struct file_info *finfo)
      63             : {
      64       12129 :         NTSTATUS status = NT_STATUS_OK;
      65       12129 :         bool windows_names = true;
      66             : 
      67       12129 :         if (cli->requested_posix_capabilities & CIFS_UNIX_POSIX_PATHNAMES_CAP) {
      68           0 :                 windows_names = false;
      69             :         }
      70       12129 :         if (finfo->name != NULL) {
      71       12129 :                 status = is_bad_name(windows_names, finfo->name);
      72       12129 :                 if (!NT_STATUS_IS_OK(status)) {
      73           0 :                         DBG_ERR("bad finfo->name\n");
      74           0 :                         return status;
      75             :                 }
      76             :         }
      77       12129 :         if (finfo->short_name != NULL) {
      78        4031 :                 status = is_bad_name(windows_names, finfo->short_name);
      79        4031 :                 if (!NT_STATUS_IS_OK(status)) {
      80           0 :                         DBG_ERR("bad finfo->short_name\n");
      81           0 :                         return status;
      82             :                 }
      83             :         }
      84       12129 :         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           6 :         if (next_entry_offset == 0 ||
      96           6 :                         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 :         state->mask = smb1_dfs_share_path(state, cli, state->mask);
     398           0 :         if (tevent_req_nomem(state->mask, req)) {
     399           0 :                 return tevent_req_post(req, ev);
     400             :         }
     401           0 :         usable_space = cli_state_available_size(cli, 100);
     402           0 :         state->num_asked = usable_space / DIR_STRUCT_SIZE;
     403             : 
     404           0 :         SSVAL(state->vwv + 0, 0, state->num_asked);
     405           0 :         SSVAL(state->vwv + 1, 0, state->attribute);
     406             : 
     407           0 :         bytes = talloc_array(state, uint8_t, 1);
     408           0 :         if (tevent_req_nomem(bytes, req)) {
     409           0 :                 return tevent_req_post(req, ev);
     410             :         }
     411           0 :         bytes[0] = 4;
     412           0 :         bytes = smb_bytes_push_str(bytes,
     413           0 :                                    smbXcli_conn_use_unicode(cli->conn),
     414           0 :                                    state->mask,
     415           0 :                                    strlen(state->mask)+1,
     416             :                                    NULL);
     417             : 
     418           0 :         bytes = smb_bytes_push_bytes(bytes, 5, (const uint8_t *)&zero, 2);
     419           0 :         if (tevent_req_nomem(bytes, req)) {
     420           0 :                 return tevent_req_post(req, ev);
     421             :         }
     422             : 
     423           0 :         subreq = cli_smb_send(state, state->ev, state->cli, SMBsearch, 0, 0,
     424           0 :                         2, state->vwv, talloc_get_size(bytes), bytes);
     425           0 :         if (tevent_req_nomem(subreq, req)) {
     426           0 :                 return tevent_req_post(req, ev);
     427             :         }
     428           0 :         tevent_req_set_callback(subreq, cli_list_old_done, req);
     429           0 :         return req;
     430             : }
     431             : 
     432           0 : static void cli_list_old_done(struct tevent_req *subreq)
     433             : {
     434           0 :         struct tevent_req *req = tevent_req_callback_data(
     435             :                 subreq, struct tevent_req);
     436           0 :         struct cli_list_old_state *state = tevent_req_data(
     437             :                 req, struct cli_list_old_state);
     438             :         NTSTATUS status;
     439             :         uint8_t cmd;
     440             :         uint8_t wct;
     441             :         uint16_t *vwv;
     442             :         uint32_t num_bytes;
     443             :         uint8_t *bytes;
     444             :         uint16_t received;
     445             :         size_t dirlist_len;
     446             :         uint8_t *tmp;
     447             : 
     448           0 :         status = cli_smb_recv(subreq, state, NULL, 0, &wct, &vwv, &num_bytes,
     449             :                               &bytes);
     450           0 :         if (!NT_STATUS_IS_OK(status)
     451           0 :             && !NT_STATUS_EQUAL(status, NT_STATUS_DOS(ERRDOS, ERRnofiles))
     452           0 :             && !NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
     453           0 :                 TALLOC_FREE(subreq);
     454           0 :                 tevent_req_nterror(req, status);
     455           0 :                 return;
     456             :         }
     457           0 :         if (NT_STATUS_EQUAL(status, NT_STATUS_DOS(ERRDOS, ERRnofiles))
     458           0 :             || NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
     459           0 :                 received = 0;
     460             :         } else {
     461           0 :                 if (wct < 1) {
     462           0 :                         TALLOC_FREE(subreq);
     463           0 :                         tevent_req_nterror(
     464             :                                 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
     465           0 :                         return;
     466             :                 }
     467           0 :                 received = SVAL(vwv + 0, 0);
     468             :         }
     469             : 
     470           0 :         if (received > 0) {
     471             :                 /*
     472             :                  * I don't think this can wrap. received is
     473             :                  * initialized from a 16-bit value.
     474             :                  */
     475           0 :                 if (num_bytes < ((uint32_t)received * DIR_STRUCT_SIZE + 3)) {
     476           0 :                         TALLOC_FREE(subreq);
     477           0 :                         tevent_req_nterror(
     478             :                                 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
     479           0 :                         return;
     480             :                 }
     481             : 
     482           0 :                 dirlist_len = talloc_get_size(state->dirlist);
     483             : 
     484           0 :                 tmp = talloc_realloc(
     485             :                         state, state->dirlist, uint8_t,
     486             :                         dirlist_len + received * DIR_STRUCT_SIZE);
     487           0 :                 if (tevent_req_nomem(tmp, req)) {
     488           0 :                         return;
     489             :                 }
     490           0 :                 state->dirlist = tmp;
     491           0 :                 memcpy(state->dirlist + dirlist_len, bytes + 3,
     492           0 :                        received * DIR_STRUCT_SIZE);
     493             : 
     494           0 :                 SSVAL(state->search_status, 0, 21);
     495           0 :                 memcpy(state->search_status + 2,
     496           0 :                        bytes + 3 + (received-1)*DIR_STRUCT_SIZE, 21);
     497           0 :                 cmd = SMBsearch;
     498             :         } else {
     499           0 :                 if (state->first || state->done) {
     500           0 :                         tevent_req_done(req);
     501           0 :                         return;
     502             :                 }
     503           0 :                 state->done = true;
     504           0 :                 state->num_asked = 0;
     505           0 :                 cmd = SMBfclose;
     506             :         }
     507           0 :         TALLOC_FREE(subreq);
     508             : 
     509           0 :         state->first = false;
     510             : 
     511           0 :         SSVAL(state->vwv + 0, 0, state->num_asked);
     512           0 :         SSVAL(state->vwv + 1, 0, state->attribute);
     513             : 
     514           0 :         bytes = talloc_array(state, uint8_t, 1);
     515           0 :         if (tevent_req_nomem(bytes, req)) {
     516           0 :                 return;
     517             :         }
     518           0 :         bytes[0] = 4;
     519           0 :         bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(state->cli->conn), "",
     520             :                                    1, NULL);
     521           0 :         bytes = smb_bytes_push_bytes(bytes, 5, state->search_status,
     522             :                                      sizeof(state->search_status));
     523           0 :         if (tevent_req_nomem(bytes, req)) {
     524           0 :                 return;
     525             :         }
     526           0 :         subreq = cli_smb_send(state, state->ev, state->cli, cmd, 0, 0,
     527           0 :                               2, state->vwv, talloc_get_size(bytes), bytes);
     528           0 :         if (tevent_req_nomem(subreq, req)) {
     529           0 :                 return;
     530             :         }
     531           0 :         tevent_req_set_callback(subreq, cli_list_old_done, req);
     532             : }
     533             : 
     534           0 : static NTSTATUS cli_list_old_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
     535             :                                   struct file_info **pfinfo)
     536             : {
     537           0 :         struct cli_list_old_state *state = tevent_req_data(
     538             :                 req, struct cli_list_old_state);
     539             :         NTSTATUS status;
     540             :         size_t i, num_received;
     541             :         struct file_info *finfo;
     542             : 
     543           0 :         if (tevent_req_is_nterror(req, &status)) {
     544           0 :                 return status;
     545             :         }
     546             : 
     547           0 :         if (state->dirlist == NULL) {
     548           0 :                 *pfinfo = NULL;
     549           0 :                 return NT_STATUS_OK;
     550             :         }
     551             : 
     552           0 :         num_received = talloc_array_length(state->dirlist) / DIR_STRUCT_SIZE;
     553             : 
     554           0 :         finfo = talloc_array(mem_ctx, struct file_info, num_received);
     555           0 :         if (finfo == NULL) {
     556           0 :                 return NT_STATUS_NO_MEMORY;
     557             :         }
     558             : 
     559           0 :         for (i=0; i<num_received; i++) {
     560           0 :                 if (!interpret_short_filename(
     561             :                             finfo, state->cli,
     562           0 :                             (char *)state->dirlist + i * DIR_STRUCT_SIZE,
     563           0 :                             &finfo[i])) {
     564           0 :                         TALLOC_FREE(finfo);
     565           0 :                         return NT_STATUS_NO_MEMORY;
     566             :                 }
     567           0 :                 if (finfo->name == NULL) {
     568           0 :                         TALLOC_FREE(finfo);
     569           0 :                         return NT_STATUS_INVALID_NETWORK_RESPONSE;
     570             :                 }
     571           0 :                 status = is_bad_finfo_name(state->cli, finfo);
     572           0 :                 if (!NT_STATUS_IS_OK(status)) {
     573           0 :                         smbXcli_conn_disconnect(state->cli->conn, status);
     574           0 :                         TALLOC_FREE(finfo);
     575           0 :                         return status;
     576             :                 }
     577             :         }
     578           0 :         TALLOC_FREE(state->dirlist);
     579           0 :         *pfinfo = finfo;
     580           0 :         return NT_STATUS_OK;
     581             : }
     582             : 
     583           0 : NTSTATUS cli_list_old(struct cli_state *cli, const char *mask,
     584             :                       uint32_t attribute,
     585             :                       NTSTATUS (*fn)(struct file_info *,
     586             :                                  const char *, void *), void *state)
     587             : {
     588           0 :         TALLOC_CTX *frame = talloc_stackframe();
     589             :         struct tevent_context *ev;
     590             :         struct tevent_req *req;
     591           0 :         NTSTATUS status = NT_STATUS_NO_MEMORY;
     592           0 :         struct file_info *finfo = NULL;
     593             :         size_t i, num_finfo;
     594             : 
     595           0 :         if (smbXcli_conn_has_async_calls(cli->conn)) {
     596             :                 /*
     597             :                  * Can't use sync call while an async call is in flight
     598             :                  */
     599           0 :                 status = NT_STATUS_INVALID_PARAMETER;
     600           0 :                 goto fail;
     601             :         }
     602           0 :         ev = samba_tevent_context_init(frame);
     603           0 :         if (ev == NULL) {
     604           0 :                 goto fail;
     605             :         }
     606           0 :         req = cli_list_old_send(frame, ev, cli, mask, attribute);
     607           0 :         if (req == NULL) {
     608           0 :                 goto fail;
     609             :         }
     610           0 :         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
     611           0 :                 goto fail;
     612             :         }
     613           0 :         status = cli_list_old_recv(req, frame, &finfo);
     614           0 :         if (!NT_STATUS_IS_OK(status)) {
     615           0 :                 goto fail;
     616             :         }
     617           0 :         num_finfo = talloc_array_length(finfo);
     618           0 :         for (i=0; i<num_finfo; i++) {
     619           0 :                 status = fn(&finfo[i], mask, state);
     620           0 :                 if (!NT_STATUS_IS_OK(status)) {
     621           0 :                         goto fail;
     622             :                 }
     623             :         }
     624           0 :  fail:
     625           0 :         TALLOC_FREE(frame);
     626           0 :         return status;
     627             : }
     628             : 
     629             : struct cli_list_trans_state {
     630             :         struct tevent_context *ev;
     631             :         struct cli_state *cli;
     632             :         char *mask;
     633             :         uint32_t attribute;
     634             :         uint16_t info_level;
     635             : 
     636             :         int loop_count;
     637             :         int total_received;
     638             :         uint16_t max_matches;
     639             :         bool first;
     640             : 
     641             :         int ff_eos;
     642             :         int ff_dir_handle;
     643             : 
     644             :         uint16_t setup[1];
     645             :         uint8_t *param;
     646             : 
     647             :         struct file_info *finfo;
     648             : };
     649             : 
     650             : static void cli_list_trans_done(struct tevent_req *subreq);
     651             : 
     652           6 : static struct tevent_req *cli_list_trans_send(TALLOC_CTX *mem_ctx,
     653             :                                               struct tevent_context *ev,
     654             :                                               struct cli_state *cli,
     655             :                                               const char *mask,
     656             :                                               uint32_t attribute,
     657             :                                               uint16_t info_level)
     658             : {
     659             :         struct tevent_req *req, *subreq;
     660             :         struct cli_list_trans_state *state;
     661             :         size_t param_len;
     662           6 :         uint16_t additional_flags2 = 0;
     663             : 
     664           6 :         req = tevent_req_create(mem_ctx, &state,
     665             :                                 struct cli_list_trans_state);
     666           6 :         if (req == NULL) {
     667           0 :                 return NULL;
     668             :         }
     669           6 :         state->ev = ev;
     670           6 :         state->cli = cli;
     671           6 :         state->mask = talloc_strdup(state, mask);
     672           6 :         if (tevent_req_nomem(state->mask, req)) {
     673           0 :                 return tevent_req_post(req, ev);
     674             :         }
     675           6 :         state->mask = smb1_dfs_share_path(state, cli, state->mask);
     676           6 :         if (tevent_req_nomem(state->mask, req)) {
     677           0 :                 return tevent_req_post(req, ev);
     678             :         }
     679           6 :         state->attribute = attribute;
     680           6 :         state->info_level = info_level;
     681           6 :         state->loop_count = 0;
     682           6 :         state->first = true;
     683             : 
     684           6 :         state->max_matches = 1366; /* Match W2k */
     685             : 
     686           6 :         SSVAL(&state->setup[0], 0, TRANSACT2_FINDFIRST);
     687             : 
     688           6 :         state->param = talloc_array(state, uint8_t, 12);
     689           6 :         if (tevent_req_nomem(state->param, req)) {
     690           0 :                 return tevent_req_post(req, ev);
     691             :         }
     692             : 
     693           6 :         SSVAL(state->param, 0, state->attribute);
     694           6 :         SSVAL(state->param, 2, state->max_matches);
     695           6 :         SSVAL(state->param, 4,
     696             :               FLAG_TRANS2_FIND_REQUIRE_RESUME
     697             :               |FLAG_TRANS2_FIND_CLOSE_IF_END
     698             :               |(cli->backup_intent ? FLAG_TRANS2_FIND_BACKUP_INTENT : 0));
     699           6 :         SSVAL(state->param, 6, state->info_level);
     700           6 :         SIVAL(state->param, 8, 0);
     701             : 
     702           6 :         state->param = trans2_bytes_push_str(state->param, smbXcli_conn_use_unicode(cli->conn),
     703           6 :                                              state->mask, strlen(state->mask)+1,
     704             :                                              NULL);
     705           6 :         if (tevent_req_nomem(state->param, req)) {
     706           0 :                 return tevent_req_post(req, ev);
     707             :         }
     708             : 
     709           6 :         if (clistr_is_previous_version_path(state->mask)) {
     710           0 :                 additional_flags2 = FLAGS2_REPARSE_PATH;
     711             :         }
     712             : 
     713           6 :         param_len = talloc_get_size(state->param);
     714             : 
     715           6 :         subreq = cli_trans_send(state, state->ev, state->cli, additional_flags2,
     716             :                                 SMBtrans2, NULL, -1, 0, 0,
     717           6 :                                 state->setup, 1, 0,
     718           6 :                                 state->param, param_len, 10,
     719             :                                 NULL, 0, CLI_BUFFER_SIZE);
     720           6 :         if (tevent_req_nomem(subreq, req)) {
     721           0 :                 return tevent_req_post(req, ev);
     722             :         }
     723           6 :         tevent_req_set_callback(subreq, cli_list_trans_done, req);
     724           6 :         return req;
     725             : }
     726             : 
     727           6 : static void cli_list_trans_done(struct tevent_req *subreq)
     728             : {
     729           6 :         struct tevent_req *req = tevent_req_callback_data(
     730             :                 subreq, struct tevent_req);
     731           6 :         struct cli_list_trans_state *state = tevent_req_data(
     732             :                 req, struct cli_list_trans_state);
     733             :         NTSTATUS status;
     734             :         uint8_t *param;
     735             :         uint32_t num_param;
     736             :         uint8_t *data;
     737             :         char *data_end;
     738             :         uint32_t num_data;
     739             :         uint32_t min_param;
     740             :         struct file_info *tmp;
     741             :         size_t old_num_finfo;
     742             :         uint16_t recv_flags2;
     743             :         int ff_searchcount;
     744             :         bool ff_eos;
     745             :         char *p, *p2;
     746           6 :         uint32_t resume_key = 0;
     747             :         int i;
     748             :         DATA_BLOB last_name_raw;
     749           6 :         struct file_info *finfo = NULL;
     750             :         size_t param_len;
     751           6 :         uint16_t additional_flags2 = 0;
     752             : 
     753           6 :         min_param = (state->first ? 6 : 4);
     754             : 
     755           6 :         status = cli_trans_recv(subreq, talloc_tos(), &recv_flags2,
     756             :                                 NULL, 0, NULL,
     757             :                                 &param, min_param, &num_param,
     758             :                                 &data, 0, &num_data);
     759           6 :         TALLOC_FREE(subreq);
     760           6 :         if (!NT_STATUS_IS_OK(status)) {
     761             :                 /*
     762             :                  * TODO: retry, OS/2 nofiles
     763             :                  */
     764           4 :                 tevent_req_nterror(req, status);
     765           6 :                 return;
     766             :         }
     767             : 
     768           2 :         if (state->first) {
     769           2 :                 state->ff_dir_handle = SVAL(param, 0);
     770           2 :                 ff_searchcount = SVAL(param, 2);
     771           2 :                 ff_eos = SVAL(param, 4) != 0;
     772             :         } else {
     773           0 :                 ff_searchcount = SVAL(param, 0);
     774           0 :                 ff_eos = SVAL(param, 2) != 0;
     775             :         }
     776             : 
     777           2 :         old_num_finfo = talloc_array_length(state->finfo);
     778             : 
     779           2 :         tmp = talloc_realloc(state, state->finfo, struct file_info,
     780             :                                    old_num_finfo + ff_searchcount);
     781           2 :         if (tevent_req_nomem(tmp, req)) {
     782           0 :                 return;
     783             :         }
     784           2 :         state->finfo = tmp;
     785             : 
     786           2 :         p2 = p = (char *)data;
     787           2 :         data_end = (char *)data + num_data;
     788           2 :         last_name_raw = data_blob_null;
     789             : 
     790           8 :         for (i=0; i<ff_searchcount; i++) {
     791           6 :                 if (p2 >= data_end) {
     792           0 :                         ff_eos = true;
     793           0 :                         break;
     794             :                 }
     795           6 :                 if ((state->info_level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO)
     796           6 :                     && (i == ff_searchcount-1)) {
     797             :                         /* Last entry - fixup the last offset length. */
     798           2 :                         SIVAL(p2, 0, PTR_DIFF((data + num_data), p2));
     799             :                 }
     800             : 
     801           6 :                 data_blob_free(&last_name_raw);
     802             : 
     803           6 :                 finfo = &state->finfo[old_num_finfo + i];
     804             : 
     805          12 :                 p2 += interpret_long_filename(
     806           6 :                         state->finfo, /* Stick fname to the array as such */
     807           6 :                         state->cli, state->info_level,
     808             :                         (char *)data, recv_flags2, p2,
     809             :                         data_end, finfo, &resume_key, &last_name_raw);
     810             : 
     811           6 :                 if (finfo->name == NULL) {
     812           0 :                         DEBUG(1, ("cli_list: Error: unable to parse name from "
     813             :                                   "info level %d\n", state->info_level));
     814           0 :                         tevent_req_nterror(req,
     815             :                                 NT_STATUS_INVALID_NETWORK_RESPONSE);
     816           0 :                         return;
     817             :                 }
     818             : 
     819           6 :                 status = is_bad_finfo_name(state->cli, finfo);
     820           6 :                 if (!NT_STATUS_IS_OK(status)) {
     821           0 :                         smbXcli_conn_disconnect(state->cli->conn, status);
     822           0 :                         tevent_req_nterror(req, status);
     823           0 :                         return;
     824             :                 }
     825             : 
     826           6 :                 if (!state->first && (state->mask[0] != '\0') &&
     827           0 :                     strcsequal(finfo->name, state->mask)) {
     828           0 :                         DEBUG(1, ("Error: Looping in FIND_NEXT as name %s has "
     829             :                                   "already been seen?\n", finfo->name));
     830           0 :                         ff_eos = true;
     831           0 :                         break;
     832             :                 }
     833             :         }
     834             : 
     835           2 :         if (ff_searchcount == 0) {
     836           0 :                 ff_eos = true;
     837             :         }
     838             : 
     839           2 :         TALLOC_FREE(param);
     840           2 :         TALLOC_FREE(data);
     841             : 
     842             :         /*
     843             :          * Shrink state->finfo to the real length we received
     844             :          */
     845           2 :         tmp = talloc_realloc(state, state->finfo, struct file_info,
     846             :                                    old_num_finfo + i);
     847           2 :         if (tevent_req_nomem(tmp, req)) {
     848           0 :                 return;
     849             :         }
     850           2 :         state->finfo = tmp;
     851             : 
     852           2 :         state->first = false;
     853             : 
     854           2 :         if (ff_eos) {
     855           2 :                 data_blob_free(&last_name_raw);
     856           2 :                 tevent_req_done(req);
     857           2 :                 return;
     858             :         }
     859             : 
     860           0 :         TALLOC_FREE(state->mask);
     861           0 :         state->mask = talloc_strdup(state, finfo->name);
     862           0 :         if (tevent_req_nomem(state->mask, req)) {
     863           0 :                 return;
     864             :         }
     865             : 
     866           0 :         SSVAL(&state->setup[0], 0, TRANSACT2_FINDNEXT);
     867             : 
     868           0 :         param = talloc_realloc(state, state->param, uint8_t, 12);
     869           0 :         if (tevent_req_nomem(param, req)) {
     870           0 :                 return;
     871             :         }
     872           0 :         state->param = param;
     873             : 
     874           0 :         SSVAL(param, 0, state->ff_dir_handle);
     875           0 :         SSVAL(param, 2, state->max_matches); /* max count */
     876           0 :         SSVAL(param, 4, state->info_level);
     877             :         /*
     878             :          * For W2K servers serving out FAT filesystems we *must* set
     879             :          * the resume key. If it's not FAT then it's returned as zero.
     880             :          */
     881           0 :         SIVAL(param, 6, resume_key); /* ff_resume_key */
     882             :         /*
     883             :          * NB. *DON'T* use continue here. If you do it seems that W2K
     884             :          * and bretheren can miss filenames. Use last filename
     885             :          * continue instead. JRA
     886             :          */
     887           0 :         SSVAL(param, 10, (FLAG_TRANS2_FIND_REQUIRE_RESUME
     888             :                           |FLAG_TRANS2_FIND_CLOSE_IF_END
     889             :                           |(state->cli->backup_intent ? FLAG_TRANS2_FIND_BACKUP_INTENT : 0)));
     890           0 :         if (last_name_raw.length) {
     891           0 :                 state->param = trans2_bytes_push_bytes(state->param,
     892           0 :                                                        last_name_raw.data,
     893             :                                                        last_name_raw.length);
     894           0 :                 if (tevent_req_nomem(state->param, req)) {
     895           0 :                         return;
     896             :                 }
     897           0 :                 data_blob_free(&last_name_raw);
     898             :         } else {
     899           0 :                 state->param = trans2_bytes_push_str(state->param,
     900           0 :                                                      smbXcli_conn_use_unicode(state->cli->conn),
     901           0 :                                                      state->mask,
     902           0 :                                                      strlen(state->mask)+1,
     903             :                                                      NULL);
     904           0 :                 if (tevent_req_nomem(state->param, req)) {
     905           0 :                         return;
     906             :                 }
     907             :         }
     908           0 :         param_len = talloc_get_size(state->param);
     909             : 
     910           0 :         if (clistr_is_previous_version_path(state->mask)) {
     911           0 :                 additional_flags2 = FLAGS2_REPARSE_PATH;
     912             :         }
     913             : 
     914           0 :         subreq = cli_trans_send(state, state->ev, state->cli, additional_flags2,
     915             :                                 SMBtrans2, NULL, -1, 0, 0,
     916           0 :                                 state->setup, 1, 0,
     917             :                                 state->param, param_len, 10,
     918             :                                 NULL, 0, CLI_BUFFER_SIZE);
     919           0 :         if (tevent_req_nomem(subreq, req)) {
     920           0 :                 return;
     921             :         }
     922           0 :         tevent_req_set_callback(subreq, cli_list_trans_done, req);
     923             : }
     924             : 
     925           8 : static NTSTATUS cli_list_trans_recv(struct tevent_req *req,
     926             :                                     TALLOC_CTX *mem_ctx,
     927             :                                     struct file_info **finfo)
     928             : {
     929           8 :         struct cli_list_trans_state *state = tevent_req_data(
     930             :                 req, struct cli_list_trans_state);
     931             :         NTSTATUS status;
     932             : 
     933           8 :         if (tevent_req_is_nterror(req, &status)) {
     934           4 :                 return status;
     935             :         }
     936           4 :         *finfo = talloc_move(mem_ctx, &state->finfo);
     937           4 :         return NT_STATUS_OK;
     938             : }
     939             : 
     940           0 : NTSTATUS cli_list_trans(struct cli_state *cli, const char *mask,
     941             :                         uint32_t attribute, int info_level,
     942             :                         NTSTATUS (*fn)(
     943             :                                 struct file_info *finfo,
     944             :                                 const char *mask,
     945             :                                 void *private_data),
     946             :                         void *private_data)
     947             : {
     948           0 :         TALLOC_CTX *frame = talloc_stackframe();
     949             :         struct tevent_context *ev;
     950             :         struct tevent_req *req;
     951             :         int i, num_finfo;
     952           0 :         struct file_info *finfo = NULL;
     953           0 :         NTSTATUS status = NT_STATUS_NO_MEMORY;
     954             : 
     955           0 :         if (smbXcli_conn_has_async_calls(cli->conn)) {
     956             :                 /*
     957             :                  * Can't use sync call while an async call is in flight
     958             :                  */
     959           0 :                 status = NT_STATUS_INVALID_PARAMETER;
     960           0 :                 goto fail;
     961             :         }
     962           0 :         ev = samba_tevent_context_init(frame);
     963           0 :         if (ev == NULL) {
     964           0 :                 goto fail;
     965             :         }
     966           0 :         req = cli_list_trans_send(frame, ev, cli, mask, attribute, info_level);
     967           0 :         if (req == NULL) {
     968           0 :                 goto fail;
     969             :         }
     970           0 :         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
     971           0 :                 goto fail;
     972             :         }
     973           0 :         status = cli_list_trans_recv(req, frame, &finfo);
     974           0 :         if (!NT_STATUS_IS_OK(status)) {
     975           0 :                 goto fail;
     976             :         }
     977           0 :         num_finfo = talloc_array_length(finfo);
     978           0 :         for (i=0; i<num_finfo; i++) {
     979           0 :                 status = fn(&finfo[i], mask, private_data);
     980           0 :                 if (!NT_STATUS_IS_OK(status)) {
     981           0 :                         goto fail;
     982             :                 }
     983             :         }
     984           0 :  fail:
     985           0 :         TALLOC_FREE(frame);
     986           0 :         return status;
     987             : }
     988             : 
     989             : struct cli_list_state {
     990             :         struct tevent_context *ev;
     991             :         struct tevent_req *subreq;
     992             :         NTSTATUS (*recv_fn)(struct tevent_req *req, TALLOC_CTX *mem_ctx,
     993             :                             struct file_info **finfo);
     994             :         struct file_info *finfo;
     995             :         size_t num_received;
     996             : };
     997             : 
     998             : static void cli_list_done(struct tevent_req *subreq);
     999             : 
    1000        2515 : struct tevent_req *cli_list_send(TALLOC_CTX *mem_ctx,
    1001             :                                  struct tevent_context *ev,
    1002             :                                  struct cli_state *cli,
    1003             :                                  const char *mask,
    1004             :                                  uint32_t attribute,
    1005             :                                  uint16_t info_level,
    1006             :                                  bool posix)
    1007             : {
    1008        2515 :         struct tevent_req *req = NULL;
    1009             :         struct cli_list_state *state;
    1010        2515 :         enum protocol_types proto = smbXcli_conn_protocol(cli->conn);
    1011             : 
    1012        2515 :         req = tevent_req_create(mem_ctx, &state, struct cli_list_state);
    1013        2515 :         if (req == NULL) {
    1014           0 :                 return NULL;
    1015             :         }
    1016        2515 :         state->ev = ev;
    1017             : 
    1018        2515 :         if (proto >= PROTOCOL_SMB2_02) {
    1019        2509 :                 state->subreq = cli_smb2_list_send(state, ev, cli, mask,
    1020             :                                                    info_level, posix);
    1021        2509 :                 state->recv_fn = cli_smb2_list_recv;
    1022           6 :         } else if (proto >= PROTOCOL_LANMAN2) {
    1023           6 :                 state->subreq = cli_list_trans_send(
    1024             :                         state, ev, cli, mask, attribute, info_level);
    1025           6 :                 state->recv_fn = cli_list_trans_recv;
    1026             :         } else {
    1027           0 :                 state->subreq = cli_list_old_send(
    1028             :                         state, ev, cli, mask, attribute);
    1029           0 :                 state->recv_fn = cli_list_old_recv;
    1030             :         }
    1031        2515 :         if (tevent_req_nomem(state->subreq, req)) {
    1032           0 :                 return tevent_req_post(req, ev);
    1033             :         }
    1034        2515 :         tevent_req_set_callback(state->subreq, cli_list_done, req);
    1035        2515 :         return req;
    1036             : }
    1037             : 
    1038        7449 : static void cli_list_done(struct tevent_req *subreq)
    1039             : {
    1040        7449 :         struct tevent_req *req = tevent_req_callback_data(
    1041             :                 subreq, struct tevent_req);
    1042        7449 :         struct cli_list_state *state = tevent_req_data(
    1043             :                 req, struct cli_list_state);
    1044             :         NTSTATUS status;
    1045             : 
    1046        7449 :         SMB_ASSERT(subreq == state->subreq);
    1047             : 
    1048             :         /*
    1049             :          * We don't want to be called by the lowerlevel routines
    1050             :          * from within state->recv_fn()
    1051             :          */
    1052        7449 :         tevent_req_set_callback(subreq, NULL, NULL);
    1053             : 
    1054        7449 :         status = state->recv_fn(subreq, state, &state->finfo);
    1055        7449 :         if (NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) {
    1056             :                 /* We'll get back here */
    1057        2485 :                 tevent_req_set_callback(subreq, cli_list_done, req);
    1058        2485 :                 return;
    1059             :         }
    1060             : 
    1061        4964 :         if (tevent_req_nterror(req, status)) {
    1062        2513 :                 return;
    1063             :         }
    1064        2451 :         tevent_req_notify_callback(req);
    1065             : }
    1066             : 
    1067       17095 : NTSTATUS cli_list_recv(
    1068             :         struct tevent_req *req,
    1069             :         TALLOC_CTX *mem_ctx,
    1070             :         struct file_info **pfinfo)
    1071             : {
    1072       17095 :         struct cli_list_state *state = tevent_req_data(
    1073             :                 req, struct cli_list_state);
    1074             :         size_t num_results;
    1075       17095 :         struct file_info *finfo = NULL;
    1076             :         NTSTATUS status;
    1077             :         bool in_progress;
    1078             : 
    1079       17095 :         in_progress = tevent_req_is_in_progress(req);
    1080             : 
    1081       17095 :         if (!in_progress) {
    1082        2515 :                 if (!tevent_req_is_nterror(req, &status)) {
    1083           0 :                         status = NT_STATUS_NO_MORE_FILES;
    1084             :                 }
    1085        2515 :                 return status;
    1086             :         }
    1087             : 
    1088       14580 :         if (state->finfo == NULL) {
    1089       12125 :                 status = state->recv_fn(state->subreq, state, &state->finfo);
    1090             : 
    1091       12125 :                 if (NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) {
    1092        2449 :                         tevent_req_set_callback(
    1093             :                                 state->subreq, cli_list_done, req);
    1094        2449 :                         return NT_STATUS_RETRY;
    1095             :                 }
    1096             : 
    1097        9676 :                 if (NT_STATUS_IS_OK(status) && (state->finfo == NULL)) {
    1098           2 :                         status = NT_STATUS_NO_MORE_FILES;
    1099             :                 }
    1100             : 
    1101        9676 :                 if (tevent_req_nterror(req, status)) {
    1102           2 :                         return status;
    1103             :                 }
    1104             : 
    1105        9674 :                 state->num_received = 0;
    1106             :         }
    1107             : 
    1108       12129 :         num_results = talloc_array_length(state->finfo);
    1109             : 
    1110       12129 :         if (num_results == 1) {
    1111       12123 :                 finfo = talloc_move(mem_ctx, &state->finfo);
    1112             :         } else {
    1113           6 :                 struct file_info *src_finfo =
    1114           6 :                         &state->finfo[state->num_received];
    1115             : 
    1116           6 :                 finfo = talloc(mem_ctx, struct file_info);
    1117           6 :                 if (finfo == NULL) {
    1118           0 :                         return NT_STATUS_NO_MEMORY;
    1119             :                 }
    1120           6 :                 *finfo = *src_finfo;
    1121           6 :                 finfo->name = talloc_move(finfo, &src_finfo->name);
    1122           6 :                 finfo->short_name = talloc_move(finfo, &src_finfo->short_name);
    1123             :         }
    1124             : 
    1125       12129 :         state->num_received += 1;
    1126             : 
    1127       12129 :         if (state->num_received == num_results) {
    1128       12125 :                 TALLOC_FREE(state->finfo);
    1129             :         }
    1130             : 
    1131       12129 :         tevent_req_defer_callback(req, state->ev);
    1132       12129 :         tevent_req_notify_callback(req);
    1133             : 
    1134       12129 :         *pfinfo = finfo;
    1135       12129 :         return NT_STATUS_OK;
    1136             : }
    1137             : 
    1138             : struct cli_list_sync_state {
    1139             :         const char *mask;
    1140             :         uint32_t attribute;
    1141             :         NTSTATUS (*fn)(struct file_info *finfo,
    1142             :                        const char *mask,
    1143             :                        void *private_data);
    1144             :         void *private_data;
    1145             :         NTSTATUS status;
    1146             :         bool processed_file;
    1147             : };
    1148             : 
    1149        8922 : static void cli_list_sync_cb(struct tevent_req *subreq)
    1150             : {
    1151             :         struct cli_list_sync_state *state =
    1152        8922 :                 tevent_req_callback_data_void(subreq);
    1153             :         struct file_info *finfo;
    1154             :         bool ok;
    1155             : 
    1156        8922 :         state->status = cli_list_recv(subreq, talloc_tos(), &finfo);
    1157             :         /* No TALLOC_FREE(subreq), we get here more than once */
    1158             : 
    1159        8922 :         if (NT_STATUS_EQUAL(state->status, NT_STATUS_RETRY)) {
    1160             :                 /*
    1161             :                  * The lowlevel SMB call was rearmed, we'll get back
    1162             :                  * here when it's done.
    1163             :                  */
    1164         918 :                 state->status = NT_STATUS_OK;
    1165         918 :                 return;
    1166             :         }
    1167             : 
    1168        8004 :         if (!NT_STATUS_IS_OK(state->status)) {
    1169         986 :                 return;
    1170             :         }
    1171             : 
    1172        7018 :         ok = dir_check_ftype(finfo->attr, state->attribute);
    1173        7018 :         if (!ok) {
    1174             :                 /*
    1175             :                  * Only process if attributes match.  On SMB1 server
    1176             :                  * does this, so on SMB2 we need to emulate in the
    1177             :                  * client.
    1178             :                  *
    1179             :                  * https://bugzilla.samba.org/show_bug.cgi?id=10260
    1180             :                  */
    1181           0 :                 return;
    1182             :         }
    1183             : 
    1184        7018 :         state->status = state->fn(finfo, state->mask, state->private_data);
    1185             : 
    1186        7018 :         state->processed_file = true;
    1187             : 
    1188        7018 :         TALLOC_FREE(finfo);
    1189             : }
    1190             : 
    1191         984 : NTSTATUS cli_list(struct cli_state *cli,
    1192             :                   const char *mask,
    1193             :                   uint32_t attribute,
    1194             :                   NTSTATUS (*fn)(struct file_info *finfo,
    1195             :                                  const char *mask,
    1196             :                                  void *private_data),
    1197             :                   void *private_data)
    1198             : {
    1199         984 :         TALLOC_CTX *frame = NULL;
    1200         984 :         struct cli_list_sync_state state = {
    1201             :                 .mask = mask,
    1202             :                 .attribute = attribute,
    1203             :                 .fn = fn,
    1204             :                 .private_data = private_data,
    1205             :         };
    1206             :         struct tevent_context *ev;
    1207             :         struct tevent_req *req;
    1208         984 :         NTSTATUS status = NT_STATUS_NO_MEMORY;
    1209             :         uint16_t info_level;
    1210         984 :         enum protocol_types proto = smbXcli_conn_protocol(cli->conn);
    1211             : 
    1212         984 :         frame = talloc_stackframe();
    1213             : 
    1214         984 :         if (smbXcli_conn_has_async_calls(cli->conn)) {
    1215             :                 /*
    1216             :                  * Can't use sync call while an async call is in flight
    1217             :                  */
    1218           0 :                 status = NT_STATUS_INVALID_PARAMETER;
    1219           0 :                 goto fail;
    1220             :         }
    1221         984 :         ev = samba_tevent_context_init(frame);
    1222         984 :         if (ev == NULL) {
    1223           0 :                 goto fail;
    1224             :         }
    1225             : 
    1226         984 :         if (proto >= PROTOCOL_SMB2_02) {
    1227         978 :                 info_level = SMB2_FIND_ID_BOTH_DIRECTORY_INFO;
    1228             :         } else {
    1229           6 :                 info_level = (smb1cli_conn_capabilities(cli->conn) & CAP_NT_SMBS)
    1230             :                         ? SMB_FIND_FILE_BOTH_DIRECTORY_INFO : SMB_FIND_INFO_STANDARD;
    1231             :         }
    1232             : 
    1233         984 :         req = cli_list_send(frame, ev, cli, mask, attribute, info_level, false);
    1234         984 :         if (req == NULL) {
    1235           0 :                 goto fail;
    1236             :         }
    1237         984 :         tevent_req_set_callback(req, cli_list_sync_cb, &state);
    1238             : 
    1239         984 :         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
    1240           0 :                 goto fail;
    1241             :         }
    1242             : 
    1243         984 :         status = state.status;
    1244             : 
    1245         984 :         if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_FILES)) {
    1246         956 :                 status = NT_STATUS_OK;
    1247             :         }
    1248             : 
    1249         984 :         if (NT_STATUS_IS_OK(status) && !state.processed_file) {
    1250          36 :                 status = NT_STATUS_NO_SUCH_FILE;
    1251             :         }
    1252             : 
    1253         948 :  fail:
    1254         984 :         TALLOC_FREE(frame);
    1255         984 :         return status;
    1256             : }

Generated by: LCOV version 1.14