4d2137e7d1b9e0e468712fed6cd7940171bb3bcd
[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                                   struct smb2_handle *dest_h,
157                                   uint64_t dest_size,
158                                   struct srv_copychunk_copy *cc_copy,
159                                   union smb_ioctl *ioctl)
160 {
161         struct req_resume_key_rsp res_key;
162         NTSTATUS status;
163         enum ndr_err_code ndr_ret;
164         uint64_t i;
165         uint8_t *buf = talloc_zero_size(mem_ctx, MAX(src_size, dest_size));
166         torture_assert(torture, (buf != NULL), "no memory for file data buf");
167
168         smb2_util_unlink(tree, FNAME);
169         smb2_util_unlink(tree, FNAME2);
170
171         status = torture_smb2_testfile(tree, FNAME, src_h);
172         torture_assert_ntstatus_ok(torture, status, "create write");
173
174         if (src_size > 0) {
175                 for (i = 0; i <= src_size - 8; i += 8) {
176                         SBVAL(buf, i, patt_hash(i));
177                 }
178                 status = smb2_util_write(tree, *src_h, buf, 0, src_size);
179                 torture_assert_ntstatus_ok(torture, status, "src write");
180         }
181
182         status = torture_smb2_testfile(tree, FNAME2, dest_h);
183         torture_assert_ntstatus_ok(torture, status, "create write");
184
185         if (dest_size > 0) {
186                 for (i = 0; i <= dest_size - 8; i += 8) {
187                         SBVAL(buf, i, patt_hash(i));
188                 }
189                 status = smb2_util_write(tree, *dest_h, buf, 0, dest_size);
190                 torture_assert_ntstatus_ok(torture, status, "dest write");
191         }
192
193         ZERO_STRUCTPN(ioctl);
194         ioctl->smb2.level = RAW_IOCTL_SMB2;
195         ioctl->smb2.in.file.handle = *src_h;
196         ioctl->smb2.in.function = FSCTL_SRV_REQUEST_RESUME_KEY;
197         /* Allow for Key + ContextLength + Context */
198         ioctl->smb2.in.max_response_size = 32;
199         ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
200
201         status = smb2_ioctl(tree, mem_ctx, &ioctl->smb2);
202         torture_assert_ntstatus_ok(torture, status,
203                                    "FSCTL_SRV_REQUEST_RESUME_KEY");
204
205
206         ndr_ret = ndr_pull_struct_blob(&ioctl->smb2.out.out, mem_ctx, &res_key,
207                         (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
208
209         torture_assert_ndr_success(torture, ndr_ret,
210                                    "ndr_pull_req_resume_key_rsp");
211
212         ZERO_STRUCTPN(ioctl);
213         ioctl->smb2.level = RAW_IOCTL_SMB2;
214         ioctl->smb2.in.file.handle = *dest_h;
215         ioctl->smb2.in.function = FSCTL_SRV_COPYCHUNK;
216         ioctl->smb2.in.max_response_size = sizeof(struct srv_copychunk_rsp);
217         ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
218
219         ZERO_STRUCTPN(cc_copy);
220         memcpy(cc_copy->source_key, res_key.resume_key, ARRAY_SIZE(cc_copy->source_key));
221         cc_copy->chunk_count = nchunks;
222         cc_copy->chunks = talloc_zero_array(mem_ctx, struct srv_copychunk, nchunks);
223         torture_assert(torture, (cc_copy->chunks != NULL), "no memory for chunks");
224
225         return true;
226 }
227
228
229 static bool check_copy_chunk_rsp(struct torture_context *torture,
230                                  struct srv_copychunk_rsp *cc_rsp,
231                                  uint32_t ex_chunks_written,
232                                  uint32_t ex_chunk_bytes_written,
233                                  uint32_t ex_total_bytes_written)
234 {
235         torture_assert_int_equal(torture, cc_rsp->chunks_written,
236                                  ex_chunks_written, "num chunks");
237         torture_assert_int_equal(torture, cc_rsp->chunk_bytes_written,
238                                  ex_chunk_bytes_written, "chunk bytes written");
239         torture_assert_int_equal(torture, cc_rsp->total_bytes_written,
240                                  ex_total_bytes_written, "chunk total bytes");
241         return true;
242 }
243
244 static bool test_ioctl_copy_chunk_simple(struct torture_context *torture,
245                                          struct smb2_tree *tree)
246 {
247         struct smb2_handle src_h;
248         struct smb2_handle dest_h;
249         NTSTATUS status;
250         union smb_ioctl ioctl;
251         TALLOC_CTX *tmp_ctx = talloc_new(tree);
252         struct srv_copychunk_copy cc_copy;
253         struct srv_copychunk_rsp cc_rsp;
254         enum ndr_err_code ndr_ret;
255         bool ok;
256
257         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
258                                    1, /* 1 chunk */
259                                    &src_h, 4096, /* fill 4096 byte src file */
260                                    &dest_h, 0,  /* 0 byte dest file */
261                                    &cc_copy,
262                                    &ioctl);
263         if (!ok) {
264                 torture_fail(torture, "setup copy chunk error");
265         }
266
267         /* copy all src file data (via a single chunk desc) */
268         cc_copy.chunks[0].source_off = 0;
269         cc_copy.chunks[0].target_off = 0;
270         cc_copy.chunks[0].length = 4096;
271
272         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
273                                        &cc_copy,
274                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
275         torture_assert_ndr_success(torture, ndr_ret,
276                                    "ndr_push_srv_copychunk_copy");
277
278         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
279         torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
280
281         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
282                                        &cc_rsp,
283                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
284         torture_assert_ndr_success(torture, ndr_ret,
285                                    "ndr_pull_srv_copychunk_rsp");
286
287         ok = check_copy_chunk_rsp(torture, &cc_rsp,
288                                   1,    /* chunks written */
289                                   0,    /* chunk bytes unsuccessfully written */
290                                   4096); /* total bytes written */
291         if (!ok) {
292                 torture_fail(torture, "bad copy chunk response data");
293         }
294
295         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
296         if (!ok) {
297                 torture_fail(torture, "inconsistent file data");
298         }
299
300         smb2_util_close(tree, src_h);
301         smb2_util_close(tree, dest_h);
302         talloc_free(tmp_ctx);
303         return true;
304 }
305
306 static bool test_ioctl_copy_chunk_multi(struct torture_context *torture,
307                                         struct smb2_tree *tree)
308 {
309         struct smb2_handle src_h;
310         struct smb2_handle dest_h;
311         NTSTATUS status;
312         union smb_ioctl ioctl;
313         TALLOC_CTX *tmp_ctx = talloc_new(tree);
314         struct srv_copychunk_copy cc_copy;
315         struct srv_copychunk_rsp cc_rsp;
316         enum ndr_err_code ndr_ret;
317         bool ok;
318
319         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
320                                    2, /* chunks */
321                                    &src_h, 8192, /* src file */
322                                    &dest_h, 0,  /* dest file */
323                                    &cc_copy,
324                                    &ioctl);
325         if (!ok) {
326                 torture_fail(torture, "setup copy chunk error");
327         }
328
329         /* copy all src file data via two chunks */
330         cc_copy.chunks[0].source_off = 0;
331         cc_copy.chunks[0].target_off = 0;
332         cc_copy.chunks[0].length = 4096;
333
334         cc_copy.chunks[1].source_off = 4096;
335         cc_copy.chunks[1].target_off = 4096;
336         cc_copy.chunks[1].length = 4096;
337
338         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
339                                        &cc_copy,
340                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
341         torture_assert_ndr_success(torture, ndr_ret,
342                                    "ndr_push_srv_copychunk_copy");
343
344         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
345         torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
346
347         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
348                                        &cc_rsp,
349                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
350         torture_assert_ndr_success(torture, ndr_ret,
351                                    "ndr_pull_srv_copychunk_rsp");
352
353         ok = check_copy_chunk_rsp(torture, &cc_rsp,
354                                   2,    /* chunks written */
355                                   0,    /* chunk bytes unsuccessfully written */
356                                   8192);        /* total bytes written */
357         if (!ok) {
358                 torture_fail(torture, "bad copy chunk response data");
359         }
360
361         smb2_util_close(tree, src_h);
362         smb2_util_close(tree, dest_h);
363         talloc_free(tmp_ctx);
364         return true;
365 }
366
367 static bool test_ioctl_copy_chunk_tiny(struct torture_context *torture,
368                                        struct smb2_tree *tree)
369 {
370         struct smb2_handle src_h;
371         struct smb2_handle dest_h;
372         NTSTATUS status;
373         union smb_ioctl ioctl;
374         TALLOC_CTX *tmp_ctx = talloc_new(tree);
375         struct srv_copychunk_copy cc_copy;
376         struct srv_copychunk_rsp cc_rsp;
377         enum ndr_err_code ndr_ret;
378         bool ok;
379
380         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
381                                    2, /* chunks */
382                                    &src_h, 100, /* src file */
383                                    &dest_h, 0,  /* dest file */
384                                    &cc_copy,
385                                    &ioctl);
386         if (!ok) {
387                 torture_fail(torture, "setup copy chunk error");
388         }
389
390         /* copy all src file data via two chunks, sub block size chunks */
391         cc_copy.chunks[0].source_off = 0;
392         cc_copy.chunks[0].target_off = 0;
393         cc_copy.chunks[0].length = 50;
394
395         cc_copy.chunks[1].source_off = 50;
396         cc_copy.chunks[1].target_off = 50;
397         cc_copy.chunks[1].length = 50;
398
399         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
400                                        &cc_copy,
401                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
402         torture_assert_ndr_success(torture, ndr_ret,
403                                    "ndr_push_srv_copychunk_copy");
404
405         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
406         torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
407
408         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
409                                        &cc_rsp,
410                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
411         torture_assert_ndr_success(torture, ndr_ret,
412                                    "ndr_pull_srv_copychunk_rsp");
413
414         ok = check_copy_chunk_rsp(torture, &cc_rsp,
415                                   2,    /* chunks written */
416                                   0,    /* chunk bytes unsuccessfully written */
417                                   100); /* total bytes written */
418         if (!ok) {
419                 torture_fail(torture, "bad copy chunk response data");
420         }
421
422         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 100, 0);
423         if (!ok) {
424                 torture_fail(torture, "inconsistent file data");
425         }
426
427         smb2_util_close(tree, src_h);
428         smb2_util_close(tree, dest_h);
429         talloc_free(tmp_ctx);
430         return true;
431 }
432
433 static bool test_ioctl_copy_chunk_over(struct torture_context *torture,
434                                        struct smb2_tree *tree)
435 {
436         struct smb2_handle src_h;
437         struct smb2_handle dest_h;
438         NTSTATUS status;
439         union smb_ioctl ioctl;
440         TALLOC_CTX *tmp_ctx = talloc_new(tree);
441         struct srv_copychunk_copy cc_copy;
442         struct srv_copychunk_rsp cc_rsp;
443         enum ndr_err_code ndr_ret;
444         bool ok;
445
446         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
447                                    2, /* chunks */
448                                    &src_h, 8192, /* src file */
449                                    &dest_h, 4096, /* dest file */
450                                    &cc_copy,
451                                    &ioctl);
452         if (!ok) {
453                 torture_fail(torture, "setup copy chunk error");
454         }
455
456         /* first chunk overwrites existing dest data */
457         cc_copy.chunks[0].source_off = 0;
458         cc_copy.chunks[0].target_off = 0;
459         cc_copy.chunks[0].length = 4096;
460
461         /* second chunk overwrites the first */
462         cc_copy.chunks[1].source_off = 4096;
463         cc_copy.chunks[1].target_off = 0;
464         cc_copy.chunks[1].length = 4096;
465
466         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
467                                        &cc_copy,
468                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
469         torture_assert_ndr_success(torture, ndr_ret,
470                                    "ndr_push_srv_copychunk_copy");
471
472         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
473         torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
474
475         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
476                                        &cc_rsp,
477                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
478         torture_assert_ndr_success(torture, ndr_ret,
479                                    "ndr_pull_srv_copychunk_rsp");
480
481         ok = check_copy_chunk_rsp(torture, &cc_rsp,
482                                   2,    /* chunks written */
483                                   0,    /* chunk bytes unsuccessfully written */
484                                   8192); /* total bytes written */
485         if (!ok) {
486                 torture_fail(torture, "bad copy chunk response data");
487         }
488
489         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 4096);
490         if (!ok) {
491                 torture_fail(torture, "inconsistent file data");
492         }
493
494         smb2_util_close(tree, src_h);
495         smb2_util_close(tree, dest_h);
496         talloc_free(tmp_ctx);
497         return true;
498 }
499
500 static bool test_ioctl_copy_chunk_append(struct torture_context *torture,
501                                        struct smb2_tree *tree)
502 {
503         struct smb2_handle src_h;
504         struct smb2_handle dest_h;
505         NTSTATUS status;
506         union smb_ioctl ioctl;
507         TALLOC_CTX *tmp_ctx = talloc_new(tree);
508         struct srv_copychunk_copy cc_copy;
509         struct srv_copychunk_rsp cc_rsp;
510         enum ndr_err_code ndr_ret;
511         bool ok;
512
513         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
514                                    2, /* chunks */
515                                    &src_h, 4096, /* src file */
516                                    &dest_h, 0,  /* dest file */
517                                    &cc_copy,
518                                    &ioctl);
519         if (!ok) {
520                 torture_fail(torture, "setup copy chunk error");
521         }
522
523         cc_copy.chunks[0].source_off = 0;
524         cc_copy.chunks[0].target_off = 0;
525         cc_copy.chunks[0].length = 4096;
526
527         /* second chunk appends the same data to the first */
528         cc_copy.chunks[1].source_off = 0;
529         cc_copy.chunks[1].target_off = 4096;
530         cc_copy.chunks[1].length = 4096;
531
532         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
533                                        &cc_copy,
534                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
535         torture_assert_ndr_success(torture, ndr_ret,
536                                    "ndr_push_srv_copychunk_copy");
537
538         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
539         torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
540
541         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
542                                        &cc_rsp,
543                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
544         torture_assert_ndr_success(torture, ndr_ret,
545                                    "ndr_pull_srv_copychunk_rsp");
546
547         ok = check_copy_chunk_rsp(torture, &cc_rsp,
548                                   2,    /* chunks written */
549                                   0,    /* chunk bytes unsuccessfully written */
550                                   8192); /* total bytes written */
551         if (!ok) {
552                 torture_fail(torture, "bad copy chunk response data");
553         }
554
555         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
556         if (!ok) {
557                 torture_fail(torture, "inconsistent file data");
558         }
559
560         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 4096, 4096, 0);
561         if (!ok) {
562                 torture_fail(torture, "inconsistent file data");
563         }
564
565         smb2_util_close(tree, src_h);
566         smb2_util_close(tree, dest_h);
567         talloc_free(tmp_ctx);
568         return true;
569 }
570
571 static bool test_ioctl_copy_chunk_limits(struct torture_context *torture,
572                                          struct smb2_tree *tree)
573 {
574         struct smb2_handle src_h;
575         struct smb2_handle dest_h;
576         NTSTATUS status;
577         union smb_ioctl ioctl;
578         TALLOC_CTX *tmp_ctx = talloc_new(tree);
579         struct srv_copychunk_copy cc_copy;
580         struct srv_copychunk_rsp cc_rsp;
581         enum ndr_err_code ndr_ret;
582         bool ok;
583
584         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
585                                    1, /* chunks */
586                                    &src_h, 4096, /* src file */
587                                    &dest_h, 0,  /* dest file */
588                                    &cc_copy,
589                                    &ioctl);
590         if (!ok) {
591                 torture_fail(torture, "setup copy chunk error");
592         }
593
594         /* send huge chunk length request */
595         cc_copy.chunks[0].source_off = 0;
596         cc_copy.chunks[0].target_off = 0;
597         cc_copy.chunks[0].length = UINT_MAX;
598
599         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
600                                        &cc_copy,
601                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
602         torture_assert_ndr_success(torture, ndr_ret, "marshalling request");
603
604         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
605         torture_assert_ntstatus_equal(torture, status,
606                                       NT_STATUS_INVALID_PARAMETER,
607                                       "bad oversize chunk response");
608
609         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
610                                        &cc_rsp,
611                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
612         torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
613
614         torture_comment(torture, "limit max chunks, got %u\n",
615                         cc_rsp.chunks_written);
616         torture_comment(torture, "limit max chunk len, got %u\n",
617                         cc_rsp.chunk_bytes_written);
618         torture_comment(torture, "limit max total bytes, got %u\n",
619                         cc_rsp.total_bytes_written);
620
621         smb2_util_close(tree, src_h);
622         smb2_util_close(tree, dest_h);
623         talloc_free(tmp_ctx);
624         return true;
625 }
626
627 static bool test_ioctl_copy_chunk_src_lck(struct torture_context *torture,
628                                           struct smb2_tree *tree)
629 {
630         struct smb2_handle src_h;
631         struct smb2_handle src_h2;
632         struct smb2_handle dest_h;
633         NTSTATUS status;
634         union smb_ioctl ioctl;
635         TALLOC_CTX *tmp_ctx = talloc_new(tree);
636         struct srv_copychunk_copy cc_copy;
637         struct srv_copychunk_rsp cc_rsp;
638         enum ndr_err_code ndr_ret;
639         bool ok;
640         struct smb2_lock lck;
641         struct smb2_lock_element el[1];
642
643         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
644                                    1, /* chunks */
645                                    &src_h, 4096, /* src file */
646                                    &dest_h, 0,  /* dest file */
647                                    &cc_copy,
648                                    &ioctl);
649         if (!ok) {
650                 torture_fail(torture, "setup copy chunk error");
651         }
652
653         cc_copy.chunks[0].source_off = 0;
654         cc_copy.chunks[0].target_off = 0;
655         cc_copy.chunks[0].length = 4096;
656
657         /* open and lock the copychunk src file */
658         status = torture_smb2_testfile(tree, FNAME, &src_h2);
659         torture_assert_ntstatus_ok(torture, status, "2nd src open");
660
661         lck.in.lock_count       = 0x0001;
662         lck.in.lock_sequence    = 0x00000000;
663         lck.in.file.handle      = src_h2;
664         lck.in.locks            = el;
665         el[0].offset            = cc_copy.chunks[0].source_off;
666         el[0].length            = cc_copy.chunks[0].length;
667         el[0].reserved          = 0;
668         el[0].flags             = SMB2_LOCK_FLAG_EXCLUSIVE;
669
670         status = smb2_lock(tree, &lck);
671         torture_assert_ntstatus_ok(torture, status, "lock");
672
673         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
674                                        &cc_copy,
675                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
676         torture_assert_ndr_success(torture, ndr_ret,
677                                    "ndr_push_srv_copychunk_copy");
678
679         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
680         /*
681          * 2k12 & Samba return lock_conflict, Windows 7 & 2k8 return success...
682          *
683          * Edgar Olougouna @ MS wrote:
684          * Regarding the FSCTL_SRV_COPYCHUNK and STATUS_FILE_LOCK_CONFLICT
685          * discrepancy observed between Windows versions, we confirm that the
686          * behavior change is expected.
687          *
688          * CopyChunk in Windows Server 2012 use regular Readfile/Writefile APIs
689          * to move the chunks from the source to the destination.
690          * These ReadFile/WriteFile APIs go through the byte-range lock checks,
691          * and this explains the observed STATUS_FILE_LOCK_CONFLICT error.
692          *
693          * Prior to Windows Server 2012, CopyChunk used mapped sections to move
694          * the data. And byte range locks are not enforced on mapped I/O, and
695          * this explains the STATUS_SUCCESS observed on Windows Server 2008 R2.
696          */
697         torture_assert_ntstatus_equal(torture, status,
698                                       NT_STATUS_FILE_LOCK_CONFLICT,
699                                       "FSCTL_SRV_COPYCHUNK locked");
700
701         /* should get cc response data with the lock conflict status */
702         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
703                                        &cc_rsp,
704                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
705         torture_assert_ndr_success(torture, ndr_ret,
706                                    "ndr_pull_srv_copychunk_rsp");
707         ok = check_copy_chunk_rsp(torture, &cc_rsp,
708                                   0,    /* chunks written */
709                                   0,    /* chunk bytes unsuccessfully written */
710                                   0);   /* total bytes written */
711
712         lck.in.lock_count       = 0x0001;
713         lck.in.lock_sequence    = 0x00000001;
714         lck.in.file.handle      = src_h2;
715         lck.in.locks            = el;
716         el[0].offset            = cc_copy.chunks[0].source_off;
717         el[0].length            = cc_copy.chunks[0].length;
718         el[0].reserved          = 0;
719         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
720         status = smb2_lock(tree, &lck);
721         torture_assert_ntstatus_ok(torture, status, "unlock");
722
723         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
724         torture_assert_ntstatus_ok(torture, status,
725                                    "FSCTL_SRV_COPYCHUNK unlocked");
726
727         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
728                                        &cc_rsp,
729                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
730         torture_assert_ndr_success(torture, ndr_ret,
731                                    "ndr_pull_srv_copychunk_rsp");
732
733         ok = check_copy_chunk_rsp(torture, &cc_rsp,
734                                   1,    /* chunks written */
735                                   0,    /* chunk bytes unsuccessfully written */
736                                   4096); /* total bytes written */
737         if (!ok) {
738                 torture_fail(torture, "bad copy chunk response data");
739         }
740
741         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
742         if (!ok) {
743                 torture_fail(torture, "inconsistent file data");
744         }
745
746         smb2_util_close(tree, src_h2);
747         smb2_util_close(tree, src_h);
748         smb2_util_close(tree, dest_h);
749         talloc_free(tmp_ctx);
750         return true;
751 }
752
753 static bool test_ioctl_copy_chunk_dest_lck(struct torture_context *torture,
754                                            struct smb2_tree *tree)
755 {
756         struct smb2_handle src_h;
757         struct smb2_handle dest_h;
758         struct smb2_handle dest_h2;
759         NTSTATUS status;
760         union smb_ioctl ioctl;
761         TALLOC_CTX *tmp_ctx = talloc_new(tree);
762         struct srv_copychunk_copy cc_copy;
763         struct srv_copychunk_rsp cc_rsp;
764         enum ndr_err_code ndr_ret;
765         bool ok;
766         struct smb2_lock lck;
767         struct smb2_lock_element el[1];
768
769         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
770                                    1, /* chunks */
771                                    &src_h, 4096, /* src file */
772                                    &dest_h, 4096,       /* dest file */
773                                    &cc_copy,
774                                    &ioctl);
775         if (!ok) {
776                 torture_fail(torture, "setup copy chunk error");
777         }
778
779         cc_copy.chunks[0].source_off = 0;
780         cc_copy.chunks[0].target_off = 0;
781         cc_copy.chunks[0].length = 4096;
782
783         /* open and lock the copychunk dest file */
784         status = torture_smb2_testfile(tree, FNAME2, &dest_h2);
785         torture_assert_ntstatus_ok(torture, status, "2nd src open");
786
787         lck.in.lock_count       = 0x0001;
788         lck.in.lock_sequence    = 0x00000000;
789         lck.in.file.handle      = dest_h2;
790         lck.in.locks            = el;
791         el[0].offset            = cc_copy.chunks[0].target_off;
792         el[0].length            = cc_copy.chunks[0].length;
793         el[0].reserved          = 0;
794         el[0].flags             = SMB2_LOCK_FLAG_EXCLUSIVE;
795
796         status = smb2_lock(tree, &lck);
797         torture_assert_ntstatus_ok(torture, status, "lock");
798
799         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
800                                        &cc_copy,
801                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
802         torture_assert_ndr_success(torture, ndr_ret,
803                                    "ndr_push_srv_copychunk_copy");
804
805         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
806         torture_assert_ntstatus_equal(torture, status,
807                                       NT_STATUS_FILE_LOCK_CONFLICT,
808                                       "FSCTL_SRV_COPYCHUNK locked");
809
810         lck.in.lock_count       = 0x0001;
811         lck.in.lock_sequence    = 0x00000001;
812         lck.in.file.handle      = dest_h2;
813         lck.in.locks            = el;
814         el[0].offset            = cc_copy.chunks[0].target_off;
815         el[0].length            = cc_copy.chunks[0].length;
816         el[0].reserved          = 0;
817         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
818         status = smb2_lock(tree, &lck);
819         torture_assert_ntstatus_ok(torture, status, "unlock");
820
821         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
822         torture_assert_ntstatus_ok(torture, status,
823                                    "FSCTL_SRV_COPYCHUNK unlocked");
824
825         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
826                                        &cc_rsp,
827                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
828         torture_assert_ndr_success(torture, ndr_ret,
829                                    "ndr_pull_srv_copychunk_rsp");
830
831         ok = check_copy_chunk_rsp(torture, &cc_rsp,
832                                   1,    /* chunks written */
833                                   0,    /* chunk bytes unsuccessfully written */
834                                   4096); /* total bytes written */
835         if (!ok) {
836                 torture_fail(torture, "bad copy chunk response data");
837         }
838
839         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
840         if (!ok) {
841                 torture_fail(torture, "inconsistent file data");
842         }
843
844         smb2_util_close(tree, dest_h2);
845         smb2_util_close(tree, src_h);
846         smb2_util_close(tree, dest_h);
847         talloc_free(tmp_ctx);
848         return true;
849 }
850
851 /*
852    basic testing of SMB2 ioctls
853 */
854 struct torture_suite *torture_smb2_ioctl_init(void)
855 {
856         struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "ioctl");
857
858         torture_suite_add_1smb2_test(suite, "shadow_copy",
859                                      test_ioctl_get_shadow_copy);
860         torture_suite_add_1smb2_test(suite, "req_resume_key",
861                                      test_ioctl_req_resume_key);
862         torture_suite_add_1smb2_test(suite, "copy_chunk_simple",
863                                      test_ioctl_copy_chunk_simple);
864         torture_suite_add_1smb2_test(suite, "copy_chunk_multi",
865                                      test_ioctl_copy_chunk_multi);
866         torture_suite_add_1smb2_test(suite, "copy_chunk_tiny",
867                                      test_ioctl_copy_chunk_tiny);
868         torture_suite_add_1smb2_test(suite, "copy_chunk_overwrite",
869                                      test_ioctl_copy_chunk_over);
870         torture_suite_add_1smb2_test(suite, "copy_chunk_append",
871                                      test_ioctl_copy_chunk_append);
872         torture_suite_add_1smb2_test(suite, "copy_chunk_limits",
873                                      test_ioctl_copy_chunk_limits);
874         torture_suite_add_1smb2_test(suite, "copy_chunk_src_lock",
875                                      test_ioctl_copy_chunk_src_lck);
876         torture_suite_add_1smb2_test(suite, "copy_chunk_dest_lock",
877                                      test_ioctl_copy_chunk_dest_lck);
878
879         suite->description = talloc_strdup(suite, "SMB2-IOCTL tests");
880
881         return suite;
882 }
883