LCOV - code coverage report
Current view: top level - source3/libsmb - clireadwrite.c (source / functions) Hit Total Coverage
Test: coverage report for abartlet/fix-coverage dd10fb34 Lines: 668 800 83.5 %
Date: 2021-09-23 10:06:22 Functions: 40 40 100.0 %

          Line data    Source code
       1             : /*
       2             :    Unix SMB/CIFS implementation.
       3             :    client file read/write 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             :   Calculate the recommended read buffer size
      29             : ****************************************************************************/
      30        2946 : static size_t cli_read_max_bufsize(struct cli_state *cli)
      31             : {
      32        2946 :         uint8_t wct = 12;
      33             :         uint32_t min_space;
      34             :         uint32_t data_offset;
      35        2946 :         uint32_t useable_space = 0;
      36             : 
      37        2946 :         data_offset = HDR_VWV;
      38        2946 :         data_offset += wct * sizeof(uint16_t);
      39        2946 :         data_offset += sizeof(uint16_t); /* byte count */
      40        2946 :         data_offset += 1; /* pad */
      41             : 
      42        2946 :         min_space = cli_state_available_size(cli, data_offset);
      43             : 
      44        2946 :         if (cli->server_posix_capabilities & CIFS_UNIX_LARGE_READ_CAP) {
      45         438 :                 useable_space = 0xFFFFFF - data_offset;
      46             : 
      47         438 :                 if (smb1cli_conn_signing_is_active(cli->conn)) {
      48           0 :                         return min_space;
      49             :                 }
      50             : 
      51         438 :                 if (smb1cli_conn_encryption_on(cli->conn)) {
      52         434 :                         return min_space;
      53             :                 }
      54             : 
      55           4 :                 return useable_space;
      56        2508 :         } else if (smb1cli_conn_capabilities(cli->conn) & CAP_LARGE_READX) {
      57             :                 /*
      58             :                  * Note: CAP_LARGE_READX also works with signing
      59             :                  */
      60        2508 :                 useable_space = 0x1FFFF - data_offset;
      61             : 
      62        2508 :                 useable_space = MIN(useable_space, UINT16_MAX);
      63             : 
      64        2508 :                 return useable_space;
      65             :         }
      66             : 
      67           0 :         return min_space;
      68             : }
      69             : 
      70             : /****************************************************************************
      71             :   Calculate the recommended write buffer size
      72             : ****************************************************************************/
      73       11409 : static size_t cli_write_max_bufsize(struct cli_state *cli,
      74             :                                     uint16_t write_mode,
      75             :                                     uint8_t wct)
      76             : {
      77             :         uint32_t min_space;
      78             :         uint32_t data_offset;
      79       11409 :         uint32_t useable_space = 0;
      80             : 
      81       11409 :         data_offset = HDR_VWV;
      82       11409 :         data_offset += wct * sizeof(uint16_t);
      83       11409 :         data_offset += sizeof(uint16_t); /* byte count */
      84       11409 :         data_offset += 1; /* pad */
      85             : 
      86       11409 :         min_space = cli_state_available_size(cli, data_offset);
      87             : 
      88       11409 :         if (cli->server_posix_capabilities & CIFS_UNIX_LARGE_WRITE_CAP) {
      89           0 :                 useable_space = 0xFFFFFF - data_offset;
      90       11409 :         } else if (smb1cli_conn_capabilities(cli->conn) & CAP_LARGE_WRITEX) {
      91       11409 :                 useable_space = 0x1FFFF - data_offset;
      92             :         } else {
      93           0 :                 return min_space;
      94             :         }
      95             : 
      96       11409 :         if (write_mode != 0) {
      97           0 :                 return min_space;
      98             :         }
      99             : 
     100       11409 :         if (smb1cli_conn_signing_is_active(cli->conn)) {
     101        4349 :                 return min_space;
     102             :         }
     103             : 
     104        7060 :         if (smb1cli_conn_encryption_on(cli->conn)) {
     105        4837 :                 return min_space;
     106             :         }
     107             : 
     108        2223 :         if (strequal(cli->dev, "LPT1:")) {
     109           0 :                 return min_space;
     110             :         }
     111             : 
     112        2223 :         return useable_space;
     113             : }
     114             : 
     115             : struct cli_read_andx_state {
     116             :         size_t size;
     117             :         uint16_t vwv[12];
     118             :         NTSTATUS status;
     119             :         size_t received;
     120             :         uint8_t *buf;
     121             : };
     122             : 
     123             : static void cli_read_andx_done(struct tevent_req *subreq);
     124             : 
     125        7297 : struct tevent_req *cli_read_andx_create(TALLOC_CTX *mem_ctx,
     126             :                                         struct tevent_context *ev,
     127             :                                         struct cli_state *cli, uint16_t fnum,
     128             :                                         off_t offset, size_t size,
     129             :                                         struct tevent_req **psmbreq)
     130             : {
     131             :         struct tevent_req *req, *subreq;
     132             :         struct cli_read_andx_state *state;
     133        7297 :         uint8_t wct = 10;
     134             : 
     135        7297 :         req = tevent_req_create(mem_ctx, &state, struct cli_read_andx_state);
     136        7297 :         if (req == NULL) {
     137           0 :                 return NULL;
     138             :         }
     139        7297 :         state->size = size;
     140             : 
     141        7297 :         SCVAL(state->vwv + 0, 0, 0xFF);
     142        7297 :         SCVAL(state->vwv + 0, 1, 0);
     143        7297 :         SSVAL(state->vwv + 1, 0, 0);
     144        7297 :         SSVAL(state->vwv + 2, 0, fnum);
     145        7297 :         SIVAL(state->vwv + 3, 0, offset);
     146        7297 :         SSVAL(state->vwv + 5, 0, size);
     147        7297 :         SSVAL(state->vwv + 6, 0, size);
     148        7297 :         SSVAL(state->vwv + 7, 0, (size >> 16));
     149        7297 :         SSVAL(state->vwv + 8, 0, 0);
     150        7297 :         SSVAL(state->vwv + 9, 0, 0);
     151             : 
     152        7297 :         if (smb1cli_conn_capabilities(cli->conn) & CAP_LARGE_FILES) {
     153        7297 :                 SIVAL(state->vwv + 10, 0,
     154             :                       (((uint64_t)offset)>>32) & 0xffffffff);
     155        7297 :                 wct = 12;
     156             :         } else {
     157           0 :                 if ((((uint64_t)offset) & 0xffffffff00000000LL) != 0) {
     158           0 :                         DEBUG(10, ("cli_read_andx_send got large offset where "
     159             :                                    "the server does not support it\n"));
     160           0 :                         tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
     161           0 :                         return tevent_req_post(req, ev);
     162             :                 }
     163             :         }
     164             : 
     165        7297 :         subreq = cli_smb_req_create(state, ev, cli, SMBreadX, 0, 0, wct,
     166        7297 :                                     state->vwv, 0, NULL);
     167        7297 :         if (subreq == NULL) {
     168           0 :                 TALLOC_FREE(req);
     169           0 :                 return NULL;
     170             :         }
     171        7297 :         tevent_req_set_callback(subreq, cli_read_andx_done, req);
     172        7297 :         *psmbreq = subreq;
     173        7297 :         return req;
     174             : }
     175             : 
     176        7287 : struct tevent_req *cli_read_andx_send(TALLOC_CTX *mem_ctx,
     177             :                                       struct tevent_context *ev,
     178             :                                       struct cli_state *cli, uint16_t fnum,
     179             :                                       off_t offset, size_t size)
     180             : {
     181             :         struct tevent_req *req, *subreq;
     182             :         NTSTATUS status;
     183             : 
     184        7287 :         req = cli_read_andx_create(mem_ctx, ev, cli, fnum, offset, size,
     185             :                                    &subreq);
     186        7287 :         if (req == NULL) {
     187           0 :                 return NULL;
     188             :         }
     189             : 
     190        7287 :         status = smb1cli_req_chain_submit(&subreq, 1);
     191        7287 :         if (tevent_req_nterror(req, status)) {
     192           0 :                 return tevent_req_post(req, ev);
     193             :         }
     194        7287 :         return req;
     195             : }
     196             : 
     197        7297 : static void cli_read_andx_done(struct tevent_req *subreq)
     198             : {
     199        7297 :         struct tevent_req *req = tevent_req_callback_data(
     200             :                 subreq, struct tevent_req);
     201        7297 :         struct cli_read_andx_state *state = tevent_req_data(
     202             :                 req, struct cli_read_andx_state);
     203             :         uint8_t *inbuf;
     204             :         uint8_t wct;
     205             :         uint16_t *vwv;
     206             :         uint32_t num_bytes;
     207             :         uint8_t *bytes;
     208             : 
     209        7297 :         state->status = cli_smb_recv(subreq, state, &inbuf, 12, &wct, &vwv,
     210             :                                      &num_bytes, &bytes);
     211        7297 :         TALLOC_FREE(subreq);
     212        7297 :         if (NT_STATUS_IS_ERR(state->status)) {
     213          34 :                 tevent_req_nterror(req, state->status);
     214          34 :                 return;
     215             :         }
     216             : 
     217             :         /* size is the number of bytes the server returned.
     218             :          * Might be zero. */
     219        7263 :         state->received = SVAL(vwv + 5, 0);
     220        7263 :         state->received |= (((unsigned int)SVAL(vwv + 7, 0)) << 16);
     221             : 
     222        7263 :         if (state->received > state->size) {
     223           0 :                 DEBUG(5,("server returned more than we wanted!\n"));
     224           0 :                 tevent_req_nterror(req, NT_STATUS_UNEXPECTED_IO_ERROR);
     225           0 :                 return;
     226             :         }
     227             : 
     228             :         /*
     229             :          * bcc field must be valid for small reads, for large reads the 16-bit
     230             :          * bcc field can't be correct.
     231             :          */
     232             : 
     233        7263 :         if ((state->received < 0xffff) && (state->received > num_bytes)) {
     234           0 :                 DEBUG(5, ("server announced more bytes than sent\n"));
     235           0 :                 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
     236           0 :                 return;
     237             :         }
     238             : 
     239        7263 :         state->buf = discard_const_p(uint8_t, smb_base(inbuf)) + SVAL(vwv+6, 0);
     240             : 
     241        7263 :         if (trans_oob(smb_len_tcp(inbuf), SVAL(vwv+6, 0), state->received)
     242        7263 :             || ((state->received != 0) && (state->buf < bytes))) {
     243           0 :                 DEBUG(5, ("server returned invalid read&x data offset\n"));
     244           0 :                 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
     245           0 :                 return;
     246             :         }
     247        7263 :         tevent_req_done(req);
     248             : }
     249             : 
     250             : /*
     251             :  * Pull the data out of a finished async read_and_x request. rcvbuf is
     252             :  * talloced from the request, so better make sure that you copy it away before
     253             :  * you talloc_free(req). "rcvbuf" is NOT a talloc_ctx of its own, so do not
     254             :  * talloc_move it!
     255             :  */
     256             : 
     257        7297 : NTSTATUS cli_read_andx_recv(struct tevent_req *req, ssize_t *received,
     258             :                             uint8_t **rcvbuf)
     259             : {
     260        7297 :         struct cli_read_andx_state *state = tevent_req_data(
     261             :                 req, struct cli_read_andx_state);
     262             :         NTSTATUS status;
     263             : 
     264        7297 :         if (tevent_req_is_nterror(req, &status)) {
     265          34 :                 return status;
     266             :         }
     267        7263 :         *received = state->received;
     268        7263 :         *rcvbuf = state->buf;
     269        7263 :         return NT_STATUS_OK;
     270             : }
     271             : 
     272             : struct cli_pull_chunk;
     273             : 
     274             : struct cli_pull_state {
     275             :         struct tevent_context *ev;
     276             :         struct cli_state *cli;
     277             :         uint16_t fnum;
     278             :         off_t start_offset;
     279             :         off_t size;
     280             : 
     281             :         NTSTATUS (*sink)(char *buf, size_t n, void *priv);
     282             :         void *priv;
     283             : 
     284             :         size_t chunk_size;
     285             :         off_t next_offset;
     286             :         off_t remaining;
     287             : 
     288             :         /*
     289             :          * How many bytes did we push into "sink"?
     290             :          */
     291             :         off_t pushed;
     292             : 
     293             :         /*
     294             :          * Outstanding requests
     295             :          *
     296             :          * The maximum is 256:
     297             :          * - which would be a window of 256 MByte
     298             :          *   for SMB2 with multi-credit
     299             :          *   or smb1 unix extensions.
     300             :          */
     301             :         uint16_t max_chunks;
     302             :         uint16_t num_chunks;
     303             :         uint16_t num_waiting;
     304             :         struct cli_pull_chunk *chunks;
     305             : };
     306             : 
     307             : struct cli_pull_chunk {
     308             :         struct cli_pull_chunk *prev, *next;
     309             :         struct tevent_req *req;/* This is the main request! Not the subreq */
     310             :         struct tevent_req *subreq;
     311             :         off_t ofs;
     312             :         uint8_t *buf;
     313             :         size_t total_size;
     314             :         size_t tmp_size;
     315             :         bool done;
     316             : };
     317             : 
     318             : static void cli_pull_setup_chunks(struct tevent_req *req);
     319             : static void cli_pull_chunk_ship(struct cli_pull_chunk *chunk);
     320             : static void cli_pull_chunk_done(struct tevent_req *subreq);
     321             : 
     322             : /*
     323             :  * Parallel read support.
     324             :  *
     325             :  * cli_pull sends as many read&x requests as the server would allow via
     326             :  * max_mux at a time. When replies flow back in, the data is written into
     327             :  * the callback function "sink" in the right order.
     328             :  */
     329             : 
     330        4927 : struct tevent_req *cli_pull_send(TALLOC_CTX *mem_ctx,
     331             :                                  struct tevent_context *ev,
     332             :                                  struct cli_state *cli,
     333             :                                  uint16_t fnum, off_t start_offset,
     334             :                                  off_t size, size_t window_size,
     335             :                                  NTSTATUS (*sink)(char *buf, size_t n,
     336             :                                                   void *priv),
     337             :                                  void *priv)
     338             : {
     339             :         struct tevent_req *req;
     340             :         struct cli_pull_state *state;
     341        4927 :         size_t page_size = 1024;
     342             :         uint64_t tmp64;
     343             : 
     344        4927 :         req = tevent_req_create(mem_ctx, &state, struct cli_pull_state);
     345        4927 :         if (req == NULL) {
     346           0 :                 return NULL;
     347             :         }
     348        4927 :         state->cli = cli;
     349        4927 :         state->ev = ev;
     350        4927 :         state->fnum = fnum;
     351        4927 :         state->start_offset = start_offset;
     352        4927 :         state->size = size;
     353        4927 :         state->sink = sink;
     354        4927 :         state->priv = priv;
     355        4927 :         state->next_offset = start_offset;
     356        4927 :         state->remaining = size;
     357             : 
     358        4927 :         if (size == 0) {
     359          74 :                 tevent_req_done(req);
     360          74 :                 return tevent_req_post(req, ev);
     361             :         }
     362             : 
     363        4853 :         if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
     364        1907 :                 state->chunk_size = smb2cli_conn_max_read_size(cli->conn);
     365             :         } else {
     366        2946 :                 state->chunk_size = cli_read_max_bufsize(cli);
     367             :         }
     368        4853 :         if (state->chunk_size > page_size) {
     369        4853 :                 state->chunk_size &= ~(page_size - 1);
     370             :         }
     371             : 
     372        4853 :         if (window_size == 0) {
     373             :                 /*
     374             :                  * We use 16 MByte as default window size.
     375             :                  */
     376         615 :                 window_size = 16 * 1024 * 1024;
     377             :         }
     378             : 
     379        4853 :         tmp64 = window_size/state->chunk_size;
     380        4853 :         if ((window_size % state->chunk_size) > 0) {
     381        4526 :                 tmp64 += 1;
     382             :         }
     383        4853 :         tmp64 = MAX(tmp64, 1);
     384        4853 :         tmp64 = MIN(tmp64, 256);
     385        4853 :         state->max_chunks = tmp64;
     386             : 
     387             :         /*
     388             :          * We defer the callback because of the complex
     389             :          * substate/subfunction logic
     390             :          */
     391        4853 :         tevent_req_defer_callback(req, ev);
     392             : 
     393        4853 :         cli_pull_setup_chunks(req);
     394        4853 :         if (!tevent_req_is_in_progress(req)) {
     395           0 :                 return tevent_req_post(req, ev);
     396             :         }
     397             : 
     398        4853 :         return req;
     399             : }
     400             : 
     401       14290 : static void cli_pull_setup_chunks(struct tevent_req *req)
     402             : {
     403       13982 :         struct cli_pull_state *state =
     404       14290 :                 tevent_req_data(req,
     405             :                 struct cli_pull_state);
     406       14290 :         struct cli_pull_chunk *chunk, *next = NULL;
     407             :         size_t i;
     408             : 
     409       68750 :         for (chunk = state->chunks; chunk; chunk = next) {
     410             :                 /*
     411             :                  * Note that chunk might be removed from this call.
     412             :                  */
     413       55056 :                 next = chunk->next;
     414       55056 :                 cli_pull_chunk_ship(chunk);
     415       55056 :                 if (!tevent_req_is_in_progress(req)) {
     416         596 :                         return;
     417             :                 }
     418             :         }
     419             : 
     420       22568 :         for (i = state->num_chunks; i < state->max_chunks; i++) {
     421             : 
     422       17313 :                 if (state->num_waiting > 0) {
     423         610 :                         return;
     424             :                 }
     425             : 
     426       16703 :                 if (state->remaining == 0) {
     427        7829 :                         break;
     428             :                 }
     429             : 
     430        8874 :                 chunk = talloc_zero(state, struct cli_pull_chunk);
     431        8874 :                 if (tevent_req_nomem(chunk, req)) {
     432           0 :                         return;
     433             :                 }
     434        8874 :                 chunk->req = req;
     435        8874 :                 chunk->ofs = state->next_offset;
     436        8874 :                 chunk->total_size = MIN(state->remaining, state->chunk_size);
     437        8874 :                 state->next_offset += chunk->total_size;
     438        8874 :                 state->remaining -= chunk->total_size;
     439             : 
     440        8874 :                 DLIST_ADD_END(state->chunks, chunk);
     441        8874 :                 state->num_chunks++;
     442        8874 :                 state->num_waiting++;
     443             : 
     444        8874 :                 cli_pull_chunk_ship(chunk);
     445        8874 :                 if (!tevent_req_is_in_progress(req)) {
     446           0 :                         return;
     447             :                 }
     448             :         }
     449             : 
     450       13084 :         if (state->remaining > 0) {
     451          14 :                 return;
     452             :         }
     453             : 
     454       13070 :         if (state->num_chunks > 0) {
     455        8838 :                 return;
     456             :         }
     457             : 
     458        4232 :         tevent_req_done(req);
     459             : }
     460             : 
     461       63930 : static void cli_pull_chunk_ship(struct cli_pull_chunk *chunk)
     462             : {
     463       63930 :         struct tevent_req *req = chunk->req;
     464       63614 :         struct cli_pull_state *state =
     465       63930 :                 tevent_req_data(req,
     466             :                 struct cli_pull_state);
     467             :         bool ok;
     468             :         off_t ofs;
     469             :         size_t size;
     470             : 
     471       63930 :         if (chunk->done) {
     472             :                 NTSTATUS status;
     473             : 
     474        9001 :                 if (chunk != state->chunks) {
     475             :                         /*
     476             :                          * this chunk is not the
     477             :                          * first one in the list.
     478             :                          *
     479             :                          * which means we should not
     480             :                          * push it into the sink yet.
     481             :                          */
     482         429 :                         return;
     483             :                 }
     484             : 
     485        8572 :                 if (chunk->tmp_size == 0) {
     486             :                         /*
     487             :                          * we got a short read, we're done
     488             :                          */
     489           8 :                         tevent_req_done(req);
     490           8 :                         return;
     491             :                 }
     492             : 
     493        8564 :                 status = state->sink((char *)chunk->buf,
     494             :                                      chunk->tmp_size,
     495             :                                      state->priv);
     496        8564 :                 if (tevent_req_nterror(req, status)) {
     497           0 :                         return;
     498             :                 }
     499        8564 :                 state->pushed += chunk->tmp_size;
     500             : 
     501        8564 :                 if (chunk->tmp_size < chunk->total_size) {
     502             :                         /*
     503             :                          * we got a short read, we're done
     504             :                          */
     505         588 :                         tevent_req_done(req);
     506         588 :                         return;
     507             :                 }
     508             : 
     509        7976 :                 DLIST_REMOVE(state->chunks, chunk);
     510        7976 :                 SMB_ASSERT(state->num_chunks > 0);
     511        7976 :                 state->num_chunks--;
     512        7976 :                 TALLOC_FREE(chunk);
     513             : 
     514        7976 :                 return;
     515             :         }
     516             : 
     517       54929 :         if (chunk->subreq != NULL) {
     518       44857 :                 return;
     519             :         }
     520             : 
     521       10072 :         SMB_ASSERT(state->num_waiting > 0);
     522             : 
     523       10072 :         ofs = chunk->ofs + chunk->tmp_size;
     524       10072 :         size = chunk->total_size - chunk->tmp_size;
     525             : 
     526       10072 :         if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
     527             :                 uint32_t max_size;
     528             : 
     529        2239 :                 ok = smb2cli_conn_req_possible(state->cli->conn, &max_size);
     530        2239 :                 if (!ok) {
     531           0 :                         return;
     532             :                 }
     533             : 
     534             :                 /*
     535             :                  * downgrade depending on the available credits
     536             :                  */
     537        2239 :                 size = MIN(max_size, size);
     538             : 
     539        2395 :                 chunk->subreq = cli_smb2_read_send(chunk,
     540             :                                                    state->ev,
     541             :                                                    state->cli,
     542        2239 :                                                    state->fnum,
     543             :                                                    ofs,
     544             :                                                    size);
     545        2239 :                 if (tevent_req_nomem(chunk->subreq, req)) {
     546           0 :                         return;
     547             :                 }
     548             :         } else {
     549        7833 :                 ok = smb1cli_conn_req_possible(state->cli->conn);
     550        7833 :                 if (!ok) {
     551         610 :                         return;
     552             :                 }
     553             : 
     554        7223 :                 chunk->subreq = cli_read_andx_send(chunk,
     555             :                                                    state->ev,
     556             :                                                    state->cli,
     557        7223 :                                                    state->fnum,
     558             :                                                    ofs,
     559             :                                                    size);
     560        7223 :                 if (tevent_req_nomem(chunk->subreq, req)) {
     561           0 :                         return;
     562             :                 }
     563             :         }
     564        9462 :         tevent_req_set_callback(chunk->subreq,
     565             :                                 cli_pull_chunk_done,
     566             :                                 chunk);
     567             : 
     568        9462 :         state->num_waiting--;
     569        9462 :         return;
     570             : }
     571             : 
     572        9462 : static void cli_pull_chunk_done(struct tevent_req *subreq)
     573             : {
     574        9306 :         struct cli_pull_chunk *chunk =
     575        9462 :                 tevent_req_callback_data(subreq,
     576             :                 struct cli_pull_chunk);
     577        9462 :         struct tevent_req *req = chunk->req;
     578        9306 :         struct cli_pull_state *state =
     579        9462 :                 tevent_req_data(req,
     580             :                 struct cli_pull_state);
     581             :         NTSTATUS status;
     582        9462 :         size_t expected = chunk->total_size - chunk->tmp_size;
     583        9462 :         ssize_t received = 0;
     584        9462 :         uint8_t *buf = NULL;
     585             : 
     586        9462 :         chunk->subreq = NULL;
     587             : 
     588        9462 :         if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
     589        2239 :                 status = cli_smb2_read_recv(subreq, &received, &buf);
     590             :         } else {
     591        7223 :                 status = cli_read_andx_recv(subreq, &received, &buf);
     592             :         }
     593        9462 :         if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE)) {
     594         310 :                 received = 0;
     595         310 :                 status = NT_STATUS_OK;
     596             :         }
     597        9462 :         if (tevent_req_nterror(req, status)) {
     598         923 :                 return;
     599             :         }
     600             : 
     601        9437 :         if (received > expected) {
     602           0 :                 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
     603           0 :                 return;
     604             :         }
     605             : 
     606        9437 :         if (received == 0) {
     607             :                 /*
     608             :                  * We got EOF we're done
     609             :                  */
     610         873 :                 chunk->done = true;
     611         873 :                 cli_pull_setup_chunks(req);
     612         873 :                 return;
     613             :         }
     614             : 
     615        8564 :         if (received == chunk->total_size) {
     616             :                 /*
     617             :                  * We got it in the first run.
     618             :                  *
     619             :                  * We don't call TALLOC_FREE(subreq)
     620             :                  * here and keep the returned buffer.
     621             :                  */
     622        7976 :                 chunk->buf = buf;
     623         588 :         } else if (chunk->buf == NULL) {
     624         588 :                 chunk->buf = talloc_array(chunk, uint8_t, chunk->total_size);
     625         588 :                 if (tevent_req_nomem(chunk->buf, req)) {
     626           0 :                         return;
     627             :                 }
     628             :         }
     629             : 
     630        8564 :         if (received != chunk->total_size) {
     631         588 :                 uint8_t *p = chunk->buf + chunk->tmp_size;
     632         588 :                 memcpy(p, buf, received);
     633         588 :                 TALLOC_FREE(subreq);
     634             :         }
     635             : 
     636        8564 :         chunk->tmp_size += received;
     637             : 
     638        8564 :         if (chunk->tmp_size == chunk->total_size) {
     639        7976 :                 chunk->done = true;
     640             :         } else {
     641         588 :                 state->num_waiting++;
     642             :         }
     643             : 
     644        8564 :         cli_pull_setup_chunks(req);
     645             : }
     646             : 
     647        4927 : NTSTATUS cli_pull_recv(struct tevent_req *req, off_t *received)
     648             : {
     649        4927 :         struct cli_pull_state *state = tevent_req_data(
     650             :                 req, struct cli_pull_state);
     651             :         NTSTATUS status;
     652             : 
     653        4927 :         if (tevent_req_is_nterror(req, &status)) {
     654          25 :                 tevent_req_received(req);
     655          25 :                 return status;
     656             :         }
     657        4902 :         *received = state->pushed;
     658        4902 :         tevent_req_received(req);
     659        4902 :         return NT_STATUS_OK;
     660             : }
     661             : 
     662        4143 : NTSTATUS cli_pull(struct cli_state *cli, uint16_t fnum,
     663             :                   off_t start_offset, off_t size, size_t window_size,
     664             :                   NTSTATUS (*sink)(char *buf, size_t n, void *priv),
     665             :                   void *priv, off_t *received)
     666             : {
     667        4143 :         TALLOC_CTX *frame = talloc_stackframe();
     668             :         struct tevent_context *ev;
     669             :         struct tevent_req *req;
     670        4143 :         NTSTATUS status = NT_STATUS_OK;
     671             : 
     672        4143 :         if (smbXcli_conn_has_async_calls(cli->conn)) {
     673             :                 /*
     674             :                  * Can't use sync call while an async call is in flight
     675             :                  */
     676           0 :                 status = NT_STATUS_INVALID_PARAMETER;
     677           0 :                 goto fail;
     678             :         }
     679             : 
     680        4143 :         ev = samba_tevent_context_init(frame);
     681        4143 :         if (ev == NULL) {
     682           0 :                 status = NT_STATUS_NO_MEMORY;
     683           0 :                 goto fail;
     684             :         }
     685             : 
     686        4143 :         req = cli_pull_send(frame, ev, cli, fnum, start_offset, size,
     687             :                             window_size, sink, priv);
     688        4143 :         if (req == NULL) {
     689           0 :                 status = NT_STATUS_NO_MEMORY;
     690           0 :                 goto fail;
     691             :         }
     692             : 
     693        4143 :         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
     694           0 :                 goto fail;
     695             :         }
     696             : 
     697        4143 :         status = cli_pull_recv(req, received);
     698        4143 :  fail:
     699        4143 :         TALLOC_FREE(frame);
     700        4143 :         return status;
     701             : }
     702             : 
     703             : struct cli_read_state {
     704             :         struct cli_state *cli;
     705             :         char *buf;
     706             :         size_t buflen;
     707             :         size_t received;
     708             : };
     709             : 
     710             : static void cli_read_done(struct tevent_req *subreq);
     711             : 
     712         483 : struct tevent_req *cli_read_send(
     713             :         TALLOC_CTX *mem_ctx,
     714             :         struct tevent_context *ev,
     715             :         struct cli_state *cli,
     716             :         uint16_t fnum,
     717             :         char *buf,
     718             :         off_t offset,
     719             :         size_t size)
     720             : {
     721             :         struct tevent_req *req, *subreq;
     722             :         struct cli_read_state *state;
     723             : 
     724         483 :         req = tevent_req_create(mem_ctx, &state, struct cli_read_state);
     725         483 :         if (req == NULL) {
     726           0 :                 return NULL;
     727             :         }
     728         483 :         state->cli = cli;
     729         483 :         state->buf = buf;
     730         483 :         state->buflen = size;
     731             : 
     732         483 :         if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
     733             :                 uint32_t max_size;
     734             :                 bool ok;
     735             : 
     736         483 :                 ok = smb2cli_conn_req_possible(state->cli->conn, &max_size);
     737         483 :                 if (!ok) {
     738           0 :                         tevent_req_nterror(
     739             :                                 req,
     740             :                                 NT_STATUS_INSUFFICIENT_RESOURCES);
     741           0 :                         return tevent_req_post(req, ev);
     742             :                 }
     743             : 
     744             :                 /*
     745             :                  * downgrade depending on the available credits
     746             :                  */
     747         483 :                 size = MIN(max_size, size);
     748             : 
     749         483 :                 subreq = cli_smb2_read_send(
     750             :                         state, ev, cli, fnum, offset, size);
     751         483 :                 if (tevent_req_nomem(subreq, req)) {
     752           0 :                         return tevent_req_post(req, ev);
     753             :                 }
     754             :         } else {
     755             :                 bool ok;
     756           0 :                 ok = smb1cli_conn_req_possible(state->cli->conn);
     757           0 :                 if (!ok) {
     758           0 :                         tevent_req_nterror(
     759             :                                 req,
     760             :                                 NT_STATUS_INSUFFICIENT_RESOURCES);
     761           0 :                         return tevent_req_post(req, ev);
     762             :                 }
     763             : 
     764           0 :                 subreq = cli_read_andx_send(
     765             :                         state, ev, cli, fnum, offset, size);
     766           0 :                 if (tevent_req_nomem(subreq, req)) {
     767           0 :                         return tevent_req_post(req, ev);
     768             :                 }
     769             :         }
     770             : 
     771         483 :         tevent_req_set_callback(subreq, cli_read_done, req);
     772             : 
     773         483 :         return req;
     774             : }
     775             : 
     776         483 : static void cli_read_done(struct tevent_req *subreq)
     777             : {
     778         483 :         struct tevent_req *req = tevent_req_callback_data(
     779             :                 subreq, struct tevent_req);
     780         483 :         struct cli_read_state *state = tevent_req_data(
     781             :                 req, struct cli_read_state);
     782             :         NTSTATUS status;
     783             :         ssize_t received;
     784         483 :         uint8_t *buf = NULL;
     785             : 
     786         483 :         if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
     787         483 :                 status = cli_smb2_read_recv(subreq, &received, &buf);
     788             :         } else {
     789           0 :                 status = cli_read_andx_recv(subreq, &received, &buf);
     790             :         }
     791             : 
     792         483 :         if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE)) {
     793           0 :                 received = 0;
     794           0 :                 status = NT_STATUS_OK;
     795             :         }
     796         483 :         if (tevent_req_nterror(req, status)) {
     797           2 :                 return;
     798             :         }
     799         482 :         if ((buf == NULL) || (received < 0) || (received > state->buflen)) {
     800           0 :                 state->received = 0;
     801           0 :                 tevent_req_nterror(req, NT_STATUS_UNEXPECTED_IO_ERROR);
     802           0 :                 return;
     803             :         }
     804             : 
     805         482 :         memcpy(state->buf, buf, received);
     806         482 :         state->received = received;
     807         482 :         tevent_req_done(req);
     808             : }
     809             : 
     810         483 : NTSTATUS cli_read_recv(struct tevent_req *req, size_t *received)
     811             : {
     812         483 :         struct cli_read_state *state = tevent_req_data(
     813             :                 req, struct cli_read_state);
     814             :         NTSTATUS status;
     815             : 
     816         483 :         if (tevent_req_is_nterror(req, &status)) {
     817           1 :                 return status;
     818             :         }
     819         482 :         if (received != NULL) {
     820         482 :                 *received = state->received;
     821             :         }
     822         482 :         return NT_STATUS_OK;
     823             : }
     824             : 
     825             : /*
     826             :  * Helper function for cli_pull(). This takes a chunk of data (buf) read from
     827             :  * a remote file and copies it into the return buffer (priv).
     828             :  */
     829        7275 : NTSTATUS cli_read_sink(char *buf, size_t n, void *priv)
     830             : {
     831        7275 :         char **pbuf = (char **)priv;
     832        7275 :         memcpy(*pbuf, buf, n);
     833        7275 :         *pbuf += n;
     834        7275 :         return NT_STATUS_OK;
     835             : }
     836             : 
     837        3526 : NTSTATUS cli_read(struct cli_state *cli, uint16_t fnum,
     838             :                  char *buf, off_t offset, size_t size,
     839             :                  size_t *nread)
     840             : {
     841             :         NTSTATUS status;
     842        3526 :         off_t ret = 0;
     843             : 
     844        3526 :         status = cli_pull(cli, fnum, offset, size, size,
     845             :                           cli_read_sink, &buf, &ret);
     846        3526 :         if (!NT_STATUS_IS_OK(status)) {
     847          25 :                 return status;
     848             :         }
     849             : 
     850        3501 :         if (nread) {
     851        3497 :                 *nread = ret;
     852             :         }
     853             : 
     854        3501 :         return NT_STATUS_OK;
     855             : }
     856             : 
     857             : /****************************************************************************
     858             :   write to a file using a SMBwrite and not bypassing 0 byte writes
     859             : ****************************************************************************/
     860             : 
     861          24 : NTSTATUS cli_smbwrite(struct cli_state *cli, uint16_t fnum, char *buf,
     862             :                       off_t offset, size_t size1, size_t *ptotal)
     863             : {
     864             :         uint8_t *bytes;
     865          24 :         ssize_t total = 0;
     866             : 
     867             :         /*
     868             :          * 3 bytes prefix
     869             :          */
     870             : 
     871          24 :         bytes = talloc_array(talloc_tos(), uint8_t, 3);
     872          24 :         if (bytes == NULL) {
     873           0 :                 return NT_STATUS_NO_MEMORY;
     874             :         }
     875          24 :         bytes[0] = 1;
     876             : 
     877             :         do {
     878         114 :                 uint32_t usable_space = cli_state_available_size(cli, 48);
     879         114 :                 size_t size = MIN(size1, usable_space);
     880             :                 struct tevent_req *req;
     881             :                 uint16_t vwv[5];
     882             :                 uint16_t *ret_vwv;
     883             :                 NTSTATUS status;
     884             : 
     885         114 :                 SSVAL(vwv+0, 0, fnum);
     886         114 :                 SSVAL(vwv+1, 0, size);
     887         114 :                 SIVAL(vwv+2, 0, offset);
     888         114 :                 SSVAL(vwv+4, 0, 0);
     889             : 
     890         114 :                 bytes = talloc_realloc(talloc_tos(), bytes, uint8_t,
     891             :                                              size+3);
     892         114 :                 if (bytes == NULL) {
     893           5 :                         return NT_STATUS_NO_MEMORY;
     894             :                 }
     895         114 :                 SSVAL(bytes, 1, size);
     896         114 :                 memcpy(bytes + 3, buf + total, size);
     897             : 
     898         114 :                 status = cli_smb(talloc_tos(), cli, SMBwrite, 0, 5, vwv,
     899             :                                  size+3, bytes, &req, 1, NULL, &ret_vwv,
     900             :                                  NULL, NULL);
     901         114 :                 if (!NT_STATUS_IS_OK(status)) {
     902           5 :                         TALLOC_FREE(bytes);
     903           5 :                         return status;
     904             :                 }
     905             : 
     906         109 :                 size = SVAL(ret_vwv+0, 0);
     907         109 :                 TALLOC_FREE(req);
     908         109 :                 if (size == 0) {
     909           0 :                         break;
     910             :                 }
     911         109 :                 size1 -= size;
     912         109 :                 total += size;
     913         109 :                 offset += size;
     914             : 
     915         109 :         } while (size1);
     916             : 
     917          19 :         TALLOC_FREE(bytes);
     918             : 
     919          19 :         if (ptotal != NULL) {
     920           0 :                 *ptotal = total;
     921             :         }
     922          19 :         return NT_STATUS_OK;
     923             : }
     924             : 
     925             : /*
     926             :  * Send a write&x request
     927             :  */
     928             : 
     929             : struct cli_write_andx_state {
     930             :         size_t size;
     931             :         uint16_t vwv[14];
     932             :         size_t written;
     933             :         uint8_t pad;
     934             :         struct iovec iov[2];
     935             : };
     936             : 
     937             : static void cli_write_andx_done(struct tevent_req *subreq);
     938             : 
     939       11337 : struct tevent_req *cli_write_andx_create(TALLOC_CTX *mem_ctx,
     940             :                                          struct tevent_context *ev,
     941             :                                          struct cli_state *cli, uint16_t fnum,
     942             :                                          uint16_t mode, const uint8_t *buf,
     943             :                                          off_t offset, size_t size,
     944             :                                          struct tevent_req **reqs_before,
     945             :                                          int num_reqs_before,
     946             :                                          struct tevent_req **psmbreq)
     947             : {
     948             :         struct tevent_req *req, *subreq;
     949             :         struct cli_write_andx_state *state;
     950       11337 :         bool bigoffset = ((smb1cli_conn_capabilities(cli->conn) & CAP_LARGE_FILES) != 0);
     951       11337 :         uint8_t wct = bigoffset ? 14 : 12;
     952       11337 :         size_t max_write = cli_write_max_bufsize(cli, mode, wct);
     953             :         uint16_t *vwv;
     954             : 
     955       11337 :         req = tevent_req_create(mem_ctx, &state, struct cli_write_andx_state);
     956       11337 :         if (req == NULL) {
     957           0 :                 return NULL;
     958             :         }
     959             : 
     960       11337 :         state->size = MIN(size, max_write);
     961             : 
     962       11337 :         vwv = state->vwv;
     963             : 
     964       11337 :         SCVAL(vwv+0, 0, 0xFF);
     965       11337 :         SCVAL(vwv+0, 1, 0);
     966       11337 :         SSVAL(vwv+1, 0, 0);
     967       11337 :         SSVAL(vwv+2, 0, fnum);
     968       11337 :         SIVAL(vwv+3, 0, offset);
     969       11337 :         SIVAL(vwv+5, 0, 0);
     970       11337 :         SSVAL(vwv+7, 0, mode);
     971       11337 :         SSVAL(vwv+8, 0, 0);
     972       11337 :         SSVAL(vwv+9, 0, (state->size>>16));
     973       11337 :         SSVAL(vwv+10, 0, state->size);
     974             : 
     975       11337 :         SSVAL(vwv+11, 0,
     976             :               smb1cli_req_wct_ofs(reqs_before, num_reqs_before)
     977             :               + 1               /* the wct field */
     978             :               + wct * 2         /* vwv */
     979             :               + 2               /* num_bytes field */
     980             :               + 1               /* pad */);
     981             : 
     982       11337 :         if (bigoffset) {
     983       11337 :                 SIVAL(vwv+12, 0, (((uint64_t)offset)>>32) & 0xffffffff);
     984             :         }
     985             : 
     986       11337 :         state->pad = 0;
     987       11337 :         state->iov[0].iov_base = (void *)&state->pad;
     988       11337 :         state->iov[0].iov_len = 1;
     989       11337 :         state->iov[1].iov_base = discard_const_p(void, buf);
     990       11337 :         state->iov[1].iov_len = state->size;
     991             : 
     992       11337 :         subreq = cli_smb_req_create(state, ev, cli, SMBwriteX, 0, 0, wct, vwv,
     993       11337 :                                     2, state->iov);
     994       11337 :         if (tevent_req_nomem(subreq, req)) {
     995           0 :                 return tevent_req_post(req, ev);
     996             :         }
     997       11337 :         tevent_req_set_callback(subreq, cli_write_andx_done, req);
     998       11337 :         *psmbreq = subreq;
     999       11337 :         return req;
    1000             : }
    1001             : 
    1002       11327 : struct tevent_req *cli_write_andx_send(TALLOC_CTX *mem_ctx,
    1003             :                                        struct tevent_context *ev,
    1004             :                                        struct cli_state *cli, uint16_t fnum,
    1005             :                                        uint16_t mode, const uint8_t *buf,
    1006             :                                        off_t offset, size_t size)
    1007             : {
    1008             :         struct tevent_req *req, *subreq;
    1009             :         NTSTATUS status;
    1010             : 
    1011       11327 :         req = cli_write_andx_create(mem_ctx, ev, cli, fnum, mode, buf, offset,
    1012             :                                     size, NULL, 0, &subreq);
    1013       11327 :         if (req == NULL) {
    1014           0 :                 return NULL;
    1015             :         }
    1016             : 
    1017       11327 :         status = smb1cli_req_chain_submit(&subreq, 1);
    1018       11327 :         if (tevent_req_nterror(req, status)) {
    1019           0 :                 return tevent_req_post(req, ev);
    1020             :         }
    1021       11327 :         return req;
    1022             : }
    1023             : 
    1024       11337 : static void cli_write_andx_done(struct tevent_req *subreq)
    1025             : {
    1026       11337 :         struct tevent_req *req = tevent_req_callback_data(
    1027             :                 subreq, struct tevent_req);
    1028       11337 :         struct cli_write_andx_state *state = tevent_req_data(
    1029             :                 req, struct cli_write_andx_state);
    1030             :         uint8_t wct;
    1031             :         uint16_t *vwv;
    1032             :         NTSTATUS status;
    1033             : 
    1034       11337 :         status = cli_smb_recv(subreq, state, NULL, 6, &wct, &vwv,
    1035             :                               NULL, NULL);
    1036       11337 :         TALLOC_FREE(subreq);
    1037       11337 :         if (NT_STATUS_IS_ERR(status)) {
    1038          45 :                 tevent_req_nterror(req, status);
    1039          45 :                 return;
    1040             :         }
    1041       11292 :         state->written = SVAL(vwv+2, 0);
    1042       11292 :         if (state->size > UINT16_MAX) {
    1043             :                 /*
    1044             :                  * It is important that we only set the
    1045             :                  * high bits only if we asked for a large write.
    1046             :                  *
    1047             :                  * OS/2 print shares get this wrong and may send
    1048             :                  * invalid values.
    1049             :                  *
    1050             :                  * See bug #5326.
    1051             :                  */
    1052        1367 :                 state->written |= SVAL(vwv+4, 0)<<16;
    1053             :         }
    1054       11292 :         tevent_req_done(req);
    1055             : }
    1056             : 
    1057       11337 : NTSTATUS cli_write_andx_recv(struct tevent_req *req, size_t *pwritten)
    1058             : {
    1059       11337 :         struct cli_write_andx_state *state = tevent_req_data(
    1060             :                 req, struct cli_write_andx_state);
    1061             :         NTSTATUS status;
    1062             : 
    1063       11337 :         if (tevent_req_is_nterror(req, &status)) {
    1064          45 :                 return status;
    1065             :         }
    1066       11292 :         if (pwritten != 0) {
    1067       11292 :                 *pwritten = state->written;
    1068             :         }
    1069       11292 :         return NT_STATUS_OK;
    1070             : }
    1071             : 
    1072             : struct cli_write_state {
    1073             :         struct cli_state *cli;
    1074             :         size_t written;
    1075             : };
    1076             : 
    1077             : static void cli_write_done(struct tevent_req *subreq);
    1078             : 
    1079             : /*
    1080             :  * Used to write to a file remotely.
    1081             :  * This is similar in functionality to cli_push_send(), except this is a more
    1082             :  * finer-grain API. For example, if the data we want to write exceeds the max
    1083             :  * write size of the underlying connection, then it's the caller's
    1084             :  * responsibility to handle this.
    1085             :  * For writing a small amount of data to file, this is a simpler API to use.
    1086             :  */
    1087         482 : struct tevent_req *cli_write_send(TALLOC_CTX *mem_ctx,
    1088             :                                   struct tevent_context *ev,
    1089             :                                   struct cli_state *cli, uint16_t fnum,
    1090             :                                   uint16_t mode, const uint8_t *buf,
    1091             :                                   off_t offset, size_t size)
    1092             : {
    1093         482 :         struct tevent_req *req = NULL;
    1094         482 :         struct cli_write_state *state = NULL;
    1095         482 :         struct tevent_req *subreq = NULL;
    1096             : 
    1097         482 :         req = tevent_req_create(mem_ctx, &state, struct cli_write_state);
    1098         482 :         if (req == NULL) {
    1099           0 :                 return NULL;
    1100             :         }
    1101         482 :         state->cli = cli;
    1102             : 
    1103         482 :         if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
    1104             :                 uint32_t max_size;
    1105             :                 bool ok;
    1106             : 
    1107         482 :                 ok = smb2cli_conn_req_possible(state->cli->conn, &max_size);
    1108         482 :                 if (!ok) {
    1109           0 :                         tevent_req_nterror(
    1110             :                                 req,
    1111             :                                 NT_STATUS_INSUFFICIENT_RESOURCES);
    1112           0 :                         return tevent_req_post(req, ev);
    1113             :                 }
    1114             : 
    1115             :                 /*
    1116             :                  * downgrade depending on the available credits
    1117             :                  */
    1118         482 :                 size = MIN(max_size, size);
    1119             : 
    1120         482 :                 subreq = cli_smb2_write_send(state,
    1121             :                                              ev,
    1122             :                                              cli,
    1123             :                                              fnum,
    1124             :                                              mode,
    1125             :                                              buf,
    1126             :                                              offset,
    1127             :                                              size);
    1128             :         } else {
    1129             :                 bool ok;
    1130             : 
    1131           0 :                 ok = smb1cli_conn_req_possible(state->cli->conn);
    1132           0 :                 if (!ok) {
    1133           0 :                         tevent_req_nterror(
    1134             :                                 req,
    1135             :                                 NT_STATUS_INSUFFICIENT_RESOURCES);
    1136           0 :                         return tevent_req_post(req, ev);
    1137             :                 }
    1138             : 
    1139           0 :                 subreq = cli_write_andx_send(state,
    1140             :                                              ev,
    1141             :                                              cli,
    1142             :                                              fnum,
    1143             :                                              mode,
    1144             :                                              buf,
    1145             :                                              offset,
    1146             :                                              size);
    1147             :         }
    1148         482 :         if (tevent_req_nomem(subreq, req)) {
    1149           0 :                 return tevent_req_post(req, ev);
    1150             :         }
    1151         482 :         tevent_req_set_callback(subreq, cli_write_done, req);
    1152             : 
    1153         482 :         return req;
    1154             : }
    1155             : 
    1156         482 : static void cli_write_done(struct tevent_req *subreq)
    1157             : {
    1158         323 :         struct tevent_req *req =
    1159         482 :                 tevent_req_callback_data(subreq,
    1160             :                 struct tevent_req);
    1161         323 :         struct cli_write_state *state =
    1162         482 :                 tevent_req_data(req,
    1163             :                 struct cli_write_state);
    1164             :         NTSTATUS status;
    1165             : 
    1166         482 :         if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
    1167         482 :                 status = cli_smb2_write_recv(subreq, &state->written);
    1168             :         } else {
    1169           0 :                 status = cli_write_andx_recv(subreq, &state->written);
    1170             :         }
    1171         482 :         TALLOC_FREE(subreq);
    1172         482 :         if (tevent_req_nterror(req, status)) {
    1173           0 :                 return;
    1174             :         }
    1175         482 :         tevent_req_done(req);
    1176             : }
    1177             : 
    1178         482 : NTSTATUS cli_write_recv(struct tevent_req *req, size_t *pwritten)
    1179             : {
    1180         323 :         struct cli_write_state *state =
    1181         482 :                 tevent_req_data(req,
    1182             :                 struct cli_write_state);
    1183             :         NTSTATUS status;
    1184             : 
    1185         482 :         if (tevent_req_is_nterror(req, &status)) {
    1186           0 :                 tevent_req_received(req);
    1187           0 :                 return status;
    1188             :         }
    1189         482 :         if (pwritten != NULL) {
    1190         482 :                 *pwritten = state->written;
    1191             :         }
    1192         482 :         tevent_req_received(req);
    1193         482 :         return NT_STATUS_OK;
    1194             : }
    1195             : 
    1196             : struct cli_smb1_writeall_state {
    1197             :         struct tevent_context *ev;
    1198             :         struct cli_state *cli;
    1199             :         uint16_t fnum;
    1200             :         uint16_t mode;
    1201             :         const uint8_t *buf;
    1202             :         off_t offset;
    1203             :         size_t size;
    1204             :         size_t written;
    1205             : };
    1206             : 
    1207             : static void cli_smb1_writeall_written(struct tevent_req *req);
    1208             : 
    1209        2101 : static struct tevent_req *cli_smb1_writeall_send(TALLOC_CTX *mem_ctx,
    1210             :                                                  struct tevent_context *ev,
    1211             :                                                  struct cli_state *cli,
    1212             :                                                  uint16_t fnum,
    1213             :                                                  uint16_t mode,
    1214             :                                                  const uint8_t *buf,
    1215             :                                                  off_t offset, size_t size)
    1216             : {
    1217             :         struct tevent_req *req, *subreq;
    1218             :         struct cli_smb1_writeall_state *state;
    1219             : 
    1220        2101 :         req = tevent_req_create(mem_ctx, &state,
    1221             :                                 struct cli_smb1_writeall_state);
    1222        2101 :         if (req == NULL) {
    1223           0 :                 return NULL;
    1224             :         }
    1225        2101 :         state->ev = ev;
    1226        2101 :         state->cli = cli;
    1227        2101 :         state->fnum = fnum;
    1228        2101 :         state->mode = mode;
    1229        2101 :         state->buf = buf;
    1230        2101 :         state->offset = offset;
    1231        2101 :         state->size = size;
    1232        2101 :         state->written = 0;
    1233             : 
    1234        8401 :         subreq = cli_write_andx_send(state, state->ev, state->cli, state->fnum,
    1235        6301 :                                      state->mode, state->buf, state->offset,
    1236        2101 :                                      state->size);
    1237        2101 :         if (tevent_req_nomem(subreq, req)) {
    1238           0 :                 return tevent_req_post(req, ev);
    1239             :         }
    1240        2101 :         tevent_req_set_callback(subreq, cli_smb1_writeall_written, req);
    1241        2101 :         return req;
    1242             : }
    1243             : 
    1244        7965 : static void cli_smb1_writeall_written(struct tevent_req *subreq)
    1245             : {
    1246        7965 :         struct tevent_req *req = tevent_req_callback_data(
    1247             :                 subreq, struct tevent_req);
    1248        7965 :         struct cli_smb1_writeall_state *state = tevent_req_data(
    1249             :                 req, struct cli_smb1_writeall_state);
    1250             :         NTSTATUS status;
    1251        7965 :         size_t written = 0, to_write;
    1252             : 
    1253        7965 :         status = cli_write_andx_recv(subreq, &written);
    1254        7965 :         TALLOC_FREE(subreq);
    1255        7965 :         if (tevent_req_nterror(req, status)) {
    1256        2146 :                 return;
    1257             :         }
    1258             : 
    1259        7920 :         state->written += written;
    1260             : 
    1261        7920 :         if (state->written > state->size) {
    1262           0 :                 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
    1263           0 :                 return;
    1264             :         }
    1265             : 
    1266        7920 :         to_write = state->size - state->written;
    1267             : 
    1268        7920 :         if (to_write == 0) {
    1269        2056 :                 tevent_req_done(req);
    1270        2056 :                 return;
    1271             :         }
    1272             : 
    1273       17592 :         subreq = cli_write_andx_send(state, state->ev, state->cli, state->fnum,
    1274        5864 :                                      state->mode,
    1275        5864 :                                      state->buf + state->written,
    1276        5864 :                                      state->offset + state->written, to_write);
    1277        5864 :         if (tevent_req_nomem(subreq, req)) {
    1278           0 :                 return;
    1279             :         }
    1280        5864 :         tevent_req_set_callback(subreq, cli_smb1_writeall_written, req);
    1281             : }
    1282             : 
    1283        2101 : static NTSTATUS cli_smb1_writeall_recv(struct tevent_req *req,
    1284             :                                        size_t *pwritten)
    1285             : {
    1286        2101 :         struct cli_smb1_writeall_state *state = tevent_req_data(
    1287             :                 req, struct cli_smb1_writeall_state);
    1288             :         NTSTATUS status;
    1289             : 
    1290        2101 :         if (tevent_req_is_nterror(req, &status)) {
    1291          45 :                 return status;
    1292             :         }
    1293        2056 :         if (pwritten != NULL) {
    1294        2056 :                 *pwritten = state->written;
    1295             :         }
    1296        2056 :         return NT_STATUS_OK;
    1297             : }
    1298             : 
    1299             : struct cli_writeall_state {
    1300             :         struct cli_state *cli;
    1301             :         size_t written;
    1302             : };
    1303             : 
    1304             : static void cli_writeall_done(struct tevent_req *subreq);
    1305             : 
    1306        2596 : struct tevent_req *cli_writeall_send(
    1307             :         TALLOC_CTX *mem_ctx,
    1308             :         struct tevent_context *ev,
    1309             :         struct cli_state *cli,
    1310             :         uint16_t fnum,
    1311             :         uint16_t mode,
    1312             :         const uint8_t *buf,
    1313             :         off_t offset,
    1314             :         size_t size)
    1315             : {
    1316             :         struct tevent_req *req, *subreq;
    1317             :         struct cli_writeall_state *state;
    1318             : 
    1319        2596 :         req = tevent_req_create(mem_ctx, &state, struct cli_writeall_state);
    1320        2596 :         if (req == NULL) {
    1321           0 :                 return NULL;
    1322             :         }
    1323        2596 :         state->cli = cli;
    1324             : 
    1325        2596 :         if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
    1326         495 :                 subreq = cli_smb2_writeall_send(
    1327             :                         state,
    1328             :                         ev,
    1329             :                         cli,
    1330             :                         fnum,
    1331             :                         mode,
    1332             :                         buf,
    1333             :                         offset,
    1334             :                         size);
    1335             :         } else {
    1336        2101 :                 subreq = cli_smb1_writeall_send(
    1337             :                         state,
    1338             :                         ev,
    1339             :                         cli,
    1340             :                         fnum,
    1341             :                         mode,
    1342             :                         buf,
    1343             :                         offset,
    1344             :                         size);
    1345             :         }
    1346             : 
    1347        2596 :         if (tevent_req_nomem(subreq, req)) {
    1348           0 :                 return tevent_req_post(req, ev);
    1349             :         }
    1350        2596 :         tevent_req_set_callback(subreq, cli_writeall_done, req);
    1351             : 
    1352        2596 :         return req;
    1353             : }
    1354             : 
    1355        2596 : static void cli_writeall_done(struct tevent_req *subreq)
    1356             : {
    1357        2596 :         struct tevent_req *req = tevent_req_callback_data(
    1358             :                 subreq, struct tevent_req);
    1359        2596 :         struct cli_writeall_state *state = tevent_req_data(
    1360             :                 req, struct cli_writeall_state);
    1361             :         NTSTATUS status;
    1362             : 
    1363        2596 :         if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
    1364         495 :                 status = cli_smb2_writeall_recv(subreq, &state->written);
    1365             :         } else {
    1366        2101 :                 status = cli_smb1_writeall_recv(subreq, &state->written);
    1367             :         }
    1368        2596 :         TALLOC_FREE(subreq);
    1369        2596 :         if (tevent_req_nterror(req, status)) {
    1370          45 :                 return;
    1371             :         }
    1372        2551 :         tevent_req_done(req);
    1373             : }
    1374             : 
    1375        2596 : NTSTATUS cli_writeall_recv(struct tevent_req *req, size_t *pwritten)
    1376             : {
    1377        2596 :         struct cli_writeall_state *state = tevent_req_data(
    1378             :                 req, struct cli_writeall_state);
    1379             :         NTSTATUS status;
    1380             : 
    1381        2596 :         if (tevent_req_is_nterror(req, &status)) {
    1382          45 :                 return status;
    1383             :         }
    1384        2551 :         if (pwritten != NULL) {
    1385           0 :                 *pwritten = state->written;
    1386             :         }
    1387        2551 :         return NT_STATUS_OK;
    1388             : }
    1389             : 
    1390             : 
    1391        2596 : NTSTATUS cli_writeall(struct cli_state *cli, uint16_t fnum, uint16_t mode,
    1392             :                       const uint8_t *buf, off_t offset, size_t size,
    1393             :                       size_t *pwritten)
    1394             : {
    1395        2596 :         TALLOC_CTX *frame = talloc_stackframe();
    1396             :         struct tevent_context *ev;
    1397             :         struct tevent_req *req;
    1398        2596 :         NTSTATUS status = NT_STATUS_NO_MEMORY;
    1399             : 
    1400        2596 :         if (smbXcli_conn_has_async_calls(cli->conn)) {
    1401             :                 /*
    1402             :                  * Can't use sync call while an async call is in flight
    1403             :                  */
    1404           0 :                 status = NT_STATUS_INVALID_PARAMETER;
    1405           0 :                 goto fail;
    1406             :         }
    1407        2596 :         ev = samba_tevent_context_init(frame);
    1408        2596 :         if (ev == NULL) {
    1409           0 :                 goto fail;
    1410             :         }
    1411        2596 :         req = cli_writeall_send(frame, ev, cli, fnum, mode, buf, offset, size);
    1412        2596 :         if (req == NULL) {
    1413           0 :                 goto fail;
    1414             :         }
    1415        2596 :         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
    1416           0 :                 goto fail;
    1417             :         }
    1418        2596 :         status = cli_writeall_recv(req, pwritten);
    1419        2596 :  fail:
    1420        2596 :         TALLOC_FREE(frame);
    1421        2596 :         return status;
    1422             : }
    1423             : 
    1424             : struct cli_push_chunk;
    1425             : 
    1426             : struct cli_push_state {
    1427             :         struct tevent_context *ev;
    1428             :         struct cli_state *cli;
    1429             :         uint16_t fnum;
    1430             :         uint16_t mode;
    1431             :         off_t start_offset;
    1432             : 
    1433             :         size_t (*source)(uint8_t *buf, size_t n, void *priv);
    1434             :         void *priv;
    1435             : 
    1436             :         bool eof;
    1437             : 
    1438             :         size_t chunk_size;
    1439             :         off_t next_offset;
    1440             : 
    1441             :         /*
    1442             :          * Outstanding requests
    1443             :          *
    1444             :          * The maximum is 256:
    1445             :          * - which would be a window of 256 MByte
    1446             :          *   for SMB2 with multi-credit
    1447             :          *   or smb1 unix extensions.
    1448             :          */
    1449             :         uint16_t max_chunks;
    1450             :         uint16_t num_chunks;
    1451             :         uint16_t num_waiting;
    1452             :         struct cli_push_chunk *chunks;
    1453             : };
    1454             : 
    1455             : struct cli_push_chunk {
    1456             :         struct cli_push_chunk *prev, *next;
    1457             :         struct tevent_req *req;/* This is the main request! Not the subreq */
    1458             :         struct tevent_req *subreq;
    1459             :         off_t ofs;
    1460             :         uint8_t *buf;
    1461             :         size_t total_size;
    1462             :         size_t tmp_size;
    1463             :         bool done;
    1464             : };
    1465             : 
    1466             : static void cli_push_setup_chunks(struct tevent_req *req);
    1467             : static void cli_push_chunk_ship(struct cli_push_chunk *chunk);
    1468             : static void cli_push_chunk_done(struct tevent_req *subreq);
    1469             : 
    1470             : /*
    1471             :  * Used to write to a file remotely.
    1472             :  * This is similar in functionality to cli_write_send(), except this API
    1473             :  * handles writing a large file by breaking the data into chunks (so we don't
    1474             :  * exceed the max write size of the underlying connection). To do this, the
    1475             :  * (*source) callback handles copying the underlying file data into a message
    1476             :  * buffer, one chunk at a time.
    1477             :  * This API is recommended when writing a potentially large amount of data,
    1478             :  * e.g. when copying a file (or doing a 'put').
    1479             :  */
    1480        1707 : struct tevent_req *cli_push_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
    1481             :                                  struct cli_state *cli,
    1482             :                                  uint16_t fnum, uint16_t mode,
    1483             :                                  off_t start_offset, size_t window_size,
    1484             :                                  size_t (*source)(uint8_t *buf, size_t n,
    1485             :                                                   void *priv),
    1486             :                                  void *priv)
    1487             : {
    1488             :         struct tevent_req *req;
    1489             :         struct cli_push_state *state;
    1490        1707 :         size_t page_size = 1024;
    1491             :         uint64_t tmp64;
    1492             : 
    1493        1707 :         req = tevent_req_create(mem_ctx, &state, struct cli_push_state);
    1494        1707 :         if (req == NULL) {
    1495           0 :                 return NULL;
    1496             :         }
    1497        1707 :         state->cli = cli;
    1498        1707 :         state->ev = ev;
    1499        1707 :         state->fnum = fnum;
    1500        1707 :         state->start_offset = start_offset;
    1501        1707 :         state->mode = mode;
    1502        1707 :         state->source = source;
    1503        1707 :         state->priv = priv;
    1504        1707 :         state->next_offset = start_offset;
    1505             : 
    1506        1707 :         if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
    1507        1635 :                 state->chunk_size = smb2cli_conn_max_write_size(cli->conn);
    1508             :         } else {
    1509          72 :                 state->chunk_size = cli_write_max_bufsize(cli, mode, 14);
    1510             :         }
    1511        1707 :         if (state->chunk_size > page_size) {
    1512        1707 :                 state->chunk_size &= ~(page_size - 1);
    1513             :         }
    1514             : 
    1515        1707 :         if (window_size == 0) {
    1516             :                 /*
    1517             :                  * We use 16 MByte as default window size.
    1518             :                  */
    1519        1707 :                 window_size = 16 * 1024 * 1024;
    1520             :         }
    1521             : 
    1522        1707 :         tmp64 = window_size/state->chunk_size;
    1523        1707 :         if ((window_size % state->chunk_size) > 0) {
    1524          38 :                 tmp64 += 1;
    1525             :         }
    1526        1707 :         tmp64 = MAX(tmp64, 1);
    1527        1707 :         tmp64 = MIN(tmp64, 256);
    1528        1707 :         state->max_chunks = tmp64;
    1529             : 
    1530             :         /*
    1531             :          * We defer the callback because of the complex
    1532             :          * substate/subfunction logic
    1533             :          */
    1534        1707 :         tevent_req_defer_callback(req, ev);
    1535             : 
    1536        1707 :         cli_push_setup_chunks(req);
    1537        1707 :         if (!tevent_req_is_in_progress(req)) {
    1538         656 :                 return tevent_req_post(req, ev);
    1539             :         }
    1540             : 
    1541        1051 :         return req;
    1542             : }
    1543             : 
    1544        6118 : static void cli_push_setup_chunks(struct tevent_req *req)
    1545             : {
    1546        5739 :         struct cli_push_state *state =
    1547        6118 :                 tevent_req_data(req,
    1548             :                 struct cli_push_state);
    1549        6118 :         struct cli_push_chunk *chunk, *next = NULL;
    1550             :         size_t i;
    1551             : 
    1552      155472 :         for (chunk = state->chunks; chunk; chunk = next) {
    1553             :                 /*
    1554             :                  * Note that chunk might be removed from this call.
    1555             :                  */
    1556      149354 :                 next = chunk->next;
    1557      149354 :                 cli_push_chunk_ship(chunk);
    1558      149354 :                 if (!tevent_req_is_in_progress(req)) {
    1559           0 :                         return;
    1560             :                 }
    1561             :         }
    1562             : 
    1563       10529 :         for (i = state->num_chunks; i < state->max_chunks; i++) {
    1564             : 
    1565       10507 :                 if (state->num_waiting > 0) {
    1566        2686 :                         return;
    1567             :                 }
    1568             : 
    1569        7821 :                 if (state->eof) {
    1570        1703 :                         break;
    1571             :                 }
    1572             : 
    1573        6118 :                 chunk = talloc_zero(state, struct cli_push_chunk);
    1574        6118 :                 if (tevent_req_nomem(chunk, req)) {
    1575           0 :                         return;
    1576             :                 }
    1577        6118 :                 chunk->req = req;
    1578        6118 :                 chunk->ofs = state->next_offset;
    1579        6118 :                 chunk->buf = talloc_array(chunk,
    1580             :                                           uint8_t,
    1581             :                                           state->chunk_size);
    1582        6118 :                 if (tevent_req_nomem(chunk->buf, req)) {
    1583           0 :                         return;
    1584             :                 }
    1585        6118 :                 chunk->total_size = state->source(chunk->buf,
    1586             :                                                   state->chunk_size,
    1587             :                                                   state->priv);
    1588        6118 :                 if (chunk->total_size == 0) {
    1589             :                         /* nothing to send */
    1590        1707 :                         talloc_free(chunk);
    1591        1707 :                         state->eof = true;
    1592        1707 :                         break;
    1593             :                 }
    1594        4411 :                 state->next_offset += chunk->total_size;
    1595             : 
    1596        4411 :                 DLIST_ADD_END(state->chunks, chunk);
    1597        4411 :                 state->num_chunks++;
    1598        4411 :                 state->num_waiting++;
    1599             : 
    1600        4411 :                 cli_push_chunk_ship(chunk);
    1601        4411 :                 if (!tevent_req_is_in_progress(req)) {
    1602           0 :                         return;
    1603             :                 }
    1604             :         }
    1605             : 
    1606        3432 :         if (!state->eof) {
    1607          22 :                 return;
    1608             :         }
    1609             : 
    1610        3410 :         if (state->num_chunks > 0) {
    1611        1703 :                 return;
    1612             :         }
    1613             : 
    1614        1707 :         tevent_req_done(req);
    1615             : }
    1616             : 
    1617      153765 : static void cli_push_chunk_ship(struct cli_push_chunk *chunk)
    1618             : {
    1619      153765 :         struct tevent_req *req = chunk->req;
    1620      153397 :         struct cli_push_state *state =
    1621      153765 :                 tevent_req_data(req,
    1622             :                 struct cli_push_state);
    1623             :         bool ok;
    1624             :         const uint8_t *buf;
    1625             :         off_t ofs;
    1626             :         size_t size;
    1627             : 
    1628      153765 :         if (chunk->done) {
    1629        4411 :                 DLIST_REMOVE(state->chunks, chunk);
    1630        4411 :                 SMB_ASSERT(state->num_chunks > 0);
    1631        4411 :                 state->num_chunks--;
    1632        4411 :                 TALLOC_FREE(chunk);
    1633             : 
    1634        4411 :                 return;
    1635             :         }
    1636             : 
    1637      149354 :         if (chunk->subreq != NULL) {
    1638      142257 :                 return;
    1639             :         }
    1640             : 
    1641        7097 :         SMB_ASSERT(state->num_waiting > 0);
    1642             : 
    1643        7097 :         buf = chunk->buf + chunk->tmp_size;
    1644        7097 :         ofs = chunk->ofs + chunk->tmp_size;
    1645        7097 :         size = chunk->total_size - chunk->tmp_size;
    1646             : 
    1647        7097 :         if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
    1648             :                 uint32_t max_size;
    1649             : 
    1650        1049 :                 ok = smb2cli_conn_req_possible(state->cli->conn, &max_size);
    1651        1049 :                 if (!ok) {
    1652           0 :                         return;
    1653             :                 }
    1654             : 
    1655             :                 /*
    1656             :                  * downgrade depending on the available credits
    1657             :                  */
    1658        1049 :                 size = MIN(max_size, size);
    1659             : 
    1660        2098 :                 chunk->subreq = cli_smb2_write_send(chunk,
    1661             :                                                     state->ev,
    1662             :                                                     state->cli,
    1663        1049 :                                                     state->fnum,
    1664        1049 :                                                     state->mode,
    1665             :                                                     buf,
    1666             :                                                     ofs,
    1667             :                                                     size);
    1668        1049 :                 if (tevent_req_nomem(chunk->subreq, req)) {
    1669           0 :                         return;
    1670             :                 }
    1671             :         } else {
    1672        6048 :                 ok = smb1cli_conn_req_possible(state->cli->conn);
    1673        6048 :                 if (!ok) {
    1674        2686 :                         return;
    1675             :                 }
    1676             : 
    1677        6724 :                 chunk->subreq = cli_write_andx_send(chunk,
    1678             :                                                     state->ev,
    1679             :                                                     state->cli,
    1680        3362 :                                                     state->fnum,
    1681        3362 :                                                     state->mode,
    1682             :                                                     buf,
    1683             :                                                     ofs,
    1684             :                                                     size);
    1685        3362 :                 if (tevent_req_nomem(chunk->subreq, req)) {
    1686           0 :                         return;
    1687             :                 }
    1688             :         }
    1689        4411 :         tevent_req_set_callback(chunk->subreq,
    1690             :                                 cli_push_chunk_done,
    1691             :                                 chunk);
    1692             : 
    1693        4411 :         state->num_waiting--;
    1694        4411 :         return;
    1695             : }
    1696             : 
    1697        4411 : static void cli_push_chunk_done(struct tevent_req *subreq)
    1698             : {
    1699        4229 :         struct cli_push_chunk *chunk =
    1700        4411 :                 tevent_req_callback_data(subreq,
    1701             :                 struct cli_push_chunk);
    1702        4411 :         struct tevent_req *req = chunk->req;
    1703        4229 :         struct cli_push_state *state =
    1704        4411 :                 tevent_req_data(req,
    1705             :                 struct cli_push_state);
    1706             :         NTSTATUS status;
    1707        4411 :         size_t expected = chunk->total_size - chunk->tmp_size;
    1708             :         size_t written;
    1709             : 
    1710        4411 :         chunk->subreq = NULL;
    1711             : 
    1712        4411 :         if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
    1713        1049 :                 status = cli_smb2_write_recv(subreq, &written);
    1714             :         } else {
    1715        3362 :                 status = cli_write_andx_recv(subreq, &written);
    1716             :         }
    1717        4411 :         TALLOC_FREE(subreq);
    1718        4411 :         if (tevent_req_nterror(req, status)) {
    1719           0 :                 return;
    1720             :         }
    1721             : 
    1722        4411 :         if (written > expected) {
    1723           0 :                 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
    1724           0 :                 return;
    1725             :         }
    1726             : 
    1727        4411 :         if (written == 0) {
    1728           0 :                 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
    1729           0 :                 return;
    1730             :         }
    1731             : 
    1732        4411 :         chunk->tmp_size += written;
    1733             : 
    1734        4411 :         if (chunk->tmp_size == chunk->total_size) {
    1735        4411 :                 chunk->done = true;
    1736             :         } else {
    1737           0 :                 state->num_waiting++;
    1738             :         }
    1739             : 
    1740        4411 :         cli_push_setup_chunks(req);
    1741             : }
    1742             : 
    1743        1707 : NTSTATUS cli_push_recv(struct tevent_req *req)
    1744             : {
    1745        1707 :         return tevent_req_simple_recv_ntstatus(req);
    1746             : }
    1747             : 
    1748         823 : NTSTATUS cli_push(struct cli_state *cli, uint16_t fnum, uint16_t mode,
    1749             :                   off_t start_offset, size_t window_size,
    1750             :                   size_t (*source)(uint8_t *buf, size_t n, void *priv),
    1751             :                   void *priv)
    1752             : {
    1753         823 :         TALLOC_CTX *frame = talloc_stackframe();
    1754             :         struct tevent_context *ev;
    1755             :         struct tevent_req *req;
    1756         823 :         NTSTATUS status = NT_STATUS_OK;
    1757             : 
    1758         823 :         if (smbXcli_conn_has_async_calls(cli->conn)) {
    1759             :                 /*
    1760             :                  * Can't use sync call while an async call is in flight
    1761             :                  */
    1762           0 :                 status = NT_STATUS_INVALID_PARAMETER;
    1763           0 :                 goto fail;
    1764             :         }
    1765             : 
    1766         823 :         ev = samba_tevent_context_init(frame);
    1767         823 :         if (ev == NULL) {
    1768           0 :                 status = NT_STATUS_NO_MEMORY;
    1769           0 :                 goto fail;
    1770             :         }
    1771             : 
    1772         823 :         req = cli_push_send(frame, ev, cli, fnum, mode, start_offset,
    1773             :                             window_size, source, priv);
    1774         823 :         if (req == NULL) {
    1775           0 :                 status = NT_STATUS_NO_MEMORY;
    1776           0 :                 goto fail;
    1777             :         }
    1778             : 
    1779         823 :         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
    1780           0 :                 goto fail;
    1781             :         }
    1782             : 
    1783         823 :         status = cli_push_recv(req);
    1784         823 :  fail:
    1785         823 :         TALLOC_FREE(frame);
    1786         823 :         return status;
    1787             : }
    1788             : 
    1789             : #define SPLICE_BLOCK_SIZE 1024 * 1024
    1790             : 
    1791           7 : static NTSTATUS cli_splice_fallback(TALLOC_CTX *frame,
    1792             :                                     struct cli_state *srccli,
    1793             :                                     struct cli_state *dstcli,
    1794             :                                     uint16_t src_fnum, uint16_t dst_fnum,
    1795             :                                     off_t initial_size,
    1796             :                                     off_t src_offset, off_t dst_offset,
    1797             :                                     off_t *written,
    1798             :                                     int (*splice_cb)(off_t n, void *priv),
    1799             :                                     void *priv)
    1800             : {
    1801             :         NTSTATUS status;
    1802           7 :         uint8_t *buf = talloc_size(frame, SPLICE_BLOCK_SIZE);
    1803             :         size_t nread;
    1804           7 :         off_t remaining = initial_size;
    1805           7 :         *written = 0;
    1806             : 
    1807          28 :         while (remaining) {
    1808          14 :                 size_t to_read = MIN(remaining, SPLICE_BLOCK_SIZE);
    1809             : 
    1810          14 :                 status = cli_read(srccli, src_fnum,
    1811             :                                   (char *)buf, src_offset, to_read,
    1812             :                                   &nread);
    1813          14 :                 if (!NT_STATUS_IS_OK(status)) {
    1814           0 :                         return status;
    1815             :                 }
    1816             : 
    1817          14 :                 status = cli_writeall(dstcli, dst_fnum, 0,
    1818             :                                       buf, dst_offset, nread, NULL);
    1819          14 :                 if (!NT_STATUS_IS_OK(status)) {
    1820           0 :                         return status;
    1821             :                 }
    1822             : 
    1823          28 :                 if ((src_offset > INT64_MAX - nread) ||
    1824          14 :                     (dst_offset > INT64_MAX - nread)) {
    1825           0 :                         return NT_STATUS_FILE_TOO_LARGE;
    1826             :                 }
    1827          14 :                 src_offset += nread;
    1828          14 :                 dst_offset += nread;
    1829          14 :                 *written += nread;
    1830          14 :                 if (remaining < nread) {
    1831           0 :                         return NT_STATUS_INTERNAL_ERROR;
    1832             :                 }
    1833          14 :                 remaining -= nread;
    1834          14 :                 if (!splice_cb(initial_size - remaining, priv)) {
    1835           0 :                         return NT_STATUS_CANCELLED;
    1836             :                 }
    1837             :         }
    1838             : 
    1839           7 :         return NT_STATUS_OK;
    1840             : }
    1841             : 
    1842           7 : NTSTATUS cli_splice(struct cli_state *srccli, struct cli_state *dstcli,
    1843             :                     uint16_t src_fnum, uint16_t dst_fnum,
    1844             :                     off_t size,
    1845             :                     off_t src_offset, off_t dst_offset,
    1846             :                     off_t *written,
    1847             :                     int (*splice_cb)(off_t n, void *priv), void *priv)
    1848             : {
    1849           7 :         TALLOC_CTX *frame = talloc_stackframe();
    1850             :         struct tevent_context *ev;
    1851             :         struct tevent_req *req;
    1852           7 :         NTSTATUS status = NT_STATUS_NO_MEMORY;
    1853           7 :         bool retry_fallback = false;
    1854             : 
    1855          14 :         if (smbXcli_conn_has_async_calls(srccli->conn) ||
    1856           7 :             smbXcli_conn_has_async_calls(dstcli->conn))
    1857             :         {
    1858             :                 /*
    1859             :                  * Can't use sync call while an async call is in flight
    1860             :                  */
    1861           0 :                 status = NT_STATUS_INVALID_PARAMETER;
    1862           0 :                 goto out;
    1863             :         }
    1864             : 
    1865             :         do {
    1866           7 :                 ev = samba_tevent_context_init(frame);
    1867           7 :                 if (ev == NULL) {
    1868           0 :                         goto out;
    1869             :                 }
    1870          14 :                 if (srccli == dstcli &&
    1871           7 :                     smbXcli_conn_protocol(srccli->conn) >= PROTOCOL_SMB2_02 &&
    1872           0 :                     !retry_fallback)
    1873             :                 {
    1874           0 :                         req = cli_smb2_splice_send(frame, ev,
    1875             :                                                    srccli, src_fnum, dst_fnum,
    1876             :                                                    size, src_offset, dst_offset,
    1877             :                                                    splice_cb, priv);
    1878             :                 } else {
    1879           7 :                         status = cli_splice_fallback(frame,
    1880             :                                                      srccli, dstcli,
    1881             :                                                      src_fnum, dst_fnum,
    1882             :                                                      size,
    1883             :                                                      src_offset, dst_offset,
    1884             :                                                      written,
    1885             :                                                      splice_cb, priv);
    1886           7 :                         goto out;
    1887             :                 }
    1888           0 :                 if (req == NULL) {
    1889           0 :                         goto out;
    1890             :                 }
    1891           0 :                 if (!tevent_req_poll(req, ev)) {
    1892           0 :                         status = map_nt_error_from_unix(errno);
    1893           0 :                         goto out;
    1894             :                 }
    1895           0 :                 status = cli_smb2_splice_recv(req, written);
    1896             : 
    1897             :                 /*
    1898             :                  * Older versions of Samba don't support
    1899             :                  * FSCTL_SRV_COPYCHUNK_WRITE so use the fallback.
    1900             :                  */
    1901           0 :                 retry_fallback = NT_STATUS_EQUAL(status, NT_STATUS_INVALID_DEVICE_REQUEST);
    1902           0 :         } while (retry_fallback);
    1903             : 
    1904           0 :  out:
    1905           7 :         TALLOC_FREE(frame);
    1906           7 :         return status;
    1907             : }

Generated by: LCOV version 1.13