8c949ef3009e763fdd10930a18d458bbf972cd30
[kai/samba.git] / source4 / torture / smb2 / ioctl.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    test suite for SMB2 ioctl operations
5
6    Copyright (C) David Disseldorp 2011
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 "librpc/gen_ndr/security.h"
24 #include "libcli/smb2/smb2.h"
25 #include "libcli/smb2/smb2_calls.h"
26 #include "torture/torture.h"
27 #include "torture/smb2/proto.h"
28 #include "librpc/gen_ndr/ndr_ioctl.h"
29
30 #define FNAME   "testfsctl.dat"
31 #define FNAME2  "testfsctl2.dat"
32
33 /*
34    basic testing of SMB2 shadow copy calls
35 */
36 static bool test_ioctl_get_shadow_copy(struct torture_context *torture,
37                                        struct smb2_tree *tree)
38 {
39         struct smb2_handle h;
40         uint8_t buf[100];
41         NTSTATUS status;
42         union smb_ioctl ioctl;
43         TALLOC_CTX *tmp_ctx = talloc_new(tree);
44
45         smb2_util_unlink(tree, FNAME);
46
47         status = torture_smb2_testfile(tree, FNAME, &h);
48         torture_assert_ntstatus_ok(torture, status, "create write");
49
50         ZERO_ARRAY(buf);
51         status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf));
52         torture_assert_ntstatus_ok(torture, status, "write");
53
54         ZERO_STRUCT(ioctl);
55         ioctl.smb2.level = RAW_IOCTL_SMB2;
56         ioctl.smb2.in.file.handle = h;
57         ioctl.smb2.in.function = FSCTL_SRV_ENUM_SNAPS;
58         ioctl.smb2.in.max_response_size = 16;
59         ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
60
61         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
62         if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)
63          || NT_STATUS_EQUAL(status, NT_STATUS_INVALID_DEVICE_REQUEST)) {
64                 torture_comment(torture,
65                         "FSCTL_SRV_ENUM_SNAPS not supported, skipping\n");
66                 return true;
67         }
68         torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_ENUM_SNAPS");
69
70         return true;
71 }
72
73 /*
74    basic testing of the SMB2 server side copy ioctls
75 */
76 static bool test_ioctl_req_resume_key(struct torture_context *torture,
77                                       struct smb2_tree *tree)
78 {
79         struct smb2_handle h;
80         uint8_t buf[100];
81         NTSTATUS status;
82         union smb_ioctl ioctl;
83         TALLOC_CTX *tmp_ctx = talloc_new(tree);
84         struct req_resume_key_rsp res_key;
85         enum ndr_err_code ndr_ret;
86
87         smb2_util_unlink(tree, FNAME);
88
89         status = torture_smb2_testfile(tree, FNAME, &h);
90         torture_assert_ntstatus_ok(torture, status, "create write");
91
92         ZERO_ARRAY(buf);
93         status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf));
94         torture_assert_ntstatus_ok(torture, status, "write");
95
96         ZERO_STRUCT(ioctl);
97         ioctl.smb2.level = RAW_IOCTL_SMB2;
98         ioctl.smb2.in.file.handle = h;
99         ioctl.smb2.in.function = FSCTL_SRV_REQUEST_RESUME_KEY;
100         ioctl.smb2.in.max_response_size = 32;
101         ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
102
103         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
104         torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_REQUEST_RESUME_KEY");
105
106         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, &res_key,
107                         (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
108         torture_assert_ndr_success(torture, ndr_ret,
109                                    "ndr_pull_req_resume_key_rsp");
110
111         ndr_print_debug((ndr_print_fn_t)ndr_print_req_resume_key_rsp, "yo", &res_key);
112
113         talloc_free(tmp_ctx);
114         return true;
115 }
116
117 static uint64_t patt_hash(uint64_t off)
118 {
119         return off;
120 }
121
122 static bool check_pattern(struct torture_context *torture,
123                           struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
124                           struct smb2_handle h, uint64_t off, uint64_t len,
125                           uint64_t patt_off)
126 {
127         uint64_t i;
128         struct smb2_read r;
129         NTSTATUS status;
130
131         ZERO_STRUCT(r);
132         r.in.file.handle = h;
133         r.in.length      = len;
134         r.in.offset      = off;
135         status = smb2_read(tree, mem_ctx, &r);
136         torture_assert_ntstatus_ok(torture, status, "read");
137
138         torture_assert_u64_equal(torture, r.out.data.length, len,
139                                  "read data len mismatch");
140
141         for (i = 0; i <= len - 8; i += 8, patt_off += 8) {
142                 uint64_t data = BVAL(r.out.data.data, i);
143                 torture_assert_u64_equal(torture, data, patt_hash(patt_off),
144                                          talloc_asprintf(torture, "read data "
145                                                          "pattern bad at %llu\n",
146                                                          (unsigned long long)i));
147         }
148
149         talloc_free(r.out.data.data);
150         return true;
151 }
152
153 static bool test_setup_copy_chunk(struct torture_context *torture,
154                                   struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
155                                   uint32_t nchunks,
156                                   struct smb2_handle *src_h,
157                                   uint64_t src_size,
158                                   struct smb2_handle *dest_h,
159                                   uint64_t dest_size,
160                                   struct srv_copychunk_copy *cc_copy,
161                                   union smb_ioctl *ioctl)
162 {
163         struct req_resume_key_rsp res_key;
164         NTSTATUS status;
165         enum ndr_err_code ndr_ret;
166         uint64_t i;
167         uint8_t *buf = talloc_zero_size(mem_ctx, MAX(src_size, dest_size));
168         if (buf == NULL) {
169                 printf("no mem for file data buffer\n");
170                 return false;
171         }
172
173         smb2_util_unlink(tree, FNAME);
174         smb2_util_unlink(tree, FNAME2);
175
176         status = torture_smb2_testfile(tree, FNAME, src_h);
177         torture_assert_ntstatus_ok(torture, status, "create write");
178
179         if (src_size > 0) {
180                 for (i = 0; i <= src_size - 8; i += 8) {
181                         SBVAL(buf, i, patt_hash(i));
182                 }
183                 status = smb2_util_write(tree, *src_h, buf, 0, src_size);
184                 torture_assert_ntstatus_ok(torture, status, "src write");
185         }
186
187         status = torture_smb2_testfile(tree, FNAME2, dest_h);
188         torture_assert_ntstatus_ok(torture, status, "create write");
189
190         if (dest_size > 0) {
191                 for (i = 0; i <= dest_size - 8; i += 8) {
192                         SBVAL(buf, i, patt_hash(i));
193                 }
194                 status = smb2_util_write(tree, *dest_h, buf, 0, dest_size);
195                 torture_assert_ntstatus_ok(torture, status, "dest write");
196         }
197
198         ZERO_STRUCTPN(ioctl);
199         ioctl->smb2.level = RAW_IOCTL_SMB2;
200         ioctl->smb2.in.file.handle = *src_h;
201         ioctl->smb2.in.function = FSCTL_SRV_REQUEST_RESUME_KEY;
202         /* Allow for Key + ContextLength + Context */
203         ioctl->smb2.in.max_response_size = 32;
204         ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
205
206         status = smb2_ioctl(tree, mem_ctx, &ioctl->smb2);
207         torture_assert_ntstatus_ok(torture, status,
208                                    "FSCTL_SRV_REQUEST_RESUME_KEY");
209
210
211         ndr_ret = ndr_pull_struct_blob(&ioctl->smb2.out.out, mem_ctx, &res_key,
212                         (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
213
214         torture_assert_ndr_success(torture, ndr_ret,
215                                    "ndr_pull_req_resume_key_rsp");
216
217         ZERO_STRUCTPN(ioctl);
218         ioctl->smb2.level = RAW_IOCTL_SMB2;
219         ioctl->smb2.in.file.handle = *dest_h;
220         ioctl->smb2.in.function = FSCTL_SRV_COPYCHUNK;
221         ioctl->smb2.in.max_response_size = sizeof(struct srv_copychunk_rsp);
222         ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
223
224         ZERO_STRUCTPN(cc_copy);
225         memcpy(cc_copy->source_key, res_key.resume_key, ARRAY_SIZE(cc_copy->source_key));
226         cc_copy->chunk_count = nchunks;
227         cc_copy->chunks = talloc_zero_array(mem_ctx, struct srv_copychunk, nchunks);
228         if (cc_copy->chunks == NULL) {
229                 printf("not enough memory to allocate %u chunks\n", nchunks);
230                 return false;
231         }
232
233         return true;
234 }
235
236
237 static bool check_copy_chunk_rsp(struct torture_context *torture,
238                                  struct srv_copychunk_rsp *cc_rsp,
239                                  uint32_t ex_chunks_written,
240                                  uint32_t ex_chunk_bytes_written,
241                                  uint32_t ex_total_bytes_written)
242 {
243         torture_assert_int_equal(torture, cc_rsp->chunks_written,
244                                  ex_chunks_written, "num chunks");
245         torture_assert_int_equal(torture, cc_rsp->chunk_bytes_written,
246                                  ex_chunk_bytes_written, "chunk bytes written");
247         torture_assert_int_equal(torture, cc_rsp->total_bytes_written,
248                                  ex_total_bytes_written, "chunk total bytes");
249         return true;
250 }
251
252 static bool test_ioctl_copy_chunk_simple(struct torture_context *torture,
253                                          struct smb2_tree *tree)
254 {
255         struct smb2_handle src_h;
256         struct smb2_handle dest_h;
257         NTSTATUS status;
258         union smb_ioctl ioctl;
259         TALLOC_CTX *tmp_ctx = talloc_new(tree);
260         struct srv_copychunk_copy cc_copy;
261         struct srv_copychunk_rsp cc_rsp;
262         enum ndr_err_code ndr_ret;
263         bool ok;
264
265         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
266                                    1, /* 1 chunk */
267                                    &src_h, 4096, /* fill 4096 byte src file */
268                                    &dest_h, 0,  /* 0 byte dest file */
269                                    &cc_copy,
270                                    &ioctl);
271         if (!ok) {
272                 return false;
273         }
274
275         /* copy all src file data (via a single chunk desc) */
276         cc_copy.chunks[0].source_off = 0;
277         cc_copy.chunks[0].target_off = 0;
278         cc_copy.chunks[0].length = 4096;
279
280         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
281                                        &cc_copy,
282                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
283         torture_assert_ndr_success(torture, ndr_ret,
284                                    "ndr_push_srv_copychunk_copy");
285
286         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
287         torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
288
289         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
290                                        &cc_rsp,
291                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
292         torture_assert_ndr_success(torture, ndr_ret,
293                                    "ndr_pull_srv_copychunk_rsp");
294
295         ok = check_copy_chunk_rsp(torture, &cc_rsp,
296                                   1,    /* chunks written */
297                                   0,    /* chunk bytes unsuccessfully written */
298                                   4096); /* total bytes written */
299         if (!ok) {
300                 return false;
301         }
302
303         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
304         if (!ok) {
305                 return false;
306         }
307
308         smb2_util_close(tree, src_h);
309         smb2_util_close(tree, dest_h);
310         talloc_free(tmp_ctx);
311         return true;
312 }
313
314 static bool test_ioctl_copy_chunk_multi(struct torture_context *torture,
315                                         struct smb2_tree *tree)
316 {
317         struct smb2_handle src_h;
318         struct smb2_handle dest_h;
319         NTSTATUS status;
320         union smb_ioctl ioctl;
321         TALLOC_CTX *tmp_ctx = talloc_new(tree);
322         struct srv_copychunk_copy cc_copy;
323         struct srv_copychunk_rsp cc_rsp;
324         enum ndr_err_code ndr_ret;
325         bool ok;
326
327         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
328                                    2, /* chunks */
329                                    &src_h, 8192, /* src file */
330                                    &dest_h, 0,  /* dest file */
331                                    &cc_copy,
332                                    &ioctl);
333         if (!ok) {
334                 return false;
335         }
336
337         /* copy all src file data via two chunks */
338         cc_copy.chunks[0].source_off = 0;
339         cc_copy.chunks[0].target_off = 0;
340         cc_copy.chunks[0].length = 4096;
341
342         cc_copy.chunks[1].source_off = 4096;
343         cc_copy.chunks[1].target_off = 4096;
344         cc_copy.chunks[1].length = 4096;
345
346         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
347                                        &cc_copy,
348                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
349         torture_assert_ndr_success(torture, ndr_ret,
350                                    "ndr_push_srv_copychunk_copy");
351
352         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
353         torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
354
355         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
356                                        &cc_rsp,
357                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
358         torture_assert_ndr_success(torture, ndr_ret,
359                                    "ndr_pull_srv_copychunk_rsp");
360
361         ok = check_copy_chunk_rsp(torture, &cc_rsp,
362                                   2,    /* chunks written */
363                                   0,    /* chunk bytes unsuccessfully written */
364                                   8192);        /* total bytes written */
365         if (!ok) {
366                 return false;
367         }
368
369         smb2_util_close(tree, src_h);
370         smb2_util_close(tree, dest_h);
371         talloc_free(tmp_ctx);
372         return true;
373 }
374
375 static bool test_ioctl_copy_chunk_tiny(struct torture_context *torture,
376                                        struct smb2_tree *tree)
377 {
378         struct smb2_handle src_h;
379         struct smb2_handle dest_h;
380         NTSTATUS status;
381         union smb_ioctl ioctl;
382         TALLOC_CTX *tmp_ctx = talloc_new(tree);
383         struct srv_copychunk_copy cc_copy;
384         struct srv_copychunk_rsp cc_rsp;
385         enum ndr_err_code ndr_ret;
386         bool ok;
387
388         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
389                                    2, /* chunks */
390                                    &src_h, 100, /* src file */
391                                    &dest_h, 0,  /* dest file */
392                                    &cc_copy,
393                                    &ioctl);
394         if (!ok) {
395                 return false;
396         }
397
398         /* copy all src file data via two chunks, sub block size chunks */
399         cc_copy.chunks[0].source_off = 0;
400         cc_copy.chunks[0].target_off = 0;
401         cc_copy.chunks[0].length = 50;
402
403         cc_copy.chunks[1].source_off = 50;
404         cc_copy.chunks[1].target_off = 50;
405         cc_copy.chunks[1].length = 50;
406
407         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
408                                        &cc_copy,
409                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
410         torture_assert_ndr_success(torture, ndr_ret,
411                                    "ndr_push_srv_copychunk_copy");
412
413         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
414         torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
415
416         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
417                                        &cc_rsp,
418                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
419         torture_assert_ndr_success(torture, ndr_ret,
420                                    "ndr_pull_srv_copychunk_rsp");
421
422         ok = check_copy_chunk_rsp(torture, &cc_rsp,
423                                   2,    /* chunks written */
424                                   0,    /* chunk bytes unsuccessfully written */
425                                   100); /* total bytes written */
426         if (!ok) {
427                 return false;
428         }
429
430         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 100, 0);
431         if (!ok) {
432                 return false;
433         }
434
435         smb2_util_close(tree, src_h);
436         smb2_util_close(tree, dest_h);
437         talloc_free(tmp_ctx);
438         return true;
439 }
440
441 static bool test_ioctl_copy_chunk_over(struct torture_context *torture,
442                                        struct smb2_tree *tree)
443 {
444         struct smb2_handle src_h;
445         struct smb2_handle dest_h;
446         NTSTATUS status;
447         union smb_ioctl ioctl;
448         TALLOC_CTX *tmp_ctx = talloc_new(tree);
449         struct srv_copychunk_copy cc_copy;
450         struct srv_copychunk_rsp cc_rsp;
451         enum ndr_err_code ndr_ret;
452         bool ok;
453
454         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
455                                    2, /* chunks */
456                                    &src_h, 8192, /* src file */
457                                    &dest_h, 4096, /* dest file */
458                                    &cc_copy,
459                                    &ioctl);
460         if (!ok) {
461                 return false;
462         }
463
464         /* first chunk overwrites existing dest data */
465         cc_copy.chunks[0].source_off = 0;
466         cc_copy.chunks[0].target_off = 0;
467         cc_copy.chunks[0].length = 4096;
468
469         /* second chunk overwrites the first */
470         cc_copy.chunks[1].source_off = 4096;
471         cc_copy.chunks[1].target_off = 0;
472         cc_copy.chunks[1].length = 4096;
473
474         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
475                                        &cc_copy,
476                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
477         torture_assert_ndr_success(torture, ndr_ret,
478                                    "ndr_push_srv_copychunk_copy");
479
480         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
481         torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
482
483         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
484                                        &cc_rsp,
485                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
486         torture_assert_ndr_success(torture, ndr_ret,
487                                    "ndr_pull_srv_copychunk_rsp");
488
489         ok = check_copy_chunk_rsp(torture, &cc_rsp,
490                                   2,    /* chunks written */
491                                   0,    /* chunk bytes unsuccessfully written */
492                                   8192); /* total bytes written */
493         if (!ok) {
494                 return false;
495         }
496
497         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 4096);
498         if (!ok) {
499                 return false;
500         }
501
502         smb2_util_close(tree, src_h);
503         smb2_util_close(tree, dest_h);
504         talloc_free(tmp_ctx);
505         return true;
506 }
507
508 static bool test_ioctl_copy_chunk_append(struct torture_context *torture,
509                                        struct smb2_tree *tree)
510 {
511         struct smb2_handle src_h;
512         struct smb2_handle dest_h;
513         NTSTATUS status;
514         union smb_ioctl ioctl;
515         TALLOC_CTX *tmp_ctx = talloc_new(tree);
516         struct srv_copychunk_copy cc_copy;
517         struct srv_copychunk_rsp cc_rsp;
518         enum ndr_err_code ndr_ret;
519         bool ok;
520
521         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
522                                    2, /* chunks */
523                                    &src_h, 4096, /* src file */
524                                    &dest_h, 0,  /* dest file */
525                                    &cc_copy,
526                                    &ioctl);
527         if (!ok) {
528                 return false;
529         }
530
531         cc_copy.chunks[0].source_off = 0;
532         cc_copy.chunks[0].target_off = 0;
533         cc_copy.chunks[0].length = 4096;
534
535         /* second chunk appends the same data to the first */
536         cc_copy.chunks[1].source_off = 0;
537         cc_copy.chunks[1].target_off = 4096;
538         cc_copy.chunks[1].length = 4096;
539
540         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
541                                        &cc_copy,
542                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
543         torture_assert_ndr_success(torture, ndr_ret,
544                                    "ndr_push_srv_copychunk_copy");
545
546         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
547         torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
548
549         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
550                                        &cc_rsp,
551                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
552         torture_assert_ndr_success(torture, ndr_ret,
553                                    "ndr_pull_srv_copychunk_rsp");
554
555         ok = check_copy_chunk_rsp(torture, &cc_rsp,
556                                   2,    /* chunks written */
557                                   0,    /* chunk bytes unsuccessfully written */
558                                   8192); /* total bytes written */
559         if (!ok) {
560                 return false;
561         }
562
563         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
564         if (!ok) {
565                 return false;
566         }
567
568         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 4096, 4096, 0);
569         if (!ok) {
570                 return false;
571         }
572
573         smb2_util_close(tree, src_h);
574         smb2_util_close(tree, dest_h);
575         talloc_free(tmp_ctx);
576         return true;
577 }
578
579 static bool test_ioctl_copy_chunk_limits(struct torture_context *torture,
580                                          struct smb2_tree *tree)
581 {
582         struct smb2_handle src_h;
583         struct smb2_handle dest_h;
584         NTSTATUS status;
585         union smb_ioctl ioctl;
586         TALLOC_CTX *tmp_ctx = talloc_new(tree);
587         struct srv_copychunk_copy cc_copy;
588         struct srv_copychunk_rsp cc_rsp;
589         enum ndr_err_code ndr_ret;
590         bool ok;
591
592         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
593                                    1, /* chunks */
594                                    &src_h, 4096, /* src file */
595                                    &dest_h, 0,  /* dest file */
596                                    &cc_copy,
597                                    &ioctl);
598         if (!ok) {
599                 return false;
600         }
601
602         /* send huge chunk length request */
603         cc_copy.chunks[0].source_off = 0;
604         cc_copy.chunks[0].target_off = 0;
605         cc_copy.chunks[0].length = UINT_MAX;
606
607         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
608                                        &cc_copy,
609                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
610         torture_assert_ndr_success(torture, ndr_ret, "marshalling request");
611
612         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
613         torture_assert_ntstatus_equal(torture, status,
614                                       NT_STATUS_INVALID_PARAMETER,
615                                       "bad oversize chunk response");
616
617         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
618                                        &cc_rsp,
619                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
620         torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
621
622         torture_comment(torture, "limit max chunks, got %u\n",
623                         cc_rsp.chunks_written);
624         torture_comment(torture, "limit max chunk len, got %u\n",
625                         cc_rsp.chunk_bytes_written);
626         torture_comment(torture, "limit max total bytes, got %u\n",
627                         cc_rsp.total_bytes_written);
628
629         smb2_util_close(tree, src_h);
630         smb2_util_close(tree, dest_h);
631         talloc_free(tmp_ctx);
632         return true;
633 }
634
635 /*
636    basic testing of SMB2 ioctls
637 */
638 struct torture_suite *torture_smb2_ioctl_init(void)
639 {
640         struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "ioctl");
641
642         torture_suite_add_1smb2_test(suite, "shadow_copy",
643                                      test_ioctl_get_shadow_copy);
644         torture_suite_add_1smb2_test(suite, "req_resume_key",
645                                      test_ioctl_req_resume_key);
646         torture_suite_add_1smb2_test(suite, "copy_chunk_simple",
647                                      test_ioctl_copy_chunk_simple);
648         torture_suite_add_1smb2_test(suite, "copy_chunk_multi",
649                                      test_ioctl_copy_chunk_multi);
650         torture_suite_add_1smb2_test(suite, "copy_chunk_tiny",
651                                      test_ioctl_copy_chunk_tiny);
652         torture_suite_add_1smb2_test(suite, "copy_chunk_overwrite",
653                                      test_ioctl_copy_chunk_over);
654         torture_suite_add_1smb2_test(suite, "copy_chunk_append",
655                                      test_ioctl_copy_chunk_append);
656         torture_suite_add_1smb2_test(suite, "copy_chunk_limits",
657                                      test_ioctl_copy_chunk_limits);
658
659         suite->description = talloc_strdup(suite, "SMB2-IOCTL tests");
660
661         return suite;
662 }
663