Coverage Report

Created: 2025-07-23 07:04

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