torture: add locking tests for copychunk
[obnox/samba/samba-obnox.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 static bool test_ioctl_copy_chunk_src_lck(struct torture_context *torture,
636                                           struct smb2_tree *tree)
637 {
638         struct smb2_handle src_h;
639         struct smb2_handle src_h2;
640         struct smb2_handle dest_h;
641         NTSTATUS status;
642         union smb_ioctl ioctl;
643         TALLOC_CTX *tmp_ctx = talloc_new(tree);
644         struct srv_copychunk_copy cc_copy;
645         struct srv_copychunk_rsp cc_rsp;
646         enum ndr_err_code ndr_ret;
647         bool ok;
648         struct smb2_lock lck;
649         struct smb2_lock_element el[1];
650
651         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
652                                    1, /* chunks */
653                                    &src_h, 4096, /* src file */
654                                    &dest_h, 0,  /* dest file */
655                                    &cc_copy,
656                                    &ioctl);
657         if (!ok) {
658                 return false;
659         }
660
661         cc_copy.chunks[0].source_off = 0;
662         cc_copy.chunks[0].target_off = 0;
663         cc_copy.chunks[0].length = 4096;
664
665         /* open and lock the copychunk src file */
666         status = torture_smb2_testfile(tree, FNAME, &src_h2);
667         torture_assert_ntstatus_ok(torture, status, "2nd src open");
668
669         lck.in.lock_count       = 0x0001;
670         lck.in.lock_sequence    = 0x00000000;
671         lck.in.file.handle      = src_h2;
672         lck.in.locks            = el;
673         el[0].offset            = cc_copy.chunks[0].source_off;
674         el[0].length            = cc_copy.chunks[0].length;
675         el[0].reserved          = 0;
676         el[0].flags             = SMB2_LOCK_FLAG_EXCLUSIVE;
677
678         status = smb2_lock(tree, &lck);
679         torture_assert_ntstatus_ok(torture, status, "lock");
680
681         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
682                                        &cc_copy,
683                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
684         torture_assert_ndr_success(torture, ndr_ret,
685                                    "ndr_push_srv_copychunk_copy");
686
687         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
688         /*
689          * 2k12 & Samba return lock_conflict, Windows 7 & 2k8 return success...
690          *
691          * Edgar Olougouna @ MS wrote:
692          * Regarding the FSCTL_SRV_COPYCHUNK and STATUS_FILE_LOCK_CONFLICT
693          * discrepancy observed between Windows versions, we confirm that the
694          * behavior change is expected.
695          *
696          * CopyChunk in Windows Server 2012 use regular Readfile/Writefile APIs
697          * to move the chunks from the source to the destination.
698          * These ReadFile/WriteFile APIs go through the byte-range lock checks,
699          * and this explains the observed STATUS_FILE_LOCK_CONFLICT error.
700          *
701          * Prior to Windows Server 2012, CopyChunk used mapped sections to move
702          * the data. And byte range locks are not enforced on mapped I/O, and
703          * this explains the STATUS_SUCCESS observed on Windows Server 2008 R2.
704          */
705         torture_assert_ntstatus_equal(torture, status,
706                                       NT_STATUS_FILE_LOCK_CONFLICT,
707                                       "FSCTL_SRV_COPYCHUNK locked");
708
709         /* should get cc response data with the lock conflict status */
710         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
711                                        &cc_rsp,
712                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
713         torture_assert_ndr_success(torture, ndr_ret,
714                                    "ndr_pull_srv_copychunk_rsp");
715         ok = check_copy_chunk_rsp(torture, &cc_rsp,
716                                   0,    /* chunks written */
717                                   0,    /* chunk bytes unsuccessfully written */
718                                   0);   /* total bytes written */
719
720         lck.in.lock_count       = 0x0001;
721         lck.in.lock_sequence    = 0x00000001;
722         lck.in.file.handle      = src_h2;
723         lck.in.locks            = el;
724         el[0].offset            = cc_copy.chunks[0].source_off;
725         el[0].length            = cc_copy.chunks[0].length;
726         el[0].reserved          = 0;
727         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
728         status = smb2_lock(tree, &lck);
729         torture_assert_ntstatus_ok(torture, status, "unlock");
730
731         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
732         torture_assert_ntstatus_ok(torture, status,
733                                    "FSCTL_SRV_COPYCHUNK unlocked");
734
735         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
736                                        &cc_rsp,
737                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
738         torture_assert_ndr_success(torture, ndr_ret,
739                                    "ndr_pull_srv_copychunk_rsp");
740
741         ok = check_copy_chunk_rsp(torture, &cc_rsp,
742                                   1,    /* chunks written */
743                                   0,    /* chunk bytes unsuccessfully written */
744                                   4096); /* total bytes written */
745         if (!ok) {
746                 return false;
747         }
748
749         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
750         if (!ok) {
751                 return false;
752         }
753
754         smb2_util_close(tree, src_h2);
755         smb2_util_close(tree, src_h);
756         smb2_util_close(tree, dest_h);
757         talloc_free(tmp_ctx);
758         return true;
759 }
760
761 static bool test_ioctl_copy_chunk_dest_lck(struct torture_context *torture,
762                                            struct smb2_tree *tree)
763 {
764         struct smb2_handle src_h;
765         struct smb2_handle dest_h;
766         struct smb2_handle dest_h2;
767         NTSTATUS status;
768         union smb_ioctl ioctl;
769         TALLOC_CTX *tmp_ctx = talloc_new(tree);
770         struct srv_copychunk_copy cc_copy;
771         struct srv_copychunk_rsp cc_rsp;
772         enum ndr_err_code ndr_ret;
773         bool ok;
774         struct smb2_lock lck;
775         struct smb2_lock_element el[1];
776
777         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
778                                    1, /* chunks */
779                                    &src_h, 4096, /* src file */
780                                    &dest_h, 4096,       /* dest file */
781                                    &cc_copy,
782                                    &ioctl);
783         if (!ok) {
784                 return false;
785         }
786
787         cc_copy.chunks[0].source_off = 0;
788         cc_copy.chunks[0].target_off = 0;
789         cc_copy.chunks[0].length = 4096;
790
791         /* open and lock the copychunk dest file */
792         status = torture_smb2_testfile(tree, FNAME2, &dest_h2);
793         torture_assert_ntstatus_ok(torture, status, "2nd src open");
794
795         lck.in.lock_count       = 0x0001;
796         lck.in.lock_sequence    = 0x00000000;
797         lck.in.file.handle      = dest_h2;
798         lck.in.locks            = el;
799         el[0].offset            = cc_copy.chunks[0].target_off;
800         el[0].length            = cc_copy.chunks[0].length;
801         el[0].reserved          = 0;
802         el[0].flags             = SMB2_LOCK_FLAG_EXCLUSIVE;
803
804         status = smb2_lock(tree, &lck);
805         torture_assert_ntstatus_ok(torture, status, "lock");
806
807         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
808                                        &cc_copy,
809                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
810         torture_assert_ndr_success(torture, ndr_ret,
811                                    "ndr_push_srv_copychunk_copy");
812
813         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
814         torture_assert_ntstatus_equal(torture, status,
815                                       NT_STATUS_FILE_LOCK_CONFLICT,
816                                       "FSCTL_SRV_COPYCHUNK locked");
817
818         lck.in.lock_count       = 0x0001;
819         lck.in.lock_sequence    = 0x00000001;
820         lck.in.file.handle      = dest_h2;
821         lck.in.locks            = el;
822         el[0].offset            = cc_copy.chunks[0].target_off;
823         el[0].length            = cc_copy.chunks[0].length;
824         el[0].reserved          = 0;
825         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
826         status = smb2_lock(tree, &lck);
827         torture_assert_ntstatus_ok(torture, status, "unlock");
828
829         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
830         torture_assert_ntstatus_ok(torture, status,
831                                    "FSCTL_SRV_COPYCHUNK unlocked");
832
833         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
834                                        &cc_rsp,
835                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
836         torture_assert_ndr_success(torture, ndr_ret,
837                                    "ndr_pull_srv_copychunk_rsp");
838
839         ok = check_copy_chunk_rsp(torture, &cc_rsp,
840                                   1,    /* chunks written */
841                                   0,    /* chunk bytes unsuccessfully written */
842                                   4096); /* total bytes written */
843         if (!ok) {
844                 return false;
845         }
846
847         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
848         if (!ok) {
849                 return false;
850         }
851
852         smb2_util_close(tree, dest_h2);
853         smb2_util_close(tree, src_h);
854         smb2_util_close(tree, dest_h);
855         talloc_free(tmp_ctx);
856         return true;
857 }
858
859 /*
860    basic testing of SMB2 ioctls
861 */
862 struct torture_suite *torture_smb2_ioctl_init(void)
863 {
864         struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "ioctl");
865
866         torture_suite_add_1smb2_test(suite, "shadow_copy",
867                                      test_ioctl_get_shadow_copy);
868         torture_suite_add_1smb2_test(suite, "req_resume_key",
869                                      test_ioctl_req_resume_key);
870         torture_suite_add_1smb2_test(suite, "copy_chunk_simple",
871                                      test_ioctl_copy_chunk_simple);
872         torture_suite_add_1smb2_test(suite, "copy_chunk_multi",
873                                      test_ioctl_copy_chunk_multi);
874         torture_suite_add_1smb2_test(suite, "copy_chunk_tiny",
875                                      test_ioctl_copy_chunk_tiny);
876         torture_suite_add_1smb2_test(suite, "copy_chunk_overwrite",
877                                      test_ioctl_copy_chunk_over);
878         torture_suite_add_1smb2_test(suite, "copy_chunk_append",
879                                      test_ioctl_copy_chunk_append);
880         torture_suite_add_1smb2_test(suite, "copy_chunk_limits",
881                                      test_ioctl_copy_chunk_limits);
882         torture_suite_add_1smb2_test(suite, "copy_chunk_src_lock",
883                                      test_ioctl_copy_chunk_src_lck);
884         torture_suite_add_1smb2_test(suite, "copy_chunk_dest_lock",
885                                      test_ioctl_copy_chunk_dest_lck);
886
887         suite->description = talloc_strdup(suite, "SMB2-IOCTL tests");
888
889         return suite;
890 }
891