spnego: Correctly check asn1_tag_remaining retval
[sfrench/samba-autobuild/.git] / source3 / libsmb / clireadwrite.c
index 821bcb18cfe9f73b719b2c9b33b72850e3e30cdc..79bf416822af94d4986a31c7808721c8f7737ffd 100644 (file)
@@ -296,7 +296,7 @@ struct cli_pull_state {
         * The maximum is 256:
         * - which would be a window of 256 MByte
         *   for SMB2 with multi-credit
-        *   or smb1 unix extentions.
+        *   or smb1 unix extensions.
         */
        uint16_t max_chunks;
        uint16_t num_chunks;
@@ -360,7 +360,11 @@ struct tevent_req *cli_pull_send(TALLOC_CTX *mem_ctx,
                return tevent_req_post(req, ev);
        }
 
-       state->chunk_size = cli_read_max_bufsize(cli);
+       if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
+               state->chunk_size = smb2cli_conn_max_read_size(cli->conn);
+       } else {
+               state->chunk_size = cli_read_max_bufsize(cli);
+       }
        if (state->chunk_size > page_size) {
                state->chunk_size &= ~(page_size - 1);
        }
@@ -519,19 +523,43 @@ static void cli_pull_chunk_ship(struct cli_pull_chunk *chunk)
        ofs = chunk->ofs + chunk->tmp_size;
        size = chunk->total_size - chunk->tmp_size;
 
-       ok = smb1cli_conn_req_possible(state->cli->conn);
-       if (!ok) {
-               return;
-       }
+       if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
+               uint32_t max_size;
 
-       chunk->subreq = cli_read_andx_send(chunk,
-                                          state->ev,
-                                          state->cli,
-                                          state->fnum,
-                                          ofs,
-                                          size);
-       if (tevent_req_nomem(chunk->subreq, req)) {
-               return;
+               ok = smb2cli_conn_req_possible(state->cli->conn, &max_size);
+               if (!ok) {
+                       return;
+               }
+
+               /*
+                * downgrade depending on the available credits
+                */
+               size = MIN(max_size, size);
+
+               chunk->subreq = cli_smb2_read_send(chunk,
+                                                  state->ev,
+                                                  state->cli,
+                                                  state->fnum,
+                                                  ofs,
+                                                  size);
+               if (tevent_req_nomem(chunk->subreq, req)) {
+                       return;
+               }
+       } else {
+               ok = smb1cli_conn_req_possible(state->cli->conn);
+               if (!ok) {
+                       return;
+               }
+
+               chunk->subreq = cli_read_andx_send(chunk,
+                                                  state->ev,
+                                                  state->cli,
+                                                  state->fnum,
+                                                  ofs,
+                                                  size);
+               if (tevent_req_nomem(chunk->subreq, req)) {
+                       return;
+               }
        }
        tevent_req_set_callback(chunk->subreq,
                                cli_pull_chunk_done,
@@ -557,7 +585,11 @@ static void cli_pull_chunk_done(struct tevent_req *subreq)
 
        chunk->subreq = NULL;
 
-       status = cli_read_andx_recv(subreq, &received, &buf);
+       if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
+               status = cli_smb2_read_recv(subreq, &received, &buf);
+       } else {
+               status = cli_read_andx_recv(subreq, &received, &buf);
+       }
        if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE)) {
                received = 0;
                status = NT_STATUS_OK;
@@ -658,8 +690,7 @@ NTSTATUS cli_pull(struct cli_state *cli, uint16_t fnum,
                goto fail;
        }
 
-       if (!tevent_req_poll(req, ev)) {
-               status = map_nt_error_from_unix(errno);
+       if (!tevent_req_poll_ntstatus(req, ev, &status)) {
                goto fail;
        }
 
@@ -1034,15 +1065,24 @@ NTSTATUS cli_writeall(struct cli_state *cli, uint16_t fnum, uint16_t mode,
        if (ev == NULL) {
                goto fail;
        }
-       req = cli_writeall_send(frame, ev, cli, fnum, mode, buf, offset, size);
+       if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+               req = cli_smb2_writeall_send(frame, ev, cli, fnum, mode,
+                                            buf, offset, size);
+       } else {
+               req = cli_writeall_send(frame, ev, cli, fnum, mode,
+                                       buf, offset, size);
+       }
        if (req == NULL) {
                goto fail;
        }
-       if (!tevent_req_poll(req, ev)) {
-               status = map_nt_error_from_unix(errno);
+       if (!tevent_req_poll_ntstatus(req, ev, &status)) {
                goto fail;
        }
-       status = cli_writeall_recv(req, pwritten);
+       if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+               status = cli_smb2_writeall_recv(req, pwritten);
+       } else {
+               status = cli_writeall_recv(req, pwritten);
+       }
  fail:
        TALLOC_FREE(frame);
        return status;
@@ -1071,7 +1111,7 @@ struct cli_push_state {
         * The maximum is 256:
         * - which would be a window of 256 MByte
         *   for SMB2 with multi-credit
-        *   or smb1 unix extentions.
+        *   or smb1 unix extensions.
         */
        uint16_t max_chunks;
        uint16_t num_chunks;
@@ -1393,8 +1433,7 @@ NTSTATUS cli_push(struct cli_state *cli, uint16_t fnum, uint16_t mode,
                goto fail;
        }
 
-       if (!tevent_req_poll(req, ev)) {
-               status = map_nt_error_from_unix(errno);
+       if (!tevent_req_poll_ntstatus(req, ev, &status)) {
                goto fail;
        }
 
@@ -1403,3 +1442,119 @@ NTSTATUS cli_push(struct cli_state *cli, uint16_t fnum, uint16_t mode,
        TALLOC_FREE(frame);
        return status;
 }
+
+#define SPLICE_BLOCK_SIZE 1024 * 1024
+
+static NTSTATUS cli_splice_fallback(TALLOC_CTX *frame,
+                                   struct cli_state *srccli,
+                                   struct cli_state *dstcli,
+                                   uint16_t src_fnum, uint16_t dst_fnum,
+                                   off_t initial_size,
+                                   off_t src_offset, off_t dst_offset,
+                                   off_t *written,
+                                   int (*splice_cb)(off_t n, void *priv),
+                                   void *priv)
+{
+       NTSTATUS status;
+       uint8_t *buf = talloc_size(frame, SPLICE_BLOCK_SIZE);
+       size_t nread;
+       off_t remaining = initial_size;
+
+       while (remaining) {
+               status = cli_read(srccli, src_fnum,
+                                 (char *)buf, src_offset, SPLICE_BLOCK_SIZE,
+                                 &nread);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+
+               status = cli_writeall(dstcli, dst_fnum, 0,
+                                     buf, dst_offset, nread, NULL);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+
+               if ((src_offset > INT64_MAX - nread) ||
+                   (dst_offset > INT64_MAX - nread)) {
+                       return NT_STATUS_FILE_TOO_LARGE;
+               }
+               src_offset += nread;
+               dst_offset += nread;
+               if (remaining < nread) {
+                       return NT_STATUS_INTERNAL_ERROR;
+               }
+               remaining -= nread;
+               if (!splice_cb(initial_size - remaining, priv)) {
+                       return NT_STATUS_CANCELLED;
+               }
+       }
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS cli_splice(struct cli_state *srccli, struct cli_state *dstcli,
+                   uint16_t src_fnum, uint16_t dst_fnum,
+                   off_t size,
+                   off_t src_offset, off_t dst_offset,
+                   off_t *written,
+                   int (*splice_cb)(off_t n, void *priv), void *priv)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       struct tevent_context *ev;
+       struct tevent_req *req;
+       NTSTATUS status = NT_STATUS_NO_MEMORY;
+       bool retry_fallback = false;
+
+       if (smbXcli_conn_has_async_calls(srccli->conn) ||
+           smbXcli_conn_has_async_calls(dstcli->conn))
+       {
+               /*
+                * Can't use sync call while an async call is in flight
+                */
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto out;
+       }
+
+       do {
+               ev = samba_tevent_context_init(frame);
+               if (ev == NULL) {
+                       goto out;
+               }
+               if (srccli == dstcli &&
+                   smbXcli_conn_protocol(srccli->conn) >= PROTOCOL_SMB2_02 &&
+                   !retry_fallback)
+               {
+                       req = cli_smb2_splice_send(frame, ev,
+                                                  srccli, src_fnum, dst_fnum,
+                                                  size, src_offset, dst_offset,
+                                                  splice_cb, priv);
+               } else {
+                       status = cli_splice_fallback(frame,
+                                                    srccli, dstcli,
+                                                    src_fnum, dst_fnum,
+                                                    size,
+                                                    src_offset, dst_offset,
+                                                    written,
+                                                    splice_cb, priv);
+                       goto out;
+               }
+               if (req == NULL) {
+                       goto out;
+               }
+               if (!tevent_req_poll(req, ev)) {
+                       status = map_nt_error_from_unix(errno);
+                       goto out;
+               }
+               status = cli_smb2_splice_recv(req, written);
+
+               /*
+                * Older versions of Samba don't support
+                * FSCTL_SRV_COPYCHUNK_WRITE so use the fallback.
+                */
+               retry_fallback = NT_STATUS_EQUAL(status, NT_STATUS_INVALID_DEVICE_REQUEST);
+       } while (retry_fallback);
+
+ out:
+       TALLOC_FREE(frame);
+       return status;
+}