2 Unix SMB/CIFS implementation.
4 test suite for SMB2 ioctl operations
6 Copyright (C) David Disseldorp 2011-2016
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 "../libcli/smb/smbXcli_base.h"
29 #include "librpc/gen_ndr/ndr_ioctl.h"
31 #define FNAME "testfsctl.dat"
32 #define FNAME2 "testfsctl2.dat"
33 #define DNAME "testfsctl_dir"
36 basic testing of SMB2 shadow copy calls
38 static bool test_ioctl_get_shadow_copy(struct torture_context *torture,
39 struct smb2_tree *tree)
44 union smb_ioctl ioctl;
45 TALLOC_CTX *tmp_ctx = talloc_new(tree);
47 smb2_util_unlink(tree, FNAME);
49 status = torture_smb2_testfile(tree, FNAME, &h);
50 torture_assert_ntstatus_ok(torture, status, "create write");
53 status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf));
54 torture_assert_ntstatus_ok(torture, status, "write");
57 ioctl.smb2.level = RAW_IOCTL_SMB2;
58 ioctl.smb2.in.file.handle = h;
59 ioctl.smb2.in.function = FSCTL_SRV_ENUM_SNAPS;
60 ioctl.smb2.in.max_response_size = 16;
61 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
63 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
64 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)
65 || NT_STATUS_EQUAL(status, NT_STATUS_INVALID_DEVICE_REQUEST)) {
66 torture_skip(torture, "FSCTL_SRV_ENUM_SNAPS not supported\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 write_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,
130 uint64_t io_sz = MIN(1024 * 64, len);
136 torture_assert(torture, (len % 8) == 0, "invalid write len");
138 buf = talloc_zero_size(mem_ctx, io_sz);
139 torture_assert(torture, (buf != NULL), "no memory for file data buf");
142 for (i = 0; i <= io_sz - 8; i += 8) {
143 SBVAL(buf, i, patt_hash(patt_off));
147 status = smb2_util_write(tree, h,
149 torture_assert_ntstatus_ok(torture, status, "file write");
160 static bool check_pattern(struct torture_context *torture,
161 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
162 struct smb2_handle h, uint64_t off, uint64_t len,
169 torture_assert(torture, (len % 8) == 0, "invalid read len");
175 uint64_t io_sz = MIN(1024 * 64, len);
178 r.in.file.handle = h;
181 status = smb2_read(tree, mem_ctx, &r);
182 torture_assert_ntstatus_ok(torture, status, "read");
184 torture_assert_u64_equal(torture, r.out.data.length, io_sz,
185 "read data len mismatch");
187 for (i = 0; i <= io_sz - 8; i += 8, patt_off += 8) {
188 uint64_t data = BVAL(r.out.data.data, i);
189 torture_assert_u64_equal(torture, data, patt_hash(patt_off),
190 talloc_asprintf(torture, "read data "
191 "pattern bad at %llu\n",
192 (unsigned long long)off + i));
194 talloc_free(r.out.data.data);
202 static bool check_zero(struct torture_context *torture,
203 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
204 struct smb2_handle h, uint64_t off, uint64_t len)
215 r.in.file.handle = h;
218 status = smb2_read(tree, mem_ctx, &r);
219 torture_assert_ntstatus_ok(torture, status, "read");
221 torture_assert_u64_equal(torture, r.out.data.length, len,
222 "read data len mismatch");
224 for (i = 0; i <= len - 8; i += 8) {
225 uint64_t data = BVAL(r.out.data.data, i);
226 torture_assert_u64_equal(torture, data, 0,
227 talloc_asprintf(mem_ctx, "read zero "
229 (unsigned long long)i));
232 talloc_free(r.out.data.data);
236 static bool test_setup_open(struct torture_context *torture,
237 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
239 struct smb2_handle *fh,
240 uint32_t desired_access,
241 uint32_t file_attributes)
243 struct smb2_create io;
247 io.in.desired_access = desired_access;
248 io.in.file_attributes = file_attributes;
249 io.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
251 NTCREATEX_SHARE_ACCESS_DELETE|
252 NTCREATEX_SHARE_ACCESS_READ|
253 NTCREATEX_SHARE_ACCESS_WRITE;
254 if (file_attributes & FILE_ATTRIBUTE_DIRECTORY) {
255 io.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
259 status = smb2_create(tree, mem_ctx, &io);
260 torture_assert_ntstatus_ok(torture, status, "file create");
262 *fh = io.out.file.handle;
267 static bool test_setup_create_fill(struct torture_context *torture,
268 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
270 struct smb2_handle *fh,
272 uint32_t desired_access,
273 uint32_t file_attributes)
276 uint32_t initial_access = desired_access;
279 initial_access |= SEC_FILE_APPEND_DATA;
282 smb2_util_unlink(tree, fname);
284 ok = test_setup_open(torture, tree, mem_ctx,
289 torture_assert(torture, ok, "file create");
292 ok = write_pattern(torture, tree, mem_ctx, *fh, 0, size, 0);
293 torture_assert(torture, ok, "write pattern");
296 if (initial_access != desired_access) {
297 smb2_util_close(tree, *fh);
298 ok = test_setup_open(torture, tree, mem_ctx,
303 torture_assert(torture, ok, "file open");
309 static bool test_setup_copy_chunk(struct torture_context *torture,
310 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
312 struct smb2_handle *src_h,
314 uint32_t src_desired_access,
315 struct smb2_handle *dest_h,
317 uint32_t dest_desired_access,
318 struct srv_copychunk_copy *cc_copy,
319 union smb_ioctl *ioctl)
321 struct req_resume_key_rsp res_key;
324 enum ndr_err_code ndr_ret;
326 ok = test_setup_create_fill(torture, tree, mem_ctx, FNAME,
327 src_h, src_size, src_desired_access,
328 FILE_ATTRIBUTE_NORMAL);
329 torture_assert(torture, ok, "src file create fill");
331 ok = test_setup_create_fill(torture, tree, mem_ctx, FNAME2,
332 dest_h, dest_size, dest_desired_access,
333 FILE_ATTRIBUTE_NORMAL);
334 torture_assert(torture, ok, "dest file create fill");
336 ZERO_STRUCTPN(ioctl);
337 ioctl->smb2.level = RAW_IOCTL_SMB2;
338 ioctl->smb2.in.file.handle = *src_h;
339 ioctl->smb2.in.function = FSCTL_SRV_REQUEST_RESUME_KEY;
340 /* Allow for Key + ContextLength + Context */
341 ioctl->smb2.in.max_response_size = 32;
342 ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
344 status = smb2_ioctl(tree, mem_ctx, &ioctl->smb2);
345 torture_assert_ntstatus_ok(torture, status,
346 "FSCTL_SRV_REQUEST_RESUME_KEY");
348 ndr_ret = ndr_pull_struct_blob(&ioctl->smb2.out.out, mem_ctx, &res_key,
349 (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
351 torture_assert_ndr_success(torture, ndr_ret,
352 "ndr_pull_req_resume_key_rsp");
354 ZERO_STRUCTPN(ioctl);
355 ioctl->smb2.level = RAW_IOCTL_SMB2;
356 ioctl->smb2.in.file.handle = *dest_h;
357 ioctl->smb2.in.function = FSCTL_SRV_COPYCHUNK;
358 ioctl->smb2.in.max_response_size = sizeof(struct srv_copychunk_rsp);
359 ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
361 ZERO_STRUCTPN(cc_copy);
362 memcpy(cc_copy->source_key, res_key.resume_key, ARRAY_SIZE(cc_copy->source_key));
363 cc_copy->chunk_count = nchunks;
364 cc_copy->chunks = talloc_zero_array(mem_ctx, struct srv_copychunk, nchunks);
365 torture_assert(torture, (cc_copy->chunks != NULL), "no memory for chunks");
371 static bool check_copy_chunk_rsp(struct torture_context *torture,
372 struct srv_copychunk_rsp *cc_rsp,
373 uint32_t ex_chunks_written,
374 uint32_t ex_chunk_bytes_written,
375 uint32_t ex_total_bytes_written)
377 torture_assert_int_equal(torture, cc_rsp->chunks_written,
378 ex_chunks_written, "num chunks");
379 torture_assert_int_equal(torture, cc_rsp->chunk_bytes_written,
380 ex_chunk_bytes_written, "chunk bytes written");
381 torture_assert_int_equal(torture, cc_rsp->total_bytes_written,
382 ex_total_bytes_written, "chunk total bytes");
386 static bool test_ioctl_copy_chunk_simple(struct torture_context *torture,
387 struct smb2_tree *tree)
389 struct smb2_handle src_h;
390 struct smb2_handle dest_h;
392 union smb_ioctl ioctl;
393 TALLOC_CTX *tmp_ctx = talloc_new(tree);
394 struct srv_copychunk_copy cc_copy;
395 struct srv_copychunk_rsp cc_rsp;
396 enum ndr_err_code ndr_ret;
399 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
401 &src_h, 4096, /* fill 4096 byte src file */
403 &dest_h, 0, /* 0 byte dest file */
408 torture_fail(torture, "setup copy chunk error");
411 /* copy all src file data (via a single chunk desc) */
412 cc_copy.chunks[0].source_off = 0;
413 cc_copy.chunks[0].target_off = 0;
414 cc_copy.chunks[0].length = 4096;
416 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
418 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
419 torture_assert_ndr_success(torture, ndr_ret,
420 "ndr_push_srv_copychunk_copy");
422 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
423 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
425 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
427 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
428 torture_assert_ndr_success(torture, ndr_ret,
429 "ndr_pull_srv_copychunk_rsp");
431 ok = check_copy_chunk_rsp(torture, &cc_rsp,
432 1, /* chunks written */
433 0, /* chunk bytes unsuccessfully written */
434 4096); /* total bytes written */
436 torture_fail(torture, "bad copy chunk response data");
439 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
441 torture_fail(torture, "inconsistent file data");
444 smb2_util_close(tree, src_h);
445 smb2_util_close(tree, dest_h);
446 talloc_free(tmp_ctx);
450 static bool test_ioctl_copy_chunk_multi(struct torture_context *torture,
451 struct smb2_tree *tree)
453 struct smb2_handle src_h;
454 struct smb2_handle dest_h;
456 union smb_ioctl ioctl;
457 TALLOC_CTX *tmp_ctx = talloc_new(tree);
458 struct srv_copychunk_copy cc_copy;
459 struct srv_copychunk_rsp cc_rsp;
460 enum ndr_err_code ndr_ret;
463 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
465 &src_h, 8192, /* src file */
467 &dest_h, 0, /* dest file */
472 torture_fail(torture, "setup copy chunk error");
475 /* copy all src file data via two chunks */
476 cc_copy.chunks[0].source_off = 0;
477 cc_copy.chunks[0].target_off = 0;
478 cc_copy.chunks[0].length = 4096;
480 cc_copy.chunks[1].source_off = 4096;
481 cc_copy.chunks[1].target_off = 4096;
482 cc_copy.chunks[1].length = 4096;
484 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
486 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
487 torture_assert_ndr_success(torture, ndr_ret,
488 "ndr_push_srv_copychunk_copy");
490 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
491 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
493 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
495 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
496 torture_assert_ndr_success(torture, ndr_ret,
497 "ndr_pull_srv_copychunk_rsp");
499 ok = check_copy_chunk_rsp(torture, &cc_rsp,
500 2, /* chunks written */
501 0, /* chunk bytes unsuccessfully written */
502 8192); /* total bytes written */
504 torture_fail(torture, "bad copy chunk response data");
507 smb2_util_close(tree, src_h);
508 smb2_util_close(tree, dest_h);
509 talloc_free(tmp_ctx);
513 static bool test_ioctl_copy_chunk_tiny(struct torture_context *torture,
514 struct smb2_tree *tree)
516 struct smb2_handle src_h;
517 struct smb2_handle dest_h;
519 union smb_ioctl ioctl;
520 TALLOC_CTX *tmp_ctx = talloc_new(tree);
521 struct srv_copychunk_copy cc_copy;
522 struct srv_copychunk_rsp cc_rsp;
523 enum ndr_err_code ndr_ret;
526 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
528 &src_h, 96, /* src file */
530 &dest_h, 0, /* dest file */
535 torture_fail(torture, "setup copy chunk error");
538 /* copy all src file data via two chunks, sub block size chunks */
539 cc_copy.chunks[0].source_off = 0;
540 cc_copy.chunks[0].target_off = 0;
541 cc_copy.chunks[0].length = 48;
543 cc_copy.chunks[1].source_off = 48;
544 cc_copy.chunks[1].target_off = 48;
545 cc_copy.chunks[1].length = 48;
547 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
549 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
550 torture_assert_ndr_success(torture, ndr_ret,
551 "ndr_push_srv_copychunk_copy");
553 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
554 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
556 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
558 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
559 torture_assert_ndr_success(torture, ndr_ret,
560 "ndr_pull_srv_copychunk_rsp");
562 ok = check_copy_chunk_rsp(torture, &cc_rsp,
563 2, /* chunks written */
564 0, /* chunk bytes unsuccessfully written */
565 96); /* total bytes written */
567 torture_fail(torture, "bad copy chunk response data");
570 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 96, 0);
572 torture_fail(torture, "inconsistent file data");
575 smb2_util_close(tree, src_h);
576 smb2_util_close(tree, dest_h);
577 talloc_free(tmp_ctx);
581 static bool test_ioctl_copy_chunk_over(struct torture_context *torture,
582 struct smb2_tree *tree)
584 struct smb2_handle src_h;
585 struct smb2_handle dest_h;
587 union smb_ioctl ioctl;
588 TALLOC_CTX *tmp_ctx = talloc_new(tree);
589 struct srv_copychunk_copy cc_copy;
590 struct srv_copychunk_rsp cc_rsp;
591 enum ndr_err_code ndr_ret;
594 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
596 &src_h, 8192, /* src file */
598 &dest_h, 4096, /* dest file */
603 torture_fail(torture, "setup copy chunk error");
606 /* first chunk overwrites existing dest data */
607 cc_copy.chunks[0].source_off = 0;
608 cc_copy.chunks[0].target_off = 0;
609 cc_copy.chunks[0].length = 4096;
611 /* second chunk overwrites the first */
612 cc_copy.chunks[1].source_off = 4096;
613 cc_copy.chunks[1].target_off = 0;
614 cc_copy.chunks[1].length = 4096;
616 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
618 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
619 torture_assert_ndr_success(torture, ndr_ret,
620 "ndr_push_srv_copychunk_copy");
622 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
623 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
625 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
627 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
628 torture_assert_ndr_success(torture, ndr_ret,
629 "ndr_pull_srv_copychunk_rsp");
631 ok = check_copy_chunk_rsp(torture, &cc_rsp,
632 2, /* chunks written */
633 0, /* chunk bytes unsuccessfully written */
634 8192); /* total bytes written */
636 torture_fail(torture, "bad copy chunk response data");
639 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 4096);
641 torture_fail(torture, "inconsistent file data");
644 smb2_util_close(tree, src_h);
645 smb2_util_close(tree, dest_h);
646 talloc_free(tmp_ctx);
650 static bool test_ioctl_copy_chunk_append(struct torture_context *torture,
651 struct smb2_tree *tree)
653 struct smb2_handle src_h;
654 struct smb2_handle dest_h;
656 union smb_ioctl ioctl;
657 TALLOC_CTX *tmp_ctx = talloc_new(tree);
658 struct srv_copychunk_copy cc_copy;
659 struct srv_copychunk_rsp cc_rsp;
660 enum ndr_err_code ndr_ret;
663 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
665 &src_h, 4096, /* src file */
667 &dest_h, 0, /* dest file */
672 torture_fail(torture, "setup copy chunk error");
675 cc_copy.chunks[0].source_off = 0;
676 cc_copy.chunks[0].target_off = 0;
677 cc_copy.chunks[0].length = 4096;
679 /* second chunk appends the same data to the first */
680 cc_copy.chunks[1].source_off = 0;
681 cc_copy.chunks[1].target_off = 4096;
682 cc_copy.chunks[1].length = 4096;
684 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
686 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
687 torture_assert_ndr_success(torture, ndr_ret,
688 "ndr_push_srv_copychunk_copy");
690 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
691 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
693 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
695 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
696 torture_assert_ndr_success(torture, ndr_ret,
697 "ndr_pull_srv_copychunk_rsp");
699 ok = check_copy_chunk_rsp(torture, &cc_rsp,
700 2, /* chunks written */
701 0, /* chunk bytes unsuccessfully written */
702 8192); /* total bytes written */
704 torture_fail(torture, "bad copy chunk response data");
707 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
709 torture_fail(torture, "inconsistent file data");
712 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 4096, 4096, 0);
714 torture_fail(torture, "inconsistent file data");
717 smb2_util_close(tree, src_h);
718 smb2_util_close(tree, dest_h);
719 talloc_free(tmp_ctx);
723 static bool test_ioctl_copy_chunk_limits(struct torture_context *torture,
724 struct smb2_tree *tree)
726 struct smb2_handle src_h;
727 struct smb2_handle dest_h;
729 union smb_ioctl ioctl;
730 TALLOC_CTX *tmp_ctx = talloc_new(tree);
731 struct srv_copychunk_copy cc_copy;
732 struct srv_copychunk_rsp cc_rsp;
733 enum ndr_err_code ndr_ret;
736 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
738 &src_h, 4096, /* src file */
740 &dest_h, 0, /* dest file */
745 torture_fail(torture, "setup copy chunk error");
748 /* send huge chunk length request */
749 cc_copy.chunks[0].source_off = 0;
750 cc_copy.chunks[0].target_off = 0;
751 cc_copy.chunks[0].length = UINT_MAX;
753 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
755 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
756 torture_assert_ndr_success(torture, ndr_ret, "marshalling request");
758 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
759 torture_assert_ntstatus_equal(torture, status,
760 NT_STATUS_INVALID_PARAMETER,
761 "bad oversize chunk response");
763 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
765 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
766 torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
768 torture_comment(torture, "limit max chunks, got %u\n",
769 cc_rsp.chunks_written);
770 torture_comment(torture, "limit max chunk len, got %u\n",
771 cc_rsp.chunk_bytes_written);
772 torture_comment(torture, "limit max total bytes, got %u\n",
773 cc_rsp.total_bytes_written);
775 smb2_util_close(tree, src_h);
776 smb2_util_close(tree, dest_h);
777 talloc_free(tmp_ctx);
781 static bool test_ioctl_copy_chunk_src_lck(struct torture_context *torture,
782 struct smb2_tree *tree)
784 struct smb2_handle src_h;
785 struct smb2_handle src_h2;
786 struct smb2_handle dest_h;
788 union smb_ioctl ioctl;
789 TALLOC_CTX *tmp_ctx = talloc_new(tree);
790 struct srv_copychunk_copy cc_copy;
791 struct srv_copychunk_rsp cc_rsp;
792 enum ndr_err_code ndr_ret;
794 struct smb2_lock lck;
795 struct smb2_lock_element el[1];
797 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
799 &src_h, 4096, /* src file */
801 &dest_h, 0, /* dest file */
806 torture_fail(torture, "setup copy chunk error");
809 cc_copy.chunks[0].source_off = 0;
810 cc_copy.chunks[0].target_off = 0;
811 cc_copy.chunks[0].length = 4096;
813 /* open and lock the copychunk src file */
814 status = torture_smb2_testfile(tree, FNAME, &src_h2);
815 torture_assert_ntstatus_ok(torture, status, "2nd src open");
817 lck.in.lock_count = 0x0001;
818 lck.in.lock_sequence = 0x00000000;
819 lck.in.file.handle = src_h2;
821 el[0].offset = cc_copy.chunks[0].source_off;
822 el[0].length = cc_copy.chunks[0].length;
824 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
826 status = smb2_lock(tree, &lck);
827 torture_assert_ntstatus_ok(torture, status, "lock");
829 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
831 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
832 torture_assert_ndr_success(torture, ndr_ret,
833 "ndr_push_srv_copychunk_copy");
835 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
837 * 2k12 & Samba return lock_conflict, Windows 7 & 2k8 return success...
839 * Edgar Olougouna @ MS wrote:
840 * Regarding the FSCTL_SRV_COPYCHUNK and STATUS_FILE_LOCK_CONFLICT
841 * discrepancy observed between Windows versions, we confirm that the
842 * behavior change is expected.
844 * CopyChunk in Windows Server 2012 use regular Readfile/Writefile APIs
845 * to move the chunks from the source to the destination.
846 * These ReadFile/WriteFile APIs go through the byte-range lock checks,
847 * and this explains the observed STATUS_FILE_LOCK_CONFLICT error.
849 * Prior to Windows Server 2012, CopyChunk used mapped sections to move
850 * the data. And byte range locks are not enforced on mapped I/O, and
851 * this explains the STATUS_SUCCESS observed on Windows Server 2008 R2.
853 torture_assert_ntstatus_equal(torture, status,
854 NT_STATUS_FILE_LOCK_CONFLICT,
855 "FSCTL_SRV_COPYCHUNK locked");
857 /* should get cc response data with the lock conflict status */
858 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
860 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
861 torture_assert_ndr_success(torture, ndr_ret,
862 "ndr_pull_srv_copychunk_rsp");
863 ok = check_copy_chunk_rsp(torture, &cc_rsp,
864 0, /* chunks written */
865 0, /* chunk bytes unsuccessfully written */
866 0); /* total bytes written */
868 lck.in.lock_count = 0x0001;
869 lck.in.lock_sequence = 0x00000001;
870 lck.in.file.handle = src_h2;
872 el[0].offset = cc_copy.chunks[0].source_off;
873 el[0].length = cc_copy.chunks[0].length;
875 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
876 status = smb2_lock(tree, &lck);
877 torture_assert_ntstatus_ok(torture, status, "unlock");
879 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
880 torture_assert_ntstatus_ok(torture, status,
881 "FSCTL_SRV_COPYCHUNK unlocked");
883 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
885 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
886 torture_assert_ndr_success(torture, ndr_ret,
887 "ndr_pull_srv_copychunk_rsp");
889 ok = check_copy_chunk_rsp(torture, &cc_rsp,
890 1, /* chunks written */
891 0, /* chunk bytes unsuccessfully written */
892 4096); /* total bytes written */
894 torture_fail(torture, "bad copy chunk response data");
897 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
899 torture_fail(torture, "inconsistent file data");
902 smb2_util_close(tree, src_h2);
903 smb2_util_close(tree, src_h);
904 smb2_util_close(tree, dest_h);
905 talloc_free(tmp_ctx);
909 static bool test_ioctl_copy_chunk_dest_lck(struct torture_context *torture,
910 struct smb2_tree *tree)
912 struct smb2_handle src_h;
913 struct smb2_handle dest_h;
914 struct smb2_handle dest_h2;
916 union smb_ioctl ioctl;
917 TALLOC_CTX *tmp_ctx = talloc_new(tree);
918 struct srv_copychunk_copy cc_copy;
919 struct srv_copychunk_rsp cc_rsp;
920 enum ndr_err_code ndr_ret;
922 struct smb2_lock lck;
923 struct smb2_lock_element el[1];
925 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
927 &src_h, 4096, /* src file */
929 &dest_h, 4096, /* dest file */
934 torture_fail(torture, "setup copy chunk error");
937 cc_copy.chunks[0].source_off = 0;
938 cc_copy.chunks[0].target_off = 0;
939 cc_copy.chunks[0].length = 4096;
941 /* open and lock the copychunk dest file */
942 status = torture_smb2_testfile(tree, FNAME2, &dest_h2);
943 torture_assert_ntstatus_ok(torture, status, "2nd src open");
945 lck.in.lock_count = 0x0001;
946 lck.in.lock_sequence = 0x00000000;
947 lck.in.file.handle = dest_h2;
949 el[0].offset = cc_copy.chunks[0].target_off;
950 el[0].length = cc_copy.chunks[0].length;
952 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
954 status = smb2_lock(tree, &lck);
955 torture_assert_ntstatus_ok(torture, status, "lock");
957 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
959 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
960 torture_assert_ndr_success(torture, ndr_ret,
961 "ndr_push_srv_copychunk_copy");
963 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
964 torture_assert_ntstatus_equal(torture, status,
965 NT_STATUS_FILE_LOCK_CONFLICT,
966 "FSCTL_SRV_COPYCHUNK locked");
968 lck.in.lock_count = 0x0001;
969 lck.in.lock_sequence = 0x00000001;
970 lck.in.file.handle = dest_h2;
972 el[0].offset = cc_copy.chunks[0].target_off;
973 el[0].length = cc_copy.chunks[0].length;
975 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
976 status = smb2_lock(tree, &lck);
977 torture_assert_ntstatus_ok(torture, status, "unlock");
979 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
980 torture_assert_ntstatus_ok(torture, status,
981 "FSCTL_SRV_COPYCHUNK unlocked");
983 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
985 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
986 torture_assert_ndr_success(torture, ndr_ret,
987 "ndr_pull_srv_copychunk_rsp");
989 ok = check_copy_chunk_rsp(torture, &cc_rsp,
990 1, /* chunks written */
991 0, /* chunk bytes unsuccessfully written */
992 4096); /* total bytes written */
994 torture_fail(torture, "bad copy chunk response data");
997 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
999 torture_fail(torture, "inconsistent file data");
1002 smb2_util_close(tree, dest_h2);
1003 smb2_util_close(tree, src_h);
1004 smb2_util_close(tree, dest_h);
1005 talloc_free(tmp_ctx);
1009 static bool test_ioctl_copy_chunk_bad_key(struct torture_context *torture,
1010 struct smb2_tree *tree)
1012 struct smb2_handle src_h;
1013 struct smb2_handle dest_h;
1015 union smb_ioctl ioctl;
1016 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1017 struct srv_copychunk_copy cc_copy;
1018 enum ndr_err_code ndr_ret;
1021 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1024 SEC_RIGHTS_FILE_ALL,
1026 SEC_RIGHTS_FILE_ALL,
1030 torture_fail(torture, "setup copy chunk error");
1033 /* overwrite the resume key with a bogus value */
1034 memcpy(cc_copy.source_key, "deadbeefdeadbeefdeadbeef", 24);
1036 cc_copy.chunks[0].source_off = 0;
1037 cc_copy.chunks[0].target_off = 0;
1038 cc_copy.chunks[0].length = 4096;
1040 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1042 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1043 torture_assert_ndr_success(torture, ndr_ret,
1044 "ndr_push_srv_copychunk_copy");
1046 /* Server 2k12 returns NT_STATUS_OBJECT_NAME_NOT_FOUND */
1047 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1048 torture_assert_ntstatus_equal(torture, status,
1049 NT_STATUS_OBJECT_NAME_NOT_FOUND,
1050 "FSCTL_SRV_COPYCHUNK");
1052 smb2_util_close(tree, src_h);
1053 smb2_util_close(tree, dest_h);
1054 talloc_free(tmp_ctx);
1058 static bool test_ioctl_copy_chunk_src_is_dest(struct torture_context *torture,
1059 struct smb2_tree *tree)
1061 struct smb2_handle src_h;
1062 struct smb2_handle dest_h;
1064 union smb_ioctl ioctl;
1065 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1066 struct srv_copychunk_copy cc_copy;
1067 struct srv_copychunk_rsp cc_rsp;
1068 enum ndr_err_code ndr_ret;
1071 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1074 SEC_RIGHTS_FILE_ALL,
1076 SEC_RIGHTS_FILE_ALL,
1080 torture_fail(torture, "setup copy chunk error");
1083 /* the source is also the destination */
1084 ioctl.smb2.in.file.handle = src_h;
1086 /* non-overlapping */
1087 cc_copy.chunks[0].source_off = 0;
1088 cc_copy.chunks[0].target_off = 4096;
1089 cc_copy.chunks[0].length = 4096;
1091 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1093 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1094 torture_assert_ndr_success(torture, ndr_ret,
1095 "ndr_push_srv_copychunk_copy");
1097 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1098 torture_assert_ntstatus_ok(torture, status,
1099 "FSCTL_SRV_COPYCHUNK");
1101 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1103 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1104 torture_assert_ndr_success(torture, ndr_ret,
1105 "ndr_pull_srv_copychunk_rsp");
1107 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1108 1, /* chunks written */
1109 0, /* chunk bytes unsuccessfully written */
1110 4096); /* total bytes written */
1112 torture_fail(torture, "bad copy chunk response data");
1115 ok = check_pattern(torture, tree, tmp_ctx, src_h, 0, 4096, 0);
1117 torture_fail(torture, "inconsistent file data");
1119 ok = check_pattern(torture, tree, tmp_ctx, src_h, 4096, 4096, 0);
1121 torture_fail(torture, "inconsistent file data");
1124 smb2_util_close(tree, src_h);
1125 smb2_util_close(tree, dest_h);
1126 talloc_free(tmp_ctx);
1131 * Test a single-chunk copychunk request, where the source and target ranges
1132 * overlap, and the SourceKey refers to the same target file. E.g:
1136 * File: src_and_dest
1137 * Offset: 0123456789
1142 * FSCTL_SRV_COPYCHUNK(src_and_dest)
1143 * SourceKey = SRV_REQUEST_RESUME_KEY(src_and_dest)
1145 * Chunks[0].SourceOffset = 0
1146 * Chunks[0].TargetOffset = 4
1147 * Chunks[0].Length = 6
1151 * File: src_and_dest
1152 * Offset: 0123456789
1155 * The resultant contents of src_and_dest is dependent on the server's
1156 * copy algorithm. In the above example, the server uses an IO buffer
1157 * large enough to hold the entire six-byte source data before writing
1158 * to TargetOffset. If the server were to use a four-byte IO buffer and
1159 * started reads/writes from the lowest offset, then the two overlapping
1160 * bytes in the above example would be overwritten before being read. The
1161 * resultant file contents would be abcdabcdab.
1163 * Windows 2008r2 appears to use a 2048 byte copy buffer, overlapping bytes
1164 * after this offset are written before being read. Windows 2012 on the
1165 * other hand appears to use a buffer large enough to hold its maximum
1166 * supported chunk size (1M). Samba currently uses a 64k copy buffer by
1167 * default (vfs_cc_state.buf).
1169 * This test uses an 8-byte overlap at 2040-2048, so that it passes against
1170 * Windows 2008r2, 2012 and Samba servers. Note, 2008GM fails, as it appears
1171 * to use a different copy algorithm to 2008r2.
1174 test_ioctl_copy_chunk_src_is_dest_overlap(struct torture_context *torture,
1175 struct smb2_tree *tree)
1177 struct smb2_handle src_h;
1178 struct smb2_handle dest_h;
1180 union smb_ioctl ioctl;
1181 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1182 struct srv_copychunk_copy cc_copy;
1183 struct srv_copychunk_rsp cc_rsp;
1184 enum ndr_err_code ndr_ret;
1187 /* exceed the vfs_default copy buffer */
1188 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1191 SEC_RIGHTS_FILE_ALL,
1193 SEC_RIGHTS_FILE_ALL,
1197 torture_fail(torture, "setup copy chunk error");
1200 /* the source is also the destination */
1201 ioctl.smb2.in.file.handle = src_h;
1203 /* 8 bytes overlap between source and target ranges */
1204 cc_copy.chunks[0].source_off = 0;
1205 cc_copy.chunks[0].target_off = 2048 - 8;
1206 cc_copy.chunks[0].length = 2048;
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_ok(torture, status,
1216 "FSCTL_SRV_COPYCHUNK");
1218 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1220 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1221 torture_assert_ndr_success(torture, ndr_ret,
1222 "ndr_pull_srv_copychunk_rsp");
1224 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1225 1, /* chunks written */
1226 0, /* chunk bytes unsuccessfully written */
1227 2048); /* total bytes written */
1229 torture_fail(torture, "bad copy chunk response data");
1232 ok = check_pattern(torture, tree, tmp_ctx, src_h, 0, 2048 - 8, 0);
1234 torture_fail(torture, "inconsistent file data");
1236 ok = check_pattern(torture, tree, tmp_ctx, src_h, 2048 - 8, 2048, 0);
1238 torture_fail(torture, "inconsistent file data");
1241 smb2_util_close(tree, src_h);
1242 smb2_util_close(tree, dest_h);
1243 talloc_free(tmp_ctx);
1247 static bool test_ioctl_copy_chunk_bad_access(struct torture_context *torture,
1248 struct smb2_tree *tree)
1250 struct smb2_handle src_h;
1251 struct smb2_handle dest_h;
1253 union smb_ioctl ioctl;
1254 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1255 struct srv_copychunk_copy cc_copy;
1256 enum ndr_err_code ndr_ret;
1258 /* read permission on src */
1259 ok = test_setup_copy_chunk(torture, tree, tmp_ctx, 1, /* 1 chunk */
1260 &src_h, 4096, /* fill 4096 byte src file */
1261 SEC_FILE_READ_DATA | SEC_FILE_READ_ATTRIBUTE,
1262 &dest_h, 0, /* 0 byte dest file */
1263 SEC_RIGHTS_FILE_ALL, &cc_copy, &ioctl);
1265 torture_fail(torture, "setup copy chunk error");
1268 cc_copy.chunks[0].source_off = 0;
1269 cc_copy.chunks[0].target_off = 0;
1270 cc_copy.chunks[0].length = 4096;
1272 ndr_ret = ndr_push_struct_blob(
1273 &ioctl.smb2.in.out, tmp_ctx, &cc_copy,
1274 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1275 torture_assert_ndr_success(torture, ndr_ret,
1276 "ndr_push_srv_copychunk_copy");
1278 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1279 torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK,
1280 "FSCTL_SRV_COPYCHUNK");
1282 smb2_util_close(tree, src_h);
1283 smb2_util_close(tree, dest_h);
1285 /* execute permission on src */
1286 ok = test_setup_copy_chunk(torture, tree, tmp_ctx, 1, /* 1 chunk */
1287 &src_h, 4096, /* fill 4096 byte src file */
1288 SEC_FILE_EXECUTE | SEC_FILE_READ_ATTRIBUTE,
1289 &dest_h, 0, /* 0 byte dest file */
1290 SEC_RIGHTS_FILE_ALL, &cc_copy, &ioctl);
1292 torture_fail(torture, "setup copy chunk error");
1295 cc_copy.chunks[0].source_off = 0;
1296 cc_copy.chunks[0].target_off = 0;
1297 cc_copy.chunks[0].length = 4096;
1299 ndr_ret = ndr_push_struct_blob(
1300 &ioctl.smb2.in.out, tmp_ctx, &cc_copy,
1301 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1302 torture_assert_ndr_success(torture, ndr_ret,
1303 "ndr_push_srv_copychunk_copy");
1305 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1306 torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK,
1307 "FSCTL_SRV_COPYCHUNK");
1309 smb2_util_close(tree, src_h);
1310 smb2_util_close(tree, dest_h);
1312 /* neither read nor execute permission on src */
1313 ok = test_setup_copy_chunk(torture, tree, tmp_ctx, 1, /* 1 chunk */
1314 &src_h, 4096, /* fill 4096 byte src file */
1315 SEC_FILE_READ_ATTRIBUTE, &dest_h,
1316 0, /* 0 byte dest file */
1317 SEC_RIGHTS_FILE_ALL, &cc_copy, &ioctl);
1319 torture_fail(torture, "setup copy chunk error");
1322 cc_copy.chunks[0].source_off = 0;
1323 cc_copy.chunks[0].target_off = 0;
1324 cc_copy.chunks[0].length = 4096;
1326 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1328 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1329 torture_assert_ndr_success(torture, ndr_ret,
1330 "ndr_push_srv_copychunk_copy");
1332 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1333 torture_assert_ntstatus_equal(torture, status,
1334 NT_STATUS_ACCESS_DENIED,
1335 "FSCTL_SRV_COPYCHUNK");
1337 smb2_util_close(tree, src_h);
1338 smb2_util_close(tree, dest_h);
1340 /* no write permission on dest */
1341 ok = test_setup_copy_chunk(
1342 torture, tree, tmp_ctx, 1, /* 1 chunk */
1343 &src_h, 4096, /* fill 4096 byte src file */
1344 SEC_FILE_READ_DATA | SEC_FILE_READ_ATTRIBUTE, &dest_h,
1345 0, /* 0 byte dest file */
1346 (SEC_RIGHTS_FILE_ALL &
1347 ~(SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)),
1350 torture_fail(torture, "setup copy chunk error");
1353 cc_copy.chunks[0].source_off = 0;
1354 cc_copy.chunks[0].target_off = 0;
1355 cc_copy.chunks[0].length = 4096;
1357 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1359 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1360 torture_assert_ndr_success(torture, ndr_ret,
1361 "ndr_push_srv_copychunk_copy");
1363 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1364 torture_assert_ntstatus_equal(torture, status,
1365 NT_STATUS_ACCESS_DENIED,
1366 "FSCTL_SRV_COPYCHUNK");
1368 smb2_util_close(tree, src_h);
1369 smb2_util_close(tree, dest_h);
1371 /* no read permission on dest */
1372 ok = test_setup_copy_chunk(torture, tree, tmp_ctx, 1, /* 1 chunk */
1373 &src_h, 4096, /* fill 4096 byte src file */
1374 SEC_FILE_READ_DATA | SEC_FILE_READ_ATTRIBUTE,
1375 &dest_h, 0, /* 0 byte dest file */
1376 (SEC_RIGHTS_FILE_ALL & ~SEC_FILE_READ_DATA),
1379 torture_fail(torture, "setup copy chunk error");
1382 cc_copy.chunks[0].source_off = 0;
1383 cc_copy.chunks[0].target_off = 0;
1384 cc_copy.chunks[0].length = 4096;
1386 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1388 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1389 torture_assert_ndr_success(torture, ndr_ret,
1390 "ndr_push_srv_copychunk_copy");
1393 * FSCTL_SRV_COPYCHUNK requires read permission on dest,
1394 * FSCTL_SRV_COPYCHUNK_WRITE on the other hand does not.
1396 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1397 torture_assert_ntstatus_equal(torture, status,
1398 NT_STATUS_ACCESS_DENIED,
1399 "FSCTL_SRV_COPYCHUNK");
1401 smb2_util_close(tree, src_h);
1402 smb2_util_close(tree, dest_h);
1403 talloc_free(tmp_ctx);
1408 static bool test_ioctl_copy_chunk_write_access(struct torture_context *torture,
1409 struct smb2_tree *tree)
1411 struct smb2_handle src_h;
1412 struct smb2_handle dest_h;
1414 union smb_ioctl ioctl;
1415 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1416 struct srv_copychunk_copy cc_copy;
1417 enum ndr_err_code ndr_ret;
1420 /* no read permission on dest with FSCTL_SRV_COPYCHUNK_WRITE */
1421 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1423 &src_h, 4096, /* fill 4096 byte src file */
1424 SEC_RIGHTS_FILE_ALL,
1425 &dest_h, 0, /* 0 byte dest file */
1426 (SEC_RIGHTS_FILE_WRITE
1427 | SEC_RIGHTS_FILE_EXECUTE),
1431 torture_fail(torture, "setup copy chunk error");
1434 ioctl.smb2.in.function = FSCTL_SRV_COPYCHUNK_WRITE;
1435 cc_copy.chunks[0].source_off = 0;
1436 cc_copy.chunks[0].target_off = 0;
1437 cc_copy.chunks[0].length = 4096;
1439 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1441 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1442 torture_assert_ndr_success(torture, ndr_ret,
1443 "ndr_push_srv_copychunk_copy");
1445 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1446 torture_assert_ntstatus_ok(torture, status,
1447 "FSCTL_SRV_COPYCHUNK_WRITE");
1449 smb2_util_close(tree, src_h);
1450 smb2_util_close(tree, dest_h);
1451 talloc_free(tmp_ctx);
1456 static bool test_ioctl_copy_chunk_src_exceed(struct torture_context *torture,
1457 struct smb2_tree *tree)
1459 struct smb2_handle src_h;
1460 struct smb2_handle dest_h;
1462 union smb_ioctl ioctl;
1463 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1464 struct srv_copychunk_copy cc_copy;
1465 struct srv_copychunk_rsp cc_rsp;
1466 enum ndr_err_code ndr_ret;
1469 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1471 &src_h, 4096, /* fill 4096 byte src file */
1472 SEC_RIGHTS_FILE_ALL,
1473 &dest_h, 0, /* 0 byte dest file */
1474 SEC_RIGHTS_FILE_ALL,
1478 torture_fail(torture, "setup copy chunk error");
1481 /* Request copy where off + length exceeds size of src */
1482 cc_copy.chunks[0].source_off = 1024;
1483 cc_copy.chunks[0].target_off = 0;
1484 cc_copy.chunks[0].length = 4096;
1486 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1488 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1489 torture_assert_ndr_success(torture, ndr_ret,
1490 "ndr_push_srv_copychunk_copy");
1492 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1493 torture_assert_ntstatus_equal(torture, status,
1494 NT_STATUS_INVALID_VIEW_SIZE,
1495 "FSCTL_SRV_COPYCHUNK oversize");
1497 /* Request copy where length exceeds size of src */
1498 cc_copy.chunks[0].source_off = 1024;
1499 cc_copy.chunks[0].target_off = 0;
1500 cc_copy.chunks[0].length = 3072;
1502 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1504 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1505 torture_assert_ndr_success(torture, ndr_ret,
1506 "ndr_push_srv_copychunk_copy");
1508 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1509 torture_assert_ntstatus_ok(torture, status,
1510 "FSCTL_SRV_COPYCHUNK just right");
1512 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1514 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1515 torture_assert_ndr_success(torture, ndr_ret,
1516 "ndr_pull_srv_copychunk_rsp");
1518 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1519 1, /* chunks written */
1520 0, /* chunk bytes unsuccessfully written */
1521 3072); /* total bytes written */
1523 torture_fail(torture, "bad copy chunk response data");
1526 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 3072, 1024);
1528 torture_fail(torture, "inconsistent file data");
1531 smb2_util_close(tree, src_h);
1532 smb2_util_close(tree, dest_h);
1533 talloc_free(tmp_ctx);
1538 test_ioctl_copy_chunk_src_exceed_multi(struct torture_context *torture,
1539 struct smb2_tree *tree)
1541 struct smb2_handle src_h;
1542 struct smb2_handle dest_h;
1544 union smb_ioctl ioctl;
1545 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1546 struct srv_copychunk_copy cc_copy;
1547 struct srv_copychunk_rsp cc_rsp;
1548 enum ndr_err_code ndr_ret;
1551 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1553 &src_h, 8192, /* fill 8192 byte src file */
1554 SEC_RIGHTS_FILE_ALL,
1555 &dest_h, 0, /* 0 byte dest file */
1556 SEC_RIGHTS_FILE_ALL,
1560 torture_fail(torture, "setup copy chunk error");
1563 /* Request copy where off + length exceeds size of src */
1564 cc_copy.chunks[0].source_off = 0;
1565 cc_copy.chunks[0].target_off = 0;
1566 cc_copy.chunks[0].length = 4096;
1568 cc_copy.chunks[1].source_off = 4096;
1569 cc_copy.chunks[1].target_off = 4096;
1570 cc_copy.chunks[1].length = 8192;
1572 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1574 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1575 torture_assert_ndr_success(torture, ndr_ret,
1576 "ndr_push_srv_copychunk_copy");
1578 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1579 torture_assert_ntstatus_equal(torture, status,
1580 NT_STATUS_INVALID_VIEW_SIZE,
1581 "FSCTL_SRV_COPYCHUNK oversize");
1582 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1584 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1585 torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
1587 /* first chunk should still be written */
1588 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1589 1, /* chunks written */
1590 0, /* chunk bytes unsuccessfully written */
1591 4096); /* total bytes written */
1593 torture_fail(torture, "bad copy chunk response data");
1595 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
1597 torture_fail(torture, "inconsistent file data");
1600 smb2_util_close(tree, src_h);
1601 smb2_util_close(tree, dest_h);
1602 talloc_free(tmp_ctx);
1606 static bool test_ioctl_copy_chunk_sparse_dest(struct torture_context *torture,
1607 struct smb2_tree *tree)
1609 struct smb2_handle src_h;
1610 struct smb2_handle dest_h;
1612 union smb_ioctl ioctl;
1614 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1615 struct srv_copychunk_copy cc_copy;
1616 struct srv_copychunk_rsp cc_rsp;
1617 enum ndr_err_code ndr_ret;
1621 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1623 &src_h, 4096, /* fill 4096 byte src file */
1624 SEC_RIGHTS_FILE_ALL,
1625 &dest_h, 0, /* 0 byte dest file */
1626 SEC_RIGHTS_FILE_ALL,
1630 torture_fail(torture, "setup copy chunk error");
1633 /* copy all src file data (via a single chunk desc) */
1634 cc_copy.chunks[0].source_off = 0;
1635 cc_copy.chunks[0].target_off = 4096;
1636 cc_copy.chunks[0].length = 4096;
1638 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1640 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1641 torture_assert_ndr_success(torture, ndr_ret,
1642 "ndr_push_srv_copychunk_copy");
1644 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1645 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
1647 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1649 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1650 torture_assert_ndr_success(torture, ndr_ret,
1651 "ndr_pull_srv_copychunk_rsp");
1653 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1654 1, /* chunks written */
1655 0, /* chunk bytes unsuccessfully written */
1656 4096); /* total bytes written */
1658 torture_fail(torture, "bad copy chunk response data");
1661 /* check for zeros in first 4k */
1663 r.in.file.handle = dest_h;
1666 status = smb2_read(tree, tmp_ctx, &r);
1667 torture_assert_ntstatus_ok(torture, status, "read");
1669 torture_assert_u64_equal(torture, r.out.data.length, 4096,
1670 "read data len mismatch");
1672 for (i = 0; i < 4096; i++) {
1673 torture_assert(torture, (r.out.data.data[i] == 0),
1674 "sparse did not pass class");
1677 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 4096, 4096, 0);
1679 torture_fail(torture, "inconsistent file data");
1682 smb2_util_close(tree, src_h);
1683 smb2_util_close(tree, dest_h);
1684 talloc_free(tmp_ctx);
1689 * set the ioctl MaxOutputResponse size to less than
1690 * sizeof(struct srv_copychunk_rsp)
1692 static bool test_ioctl_copy_chunk_max_output_sz(struct torture_context *torture,
1693 struct smb2_tree *tree)
1695 struct smb2_handle src_h;
1696 struct smb2_handle dest_h;
1698 union smb_ioctl ioctl;
1699 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1700 struct srv_copychunk_copy cc_copy;
1701 enum ndr_err_code ndr_ret;
1704 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1706 &src_h, 4096, /* fill 4096 byte src file */
1707 SEC_RIGHTS_FILE_ALL,
1708 &dest_h, 0, /* 0 byte dest file */
1709 SEC_RIGHTS_FILE_ALL,
1713 torture_fail(torture, "setup copy chunk error");
1716 cc_copy.chunks[0].source_off = 0;
1717 cc_copy.chunks[0].target_off = 0;
1718 cc_copy.chunks[0].length = 4096;
1719 /* req is valid, but use undersize max_response_size */
1720 ioctl.smb2.in.max_response_size = sizeof(struct srv_copychunk_rsp) - 1;
1722 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1724 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1725 torture_assert_ndr_success(torture, ndr_ret,
1726 "ndr_push_srv_copychunk_copy");
1728 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1729 torture_assert_ntstatus_equal(torture, status,
1730 NT_STATUS_INVALID_PARAMETER,
1731 "FSCTL_SRV_COPYCHUNK");
1733 smb2_util_close(tree, src_h);
1734 smb2_util_close(tree, dest_h);
1735 talloc_free(tmp_ctx);
1739 static bool test_ioctl_copy_chunk_zero_length(struct torture_context *torture,
1740 struct smb2_tree *tree)
1742 struct smb2_handle src_h;
1743 struct smb2_handle dest_h;
1745 union smb_ioctl ioctl;
1746 union smb_fileinfo q;
1747 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1748 struct srv_copychunk_copy cc_copy;
1749 struct srv_copychunk_rsp cc_rsp;
1750 enum ndr_err_code ndr_ret;
1753 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1755 &src_h, 4096, /* fill 4096 byte src file */
1756 SEC_RIGHTS_FILE_ALL,
1757 &dest_h, 0, /* 0 byte dest file */
1758 SEC_RIGHTS_FILE_ALL,
1762 torture_fail(torture, "setup copy chunk error");
1765 /* zero length server-side copy (via a single chunk desc) */
1766 cc_copy.chunks[0].source_off = 0;
1767 cc_copy.chunks[0].target_off = 0;
1768 cc_copy.chunks[0].length = 0;
1770 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1772 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1773 torture_assert_ndr_success(torture, ndr_ret,
1774 "ndr_push_srv_copychunk_copy");
1776 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1777 torture_assert_ntstatus_equal(torture, status,
1778 NT_STATUS_INVALID_PARAMETER,
1779 "bad zero-length chunk response");
1781 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1783 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1784 torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
1787 q.all_info2.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
1788 q.all_info2.in.file.handle = dest_h;
1789 status = smb2_getinfo_file(tree, torture, &q);
1790 torture_assert_ntstatus_ok(torture, status, "getinfo");
1792 torture_assert_int_equal(torture, q.all_info2.out.size, 0,
1793 "size after zero len clone");
1795 smb2_util_close(tree, src_h);
1796 smb2_util_close(tree, dest_h);
1797 talloc_free(tmp_ctx);
1801 static NTSTATUS test_ioctl_compress_fs_supported(struct torture_context *torture,
1802 struct smb2_tree *tree,
1803 TALLOC_CTX *mem_ctx,
1804 struct smb2_handle *fh,
1805 bool *compress_support)
1808 union smb_fsinfo info;
1811 info.generic.level = RAW_QFS_ATTRIBUTE_INFORMATION;
1812 info.generic.handle = *fh;
1813 status = smb2_getinfo_fs(tree, tree, &info);
1814 if (!NT_STATUS_IS_OK(status)) {
1818 if (info.attribute_info.out.fs_attr & FILE_FILE_COMPRESSION) {
1819 *compress_support = true;
1821 *compress_support = false;
1823 return NT_STATUS_OK;
1826 static NTSTATUS test_ioctl_compress_get(struct torture_context *torture,
1827 TALLOC_CTX *mem_ctx,
1828 struct smb2_tree *tree,
1829 struct smb2_handle fh,
1830 uint16_t *_compression_fmt)
1832 union smb_ioctl ioctl;
1833 struct compression_state cmpr_state;
1834 enum ndr_err_code ndr_ret;
1838 ioctl.smb2.level = RAW_IOCTL_SMB2;
1839 ioctl.smb2.in.file.handle = fh;
1840 ioctl.smb2.in.function = FSCTL_GET_COMPRESSION;
1841 ioctl.smb2.in.max_response_size = sizeof(struct compression_state);
1842 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
1844 status = smb2_ioctl(tree, mem_ctx, &ioctl.smb2);
1845 if (!NT_STATUS_IS_OK(status)) {
1849 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, mem_ctx,
1851 (ndr_pull_flags_fn_t)ndr_pull_compression_state);
1853 if (ndr_ret != NDR_ERR_SUCCESS) {
1854 return NT_STATUS_INTERNAL_ERROR;
1857 *_compression_fmt = cmpr_state.format;
1858 return NT_STATUS_OK;
1861 static NTSTATUS test_ioctl_compress_set(struct torture_context *torture,
1862 TALLOC_CTX *mem_ctx,
1863 struct smb2_tree *tree,
1864 struct smb2_handle fh,
1865 uint16_t compression_fmt)
1867 union smb_ioctl ioctl;
1868 struct compression_state cmpr_state;
1869 enum ndr_err_code ndr_ret;
1873 ioctl.smb2.level = RAW_IOCTL_SMB2;
1874 ioctl.smb2.in.file.handle = fh;
1875 ioctl.smb2.in.function = FSCTL_SET_COMPRESSION;
1876 ioctl.smb2.in.max_response_size = 0;
1877 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
1879 cmpr_state.format = compression_fmt;
1880 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, mem_ctx,
1882 (ndr_push_flags_fn_t)ndr_push_compression_state);
1883 if (ndr_ret != NDR_ERR_SUCCESS) {
1884 return NT_STATUS_INTERNAL_ERROR;
1887 status = smb2_ioctl(tree, mem_ctx, &ioctl.smb2);
1891 static bool test_ioctl_compress_file_flag(struct torture_context *torture,
1892 struct smb2_tree *tree)
1894 struct smb2_handle fh;
1896 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1898 uint16_t compression_fmt;
1900 ok = test_setup_create_fill(torture, tree, tmp_ctx,
1901 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
1902 FILE_ATTRIBUTE_NORMAL);
1903 torture_assert(torture, ok, "setup compression file");
1905 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
1907 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
1909 smb2_util_close(tree, fh);
1910 torture_skip(torture, "FS compression not supported\n");
1913 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1915 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1917 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
1918 "initial compression state not NONE");
1920 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
1921 COMPRESSION_FORMAT_DEFAULT);
1922 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
1924 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1926 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1928 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
1929 "invalid compression state after set");
1931 smb2_util_close(tree, fh);
1932 talloc_free(tmp_ctx);
1936 static bool test_ioctl_compress_dir_inherit(struct torture_context *torture,
1937 struct smb2_tree *tree)
1939 struct smb2_handle dirh;
1940 struct smb2_handle fh;
1942 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1943 uint16_t compression_fmt;
1945 char path_buf[PATH_MAX];
1947 smb2_deltree(tree, DNAME);
1948 ok = test_setup_create_fill(torture, tree, tmp_ctx,
1949 DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL,
1950 FILE_ATTRIBUTE_DIRECTORY);
1951 torture_assert(torture, ok, "setup compression directory");
1953 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &dirh,
1955 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
1957 smb2_util_close(tree, dirh);
1958 smb2_deltree(tree, DNAME);
1959 torture_skip(torture, "FS compression not supported\n");
1962 /* set compression on parent dir, then check for inheritance */
1963 status = test_ioctl_compress_set(torture, tmp_ctx, tree, dirh,
1964 COMPRESSION_FORMAT_LZNT1);
1965 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
1967 status = test_ioctl_compress_get(torture, tmp_ctx, tree, dirh,
1969 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1971 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
1972 "invalid compression state after set");
1974 snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME);
1975 ok = test_setup_create_fill(torture, tree, tmp_ctx,
1976 path_buf, &fh, 4096, SEC_RIGHTS_FILE_ALL,
1977 FILE_ATTRIBUTE_NORMAL);
1978 torture_assert(torture, ok, "setup compression file");
1980 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1982 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1984 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
1985 "compression attr not inherited by new file");
1987 /* check compressed data is consistent */
1988 ok = check_pattern(torture, tree, tmp_ctx, fh, 0, 4096, 0);
1990 /* disable dir compression attr, file should remain compressed */
1991 status = test_ioctl_compress_set(torture, tmp_ctx, tree, dirh,
1992 COMPRESSION_FORMAT_NONE);
1993 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
1995 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1997 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1999 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
2000 "file compression attr removed after dir change");
2001 smb2_util_close(tree, fh);
2003 /* new files should no longer inherit compression attr */
2004 snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME2);
2005 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2006 path_buf, &fh, 0, SEC_RIGHTS_FILE_ALL,
2007 FILE_ATTRIBUTE_NORMAL);
2008 torture_assert(torture, ok, "setup file");
2010 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2012 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2014 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2015 "compression attr present on new file");
2017 smb2_util_close(tree, fh);
2018 smb2_util_close(tree, dirh);
2019 smb2_deltree(tree, DNAME);
2020 talloc_free(tmp_ctx);
2024 static bool test_ioctl_compress_invalid_format(struct torture_context *torture,
2025 struct smb2_tree *tree)
2027 struct smb2_handle fh;
2029 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2031 uint16_t compression_fmt;
2033 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2034 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2035 FILE_ATTRIBUTE_NORMAL);
2036 torture_assert(torture, ok, "setup compression file");
2038 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
2040 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2042 smb2_util_close(tree, fh);
2043 torture_skip(torture, "FS compression not supported\n");
2046 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
2047 0x0042); /* bogus */
2048 torture_assert_ntstatus_equal(torture, status,
2049 NT_STATUS_INVALID_PARAMETER,
2050 "invalid FSCTL_SET_COMPRESSION");
2052 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2054 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2056 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2057 "initial compression state not NONE");
2059 smb2_util_close(tree, fh);
2060 talloc_free(tmp_ctx);
2064 static bool test_ioctl_compress_invalid_buf(struct torture_context *torture,
2065 struct smb2_tree *tree)
2067 struct smb2_handle fh;
2069 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2071 union smb_ioctl ioctl;
2073 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2074 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2075 FILE_ATTRIBUTE_NORMAL);
2076 torture_assert(torture, ok, "setup compression file");
2078 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
2080 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2082 smb2_util_close(tree, fh);
2083 torture_skip(torture, "FS compression not supported\n");
2087 ioctl.smb2.level = RAW_IOCTL_SMB2;
2088 ioctl.smb2.in.file.handle = fh;
2089 ioctl.smb2.in.function = FSCTL_GET_COMPRESSION;
2090 ioctl.smb2.in.max_response_size = 0; /* no room for rsp data */
2091 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2093 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2094 if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_USER_BUFFER)
2095 && !NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
2096 /* neither Server 2k12 nor 2k8r2 response status */
2097 torture_assert(torture, true,
2098 "invalid FSCTL_SET_COMPRESSION");
2101 smb2_util_close(tree, fh);
2102 talloc_free(tmp_ctx);
2106 static bool test_ioctl_compress_query_file_attr(struct torture_context *torture,
2107 struct smb2_tree *tree)
2109 struct smb2_handle fh;
2110 union smb_fileinfo io;
2112 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2115 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2116 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2117 FILE_ATTRIBUTE_NORMAL);
2118 torture_assert(torture, ok, "setup compression file");
2120 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
2122 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2124 smb2_util_close(tree, fh);
2125 torture_skip(torture, "FS compression not supported\n");
2129 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
2130 io.generic.in.file.handle = fh;
2131 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2132 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2134 torture_assert(torture,
2135 ((io.all_info2.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2136 "compression attr before set");
2138 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
2139 COMPRESSION_FORMAT_DEFAULT);
2140 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
2143 io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2144 io.generic.in.file.handle = fh;
2145 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2146 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2148 torture_assert(torture,
2149 (io.basic_info.out.attrib & FILE_ATTRIBUTE_COMPRESSED),
2150 "no compression attr after set");
2152 smb2_util_close(tree, fh);
2153 talloc_free(tmp_ctx);
2158 * Specify FILE_ATTRIBUTE_COMPRESSED on creation, Windows does not retain this
2161 static bool test_ioctl_compress_create_with_attr(struct torture_context *torture,
2162 struct smb2_tree *tree)
2164 struct smb2_handle fh2;
2165 union smb_fileinfo io;
2167 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2168 uint16_t compression_fmt;
2171 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2172 FNAME2, &fh2, 0, SEC_RIGHTS_FILE_ALL,
2173 (FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_COMPRESSED));
2174 torture_assert(torture, ok, "setup compression file");
2176 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh2,
2178 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2180 smb2_util_close(tree, fh2);
2181 torture_skip(torture, "FS compression not supported\n");
2184 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh2,
2186 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2188 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2189 "initial compression state not NONE");
2192 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
2193 io.generic.in.file.handle = fh2;
2194 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2195 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2197 torture_assert(torture,
2198 ((io.all_info2.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2199 "incorrect compression attr");
2201 smb2_util_close(tree, fh2);
2202 talloc_free(tmp_ctx);
2206 static bool test_ioctl_compress_inherit_disable(struct torture_context *torture,
2207 struct smb2_tree *tree)
2209 struct smb2_handle fh;
2210 struct smb2_handle dirh;
2211 char path_buf[PATH_MAX];
2213 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2215 uint16_t compression_fmt;
2217 struct smb2_create io;
2219 smb2_deltree(tree, DNAME);
2220 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2221 DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL,
2222 FILE_ATTRIBUTE_DIRECTORY);
2223 torture_assert(torture, ok, "setup compression directory");
2225 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &dirh,
2227 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2229 smb2_util_close(tree, dirh);
2230 smb2_deltree(tree, DNAME);
2231 torture_skip(torture, "FS compression not supported\n");
2234 /* set compression on parent dir, then check for inheritance */
2235 status = test_ioctl_compress_set(torture, tmp_ctx, tree, dirh,
2236 COMPRESSION_FORMAT_LZNT1);
2237 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
2239 status = test_ioctl_compress_get(torture, tmp_ctx, tree, dirh,
2241 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2243 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
2244 "invalid compression state after set");
2245 smb2_util_close(tree, dirh);
2247 snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME);
2248 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2249 path_buf, &fh, 0, SEC_RIGHTS_FILE_ALL,
2250 FILE_ATTRIBUTE_NORMAL);
2251 torture_assert(torture, ok, "setup compression file");
2253 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2255 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2257 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
2258 "compression attr not inherited by new file");
2259 smb2_util_close(tree, fh);
2261 snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME2);
2263 /* NO_COMPRESSION option should block inheritance */
2265 io.in.desired_access = SEC_RIGHTS_FILE_ALL;
2266 io.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
2267 io.in.create_disposition = NTCREATEX_DISP_CREATE;
2268 io.in.create_options = NTCREATEX_OPTIONS_NO_COMPRESSION;
2269 io.in.share_access =
2270 NTCREATEX_SHARE_ACCESS_DELETE|
2271 NTCREATEX_SHARE_ACCESS_READ|
2272 NTCREATEX_SHARE_ACCESS_WRITE;
2273 io.in.fname = path_buf;
2275 status = smb2_create(tree, tmp_ctx, &io);
2276 torture_assert_ntstatus_ok(torture, status, "file create");
2278 fh = io.out.file.handle;
2280 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2282 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2284 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2285 "compression attr inherited by NO_COMPRESSION file");
2286 smb2_util_close(tree, fh);
2289 snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, DNAME);
2291 io.in.desired_access = SEC_RIGHTS_FILE_ALL;
2292 io.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
2293 io.in.create_disposition = NTCREATEX_DISP_CREATE;
2294 io.in.create_options = (NTCREATEX_OPTIONS_NO_COMPRESSION
2295 | NTCREATEX_OPTIONS_DIRECTORY);
2296 io.in.share_access =
2297 NTCREATEX_SHARE_ACCESS_DELETE|
2298 NTCREATEX_SHARE_ACCESS_READ|
2299 NTCREATEX_SHARE_ACCESS_WRITE;
2300 io.in.fname = path_buf;
2302 status = smb2_create(tree, tmp_ctx, &io);
2303 torture_assert_ntstatus_ok(torture, status, "dir create");
2305 dirh = io.out.file.handle;
2307 status = test_ioctl_compress_get(torture, tmp_ctx, tree, dirh,
2309 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2311 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2312 "compression attr inherited by NO_COMPRESSION dir");
2313 smb2_util_close(tree, dirh);
2314 smb2_deltree(tree, DNAME);
2316 talloc_free(tmp_ctx);
2320 /* attempting to set compression via SetInfo should not stick */
2321 static bool test_ioctl_compress_set_file_attr(struct torture_context *torture,
2322 struct smb2_tree *tree)
2324 struct smb2_handle fh;
2325 struct smb2_handle dirh;
2326 union smb_fileinfo io;
2327 union smb_setfileinfo set_io;
2328 uint16_t compression_fmt;
2330 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2333 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2334 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2335 FILE_ATTRIBUTE_NORMAL);
2336 torture_assert(torture, ok, "setup compression file");
2338 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
2340 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2342 smb2_util_close(tree, fh);
2343 torture_skip(torture, "FS compression not supported\n");
2347 io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2348 io.generic.in.file.handle = fh;
2349 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2350 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2352 torture_assert(torture,
2353 ((io.basic_info.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2354 "compression attr before set");
2356 ZERO_STRUCT(set_io);
2357 set_io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2358 set_io.basic_info.in.file.handle = fh;
2359 set_io.basic_info.in.create_time = io.basic_info.out.create_time;
2360 set_io.basic_info.in.access_time = io.basic_info.out.access_time;
2361 set_io.basic_info.in.write_time = io.basic_info.out.write_time;
2362 set_io.basic_info.in.change_time = io.basic_info.out.change_time;
2363 set_io.basic_info.in.attrib = (io.basic_info.out.attrib
2364 | FILE_ATTRIBUTE_COMPRESSED);
2365 status = smb2_setinfo_file(tree, &set_io);
2366 torture_assert_ntstatus_ok(torture, status, "SMB2_SETINFO_FILE");
2369 io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2370 io.generic.in.file.handle = fh;
2371 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2372 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2374 torture_assert(torture,
2375 ((io.basic_info.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2376 "compression attr after set");
2378 smb2_util_close(tree, fh);
2379 smb2_deltree(tree, DNAME);
2380 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2381 DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL,
2382 FILE_ATTRIBUTE_DIRECTORY);
2383 torture_assert(torture, ok, "setup compression directory");
2386 io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2387 io.generic.in.file.handle = dirh;
2388 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2389 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2391 torture_assert(torture,
2392 ((io.basic_info.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2393 "compression attr before set");
2395 ZERO_STRUCT(set_io);
2396 set_io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2397 set_io.basic_info.in.file.handle = dirh;
2398 set_io.basic_info.in.create_time = io.basic_info.out.create_time;
2399 set_io.basic_info.in.access_time = io.basic_info.out.access_time;
2400 set_io.basic_info.in.write_time = io.basic_info.out.write_time;
2401 set_io.basic_info.in.change_time = io.basic_info.out.change_time;
2402 set_io.basic_info.in.attrib = (io.basic_info.out.attrib
2403 | FILE_ATTRIBUTE_COMPRESSED);
2404 status = smb2_setinfo_file(tree, &set_io);
2405 torture_assert_ntstatus_ok(torture, status, "SMB2_SETINFO_FILE");
2407 status = test_ioctl_compress_get(torture, tmp_ctx, tree, dirh,
2409 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2411 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2412 "dir compression set after SetInfo");
2414 smb2_util_close(tree, dirh);
2415 talloc_free(tmp_ctx);
2419 static bool test_ioctl_compress_perms(struct torture_context *torture,
2420 struct smb2_tree *tree)
2422 struct smb2_handle fh;
2423 uint16_t compression_fmt;
2424 union smb_fileinfo io;
2426 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2429 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2430 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2431 FILE_ATTRIBUTE_NORMAL);
2432 torture_assert(torture, ok, "setup compression file");
2434 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
2436 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2437 smb2_util_close(tree, fh);
2439 torture_skip(torture, "FS compression not supported\n");
2442 /* attempt get compression without READ_ATTR permission */
2443 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2445 (SEC_RIGHTS_FILE_READ & ~(SEC_FILE_READ_ATTRIBUTE
2446 | SEC_STD_READ_CONTROL
2447 | SEC_FILE_READ_EA)),
2448 FILE_ATTRIBUTE_NORMAL);
2449 torture_assert(torture, ok, "setup compression file");
2451 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2453 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2454 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2455 "compression set after create");
2456 smb2_util_close(tree, fh);
2458 /* set compression without WRITE_ATTR permission should succeed */
2459 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2461 (SEC_RIGHTS_FILE_WRITE & ~(SEC_FILE_WRITE_ATTRIBUTE
2463 | SEC_FILE_WRITE_EA)),
2464 FILE_ATTRIBUTE_NORMAL);
2465 torture_assert(torture, ok, "setup compression file");
2467 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
2468 COMPRESSION_FORMAT_DEFAULT);
2469 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
2470 smb2_util_close(tree, fh);
2472 ok = test_setup_open(torture, tree, tmp_ctx,
2473 FNAME, &fh, SEC_RIGHTS_FILE_ALL,
2474 FILE_ATTRIBUTE_NORMAL);
2475 torture_assert(torture, ok, "setup compression file");
2477 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
2478 io.generic.in.file.handle = fh;
2479 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2480 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2482 torture_assert(torture,
2483 (io.all_info2.out.attrib & FILE_ATTRIBUTE_COMPRESSED),
2484 "incorrect compression attr");
2485 smb2_util_close(tree, fh);
2487 /* attempt get compression without READ_DATA permission */
2488 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2490 (SEC_RIGHTS_FILE_READ & ~SEC_FILE_READ_DATA),
2491 FILE_ATTRIBUTE_NORMAL);
2492 torture_assert(torture, ok, "setup compression file");
2494 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2496 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2497 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2498 "compression enabled after set");
2499 smb2_util_close(tree, fh);
2501 /* attempt get compression with only SYNCHRONIZE permission */
2502 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2504 SEC_STD_SYNCHRONIZE,
2505 FILE_ATTRIBUTE_NORMAL);
2506 torture_assert(torture, ok, "setup compression file");
2508 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2510 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2511 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2512 "compression not enabled after set");
2513 smb2_util_close(tree, fh);
2515 /* attempt to set compression without WRITE_DATA permission */
2516 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2518 (SEC_RIGHTS_FILE_WRITE & (~SEC_FILE_WRITE_DATA)),
2519 FILE_ATTRIBUTE_NORMAL);
2520 torture_assert(torture, ok, "setup compression file");
2522 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
2523 COMPRESSION_FORMAT_DEFAULT);
2524 torture_assert_ntstatus_equal(torture, status,
2525 NT_STATUS_ACCESS_DENIED,
2526 "FSCTL_SET_COMPRESSION permission");
2527 smb2_util_close(tree, fh);
2529 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2531 (SEC_RIGHTS_FILE_WRITE & (~SEC_FILE_WRITE_DATA)),
2532 FILE_ATTRIBUTE_NORMAL);
2533 torture_assert(torture, ok, "setup compression file");
2535 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
2536 COMPRESSION_FORMAT_NONE);
2537 torture_assert_ntstatus_equal(torture, status,
2538 NT_STATUS_ACCESS_DENIED,
2539 "FSCTL_SET_COMPRESSION permission");
2540 smb2_util_close(tree, fh);
2542 talloc_free(tmp_ctx);
2547 basic testing of the SMB2 FSCTL_QUERY_NETWORK_INTERFACE_INFO ioctl
2549 static bool test_ioctl_network_interface_info(struct torture_context *torture,
2550 struct smb2_tree *tree)
2552 union smb_ioctl ioctl;
2553 struct smb2_handle fh;
2555 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2556 struct fsctl_net_iface_info net_iface;
2557 enum ndr_err_code ndr_ret;
2560 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
2561 if (!(caps & SMB2_CAP_MULTI_CHANNEL)) {
2562 torture_skip(torture, "server doesn't support SMB2_CAP_MULTI_CHANNEL\n");
2566 ioctl.smb2.level = RAW_IOCTL_SMB2;
2567 fh.data[0] = UINT64_MAX;
2568 fh.data[1] = UINT64_MAX;
2569 ioctl.smb2.in.file.handle = fh;
2570 ioctl.smb2.in.function = FSCTL_QUERY_NETWORK_INTERFACE_INFO;
2571 ioctl.smb2.in.max_response_size = 0x10000; /* Windows client sets this to 64KiB */
2572 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2574 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2575 torture_assert_ntstatus_ok(torture, status, "FSCTL_QUERY_NETWORK_INTERFACE_INFO");
2577 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, &net_iface,
2578 (ndr_pull_flags_fn_t)ndr_pull_fsctl_net_iface_info);
2579 torture_assert_ndr_success(torture, ndr_ret,
2580 "ndr_pull_fsctl_net_iface_info");
2582 ndr_print_debug((ndr_print_fn_t)ndr_print_fsctl_net_iface_info,
2583 "Network Interface Info", &net_iface);
2585 talloc_free(tmp_ctx);
2590 * Check whether all @fs_support_flags are set in the server's
2591 * RAW_QFS_ATTRIBUTE_INFORMATION FileSystemAttributes response.
2593 static NTSTATUS test_ioctl_fs_supported(struct torture_context *torture,
2594 struct smb2_tree *tree,
2595 TALLOC_CTX *mem_ctx,
2596 struct smb2_handle *fh,
2597 uint64_t fs_support_flags,
2601 union smb_fsinfo info;
2604 info.generic.level = RAW_QFS_ATTRIBUTE_INFORMATION;
2605 info.generic.handle = *fh;
2606 status = smb2_getinfo_fs(tree, tree, &info);
2607 if (!NT_STATUS_IS_OK(status)) {
2611 if ((info.attribute_info.out.fs_attr & fs_support_flags)
2612 == fs_support_flags) {
2617 return NT_STATUS_OK;
2620 static NTSTATUS test_ioctl_sparse_req(struct torture_context *torture,
2621 TALLOC_CTX *mem_ctx,
2622 struct smb2_tree *tree,
2623 struct smb2_handle fh,
2626 union smb_ioctl ioctl;
2631 ioctl.smb2.level = RAW_IOCTL_SMB2;
2632 ioctl.smb2.in.file.handle = fh;
2633 ioctl.smb2.in.function = FSCTL_SET_SPARSE;
2634 ioctl.smb2.in.max_response_size = 0;
2635 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2636 set_sparse = (set ? 0xFF : 0x0);
2637 ioctl.smb2.in.out.data = &set_sparse;
2638 ioctl.smb2.in.out.length = sizeof(set_sparse);
2640 status = smb2_ioctl(tree, mem_ctx, &ioctl.smb2);
2644 static NTSTATUS test_sparse_get(struct torture_context *torture,
2645 TALLOC_CTX *mem_ctx,
2646 struct smb2_tree *tree,
2647 struct smb2_handle fh,
2650 union smb_fileinfo io;
2654 io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2655 io.generic.in.file.handle = fh;
2656 status = smb2_getinfo_file(tree, mem_ctx, &io);
2657 if (!NT_STATUS_IS_OK(status)) {
2660 *_is_sparse = !!(io.basic_info.out.attrib & FILE_ATTRIBUTE_SPARSE);
2665 static bool test_ioctl_sparse_file_flag(struct torture_context *torture,
2666 struct smb2_tree *tree)
2668 struct smb2_handle fh;
2669 union smb_fileinfo io;
2671 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2675 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2676 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2677 FILE_ATTRIBUTE_NORMAL);
2678 torture_assert(torture, ok, "setup file");
2680 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
2681 FILE_SUPPORTS_SPARSE_FILES, &ok);
2682 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2684 smb2_util_close(tree, fh);
2685 torture_skip(torture, "Sparse files not supported\n");
2689 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
2690 io.generic.in.file.handle = fh;
2691 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2692 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2694 torture_assert(torture,
2695 ((io.all_info2.out.attrib & FILE_ATTRIBUTE_SPARSE) == 0),
2696 "sparse attr before set");
2698 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
2699 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2701 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2702 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2703 torture_assert(torture, is_sparse, "no sparse attr after set");
2705 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false);
2706 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2708 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2709 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2710 torture_assert(torture, !is_sparse, "sparse attr after unset");
2712 smb2_util_close(tree, fh);
2713 talloc_free(tmp_ctx);
2717 static bool test_ioctl_sparse_file_attr(struct torture_context *torture,
2718 struct smb2_tree *tree)
2720 struct smb2_handle fh;
2722 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2726 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2727 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2728 (FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_SPARSE));
2729 torture_assert(torture, ok, "setup file");
2731 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
2732 FILE_SUPPORTS_SPARSE_FILES, &ok);
2733 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2735 smb2_util_close(tree, fh);
2736 torture_skip(torture, "Sparse files not supported\n");
2739 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2740 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2741 torture_assert(torture, !is_sparse, "sparse attr on open");
2743 smb2_util_close(tree, fh);
2744 talloc_free(tmp_ctx);
2748 static bool test_ioctl_sparse_dir_flag(struct torture_context *torture,
2749 struct smb2_tree *tree)
2751 struct smb2_handle dirh;
2753 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2756 smb2_deltree(tree, DNAME);
2757 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2758 DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL,
2759 FILE_ATTRIBUTE_DIRECTORY);
2760 torture_assert(torture, ok, "setup sparse directory");
2762 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &dirh,
2763 FILE_SUPPORTS_SPARSE_FILES, &ok);
2764 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2766 smb2_util_close(tree, dirh);
2767 smb2_deltree(tree, DNAME);
2768 torture_skip(torture, "Sparse files not supported\n");
2771 /* set sparse dir should fail, check for 2k12 & 2k8 response */
2772 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, dirh, true);
2773 torture_assert_ntstatus_equal(torture, status,
2774 NT_STATUS_INVALID_PARAMETER,
2775 "dir FSCTL_SET_SPARSE status");
2777 smb2_util_close(tree, dirh);
2778 smb2_deltree(tree, DNAME);
2779 talloc_free(tmp_ctx);
2784 * FSCTL_SET_SPARSE can be sent with (already tested) or without a SetSparse
2785 * buffer to indicate whether the flag should be set or cleared. When sent
2786 * without a buffer, it must be handled as if SetSparse=TRUE.
2788 static bool test_ioctl_sparse_set_nobuf(struct torture_context *torture,
2789 struct smb2_tree *tree)
2791 struct smb2_handle fh;
2792 union smb_ioctl ioctl;
2794 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2798 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2799 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2800 FILE_ATTRIBUTE_NORMAL);
2801 torture_assert(torture, ok, "setup file");
2803 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
2804 FILE_SUPPORTS_SPARSE_FILES, &ok);
2805 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2807 smb2_util_close(tree, fh);
2808 torture_skip(torture, "Sparse files not supported\n");
2811 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2812 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2813 torture_assert(torture, !is_sparse, "sparse attr before set");
2816 ioctl.smb2.level = RAW_IOCTL_SMB2;
2817 ioctl.smb2.in.file.handle = fh;
2818 ioctl.smb2.in.function = FSCTL_SET_SPARSE;
2819 ioctl.smb2.in.max_response_size = 0;
2820 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2821 /* ioctl.smb2.in.out is zeroed, no SetSparse buffer */
2823 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2824 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2826 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2827 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2828 torture_assert(torture, is_sparse, "no sparse attr after set");
2830 /* second non-SetSparse request shouldn't toggle sparse */
2832 ioctl.smb2.level = RAW_IOCTL_SMB2;
2833 ioctl.smb2.in.file.handle = fh;
2834 ioctl.smb2.in.function = FSCTL_SET_SPARSE;
2835 ioctl.smb2.in.max_response_size = 0;
2836 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2838 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2839 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2841 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2842 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2843 torture_assert(torture, is_sparse, "no sparse attr after 2nd set");
2845 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false);
2846 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2848 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2849 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2850 torture_assert(torture, !is_sparse, "sparse attr after unset");
2852 smb2_util_close(tree, fh);
2853 talloc_free(tmp_ctx);
2857 static bool test_ioctl_sparse_set_oversize(struct torture_context *torture,
2858 struct smb2_tree *tree)
2860 struct smb2_handle fh;
2861 union smb_ioctl ioctl;
2863 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2868 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2869 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2870 FILE_ATTRIBUTE_NORMAL);
2871 torture_assert(torture, ok, "setup file");
2873 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
2874 FILE_SUPPORTS_SPARSE_FILES, &ok);
2875 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2877 smb2_util_close(tree, fh);
2878 torture_skip(torture, "Sparse files not supported\n");
2881 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2882 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2883 torture_assert(torture, !is_sparse, "sparse attr before set");
2886 ioctl.smb2.level = RAW_IOCTL_SMB2;
2887 ioctl.smb2.in.file.handle = fh;
2888 ioctl.smb2.in.function = FSCTL_SET_SPARSE;
2889 ioctl.smb2.in.max_response_size = 0;
2890 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2893 * Attach a request buffer larger than FILE_SET_SPARSE_BUFFER
2894 * Windows still successfully processes the request.
2897 buf[0] = 0xFF; /* attempt to set sparse */
2898 ioctl.smb2.in.out.data = buf;
2899 ioctl.smb2.in.out.length = ARRAY_SIZE(buf);
2901 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2902 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2904 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2905 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2906 torture_assert(torture, is_sparse, "no sparse attr after set");
2909 ioctl.smb2.level = RAW_IOCTL_SMB2;
2910 ioctl.smb2.in.file.handle = fh;
2911 ioctl.smb2.in.function = FSCTL_SET_SPARSE;
2912 ioctl.smb2.in.max_response_size = 0;
2913 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2915 ZERO_ARRAY(buf); /* clear sparse */
2916 ioctl.smb2.in.out.data = buf;
2917 ioctl.smb2.in.out.length = ARRAY_SIZE(buf);
2919 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2920 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2922 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2923 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2924 torture_assert(torture, !is_sparse, "sparse attr after clear");
2926 smb2_util_close(tree, fh);
2927 talloc_free(tmp_ctx);
2931 static NTSTATUS test_ioctl_qar_req(struct torture_context *torture,
2932 TALLOC_CTX *mem_ctx,
2933 struct smb2_tree *tree,
2934 struct smb2_handle fh,
2937 struct file_alloced_range_buf **_rsp,
2938 uint64_t *_rsp_count)
2940 union smb_ioctl ioctl;
2942 enum ndr_err_code ndr_ret;
2943 struct file_alloced_range_buf far_buf;
2944 struct file_alloced_range_buf *far_rsp = NULL;
2945 uint64_t far_count = 0;
2947 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
2948 if (tmp_ctx == NULL) {
2949 return NT_STATUS_NO_MEMORY;
2953 ioctl.smb2.level = RAW_IOCTL_SMB2;
2954 ioctl.smb2.in.file.handle = fh;
2955 ioctl.smb2.in.function = FSCTL_QUERY_ALLOCATED_RANGES;
2956 ioctl.smb2.in.max_response_size = 1024;
2957 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2959 far_buf.file_off = req_off;
2960 far_buf.len = req_len;
2962 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
2964 (ndr_push_flags_fn_t)ndr_push_file_alloced_range_buf);
2965 if (ndr_ret != NDR_ERR_SUCCESS) {
2966 status = NT_STATUS_UNSUCCESSFUL;
2970 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2971 if (!NT_STATUS_IS_OK(status)) {
2975 if (ioctl.smb2.out.out.length == 0) {
2979 if ((ioctl.smb2.out.out.length % sizeof(far_buf)) != 0) {
2980 torture_comment(torture, "invalid qry_alloced rsp len: %zd:",
2981 ioctl.smb2.out.out.length);
2982 status = NT_STATUS_INVALID_VIEW_SIZE;
2986 far_count = (ioctl.smb2.out.out.length / sizeof(far_buf));
2987 far_rsp = talloc_array(mem_ctx, struct file_alloced_range_buf,
2989 if (far_rsp == NULL) {
2990 status = NT_STATUS_NO_MEMORY;
2994 for (i = 0; i < far_count; i++) {
2995 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
2997 (ndr_pull_flags_fn_t)ndr_pull_file_alloced_range_buf);
2998 if (ndr_ret != NDR_ERR_SUCCESS) {
2999 status = NT_STATUS_UNSUCCESSFUL;
3002 /* move to next buffer */
3003 ioctl.smb2.out.out.data += sizeof(far_buf);
3004 ioctl.smb2.out.out.length -= sizeof(far_buf);
3009 *_rsp_count = far_count;
3010 status = NT_STATUS_OK;
3012 talloc_free(tmp_ctx);
3016 static bool test_ioctl_sparse_qar(struct torture_context *torture,
3017 struct smb2_tree *tree)
3019 struct smb2_handle fh;
3021 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3024 struct file_alloced_range_buf *far_rsp = NULL;
3025 uint64_t far_count = 0;
3027 /* zero length file, shouldn't have any ranges */
3028 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3029 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3030 FILE_ATTRIBUTE_NORMAL);
3031 torture_assert(torture, ok, "setup file");
3033 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3034 FILE_SUPPORTS_SPARSE_FILES, &ok);
3035 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3037 smb2_util_close(tree, fh);
3038 torture_skip(torture, "Sparse files not supported\n");
3041 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3042 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3043 torture_assert(torture, !is_sparse, "sparse attr before set");
3045 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3050 torture_assert_ntstatus_ok(torture, status,
3051 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3052 torture_assert_u64_equal(torture, far_count, 0,
3053 "unexpected response len");
3055 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3060 torture_assert_ntstatus_ok(torture, status,
3061 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3062 torture_assert_u64_equal(torture, far_count, 0,
3063 "unexpected response len");
3065 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
3066 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3068 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3069 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3070 torture_assert(torture, is_sparse, "no sparse attr after set");
3072 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3077 torture_assert_ntstatus_ok(torture, status,
3078 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3079 torture_assert_u64_equal(torture, far_count, 0,
3080 "unexpected response len");
3082 /* write into the (now) sparse file at 4k offset */
3083 ok = write_pattern(torture, tree, tmp_ctx, fh,
3086 4096); /* pattern offset */
3087 torture_assert(torture, ok, "write pattern");
3090 * Query range before write off. Whether it's allocated or not is FS
3091 * dependent. NTFS deallocates chunks in 64K increments, but others
3092 * (e.g. XFS, Btrfs, etc.) may deallocate 4K chunks.
3094 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3099 torture_assert_ntstatus_ok(torture, status,
3100 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3101 if (far_count == 0) {
3102 torture_comment(torture, "FS deallocated 4K chunk\n");
3104 /* expect fully allocated */
3105 torture_assert_u64_equal(torture, far_count, 1,
3106 "unexpected response len");
3107 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0, "far offset");
3108 torture_assert_u64_equal(torture, far_rsp[0].len, 4096, "far len");
3112 * Query range before and past write, it should be allocated up to the
3115 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3120 torture_assert_ntstatus_ok(torture, status,
3121 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3122 torture_assert_u64_equal(torture, far_count, 1,
3123 "unexpected response len");
3125 if (far_rsp[0].file_off == 4096) {
3126 /* 4K chunk unallocated */
3127 torture_assert_u64_equal(torture, far_rsp[0].file_off, 4096, "far offset");
3128 torture_assert_u64_equal(torture, far_rsp[0].len, 1024, "far len");
3130 /* expect fully allocated */
3131 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0, "far offset");
3132 torture_assert_u64_equal(torture, far_rsp[0].len, 5120, "far len");
3135 smb2_util_close(tree, fh);
3136 talloc_free(tmp_ctx);
3140 static bool test_ioctl_sparse_qar_malformed(struct torture_context *torture,
3141 struct smb2_tree *tree)
3143 struct smb2_handle fh;
3144 union smb_ioctl ioctl;
3145 struct file_alloced_range_buf far_buf;
3147 enum ndr_err_code ndr_ret;
3148 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3152 /* zero length file, shouldn't have any ranges */
3153 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3154 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3155 FILE_ATTRIBUTE_NORMAL);
3156 torture_assert(torture, ok, "setup file");
3158 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3159 FILE_SUPPORTS_SPARSE_FILES, &ok);
3160 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3162 smb2_util_close(tree, fh);
3163 torture_skip(torture, "Sparse files not supported\n");
3166 /* no allocated ranges, no space for range response, should pass */
3168 ioctl.smb2.level = RAW_IOCTL_SMB2;
3169 ioctl.smb2.in.file.handle = fh;
3170 ioctl.smb2.in.function = FSCTL_QUERY_ALLOCATED_RANGES;
3171 ioctl.smb2.in.max_response_size = 0;
3172 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3174 far_buf.file_off = 0;
3176 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
3178 (ndr_push_flags_fn_t)ndr_push_file_alloced_range_buf);
3179 torture_assert_ndr_success(torture, ndr_ret, "push far ndr buf");
3181 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3182 torture_assert_ntstatus_ok(torture, status, "FSCTL_QUERY_ALLOCATED_RANGES");
3184 /* write into the file at 4k offset */
3185 ok = write_pattern(torture, tree, tmp_ctx, fh,
3188 0); /* pattern offset */
3189 torture_assert(torture, ok, "write pattern");
3191 /* allocated range, no space for range response, should fail */
3192 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3193 torture_assert_ntstatus_equal(torture, status,
3194 NT_STATUS_BUFFER_TOO_SMALL, "qar no space");
3196 /* oversize (2x) file_alloced_range_buf in request, should pass */
3197 ioctl.smb2.in.max_response_size = 1024;
3198 old_len = ioctl.smb2.in.out.length;
3199 ok = data_blob_realloc(tmp_ctx, &ioctl.smb2.in.out,
3200 (ioctl.smb2.in.out.length * 2));
3201 torture_assert(torture, ok, "2x data buffer");
3202 memcpy(ioctl.smb2.in.out.data + old_len, ioctl.smb2.in.out.data,
3204 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3205 torture_assert_ntstatus_ok(torture, status, "qar too big");
3207 /* no file_alloced_range_buf in request, should fail */
3208 data_blob_free(&ioctl.smb2.in.out);
3209 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3210 torture_assert_ntstatus_equal(torture, status,
3211 NT_STATUS_INVALID_PARAMETER, "qar empty");
3217 * 2.3.57 FSCTL_SET_ZERO_DATA Request
3219 * How an implementation zeros data within a file is implementation-dependent.
3220 * A file system MAY choose to deallocate regions of disk space that have been
3223 * ... NTFS might deallocate disk space in the file if the file is stored on an
3224 * NTFS volume, and the file is sparse or compressed. It will free any allocated
3225 * space in chunks of 64 kilobytes that begin at an offset that is a multiple of
3226 * 64 kilobytes. Other bytes in the file (prior to the first freed 64-kilobyte
3227 * chunk and after the last freed 64-kilobyte chunk) will be zeroed but not
3230 static NTSTATUS test_ioctl_zdata_req(struct torture_context *torture,
3231 TALLOC_CTX *mem_ctx,
3232 struct smb2_tree *tree,
3233 struct smb2_handle fh,
3235 int64_t beyond_final_zero)
3237 union smb_ioctl ioctl;
3239 enum ndr_err_code ndr_ret;
3240 struct file_zero_data_info zdata_info;
3241 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
3242 if (tmp_ctx == NULL) {
3243 return NT_STATUS_NO_MEMORY;
3247 ioctl.smb2.level = RAW_IOCTL_SMB2;
3248 ioctl.smb2.in.file.handle = fh;
3249 ioctl.smb2.in.function = FSCTL_SET_ZERO_DATA;
3250 ioctl.smb2.in.max_response_size = 0;
3251 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3253 zdata_info.file_off = off;
3254 zdata_info.beyond_final_zero = beyond_final_zero;
3256 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
3258 (ndr_push_flags_fn_t)ndr_push_file_zero_data_info);
3259 if (ndr_ret != NDR_ERR_SUCCESS) {
3260 status = NT_STATUS_UNSUCCESSFUL;
3264 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3265 if (!NT_STATUS_IS_OK(status)) {
3269 status = NT_STATUS_OK;
3271 talloc_free(tmp_ctx);
3275 static bool test_ioctl_sparse_punch(struct torture_context *torture,
3276 struct smb2_tree *tree)
3278 struct smb2_handle fh;
3280 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3283 struct file_alloced_range_buf *far_rsp = NULL;
3284 uint64_t far_count = 0;
3286 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3287 FNAME, &fh, 4096, SEC_RIGHTS_FILE_ALL,
3288 FILE_ATTRIBUTE_NORMAL);
3289 torture_assert(torture, ok, "setup file");
3291 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3292 FILE_SUPPORTS_SPARSE_FILES, &ok);
3293 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3295 smb2_util_close(tree, fh);
3296 torture_skip(torture, "Sparse files not supported\n");
3299 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3300 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3301 torture_assert(torture, !is_sparse, "sparse attr before set");
3303 /* zero (hole-punch) the data, without sparse flag */
3304 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3306 4096); /* beyond_final_zero */
3307 torture_assert_ntstatus_ok(torture, status, "zero_data");
3309 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3314 torture_assert_ntstatus_ok(torture, status,
3315 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3316 torture_assert_u64_equal(torture, far_count, 1,
3317 "unexpected response len");
3319 /* expect fully allocated */
3320 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3321 "unexpected far off");
3322 torture_assert_u64_equal(torture, far_rsp[0].len, 4096,
3323 "unexpected far len");
3324 /* check that the data is now zeroed */
3325 ok = check_zero(torture, tree, tmp_ctx, fh, 0, 4096);
3326 torture_assert(torture, ok, "non-sparse zeroed range");
3329 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
3330 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3332 /* still fully allocated on NTFS, see note below for Samba */
3333 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3338 torture_assert_ntstatus_ok(torture, status,
3339 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3341 * FS specific: Samba uses PUNCH_HOLE to zero the range, and
3342 * subsequently uses fallocate() to allocate the punched range if the
3343 * file is marked non-sparse and "strict allocate" is enabled. In both
3344 * cases, the zeroed range will not be detected by SEEK_DATA, so the
3345 * range won't be present in QAR responses until the file is marked
3348 if (far_count == 0) {
3349 torture_comment(torture, "non-sparse zeroed range disappeared "
3350 "after marking sparse\n");
3352 /* NTFS: range remains fully allocated */
3353 torture_assert_u64_equal(torture, far_count, 1,
3354 "unexpected response len");
3355 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3356 "unexpected far off");
3357 torture_assert_u64_equal(torture, far_rsp[0].len, 4096,
3358 "unexpected far len");
3361 /* zero (hole-punch) the data, _with_ sparse flag */
3362 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3364 4096); /* beyond_final_zero */
3365 torture_assert_ntstatus_ok(torture, status, "zero_data");
3367 /* the range should no longer be alloced */
3368 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3373 torture_assert_ntstatus_ok(torture, status,
3374 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3375 torture_assert_u64_equal(torture, far_count, 0,
3376 "unexpected response len");
3378 ok = check_zero(torture, tree, tmp_ctx, fh, 0, 4096);
3379 torture_assert(torture, ok, "sparse zeroed range");
3381 /* remove sparse flag, this should "unsparse" the zeroed range */
3382 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false);
3383 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3385 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3390 torture_assert_ntstatus_ok(torture, status,
3391 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3392 torture_assert_u64_equal(torture, far_count, 1,
3393 "unexpected response len");
3394 /* expect fully allocated */
3395 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3396 "unexpected far off");
3397 torture_assert_u64_equal(torture, far_rsp[0].len, 4096,
3398 "unexpected far len");
3400 ok = check_zero(torture, tree, tmp_ctx, fh, 0, 4096);
3401 torture_assert(torture, ok, "sparse zeroed range");
3403 smb2_util_close(tree, fh);
3404 talloc_free(tmp_ctx);
3409 * Find the point at which a zeroed range in a sparse file is deallocated by the
3410 * underlying filesystem. NTFS on Windows Server 2012 deallocates chunks in 64k
3411 * increments. Also check whether zeroed neighbours are merged for deallocation.
3413 static bool test_ioctl_sparse_hole_dealloc(struct torture_context *torture,
3414 struct smb2_tree *tree)
3416 struct smb2_handle fh;
3418 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3422 uint64_t dealloc_chunk_len = 0;
3423 struct file_alloced_range_buf *far_rsp = NULL;
3424 uint64_t far_count = 0;
3426 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3427 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3428 FILE_ATTRIBUTE_NORMAL);
3429 torture_assert(torture, ok, "setup file 1");
3431 /* check for FS sparse file */
3432 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3433 FILE_SUPPORTS_SPARSE_FILES, &ok);
3434 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3436 smb2_util_close(tree, fh);
3437 torture_skip(torture, "Sparse files not supported\n");
3441 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
3442 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3444 file_size = 1024 * 1024;
3446 ok = write_pattern(torture, tree, tmp_ctx, fh,
3448 file_size, /* len */
3449 0); /* pattern offset */
3450 torture_assert(torture, ok, "write pattern");
3452 /* check allocated ranges, should be fully allocated */
3453 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3455 file_size, /* len */
3458 torture_assert_ntstatus_ok(torture, status,
3459 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3460 torture_assert_u64_equal(torture, far_count, 1,
3461 "unexpected response len");
3462 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3463 "unexpected far off");
3464 torture_assert_u64_equal(torture, far_rsp[0].len, file_size,
3465 "unexpected far len");
3467 /* punch holes in sizes of 1k increments */
3468 for (hlen = 0; hlen <= file_size; hlen += 4096) {
3470 /* punch a hole from zero to the current increment */
3471 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3473 hlen); /* beyond_final_zero */
3474 torture_assert_ntstatus_ok(torture, status, "zero_data");
3476 /* ensure hole is zeroed, and pattern is consistent */
3477 ok = check_zero(torture, tree, tmp_ctx, fh, 0, hlen);
3478 torture_assert(torture, ok, "sparse zeroed range");
3480 ok = check_pattern(torture, tree, tmp_ctx, fh, hlen,
3481 file_size - hlen, hlen);
3482 torture_assert(torture, ok, "allocated pattern range");
3484 /* Check allocated ranges, hole might have been deallocated */
3485 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3487 file_size, /* len */
3490 torture_assert_ntstatus_ok(torture, status,
3491 "FSCTL_QUERY_ALLOCATED_RANGES");
3492 if ((hlen == file_size) && (far_count == 0)) {
3493 /* hole covered entire file, deallocation occurred */
3494 dealloc_chunk_len = file_size;
3498 torture_assert_u64_equal(torture, far_count, 1,
3499 "unexpected response len");
3500 if (far_rsp[0].file_off != 0) {
3502 * We now know the hole punch length needed to trigger a
3503 * deallocation on this FS...
3505 dealloc_chunk_len = hlen;
3506 torture_comment(torture, "hole punch %ju@0 resulted in "
3507 "deallocation of %ju@0\n",
3509 (uintmax_t)far_rsp[0].file_off);
3510 torture_assert_u64_equal(torture,
3511 file_size - far_rsp[0].len,
3512 far_rsp[0].file_off,
3513 "invalid alloced range");
3518 if (dealloc_chunk_len == 0) {
3519 torture_comment(torture, "strange, this FS never deallocates"
3520 "zeroed ranges in sparse files\n");
3521 return true; /* FS specific, not a failure */
3525 * Check whether deallocation occurs when the (now known)
3526 * deallocation chunk size is punched via two ZERO_DATA requests.
3527 * I.e. Does the FS merge the two ranges and deallocate the chunk?
3528 * NTFS on Windows Server 2012 does not.
3530 ok = write_pattern(torture, tree, tmp_ctx, fh,
3532 file_size, /* len */
3533 0); /* pattern offset */
3534 torture_assert(torture, ok, "write pattern");
3536 /* divide dealloc chunk size by two, to use as punch length */
3537 hlen = dealloc_chunk_len >> 1;
3540 * /half of dealloc chunk size 1M\
3542 * /offset 0 | /dealloc chunk size |
3543 * |------------------ |-------------------|-------------------|
3544 * | zeroed, 1st punch | zeroed, 2nd punch | existing pattern |
3546 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3548 hlen); /* beyond final zero */
3549 torture_assert_ntstatus_ok(torture, status, "zero_data");
3551 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3553 dealloc_chunk_len); /* beyond final */
3554 torture_assert_ntstatus_ok(torture, status, "zero_data");
3556 /* ensure holes are zeroed, and pattern is consistent */
3557 ok = check_zero(torture, tree, tmp_ctx, fh, 0, dealloc_chunk_len);
3558 torture_assert(torture, ok, "sparse zeroed range");
3560 ok = check_pattern(torture, tree, tmp_ctx, fh, dealloc_chunk_len,
3561 file_size - dealloc_chunk_len, dealloc_chunk_len);
3562 torture_assert(torture, ok, "allocated pattern range");
3564 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3566 file_size, /* len */
3569 torture_assert_ntstatus_ok(torture, status,
3570 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3572 if ((far_count == 0) && (dealloc_chunk_len == file_size)) {
3573 torture_comment(torture, "holes merged for deallocation of "
3577 torture_assert_u64_equal(torture, far_count, 1,
3578 "unexpected response len");
3579 if (far_rsp[0].file_off == dealloc_chunk_len) {
3580 torture_comment(torture, "holes merged for deallocation of "
3581 "%ju chunk\n", (uintmax_t)dealloc_chunk_len);
3582 torture_assert_u64_equal(torture,
3583 file_size - far_rsp[0].len,
3584 far_rsp[0].file_off,
3585 "invalid alloced range");
3587 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3588 "unexpected deallocation");
3589 torture_comment(torture, "holes not merged for deallocation\n");
3592 smb2_util_close(tree, fh);
3595 * Check whether an unwritten range is allocated when a sparse file is
3596 * written to at an offset past the dealloc chunk size:
3598 * /dealloc chunk size
3600 * |------------------ |-------------------|
3601 * | unwritten | pattern |
3603 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3604 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3605 FILE_ATTRIBUTE_NORMAL);
3606 torture_assert(torture, ok, "setup file 1");
3609 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
3610 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3612 ok = write_pattern(torture, tree, tmp_ctx, fh,
3613 dealloc_chunk_len, /* off */
3615 dealloc_chunk_len); /* pattern offset */
3616 torture_assert(torture, ok, "write pattern");
3618 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3620 dealloc_chunk_len + 1024, /* len */
3623 torture_assert_ntstatus_ok(torture, status,
3624 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3625 torture_assert_u64_equal(torture, far_count, 1,
3626 "unexpected response len");
3627 if (far_rsp[0].file_off == 0) {
3628 torture_assert_u64_equal(torture, far_rsp[0].len,
3629 dealloc_chunk_len + 1024,
3630 "unexpected far len");
3631 torture_comment(torture, "unwritten range fully allocated\n");
3633 torture_assert_u64_equal(torture, far_rsp[0].file_off, dealloc_chunk_len,
3634 "unexpected deallocation");
3635 torture_assert_u64_equal(torture, far_rsp[0].len, 1024,
3636 "unexpected far len");
3637 torture_comment(torture, "unwritten range not allocated\n");
3640 ok = check_zero(torture, tree, tmp_ctx, fh, 0, dealloc_chunk_len);
3641 torture_assert(torture, ok, "sparse zeroed range");
3643 ok = check_pattern(torture, tree, tmp_ctx, fh, dealloc_chunk_len,
3644 1024, dealloc_chunk_len);
3645 torture_assert(torture, ok, "allocated pattern range");
3647 /* unsparse, should now be fully allocated */
3648 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false);
3649 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3651 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3653 dealloc_chunk_len + 1024, /* len */
3656 torture_assert_ntstatus_ok(torture, status,
3657 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3658 torture_assert_u64_equal(torture, far_count, 1,
3659 "unexpected response len");
3660 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3661 "unexpected deallocation");
3662 torture_assert_u64_equal(torture, far_rsp[0].len,
3663 dealloc_chunk_len + 1024,
3664 "unexpected far len");
3666 smb2_util_close(tree, fh);
3667 talloc_free(tmp_ctx);
3671 /* check whether a file with compression and sparse attrs can be deallocated */
3672 static bool test_ioctl_sparse_compressed(struct torture_context *torture,
3673 struct smb2_tree *tree)
3675 struct smb2_handle fh;
3677 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3679 uint64_t file_size = 1024 * 1024;
3680 struct file_alloced_range_buf *far_rsp = NULL;
3681 uint64_t far_count = 0;
3683 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3684 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3685 FILE_ATTRIBUTE_NORMAL);
3686 torture_assert(torture, ok, "setup file 1");
3688 /* check for FS sparse file and compression support */
3689 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3690 FILE_SUPPORTS_SPARSE_FILES, &ok);
3691 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3693 smb2_util_close(tree, fh);
3694 torture_skip(torture, "Sparse files not supported\n");
3697 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
3699 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3701 smb2_util_close(tree, fh);
3702 torture_skip(torture, "FS compression not supported\n");
3705 /* set compression and write some data */
3706 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
3707 COMPRESSION_FORMAT_DEFAULT);
3708 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
3710 ok = write_pattern(torture, tree, tmp_ctx, fh,
3712 file_size, /* len */
3713 0); /* pattern offset */
3714 torture_assert(torture, ok, "write pattern");
3716 /* set sparse - now sparse and compressed */
3717 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
3718 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3720 /* check allocated ranges, should be fully alloced */
3721 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3723 file_size, /* len */
3726 torture_assert_ntstatus_ok(torture, status,
3727 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3728 torture_assert_u64_equal(torture, far_count, 1,
3729 "unexpected response len");
3730 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3731 "unexpected far off");
3732 torture_assert_u64_equal(torture, far_rsp[0].len, file_size,
3733 "unexpected far len");
3735 /* zero (hole-punch) all data, with sparse and compressed attrs */
3736 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3738 file_size); /* beyond_final_zero */
3739 torture_assert_ntstatus_ok(torture, status, "zero_data");
3742 * Windows Server 2012 still deallocates a zeroed range when a sparse
3743 * file carries the compression attribute.
3745 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3747 file_size, /* len */
3750 torture_assert_ntstatus_ok(torture, status,
3751 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3752 if (far_count == 0) {
3753 torture_comment(torture, "sparse & compressed file "
3754 "deallocated after hole-punch\n");
3756 torture_assert_u64_equal(torture, far_count, 1,
3757 "unexpected response len");
3758 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3759 "unexpected far off");
3760 torture_assert_u64_equal(torture, far_rsp[0].len, file_size,
3761 "unexpected far len");
3762 torture_comment(torture, "sparse & compressed file fully "
3763 "allocated after hole-punch\n");
3766 smb2_util_close(tree, fh);
3767 talloc_free(tmp_ctx);
3772 * Create a sparse file, then attempt to copy unallocated and allocated ranges
3773 * into a target file using FSCTL_SRV_COPYCHUNK.
3775 static bool test_ioctl_sparse_copy_chunk(struct torture_context *torture,
3776 struct smb2_tree *tree)
3778 struct smb2_handle src_h;
3779 struct smb2_handle dest_h;
3781 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3783 uint64_t dealloc_chunk_len = 64 * 1024; /* Windows 2012 */
3784 struct file_alloced_range_buf *far_rsp = NULL;
3785 uint64_t far_count = 0;
3786 union smb_ioctl ioctl;
3787 struct srv_copychunk_copy cc_copy;
3788 struct srv_copychunk_rsp cc_rsp;
3789 enum ndr_err_code ndr_ret;
3791 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3792 FNAME, &src_h, 0, SEC_RIGHTS_FILE_ALL,
3793 FILE_ATTRIBUTE_NORMAL);
3794 torture_assert(torture, ok, "setup file");
3796 /* check for FS sparse file support */
3797 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &src_h,
3798 FILE_SUPPORTS_SPARSE_FILES, &ok);
3799 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3800 smb2_util_close(tree, src_h);
3802 torture_skip(torture, "Sparse files not supported\n");
3805 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
3807 &src_h, 0, /* src file */
3808 SEC_RIGHTS_FILE_ALL,
3809 &dest_h, 0, /* dest file */
3810 SEC_RIGHTS_FILE_ALL,
3813 torture_assert(torture, ok, "setup copy chunk error");
3816 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, src_h, true);
3817 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3819 /* start after dealloc_chunk_len, to create an unwritten sparse range */
3820 ok = write_pattern(torture, tree, tmp_ctx, src_h,
3821 dealloc_chunk_len, /* off */
3823 dealloc_chunk_len); /* pattern offset */
3824 torture_assert(torture, ok, "write pattern");
3826 /* Skip test if 64k chunk is allocated - FS specific */
3827 status = test_ioctl_qar_req(torture, tmp_ctx, tree, src_h,
3829 dealloc_chunk_len + 1024, /* len */
3832 torture_assert_ntstatus_ok(torture, status,
3833 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3834 torture_assert_u64_equal(torture, far_count, 1,
3835 "unexpected response len");
3836 if (far_rsp[0].file_off == 0) {
3837 torture_skip(torture, "unwritten range fully allocated\n");
3840 torture_assert_u64_equal(torture, far_rsp[0].file_off, dealloc_chunk_len,
3841 "unexpected allocation");
3842 torture_assert_u64_equal(torture, far_rsp[0].len, 1024,
3843 "unexpected far len");
3845 /* copy-chunk unallocated + written ranges into non-sparse dest */
3847 cc_copy.chunks[0].source_off = 0;
3848 cc_copy.chunks[0].target_off = 0;
3849 cc_copy.chunks[0].length = dealloc_chunk_len + 1024;
3851 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
3853 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
3854 torture_assert_ndr_success(torture, ndr_ret,
3855 "ndr_push_srv_copychunk_copy");
3857 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3858 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
3860 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
3862 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
3863 torture_assert_ndr_success(torture, ndr_ret,
3864 "ndr_pull_srv_copychunk_rsp");
3866 ok = check_copy_chunk_rsp(torture, &cc_rsp,
3867 1, /* chunks written */
3868 0, /* chunk bytes unsuccessfully written */
3869 dealloc_chunk_len + 1024); /* bytes written */
3870 torture_assert(torture, ok, "bad copy chunk response data");
3872 ok = check_zero(torture, tree, tmp_ctx, dest_h, 0, dealloc_chunk_len);
3873 torture_assert(torture, ok, "sparse zeroed range");
3875 ok = check_pattern(torture, tree, tmp_ctx, dest_h, dealloc_chunk_len,
3876 1024, dealloc_chunk_len);
3877 torture_assert(torture, ok, "copychunked range");
3879 /* copied range should be allocated in non-sparse dest */
3880 status = test_ioctl_qar_req(torture, tmp_ctx, tree, dest_h,
3882 dealloc_chunk_len + 1024, /* len */
3885 torture_assert_ntstatus_ok(torture, status,
3886 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3887 torture_assert_u64_equal(torture, far_count, 1,
3888 "unexpected response len");
3889 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3890 "unexpected allocation");
3891 torture_assert_u64_equal(torture, far_rsp[0].len,
3892 dealloc_chunk_len + 1024,
3893 "unexpected far len");
3895 /* set dest as sparse */
3896 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, dest_h, true);
3897 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3899 /* zero (hole-punch) all data */
3900 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, dest_h,
3902 dealloc_chunk_len + 1024);
3903 torture_assert_ntstatus_ok(torture, status, "zero_data");
3905 /* zeroed range might be deallocated */
3906 status = test_ioctl_qar_req(torture, tmp_ctx, tree, dest_h,
3908 dealloc_chunk_len + 1024, /* len */
3911 torture_assert_ntstatus_ok(torture, status,
3912 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3913 if (far_count == 0) {
3914 /* FS specific (e.g. NTFS) */
3915 torture_comment(torture, "FS deallocates file on full-range "
3918 /* FS specific (e.g. EXT4) */
3919 torture_comment(torture, "FS doesn't deallocate file on "
3920 "full-range punch\n");
3922 ok = check_zero(torture, tree, tmp_ctx, dest_h, 0,
3923 dealloc_chunk_len + 1024);
3924 torture_assert(torture, ok, "punched zeroed range");
3926 /* copy-chunk again, this time with sparse dest */
3927 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3928 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
3930 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
3932 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
3933 torture_assert_ndr_success(torture, ndr_ret,
3934 "ndr_pull_srv_copychunk_rsp");
3936 ok = check_copy_chunk_rsp(torture, &cc_rsp,
3937 1, /* chunks written */
3938 0, /* chunk bytes unsuccessfully written */
3939 dealloc_chunk_len + 1024); /* bytes written */
3940 torture_assert(torture, ok, "bad copy chunk response data");
3942 ok = check_zero(torture, tree, tmp_ctx, dest_h, 0, dealloc_chunk_len);
3943 torture_assert(torture, ok, "sparse zeroed range");
3945 ok = check_pattern(torture, tree, tmp_ctx, dest_h, dealloc_chunk_len,
3946 1024, dealloc_chunk_len);
3947 torture_assert(torture, ok, "copychunked range");
3949 /* copied range may be allocated in sparse dest */
3950 status = test_ioctl_qar_req(torture, tmp_ctx, tree, dest_h,
3952 dealloc_chunk_len + 1024, /* len */
3955 torture_assert_ntstatus_ok(torture, status,
3956 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3957 torture_assert_u64_equal(torture, far_count, 1,
3958 "unexpected response len");
3960 * FS specific: sparse region may be unallocated in dest if copy-chunk
3961 * is handled in a sparse preserving way - E.g. vfs_btrfs
3962 * with BTRFS_IOC_CLONE_RANGE.
3964 if (far_rsp[0].file_off == dealloc_chunk_len) {
3965 torture_comment(torture, "copy-chunk sparse range preserved\n");
3966 torture_assert_u64_equal(torture, far_rsp[0].len, 1024,
3967 "unexpected far len");
3969 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3970 "unexpected allocation");
3971 torture_assert_u64_equal(torture, far_rsp[0].len,
3972 dealloc_chunk_len + 1024,
3973 "unexpected far len");
3976 smb2_util_close(tree, src_h);
3977 smb2_util_close(tree, dest_h);
3978 talloc_free(tmp_ctx);
3982 static bool test_ioctl_sparse_punch_invalid(struct torture_context *torture,
3983 struct smb2_tree *tree)
3985 struct smb2_handle fh;
3987 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3992 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3993 FNAME, &fh, 4096, SEC_RIGHTS_FILE_ALL,
3994 FILE_ATTRIBUTE_NORMAL);
3995 torture_assert(torture, ok, "setup file");
3997 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3998 FILE_SUPPORTS_SPARSE_FILES, &ok);
3999 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4001 smb2_util_close(tree, fh);
4002 torture_skip(torture, "Sparse files not supported\n");
4005 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4006 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4007 torture_assert(torture, !is_sparse, "sparse attr before set");
4009 /* loop twice, without and with sparse attrib */
4010 for (i = 0; i <= 1; i++) {
4011 union smb_fileinfo io;
4012 struct file_alloced_range_buf *far_rsp = NULL;
4013 uint64_t far_count = 0;
4015 /* get size before & after. zero data should never change it */
4017 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
4018 io.generic.in.file.handle = fh;
4019 status = smb2_getinfo_file(tree, tmp_ctx, &io);
4020 torture_assert_ntstatus_ok(torture, status, "getinfo");
4021 torture_assert_int_equal(torture, (int)io.all_info2.out.size,
4022 4096, "size after IO");
4024 /* valid 8 byte zero data, but after EOF */
4025 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4027 4104); /* beyond_final_zero */
4028 torture_assert_ntstatus_ok(torture, status, "zero_data");
4030 /* valid 8 byte zero data, but after EOF */
4031 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4033 8200); /* beyond_final_zero */
4034 torture_assert_ntstatus_ok(torture, status, "zero_data");
4037 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
4038 io.generic.in.file.handle = fh;
4039 status = smb2_getinfo_file(tree, tmp_ctx, &io);
4040 torture_assert_ntstatus_ok(torture, status, "getinfo");
4041 torture_assert_int_equal(torture, (int)io.all_info2.out.size,
4042 4096, "size after IO");
4044 /* valid 0 byte zero data, without sparse flag */
4045 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4047 4095); /* beyond_final_zero */
4048 torture_assert_ntstatus_ok(torture, status, "zero_data");
4050 /* INVALID off is past beyond_final_zero */
4051 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4053 4095); /* beyond_final_zero */
4054 torture_assert_ntstatus_equal(torture, status,
4055 NT_STATUS_INVALID_PARAMETER,
4056 "invalid zero_data");
4058 /* zero length QAR - valid */
4059 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4062 &far_rsp, &far_count);
4063 torture_assert_ntstatus_ok(torture, status,
4064 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4065 torture_assert_u64_equal(torture, far_count, 0,
4066 "unexpected response len");
4068 /* QAR after EOF - valid */
4069 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4072 &far_rsp, &far_count);
4073 torture_assert_ntstatus_ok(torture, status,
4074 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4075 torture_assert_u64_equal(torture, far_count, 0,
4076 "unexpected response len");
4079 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh,
4081 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4084 smb2_util_close(tree, fh);
4085 talloc_free(tmp_ctx);
4089 static bool test_ioctl_sparse_perms(struct torture_context *torture,
4090 struct smb2_tree *tree)
4092 struct smb2_handle fh;
4094 TALLOC_CTX *tmp_ctx = talloc_new(tree);
4097 struct file_alloced_range_buf *far_rsp = NULL;
4098 uint64_t far_count = 0;
4100 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4101 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
4102 FILE_ATTRIBUTE_NORMAL);
4103 torture_assert(torture, ok, "setup file");
4105 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
4106 FILE_SUPPORTS_SPARSE_FILES, &ok);
4107 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4108 smb2_util_close(tree, fh);
4110 torture_skip(torture, "Sparse files not supported\n");
4113 /* set sparse without WRITE_ATTR permission should succeed */
4114 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4116 (SEC_RIGHTS_FILE_WRITE & ~(SEC_FILE_WRITE_ATTRIBUTE
4118 | SEC_FILE_WRITE_EA)),
4119 FILE_ATTRIBUTE_NORMAL);
4120 torture_assert(torture, ok, "setup file");
4122 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4123 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4124 smb2_util_close(tree, fh);
4126 ok = test_setup_open(torture, tree, tmp_ctx,
4127 FNAME, &fh, SEC_RIGHTS_FILE_ALL,
4128 FILE_ATTRIBUTE_NORMAL);
4129 torture_assert(torture, ok, "setup file");
4130 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4131 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4132 torture_assert(torture, is_sparse, "sparse after set");
4133 smb2_util_close(tree, fh);
4135 /* attempt get sparse without READ_DATA permission */
4136 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4138 (SEC_RIGHTS_FILE_READ & ~SEC_FILE_READ_DATA),
4139 FILE_ATTRIBUTE_NORMAL);
4140 torture_assert(torture, ok, "setup file");
4142 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4143 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4144 torture_assert(torture, !is_sparse, "sparse set");
4145 smb2_util_close(tree, fh);
4147 /* attempt to set sparse with only WRITE_ATTR permission */
4148 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4150 SEC_FILE_WRITE_ATTRIBUTE,
4151 FILE_ATTRIBUTE_NORMAL);
4152 torture_assert(torture, ok, "setup file");
4154 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4155 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4156 smb2_util_close(tree, fh);
4158 /* attempt to set sparse with only WRITE_DATA permission */
4159 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4161 SEC_FILE_WRITE_DATA,
4162 FILE_ATTRIBUTE_NORMAL);
4163 torture_assert(torture, ok, "setup file");
4165 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4166 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4167 smb2_util_close(tree, fh);
4169 ok = test_setup_open(torture, tree, tmp_ctx,
4170 FNAME, &fh, SEC_RIGHTS_FILE_ALL,
4171 FILE_ATTRIBUTE_NORMAL);
4172 torture_assert(torture, ok, "setup file");
4173 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4174 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4175 torture_assert(torture, is_sparse, "sparse after set");
4176 smb2_util_close(tree, fh);
4178 /* attempt to set sparse with only APPEND_DATA permission */
4179 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4181 SEC_FILE_APPEND_DATA,
4182 FILE_ATTRIBUTE_NORMAL);
4183 torture_assert(torture, ok, "setup file");
4185 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4186 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4187 smb2_util_close(tree, fh);
4189 ok = test_setup_open(torture, tree, tmp_ctx,
4190 FNAME, &fh, SEC_RIGHTS_FILE_ALL,
4191 FILE_ATTRIBUTE_NORMAL);
4192 torture_assert(torture, ok, "setup file");
4193 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4194 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4195 torture_assert(torture, is_sparse, "sparse after set");
4196 smb2_util_close(tree, fh);
4198 /* attempt to set sparse with only WRITE_EA permission - should fail */
4199 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4202 FILE_ATTRIBUTE_NORMAL);
4203 torture_assert(torture, ok, "setup file");
4205 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4206 torture_assert_ntstatus_equal(torture, status,
4207 NT_STATUS_ACCESS_DENIED,
4208 "FSCTL_SET_SPARSE permission");
4209 smb2_util_close(tree, fh);
4211 ok = test_setup_open(torture, tree, tmp_ctx,
4212 FNAME, &fh, SEC_RIGHTS_FILE_ALL,
4213 FILE_ATTRIBUTE_NORMAL);
4214 torture_assert(torture, ok, "setup file");
4215 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4216 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4217 torture_assert(torture, !is_sparse, "sparse after set");
4218 smb2_util_close(tree, fh);
4220 /* attempt QAR with only READ_ATTR permission - should fail */
4221 ok = test_setup_open(torture, tree, tmp_ctx,
4222 FNAME, &fh, SEC_FILE_READ_ATTRIBUTE,
4223 FILE_ATTRIBUTE_NORMAL);
4224 torture_assert(torture, ok, "setup file");
4225 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4228 &far_rsp, &far_count);
4229 torture_assert_ntstatus_equal(torture, status,
4230 NT_STATUS_ACCESS_DENIED,
4231 "FSCTL_QUERY_ALLOCATED_RANGES req passed");
4232 smb2_util_close(tree, fh);
4234 /* attempt QAR with only READ_DATA permission */
4235 ok = test_setup_open(torture, tree, tmp_ctx,
4236 FNAME, &fh, SEC_FILE_READ_DATA,
4237 FILE_ATTRIBUTE_NORMAL);
4238 torture_assert(torture, ok, "setup file");
4239 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4242 &far_rsp, &far_count);
4243 torture_assert_ntstatus_ok(torture, status,
4244 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4245 torture_assert_u64_equal(torture, far_count, 0,
4246 "unexpected response len");
4247 smb2_util_close(tree, fh);
4249 /* attempt QAR with only READ_EA permission - should fail */
4250 ok = test_setup_open(torture, tree, tmp_ctx,
4251 FNAME, &fh, SEC_FILE_READ_EA,
4252 FILE_ATTRIBUTE_NORMAL);
4253 torture_assert(torture, ok, "setup file");
4254 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4257 &far_rsp, &far_count);
4258 torture_assert_ntstatus_equal(torture, status,
4259 NT_STATUS_ACCESS_DENIED,
4260 "FSCTL_QUERY_ALLOCATED_RANGES req passed");
4261 smb2_util_close(tree, fh);
4263 /* setup file for ZERO_DATA permissions tests */
4264 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4266 SEC_RIGHTS_FILE_ALL,
4267 FILE_ATTRIBUTE_NORMAL);
4268 torture_assert(torture, ok, "setup file");
4270 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4271 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4272 smb2_util_close(tree, fh);
4274 /* attempt ZERO_DATA with only WRITE_ATTR permission - should fail */
4275 ok = test_setup_open(torture, tree, tmp_ctx,
4276 FNAME, &fh, SEC_FILE_WRITE_ATTRIBUTE,
4277 FILE_ATTRIBUTE_NORMAL);
4278 torture_assert(torture, ok, "setup file");
4279 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4281 4096); /* beyond_final_zero */
4282 torture_assert_ntstatus_equal(torture, status,
4283 NT_STATUS_ACCESS_DENIED,
4284 "zero_data permission");
4285 smb2_util_close(tree, fh);
4287 /* attempt ZERO_DATA with only WRITE_DATA permission */
4288 ok = test_setup_open(torture, tree, tmp_ctx,
4289 FNAME, &fh, SEC_FILE_WRITE_DATA,
4290 FILE_ATTRIBUTE_NORMAL);
4291 torture_assert(torture, ok, "setup file");
4292 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4294 4096); /* beyond_final_zero */
4295 torture_assert_ntstatus_ok(torture, status, "zero_data");
4296 smb2_util_close(tree, fh);
4298 /* attempt ZERO_DATA with only APPEND_DATA permission - should fail */
4299 ok = test_setup_open(torture, tree, tmp_ctx,
4300 FNAME, &fh, SEC_FILE_APPEND_DATA,
4301 FILE_ATTRIBUTE_NORMAL);
4302 torture_assert(torture, ok, "setup file");
4303 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4305 4096); /* beyond_final_zero */
4306 torture_assert_ntstatus_equal(torture, status,
4307 NT_STATUS_ACCESS_DENIED,
4308 "zero_data permission");
4309 smb2_util_close(tree, fh);
4311 /* attempt ZERO_DATA with only WRITE_EA permission - should fail */
4312 ok = test_setup_open(torture, tree, tmp_ctx,
4313 FNAME, &fh, SEC_FILE_WRITE_EA,
4314 FILE_ATTRIBUTE_NORMAL);
4315 torture_assert(torture, ok, "setup file");
4316 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4318 4096); /* beyond_final_zero */
4319 torture_assert_ntstatus_equal(torture, status,
4320 NT_STATUS_ACCESS_DENIED,
4321 "zero_data permission");
4322 smb2_util_close(tree, fh);
4324 talloc_free(tmp_ctx);
4328 static bool test_ioctl_sparse_lck(struct torture_context *torture,
4329 struct smb2_tree *tree)
4331 struct smb2_handle fh;
4332 struct smb2_handle fh2;
4334 uint64_t dealloc_chunk_len = 64 * 1024; /* Windows 2012 */
4335 TALLOC_CTX *tmp_ctx = talloc_new(tree);
4338 struct smb2_lock lck;
4339 struct smb2_lock_element el[1];
4340 struct file_alloced_range_buf *far_rsp = NULL;
4341 uint64_t far_count = 0;
4343 ok = test_setup_create_fill(torture, tree, tmp_ctx, FNAME, &fh,
4344 dealloc_chunk_len, SEC_RIGHTS_FILE_ALL,
4345 FILE_ATTRIBUTE_NORMAL);
4346 torture_assert(torture, ok, "setup file");
4348 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
4349 FILE_SUPPORTS_SPARSE_FILES, &ok);
4350 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4352 torture_skip(torture, "Sparse files not supported\n");
4353 smb2_util_close(tree, fh);
4356 /* open and lock via separate fh2 */
4357 status = torture_smb2_testfile(tree, FNAME, &fh2);
4358 torture_assert_ntstatus_ok(torture, status, "2nd src open");
4360 lck.in.lock_count = 0x0001;
4361 lck.in.lock_sequence = 0x00000000;
4362 lck.in.file.handle = fh2;
4365 el[0].length = dealloc_chunk_len;
4367 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
4369 status = smb2_lock(tree, &lck);
4370 torture_assert_ntstatus_ok(torture, status, "lock");
4372 /* set sparse while locked */
4373 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4374 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4376 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4377 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4378 torture_assert(torture, is_sparse, "sparse attr after set");
4380 /* zero data over locked range should fail */
4381 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4383 4096); /* beyond_final_zero */
4384 torture_assert_ntstatus_equal(torture, status,
4385 NT_STATUS_FILE_LOCK_CONFLICT,
4386 "zero_data locked");
4388 /* QAR over locked range should pass */
4389 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4392 &far_rsp, &far_count);
4393 torture_assert_ntstatus_ok(torture, status,
4394 "FSCTL_QUERY_ALLOCATED_RANGES locked");
4395 torture_assert_u64_equal(torture, far_count, 1,
4396 "unexpected response len");
4397 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4398 "unexpected allocation");
4399 torture_assert_u64_equal(torture, far_rsp[0].len,
4401 "unexpected far len");
4403 /* zero data over range past EOF should pass */
4404 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4405 dealloc_chunk_len, /* off */
4406 dealloc_chunk_len + 4096);
4407 torture_assert_ntstatus_ok(torture, status,
4408 "zero_data past EOF locked");
4410 /* QAR over range past EOF should pass */
4411 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4412 dealloc_chunk_len, /* off */
4414 &far_rsp, &far_count);
4415 torture_assert_ntstatus_ok(torture, status,
4416 "FSCTL_QUERY_ALLOCATED_RANGES past EOF locked");
4417 torture_assert_u64_equal(torture, far_count, 0,
4418 "unexpected response len");
4420 lck.in.lock_count = 0x0001;
4421 lck.in.lock_sequence = 0x00000001;
4422 lck.in.file.handle = fh2;
4425 el[0].length = dealloc_chunk_len;
4427 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
4428 status = smb2_lock(tree, &lck);
4429 torture_assert_ntstatus_ok(torture, status, "unlock");
4431 smb2_util_close(tree, fh2);
4432 smb2_util_close(tree, fh);
4433 talloc_free(tmp_ctx);
4437 /* alleviate QAR off-by-one bug paranoia - help me ob1 */
4438 static bool test_ioctl_sparse_qar_ob1(struct torture_context *torture,
4439 struct smb2_tree *tree)
4441 struct smb2_handle fh;
4443 TALLOC_CTX *tmp_ctx = talloc_new(tree);
4445 uint64_t dealloc_chunk_len = 64 * 1024; /* Windows 2012 */
4446 struct file_alloced_range_buf *far_rsp = NULL;
4447 uint64_t far_count = 0;
4449 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4450 FNAME, &fh, dealloc_chunk_len * 2,
4451 SEC_RIGHTS_FILE_ALL,
4452 FILE_ATTRIBUTE_NORMAL);
4453 torture_assert(torture, ok, "setup file");
4455 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
4456 FILE_SUPPORTS_SPARSE_FILES, &ok);
4457 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4459 torture_skip(torture, "Sparse files not supported\n");
4460 smb2_util_close(tree, fh);
4463 /* non-sparse QAR with range one before EOF */
4464 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4466 dealloc_chunk_len * 2 - 1, /* len */
4467 &far_rsp, &far_count);
4468 torture_assert_ntstatus_ok(torture, status,
4469 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4470 torture_assert_u64_equal(torture, far_count, 1,
4471 "unexpected response len");
4472 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4473 "unexpected allocation");
4474 torture_assert_u64_equal(torture, far_rsp[0].len,
4475 dealloc_chunk_len * 2 - 1,
4476 "unexpected far len");
4478 /* non-sparse QAR with range one after EOF */
4479 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4481 dealloc_chunk_len * 2 + 1, /* len */
4482 &far_rsp, &far_count);
4483 torture_assert_ntstatus_ok(torture, status,
4484 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4485 torture_assert_u64_equal(torture, far_count, 1,
4486 "unexpected response len");
4487 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4488 "unexpected allocation");
4489 torture_assert_u64_equal(torture, far_rsp[0].len,
4490 dealloc_chunk_len * 2,
4491 "unexpected far len");
4493 /* non-sparse QAR with range one after EOF from off=1 */
4494 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4496 dealloc_chunk_len * 2, /* len */
4497 &far_rsp, &far_count);
4498 torture_assert_ntstatus_ok(torture, status,
4499 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4500 torture_assert_u64_equal(torture, far_count, 1,
4501 "unexpected response len");
4502 torture_assert_u64_equal(torture, far_rsp[0].file_off, 1,
4503 "unexpected allocation");
4504 torture_assert_u64_equal(torture, far_rsp[0].len,
4505 dealloc_chunk_len * 2 - 1,
4506 "unexpected far len");
4508 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4509 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4511 /* punch out second chunk */
4512 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4513 dealloc_chunk_len, /* off */
4514 dealloc_chunk_len * 2);
4515 torture_assert_ntstatus_ok(torture, status, "zero_data");
4517 /* sparse QAR with range one before hole */
4518 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4520 dealloc_chunk_len - 1, /* len */
4521 &far_rsp, &far_count);
4522 torture_assert_ntstatus_ok(torture, status,
4523 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4524 torture_assert_u64_equal(torture, far_count, 1,
4525 "unexpected response len");
4526 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4527 "unexpected allocation");
4528 torture_assert_u64_equal(torture, far_rsp[0].len,
4529 dealloc_chunk_len - 1,
4530 "unexpected far len");
4532 /* sparse QAR with range one after hole */
4533 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4535 dealloc_chunk_len + 1, /* len */
4536 &far_rsp, &far_count);
4537 torture_assert_ntstatus_ok(torture, status,
4538 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4539 torture_assert_u64_equal(torture, far_count, 1,
4540 "unexpected response len");
4541 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4542 "unexpected allocation");
4543 torture_assert_u64_equal(torture, far_rsp[0].len,
4545 "unexpected far len");
4547 /* sparse QAR with range one after hole from off=1 */
4548 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4550 dealloc_chunk_len, /* len */
4551 &far_rsp, &far_count);
4552 torture_assert_ntstatus_ok(torture, status,
4553 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4554 torture_assert_u64_equal(torture, far_count, 1,
4555 "unexpected response len");
4556 torture_assert_u64_equal(torture, far_rsp[0].file_off, 1,
4557 "unexpected allocation");
4558 torture_assert_u64_equal(torture, far_rsp[0].len,
4559 dealloc_chunk_len - 1,
4560 "unexpected far len");
4562 /* sparse QAR with range one before EOF from off=chunk_len-1 */
4563 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4564 dealloc_chunk_len - 1, /* off */
4565 dealloc_chunk_len, /* len */
4566 &far_rsp, &far_count);
4567 torture_assert_ntstatus_ok(torture, status,
4568 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4569 torture_assert_u64_equal(torture, far_count, 1,
4570 "unexpected response len");
4571 torture_assert_u64_equal(torture, far_rsp[0].file_off,
4572 dealloc_chunk_len - 1,
4573 "unexpected allocation");
4574 torture_assert_u64_equal(torture, far_rsp[0].len,
4575 1, "unexpected far len");
4577 /* sparse QAR with range one after EOF from off=chunk_len+1 */
4578 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4579 dealloc_chunk_len + 1, /* off */
4580 dealloc_chunk_len, /* len */
4581 &far_rsp, &far_count);
4582 torture_assert_ntstatus_ok(torture, status,
4583 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4584 torture_assert_u64_equal(torture, far_count, 0,
4585 "unexpected response len");
4586 smb2_util_close(tree, fh);
4587 talloc_free(tmp_ctx);
4591 /* test QAR with multi-range responses */
4592 static bool test_ioctl_sparse_qar_multi(struct torture_context *torture,
4593 struct smb2_tree *tree)
4595 struct smb2_handle fh;
4597 TALLOC_CTX *tmp_ctx = talloc_new(tree);
4599 uint64_t dealloc_chunk_len = 64 * 1024; /* Windows 2012 */
4602 struct file_alloced_range_buf *far_rsp = NULL;
4603 uint64_t far_count = 0;
4605 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4606 FNAME, &fh, dealloc_chunk_len * 2,
4607 SEC_RIGHTS_FILE_ALL,
4608 FILE_ATTRIBUTE_NORMAL);
4609 torture_assert(torture, ok, "setup file");
4611 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
4612 FILE_SUPPORTS_SPARSE_FILES, &ok);
4613 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4615 torture_skip(torture, "Sparse files not supported\n");
4616 smb2_util_close(tree, fh);
4619 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4620 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4622 /* each loop, write out two chunks and punch the first out */
4623 for (i = 0; i < 10; i++) {
4624 this_off = i * dealloc_chunk_len * 2;
4626 ok = write_pattern(torture, tree, tmp_ctx, fh,
4628 dealloc_chunk_len * 2, /* len */
4629 this_off); /* pattern offset */
4630 torture_assert(torture, ok, "write pattern");
4632 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4634 this_off + dealloc_chunk_len);
4635 torture_assert_ntstatus_ok(torture, status, "zero_data");
4638 /* should now have one separate region for each iteration */
4639 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4641 10 * dealloc_chunk_len * 2,
4642 &far_rsp, &far_count);
4643 torture_assert_ntstatus_ok(torture, status,
4644 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4645 if (far_count == 1) {
4646 torture_comment(torture, "this FS doesn't deallocate 64K"
4647 "zeroed ranges in sparse files\n");
4648 return true; /* FS specific, not a failure */
4650 torture_assert_u64_equal(torture, far_count, 10,
4651 "unexpected response len");
4652 for (i = 0; i < 10; i++) {
4653 this_off = i * dealloc_chunk_len * 2;
4655 torture_assert_u64_equal(torture, far_rsp[i].file_off,
4656 this_off + dealloc_chunk_len,
4657 "unexpected allocation");
4658 torture_assert_u64_equal(torture, far_rsp[i].len,
4660 "unexpected far len");
4663 smb2_util_close(tree, fh);
4664 talloc_free(tmp_ctx);
4668 static bool test_ioctl_sparse_qar_overflow(struct torture_context *torture,
4669 struct smb2_tree *tree)
4671 struct smb2_handle fh;
4672 union smb_ioctl ioctl;
4673 struct file_alloced_range_buf far_buf;
4675 enum ndr_err_code ndr_ret;
4676 TALLOC_CTX *tmp_ctx = talloc_new(tree);
4679 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4680 FNAME, &fh, 1024, SEC_RIGHTS_FILE_ALL,
4681 FILE_ATTRIBUTE_NORMAL);
4682 torture_assert(torture, ok, "setup file");
4684 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
4685 FILE_SUPPORTS_SPARSE_FILES, &ok);
4686 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4688 smb2_util_close(tree, fh);
4689 torture_skip(torture, "Sparse files not supported\n");
4692 /* no allocated ranges, no space for range response, should pass */
4694 ioctl.smb2.level = RAW_IOCTL_SMB2;
4695 ioctl.smb2.in.file.handle = fh;
4696 ioctl.smb2.in.function = FSCTL_QUERY_ALLOCATED_RANGES;
4697 ioctl.smb2.in.max_response_size = 1024;
4698 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
4700 /* off + length wraps around to 511 */
4701 far_buf.file_off = 512;
4702 far_buf.len = 0xffffffffffffffffLL;
4703 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
4705 (ndr_push_flags_fn_t)ndr_push_file_alloced_range_buf);
4706 torture_assert_ndr_success(torture, ndr_ret, "push far ndr buf");
4708 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
4709 torture_assert_ntstatus_equal(torture, status,
4710 NT_STATUS_INVALID_PARAMETER,
4711 "FSCTL_QUERY_ALLOCATED_RANGES overflow");
4716 static NTSTATUS test_ioctl_trim_supported(struct torture_context *torture,
4717 struct smb2_tree *tree,
4718 TALLOC_CTX *mem_ctx,
4719 struct smb2_handle *fh,
4723 union smb_fsinfo info;
4726 info.generic.level = RAW_QFS_SECTOR_SIZE_INFORMATION;
4727 info.generic.handle = *fh;
4728 status = smb2_getinfo_fs(tree, tree, &info);
4729 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_INFO_CLASS)) {
4731 * Windows < Server 2012, 8 etc. don't support this info level
4732 * or the trim ioctl. Ignore the error and let the caller skip.
4734 *trim_support = false;
4735 return NT_STATUS_OK;
4736 } else if (!NT_STATUS_IS_OK(status)) {
4740 torture_comment(torture, "sector size info: lb/s=%u, pb/sA=%u, "
4741 "pb/sP=%u, fse/sA=%u, flags=0x%x, bosa=%u, bopa=%u\n",
4742 (unsigned)info.sector_size_info.out.logical_bytes_per_sector,
4743 (unsigned)info.sector_size_info.out.phys_bytes_per_sector_atomic,
4744 (unsigned)info.sector_size_info.out.phys_bytes_per_sector_perf,
4745 (unsigned)info.sector_size_info.out.fs_effective_phys_bytes_per_sector_atomic,
4746 (unsigned)info.sector_size_info.out.flags,
4747 (unsigned)info.sector_size_info.out.byte_off_sector_align,
4748 (unsigned)info.sector_size_info.out.byte_off_partition_align);
4750 if (info.sector_size_info.out.flags & QFS_SSINFO_FLAGS_TRIM_ENABLED) {
4751 *trim_support = true;
4753 *trim_support = false;
4755 return NT_STATUS_OK;
4758 static bool test_setup_trim(struct torture_context *torture,
4759 struct smb2_tree *tree,
4760 TALLOC_CTX *mem_ctx,
4761 uint32_t num_ranges,
4762 struct smb2_handle *fh,
4764 uint32_t desired_access,
4765 struct fsctl_file_level_trim_req *trim_req,
4766 union smb_ioctl *ioctl)
4770 ok = test_setup_create_fill(torture, tree, mem_ctx, FNAME,
4771 fh, file_size, desired_access,
4772 FILE_ATTRIBUTE_NORMAL);
4773 torture_assert(torture, ok, "src file create fill");
4775 ZERO_STRUCTPN(ioctl);
4776 ioctl->smb2.level = RAW_IOCTL_SMB2;
4777 ioctl->smb2.in.file.handle = *fh;
4778 ioctl->smb2.in.function = FSCTL_FILE_LEVEL_TRIM;
4779 ioctl->smb2.in.max_response_size
4780 = sizeof(struct fsctl_file_level_trim_rsp);
4781 ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
4783 ZERO_STRUCTPN(trim_req);
4784 /* leave key as zero for now. TODO test locking with differing keys */
4785 trim_req->num_ranges = num_ranges;
4786 trim_req->ranges = talloc_zero_array(mem_ctx,
4787 struct file_level_trim_range,
4789 torture_assert(torture, (trim_req->ranges != NULL), "no memory for ranges");
4794 static bool test_ioctl_trim_simple(struct torture_context *torture,
4795 struct smb2_tree *tree)
4797 struct smb2_handle fh;
4799 union smb_ioctl ioctl;
4800 bool trim_supported;
4801 TALLOC_CTX *tmp_ctx = talloc_new(tree);
4802 struct fsctl_file_level_trim_req trim_req;
4803 struct fsctl_file_level_trim_rsp trim_rsp;
4804 uint64_t trim_chunk_len = 64 * 1024; /* trim 64K chunks */
4805 enum ndr_err_code ndr_ret;
4808 ok = test_setup_trim(torture, tree, tmp_ctx,
4810 &fh, 2 * trim_chunk_len, /* fill 128K file */
4811 SEC_RIGHTS_FILE_ALL,
4815 torture_fail(torture, "setup trim error");
4818 status = test_ioctl_trim_supported(torture, tree, tmp_ctx, &fh,
4820 torture_assert_ntstatus_ok(torture, status, "fsinfo");
4821 if (!trim_supported) {
4822 smb2_util_close(tree, fh);
4823 talloc_free(tmp_ctx);
4824 torture_skip(torture, "trim not supported\n");
4827 /* trim first chunk, leave second */
4828 trim_req.ranges[0].off = 0;
4829 trim_req.ranges[0].len = trim_chunk_len;
4831 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, &trim_req,
4832 (ndr_push_flags_fn_t)ndr_push_fsctl_file_level_trim_req);
4833 torture_assert_ndr_success(torture, ndr_ret,
4834 "ndr_push_fsctl_file_level_trim_req");
4836 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
4837 torture_assert_ntstatus_ok(torture, status, "FILE_LEVEL_TRIM_RANGE");
4839 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
4841 (ndr_pull_flags_fn_t)ndr_pull_fsctl_file_level_trim_rsp);
4842 torture_assert_ndr_success(torture, ndr_ret,
4843 "ndr_pull_fsctl_file_level_trim_rsp");
4845 torture_assert_int_equal(torture, trim_rsp.num_ranges_processed, 1, "");
4847 /* second half of the file should remain consitent */
4848 ok = check_pattern(torture, tree, tmp_ctx, fh, trim_chunk_len,
4849 trim_chunk_len, trim_chunk_len);
4850 torture_assert(torture, ok, "non-trimmed range inconsistent");
4855 static bool test_setup_dup_extents(struct torture_context *tctx,
4856 struct smb2_tree *tree,
4857 TALLOC_CTX *mem_ctx,
4858 struct smb2_handle *src_h,
4860 uint32_t src_desired_access,
4861 struct smb2_handle *dest_h,
4863 uint32_t dest_desired_access,
4864 struct fsctl_dup_extents_to_file *dup_ext_buf,
4865 union smb_ioctl *ioctl)
4869 ok = test_setup_create_fill(tctx, tree, mem_ctx, FNAME,
4870 src_h, src_size, src_desired_access,
4871 FILE_ATTRIBUTE_NORMAL);
4872 torture_assert(tctx, ok, "src file create fill");
4874 ok = test_setup_create_fill(tctx, tree, mem_ctx, FNAME2,
4875 dest_h, dest_size, dest_desired_access,
4876 FILE_ATTRIBUTE_NORMAL);
4877 torture_assert(tctx, ok, "dest file create fill");
4879 ZERO_STRUCTPN(ioctl);
4880 ioctl->smb2.level = RAW_IOCTL_SMB2;
4881 ioctl->smb2.in.file.handle = *dest_h;
4882 ioctl->smb2.in.function = FSCTL_DUP_EXTENTS_TO_FILE;
4883 ioctl->smb2.in.max_response_size = 0;
4884 ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
4886 ZERO_STRUCTPN(dup_ext_buf);
4887 smb2_push_handle(dup_ext_buf->source_fid, src_h);
4892 static bool test_ioctl_dup_extents_simple(struct torture_context *tctx,
4893 struct smb2_tree *tree)
4895 struct smb2_handle src_h;
4896 struct smb2_handle dest_h;
4898 union smb_ioctl ioctl;
4899 TALLOC_CTX *tmp_ctx = talloc_new(tree);
4900 struct fsctl_dup_extents_to_file dup_ext_buf;
4901 enum ndr_err_code ndr_ret;
4902 union smb_fileinfo io;
4903 union smb_setfileinfo sinfo;
4906 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
4907 &src_h, 4096, /* fill 4096 byte src file */
4908 SEC_RIGHTS_FILE_ALL,
4909 &dest_h, 0, /* 0 byte dest file */
4910 SEC_RIGHTS_FILE_ALL,
4914 torture_fail(tctx, "setup dup extents error");
4917 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
4918 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
4919 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
4921 smb2_util_close(tree, src_h);
4922 smb2_util_close(tree, dest_h);
4923 talloc_free(tmp_ctx);
4924 torture_skip(tctx, "block refcounting not supported\n");
4927 /* extend dest to match src len */
4929 sinfo.end_of_file_info.level =
4930 RAW_SFILEINFO_END_OF_FILE_INFORMATION;
4931 sinfo.end_of_file_info.in.file.handle = dest_h;
4932 sinfo.end_of_file_info.in.size = 4096;
4933 status = smb2_setinfo_file(tree, &sinfo);
4934 torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
4936 /* copy all src file data */
4937 dup_ext_buf.source_off = 0;
4938 dup_ext_buf.target_off = 0;
4939 dup_ext_buf.byte_count = 4096;
4941 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
4943 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
4944 torture_assert_ndr_success(tctx, ndr_ret,
4945 "ndr_push_fsctl_dup_extents_to_file");
4947 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
4948 torture_assert_ntstatus_ok(tctx, status,
4949 "FSCTL_DUP_EXTENTS_TO_FILE");
4951 /* the file size shouldn't have been changed by this operation! */
4953 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
4954 io.generic.in.file.handle = dest_h;
4955 status = smb2_getinfo_file(tree, tmp_ctx, &io);
4956 torture_assert_ntstatus_ok(tctx, status, "getinfo");
4957 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
4958 4096, "size after IO");
4960 smb2_util_close(tree, src_h);
4961 smb2_util_close(tree, dest_h);
4963 /* reopen for pattern check */
4964 ok = test_setup_open(tctx, tree, tmp_ctx, FNAME, &src_h,
4965 SEC_RIGHTS_FILE_ALL, FILE_ATTRIBUTE_NORMAL);
4966 torture_assert_ntstatus_ok(tctx, status, "src open after dup");
4967 ok = test_setup_open(tctx, tree, tmp_ctx, FNAME2, &dest_h,
4968 SEC_RIGHTS_FILE_ALL, FILE_ATTRIBUTE_NORMAL);
4969 torture_assert_ntstatus_ok(tctx, status, "dest open after dup");
4971 ok = check_pattern(tctx, tree, tmp_ctx, src_h, 0, 4096, 0);
4973 torture_fail(tctx, "inconsistent src file data");
4976 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 4096, 0);
4978 torture_fail(tctx, "inconsistent dest file data");
4981 smb2_util_close(tree, src_h);
4982 smb2_util_close(tree, dest_h);
4983 talloc_free(tmp_ctx);
4987 static bool test_ioctl_dup_extents_len_beyond_dest(struct torture_context *tctx,
4988 struct smb2_tree *tree)
4990 struct smb2_handle src_h;
4991 struct smb2_handle dest_h;
4993 union smb_ioctl ioctl;
4994 TALLOC_CTX *tmp_ctx = talloc_new(tree);
4995 struct fsctl_dup_extents_to_file dup_ext_buf;
4996 enum ndr_err_code ndr_ret;
4997 union smb_fileinfo io;
4998 union smb_setfileinfo sinfo;
5001 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5002 &src_h, 32768, /* fill 32768 byte src file */
5003 SEC_RIGHTS_FILE_ALL,
5004 &dest_h, 0, /* 0 byte dest file */
5005 SEC_RIGHTS_FILE_ALL,
5009 torture_fail(tctx, "setup dup extents error");
5012 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5013 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
5014 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5016 smb2_util_close(tree, src_h);
5017 smb2_util_close(tree, dest_h);
5018 talloc_free(tmp_ctx);
5019 torture_skip(tctx, "block refcounting not supported\n");
5023 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5024 io.generic.in.file.handle = dest_h;
5025 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5026 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5027 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5028 0, "size after IO");
5030 /* copy all src file data */
5031 dup_ext_buf.source_off = 0;
5032 dup_ext_buf.target_off = 0;
5033 dup_ext_buf.byte_count = 32768;
5035 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5037 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5038 torture_assert_ndr_success(tctx, ndr_ret,
5039 "ndr_push_fsctl_dup_extents_to_file");
5041 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5044 * 2.3.8 FSCTL_DUPLICATE_EXTENTS_TO_FILE Reply - this should fail, but
5045 * passes against WS2016 RTM!
5047 torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_SUPPORTED,
5048 "FSCTL_DUP_EXTENTS_TO_FILE");
5051 /* the file sizes shouldn't have been changed */
5053 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5054 io.generic.in.file.handle = src_h;
5055 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5056 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5057 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5058 32768, "size after IO");
5061 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5062 io.generic.in.file.handle = dest_h;
5063 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5064 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5065 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5066 0, "size after IO");
5070 sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
5071 sinfo.end_of_file_info.in.file.handle = dest_h;
5072 sinfo.end_of_file_info.in.size = 32768;
5073 status = smb2_setinfo_file(tree, &sinfo);
5074 torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
5076 ok = check_zero(tctx, tree, tmp_ctx, dest_h, 0, 32768);
5078 torture_fail(tctx, "inconsistent file data");
5081 /* reissue ioctl, now with enough space */
5082 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5083 torture_assert_ntstatus_ok(tctx, status,
5084 "FSCTL_DUP_EXTENTS_TO_FILE");
5086 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
5088 torture_fail(tctx, "inconsistent file data");
5091 smb2_util_close(tree, src_h);
5092 smb2_util_close(tree, dest_h);
5093 talloc_free(tmp_ctx);
5097 static bool test_ioctl_dup_extents_len_beyond_src(struct torture_context *tctx,
5098 struct smb2_tree *tree)
5100 struct smb2_handle src_h;
5101 struct smb2_handle dest_h;
5103 union smb_ioctl ioctl;
5104 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5105 struct fsctl_dup_extents_to_file dup_ext_buf;
5106 enum ndr_err_code ndr_ret;
5107 union smb_fileinfo io;
5110 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5111 &src_h, 32768, /* fill 32768 byte src file */
5112 SEC_RIGHTS_FILE_ALL,
5113 &dest_h, 0, /* 0 byte dest file */
5114 SEC_RIGHTS_FILE_ALL,
5118 torture_fail(tctx, "setup dup extents error");
5121 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5122 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
5123 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5125 smb2_util_close(tree, src_h);
5126 smb2_util_close(tree, dest_h);
5127 talloc_free(tmp_ctx);
5128 torture_skip(tctx, "block refcounting not supported\n");
5132 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5133 io.generic.in.file.handle = dest_h;
5134 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5135 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5136 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5137 0, "size after IO");
5139 /* exceed src file len */
5140 dup_ext_buf.source_off = 0;
5141 dup_ext_buf.target_off = 0;
5142 dup_ext_buf.byte_count = 32768 * 2;
5144 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5146 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5147 torture_assert_ndr_success(tctx, ndr_ret,
5148 "ndr_push_fsctl_dup_extents_to_file");
5150 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5151 torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_SUPPORTED,
5152 "FSCTL_DUP_EXTENTS_TO_FILE");
5154 /* the file sizes shouldn't have been changed */
5156 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5157 io.generic.in.file.handle = src_h;
5158 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5159 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5160 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5161 32768, "size after IO");
5164 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5165 io.generic.in.file.handle = dest_h;
5166 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5167 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5168 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5169 0, "size after IO");
5171 smb2_util_close(tree, src_h);
5172 smb2_util_close(tree, dest_h);
5173 talloc_free(tmp_ctx);
5177 static bool test_ioctl_dup_extents_len_zero(struct torture_context *tctx,
5178 struct smb2_tree *tree)
5180 struct smb2_handle src_h;
5181 struct smb2_handle dest_h;
5183 union smb_ioctl ioctl;
5184 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5185 struct fsctl_dup_extents_to_file dup_ext_buf;
5186 enum ndr_err_code ndr_ret;
5187 union smb_fileinfo io;
5190 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5191 &src_h, 32768, /* fill 32768 byte src file */
5192 SEC_RIGHTS_FILE_ALL,
5193 &dest_h, 0, /* 0 byte dest file */
5194 SEC_RIGHTS_FILE_ALL,
5198 torture_fail(tctx, "setup dup extents error");
5201 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5202 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
5203 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5205 smb2_util_close(tree, src_h);
5206 smb2_util_close(tree, dest_h);
5207 talloc_free(tmp_ctx);
5208 torture_skip(tctx, "block refcounting not supported\n");
5212 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5213 io.generic.in.file.handle = dest_h;
5214 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5215 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5216 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5217 0, "size after IO");
5219 dup_ext_buf.source_off = 0;
5220 dup_ext_buf.target_off = 0;
5221 dup_ext_buf.byte_count = 0;
5223 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5225 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5226 torture_assert_ndr_success(tctx, ndr_ret,
5227 "ndr_push_fsctl_dup_extents_to_file");
5229 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5230 torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
5232 /* the file sizes shouldn't have been changed */
5234 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5235 io.generic.in.file.handle = src_h;
5236 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5237 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5238 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5239 32768, "size after IO");
5242 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5243 io.generic.in.file.handle = dest_h;
5244 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5245 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5246 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5247 0, "size after IO");
5249 smb2_util_close(tree, src_h);
5250 smb2_util_close(tree, dest_h);
5251 talloc_free(tmp_ctx);
5255 static bool test_ioctl_dup_extents_sparse_src(struct torture_context *tctx,
5256 struct smb2_tree *tree)
5258 struct smb2_handle src_h;
5259 struct smb2_handle dest_h;
5261 union smb_ioctl ioctl;
5262 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5263 struct fsctl_dup_extents_to_file dup_ext_buf;
5264 enum ndr_err_code ndr_ret;
5265 union smb_setfileinfo sinfo;
5268 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5269 &src_h, 0, /* filled after sparse flag */
5270 SEC_RIGHTS_FILE_ALL,
5271 &dest_h, 0, /* 0 byte dest file */
5272 SEC_RIGHTS_FILE_ALL,
5276 torture_fail(tctx, "setup dup extents error");
5279 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5280 FILE_SUPPORTS_BLOCK_REFCOUNTING
5281 | FILE_SUPPORTS_SPARSE_FILES, &ok);
5282 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5284 smb2_util_close(tree, src_h);
5285 smb2_util_close(tree, dest_h);
5286 talloc_free(tmp_ctx);
5288 "block refcounting and sparse files not supported\n");
5291 /* set sparse flag on src */
5292 status = test_ioctl_sparse_req(tctx, tmp_ctx, tree, src_h, true);
5293 torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_SPARSE");
5295 ok = write_pattern(tctx, tree, tmp_ctx, src_h, 0, 4096, 0);
5296 torture_assert(tctx, ok, "write pattern");
5300 sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
5301 sinfo.end_of_file_info.in.file.handle = dest_h;
5302 sinfo.end_of_file_info.in.size = 4096;
5303 status = smb2_setinfo_file(tree, &sinfo);
5304 torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
5306 /* copy all src file data */
5307 dup_ext_buf.source_off = 0;
5308 dup_ext_buf.target_off = 0;
5309 dup_ext_buf.byte_count = 4096;
5311 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5313 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5314 torture_assert_ndr_success(tctx, ndr_ret,
5315 "ndr_push_fsctl_dup_extents_to_file");
5318 * src is sparse, but spec says: 2.3.8 FSCTL_DUPLICATE_EXTENTS_TO_FILE
5319 * Reply... STATUS_NOT_SUPPORTED: Target file is sparse, while source
5320 * is a non-sparse file.
5322 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5323 torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_SUPPORTED,
5324 "FSCTL_DUP_EXTENTS_TO_FILE");
5326 smb2_util_close(tree, src_h);
5327 smb2_util_close(tree, dest_h);
5328 talloc_free(tmp_ctx);
5332 static bool test_ioctl_dup_extents_sparse_dest(struct torture_context *tctx,
5333 struct smb2_tree *tree)
5335 struct smb2_handle src_h;
5336 struct smb2_handle dest_h;
5338 union smb_ioctl ioctl;
5339 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5340 struct fsctl_dup_extents_to_file dup_ext_buf;
5341 enum ndr_err_code ndr_ret;
5342 union smb_setfileinfo sinfo;
5345 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5346 &src_h, 4096, /* fill 4096 byte src file */
5347 SEC_RIGHTS_FILE_ALL,
5348 &dest_h, 0, /* 0 byte dest file */
5349 SEC_RIGHTS_FILE_ALL,
5353 torture_fail(tctx, "setup dup extents error");
5356 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5357 FILE_SUPPORTS_BLOCK_REFCOUNTING
5358 | FILE_SUPPORTS_SPARSE_FILES, &ok);
5359 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5361 smb2_util_close(tree, src_h);
5362 smb2_util_close(tree, dest_h);
5363 talloc_free(tmp_ctx);
5365 "block refcounting and sparse files not supported\n");
5368 /* set sparse flag on dest */
5369 status = test_ioctl_sparse_req(tctx, tmp_ctx, tree, dest_h, true);
5370 torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_SPARSE");
5374 sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
5375 sinfo.end_of_file_info.in.file.handle = dest_h;
5376 sinfo.end_of_file_info.in.size = dup_ext_buf.byte_count;
5377 status = smb2_setinfo_file(tree, &sinfo);
5378 torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
5380 /* copy all src file data */
5381 dup_ext_buf.source_off = 0;
5382 dup_ext_buf.target_off = 0;
5383 dup_ext_buf.byte_count = 4096;
5385 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5387 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5388 torture_assert_ndr_success(tctx, ndr_ret,
5389 "ndr_push_fsctl_dup_extents_to_file");
5392 * dest is sparse, but spec says: 2.3.8 FSCTL_DUPLICATE_EXTENTS_TO_FILE
5393 * Reply... STATUS_NOT_SUPPORTED: Target file is sparse, while source
5394 * is a non-sparse file.
5396 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5397 torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
5399 smb2_util_close(tree, src_h);
5400 smb2_util_close(tree, dest_h);
5401 talloc_free(tmp_ctx);
5405 static bool test_ioctl_dup_extents_sparse_both(struct torture_context *tctx,
5406 struct smb2_tree *tree)
5408 struct smb2_handle src_h;
5409 struct smb2_handle dest_h;
5411 union smb_ioctl ioctl;
5412 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5413 struct fsctl_dup_extents_to_file dup_ext_buf;
5414 enum ndr_err_code ndr_ret;
5415 union smb_setfileinfo sinfo;
5418 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5419 &src_h, 0, /* fill 4096 byte src file */
5420 SEC_RIGHTS_FILE_ALL,
5421 &dest_h, 0, /* 0 byte dest file */
5422 SEC_RIGHTS_FILE_ALL,
5426 torture_fail(tctx, "setup dup extents error");
5429 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5430 FILE_SUPPORTS_BLOCK_REFCOUNTING
5431 | FILE_SUPPORTS_SPARSE_FILES, &ok);
5432 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5434 smb2_util_close(tree, src_h);
5435 smb2_util_close(tree, dest_h);
5436 talloc_free(tmp_ctx);
5438 "block refcounting and sparse files not supported\n");
5441 /* set sparse flag on src and dest */
5442 status = test_ioctl_sparse_req(tctx, tmp_ctx, tree, src_h, true);
5443 torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_SPARSE");
5444 status = test_ioctl_sparse_req(tctx, tmp_ctx, tree, dest_h, true);
5445 torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_SPARSE");
5447 ok = write_pattern(tctx, tree, tmp_ctx, src_h, 0, 4096, 0);
5448 torture_assert(tctx, ok, "write pattern");
5452 sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
5453 sinfo.end_of_file_info.in.file.handle = dest_h;
5454 sinfo.end_of_file_info.in.size = 4096;
5455 status = smb2_setinfo_file(tree, &sinfo);
5456 torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
5458 /* copy all src file data */
5459 dup_ext_buf.source_off = 0;
5460 dup_ext_buf.target_off = 0;
5461 dup_ext_buf.byte_count = 4096;
5463 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5465 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5466 torture_assert_ndr_success(tctx, ndr_ret,
5467 "ndr_push_fsctl_dup_extents_to_file");
5469 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5470 torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
5472 smb2_util_close(tree, src_h);
5473 smb2_util_close(tree, dest_h);
5475 /* reopen for pattern check */
5476 ok = test_setup_open(tctx, tree, tmp_ctx, FNAME2, &dest_h,
5477 SEC_RIGHTS_FILE_ALL, FILE_ATTRIBUTE_NORMAL);
5478 torture_assert_ntstatus_ok(tctx, status, "dest open ater dup");
5480 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 4096, 0);
5482 torture_fail(tctx, "inconsistent file data");
5485 smb2_util_close(tree, dest_h);
5486 talloc_free(tmp_ctx);
5490 static bool test_ioctl_dup_extents_src_is_dest(struct torture_context *tctx,
5491 struct smb2_tree *tree)
5493 struct smb2_handle src_h;
5494 struct smb2_handle dest_h;
5496 union smb_ioctl ioctl;
5497 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5498 struct fsctl_dup_extents_to_file dup_ext_buf;
5499 enum ndr_err_code ndr_ret;
5500 union smb_fileinfo io;
5503 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5504 &src_h, 32768, /* fill 32768 byte src file */
5505 SEC_RIGHTS_FILE_ALL,
5507 SEC_RIGHTS_FILE_ALL,
5511 torture_fail(tctx, "setup dup extents error");
5513 /* dest_h not needed for this test */
5514 smb2_util_close(tree, dest_h);
5516 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5517 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
5518 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5520 smb2_util_close(tree, src_h);
5521 talloc_free(tmp_ctx);
5522 torture_skip(tctx, "block refcounting not supported\n");
5525 /* src and dest are the same file handle */
5526 ioctl.smb2.in.file.handle = src_h;
5528 /* no overlap between src and tgt */
5529 dup_ext_buf.source_off = 0;
5530 dup_ext_buf.target_off = 16384;
5531 dup_ext_buf.byte_count = 16384;
5533 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5535 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5536 torture_assert_ndr_success(tctx, ndr_ret,
5537 "ndr_push_fsctl_dup_extents_to_file");
5539 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5540 torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
5542 /* the file size shouldn't have been changed */
5544 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5545 io.generic.in.file.handle = src_h;
5546 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5547 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5548 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5549 32768, "size after IO");
5551 ok = check_pattern(tctx, tree, tmp_ctx, src_h, 0, 16384, 0);
5553 torture_fail(tctx, "inconsistent file data");
5555 ok = check_pattern(tctx, tree, tmp_ctx, src_h, 16384, 16384, 0);
5557 torture_fail(tctx, "inconsistent file data");
5560 smb2_util_close(tree, src_h);
5561 talloc_free(tmp_ctx);
5566 * unlike copy-chunk, dup extents doesn't support overlapping ranges between
5567 * source and target. This makes it a *lot* cleaner to implement on the server.
5570 test_ioctl_dup_extents_src_is_dest_overlap(struct torture_context *tctx,
5571 struct smb2_tree *tree)
5573 struct smb2_handle src_h;
5574 struct smb2_handle dest_h;
5576 union smb_ioctl ioctl;
5577 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5578 struct fsctl_dup_extents_to_file dup_ext_buf;
5579 enum ndr_err_code ndr_ret;
5580 union smb_fileinfo io;
5583 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5584 &src_h, 32768, /* fill 32768 byte src file */
5585 SEC_RIGHTS_FILE_ALL,
5587 SEC_RIGHTS_FILE_ALL,
5591 torture_fail(tctx, "setup dup extents error");
5593 /* dest_h not needed for this test */
5594 smb2_util_close(tree, dest_h);
5596 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5597 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
5598 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5600 smb2_util_close(tree, src_h);
5601 talloc_free(tmp_ctx);
5602 torture_skip(tctx, "block refcounting not supported\n");
5605 /* src and dest are the same file handle */
5606 ioctl.smb2.in.file.handle = src_h;
5608 /* 8K overlap between src and tgt */
5609 dup_ext_buf.source_off = 0;
5610 dup_ext_buf.target_off = 8192;
5611 dup_ext_buf.byte_count = 16384;
5613 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5615 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5616 torture_assert_ndr_success(tctx, ndr_ret,
5617 "ndr_push_fsctl_dup_extents_to_file");
5619 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5620 torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_SUPPORTED,
5621 "FSCTL_DUP_EXTENTS_TO_FILE");
5623 /* the file size and data should match beforehand */
5625 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5626 io.generic.in.file.handle = src_h;
5627 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5628 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5629 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5630 32768, "size after IO");
5632 ok = check_pattern(tctx, tree, tmp_ctx, src_h, 0, 32768, 0);
5634 torture_fail(tctx, "inconsistent file data");
5637 smb2_util_close(tree, src_h);
5638 talloc_free(tmp_ctx);
5643 * The compression tests won't run against Windows servers yet - ReFS doesn't
5644 * (yet) offer support for compression.
5646 static bool test_ioctl_dup_extents_compressed_src(struct torture_context *tctx,
5647 struct smb2_tree *tree)
5649 struct smb2_handle src_h;
5650 struct smb2_handle dest_h;
5652 union smb_ioctl ioctl;
5653 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5654 struct fsctl_dup_extents_to_file dup_ext_buf;
5655 enum ndr_err_code ndr_ret;
5656 union smb_setfileinfo sinfo;
5659 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5660 &src_h, 0, /* filled after compressed flag */
5661 SEC_RIGHTS_FILE_ALL,
5663 SEC_RIGHTS_FILE_ALL,
5667 torture_fail(tctx, "setup dup extents error");
5670 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5671 FILE_SUPPORTS_BLOCK_REFCOUNTING
5672 | FILE_FILE_COMPRESSION, &ok);
5673 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5675 smb2_util_close(tree, src_h);
5676 smb2_util_close(tree, dest_h);
5677 talloc_free(tmp_ctx);
5679 "block refcounting and compressed files not supported\n");
5682 /* set compressed flag on src */
5683 status = test_ioctl_compress_set(tctx, tmp_ctx, tree, src_h,
5684 COMPRESSION_FORMAT_DEFAULT);
5685 torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_COMPRESSION");
5687 ok = write_pattern(tctx, tree, tmp_ctx, src_h, 0, 4096, 0);
5688 torture_assert(tctx, ok, "write pattern");
5692 sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
5693 sinfo.end_of_file_info.in.file.handle = dest_h;
5694 sinfo.end_of_file_info.in.size = 4096;
5695 status = smb2_setinfo_file(tree, &sinfo);
5696 torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
5698 /* copy all src file data */
5699 dup_ext_buf.source_off = 0;
5700 dup_ext_buf.target_off = 0;
5701 dup_ext_buf.byte_count = 4096;
5703 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5705 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5706 torture_assert_ndr_success(tctx, ndr_ret,
5707 "ndr_push_fsctl_dup_extents_to_file");
5709 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5710 torture_assert_ntstatus_ok(tctx, status,
5711 "FSCTL_DUP_EXTENTS_TO_FILE");
5713 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 4096, 0);
5715 torture_fail(tctx, "inconsistent file data");
5718 smb2_util_close(tree, src_h);
5719 smb2_util_close(tree, dest_h);
5720 talloc_free(tmp_ctx);
5724 static bool test_ioctl_dup_extents_compressed_dest(struct torture_context *tctx,
5725 struct smb2_tree *tree)
5727 struct smb2_handle src_h;
5728 struct smb2_handle dest_h;
5730 union smb_ioctl ioctl;
5731 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5732 struct fsctl_dup_extents_to_file dup_ext_buf;
5733 enum ndr_err_code ndr_ret;
5734 union smb_setfileinfo sinfo;
5737 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5739 SEC_RIGHTS_FILE_ALL,
5741 SEC_RIGHTS_FILE_ALL,
5745 torture_fail(tctx, "setup dup extents error");
5748 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5749 FILE_SUPPORTS_BLOCK_REFCOUNTING
5750 | FILE_FILE_COMPRESSION, &ok);
5751 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5753 smb2_util_close(tree, src_h);
5754 smb2_util_close(tree, dest_h);
5755 talloc_free(tmp_ctx);
5757 "block refcounting and compressed files not supported\n");
5760 /* set compressed flag on dest */
5761 status = test_ioctl_compress_set(tctx, tmp_ctx, tree, dest_h,
5762 COMPRESSION_FORMAT_DEFAULT);
5763 torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_COMPRESSION");
5767 sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
5768 sinfo.end_of_file_info.in.file.handle = dest_h;
5769 sinfo.end_of_file_info.in.size = dup_ext_buf.byte_count;
5770 status = smb2_setinfo_file(tree, &sinfo);
5771 torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
5773 /* copy all src file data */
5774 dup_ext_buf.source_off = 0;
5775 dup_ext_buf.target_off = 0;
5776 dup_ext_buf.byte_count = 4096;
5778 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5780 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5781 torture_assert_ndr_success(tctx, ndr_ret,
5782 "ndr_push_fsctl_dup_extents_to_file");
5784 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5785 torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_SUPPORTED,
5786 "FSCTL_DUP_EXTENTS_TO_FILE");
5788 smb2_util_close(tree, src_h);
5789 smb2_util_close(tree, dest_h);
5790 talloc_free(tmp_ctx);
5794 static bool test_ioctl_dup_extents_bad_handle(struct torture_context *tctx,
5795 struct smb2_tree *tree)
5797 struct smb2_handle src_h;
5798 struct smb2_handle dest_h;
5799 struct smb2_handle bogus_h;
5801 union smb_ioctl ioctl;
5802 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5803 struct fsctl_dup_extents_to_file dup_ext_buf;
5804 enum ndr_err_code ndr_ret;
5807 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5808 &src_h, 32768, /* fill 32768 byte src file */
5809 SEC_RIGHTS_FILE_ALL,
5811 SEC_RIGHTS_FILE_ALL,
5815 torture_fail(tctx, "setup dup extents error");
5818 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5819 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
5820 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5822 smb2_util_close(tree, src_h);
5823 smb2_util_close(tree, dest_h);
5824 talloc_free(tmp_ctx);
5825 torture_skip(tctx, "block refcounting not supported\n");
5828 /* open and close a file, keeping the handle as now a "bogus" handle */
5829 ok = test_setup_create_fill(tctx, tree, tmp_ctx, "bogus_file",
5830 &bogus_h, 0, SEC_RIGHTS_FILE_ALL,
5831 FILE_ATTRIBUTE_NORMAL);
5832 torture_assert(tctx, ok, "bogus file create fill");
5833 smb2_util_close(tree, bogus_h);
5835 /* bogus dest file handle */
5836 ioctl.smb2.in.file.handle = bogus_h;
5838 dup_ext_buf.source_off = 0;
5839 dup_ext_buf.target_off = 0;
5840 dup_ext_buf.byte_count = 32768;
5842 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5844 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5845 torture_assert_ndr_success(tctx, ndr_ret,
5846 "ndr_push_fsctl_dup_extents_to_file");
5848 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5849 torture_assert_ntstatus_equal(tctx, status, NT_STATUS_FILE_CLOSED,
5850 "FSCTL_DUP_EXTENTS_TO_FILE");
5852 ok = check_pattern(tctx, tree, tmp_ctx, src_h, 0, 32768, 0);
5854 torture_fail(tctx, "inconsistent file data");
5856 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
5858 torture_fail(tctx, "inconsistent file data");
5861 /* reinstate dest, add bogus src file handle */
5862 ioctl.smb2.in.file.handle = dest_h;
5863 smb2_push_handle(dup_ext_buf.source_fid, &bogus_h);
5865 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5867 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5868 torture_assert_ndr_success(tctx, ndr_ret,
5869 "ndr_push_fsctl_dup_extents_to_file");
5871 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5872 torture_assert_ntstatus_equal(tctx, status, NT_STATUS_INVALID_HANDLE,
5873 "FSCTL_DUP_EXTENTS_TO_FILE");
5875 ok = check_pattern(tctx, tree, tmp_ctx, src_h, 0, 32768, 0);
5877 torture_fail(tctx, "inconsistent file data");
5879 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
5881 torture_fail(tctx, "inconsistent file data");
5884 smb2_util_close(tree, src_h);
5885 smb2_util_close(tree, dest_h);
5886 talloc_free(tmp_ctx);
5890 static bool test_ioctl_dup_extents_src_lck(struct torture_context *tctx,
5891 struct smb2_tree *tree)
5893 struct smb2_handle src_h;
5894 struct smb2_handle src_h2;
5895 struct smb2_handle dest_h;
5897 union smb_ioctl ioctl;
5898 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5899 struct fsctl_dup_extents_to_file dup_ext_buf;
5900 enum ndr_err_code ndr_ret;
5902 struct smb2_lock lck;
5903 struct smb2_lock_element el[1];
5905 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5906 &src_h, 32768, /* fill 32768 byte src file */
5907 SEC_RIGHTS_FILE_ALL,
5909 SEC_RIGHTS_FILE_ALL,
5913 torture_fail(tctx, "setup dup extents error");
5916 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5917 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
5918 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5920 smb2_util_close(tree, src_h);
5921 smb2_util_close(tree, dest_h);
5922 talloc_free(tmp_ctx);
5923 torture_skip(tctx, "block refcounting not supported\n");
5926 /* dest pattern is different to src */
5927 ok = write_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 32768);
5928 torture_assert(tctx, ok, "write pattern");
5930 /* setup dup ext req, values used for locking */
5931 dup_ext_buf.source_off = 0;
5932 dup_ext_buf.target_off = 0;
5933 dup_ext_buf.byte_count = 32768;
5935 /* open and lock the dup extents src file */
5936 status = torture_smb2_testfile(tree, FNAME, &src_h2);
5937 torture_assert_ntstatus_ok(tctx, status, "2nd src open");
5939 lck.in.lock_count = 0x0001;
5940 lck.in.lock_sequence = 0x00000000;
5941 lck.in.file.handle = src_h2;
5943 el[0].offset = dup_ext_buf.source_off;
5944 el[0].length = dup_ext_buf.byte_count;
5946 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
5948 status = smb2_lock(tree, &lck);
5949 torture_assert_ntstatus_ok(tctx, status, "lock");
5951 status = smb2_util_write(tree, src_h,
5952 "conflicted", 0, sizeof("conflicted"));
5953 torture_assert_ntstatus_equal(tctx, status,
5954 NT_STATUS_FILE_LOCK_CONFLICT, "file write");
5956 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5958 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5959 torture_assert_ndr_success(tctx, ndr_ret,
5960 "ndr_push_fsctl_dup_extents_to_file");
5963 * In contrast to copy-chunk, dup extents doesn't cause a lock conflict
5966 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5967 torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
5969 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
5971 torture_fail(tctx, "inconsistent file data");
5974 lck.in.lock_count = 0x0001;
5975 lck.in.lock_sequence = 0x00000001;
5976 lck.in.file.handle = src_h2;
5978 el[0].offset = dup_ext_buf.source_off;
5979 el[0].length = dup_ext_buf.byte_count;
5981 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
5982 status = smb2_lock(tree, &lck);
5983 torture_assert_ntstatus_ok(tctx, status, "unlock");
5985 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5986 torture_assert_ntstatus_ok(tctx, status,
5987 "FSCTL_DUP_EXTENTS_TO_FILE unlocked");
5989 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
5991 torture_fail(tctx, "inconsistent file data");
5994 smb2_util_close(tree, src_h2);
5995 smb2_util_close(tree, src_h);
5996 smb2_util_close(tree, dest_h);
5997 talloc_free(tmp_ctx);
6001 static bool test_ioctl_dup_extents_dest_lck(struct torture_context *tctx,
6002 struct smb2_tree *tree)
6004 struct smb2_handle src_h;
6005 struct smb2_handle dest_h;
6006 struct smb2_handle dest_h2;
6008 union smb_ioctl ioctl;
6009 TALLOC_CTX *tmp_ctx = talloc_new(tree);
6010 struct fsctl_dup_extents_to_file dup_ext_buf;
6011 enum ndr_err_code ndr_ret;
6013 struct smb2_lock lck;
6014 struct smb2_lock_element el[1];
6016 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
6017 &src_h, 32768, /* fill 32768 byte src file */
6018 SEC_RIGHTS_FILE_ALL,
6020 SEC_RIGHTS_FILE_ALL,
6024 torture_fail(tctx, "setup dup extents error");
6027 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
6028 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
6029 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
6031 smb2_util_close(tree, src_h);
6032 smb2_util_close(tree, dest_h);
6033 talloc_free(tmp_ctx);
6034 torture_skip(tctx, "block refcounting not supported\n");
6037 /* dest pattern is different to src */
6038 ok = write_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 32768);
6039 torture_assert(tctx, ok, "write pattern");
6041 /* setup dup ext req, values used for locking */
6042 dup_ext_buf.source_off = 0;
6043 dup_ext_buf.target_off = 0;
6044 dup_ext_buf.byte_count = 32768;
6046 /* open and lock the dup extents dest file */
6047 status = torture_smb2_testfile(tree, FNAME2, &dest_h2);
6048 torture_assert_ntstatus_ok(tctx, status, "2nd src open");
6050 lck.in.lock_count = 0x0001;
6051 lck.in.lock_sequence = 0x00000000;
6052 lck.in.file.handle = dest_h2;
6054 el[0].offset = dup_ext_buf.source_off;
6055 el[0].length = dup_ext_buf.byte_count;
6057 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
6059 status = smb2_lock(tree, &lck);
6060 torture_assert_ntstatus_ok(tctx, status, "lock");
6062 status = smb2_util_write(tree, dest_h,
6063 "conflicted", 0, sizeof("conflicted"));
6064 torture_assert_ntstatus_equal(tctx, status,
6065 NT_STATUS_FILE_LOCK_CONFLICT, "file write");
6067 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6069 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6070 torture_assert_ndr_success(tctx, ndr_ret,
6071 "ndr_push_fsctl_dup_extents_to_file");
6074 * In contrast to copy-chunk, dup extents doesn't cause a lock conflict
6077 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6078 torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
6080 lck.in.lock_count = 0x0001;
6081 lck.in.lock_sequence = 0x00000001;
6082 lck.in.file.handle = dest_h2;
6084 el[0].offset = dup_ext_buf.source_off;
6085 el[0].length = dup_ext_buf.byte_count;
6087 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
6088 status = smb2_lock(tree, &lck);
6089 torture_assert_ntstatus_ok(tctx, status, "unlock");
6091 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6092 torture_assert_ntstatus_ok(tctx, status,
6093 "FSCTL_DUP_EXTENTS_TO_FILE unlocked");
6095 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
6097 torture_fail(tctx, "inconsistent file data");
6100 smb2_util_close(tree, src_h);
6101 smb2_util_close(tree, dest_h);
6102 smb2_util_close(tree, dest_h2);
6103 talloc_free(tmp_ctx);
6108 * testing of SMB2 ioctls
6110 struct torture_suite *torture_smb2_ioctl_init(void)
6112 struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "ioctl");
6114 torture_suite_add_1smb2_test(suite, "shadow_copy",
6115 test_ioctl_get_shadow_copy);
6116 torture_suite_add_1smb2_test(suite, "req_resume_key",
6117 test_ioctl_req_resume_key);
6118 torture_suite_add_1smb2_test(suite, "copy_chunk_simple",
6119 test_ioctl_copy_chunk_simple);
6120 torture_suite_add_1smb2_test(suite, "copy_chunk_multi",
6121 test_ioctl_copy_chunk_multi);
6122 torture_suite_add_1smb2_test(suite, "copy_chunk_tiny",
6123 test_ioctl_copy_chunk_tiny);
6124 torture_suite_add_1smb2_test(suite, "copy_chunk_overwrite",
6125 test_ioctl_copy_chunk_over);
6126 torture_suite_add_1smb2_test(suite, "copy_chunk_append",
6127 test_ioctl_copy_chunk_append);
6128 torture_suite_add_1smb2_test(suite, "copy_chunk_limits",
6129 test_ioctl_copy_chunk_limits);
6130 torture_suite_add_1smb2_test(suite, "copy_chunk_src_lock",
6131 test_ioctl_copy_chunk_src_lck);
6132 torture_suite_add_1smb2_test(suite, "copy_chunk_dest_lock",
6133 test_ioctl_copy_chunk_dest_lck);
6134 torture_suite_add_1smb2_test(suite, "copy_chunk_bad_key",
6135 test_ioctl_copy_chunk_bad_key);
6136 torture_suite_add_1smb2_test(suite, "copy_chunk_src_is_dest",
6137 test_ioctl_copy_chunk_src_is_dest);
6138 torture_suite_add_1smb2_test(suite, "copy_chunk_src_is_dest_overlap",
6139 test_ioctl_copy_chunk_src_is_dest_overlap);
6140 torture_suite_add_1smb2_test(suite, "copy_chunk_bad_access",
6141 test_ioctl_copy_chunk_bad_access);
6142 torture_suite_add_1smb2_test(suite, "copy_chunk_write_access",
6143 test_ioctl_copy_chunk_write_access);
6144 torture_suite_add_1smb2_test(suite, "copy_chunk_src_exceed",
6145 test_ioctl_copy_chunk_src_exceed);
6146 torture_suite_add_1smb2_test(suite, "copy_chunk_src_exceed_multi",
6147 test_ioctl_copy_chunk_src_exceed_multi);
6148 torture_suite_add_1smb2_test(suite, "copy_chunk_sparse_dest",
6149 test_ioctl_copy_chunk_sparse_dest);
6150 torture_suite_add_1smb2_test(suite, "copy_chunk_max_output_sz",
6151 test_ioctl_copy_chunk_max_output_sz);
6152 torture_suite_add_1smb2_test(suite, "copy_chunk_zero_length",
6153 test_ioctl_copy_chunk_zero_length);
6154 torture_suite_add_1smb2_test(suite, "compress_file_flag",
6155 test_ioctl_compress_file_flag);
6156 torture_suite_add_1smb2_test(suite, "compress_dir_inherit",
6157 test_ioctl_compress_dir_inherit);
6158 torture_suite_add_1smb2_test(suite, "compress_invalid_format",
6159 test_ioctl_compress_invalid_format);
6160 torture_suite_add_1smb2_test(suite, "compress_invalid_buf",
6161 test_ioctl_compress_invalid_buf);
6162 torture_suite_add_1smb2_test(suite, "compress_query_file_attr",
6163 test_ioctl_compress_query_file_attr);
6164 torture_suite_add_1smb2_test(suite, "compress_create_with_attr",
6165 test_ioctl_compress_create_with_attr);
6166 torture_suite_add_1smb2_test(suite, "compress_inherit_disable",
6167 test_ioctl_compress_inherit_disable);
6168 torture_suite_add_1smb2_test(suite, "compress_set_file_attr",
6169 test_ioctl_compress_set_file_attr);
6170 torture_suite_add_1smb2_test(suite, "compress_perms",
6171 test_ioctl_compress_perms);
6172 torture_suite_add_1smb2_test(suite, "network_interface_info",
6173 test_ioctl_network_interface_info);
6174 torture_suite_add_1smb2_test(suite, "sparse_file_flag",
6175 test_ioctl_sparse_file_flag);
6176 torture_suite_add_1smb2_test(suite, "sparse_file_attr",
6177 test_ioctl_sparse_file_attr);
6178 torture_suite_add_1smb2_test(suite, "sparse_dir_flag",
6179 test_ioctl_sparse_dir_flag);
6180 torture_suite_add_1smb2_test(suite, "sparse_set_nobuf",
6181 test_ioctl_sparse_set_nobuf);
6182 torture_suite_add_1smb2_test(suite, "sparse_set_oversize",
6183 test_ioctl_sparse_set_oversize);
6184 torture_suite_add_1smb2_test(suite, "sparse_qar",
6185 test_ioctl_sparse_qar);
6186 torture_suite_add_1smb2_test(suite, "sparse_qar_malformed",
6187 test_ioctl_sparse_qar_malformed);
6188 torture_suite_add_1smb2_test(suite, "sparse_punch",
6189 test_ioctl_sparse_punch);
6190 torture_suite_add_1smb2_test(suite, "sparse_hole_dealloc",
6191 test_ioctl_sparse_hole_dealloc);
6192 torture_suite_add_1smb2_test(suite, "sparse_compressed",
6193 test_ioctl_sparse_compressed);
6194 torture_suite_add_1smb2_test(suite, "sparse_copy_chunk",
6195 test_ioctl_sparse_copy_chunk);
6196 torture_suite_add_1smb2_test(suite, "sparse_punch_invalid",
6197 test_ioctl_sparse_punch_invalid);
6198 torture_suite_add_1smb2_test(suite, "sparse_perms",
6199 test_ioctl_sparse_perms);
6200 torture_suite_add_1smb2_test(suite, "sparse_lock",
6201 test_ioctl_sparse_lck);
6202 torture_suite_add_1smb2_test(suite, "sparse_qar_ob1",
6203 test_ioctl_sparse_qar_ob1);
6204 torture_suite_add_1smb2_test(suite, "sparse_qar_multi",
6205 test_ioctl_sparse_qar_multi);
6206 torture_suite_add_1smb2_test(suite, "sparse_qar_overflow",
6207 test_ioctl_sparse_qar_overflow);
6208 torture_suite_add_1smb2_test(suite, "trim_simple",
6209 test_ioctl_trim_simple);
6210 torture_suite_add_1smb2_test(suite, "dup_extents_simple",
6211 test_ioctl_dup_extents_simple);
6212 torture_suite_add_1smb2_test(suite, "dup_extents_len_beyond_dest",
6213 test_ioctl_dup_extents_len_beyond_dest);
6214 torture_suite_add_1smb2_test(suite, "dup_extents_len_beyond_src",
6215 test_ioctl_dup_extents_len_beyond_src);
6216 torture_suite_add_1smb2_test(suite, "dup_extents_len_zero",
6217 test_ioctl_dup_extents_len_zero);
6218 torture_suite_add_1smb2_test(suite, "dup_extents_sparse_src",
6219 test_ioctl_dup_extents_sparse_src);
6220 torture_suite_add_1smb2_test(suite, "dup_extents_sparse_dest",
6221 test_ioctl_dup_extents_sparse_dest);
6222 torture_suite_add_1smb2_test(suite, "dup_extents_sparse_both",
6223 test_ioctl_dup_extents_sparse_both);
6224 torture_suite_add_1smb2_test(suite, "dup_extents_src_is_dest",
6225 test_ioctl_dup_extents_src_is_dest);
6226 torture_suite_add_1smb2_test(suite, "dup_extents_src_is_dest_overlap",
6227 test_ioctl_dup_extents_src_is_dest_overlap);
6228 torture_suite_add_1smb2_test(suite, "dup_extents_compressed_src",
6229 test_ioctl_dup_extents_compressed_src);
6230 torture_suite_add_1smb2_test(suite, "dup_extents_compressed_dest",
6231 test_ioctl_dup_extents_compressed_dest);
6232 torture_suite_add_1smb2_test(suite, "dup_extents_bad_handle",
6233 test_ioctl_dup_extents_bad_handle);
6234 torture_suite_add_1smb2_test(suite, "dup_extents_src_lock",
6235 test_ioctl_dup_extents_src_lck);
6236 torture_suite_add_1smb2_test(suite, "dup_extents_dest_lock",
6237 test_ioctl_dup_extents_dest_lck);
6239 suite->description = talloc_strdup(suite, "SMB2-IOCTL tests");