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);
2546 static bool test_ioctl_compress_notsup_get(struct torture_context *torture,
2547 struct smb2_tree *tree)
2549 struct smb2_handle fh;
2551 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2553 uint16_t compression_fmt;
2555 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2556 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2557 FILE_ATTRIBUTE_NORMAL);
2558 torture_assert(torture, ok, "setup compression file");
2560 /* skip if the server DOES support compression */
2561 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
2563 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2565 smb2_util_close(tree, fh);
2566 torture_skip(torture, "FS compression supported\n");
2570 * Despite not supporting compression, we should get a successful
2571 * response indicating that the file is uncompressed - like WS2016.
2573 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2575 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2577 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2578 "initial compression state not NONE");
2580 smb2_util_close(tree, fh);
2581 talloc_free(tmp_ctx);
2585 static bool test_ioctl_compress_notsup_set(struct torture_context *torture,
2586 struct smb2_tree *tree)
2588 struct smb2_handle fh;
2590 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2593 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2594 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2595 FILE_ATTRIBUTE_NORMAL);
2596 torture_assert(torture, ok, "setup compression file");
2598 /* skip if the server DOES support compression */
2599 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
2601 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2603 smb2_util_close(tree, fh);
2604 torture_skip(torture, "FS compression supported\n");
2607 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
2608 COMPRESSION_FORMAT_DEFAULT);
2609 torture_assert_ntstatus_equal(torture, status,
2610 NT_STATUS_NOT_SUPPORTED,
2611 "FSCTL_GET_COMPRESSION");
2613 smb2_util_close(tree, fh);
2614 talloc_free(tmp_ctx);
2619 basic testing of the SMB2 FSCTL_QUERY_NETWORK_INTERFACE_INFO ioctl
2621 static bool test_ioctl_network_interface_info(struct torture_context *torture,
2622 struct smb2_tree *tree)
2624 union smb_ioctl ioctl;
2625 struct smb2_handle fh;
2627 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2628 struct fsctl_net_iface_info net_iface;
2629 enum ndr_err_code ndr_ret;
2632 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
2633 if (!(caps & SMB2_CAP_MULTI_CHANNEL)) {
2634 torture_skip(torture, "server doesn't support SMB2_CAP_MULTI_CHANNEL\n");
2638 ioctl.smb2.level = RAW_IOCTL_SMB2;
2639 fh.data[0] = UINT64_MAX;
2640 fh.data[1] = UINT64_MAX;
2641 ioctl.smb2.in.file.handle = fh;
2642 ioctl.smb2.in.function = FSCTL_QUERY_NETWORK_INTERFACE_INFO;
2643 ioctl.smb2.in.max_response_size = 0x10000; /* Windows client sets this to 64KiB */
2644 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2646 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2647 torture_assert_ntstatus_ok(torture, status, "FSCTL_QUERY_NETWORK_INTERFACE_INFO");
2649 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, &net_iface,
2650 (ndr_pull_flags_fn_t)ndr_pull_fsctl_net_iface_info);
2651 torture_assert_ndr_success(torture, ndr_ret,
2652 "ndr_pull_fsctl_net_iface_info");
2654 ndr_print_debug((ndr_print_fn_t)ndr_print_fsctl_net_iface_info,
2655 "Network Interface Info", &net_iface);
2657 talloc_free(tmp_ctx);
2662 * Check whether all @fs_support_flags are set in the server's
2663 * RAW_QFS_ATTRIBUTE_INFORMATION FileSystemAttributes response.
2665 static NTSTATUS test_ioctl_fs_supported(struct torture_context *torture,
2666 struct smb2_tree *tree,
2667 TALLOC_CTX *mem_ctx,
2668 struct smb2_handle *fh,
2669 uint64_t fs_support_flags,
2673 union smb_fsinfo info;
2676 info.generic.level = RAW_QFS_ATTRIBUTE_INFORMATION;
2677 info.generic.handle = *fh;
2678 status = smb2_getinfo_fs(tree, tree, &info);
2679 if (!NT_STATUS_IS_OK(status)) {
2683 if ((info.attribute_info.out.fs_attr & fs_support_flags)
2684 == fs_support_flags) {
2689 return NT_STATUS_OK;
2692 static NTSTATUS test_ioctl_sparse_req(struct torture_context *torture,
2693 TALLOC_CTX *mem_ctx,
2694 struct smb2_tree *tree,
2695 struct smb2_handle fh,
2698 union smb_ioctl ioctl;
2703 ioctl.smb2.level = RAW_IOCTL_SMB2;
2704 ioctl.smb2.in.file.handle = fh;
2705 ioctl.smb2.in.function = FSCTL_SET_SPARSE;
2706 ioctl.smb2.in.max_response_size = 0;
2707 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2708 set_sparse = (set ? 0xFF : 0x0);
2709 ioctl.smb2.in.out.data = &set_sparse;
2710 ioctl.smb2.in.out.length = sizeof(set_sparse);
2712 status = smb2_ioctl(tree, mem_ctx, &ioctl.smb2);
2716 static NTSTATUS test_sparse_get(struct torture_context *torture,
2717 TALLOC_CTX *mem_ctx,
2718 struct smb2_tree *tree,
2719 struct smb2_handle fh,
2722 union smb_fileinfo io;
2726 io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2727 io.generic.in.file.handle = fh;
2728 status = smb2_getinfo_file(tree, mem_ctx, &io);
2729 if (!NT_STATUS_IS_OK(status)) {
2732 *_is_sparse = !!(io.basic_info.out.attrib & FILE_ATTRIBUTE_SPARSE);
2737 static bool test_ioctl_sparse_file_flag(struct torture_context *torture,
2738 struct smb2_tree *tree)
2740 struct smb2_handle fh;
2741 union smb_fileinfo io;
2743 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2747 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2748 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2749 FILE_ATTRIBUTE_NORMAL);
2750 torture_assert(torture, ok, "setup file");
2752 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
2753 FILE_SUPPORTS_SPARSE_FILES, &ok);
2754 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2756 smb2_util_close(tree, fh);
2757 torture_skip(torture, "Sparse files not supported\n");
2761 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
2762 io.generic.in.file.handle = fh;
2763 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2764 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2766 torture_assert(torture,
2767 ((io.all_info2.out.attrib & FILE_ATTRIBUTE_SPARSE) == 0),
2768 "sparse attr before set");
2770 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
2771 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2773 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2774 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2775 torture_assert(torture, is_sparse, "no sparse attr after set");
2777 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false);
2778 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2780 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2781 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2782 torture_assert(torture, !is_sparse, "sparse attr after unset");
2784 smb2_util_close(tree, fh);
2785 talloc_free(tmp_ctx);
2789 static bool test_ioctl_sparse_file_attr(struct torture_context *torture,
2790 struct smb2_tree *tree)
2792 struct smb2_handle fh;
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 | FILE_ATTRIBUTE_SPARSE));
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 on open");
2815 smb2_util_close(tree, fh);
2816 talloc_free(tmp_ctx);
2820 static bool test_ioctl_sparse_dir_flag(struct torture_context *torture,
2821 struct smb2_tree *tree)
2823 struct smb2_handle dirh;
2825 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2828 smb2_deltree(tree, DNAME);
2829 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2830 DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL,
2831 FILE_ATTRIBUTE_DIRECTORY);
2832 torture_assert(torture, ok, "setup sparse directory");
2834 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &dirh,
2835 FILE_SUPPORTS_SPARSE_FILES, &ok);
2836 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2838 smb2_util_close(tree, dirh);
2839 smb2_deltree(tree, DNAME);
2840 torture_skip(torture, "Sparse files not supported\n");
2843 /* set sparse dir should fail, check for 2k12 & 2k8 response */
2844 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, dirh, true);
2845 torture_assert_ntstatus_equal(torture, status,
2846 NT_STATUS_INVALID_PARAMETER,
2847 "dir FSCTL_SET_SPARSE status");
2849 smb2_util_close(tree, dirh);
2850 smb2_deltree(tree, DNAME);
2851 talloc_free(tmp_ctx);
2856 * FSCTL_SET_SPARSE can be sent with (already tested) or without a SetSparse
2857 * buffer to indicate whether the flag should be set or cleared. When sent
2858 * without a buffer, it must be handled as if SetSparse=TRUE.
2860 static bool test_ioctl_sparse_set_nobuf(struct torture_context *torture,
2861 struct smb2_tree *tree)
2863 struct smb2_handle fh;
2864 union smb_ioctl ioctl;
2866 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2870 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2871 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2872 FILE_ATTRIBUTE_NORMAL);
2873 torture_assert(torture, ok, "setup file");
2875 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
2876 FILE_SUPPORTS_SPARSE_FILES, &ok);
2877 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2879 smb2_util_close(tree, fh);
2880 torture_skip(torture, "Sparse files not supported\n");
2883 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2884 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2885 torture_assert(torture, !is_sparse, "sparse attr before set");
2888 ioctl.smb2.level = RAW_IOCTL_SMB2;
2889 ioctl.smb2.in.file.handle = fh;
2890 ioctl.smb2.in.function = FSCTL_SET_SPARSE;
2891 ioctl.smb2.in.max_response_size = 0;
2892 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2893 /* ioctl.smb2.in.out is zeroed, no SetSparse buffer */
2895 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2896 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2898 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2899 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2900 torture_assert(torture, is_sparse, "no sparse attr after set");
2902 /* second non-SetSparse request shouldn't toggle sparse */
2904 ioctl.smb2.level = RAW_IOCTL_SMB2;
2905 ioctl.smb2.in.file.handle = fh;
2906 ioctl.smb2.in.function = FSCTL_SET_SPARSE;
2907 ioctl.smb2.in.max_response_size = 0;
2908 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2910 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2911 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2913 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2914 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2915 torture_assert(torture, is_sparse, "no sparse attr after 2nd set");
2917 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false);
2918 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2920 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2921 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2922 torture_assert(torture, !is_sparse, "sparse attr after unset");
2924 smb2_util_close(tree, fh);
2925 talloc_free(tmp_ctx);
2929 static bool test_ioctl_sparse_set_oversize(struct torture_context *torture,
2930 struct smb2_tree *tree)
2932 struct smb2_handle fh;
2933 union smb_ioctl ioctl;
2935 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2940 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2941 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2942 FILE_ATTRIBUTE_NORMAL);
2943 torture_assert(torture, ok, "setup file");
2945 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
2946 FILE_SUPPORTS_SPARSE_FILES, &ok);
2947 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2949 smb2_util_close(tree, fh);
2950 torture_skip(torture, "Sparse files not supported\n");
2953 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2954 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2955 torture_assert(torture, !is_sparse, "sparse attr before set");
2958 ioctl.smb2.level = RAW_IOCTL_SMB2;
2959 ioctl.smb2.in.file.handle = fh;
2960 ioctl.smb2.in.function = FSCTL_SET_SPARSE;
2961 ioctl.smb2.in.max_response_size = 0;
2962 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2965 * Attach a request buffer larger than FILE_SET_SPARSE_BUFFER
2966 * Windows still successfully processes the request.
2969 buf[0] = 0xFF; /* attempt to set sparse */
2970 ioctl.smb2.in.out.data = buf;
2971 ioctl.smb2.in.out.length = ARRAY_SIZE(buf);
2973 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2974 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2976 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2977 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2978 torture_assert(torture, is_sparse, "no sparse attr after set");
2981 ioctl.smb2.level = RAW_IOCTL_SMB2;
2982 ioctl.smb2.in.file.handle = fh;
2983 ioctl.smb2.in.function = FSCTL_SET_SPARSE;
2984 ioctl.smb2.in.max_response_size = 0;
2985 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2987 ZERO_ARRAY(buf); /* clear sparse */
2988 ioctl.smb2.in.out.data = buf;
2989 ioctl.smb2.in.out.length = ARRAY_SIZE(buf);
2991 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2992 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2994 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2995 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2996 torture_assert(torture, !is_sparse, "sparse attr after clear");
2998 smb2_util_close(tree, fh);
2999 talloc_free(tmp_ctx);
3003 static NTSTATUS test_ioctl_qar_req(struct torture_context *torture,
3004 TALLOC_CTX *mem_ctx,
3005 struct smb2_tree *tree,
3006 struct smb2_handle fh,
3009 struct file_alloced_range_buf **_rsp,
3010 uint64_t *_rsp_count)
3012 union smb_ioctl ioctl;
3014 enum ndr_err_code ndr_ret;
3015 struct file_alloced_range_buf far_buf;
3016 struct file_alloced_range_buf *far_rsp = NULL;
3017 uint64_t far_count = 0;
3019 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
3020 if (tmp_ctx == NULL) {
3021 return NT_STATUS_NO_MEMORY;
3025 ioctl.smb2.level = RAW_IOCTL_SMB2;
3026 ioctl.smb2.in.file.handle = fh;
3027 ioctl.smb2.in.function = FSCTL_QUERY_ALLOCATED_RANGES;
3028 ioctl.smb2.in.max_response_size = 1024;
3029 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3031 far_buf.file_off = req_off;
3032 far_buf.len = req_len;
3034 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
3036 (ndr_push_flags_fn_t)ndr_push_file_alloced_range_buf);
3037 if (ndr_ret != NDR_ERR_SUCCESS) {
3038 status = NT_STATUS_UNSUCCESSFUL;
3042 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3043 if (!NT_STATUS_IS_OK(status)) {
3047 if (ioctl.smb2.out.out.length == 0) {
3051 if ((ioctl.smb2.out.out.length % sizeof(far_buf)) != 0) {
3052 torture_comment(torture, "invalid qry_alloced rsp len: %zd:",
3053 ioctl.smb2.out.out.length);
3054 status = NT_STATUS_INVALID_VIEW_SIZE;
3058 far_count = (ioctl.smb2.out.out.length / sizeof(far_buf));
3059 far_rsp = talloc_array(mem_ctx, struct file_alloced_range_buf,
3061 if (far_rsp == NULL) {
3062 status = NT_STATUS_NO_MEMORY;
3066 for (i = 0; i < far_count; i++) {
3067 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
3069 (ndr_pull_flags_fn_t)ndr_pull_file_alloced_range_buf);
3070 if (ndr_ret != NDR_ERR_SUCCESS) {
3071 status = NT_STATUS_UNSUCCESSFUL;
3074 /* move to next buffer */
3075 ioctl.smb2.out.out.data += sizeof(far_buf);
3076 ioctl.smb2.out.out.length -= sizeof(far_buf);
3081 *_rsp_count = far_count;
3082 status = NT_STATUS_OK;
3084 talloc_free(tmp_ctx);
3088 static bool test_ioctl_sparse_qar(struct torture_context *torture,
3089 struct smb2_tree *tree)
3091 struct smb2_handle fh;
3093 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3096 struct file_alloced_range_buf *far_rsp = NULL;
3097 uint64_t far_count = 0;
3099 /* zero length file, shouldn't have any ranges */
3100 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3101 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3102 FILE_ATTRIBUTE_NORMAL);
3103 torture_assert(torture, ok, "setup file");
3105 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3106 FILE_SUPPORTS_SPARSE_FILES, &ok);
3107 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3109 smb2_util_close(tree, fh);
3110 torture_skip(torture, "Sparse files not supported\n");
3113 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3114 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3115 torture_assert(torture, !is_sparse, "sparse attr before set");
3117 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3122 torture_assert_ntstatus_ok(torture, status,
3123 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3124 torture_assert_u64_equal(torture, far_count, 0,
3125 "unexpected response len");
3127 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3132 torture_assert_ntstatus_ok(torture, status,
3133 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3134 torture_assert_u64_equal(torture, far_count, 0,
3135 "unexpected response len");
3137 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
3138 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3140 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3141 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3142 torture_assert(torture, is_sparse, "no sparse attr after set");
3144 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3149 torture_assert_ntstatus_ok(torture, status,
3150 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3151 torture_assert_u64_equal(torture, far_count, 0,
3152 "unexpected response len");
3154 /* write into the (now) sparse file at 4k offset */
3155 ok = write_pattern(torture, tree, tmp_ctx, fh,
3158 4096); /* pattern offset */
3159 torture_assert(torture, ok, "write pattern");
3162 * Query range before write off. Whether it's allocated or not is FS
3163 * dependent. NTFS deallocates chunks in 64K increments, but others
3164 * (e.g. XFS, Btrfs, etc.) may deallocate 4K chunks.
3166 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3171 torture_assert_ntstatus_ok(torture, status,
3172 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3173 if (far_count == 0) {
3174 torture_comment(torture, "FS deallocated 4K chunk\n");
3176 /* expect fully allocated */
3177 torture_assert_u64_equal(torture, far_count, 1,
3178 "unexpected response len");
3179 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0, "far offset");
3180 torture_assert_u64_equal(torture, far_rsp[0].len, 4096, "far len");
3184 * Query range before and past write, it should be allocated up to the
3187 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3192 torture_assert_ntstatus_ok(torture, status,
3193 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3194 torture_assert_u64_equal(torture, far_count, 1,
3195 "unexpected response len");
3197 if (far_rsp[0].file_off == 4096) {
3198 /* 4K chunk unallocated */
3199 torture_assert_u64_equal(torture, far_rsp[0].file_off, 4096, "far offset");
3200 torture_assert_u64_equal(torture, far_rsp[0].len, 1024, "far len");
3202 /* expect fully allocated */
3203 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0, "far offset");
3204 torture_assert_u64_equal(torture, far_rsp[0].len, 5120, "far len");
3207 smb2_util_close(tree, fh);
3208 talloc_free(tmp_ctx);
3212 static bool test_ioctl_sparse_qar_malformed(struct torture_context *torture,
3213 struct smb2_tree *tree)
3215 struct smb2_handle fh;
3216 union smb_ioctl ioctl;
3217 struct file_alloced_range_buf far_buf;
3219 enum ndr_err_code ndr_ret;
3220 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3224 /* zero length file, shouldn't have any ranges */
3225 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3226 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3227 FILE_ATTRIBUTE_NORMAL);
3228 torture_assert(torture, ok, "setup file");
3230 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3231 FILE_SUPPORTS_SPARSE_FILES, &ok);
3232 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3234 smb2_util_close(tree, fh);
3235 torture_skip(torture, "Sparse files not supported\n");
3238 /* no allocated ranges, no space for range response, should pass */
3240 ioctl.smb2.level = RAW_IOCTL_SMB2;
3241 ioctl.smb2.in.file.handle = fh;
3242 ioctl.smb2.in.function = FSCTL_QUERY_ALLOCATED_RANGES;
3243 ioctl.smb2.in.max_response_size = 0;
3244 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3246 far_buf.file_off = 0;
3248 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
3250 (ndr_push_flags_fn_t)ndr_push_file_alloced_range_buf);
3251 torture_assert_ndr_success(torture, ndr_ret, "push far ndr buf");
3253 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3254 torture_assert_ntstatus_ok(torture, status, "FSCTL_QUERY_ALLOCATED_RANGES");
3256 /* write into the file at 4k offset */
3257 ok = write_pattern(torture, tree, tmp_ctx, fh,
3260 0); /* pattern offset */
3261 torture_assert(torture, ok, "write pattern");
3263 /* allocated range, no space for range response, should fail */
3264 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3265 torture_assert_ntstatus_equal(torture, status,
3266 NT_STATUS_BUFFER_TOO_SMALL, "qar no space");
3268 /* oversize (2x) file_alloced_range_buf in request, should pass */
3269 ioctl.smb2.in.max_response_size = 1024;
3270 old_len = ioctl.smb2.in.out.length;
3271 ok = data_blob_realloc(tmp_ctx, &ioctl.smb2.in.out,
3272 (ioctl.smb2.in.out.length * 2));
3273 torture_assert(torture, ok, "2x data buffer");
3274 memcpy(ioctl.smb2.in.out.data + old_len, ioctl.smb2.in.out.data,
3276 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3277 torture_assert_ntstatus_ok(torture, status, "qar too big");
3279 /* no file_alloced_range_buf in request, should fail */
3280 data_blob_free(&ioctl.smb2.in.out);
3281 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3282 torture_assert_ntstatus_equal(torture, status,
3283 NT_STATUS_INVALID_PARAMETER, "qar empty");
3289 * 2.3.57 FSCTL_SET_ZERO_DATA Request
3291 * How an implementation zeros data within a file is implementation-dependent.
3292 * A file system MAY choose to deallocate regions of disk space that have been
3295 * ... NTFS might deallocate disk space in the file if the file is stored on an
3296 * NTFS volume, and the file is sparse or compressed. It will free any allocated
3297 * space in chunks of 64 kilobytes that begin at an offset that is a multiple of
3298 * 64 kilobytes. Other bytes in the file (prior to the first freed 64-kilobyte
3299 * chunk and after the last freed 64-kilobyte chunk) will be zeroed but not
3302 static NTSTATUS test_ioctl_zdata_req(struct torture_context *torture,
3303 TALLOC_CTX *mem_ctx,
3304 struct smb2_tree *tree,
3305 struct smb2_handle fh,
3307 int64_t beyond_final_zero)
3309 union smb_ioctl ioctl;
3311 enum ndr_err_code ndr_ret;
3312 struct file_zero_data_info zdata_info;
3313 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
3314 if (tmp_ctx == NULL) {
3315 return NT_STATUS_NO_MEMORY;
3319 ioctl.smb2.level = RAW_IOCTL_SMB2;
3320 ioctl.smb2.in.file.handle = fh;
3321 ioctl.smb2.in.function = FSCTL_SET_ZERO_DATA;
3322 ioctl.smb2.in.max_response_size = 0;
3323 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3325 zdata_info.file_off = off;
3326 zdata_info.beyond_final_zero = beyond_final_zero;
3328 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
3330 (ndr_push_flags_fn_t)ndr_push_file_zero_data_info);
3331 if (ndr_ret != NDR_ERR_SUCCESS) {
3332 status = NT_STATUS_UNSUCCESSFUL;
3336 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3337 if (!NT_STATUS_IS_OK(status)) {
3341 status = NT_STATUS_OK;
3343 talloc_free(tmp_ctx);
3347 static bool test_ioctl_sparse_punch(struct torture_context *torture,
3348 struct smb2_tree *tree)
3350 struct smb2_handle fh;
3352 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3355 struct file_alloced_range_buf *far_rsp = NULL;
3356 uint64_t far_count = 0;
3358 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3359 FNAME, &fh, 4096, SEC_RIGHTS_FILE_ALL,
3360 FILE_ATTRIBUTE_NORMAL);
3361 torture_assert(torture, ok, "setup file");
3363 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3364 FILE_SUPPORTS_SPARSE_FILES, &ok);
3365 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3367 smb2_util_close(tree, fh);
3368 torture_skip(torture, "Sparse files not supported\n");
3371 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3372 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3373 torture_assert(torture, !is_sparse, "sparse attr before set");
3375 /* zero (hole-punch) the data, without sparse flag */
3376 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3378 4096); /* beyond_final_zero */
3379 torture_assert_ntstatus_ok(torture, status, "zero_data");
3381 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3386 torture_assert_ntstatus_ok(torture, status,
3387 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3388 torture_assert_u64_equal(torture, far_count, 1,
3389 "unexpected response len");
3391 /* expect fully allocated */
3392 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3393 "unexpected far off");
3394 torture_assert_u64_equal(torture, far_rsp[0].len, 4096,
3395 "unexpected far len");
3396 /* check that the data is now zeroed */
3397 ok = check_zero(torture, tree, tmp_ctx, fh, 0, 4096);
3398 torture_assert(torture, ok, "non-sparse zeroed range");
3401 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
3402 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3404 /* still fully allocated on NTFS, see note below for Samba */
3405 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3410 torture_assert_ntstatus_ok(torture, status,
3411 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3413 * FS specific: Samba uses PUNCH_HOLE to zero the range, and
3414 * subsequently uses fallocate() to allocate the punched range if the
3415 * file is marked non-sparse and "strict allocate" is enabled. In both
3416 * cases, the zeroed range will not be detected by SEEK_DATA, so the
3417 * range won't be present in QAR responses until the file is marked
3420 if (far_count == 0) {
3421 torture_comment(torture, "non-sparse zeroed range disappeared "
3422 "after marking sparse\n");
3424 /* NTFS: range remains fully allocated */
3425 torture_assert_u64_equal(torture, far_count, 1,
3426 "unexpected response len");
3427 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3428 "unexpected far off");
3429 torture_assert_u64_equal(torture, far_rsp[0].len, 4096,
3430 "unexpected far len");
3433 /* zero (hole-punch) the data, _with_ sparse flag */
3434 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3436 4096); /* beyond_final_zero */
3437 torture_assert_ntstatus_ok(torture, status, "zero_data");
3439 /* the range should no longer be alloced */
3440 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3445 torture_assert_ntstatus_ok(torture, status,
3446 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3447 torture_assert_u64_equal(torture, far_count, 0,
3448 "unexpected response len");
3450 ok = check_zero(torture, tree, tmp_ctx, fh, 0, 4096);
3451 torture_assert(torture, ok, "sparse zeroed range");
3453 /* remove sparse flag, this should "unsparse" the zeroed range */
3454 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false);
3455 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3457 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3462 torture_assert_ntstatus_ok(torture, status,
3463 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3464 torture_assert_u64_equal(torture, far_count, 1,
3465 "unexpected response len");
3466 /* expect fully allocated */
3467 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3468 "unexpected far off");
3469 torture_assert_u64_equal(torture, far_rsp[0].len, 4096,
3470 "unexpected far len");
3472 ok = check_zero(torture, tree, tmp_ctx, fh, 0, 4096);
3473 torture_assert(torture, ok, "sparse zeroed range");
3475 smb2_util_close(tree, fh);
3476 talloc_free(tmp_ctx);
3481 * Find the point at which a zeroed range in a sparse file is deallocated by the
3482 * underlying filesystem. NTFS on Windows Server 2012 deallocates chunks in 64k
3483 * increments. Also check whether zeroed neighbours are merged for deallocation.
3485 static bool test_ioctl_sparse_hole_dealloc(struct torture_context *torture,
3486 struct smb2_tree *tree)
3488 struct smb2_handle fh;
3490 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3494 uint64_t dealloc_chunk_len = 0;
3495 struct file_alloced_range_buf *far_rsp = NULL;
3496 uint64_t far_count = 0;
3498 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3499 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3500 FILE_ATTRIBUTE_NORMAL);
3501 torture_assert(torture, ok, "setup file 1");
3503 /* check for FS sparse file */
3504 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3505 FILE_SUPPORTS_SPARSE_FILES, &ok);
3506 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3508 smb2_util_close(tree, fh);
3509 torture_skip(torture, "Sparse files not supported\n");
3513 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
3514 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3516 file_size = 1024 * 1024;
3518 ok = write_pattern(torture, tree, tmp_ctx, fh,
3520 file_size, /* len */
3521 0); /* pattern offset */
3522 torture_assert(torture, ok, "write pattern");
3524 /* check allocated ranges, should be fully allocated */
3525 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3527 file_size, /* len */
3530 torture_assert_ntstatus_ok(torture, status,
3531 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3532 torture_assert_u64_equal(torture, far_count, 1,
3533 "unexpected response len");
3534 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3535 "unexpected far off");
3536 torture_assert_u64_equal(torture, far_rsp[0].len, file_size,
3537 "unexpected far len");
3539 /* punch holes in sizes of 1k increments */
3540 for (hlen = 0; hlen <= file_size; hlen += 4096) {
3542 /* punch a hole from zero to the current increment */
3543 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3545 hlen); /* beyond_final_zero */
3546 torture_assert_ntstatus_ok(torture, status, "zero_data");
3548 /* ensure hole is zeroed, and pattern is consistent */
3549 ok = check_zero(torture, tree, tmp_ctx, fh, 0, hlen);
3550 torture_assert(torture, ok, "sparse zeroed range");
3552 ok = check_pattern(torture, tree, tmp_ctx, fh, hlen,
3553 file_size - hlen, hlen);
3554 torture_assert(torture, ok, "allocated pattern range");
3556 /* Check allocated ranges, hole might have been deallocated */
3557 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3559 file_size, /* len */
3562 torture_assert_ntstatus_ok(torture, status,
3563 "FSCTL_QUERY_ALLOCATED_RANGES");
3564 if ((hlen == file_size) && (far_count == 0)) {
3565 /* hole covered entire file, deallocation occurred */
3566 dealloc_chunk_len = file_size;
3570 torture_assert_u64_equal(torture, far_count, 1,
3571 "unexpected response len");
3572 if (far_rsp[0].file_off != 0) {
3574 * We now know the hole punch length needed to trigger a
3575 * deallocation on this FS...
3577 dealloc_chunk_len = hlen;
3578 torture_comment(torture, "hole punch %ju@0 resulted in "
3579 "deallocation of %ju@0\n",
3581 (uintmax_t)far_rsp[0].file_off);
3582 torture_assert_u64_equal(torture,
3583 file_size - far_rsp[0].len,
3584 far_rsp[0].file_off,
3585 "invalid alloced range");
3590 if (dealloc_chunk_len == 0) {
3591 torture_comment(torture, "strange, this FS never deallocates"
3592 "zeroed ranges in sparse files\n");
3593 return true; /* FS specific, not a failure */
3597 * Check whether deallocation occurs when the (now known)
3598 * deallocation chunk size is punched via two ZERO_DATA requests.
3599 * I.e. Does the FS merge the two ranges and deallocate the chunk?
3600 * NTFS on Windows Server 2012 does not.
3602 ok = write_pattern(torture, tree, tmp_ctx, fh,
3604 file_size, /* len */
3605 0); /* pattern offset */
3606 torture_assert(torture, ok, "write pattern");
3608 /* divide dealloc chunk size by two, to use as punch length */
3609 hlen = dealloc_chunk_len >> 1;
3612 * /half of dealloc chunk size 1M\
3614 * /offset 0 | /dealloc chunk size |
3615 * |------------------ |-------------------|-------------------|
3616 * | zeroed, 1st punch | zeroed, 2nd punch | existing pattern |
3618 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3620 hlen); /* beyond final zero */
3621 torture_assert_ntstatus_ok(torture, status, "zero_data");
3623 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3625 dealloc_chunk_len); /* beyond final */
3626 torture_assert_ntstatus_ok(torture, status, "zero_data");
3628 /* ensure holes are zeroed, and pattern is consistent */
3629 ok = check_zero(torture, tree, tmp_ctx, fh, 0, dealloc_chunk_len);
3630 torture_assert(torture, ok, "sparse zeroed range");
3632 ok = check_pattern(torture, tree, tmp_ctx, fh, dealloc_chunk_len,
3633 file_size - dealloc_chunk_len, dealloc_chunk_len);
3634 torture_assert(torture, ok, "allocated pattern range");
3636 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3638 file_size, /* len */
3641 torture_assert_ntstatus_ok(torture, status,
3642 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3644 if ((far_count == 0) && (dealloc_chunk_len == file_size)) {
3645 torture_comment(torture, "holes merged for deallocation of "
3649 torture_assert_u64_equal(torture, far_count, 1,
3650 "unexpected response len");
3651 if (far_rsp[0].file_off == dealloc_chunk_len) {
3652 torture_comment(torture, "holes merged for deallocation of "
3653 "%ju chunk\n", (uintmax_t)dealloc_chunk_len);
3654 torture_assert_u64_equal(torture,
3655 file_size - far_rsp[0].len,
3656 far_rsp[0].file_off,
3657 "invalid alloced range");
3659 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3660 "unexpected deallocation");
3661 torture_comment(torture, "holes not merged for deallocation\n");
3664 smb2_util_close(tree, fh);
3667 * Check whether an unwritten range is allocated when a sparse file is
3668 * written to at an offset past the dealloc chunk size:
3670 * /dealloc chunk size
3672 * |------------------ |-------------------|
3673 * | unwritten | pattern |
3675 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3676 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3677 FILE_ATTRIBUTE_NORMAL);
3678 torture_assert(torture, ok, "setup file 1");
3681 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
3682 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3684 ok = write_pattern(torture, tree, tmp_ctx, fh,
3685 dealloc_chunk_len, /* off */
3687 dealloc_chunk_len); /* pattern offset */
3688 torture_assert(torture, ok, "write pattern");
3690 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3692 dealloc_chunk_len + 1024, /* len */
3695 torture_assert_ntstatus_ok(torture, status,
3696 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3697 torture_assert_u64_equal(torture, far_count, 1,
3698 "unexpected response len");
3699 if (far_rsp[0].file_off == 0) {
3700 torture_assert_u64_equal(torture, far_rsp[0].len,
3701 dealloc_chunk_len + 1024,
3702 "unexpected far len");
3703 torture_comment(torture, "unwritten range fully allocated\n");
3705 torture_assert_u64_equal(torture, far_rsp[0].file_off, dealloc_chunk_len,
3706 "unexpected deallocation");
3707 torture_assert_u64_equal(torture, far_rsp[0].len, 1024,
3708 "unexpected far len");
3709 torture_comment(torture, "unwritten range not allocated\n");
3712 ok = check_zero(torture, tree, tmp_ctx, fh, 0, dealloc_chunk_len);
3713 torture_assert(torture, ok, "sparse zeroed range");
3715 ok = check_pattern(torture, tree, tmp_ctx, fh, dealloc_chunk_len,
3716 1024, dealloc_chunk_len);
3717 torture_assert(torture, ok, "allocated pattern range");
3719 /* unsparse, should now be fully allocated */
3720 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false);
3721 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3723 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3725 dealloc_chunk_len + 1024, /* len */
3728 torture_assert_ntstatus_ok(torture, status,
3729 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3730 torture_assert_u64_equal(torture, far_count, 1,
3731 "unexpected response len");
3732 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3733 "unexpected deallocation");
3734 torture_assert_u64_equal(torture, far_rsp[0].len,
3735 dealloc_chunk_len + 1024,
3736 "unexpected far len");
3738 smb2_util_close(tree, fh);
3739 talloc_free(tmp_ctx);
3743 /* check whether a file with compression and sparse attrs can be deallocated */
3744 static bool test_ioctl_sparse_compressed(struct torture_context *torture,
3745 struct smb2_tree *tree)
3747 struct smb2_handle fh;
3749 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3751 uint64_t file_size = 1024 * 1024;
3752 struct file_alloced_range_buf *far_rsp = NULL;
3753 uint64_t far_count = 0;
3755 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3756 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3757 FILE_ATTRIBUTE_NORMAL);
3758 torture_assert(torture, ok, "setup file 1");
3760 /* check for FS sparse file and compression support */
3761 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3762 FILE_SUPPORTS_SPARSE_FILES, &ok);
3763 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3765 smb2_util_close(tree, fh);
3766 torture_skip(torture, "Sparse files not supported\n");
3769 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
3771 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3773 smb2_util_close(tree, fh);
3774 torture_skip(torture, "FS compression not supported\n");
3777 /* set compression and write some data */
3778 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
3779 COMPRESSION_FORMAT_DEFAULT);
3780 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
3782 ok = write_pattern(torture, tree, tmp_ctx, fh,
3784 file_size, /* len */
3785 0); /* pattern offset */
3786 torture_assert(torture, ok, "write pattern");
3788 /* set sparse - now sparse and compressed */
3789 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
3790 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3792 /* check allocated ranges, should be fully alloced */
3793 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3795 file_size, /* len */
3798 torture_assert_ntstatus_ok(torture, status,
3799 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3800 torture_assert_u64_equal(torture, far_count, 1,
3801 "unexpected response len");
3802 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3803 "unexpected far off");
3804 torture_assert_u64_equal(torture, far_rsp[0].len, file_size,
3805 "unexpected far len");
3807 /* zero (hole-punch) all data, with sparse and compressed attrs */
3808 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3810 file_size); /* beyond_final_zero */
3811 torture_assert_ntstatus_ok(torture, status, "zero_data");
3814 * Windows Server 2012 still deallocates a zeroed range when a sparse
3815 * file carries the compression attribute.
3817 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3819 file_size, /* len */
3822 torture_assert_ntstatus_ok(torture, status,
3823 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3824 if (far_count == 0) {
3825 torture_comment(torture, "sparse & compressed file "
3826 "deallocated after hole-punch\n");
3828 torture_assert_u64_equal(torture, far_count, 1,
3829 "unexpected response len");
3830 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3831 "unexpected far off");
3832 torture_assert_u64_equal(torture, far_rsp[0].len, file_size,
3833 "unexpected far len");
3834 torture_comment(torture, "sparse & compressed file fully "
3835 "allocated after hole-punch\n");
3838 smb2_util_close(tree, fh);
3839 talloc_free(tmp_ctx);
3844 * Create a sparse file, then attempt to copy unallocated and allocated ranges
3845 * into a target file using FSCTL_SRV_COPYCHUNK.
3847 static bool test_ioctl_sparse_copy_chunk(struct torture_context *torture,
3848 struct smb2_tree *tree)
3850 struct smb2_handle src_h;
3851 struct smb2_handle dest_h;
3853 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3855 uint64_t dealloc_chunk_len = 64 * 1024; /* Windows 2012 */
3856 struct file_alloced_range_buf *far_rsp = NULL;
3857 uint64_t far_count = 0;
3858 union smb_ioctl ioctl;
3859 struct srv_copychunk_copy cc_copy;
3860 struct srv_copychunk_rsp cc_rsp;
3861 enum ndr_err_code ndr_ret;
3863 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3864 FNAME, &src_h, 0, SEC_RIGHTS_FILE_ALL,
3865 FILE_ATTRIBUTE_NORMAL);
3866 torture_assert(torture, ok, "setup file");
3868 /* check for FS sparse file support */
3869 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &src_h,
3870 FILE_SUPPORTS_SPARSE_FILES, &ok);
3871 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3872 smb2_util_close(tree, src_h);
3874 torture_skip(torture, "Sparse files not supported\n");
3877 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
3879 &src_h, 0, /* src file */
3880 SEC_RIGHTS_FILE_ALL,
3881 &dest_h, 0, /* dest file */
3882 SEC_RIGHTS_FILE_ALL,
3885 torture_assert(torture, ok, "setup copy chunk error");
3888 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, src_h, true);
3889 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3891 /* start after dealloc_chunk_len, to create an unwritten sparse range */
3892 ok = write_pattern(torture, tree, tmp_ctx, src_h,
3893 dealloc_chunk_len, /* off */
3895 dealloc_chunk_len); /* pattern offset */
3896 torture_assert(torture, ok, "write pattern");
3898 /* Skip test if 64k chunk is allocated - FS specific */
3899 status = test_ioctl_qar_req(torture, tmp_ctx, tree, src_h,
3901 dealloc_chunk_len + 1024, /* len */
3904 torture_assert_ntstatus_ok(torture, status,
3905 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3906 torture_assert_u64_equal(torture, far_count, 1,
3907 "unexpected response len");
3908 if (far_rsp[0].file_off == 0) {
3909 torture_skip(torture, "unwritten range fully allocated\n");
3912 torture_assert_u64_equal(torture, far_rsp[0].file_off, dealloc_chunk_len,
3913 "unexpected allocation");
3914 torture_assert_u64_equal(torture, far_rsp[0].len, 1024,
3915 "unexpected far len");
3917 /* copy-chunk unallocated + written ranges into non-sparse dest */
3919 cc_copy.chunks[0].source_off = 0;
3920 cc_copy.chunks[0].target_off = 0;
3921 cc_copy.chunks[0].length = dealloc_chunk_len + 1024;
3923 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
3925 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
3926 torture_assert_ndr_success(torture, ndr_ret,
3927 "ndr_push_srv_copychunk_copy");
3929 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3930 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
3932 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
3934 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
3935 torture_assert_ndr_success(torture, ndr_ret,
3936 "ndr_pull_srv_copychunk_rsp");
3938 ok = check_copy_chunk_rsp(torture, &cc_rsp,
3939 1, /* chunks written */
3940 0, /* chunk bytes unsuccessfully written */
3941 dealloc_chunk_len + 1024); /* bytes written */
3942 torture_assert(torture, ok, "bad copy chunk response data");
3944 ok = check_zero(torture, tree, tmp_ctx, dest_h, 0, dealloc_chunk_len);
3945 torture_assert(torture, ok, "sparse zeroed range");
3947 ok = check_pattern(torture, tree, tmp_ctx, dest_h, dealloc_chunk_len,
3948 1024, dealloc_chunk_len);
3949 torture_assert(torture, ok, "copychunked range");
3951 /* copied range should be allocated in non-sparse dest */
3952 status = test_ioctl_qar_req(torture, tmp_ctx, tree, dest_h,
3954 dealloc_chunk_len + 1024, /* len */
3957 torture_assert_ntstatus_ok(torture, status,
3958 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3959 torture_assert_u64_equal(torture, far_count, 1,
3960 "unexpected response len");
3961 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3962 "unexpected allocation");
3963 torture_assert_u64_equal(torture, far_rsp[0].len,
3964 dealloc_chunk_len + 1024,
3965 "unexpected far len");
3967 /* set dest as sparse */
3968 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, dest_h, true);
3969 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3971 /* zero (hole-punch) all data */
3972 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, dest_h,
3974 dealloc_chunk_len + 1024);
3975 torture_assert_ntstatus_ok(torture, status, "zero_data");
3977 /* zeroed range might be deallocated */
3978 status = test_ioctl_qar_req(torture, tmp_ctx, tree, dest_h,
3980 dealloc_chunk_len + 1024, /* len */
3983 torture_assert_ntstatus_ok(torture, status,
3984 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3985 if (far_count == 0) {
3986 /* FS specific (e.g. NTFS) */
3987 torture_comment(torture, "FS deallocates file on full-range "
3990 /* FS specific (e.g. EXT4) */
3991 torture_comment(torture, "FS doesn't deallocate file on "
3992 "full-range punch\n");
3994 ok = check_zero(torture, tree, tmp_ctx, dest_h, 0,
3995 dealloc_chunk_len + 1024);
3996 torture_assert(torture, ok, "punched zeroed range");
3998 /* copy-chunk again, this time with sparse dest */
3999 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
4000 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
4002 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
4004 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
4005 torture_assert_ndr_success(torture, ndr_ret,
4006 "ndr_pull_srv_copychunk_rsp");
4008 ok = check_copy_chunk_rsp(torture, &cc_rsp,
4009 1, /* chunks written */
4010 0, /* chunk bytes unsuccessfully written */
4011 dealloc_chunk_len + 1024); /* bytes written */
4012 torture_assert(torture, ok, "bad copy chunk response data");
4014 ok = check_zero(torture, tree, tmp_ctx, dest_h, 0, dealloc_chunk_len);
4015 torture_assert(torture, ok, "sparse zeroed range");
4017 ok = check_pattern(torture, tree, tmp_ctx, dest_h, dealloc_chunk_len,
4018 1024, dealloc_chunk_len);
4019 torture_assert(torture, ok, "copychunked range");
4021 /* copied range may be allocated in sparse dest */
4022 status = test_ioctl_qar_req(torture, tmp_ctx, tree, dest_h,
4024 dealloc_chunk_len + 1024, /* len */
4027 torture_assert_ntstatus_ok(torture, status,
4028 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4029 torture_assert_u64_equal(torture, far_count, 1,
4030 "unexpected response len");
4032 * FS specific: sparse region may be unallocated in dest if copy-chunk
4033 * is handled in a sparse preserving way - E.g. vfs_btrfs
4034 * with BTRFS_IOC_CLONE_RANGE.
4036 if (far_rsp[0].file_off == dealloc_chunk_len) {
4037 torture_comment(torture, "copy-chunk sparse range preserved\n");
4038 torture_assert_u64_equal(torture, far_rsp[0].len, 1024,
4039 "unexpected far len");
4041 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4042 "unexpected allocation");
4043 torture_assert_u64_equal(torture, far_rsp[0].len,
4044 dealloc_chunk_len + 1024,
4045 "unexpected far len");
4048 smb2_util_close(tree, src_h);
4049 smb2_util_close(tree, dest_h);
4050 talloc_free(tmp_ctx);
4054 static bool test_ioctl_sparse_punch_invalid(struct torture_context *torture,
4055 struct smb2_tree *tree)
4057 struct smb2_handle fh;
4059 TALLOC_CTX *tmp_ctx = talloc_new(tree);
4064 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4065 FNAME, &fh, 4096, SEC_RIGHTS_FILE_ALL,
4066 FILE_ATTRIBUTE_NORMAL);
4067 torture_assert(torture, ok, "setup file");
4069 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
4070 FILE_SUPPORTS_SPARSE_FILES, &ok);
4071 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4073 smb2_util_close(tree, fh);
4074 torture_skip(torture, "Sparse files not supported\n");
4077 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4078 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4079 torture_assert(torture, !is_sparse, "sparse attr before set");
4081 /* loop twice, without and with sparse attrib */
4082 for (i = 0; i <= 1; i++) {
4083 union smb_fileinfo io;
4084 struct file_alloced_range_buf *far_rsp = NULL;
4085 uint64_t far_count = 0;
4087 /* get size before & after. zero data should never change it */
4089 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
4090 io.generic.in.file.handle = fh;
4091 status = smb2_getinfo_file(tree, tmp_ctx, &io);
4092 torture_assert_ntstatus_ok(torture, status, "getinfo");
4093 torture_assert_int_equal(torture, (int)io.all_info2.out.size,
4094 4096, "size after IO");
4096 /* valid 8 byte zero data, but after EOF */
4097 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4099 4104); /* beyond_final_zero */
4100 torture_assert_ntstatus_ok(torture, status, "zero_data");
4102 /* valid 8 byte zero data, but after EOF */
4103 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4105 8200); /* beyond_final_zero */
4106 torture_assert_ntstatus_ok(torture, status, "zero_data");
4109 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
4110 io.generic.in.file.handle = fh;
4111 status = smb2_getinfo_file(tree, tmp_ctx, &io);
4112 torture_assert_ntstatus_ok(torture, status, "getinfo");
4113 torture_assert_int_equal(torture, (int)io.all_info2.out.size,
4114 4096, "size after IO");
4116 /* valid 0 byte zero data, without sparse flag */
4117 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4119 4095); /* beyond_final_zero */
4120 torture_assert_ntstatus_ok(torture, status, "zero_data");
4122 /* INVALID off is past beyond_final_zero */
4123 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4125 4095); /* beyond_final_zero */
4126 torture_assert_ntstatus_equal(torture, status,
4127 NT_STATUS_INVALID_PARAMETER,
4128 "invalid zero_data");
4130 /* zero length QAR - valid */
4131 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4134 &far_rsp, &far_count);
4135 torture_assert_ntstatus_ok(torture, status,
4136 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4137 torture_assert_u64_equal(torture, far_count, 0,
4138 "unexpected response len");
4140 /* QAR after EOF - valid */
4141 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4144 &far_rsp, &far_count);
4145 torture_assert_ntstatus_ok(torture, status,
4146 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4147 torture_assert_u64_equal(torture, far_count, 0,
4148 "unexpected response len");
4151 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh,
4153 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4156 smb2_util_close(tree, fh);
4157 talloc_free(tmp_ctx);
4161 static bool test_ioctl_sparse_perms(struct torture_context *torture,
4162 struct smb2_tree *tree)
4164 struct smb2_handle fh;
4166 TALLOC_CTX *tmp_ctx = talloc_new(tree);
4169 struct file_alloced_range_buf *far_rsp = NULL;
4170 uint64_t far_count = 0;
4172 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4173 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
4174 FILE_ATTRIBUTE_NORMAL);
4175 torture_assert(torture, ok, "setup file");
4177 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
4178 FILE_SUPPORTS_SPARSE_FILES, &ok);
4179 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4180 smb2_util_close(tree, fh);
4182 torture_skip(torture, "Sparse files not supported\n");
4185 /* set sparse without WRITE_ATTR permission should succeed */
4186 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4188 (SEC_RIGHTS_FILE_WRITE & ~(SEC_FILE_WRITE_ATTRIBUTE
4190 | SEC_FILE_WRITE_EA)),
4191 FILE_ATTRIBUTE_NORMAL);
4192 torture_assert(torture, ok, "setup file");
4194 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4195 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4196 smb2_util_close(tree, fh);
4198 ok = test_setup_open(torture, tree, tmp_ctx,
4199 FNAME, &fh, SEC_RIGHTS_FILE_ALL,
4200 FILE_ATTRIBUTE_NORMAL);
4201 torture_assert(torture, ok, "setup file");
4202 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4203 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4204 torture_assert(torture, is_sparse, "sparse after set");
4205 smb2_util_close(tree, fh);
4207 /* attempt get sparse without READ_DATA permission */
4208 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4210 (SEC_RIGHTS_FILE_READ & ~SEC_FILE_READ_DATA),
4211 FILE_ATTRIBUTE_NORMAL);
4212 torture_assert(torture, ok, "setup file");
4214 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4215 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4216 torture_assert(torture, !is_sparse, "sparse set");
4217 smb2_util_close(tree, fh);
4219 /* attempt to set sparse with only WRITE_ATTR permission */
4220 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4222 SEC_FILE_WRITE_ATTRIBUTE,
4223 FILE_ATTRIBUTE_NORMAL);
4224 torture_assert(torture, ok, "setup file");
4226 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4227 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4228 smb2_util_close(tree, fh);
4230 /* attempt to set sparse with only WRITE_DATA permission */
4231 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4233 SEC_FILE_WRITE_DATA,
4234 FILE_ATTRIBUTE_NORMAL);
4235 torture_assert(torture, ok, "setup file");
4237 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4238 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4239 smb2_util_close(tree, fh);
4241 ok = test_setup_open(torture, tree, tmp_ctx,
4242 FNAME, &fh, SEC_RIGHTS_FILE_ALL,
4243 FILE_ATTRIBUTE_NORMAL);
4244 torture_assert(torture, ok, "setup file");
4245 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4246 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4247 torture_assert(torture, is_sparse, "sparse after set");
4248 smb2_util_close(tree, fh);
4250 /* attempt to set sparse with only APPEND_DATA permission */
4251 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4253 SEC_FILE_APPEND_DATA,
4254 FILE_ATTRIBUTE_NORMAL);
4255 torture_assert(torture, ok, "setup file");
4257 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4258 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4259 smb2_util_close(tree, fh);
4261 ok = test_setup_open(torture, tree, tmp_ctx,
4262 FNAME, &fh, SEC_RIGHTS_FILE_ALL,
4263 FILE_ATTRIBUTE_NORMAL);
4264 torture_assert(torture, ok, "setup file");
4265 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4266 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4267 torture_assert(torture, is_sparse, "sparse after set");
4268 smb2_util_close(tree, fh);
4270 /* attempt to set sparse with only WRITE_EA permission - should fail */
4271 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4274 FILE_ATTRIBUTE_NORMAL);
4275 torture_assert(torture, ok, "setup file");
4277 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4278 torture_assert_ntstatus_equal(torture, status,
4279 NT_STATUS_ACCESS_DENIED,
4280 "FSCTL_SET_SPARSE permission");
4281 smb2_util_close(tree, fh);
4283 ok = test_setup_open(torture, tree, tmp_ctx,
4284 FNAME, &fh, SEC_RIGHTS_FILE_ALL,
4285 FILE_ATTRIBUTE_NORMAL);
4286 torture_assert(torture, ok, "setup file");
4287 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4288 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4289 torture_assert(torture, !is_sparse, "sparse after set");
4290 smb2_util_close(tree, fh);
4292 /* attempt QAR with only READ_ATTR permission - should fail */
4293 ok = test_setup_open(torture, tree, tmp_ctx,
4294 FNAME, &fh, SEC_FILE_READ_ATTRIBUTE,
4295 FILE_ATTRIBUTE_NORMAL);
4296 torture_assert(torture, ok, "setup file");
4297 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4300 &far_rsp, &far_count);
4301 torture_assert_ntstatus_equal(torture, status,
4302 NT_STATUS_ACCESS_DENIED,
4303 "FSCTL_QUERY_ALLOCATED_RANGES req passed");
4304 smb2_util_close(tree, fh);
4306 /* attempt QAR with only READ_DATA permission */
4307 ok = test_setup_open(torture, tree, tmp_ctx,
4308 FNAME, &fh, SEC_FILE_READ_DATA,
4309 FILE_ATTRIBUTE_NORMAL);
4310 torture_assert(torture, ok, "setup file");
4311 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4314 &far_rsp, &far_count);
4315 torture_assert_ntstatus_ok(torture, status,
4316 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4317 torture_assert_u64_equal(torture, far_count, 0,
4318 "unexpected response len");
4319 smb2_util_close(tree, fh);
4321 /* attempt QAR with only READ_EA permission - should fail */
4322 ok = test_setup_open(torture, tree, tmp_ctx,
4323 FNAME, &fh, SEC_FILE_READ_EA,
4324 FILE_ATTRIBUTE_NORMAL);
4325 torture_assert(torture, ok, "setup file");
4326 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4329 &far_rsp, &far_count);
4330 torture_assert_ntstatus_equal(torture, status,
4331 NT_STATUS_ACCESS_DENIED,
4332 "FSCTL_QUERY_ALLOCATED_RANGES req passed");
4333 smb2_util_close(tree, fh);
4335 /* setup file for ZERO_DATA permissions tests */
4336 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4338 SEC_RIGHTS_FILE_ALL,
4339 FILE_ATTRIBUTE_NORMAL);
4340 torture_assert(torture, ok, "setup file");
4342 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4343 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4344 smb2_util_close(tree, fh);
4346 /* attempt ZERO_DATA with only WRITE_ATTR permission - should fail */
4347 ok = test_setup_open(torture, tree, tmp_ctx,
4348 FNAME, &fh, SEC_FILE_WRITE_ATTRIBUTE,
4349 FILE_ATTRIBUTE_NORMAL);
4350 torture_assert(torture, ok, "setup file");
4351 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4353 4096); /* beyond_final_zero */
4354 torture_assert_ntstatus_equal(torture, status,
4355 NT_STATUS_ACCESS_DENIED,
4356 "zero_data permission");
4357 smb2_util_close(tree, fh);
4359 /* attempt ZERO_DATA with only WRITE_DATA permission */
4360 ok = test_setup_open(torture, tree, tmp_ctx,
4361 FNAME, &fh, SEC_FILE_WRITE_DATA,
4362 FILE_ATTRIBUTE_NORMAL);
4363 torture_assert(torture, ok, "setup file");
4364 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4366 4096); /* beyond_final_zero */
4367 torture_assert_ntstatus_ok(torture, status, "zero_data");
4368 smb2_util_close(tree, fh);
4370 /* attempt ZERO_DATA with only APPEND_DATA permission - should fail */
4371 ok = test_setup_open(torture, tree, tmp_ctx,
4372 FNAME, &fh, SEC_FILE_APPEND_DATA,
4373 FILE_ATTRIBUTE_NORMAL);
4374 torture_assert(torture, ok, "setup file");
4375 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4377 4096); /* beyond_final_zero */
4378 torture_assert_ntstatus_equal(torture, status,
4379 NT_STATUS_ACCESS_DENIED,
4380 "zero_data permission");
4381 smb2_util_close(tree, fh);
4383 /* attempt ZERO_DATA with only WRITE_EA permission - should fail */
4384 ok = test_setup_open(torture, tree, tmp_ctx,
4385 FNAME, &fh, SEC_FILE_WRITE_EA,
4386 FILE_ATTRIBUTE_NORMAL);
4387 torture_assert(torture, ok, "setup file");
4388 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4390 4096); /* beyond_final_zero */
4391 torture_assert_ntstatus_equal(torture, status,
4392 NT_STATUS_ACCESS_DENIED,
4393 "zero_data permission");
4394 smb2_util_close(tree, fh);
4396 talloc_free(tmp_ctx);
4400 static bool test_ioctl_sparse_lck(struct torture_context *torture,
4401 struct smb2_tree *tree)
4403 struct smb2_handle fh;
4404 struct smb2_handle fh2;
4406 uint64_t dealloc_chunk_len = 64 * 1024; /* Windows 2012 */
4407 TALLOC_CTX *tmp_ctx = talloc_new(tree);
4410 struct smb2_lock lck;
4411 struct smb2_lock_element el[1];
4412 struct file_alloced_range_buf *far_rsp = NULL;
4413 uint64_t far_count = 0;
4415 ok = test_setup_create_fill(torture, tree, tmp_ctx, FNAME, &fh,
4416 dealloc_chunk_len, SEC_RIGHTS_FILE_ALL,
4417 FILE_ATTRIBUTE_NORMAL);
4418 torture_assert(torture, ok, "setup file");
4420 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
4421 FILE_SUPPORTS_SPARSE_FILES, &ok);
4422 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4424 torture_skip(torture, "Sparse files not supported\n");
4425 smb2_util_close(tree, fh);
4428 /* open and lock via separate fh2 */
4429 status = torture_smb2_testfile(tree, FNAME, &fh2);
4430 torture_assert_ntstatus_ok(torture, status, "2nd src open");
4432 lck.in.lock_count = 0x0001;
4433 lck.in.lock_sequence = 0x00000000;
4434 lck.in.file.handle = fh2;
4437 el[0].length = dealloc_chunk_len;
4439 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
4441 status = smb2_lock(tree, &lck);
4442 torture_assert_ntstatus_ok(torture, status, "lock");
4444 /* set sparse while locked */
4445 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4446 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4448 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4449 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4450 torture_assert(torture, is_sparse, "sparse attr after set");
4452 /* zero data over locked range should fail */
4453 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4455 4096); /* beyond_final_zero */
4456 torture_assert_ntstatus_equal(torture, status,
4457 NT_STATUS_FILE_LOCK_CONFLICT,
4458 "zero_data locked");
4460 /* QAR over locked range should pass */
4461 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4464 &far_rsp, &far_count);
4465 torture_assert_ntstatus_ok(torture, status,
4466 "FSCTL_QUERY_ALLOCATED_RANGES locked");
4467 torture_assert_u64_equal(torture, far_count, 1,
4468 "unexpected response len");
4469 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4470 "unexpected allocation");
4471 torture_assert_u64_equal(torture, far_rsp[0].len,
4473 "unexpected far len");
4475 /* zero data over range past EOF should pass */
4476 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4477 dealloc_chunk_len, /* off */
4478 dealloc_chunk_len + 4096);
4479 torture_assert_ntstatus_ok(torture, status,
4480 "zero_data past EOF locked");
4482 /* QAR over range past EOF should pass */
4483 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4484 dealloc_chunk_len, /* off */
4486 &far_rsp, &far_count);
4487 torture_assert_ntstatus_ok(torture, status,
4488 "FSCTL_QUERY_ALLOCATED_RANGES past EOF locked");
4489 torture_assert_u64_equal(torture, far_count, 0,
4490 "unexpected response len");
4492 lck.in.lock_count = 0x0001;
4493 lck.in.lock_sequence = 0x00000001;
4494 lck.in.file.handle = fh2;
4497 el[0].length = dealloc_chunk_len;
4499 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
4500 status = smb2_lock(tree, &lck);
4501 torture_assert_ntstatus_ok(torture, status, "unlock");
4503 smb2_util_close(tree, fh2);
4504 smb2_util_close(tree, fh);
4505 talloc_free(tmp_ctx);
4509 /* alleviate QAR off-by-one bug paranoia - help me ob1 */
4510 static bool test_ioctl_sparse_qar_ob1(struct torture_context *torture,
4511 struct smb2_tree *tree)
4513 struct smb2_handle fh;
4515 TALLOC_CTX *tmp_ctx = talloc_new(tree);
4517 uint64_t dealloc_chunk_len = 64 * 1024; /* Windows 2012 */
4518 struct file_alloced_range_buf *far_rsp = NULL;
4519 uint64_t far_count = 0;
4521 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4522 FNAME, &fh, dealloc_chunk_len * 2,
4523 SEC_RIGHTS_FILE_ALL,
4524 FILE_ATTRIBUTE_NORMAL);
4525 torture_assert(torture, ok, "setup file");
4527 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
4528 FILE_SUPPORTS_SPARSE_FILES, &ok);
4529 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4531 torture_skip(torture, "Sparse files not supported\n");
4532 smb2_util_close(tree, fh);
4535 /* non-sparse QAR with range one before EOF */
4536 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4538 dealloc_chunk_len * 2 - 1, /* len */
4539 &far_rsp, &far_count);
4540 torture_assert_ntstatus_ok(torture, status,
4541 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4542 torture_assert_u64_equal(torture, far_count, 1,
4543 "unexpected response len");
4544 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4545 "unexpected allocation");
4546 torture_assert_u64_equal(torture, far_rsp[0].len,
4547 dealloc_chunk_len * 2 - 1,
4548 "unexpected far len");
4550 /* non-sparse QAR with range one after EOF */
4551 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4553 dealloc_chunk_len * 2 + 1, /* len */
4554 &far_rsp, &far_count);
4555 torture_assert_ntstatus_ok(torture, status,
4556 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4557 torture_assert_u64_equal(torture, far_count, 1,
4558 "unexpected response len");
4559 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4560 "unexpected allocation");
4561 torture_assert_u64_equal(torture, far_rsp[0].len,
4562 dealloc_chunk_len * 2,
4563 "unexpected far len");
4565 /* non-sparse QAR with range one after EOF from off=1 */
4566 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4568 dealloc_chunk_len * 2, /* len */
4569 &far_rsp, &far_count);
4570 torture_assert_ntstatus_ok(torture, status,
4571 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4572 torture_assert_u64_equal(torture, far_count, 1,
4573 "unexpected response len");
4574 torture_assert_u64_equal(torture, far_rsp[0].file_off, 1,
4575 "unexpected allocation");
4576 torture_assert_u64_equal(torture, far_rsp[0].len,
4577 dealloc_chunk_len * 2 - 1,
4578 "unexpected far len");
4580 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4581 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4583 /* punch out second chunk */
4584 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4585 dealloc_chunk_len, /* off */
4586 dealloc_chunk_len * 2);
4587 torture_assert_ntstatus_ok(torture, status, "zero_data");
4589 /* sparse QAR with range one before hole */
4590 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4592 dealloc_chunk_len - 1, /* len */
4593 &far_rsp, &far_count);
4594 torture_assert_ntstatus_ok(torture, status,
4595 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4596 torture_assert_u64_equal(torture, far_count, 1,
4597 "unexpected response len");
4598 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4599 "unexpected allocation");
4600 torture_assert_u64_equal(torture, far_rsp[0].len,
4601 dealloc_chunk_len - 1,
4602 "unexpected far len");
4604 /* sparse QAR with range one after hole */
4605 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4607 dealloc_chunk_len + 1, /* len */
4608 &far_rsp, &far_count);
4609 torture_assert_ntstatus_ok(torture, status,
4610 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4611 torture_assert_u64_equal(torture, far_count, 1,
4612 "unexpected response len");
4613 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4614 "unexpected allocation");
4615 torture_assert_u64_equal(torture, far_rsp[0].len,
4617 "unexpected far len");
4619 /* sparse QAR with range one after hole from off=1 */
4620 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4622 dealloc_chunk_len, /* len */
4623 &far_rsp, &far_count);
4624 torture_assert_ntstatus_ok(torture, status,
4625 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4626 torture_assert_u64_equal(torture, far_count, 1,
4627 "unexpected response len");
4628 torture_assert_u64_equal(torture, far_rsp[0].file_off, 1,
4629 "unexpected allocation");
4630 torture_assert_u64_equal(torture, far_rsp[0].len,
4631 dealloc_chunk_len - 1,
4632 "unexpected far len");
4634 /* sparse QAR with range one before EOF from off=chunk_len-1 */
4635 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4636 dealloc_chunk_len - 1, /* off */
4637 dealloc_chunk_len, /* len */
4638 &far_rsp, &far_count);
4639 torture_assert_ntstatus_ok(torture, status,
4640 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4641 torture_assert_u64_equal(torture, far_count, 1,
4642 "unexpected response len");
4643 torture_assert_u64_equal(torture, far_rsp[0].file_off,
4644 dealloc_chunk_len - 1,
4645 "unexpected allocation");
4646 torture_assert_u64_equal(torture, far_rsp[0].len,
4647 1, "unexpected far len");
4649 /* sparse QAR with range one after EOF from off=chunk_len+1 */
4650 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4651 dealloc_chunk_len + 1, /* off */
4652 dealloc_chunk_len, /* len */
4653 &far_rsp, &far_count);
4654 torture_assert_ntstatus_ok(torture, status,
4655 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4656 torture_assert_u64_equal(torture, far_count, 0,
4657 "unexpected response len");
4658 smb2_util_close(tree, fh);
4659 talloc_free(tmp_ctx);
4663 /* test QAR with multi-range responses */
4664 static bool test_ioctl_sparse_qar_multi(struct torture_context *torture,
4665 struct smb2_tree *tree)
4667 struct smb2_handle fh;
4669 TALLOC_CTX *tmp_ctx = talloc_new(tree);
4671 uint64_t dealloc_chunk_len = 64 * 1024; /* Windows 2012 */
4674 struct file_alloced_range_buf *far_rsp = NULL;
4675 uint64_t far_count = 0;
4677 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4678 FNAME, &fh, dealloc_chunk_len * 2,
4679 SEC_RIGHTS_FILE_ALL,
4680 FILE_ATTRIBUTE_NORMAL);
4681 torture_assert(torture, ok, "setup file");
4683 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
4684 FILE_SUPPORTS_SPARSE_FILES, &ok);
4685 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4687 torture_skip(torture, "Sparse files not supported\n");
4688 smb2_util_close(tree, fh);
4691 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4692 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4694 /* each loop, write out two chunks and punch the first out */
4695 for (i = 0; i < 10; i++) {
4696 this_off = i * dealloc_chunk_len * 2;
4698 ok = write_pattern(torture, tree, tmp_ctx, fh,
4700 dealloc_chunk_len * 2, /* len */
4701 this_off); /* pattern offset */
4702 torture_assert(torture, ok, "write pattern");
4704 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4706 this_off + dealloc_chunk_len);
4707 torture_assert_ntstatus_ok(torture, status, "zero_data");
4710 /* should now have one separate region for each iteration */
4711 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4713 10 * dealloc_chunk_len * 2,
4714 &far_rsp, &far_count);
4715 torture_assert_ntstatus_ok(torture, status,
4716 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4717 if (far_count == 1) {
4718 torture_comment(torture, "this FS doesn't deallocate 64K"
4719 "zeroed ranges in sparse files\n");
4720 return true; /* FS specific, not a failure */
4722 torture_assert_u64_equal(torture, far_count, 10,
4723 "unexpected response len");
4724 for (i = 0; i < 10; i++) {
4725 this_off = i * dealloc_chunk_len * 2;
4727 torture_assert_u64_equal(torture, far_rsp[i].file_off,
4728 this_off + dealloc_chunk_len,
4729 "unexpected allocation");
4730 torture_assert_u64_equal(torture, far_rsp[i].len,
4732 "unexpected far len");
4735 smb2_util_close(tree, fh);
4736 talloc_free(tmp_ctx);
4740 static bool test_ioctl_sparse_qar_overflow(struct torture_context *torture,
4741 struct smb2_tree *tree)
4743 struct smb2_handle fh;
4744 union smb_ioctl ioctl;
4745 struct file_alloced_range_buf far_buf;
4747 enum ndr_err_code ndr_ret;
4748 TALLOC_CTX *tmp_ctx = talloc_new(tree);
4751 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4752 FNAME, &fh, 1024, SEC_RIGHTS_FILE_ALL,
4753 FILE_ATTRIBUTE_NORMAL);
4754 torture_assert(torture, ok, "setup file");
4756 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
4757 FILE_SUPPORTS_SPARSE_FILES, &ok);
4758 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4760 smb2_util_close(tree, fh);
4761 torture_skip(torture, "Sparse files not supported\n");
4764 /* no allocated ranges, no space for range response, should pass */
4766 ioctl.smb2.level = RAW_IOCTL_SMB2;
4767 ioctl.smb2.in.file.handle = fh;
4768 ioctl.smb2.in.function = FSCTL_QUERY_ALLOCATED_RANGES;
4769 ioctl.smb2.in.max_response_size = 1024;
4770 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
4772 /* off + length wraps around to 511 */
4773 far_buf.file_off = 512;
4774 far_buf.len = 0xffffffffffffffffLL;
4775 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
4777 (ndr_push_flags_fn_t)ndr_push_file_alloced_range_buf);
4778 torture_assert_ndr_success(torture, ndr_ret, "push far ndr buf");
4780 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
4781 torture_assert_ntstatus_equal(torture, status,
4782 NT_STATUS_INVALID_PARAMETER,
4783 "FSCTL_QUERY_ALLOCATED_RANGES overflow");
4788 static NTSTATUS test_ioctl_trim_supported(struct torture_context *torture,
4789 struct smb2_tree *tree,
4790 TALLOC_CTX *mem_ctx,
4791 struct smb2_handle *fh,
4795 union smb_fsinfo info;
4798 info.generic.level = RAW_QFS_SECTOR_SIZE_INFORMATION;
4799 info.generic.handle = *fh;
4800 status = smb2_getinfo_fs(tree, tree, &info);
4801 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_INFO_CLASS)) {
4803 * Windows < Server 2012, 8 etc. don't support this info level
4804 * or the trim ioctl. Ignore the error and let the caller skip.
4806 *trim_support = false;
4807 return NT_STATUS_OK;
4808 } else if (!NT_STATUS_IS_OK(status)) {
4812 torture_comment(torture, "sector size info: lb/s=%u, pb/sA=%u, "
4813 "pb/sP=%u, fse/sA=%u, flags=0x%x, bosa=%u, bopa=%u\n",
4814 (unsigned)info.sector_size_info.out.logical_bytes_per_sector,
4815 (unsigned)info.sector_size_info.out.phys_bytes_per_sector_atomic,
4816 (unsigned)info.sector_size_info.out.phys_bytes_per_sector_perf,
4817 (unsigned)info.sector_size_info.out.fs_effective_phys_bytes_per_sector_atomic,
4818 (unsigned)info.sector_size_info.out.flags,
4819 (unsigned)info.sector_size_info.out.byte_off_sector_align,
4820 (unsigned)info.sector_size_info.out.byte_off_partition_align);
4822 if (info.sector_size_info.out.flags & QFS_SSINFO_FLAGS_TRIM_ENABLED) {
4823 *trim_support = true;
4825 *trim_support = false;
4827 return NT_STATUS_OK;
4830 static bool test_setup_trim(struct torture_context *torture,
4831 struct smb2_tree *tree,
4832 TALLOC_CTX *mem_ctx,
4833 uint32_t num_ranges,
4834 struct smb2_handle *fh,
4836 uint32_t desired_access,
4837 struct fsctl_file_level_trim_req *trim_req,
4838 union smb_ioctl *ioctl)
4842 ok = test_setup_create_fill(torture, tree, mem_ctx, FNAME,
4843 fh, file_size, desired_access,
4844 FILE_ATTRIBUTE_NORMAL);
4845 torture_assert(torture, ok, "src file create fill");
4847 ZERO_STRUCTPN(ioctl);
4848 ioctl->smb2.level = RAW_IOCTL_SMB2;
4849 ioctl->smb2.in.file.handle = *fh;
4850 ioctl->smb2.in.function = FSCTL_FILE_LEVEL_TRIM;
4851 ioctl->smb2.in.max_response_size
4852 = sizeof(struct fsctl_file_level_trim_rsp);
4853 ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
4855 ZERO_STRUCTPN(trim_req);
4856 /* leave key as zero for now. TODO test locking with differing keys */
4857 trim_req->num_ranges = num_ranges;
4858 trim_req->ranges = talloc_zero_array(mem_ctx,
4859 struct file_level_trim_range,
4861 torture_assert(torture, (trim_req->ranges != NULL), "no memory for ranges");
4866 static bool test_ioctl_trim_simple(struct torture_context *torture,
4867 struct smb2_tree *tree)
4869 struct smb2_handle fh;
4871 union smb_ioctl ioctl;
4872 bool trim_supported;
4873 TALLOC_CTX *tmp_ctx = talloc_new(tree);
4874 struct fsctl_file_level_trim_req trim_req;
4875 struct fsctl_file_level_trim_rsp trim_rsp;
4876 uint64_t trim_chunk_len = 64 * 1024; /* trim 64K chunks */
4877 enum ndr_err_code ndr_ret;
4880 ok = test_setup_trim(torture, tree, tmp_ctx,
4882 &fh, 2 * trim_chunk_len, /* fill 128K file */
4883 SEC_RIGHTS_FILE_ALL,
4887 torture_fail(torture, "setup trim error");
4890 status = test_ioctl_trim_supported(torture, tree, tmp_ctx, &fh,
4892 torture_assert_ntstatus_ok(torture, status, "fsinfo");
4893 if (!trim_supported) {
4894 smb2_util_close(tree, fh);
4895 talloc_free(tmp_ctx);
4896 torture_skip(torture, "trim not supported\n");
4899 /* trim first chunk, leave second */
4900 trim_req.ranges[0].off = 0;
4901 trim_req.ranges[0].len = trim_chunk_len;
4903 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, &trim_req,
4904 (ndr_push_flags_fn_t)ndr_push_fsctl_file_level_trim_req);
4905 torture_assert_ndr_success(torture, ndr_ret,
4906 "ndr_push_fsctl_file_level_trim_req");
4908 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
4909 torture_assert_ntstatus_ok(torture, status, "FILE_LEVEL_TRIM_RANGE");
4911 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
4913 (ndr_pull_flags_fn_t)ndr_pull_fsctl_file_level_trim_rsp);
4914 torture_assert_ndr_success(torture, ndr_ret,
4915 "ndr_pull_fsctl_file_level_trim_rsp");
4917 torture_assert_int_equal(torture, trim_rsp.num_ranges_processed, 1, "");
4919 /* second half of the file should remain consitent */
4920 ok = check_pattern(torture, tree, tmp_ctx, fh, trim_chunk_len,
4921 trim_chunk_len, trim_chunk_len);
4922 torture_assert(torture, ok, "non-trimmed range inconsistent");
4927 static bool test_setup_dup_extents(struct torture_context *tctx,
4928 struct smb2_tree *tree,
4929 TALLOC_CTX *mem_ctx,
4930 struct smb2_handle *src_h,
4932 uint32_t src_desired_access,
4933 struct smb2_handle *dest_h,
4935 uint32_t dest_desired_access,
4936 struct fsctl_dup_extents_to_file *dup_ext_buf,
4937 union smb_ioctl *ioctl)
4941 ok = test_setup_create_fill(tctx, tree, mem_ctx, FNAME,
4942 src_h, src_size, src_desired_access,
4943 FILE_ATTRIBUTE_NORMAL);
4944 torture_assert(tctx, ok, "src file create fill");
4946 ok = test_setup_create_fill(tctx, tree, mem_ctx, FNAME2,
4947 dest_h, dest_size, dest_desired_access,
4948 FILE_ATTRIBUTE_NORMAL);
4949 torture_assert(tctx, ok, "dest file create fill");
4951 ZERO_STRUCTPN(ioctl);
4952 ioctl->smb2.level = RAW_IOCTL_SMB2;
4953 ioctl->smb2.in.file.handle = *dest_h;
4954 ioctl->smb2.in.function = FSCTL_DUP_EXTENTS_TO_FILE;
4955 ioctl->smb2.in.max_response_size = 0;
4956 ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
4958 ZERO_STRUCTPN(dup_ext_buf);
4959 smb2_push_handle(dup_ext_buf->source_fid, src_h);
4964 static bool test_ioctl_dup_extents_simple(struct torture_context *tctx,
4965 struct smb2_tree *tree)
4967 struct smb2_handle src_h;
4968 struct smb2_handle dest_h;
4970 union smb_ioctl ioctl;
4971 TALLOC_CTX *tmp_ctx = talloc_new(tree);
4972 struct fsctl_dup_extents_to_file dup_ext_buf;
4973 enum ndr_err_code ndr_ret;
4974 union smb_fileinfo io;
4975 union smb_setfileinfo sinfo;
4978 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
4979 &src_h, 4096, /* fill 4096 byte src file */
4980 SEC_RIGHTS_FILE_ALL,
4981 &dest_h, 0, /* 0 byte dest file */
4982 SEC_RIGHTS_FILE_ALL,
4986 torture_fail(tctx, "setup dup extents error");
4989 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
4990 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
4991 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
4993 smb2_util_close(tree, src_h);
4994 smb2_util_close(tree, dest_h);
4995 talloc_free(tmp_ctx);
4996 torture_skip(tctx, "block refcounting not supported\n");
4999 /* extend dest to match src len */
5001 sinfo.end_of_file_info.level =
5002 RAW_SFILEINFO_END_OF_FILE_INFORMATION;
5003 sinfo.end_of_file_info.in.file.handle = dest_h;
5004 sinfo.end_of_file_info.in.size = 4096;
5005 status = smb2_setinfo_file(tree, &sinfo);
5006 torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
5008 /* copy all src file data */
5009 dup_ext_buf.source_off = 0;
5010 dup_ext_buf.target_off = 0;
5011 dup_ext_buf.byte_count = 4096;
5013 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5015 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5016 torture_assert_ndr_success(tctx, ndr_ret,
5017 "ndr_push_fsctl_dup_extents_to_file");
5019 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5020 torture_assert_ntstatus_ok(tctx, status,
5021 "FSCTL_DUP_EXTENTS_TO_FILE");
5023 /* the file size shouldn't have been changed by this operation! */
5025 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5026 io.generic.in.file.handle = dest_h;
5027 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5028 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5029 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5030 4096, "size after IO");
5032 smb2_util_close(tree, src_h);
5033 smb2_util_close(tree, dest_h);
5035 /* reopen for pattern check */
5036 ok = test_setup_open(tctx, tree, tmp_ctx, FNAME, &src_h,
5037 SEC_RIGHTS_FILE_ALL, FILE_ATTRIBUTE_NORMAL);
5038 torture_assert_ntstatus_ok(tctx, status, "src open after dup");
5039 ok = test_setup_open(tctx, tree, tmp_ctx, FNAME2, &dest_h,
5040 SEC_RIGHTS_FILE_ALL, FILE_ATTRIBUTE_NORMAL);
5041 torture_assert_ntstatus_ok(tctx, status, "dest open after dup");
5043 ok = check_pattern(tctx, tree, tmp_ctx, src_h, 0, 4096, 0);
5045 torture_fail(tctx, "inconsistent src file data");
5048 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 4096, 0);
5050 torture_fail(tctx, "inconsistent dest file data");
5053 smb2_util_close(tree, src_h);
5054 smb2_util_close(tree, dest_h);
5055 talloc_free(tmp_ctx);
5059 static bool test_ioctl_dup_extents_len_beyond_dest(struct torture_context *tctx,
5060 struct smb2_tree *tree)
5062 struct smb2_handle src_h;
5063 struct smb2_handle dest_h;
5065 union smb_ioctl ioctl;
5066 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5067 struct fsctl_dup_extents_to_file dup_ext_buf;
5068 enum ndr_err_code ndr_ret;
5069 union smb_fileinfo io;
5070 union smb_setfileinfo sinfo;
5073 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5074 &src_h, 32768, /* fill 32768 byte src file */
5075 SEC_RIGHTS_FILE_ALL,
5076 &dest_h, 0, /* 0 byte dest file */
5077 SEC_RIGHTS_FILE_ALL,
5081 torture_fail(tctx, "setup dup extents error");
5084 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5085 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
5086 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5088 smb2_util_close(tree, src_h);
5089 smb2_util_close(tree, dest_h);
5090 talloc_free(tmp_ctx);
5091 torture_skip(tctx, "block refcounting not supported\n");
5095 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5096 io.generic.in.file.handle = dest_h;
5097 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5098 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5099 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5100 0, "size after IO");
5102 /* copy all src file data */
5103 dup_ext_buf.source_off = 0;
5104 dup_ext_buf.target_off = 0;
5105 dup_ext_buf.byte_count = 32768;
5107 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5109 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5110 torture_assert_ndr_success(tctx, ndr_ret,
5111 "ndr_push_fsctl_dup_extents_to_file");
5113 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5116 * 2.3.8 FSCTL_DUPLICATE_EXTENTS_TO_FILE Reply - this should fail, but
5117 * passes against WS2016 RTM!
5119 torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_SUPPORTED,
5120 "FSCTL_DUP_EXTENTS_TO_FILE");
5123 /* the file sizes shouldn't have been changed */
5125 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5126 io.generic.in.file.handle = src_h;
5127 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5128 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5129 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5130 32768, "size after IO");
5133 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5134 io.generic.in.file.handle = dest_h;
5135 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5136 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5137 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5138 0, "size after IO");
5142 sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
5143 sinfo.end_of_file_info.in.file.handle = dest_h;
5144 sinfo.end_of_file_info.in.size = 32768;
5145 status = smb2_setinfo_file(tree, &sinfo);
5146 torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
5148 ok = check_zero(tctx, tree, tmp_ctx, dest_h, 0, 32768);
5150 torture_fail(tctx, "inconsistent file data");
5153 /* reissue ioctl, now with enough space */
5154 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5155 torture_assert_ntstatus_ok(tctx, status,
5156 "FSCTL_DUP_EXTENTS_TO_FILE");
5158 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
5160 torture_fail(tctx, "inconsistent file data");
5163 smb2_util_close(tree, src_h);
5164 smb2_util_close(tree, dest_h);
5165 talloc_free(tmp_ctx);
5169 static bool test_ioctl_dup_extents_len_beyond_src(struct torture_context *tctx,
5170 struct smb2_tree *tree)
5172 struct smb2_handle src_h;
5173 struct smb2_handle dest_h;
5175 union smb_ioctl ioctl;
5176 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5177 struct fsctl_dup_extents_to_file dup_ext_buf;
5178 enum ndr_err_code ndr_ret;
5179 union smb_fileinfo io;
5182 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5183 &src_h, 32768, /* fill 32768 byte src file */
5184 SEC_RIGHTS_FILE_ALL,
5185 &dest_h, 0, /* 0 byte dest file */
5186 SEC_RIGHTS_FILE_ALL,
5190 torture_fail(tctx, "setup dup extents error");
5193 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5194 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
5195 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5197 smb2_util_close(tree, src_h);
5198 smb2_util_close(tree, dest_h);
5199 talloc_free(tmp_ctx);
5200 torture_skip(tctx, "block refcounting not supported\n");
5204 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5205 io.generic.in.file.handle = dest_h;
5206 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5207 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5208 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5209 0, "size after IO");
5211 /* exceed src file len */
5212 dup_ext_buf.source_off = 0;
5213 dup_ext_buf.target_off = 0;
5214 dup_ext_buf.byte_count = 32768 * 2;
5216 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5218 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5219 torture_assert_ndr_success(tctx, ndr_ret,
5220 "ndr_push_fsctl_dup_extents_to_file");
5222 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5223 torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_SUPPORTED,
5224 "FSCTL_DUP_EXTENTS_TO_FILE");
5226 /* the file sizes shouldn't have been changed */
5228 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5229 io.generic.in.file.handle = src_h;
5230 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5231 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5232 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5233 32768, "size after IO");
5236 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5237 io.generic.in.file.handle = dest_h;
5238 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5239 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5240 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5241 0, "size after IO");
5243 smb2_util_close(tree, src_h);
5244 smb2_util_close(tree, dest_h);
5245 talloc_free(tmp_ctx);
5249 static bool test_ioctl_dup_extents_len_zero(struct torture_context *tctx,
5250 struct smb2_tree *tree)
5252 struct smb2_handle src_h;
5253 struct smb2_handle dest_h;
5255 union smb_ioctl ioctl;
5256 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5257 struct fsctl_dup_extents_to_file dup_ext_buf;
5258 enum ndr_err_code ndr_ret;
5259 union smb_fileinfo io;
5262 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5263 &src_h, 32768, /* fill 32768 byte src file */
5264 SEC_RIGHTS_FILE_ALL,
5265 &dest_h, 0, /* 0 byte dest file */
5266 SEC_RIGHTS_FILE_ALL,
5270 torture_fail(tctx, "setup dup extents error");
5273 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5274 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
5275 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5277 smb2_util_close(tree, src_h);
5278 smb2_util_close(tree, dest_h);
5279 talloc_free(tmp_ctx);
5280 torture_skip(tctx, "block refcounting not supported\n");
5284 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5285 io.generic.in.file.handle = dest_h;
5286 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5287 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5288 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5289 0, "size after IO");
5291 dup_ext_buf.source_off = 0;
5292 dup_ext_buf.target_off = 0;
5293 dup_ext_buf.byte_count = 0;
5295 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5297 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5298 torture_assert_ndr_success(tctx, ndr_ret,
5299 "ndr_push_fsctl_dup_extents_to_file");
5301 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5302 torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
5304 /* the file sizes shouldn't have been changed */
5306 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5307 io.generic.in.file.handle = src_h;
5308 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5309 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5310 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5311 32768, "size after IO");
5314 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5315 io.generic.in.file.handle = dest_h;
5316 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5317 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5318 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5319 0, "size after IO");
5321 smb2_util_close(tree, src_h);
5322 smb2_util_close(tree, dest_h);
5323 talloc_free(tmp_ctx);
5327 static bool test_ioctl_dup_extents_sparse_src(struct torture_context *tctx,
5328 struct smb2_tree *tree)
5330 struct smb2_handle src_h;
5331 struct smb2_handle dest_h;
5333 union smb_ioctl ioctl;
5334 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5335 struct fsctl_dup_extents_to_file dup_ext_buf;
5336 enum ndr_err_code ndr_ret;
5337 union smb_setfileinfo sinfo;
5340 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5341 &src_h, 0, /* filled after sparse flag */
5342 SEC_RIGHTS_FILE_ALL,
5343 &dest_h, 0, /* 0 byte dest file */
5344 SEC_RIGHTS_FILE_ALL,
5348 torture_fail(tctx, "setup dup extents error");
5351 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5352 FILE_SUPPORTS_BLOCK_REFCOUNTING
5353 | FILE_SUPPORTS_SPARSE_FILES, &ok);
5354 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5356 smb2_util_close(tree, src_h);
5357 smb2_util_close(tree, dest_h);
5358 talloc_free(tmp_ctx);
5360 "block refcounting and sparse files not supported\n");
5363 /* set sparse flag on src */
5364 status = test_ioctl_sparse_req(tctx, tmp_ctx, tree, src_h, true);
5365 torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_SPARSE");
5367 ok = write_pattern(tctx, tree, tmp_ctx, src_h, 0, 4096, 0);
5368 torture_assert(tctx, ok, "write pattern");
5372 sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
5373 sinfo.end_of_file_info.in.file.handle = dest_h;
5374 sinfo.end_of_file_info.in.size = 4096;
5375 status = smb2_setinfo_file(tree, &sinfo);
5376 torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
5378 /* copy all src file data */
5379 dup_ext_buf.source_off = 0;
5380 dup_ext_buf.target_off = 0;
5381 dup_ext_buf.byte_count = 4096;
5383 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5385 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5386 torture_assert_ndr_success(tctx, ndr_ret,
5387 "ndr_push_fsctl_dup_extents_to_file");
5390 * src is sparse, but spec says: 2.3.8 FSCTL_DUPLICATE_EXTENTS_TO_FILE
5391 * Reply... STATUS_NOT_SUPPORTED: Target file is sparse, while source
5392 * is a non-sparse file.
5394 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5395 torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_SUPPORTED,
5396 "FSCTL_DUP_EXTENTS_TO_FILE");
5398 smb2_util_close(tree, src_h);
5399 smb2_util_close(tree, dest_h);
5400 talloc_free(tmp_ctx);
5404 static bool test_ioctl_dup_extents_sparse_dest(struct torture_context *tctx,
5405 struct smb2_tree *tree)
5407 struct smb2_handle src_h;
5408 struct smb2_handle dest_h;
5410 union smb_ioctl ioctl;
5411 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5412 struct fsctl_dup_extents_to_file dup_ext_buf;
5413 enum ndr_err_code ndr_ret;
5414 union smb_setfileinfo sinfo;
5417 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5418 &src_h, 4096, /* fill 4096 byte src file */
5419 SEC_RIGHTS_FILE_ALL,
5420 &dest_h, 0, /* 0 byte dest file */
5421 SEC_RIGHTS_FILE_ALL,
5425 torture_fail(tctx, "setup dup extents error");
5428 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5429 FILE_SUPPORTS_BLOCK_REFCOUNTING
5430 | FILE_SUPPORTS_SPARSE_FILES, &ok);
5431 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5433 smb2_util_close(tree, src_h);
5434 smb2_util_close(tree, dest_h);
5435 talloc_free(tmp_ctx);
5437 "block refcounting and sparse files not supported\n");
5440 /* set sparse flag on dest */
5441 status = test_ioctl_sparse_req(tctx, tmp_ctx, tree, dest_h, true);
5442 torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_SPARSE");
5446 sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
5447 sinfo.end_of_file_info.in.file.handle = dest_h;
5448 sinfo.end_of_file_info.in.size = dup_ext_buf.byte_count;
5449 status = smb2_setinfo_file(tree, &sinfo);
5450 torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
5452 /* copy all src file data */
5453 dup_ext_buf.source_off = 0;
5454 dup_ext_buf.target_off = 0;
5455 dup_ext_buf.byte_count = 4096;
5457 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5459 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5460 torture_assert_ndr_success(tctx, ndr_ret,
5461 "ndr_push_fsctl_dup_extents_to_file");
5464 * dest is sparse, but spec says: 2.3.8 FSCTL_DUPLICATE_EXTENTS_TO_FILE
5465 * Reply... STATUS_NOT_SUPPORTED: Target file is sparse, while source
5466 * is a non-sparse file.
5468 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5469 torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
5471 smb2_util_close(tree, src_h);
5472 smb2_util_close(tree, dest_h);
5473 talloc_free(tmp_ctx);
5477 static bool test_ioctl_dup_extents_sparse_both(struct torture_context *tctx,
5478 struct smb2_tree *tree)
5480 struct smb2_handle src_h;
5481 struct smb2_handle dest_h;
5483 union smb_ioctl ioctl;
5484 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5485 struct fsctl_dup_extents_to_file dup_ext_buf;
5486 enum ndr_err_code ndr_ret;
5487 union smb_setfileinfo sinfo;
5490 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5491 &src_h, 0, /* fill 4096 byte src file */
5492 SEC_RIGHTS_FILE_ALL,
5493 &dest_h, 0, /* 0 byte dest file */
5494 SEC_RIGHTS_FILE_ALL,
5498 torture_fail(tctx, "setup dup extents error");
5501 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5502 FILE_SUPPORTS_BLOCK_REFCOUNTING
5503 | FILE_SUPPORTS_SPARSE_FILES, &ok);
5504 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5506 smb2_util_close(tree, src_h);
5507 smb2_util_close(tree, dest_h);
5508 talloc_free(tmp_ctx);
5510 "block refcounting and sparse files not supported\n");
5513 /* set sparse flag on src and dest */
5514 status = test_ioctl_sparse_req(tctx, tmp_ctx, tree, src_h, true);
5515 torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_SPARSE");
5516 status = test_ioctl_sparse_req(tctx, tmp_ctx, tree, dest_h, true);
5517 torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_SPARSE");
5519 ok = write_pattern(tctx, tree, tmp_ctx, src_h, 0, 4096, 0);
5520 torture_assert(tctx, ok, "write pattern");
5524 sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
5525 sinfo.end_of_file_info.in.file.handle = dest_h;
5526 sinfo.end_of_file_info.in.size = 4096;
5527 status = smb2_setinfo_file(tree, &sinfo);
5528 torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
5530 /* copy all src file data */
5531 dup_ext_buf.source_off = 0;
5532 dup_ext_buf.target_off = 0;
5533 dup_ext_buf.byte_count = 4096;
5535 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5537 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5538 torture_assert_ndr_success(tctx, ndr_ret,
5539 "ndr_push_fsctl_dup_extents_to_file");
5541 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5542 torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
5544 smb2_util_close(tree, src_h);
5545 smb2_util_close(tree, dest_h);
5547 /* reopen for pattern check */
5548 ok = test_setup_open(tctx, tree, tmp_ctx, FNAME2, &dest_h,
5549 SEC_RIGHTS_FILE_ALL, FILE_ATTRIBUTE_NORMAL);
5550 torture_assert_ntstatus_ok(tctx, status, "dest open ater dup");
5552 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 4096, 0);
5554 torture_fail(tctx, "inconsistent file data");
5557 smb2_util_close(tree, dest_h);
5558 talloc_free(tmp_ctx);
5562 static bool test_ioctl_dup_extents_src_is_dest(struct torture_context *tctx,
5563 struct smb2_tree *tree)
5565 struct smb2_handle src_h;
5566 struct smb2_handle dest_h;
5568 union smb_ioctl ioctl;
5569 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5570 struct fsctl_dup_extents_to_file dup_ext_buf;
5571 enum ndr_err_code ndr_ret;
5572 union smb_fileinfo io;
5575 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5576 &src_h, 32768, /* fill 32768 byte src file */
5577 SEC_RIGHTS_FILE_ALL,
5579 SEC_RIGHTS_FILE_ALL,
5583 torture_fail(tctx, "setup dup extents error");
5585 /* dest_h not needed for this test */
5586 smb2_util_close(tree, dest_h);
5588 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5589 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
5590 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5592 smb2_util_close(tree, src_h);
5593 talloc_free(tmp_ctx);
5594 torture_skip(tctx, "block refcounting not supported\n");
5597 /* src and dest are the same file handle */
5598 ioctl.smb2.in.file.handle = src_h;
5600 /* no overlap between src and tgt */
5601 dup_ext_buf.source_off = 0;
5602 dup_ext_buf.target_off = 16384;
5603 dup_ext_buf.byte_count = 16384;
5605 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5607 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5608 torture_assert_ndr_success(tctx, ndr_ret,
5609 "ndr_push_fsctl_dup_extents_to_file");
5611 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5612 torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
5614 /* the file size shouldn't have been changed */
5616 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5617 io.generic.in.file.handle = src_h;
5618 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5619 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5620 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5621 32768, "size after IO");
5623 ok = check_pattern(tctx, tree, tmp_ctx, src_h, 0, 16384, 0);
5625 torture_fail(tctx, "inconsistent file data");
5627 ok = check_pattern(tctx, tree, tmp_ctx, src_h, 16384, 16384, 0);
5629 torture_fail(tctx, "inconsistent file data");
5632 smb2_util_close(tree, src_h);
5633 talloc_free(tmp_ctx);
5638 * unlike copy-chunk, dup extents doesn't support overlapping ranges between
5639 * source and target. This makes it a *lot* cleaner to implement on the server.
5642 test_ioctl_dup_extents_src_is_dest_overlap(struct torture_context *tctx,
5643 struct smb2_tree *tree)
5645 struct smb2_handle src_h;
5646 struct smb2_handle dest_h;
5648 union smb_ioctl ioctl;
5649 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5650 struct fsctl_dup_extents_to_file dup_ext_buf;
5651 enum ndr_err_code ndr_ret;
5652 union smb_fileinfo io;
5655 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5656 &src_h, 32768, /* fill 32768 byte src file */
5657 SEC_RIGHTS_FILE_ALL,
5659 SEC_RIGHTS_FILE_ALL,
5663 torture_fail(tctx, "setup dup extents error");
5665 /* dest_h not needed for this test */
5666 smb2_util_close(tree, dest_h);
5668 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5669 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
5670 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5672 smb2_util_close(tree, src_h);
5673 talloc_free(tmp_ctx);
5674 torture_skip(tctx, "block refcounting not supported\n");
5677 /* src and dest are the same file handle */
5678 ioctl.smb2.in.file.handle = src_h;
5680 /* 8K overlap between src and tgt */
5681 dup_ext_buf.source_off = 0;
5682 dup_ext_buf.target_off = 8192;
5683 dup_ext_buf.byte_count = 16384;
5685 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5687 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5688 torture_assert_ndr_success(tctx, ndr_ret,
5689 "ndr_push_fsctl_dup_extents_to_file");
5691 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5692 torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_SUPPORTED,
5693 "FSCTL_DUP_EXTENTS_TO_FILE");
5695 /* the file size and data should match beforehand */
5697 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5698 io.generic.in.file.handle = src_h;
5699 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5700 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5701 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5702 32768, "size after IO");
5704 ok = check_pattern(tctx, tree, tmp_ctx, src_h, 0, 32768, 0);
5706 torture_fail(tctx, "inconsistent file data");
5709 smb2_util_close(tree, src_h);
5710 talloc_free(tmp_ctx);
5715 * The compression tests won't run against Windows servers yet - ReFS doesn't
5716 * (yet) offer support for compression.
5718 static bool test_ioctl_dup_extents_compressed_src(struct torture_context *tctx,
5719 struct smb2_tree *tree)
5721 struct smb2_handle src_h;
5722 struct smb2_handle dest_h;
5724 union smb_ioctl ioctl;
5725 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5726 struct fsctl_dup_extents_to_file dup_ext_buf;
5727 enum ndr_err_code ndr_ret;
5728 union smb_setfileinfo sinfo;
5731 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5732 &src_h, 0, /* filled after compressed flag */
5733 SEC_RIGHTS_FILE_ALL,
5735 SEC_RIGHTS_FILE_ALL,
5739 torture_fail(tctx, "setup dup extents error");
5742 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5743 FILE_SUPPORTS_BLOCK_REFCOUNTING
5744 | FILE_FILE_COMPRESSION, &ok);
5745 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5747 smb2_util_close(tree, src_h);
5748 smb2_util_close(tree, dest_h);
5749 talloc_free(tmp_ctx);
5751 "block refcounting and compressed files not supported\n");
5754 /* set compressed flag on src */
5755 status = test_ioctl_compress_set(tctx, tmp_ctx, tree, src_h,
5756 COMPRESSION_FORMAT_DEFAULT);
5757 torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_COMPRESSION");
5759 ok = write_pattern(tctx, tree, tmp_ctx, src_h, 0, 4096, 0);
5760 torture_assert(tctx, ok, "write pattern");
5764 sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
5765 sinfo.end_of_file_info.in.file.handle = dest_h;
5766 sinfo.end_of_file_info.in.size = 4096;
5767 status = smb2_setinfo_file(tree, &sinfo);
5768 torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
5770 /* copy all src file data */
5771 dup_ext_buf.source_off = 0;
5772 dup_ext_buf.target_off = 0;
5773 dup_ext_buf.byte_count = 4096;
5775 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5777 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5778 torture_assert_ndr_success(tctx, ndr_ret,
5779 "ndr_push_fsctl_dup_extents_to_file");
5781 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5782 torture_assert_ntstatus_ok(tctx, status,
5783 "FSCTL_DUP_EXTENTS_TO_FILE");
5785 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 4096, 0);
5787 torture_fail(tctx, "inconsistent file data");
5790 smb2_util_close(tree, src_h);
5791 smb2_util_close(tree, dest_h);
5792 talloc_free(tmp_ctx);
5796 static bool test_ioctl_dup_extents_compressed_dest(struct torture_context *tctx,
5797 struct smb2_tree *tree)
5799 struct smb2_handle src_h;
5800 struct smb2_handle dest_h;
5802 union smb_ioctl ioctl;
5803 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5804 struct fsctl_dup_extents_to_file dup_ext_buf;
5805 enum ndr_err_code ndr_ret;
5806 union smb_setfileinfo sinfo;
5809 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5811 SEC_RIGHTS_FILE_ALL,
5813 SEC_RIGHTS_FILE_ALL,
5817 torture_fail(tctx, "setup dup extents error");
5820 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5821 FILE_SUPPORTS_BLOCK_REFCOUNTING
5822 | FILE_FILE_COMPRESSION, &ok);
5823 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5825 smb2_util_close(tree, src_h);
5826 smb2_util_close(tree, dest_h);
5827 talloc_free(tmp_ctx);
5829 "block refcounting and compressed files not supported\n");
5832 /* set compressed flag on dest */
5833 status = test_ioctl_compress_set(tctx, tmp_ctx, tree, dest_h,
5834 COMPRESSION_FORMAT_DEFAULT);
5835 torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_COMPRESSION");
5839 sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
5840 sinfo.end_of_file_info.in.file.handle = dest_h;
5841 sinfo.end_of_file_info.in.size = dup_ext_buf.byte_count;
5842 status = smb2_setinfo_file(tree, &sinfo);
5843 torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
5845 /* copy all src file data */
5846 dup_ext_buf.source_off = 0;
5847 dup_ext_buf.target_off = 0;
5848 dup_ext_buf.byte_count = 4096;
5850 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5852 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5853 torture_assert_ndr_success(tctx, ndr_ret,
5854 "ndr_push_fsctl_dup_extents_to_file");
5856 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5857 torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_SUPPORTED,
5858 "FSCTL_DUP_EXTENTS_TO_FILE");
5860 smb2_util_close(tree, src_h);
5861 smb2_util_close(tree, dest_h);
5862 talloc_free(tmp_ctx);
5866 static bool test_ioctl_dup_extents_bad_handle(struct torture_context *tctx,
5867 struct smb2_tree *tree)
5869 struct smb2_handle src_h;
5870 struct smb2_handle dest_h;
5871 struct smb2_handle bogus_h;
5873 union smb_ioctl ioctl;
5874 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5875 struct fsctl_dup_extents_to_file dup_ext_buf;
5876 enum ndr_err_code ndr_ret;
5879 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5880 &src_h, 32768, /* fill 32768 byte src file */
5881 SEC_RIGHTS_FILE_ALL,
5883 SEC_RIGHTS_FILE_ALL,
5887 torture_fail(tctx, "setup dup extents error");
5890 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5891 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
5892 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5894 smb2_util_close(tree, src_h);
5895 smb2_util_close(tree, dest_h);
5896 talloc_free(tmp_ctx);
5897 torture_skip(tctx, "block refcounting not supported\n");
5900 /* open and close a file, keeping the handle as now a "bogus" handle */
5901 ok = test_setup_create_fill(tctx, tree, tmp_ctx, "bogus_file",
5902 &bogus_h, 0, SEC_RIGHTS_FILE_ALL,
5903 FILE_ATTRIBUTE_NORMAL);
5904 torture_assert(tctx, ok, "bogus file create fill");
5905 smb2_util_close(tree, bogus_h);
5907 /* bogus dest file handle */
5908 ioctl.smb2.in.file.handle = bogus_h;
5910 dup_ext_buf.source_off = 0;
5911 dup_ext_buf.target_off = 0;
5912 dup_ext_buf.byte_count = 32768;
5914 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5916 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5917 torture_assert_ndr_success(tctx, ndr_ret,
5918 "ndr_push_fsctl_dup_extents_to_file");
5920 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5921 torture_assert_ntstatus_equal(tctx, status, NT_STATUS_FILE_CLOSED,
5922 "FSCTL_DUP_EXTENTS_TO_FILE");
5924 ok = check_pattern(tctx, tree, tmp_ctx, src_h, 0, 32768, 0);
5926 torture_fail(tctx, "inconsistent file data");
5928 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
5930 torture_fail(tctx, "inconsistent file data");
5933 /* reinstate dest, add bogus src file handle */
5934 ioctl.smb2.in.file.handle = dest_h;
5935 smb2_push_handle(dup_ext_buf.source_fid, &bogus_h);
5937 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5939 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5940 torture_assert_ndr_success(tctx, ndr_ret,
5941 "ndr_push_fsctl_dup_extents_to_file");
5943 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5944 torture_assert_ntstatus_equal(tctx, status, NT_STATUS_INVALID_HANDLE,
5945 "FSCTL_DUP_EXTENTS_TO_FILE");
5947 ok = check_pattern(tctx, tree, tmp_ctx, src_h, 0, 32768, 0);
5949 torture_fail(tctx, "inconsistent file data");
5951 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
5953 torture_fail(tctx, "inconsistent file data");
5956 smb2_util_close(tree, src_h);
5957 smb2_util_close(tree, dest_h);
5958 talloc_free(tmp_ctx);
5962 static bool test_ioctl_dup_extents_src_lck(struct torture_context *tctx,
5963 struct smb2_tree *tree)
5965 struct smb2_handle src_h;
5966 struct smb2_handle src_h2;
5967 struct smb2_handle dest_h;
5969 union smb_ioctl ioctl;
5970 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5971 struct fsctl_dup_extents_to_file dup_ext_buf;
5972 enum ndr_err_code ndr_ret;
5974 struct smb2_lock lck;
5975 struct smb2_lock_element el[1];
5977 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5978 &src_h, 32768, /* fill 32768 byte src file */
5979 SEC_RIGHTS_FILE_ALL,
5981 SEC_RIGHTS_FILE_ALL,
5985 torture_fail(tctx, "setup dup extents error");
5988 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5989 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
5990 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5992 smb2_util_close(tree, src_h);
5993 smb2_util_close(tree, dest_h);
5994 talloc_free(tmp_ctx);
5995 torture_skip(tctx, "block refcounting not supported\n");
5998 /* dest pattern is different to src */
5999 ok = write_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 32768);
6000 torture_assert(tctx, ok, "write pattern");
6002 /* setup dup ext req, values used for locking */
6003 dup_ext_buf.source_off = 0;
6004 dup_ext_buf.target_off = 0;
6005 dup_ext_buf.byte_count = 32768;
6007 /* open and lock the dup extents src file */
6008 status = torture_smb2_testfile(tree, FNAME, &src_h2);
6009 torture_assert_ntstatus_ok(tctx, status, "2nd src open");
6011 lck.in.lock_count = 0x0001;
6012 lck.in.lock_sequence = 0x00000000;
6013 lck.in.file.handle = src_h2;
6015 el[0].offset = dup_ext_buf.source_off;
6016 el[0].length = dup_ext_buf.byte_count;
6018 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
6020 status = smb2_lock(tree, &lck);
6021 torture_assert_ntstatus_ok(tctx, status, "lock");
6023 status = smb2_util_write(tree, src_h,
6024 "conflicted", 0, sizeof("conflicted"));
6025 torture_assert_ntstatus_equal(tctx, status,
6026 NT_STATUS_FILE_LOCK_CONFLICT, "file write");
6028 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6030 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6031 torture_assert_ndr_success(tctx, ndr_ret,
6032 "ndr_push_fsctl_dup_extents_to_file");
6035 * In contrast to copy-chunk, dup extents doesn't cause a lock conflict
6038 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6039 torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
6041 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
6043 torture_fail(tctx, "inconsistent file data");
6046 lck.in.lock_count = 0x0001;
6047 lck.in.lock_sequence = 0x00000001;
6048 lck.in.file.handle = src_h2;
6050 el[0].offset = dup_ext_buf.source_off;
6051 el[0].length = dup_ext_buf.byte_count;
6053 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
6054 status = smb2_lock(tree, &lck);
6055 torture_assert_ntstatus_ok(tctx, status, "unlock");
6057 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6058 torture_assert_ntstatus_ok(tctx, status,
6059 "FSCTL_DUP_EXTENTS_TO_FILE unlocked");
6061 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
6063 torture_fail(tctx, "inconsistent file data");
6066 smb2_util_close(tree, src_h2);
6067 smb2_util_close(tree, src_h);
6068 smb2_util_close(tree, dest_h);
6069 talloc_free(tmp_ctx);
6073 static bool test_ioctl_dup_extents_dest_lck(struct torture_context *tctx,
6074 struct smb2_tree *tree)
6076 struct smb2_handle src_h;
6077 struct smb2_handle dest_h;
6078 struct smb2_handle dest_h2;
6080 union smb_ioctl ioctl;
6081 TALLOC_CTX *tmp_ctx = talloc_new(tree);
6082 struct fsctl_dup_extents_to_file dup_ext_buf;
6083 enum ndr_err_code ndr_ret;
6085 struct smb2_lock lck;
6086 struct smb2_lock_element el[1];
6088 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
6089 &src_h, 32768, /* fill 32768 byte src file */
6090 SEC_RIGHTS_FILE_ALL,
6092 SEC_RIGHTS_FILE_ALL,
6096 torture_fail(tctx, "setup dup extents error");
6099 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
6100 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
6101 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
6103 smb2_util_close(tree, src_h);
6104 smb2_util_close(tree, dest_h);
6105 talloc_free(tmp_ctx);
6106 torture_skip(tctx, "block refcounting not supported\n");
6109 /* dest pattern is different to src */
6110 ok = write_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 32768);
6111 torture_assert(tctx, ok, "write pattern");
6113 /* setup dup ext req, values used for locking */
6114 dup_ext_buf.source_off = 0;
6115 dup_ext_buf.target_off = 0;
6116 dup_ext_buf.byte_count = 32768;
6118 /* open and lock the dup extents dest file */
6119 status = torture_smb2_testfile(tree, FNAME2, &dest_h2);
6120 torture_assert_ntstatus_ok(tctx, status, "2nd src open");
6122 lck.in.lock_count = 0x0001;
6123 lck.in.lock_sequence = 0x00000000;
6124 lck.in.file.handle = dest_h2;
6126 el[0].offset = dup_ext_buf.source_off;
6127 el[0].length = dup_ext_buf.byte_count;
6129 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
6131 status = smb2_lock(tree, &lck);
6132 torture_assert_ntstatus_ok(tctx, status, "lock");
6134 status = smb2_util_write(tree, dest_h,
6135 "conflicted", 0, sizeof("conflicted"));
6136 torture_assert_ntstatus_equal(tctx, status,
6137 NT_STATUS_FILE_LOCK_CONFLICT, "file write");
6139 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6141 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6142 torture_assert_ndr_success(tctx, ndr_ret,
6143 "ndr_push_fsctl_dup_extents_to_file");
6146 * In contrast to copy-chunk, dup extents doesn't cause a lock conflict
6149 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6150 torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
6152 lck.in.lock_count = 0x0001;
6153 lck.in.lock_sequence = 0x00000001;
6154 lck.in.file.handle = dest_h2;
6156 el[0].offset = dup_ext_buf.source_off;
6157 el[0].length = dup_ext_buf.byte_count;
6159 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
6160 status = smb2_lock(tree, &lck);
6161 torture_assert_ntstatus_ok(tctx, status, "unlock");
6163 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6164 torture_assert_ntstatus_ok(tctx, status,
6165 "FSCTL_DUP_EXTENTS_TO_FILE unlocked");
6167 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
6169 torture_fail(tctx, "inconsistent file data");
6172 smb2_util_close(tree, src_h);
6173 smb2_util_close(tree, dest_h);
6174 smb2_util_close(tree, dest_h2);
6175 talloc_free(tmp_ctx);
6180 * testing of SMB2 ioctls
6182 struct torture_suite *torture_smb2_ioctl_init(void)
6184 struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "ioctl");
6186 torture_suite_add_1smb2_test(suite, "shadow_copy",
6187 test_ioctl_get_shadow_copy);
6188 torture_suite_add_1smb2_test(suite, "req_resume_key",
6189 test_ioctl_req_resume_key);
6190 torture_suite_add_1smb2_test(suite, "copy_chunk_simple",
6191 test_ioctl_copy_chunk_simple);
6192 torture_suite_add_1smb2_test(suite, "copy_chunk_multi",
6193 test_ioctl_copy_chunk_multi);
6194 torture_suite_add_1smb2_test(suite, "copy_chunk_tiny",
6195 test_ioctl_copy_chunk_tiny);
6196 torture_suite_add_1smb2_test(suite, "copy_chunk_overwrite",
6197 test_ioctl_copy_chunk_over);
6198 torture_suite_add_1smb2_test(suite, "copy_chunk_append",
6199 test_ioctl_copy_chunk_append);
6200 torture_suite_add_1smb2_test(suite, "copy_chunk_limits",
6201 test_ioctl_copy_chunk_limits);
6202 torture_suite_add_1smb2_test(suite, "copy_chunk_src_lock",
6203 test_ioctl_copy_chunk_src_lck);
6204 torture_suite_add_1smb2_test(suite, "copy_chunk_dest_lock",
6205 test_ioctl_copy_chunk_dest_lck);
6206 torture_suite_add_1smb2_test(suite, "copy_chunk_bad_key",
6207 test_ioctl_copy_chunk_bad_key);
6208 torture_suite_add_1smb2_test(suite, "copy_chunk_src_is_dest",
6209 test_ioctl_copy_chunk_src_is_dest);
6210 torture_suite_add_1smb2_test(suite, "copy_chunk_src_is_dest_overlap",
6211 test_ioctl_copy_chunk_src_is_dest_overlap);
6212 torture_suite_add_1smb2_test(suite, "copy_chunk_bad_access",
6213 test_ioctl_copy_chunk_bad_access);
6214 torture_suite_add_1smb2_test(suite, "copy_chunk_write_access",
6215 test_ioctl_copy_chunk_write_access);
6216 torture_suite_add_1smb2_test(suite, "copy_chunk_src_exceed",
6217 test_ioctl_copy_chunk_src_exceed);
6218 torture_suite_add_1smb2_test(suite, "copy_chunk_src_exceed_multi",
6219 test_ioctl_copy_chunk_src_exceed_multi);
6220 torture_suite_add_1smb2_test(suite, "copy_chunk_sparse_dest",
6221 test_ioctl_copy_chunk_sparse_dest);
6222 torture_suite_add_1smb2_test(suite, "copy_chunk_max_output_sz",
6223 test_ioctl_copy_chunk_max_output_sz);
6224 torture_suite_add_1smb2_test(suite, "copy_chunk_zero_length",
6225 test_ioctl_copy_chunk_zero_length);
6226 torture_suite_add_1smb2_test(suite, "compress_file_flag",
6227 test_ioctl_compress_file_flag);
6228 torture_suite_add_1smb2_test(suite, "compress_dir_inherit",
6229 test_ioctl_compress_dir_inherit);
6230 torture_suite_add_1smb2_test(suite, "compress_invalid_format",
6231 test_ioctl_compress_invalid_format);
6232 torture_suite_add_1smb2_test(suite, "compress_invalid_buf",
6233 test_ioctl_compress_invalid_buf);
6234 torture_suite_add_1smb2_test(suite, "compress_query_file_attr",
6235 test_ioctl_compress_query_file_attr);
6236 torture_suite_add_1smb2_test(suite, "compress_create_with_attr",
6237 test_ioctl_compress_create_with_attr);
6238 torture_suite_add_1smb2_test(suite, "compress_inherit_disable",
6239 test_ioctl_compress_inherit_disable);
6240 torture_suite_add_1smb2_test(suite, "compress_set_file_attr",
6241 test_ioctl_compress_set_file_attr);
6242 torture_suite_add_1smb2_test(suite, "compress_perms",
6243 test_ioctl_compress_perms);
6244 torture_suite_add_1smb2_test(suite, "compress_notsup_get",
6245 test_ioctl_compress_notsup_get);
6246 torture_suite_add_1smb2_test(suite, "compress_notsup_set",
6247 test_ioctl_compress_notsup_set);
6248 torture_suite_add_1smb2_test(suite, "network_interface_info",
6249 test_ioctl_network_interface_info);
6250 torture_suite_add_1smb2_test(suite, "sparse_file_flag",
6251 test_ioctl_sparse_file_flag);
6252 torture_suite_add_1smb2_test(suite, "sparse_file_attr",
6253 test_ioctl_sparse_file_attr);
6254 torture_suite_add_1smb2_test(suite, "sparse_dir_flag",
6255 test_ioctl_sparse_dir_flag);
6256 torture_suite_add_1smb2_test(suite, "sparse_set_nobuf",
6257 test_ioctl_sparse_set_nobuf);
6258 torture_suite_add_1smb2_test(suite, "sparse_set_oversize",
6259 test_ioctl_sparse_set_oversize);
6260 torture_suite_add_1smb2_test(suite, "sparse_qar",
6261 test_ioctl_sparse_qar);
6262 torture_suite_add_1smb2_test(suite, "sparse_qar_malformed",
6263 test_ioctl_sparse_qar_malformed);
6264 torture_suite_add_1smb2_test(suite, "sparse_punch",
6265 test_ioctl_sparse_punch);
6266 torture_suite_add_1smb2_test(suite, "sparse_hole_dealloc",
6267 test_ioctl_sparse_hole_dealloc);
6268 torture_suite_add_1smb2_test(suite, "sparse_compressed",
6269 test_ioctl_sparse_compressed);
6270 torture_suite_add_1smb2_test(suite, "sparse_copy_chunk",
6271 test_ioctl_sparse_copy_chunk);
6272 torture_suite_add_1smb2_test(suite, "sparse_punch_invalid",
6273 test_ioctl_sparse_punch_invalid);
6274 torture_suite_add_1smb2_test(suite, "sparse_perms",
6275 test_ioctl_sparse_perms);
6276 torture_suite_add_1smb2_test(suite, "sparse_lock",
6277 test_ioctl_sparse_lck);
6278 torture_suite_add_1smb2_test(suite, "sparse_qar_ob1",
6279 test_ioctl_sparse_qar_ob1);
6280 torture_suite_add_1smb2_test(suite, "sparse_qar_multi",
6281 test_ioctl_sparse_qar_multi);
6282 torture_suite_add_1smb2_test(suite, "sparse_qar_overflow",
6283 test_ioctl_sparse_qar_overflow);
6284 torture_suite_add_1smb2_test(suite, "trim_simple",
6285 test_ioctl_trim_simple);
6286 torture_suite_add_1smb2_test(suite, "dup_extents_simple",
6287 test_ioctl_dup_extents_simple);
6288 torture_suite_add_1smb2_test(suite, "dup_extents_len_beyond_dest",
6289 test_ioctl_dup_extents_len_beyond_dest);
6290 torture_suite_add_1smb2_test(suite, "dup_extents_len_beyond_src",
6291 test_ioctl_dup_extents_len_beyond_src);
6292 torture_suite_add_1smb2_test(suite, "dup_extents_len_zero",
6293 test_ioctl_dup_extents_len_zero);
6294 torture_suite_add_1smb2_test(suite, "dup_extents_sparse_src",
6295 test_ioctl_dup_extents_sparse_src);
6296 torture_suite_add_1smb2_test(suite, "dup_extents_sparse_dest",
6297 test_ioctl_dup_extents_sparse_dest);
6298 torture_suite_add_1smb2_test(suite, "dup_extents_sparse_both",
6299 test_ioctl_dup_extents_sparse_both);
6300 torture_suite_add_1smb2_test(suite, "dup_extents_src_is_dest",
6301 test_ioctl_dup_extents_src_is_dest);
6302 torture_suite_add_1smb2_test(suite, "dup_extents_src_is_dest_overlap",
6303 test_ioctl_dup_extents_src_is_dest_overlap);
6304 torture_suite_add_1smb2_test(suite, "dup_extents_compressed_src",
6305 test_ioctl_dup_extents_compressed_src);
6306 torture_suite_add_1smb2_test(suite, "dup_extents_compressed_dest",
6307 test_ioctl_dup_extents_compressed_dest);
6308 torture_suite_add_1smb2_test(suite, "dup_extents_bad_handle",
6309 test_ioctl_dup_extents_bad_handle);
6310 torture_suite_add_1smb2_test(suite, "dup_extents_src_lock",
6311 test_ioctl_dup_extents_src_lck);
6312 torture_suite_add_1smb2_test(suite, "dup_extents_dest_lock",
6313 test_ioctl_dup_extents_dest_lck);
6315 suite->description = talloc_strdup(suite, "SMB2-IOCTL tests");