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/>.
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)
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");
51 status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf));
52 torture_assert_ntstatus_ok(torture, status, "write");
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");
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)
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");
91 status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf));
92 torture_assert_ntstatus_ok(torture, status, "write");
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);
115 static uint64_t patt_hash(uint64_t 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,
130 r.in.file.handle = h;
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);
151 static bool test_setup_copy_chunk(struct torture_context *torture,
152 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
154 struct smb2_handle *src_h,
156 uint32_t src_desired_access,
157 struct smb2_handle *dest_h,
159 uint32_t dest_desired_access,
160 struct srv_copychunk_copy *cc_copy,
161 union smb_ioctl *ioctl)
163 struct req_resume_key_rsp res_key;
164 struct smb2_create io;
166 enum ndr_err_code ndr_ret;
168 uint8_t *buf = talloc_zero_size(mem_ctx, MAX(src_size, dest_size));
169 torture_assert(torture, (buf != NULL), "no memory for file data buf");
171 smb2_util_unlink(tree, FNAME);
172 smb2_util_unlink(tree, FNAME2);
175 io.in.desired_access = src_desired_access;
176 io.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
177 io.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
179 NTCREATEX_SHARE_ACCESS_DELETE|
180 NTCREATEX_SHARE_ACCESS_READ|
181 NTCREATEX_SHARE_ACCESS_WRITE;
184 status = smb2_create(tree, mem_ctx, &io);
185 torture_assert_ntstatus_ok(torture, status, "src create");
187 *src_h = io.out.file.handle;
190 uint64_t cur_off = 0;
191 for (i = 0; i <= src_size - 8; i += 8) {
192 SBVAL(buf, i, patt_hash(i));
194 while (src_size > 0) {
195 uint64_t io_sz = MIN(1024 * 1024, src_size);
196 status = smb2_util_write(tree, *src_h,
197 buf + cur_off, cur_off, io_sz);
198 torture_assert_ntstatus_ok(torture, status, "src write");
206 io.in.desired_access = dest_desired_access;
207 io.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
208 io.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
210 NTCREATEX_SHARE_ACCESS_DELETE|
211 NTCREATEX_SHARE_ACCESS_READ|
212 NTCREATEX_SHARE_ACCESS_WRITE;
213 io.in.fname = FNAME2;
215 status = smb2_create(tree, mem_ctx, &io);
216 torture_assert_ntstatus_ok(torture, status, "dest create");
218 *dest_h = io.out.file.handle;
221 uint64_t cur_off = 0;
222 for (i = 0; i <= dest_size - 8; i += 8) {
223 SBVAL(buf, i, patt_hash(i));
225 while (dest_size > 0) {
226 uint64_t io_sz = MIN(1024 * 1024, dest_size);
227 status = smb2_util_write(tree, *dest_h,
228 buf + cur_off, cur_off, io_sz);
229 torture_assert_ntstatus_ok(torture, status, "dest write");
236 ZERO_STRUCTPN(ioctl);
237 ioctl->smb2.level = RAW_IOCTL_SMB2;
238 ioctl->smb2.in.file.handle = *src_h;
239 ioctl->smb2.in.function = FSCTL_SRV_REQUEST_RESUME_KEY;
240 /* Allow for Key + ContextLength + Context */
241 ioctl->smb2.in.max_response_size = 32;
242 ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
244 status = smb2_ioctl(tree, mem_ctx, &ioctl->smb2);
245 torture_assert_ntstatus_ok(torture, status,
246 "FSCTL_SRV_REQUEST_RESUME_KEY");
249 ndr_ret = ndr_pull_struct_blob(&ioctl->smb2.out.out, mem_ctx, &res_key,
250 (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
252 torture_assert_ndr_success(torture, ndr_ret,
253 "ndr_pull_req_resume_key_rsp");
255 ZERO_STRUCTPN(ioctl);
256 ioctl->smb2.level = RAW_IOCTL_SMB2;
257 ioctl->smb2.in.file.handle = *dest_h;
258 ioctl->smb2.in.function = FSCTL_SRV_COPYCHUNK;
259 ioctl->smb2.in.max_response_size = sizeof(struct srv_copychunk_rsp);
260 ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
262 ZERO_STRUCTPN(cc_copy);
263 memcpy(cc_copy->source_key, res_key.resume_key, ARRAY_SIZE(cc_copy->source_key));
264 cc_copy->chunk_count = nchunks;
265 cc_copy->chunks = talloc_zero_array(mem_ctx, struct srv_copychunk, nchunks);
266 torture_assert(torture, (cc_copy->chunks != NULL), "no memory for chunks");
272 static bool check_copy_chunk_rsp(struct torture_context *torture,
273 struct srv_copychunk_rsp *cc_rsp,
274 uint32_t ex_chunks_written,
275 uint32_t ex_chunk_bytes_written,
276 uint32_t ex_total_bytes_written)
278 torture_assert_int_equal(torture, cc_rsp->chunks_written,
279 ex_chunks_written, "num chunks");
280 torture_assert_int_equal(torture, cc_rsp->chunk_bytes_written,
281 ex_chunk_bytes_written, "chunk bytes written");
282 torture_assert_int_equal(torture, cc_rsp->total_bytes_written,
283 ex_total_bytes_written, "chunk total bytes");
287 static bool test_ioctl_copy_chunk_simple(struct torture_context *torture,
288 struct smb2_tree *tree)
290 struct smb2_handle src_h;
291 struct smb2_handle dest_h;
293 union smb_ioctl ioctl;
294 TALLOC_CTX *tmp_ctx = talloc_new(tree);
295 struct srv_copychunk_copy cc_copy;
296 struct srv_copychunk_rsp cc_rsp;
297 enum ndr_err_code ndr_ret;
300 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
302 &src_h, 4096, /* fill 4096 byte src file */
304 &dest_h, 0, /* 0 byte dest file */
309 torture_fail(torture, "setup copy chunk error");
312 /* copy all src file data (via a single chunk desc) */
313 cc_copy.chunks[0].source_off = 0;
314 cc_copy.chunks[0].target_off = 0;
315 cc_copy.chunks[0].length = 4096;
317 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
319 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
320 torture_assert_ndr_success(torture, ndr_ret,
321 "ndr_push_srv_copychunk_copy");
323 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
324 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
326 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
328 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
329 torture_assert_ndr_success(torture, ndr_ret,
330 "ndr_pull_srv_copychunk_rsp");
332 ok = check_copy_chunk_rsp(torture, &cc_rsp,
333 1, /* chunks written */
334 0, /* chunk bytes unsuccessfully written */
335 4096); /* total bytes written */
337 torture_fail(torture, "bad copy chunk response data");
340 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
342 torture_fail(torture, "inconsistent file data");
345 smb2_util_close(tree, src_h);
346 smb2_util_close(tree, dest_h);
347 talloc_free(tmp_ctx);
351 static bool test_ioctl_copy_chunk_multi(struct torture_context *torture,
352 struct smb2_tree *tree)
354 struct smb2_handle src_h;
355 struct smb2_handle dest_h;
357 union smb_ioctl ioctl;
358 TALLOC_CTX *tmp_ctx = talloc_new(tree);
359 struct srv_copychunk_copy cc_copy;
360 struct srv_copychunk_rsp cc_rsp;
361 enum ndr_err_code ndr_ret;
364 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
366 &src_h, 8192, /* src file */
368 &dest_h, 0, /* dest file */
373 torture_fail(torture, "setup copy chunk error");
376 /* copy all src file data via two chunks */
377 cc_copy.chunks[0].source_off = 0;
378 cc_copy.chunks[0].target_off = 0;
379 cc_copy.chunks[0].length = 4096;
381 cc_copy.chunks[1].source_off = 4096;
382 cc_copy.chunks[1].target_off = 4096;
383 cc_copy.chunks[1].length = 4096;
385 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
387 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
388 torture_assert_ndr_success(torture, ndr_ret,
389 "ndr_push_srv_copychunk_copy");
391 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
392 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
394 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
396 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
397 torture_assert_ndr_success(torture, ndr_ret,
398 "ndr_pull_srv_copychunk_rsp");
400 ok = check_copy_chunk_rsp(torture, &cc_rsp,
401 2, /* chunks written */
402 0, /* chunk bytes unsuccessfully written */
403 8192); /* total bytes written */
405 torture_fail(torture, "bad copy chunk response data");
408 smb2_util_close(tree, src_h);
409 smb2_util_close(tree, dest_h);
410 talloc_free(tmp_ctx);
414 static bool test_ioctl_copy_chunk_tiny(struct torture_context *torture,
415 struct smb2_tree *tree)
417 struct smb2_handle src_h;
418 struct smb2_handle dest_h;
420 union smb_ioctl ioctl;
421 TALLOC_CTX *tmp_ctx = talloc_new(tree);
422 struct srv_copychunk_copy cc_copy;
423 struct srv_copychunk_rsp cc_rsp;
424 enum ndr_err_code ndr_ret;
427 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
429 &src_h, 100, /* src file */
431 &dest_h, 0, /* dest file */
436 torture_fail(torture, "setup copy chunk error");
439 /* copy all src file data via two chunks, sub block size chunks */
440 cc_copy.chunks[0].source_off = 0;
441 cc_copy.chunks[0].target_off = 0;
442 cc_copy.chunks[0].length = 50;
444 cc_copy.chunks[1].source_off = 50;
445 cc_copy.chunks[1].target_off = 50;
446 cc_copy.chunks[1].length = 50;
448 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
450 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
451 torture_assert_ndr_success(torture, ndr_ret,
452 "ndr_push_srv_copychunk_copy");
454 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
455 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
457 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
459 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
460 torture_assert_ndr_success(torture, ndr_ret,
461 "ndr_pull_srv_copychunk_rsp");
463 ok = check_copy_chunk_rsp(torture, &cc_rsp,
464 2, /* chunks written */
465 0, /* chunk bytes unsuccessfully written */
466 100); /* total bytes written */
468 torture_fail(torture, "bad copy chunk response data");
471 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 100, 0);
473 torture_fail(torture, "inconsistent file data");
476 smb2_util_close(tree, src_h);
477 smb2_util_close(tree, dest_h);
478 talloc_free(tmp_ctx);
482 static bool test_ioctl_copy_chunk_over(struct torture_context *torture,
483 struct smb2_tree *tree)
485 struct smb2_handle src_h;
486 struct smb2_handle dest_h;
488 union smb_ioctl ioctl;
489 TALLOC_CTX *tmp_ctx = talloc_new(tree);
490 struct srv_copychunk_copy cc_copy;
491 struct srv_copychunk_rsp cc_rsp;
492 enum ndr_err_code ndr_ret;
495 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
497 &src_h, 8192, /* src file */
499 &dest_h, 4096, /* dest file */
504 torture_fail(torture, "setup copy chunk error");
507 /* first chunk overwrites existing dest data */
508 cc_copy.chunks[0].source_off = 0;
509 cc_copy.chunks[0].target_off = 0;
510 cc_copy.chunks[0].length = 4096;
512 /* second chunk overwrites the first */
513 cc_copy.chunks[1].source_off = 4096;
514 cc_copy.chunks[1].target_off = 0;
515 cc_copy.chunks[1].length = 4096;
517 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
519 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
520 torture_assert_ndr_success(torture, ndr_ret,
521 "ndr_push_srv_copychunk_copy");
523 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
524 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
526 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
528 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
529 torture_assert_ndr_success(torture, ndr_ret,
530 "ndr_pull_srv_copychunk_rsp");
532 ok = check_copy_chunk_rsp(torture, &cc_rsp,
533 2, /* chunks written */
534 0, /* chunk bytes unsuccessfully written */
535 8192); /* total bytes written */
537 torture_fail(torture, "bad copy chunk response data");
540 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 4096);
542 torture_fail(torture, "inconsistent file data");
545 smb2_util_close(tree, src_h);
546 smb2_util_close(tree, dest_h);
547 talloc_free(tmp_ctx);
551 static bool test_ioctl_copy_chunk_append(struct torture_context *torture,
552 struct smb2_tree *tree)
554 struct smb2_handle src_h;
555 struct smb2_handle dest_h;
557 union smb_ioctl ioctl;
558 TALLOC_CTX *tmp_ctx = talloc_new(tree);
559 struct srv_copychunk_copy cc_copy;
560 struct srv_copychunk_rsp cc_rsp;
561 enum ndr_err_code ndr_ret;
564 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
566 &src_h, 4096, /* src file */
568 &dest_h, 0, /* dest file */
573 torture_fail(torture, "setup copy chunk error");
576 cc_copy.chunks[0].source_off = 0;
577 cc_copy.chunks[0].target_off = 0;
578 cc_copy.chunks[0].length = 4096;
580 /* second chunk appends the same data to the first */
581 cc_copy.chunks[1].source_off = 0;
582 cc_copy.chunks[1].target_off = 4096;
583 cc_copy.chunks[1].length = 4096;
585 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
587 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
588 torture_assert_ndr_success(torture, ndr_ret,
589 "ndr_push_srv_copychunk_copy");
591 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
592 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
594 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
596 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
597 torture_assert_ndr_success(torture, ndr_ret,
598 "ndr_pull_srv_copychunk_rsp");
600 ok = check_copy_chunk_rsp(torture, &cc_rsp,
601 2, /* chunks written */
602 0, /* chunk bytes unsuccessfully written */
603 8192); /* total bytes written */
605 torture_fail(torture, "bad copy chunk response data");
608 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
610 torture_fail(torture, "inconsistent file data");
613 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 4096, 4096, 0);
615 torture_fail(torture, "inconsistent file data");
618 smb2_util_close(tree, src_h);
619 smb2_util_close(tree, dest_h);
620 talloc_free(tmp_ctx);
624 static bool test_ioctl_copy_chunk_limits(struct torture_context *torture,
625 struct smb2_tree *tree)
627 struct smb2_handle src_h;
628 struct smb2_handle dest_h;
630 union smb_ioctl ioctl;
631 TALLOC_CTX *tmp_ctx = talloc_new(tree);
632 struct srv_copychunk_copy cc_copy;
633 struct srv_copychunk_rsp cc_rsp;
634 enum ndr_err_code ndr_ret;
637 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
639 &src_h, 4096, /* src file */
641 &dest_h, 0, /* dest file */
646 torture_fail(torture, "setup copy chunk error");
649 /* send huge chunk length request */
650 cc_copy.chunks[0].source_off = 0;
651 cc_copy.chunks[0].target_off = 0;
652 cc_copy.chunks[0].length = UINT_MAX;
654 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
656 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
657 torture_assert_ndr_success(torture, ndr_ret, "marshalling request");
659 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
660 torture_assert_ntstatus_equal(torture, status,
661 NT_STATUS_INVALID_PARAMETER,
662 "bad oversize chunk response");
664 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
666 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
667 torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
669 torture_comment(torture, "limit max chunks, got %u\n",
670 cc_rsp.chunks_written);
671 torture_comment(torture, "limit max chunk len, got %u\n",
672 cc_rsp.chunk_bytes_written);
673 torture_comment(torture, "limit max total bytes, got %u\n",
674 cc_rsp.total_bytes_written);
676 smb2_util_close(tree, src_h);
677 smb2_util_close(tree, dest_h);
678 talloc_free(tmp_ctx);
682 static bool test_ioctl_copy_chunk_src_lck(struct torture_context *torture,
683 struct smb2_tree *tree)
685 struct smb2_handle src_h;
686 struct smb2_handle src_h2;
687 struct smb2_handle dest_h;
689 union smb_ioctl ioctl;
690 TALLOC_CTX *tmp_ctx = talloc_new(tree);
691 struct srv_copychunk_copy cc_copy;
692 struct srv_copychunk_rsp cc_rsp;
693 enum ndr_err_code ndr_ret;
695 struct smb2_lock lck;
696 struct smb2_lock_element el[1];
698 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
700 &src_h, 4096, /* src file */
702 &dest_h, 0, /* dest file */
707 torture_fail(torture, "setup copy chunk error");
710 cc_copy.chunks[0].source_off = 0;
711 cc_copy.chunks[0].target_off = 0;
712 cc_copy.chunks[0].length = 4096;
714 /* open and lock the copychunk src file */
715 status = torture_smb2_testfile(tree, FNAME, &src_h2);
716 torture_assert_ntstatus_ok(torture, status, "2nd src open");
718 lck.in.lock_count = 0x0001;
719 lck.in.lock_sequence = 0x00000000;
720 lck.in.file.handle = src_h2;
722 el[0].offset = cc_copy.chunks[0].source_off;
723 el[0].length = cc_copy.chunks[0].length;
725 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
727 status = smb2_lock(tree, &lck);
728 torture_assert_ntstatus_ok(torture, status, "lock");
730 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
732 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
733 torture_assert_ndr_success(torture, ndr_ret,
734 "ndr_push_srv_copychunk_copy");
736 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
738 * 2k12 & Samba return lock_conflict, Windows 7 & 2k8 return success...
740 * Edgar Olougouna @ MS wrote:
741 * Regarding the FSCTL_SRV_COPYCHUNK and STATUS_FILE_LOCK_CONFLICT
742 * discrepancy observed between Windows versions, we confirm that the
743 * behavior change is expected.
745 * CopyChunk in Windows Server 2012 use regular Readfile/Writefile APIs
746 * to move the chunks from the source to the destination.
747 * These ReadFile/WriteFile APIs go through the byte-range lock checks,
748 * and this explains the observed STATUS_FILE_LOCK_CONFLICT error.
750 * Prior to Windows Server 2012, CopyChunk used mapped sections to move
751 * the data. And byte range locks are not enforced on mapped I/O, and
752 * this explains the STATUS_SUCCESS observed on Windows Server 2008 R2.
754 torture_assert_ntstatus_equal(torture, status,
755 NT_STATUS_FILE_LOCK_CONFLICT,
756 "FSCTL_SRV_COPYCHUNK locked");
758 /* should get cc response data with the lock conflict status */
759 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
761 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
762 torture_assert_ndr_success(torture, ndr_ret,
763 "ndr_pull_srv_copychunk_rsp");
764 ok = check_copy_chunk_rsp(torture, &cc_rsp,
765 0, /* chunks written */
766 0, /* chunk bytes unsuccessfully written */
767 0); /* total bytes written */
769 lck.in.lock_count = 0x0001;
770 lck.in.lock_sequence = 0x00000001;
771 lck.in.file.handle = src_h2;
773 el[0].offset = cc_copy.chunks[0].source_off;
774 el[0].length = cc_copy.chunks[0].length;
776 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
777 status = smb2_lock(tree, &lck);
778 torture_assert_ntstatus_ok(torture, status, "unlock");
780 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
781 torture_assert_ntstatus_ok(torture, status,
782 "FSCTL_SRV_COPYCHUNK unlocked");
784 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
786 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
787 torture_assert_ndr_success(torture, ndr_ret,
788 "ndr_pull_srv_copychunk_rsp");
790 ok = check_copy_chunk_rsp(torture, &cc_rsp,
791 1, /* chunks written */
792 0, /* chunk bytes unsuccessfully written */
793 4096); /* total bytes written */
795 torture_fail(torture, "bad copy chunk response data");
798 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
800 torture_fail(torture, "inconsistent file data");
803 smb2_util_close(tree, src_h2);
804 smb2_util_close(tree, src_h);
805 smb2_util_close(tree, dest_h);
806 talloc_free(tmp_ctx);
810 static bool test_ioctl_copy_chunk_dest_lck(struct torture_context *torture,
811 struct smb2_tree *tree)
813 struct smb2_handle src_h;
814 struct smb2_handle dest_h;
815 struct smb2_handle dest_h2;
817 union smb_ioctl ioctl;
818 TALLOC_CTX *tmp_ctx = talloc_new(tree);
819 struct srv_copychunk_copy cc_copy;
820 struct srv_copychunk_rsp cc_rsp;
821 enum ndr_err_code ndr_ret;
823 struct smb2_lock lck;
824 struct smb2_lock_element el[1];
826 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
828 &src_h, 4096, /* src file */
830 &dest_h, 4096, /* dest file */
835 torture_fail(torture, "setup copy chunk error");
838 cc_copy.chunks[0].source_off = 0;
839 cc_copy.chunks[0].target_off = 0;
840 cc_copy.chunks[0].length = 4096;
842 /* open and lock the copychunk dest file */
843 status = torture_smb2_testfile(tree, FNAME2, &dest_h2);
844 torture_assert_ntstatus_ok(torture, status, "2nd src open");
846 lck.in.lock_count = 0x0001;
847 lck.in.lock_sequence = 0x00000000;
848 lck.in.file.handle = dest_h2;
850 el[0].offset = cc_copy.chunks[0].target_off;
851 el[0].length = cc_copy.chunks[0].length;
853 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
855 status = smb2_lock(tree, &lck);
856 torture_assert_ntstatus_ok(torture, status, "lock");
858 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
860 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
861 torture_assert_ndr_success(torture, ndr_ret,
862 "ndr_push_srv_copychunk_copy");
864 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
865 torture_assert_ntstatus_equal(torture, status,
866 NT_STATUS_FILE_LOCK_CONFLICT,
867 "FSCTL_SRV_COPYCHUNK locked");
869 lck.in.lock_count = 0x0001;
870 lck.in.lock_sequence = 0x00000001;
871 lck.in.file.handle = dest_h2;
873 el[0].offset = cc_copy.chunks[0].target_off;
874 el[0].length = cc_copy.chunks[0].length;
876 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
877 status = smb2_lock(tree, &lck);
878 torture_assert_ntstatus_ok(torture, status, "unlock");
880 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
881 torture_assert_ntstatus_ok(torture, status,
882 "FSCTL_SRV_COPYCHUNK unlocked");
884 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
886 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
887 torture_assert_ndr_success(torture, ndr_ret,
888 "ndr_pull_srv_copychunk_rsp");
890 ok = check_copy_chunk_rsp(torture, &cc_rsp,
891 1, /* chunks written */
892 0, /* chunk bytes unsuccessfully written */
893 4096); /* total bytes written */
895 torture_fail(torture, "bad copy chunk response data");
898 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
900 torture_fail(torture, "inconsistent file data");
903 smb2_util_close(tree, dest_h2);
904 smb2_util_close(tree, src_h);
905 smb2_util_close(tree, dest_h);
906 talloc_free(tmp_ctx);
910 static bool test_ioctl_copy_chunk_bad_key(struct torture_context *torture,
911 struct smb2_tree *tree)
913 struct smb2_handle src_h;
914 struct smb2_handle dest_h;
916 union smb_ioctl ioctl;
917 TALLOC_CTX *tmp_ctx = talloc_new(tree);
918 struct srv_copychunk_copy cc_copy;
919 enum ndr_err_code ndr_ret;
922 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
931 torture_fail(torture, "setup copy chunk error");
934 /* overwrite the resume key with a bogus value */
935 memcpy(cc_copy.source_key, "deadbeefdeadbeefdeadbeef", 24);
937 cc_copy.chunks[0].source_off = 0;
938 cc_copy.chunks[0].target_off = 0;
939 cc_copy.chunks[0].length = 4096;
941 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
943 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
944 torture_assert_ndr_success(torture, ndr_ret,
945 "ndr_push_srv_copychunk_copy");
947 /* Server 2k12 returns NT_STATUS_OBJECT_NAME_NOT_FOUND */
948 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
949 torture_assert_ntstatus_equal(torture, status,
950 NT_STATUS_OBJECT_NAME_NOT_FOUND,
951 "FSCTL_SRV_COPYCHUNK");
953 smb2_util_close(tree, src_h);
954 smb2_util_close(tree, dest_h);
955 talloc_free(tmp_ctx);
959 static bool test_ioctl_copy_chunk_src_is_dest(struct torture_context *torture,
960 struct smb2_tree *tree)
962 struct smb2_handle src_h;
963 struct smb2_handle dest_h;
965 union smb_ioctl ioctl;
966 TALLOC_CTX *tmp_ctx = talloc_new(tree);
967 struct srv_copychunk_copy cc_copy;
968 struct srv_copychunk_rsp cc_rsp;
969 enum ndr_err_code ndr_ret;
972 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
981 torture_fail(torture, "setup copy chunk error");
984 /* the source is also the destination */
985 ioctl.smb2.in.file.handle = src_h;
987 /* non-overlapping */
988 cc_copy.chunks[0].source_off = 0;
989 cc_copy.chunks[0].target_off = 4096;
990 cc_copy.chunks[0].length = 4096;
992 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
994 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
995 torture_assert_ndr_success(torture, ndr_ret,
996 "ndr_push_srv_copychunk_copy");
998 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
999 torture_assert_ntstatus_ok(torture, status,
1000 "FSCTL_SRV_COPYCHUNK");
1002 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1004 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1005 torture_assert_ndr_success(torture, ndr_ret,
1006 "ndr_pull_srv_copychunk_rsp");
1008 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1009 1, /* chunks written */
1010 0, /* chunk bytes unsuccessfully written */
1011 4096); /* total bytes written */
1013 torture_fail(torture, "bad copy chunk response data");
1016 ok = check_pattern(torture, tree, tmp_ctx, src_h, 0, 4096, 0);
1018 torture_fail(torture, "inconsistent file data");
1020 ok = check_pattern(torture, tree, tmp_ctx, src_h, 4096, 4096, 0);
1022 torture_fail(torture, "inconsistent file data");
1025 smb2_util_close(tree, src_h);
1026 smb2_util_close(tree, dest_h);
1027 talloc_free(tmp_ctx);
1032 * Test a single-chunk copychunk request, where the source and target ranges
1033 * overlap, and the SourceKey refers to the same target file. E.g:
1037 * File: src_and_dest
1038 * Offset: 0123456789
1043 * FSCTL_SRV_COPYCHUNK(src_and_dest)
1044 * SourceKey = SRV_REQUEST_RESUME_KEY(src_and_dest)
1046 * Chunks[0].SourceOffset = 0
1047 * Chunks[0].TargetOffset = 4
1048 * Chunks[0].Length = 6
1052 * File: src_and_dest
1053 * Offset: 0123456789
1056 * The resultant contents of src_and_dest is dependent on the server's
1057 * copy algorithm. In the above example, the server uses an IO buffer
1058 * large enough to hold the entire six-byte source data before writing
1059 * to TargetOffset. If the server were to use a four-byte IO buffer and
1060 * started reads/writes from the lowest offset, then the two overlapping
1061 * bytes in the above example would be overwritten before being read. The
1062 * resultant file contents would be abcdabcdab.
1064 * Windows 2008r2 appears to use a 2048 byte copy buffer, overlapping bytes
1065 * after this offset are written before being read. Windows 2012 on the
1066 * other hand appears to use a buffer large enough to hold its maximum
1067 * supported chunk size (1M). Samba currently uses a 64k copy buffer by
1068 * default (vfs_cc_state.buf).
1070 * This test uses an 8-byte overlap at 2040-2048, so that it passes against
1071 * Windows 2008, 2012 and Samba servers.
1074 test_ioctl_copy_chunk_src_is_dest_overlap(struct torture_context *torture,
1075 struct smb2_tree *tree)
1077 struct smb2_handle src_h;
1078 struct smb2_handle dest_h;
1080 union smb_ioctl ioctl;
1081 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1082 struct srv_copychunk_copy cc_copy;
1083 struct srv_copychunk_rsp cc_rsp;
1084 enum ndr_err_code ndr_ret;
1087 /* exceed the vfs_default copy buffer */
1088 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1091 SEC_RIGHTS_FILE_ALL,
1093 SEC_RIGHTS_FILE_ALL,
1097 torture_fail(torture, "setup copy chunk error");
1100 /* the source is also the destination */
1101 ioctl.smb2.in.file.handle = src_h;
1103 /* 8 bytes overlap between source and target ranges */
1104 cc_copy.chunks[0].source_off = 0;
1105 cc_copy.chunks[0].target_off = 2048 - 8;
1106 cc_copy.chunks[0].length = 2048;
1108 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1110 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1111 torture_assert_ndr_success(torture, ndr_ret,
1112 "ndr_push_srv_copychunk_copy");
1114 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1115 torture_assert_ntstatus_ok(torture, status,
1116 "FSCTL_SRV_COPYCHUNK");
1118 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1120 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1121 torture_assert_ndr_success(torture, ndr_ret,
1122 "ndr_pull_srv_copychunk_rsp");
1124 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1125 1, /* chunks written */
1126 0, /* chunk bytes unsuccessfully written */
1127 2048); /* total bytes written */
1129 torture_fail(torture, "bad copy chunk response data");
1132 ok = check_pattern(torture, tree, tmp_ctx, src_h, 0, 2048 - 8, 0);
1134 torture_fail(torture, "inconsistent file data");
1136 ok = check_pattern(torture, tree, tmp_ctx, src_h, 2048 - 8, 2048, 0);
1138 torture_fail(torture, "inconsistent file data");
1141 smb2_util_close(tree, src_h);
1142 smb2_util_close(tree, dest_h);
1143 talloc_free(tmp_ctx);
1147 static bool test_ioctl_copy_chunk_bad_access(struct torture_context *torture,
1148 struct smb2_tree *tree)
1150 struct smb2_handle src_h;
1151 struct smb2_handle dest_h;
1153 union smb_ioctl ioctl;
1154 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1155 struct srv_copychunk_copy cc_copy;
1156 enum ndr_err_code ndr_ret;
1159 /* no read permission on src */
1160 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1162 &src_h, 4096, /* fill 4096 byte src file */
1163 SEC_RIGHTS_FILE_WRITE,
1164 &dest_h, 0, /* 0 byte dest file */
1165 SEC_RIGHTS_FILE_ALL,
1169 torture_fail(torture, "setup copy chunk error");
1172 cc_copy.chunks[0].source_off = 0;
1173 cc_copy.chunks[0].target_off = 0;
1174 cc_copy.chunks[0].length = 4096;
1176 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1178 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1179 torture_assert_ndr_success(torture, ndr_ret,
1180 "ndr_push_srv_copychunk_copy");
1182 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1183 torture_assert_ntstatus_equal(torture, status,
1184 NT_STATUS_ACCESS_DENIED,
1185 "FSCTL_SRV_COPYCHUNK");
1187 smb2_util_close(tree, src_h);
1188 smb2_util_close(tree, dest_h);
1190 /* no write permission on dest */
1191 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1193 &src_h, 4096, /* fill 4096 byte src file */
1194 SEC_RIGHTS_FILE_ALL,
1195 &dest_h, 0, /* 0 byte dest file */
1196 (SEC_RIGHTS_FILE_READ
1197 | SEC_RIGHTS_FILE_EXECUTE),
1201 torture_fail(torture, "setup copy chunk error");
1204 cc_copy.chunks[0].source_off = 0;
1205 cc_copy.chunks[0].target_off = 0;
1206 cc_copy.chunks[0].length = 4096;
1208 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1210 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1211 torture_assert_ndr_success(torture, ndr_ret,
1212 "ndr_push_srv_copychunk_copy");
1214 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1215 torture_assert_ntstatus_equal(torture, status,
1216 NT_STATUS_ACCESS_DENIED,
1217 "FSCTL_SRV_COPYCHUNK");
1219 smb2_util_close(tree, src_h);
1220 smb2_util_close(tree, dest_h);
1222 /* no read permission on dest */
1223 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1225 &src_h, 4096, /* fill 4096 byte src file */
1226 SEC_RIGHTS_FILE_ALL,
1227 &dest_h, 0, /* 0 byte dest file */
1228 (SEC_RIGHTS_FILE_WRITE
1229 | SEC_RIGHTS_FILE_EXECUTE),
1233 torture_fail(torture, "setup copy chunk error");
1236 cc_copy.chunks[0].source_off = 0;
1237 cc_copy.chunks[0].target_off = 0;
1238 cc_copy.chunks[0].length = 4096;
1240 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1242 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1243 torture_assert_ndr_success(torture, ndr_ret,
1244 "ndr_push_srv_copychunk_copy");
1247 * FSCTL_SRV_COPYCHUNK requires read permission on dest,
1248 * FSCTL_SRV_COPYCHUNK_WRITE (not supported by Samba) on the other hand
1251 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1252 torture_assert_ntstatus_equal(torture, status,
1253 NT_STATUS_ACCESS_DENIED,
1254 "FSCTL_SRV_COPYCHUNK");
1256 smb2_util_close(tree, src_h);
1257 smb2_util_close(tree, dest_h);
1258 talloc_free(tmp_ctx);
1263 static bool test_ioctl_copy_chunk_src_exceed(struct torture_context *torture,
1264 struct smb2_tree *tree)
1266 struct smb2_handle src_h;
1267 struct smb2_handle dest_h;
1269 union smb_ioctl ioctl;
1270 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1271 struct srv_copychunk_copy cc_copy;
1272 struct srv_copychunk_rsp cc_rsp;
1273 enum ndr_err_code ndr_ret;
1276 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1278 &src_h, 4096, /* fill 4096 byte src file */
1279 SEC_RIGHTS_FILE_ALL,
1280 &dest_h, 0, /* 0 byte dest file */
1281 SEC_RIGHTS_FILE_ALL,
1285 torture_fail(torture, "setup copy chunk error");
1288 /* Request copy where off + length exceeds size of src */
1289 cc_copy.chunks[0].source_off = 1024;
1290 cc_copy.chunks[0].target_off = 0;
1291 cc_copy.chunks[0].length = 4096;
1293 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1295 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1296 torture_assert_ndr_success(torture, ndr_ret,
1297 "ndr_push_srv_copychunk_copy");
1299 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1300 torture_assert_ntstatus_equal(torture, status,
1301 NT_STATUS_INVALID_VIEW_SIZE,
1302 "FSCTL_SRV_COPYCHUNK oversize");
1304 /* Request copy where length exceeds size of src */
1305 cc_copy.chunks[0].source_off = 1024;
1306 cc_copy.chunks[0].target_off = 0;
1307 cc_copy.chunks[0].length = 3072;
1309 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1311 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1312 torture_assert_ndr_success(torture, ndr_ret,
1313 "ndr_push_srv_copychunk_copy");
1315 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1316 torture_assert_ntstatus_ok(torture, status,
1317 "FSCTL_SRV_COPYCHUNK just right");
1319 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1321 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1322 torture_assert_ndr_success(torture, ndr_ret,
1323 "ndr_pull_srv_copychunk_rsp");
1325 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1326 1, /* chunks written */
1327 0, /* chunk bytes unsuccessfully written */
1328 3072); /* total bytes written */
1330 torture_fail(torture, "bad copy chunk response data");
1333 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 3072, 1024);
1335 torture_fail(torture, "inconsistent file data");
1338 smb2_util_close(tree, src_h);
1339 smb2_util_close(tree, dest_h);
1340 talloc_free(tmp_ctx);
1345 test_ioctl_copy_chunk_src_exceed_multi(struct torture_context *torture,
1346 struct smb2_tree *tree)
1348 struct smb2_handle src_h;
1349 struct smb2_handle dest_h;
1351 union smb_ioctl ioctl;
1352 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1353 struct srv_copychunk_copy cc_copy;
1354 struct srv_copychunk_rsp cc_rsp;
1355 enum ndr_err_code ndr_ret;
1358 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1360 &src_h, 8192, /* fill 8192 byte src file */
1361 SEC_RIGHTS_FILE_ALL,
1362 &dest_h, 0, /* 0 byte dest file */
1363 SEC_RIGHTS_FILE_ALL,
1367 torture_fail(torture, "setup copy chunk error");
1370 /* Request copy where off + length exceeds size of src */
1371 cc_copy.chunks[0].source_off = 0;
1372 cc_copy.chunks[0].target_off = 0;
1373 cc_copy.chunks[0].length = 4096;
1375 cc_copy.chunks[1].source_off = 4096;
1376 cc_copy.chunks[1].target_off = 4096;
1377 cc_copy.chunks[1].length = 8192;
1379 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1381 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1382 torture_assert_ndr_success(torture, ndr_ret,
1383 "ndr_push_srv_copychunk_copy");
1385 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1386 torture_assert_ntstatus_equal(torture, status,
1387 NT_STATUS_INVALID_VIEW_SIZE,
1388 "FSCTL_SRV_COPYCHUNK oversize");
1389 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1391 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1392 torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
1394 /* first chunk should still be written */
1395 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1396 1, /* chunks written */
1397 0, /* chunk bytes unsuccessfully written */
1398 4096); /* total bytes written */
1400 torture_fail(torture, "bad copy chunk response data");
1402 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
1404 torture_fail(torture, "inconsistent file data");
1407 smb2_util_close(tree, src_h);
1408 smb2_util_close(tree, dest_h);
1409 talloc_free(tmp_ctx);
1413 static bool test_ioctl_copy_chunk_sparse_dest(struct torture_context *torture,
1414 struct smb2_tree *tree)
1416 struct smb2_handle src_h;
1417 struct smb2_handle dest_h;
1419 union smb_ioctl ioctl;
1421 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1422 struct srv_copychunk_copy cc_copy;
1423 struct srv_copychunk_rsp cc_rsp;
1424 enum ndr_err_code ndr_ret;
1428 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1430 &src_h, 4096, /* fill 4096 byte src file */
1431 SEC_RIGHTS_FILE_ALL,
1432 &dest_h, 0, /* 0 byte dest file */
1433 SEC_RIGHTS_FILE_ALL,
1437 torture_fail(torture, "setup copy chunk error");
1440 /* copy all src file data (via a single chunk desc) */
1441 cc_copy.chunks[0].source_off = 0;
1442 cc_copy.chunks[0].target_off = 4096;
1443 cc_copy.chunks[0].length = 4096;
1445 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1447 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1448 torture_assert_ndr_success(torture, ndr_ret,
1449 "ndr_push_srv_copychunk_copy");
1451 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1452 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
1454 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1456 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1457 torture_assert_ndr_success(torture, ndr_ret,
1458 "ndr_pull_srv_copychunk_rsp");
1460 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1461 1, /* chunks written */
1462 0, /* chunk bytes unsuccessfully written */
1463 4096); /* total bytes written */
1465 torture_fail(torture, "bad copy chunk response data");
1468 /* check for zeros in first 4k */
1470 r.in.file.handle = dest_h;
1473 status = smb2_read(tree, tmp_ctx, &r);
1474 torture_assert_ntstatus_ok(torture, status, "read");
1476 torture_assert_u64_equal(torture, r.out.data.length, 4096,
1477 "read data len mismatch");
1479 for (i = 0; i < 4096; i++) {
1480 torture_assert(torture, (r.out.data.data[i] == 0),
1481 "sparse did not pass class");
1484 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 4096, 4096, 0);
1486 torture_fail(torture, "inconsistent file data");
1489 smb2_util_close(tree, src_h);
1490 smb2_util_close(tree, dest_h);
1491 talloc_free(tmp_ctx);
1496 * set the ioctl MaxOutputResponse size to less than
1497 * sizeof(struct srv_copychunk_rsp)
1499 static bool test_ioctl_copy_chunk_max_output_sz(struct torture_context *torture,
1500 struct smb2_tree *tree)
1502 struct smb2_handle src_h;
1503 struct smb2_handle dest_h;
1505 union smb_ioctl ioctl;
1506 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1507 struct srv_copychunk_copy cc_copy;
1508 enum ndr_err_code ndr_ret;
1511 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1513 &src_h, 4096, /* fill 4096 byte src file */
1514 SEC_RIGHTS_FILE_ALL,
1515 &dest_h, 0, /* 0 byte dest file */
1516 SEC_RIGHTS_FILE_ALL,
1520 torture_fail(torture, "setup copy chunk error");
1523 cc_copy.chunks[0].source_off = 0;
1524 cc_copy.chunks[0].target_off = 0;
1525 cc_copy.chunks[0].length = 4096;
1526 /* req is valid, but use undersize max_response_size */
1527 ioctl.smb2.in.max_response_size = sizeof(struct srv_copychunk_rsp) - 1;
1529 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1531 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1532 torture_assert_ndr_success(torture, ndr_ret,
1533 "ndr_push_srv_copychunk_copy");
1535 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1536 torture_assert_ntstatus_equal(torture, status,
1537 NT_STATUS_INVALID_PARAMETER,
1538 "FSCTL_SRV_COPYCHUNK");
1540 smb2_util_close(tree, src_h);
1541 smb2_util_close(tree, dest_h);
1542 talloc_free(tmp_ctx);
1547 basic testing of SMB2 ioctls
1549 struct torture_suite *torture_smb2_ioctl_init(void)
1551 struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "ioctl");
1553 torture_suite_add_1smb2_test(suite, "shadow_copy",
1554 test_ioctl_get_shadow_copy);
1555 torture_suite_add_1smb2_test(suite, "req_resume_key",
1556 test_ioctl_req_resume_key);
1557 torture_suite_add_1smb2_test(suite, "copy_chunk_simple",
1558 test_ioctl_copy_chunk_simple);
1559 torture_suite_add_1smb2_test(suite, "copy_chunk_multi",
1560 test_ioctl_copy_chunk_multi);
1561 torture_suite_add_1smb2_test(suite, "copy_chunk_tiny",
1562 test_ioctl_copy_chunk_tiny);
1563 torture_suite_add_1smb2_test(suite, "copy_chunk_overwrite",
1564 test_ioctl_copy_chunk_over);
1565 torture_suite_add_1smb2_test(suite, "copy_chunk_append",
1566 test_ioctl_copy_chunk_append);
1567 torture_suite_add_1smb2_test(suite, "copy_chunk_limits",
1568 test_ioctl_copy_chunk_limits);
1569 torture_suite_add_1smb2_test(suite, "copy_chunk_src_lock",
1570 test_ioctl_copy_chunk_src_lck);
1571 torture_suite_add_1smb2_test(suite, "copy_chunk_dest_lock",
1572 test_ioctl_copy_chunk_dest_lck);
1573 torture_suite_add_1smb2_test(suite, "copy_chunk_bad_key",
1574 test_ioctl_copy_chunk_bad_key);
1575 torture_suite_add_1smb2_test(suite, "copy_chunk_src_is_dest",
1576 test_ioctl_copy_chunk_src_is_dest);
1577 torture_suite_add_1smb2_test(suite, "copy_chunk_src_is_dest_overlap",
1578 test_ioctl_copy_chunk_src_is_dest_overlap);
1579 torture_suite_add_1smb2_test(suite, "copy_chunk_bad_access",
1580 test_ioctl_copy_chunk_bad_access);
1581 torture_suite_add_1smb2_test(suite, "copy_chunk_src_exceed",
1582 test_ioctl_copy_chunk_src_exceed);
1583 torture_suite_add_1smb2_test(suite, "copy_chunk_src_exceed_multi",
1584 test_ioctl_copy_chunk_src_exceed_multi);
1585 torture_suite_add_1smb2_test(suite, "copy_chunk_sparse_dest",
1586 test_ioctl_copy_chunk_sparse_dest);
1587 torture_suite_add_1smb2_test(suite, "copy_chunk_max_output_sz",
1588 test_ioctl_copy_chunk_max_output_sz);
1590 suite->description = talloc_strdup(suite, "SMB2-IOCTL tests");