Move handle checking code to copychunk_check_handles().
[samba.git] / source3 / smbd / smb2_ioctl_network_fs.c
1 /*
2    Unix SMB/CIFS implementation.
3    Core SMB2 server
4
5    Copyright (C) Stefan Metzmacher 2009
6    Copyright (C) David Disseldorp 2012
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "includes.h"
23 #include "smbd/smbd.h"
24 #include "smbd/globals.h"
25 #include "../libcli/smb/smb_common.h"
26 #include "../libcli/security/security.h"
27 #include "../lib/util/tevent_ntstatus.h"
28 #include "../lib/ccan/build_assert/build_assert.h"
29 #include "include/ntioctl.h"
30 #include "../librpc/ndr/libndr.h"
31 #include "librpc/gen_ndr/ndr_ioctl.h"
32 #include "smb2_ioctl_private.h"
33
34 #define COPYCHUNK_MAX_CHUNKS    256             /* 2k8r2 & win8 = 256 */
35 #define COPYCHUNK_MAX_CHUNK_LEN 1048576         /* 2k8r2 & win8 = 1048576 */
36 #define COPYCHUNK_MAX_TOTAL_LEN 16777216        /* 2k8r2 & win8 = 16777216 */
37 static void copychunk_pack_limits(struct srv_copychunk_rsp *cc_rsp)
38 {
39         cc_rsp->chunks_written = COPYCHUNK_MAX_CHUNKS;
40         cc_rsp->chunk_bytes_written = COPYCHUNK_MAX_CHUNK_LEN;
41         cc_rsp->total_bytes_written = COPYCHUNK_MAX_TOTAL_LEN;
42 }
43
44 static NTSTATUS copychunk_check_limits(struct srv_copychunk_copy *cc_copy)
45 {
46         uint32_t i;
47         uint32_t total_len = 0;
48
49         if (cc_copy->chunk_count > COPYCHUNK_MAX_CHUNKS) {
50                 return NT_STATUS_INVALID_PARAMETER;
51         }
52
53         for (i = 0; i < cc_copy->chunk_count; i++) {
54                 if (cc_copy->chunks[i].length > COPYCHUNK_MAX_CHUNK_LEN) {
55                         return NT_STATUS_INVALID_PARAMETER;
56                 }
57                 total_len += cc_copy->chunks[i].length;
58         }
59         if (total_len > COPYCHUNK_MAX_TOTAL_LEN) {
60                 return NT_STATUS_INVALID_PARAMETER;
61         }
62
63         return NT_STATUS_OK;
64 }
65
66 static void copychunk_unlock_all(struct files_struct *src_fsp,
67                                  struct files_struct *dst_fsp,
68                                  struct lock_struct *rd_locks,
69                                  struct lock_struct *wr_locks,
70                                  uint32_t num_locks)
71 {
72
73         uint32_t i;
74
75         for (i = 0; i < num_locks; i++) {
76                 SMB_VFS_STRICT_UNLOCK(src_fsp->conn, src_fsp, &rd_locks[i]);
77                 SMB_VFS_STRICT_UNLOCK(dst_fsp->conn, dst_fsp, &wr_locks[i]);
78         }
79 }
80
81 /* request read and write locks for each chunk */
82 static NTSTATUS copychunk_lock_all(TALLOC_CTX *mem_ctx,
83                                    struct srv_copychunk_copy *cc_copy,
84                                    struct files_struct *src_fsp,
85                                    struct files_struct *dst_fsp,
86                                    struct lock_struct **rd_locks,
87                                    struct lock_struct **wr_locks,
88                                    uint32_t *num_locks)
89 {
90         NTSTATUS status;
91         uint32_t i;
92         struct lock_struct *rlocks;
93         struct lock_struct *wlocks;
94
95         rlocks = talloc_array(mem_ctx, struct lock_struct,
96                               cc_copy->chunk_count);
97         if (rlocks == NULL) {
98                 status = NT_STATUS_NO_MEMORY;
99                 goto err_out;
100         }
101
102         wlocks = talloc_array(mem_ctx, struct lock_struct,
103                               cc_copy->chunk_count);
104         if (wlocks == NULL) {
105                 status = NT_STATUS_NO_MEMORY;
106                 goto err_rlocks_free;
107         }
108
109         for (i = 0; i < cc_copy->chunk_count; i++) {
110                 init_strict_lock_struct(src_fsp,
111                                         src_fsp->op->global->open_persistent_id,
112                                         cc_copy->chunks[i].source_off,
113                                         cc_copy->chunks[i].length,
114                                         READ_LOCK,
115                                         &rlocks[i]);
116                 init_strict_lock_struct(dst_fsp,
117                                         dst_fsp->op->global->open_persistent_id,
118                                         cc_copy->chunks[i].target_off,
119                                         cc_copy->chunks[i].length,
120                                         WRITE_LOCK,
121                                         &wlocks[i]);
122
123                 if (!SMB_VFS_STRICT_LOCK(src_fsp->conn, src_fsp, &rlocks[i])) {
124                         status = NT_STATUS_FILE_LOCK_CONFLICT;
125                         goto err_unlock;
126                 }
127                 if (!SMB_VFS_STRICT_LOCK(dst_fsp->conn, dst_fsp, &wlocks[i])) {
128                         /* unlock last rlock, otherwise missed by cleanup */
129                         SMB_VFS_STRICT_UNLOCK(src_fsp->conn, src_fsp,
130                                               &rlocks[i]);
131                         status = NT_STATUS_FILE_LOCK_CONFLICT;
132                         goto err_unlock;
133                 }
134         }
135
136         *rd_locks = rlocks;
137         *wr_locks = wlocks;
138         *num_locks = i;
139
140         return NT_STATUS_OK;
141
142 err_unlock:
143         if (i > 0) {
144                 /* cleanup all locks successfully issued so far */
145                 copychunk_unlock_all(src_fsp, dst_fsp, rlocks, wlocks, i);
146         }
147         talloc_free(wlocks);
148 err_rlocks_free:
149         talloc_free(rlocks);
150 err_out:
151         return status;
152 }
153
154 struct fsctl_srv_copychunk_state {
155         struct connection_struct *conn;
156         uint32_t dispatch_count;
157         uint32_t recv_count;
158         uint32_t bad_recv_count;
159         NTSTATUS status;
160         off_t total_written;
161         struct files_struct *src_fsp;
162         struct files_struct *dst_fsp;
163         struct lock_struct *wlocks;
164         struct lock_struct *rlocks;
165         uint32_t num_locks;
166         enum {
167                 COPYCHUNK_OUT_EMPTY = 0,
168                 COPYCHUNK_OUT_LIMITS,
169                 COPYCHUNK_OUT_RSP,
170         } out_data;
171 };
172 static void fsctl_srv_copychunk_vfs_done(struct tevent_req *subreq);
173
174 static NTSTATUS copychunk_check_handles(struct files_struct *src_fsp,
175                                         struct files_struct *dst_fsp,
176                                         struct smb_request *smb1req)
177 {
178         /*
179          * [MS-SMB2] 3.3.5.15.6 Handling a Server-Side Data Copy Request
180          * If Open.GrantedAccess of the destination file does not
181          * include FILE_WRITE_DATA, then the request MUST be failed with
182          * STATUS_ACCESS_DENIED. If Open.GrantedAccess of the
183          * destination file does not include FILE_READ_DATA access and
184          * the CtlCode is FSCTL_SRV_COPYCHUNK, then the request MUST be
185          * failed with STATUS_ACCESS_DENIED.
186          */
187         if (!CHECK_WRITE(dst_fsp)) {
188                 DEBUG(5, ("copy chunk no write on dest handle (%s).\n",
189                         smb_fname_str_dbg(dst_fsp->fsp_name) ));
190                 return NT_STATUS_ACCESS_DENIED;
191         }
192         if (!CHECK_READ(dst_fsp, smb1req)) {
193                 DEBUG(5, ("copy chunk no read on dest handle (%s).\n",
194                         smb_fname_str_dbg(dst_fsp->fsp_name) ));
195                 return NT_STATUS_ACCESS_DENIED;
196         }
197         if (!CHECK_READ(src_fsp, smb1req)) {
198                 DEBUG(5, ("copy chunk no read on src handle (%s).\n",
199                         smb_fname_str_dbg(src_fsp->fsp_name) ));
200                 return NT_STATUS_ACCESS_DENIED;
201         }
202
203         return NT_STATUS_OK;
204 }
205
206 static struct tevent_req *fsctl_srv_copychunk_send(TALLOC_CTX *mem_ctx,
207                                                    struct tevent_context *ev,
208                                                    struct files_struct *dst_fsp,
209                                                    DATA_BLOB *in_input,
210                                                    size_t in_max_output,
211                                                    struct smbd_smb2_request *smb2req)
212 {
213         struct tevent_req *req;
214         struct srv_copychunk_copy cc_copy;
215         enum ndr_err_code ndr_ret;
216         uint64_t src_persistent_h;
217         uint64_t src_volatile_h;
218         int i;
219         struct srv_copychunk *chunk;
220         struct fsctl_srv_copychunk_state *state;
221
222         req = tevent_req_create(mem_ctx, &state,
223                                 struct fsctl_srv_copychunk_state);
224         if (req == NULL) {
225                 return NULL;
226         }
227         state->conn = dst_fsp->conn;
228
229         if (in_max_output < sizeof(struct srv_copychunk_rsp)) {
230                 DEBUG(3, ("max output %d not large enough to hold copy chunk "
231                           "response %lu\n", (int)in_max_output,
232                           sizeof(struct srv_copychunk_rsp)));
233                 state->status = NT_STATUS_INVALID_PARAMETER;
234                 tevent_req_nterror(req, state->status);
235                 return tevent_req_post(req, ev);
236         }
237
238         ndr_ret = ndr_pull_struct_blob(in_input, mem_ctx, &cc_copy,
239                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_copy);
240         if (ndr_ret != NDR_ERR_SUCCESS) {
241                 DEBUG(0, ("failed to unmarshall copy chunk req\n"));
242                 state->status = NT_STATUS_INVALID_PARAMETER;
243                 tevent_req_nterror(req, state->status);
244                 return tevent_req_post(req, ev);
245         }
246
247         /* persistent/volatile keys sent as the resume key */
248         src_persistent_h = BVAL(cc_copy.source_key, 0);
249         src_volatile_h = BVAL(cc_copy.source_key, 8);
250         state->src_fsp = file_fsp_get(smb2req, src_persistent_h, src_volatile_h);
251         if (state->src_fsp == NULL) {
252                 DEBUG(3, ("invalid resume key in copy chunk req\n"));
253                 state->status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
254                 tevent_req_nterror(req, state->status);
255                 return tevent_req_post(req, ev);
256         }
257
258         state->dst_fsp = dst_fsp;
259
260         state->status = copychunk_check_handles(state->src_fsp,
261                                                 state->dst_fsp,
262                                                 smb2req->smb1req);
263         if (!NT_STATUS_IS_OK(state->status)) {
264                 tevent_req_nterror(req, state->status);
265                 return tevent_req_post(req, ev);
266         }
267
268         state->status = copychunk_check_limits(&cc_copy);
269         if (tevent_req_nterror(req, state->status)) {
270                 DEBUG(3, ("copy chunk req exceeds limits\n"));
271                 state->out_data = COPYCHUNK_OUT_LIMITS;
272                 return tevent_req_post(req, ev);
273         }
274
275         /* any errors from here onwards should carry copychunk response data */
276         state->out_data = COPYCHUNK_OUT_RSP;
277
278         state->status = copychunk_lock_all(state,
279                                            &cc_copy,
280                                            state->src_fsp,
281                                            state->dst_fsp,
282                                            &state->rlocks,
283                                            &state->wlocks,
284                                            &state->num_locks);
285         if (tevent_req_nterror(req, state->status)) {
286                 return tevent_req_post(req, ev);
287         }
288
289         for (i = 0; i < cc_copy.chunk_count; i++) {
290                 struct tevent_req *vfs_subreq;
291                 chunk = &cc_copy.chunks[i];
292                 vfs_subreq = SMB_VFS_COPY_CHUNK_SEND(dst_fsp->conn,
293                                                      state, ev,
294                                                      state->src_fsp,
295                                                      chunk->source_off,
296                                                      state->dst_fsp,
297                                                      chunk->target_off,
298                                                      chunk->length);
299                 if (vfs_subreq == NULL) {
300                         DEBUG(0, ("VFS copy chunk send failed\n"));
301                         state->status = NT_STATUS_NO_MEMORY;
302                         if (state->dispatch_count == 0) {
303                                 /* nothing dispatched, return immediately */
304                                 copychunk_unlock_all(state->src_fsp,
305                                                      state->dst_fsp,
306                                                      state->rlocks,
307                                                      state->wlocks,
308                                                      state->num_locks);
309                                 tevent_req_nterror(req, state->status);
310                                 return tevent_req_post(req, ev);
311                         } else {
312                                 /*
313                                  * wait for dispatched to complete before
314                                  * returning error, locks held.
315                                  */
316                                 break;
317                         }
318                 }
319                 tevent_req_set_callback(vfs_subreq,
320                                         fsctl_srv_copychunk_vfs_done, req);
321                 state->dispatch_count++;
322         }
323
324         /* hold locks until all dispatched requests are completed */
325         return req;
326 }
327
328 static void fsctl_srv_copychunk_vfs_done(struct tevent_req *subreq)
329 {
330         struct tevent_req *req = tevent_req_callback_data(
331                 subreq, struct tevent_req);
332         struct fsctl_srv_copychunk_state *state = tevent_req_data(req,
333                                         struct fsctl_srv_copychunk_state);
334         off_t chunk_nwritten;
335         NTSTATUS status;
336
337         state->recv_count++;
338         status = SMB_VFS_COPY_CHUNK_RECV(state->conn, subreq,
339                                          &chunk_nwritten);
340         TALLOC_FREE(subreq);
341         if (NT_STATUS_IS_OK(status)) {
342                 DEBUG(10, ("good copy chunk recv %d of %d\n",
343                            state->recv_count,
344                            state->dispatch_count));
345                 state->total_written += chunk_nwritten;
346         } else {
347                 DEBUG(0, ("bad status in copy chunk recv %d of %d: %s\n",
348                           state->recv_count,
349                           state->dispatch_count,
350                           nt_errstr(status)));
351                 state->bad_recv_count++;
352                 /* may overwrite previous failed status */
353                 state->status = status;
354         }
355
356         if (state->recv_count != state->dispatch_count) {
357                 /*
358                  * Wait for all VFS copy_chunk requests to complete, even
359                  * if an error is received for a specific chunk.
360                  */
361                 return;
362         }
363
364         /* all VFS copy_chunk requests done */
365         copychunk_unlock_all(state->src_fsp,
366                              state->dst_fsp,
367                              state->rlocks,
368                              state->wlocks,
369                              state->num_locks);
370
371         if (!tevent_req_nterror(req, state->status)) {
372                 tevent_req_done(req);
373         }
374 }
375
376 static NTSTATUS fsctl_srv_copychunk_recv(struct tevent_req *req,
377                                          struct srv_copychunk_rsp *cc_rsp,
378                                          bool *pack_rsp)
379 {
380         struct fsctl_srv_copychunk_state *state = tevent_req_data(req,
381                                         struct fsctl_srv_copychunk_state);
382         NTSTATUS status;
383
384         switch (state->out_data) {
385         case COPYCHUNK_OUT_EMPTY:
386                 *pack_rsp = false;
387                 break;
388         case COPYCHUNK_OUT_LIMITS:
389                 /* 2.2.32.1 - send back our maximum transfer size limits */
390                 copychunk_pack_limits(cc_rsp);
391                 *pack_rsp = true;
392                 break;
393         case COPYCHUNK_OUT_RSP:
394                 cc_rsp->chunks_written = state->recv_count - state->bad_recv_count;
395                 cc_rsp->chunk_bytes_written = 0;
396                 cc_rsp->total_bytes_written = state->total_written;
397                 *pack_rsp = true;
398                 break;
399         default:        /* not reached */
400                 assert(1);
401                 break;
402         }
403         status = state->status;
404         tevent_req_received(req);
405
406         return status;
407 }
408
409 static NTSTATUS fsctl_validate_neg_info(TALLOC_CTX *mem_ctx,
410                                         struct tevent_context *ev,
411                                         struct smbXsrv_connection *conn,
412                                         DATA_BLOB *in_input,
413                                         uint32_t in_max_output,
414                                         DATA_BLOB *out_output,
415                                         bool *disconnect)
416 {
417         uint32_t in_capabilities;
418         DATA_BLOB in_guid_blob;
419         struct GUID in_guid;
420         uint16_t in_security_mode;
421         uint16_t in_num_dialects;
422         uint16_t i;
423         DATA_BLOB out_guid_blob;
424         NTSTATUS status;
425
426         if (in_input->length < 0x18) {
427                 return NT_STATUS_INVALID_PARAMETER;
428         }
429
430         in_capabilities = IVAL(in_input->data, 0x00);
431         in_guid_blob = data_blob_const(in_input->data + 0x04, 16);
432         in_security_mode = SVAL(in_input->data, 0x14);
433         in_num_dialects = SVAL(in_input->data, 0x16);
434
435         if (in_input->length < (0x18 + in_num_dialects*2)) {
436                 return NT_STATUS_INVALID_PARAMETER;
437         }
438
439         if (in_max_output < 0x18) {
440                 return NT_STATUS_BUFFER_TOO_SMALL;
441         }
442
443         status = GUID_from_ndr_blob(&in_guid_blob, &in_guid);
444         if (!NT_STATUS_IS_OK(status)) {
445                 return status;
446         }
447
448         if (in_num_dialects != conn->smb2.client.num_dialects) {
449                 *disconnect = true;
450                 return NT_STATUS_ACCESS_DENIED;
451         }
452
453         for (i=0; i < in_num_dialects; i++) {
454                 uint16_t v = SVAL(in_input->data, 0x18 + i*2);
455
456                 if (conn->smb2.client.dialects[i] != v) {
457                         *disconnect = true;
458                         return NT_STATUS_ACCESS_DENIED;
459                 }
460         }
461
462         if (GUID_compare(&in_guid, &conn->smb2.client.guid) != 0) {
463                 *disconnect = true;
464                 return NT_STATUS_ACCESS_DENIED;
465         }
466
467         if (in_security_mode != conn->smb2.client.security_mode) {
468                 *disconnect = true;
469                 return NT_STATUS_ACCESS_DENIED;
470         }
471
472         if (in_capabilities != conn->smb2.client.capabilities) {
473                 *disconnect = true;
474                 return NT_STATUS_ACCESS_DENIED;
475         }
476
477         status = GUID_to_ndr_blob(&conn->smb2.server.guid, mem_ctx,
478                                   &out_guid_blob);
479         if (!NT_STATUS_IS_OK(status)) {
480                 return status;
481         }
482
483         *out_output = data_blob_talloc(mem_ctx, NULL, 0x18);
484         if (out_output->data == NULL) {
485                 return NT_STATUS_NO_MEMORY;
486         }
487
488         SIVAL(out_output->data, 0x00, conn->smb2.server.capabilities);
489         memcpy(out_output->data+0x04, out_guid_blob.data, 16);
490         SIVAL(out_output->data, 0x14, conn->smb2.server.security_mode);
491         SIVAL(out_output->data, 0x16, conn->smb2.server.dialect);
492
493         return NT_STATUS_OK;
494 }
495
496 static NTSTATUS fsctl_srv_req_resume_key(TALLOC_CTX *mem_ctx,
497                                          struct tevent_context *ev,
498                                          struct files_struct *fsp,
499                                          uint32_t in_max_output,
500                                          DATA_BLOB *out_output)
501 {
502         struct req_resume_key_rsp rkey_rsp;
503         enum ndr_err_code ndr_ret;
504         DATA_BLOB output;
505
506         if (fsp == NULL) {
507                 return NT_STATUS_FILE_CLOSED;
508         }
509
510         ZERO_STRUCT(rkey_rsp);
511         /* combine persistent and volatile handles for the resume key */
512         SBVAL(rkey_rsp.resume_key, 0, fsp->op->global->open_persistent_id);
513         SBVAL(rkey_rsp.resume_key, 8, fsp->op->global->open_volatile_id);
514
515         ndr_ret = ndr_push_struct_blob(&output, mem_ctx, &rkey_rsp,
516                         (ndr_push_flags_fn_t)ndr_push_req_resume_key_rsp);
517         if (ndr_ret != NDR_ERR_SUCCESS) {
518                 return NT_STATUS_INTERNAL_ERROR;
519         }
520
521         if (in_max_output < output.length) {
522                 DEBUG(1, ("max output %u too small for resume key rsp %ld\n",
523                           in_max_output, (long int)output.length));
524                 return NT_STATUS_INVALID_PARAMETER;
525         }
526         *out_output = output;
527
528         return NT_STATUS_OK;
529 }
530
531 static void smb2_ioctl_network_fs_copychunk_done(struct tevent_req *subreq);
532
533 struct tevent_req *smb2_ioctl_network_fs(uint32_t ctl_code,
534                                          struct tevent_context *ev,
535                                          struct tevent_req *req,
536                                          struct smbd_smb2_ioctl_state *state)
537 {
538         struct tevent_req *subreq;
539         NTSTATUS status;
540
541         switch (ctl_code) {
542         case FSCTL_SRV_COPYCHUNK:
543                 subreq = fsctl_srv_copychunk_send(state, ev, state->fsp,
544                                                   &state->in_input,
545                                                   state->in_max_output,
546                                                   state->smb2req);
547                 if (tevent_req_nomem(subreq, req)) {
548                         return tevent_req_post(req, ev);
549                 }
550                 tevent_req_set_callback(subreq,
551                                         smb2_ioctl_network_fs_copychunk_done,
552                                         req);
553                 return req;
554                 break;
555         case FSCTL_VALIDATE_NEGOTIATE_INFO:
556                 status = fsctl_validate_neg_info(state, ev,
557                                                  state->smbreq->sconn->conn,
558                                                  &state->in_input,
559                                                  state->in_max_output,
560                                                  &state->out_output,
561                                                  &state->disconnect);
562                 if (!tevent_req_nterror(req, status)) {
563                         tevent_req_done(req);
564                 }
565                 return tevent_req_post(req, ev);
566                 break;
567         case FSCTL_SRV_REQUEST_RESUME_KEY:
568                 status = fsctl_srv_req_resume_key(state, ev, state->fsp,
569                                                   state->in_max_output,
570                                                   &state->out_output);
571                 if (!tevent_req_nterror(req, status)) {
572                         tevent_req_done(req);
573                 }
574                 return tevent_req_post(req, ev);
575                 break;
576         default: {
577                 uint8_t *out_data = NULL;
578                 uint32_t out_data_len = 0;
579
580                 if (state->fsp == NULL) {
581                         status = NT_STATUS_NOT_SUPPORTED;
582                 } else {
583                         status = SMB_VFS_FSCTL(state->fsp,
584                                                state,
585                                                ctl_code,
586                                                state->smbreq->flags2,
587                                                state->in_input.data,
588                                                state->in_input.length,
589                                                &out_data,
590                                                state->in_max_output,
591                                                &out_data_len);
592                         state->out_output = data_blob_const(out_data, out_data_len);
593                         if (NT_STATUS_IS_OK(status)) {
594                                 tevent_req_done(req);
595                                 return tevent_req_post(req, ev);
596                         }
597                 }
598
599                 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
600                         if (IS_IPC(state->smbreq->conn)) {
601                                 status = NT_STATUS_FS_DRIVER_REQUIRED;
602                         } else {
603                                 status = NT_STATUS_INVALID_DEVICE_REQUEST;
604                         }
605                 }
606
607                 tevent_req_nterror(req, status);
608                 return tevent_req_post(req, ev);
609                 break;
610         }
611         }
612
613         tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
614         return tevent_req_post(req, ev);
615 }
616
617 static void smb2_ioctl_network_fs_copychunk_done(struct tevent_req *subreq)
618 {
619         struct tevent_req *req = tevent_req_callback_data(subreq,
620                                                           struct tevent_req);
621         struct smbd_smb2_ioctl_state *ioctl_state = tevent_req_data(req,
622                                                 struct smbd_smb2_ioctl_state);
623         struct srv_copychunk_rsp cc_rsp;
624         NTSTATUS status;
625         bool pack_rsp = false;
626
627         ZERO_STRUCT(cc_rsp);
628         status = fsctl_srv_copychunk_recv(subreq, &cc_rsp, &pack_rsp);
629         TALLOC_FREE(subreq);
630         if (pack_rsp == true) {
631                 enum ndr_err_code ndr_ret;
632                 ndr_ret = ndr_push_struct_blob(&ioctl_state->out_output,
633                                                ioctl_state,
634                                                &cc_rsp,
635                                 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_rsp);
636                 if (ndr_ret != NDR_ERR_SUCCESS) {
637                         status = NT_STATUS_INTERNAL_ERROR;
638                 }
639         }
640
641         if (!tevent_req_nterror(req, status)) {
642                 tevent_req_done(req);
643         }
644 }