torture: split out ioctl test file creation helper
[Samba/wip.git] / source4 / torture / smb2 / ioctl.c
blobe8d12038e0b346c4350db524456fe8304a609706
1 /*
2 Unix SMB/CIFS implementation.
4 test suite for SMB2 ioctl operations
6 Copyright (C) David Disseldorp 2011
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.
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.
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/>.
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"
30 #define FNAME "testfsctl.dat"
31 #define FNAME2 "testfsctl2.dat"
34 basic testing of SMB2 shadow copy calls
36 static bool test_ioctl_get_shadow_copy(struct torture_context *torture,
37 struct smb2_tree *tree)
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);
45 smb2_util_unlink(tree, FNAME);
47 status = torture_smb2_testfile(tree, FNAME, &h);
48 torture_assert_ntstatus_ok(torture, status, "create write");
50 ZERO_ARRAY(buf);
51 status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf));
52 torture_assert_ntstatus_ok(torture, status, "write");
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;
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");
66 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_ENUM_SNAPS");
68 return true;
72 basic testing of the SMB2 server side copy ioctls
74 static bool test_ioctl_req_resume_key(struct torture_context *torture,
75 struct smb2_tree *tree)
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;
85 smb2_util_unlink(tree, FNAME);
87 status = torture_smb2_testfile(tree, FNAME, &h);
88 torture_assert_ntstatus_ok(torture, status, "create write");
90 ZERO_ARRAY(buf);
91 status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf));
92 torture_assert_ntstatus_ok(torture, status, "write");
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;
101 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
102 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_REQUEST_RESUME_KEY");
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");
109 ndr_print_debug((ndr_print_fn_t)ndr_print_req_resume_key_rsp, "yo", &res_key);
111 talloc_free(tmp_ctx);
112 return true;
115 static uint64_t patt_hash(uint64_t off)
117 return off;
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)
125 uint64_t i;
126 struct smb2_read r;
127 NTSTATUS status;
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");
136 torture_assert_u64_equal(torture, r.out.data.length, len,
137 "read data len mismatch");
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));
147 talloc_free(r.out.data.data);
148 return true;
151 static bool test_setup_create_fill(struct torture_context *torture,
152 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
153 const char *fname,
154 struct smb2_handle *fh,
155 uint64_t size,
156 uint32_t desired_access)
158 struct smb2_create io;
159 NTSTATUS status;
160 uint64_t i;
161 uint8_t *buf = talloc_zero_size(mem_ctx, size);
162 torture_assert(torture, (buf != NULL), "no memory for file data buf");
164 smb2_util_unlink(tree, fname);
166 ZERO_STRUCT(io);
167 io.in.desired_access = desired_access;
168 io.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
169 io.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
170 io.in.share_access =
171 NTCREATEX_SHARE_ACCESS_DELETE|
172 NTCREATEX_SHARE_ACCESS_READ|
173 NTCREATEX_SHARE_ACCESS_WRITE;
174 io.in.fname = fname;
176 status = smb2_create(tree, mem_ctx, &io);
177 torture_assert_ntstatus_ok(torture, status, "file create");
179 *fh = io.out.file.handle;
181 if (size > 0) {
182 uint64_t cur_off = 0;
183 for (i = 0; i <= size - 8; i += 8) {
184 SBVAL(buf, i, patt_hash(i));
186 while (size > 0) {
187 uint64_t io_sz = MIN(1024 * 1024, size);
188 status = smb2_util_write(tree, *fh,
189 buf + cur_off, cur_off, io_sz);
190 torture_assert_ntstatus_ok(torture, status, "file write");
192 size -= io_sz;
193 cur_off += io_sz;
196 return true;
199 static bool test_setup_copy_chunk(struct torture_context *torture,
200 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
201 uint32_t nchunks,
202 struct smb2_handle *src_h,
203 uint64_t src_size,
204 uint32_t src_desired_access,
205 struct smb2_handle *dest_h,
206 uint64_t dest_size,
207 uint32_t dest_desired_access,
208 struct srv_copychunk_copy *cc_copy,
209 union smb_ioctl *ioctl)
211 struct req_resume_key_rsp res_key;
212 bool ok;
213 NTSTATUS status;
214 enum ndr_err_code ndr_ret;
216 ok = test_setup_create_fill(torture, tree, mem_ctx, FNAME,
217 src_h, src_size, src_desired_access);
218 torture_assert(torture, ok, "src file create fill");
220 ok = test_setup_create_fill(torture, tree, mem_ctx, FNAME2,
221 dest_h, dest_size, dest_desired_access);
222 torture_assert(torture, ok, "dest file create fill");
224 ZERO_STRUCTPN(ioctl);
225 ioctl->smb2.level = RAW_IOCTL_SMB2;
226 ioctl->smb2.in.file.handle = *src_h;
227 ioctl->smb2.in.function = FSCTL_SRV_REQUEST_RESUME_KEY;
228 /* Allow for Key + ContextLength + Context */
229 ioctl->smb2.in.max_response_size = 32;
230 ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
232 status = smb2_ioctl(tree, mem_ctx, &ioctl->smb2);
233 torture_assert_ntstatus_ok(torture, status,
234 "FSCTL_SRV_REQUEST_RESUME_KEY");
236 ndr_ret = ndr_pull_struct_blob(&ioctl->smb2.out.out, mem_ctx, &res_key,
237 (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
239 torture_assert_ndr_success(torture, ndr_ret,
240 "ndr_pull_req_resume_key_rsp");
242 ZERO_STRUCTPN(ioctl);
243 ioctl->smb2.level = RAW_IOCTL_SMB2;
244 ioctl->smb2.in.file.handle = *dest_h;
245 ioctl->smb2.in.function = FSCTL_SRV_COPYCHUNK;
246 ioctl->smb2.in.max_response_size = sizeof(struct srv_copychunk_rsp);
247 ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
249 ZERO_STRUCTPN(cc_copy);
250 memcpy(cc_copy->source_key, res_key.resume_key, ARRAY_SIZE(cc_copy->source_key));
251 cc_copy->chunk_count = nchunks;
252 cc_copy->chunks = talloc_zero_array(mem_ctx, struct srv_copychunk, nchunks);
253 torture_assert(torture, (cc_copy->chunks != NULL), "no memory for chunks");
255 return true;
259 static bool check_copy_chunk_rsp(struct torture_context *torture,
260 struct srv_copychunk_rsp *cc_rsp,
261 uint32_t ex_chunks_written,
262 uint32_t ex_chunk_bytes_written,
263 uint32_t ex_total_bytes_written)
265 torture_assert_int_equal(torture, cc_rsp->chunks_written,
266 ex_chunks_written, "num chunks");
267 torture_assert_int_equal(torture, cc_rsp->chunk_bytes_written,
268 ex_chunk_bytes_written, "chunk bytes written");
269 torture_assert_int_equal(torture, cc_rsp->total_bytes_written,
270 ex_total_bytes_written, "chunk total bytes");
271 return true;
274 static bool test_ioctl_copy_chunk_simple(struct torture_context *torture,
275 struct smb2_tree *tree)
277 struct smb2_handle src_h;
278 struct smb2_handle dest_h;
279 NTSTATUS status;
280 union smb_ioctl ioctl;
281 TALLOC_CTX *tmp_ctx = talloc_new(tree);
282 struct srv_copychunk_copy cc_copy;
283 struct srv_copychunk_rsp cc_rsp;
284 enum ndr_err_code ndr_ret;
285 bool ok;
287 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
288 1, /* 1 chunk */
289 &src_h, 4096, /* fill 4096 byte src file */
290 SEC_RIGHTS_FILE_ALL,
291 &dest_h, 0, /* 0 byte dest file */
292 SEC_RIGHTS_FILE_ALL,
293 &cc_copy,
294 &ioctl);
295 if (!ok) {
296 torture_fail(torture, "setup copy chunk error");
299 /* copy all src file data (via a single chunk desc) */
300 cc_copy.chunks[0].source_off = 0;
301 cc_copy.chunks[0].target_off = 0;
302 cc_copy.chunks[0].length = 4096;
304 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
305 &cc_copy,
306 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
307 torture_assert_ndr_success(torture, ndr_ret,
308 "ndr_push_srv_copychunk_copy");
310 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
311 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
313 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
314 &cc_rsp,
315 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
316 torture_assert_ndr_success(torture, ndr_ret,
317 "ndr_pull_srv_copychunk_rsp");
319 ok = check_copy_chunk_rsp(torture, &cc_rsp,
320 1, /* chunks written */
321 0, /* chunk bytes unsuccessfully written */
322 4096); /* total bytes written */
323 if (!ok) {
324 torture_fail(torture, "bad copy chunk response data");
327 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
328 if (!ok) {
329 torture_fail(torture, "inconsistent file data");
332 smb2_util_close(tree, src_h);
333 smb2_util_close(tree, dest_h);
334 talloc_free(tmp_ctx);
335 return true;
338 static bool test_ioctl_copy_chunk_multi(struct torture_context *torture,
339 struct smb2_tree *tree)
341 struct smb2_handle src_h;
342 struct smb2_handle dest_h;
343 NTSTATUS status;
344 union smb_ioctl ioctl;
345 TALLOC_CTX *tmp_ctx = talloc_new(tree);
346 struct srv_copychunk_copy cc_copy;
347 struct srv_copychunk_rsp cc_rsp;
348 enum ndr_err_code ndr_ret;
349 bool ok;
351 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
352 2, /* chunks */
353 &src_h, 8192, /* src file */
354 SEC_RIGHTS_FILE_ALL,
355 &dest_h, 0, /* dest file */
356 SEC_RIGHTS_FILE_ALL,
357 &cc_copy,
358 &ioctl);
359 if (!ok) {
360 torture_fail(torture, "setup copy chunk error");
363 /* copy all src file data via two chunks */
364 cc_copy.chunks[0].source_off = 0;
365 cc_copy.chunks[0].target_off = 0;
366 cc_copy.chunks[0].length = 4096;
368 cc_copy.chunks[1].source_off = 4096;
369 cc_copy.chunks[1].target_off = 4096;
370 cc_copy.chunks[1].length = 4096;
372 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
373 &cc_copy,
374 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
375 torture_assert_ndr_success(torture, ndr_ret,
376 "ndr_push_srv_copychunk_copy");
378 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
379 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
381 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
382 &cc_rsp,
383 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
384 torture_assert_ndr_success(torture, ndr_ret,
385 "ndr_pull_srv_copychunk_rsp");
387 ok = check_copy_chunk_rsp(torture, &cc_rsp,
388 2, /* chunks written */
389 0, /* chunk bytes unsuccessfully written */
390 8192); /* total bytes written */
391 if (!ok) {
392 torture_fail(torture, "bad copy chunk response data");
395 smb2_util_close(tree, src_h);
396 smb2_util_close(tree, dest_h);
397 talloc_free(tmp_ctx);
398 return true;
401 static bool test_ioctl_copy_chunk_tiny(struct torture_context *torture,
402 struct smb2_tree *tree)
404 struct smb2_handle src_h;
405 struct smb2_handle dest_h;
406 NTSTATUS status;
407 union smb_ioctl ioctl;
408 TALLOC_CTX *tmp_ctx = talloc_new(tree);
409 struct srv_copychunk_copy cc_copy;
410 struct srv_copychunk_rsp cc_rsp;
411 enum ndr_err_code ndr_ret;
412 bool ok;
414 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
415 2, /* chunks */
416 &src_h, 100, /* src file */
417 SEC_RIGHTS_FILE_ALL,
418 &dest_h, 0, /* dest file */
419 SEC_RIGHTS_FILE_ALL,
420 &cc_copy,
421 &ioctl);
422 if (!ok) {
423 torture_fail(torture, "setup copy chunk error");
426 /* copy all src file data via two chunks, sub block size chunks */
427 cc_copy.chunks[0].source_off = 0;
428 cc_copy.chunks[0].target_off = 0;
429 cc_copy.chunks[0].length = 50;
431 cc_copy.chunks[1].source_off = 50;
432 cc_copy.chunks[1].target_off = 50;
433 cc_copy.chunks[1].length = 50;
435 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
436 &cc_copy,
437 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
438 torture_assert_ndr_success(torture, ndr_ret,
439 "ndr_push_srv_copychunk_copy");
441 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
442 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
444 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
445 &cc_rsp,
446 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
447 torture_assert_ndr_success(torture, ndr_ret,
448 "ndr_pull_srv_copychunk_rsp");
450 ok = check_copy_chunk_rsp(torture, &cc_rsp,
451 2, /* chunks written */
452 0, /* chunk bytes unsuccessfully written */
453 100); /* total bytes written */
454 if (!ok) {
455 torture_fail(torture, "bad copy chunk response data");
458 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 100, 0);
459 if (!ok) {
460 torture_fail(torture, "inconsistent file data");
463 smb2_util_close(tree, src_h);
464 smb2_util_close(tree, dest_h);
465 talloc_free(tmp_ctx);
466 return true;
469 static bool test_ioctl_copy_chunk_over(struct torture_context *torture,
470 struct smb2_tree *tree)
472 struct smb2_handle src_h;
473 struct smb2_handle dest_h;
474 NTSTATUS status;
475 union smb_ioctl ioctl;
476 TALLOC_CTX *tmp_ctx = talloc_new(tree);
477 struct srv_copychunk_copy cc_copy;
478 struct srv_copychunk_rsp cc_rsp;
479 enum ndr_err_code ndr_ret;
480 bool ok;
482 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
483 2, /* chunks */
484 &src_h, 8192, /* src file */
485 SEC_RIGHTS_FILE_ALL,
486 &dest_h, 4096, /* dest file */
487 SEC_RIGHTS_FILE_ALL,
488 &cc_copy,
489 &ioctl);
490 if (!ok) {
491 torture_fail(torture, "setup copy chunk error");
494 /* first chunk overwrites existing dest data */
495 cc_copy.chunks[0].source_off = 0;
496 cc_copy.chunks[0].target_off = 0;
497 cc_copy.chunks[0].length = 4096;
499 /* second chunk overwrites the first */
500 cc_copy.chunks[1].source_off = 4096;
501 cc_copy.chunks[1].target_off = 0;
502 cc_copy.chunks[1].length = 4096;
504 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
505 &cc_copy,
506 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
507 torture_assert_ndr_success(torture, ndr_ret,
508 "ndr_push_srv_copychunk_copy");
510 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
511 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
513 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
514 &cc_rsp,
515 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
516 torture_assert_ndr_success(torture, ndr_ret,
517 "ndr_pull_srv_copychunk_rsp");
519 ok = check_copy_chunk_rsp(torture, &cc_rsp,
520 2, /* chunks written */
521 0, /* chunk bytes unsuccessfully written */
522 8192); /* total bytes written */
523 if (!ok) {
524 torture_fail(torture, "bad copy chunk response data");
527 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 4096);
528 if (!ok) {
529 torture_fail(torture, "inconsistent file data");
532 smb2_util_close(tree, src_h);
533 smb2_util_close(tree, dest_h);
534 talloc_free(tmp_ctx);
535 return true;
538 static bool test_ioctl_copy_chunk_append(struct torture_context *torture,
539 struct smb2_tree *tree)
541 struct smb2_handle src_h;
542 struct smb2_handle dest_h;
543 NTSTATUS status;
544 union smb_ioctl ioctl;
545 TALLOC_CTX *tmp_ctx = talloc_new(tree);
546 struct srv_copychunk_copy cc_copy;
547 struct srv_copychunk_rsp cc_rsp;
548 enum ndr_err_code ndr_ret;
549 bool ok;
551 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
552 2, /* chunks */
553 &src_h, 4096, /* src file */
554 SEC_RIGHTS_FILE_ALL,
555 &dest_h, 0, /* dest file */
556 SEC_RIGHTS_FILE_ALL,
557 &cc_copy,
558 &ioctl);
559 if (!ok) {
560 torture_fail(torture, "setup copy chunk error");
563 cc_copy.chunks[0].source_off = 0;
564 cc_copy.chunks[0].target_off = 0;
565 cc_copy.chunks[0].length = 4096;
567 /* second chunk appends the same data to the first */
568 cc_copy.chunks[1].source_off = 0;
569 cc_copy.chunks[1].target_off = 4096;
570 cc_copy.chunks[1].length = 4096;
572 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
573 &cc_copy,
574 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
575 torture_assert_ndr_success(torture, ndr_ret,
576 "ndr_push_srv_copychunk_copy");
578 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
579 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
581 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
582 &cc_rsp,
583 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
584 torture_assert_ndr_success(torture, ndr_ret,
585 "ndr_pull_srv_copychunk_rsp");
587 ok = check_copy_chunk_rsp(torture, &cc_rsp,
588 2, /* chunks written */
589 0, /* chunk bytes unsuccessfully written */
590 8192); /* total bytes written */
591 if (!ok) {
592 torture_fail(torture, "bad copy chunk response data");
595 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
596 if (!ok) {
597 torture_fail(torture, "inconsistent file data");
600 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 4096, 4096, 0);
601 if (!ok) {
602 torture_fail(torture, "inconsistent file data");
605 smb2_util_close(tree, src_h);
606 smb2_util_close(tree, dest_h);
607 talloc_free(tmp_ctx);
608 return true;
611 static bool test_ioctl_copy_chunk_limits(struct torture_context *torture,
612 struct smb2_tree *tree)
614 struct smb2_handle src_h;
615 struct smb2_handle dest_h;
616 NTSTATUS status;
617 union smb_ioctl ioctl;
618 TALLOC_CTX *tmp_ctx = talloc_new(tree);
619 struct srv_copychunk_copy cc_copy;
620 struct srv_copychunk_rsp cc_rsp;
621 enum ndr_err_code ndr_ret;
622 bool ok;
624 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
625 1, /* chunks */
626 &src_h, 4096, /* src file */
627 SEC_RIGHTS_FILE_ALL,
628 &dest_h, 0, /* dest file */
629 SEC_RIGHTS_FILE_ALL,
630 &cc_copy,
631 &ioctl);
632 if (!ok) {
633 torture_fail(torture, "setup copy chunk error");
636 /* send huge chunk length request */
637 cc_copy.chunks[0].source_off = 0;
638 cc_copy.chunks[0].target_off = 0;
639 cc_copy.chunks[0].length = UINT_MAX;
641 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
642 &cc_copy,
643 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
644 torture_assert_ndr_success(torture, ndr_ret, "marshalling request");
646 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
647 torture_assert_ntstatus_equal(torture, status,
648 NT_STATUS_INVALID_PARAMETER,
649 "bad oversize chunk response");
651 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
652 &cc_rsp,
653 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
654 torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
656 torture_comment(torture, "limit max chunks, got %u\n",
657 cc_rsp.chunks_written);
658 torture_comment(torture, "limit max chunk len, got %u\n",
659 cc_rsp.chunk_bytes_written);
660 torture_comment(torture, "limit max total bytes, got %u\n",
661 cc_rsp.total_bytes_written);
663 smb2_util_close(tree, src_h);
664 smb2_util_close(tree, dest_h);
665 talloc_free(tmp_ctx);
666 return true;
669 static bool test_ioctl_copy_chunk_src_lck(struct torture_context *torture,
670 struct smb2_tree *tree)
672 struct smb2_handle src_h;
673 struct smb2_handle src_h2;
674 struct smb2_handle dest_h;
675 NTSTATUS status;
676 union smb_ioctl ioctl;
677 TALLOC_CTX *tmp_ctx = talloc_new(tree);
678 struct srv_copychunk_copy cc_copy;
679 struct srv_copychunk_rsp cc_rsp;
680 enum ndr_err_code ndr_ret;
681 bool ok;
682 struct smb2_lock lck;
683 struct smb2_lock_element el[1];
685 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
686 1, /* chunks */
687 &src_h, 4096, /* src file */
688 SEC_RIGHTS_FILE_ALL,
689 &dest_h, 0, /* dest file */
690 SEC_RIGHTS_FILE_ALL,
691 &cc_copy,
692 &ioctl);
693 if (!ok) {
694 torture_fail(torture, "setup copy chunk error");
697 cc_copy.chunks[0].source_off = 0;
698 cc_copy.chunks[0].target_off = 0;
699 cc_copy.chunks[0].length = 4096;
701 /* open and lock the copychunk src file */
702 status = torture_smb2_testfile(tree, FNAME, &src_h2);
703 torture_assert_ntstatus_ok(torture, status, "2nd src open");
705 lck.in.lock_count = 0x0001;
706 lck.in.lock_sequence = 0x00000000;
707 lck.in.file.handle = src_h2;
708 lck.in.locks = el;
709 el[0].offset = cc_copy.chunks[0].source_off;
710 el[0].length = cc_copy.chunks[0].length;
711 el[0].reserved = 0;
712 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
714 status = smb2_lock(tree, &lck);
715 torture_assert_ntstatus_ok(torture, status, "lock");
717 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
718 &cc_copy,
719 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
720 torture_assert_ndr_success(torture, ndr_ret,
721 "ndr_push_srv_copychunk_copy");
723 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
725 * 2k12 & Samba return lock_conflict, Windows 7 & 2k8 return success...
727 * Edgar Olougouna @ MS wrote:
728 * Regarding the FSCTL_SRV_COPYCHUNK and STATUS_FILE_LOCK_CONFLICT
729 * discrepancy observed between Windows versions, we confirm that the
730 * behavior change is expected.
732 * CopyChunk in Windows Server 2012 use regular Readfile/Writefile APIs
733 * to move the chunks from the source to the destination.
734 * These ReadFile/WriteFile APIs go through the byte-range lock checks,
735 * and this explains the observed STATUS_FILE_LOCK_CONFLICT error.
737 * Prior to Windows Server 2012, CopyChunk used mapped sections to move
738 * the data. And byte range locks are not enforced on mapped I/O, and
739 * this explains the STATUS_SUCCESS observed on Windows Server 2008 R2.
741 torture_assert_ntstatus_equal(torture, status,
742 NT_STATUS_FILE_LOCK_CONFLICT,
743 "FSCTL_SRV_COPYCHUNK locked");
745 /* should get cc response data with the lock conflict status */
746 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
747 &cc_rsp,
748 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
749 torture_assert_ndr_success(torture, ndr_ret,
750 "ndr_pull_srv_copychunk_rsp");
751 ok = check_copy_chunk_rsp(torture, &cc_rsp,
752 0, /* chunks written */
753 0, /* chunk bytes unsuccessfully written */
754 0); /* total bytes written */
756 lck.in.lock_count = 0x0001;
757 lck.in.lock_sequence = 0x00000001;
758 lck.in.file.handle = src_h2;
759 lck.in.locks = el;
760 el[0].offset = cc_copy.chunks[0].source_off;
761 el[0].length = cc_copy.chunks[0].length;
762 el[0].reserved = 0;
763 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
764 status = smb2_lock(tree, &lck);
765 torture_assert_ntstatus_ok(torture, status, "unlock");
767 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
768 torture_assert_ntstatus_ok(torture, status,
769 "FSCTL_SRV_COPYCHUNK unlocked");
771 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
772 &cc_rsp,
773 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
774 torture_assert_ndr_success(torture, ndr_ret,
775 "ndr_pull_srv_copychunk_rsp");
777 ok = check_copy_chunk_rsp(torture, &cc_rsp,
778 1, /* chunks written */
779 0, /* chunk bytes unsuccessfully written */
780 4096); /* total bytes written */
781 if (!ok) {
782 torture_fail(torture, "bad copy chunk response data");
785 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
786 if (!ok) {
787 torture_fail(torture, "inconsistent file data");
790 smb2_util_close(tree, src_h2);
791 smb2_util_close(tree, src_h);
792 smb2_util_close(tree, dest_h);
793 talloc_free(tmp_ctx);
794 return true;
797 static bool test_ioctl_copy_chunk_dest_lck(struct torture_context *torture,
798 struct smb2_tree *tree)
800 struct smb2_handle src_h;
801 struct smb2_handle dest_h;
802 struct smb2_handle dest_h2;
803 NTSTATUS status;
804 union smb_ioctl ioctl;
805 TALLOC_CTX *tmp_ctx = talloc_new(tree);
806 struct srv_copychunk_copy cc_copy;
807 struct srv_copychunk_rsp cc_rsp;
808 enum ndr_err_code ndr_ret;
809 bool ok;
810 struct smb2_lock lck;
811 struct smb2_lock_element el[1];
813 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
814 1, /* chunks */
815 &src_h, 4096, /* src file */
816 SEC_RIGHTS_FILE_ALL,
817 &dest_h, 4096, /* dest file */
818 SEC_RIGHTS_FILE_ALL,
819 &cc_copy,
820 &ioctl);
821 if (!ok) {
822 torture_fail(torture, "setup copy chunk error");
825 cc_copy.chunks[0].source_off = 0;
826 cc_copy.chunks[0].target_off = 0;
827 cc_copy.chunks[0].length = 4096;
829 /* open and lock the copychunk dest file */
830 status = torture_smb2_testfile(tree, FNAME2, &dest_h2);
831 torture_assert_ntstatus_ok(torture, status, "2nd src open");
833 lck.in.lock_count = 0x0001;
834 lck.in.lock_sequence = 0x00000000;
835 lck.in.file.handle = dest_h2;
836 lck.in.locks = el;
837 el[0].offset = cc_copy.chunks[0].target_off;
838 el[0].length = cc_copy.chunks[0].length;
839 el[0].reserved = 0;
840 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
842 status = smb2_lock(tree, &lck);
843 torture_assert_ntstatus_ok(torture, status, "lock");
845 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
846 &cc_copy,
847 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
848 torture_assert_ndr_success(torture, ndr_ret,
849 "ndr_push_srv_copychunk_copy");
851 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
852 torture_assert_ntstatus_equal(torture, status,
853 NT_STATUS_FILE_LOCK_CONFLICT,
854 "FSCTL_SRV_COPYCHUNK locked");
856 lck.in.lock_count = 0x0001;
857 lck.in.lock_sequence = 0x00000001;
858 lck.in.file.handle = dest_h2;
859 lck.in.locks = el;
860 el[0].offset = cc_copy.chunks[0].target_off;
861 el[0].length = cc_copy.chunks[0].length;
862 el[0].reserved = 0;
863 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
864 status = smb2_lock(tree, &lck);
865 torture_assert_ntstatus_ok(torture, status, "unlock");
867 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
868 torture_assert_ntstatus_ok(torture, status,
869 "FSCTL_SRV_COPYCHUNK unlocked");
871 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
872 &cc_rsp,
873 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
874 torture_assert_ndr_success(torture, ndr_ret,
875 "ndr_pull_srv_copychunk_rsp");
877 ok = check_copy_chunk_rsp(torture, &cc_rsp,
878 1, /* chunks written */
879 0, /* chunk bytes unsuccessfully written */
880 4096); /* total bytes written */
881 if (!ok) {
882 torture_fail(torture, "bad copy chunk response data");
885 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
886 if (!ok) {
887 torture_fail(torture, "inconsistent file data");
890 smb2_util_close(tree, dest_h2);
891 smb2_util_close(tree, src_h);
892 smb2_util_close(tree, dest_h);
893 talloc_free(tmp_ctx);
894 return true;
897 static bool test_ioctl_copy_chunk_bad_key(struct torture_context *torture,
898 struct smb2_tree *tree)
900 struct smb2_handle src_h;
901 struct smb2_handle dest_h;
902 NTSTATUS status;
903 union smb_ioctl ioctl;
904 TALLOC_CTX *tmp_ctx = talloc_new(tree);
905 struct srv_copychunk_copy cc_copy;
906 enum ndr_err_code ndr_ret;
907 bool ok;
909 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
911 &src_h, 4096,
912 SEC_RIGHTS_FILE_ALL,
913 &dest_h, 0,
914 SEC_RIGHTS_FILE_ALL,
915 &cc_copy,
916 &ioctl);
917 if (!ok) {
918 torture_fail(torture, "setup copy chunk error");
921 /* overwrite the resume key with a bogus value */
922 memcpy(cc_copy.source_key, "deadbeefdeadbeefdeadbeef", 24);
924 cc_copy.chunks[0].source_off = 0;
925 cc_copy.chunks[0].target_off = 0;
926 cc_copy.chunks[0].length = 4096;
928 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
929 &cc_copy,
930 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
931 torture_assert_ndr_success(torture, ndr_ret,
932 "ndr_push_srv_copychunk_copy");
934 /* Server 2k12 returns NT_STATUS_OBJECT_NAME_NOT_FOUND */
935 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
936 torture_assert_ntstatus_equal(torture, status,
937 NT_STATUS_OBJECT_NAME_NOT_FOUND,
938 "FSCTL_SRV_COPYCHUNK");
940 smb2_util_close(tree, src_h);
941 smb2_util_close(tree, dest_h);
942 talloc_free(tmp_ctx);
943 return true;
946 static bool test_ioctl_copy_chunk_src_is_dest(struct torture_context *torture,
947 struct smb2_tree *tree)
949 struct smb2_handle src_h;
950 struct smb2_handle dest_h;
951 NTSTATUS status;
952 union smb_ioctl ioctl;
953 TALLOC_CTX *tmp_ctx = talloc_new(tree);
954 struct srv_copychunk_copy cc_copy;
955 struct srv_copychunk_rsp cc_rsp;
956 enum ndr_err_code ndr_ret;
957 bool ok;
959 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
961 &src_h, 8192,
962 SEC_RIGHTS_FILE_ALL,
963 &dest_h, 0,
964 SEC_RIGHTS_FILE_ALL,
965 &cc_copy,
966 &ioctl);
967 if (!ok) {
968 torture_fail(torture, "setup copy chunk error");
971 /* the source is also the destination */
972 ioctl.smb2.in.file.handle = src_h;
974 /* non-overlapping */
975 cc_copy.chunks[0].source_off = 0;
976 cc_copy.chunks[0].target_off = 4096;
977 cc_copy.chunks[0].length = 4096;
979 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
980 &cc_copy,
981 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
982 torture_assert_ndr_success(torture, ndr_ret,
983 "ndr_push_srv_copychunk_copy");
985 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
986 torture_assert_ntstatus_ok(torture, status,
987 "FSCTL_SRV_COPYCHUNK");
989 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
990 &cc_rsp,
991 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
992 torture_assert_ndr_success(torture, ndr_ret,
993 "ndr_pull_srv_copychunk_rsp");
995 ok = check_copy_chunk_rsp(torture, &cc_rsp,
996 1, /* chunks written */
997 0, /* chunk bytes unsuccessfully written */
998 4096); /* total bytes written */
999 if (!ok) {
1000 torture_fail(torture, "bad copy chunk response data");
1003 ok = check_pattern(torture, tree, tmp_ctx, src_h, 0, 4096, 0);
1004 if (!ok) {
1005 torture_fail(torture, "inconsistent file data");
1007 ok = check_pattern(torture, tree, tmp_ctx, src_h, 4096, 4096, 0);
1008 if (!ok) {
1009 torture_fail(torture, "inconsistent file data");
1012 smb2_util_close(tree, src_h);
1013 smb2_util_close(tree, dest_h);
1014 talloc_free(tmp_ctx);
1015 return true;
1019 * Test a single-chunk copychunk request, where the source and target ranges
1020 * overlap, and the SourceKey refers to the same target file. E.g:
1022 * Initial State
1023 * -------------
1024 * File: src_and_dest
1025 * Offset: 0123456789
1026 * Data: abcdefghij
1028 * Request
1029 * -------
1030 * FSCTL_SRV_COPYCHUNK(src_and_dest)
1031 * SourceKey = SRV_REQUEST_RESUME_KEY(src_and_dest)
1032 * ChunkCount = 1
1033 * Chunks[0].SourceOffset = 0
1034 * Chunks[0].TargetOffset = 4
1035 * Chunks[0].Length = 6
1037 * Resultant State
1038 * ---------------
1039 * File: src_and_dest
1040 * Offset: 0123456789
1041 * Data: abcdabcdef
1043 * The resultant contents of src_and_dest is dependent on the server's
1044 * copy algorithm. In the above example, the server uses an IO buffer
1045 * large enough to hold the entire six-byte source data before writing
1046 * to TargetOffset. If the server were to use a four-byte IO buffer and
1047 * started reads/writes from the lowest offset, then the two overlapping
1048 * bytes in the above example would be overwritten before being read. The
1049 * resultant file contents would be abcdabcdab.
1051 * Windows 2008r2 appears to use a 2048 byte copy buffer, overlapping bytes
1052 * after this offset are written before being read. Windows 2012 on the
1053 * other hand appears to use a buffer large enough to hold its maximum
1054 * supported chunk size (1M). Samba currently uses a 64k copy buffer by
1055 * default (vfs_cc_state.buf).
1057 * This test uses an 8-byte overlap at 2040-2048, so that it passes against
1058 * Windows 2008, 2012 and Samba servers.
1060 static bool
1061 test_ioctl_copy_chunk_src_is_dest_overlap(struct torture_context *torture,
1062 struct smb2_tree *tree)
1064 struct smb2_handle src_h;
1065 struct smb2_handle dest_h;
1066 NTSTATUS status;
1067 union smb_ioctl ioctl;
1068 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1069 struct srv_copychunk_copy cc_copy;
1070 struct srv_copychunk_rsp cc_rsp;
1071 enum ndr_err_code ndr_ret;
1072 bool ok;
1074 /* exceed the vfs_default copy buffer */
1075 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1077 &src_h, 2048 * 2,
1078 SEC_RIGHTS_FILE_ALL,
1079 &dest_h, 0,
1080 SEC_RIGHTS_FILE_ALL,
1081 &cc_copy,
1082 &ioctl);
1083 if (!ok) {
1084 torture_fail(torture, "setup copy chunk error");
1087 /* the source is also the destination */
1088 ioctl.smb2.in.file.handle = src_h;
1090 /* 8 bytes overlap between source and target ranges */
1091 cc_copy.chunks[0].source_off = 0;
1092 cc_copy.chunks[0].target_off = 2048 - 8;
1093 cc_copy.chunks[0].length = 2048;
1095 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1096 &cc_copy,
1097 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1098 torture_assert_ndr_success(torture, ndr_ret,
1099 "ndr_push_srv_copychunk_copy");
1101 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1102 torture_assert_ntstatus_ok(torture, status,
1103 "FSCTL_SRV_COPYCHUNK");
1105 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1106 &cc_rsp,
1107 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1108 torture_assert_ndr_success(torture, ndr_ret,
1109 "ndr_pull_srv_copychunk_rsp");
1111 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1112 1, /* chunks written */
1113 0, /* chunk bytes unsuccessfully written */
1114 2048); /* total bytes written */
1115 if (!ok) {
1116 torture_fail(torture, "bad copy chunk response data");
1119 ok = check_pattern(torture, tree, tmp_ctx, src_h, 0, 2048 - 8, 0);
1120 if (!ok) {
1121 torture_fail(torture, "inconsistent file data");
1123 ok = check_pattern(torture, tree, tmp_ctx, src_h, 2048 - 8, 2048, 0);
1124 if (!ok) {
1125 torture_fail(torture, "inconsistent file data");
1128 smb2_util_close(tree, src_h);
1129 smb2_util_close(tree, dest_h);
1130 talloc_free(tmp_ctx);
1131 return true;
1134 static bool test_ioctl_copy_chunk_bad_access(struct torture_context *torture,
1135 struct smb2_tree *tree)
1137 struct smb2_handle src_h;
1138 struct smb2_handle dest_h;
1139 NTSTATUS status;
1140 union smb_ioctl ioctl;
1141 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1142 struct srv_copychunk_copy cc_copy;
1143 enum ndr_err_code ndr_ret;
1144 bool ok;
1146 /* no read permission on src */
1147 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1148 1, /* 1 chunk */
1149 &src_h, 4096, /* fill 4096 byte src file */
1150 SEC_RIGHTS_FILE_WRITE,
1151 &dest_h, 0, /* 0 byte dest file */
1152 SEC_RIGHTS_FILE_ALL,
1153 &cc_copy,
1154 &ioctl);
1155 if (!ok) {
1156 torture_fail(torture, "setup copy chunk error");
1159 cc_copy.chunks[0].source_off = 0;
1160 cc_copy.chunks[0].target_off = 0;
1161 cc_copy.chunks[0].length = 4096;
1163 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1164 &cc_copy,
1165 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1166 torture_assert_ndr_success(torture, ndr_ret,
1167 "ndr_push_srv_copychunk_copy");
1169 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1170 torture_assert_ntstatus_equal(torture, status,
1171 NT_STATUS_ACCESS_DENIED,
1172 "FSCTL_SRV_COPYCHUNK");
1174 smb2_util_close(tree, src_h);
1175 smb2_util_close(tree, dest_h);
1177 /* no write permission on dest */
1178 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1179 1, /* 1 chunk */
1180 &src_h, 4096, /* fill 4096 byte src file */
1181 SEC_RIGHTS_FILE_ALL,
1182 &dest_h, 0, /* 0 byte dest file */
1183 (SEC_RIGHTS_FILE_READ
1184 | SEC_RIGHTS_FILE_EXECUTE),
1185 &cc_copy,
1186 &ioctl);
1187 if (!ok) {
1188 torture_fail(torture, "setup copy chunk error");
1191 cc_copy.chunks[0].source_off = 0;
1192 cc_copy.chunks[0].target_off = 0;
1193 cc_copy.chunks[0].length = 4096;
1195 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1196 &cc_copy,
1197 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1198 torture_assert_ndr_success(torture, ndr_ret,
1199 "ndr_push_srv_copychunk_copy");
1201 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1202 torture_assert_ntstatus_equal(torture, status,
1203 NT_STATUS_ACCESS_DENIED,
1204 "FSCTL_SRV_COPYCHUNK");
1206 smb2_util_close(tree, src_h);
1207 smb2_util_close(tree, dest_h);
1209 /* no read permission on dest */
1210 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1211 1, /* 1 chunk */
1212 &src_h, 4096, /* fill 4096 byte src file */
1213 SEC_RIGHTS_FILE_ALL,
1214 &dest_h, 0, /* 0 byte dest file */
1215 (SEC_RIGHTS_FILE_WRITE
1216 | SEC_RIGHTS_FILE_EXECUTE),
1217 &cc_copy,
1218 &ioctl);
1219 if (!ok) {
1220 torture_fail(torture, "setup copy chunk error");
1223 cc_copy.chunks[0].source_off = 0;
1224 cc_copy.chunks[0].target_off = 0;
1225 cc_copy.chunks[0].length = 4096;
1227 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1228 &cc_copy,
1229 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1230 torture_assert_ndr_success(torture, ndr_ret,
1231 "ndr_push_srv_copychunk_copy");
1234 * FSCTL_SRV_COPYCHUNK requires read permission on dest,
1235 * FSCTL_SRV_COPYCHUNK_WRITE (not supported by Samba) on the other hand
1236 * does not.
1238 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1239 torture_assert_ntstatus_equal(torture, status,
1240 NT_STATUS_ACCESS_DENIED,
1241 "FSCTL_SRV_COPYCHUNK");
1243 smb2_util_close(tree, src_h);
1244 smb2_util_close(tree, dest_h);
1245 talloc_free(tmp_ctx);
1247 return true;
1250 static bool test_ioctl_copy_chunk_src_exceed(struct torture_context *torture,
1251 struct smb2_tree *tree)
1253 struct smb2_handle src_h;
1254 struct smb2_handle dest_h;
1255 NTSTATUS status;
1256 union smb_ioctl ioctl;
1257 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1258 struct srv_copychunk_copy cc_copy;
1259 struct srv_copychunk_rsp cc_rsp;
1260 enum ndr_err_code ndr_ret;
1261 bool ok;
1263 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1264 1, /* 1 chunk */
1265 &src_h, 4096, /* fill 4096 byte src file */
1266 SEC_RIGHTS_FILE_ALL,
1267 &dest_h, 0, /* 0 byte dest file */
1268 SEC_RIGHTS_FILE_ALL,
1269 &cc_copy,
1270 &ioctl);
1271 if (!ok) {
1272 torture_fail(torture, "setup copy chunk error");
1275 /* Request copy where off + length exceeds size of src */
1276 cc_copy.chunks[0].source_off = 1024;
1277 cc_copy.chunks[0].target_off = 0;
1278 cc_copy.chunks[0].length = 4096;
1280 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1281 &cc_copy,
1282 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1283 torture_assert_ndr_success(torture, ndr_ret,
1284 "ndr_push_srv_copychunk_copy");
1286 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1287 torture_assert_ntstatus_equal(torture, status,
1288 NT_STATUS_INVALID_VIEW_SIZE,
1289 "FSCTL_SRV_COPYCHUNK oversize");
1291 /* Request copy where length exceeds size of src */
1292 cc_copy.chunks[0].source_off = 1024;
1293 cc_copy.chunks[0].target_off = 0;
1294 cc_copy.chunks[0].length = 3072;
1296 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1297 &cc_copy,
1298 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1299 torture_assert_ndr_success(torture, ndr_ret,
1300 "ndr_push_srv_copychunk_copy");
1302 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1303 torture_assert_ntstatus_ok(torture, status,
1304 "FSCTL_SRV_COPYCHUNK just right");
1306 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1307 &cc_rsp,
1308 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1309 torture_assert_ndr_success(torture, ndr_ret,
1310 "ndr_pull_srv_copychunk_rsp");
1312 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1313 1, /* chunks written */
1314 0, /* chunk bytes unsuccessfully written */
1315 3072); /* total bytes written */
1316 if (!ok) {
1317 torture_fail(torture, "bad copy chunk response data");
1320 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 3072, 1024);
1321 if (!ok) {
1322 torture_fail(torture, "inconsistent file data");
1325 smb2_util_close(tree, src_h);
1326 smb2_util_close(tree, dest_h);
1327 talloc_free(tmp_ctx);
1328 return true;
1331 static bool
1332 test_ioctl_copy_chunk_src_exceed_multi(struct torture_context *torture,
1333 struct smb2_tree *tree)
1335 struct smb2_handle src_h;
1336 struct smb2_handle dest_h;
1337 NTSTATUS status;
1338 union smb_ioctl ioctl;
1339 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1340 struct srv_copychunk_copy cc_copy;
1341 struct srv_copychunk_rsp cc_rsp;
1342 enum ndr_err_code ndr_ret;
1343 bool ok;
1345 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1346 2, /* 2 chunks */
1347 &src_h, 8192, /* fill 8192 byte src file */
1348 SEC_RIGHTS_FILE_ALL,
1349 &dest_h, 0, /* 0 byte dest file */
1350 SEC_RIGHTS_FILE_ALL,
1351 &cc_copy,
1352 &ioctl);
1353 if (!ok) {
1354 torture_fail(torture, "setup copy chunk error");
1357 /* Request copy where off + length exceeds size of src */
1358 cc_copy.chunks[0].source_off = 0;
1359 cc_copy.chunks[0].target_off = 0;
1360 cc_copy.chunks[0].length = 4096;
1362 cc_copy.chunks[1].source_off = 4096;
1363 cc_copy.chunks[1].target_off = 4096;
1364 cc_copy.chunks[1].length = 8192;
1366 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1367 &cc_copy,
1368 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1369 torture_assert_ndr_success(torture, ndr_ret,
1370 "ndr_push_srv_copychunk_copy");
1372 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1373 torture_assert_ntstatus_equal(torture, status,
1374 NT_STATUS_INVALID_VIEW_SIZE,
1375 "FSCTL_SRV_COPYCHUNK oversize");
1376 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1377 &cc_rsp,
1378 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1379 torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
1381 /* first chunk should still be written */
1382 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1383 1, /* chunks written */
1384 0, /* chunk bytes unsuccessfully written */
1385 4096); /* total bytes written */
1386 if (!ok) {
1387 torture_fail(torture, "bad copy chunk response data");
1389 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
1390 if (!ok) {
1391 torture_fail(torture, "inconsistent file data");
1394 smb2_util_close(tree, src_h);
1395 smb2_util_close(tree, dest_h);
1396 talloc_free(tmp_ctx);
1397 return true;
1400 static bool test_ioctl_copy_chunk_sparse_dest(struct torture_context *torture,
1401 struct smb2_tree *tree)
1403 struct smb2_handle src_h;
1404 struct smb2_handle dest_h;
1405 NTSTATUS status;
1406 union smb_ioctl ioctl;
1407 struct smb2_read r;
1408 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1409 struct srv_copychunk_copy cc_copy;
1410 struct srv_copychunk_rsp cc_rsp;
1411 enum ndr_err_code ndr_ret;
1412 bool ok;
1413 int i;
1415 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1416 1, /* 1 chunk */
1417 &src_h, 4096, /* fill 4096 byte src file */
1418 SEC_RIGHTS_FILE_ALL,
1419 &dest_h, 0, /* 0 byte dest file */
1420 SEC_RIGHTS_FILE_ALL,
1421 &cc_copy,
1422 &ioctl);
1423 if (!ok) {
1424 torture_fail(torture, "setup copy chunk error");
1427 /* copy all src file data (via a single chunk desc) */
1428 cc_copy.chunks[0].source_off = 0;
1429 cc_copy.chunks[0].target_off = 4096;
1430 cc_copy.chunks[0].length = 4096;
1432 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1433 &cc_copy,
1434 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1435 torture_assert_ndr_success(torture, ndr_ret,
1436 "ndr_push_srv_copychunk_copy");
1438 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1439 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
1441 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1442 &cc_rsp,
1443 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1444 torture_assert_ndr_success(torture, ndr_ret,
1445 "ndr_pull_srv_copychunk_rsp");
1447 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1448 1, /* chunks written */
1449 0, /* chunk bytes unsuccessfully written */
1450 4096); /* total bytes written */
1451 if (!ok) {
1452 torture_fail(torture, "bad copy chunk response data");
1455 /* check for zeros in first 4k */
1456 ZERO_STRUCT(r);
1457 r.in.file.handle = dest_h;
1458 r.in.length = 4096;
1459 r.in.offset = 0;
1460 status = smb2_read(tree, tmp_ctx, &r);
1461 torture_assert_ntstatus_ok(torture, status, "read");
1463 torture_assert_u64_equal(torture, r.out.data.length, 4096,
1464 "read data len mismatch");
1466 for (i = 0; i < 4096; i++) {
1467 torture_assert(torture, (r.out.data.data[i] == 0),
1468 "sparse did not pass class");
1471 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 4096, 4096, 0);
1472 if (!ok) {
1473 torture_fail(torture, "inconsistent file data");
1476 smb2_util_close(tree, src_h);
1477 smb2_util_close(tree, dest_h);
1478 talloc_free(tmp_ctx);
1479 return true;
1483 * set the ioctl MaxOutputResponse size to less than
1484 * sizeof(struct srv_copychunk_rsp)
1486 static bool test_ioctl_copy_chunk_max_output_sz(struct torture_context *torture,
1487 struct smb2_tree *tree)
1489 struct smb2_handle src_h;
1490 struct smb2_handle dest_h;
1491 NTSTATUS status;
1492 union smb_ioctl ioctl;
1493 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1494 struct srv_copychunk_copy cc_copy;
1495 enum ndr_err_code ndr_ret;
1496 bool ok;
1498 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1499 1, /* 1 chunk */
1500 &src_h, 4096, /* fill 4096 byte src file */
1501 SEC_RIGHTS_FILE_ALL,
1502 &dest_h, 0, /* 0 byte dest file */
1503 SEC_RIGHTS_FILE_ALL,
1504 &cc_copy,
1505 &ioctl);
1506 if (!ok) {
1507 torture_fail(torture, "setup copy chunk error");
1510 cc_copy.chunks[0].source_off = 0;
1511 cc_copy.chunks[0].target_off = 0;
1512 cc_copy.chunks[0].length = 4096;
1513 /* req is valid, but use undersize max_response_size */
1514 ioctl.smb2.in.max_response_size = sizeof(struct srv_copychunk_rsp) - 1;
1516 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1517 &cc_copy,
1518 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1519 torture_assert_ndr_success(torture, ndr_ret,
1520 "ndr_push_srv_copychunk_copy");
1522 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1523 torture_assert_ntstatus_equal(torture, status,
1524 NT_STATUS_INVALID_PARAMETER,
1525 "FSCTL_SRV_COPYCHUNK");
1527 smb2_util_close(tree, src_h);
1528 smb2_util_close(tree, dest_h);
1529 talloc_free(tmp_ctx);
1530 return true;
1534 basic testing of SMB2 ioctls
1536 struct torture_suite *torture_smb2_ioctl_init(void)
1538 struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "ioctl");
1540 torture_suite_add_1smb2_test(suite, "shadow_copy",
1541 test_ioctl_get_shadow_copy);
1542 torture_suite_add_1smb2_test(suite, "req_resume_key",
1543 test_ioctl_req_resume_key);
1544 torture_suite_add_1smb2_test(suite, "copy_chunk_simple",
1545 test_ioctl_copy_chunk_simple);
1546 torture_suite_add_1smb2_test(suite, "copy_chunk_multi",
1547 test_ioctl_copy_chunk_multi);
1548 torture_suite_add_1smb2_test(suite, "copy_chunk_tiny",
1549 test_ioctl_copy_chunk_tiny);
1550 torture_suite_add_1smb2_test(suite, "copy_chunk_overwrite",
1551 test_ioctl_copy_chunk_over);
1552 torture_suite_add_1smb2_test(suite, "copy_chunk_append",
1553 test_ioctl_copy_chunk_append);
1554 torture_suite_add_1smb2_test(suite, "copy_chunk_limits",
1555 test_ioctl_copy_chunk_limits);
1556 torture_suite_add_1smb2_test(suite, "copy_chunk_src_lock",
1557 test_ioctl_copy_chunk_src_lck);
1558 torture_suite_add_1smb2_test(suite, "copy_chunk_dest_lock",
1559 test_ioctl_copy_chunk_dest_lck);
1560 torture_suite_add_1smb2_test(suite, "copy_chunk_bad_key",
1561 test_ioctl_copy_chunk_bad_key);
1562 torture_suite_add_1smb2_test(suite, "copy_chunk_src_is_dest",
1563 test_ioctl_copy_chunk_src_is_dest);
1564 torture_suite_add_1smb2_test(suite, "copy_chunk_src_is_dest_overlap",
1565 test_ioctl_copy_chunk_src_is_dest_overlap);
1566 torture_suite_add_1smb2_test(suite, "copy_chunk_bad_access",
1567 test_ioctl_copy_chunk_bad_access);
1568 torture_suite_add_1smb2_test(suite, "copy_chunk_src_exceed",
1569 test_ioctl_copy_chunk_src_exceed);
1570 torture_suite_add_1smb2_test(suite, "copy_chunk_src_exceed_multi",
1571 test_ioctl_copy_chunk_src_exceed_multi);
1572 torture_suite_add_1smb2_test(suite, "copy_chunk_sparse_dest",
1573 test_ioctl_copy_chunk_sparse_dest);
1574 torture_suite_add_1smb2_test(suite, "copy_chunk_max_output_sz",
1575 test_ioctl_copy_chunk_max_output_sz);
1577 suite->description = talloc_strdup(suite, "SMB2-IOCTL tests");
1579 return suite;