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_SET_COMPRESSION default");
2614 * Despite not supporting compression, we should get a successful
2615 * response for set(COMPRESSION_FORMAT_NONE) - like WS2016 ReFS.
2617 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
2618 COMPRESSION_FORMAT_NONE);
2619 torture_assert_ntstatus_ok(torture, status,
2620 "FSCTL_SET_COMPRESSION none");
2622 smb2_util_close(tree, fh);
2623 talloc_free(tmp_ctx);
2628 basic testing of the SMB2 FSCTL_QUERY_NETWORK_INTERFACE_INFO ioctl
2630 static bool test_ioctl_network_interface_info(struct torture_context *torture,
2631 struct smb2_tree *tree)
2633 union smb_ioctl ioctl;
2634 struct smb2_handle fh;
2636 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2637 struct fsctl_net_iface_info net_iface;
2638 enum ndr_err_code ndr_ret;
2641 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
2642 if (!(caps & SMB2_CAP_MULTI_CHANNEL)) {
2643 torture_skip(torture, "server doesn't support SMB2_CAP_MULTI_CHANNEL\n");
2647 ioctl.smb2.level = RAW_IOCTL_SMB2;
2648 fh.data[0] = UINT64_MAX;
2649 fh.data[1] = UINT64_MAX;
2650 ioctl.smb2.in.file.handle = fh;
2651 ioctl.smb2.in.function = FSCTL_QUERY_NETWORK_INTERFACE_INFO;
2652 ioctl.smb2.in.max_response_size = 0x10000; /* Windows client sets this to 64KiB */
2653 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2655 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2656 torture_assert_ntstatus_ok(torture, status, "FSCTL_QUERY_NETWORK_INTERFACE_INFO");
2658 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, &net_iface,
2659 (ndr_pull_flags_fn_t)ndr_pull_fsctl_net_iface_info);
2660 torture_assert_ndr_success(torture, ndr_ret,
2661 "ndr_pull_fsctl_net_iface_info");
2663 ndr_print_debug((ndr_print_fn_t)ndr_print_fsctl_net_iface_info,
2664 "Network Interface Info", &net_iface);
2666 talloc_free(tmp_ctx);
2671 * Check whether all @fs_support_flags are set in the server's
2672 * RAW_QFS_ATTRIBUTE_INFORMATION FileSystemAttributes response.
2674 static NTSTATUS test_ioctl_fs_supported(struct torture_context *torture,
2675 struct smb2_tree *tree,
2676 TALLOC_CTX *mem_ctx,
2677 struct smb2_handle *fh,
2678 uint64_t fs_support_flags,
2682 union smb_fsinfo info;
2685 info.generic.level = RAW_QFS_ATTRIBUTE_INFORMATION;
2686 info.generic.handle = *fh;
2687 status = smb2_getinfo_fs(tree, tree, &info);
2688 if (!NT_STATUS_IS_OK(status)) {
2692 if ((info.attribute_info.out.fs_attr & fs_support_flags)
2693 == fs_support_flags) {
2698 return NT_STATUS_OK;
2701 static NTSTATUS test_ioctl_sparse_req(struct torture_context *torture,
2702 TALLOC_CTX *mem_ctx,
2703 struct smb2_tree *tree,
2704 struct smb2_handle fh,
2707 union smb_ioctl ioctl;
2712 ioctl.smb2.level = RAW_IOCTL_SMB2;
2713 ioctl.smb2.in.file.handle = fh;
2714 ioctl.smb2.in.function = FSCTL_SET_SPARSE;
2715 ioctl.smb2.in.max_response_size = 0;
2716 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2717 set_sparse = (set ? 0xFF : 0x0);
2718 ioctl.smb2.in.out.data = &set_sparse;
2719 ioctl.smb2.in.out.length = sizeof(set_sparse);
2721 status = smb2_ioctl(tree, mem_ctx, &ioctl.smb2);
2725 static NTSTATUS test_sparse_get(struct torture_context *torture,
2726 TALLOC_CTX *mem_ctx,
2727 struct smb2_tree *tree,
2728 struct smb2_handle fh,
2731 union smb_fileinfo io;
2735 io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2736 io.generic.in.file.handle = fh;
2737 status = smb2_getinfo_file(tree, mem_ctx, &io);
2738 if (!NT_STATUS_IS_OK(status)) {
2741 *_is_sparse = !!(io.basic_info.out.attrib & FILE_ATTRIBUTE_SPARSE);
2746 static bool test_ioctl_sparse_file_flag(struct torture_context *torture,
2747 struct smb2_tree *tree)
2749 struct smb2_handle fh;
2750 union smb_fileinfo io;
2752 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2756 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2757 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2758 FILE_ATTRIBUTE_NORMAL);
2759 torture_assert(torture, ok, "setup file");
2761 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
2762 FILE_SUPPORTS_SPARSE_FILES, &ok);
2763 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2765 smb2_util_close(tree, fh);
2766 torture_skip(torture, "Sparse files not supported\n");
2770 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
2771 io.generic.in.file.handle = fh;
2772 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2773 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2775 torture_assert(torture,
2776 ((io.all_info2.out.attrib & FILE_ATTRIBUTE_SPARSE) == 0),
2777 "sparse attr before set");
2779 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
2780 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2782 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2783 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2784 torture_assert(torture, is_sparse, "no sparse attr after set");
2786 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false);
2787 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2789 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2790 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2791 torture_assert(torture, !is_sparse, "sparse attr after unset");
2793 smb2_util_close(tree, fh);
2794 talloc_free(tmp_ctx);
2798 static bool test_ioctl_sparse_file_attr(struct torture_context *torture,
2799 struct smb2_tree *tree)
2801 struct smb2_handle fh;
2803 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2807 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2808 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2809 (FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_SPARSE));
2810 torture_assert(torture, ok, "setup file");
2812 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
2813 FILE_SUPPORTS_SPARSE_FILES, &ok);
2814 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2816 smb2_util_close(tree, fh);
2817 torture_skip(torture, "Sparse files not supported\n");
2820 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2821 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2822 torture_assert(torture, !is_sparse, "sparse attr on open");
2824 smb2_util_close(tree, fh);
2825 talloc_free(tmp_ctx);
2829 static bool test_ioctl_sparse_dir_flag(struct torture_context *torture,
2830 struct smb2_tree *tree)
2832 struct smb2_handle dirh;
2834 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2837 smb2_deltree(tree, DNAME);
2838 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2839 DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL,
2840 FILE_ATTRIBUTE_DIRECTORY);
2841 torture_assert(torture, ok, "setup sparse directory");
2843 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &dirh,
2844 FILE_SUPPORTS_SPARSE_FILES, &ok);
2845 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2847 smb2_util_close(tree, dirh);
2848 smb2_deltree(tree, DNAME);
2849 torture_skip(torture, "Sparse files not supported\n");
2852 /* set sparse dir should fail, check for 2k12 & 2k8 response */
2853 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, dirh, true);
2854 torture_assert_ntstatus_equal(torture, status,
2855 NT_STATUS_INVALID_PARAMETER,
2856 "dir FSCTL_SET_SPARSE status");
2858 smb2_util_close(tree, dirh);
2859 smb2_deltree(tree, DNAME);
2860 talloc_free(tmp_ctx);
2865 * FSCTL_SET_SPARSE can be sent with (already tested) or without a SetSparse
2866 * buffer to indicate whether the flag should be set or cleared. When sent
2867 * without a buffer, it must be handled as if SetSparse=TRUE.
2869 static bool test_ioctl_sparse_set_nobuf(struct torture_context *torture,
2870 struct smb2_tree *tree)
2872 struct smb2_handle fh;
2873 union smb_ioctl ioctl;
2875 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2879 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2880 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2881 FILE_ATTRIBUTE_NORMAL);
2882 torture_assert(torture, ok, "setup file");
2884 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
2885 FILE_SUPPORTS_SPARSE_FILES, &ok);
2886 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2888 smb2_util_close(tree, fh);
2889 torture_skip(torture, "Sparse files not supported\n");
2892 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2893 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2894 torture_assert(torture, !is_sparse, "sparse attr before set");
2897 ioctl.smb2.level = RAW_IOCTL_SMB2;
2898 ioctl.smb2.in.file.handle = fh;
2899 ioctl.smb2.in.function = FSCTL_SET_SPARSE;
2900 ioctl.smb2.in.max_response_size = 0;
2901 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2902 /* ioctl.smb2.in.out is zeroed, no SetSparse buffer */
2904 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2905 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2907 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2908 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2909 torture_assert(torture, is_sparse, "no sparse attr after set");
2911 /* second non-SetSparse request shouldn't toggle sparse */
2913 ioctl.smb2.level = RAW_IOCTL_SMB2;
2914 ioctl.smb2.in.file.handle = fh;
2915 ioctl.smb2.in.function = FSCTL_SET_SPARSE;
2916 ioctl.smb2.in.max_response_size = 0;
2917 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2919 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2920 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2922 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2923 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2924 torture_assert(torture, is_sparse, "no sparse attr after 2nd set");
2926 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false);
2927 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2929 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2930 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2931 torture_assert(torture, !is_sparse, "sparse attr after unset");
2933 smb2_util_close(tree, fh);
2934 talloc_free(tmp_ctx);
2938 static bool test_ioctl_sparse_set_oversize(struct torture_context *torture,
2939 struct smb2_tree *tree)
2941 struct smb2_handle fh;
2942 union smb_ioctl ioctl;
2944 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2949 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2950 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2951 FILE_ATTRIBUTE_NORMAL);
2952 torture_assert(torture, ok, "setup file");
2954 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
2955 FILE_SUPPORTS_SPARSE_FILES, &ok);
2956 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2958 smb2_util_close(tree, fh);
2959 torture_skip(torture, "Sparse files not supported\n");
2962 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2963 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2964 torture_assert(torture, !is_sparse, "sparse attr before set");
2967 ioctl.smb2.level = RAW_IOCTL_SMB2;
2968 ioctl.smb2.in.file.handle = fh;
2969 ioctl.smb2.in.function = FSCTL_SET_SPARSE;
2970 ioctl.smb2.in.max_response_size = 0;
2971 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2974 * Attach a request buffer larger than FILE_SET_SPARSE_BUFFER
2975 * Windows still successfully processes the request.
2978 buf[0] = 0xFF; /* attempt to set sparse */
2979 ioctl.smb2.in.out.data = buf;
2980 ioctl.smb2.in.out.length = ARRAY_SIZE(buf);
2982 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2983 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2985 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2986 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2987 torture_assert(torture, is_sparse, "no sparse attr after set");
2990 ioctl.smb2.level = RAW_IOCTL_SMB2;
2991 ioctl.smb2.in.file.handle = fh;
2992 ioctl.smb2.in.function = FSCTL_SET_SPARSE;
2993 ioctl.smb2.in.max_response_size = 0;
2994 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2996 ZERO_ARRAY(buf); /* clear sparse */
2997 ioctl.smb2.in.out.data = buf;
2998 ioctl.smb2.in.out.length = ARRAY_SIZE(buf);
3000 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3001 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3003 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3004 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3005 torture_assert(torture, !is_sparse, "sparse attr after clear");
3007 smb2_util_close(tree, fh);
3008 talloc_free(tmp_ctx);
3012 static NTSTATUS test_ioctl_qar_req(struct torture_context *torture,
3013 TALLOC_CTX *mem_ctx,
3014 struct smb2_tree *tree,
3015 struct smb2_handle fh,
3018 struct file_alloced_range_buf **_rsp,
3019 uint64_t *_rsp_count)
3021 union smb_ioctl ioctl;
3023 enum ndr_err_code ndr_ret;
3024 struct file_alloced_range_buf far_buf;
3025 struct file_alloced_range_buf *far_rsp = NULL;
3026 uint64_t far_count = 0;
3028 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
3029 if (tmp_ctx == NULL) {
3030 return NT_STATUS_NO_MEMORY;
3034 ioctl.smb2.level = RAW_IOCTL_SMB2;
3035 ioctl.smb2.in.file.handle = fh;
3036 ioctl.smb2.in.function = FSCTL_QUERY_ALLOCATED_RANGES;
3037 ioctl.smb2.in.max_response_size = 1024;
3038 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3040 far_buf.file_off = req_off;
3041 far_buf.len = req_len;
3043 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
3045 (ndr_push_flags_fn_t)ndr_push_file_alloced_range_buf);
3046 if (ndr_ret != NDR_ERR_SUCCESS) {
3047 status = NT_STATUS_UNSUCCESSFUL;
3051 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3052 if (!NT_STATUS_IS_OK(status)) {
3056 if (ioctl.smb2.out.out.length == 0) {
3060 if ((ioctl.smb2.out.out.length % sizeof(far_buf)) != 0) {
3061 torture_comment(torture, "invalid qry_alloced rsp len: %zd:",
3062 ioctl.smb2.out.out.length);
3063 status = NT_STATUS_INVALID_VIEW_SIZE;
3067 far_count = (ioctl.smb2.out.out.length / sizeof(far_buf));
3068 far_rsp = talloc_array(mem_ctx, struct file_alloced_range_buf,
3070 if (far_rsp == NULL) {
3071 status = NT_STATUS_NO_MEMORY;
3075 for (i = 0; i < far_count; i++) {
3076 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
3078 (ndr_pull_flags_fn_t)ndr_pull_file_alloced_range_buf);
3079 if (ndr_ret != NDR_ERR_SUCCESS) {
3080 status = NT_STATUS_UNSUCCESSFUL;
3083 /* move to next buffer */
3084 ioctl.smb2.out.out.data += sizeof(far_buf);
3085 ioctl.smb2.out.out.length -= sizeof(far_buf);
3090 *_rsp_count = far_count;
3091 status = NT_STATUS_OK;
3093 talloc_free(tmp_ctx);
3097 static bool test_ioctl_sparse_qar(struct torture_context *torture,
3098 struct smb2_tree *tree)
3100 struct smb2_handle fh;
3102 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3105 struct file_alloced_range_buf *far_rsp = NULL;
3106 uint64_t far_count = 0;
3108 /* zero length file, shouldn't have any ranges */
3109 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3110 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3111 FILE_ATTRIBUTE_NORMAL);
3112 torture_assert(torture, ok, "setup file");
3114 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3115 FILE_SUPPORTS_SPARSE_FILES, &ok);
3116 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3118 smb2_util_close(tree, fh);
3119 torture_skip(torture, "Sparse files not supported\n");
3122 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3123 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3124 torture_assert(torture, !is_sparse, "sparse attr before set");
3126 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3131 torture_assert_ntstatus_ok(torture, status,
3132 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3133 torture_assert_u64_equal(torture, far_count, 0,
3134 "unexpected response len");
3136 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3141 torture_assert_ntstatus_ok(torture, status,
3142 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3143 torture_assert_u64_equal(torture, far_count, 0,
3144 "unexpected response len");
3146 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
3147 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3149 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3150 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3151 torture_assert(torture, is_sparse, "no sparse attr after set");
3153 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3158 torture_assert_ntstatus_ok(torture, status,
3159 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3160 torture_assert_u64_equal(torture, far_count, 0,
3161 "unexpected response len");
3163 /* write into the (now) sparse file at 4k offset */
3164 ok = write_pattern(torture, tree, tmp_ctx, fh,
3167 4096); /* pattern offset */
3168 torture_assert(torture, ok, "write pattern");
3171 * Query range before write off. Whether it's allocated or not is FS
3172 * dependent. NTFS deallocates chunks in 64K increments, but others
3173 * (e.g. XFS, Btrfs, etc.) may deallocate 4K chunks.
3175 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3180 torture_assert_ntstatus_ok(torture, status,
3181 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3182 if (far_count == 0) {
3183 torture_comment(torture, "FS deallocated 4K chunk\n");
3185 /* expect fully allocated */
3186 torture_assert_u64_equal(torture, far_count, 1,
3187 "unexpected response len");
3188 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0, "far offset");
3189 torture_assert_u64_equal(torture, far_rsp[0].len, 4096, "far len");
3193 * Query range before and past write, it should be allocated up to the
3196 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3201 torture_assert_ntstatus_ok(torture, status,
3202 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3203 torture_assert_u64_equal(torture, far_count, 1,
3204 "unexpected response len");
3206 if (far_rsp[0].file_off == 4096) {
3207 /* 4K chunk unallocated */
3208 torture_assert_u64_equal(torture, far_rsp[0].file_off, 4096, "far offset");
3209 torture_assert_u64_equal(torture, far_rsp[0].len, 1024, "far len");
3211 /* expect fully allocated */
3212 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0, "far offset");
3213 torture_assert_u64_equal(torture, far_rsp[0].len, 5120, "far len");
3216 smb2_util_close(tree, fh);
3217 talloc_free(tmp_ctx);
3221 static bool test_ioctl_sparse_qar_malformed(struct torture_context *torture,
3222 struct smb2_tree *tree)
3224 struct smb2_handle fh;
3225 union smb_ioctl ioctl;
3226 struct file_alloced_range_buf far_buf;
3228 enum ndr_err_code ndr_ret;
3229 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3233 /* zero length file, shouldn't have any ranges */
3234 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3235 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3236 FILE_ATTRIBUTE_NORMAL);
3237 torture_assert(torture, ok, "setup file");
3239 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3240 FILE_SUPPORTS_SPARSE_FILES, &ok);
3241 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3243 smb2_util_close(tree, fh);
3244 torture_skip(torture, "Sparse files not supported\n");
3247 /* no allocated ranges, no space for range response, should pass */
3249 ioctl.smb2.level = RAW_IOCTL_SMB2;
3250 ioctl.smb2.in.file.handle = fh;
3251 ioctl.smb2.in.function = FSCTL_QUERY_ALLOCATED_RANGES;
3252 ioctl.smb2.in.max_response_size = 0;
3253 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3255 far_buf.file_off = 0;
3257 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
3259 (ndr_push_flags_fn_t)ndr_push_file_alloced_range_buf);
3260 torture_assert_ndr_success(torture, ndr_ret, "push far ndr buf");
3262 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3263 torture_assert_ntstatus_ok(torture, status, "FSCTL_QUERY_ALLOCATED_RANGES");
3265 /* write into the file at 4k offset */
3266 ok = write_pattern(torture, tree, tmp_ctx, fh,
3269 0); /* pattern offset */
3270 torture_assert(torture, ok, "write pattern");
3272 /* allocated range, no space for range response, should fail */
3273 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3274 torture_assert_ntstatus_equal(torture, status,
3275 NT_STATUS_BUFFER_TOO_SMALL, "qar no space");
3277 /* oversize (2x) file_alloced_range_buf in request, should pass */
3278 ioctl.smb2.in.max_response_size = 1024;
3279 old_len = ioctl.smb2.in.out.length;
3280 ok = data_blob_realloc(tmp_ctx, &ioctl.smb2.in.out,
3281 (ioctl.smb2.in.out.length * 2));
3282 torture_assert(torture, ok, "2x data buffer");
3283 memcpy(ioctl.smb2.in.out.data + old_len, ioctl.smb2.in.out.data,
3285 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3286 torture_assert_ntstatus_ok(torture, status, "qar too big");
3288 /* no file_alloced_range_buf in request, should fail */
3289 data_blob_free(&ioctl.smb2.in.out);
3290 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3291 torture_assert_ntstatus_equal(torture, status,
3292 NT_STATUS_INVALID_PARAMETER, "qar empty");
3298 * 2.3.57 FSCTL_SET_ZERO_DATA Request
3300 * How an implementation zeros data within a file is implementation-dependent.
3301 * A file system MAY choose to deallocate regions of disk space that have been
3304 * ... NTFS might deallocate disk space in the file if the file is stored on an
3305 * NTFS volume, and the file is sparse or compressed. It will free any allocated
3306 * space in chunks of 64 kilobytes that begin at an offset that is a multiple of
3307 * 64 kilobytes. Other bytes in the file (prior to the first freed 64-kilobyte
3308 * chunk and after the last freed 64-kilobyte chunk) will be zeroed but not
3311 static NTSTATUS test_ioctl_zdata_req(struct torture_context *torture,
3312 TALLOC_CTX *mem_ctx,
3313 struct smb2_tree *tree,
3314 struct smb2_handle fh,
3316 int64_t beyond_final_zero)
3318 union smb_ioctl ioctl;
3320 enum ndr_err_code ndr_ret;
3321 struct file_zero_data_info zdata_info;
3322 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
3323 if (tmp_ctx == NULL) {
3324 return NT_STATUS_NO_MEMORY;
3328 ioctl.smb2.level = RAW_IOCTL_SMB2;
3329 ioctl.smb2.in.file.handle = fh;
3330 ioctl.smb2.in.function = FSCTL_SET_ZERO_DATA;
3331 ioctl.smb2.in.max_response_size = 0;
3332 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3334 zdata_info.file_off = off;
3335 zdata_info.beyond_final_zero = beyond_final_zero;
3337 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
3339 (ndr_push_flags_fn_t)ndr_push_file_zero_data_info);
3340 if (ndr_ret != NDR_ERR_SUCCESS) {
3341 status = NT_STATUS_UNSUCCESSFUL;
3345 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3346 if (!NT_STATUS_IS_OK(status)) {
3350 status = NT_STATUS_OK;
3352 talloc_free(tmp_ctx);
3356 static bool test_ioctl_sparse_punch(struct torture_context *torture,
3357 struct smb2_tree *tree)
3359 struct smb2_handle fh;
3361 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3364 struct file_alloced_range_buf *far_rsp = NULL;
3365 uint64_t far_count = 0;
3367 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3368 FNAME, &fh, 4096, SEC_RIGHTS_FILE_ALL,
3369 FILE_ATTRIBUTE_NORMAL);
3370 torture_assert(torture, ok, "setup file");
3372 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3373 FILE_SUPPORTS_SPARSE_FILES, &ok);
3374 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3376 smb2_util_close(tree, fh);
3377 torture_skip(torture, "Sparse files not supported\n");
3380 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3381 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3382 torture_assert(torture, !is_sparse, "sparse attr before set");
3384 /* zero (hole-punch) the data, without sparse flag */
3385 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3387 4096); /* beyond_final_zero */
3388 torture_assert_ntstatus_ok(torture, status, "zero_data");
3390 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3395 torture_assert_ntstatus_ok(torture, status,
3396 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3397 torture_assert_u64_equal(torture, far_count, 1,
3398 "unexpected response len");
3400 /* expect fully allocated */
3401 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3402 "unexpected far off");
3403 torture_assert_u64_equal(torture, far_rsp[0].len, 4096,
3404 "unexpected far len");
3405 /* check that the data is now zeroed */
3406 ok = check_zero(torture, tree, tmp_ctx, fh, 0, 4096);
3407 torture_assert(torture, ok, "non-sparse zeroed range");
3410 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
3411 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3413 /* still fully allocated on NTFS, see note below for Samba */
3414 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3419 torture_assert_ntstatus_ok(torture, status,
3420 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3422 * FS specific: Samba uses PUNCH_HOLE to zero the range, and
3423 * subsequently uses fallocate() to allocate the punched range if the
3424 * file is marked non-sparse and "strict allocate" is enabled. In both
3425 * cases, the zeroed range will not be detected by SEEK_DATA, so the
3426 * range won't be present in QAR responses until the file is marked
3429 if (far_count == 0) {
3430 torture_comment(torture, "non-sparse zeroed range disappeared "
3431 "after marking sparse\n");
3433 /* NTFS: range remains fully allocated */
3434 torture_assert_u64_equal(torture, far_count, 1,
3435 "unexpected response len");
3436 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3437 "unexpected far off");
3438 torture_assert_u64_equal(torture, far_rsp[0].len, 4096,
3439 "unexpected far len");
3442 /* zero (hole-punch) the data, _with_ sparse flag */
3443 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3445 4096); /* beyond_final_zero */
3446 torture_assert_ntstatus_ok(torture, status, "zero_data");
3448 /* the range should no longer be alloced */
3449 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3454 torture_assert_ntstatus_ok(torture, status,
3455 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3456 torture_assert_u64_equal(torture, far_count, 0,
3457 "unexpected response len");
3459 ok = check_zero(torture, tree, tmp_ctx, fh, 0, 4096);
3460 torture_assert(torture, ok, "sparse zeroed range");
3462 /* remove sparse flag, this should "unsparse" the zeroed range */
3463 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false);
3464 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3466 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3471 torture_assert_ntstatus_ok(torture, status,
3472 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3473 torture_assert_u64_equal(torture, far_count, 1,
3474 "unexpected response len");
3475 /* expect fully allocated */
3476 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3477 "unexpected far off");
3478 torture_assert_u64_equal(torture, far_rsp[0].len, 4096,
3479 "unexpected far len");
3481 ok = check_zero(torture, tree, tmp_ctx, fh, 0, 4096);
3482 torture_assert(torture, ok, "sparse zeroed range");
3484 smb2_util_close(tree, fh);
3485 talloc_free(tmp_ctx);
3490 * Find the point at which a zeroed range in a sparse file is deallocated by the
3491 * underlying filesystem. NTFS on Windows Server 2012 deallocates chunks in 64k
3492 * increments. Also check whether zeroed neighbours are merged for deallocation.
3494 static bool test_ioctl_sparse_hole_dealloc(struct torture_context *torture,
3495 struct smb2_tree *tree)
3497 struct smb2_handle fh;
3499 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3503 uint64_t dealloc_chunk_len = 0;
3504 struct file_alloced_range_buf *far_rsp = NULL;
3505 uint64_t far_count = 0;
3507 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3508 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3509 FILE_ATTRIBUTE_NORMAL);
3510 torture_assert(torture, ok, "setup file 1");
3512 /* check for FS sparse file */
3513 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3514 FILE_SUPPORTS_SPARSE_FILES, &ok);
3515 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3517 smb2_util_close(tree, fh);
3518 torture_skip(torture, "Sparse files not supported\n");
3522 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
3523 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3525 file_size = 1024 * 1024;
3527 ok = write_pattern(torture, tree, tmp_ctx, fh,
3529 file_size, /* len */
3530 0); /* pattern offset */
3531 torture_assert(torture, ok, "write pattern");
3533 /* check allocated ranges, should be fully allocated */
3534 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3536 file_size, /* len */
3539 torture_assert_ntstatus_ok(torture, status,
3540 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3541 torture_assert_u64_equal(torture, far_count, 1,
3542 "unexpected response len");
3543 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3544 "unexpected far off");
3545 torture_assert_u64_equal(torture, far_rsp[0].len, file_size,
3546 "unexpected far len");
3548 /* punch holes in sizes of 1k increments */
3549 for (hlen = 0; hlen <= file_size; hlen += 4096) {
3551 /* punch a hole from zero to the current increment */
3552 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3554 hlen); /* beyond_final_zero */
3555 torture_assert_ntstatus_ok(torture, status, "zero_data");
3557 /* ensure hole is zeroed, and pattern is consistent */
3558 ok = check_zero(torture, tree, tmp_ctx, fh, 0, hlen);
3559 torture_assert(torture, ok, "sparse zeroed range");
3561 ok = check_pattern(torture, tree, tmp_ctx, fh, hlen,
3562 file_size - hlen, hlen);
3563 torture_assert(torture, ok, "allocated pattern range");
3565 /* Check allocated ranges, hole might have been deallocated */
3566 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3568 file_size, /* len */
3571 torture_assert_ntstatus_ok(torture, status,
3572 "FSCTL_QUERY_ALLOCATED_RANGES");
3573 if ((hlen == file_size) && (far_count == 0)) {
3574 /* hole covered entire file, deallocation occurred */
3575 dealloc_chunk_len = file_size;
3579 torture_assert_u64_equal(torture, far_count, 1,
3580 "unexpected response len");
3581 if (far_rsp[0].file_off != 0) {
3583 * We now know the hole punch length needed to trigger a
3584 * deallocation on this FS...
3586 dealloc_chunk_len = hlen;
3587 torture_comment(torture, "hole punch %ju@0 resulted in "
3588 "deallocation of %ju@0\n",
3590 (uintmax_t)far_rsp[0].file_off);
3591 torture_assert_u64_equal(torture,
3592 file_size - far_rsp[0].len,
3593 far_rsp[0].file_off,
3594 "invalid alloced range");
3599 if (dealloc_chunk_len == 0) {
3600 torture_comment(torture, "strange, this FS never deallocates"
3601 "zeroed ranges in sparse files\n");
3602 return true; /* FS specific, not a failure */
3606 * Check whether deallocation occurs when the (now known)
3607 * deallocation chunk size is punched via two ZERO_DATA requests.
3608 * I.e. Does the FS merge the two ranges and deallocate the chunk?
3609 * NTFS on Windows Server 2012 does not.
3611 ok = write_pattern(torture, tree, tmp_ctx, fh,
3613 file_size, /* len */
3614 0); /* pattern offset */
3615 torture_assert(torture, ok, "write pattern");
3617 /* divide dealloc chunk size by two, to use as punch length */
3618 hlen = dealloc_chunk_len >> 1;
3621 * /half of dealloc chunk size 1M\
3623 * /offset 0 | /dealloc chunk size |
3624 * |------------------ |-------------------|-------------------|
3625 * | zeroed, 1st punch | zeroed, 2nd punch | existing pattern |
3627 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3629 hlen); /* beyond final zero */
3630 torture_assert_ntstatus_ok(torture, status, "zero_data");
3632 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3634 dealloc_chunk_len); /* beyond final */
3635 torture_assert_ntstatus_ok(torture, status, "zero_data");
3637 /* ensure holes are zeroed, and pattern is consistent */
3638 ok = check_zero(torture, tree, tmp_ctx, fh, 0, dealloc_chunk_len);
3639 torture_assert(torture, ok, "sparse zeroed range");
3641 ok = check_pattern(torture, tree, tmp_ctx, fh, dealloc_chunk_len,
3642 file_size - dealloc_chunk_len, dealloc_chunk_len);
3643 torture_assert(torture, ok, "allocated pattern range");
3645 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3647 file_size, /* len */
3650 torture_assert_ntstatus_ok(torture, status,
3651 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3653 if ((far_count == 0) && (dealloc_chunk_len == file_size)) {
3654 torture_comment(torture, "holes merged for deallocation of "
3658 torture_assert_u64_equal(torture, far_count, 1,
3659 "unexpected response len");
3660 if (far_rsp[0].file_off == dealloc_chunk_len) {
3661 torture_comment(torture, "holes merged for deallocation of "
3662 "%ju chunk\n", (uintmax_t)dealloc_chunk_len);
3663 torture_assert_u64_equal(torture,
3664 file_size - far_rsp[0].len,
3665 far_rsp[0].file_off,
3666 "invalid alloced range");
3668 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3669 "unexpected deallocation");
3670 torture_comment(torture, "holes not merged for deallocation\n");
3673 smb2_util_close(tree, fh);
3676 * Check whether an unwritten range is allocated when a sparse file is
3677 * written to at an offset past the dealloc chunk size:
3679 * /dealloc chunk size
3681 * |------------------ |-------------------|
3682 * | unwritten | pattern |
3684 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3685 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3686 FILE_ATTRIBUTE_NORMAL);
3687 torture_assert(torture, ok, "setup file 1");
3690 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
3691 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3693 ok = write_pattern(torture, tree, tmp_ctx, fh,
3694 dealloc_chunk_len, /* off */
3696 dealloc_chunk_len); /* pattern offset */
3697 torture_assert(torture, ok, "write pattern");
3699 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3701 dealloc_chunk_len + 1024, /* len */
3704 torture_assert_ntstatus_ok(torture, status,
3705 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3706 torture_assert_u64_equal(torture, far_count, 1,
3707 "unexpected response len");
3708 if (far_rsp[0].file_off == 0) {
3709 torture_assert_u64_equal(torture, far_rsp[0].len,
3710 dealloc_chunk_len + 1024,
3711 "unexpected far len");
3712 torture_comment(torture, "unwritten range fully allocated\n");
3714 torture_assert_u64_equal(torture, far_rsp[0].file_off, dealloc_chunk_len,
3715 "unexpected deallocation");
3716 torture_assert_u64_equal(torture, far_rsp[0].len, 1024,
3717 "unexpected far len");
3718 torture_comment(torture, "unwritten range not allocated\n");
3721 ok = check_zero(torture, tree, tmp_ctx, fh, 0, dealloc_chunk_len);
3722 torture_assert(torture, ok, "sparse zeroed range");
3724 ok = check_pattern(torture, tree, tmp_ctx, fh, dealloc_chunk_len,
3725 1024, dealloc_chunk_len);
3726 torture_assert(torture, ok, "allocated pattern range");
3728 /* unsparse, should now be fully allocated */
3729 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false);
3730 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3732 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3734 dealloc_chunk_len + 1024, /* len */
3737 torture_assert_ntstatus_ok(torture, status,
3738 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3739 torture_assert_u64_equal(torture, far_count, 1,
3740 "unexpected response len");
3741 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3742 "unexpected deallocation");
3743 torture_assert_u64_equal(torture, far_rsp[0].len,
3744 dealloc_chunk_len + 1024,
3745 "unexpected far len");
3747 smb2_util_close(tree, fh);
3748 talloc_free(tmp_ctx);
3752 /* check whether a file with compression and sparse attrs can be deallocated */
3753 static bool test_ioctl_sparse_compressed(struct torture_context *torture,
3754 struct smb2_tree *tree)
3756 struct smb2_handle fh;
3758 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3760 uint64_t file_size = 1024 * 1024;
3761 struct file_alloced_range_buf *far_rsp = NULL;
3762 uint64_t far_count = 0;
3764 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3765 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3766 FILE_ATTRIBUTE_NORMAL);
3767 torture_assert(torture, ok, "setup file 1");
3769 /* check for FS sparse file and compression support */
3770 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3771 FILE_SUPPORTS_SPARSE_FILES, &ok);
3772 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3774 smb2_util_close(tree, fh);
3775 torture_skip(torture, "Sparse files not supported\n");
3778 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
3780 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3782 smb2_util_close(tree, fh);
3783 torture_skip(torture, "FS compression not supported\n");
3786 /* set compression and write some data */
3787 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
3788 COMPRESSION_FORMAT_DEFAULT);
3789 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
3791 ok = write_pattern(torture, tree, tmp_ctx, fh,
3793 file_size, /* len */
3794 0); /* pattern offset */
3795 torture_assert(torture, ok, "write pattern");
3797 /* set sparse - now sparse and compressed */
3798 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
3799 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3801 /* check allocated ranges, should be fully alloced */
3802 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3804 file_size, /* len */
3807 torture_assert_ntstatus_ok(torture, status,
3808 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3809 torture_assert_u64_equal(torture, far_count, 1,
3810 "unexpected response len");
3811 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3812 "unexpected far off");
3813 torture_assert_u64_equal(torture, far_rsp[0].len, file_size,
3814 "unexpected far len");
3816 /* zero (hole-punch) all data, with sparse and compressed attrs */
3817 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3819 file_size); /* beyond_final_zero */
3820 torture_assert_ntstatus_ok(torture, status, "zero_data");
3823 * Windows Server 2012 still deallocates a zeroed range when a sparse
3824 * file carries the compression attribute.
3826 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3828 file_size, /* len */
3831 torture_assert_ntstatus_ok(torture, status,
3832 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3833 if (far_count == 0) {
3834 torture_comment(torture, "sparse & compressed file "
3835 "deallocated after hole-punch\n");
3837 torture_assert_u64_equal(torture, far_count, 1,
3838 "unexpected response len");
3839 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3840 "unexpected far off");
3841 torture_assert_u64_equal(torture, far_rsp[0].len, file_size,
3842 "unexpected far len");
3843 torture_comment(torture, "sparse & compressed file fully "
3844 "allocated after hole-punch\n");
3847 smb2_util_close(tree, fh);
3848 talloc_free(tmp_ctx);
3853 * Create a sparse file, then attempt to copy unallocated and allocated ranges
3854 * into a target file using FSCTL_SRV_COPYCHUNK.
3856 static bool test_ioctl_sparse_copy_chunk(struct torture_context *torture,
3857 struct smb2_tree *tree)
3859 struct smb2_handle src_h;
3860 struct smb2_handle dest_h;
3862 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3864 uint64_t dealloc_chunk_len = 64 * 1024; /* Windows 2012 */
3865 struct file_alloced_range_buf *far_rsp = NULL;
3866 uint64_t far_count = 0;
3867 union smb_ioctl ioctl;
3868 struct srv_copychunk_copy cc_copy;
3869 struct srv_copychunk_rsp cc_rsp;
3870 enum ndr_err_code ndr_ret;
3872 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3873 FNAME, &src_h, 0, SEC_RIGHTS_FILE_ALL,
3874 FILE_ATTRIBUTE_NORMAL);
3875 torture_assert(torture, ok, "setup file");
3877 /* check for FS sparse file support */
3878 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &src_h,
3879 FILE_SUPPORTS_SPARSE_FILES, &ok);
3880 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3881 smb2_util_close(tree, src_h);
3883 torture_skip(torture, "Sparse files not supported\n");
3886 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
3888 &src_h, 0, /* src file */
3889 SEC_RIGHTS_FILE_ALL,
3890 &dest_h, 0, /* dest file */
3891 SEC_RIGHTS_FILE_ALL,
3894 torture_assert(torture, ok, "setup copy chunk error");
3897 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, src_h, true);
3898 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3900 /* start after dealloc_chunk_len, to create an unwritten sparse range */
3901 ok = write_pattern(torture, tree, tmp_ctx, src_h,
3902 dealloc_chunk_len, /* off */
3904 dealloc_chunk_len); /* pattern offset */
3905 torture_assert(torture, ok, "write pattern");
3907 /* Skip test if 64k chunk is allocated - FS specific */
3908 status = test_ioctl_qar_req(torture, tmp_ctx, tree, src_h,
3910 dealloc_chunk_len + 1024, /* len */
3913 torture_assert_ntstatus_ok(torture, status,
3914 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3915 torture_assert_u64_equal(torture, far_count, 1,
3916 "unexpected response len");
3917 if (far_rsp[0].file_off == 0) {
3918 torture_skip(torture, "unwritten range fully allocated\n");
3921 torture_assert_u64_equal(torture, far_rsp[0].file_off, dealloc_chunk_len,
3922 "unexpected allocation");
3923 torture_assert_u64_equal(torture, far_rsp[0].len, 1024,
3924 "unexpected far len");
3926 /* copy-chunk unallocated + written ranges into non-sparse dest */
3928 cc_copy.chunks[0].source_off = 0;
3929 cc_copy.chunks[0].target_off = 0;
3930 cc_copy.chunks[0].length = dealloc_chunk_len + 1024;
3932 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
3934 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
3935 torture_assert_ndr_success(torture, ndr_ret,
3936 "ndr_push_srv_copychunk_copy");
3938 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3939 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
3941 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
3943 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
3944 torture_assert_ndr_success(torture, ndr_ret,
3945 "ndr_pull_srv_copychunk_rsp");
3947 ok = check_copy_chunk_rsp(torture, &cc_rsp,
3948 1, /* chunks written */
3949 0, /* chunk bytes unsuccessfully written */
3950 dealloc_chunk_len + 1024); /* bytes written */
3951 torture_assert(torture, ok, "bad copy chunk response data");
3953 ok = check_zero(torture, tree, tmp_ctx, dest_h, 0, dealloc_chunk_len);
3954 torture_assert(torture, ok, "sparse zeroed range");
3956 ok = check_pattern(torture, tree, tmp_ctx, dest_h, dealloc_chunk_len,
3957 1024, dealloc_chunk_len);
3958 torture_assert(torture, ok, "copychunked range");
3960 /* copied range should be allocated in non-sparse dest */
3961 status = test_ioctl_qar_req(torture, tmp_ctx, tree, dest_h,
3963 dealloc_chunk_len + 1024, /* len */
3966 torture_assert_ntstatus_ok(torture, status,
3967 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3968 torture_assert_u64_equal(torture, far_count, 1,
3969 "unexpected response len");
3970 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3971 "unexpected allocation");
3972 torture_assert_u64_equal(torture, far_rsp[0].len,
3973 dealloc_chunk_len + 1024,
3974 "unexpected far len");
3976 /* set dest as sparse */
3977 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, dest_h, true);
3978 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3980 /* zero (hole-punch) all data */
3981 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, dest_h,
3983 dealloc_chunk_len + 1024);
3984 torture_assert_ntstatus_ok(torture, status, "zero_data");
3986 /* zeroed range might be deallocated */
3987 status = test_ioctl_qar_req(torture, tmp_ctx, tree, dest_h,
3989 dealloc_chunk_len + 1024, /* len */
3992 torture_assert_ntstatus_ok(torture, status,
3993 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3994 if (far_count == 0) {
3995 /* FS specific (e.g. NTFS) */
3996 torture_comment(torture, "FS deallocates file on full-range "
3999 /* FS specific (e.g. EXT4) */
4000 torture_comment(torture, "FS doesn't deallocate file on "
4001 "full-range punch\n");
4003 ok = check_zero(torture, tree, tmp_ctx, dest_h, 0,
4004 dealloc_chunk_len + 1024);
4005 torture_assert(torture, ok, "punched zeroed range");
4007 /* copy-chunk again, this time with sparse dest */
4008 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
4009 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
4011 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
4013 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
4014 torture_assert_ndr_success(torture, ndr_ret,
4015 "ndr_pull_srv_copychunk_rsp");
4017 ok = check_copy_chunk_rsp(torture, &cc_rsp,
4018 1, /* chunks written */
4019 0, /* chunk bytes unsuccessfully written */
4020 dealloc_chunk_len + 1024); /* bytes written */
4021 torture_assert(torture, ok, "bad copy chunk response data");
4023 ok = check_zero(torture, tree, tmp_ctx, dest_h, 0, dealloc_chunk_len);
4024 torture_assert(torture, ok, "sparse zeroed range");
4026 ok = check_pattern(torture, tree, tmp_ctx, dest_h, dealloc_chunk_len,
4027 1024, dealloc_chunk_len);
4028 torture_assert(torture, ok, "copychunked range");
4030 /* copied range may be allocated in sparse dest */
4031 status = test_ioctl_qar_req(torture, tmp_ctx, tree, dest_h,
4033 dealloc_chunk_len + 1024, /* len */
4036 torture_assert_ntstatus_ok(torture, status,
4037 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4038 torture_assert_u64_equal(torture, far_count, 1,
4039 "unexpected response len");
4041 * FS specific: sparse region may be unallocated in dest if copy-chunk
4042 * is handled in a sparse preserving way - E.g. vfs_btrfs
4043 * with BTRFS_IOC_CLONE_RANGE.
4045 if (far_rsp[0].file_off == dealloc_chunk_len) {
4046 torture_comment(torture, "copy-chunk sparse range preserved\n");
4047 torture_assert_u64_equal(torture, far_rsp[0].len, 1024,
4048 "unexpected far len");
4050 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4051 "unexpected allocation");
4052 torture_assert_u64_equal(torture, far_rsp[0].len,
4053 dealloc_chunk_len + 1024,
4054 "unexpected far len");
4057 smb2_util_close(tree, src_h);
4058 smb2_util_close(tree, dest_h);
4059 talloc_free(tmp_ctx);
4063 static bool test_ioctl_sparse_punch_invalid(struct torture_context *torture,
4064 struct smb2_tree *tree)
4066 struct smb2_handle fh;
4068 TALLOC_CTX *tmp_ctx = talloc_new(tree);
4073 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4074 FNAME, &fh, 4096, SEC_RIGHTS_FILE_ALL,
4075 FILE_ATTRIBUTE_NORMAL);
4076 torture_assert(torture, ok, "setup file");
4078 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
4079 FILE_SUPPORTS_SPARSE_FILES, &ok);
4080 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4082 smb2_util_close(tree, fh);
4083 torture_skip(torture, "Sparse files not supported\n");
4086 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4087 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4088 torture_assert(torture, !is_sparse, "sparse attr before set");
4090 /* loop twice, without and with sparse attrib */
4091 for (i = 0; i <= 1; i++) {
4092 union smb_fileinfo io;
4093 struct file_alloced_range_buf *far_rsp = NULL;
4094 uint64_t far_count = 0;
4096 /* get size before & after. zero data should never change it */
4098 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
4099 io.generic.in.file.handle = fh;
4100 status = smb2_getinfo_file(tree, tmp_ctx, &io);
4101 torture_assert_ntstatus_ok(torture, status, "getinfo");
4102 torture_assert_int_equal(torture, (int)io.all_info2.out.size,
4103 4096, "size after IO");
4105 /* valid 8 byte zero data, but after EOF */
4106 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4108 4104); /* beyond_final_zero */
4109 torture_assert_ntstatus_ok(torture, status, "zero_data");
4111 /* valid 8 byte zero data, but after EOF */
4112 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4114 8200); /* beyond_final_zero */
4115 torture_assert_ntstatus_ok(torture, status, "zero_data");
4118 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
4119 io.generic.in.file.handle = fh;
4120 status = smb2_getinfo_file(tree, tmp_ctx, &io);
4121 torture_assert_ntstatus_ok(torture, status, "getinfo");
4122 torture_assert_int_equal(torture, (int)io.all_info2.out.size,
4123 4096, "size after IO");
4125 /* valid 0 byte zero data, without sparse flag */
4126 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4128 4095); /* beyond_final_zero */
4129 torture_assert_ntstatus_ok(torture, status, "zero_data");
4131 /* INVALID off is past beyond_final_zero */
4132 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4134 4095); /* beyond_final_zero */
4135 torture_assert_ntstatus_equal(torture, status,
4136 NT_STATUS_INVALID_PARAMETER,
4137 "invalid zero_data");
4139 /* zero length QAR - valid */
4140 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4143 &far_rsp, &far_count);
4144 torture_assert_ntstatus_ok(torture, status,
4145 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4146 torture_assert_u64_equal(torture, far_count, 0,
4147 "unexpected response len");
4149 /* QAR after EOF - valid */
4150 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4153 &far_rsp, &far_count);
4154 torture_assert_ntstatus_ok(torture, status,
4155 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4156 torture_assert_u64_equal(torture, far_count, 0,
4157 "unexpected response len");
4160 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh,
4162 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4165 smb2_util_close(tree, fh);
4166 talloc_free(tmp_ctx);
4170 static bool test_ioctl_sparse_perms(struct torture_context *torture,
4171 struct smb2_tree *tree)
4173 struct smb2_handle fh;
4175 TALLOC_CTX *tmp_ctx = talloc_new(tree);
4178 struct file_alloced_range_buf *far_rsp = NULL;
4179 uint64_t far_count = 0;
4181 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4182 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
4183 FILE_ATTRIBUTE_NORMAL);
4184 torture_assert(torture, ok, "setup file");
4186 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
4187 FILE_SUPPORTS_SPARSE_FILES, &ok);
4188 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4189 smb2_util_close(tree, fh);
4191 torture_skip(torture, "Sparse files not supported\n");
4194 /* set sparse without WRITE_ATTR permission should succeed */
4195 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4197 (SEC_RIGHTS_FILE_WRITE & ~(SEC_FILE_WRITE_ATTRIBUTE
4199 | SEC_FILE_WRITE_EA)),
4200 FILE_ATTRIBUTE_NORMAL);
4201 torture_assert(torture, ok, "setup file");
4203 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4204 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4205 smb2_util_close(tree, fh);
4207 ok = test_setup_open(torture, tree, tmp_ctx,
4208 FNAME, &fh, SEC_RIGHTS_FILE_ALL,
4209 FILE_ATTRIBUTE_NORMAL);
4210 torture_assert(torture, ok, "setup file");
4211 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4212 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4213 torture_assert(torture, is_sparse, "sparse after set");
4214 smb2_util_close(tree, fh);
4216 /* attempt get sparse without READ_DATA permission */
4217 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4219 (SEC_RIGHTS_FILE_READ & ~SEC_FILE_READ_DATA),
4220 FILE_ATTRIBUTE_NORMAL);
4221 torture_assert(torture, ok, "setup file");
4223 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4224 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4225 torture_assert(torture, !is_sparse, "sparse set");
4226 smb2_util_close(tree, fh);
4228 /* attempt to set sparse with only WRITE_ATTR permission */
4229 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4231 SEC_FILE_WRITE_ATTRIBUTE,
4232 FILE_ATTRIBUTE_NORMAL);
4233 torture_assert(torture, ok, "setup file");
4235 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4236 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4237 smb2_util_close(tree, fh);
4239 /* attempt to set sparse with only WRITE_DATA permission */
4240 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4242 SEC_FILE_WRITE_DATA,
4243 FILE_ATTRIBUTE_NORMAL);
4244 torture_assert(torture, ok, "setup file");
4246 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4247 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4248 smb2_util_close(tree, fh);
4250 ok = test_setup_open(torture, tree, tmp_ctx,
4251 FNAME, &fh, SEC_RIGHTS_FILE_ALL,
4252 FILE_ATTRIBUTE_NORMAL);
4253 torture_assert(torture, ok, "setup file");
4254 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4255 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4256 torture_assert(torture, is_sparse, "sparse after set");
4257 smb2_util_close(tree, fh);
4259 /* attempt to set sparse with only APPEND_DATA permission */
4260 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4262 SEC_FILE_APPEND_DATA,
4263 FILE_ATTRIBUTE_NORMAL);
4264 torture_assert(torture, ok, "setup file");
4266 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4267 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4268 smb2_util_close(tree, fh);
4270 ok = test_setup_open(torture, tree, tmp_ctx,
4271 FNAME, &fh, SEC_RIGHTS_FILE_ALL,
4272 FILE_ATTRIBUTE_NORMAL);
4273 torture_assert(torture, ok, "setup file");
4274 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4275 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4276 torture_assert(torture, is_sparse, "sparse after set");
4277 smb2_util_close(tree, fh);
4279 /* attempt to set sparse with only WRITE_EA permission - should fail */
4280 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4283 FILE_ATTRIBUTE_NORMAL);
4284 torture_assert(torture, ok, "setup file");
4286 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4287 torture_assert_ntstatus_equal(torture, status,
4288 NT_STATUS_ACCESS_DENIED,
4289 "FSCTL_SET_SPARSE permission");
4290 smb2_util_close(tree, fh);
4292 ok = test_setup_open(torture, tree, tmp_ctx,
4293 FNAME, &fh, SEC_RIGHTS_FILE_ALL,
4294 FILE_ATTRIBUTE_NORMAL);
4295 torture_assert(torture, ok, "setup file");
4296 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4297 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4298 torture_assert(torture, !is_sparse, "sparse after set");
4299 smb2_util_close(tree, fh);
4301 /* attempt QAR with only READ_ATTR permission - should fail */
4302 ok = test_setup_open(torture, tree, tmp_ctx,
4303 FNAME, &fh, SEC_FILE_READ_ATTRIBUTE,
4304 FILE_ATTRIBUTE_NORMAL);
4305 torture_assert(torture, ok, "setup file");
4306 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4309 &far_rsp, &far_count);
4310 torture_assert_ntstatus_equal(torture, status,
4311 NT_STATUS_ACCESS_DENIED,
4312 "FSCTL_QUERY_ALLOCATED_RANGES req passed");
4313 smb2_util_close(tree, fh);
4315 /* attempt QAR with only READ_DATA permission */
4316 ok = test_setup_open(torture, tree, tmp_ctx,
4317 FNAME, &fh, SEC_FILE_READ_DATA,
4318 FILE_ATTRIBUTE_NORMAL);
4319 torture_assert(torture, ok, "setup file");
4320 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4323 &far_rsp, &far_count);
4324 torture_assert_ntstatus_ok(torture, status,
4325 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4326 torture_assert_u64_equal(torture, far_count, 0,
4327 "unexpected response len");
4328 smb2_util_close(tree, fh);
4330 /* attempt QAR with only READ_EA permission - should fail */
4331 ok = test_setup_open(torture, tree, tmp_ctx,
4332 FNAME, &fh, SEC_FILE_READ_EA,
4333 FILE_ATTRIBUTE_NORMAL);
4334 torture_assert(torture, ok, "setup file");
4335 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4338 &far_rsp, &far_count);
4339 torture_assert_ntstatus_equal(torture, status,
4340 NT_STATUS_ACCESS_DENIED,
4341 "FSCTL_QUERY_ALLOCATED_RANGES req passed");
4342 smb2_util_close(tree, fh);
4344 /* setup file for ZERO_DATA permissions tests */
4345 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4347 SEC_RIGHTS_FILE_ALL,
4348 FILE_ATTRIBUTE_NORMAL);
4349 torture_assert(torture, ok, "setup file");
4351 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4352 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4353 smb2_util_close(tree, fh);
4355 /* attempt ZERO_DATA with only WRITE_ATTR permission - should fail */
4356 ok = test_setup_open(torture, tree, tmp_ctx,
4357 FNAME, &fh, SEC_FILE_WRITE_ATTRIBUTE,
4358 FILE_ATTRIBUTE_NORMAL);
4359 torture_assert(torture, ok, "setup file");
4360 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4362 4096); /* beyond_final_zero */
4363 torture_assert_ntstatus_equal(torture, status,
4364 NT_STATUS_ACCESS_DENIED,
4365 "zero_data permission");
4366 smb2_util_close(tree, fh);
4368 /* attempt ZERO_DATA with only WRITE_DATA permission */
4369 ok = test_setup_open(torture, tree, tmp_ctx,
4370 FNAME, &fh, SEC_FILE_WRITE_DATA,
4371 FILE_ATTRIBUTE_NORMAL);
4372 torture_assert(torture, ok, "setup file");
4373 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4375 4096); /* beyond_final_zero */
4376 torture_assert_ntstatus_ok(torture, status, "zero_data");
4377 smb2_util_close(tree, fh);
4379 /* attempt ZERO_DATA with only APPEND_DATA permission - should fail */
4380 ok = test_setup_open(torture, tree, tmp_ctx,
4381 FNAME, &fh, SEC_FILE_APPEND_DATA,
4382 FILE_ATTRIBUTE_NORMAL);
4383 torture_assert(torture, ok, "setup file");
4384 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4386 4096); /* beyond_final_zero */
4387 torture_assert_ntstatus_equal(torture, status,
4388 NT_STATUS_ACCESS_DENIED,
4389 "zero_data permission");
4390 smb2_util_close(tree, fh);
4392 /* attempt ZERO_DATA with only WRITE_EA permission - should fail */
4393 ok = test_setup_open(torture, tree, tmp_ctx,
4394 FNAME, &fh, SEC_FILE_WRITE_EA,
4395 FILE_ATTRIBUTE_NORMAL);
4396 torture_assert(torture, ok, "setup file");
4397 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4399 4096); /* beyond_final_zero */
4400 torture_assert_ntstatus_equal(torture, status,
4401 NT_STATUS_ACCESS_DENIED,
4402 "zero_data permission");
4403 smb2_util_close(tree, fh);
4405 talloc_free(tmp_ctx);
4409 static bool test_ioctl_sparse_lck(struct torture_context *torture,
4410 struct smb2_tree *tree)
4412 struct smb2_handle fh;
4413 struct smb2_handle fh2;
4415 uint64_t dealloc_chunk_len = 64 * 1024; /* Windows 2012 */
4416 TALLOC_CTX *tmp_ctx = talloc_new(tree);
4419 struct smb2_lock lck;
4420 struct smb2_lock_element el[1];
4421 struct file_alloced_range_buf *far_rsp = NULL;
4422 uint64_t far_count = 0;
4424 ok = test_setup_create_fill(torture, tree, tmp_ctx, FNAME, &fh,
4425 dealloc_chunk_len, SEC_RIGHTS_FILE_ALL,
4426 FILE_ATTRIBUTE_NORMAL);
4427 torture_assert(torture, ok, "setup file");
4429 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
4430 FILE_SUPPORTS_SPARSE_FILES, &ok);
4431 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4433 torture_skip(torture, "Sparse files not supported\n");
4434 smb2_util_close(tree, fh);
4437 /* open and lock via separate fh2 */
4438 status = torture_smb2_testfile(tree, FNAME, &fh2);
4439 torture_assert_ntstatus_ok(torture, status, "2nd src open");
4441 lck.in.lock_count = 0x0001;
4442 lck.in.lock_sequence = 0x00000000;
4443 lck.in.file.handle = fh2;
4446 el[0].length = dealloc_chunk_len;
4448 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
4450 status = smb2_lock(tree, &lck);
4451 torture_assert_ntstatus_ok(torture, status, "lock");
4453 /* set sparse while locked */
4454 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4455 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4457 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4458 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4459 torture_assert(torture, is_sparse, "sparse attr after set");
4461 /* zero data over locked range should fail */
4462 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4464 4096); /* beyond_final_zero */
4465 torture_assert_ntstatus_equal(torture, status,
4466 NT_STATUS_FILE_LOCK_CONFLICT,
4467 "zero_data locked");
4469 /* QAR over locked range should pass */
4470 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4473 &far_rsp, &far_count);
4474 torture_assert_ntstatus_ok(torture, status,
4475 "FSCTL_QUERY_ALLOCATED_RANGES locked");
4476 torture_assert_u64_equal(torture, far_count, 1,
4477 "unexpected response len");
4478 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4479 "unexpected allocation");
4480 torture_assert_u64_equal(torture, far_rsp[0].len,
4482 "unexpected far len");
4484 /* zero data over range past EOF should pass */
4485 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4486 dealloc_chunk_len, /* off */
4487 dealloc_chunk_len + 4096);
4488 torture_assert_ntstatus_ok(torture, status,
4489 "zero_data past EOF locked");
4491 /* QAR over range past EOF should pass */
4492 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4493 dealloc_chunk_len, /* off */
4495 &far_rsp, &far_count);
4496 torture_assert_ntstatus_ok(torture, status,
4497 "FSCTL_QUERY_ALLOCATED_RANGES past EOF locked");
4498 torture_assert_u64_equal(torture, far_count, 0,
4499 "unexpected response len");
4501 lck.in.lock_count = 0x0001;
4502 lck.in.lock_sequence = 0x00000001;
4503 lck.in.file.handle = fh2;
4506 el[0].length = dealloc_chunk_len;
4508 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
4509 status = smb2_lock(tree, &lck);
4510 torture_assert_ntstatus_ok(torture, status, "unlock");
4512 smb2_util_close(tree, fh2);
4513 smb2_util_close(tree, fh);
4514 talloc_free(tmp_ctx);
4518 /* alleviate QAR off-by-one bug paranoia - help me ob1 */
4519 static bool test_ioctl_sparse_qar_ob1(struct torture_context *torture,
4520 struct smb2_tree *tree)
4522 struct smb2_handle fh;
4524 TALLOC_CTX *tmp_ctx = talloc_new(tree);
4526 uint64_t dealloc_chunk_len = 64 * 1024; /* Windows 2012 */
4527 struct file_alloced_range_buf *far_rsp = NULL;
4528 uint64_t far_count = 0;
4530 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4531 FNAME, &fh, dealloc_chunk_len * 2,
4532 SEC_RIGHTS_FILE_ALL,
4533 FILE_ATTRIBUTE_NORMAL);
4534 torture_assert(torture, ok, "setup file");
4536 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
4537 FILE_SUPPORTS_SPARSE_FILES, &ok);
4538 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4540 torture_skip(torture, "Sparse files not supported\n");
4541 smb2_util_close(tree, fh);
4544 /* non-sparse QAR with range one before EOF */
4545 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4547 dealloc_chunk_len * 2 - 1, /* len */
4548 &far_rsp, &far_count);
4549 torture_assert_ntstatus_ok(torture, status,
4550 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4551 torture_assert_u64_equal(torture, far_count, 1,
4552 "unexpected response len");
4553 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4554 "unexpected allocation");
4555 torture_assert_u64_equal(torture, far_rsp[0].len,
4556 dealloc_chunk_len * 2 - 1,
4557 "unexpected far len");
4559 /* non-sparse QAR with range one after EOF */
4560 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4562 dealloc_chunk_len * 2 + 1, /* len */
4563 &far_rsp, &far_count);
4564 torture_assert_ntstatus_ok(torture, status,
4565 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4566 torture_assert_u64_equal(torture, far_count, 1,
4567 "unexpected response len");
4568 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4569 "unexpected allocation");
4570 torture_assert_u64_equal(torture, far_rsp[0].len,
4571 dealloc_chunk_len * 2,
4572 "unexpected far len");
4574 /* non-sparse QAR with range one after EOF from off=1 */
4575 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4577 dealloc_chunk_len * 2, /* len */
4578 &far_rsp, &far_count);
4579 torture_assert_ntstatus_ok(torture, status,
4580 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4581 torture_assert_u64_equal(torture, far_count, 1,
4582 "unexpected response len");
4583 torture_assert_u64_equal(torture, far_rsp[0].file_off, 1,
4584 "unexpected allocation");
4585 torture_assert_u64_equal(torture, far_rsp[0].len,
4586 dealloc_chunk_len * 2 - 1,
4587 "unexpected far len");
4589 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4590 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4592 /* punch out second chunk */
4593 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4594 dealloc_chunk_len, /* off */
4595 dealloc_chunk_len * 2);
4596 torture_assert_ntstatus_ok(torture, status, "zero_data");
4598 /* sparse QAR with range one before hole */
4599 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4601 dealloc_chunk_len - 1, /* len */
4602 &far_rsp, &far_count);
4603 torture_assert_ntstatus_ok(torture, status,
4604 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4605 torture_assert_u64_equal(torture, far_count, 1,
4606 "unexpected response len");
4607 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4608 "unexpected allocation");
4609 torture_assert_u64_equal(torture, far_rsp[0].len,
4610 dealloc_chunk_len - 1,
4611 "unexpected far len");
4613 /* sparse QAR with range one after hole */
4614 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4616 dealloc_chunk_len + 1, /* len */
4617 &far_rsp, &far_count);
4618 torture_assert_ntstatus_ok(torture, status,
4619 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4620 torture_assert_u64_equal(torture, far_count, 1,
4621 "unexpected response len");
4622 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4623 "unexpected allocation");
4624 torture_assert_u64_equal(torture, far_rsp[0].len,
4626 "unexpected far len");
4628 /* sparse QAR with range one after hole from off=1 */
4629 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4631 dealloc_chunk_len, /* len */
4632 &far_rsp, &far_count);
4633 torture_assert_ntstatus_ok(torture, status,
4634 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4635 torture_assert_u64_equal(torture, far_count, 1,
4636 "unexpected response len");
4637 torture_assert_u64_equal(torture, far_rsp[0].file_off, 1,
4638 "unexpected allocation");
4639 torture_assert_u64_equal(torture, far_rsp[0].len,
4640 dealloc_chunk_len - 1,
4641 "unexpected far len");
4643 /* sparse QAR with range one before EOF from off=chunk_len-1 */
4644 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4645 dealloc_chunk_len - 1, /* off */
4646 dealloc_chunk_len, /* len */
4647 &far_rsp, &far_count);
4648 torture_assert_ntstatus_ok(torture, status,
4649 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4650 torture_assert_u64_equal(torture, far_count, 1,
4651 "unexpected response len");
4652 torture_assert_u64_equal(torture, far_rsp[0].file_off,
4653 dealloc_chunk_len - 1,
4654 "unexpected allocation");
4655 torture_assert_u64_equal(torture, far_rsp[0].len,
4656 1, "unexpected far len");
4658 /* sparse QAR with range one after EOF from off=chunk_len+1 */
4659 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4660 dealloc_chunk_len + 1, /* off */
4661 dealloc_chunk_len, /* len */
4662 &far_rsp, &far_count);
4663 torture_assert_ntstatus_ok(torture, status,
4664 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4665 torture_assert_u64_equal(torture, far_count, 0,
4666 "unexpected response len");
4667 smb2_util_close(tree, fh);
4668 talloc_free(tmp_ctx);
4672 /* test QAR with multi-range responses */
4673 static bool test_ioctl_sparse_qar_multi(struct torture_context *torture,
4674 struct smb2_tree *tree)
4676 struct smb2_handle fh;
4678 TALLOC_CTX *tmp_ctx = talloc_new(tree);
4680 uint64_t dealloc_chunk_len = 64 * 1024; /* Windows 2012 */
4683 struct file_alloced_range_buf *far_rsp = NULL;
4684 uint64_t far_count = 0;
4686 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4687 FNAME, &fh, dealloc_chunk_len * 2,
4688 SEC_RIGHTS_FILE_ALL,
4689 FILE_ATTRIBUTE_NORMAL);
4690 torture_assert(torture, ok, "setup file");
4692 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
4693 FILE_SUPPORTS_SPARSE_FILES, &ok);
4694 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4696 torture_skip(torture, "Sparse files not supported\n");
4697 smb2_util_close(tree, fh);
4700 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4701 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4703 /* each loop, write out two chunks and punch the first out */
4704 for (i = 0; i < 10; i++) {
4705 this_off = i * dealloc_chunk_len * 2;
4707 ok = write_pattern(torture, tree, tmp_ctx, fh,
4709 dealloc_chunk_len * 2, /* len */
4710 this_off); /* pattern offset */
4711 torture_assert(torture, ok, "write pattern");
4713 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4715 this_off + dealloc_chunk_len);
4716 torture_assert_ntstatus_ok(torture, status, "zero_data");
4719 /* should now have one separate region for each iteration */
4720 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4722 10 * dealloc_chunk_len * 2,
4723 &far_rsp, &far_count);
4724 torture_assert_ntstatus_ok(torture, status,
4725 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4726 if (far_count == 1) {
4727 torture_comment(torture, "this FS doesn't deallocate 64K"
4728 "zeroed ranges in sparse files\n");
4729 return true; /* FS specific, not a failure */
4731 torture_assert_u64_equal(torture, far_count, 10,
4732 "unexpected response len");
4733 for (i = 0; i < 10; i++) {
4734 this_off = i * dealloc_chunk_len * 2;
4736 torture_assert_u64_equal(torture, far_rsp[i].file_off,
4737 this_off + dealloc_chunk_len,
4738 "unexpected allocation");
4739 torture_assert_u64_equal(torture, far_rsp[i].len,
4741 "unexpected far len");
4744 smb2_util_close(tree, fh);
4745 talloc_free(tmp_ctx);
4749 static bool test_ioctl_sparse_qar_overflow(struct torture_context *torture,
4750 struct smb2_tree *tree)
4752 struct smb2_handle fh;
4753 union smb_ioctl ioctl;
4754 struct file_alloced_range_buf far_buf;
4756 enum ndr_err_code ndr_ret;
4757 TALLOC_CTX *tmp_ctx = talloc_new(tree);
4760 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4761 FNAME, &fh, 1024, SEC_RIGHTS_FILE_ALL,
4762 FILE_ATTRIBUTE_NORMAL);
4763 torture_assert(torture, ok, "setup file");
4765 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
4766 FILE_SUPPORTS_SPARSE_FILES, &ok);
4767 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4769 smb2_util_close(tree, fh);
4770 torture_skip(torture, "Sparse files not supported\n");
4773 /* no allocated ranges, no space for range response, should pass */
4775 ioctl.smb2.level = RAW_IOCTL_SMB2;
4776 ioctl.smb2.in.file.handle = fh;
4777 ioctl.smb2.in.function = FSCTL_QUERY_ALLOCATED_RANGES;
4778 ioctl.smb2.in.max_response_size = 1024;
4779 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
4781 /* off + length wraps around to 511 */
4782 far_buf.file_off = 512;
4783 far_buf.len = 0xffffffffffffffffLL;
4784 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
4786 (ndr_push_flags_fn_t)ndr_push_file_alloced_range_buf);
4787 torture_assert_ndr_success(torture, ndr_ret, "push far ndr buf");
4789 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
4790 torture_assert_ntstatus_equal(torture, status,
4791 NT_STATUS_INVALID_PARAMETER,
4792 "FSCTL_QUERY_ALLOCATED_RANGES overflow");
4797 static NTSTATUS test_ioctl_trim_supported(struct torture_context *torture,
4798 struct smb2_tree *tree,
4799 TALLOC_CTX *mem_ctx,
4800 struct smb2_handle *fh,
4804 union smb_fsinfo info;
4807 info.generic.level = RAW_QFS_SECTOR_SIZE_INFORMATION;
4808 info.generic.handle = *fh;
4809 status = smb2_getinfo_fs(tree, tree, &info);
4810 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_INFO_CLASS)) {
4812 * Windows < Server 2012, 8 etc. don't support this info level
4813 * or the trim ioctl. Ignore the error and let the caller skip.
4815 *trim_support = false;
4816 return NT_STATUS_OK;
4817 } else if (!NT_STATUS_IS_OK(status)) {
4821 torture_comment(torture, "sector size info: lb/s=%u, pb/sA=%u, "
4822 "pb/sP=%u, fse/sA=%u, flags=0x%x, bosa=%u, bopa=%u\n",
4823 (unsigned)info.sector_size_info.out.logical_bytes_per_sector,
4824 (unsigned)info.sector_size_info.out.phys_bytes_per_sector_atomic,
4825 (unsigned)info.sector_size_info.out.phys_bytes_per_sector_perf,
4826 (unsigned)info.sector_size_info.out.fs_effective_phys_bytes_per_sector_atomic,
4827 (unsigned)info.sector_size_info.out.flags,
4828 (unsigned)info.sector_size_info.out.byte_off_sector_align,
4829 (unsigned)info.sector_size_info.out.byte_off_partition_align);
4831 if (info.sector_size_info.out.flags & QFS_SSINFO_FLAGS_TRIM_ENABLED) {
4832 *trim_support = true;
4834 *trim_support = false;
4836 return NT_STATUS_OK;
4839 static bool test_setup_trim(struct torture_context *torture,
4840 struct smb2_tree *tree,
4841 TALLOC_CTX *mem_ctx,
4842 uint32_t num_ranges,
4843 struct smb2_handle *fh,
4845 uint32_t desired_access,
4846 struct fsctl_file_level_trim_req *trim_req,
4847 union smb_ioctl *ioctl)
4851 ok = test_setup_create_fill(torture, tree, mem_ctx, FNAME,
4852 fh, file_size, desired_access,
4853 FILE_ATTRIBUTE_NORMAL);
4854 torture_assert(torture, ok, "src file create fill");
4856 ZERO_STRUCTPN(ioctl);
4857 ioctl->smb2.level = RAW_IOCTL_SMB2;
4858 ioctl->smb2.in.file.handle = *fh;
4859 ioctl->smb2.in.function = FSCTL_FILE_LEVEL_TRIM;
4860 ioctl->smb2.in.max_response_size
4861 = sizeof(struct fsctl_file_level_trim_rsp);
4862 ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
4864 ZERO_STRUCTPN(trim_req);
4865 /* leave key as zero for now. TODO test locking with differing keys */
4866 trim_req->num_ranges = num_ranges;
4867 trim_req->ranges = talloc_zero_array(mem_ctx,
4868 struct file_level_trim_range,
4870 torture_assert(torture, (trim_req->ranges != NULL), "no memory for ranges");
4875 static bool test_ioctl_trim_simple(struct torture_context *torture,
4876 struct smb2_tree *tree)
4878 struct smb2_handle fh;
4880 union smb_ioctl ioctl;
4881 bool trim_supported;
4882 TALLOC_CTX *tmp_ctx = talloc_new(tree);
4883 struct fsctl_file_level_trim_req trim_req;
4884 struct fsctl_file_level_trim_rsp trim_rsp;
4885 uint64_t trim_chunk_len = 64 * 1024; /* trim 64K chunks */
4886 enum ndr_err_code ndr_ret;
4889 ok = test_setup_trim(torture, tree, tmp_ctx,
4891 &fh, 2 * trim_chunk_len, /* fill 128K file */
4892 SEC_RIGHTS_FILE_ALL,
4896 torture_fail(torture, "setup trim error");
4899 status = test_ioctl_trim_supported(torture, tree, tmp_ctx, &fh,
4901 torture_assert_ntstatus_ok(torture, status, "fsinfo");
4902 if (!trim_supported) {
4903 smb2_util_close(tree, fh);
4904 talloc_free(tmp_ctx);
4905 torture_skip(torture, "trim not supported\n");
4908 /* trim first chunk, leave second */
4909 trim_req.ranges[0].off = 0;
4910 trim_req.ranges[0].len = trim_chunk_len;
4912 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, &trim_req,
4913 (ndr_push_flags_fn_t)ndr_push_fsctl_file_level_trim_req);
4914 torture_assert_ndr_success(torture, ndr_ret,
4915 "ndr_push_fsctl_file_level_trim_req");
4917 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
4918 torture_assert_ntstatus_ok(torture, status, "FILE_LEVEL_TRIM_RANGE");
4920 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
4922 (ndr_pull_flags_fn_t)ndr_pull_fsctl_file_level_trim_rsp);
4923 torture_assert_ndr_success(torture, ndr_ret,
4924 "ndr_pull_fsctl_file_level_trim_rsp");
4926 torture_assert_int_equal(torture, trim_rsp.num_ranges_processed, 1, "");
4928 /* second half of the file should remain consitent */
4929 ok = check_pattern(torture, tree, tmp_ctx, fh, trim_chunk_len,
4930 trim_chunk_len, trim_chunk_len);
4931 torture_assert(torture, ok, "non-trimmed range inconsistent");
4936 static bool test_setup_dup_extents(struct torture_context *tctx,
4937 struct smb2_tree *tree,
4938 TALLOC_CTX *mem_ctx,
4939 struct smb2_handle *src_h,
4941 uint32_t src_desired_access,
4942 struct smb2_handle *dest_h,
4944 uint32_t dest_desired_access,
4945 struct fsctl_dup_extents_to_file *dup_ext_buf,
4946 union smb_ioctl *ioctl)
4950 ok = test_setup_create_fill(tctx, tree, mem_ctx, FNAME,
4951 src_h, src_size, src_desired_access,
4952 FILE_ATTRIBUTE_NORMAL);
4953 torture_assert(tctx, ok, "src file create fill");
4955 ok = test_setup_create_fill(tctx, tree, mem_ctx, FNAME2,
4956 dest_h, dest_size, dest_desired_access,
4957 FILE_ATTRIBUTE_NORMAL);
4958 torture_assert(tctx, ok, "dest file create fill");
4960 ZERO_STRUCTPN(ioctl);
4961 ioctl->smb2.level = RAW_IOCTL_SMB2;
4962 ioctl->smb2.in.file.handle = *dest_h;
4963 ioctl->smb2.in.function = FSCTL_DUP_EXTENTS_TO_FILE;
4964 ioctl->smb2.in.max_response_size = 0;
4965 ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
4967 ZERO_STRUCTPN(dup_ext_buf);
4968 smb2_push_handle(dup_ext_buf->source_fid, src_h);
4973 static bool test_ioctl_dup_extents_simple(struct torture_context *tctx,
4974 struct smb2_tree *tree)
4976 struct smb2_handle src_h;
4977 struct smb2_handle dest_h;
4979 union smb_ioctl ioctl;
4980 TALLOC_CTX *tmp_ctx = talloc_new(tree);
4981 struct fsctl_dup_extents_to_file dup_ext_buf;
4982 enum ndr_err_code ndr_ret;
4983 union smb_fileinfo io;
4984 union smb_setfileinfo sinfo;
4987 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
4988 &src_h, 4096, /* fill 4096 byte src file */
4989 SEC_RIGHTS_FILE_ALL,
4990 &dest_h, 0, /* 0 byte dest file */
4991 SEC_RIGHTS_FILE_ALL,
4995 torture_fail(tctx, "setup dup extents error");
4998 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
4999 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
5000 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5002 smb2_util_close(tree, src_h);
5003 smb2_util_close(tree, dest_h);
5004 talloc_free(tmp_ctx);
5005 torture_skip(tctx, "block refcounting not supported\n");
5008 /* extend dest to match src len */
5010 sinfo.end_of_file_info.level =
5011 RAW_SFILEINFO_END_OF_FILE_INFORMATION;
5012 sinfo.end_of_file_info.in.file.handle = dest_h;
5013 sinfo.end_of_file_info.in.size = 4096;
5014 status = smb2_setinfo_file(tree, &sinfo);
5015 torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
5017 /* copy all src file data */
5018 dup_ext_buf.source_off = 0;
5019 dup_ext_buf.target_off = 0;
5020 dup_ext_buf.byte_count = 4096;
5022 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5024 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5025 torture_assert_ndr_success(tctx, ndr_ret,
5026 "ndr_push_fsctl_dup_extents_to_file");
5028 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5029 torture_assert_ntstatus_ok(tctx, status,
5030 "FSCTL_DUP_EXTENTS_TO_FILE");
5032 /* the file size shouldn't have been changed by this operation! */
5034 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5035 io.generic.in.file.handle = dest_h;
5036 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5037 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5038 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5039 4096, "size after IO");
5041 smb2_util_close(tree, src_h);
5042 smb2_util_close(tree, dest_h);
5044 /* reopen for pattern check */
5045 ok = test_setup_open(tctx, tree, tmp_ctx, FNAME, &src_h,
5046 SEC_RIGHTS_FILE_ALL, FILE_ATTRIBUTE_NORMAL);
5047 torture_assert_ntstatus_ok(tctx, status, "src open after dup");
5048 ok = test_setup_open(tctx, tree, tmp_ctx, FNAME2, &dest_h,
5049 SEC_RIGHTS_FILE_ALL, FILE_ATTRIBUTE_NORMAL);
5050 torture_assert_ntstatus_ok(tctx, status, "dest open after dup");
5052 ok = check_pattern(tctx, tree, tmp_ctx, src_h, 0, 4096, 0);
5054 torture_fail(tctx, "inconsistent src file data");
5057 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 4096, 0);
5059 torture_fail(tctx, "inconsistent dest file data");
5062 smb2_util_close(tree, src_h);
5063 smb2_util_close(tree, dest_h);
5064 talloc_free(tmp_ctx);
5068 static bool test_ioctl_dup_extents_len_beyond_dest(struct torture_context *tctx,
5069 struct smb2_tree *tree)
5071 struct smb2_handle src_h;
5072 struct smb2_handle dest_h;
5074 union smb_ioctl ioctl;
5075 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5076 struct fsctl_dup_extents_to_file dup_ext_buf;
5077 enum ndr_err_code ndr_ret;
5078 union smb_fileinfo io;
5079 union smb_setfileinfo sinfo;
5082 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5083 &src_h, 32768, /* fill 32768 byte src file */
5084 SEC_RIGHTS_FILE_ALL,
5085 &dest_h, 0, /* 0 byte dest file */
5086 SEC_RIGHTS_FILE_ALL,
5090 torture_fail(tctx, "setup dup extents error");
5093 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5094 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
5095 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5097 smb2_util_close(tree, src_h);
5098 smb2_util_close(tree, dest_h);
5099 talloc_free(tmp_ctx);
5100 torture_skip(tctx, "block refcounting not supported\n");
5104 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5105 io.generic.in.file.handle = dest_h;
5106 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5107 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5108 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5109 0, "size after IO");
5111 /* copy all src file data */
5112 dup_ext_buf.source_off = 0;
5113 dup_ext_buf.target_off = 0;
5114 dup_ext_buf.byte_count = 32768;
5116 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5118 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5119 torture_assert_ndr_success(tctx, ndr_ret,
5120 "ndr_push_fsctl_dup_extents_to_file");
5122 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5125 * 2.3.8 FSCTL_DUPLICATE_EXTENTS_TO_FILE Reply - this should fail, but
5126 * passes against WS2016 RTM!
5128 torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_SUPPORTED,
5129 "FSCTL_DUP_EXTENTS_TO_FILE");
5132 /* the file sizes shouldn't have been changed */
5134 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5135 io.generic.in.file.handle = src_h;
5136 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5137 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5138 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5139 32768, "size after IO");
5142 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5143 io.generic.in.file.handle = dest_h;
5144 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5145 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5146 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5147 0, "size after IO");
5151 sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
5152 sinfo.end_of_file_info.in.file.handle = dest_h;
5153 sinfo.end_of_file_info.in.size = 32768;
5154 status = smb2_setinfo_file(tree, &sinfo);
5155 torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
5157 ok = check_zero(tctx, tree, tmp_ctx, dest_h, 0, 32768);
5159 torture_fail(tctx, "inconsistent file data");
5162 /* reissue ioctl, now with enough space */
5163 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5164 torture_assert_ntstatus_ok(tctx, status,
5165 "FSCTL_DUP_EXTENTS_TO_FILE");
5167 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
5169 torture_fail(tctx, "inconsistent file data");
5172 smb2_util_close(tree, src_h);
5173 smb2_util_close(tree, dest_h);
5174 talloc_free(tmp_ctx);
5178 static bool test_ioctl_dup_extents_len_beyond_src(struct torture_context *tctx,
5179 struct smb2_tree *tree)
5181 struct smb2_handle src_h;
5182 struct smb2_handle dest_h;
5184 union smb_ioctl ioctl;
5185 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5186 struct fsctl_dup_extents_to_file dup_ext_buf;
5187 enum ndr_err_code ndr_ret;
5188 union smb_fileinfo io;
5191 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5192 &src_h, 32768, /* fill 32768 byte src file */
5193 SEC_RIGHTS_FILE_ALL,
5194 &dest_h, 0, /* 0 byte dest file */
5195 SEC_RIGHTS_FILE_ALL,
5199 torture_fail(tctx, "setup dup extents error");
5202 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5203 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
5204 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5206 smb2_util_close(tree, src_h);
5207 smb2_util_close(tree, dest_h);
5208 talloc_free(tmp_ctx);
5209 torture_skip(tctx, "block refcounting not supported\n");
5213 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5214 io.generic.in.file.handle = dest_h;
5215 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5216 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5217 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5218 0, "size after IO");
5220 /* exceed src file len */
5221 dup_ext_buf.source_off = 0;
5222 dup_ext_buf.target_off = 0;
5223 dup_ext_buf.byte_count = 32768 * 2;
5225 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5227 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5228 torture_assert_ndr_success(tctx, ndr_ret,
5229 "ndr_push_fsctl_dup_extents_to_file");
5231 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5232 torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_SUPPORTED,
5233 "FSCTL_DUP_EXTENTS_TO_FILE");
5235 /* the file sizes shouldn't have been changed */
5237 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5238 io.generic.in.file.handle = src_h;
5239 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5240 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5241 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5242 32768, "size after IO");
5245 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5246 io.generic.in.file.handle = dest_h;
5247 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5248 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5249 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5250 0, "size after IO");
5252 smb2_util_close(tree, src_h);
5253 smb2_util_close(tree, dest_h);
5254 talloc_free(tmp_ctx);
5258 static bool test_ioctl_dup_extents_len_zero(struct torture_context *tctx,
5259 struct smb2_tree *tree)
5261 struct smb2_handle src_h;
5262 struct smb2_handle dest_h;
5264 union smb_ioctl ioctl;
5265 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5266 struct fsctl_dup_extents_to_file dup_ext_buf;
5267 enum ndr_err_code ndr_ret;
5268 union smb_fileinfo io;
5271 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5272 &src_h, 32768, /* fill 32768 byte src file */
5273 SEC_RIGHTS_FILE_ALL,
5274 &dest_h, 0, /* 0 byte dest file */
5275 SEC_RIGHTS_FILE_ALL,
5279 torture_fail(tctx, "setup dup extents error");
5282 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5283 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
5284 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5286 smb2_util_close(tree, src_h);
5287 smb2_util_close(tree, dest_h);
5288 talloc_free(tmp_ctx);
5289 torture_skip(tctx, "block refcounting not supported\n");
5293 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5294 io.generic.in.file.handle = dest_h;
5295 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5296 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5297 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5298 0, "size after IO");
5300 dup_ext_buf.source_off = 0;
5301 dup_ext_buf.target_off = 0;
5302 dup_ext_buf.byte_count = 0;
5304 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5306 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5307 torture_assert_ndr_success(tctx, ndr_ret,
5308 "ndr_push_fsctl_dup_extents_to_file");
5310 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5311 torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
5313 /* the file sizes shouldn't have been changed */
5315 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5316 io.generic.in.file.handle = src_h;
5317 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5318 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5319 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5320 32768, "size after IO");
5323 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5324 io.generic.in.file.handle = dest_h;
5325 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5326 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5327 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5328 0, "size after IO");
5330 smb2_util_close(tree, src_h);
5331 smb2_util_close(tree, dest_h);
5332 talloc_free(tmp_ctx);
5336 static bool test_ioctl_dup_extents_sparse_src(struct torture_context *tctx,
5337 struct smb2_tree *tree)
5339 struct smb2_handle src_h;
5340 struct smb2_handle dest_h;
5342 union smb_ioctl ioctl;
5343 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5344 struct fsctl_dup_extents_to_file dup_ext_buf;
5345 enum ndr_err_code ndr_ret;
5346 union smb_setfileinfo sinfo;
5349 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5350 &src_h, 0, /* filled after sparse flag */
5351 SEC_RIGHTS_FILE_ALL,
5352 &dest_h, 0, /* 0 byte dest file */
5353 SEC_RIGHTS_FILE_ALL,
5357 torture_fail(tctx, "setup dup extents error");
5360 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5361 FILE_SUPPORTS_BLOCK_REFCOUNTING
5362 | FILE_SUPPORTS_SPARSE_FILES, &ok);
5363 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5365 smb2_util_close(tree, src_h);
5366 smb2_util_close(tree, dest_h);
5367 talloc_free(tmp_ctx);
5369 "block refcounting and sparse files not supported\n");
5372 /* set sparse flag on src */
5373 status = test_ioctl_sparse_req(tctx, tmp_ctx, tree, src_h, true);
5374 torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_SPARSE");
5376 ok = write_pattern(tctx, tree, tmp_ctx, src_h, 0, 4096, 0);
5377 torture_assert(tctx, ok, "write pattern");
5381 sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
5382 sinfo.end_of_file_info.in.file.handle = dest_h;
5383 sinfo.end_of_file_info.in.size = 4096;
5384 status = smb2_setinfo_file(tree, &sinfo);
5385 torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
5387 /* copy all src file data */
5388 dup_ext_buf.source_off = 0;
5389 dup_ext_buf.target_off = 0;
5390 dup_ext_buf.byte_count = 4096;
5392 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5394 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5395 torture_assert_ndr_success(tctx, ndr_ret,
5396 "ndr_push_fsctl_dup_extents_to_file");
5399 * src is sparse, but spec says: 2.3.8 FSCTL_DUPLICATE_EXTENTS_TO_FILE
5400 * Reply... STATUS_NOT_SUPPORTED: Target file is sparse, while source
5401 * is a non-sparse file.
5403 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5404 torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_SUPPORTED,
5405 "FSCTL_DUP_EXTENTS_TO_FILE");
5407 smb2_util_close(tree, src_h);
5408 smb2_util_close(tree, dest_h);
5409 talloc_free(tmp_ctx);
5413 static bool test_ioctl_dup_extents_sparse_dest(struct torture_context *tctx,
5414 struct smb2_tree *tree)
5416 struct smb2_handle src_h;
5417 struct smb2_handle dest_h;
5419 union smb_ioctl ioctl;
5420 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5421 struct fsctl_dup_extents_to_file dup_ext_buf;
5422 enum ndr_err_code ndr_ret;
5423 union smb_setfileinfo sinfo;
5426 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5427 &src_h, 4096, /* fill 4096 byte src file */
5428 SEC_RIGHTS_FILE_ALL,
5429 &dest_h, 0, /* 0 byte dest file */
5430 SEC_RIGHTS_FILE_ALL,
5434 torture_fail(tctx, "setup dup extents error");
5437 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5438 FILE_SUPPORTS_BLOCK_REFCOUNTING
5439 | FILE_SUPPORTS_SPARSE_FILES, &ok);
5440 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5442 smb2_util_close(tree, src_h);
5443 smb2_util_close(tree, dest_h);
5444 talloc_free(tmp_ctx);
5446 "block refcounting and sparse files not supported\n");
5449 /* set sparse flag on dest */
5450 status = test_ioctl_sparse_req(tctx, tmp_ctx, tree, dest_h, true);
5451 torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_SPARSE");
5455 sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
5456 sinfo.end_of_file_info.in.file.handle = dest_h;
5457 sinfo.end_of_file_info.in.size = dup_ext_buf.byte_count;
5458 status = smb2_setinfo_file(tree, &sinfo);
5459 torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
5461 /* copy all src file data */
5462 dup_ext_buf.source_off = 0;
5463 dup_ext_buf.target_off = 0;
5464 dup_ext_buf.byte_count = 4096;
5466 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5468 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5469 torture_assert_ndr_success(tctx, ndr_ret,
5470 "ndr_push_fsctl_dup_extents_to_file");
5473 * dest is sparse, but spec says: 2.3.8 FSCTL_DUPLICATE_EXTENTS_TO_FILE
5474 * Reply... STATUS_NOT_SUPPORTED: Target file is sparse, while source
5475 * is a non-sparse file.
5477 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5478 torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
5480 smb2_util_close(tree, src_h);
5481 smb2_util_close(tree, dest_h);
5482 talloc_free(tmp_ctx);
5486 static bool test_ioctl_dup_extents_sparse_both(struct torture_context *tctx,
5487 struct smb2_tree *tree)
5489 struct smb2_handle src_h;
5490 struct smb2_handle dest_h;
5492 union smb_ioctl ioctl;
5493 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5494 struct fsctl_dup_extents_to_file dup_ext_buf;
5495 enum ndr_err_code ndr_ret;
5496 union smb_setfileinfo sinfo;
5499 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5500 &src_h, 0, /* fill 4096 byte src file */
5501 SEC_RIGHTS_FILE_ALL,
5502 &dest_h, 0, /* 0 byte dest file */
5503 SEC_RIGHTS_FILE_ALL,
5507 torture_fail(tctx, "setup dup extents error");
5510 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5511 FILE_SUPPORTS_BLOCK_REFCOUNTING
5512 | FILE_SUPPORTS_SPARSE_FILES, &ok);
5513 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5515 smb2_util_close(tree, src_h);
5516 smb2_util_close(tree, dest_h);
5517 talloc_free(tmp_ctx);
5519 "block refcounting and sparse files not supported\n");
5522 /* set sparse flag on src and dest */
5523 status = test_ioctl_sparse_req(tctx, tmp_ctx, tree, src_h, true);
5524 torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_SPARSE");
5525 status = test_ioctl_sparse_req(tctx, tmp_ctx, tree, dest_h, true);
5526 torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_SPARSE");
5528 ok = write_pattern(tctx, tree, tmp_ctx, src_h, 0, 4096, 0);
5529 torture_assert(tctx, ok, "write pattern");
5533 sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
5534 sinfo.end_of_file_info.in.file.handle = dest_h;
5535 sinfo.end_of_file_info.in.size = 4096;
5536 status = smb2_setinfo_file(tree, &sinfo);
5537 torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
5539 /* copy all src file data */
5540 dup_ext_buf.source_off = 0;
5541 dup_ext_buf.target_off = 0;
5542 dup_ext_buf.byte_count = 4096;
5544 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5546 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5547 torture_assert_ndr_success(tctx, ndr_ret,
5548 "ndr_push_fsctl_dup_extents_to_file");
5550 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5551 torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
5553 smb2_util_close(tree, src_h);
5554 smb2_util_close(tree, dest_h);
5556 /* reopen for pattern check */
5557 ok = test_setup_open(tctx, tree, tmp_ctx, FNAME2, &dest_h,
5558 SEC_RIGHTS_FILE_ALL, FILE_ATTRIBUTE_NORMAL);
5559 torture_assert_ntstatus_ok(tctx, status, "dest open ater dup");
5561 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 4096, 0);
5563 torture_fail(tctx, "inconsistent file data");
5566 smb2_util_close(tree, dest_h);
5567 talloc_free(tmp_ctx);
5571 static bool test_ioctl_dup_extents_src_is_dest(struct torture_context *tctx,
5572 struct smb2_tree *tree)
5574 struct smb2_handle src_h;
5575 struct smb2_handle dest_h;
5577 union smb_ioctl ioctl;
5578 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5579 struct fsctl_dup_extents_to_file dup_ext_buf;
5580 enum ndr_err_code ndr_ret;
5581 union smb_fileinfo io;
5584 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5585 &src_h, 32768, /* fill 32768 byte src file */
5586 SEC_RIGHTS_FILE_ALL,
5588 SEC_RIGHTS_FILE_ALL,
5592 torture_fail(tctx, "setup dup extents error");
5594 /* dest_h not needed for this test */
5595 smb2_util_close(tree, dest_h);
5597 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5598 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
5599 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5601 smb2_util_close(tree, src_h);
5602 talloc_free(tmp_ctx);
5603 torture_skip(tctx, "block refcounting not supported\n");
5606 /* src and dest are the same file handle */
5607 ioctl.smb2.in.file.handle = src_h;
5609 /* no overlap between src and tgt */
5610 dup_ext_buf.source_off = 0;
5611 dup_ext_buf.target_off = 16384;
5612 dup_ext_buf.byte_count = 16384;
5614 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5616 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5617 torture_assert_ndr_success(tctx, ndr_ret,
5618 "ndr_push_fsctl_dup_extents_to_file");
5620 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5621 torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
5623 /* the file size shouldn't have been changed */
5625 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5626 io.generic.in.file.handle = src_h;
5627 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5628 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5629 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5630 32768, "size after IO");
5632 ok = check_pattern(tctx, tree, tmp_ctx, src_h, 0, 16384, 0);
5634 torture_fail(tctx, "inconsistent file data");
5636 ok = check_pattern(tctx, tree, tmp_ctx, src_h, 16384, 16384, 0);
5638 torture_fail(tctx, "inconsistent file data");
5641 smb2_util_close(tree, src_h);
5642 talloc_free(tmp_ctx);
5647 * unlike copy-chunk, dup extents doesn't support overlapping ranges between
5648 * source and target. This makes it a *lot* cleaner to implement on the server.
5651 test_ioctl_dup_extents_src_is_dest_overlap(struct torture_context *tctx,
5652 struct smb2_tree *tree)
5654 struct smb2_handle src_h;
5655 struct smb2_handle dest_h;
5657 union smb_ioctl ioctl;
5658 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5659 struct fsctl_dup_extents_to_file dup_ext_buf;
5660 enum ndr_err_code ndr_ret;
5661 union smb_fileinfo io;
5664 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5665 &src_h, 32768, /* fill 32768 byte src file */
5666 SEC_RIGHTS_FILE_ALL,
5668 SEC_RIGHTS_FILE_ALL,
5672 torture_fail(tctx, "setup dup extents error");
5674 /* dest_h not needed for this test */
5675 smb2_util_close(tree, dest_h);
5677 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5678 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
5679 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5681 smb2_util_close(tree, src_h);
5682 talloc_free(tmp_ctx);
5683 torture_skip(tctx, "block refcounting not supported\n");
5686 /* src and dest are the same file handle */
5687 ioctl.smb2.in.file.handle = src_h;
5689 /* 8K overlap between src and tgt */
5690 dup_ext_buf.source_off = 0;
5691 dup_ext_buf.target_off = 8192;
5692 dup_ext_buf.byte_count = 16384;
5694 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5696 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5697 torture_assert_ndr_success(tctx, ndr_ret,
5698 "ndr_push_fsctl_dup_extents_to_file");
5700 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5701 torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_SUPPORTED,
5702 "FSCTL_DUP_EXTENTS_TO_FILE");
5704 /* the file size and data should match beforehand */
5706 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5707 io.generic.in.file.handle = src_h;
5708 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5709 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5710 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5711 32768, "size after IO");
5713 ok = check_pattern(tctx, tree, tmp_ctx, src_h, 0, 32768, 0);
5715 torture_fail(tctx, "inconsistent file data");
5718 smb2_util_close(tree, src_h);
5719 talloc_free(tmp_ctx);
5724 * The compression tests won't run against Windows servers yet - ReFS doesn't
5725 * (yet) offer support for compression.
5727 static bool test_ioctl_dup_extents_compressed_src(struct torture_context *tctx,
5728 struct smb2_tree *tree)
5730 struct smb2_handle src_h;
5731 struct smb2_handle dest_h;
5733 union smb_ioctl ioctl;
5734 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5735 struct fsctl_dup_extents_to_file dup_ext_buf;
5736 enum ndr_err_code ndr_ret;
5737 union smb_setfileinfo sinfo;
5740 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5741 &src_h, 0, /* filled after compressed flag */
5742 SEC_RIGHTS_FILE_ALL,
5744 SEC_RIGHTS_FILE_ALL,
5748 torture_fail(tctx, "setup dup extents error");
5751 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5752 FILE_SUPPORTS_BLOCK_REFCOUNTING
5753 | FILE_FILE_COMPRESSION, &ok);
5754 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5756 smb2_util_close(tree, src_h);
5757 smb2_util_close(tree, dest_h);
5758 talloc_free(tmp_ctx);
5760 "block refcounting and compressed files not supported\n");
5763 /* set compressed flag on src */
5764 status = test_ioctl_compress_set(tctx, tmp_ctx, tree, src_h,
5765 COMPRESSION_FORMAT_DEFAULT);
5766 torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_COMPRESSION");
5768 ok = write_pattern(tctx, tree, tmp_ctx, src_h, 0, 4096, 0);
5769 torture_assert(tctx, ok, "write pattern");
5773 sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
5774 sinfo.end_of_file_info.in.file.handle = dest_h;
5775 sinfo.end_of_file_info.in.size = 4096;
5776 status = smb2_setinfo_file(tree, &sinfo);
5777 torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
5779 /* copy all src file data */
5780 dup_ext_buf.source_off = 0;
5781 dup_ext_buf.target_off = 0;
5782 dup_ext_buf.byte_count = 4096;
5784 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5786 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5787 torture_assert_ndr_success(tctx, ndr_ret,
5788 "ndr_push_fsctl_dup_extents_to_file");
5790 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5791 torture_assert_ntstatus_ok(tctx, status,
5792 "FSCTL_DUP_EXTENTS_TO_FILE");
5794 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 4096, 0);
5796 torture_fail(tctx, "inconsistent file data");
5799 smb2_util_close(tree, src_h);
5800 smb2_util_close(tree, dest_h);
5801 talloc_free(tmp_ctx);
5805 static bool test_ioctl_dup_extents_compressed_dest(struct torture_context *tctx,
5806 struct smb2_tree *tree)
5808 struct smb2_handle src_h;
5809 struct smb2_handle dest_h;
5811 union smb_ioctl ioctl;
5812 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5813 struct fsctl_dup_extents_to_file dup_ext_buf;
5814 enum ndr_err_code ndr_ret;
5815 union smb_setfileinfo sinfo;
5818 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5820 SEC_RIGHTS_FILE_ALL,
5822 SEC_RIGHTS_FILE_ALL,
5826 torture_fail(tctx, "setup dup extents error");
5829 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5830 FILE_SUPPORTS_BLOCK_REFCOUNTING
5831 | FILE_FILE_COMPRESSION, &ok);
5832 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5834 smb2_util_close(tree, src_h);
5835 smb2_util_close(tree, dest_h);
5836 talloc_free(tmp_ctx);
5838 "block refcounting and compressed files not supported\n");
5841 /* set compressed flag on dest */
5842 status = test_ioctl_compress_set(tctx, tmp_ctx, tree, dest_h,
5843 COMPRESSION_FORMAT_DEFAULT);
5844 torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_COMPRESSION");
5848 sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
5849 sinfo.end_of_file_info.in.file.handle = dest_h;
5850 sinfo.end_of_file_info.in.size = dup_ext_buf.byte_count;
5851 status = smb2_setinfo_file(tree, &sinfo);
5852 torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
5854 /* copy all src file data */
5855 dup_ext_buf.source_off = 0;
5856 dup_ext_buf.target_off = 0;
5857 dup_ext_buf.byte_count = 4096;
5859 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5861 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5862 torture_assert_ndr_success(tctx, ndr_ret,
5863 "ndr_push_fsctl_dup_extents_to_file");
5865 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5866 torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_SUPPORTED,
5867 "FSCTL_DUP_EXTENTS_TO_FILE");
5869 smb2_util_close(tree, src_h);
5870 smb2_util_close(tree, dest_h);
5871 talloc_free(tmp_ctx);
5875 static bool test_ioctl_dup_extents_bad_handle(struct torture_context *tctx,
5876 struct smb2_tree *tree)
5878 struct smb2_handle src_h;
5879 struct smb2_handle dest_h;
5880 struct smb2_handle bogus_h;
5882 union smb_ioctl ioctl;
5883 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5884 struct fsctl_dup_extents_to_file dup_ext_buf;
5885 enum ndr_err_code ndr_ret;
5888 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5889 &src_h, 32768, /* fill 32768 byte src file */
5890 SEC_RIGHTS_FILE_ALL,
5892 SEC_RIGHTS_FILE_ALL,
5896 torture_fail(tctx, "setup dup extents error");
5899 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5900 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
5901 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5903 smb2_util_close(tree, src_h);
5904 smb2_util_close(tree, dest_h);
5905 talloc_free(tmp_ctx);
5906 torture_skip(tctx, "block refcounting not supported\n");
5909 /* open and close a file, keeping the handle as now a "bogus" handle */
5910 ok = test_setup_create_fill(tctx, tree, tmp_ctx, "bogus_file",
5911 &bogus_h, 0, SEC_RIGHTS_FILE_ALL,
5912 FILE_ATTRIBUTE_NORMAL);
5913 torture_assert(tctx, ok, "bogus file create fill");
5914 smb2_util_close(tree, bogus_h);
5916 /* bogus dest file handle */
5917 ioctl.smb2.in.file.handle = bogus_h;
5919 dup_ext_buf.source_off = 0;
5920 dup_ext_buf.target_off = 0;
5921 dup_ext_buf.byte_count = 32768;
5923 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5925 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5926 torture_assert_ndr_success(tctx, ndr_ret,
5927 "ndr_push_fsctl_dup_extents_to_file");
5929 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5930 torture_assert_ntstatus_equal(tctx, status, NT_STATUS_FILE_CLOSED,
5931 "FSCTL_DUP_EXTENTS_TO_FILE");
5933 ok = check_pattern(tctx, tree, tmp_ctx, src_h, 0, 32768, 0);
5935 torture_fail(tctx, "inconsistent file data");
5937 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
5939 torture_fail(tctx, "inconsistent file data");
5942 /* reinstate dest, add bogus src file handle */
5943 ioctl.smb2.in.file.handle = dest_h;
5944 smb2_push_handle(dup_ext_buf.source_fid, &bogus_h);
5946 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5948 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5949 torture_assert_ndr_success(tctx, ndr_ret,
5950 "ndr_push_fsctl_dup_extents_to_file");
5952 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5953 torture_assert_ntstatus_equal(tctx, status, NT_STATUS_INVALID_HANDLE,
5954 "FSCTL_DUP_EXTENTS_TO_FILE");
5956 ok = check_pattern(tctx, tree, tmp_ctx, src_h, 0, 32768, 0);
5958 torture_fail(tctx, "inconsistent file data");
5960 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
5962 torture_fail(tctx, "inconsistent file data");
5965 smb2_util_close(tree, src_h);
5966 smb2_util_close(tree, dest_h);
5967 talloc_free(tmp_ctx);
5971 static bool test_ioctl_dup_extents_src_lck(struct torture_context *tctx,
5972 struct smb2_tree *tree)
5974 struct smb2_handle src_h;
5975 struct smb2_handle src_h2;
5976 struct smb2_handle dest_h;
5978 union smb_ioctl ioctl;
5979 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5980 struct fsctl_dup_extents_to_file dup_ext_buf;
5981 enum ndr_err_code ndr_ret;
5983 struct smb2_lock lck;
5984 struct smb2_lock_element el[1];
5986 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5987 &src_h, 32768, /* fill 32768 byte src file */
5988 SEC_RIGHTS_FILE_ALL,
5990 SEC_RIGHTS_FILE_ALL,
5994 torture_fail(tctx, "setup dup extents error");
5997 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5998 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
5999 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
6001 smb2_util_close(tree, src_h);
6002 smb2_util_close(tree, dest_h);
6003 talloc_free(tmp_ctx);
6004 torture_skip(tctx, "block refcounting not supported\n");
6007 /* dest pattern is different to src */
6008 ok = write_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 32768);
6009 torture_assert(tctx, ok, "write pattern");
6011 /* setup dup ext req, values used for locking */
6012 dup_ext_buf.source_off = 0;
6013 dup_ext_buf.target_off = 0;
6014 dup_ext_buf.byte_count = 32768;
6016 /* open and lock the dup extents src file */
6017 status = torture_smb2_testfile(tree, FNAME, &src_h2);
6018 torture_assert_ntstatus_ok(tctx, status, "2nd src open");
6020 lck.in.lock_count = 0x0001;
6021 lck.in.lock_sequence = 0x00000000;
6022 lck.in.file.handle = src_h2;
6024 el[0].offset = dup_ext_buf.source_off;
6025 el[0].length = dup_ext_buf.byte_count;
6027 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
6029 status = smb2_lock(tree, &lck);
6030 torture_assert_ntstatus_ok(tctx, status, "lock");
6032 status = smb2_util_write(tree, src_h,
6033 "conflicted", 0, sizeof("conflicted"));
6034 torture_assert_ntstatus_equal(tctx, status,
6035 NT_STATUS_FILE_LOCK_CONFLICT, "file write");
6037 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6039 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6040 torture_assert_ndr_success(tctx, ndr_ret,
6041 "ndr_push_fsctl_dup_extents_to_file");
6044 * In contrast to copy-chunk, dup extents doesn't cause a lock conflict
6047 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6048 torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
6050 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
6052 torture_fail(tctx, "inconsistent file data");
6055 lck.in.lock_count = 0x0001;
6056 lck.in.lock_sequence = 0x00000001;
6057 lck.in.file.handle = src_h2;
6059 el[0].offset = dup_ext_buf.source_off;
6060 el[0].length = dup_ext_buf.byte_count;
6062 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
6063 status = smb2_lock(tree, &lck);
6064 torture_assert_ntstatus_ok(tctx, status, "unlock");
6066 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6067 torture_assert_ntstatus_ok(tctx, status,
6068 "FSCTL_DUP_EXTENTS_TO_FILE unlocked");
6070 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
6072 torture_fail(tctx, "inconsistent file data");
6075 smb2_util_close(tree, src_h2);
6076 smb2_util_close(tree, src_h);
6077 smb2_util_close(tree, dest_h);
6078 talloc_free(tmp_ctx);
6082 static bool test_ioctl_dup_extents_dest_lck(struct torture_context *tctx,
6083 struct smb2_tree *tree)
6085 struct smb2_handle src_h;
6086 struct smb2_handle dest_h;
6087 struct smb2_handle dest_h2;
6089 union smb_ioctl ioctl;
6090 TALLOC_CTX *tmp_ctx = talloc_new(tree);
6091 struct fsctl_dup_extents_to_file dup_ext_buf;
6092 enum ndr_err_code ndr_ret;
6094 struct smb2_lock lck;
6095 struct smb2_lock_element el[1];
6097 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
6098 &src_h, 32768, /* fill 32768 byte src file */
6099 SEC_RIGHTS_FILE_ALL,
6101 SEC_RIGHTS_FILE_ALL,
6105 torture_fail(tctx, "setup dup extents error");
6108 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
6109 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
6110 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
6112 smb2_util_close(tree, src_h);
6113 smb2_util_close(tree, dest_h);
6114 talloc_free(tmp_ctx);
6115 torture_skip(tctx, "block refcounting not supported\n");
6118 /* dest pattern is different to src */
6119 ok = write_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 32768);
6120 torture_assert(tctx, ok, "write pattern");
6122 /* setup dup ext req, values used for locking */
6123 dup_ext_buf.source_off = 0;
6124 dup_ext_buf.target_off = 0;
6125 dup_ext_buf.byte_count = 32768;
6127 /* open and lock the dup extents dest file */
6128 status = torture_smb2_testfile(tree, FNAME2, &dest_h2);
6129 torture_assert_ntstatus_ok(tctx, status, "2nd src open");
6131 lck.in.lock_count = 0x0001;
6132 lck.in.lock_sequence = 0x00000000;
6133 lck.in.file.handle = dest_h2;
6135 el[0].offset = dup_ext_buf.source_off;
6136 el[0].length = dup_ext_buf.byte_count;
6138 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
6140 status = smb2_lock(tree, &lck);
6141 torture_assert_ntstatus_ok(tctx, status, "lock");
6143 status = smb2_util_write(tree, dest_h,
6144 "conflicted", 0, sizeof("conflicted"));
6145 torture_assert_ntstatus_equal(tctx, status,
6146 NT_STATUS_FILE_LOCK_CONFLICT, "file write");
6148 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6150 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6151 torture_assert_ndr_success(tctx, ndr_ret,
6152 "ndr_push_fsctl_dup_extents_to_file");
6155 * In contrast to copy-chunk, dup extents doesn't cause a lock conflict
6158 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6159 torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
6161 lck.in.lock_count = 0x0001;
6162 lck.in.lock_sequence = 0x00000001;
6163 lck.in.file.handle = dest_h2;
6165 el[0].offset = dup_ext_buf.source_off;
6166 el[0].length = dup_ext_buf.byte_count;
6168 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
6169 status = smb2_lock(tree, &lck);
6170 torture_assert_ntstatus_ok(tctx, status, "unlock");
6172 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6173 torture_assert_ntstatus_ok(tctx, status,
6174 "FSCTL_DUP_EXTENTS_TO_FILE unlocked");
6176 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
6178 torture_fail(tctx, "inconsistent file data");
6181 smb2_util_close(tree, src_h);
6182 smb2_util_close(tree, dest_h);
6183 smb2_util_close(tree, dest_h2);
6184 talloc_free(tmp_ctx);
6189 * testing of SMB2 ioctls
6191 struct torture_suite *torture_smb2_ioctl_init(void)
6193 struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "ioctl");
6195 torture_suite_add_1smb2_test(suite, "shadow_copy",
6196 test_ioctl_get_shadow_copy);
6197 torture_suite_add_1smb2_test(suite, "req_resume_key",
6198 test_ioctl_req_resume_key);
6199 torture_suite_add_1smb2_test(suite, "copy_chunk_simple",
6200 test_ioctl_copy_chunk_simple);
6201 torture_suite_add_1smb2_test(suite, "copy_chunk_multi",
6202 test_ioctl_copy_chunk_multi);
6203 torture_suite_add_1smb2_test(suite, "copy_chunk_tiny",
6204 test_ioctl_copy_chunk_tiny);
6205 torture_suite_add_1smb2_test(suite, "copy_chunk_overwrite",
6206 test_ioctl_copy_chunk_over);
6207 torture_suite_add_1smb2_test(suite, "copy_chunk_append",
6208 test_ioctl_copy_chunk_append);
6209 torture_suite_add_1smb2_test(suite, "copy_chunk_limits",
6210 test_ioctl_copy_chunk_limits);
6211 torture_suite_add_1smb2_test(suite, "copy_chunk_src_lock",
6212 test_ioctl_copy_chunk_src_lck);
6213 torture_suite_add_1smb2_test(suite, "copy_chunk_dest_lock",
6214 test_ioctl_copy_chunk_dest_lck);
6215 torture_suite_add_1smb2_test(suite, "copy_chunk_bad_key",
6216 test_ioctl_copy_chunk_bad_key);
6217 torture_suite_add_1smb2_test(suite, "copy_chunk_src_is_dest",
6218 test_ioctl_copy_chunk_src_is_dest);
6219 torture_suite_add_1smb2_test(suite, "copy_chunk_src_is_dest_overlap",
6220 test_ioctl_copy_chunk_src_is_dest_overlap);
6221 torture_suite_add_1smb2_test(suite, "copy_chunk_bad_access",
6222 test_ioctl_copy_chunk_bad_access);
6223 torture_suite_add_1smb2_test(suite, "copy_chunk_write_access",
6224 test_ioctl_copy_chunk_write_access);
6225 torture_suite_add_1smb2_test(suite, "copy_chunk_src_exceed",
6226 test_ioctl_copy_chunk_src_exceed);
6227 torture_suite_add_1smb2_test(suite, "copy_chunk_src_exceed_multi",
6228 test_ioctl_copy_chunk_src_exceed_multi);
6229 torture_suite_add_1smb2_test(suite, "copy_chunk_sparse_dest",
6230 test_ioctl_copy_chunk_sparse_dest);
6231 torture_suite_add_1smb2_test(suite, "copy_chunk_max_output_sz",
6232 test_ioctl_copy_chunk_max_output_sz);
6233 torture_suite_add_1smb2_test(suite, "copy_chunk_zero_length",
6234 test_ioctl_copy_chunk_zero_length);
6235 torture_suite_add_1smb2_test(suite, "compress_file_flag",
6236 test_ioctl_compress_file_flag);
6237 torture_suite_add_1smb2_test(suite, "compress_dir_inherit",
6238 test_ioctl_compress_dir_inherit);
6239 torture_suite_add_1smb2_test(suite, "compress_invalid_format",
6240 test_ioctl_compress_invalid_format);
6241 torture_suite_add_1smb2_test(suite, "compress_invalid_buf",
6242 test_ioctl_compress_invalid_buf);
6243 torture_suite_add_1smb2_test(suite, "compress_query_file_attr",
6244 test_ioctl_compress_query_file_attr);
6245 torture_suite_add_1smb2_test(suite, "compress_create_with_attr",
6246 test_ioctl_compress_create_with_attr);
6247 torture_suite_add_1smb2_test(suite, "compress_inherit_disable",
6248 test_ioctl_compress_inherit_disable);
6249 torture_suite_add_1smb2_test(suite, "compress_set_file_attr",
6250 test_ioctl_compress_set_file_attr);
6251 torture_suite_add_1smb2_test(suite, "compress_perms",
6252 test_ioctl_compress_perms);
6253 torture_suite_add_1smb2_test(suite, "compress_notsup_get",
6254 test_ioctl_compress_notsup_get);
6255 torture_suite_add_1smb2_test(suite, "compress_notsup_set",
6256 test_ioctl_compress_notsup_set);
6257 torture_suite_add_1smb2_test(suite, "network_interface_info",
6258 test_ioctl_network_interface_info);
6259 torture_suite_add_1smb2_test(suite, "sparse_file_flag",
6260 test_ioctl_sparse_file_flag);
6261 torture_suite_add_1smb2_test(suite, "sparse_file_attr",
6262 test_ioctl_sparse_file_attr);
6263 torture_suite_add_1smb2_test(suite, "sparse_dir_flag",
6264 test_ioctl_sparse_dir_flag);
6265 torture_suite_add_1smb2_test(suite, "sparse_set_nobuf",
6266 test_ioctl_sparse_set_nobuf);
6267 torture_suite_add_1smb2_test(suite, "sparse_set_oversize",
6268 test_ioctl_sparse_set_oversize);
6269 torture_suite_add_1smb2_test(suite, "sparse_qar",
6270 test_ioctl_sparse_qar);
6271 torture_suite_add_1smb2_test(suite, "sparse_qar_malformed",
6272 test_ioctl_sparse_qar_malformed);
6273 torture_suite_add_1smb2_test(suite, "sparse_punch",
6274 test_ioctl_sparse_punch);
6275 torture_suite_add_1smb2_test(suite, "sparse_hole_dealloc",
6276 test_ioctl_sparse_hole_dealloc);
6277 torture_suite_add_1smb2_test(suite, "sparse_compressed",
6278 test_ioctl_sparse_compressed);
6279 torture_suite_add_1smb2_test(suite, "sparse_copy_chunk",
6280 test_ioctl_sparse_copy_chunk);
6281 torture_suite_add_1smb2_test(suite, "sparse_punch_invalid",
6282 test_ioctl_sparse_punch_invalid);
6283 torture_suite_add_1smb2_test(suite, "sparse_perms",
6284 test_ioctl_sparse_perms);
6285 torture_suite_add_1smb2_test(suite, "sparse_lock",
6286 test_ioctl_sparse_lck);
6287 torture_suite_add_1smb2_test(suite, "sparse_qar_ob1",
6288 test_ioctl_sparse_qar_ob1);
6289 torture_suite_add_1smb2_test(suite, "sparse_qar_multi",
6290 test_ioctl_sparse_qar_multi);
6291 torture_suite_add_1smb2_test(suite, "sparse_qar_overflow",
6292 test_ioctl_sparse_qar_overflow);
6293 torture_suite_add_1smb2_test(suite, "trim_simple",
6294 test_ioctl_trim_simple);
6295 torture_suite_add_1smb2_test(suite, "dup_extents_simple",
6296 test_ioctl_dup_extents_simple);
6297 torture_suite_add_1smb2_test(suite, "dup_extents_len_beyond_dest",
6298 test_ioctl_dup_extents_len_beyond_dest);
6299 torture_suite_add_1smb2_test(suite, "dup_extents_len_beyond_src",
6300 test_ioctl_dup_extents_len_beyond_src);
6301 torture_suite_add_1smb2_test(suite, "dup_extents_len_zero",
6302 test_ioctl_dup_extents_len_zero);
6303 torture_suite_add_1smb2_test(suite, "dup_extents_sparse_src",
6304 test_ioctl_dup_extents_sparse_src);
6305 torture_suite_add_1smb2_test(suite, "dup_extents_sparse_dest",
6306 test_ioctl_dup_extents_sparse_dest);
6307 torture_suite_add_1smb2_test(suite, "dup_extents_sparse_both",
6308 test_ioctl_dup_extents_sparse_both);
6309 torture_suite_add_1smb2_test(suite, "dup_extents_src_is_dest",
6310 test_ioctl_dup_extents_src_is_dest);
6311 torture_suite_add_1smb2_test(suite, "dup_extents_src_is_dest_overlap",
6312 test_ioctl_dup_extents_src_is_dest_overlap);
6313 torture_suite_add_1smb2_test(suite, "dup_extents_compressed_src",
6314 test_ioctl_dup_extents_compressed_src);
6315 torture_suite_add_1smb2_test(suite, "dup_extents_compressed_dest",
6316 test_ioctl_dup_extents_compressed_dest);
6317 torture_suite_add_1smb2_test(suite, "dup_extents_bad_handle",
6318 test_ioctl_dup_extents_bad_handle);
6319 torture_suite_add_1smb2_test(suite, "dup_extents_src_lock",
6320 test_ioctl_dup_extents_src_lck);
6321 torture_suite_add_1smb2_test(suite, "dup_extents_dest_lock",
6322 test_ioctl_dup_extents_dest_lck);
6324 suite->description = talloc_strdup(suite, "SMB2-IOCTL tests");