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 struct smb2_handle *dest_h,
158 struct srv_copychunk_copy *cc_copy,
159 union smb_ioctl *ioctl)
161 struct req_resume_key_rsp res_key;
163 enum ndr_err_code ndr_ret;
165 uint8_t *buf = talloc_zero_size(mem_ctx, MAX(src_size, dest_size));
166 torture_assert(torture, (buf != NULL), "no memory for file data buf");
168 smb2_util_unlink(tree, FNAME);
169 smb2_util_unlink(tree, FNAME2);
171 status = torture_smb2_testfile(tree, FNAME, src_h);
172 torture_assert_ntstatus_ok(torture, status, "create write");
175 for (i = 0; i <= src_size - 8; i += 8) {
176 SBVAL(buf, i, patt_hash(i));
178 status = smb2_util_write(tree, *src_h, buf, 0, src_size);
179 torture_assert_ntstatus_ok(torture, status, "src write");
182 status = torture_smb2_testfile(tree, FNAME2, dest_h);
183 torture_assert_ntstatus_ok(torture, status, "create write");
186 for (i = 0; i <= dest_size - 8; i += 8) {
187 SBVAL(buf, i, patt_hash(i));
189 status = smb2_util_write(tree, *dest_h, buf, 0, dest_size);
190 torture_assert_ntstatus_ok(torture, status, "dest write");
193 ZERO_STRUCTPN(ioctl);
194 ioctl->smb2.level = RAW_IOCTL_SMB2;
195 ioctl->smb2.in.file.handle = *src_h;
196 ioctl->smb2.in.function = FSCTL_SRV_REQUEST_RESUME_KEY;
197 /* Allow for Key + ContextLength + Context */
198 ioctl->smb2.in.max_response_size = 32;
199 ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
201 status = smb2_ioctl(tree, mem_ctx, &ioctl->smb2);
202 torture_assert_ntstatus_ok(torture, status,
203 "FSCTL_SRV_REQUEST_RESUME_KEY");
206 ndr_ret = ndr_pull_struct_blob(&ioctl->smb2.out.out, mem_ctx, &res_key,
207 (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
209 torture_assert_ndr_success(torture, ndr_ret,
210 "ndr_pull_req_resume_key_rsp");
212 ZERO_STRUCTPN(ioctl);
213 ioctl->smb2.level = RAW_IOCTL_SMB2;
214 ioctl->smb2.in.file.handle = *dest_h;
215 ioctl->smb2.in.function = FSCTL_SRV_COPYCHUNK;
216 ioctl->smb2.in.max_response_size = sizeof(struct srv_copychunk_rsp);
217 ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
219 ZERO_STRUCTPN(cc_copy);
220 memcpy(cc_copy->source_key, res_key.resume_key, ARRAY_SIZE(cc_copy->source_key));
221 cc_copy->chunk_count = nchunks;
222 cc_copy->chunks = talloc_zero_array(mem_ctx, struct srv_copychunk, nchunks);
223 torture_assert(torture, (cc_copy->chunks != NULL), "no memory for chunks");
229 static bool check_copy_chunk_rsp(struct torture_context *torture,
230 struct srv_copychunk_rsp *cc_rsp,
231 uint32_t ex_chunks_written,
232 uint32_t ex_chunk_bytes_written,
233 uint32_t ex_total_bytes_written)
235 torture_assert_int_equal(torture, cc_rsp->chunks_written,
236 ex_chunks_written, "num chunks");
237 torture_assert_int_equal(torture, cc_rsp->chunk_bytes_written,
238 ex_chunk_bytes_written, "chunk bytes written");
239 torture_assert_int_equal(torture, cc_rsp->total_bytes_written,
240 ex_total_bytes_written, "chunk total bytes");
244 static bool test_ioctl_copy_chunk_simple(struct torture_context *torture,
245 struct smb2_tree *tree)
247 struct smb2_handle src_h;
248 struct smb2_handle dest_h;
250 union smb_ioctl ioctl;
251 TALLOC_CTX *tmp_ctx = talloc_new(tree);
252 struct srv_copychunk_copy cc_copy;
253 struct srv_copychunk_rsp cc_rsp;
254 enum ndr_err_code ndr_ret;
257 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
259 &src_h, 4096, /* fill 4096 byte src file */
260 &dest_h, 0, /* 0 byte dest file */
264 torture_fail(torture, "setup copy chunk error");
267 /* copy all src file data (via a single chunk desc) */
268 cc_copy.chunks[0].source_off = 0;
269 cc_copy.chunks[0].target_off = 0;
270 cc_copy.chunks[0].length = 4096;
272 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
274 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
275 torture_assert_ndr_success(torture, ndr_ret,
276 "ndr_push_srv_copychunk_copy");
278 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
279 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
281 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
283 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
284 torture_assert_ndr_success(torture, ndr_ret,
285 "ndr_pull_srv_copychunk_rsp");
287 ok = check_copy_chunk_rsp(torture, &cc_rsp,
288 1, /* chunks written */
289 0, /* chunk bytes unsuccessfully written */
290 4096); /* total bytes written */
292 torture_fail(torture, "bad copy chunk response data");
295 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
297 torture_fail(torture, "inconsistent file data");
300 smb2_util_close(tree, src_h);
301 smb2_util_close(tree, dest_h);
302 talloc_free(tmp_ctx);
306 static bool test_ioctl_copy_chunk_multi(struct torture_context *torture,
307 struct smb2_tree *tree)
309 struct smb2_handle src_h;
310 struct smb2_handle dest_h;
312 union smb_ioctl ioctl;
313 TALLOC_CTX *tmp_ctx = talloc_new(tree);
314 struct srv_copychunk_copy cc_copy;
315 struct srv_copychunk_rsp cc_rsp;
316 enum ndr_err_code ndr_ret;
319 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
321 &src_h, 8192, /* src file */
322 &dest_h, 0, /* dest file */
326 torture_fail(torture, "setup copy chunk error");
329 /* copy all src file data via two chunks */
330 cc_copy.chunks[0].source_off = 0;
331 cc_copy.chunks[0].target_off = 0;
332 cc_copy.chunks[0].length = 4096;
334 cc_copy.chunks[1].source_off = 4096;
335 cc_copy.chunks[1].target_off = 4096;
336 cc_copy.chunks[1].length = 4096;
338 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
340 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
341 torture_assert_ndr_success(torture, ndr_ret,
342 "ndr_push_srv_copychunk_copy");
344 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
345 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
347 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
349 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
350 torture_assert_ndr_success(torture, ndr_ret,
351 "ndr_pull_srv_copychunk_rsp");
353 ok = check_copy_chunk_rsp(torture, &cc_rsp,
354 2, /* chunks written */
355 0, /* chunk bytes unsuccessfully written */
356 8192); /* total bytes written */
358 torture_fail(torture, "bad copy chunk response data");
361 smb2_util_close(tree, src_h);
362 smb2_util_close(tree, dest_h);
363 talloc_free(tmp_ctx);
367 static bool test_ioctl_copy_chunk_tiny(struct torture_context *torture,
368 struct smb2_tree *tree)
370 struct smb2_handle src_h;
371 struct smb2_handle dest_h;
373 union smb_ioctl ioctl;
374 TALLOC_CTX *tmp_ctx = talloc_new(tree);
375 struct srv_copychunk_copy cc_copy;
376 struct srv_copychunk_rsp cc_rsp;
377 enum ndr_err_code ndr_ret;
380 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
382 &src_h, 100, /* src file */
383 &dest_h, 0, /* dest file */
387 torture_fail(torture, "setup copy chunk error");
390 /* copy all src file data via two chunks, sub block size chunks */
391 cc_copy.chunks[0].source_off = 0;
392 cc_copy.chunks[0].target_off = 0;
393 cc_copy.chunks[0].length = 50;
395 cc_copy.chunks[1].source_off = 50;
396 cc_copy.chunks[1].target_off = 50;
397 cc_copy.chunks[1].length = 50;
399 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
401 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
402 torture_assert_ndr_success(torture, ndr_ret,
403 "ndr_push_srv_copychunk_copy");
405 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
406 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
408 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
410 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
411 torture_assert_ndr_success(torture, ndr_ret,
412 "ndr_pull_srv_copychunk_rsp");
414 ok = check_copy_chunk_rsp(torture, &cc_rsp,
415 2, /* chunks written */
416 0, /* chunk bytes unsuccessfully written */
417 100); /* total bytes written */
419 torture_fail(torture, "bad copy chunk response data");
422 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 100, 0);
424 torture_fail(torture, "inconsistent file data");
427 smb2_util_close(tree, src_h);
428 smb2_util_close(tree, dest_h);
429 talloc_free(tmp_ctx);
433 static bool test_ioctl_copy_chunk_over(struct torture_context *torture,
434 struct smb2_tree *tree)
436 struct smb2_handle src_h;
437 struct smb2_handle dest_h;
439 union smb_ioctl ioctl;
440 TALLOC_CTX *tmp_ctx = talloc_new(tree);
441 struct srv_copychunk_copy cc_copy;
442 struct srv_copychunk_rsp cc_rsp;
443 enum ndr_err_code ndr_ret;
446 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
448 &src_h, 8192, /* src file */
449 &dest_h, 4096, /* dest file */
453 torture_fail(torture, "setup copy chunk error");
456 /* first chunk overwrites existing dest data */
457 cc_copy.chunks[0].source_off = 0;
458 cc_copy.chunks[0].target_off = 0;
459 cc_copy.chunks[0].length = 4096;
461 /* second chunk overwrites the first */
462 cc_copy.chunks[1].source_off = 4096;
463 cc_copy.chunks[1].target_off = 0;
464 cc_copy.chunks[1].length = 4096;
466 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
468 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
469 torture_assert_ndr_success(torture, ndr_ret,
470 "ndr_push_srv_copychunk_copy");
472 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
473 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
475 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
477 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
478 torture_assert_ndr_success(torture, ndr_ret,
479 "ndr_pull_srv_copychunk_rsp");
481 ok = check_copy_chunk_rsp(torture, &cc_rsp,
482 2, /* chunks written */
483 0, /* chunk bytes unsuccessfully written */
484 8192); /* total bytes written */
486 torture_fail(torture, "bad copy chunk response data");
489 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 4096);
491 torture_fail(torture, "inconsistent file data");
494 smb2_util_close(tree, src_h);
495 smb2_util_close(tree, dest_h);
496 talloc_free(tmp_ctx);
500 static bool test_ioctl_copy_chunk_append(struct torture_context *torture,
501 struct smb2_tree *tree)
503 struct smb2_handle src_h;
504 struct smb2_handle dest_h;
506 union smb_ioctl ioctl;
507 TALLOC_CTX *tmp_ctx = talloc_new(tree);
508 struct srv_copychunk_copy cc_copy;
509 struct srv_copychunk_rsp cc_rsp;
510 enum ndr_err_code ndr_ret;
513 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
515 &src_h, 4096, /* src file */
516 &dest_h, 0, /* dest file */
520 torture_fail(torture, "setup copy chunk error");
523 cc_copy.chunks[0].source_off = 0;
524 cc_copy.chunks[0].target_off = 0;
525 cc_copy.chunks[0].length = 4096;
527 /* second chunk appends the same data to the first */
528 cc_copy.chunks[1].source_off = 0;
529 cc_copy.chunks[1].target_off = 4096;
530 cc_copy.chunks[1].length = 4096;
532 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
534 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
535 torture_assert_ndr_success(torture, ndr_ret,
536 "ndr_push_srv_copychunk_copy");
538 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
539 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
541 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
543 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
544 torture_assert_ndr_success(torture, ndr_ret,
545 "ndr_pull_srv_copychunk_rsp");
547 ok = check_copy_chunk_rsp(torture, &cc_rsp,
548 2, /* chunks written */
549 0, /* chunk bytes unsuccessfully written */
550 8192); /* total bytes written */
552 torture_fail(torture, "bad copy chunk response data");
555 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
557 torture_fail(torture, "inconsistent file data");
560 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 4096, 4096, 0);
562 torture_fail(torture, "inconsistent file data");
565 smb2_util_close(tree, src_h);
566 smb2_util_close(tree, dest_h);
567 talloc_free(tmp_ctx);
571 static bool test_ioctl_copy_chunk_limits(struct torture_context *torture,
572 struct smb2_tree *tree)
574 struct smb2_handle src_h;
575 struct smb2_handle dest_h;
577 union smb_ioctl ioctl;
578 TALLOC_CTX *tmp_ctx = talloc_new(tree);
579 struct srv_copychunk_copy cc_copy;
580 struct srv_copychunk_rsp cc_rsp;
581 enum ndr_err_code ndr_ret;
584 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
586 &src_h, 4096, /* src file */
587 &dest_h, 0, /* dest file */
591 torture_fail(torture, "setup copy chunk error");
594 /* send huge chunk length request */
595 cc_copy.chunks[0].source_off = 0;
596 cc_copy.chunks[0].target_off = 0;
597 cc_copy.chunks[0].length = UINT_MAX;
599 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
601 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
602 torture_assert_ndr_success(torture, ndr_ret, "marshalling request");
604 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
605 torture_assert_ntstatus_equal(torture, status,
606 NT_STATUS_INVALID_PARAMETER,
607 "bad oversize chunk response");
609 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
611 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
612 torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
614 torture_comment(torture, "limit max chunks, got %u\n",
615 cc_rsp.chunks_written);
616 torture_comment(torture, "limit max chunk len, got %u\n",
617 cc_rsp.chunk_bytes_written);
618 torture_comment(torture, "limit max total bytes, got %u\n",
619 cc_rsp.total_bytes_written);
621 smb2_util_close(tree, src_h);
622 smb2_util_close(tree, dest_h);
623 talloc_free(tmp_ctx);
627 static bool test_ioctl_copy_chunk_src_lck(struct torture_context *torture,
628 struct smb2_tree *tree)
630 struct smb2_handle src_h;
631 struct smb2_handle src_h2;
632 struct smb2_handle dest_h;
634 union smb_ioctl ioctl;
635 TALLOC_CTX *tmp_ctx = talloc_new(tree);
636 struct srv_copychunk_copy cc_copy;
637 struct srv_copychunk_rsp cc_rsp;
638 enum ndr_err_code ndr_ret;
640 struct smb2_lock lck;
641 struct smb2_lock_element el[1];
643 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
645 &src_h, 4096, /* src file */
646 &dest_h, 0, /* dest file */
650 torture_fail(torture, "setup copy chunk error");
653 cc_copy.chunks[0].source_off = 0;
654 cc_copy.chunks[0].target_off = 0;
655 cc_copy.chunks[0].length = 4096;
657 /* open and lock the copychunk src file */
658 status = torture_smb2_testfile(tree, FNAME, &src_h2);
659 torture_assert_ntstatus_ok(torture, status, "2nd src open");
661 lck.in.lock_count = 0x0001;
662 lck.in.lock_sequence = 0x00000000;
663 lck.in.file.handle = src_h2;
665 el[0].offset = cc_copy.chunks[0].source_off;
666 el[0].length = cc_copy.chunks[0].length;
668 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
670 status = smb2_lock(tree, &lck);
671 torture_assert_ntstatus_ok(torture, status, "lock");
673 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
675 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
676 torture_assert_ndr_success(torture, ndr_ret,
677 "ndr_push_srv_copychunk_copy");
679 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
681 * 2k12 & Samba return lock_conflict, Windows 7 & 2k8 return success...
683 * Edgar Olougouna @ MS wrote:
684 * Regarding the FSCTL_SRV_COPYCHUNK and STATUS_FILE_LOCK_CONFLICT
685 * discrepancy observed between Windows versions, we confirm that the
686 * behavior change is expected.
688 * CopyChunk in Windows Server 2012 use regular Readfile/Writefile APIs
689 * to move the chunks from the source to the destination.
690 * These ReadFile/WriteFile APIs go through the byte-range lock checks,
691 * and this explains the observed STATUS_FILE_LOCK_CONFLICT error.
693 * Prior to Windows Server 2012, CopyChunk used mapped sections to move
694 * the data. And byte range locks are not enforced on mapped I/O, and
695 * this explains the STATUS_SUCCESS observed on Windows Server 2008 R2.
697 torture_assert_ntstatus_equal(torture, status,
698 NT_STATUS_FILE_LOCK_CONFLICT,
699 "FSCTL_SRV_COPYCHUNK locked");
701 /* should get cc response data with the lock conflict status */
702 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
704 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
705 torture_assert_ndr_success(torture, ndr_ret,
706 "ndr_pull_srv_copychunk_rsp");
707 ok = check_copy_chunk_rsp(torture, &cc_rsp,
708 0, /* chunks written */
709 0, /* chunk bytes unsuccessfully written */
710 0); /* total bytes written */
712 lck.in.lock_count = 0x0001;
713 lck.in.lock_sequence = 0x00000001;
714 lck.in.file.handle = src_h2;
716 el[0].offset = cc_copy.chunks[0].source_off;
717 el[0].length = cc_copy.chunks[0].length;
719 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
720 status = smb2_lock(tree, &lck);
721 torture_assert_ntstatus_ok(torture, status, "unlock");
723 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
724 torture_assert_ntstatus_ok(torture, status,
725 "FSCTL_SRV_COPYCHUNK unlocked");
727 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
729 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
730 torture_assert_ndr_success(torture, ndr_ret,
731 "ndr_pull_srv_copychunk_rsp");
733 ok = check_copy_chunk_rsp(torture, &cc_rsp,
734 1, /* chunks written */
735 0, /* chunk bytes unsuccessfully written */
736 4096); /* total bytes written */
738 torture_fail(torture, "bad copy chunk response data");
741 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
743 torture_fail(torture, "inconsistent file data");
746 smb2_util_close(tree, src_h2);
747 smb2_util_close(tree, src_h);
748 smb2_util_close(tree, dest_h);
749 talloc_free(tmp_ctx);
753 static bool test_ioctl_copy_chunk_dest_lck(struct torture_context *torture,
754 struct smb2_tree *tree)
756 struct smb2_handle src_h;
757 struct smb2_handle dest_h;
758 struct smb2_handle dest_h2;
760 union smb_ioctl ioctl;
761 TALLOC_CTX *tmp_ctx = talloc_new(tree);
762 struct srv_copychunk_copy cc_copy;
763 struct srv_copychunk_rsp cc_rsp;
764 enum ndr_err_code ndr_ret;
766 struct smb2_lock lck;
767 struct smb2_lock_element el[1];
769 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
771 &src_h, 4096, /* src file */
772 &dest_h, 4096, /* dest file */
776 torture_fail(torture, "setup copy chunk error");
779 cc_copy.chunks[0].source_off = 0;
780 cc_copy.chunks[0].target_off = 0;
781 cc_copy.chunks[0].length = 4096;
783 /* open and lock the copychunk dest file */
784 status = torture_smb2_testfile(tree, FNAME2, &dest_h2);
785 torture_assert_ntstatus_ok(torture, status, "2nd src open");
787 lck.in.lock_count = 0x0001;
788 lck.in.lock_sequence = 0x00000000;
789 lck.in.file.handle = dest_h2;
791 el[0].offset = cc_copy.chunks[0].target_off;
792 el[0].length = cc_copy.chunks[0].length;
794 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
796 status = smb2_lock(tree, &lck);
797 torture_assert_ntstatus_ok(torture, status, "lock");
799 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
801 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
802 torture_assert_ndr_success(torture, ndr_ret,
803 "ndr_push_srv_copychunk_copy");
805 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
806 torture_assert_ntstatus_equal(torture, status,
807 NT_STATUS_FILE_LOCK_CONFLICT,
808 "FSCTL_SRV_COPYCHUNK locked");
810 lck.in.lock_count = 0x0001;
811 lck.in.lock_sequence = 0x00000001;
812 lck.in.file.handle = dest_h2;
814 el[0].offset = cc_copy.chunks[0].target_off;
815 el[0].length = cc_copy.chunks[0].length;
817 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
818 status = smb2_lock(tree, &lck);
819 torture_assert_ntstatus_ok(torture, status, "unlock");
821 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
822 torture_assert_ntstatus_ok(torture, status,
823 "FSCTL_SRV_COPYCHUNK unlocked");
825 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
827 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
828 torture_assert_ndr_success(torture, ndr_ret,
829 "ndr_pull_srv_copychunk_rsp");
831 ok = check_copy_chunk_rsp(torture, &cc_rsp,
832 1, /* chunks written */
833 0, /* chunk bytes unsuccessfully written */
834 4096); /* total bytes written */
836 torture_fail(torture, "bad copy chunk response data");
839 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
841 torture_fail(torture, "inconsistent file data");
844 smb2_util_close(tree, dest_h2);
845 smb2_util_close(tree, src_h);
846 smb2_util_close(tree, dest_h);
847 talloc_free(tmp_ctx);
852 basic testing of SMB2 ioctls
854 struct torture_suite *torture_smb2_ioctl_init(void)
856 struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "ioctl");
858 torture_suite_add_1smb2_test(suite, "shadow_copy",
859 test_ioctl_get_shadow_copy);
860 torture_suite_add_1smb2_test(suite, "req_resume_key",
861 test_ioctl_req_resume_key);
862 torture_suite_add_1smb2_test(suite, "copy_chunk_simple",
863 test_ioctl_copy_chunk_simple);
864 torture_suite_add_1smb2_test(suite, "copy_chunk_multi",
865 test_ioctl_copy_chunk_multi);
866 torture_suite_add_1smb2_test(suite, "copy_chunk_tiny",
867 test_ioctl_copy_chunk_tiny);
868 torture_suite_add_1smb2_test(suite, "copy_chunk_overwrite",
869 test_ioctl_copy_chunk_over);
870 torture_suite_add_1smb2_test(suite, "copy_chunk_append",
871 test_ioctl_copy_chunk_append);
872 torture_suite_add_1smb2_test(suite, "copy_chunk_limits",
873 test_ioctl_copy_chunk_limits);
874 torture_suite_add_1smb2_test(suite, "copy_chunk_src_lock",
875 test_ioctl_copy_chunk_src_lck);
876 torture_suite_add_1smb2_test(suite, "copy_chunk_dest_lock",
877 test_ioctl_copy_chunk_dest_lck);
879 suite->description = talloc_strdup(suite, "SMB2-IOCTL tests");