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_comment(torture,
65 "FSCTL_SRV_ENUM_SNAPS not supported, skipping\n");
68 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_ENUM_SNAPS");
74 basic testing of the SMB2 server side copy ioctls
76 static bool test_ioctl_req_resume_key(struct torture_context *torture,
77 struct smb2_tree *tree)
82 union smb_ioctl ioctl;
83 TALLOC_CTX *tmp_ctx = talloc_new(tree);
84 struct req_resume_key_rsp res_key;
85 enum ndr_err_code ndr_ret;
87 smb2_util_unlink(tree, FNAME);
89 status = torture_smb2_testfile(tree, FNAME, &h);
90 torture_assert_ntstatus_ok(torture, status, "create write");
93 status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf));
94 torture_assert_ntstatus_ok(torture, status, "write");
97 ioctl.smb2.level = RAW_IOCTL_SMB2;
98 ioctl.smb2.in.file.handle = h;
99 ioctl.smb2.in.function = FSCTL_SRV_REQUEST_RESUME_KEY;
100 ioctl.smb2.in.max_response_size = 32;
101 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
103 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
104 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_REQUEST_RESUME_KEY");
106 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, &res_key,
107 (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
108 torture_assert_ndr_success(torture, ndr_ret,
109 "ndr_pull_req_resume_key_rsp");
111 ndr_print_debug((ndr_print_fn_t)ndr_print_req_resume_key_rsp, "yo", &res_key);
113 talloc_free(tmp_ctx);
117 static uint64_t patt_hash(uint64_t off)
122 static bool check_pattern(struct torture_context *torture,
123 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
124 struct smb2_handle h, uint64_t off, uint64_t len,
132 r.in.file.handle = h;
135 status = smb2_read(tree, mem_ctx, &r);
136 torture_assert_ntstatus_ok(torture, status, "read");
138 torture_assert_u64_equal(torture, r.out.data.length, len,
139 "read data len mismatch");
141 for (i = 0; i <= len - 8; i += 8, patt_off += 8) {
142 uint64_t data = BVAL(r.out.data.data, i);
143 torture_assert_u64_equal(torture, data, patt_hash(patt_off),
144 talloc_asprintf(torture, "read data "
145 "pattern bad at %llu\n",
146 (unsigned long long)i));
149 talloc_free(r.out.data.data);
153 static bool test_setup_copy_chunk(struct torture_context *torture,
154 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
156 struct smb2_handle *src_h,
158 struct smb2_handle *dest_h,
160 struct srv_copychunk_copy *cc_copy,
161 union smb_ioctl *ioctl)
163 struct req_resume_key_rsp res_key;
165 enum ndr_err_code ndr_ret;
167 uint8_t *buf = talloc_zero_size(mem_ctx, MAX(src_size, dest_size));
169 printf("no mem for file data buffer\n");
173 smb2_util_unlink(tree, FNAME);
174 smb2_util_unlink(tree, FNAME2);
176 status = torture_smb2_testfile(tree, FNAME, src_h);
177 torture_assert_ntstatus_ok(torture, status, "create write");
180 for (i = 0; i <= src_size - 8; i += 8) {
181 SBVAL(buf, i, patt_hash(i));
183 status = smb2_util_write(tree, *src_h, buf, 0, src_size);
184 torture_assert_ntstatus_ok(torture, status, "src write");
187 status = torture_smb2_testfile(tree, FNAME2, dest_h);
188 torture_assert_ntstatus_ok(torture, status, "create write");
191 for (i = 0; i <= dest_size - 8; i += 8) {
192 SBVAL(buf, i, patt_hash(i));
194 status = smb2_util_write(tree, *dest_h, buf, 0, dest_size);
195 torture_assert_ntstatus_ok(torture, status, "dest write");
198 ZERO_STRUCTPN(ioctl);
199 ioctl->smb2.level = RAW_IOCTL_SMB2;
200 ioctl->smb2.in.file.handle = *src_h;
201 ioctl->smb2.in.function = FSCTL_SRV_REQUEST_RESUME_KEY;
202 /* Allow for Key + ContextLength + Context */
203 ioctl->smb2.in.max_response_size = 32;
204 ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
206 status = smb2_ioctl(tree, mem_ctx, &ioctl->smb2);
207 torture_assert_ntstatus_ok(torture, status,
208 "FSCTL_SRV_REQUEST_RESUME_KEY");
211 ndr_ret = ndr_pull_struct_blob(&ioctl->smb2.out.out, mem_ctx, &res_key,
212 (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
214 torture_assert_ndr_success(torture, ndr_ret,
215 "ndr_pull_req_resume_key_rsp");
217 ZERO_STRUCTPN(ioctl);
218 ioctl->smb2.level = RAW_IOCTL_SMB2;
219 ioctl->smb2.in.file.handle = *dest_h;
220 ioctl->smb2.in.function = FSCTL_SRV_COPYCHUNK;
221 ioctl->smb2.in.max_response_size = sizeof(struct srv_copychunk_rsp);
222 ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
224 ZERO_STRUCTPN(cc_copy);
225 memcpy(cc_copy->source_key, res_key.resume_key, ARRAY_SIZE(cc_copy->source_key));
226 cc_copy->chunk_count = nchunks;
227 cc_copy->chunks = talloc_zero_array(mem_ctx, struct srv_copychunk, nchunks);
228 if (cc_copy->chunks == NULL) {
229 printf("not enough memory to allocate %u chunks\n", nchunks);
237 static bool check_copy_chunk_rsp(struct torture_context *torture,
238 struct srv_copychunk_rsp *cc_rsp,
239 uint32_t ex_chunks_written,
240 uint32_t ex_chunk_bytes_written,
241 uint32_t ex_total_bytes_written)
243 torture_assert_int_equal(torture, cc_rsp->chunks_written,
244 ex_chunks_written, "num chunks");
245 torture_assert_int_equal(torture, cc_rsp->chunk_bytes_written,
246 ex_chunk_bytes_written, "chunk bytes written");
247 torture_assert_int_equal(torture, cc_rsp->total_bytes_written,
248 ex_total_bytes_written, "chunk total bytes");
252 static bool test_ioctl_copy_chunk_simple(struct torture_context *torture,
253 struct smb2_tree *tree)
255 struct smb2_handle src_h;
256 struct smb2_handle dest_h;
258 union smb_ioctl ioctl;
259 TALLOC_CTX *tmp_ctx = talloc_new(tree);
260 struct srv_copychunk_copy cc_copy;
261 struct srv_copychunk_rsp cc_rsp;
262 enum ndr_err_code ndr_ret;
265 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
267 &src_h, 4096, /* fill 4096 byte src file */
268 &dest_h, 0, /* 0 byte dest file */
275 /* copy all src file data (via a single chunk desc) */
276 cc_copy.chunks[0].source_off = 0;
277 cc_copy.chunks[0].target_off = 0;
278 cc_copy.chunks[0].length = 4096;
280 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
282 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
283 torture_assert_ndr_success(torture, ndr_ret,
284 "ndr_push_srv_copychunk_copy");
286 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
287 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
289 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
291 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
292 torture_assert_ndr_success(torture, ndr_ret,
293 "ndr_pull_srv_copychunk_rsp");
295 ok = check_copy_chunk_rsp(torture, &cc_rsp,
296 1, /* chunks written */
297 0, /* chunk bytes unsuccessfully written */
298 4096); /* total bytes written */
303 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
308 smb2_util_close(tree, src_h);
309 smb2_util_close(tree, dest_h);
310 talloc_free(tmp_ctx);
314 static bool test_ioctl_copy_chunk_multi(struct torture_context *torture,
315 struct smb2_tree *tree)
317 struct smb2_handle src_h;
318 struct smb2_handle dest_h;
320 union smb_ioctl ioctl;
321 TALLOC_CTX *tmp_ctx = talloc_new(tree);
322 struct srv_copychunk_copy cc_copy;
323 struct srv_copychunk_rsp cc_rsp;
324 enum ndr_err_code ndr_ret;
327 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
329 &src_h, 8192, /* src file */
330 &dest_h, 0, /* dest file */
337 /* copy all src file data via two chunks */
338 cc_copy.chunks[0].source_off = 0;
339 cc_copy.chunks[0].target_off = 0;
340 cc_copy.chunks[0].length = 4096;
342 cc_copy.chunks[1].source_off = 4096;
343 cc_copy.chunks[1].target_off = 4096;
344 cc_copy.chunks[1].length = 4096;
346 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
348 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
349 torture_assert_ndr_success(torture, ndr_ret,
350 "ndr_push_srv_copychunk_copy");
352 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
353 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
355 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
357 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
358 torture_assert_ndr_success(torture, ndr_ret,
359 "ndr_pull_srv_copychunk_rsp");
361 ok = check_copy_chunk_rsp(torture, &cc_rsp,
362 2, /* chunks written */
363 0, /* chunk bytes unsuccessfully written */
364 8192); /* total bytes written */
369 smb2_util_close(tree, src_h);
370 smb2_util_close(tree, dest_h);
371 talloc_free(tmp_ctx);
375 static bool test_ioctl_copy_chunk_tiny(struct torture_context *torture,
376 struct smb2_tree *tree)
378 struct smb2_handle src_h;
379 struct smb2_handle dest_h;
381 union smb_ioctl ioctl;
382 TALLOC_CTX *tmp_ctx = talloc_new(tree);
383 struct srv_copychunk_copy cc_copy;
384 struct srv_copychunk_rsp cc_rsp;
385 enum ndr_err_code ndr_ret;
388 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
390 &src_h, 100, /* src file */
391 &dest_h, 0, /* dest file */
398 /* copy all src file data via two chunks, sub block size chunks */
399 cc_copy.chunks[0].source_off = 0;
400 cc_copy.chunks[0].target_off = 0;
401 cc_copy.chunks[0].length = 50;
403 cc_copy.chunks[1].source_off = 50;
404 cc_copy.chunks[1].target_off = 50;
405 cc_copy.chunks[1].length = 50;
407 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
409 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
410 torture_assert_ndr_success(torture, ndr_ret,
411 "ndr_push_srv_copychunk_copy");
413 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
414 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
416 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
418 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
419 torture_assert_ndr_success(torture, ndr_ret,
420 "ndr_pull_srv_copychunk_rsp");
422 ok = check_copy_chunk_rsp(torture, &cc_rsp,
423 2, /* chunks written */
424 0, /* chunk bytes unsuccessfully written */
425 100); /* total bytes written */
430 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 100, 0);
435 smb2_util_close(tree, src_h);
436 smb2_util_close(tree, dest_h);
437 talloc_free(tmp_ctx);
441 static bool test_ioctl_copy_chunk_over(struct torture_context *torture,
442 struct smb2_tree *tree)
444 struct smb2_handle src_h;
445 struct smb2_handle dest_h;
447 union smb_ioctl ioctl;
448 TALLOC_CTX *tmp_ctx = talloc_new(tree);
449 struct srv_copychunk_copy cc_copy;
450 struct srv_copychunk_rsp cc_rsp;
451 enum ndr_err_code ndr_ret;
454 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
456 &src_h, 8192, /* src file */
457 &dest_h, 4096, /* dest file */
464 /* first chunk overwrites existing dest data */
465 cc_copy.chunks[0].source_off = 0;
466 cc_copy.chunks[0].target_off = 0;
467 cc_copy.chunks[0].length = 4096;
469 /* second chunk overwrites the first */
470 cc_copy.chunks[1].source_off = 4096;
471 cc_copy.chunks[1].target_off = 0;
472 cc_copy.chunks[1].length = 4096;
474 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
476 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
477 torture_assert_ndr_success(torture, ndr_ret,
478 "ndr_push_srv_copychunk_copy");
480 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
481 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
483 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
485 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
486 torture_assert_ndr_success(torture, ndr_ret,
487 "ndr_pull_srv_copychunk_rsp");
489 ok = check_copy_chunk_rsp(torture, &cc_rsp,
490 2, /* chunks written */
491 0, /* chunk bytes unsuccessfully written */
492 8192); /* total bytes written */
497 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 4096);
502 smb2_util_close(tree, src_h);
503 smb2_util_close(tree, dest_h);
504 talloc_free(tmp_ctx);
508 static bool test_ioctl_copy_chunk_append(struct torture_context *torture,
509 struct smb2_tree *tree)
511 struct smb2_handle src_h;
512 struct smb2_handle dest_h;
514 union smb_ioctl ioctl;
515 TALLOC_CTX *tmp_ctx = talloc_new(tree);
516 struct srv_copychunk_copy cc_copy;
517 struct srv_copychunk_rsp cc_rsp;
518 enum ndr_err_code ndr_ret;
521 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
523 &src_h, 4096, /* src file */
524 &dest_h, 0, /* dest file */
531 cc_copy.chunks[0].source_off = 0;
532 cc_copy.chunks[0].target_off = 0;
533 cc_copy.chunks[0].length = 4096;
535 /* second chunk appends the same data to the first */
536 cc_copy.chunks[1].source_off = 0;
537 cc_copy.chunks[1].target_off = 4096;
538 cc_copy.chunks[1].length = 4096;
540 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
542 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
543 torture_assert_ndr_success(torture, ndr_ret,
544 "ndr_push_srv_copychunk_copy");
546 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
547 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
549 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
551 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
552 torture_assert_ndr_success(torture, ndr_ret,
553 "ndr_pull_srv_copychunk_rsp");
555 ok = check_copy_chunk_rsp(torture, &cc_rsp,
556 2, /* chunks written */
557 0, /* chunk bytes unsuccessfully written */
558 8192); /* total bytes written */
563 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
568 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 4096, 4096, 0);
573 smb2_util_close(tree, src_h);
574 smb2_util_close(tree, dest_h);
575 talloc_free(tmp_ctx);
579 static bool test_ioctl_copy_chunk_limits(struct torture_context *torture,
580 struct smb2_tree *tree)
582 struct smb2_handle src_h;
583 struct smb2_handle dest_h;
585 union smb_ioctl ioctl;
586 TALLOC_CTX *tmp_ctx = talloc_new(tree);
587 struct srv_copychunk_copy cc_copy;
588 struct srv_copychunk_rsp cc_rsp;
589 enum ndr_err_code ndr_ret;
592 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
594 &src_h, 4096, /* src file */
595 &dest_h, 0, /* dest file */
602 /* send huge chunk length request */
603 cc_copy.chunks[0].source_off = 0;
604 cc_copy.chunks[0].target_off = 0;
605 cc_copy.chunks[0].length = UINT_MAX;
607 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
609 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
610 torture_assert_ndr_success(torture, ndr_ret, "marshalling request");
612 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
613 torture_assert_ntstatus_equal(torture, status,
614 NT_STATUS_INVALID_PARAMETER,
615 "bad oversize chunk response");
617 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
619 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
620 torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
622 torture_comment(torture, "limit max chunks, got %u\n",
623 cc_rsp.chunks_written);
624 torture_comment(torture, "limit max chunk len, got %u\n",
625 cc_rsp.chunk_bytes_written);
626 torture_comment(torture, "limit max total bytes, got %u\n",
627 cc_rsp.total_bytes_written);
629 smb2_util_close(tree, src_h);
630 smb2_util_close(tree, dest_h);
631 talloc_free(tmp_ctx);
635 static bool test_ioctl_copy_chunk_src_lck(struct torture_context *torture,
636 struct smb2_tree *tree)
638 struct smb2_handle src_h;
639 struct smb2_handle src_h2;
640 struct smb2_handle dest_h;
642 union smb_ioctl ioctl;
643 TALLOC_CTX *tmp_ctx = talloc_new(tree);
644 struct srv_copychunk_copy cc_copy;
645 struct srv_copychunk_rsp cc_rsp;
646 enum ndr_err_code ndr_ret;
648 struct smb2_lock lck;
649 struct smb2_lock_element el[1];
651 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
653 &src_h, 4096, /* src file */
654 &dest_h, 0, /* dest file */
661 cc_copy.chunks[0].source_off = 0;
662 cc_copy.chunks[0].target_off = 0;
663 cc_copy.chunks[0].length = 4096;
665 /* open and lock the copychunk src file */
666 status = torture_smb2_testfile(tree, FNAME, &src_h2);
667 torture_assert_ntstatus_ok(torture, status, "2nd src open");
669 lck.in.lock_count = 0x0001;
670 lck.in.lock_sequence = 0x00000000;
671 lck.in.file.handle = src_h2;
673 el[0].offset = cc_copy.chunks[0].source_off;
674 el[0].length = cc_copy.chunks[0].length;
676 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
678 status = smb2_lock(tree, &lck);
679 torture_assert_ntstatus_ok(torture, status, "lock");
681 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
683 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
684 torture_assert_ndr_success(torture, ndr_ret,
685 "ndr_push_srv_copychunk_copy");
687 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
689 * 2k12 & Samba return lock_conflict, Windows 7 & 2k8 return success...
691 * Edgar Olougouna @ MS wrote:
692 * Regarding the FSCTL_SRV_COPYCHUNK and STATUS_FILE_LOCK_CONFLICT
693 * discrepancy observed between Windows versions, we confirm that the
694 * behavior change is expected.
696 * CopyChunk in Windows Server 2012 use regular Readfile/Writefile APIs
697 * to move the chunks from the source to the destination.
698 * These ReadFile/WriteFile APIs go through the byte-range lock checks,
699 * and this explains the observed STATUS_FILE_LOCK_CONFLICT error.
701 * Prior to Windows Server 2012, CopyChunk used mapped sections to move
702 * the data. And byte range locks are not enforced on mapped I/O, and
703 * this explains the STATUS_SUCCESS observed on Windows Server 2008 R2.
705 torture_assert_ntstatus_equal(torture, status,
706 NT_STATUS_FILE_LOCK_CONFLICT,
707 "FSCTL_SRV_COPYCHUNK locked");
709 /* should get cc response data with the lock conflict status */
710 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
712 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
713 torture_assert_ndr_success(torture, ndr_ret,
714 "ndr_pull_srv_copychunk_rsp");
715 ok = check_copy_chunk_rsp(torture, &cc_rsp,
716 0, /* chunks written */
717 0, /* chunk bytes unsuccessfully written */
718 0); /* total bytes written */
720 lck.in.lock_count = 0x0001;
721 lck.in.lock_sequence = 0x00000001;
722 lck.in.file.handle = src_h2;
724 el[0].offset = cc_copy.chunks[0].source_off;
725 el[0].length = cc_copy.chunks[0].length;
727 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
728 status = smb2_lock(tree, &lck);
729 torture_assert_ntstatus_ok(torture, status, "unlock");
731 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
732 torture_assert_ntstatus_ok(torture, status,
733 "FSCTL_SRV_COPYCHUNK unlocked");
735 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
737 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
738 torture_assert_ndr_success(torture, ndr_ret,
739 "ndr_pull_srv_copychunk_rsp");
741 ok = check_copy_chunk_rsp(torture, &cc_rsp,
742 1, /* chunks written */
743 0, /* chunk bytes unsuccessfully written */
744 4096); /* total bytes written */
749 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
754 smb2_util_close(tree, src_h2);
755 smb2_util_close(tree, src_h);
756 smb2_util_close(tree, dest_h);
757 talloc_free(tmp_ctx);
761 static bool test_ioctl_copy_chunk_dest_lck(struct torture_context *torture,
762 struct smb2_tree *tree)
764 struct smb2_handle src_h;
765 struct smb2_handle dest_h;
766 struct smb2_handle dest_h2;
768 union smb_ioctl ioctl;
769 TALLOC_CTX *tmp_ctx = talloc_new(tree);
770 struct srv_copychunk_copy cc_copy;
771 struct srv_copychunk_rsp cc_rsp;
772 enum ndr_err_code ndr_ret;
774 struct smb2_lock lck;
775 struct smb2_lock_element el[1];
777 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
779 &src_h, 4096, /* src file */
780 &dest_h, 4096, /* dest file */
787 cc_copy.chunks[0].source_off = 0;
788 cc_copy.chunks[0].target_off = 0;
789 cc_copy.chunks[0].length = 4096;
791 /* open and lock the copychunk dest file */
792 status = torture_smb2_testfile(tree, FNAME2, &dest_h2);
793 torture_assert_ntstatus_ok(torture, status, "2nd src open");
795 lck.in.lock_count = 0x0001;
796 lck.in.lock_sequence = 0x00000000;
797 lck.in.file.handle = dest_h2;
799 el[0].offset = cc_copy.chunks[0].target_off;
800 el[0].length = cc_copy.chunks[0].length;
802 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
804 status = smb2_lock(tree, &lck);
805 torture_assert_ntstatus_ok(torture, status, "lock");
807 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
809 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
810 torture_assert_ndr_success(torture, ndr_ret,
811 "ndr_push_srv_copychunk_copy");
813 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
814 torture_assert_ntstatus_equal(torture, status,
815 NT_STATUS_FILE_LOCK_CONFLICT,
816 "FSCTL_SRV_COPYCHUNK locked");
818 lck.in.lock_count = 0x0001;
819 lck.in.lock_sequence = 0x00000001;
820 lck.in.file.handle = dest_h2;
822 el[0].offset = cc_copy.chunks[0].target_off;
823 el[0].length = cc_copy.chunks[0].length;
825 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
826 status = smb2_lock(tree, &lck);
827 torture_assert_ntstatus_ok(torture, status, "unlock");
829 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
830 torture_assert_ntstatus_ok(torture, status,
831 "FSCTL_SRV_COPYCHUNK unlocked");
833 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
835 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
836 torture_assert_ndr_success(torture, ndr_ret,
837 "ndr_pull_srv_copychunk_rsp");
839 ok = check_copy_chunk_rsp(torture, &cc_rsp,
840 1, /* chunks written */
841 0, /* chunk bytes unsuccessfully written */
842 4096); /* total bytes written */
847 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
852 smb2_util_close(tree, dest_h2);
853 smb2_util_close(tree, src_h);
854 smb2_util_close(tree, dest_h);
855 talloc_free(tmp_ctx);
860 basic testing of SMB2 ioctls
862 struct torture_suite *torture_smb2_ioctl_init(void)
864 struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "ioctl");
866 torture_suite_add_1smb2_test(suite, "shadow_copy",
867 test_ioctl_get_shadow_copy);
868 torture_suite_add_1smb2_test(suite, "req_resume_key",
869 test_ioctl_req_resume_key);
870 torture_suite_add_1smb2_test(suite, "copy_chunk_simple",
871 test_ioctl_copy_chunk_simple);
872 torture_suite_add_1smb2_test(suite, "copy_chunk_multi",
873 test_ioctl_copy_chunk_multi);
874 torture_suite_add_1smb2_test(suite, "copy_chunk_tiny",
875 test_ioctl_copy_chunk_tiny);
876 torture_suite_add_1smb2_test(suite, "copy_chunk_overwrite",
877 test_ioctl_copy_chunk_over);
878 torture_suite_add_1smb2_test(suite, "copy_chunk_append",
879 test_ioctl_copy_chunk_append);
880 torture_suite_add_1smb2_test(suite, "copy_chunk_limits",
881 test_ioctl_copy_chunk_limits);
882 torture_suite_add_1smb2_test(suite, "copy_chunk_src_lock",
883 test_ioctl_copy_chunk_src_lck);
884 torture_suite_add_1smb2_test(suite, "copy_chunk_dest_lock",
885 test_ioctl_copy_chunk_dest_lck);
887 suite->description = talloc_strdup(suite, "SMB2-IOCTL tests");