torture: copychunk test suite improvements
[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_skip(torture, "FSCTL_SRV_ENUM_SNAPS not supported\n");
65         }
66         torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_ENUM_SNAPS");
67
68         return true;
69 }
70
71 /*
72    basic testing of the SMB2 server side copy ioctls
73 */
74 static bool test_ioctl_req_resume_key(struct torture_context *torture,
75                                       struct smb2_tree *tree)
76 {
77         struct smb2_handle h;
78         uint8_t buf[100];
79         NTSTATUS status;
80         union smb_ioctl ioctl;
81         TALLOC_CTX *tmp_ctx = talloc_new(tree);
82         struct req_resume_key_rsp res_key;
83         enum ndr_err_code ndr_ret;
84
85         smb2_util_unlink(tree, FNAME);
86
87         status = torture_smb2_testfile(tree, FNAME, &h);
88         torture_assert_ntstatus_ok(torture, status, "create write");
89
90         ZERO_ARRAY(buf);
91         status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf));
92         torture_assert_ntstatus_ok(torture, status, "write");
93
94         ZERO_STRUCT(ioctl);
95         ioctl.smb2.level = RAW_IOCTL_SMB2;
96         ioctl.smb2.in.file.handle = h;
97         ioctl.smb2.in.function = FSCTL_SRV_REQUEST_RESUME_KEY;
98         ioctl.smb2.in.max_response_size = 32;
99         ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
100
101         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
102         torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_REQUEST_RESUME_KEY");
103
104         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, &res_key,
105                         (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
106         torture_assert_ndr_success(torture, ndr_ret,
107                                    "ndr_pull_req_resume_key_rsp");
108
109         ndr_print_debug((ndr_print_fn_t)ndr_print_req_resume_key_rsp, "yo", &res_key);
110
111         talloc_free(tmp_ctx);
112         return true;
113 }
114
115 static uint64_t patt_hash(uint64_t off)
116 {
117         return off;
118 }
119
120 static bool check_pattern(struct torture_context *torture,
121                           struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
122                           struct smb2_handle h, uint64_t off, uint64_t len,
123                           uint64_t patt_off)
124 {
125         uint64_t i;
126         struct smb2_read r;
127         NTSTATUS status;
128
129         ZERO_STRUCT(r);
130         r.in.file.handle = h;
131         r.in.length      = len;
132         r.in.offset      = off;
133         status = smb2_read(tree, mem_ctx, &r);
134         torture_assert_ntstatus_ok(torture, status, "read");
135
136         torture_assert_u64_equal(torture, r.out.data.length, len,
137                                  "read data len mismatch");
138
139         for (i = 0; i <= len - 8; i += 8, patt_off += 8) {
140                 uint64_t data = BVAL(r.out.data.data, i);
141                 torture_assert_u64_equal(torture, data, patt_hash(patt_off),
142                                          talloc_asprintf(torture, "read data "
143                                                          "pattern bad at %llu\n",
144                                                          (unsigned long long)i));
145         }
146
147         talloc_free(r.out.data.data);
148         return true;
149 }
150
151 static bool test_setup_copy_chunk(struct torture_context *torture,
152                                   struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
153                                   uint32_t nchunks,
154                                   struct smb2_handle *src_h,
155                                   uint64_t src_size,
156                                   uint32_t src_desired_access,
157                                   struct smb2_handle *dest_h,
158                                   uint64_t dest_size,
159                                   uint32_t dest_desired_access,
160                                   struct srv_copychunk_copy *cc_copy,
161                                   union smb_ioctl *ioctl)
162 {
163         struct req_resume_key_rsp res_key;
164         struct smb2_create io;
165         NTSTATUS status;
166         enum ndr_err_code ndr_ret;
167         uint64_t i;
168         uint8_t *buf = talloc_zero_size(mem_ctx, MAX(src_size, dest_size));
169         torture_assert(torture, (buf != NULL), "no memory for file data buf");
170
171         smb2_util_unlink(tree, FNAME);
172         smb2_util_unlink(tree, FNAME2);
173
174         ZERO_STRUCT(io);
175         io.in.desired_access = src_desired_access;
176         io.in.file_attributes   = FILE_ATTRIBUTE_NORMAL;
177         io.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
178         io.in.share_access =
179                 NTCREATEX_SHARE_ACCESS_DELETE|
180                 NTCREATEX_SHARE_ACCESS_READ|
181                 NTCREATEX_SHARE_ACCESS_WRITE;
182         io.in.fname = FNAME;
183
184         status = smb2_create(tree, mem_ctx, &io);
185         torture_assert_ntstatus_ok(torture, status, "src create");
186
187         *src_h = io.out.file.handle;
188
189         if (src_size > 0) {
190                 uint64_t cur_off = 0;
191                 for (i = 0; i <= src_size - 8; i += 8) {
192                         SBVAL(buf, i, patt_hash(i));
193                 }
194                 while (src_size > 0) {
195                         uint64_t io_sz = MIN(1024 * 1024, src_size);
196                         status = smb2_util_write(tree, *src_h,
197                                                  buf + cur_off, cur_off, io_sz);
198                         torture_assert_ntstatus_ok(torture, status, "src write");
199
200                         src_size -= io_sz;
201                         cur_off += io_sz;
202                 }
203         }
204
205         ZERO_STRUCT(io);
206         io.in.desired_access = dest_desired_access;
207         io.in.file_attributes   = FILE_ATTRIBUTE_NORMAL;
208         io.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
209         io.in.share_access =
210                 NTCREATEX_SHARE_ACCESS_DELETE|
211                 NTCREATEX_SHARE_ACCESS_READ|
212                 NTCREATEX_SHARE_ACCESS_WRITE;
213         io.in.fname = FNAME2;
214
215         status = smb2_create(tree, mem_ctx, &io);
216         torture_assert_ntstatus_ok(torture, status, "dest create");
217
218         *dest_h = io.out.file.handle;
219
220         if (dest_size > 0) {
221                 uint64_t cur_off = 0;
222                 for (i = 0; i <= dest_size - 8; i += 8) {
223                         SBVAL(buf, i, patt_hash(i));
224                 }
225                 while (dest_size > 0) {
226                         uint64_t io_sz = MIN(1024 * 1024, dest_size);
227                         status = smb2_util_write(tree, *dest_h,
228                                                  buf + cur_off, cur_off, io_sz);
229                         torture_assert_ntstatus_ok(torture, status, "dest write");
230
231                         dest_size -= io_sz;
232                         cur_off += io_sz;
233                 }
234         }
235
236         ZERO_STRUCTPN(ioctl);
237         ioctl->smb2.level = RAW_IOCTL_SMB2;
238         ioctl->smb2.in.file.handle = *src_h;
239         ioctl->smb2.in.function = FSCTL_SRV_REQUEST_RESUME_KEY;
240         /* Allow for Key + ContextLength + Context */
241         ioctl->smb2.in.max_response_size = 32;
242         ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
243
244         status = smb2_ioctl(tree, mem_ctx, &ioctl->smb2);
245         torture_assert_ntstatus_ok(torture, status,
246                                    "FSCTL_SRV_REQUEST_RESUME_KEY");
247
248
249         ndr_ret = ndr_pull_struct_blob(&ioctl->smb2.out.out, mem_ctx, &res_key,
250                         (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
251
252         torture_assert_ndr_success(torture, ndr_ret,
253                                    "ndr_pull_req_resume_key_rsp");
254
255         ZERO_STRUCTPN(ioctl);
256         ioctl->smb2.level = RAW_IOCTL_SMB2;
257         ioctl->smb2.in.file.handle = *dest_h;
258         ioctl->smb2.in.function = FSCTL_SRV_COPYCHUNK;
259         ioctl->smb2.in.max_response_size = sizeof(struct srv_copychunk_rsp);
260         ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
261
262         ZERO_STRUCTPN(cc_copy);
263         memcpy(cc_copy->source_key, res_key.resume_key, ARRAY_SIZE(cc_copy->source_key));
264         cc_copy->chunk_count = nchunks;
265         cc_copy->chunks = talloc_zero_array(mem_ctx, struct srv_copychunk, nchunks);
266         torture_assert(torture, (cc_copy->chunks != NULL), "no memory for chunks");
267
268         return true;
269 }
270
271
272 static bool check_copy_chunk_rsp(struct torture_context *torture,
273                                  struct srv_copychunk_rsp *cc_rsp,
274                                  uint32_t ex_chunks_written,
275                                  uint32_t ex_chunk_bytes_written,
276                                  uint32_t ex_total_bytes_written)
277 {
278         torture_assert_int_equal(torture, cc_rsp->chunks_written,
279                                  ex_chunks_written, "num chunks");
280         torture_assert_int_equal(torture, cc_rsp->chunk_bytes_written,
281                                  ex_chunk_bytes_written, "chunk bytes written");
282         torture_assert_int_equal(torture, cc_rsp->total_bytes_written,
283                                  ex_total_bytes_written, "chunk total bytes");
284         return true;
285 }
286
287 static bool test_ioctl_copy_chunk_simple(struct torture_context *torture,
288                                          struct smb2_tree *tree)
289 {
290         struct smb2_handle src_h;
291         struct smb2_handle dest_h;
292         NTSTATUS status;
293         union smb_ioctl ioctl;
294         TALLOC_CTX *tmp_ctx = talloc_new(tree);
295         struct srv_copychunk_copy cc_copy;
296         struct srv_copychunk_rsp cc_rsp;
297         enum ndr_err_code ndr_ret;
298         bool ok;
299
300         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
301                                    1, /* 1 chunk */
302                                    &src_h, 4096, /* fill 4096 byte src file */
303                                    SEC_RIGHTS_FILE_ALL,
304                                    &dest_h, 0,  /* 0 byte dest file */
305                                    SEC_RIGHTS_FILE_ALL,
306                                    &cc_copy,
307                                    &ioctl);
308         if (!ok) {
309                 torture_fail(torture, "setup copy chunk error");
310         }
311
312         /* copy all src file data (via a single chunk desc) */
313         cc_copy.chunks[0].source_off = 0;
314         cc_copy.chunks[0].target_off = 0;
315         cc_copy.chunks[0].length = 4096;
316
317         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
318                                        &cc_copy,
319                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
320         torture_assert_ndr_success(torture, ndr_ret,
321                                    "ndr_push_srv_copychunk_copy");
322
323         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
324         torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
325
326         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
327                                        &cc_rsp,
328                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
329         torture_assert_ndr_success(torture, ndr_ret,
330                                    "ndr_pull_srv_copychunk_rsp");
331
332         ok = check_copy_chunk_rsp(torture, &cc_rsp,
333                                   1,    /* chunks written */
334                                   0,    /* chunk bytes unsuccessfully written */
335                                   4096); /* total bytes written */
336         if (!ok) {
337                 torture_fail(torture, "bad copy chunk response data");
338         }
339
340         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
341         if (!ok) {
342                 torture_fail(torture, "inconsistent file data");
343         }
344
345         smb2_util_close(tree, src_h);
346         smb2_util_close(tree, dest_h);
347         talloc_free(tmp_ctx);
348         return true;
349 }
350
351 static bool test_ioctl_copy_chunk_multi(struct torture_context *torture,
352                                         struct smb2_tree *tree)
353 {
354         struct smb2_handle src_h;
355         struct smb2_handle dest_h;
356         NTSTATUS status;
357         union smb_ioctl ioctl;
358         TALLOC_CTX *tmp_ctx = talloc_new(tree);
359         struct srv_copychunk_copy cc_copy;
360         struct srv_copychunk_rsp cc_rsp;
361         enum ndr_err_code ndr_ret;
362         bool ok;
363
364         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
365                                    2, /* chunks */
366                                    &src_h, 8192, /* src file */
367                                    SEC_RIGHTS_FILE_ALL,
368                                    &dest_h, 0,  /* dest file */
369                                    SEC_RIGHTS_FILE_ALL,
370                                    &cc_copy,
371                                    &ioctl);
372         if (!ok) {
373                 torture_fail(torture, "setup copy chunk error");
374         }
375
376         /* copy all src file data via two chunks */
377         cc_copy.chunks[0].source_off = 0;
378         cc_copy.chunks[0].target_off = 0;
379         cc_copy.chunks[0].length = 4096;
380
381         cc_copy.chunks[1].source_off = 4096;
382         cc_copy.chunks[1].target_off = 4096;
383         cc_copy.chunks[1].length = 4096;
384
385         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
386                                        &cc_copy,
387                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
388         torture_assert_ndr_success(torture, ndr_ret,
389                                    "ndr_push_srv_copychunk_copy");
390
391         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
392         torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
393
394         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
395                                        &cc_rsp,
396                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
397         torture_assert_ndr_success(torture, ndr_ret,
398                                    "ndr_pull_srv_copychunk_rsp");
399
400         ok = check_copy_chunk_rsp(torture, &cc_rsp,
401                                   2,    /* chunks written */
402                                   0,    /* chunk bytes unsuccessfully written */
403                                   8192);        /* total bytes written */
404         if (!ok) {
405                 torture_fail(torture, "bad copy chunk response data");
406         }
407
408         smb2_util_close(tree, src_h);
409         smb2_util_close(tree, dest_h);
410         talloc_free(tmp_ctx);
411         return true;
412 }
413
414 static bool test_ioctl_copy_chunk_tiny(struct torture_context *torture,
415                                        struct smb2_tree *tree)
416 {
417         struct smb2_handle src_h;
418         struct smb2_handle dest_h;
419         NTSTATUS status;
420         union smb_ioctl ioctl;
421         TALLOC_CTX *tmp_ctx = talloc_new(tree);
422         struct srv_copychunk_copy cc_copy;
423         struct srv_copychunk_rsp cc_rsp;
424         enum ndr_err_code ndr_ret;
425         bool ok;
426
427         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
428                                    2, /* chunks */
429                                    &src_h, 100, /* src file */
430                                    SEC_RIGHTS_FILE_ALL,
431                                    &dest_h, 0,  /* dest file */
432                                    SEC_RIGHTS_FILE_ALL,
433                                    &cc_copy,
434                                    &ioctl);
435         if (!ok) {
436                 torture_fail(torture, "setup copy chunk error");
437         }
438
439         /* copy all src file data via two chunks, sub block size chunks */
440         cc_copy.chunks[0].source_off = 0;
441         cc_copy.chunks[0].target_off = 0;
442         cc_copy.chunks[0].length = 50;
443
444         cc_copy.chunks[1].source_off = 50;
445         cc_copy.chunks[1].target_off = 50;
446         cc_copy.chunks[1].length = 50;
447
448         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
449                                        &cc_copy,
450                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
451         torture_assert_ndr_success(torture, ndr_ret,
452                                    "ndr_push_srv_copychunk_copy");
453
454         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
455         torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
456
457         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
458                                        &cc_rsp,
459                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
460         torture_assert_ndr_success(torture, ndr_ret,
461                                    "ndr_pull_srv_copychunk_rsp");
462
463         ok = check_copy_chunk_rsp(torture, &cc_rsp,
464                                   2,    /* chunks written */
465                                   0,    /* chunk bytes unsuccessfully written */
466                                   100); /* total bytes written */
467         if (!ok) {
468                 torture_fail(torture, "bad copy chunk response data");
469         }
470
471         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 100, 0);
472         if (!ok) {
473                 torture_fail(torture, "inconsistent file data");
474         }
475
476         smb2_util_close(tree, src_h);
477         smb2_util_close(tree, dest_h);
478         talloc_free(tmp_ctx);
479         return true;
480 }
481
482 static bool test_ioctl_copy_chunk_over(struct torture_context *torture,
483                                        struct smb2_tree *tree)
484 {
485         struct smb2_handle src_h;
486         struct smb2_handle dest_h;
487         NTSTATUS status;
488         union smb_ioctl ioctl;
489         TALLOC_CTX *tmp_ctx = talloc_new(tree);
490         struct srv_copychunk_copy cc_copy;
491         struct srv_copychunk_rsp cc_rsp;
492         enum ndr_err_code ndr_ret;
493         bool ok;
494
495         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
496                                    2, /* chunks */
497                                    &src_h, 8192, /* src file */
498                                    SEC_RIGHTS_FILE_ALL,
499                                    &dest_h, 4096, /* dest file */
500                                    SEC_RIGHTS_FILE_ALL,
501                                    &cc_copy,
502                                    &ioctl);
503         if (!ok) {
504                 torture_fail(torture, "setup copy chunk error");
505         }
506
507         /* first chunk overwrites existing dest data */
508         cc_copy.chunks[0].source_off = 0;
509         cc_copy.chunks[0].target_off = 0;
510         cc_copy.chunks[0].length = 4096;
511
512         /* second chunk overwrites the first */
513         cc_copy.chunks[1].source_off = 4096;
514         cc_copy.chunks[1].target_off = 0;
515         cc_copy.chunks[1].length = 4096;
516
517         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
518                                        &cc_copy,
519                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
520         torture_assert_ndr_success(torture, ndr_ret,
521                                    "ndr_push_srv_copychunk_copy");
522
523         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
524         torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
525
526         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
527                                        &cc_rsp,
528                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
529         torture_assert_ndr_success(torture, ndr_ret,
530                                    "ndr_pull_srv_copychunk_rsp");
531
532         ok = check_copy_chunk_rsp(torture, &cc_rsp,
533                                   2,    /* chunks written */
534                                   0,    /* chunk bytes unsuccessfully written */
535                                   8192); /* total bytes written */
536         if (!ok) {
537                 torture_fail(torture, "bad copy chunk response data");
538         }
539
540         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 4096);
541         if (!ok) {
542                 torture_fail(torture, "inconsistent file data");
543         }
544
545         smb2_util_close(tree, src_h);
546         smb2_util_close(tree, dest_h);
547         talloc_free(tmp_ctx);
548         return true;
549 }
550
551 static bool test_ioctl_copy_chunk_append(struct torture_context *torture,
552                                        struct smb2_tree *tree)
553 {
554         struct smb2_handle src_h;
555         struct smb2_handle dest_h;
556         NTSTATUS status;
557         union smb_ioctl ioctl;
558         TALLOC_CTX *tmp_ctx = talloc_new(tree);
559         struct srv_copychunk_copy cc_copy;
560         struct srv_copychunk_rsp cc_rsp;
561         enum ndr_err_code ndr_ret;
562         bool ok;
563
564         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
565                                    2, /* chunks */
566                                    &src_h, 4096, /* src file */
567                                    SEC_RIGHTS_FILE_ALL,
568                                    &dest_h, 0,  /* dest file */
569                                    SEC_RIGHTS_FILE_ALL,
570                                    &cc_copy,
571                                    &ioctl);
572         if (!ok) {
573                 torture_fail(torture, "setup copy chunk error");
574         }
575
576         cc_copy.chunks[0].source_off = 0;
577         cc_copy.chunks[0].target_off = 0;
578         cc_copy.chunks[0].length = 4096;
579
580         /* second chunk appends the same data to the first */
581         cc_copy.chunks[1].source_off = 0;
582         cc_copy.chunks[1].target_off = 4096;
583         cc_copy.chunks[1].length = 4096;
584
585         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
586                                        &cc_copy,
587                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
588         torture_assert_ndr_success(torture, ndr_ret,
589                                    "ndr_push_srv_copychunk_copy");
590
591         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
592         torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
593
594         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
595                                        &cc_rsp,
596                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
597         torture_assert_ndr_success(torture, ndr_ret,
598                                    "ndr_pull_srv_copychunk_rsp");
599
600         ok = check_copy_chunk_rsp(torture, &cc_rsp,
601                                   2,    /* chunks written */
602                                   0,    /* chunk bytes unsuccessfully written */
603                                   8192); /* total bytes written */
604         if (!ok) {
605                 torture_fail(torture, "bad copy chunk response data");
606         }
607
608         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
609         if (!ok) {
610                 torture_fail(torture, "inconsistent file data");
611         }
612
613         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 4096, 4096, 0);
614         if (!ok) {
615                 torture_fail(torture, "inconsistent file data");
616         }
617
618         smb2_util_close(tree, src_h);
619         smb2_util_close(tree, dest_h);
620         talloc_free(tmp_ctx);
621         return true;
622 }
623
624 static bool test_ioctl_copy_chunk_limits(struct torture_context *torture,
625                                          struct smb2_tree *tree)
626 {
627         struct smb2_handle src_h;
628         struct smb2_handle dest_h;
629         NTSTATUS status;
630         union smb_ioctl ioctl;
631         TALLOC_CTX *tmp_ctx = talloc_new(tree);
632         struct srv_copychunk_copy cc_copy;
633         struct srv_copychunk_rsp cc_rsp;
634         enum ndr_err_code ndr_ret;
635         bool ok;
636
637         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
638                                    1, /* chunks */
639                                    &src_h, 4096, /* src file */
640                                    SEC_RIGHTS_FILE_ALL,
641                                    &dest_h, 0,  /* dest file */
642                                    SEC_RIGHTS_FILE_ALL,
643                                    &cc_copy,
644                                    &ioctl);
645         if (!ok) {
646                 torture_fail(torture, "setup copy chunk error");
647         }
648
649         /* send huge chunk length request */
650         cc_copy.chunks[0].source_off = 0;
651         cc_copy.chunks[0].target_off = 0;
652         cc_copy.chunks[0].length = UINT_MAX;
653
654         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
655                                        &cc_copy,
656                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
657         torture_assert_ndr_success(torture, ndr_ret, "marshalling request");
658
659         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
660         torture_assert_ntstatus_equal(torture, status,
661                                       NT_STATUS_INVALID_PARAMETER,
662                                       "bad oversize chunk response");
663
664         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
665                                        &cc_rsp,
666                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
667         torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
668
669         torture_comment(torture, "limit max chunks, got %u\n",
670                         cc_rsp.chunks_written);
671         torture_comment(torture, "limit max chunk len, got %u\n",
672                         cc_rsp.chunk_bytes_written);
673         torture_comment(torture, "limit max total bytes, got %u\n",
674                         cc_rsp.total_bytes_written);
675
676         smb2_util_close(tree, src_h);
677         smb2_util_close(tree, dest_h);
678         talloc_free(tmp_ctx);
679         return true;
680 }
681
682 static bool test_ioctl_copy_chunk_src_lck(struct torture_context *torture,
683                                           struct smb2_tree *tree)
684 {
685         struct smb2_handle src_h;
686         struct smb2_handle src_h2;
687         struct smb2_handle dest_h;
688         NTSTATUS status;
689         union smb_ioctl ioctl;
690         TALLOC_CTX *tmp_ctx = talloc_new(tree);
691         struct srv_copychunk_copy cc_copy;
692         struct srv_copychunk_rsp cc_rsp;
693         enum ndr_err_code ndr_ret;
694         bool ok;
695         struct smb2_lock lck;
696         struct smb2_lock_element el[1];
697
698         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
699                                    1, /* chunks */
700                                    &src_h, 4096, /* src file */
701                                    SEC_RIGHTS_FILE_ALL,
702                                    &dest_h, 0,  /* dest file */
703                                    SEC_RIGHTS_FILE_ALL,
704                                    &cc_copy,
705                                    &ioctl);
706         if (!ok) {
707                 torture_fail(torture, "setup copy chunk error");
708         }
709
710         cc_copy.chunks[0].source_off = 0;
711         cc_copy.chunks[0].target_off = 0;
712         cc_copy.chunks[0].length = 4096;
713
714         /* open and lock the copychunk src file */
715         status = torture_smb2_testfile(tree, FNAME, &src_h2);
716         torture_assert_ntstatus_ok(torture, status, "2nd src open");
717
718         lck.in.lock_count       = 0x0001;
719         lck.in.lock_sequence    = 0x00000000;
720         lck.in.file.handle      = src_h2;
721         lck.in.locks            = el;
722         el[0].offset            = cc_copy.chunks[0].source_off;
723         el[0].length            = cc_copy.chunks[0].length;
724         el[0].reserved          = 0;
725         el[0].flags             = SMB2_LOCK_FLAG_EXCLUSIVE;
726
727         status = smb2_lock(tree, &lck);
728         torture_assert_ntstatus_ok(torture, status, "lock");
729
730         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
731                                        &cc_copy,
732                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
733         torture_assert_ndr_success(torture, ndr_ret,
734                                    "ndr_push_srv_copychunk_copy");
735
736         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
737         /*
738          * 2k12 & Samba return lock_conflict, Windows 7 & 2k8 return success...
739          *
740          * Edgar Olougouna @ MS wrote:
741          * Regarding the FSCTL_SRV_COPYCHUNK and STATUS_FILE_LOCK_CONFLICT
742          * discrepancy observed between Windows versions, we confirm that the
743          * behavior change is expected.
744          *
745          * CopyChunk in Windows Server 2012 use regular Readfile/Writefile APIs
746          * to move the chunks from the source to the destination.
747          * These ReadFile/WriteFile APIs go through the byte-range lock checks,
748          * and this explains the observed STATUS_FILE_LOCK_CONFLICT error.
749          *
750          * Prior to Windows Server 2012, CopyChunk used mapped sections to move
751          * the data. And byte range locks are not enforced on mapped I/O, and
752          * this explains the STATUS_SUCCESS observed on Windows Server 2008 R2.
753          */
754         torture_assert_ntstatus_equal(torture, status,
755                                       NT_STATUS_FILE_LOCK_CONFLICT,
756                                       "FSCTL_SRV_COPYCHUNK locked");
757
758         /* should get cc response data with the lock conflict status */
759         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
760                                        &cc_rsp,
761                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
762         torture_assert_ndr_success(torture, ndr_ret,
763                                    "ndr_pull_srv_copychunk_rsp");
764         ok = check_copy_chunk_rsp(torture, &cc_rsp,
765                                   0,    /* chunks written */
766                                   0,    /* chunk bytes unsuccessfully written */
767                                   0);   /* total bytes written */
768
769         lck.in.lock_count       = 0x0001;
770         lck.in.lock_sequence    = 0x00000001;
771         lck.in.file.handle      = src_h2;
772         lck.in.locks            = el;
773         el[0].offset            = cc_copy.chunks[0].source_off;
774         el[0].length            = cc_copy.chunks[0].length;
775         el[0].reserved          = 0;
776         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
777         status = smb2_lock(tree, &lck);
778         torture_assert_ntstatus_ok(torture, status, "unlock");
779
780         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
781         torture_assert_ntstatus_ok(torture, status,
782                                    "FSCTL_SRV_COPYCHUNK unlocked");
783
784         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
785                                        &cc_rsp,
786                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
787         torture_assert_ndr_success(torture, ndr_ret,
788                                    "ndr_pull_srv_copychunk_rsp");
789
790         ok = check_copy_chunk_rsp(torture, &cc_rsp,
791                                   1,    /* chunks written */
792                                   0,    /* chunk bytes unsuccessfully written */
793                                   4096); /* total bytes written */
794         if (!ok) {
795                 torture_fail(torture, "bad copy chunk response data");
796         }
797
798         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
799         if (!ok) {
800                 torture_fail(torture, "inconsistent file data");
801         }
802
803         smb2_util_close(tree, src_h2);
804         smb2_util_close(tree, src_h);
805         smb2_util_close(tree, dest_h);
806         talloc_free(tmp_ctx);
807         return true;
808 }
809
810 static bool test_ioctl_copy_chunk_dest_lck(struct torture_context *torture,
811                                            struct smb2_tree *tree)
812 {
813         struct smb2_handle src_h;
814         struct smb2_handle dest_h;
815         struct smb2_handle dest_h2;
816         NTSTATUS status;
817         union smb_ioctl ioctl;
818         TALLOC_CTX *tmp_ctx = talloc_new(tree);
819         struct srv_copychunk_copy cc_copy;
820         struct srv_copychunk_rsp cc_rsp;
821         enum ndr_err_code ndr_ret;
822         bool ok;
823         struct smb2_lock lck;
824         struct smb2_lock_element el[1];
825
826         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
827                                    1, /* chunks */
828                                    &src_h, 4096, /* src file */
829                                    SEC_RIGHTS_FILE_ALL,
830                                    &dest_h, 4096,       /* dest file */
831                                    SEC_RIGHTS_FILE_ALL,
832                                    &cc_copy,
833                                    &ioctl);
834         if (!ok) {
835                 torture_fail(torture, "setup copy chunk error");
836         }
837
838         cc_copy.chunks[0].source_off = 0;
839         cc_copy.chunks[0].target_off = 0;
840         cc_copy.chunks[0].length = 4096;
841
842         /* open and lock the copychunk dest file */
843         status = torture_smb2_testfile(tree, FNAME2, &dest_h2);
844         torture_assert_ntstatus_ok(torture, status, "2nd src open");
845
846         lck.in.lock_count       = 0x0001;
847         lck.in.lock_sequence    = 0x00000000;
848         lck.in.file.handle      = dest_h2;
849         lck.in.locks            = el;
850         el[0].offset            = cc_copy.chunks[0].target_off;
851         el[0].length            = cc_copy.chunks[0].length;
852         el[0].reserved          = 0;
853         el[0].flags             = SMB2_LOCK_FLAG_EXCLUSIVE;
854
855         status = smb2_lock(tree, &lck);
856         torture_assert_ntstatus_ok(torture, status, "lock");
857
858         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
859                                        &cc_copy,
860                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
861         torture_assert_ndr_success(torture, ndr_ret,
862                                    "ndr_push_srv_copychunk_copy");
863
864         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
865         torture_assert_ntstatus_equal(torture, status,
866                                       NT_STATUS_FILE_LOCK_CONFLICT,
867                                       "FSCTL_SRV_COPYCHUNK locked");
868
869         lck.in.lock_count       = 0x0001;
870         lck.in.lock_sequence    = 0x00000001;
871         lck.in.file.handle      = dest_h2;
872         lck.in.locks            = el;
873         el[0].offset            = cc_copy.chunks[0].target_off;
874         el[0].length            = cc_copy.chunks[0].length;
875         el[0].reserved          = 0;
876         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
877         status = smb2_lock(tree, &lck);
878         torture_assert_ntstatus_ok(torture, status, "unlock");
879
880         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
881         torture_assert_ntstatus_ok(torture, status,
882                                    "FSCTL_SRV_COPYCHUNK unlocked");
883
884         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
885                                        &cc_rsp,
886                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
887         torture_assert_ndr_success(torture, ndr_ret,
888                                    "ndr_pull_srv_copychunk_rsp");
889
890         ok = check_copy_chunk_rsp(torture, &cc_rsp,
891                                   1,    /* chunks written */
892                                   0,    /* chunk bytes unsuccessfully written */
893                                   4096); /* total bytes written */
894         if (!ok) {
895                 torture_fail(torture, "bad copy chunk response data");
896         }
897
898         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
899         if (!ok) {
900                 torture_fail(torture, "inconsistent file data");
901         }
902
903         smb2_util_close(tree, dest_h2);
904         smb2_util_close(tree, src_h);
905         smb2_util_close(tree, dest_h);
906         talloc_free(tmp_ctx);
907         return true;
908 }
909
910 static bool test_ioctl_copy_chunk_bad_key(struct torture_context *torture,
911                                           struct smb2_tree *tree)
912 {
913         struct smb2_handle src_h;
914         struct smb2_handle dest_h;
915         NTSTATUS status;
916         union smb_ioctl ioctl;
917         TALLOC_CTX *tmp_ctx = talloc_new(tree);
918         struct srv_copychunk_copy cc_copy;
919         enum ndr_err_code ndr_ret;
920         bool ok;
921
922         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
923                                    1,
924                                    &src_h, 4096,
925                                    SEC_RIGHTS_FILE_ALL,
926                                    &dest_h, 0,
927                                    SEC_RIGHTS_FILE_ALL,
928                                    &cc_copy,
929                                    &ioctl);
930         if (!ok) {
931                 torture_fail(torture, "setup copy chunk error");
932         }
933
934         /* overwrite the resume key with a bogus value */
935         memcpy(cc_copy.source_key, "deadbeefdeadbeefdeadbeef", 24);
936
937         cc_copy.chunks[0].source_off = 0;
938         cc_copy.chunks[0].target_off = 0;
939         cc_copy.chunks[0].length = 4096;
940
941         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
942                                        &cc_copy,
943                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
944         torture_assert_ndr_success(torture, ndr_ret,
945                                    "ndr_push_srv_copychunk_copy");
946
947         /* Server 2k12 returns NT_STATUS_OBJECT_NAME_NOT_FOUND */
948         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
949         torture_assert_ntstatus_equal(torture, status,
950                                       NT_STATUS_OBJECT_NAME_NOT_FOUND,
951                                       "FSCTL_SRV_COPYCHUNK");
952
953         smb2_util_close(tree, src_h);
954         smb2_util_close(tree, dest_h);
955         talloc_free(tmp_ctx);
956         return true;
957 }
958
959 static bool test_ioctl_copy_chunk_src_is_dest(struct torture_context *torture,
960                                               struct smb2_tree *tree)
961 {
962         struct smb2_handle src_h;
963         struct smb2_handle dest_h;
964         NTSTATUS status;
965         union smb_ioctl ioctl;
966         TALLOC_CTX *tmp_ctx = talloc_new(tree);
967         struct srv_copychunk_copy cc_copy;
968         struct srv_copychunk_rsp cc_rsp;
969         enum ndr_err_code ndr_ret;
970         bool ok;
971
972         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
973                                    1,
974                                    &src_h, 8192,
975                                    SEC_RIGHTS_FILE_ALL,
976                                    &dest_h, 0,
977                                    SEC_RIGHTS_FILE_ALL,
978                                    &cc_copy,
979                                    &ioctl);
980         if (!ok) {
981                 torture_fail(torture, "setup copy chunk error");
982         }
983
984         /* the source is also the destination */
985         ioctl.smb2.in.file.handle = src_h;
986
987         /* non-overlapping */
988         cc_copy.chunks[0].source_off = 0;
989         cc_copy.chunks[0].target_off = 4096;
990         cc_copy.chunks[0].length = 4096;
991
992         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
993                                        &cc_copy,
994                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
995         torture_assert_ndr_success(torture, ndr_ret,
996                                    "ndr_push_srv_copychunk_copy");
997
998         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
999         torture_assert_ntstatus_ok(torture, status,
1000                                    "FSCTL_SRV_COPYCHUNK");
1001
1002         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1003                                        &cc_rsp,
1004                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1005         torture_assert_ndr_success(torture, ndr_ret,
1006                                    "ndr_pull_srv_copychunk_rsp");
1007
1008         ok = check_copy_chunk_rsp(torture, &cc_rsp,
1009                                   1,    /* chunks written */
1010                                   0,    /* chunk bytes unsuccessfully written */
1011                                   4096); /* total bytes written */
1012         if (!ok) {
1013                 torture_fail(torture, "bad copy chunk response data");
1014         }
1015
1016         ok = check_pattern(torture, tree, tmp_ctx, src_h, 0, 4096, 0);
1017         if (!ok) {
1018                 torture_fail(torture, "inconsistent file data");
1019         }
1020         ok = check_pattern(torture, tree, tmp_ctx, src_h, 4096, 4096, 0);
1021         if (!ok) {
1022                 torture_fail(torture, "inconsistent file data");
1023         }
1024
1025         smb2_util_close(tree, src_h);
1026         smb2_util_close(tree, dest_h);
1027         talloc_free(tmp_ctx);
1028         return true;
1029 }
1030
1031 /*
1032  * Test a single-chunk copychunk request, where the source and target ranges
1033  * overlap, and the SourceKey refers to the same target file. E.g:
1034  *
1035  * Initial State
1036  * -------------
1037  *      File:           src_and_dest
1038  *      Offset:         0123456789
1039  *      Data:           abcdefghij
1040  *
1041  * Request
1042  * -------
1043  *      FSCTL_SRV_COPYCHUNK(src_and_dest)
1044  *      SourceKey = SRV_REQUEST_RESUME_KEY(src_and_dest)
1045  *      ChunkCount = 1
1046  *      Chunks[0].SourceOffset = 0
1047  *      Chunks[0].TargetOffset = 4
1048  *      Chunks[0].Length = 6
1049  *
1050  * Resultant State
1051  * ---------------
1052  *      File:           src_and_dest
1053  *      Offset:         0123456789
1054  *      Data:           abcdabcdef
1055  *
1056  * The resultant contents of src_and_dest is dependent on the server's
1057  * copy algorithm. In the above example, the server uses an IO buffer
1058  * large enough to hold the entire six-byte source data before writing
1059  * to TargetOffset. If the server were to use a four-byte IO buffer and
1060  * started reads/writes from the lowest offset, then the two overlapping
1061  * bytes in the above example would be overwritten before being read. The
1062  * resultant file contents would be abcdabcdab.
1063  *
1064  * Windows 2008r2 appears to use a 2048 byte copy buffer, overlapping bytes
1065  * after this offset are written before being read. Windows 2012 on the
1066  * other hand appears to use a buffer large enough to hold its maximum
1067  * supported chunk size (1M). Samba currently uses a 64k copy buffer by
1068  * default (vfs_cc_state.buf).
1069  *
1070  * This test uses an 8-byte overlap at 2040-2048, so that it passes against
1071  * Windows 2008, 2012 and Samba servers.
1072  */
1073 static bool
1074 test_ioctl_copy_chunk_src_is_dest_overlap(struct torture_context *torture,
1075                                           struct smb2_tree *tree)
1076 {
1077         struct smb2_handle src_h;
1078         struct smb2_handle dest_h;
1079         NTSTATUS status;
1080         union smb_ioctl ioctl;
1081         TALLOC_CTX *tmp_ctx = talloc_new(tree);
1082         struct srv_copychunk_copy cc_copy;
1083         struct srv_copychunk_rsp cc_rsp;
1084         enum ndr_err_code ndr_ret;
1085         bool ok;
1086
1087         /* exceed the vfs_default copy buffer */
1088         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1089                                    1,
1090                                    &src_h, 2048 * 2,
1091                                    SEC_RIGHTS_FILE_ALL,
1092                                    &dest_h, 0,
1093                                    SEC_RIGHTS_FILE_ALL,
1094                                    &cc_copy,
1095                                    &ioctl);
1096         if (!ok) {
1097                 torture_fail(torture, "setup copy chunk error");
1098         }
1099
1100         /* the source is also the destination */
1101         ioctl.smb2.in.file.handle = src_h;
1102
1103         /* 8 bytes overlap between source and target ranges */
1104         cc_copy.chunks[0].source_off = 0;
1105         cc_copy.chunks[0].target_off = 2048 - 8;
1106         cc_copy.chunks[0].length = 2048;
1107
1108         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1109                                        &cc_copy,
1110                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1111         torture_assert_ndr_success(torture, ndr_ret,
1112                                    "ndr_push_srv_copychunk_copy");
1113
1114         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1115         torture_assert_ntstatus_ok(torture, status,
1116                                    "FSCTL_SRV_COPYCHUNK");
1117
1118         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1119                                        &cc_rsp,
1120                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1121         torture_assert_ndr_success(torture, ndr_ret,
1122                                    "ndr_pull_srv_copychunk_rsp");
1123
1124         ok = check_copy_chunk_rsp(torture, &cc_rsp,
1125                                   1,    /* chunks written */
1126                                   0,    /* chunk bytes unsuccessfully written */
1127                                   2048); /* total bytes written */
1128         if (!ok) {
1129                 torture_fail(torture, "bad copy chunk response data");
1130         }
1131
1132         ok = check_pattern(torture, tree, tmp_ctx, src_h, 0, 2048 - 8, 0);
1133         if (!ok) {
1134                 torture_fail(torture, "inconsistent file data");
1135         }
1136         ok = check_pattern(torture, tree, tmp_ctx, src_h, 2048 - 8, 2048, 0);
1137         if (!ok) {
1138                 torture_fail(torture, "inconsistent file data");
1139         }
1140
1141         smb2_util_close(tree, src_h);
1142         smb2_util_close(tree, dest_h);
1143         talloc_free(tmp_ctx);
1144         return true;
1145 }
1146
1147 static bool test_ioctl_copy_chunk_bad_access(struct torture_context *torture,
1148                                              struct smb2_tree *tree)
1149 {
1150         struct smb2_handle src_h;
1151         struct smb2_handle dest_h;
1152         NTSTATUS status;
1153         union smb_ioctl ioctl;
1154         TALLOC_CTX *tmp_ctx = talloc_new(tree);
1155         struct srv_copychunk_copy cc_copy;
1156         enum ndr_err_code ndr_ret;
1157         bool ok;
1158
1159         /* no read permission on src */
1160         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1161                                    1, /* 1 chunk */
1162                                    &src_h, 4096, /* fill 4096 byte src file */
1163                                    SEC_RIGHTS_FILE_WRITE,
1164                                    &dest_h, 0,  /* 0 byte dest file */
1165                                    SEC_RIGHTS_FILE_ALL,
1166                                    &cc_copy,
1167                                    &ioctl);
1168         if (!ok) {
1169                 torture_fail(torture, "setup copy chunk error");
1170         }
1171
1172         cc_copy.chunks[0].source_off = 0;
1173         cc_copy.chunks[0].target_off = 0;
1174         cc_copy.chunks[0].length = 4096;
1175
1176         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1177                                        &cc_copy,
1178                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1179         torture_assert_ndr_success(torture, ndr_ret,
1180                                    "ndr_push_srv_copychunk_copy");
1181
1182         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1183         torture_assert_ntstatus_equal(torture, status,
1184                                       NT_STATUS_ACCESS_DENIED,
1185                                       "FSCTL_SRV_COPYCHUNK");
1186
1187         smb2_util_close(tree, src_h);
1188         smb2_util_close(tree, dest_h);
1189
1190         /* no write permission on dest */
1191         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1192                                    1, /* 1 chunk */
1193                                    &src_h, 4096, /* fill 4096 byte src file */
1194                                    SEC_RIGHTS_FILE_ALL,
1195                                    &dest_h, 0,  /* 0 byte dest file */
1196                                    (SEC_RIGHTS_FILE_READ
1197                                     | SEC_RIGHTS_FILE_EXECUTE),
1198                                    &cc_copy,
1199                                    &ioctl);
1200         if (!ok) {
1201                 torture_fail(torture, "setup copy chunk error");
1202         }
1203
1204         cc_copy.chunks[0].source_off = 0;
1205         cc_copy.chunks[0].target_off = 0;
1206         cc_copy.chunks[0].length = 4096;
1207
1208         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1209                                        &cc_copy,
1210                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1211         torture_assert_ndr_success(torture, ndr_ret,
1212                                    "ndr_push_srv_copychunk_copy");
1213
1214         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1215         torture_assert_ntstatus_equal(torture, status,
1216                                       NT_STATUS_ACCESS_DENIED,
1217                                       "FSCTL_SRV_COPYCHUNK");
1218
1219         smb2_util_close(tree, src_h);
1220         smb2_util_close(tree, dest_h);
1221
1222         /* no read permission on dest */
1223         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1224                                    1, /* 1 chunk */
1225                                    &src_h, 4096, /* fill 4096 byte src file */
1226                                    SEC_RIGHTS_FILE_ALL,
1227                                    &dest_h, 0,  /* 0 byte dest file */
1228                                    (SEC_RIGHTS_FILE_WRITE
1229                                     | SEC_RIGHTS_FILE_EXECUTE),
1230                                    &cc_copy,
1231                                    &ioctl);
1232         if (!ok) {
1233                 torture_fail(torture, "setup copy chunk error");
1234         }
1235
1236         cc_copy.chunks[0].source_off = 0;
1237         cc_copy.chunks[0].target_off = 0;
1238         cc_copy.chunks[0].length = 4096;
1239
1240         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1241                                        &cc_copy,
1242                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1243         torture_assert_ndr_success(torture, ndr_ret,
1244                                    "ndr_push_srv_copychunk_copy");
1245
1246         /*
1247          * FSCTL_SRV_COPYCHUNK requires read permission on dest,
1248          * FSCTL_SRV_COPYCHUNK_WRITE (not supported by Samba) on the other hand
1249          * does not.
1250          */
1251         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1252         torture_assert_ntstatus_equal(torture, status,
1253                                       NT_STATUS_ACCESS_DENIED,
1254                                       "FSCTL_SRV_COPYCHUNK");
1255
1256         smb2_util_close(tree, src_h);
1257         smb2_util_close(tree, dest_h);
1258         talloc_free(tmp_ctx);
1259
1260         return true;
1261 }
1262
1263 static bool test_ioctl_copy_chunk_src_exceed(struct torture_context *torture,
1264                                              struct smb2_tree *tree)
1265 {
1266         struct smb2_handle src_h;
1267         struct smb2_handle dest_h;
1268         NTSTATUS status;
1269         union smb_ioctl ioctl;
1270         TALLOC_CTX *tmp_ctx = talloc_new(tree);
1271         struct srv_copychunk_copy cc_copy;
1272         struct srv_copychunk_rsp cc_rsp;
1273         enum ndr_err_code ndr_ret;
1274         bool ok;
1275
1276         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1277                                    1, /* 1 chunk */
1278                                    &src_h, 4096, /* fill 4096 byte src file */
1279                                    SEC_RIGHTS_FILE_ALL,
1280                                    &dest_h, 0,  /* 0 byte dest file */
1281                                    SEC_RIGHTS_FILE_ALL,
1282                                    &cc_copy,
1283                                    &ioctl);
1284         if (!ok) {
1285                 torture_fail(torture, "setup copy chunk error");
1286         }
1287
1288         /* Request copy where off + length exceeds size of src */
1289         cc_copy.chunks[0].source_off = 1024;
1290         cc_copy.chunks[0].target_off = 0;
1291         cc_copy.chunks[0].length = 4096;
1292
1293         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1294                                        &cc_copy,
1295                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1296         torture_assert_ndr_success(torture, ndr_ret,
1297                                    "ndr_push_srv_copychunk_copy");
1298
1299         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1300         torture_assert_ntstatus_equal(torture, status,
1301                                       NT_STATUS_INVALID_VIEW_SIZE,
1302                                       "FSCTL_SRV_COPYCHUNK oversize");
1303
1304         /* Request copy where length exceeds size of src */
1305         cc_copy.chunks[0].source_off = 1024;
1306         cc_copy.chunks[0].target_off = 0;
1307         cc_copy.chunks[0].length = 3072;
1308
1309         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1310                                        &cc_copy,
1311                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1312         torture_assert_ndr_success(torture, ndr_ret,
1313                                    "ndr_push_srv_copychunk_copy");
1314
1315         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1316         torture_assert_ntstatus_ok(torture, status,
1317                                    "FSCTL_SRV_COPYCHUNK just right");
1318
1319         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1320                                        &cc_rsp,
1321                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1322         torture_assert_ndr_success(torture, ndr_ret,
1323                                    "ndr_pull_srv_copychunk_rsp");
1324
1325         ok = check_copy_chunk_rsp(torture, &cc_rsp,
1326                                   1,    /* chunks written */
1327                                   0,    /* chunk bytes unsuccessfully written */
1328                                   3072); /* total bytes written */
1329         if (!ok) {
1330                 torture_fail(torture, "bad copy chunk response data");
1331         }
1332
1333         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 3072, 1024);
1334         if (!ok) {
1335                 torture_fail(torture, "inconsistent file data");
1336         }
1337
1338         smb2_util_close(tree, src_h);
1339         smb2_util_close(tree, dest_h);
1340         talloc_free(tmp_ctx);
1341         return true;
1342 }
1343
1344 static bool
1345 test_ioctl_copy_chunk_src_exceed_multi(struct torture_context *torture,
1346                                        struct smb2_tree *tree)
1347 {
1348         struct smb2_handle src_h;
1349         struct smb2_handle dest_h;
1350         NTSTATUS status;
1351         union smb_ioctl ioctl;
1352         TALLOC_CTX *tmp_ctx = talloc_new(tree);
1353         struct srv_copychunk_copy cc_copy;
1354         struct srv_copychunk_rsp cc_rsp;
1355         enum ndr_err_code ndr_ret;
1356         bool ok;
1357
1358         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1359                                    2, /* 2 chunks */
1360                                    &src_h, 8192, /* fill 8192 byte src file */
1361                                    SEC_RIGHTS_FILE_ALL,
1362                                    &dest_h, 0,  /* 0 byte dest file */
1363                                    SEC_RIGHTS_FILE_ALL,
1364                                    &cc_copy,
1365                                    &ioctl);
1366         if (!ok) {
1367                 torture_fail(torture, "setup copy chunk error");
1368         }
1369
1370         /* Request copy where off + length exceeds size of src */
1371         cc_copy.chunks[0].source_off = 0;
1372         cc_copy.chunks[0].target_off = 0;
1373         cc_copy.chunks[0].length = 4096;
1374
1375         cc_copy.chunks[1].source_off = 4096;
1376         cc_copy.chunks[1].target_off = 4096;
1377         cc_copy.chunks[1].length = 8192;
1378
1379         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1380                                        &cc_copy,
1381                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1382         torture_assert_ndr_success(torture, ndr_ret,
1383                                    "ndr_push_srv_copychunk_copy");
1384
1385         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1386         torture_assert_ntstatus_equal(torture, status,
1387                                       NT_STATUS_INVALID_VIEW_SIZE,
1388                                       "FSCTL_SRV_COPYCHUNK oversize");
1389         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1390                                        &cc_rsp,
1391                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1392         torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
1393
1394         /* first chunk should still be written */
1395         ok = check_copy_chunk_rsp(torture, &cc_rsp,
1396                                   1,    /* chunks written */
1397                                   0,    /* chunk bytes unsuccessfully written */
1398                                   4096); /* total bytes written */
1399         if (!ok) {
1400                 torture_fail(torture, "bad copy chunk response data");
1401         }
1402         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
1403         if (!ok) {
1404                 torture_fail(torture, "inconsistent file data");
1405         }
1406
1407         smb2_util_close(tree, src_h);
1408         smb2_util_close(tree, dest_h);
1409         talloc_free(tmp_ctx);
1410         return true;
1411 }
1412
1413 static bool test_ioctl_copy_chunk_sparse_dest(struct torture_context *torture,
1414                                               struct smb2_tree *tree)
1415 {
1416         struct smb2_handle src_h;
1417         struct smb2_handle dest_h;
1418         NTSTATUS status;
1419         union smb_ioctl ioctl;
1420         struct smb2_read r;
1421         TALLOC_CTX *tmp_ctx = talloc_new(tree);
1422         struct srv_copychunk_copy cc_copy;
1423         struct srv_copychunk_rsp cc_rsp;
1424         enum ndr_err_code ndr_ret;
1425         bool ok;
1426         int i;
1427
1428         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1429                                    1, /* 1 chunk */
1430                                    &src_h, 4096, /* fill 4096 byte src file */
1431                                    SEC_RIGHTS_FILE_ALL,
1432                                    &dest_h, 0,  /* 0 byte dest file */
1433                                    SEC_RIGHTS_FILE_ALL,
1434                                    &cc_copy,
1435                                    &ioctl);
1436         if (!ok) {
1437                 torture_fail(torture, "setup copy chunk error");
1438         }
1439
1440         /* copy all src file data (via a single chunk desc) */
1441         cc_copy.chunks[0].source_off = 0;
1442         cc_copy.chunks[0].target_off = 4096;
1443         cc_copy.chunks[0].length = 4096;
1444
1445         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1446                                        &cc_copy,
1447                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1448         torture_assert_ndr_success(torture, ndr_ret,
1449                                    "ndr_push_srv_copychunk_copy");
1450
1451         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1452         torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
1453
1454         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1455                                        &cc_rsp,
1456                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1457         torture_assert_ndr_success(torture, ndr_ret,
1458                                    "ndr_pull_srv_copychunk_rsp");
1459
1460         ok = check_copy_chunk_rsp(torture, &cc_rsp,
1461                                   1,    /* chunks written */
1462                                   0,    /* chunk bytes unsuccessfully written */
1463                                   4096); /* total bytes written */
1464         if (!ok) {
1465                 torture_fail(torture, "bad copy chunk response data");
1466         }
1467
1468         /* check for zeros in first 4k */
1469         ZERO_STRUCT(r);
1470         r.in.file.handle = dest_h;
1471         r.in.length      = 4096;
1472         r.in.offset      = 0;
1473         status = smb2_read(tree, tmp_ctx, &r);
1474         torture_assert_ntstatus_ok(torture, status, "read");
1475
1476         torture_assert_u64_equal(torture, r.out.data.length, 4096,
1477                                  "read data len mismatch");
1478
1479         for (i = 0; i < 4096; i++) {
1480                 torture_assert(torture, (r.out.data.data[i] == 0),
1481                                "sparse did not pass class");
1482         }
1483
1484         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 4096, 4096, 0);
1485         if (!ok) {
1486                 torture_fail(torture, "inconsistent file data");
1487         }
1488
1489         smb2_util_close(tree, src_h);
1490         smb2_util_close(tree, dest_h);
1491         talloc_free(tmp_ctx);
1492         return true;
1493 }
1494
1495 /*
1496  * set the ioctl MaxOutputResponse size to less than
1497  * sizeof(struct srv_copychunk_rsp)
1498  */
1499 static bool test_ioctl_copy_chunk_max_output_sz(struct torture_context *torture,
1500                                                 struct smb2_tree *tree)
1501 {
1502         struct smb2_handle src_h;
1503         struct smb2_handle dest_h;
1504         NTSTATUS status;
1505         union smb_ioctl ioctl;
1506         TALLOC_CTX *tmp_ctx = talloc_new(tree);
1507         struct srv_copychunk_copy cc_copy;
1508         enum ndr_err_code ndr_ret;
1509         bool ok;
1510
1511         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1512                                    1, /* 1 chunk */
1513                                    &src_h, 4096, /* fill 4096 byte src file */
1514                                    SEC_RIGHTS_FILE_ALL,
1515                                    &dest_h, 0,  /* 0 byte dest file */
1516                                    SEC_RIGHTS_FILE_ALL,
1517                                    &cc_copy,
1518                                    &ioctl);
1519         if (!ok) {
1520                 torture_fail(torture, "setup copy chunk error");
1521         }
1522
1523         cc_copy.chunks[0].source_off = 0;
1524         cc_copy.chunks[0].target_off = 0;
1525         cc_copy.chunks[0].length = 4096;
1526         /* req is valid, but use undersize max_response_size */
1527         ioctl.smb2.in.max_response_size = sizeof(struct srv_copychunk_rsp) - 1;
1528
1529         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1530                                        &cc_copy,
1531                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1532         torture_assert_ndr_success(torture, ndr_ret,
1533                                    "ndr_push_srv_copychunk_copy");
1534
1535         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1536         torture_assert_ntstatus_equal(torture, status,
1537                                       NT_STATUS_INVALID_PARAMETER,
1538                                       "FSCTL_SRV_COPYCHUNK");
1539
1540         smb2_util_close(tree, src_h);
1541         smb2_util_close(tree, dest_h);
1542         talloc_free(tmp_ctx);
1543         return true;
1544 }
1545
1546 /*
1547    basic testing of SMB2 ioctls
1548 */
1549 struct torture_suite *torture_smb2_ioctl_init(void)
1550 {
1551         struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "ioctl");
1552
1553         torture_suite_add_1smb2_test(suite, "shadow_copy",
1554                                      test_ioctl_get_shadow_copy);
1555         torture_suite_add_1smb2_test(suite, "req_resume_key",
1556                                      test_ioctl_req_resume_key);
1557         torture_suite_add_1smb2_test(suite, "copy_chunk_simple",
1558                                      test_ioctl_copy_chunk_simple);
1559         torture_suite_add_1smb2_test(suite, "copy_chunk_multi",
1560                                      test_ioctl_copy_chunk_multi);
1561         torture_suite_add_1smb2_test(suite, "copy_chunk_tiny",
1562                                      test_ioctl_copy_chunk_tiny);
1563         torture_suite_add_1smb2_test(suite, "copy_chunk_overwrite",
1564                                      test_ioctl_copy_chunk_over);
1565         torture_suite_add_1smb2_test(suite, "copy_chunk_append",
1566                                      test_ioctl_copy_chunk_append);
1567         torture_suite_add_1smb2_test(suite, "copy_chunk_limits",
1568                                      test_ioctl_copy_chunk_limits);
1569         torture_suite_add_1smb2_test(suite, "copy_chunk_src_lock",
1570                                      test_ioctl_copy_chunk_src_lck);
1571         torture_suite_add_1smb2_test(suite, "copy_chunk_dest_lock",
1572                                      test_ioctl_copy_chunk_dest_lck);
1573         torture_suite_add_1smb2_test(suite, "copy_chunk_bad_key",
1574                                      test_ioctl_copy_chunk_bad_key);
1575         torture_suite_add_1smb2_test(suite, "copy_chunk_src_is_dest",
1576                                      test_ioctl_copy_chunk_src_is_dest);
1577         torture_suite_add_1smb2_test(suite, "copy_chunk_src_is_dest_overlap",
1578                                      test_ioctl_copy_chunk_src_is_dest_overlap);
1579         torture_suite_add_1smb2_test(suite, "copy_chunk_bad_access",
1580                                      test_ioctl_copy_chunk_bad_access);
1581         torture_suite_add_1smb2_test(suite, "copy_chunk_src_exceed",
1582                                      test_ioctl_copy_chunk_src_exceed);
1583         torture_suite_add_1smb2_test(suite, "copy_chunk_src_exceed_multi",
1584                                      test_ioctl_copy_chunk_src_exceed_multi);
1585         torture_suite_add_1smb2_test(suite, "copy_chunk_sparse_dest",
1586                                      test_ioctl_copy_chunk_sparse_dest);
1587         torture_suite_add_1smb2_test(suite, "copy_chunk_max_output_sz",
1588                                      test_ioctl_copy_chunk_max_output_sz);
1589
1590         suite->description = talloc_strdup(suite, "SMB2-IOCTL tests");
1591
1592         return suite;
1593 }
1594