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
);
118 testing fetching a resume key twice for one file handle
120 static bool test_ioctl_req_two_resume_keys(struct torture_context
*torture
,
121 struct smb2_tree
*tree
)
123 struct smb2_handle h
;
126 union smb_ioctl ioctl
;
127 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
128 struct req_resume_key_rsp res_key
;
129 enum ndr_err_code ndr_ret
;
131 smb2_util_unlink(tree
, FNAME
);
133 status
= torture_smb2_testfile(tree
, FNAME
, &h
);
134 torture_assert_ntstatus_ok(torture
, status
, "create write");
137 status
= smb2_util_write(tree
, h
, buf
, 0, ARRAY_SIZE(buf
));
138 torture_assert_ntstatus_ok(torture
, status
, "write");
141 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
142 ioctl
.smb2
.in
.file
.handle
= h
;
143 ioctl
.smb2
.in
.function
= FSCTL_SRV_REQUEST_RESUME_KEY
;
144 ioctl
.smb2
.in
.max_response_size
= 32;
145 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
147 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
148 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_REQUEST_RESUME_KEY");
150 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
, &res_key
,
151 (ndr_pull_flags_fn_t
)ndr_pull_req_resume_key_rsp
);
152 torture_assert_ndr_success(torture
, ndr_ret
,
153 "ndr_pull_req_resume_key_rsp");
155 ndr_print_debug((ndr_print_fn_t
)ndr_print_req_resume_key_rsp
, "yo", &res_key
);
158 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
159 ioctl
.smb2
.in
.file
.handle
= h
;
160 ioctl
.smb2
.in
.function
= FSCTL_SRV_REQUEST_RESUME_KEY
;
161 ioctl
.smb2
.in
.max_response_size
= 32;
162 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
164 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
165 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_REQUEST_RESUME_KEY");
167 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
, &res_key
,
168 (ndr_pull_flags_fn_t
)ndr_pull_req_resume_key_rsp
);
169 torture_assert_ndr_success(torture
, ndr_ret
,
170 "ndr_pull_req_resume_key_rsp");
172 ndr_print_debug((ndr_print_fn_t
)ndr_print_req_resume_key_rsp
, "yo", &res_key
);
174 talloc_free(tmp_ctx
);
178 static uint64_t patt_hash(uint64_t off
)
183 static bool write_pattern(struct torture_context
*torture
,
184 struct smb2_tree
*tree
, TALLOC_CTX
*mem_ctx
,
185 struct smb2_handle h
, uint64_t off
, uint64_t len
,
191 uint64_t io_sz
= MIN(1024 * 64, len
);
197 torture_assert(torture
, (len
% 8) == 0, "invalid write len");
199 buf
= talloc_zero_size(mem_ctx
, io_sz
);
200 torture_assert(torture
, (buf
!= NULL
), "no memory for file data buf");
203 for (i
= 0; i
<= io_sz
- 8; i
+= 8) {
204 SBVAL(buf
, i
, patt_hash(patt_off
));
208 status
= smb2_util_write(tree
, h
,
210 torture_assert_ntstatus_ok(torture
, status
, "file write");
221 static bool check_pattern(struct torture_context
*torture
,
222 struct smb2_tree
*tree
, TALLOC_CTX
*mem_ctx
,
223 struct smb2_handle h
, uint64_t off
, uint64_t len
,
230 torture_assert(torture
, (len
% 8) == 0, "invalid read len");
236 uint64_t io_sz
= MIN(1024 * 64, len
);
239 r
.in
.file
.handle
= h
;
242 status
= smb2_read(tree
, mem_ctx
, &r
);
243 torture_assert_ntstatus_ok(torture
, status
, "read");
245 torture_assert_u64_equal(torture
, r
.out
.data
.length
, io_sz
,
246 "read data len mismatch");
248 for (i
= 0; i
<= io_sz
- 8; i
+= 8, patt_off
+= 8) {
249 uint64_t data
= BVAL(r
.out
.data
.data
, i
);
250 torture_assert_u64_equal(torture
, data
, patt_hash(patt_off
),
251 talloc_asprintf(torture
, "read data "
252 "pattern bad at %llu\n",
253 (unsigned long long)off
+ i
));
255 talloc_free(r
.out
.data
.data
);
263 static bool check_zero(struct torture_context
*torture
,
264 struct smb2_tree
*tree
, TALLOC_CTX
*mem_ctx
,
265 struct smb2_handle h
, uint64_t off
, uint64_t len
)
276 r
.in
.file
.handle
= h
;
279 status
= smb2_read(tree
, mem_ctx
, &r
);
280 torture_assert_ntstatus_ok(torture
, status
, "read");
282 torture_assert_u64_equal(torture
, r
.out
.data
.length
, len
,
283 "read data len mismatch");
285 for (i
= 0; i
<= len
- 8; i
+= 8) {
286 uint64_t data
= BVAL(r
.out
.data
.data
, i
);
287 torture_assert_u64_equal(torture
, data
, 0,
288 talloc_asprintf(mem_ctx
, "read zero "
290 (unsigned long long)i
));
293 talloc_free(r
.out
.data
.data
);
297 static bool test_setup_open(struct torture_context
*torture
,
298 struct smb2_tree
*tree
, TALLOC_CTX
*mem_ctx
,
300 struct smb2_handle
*fh
,
301 uint32_t desired_access
,
302 uint32_t file_attributes
)
304 struct smb2_create io
;
308 io
.in
.desired_access
= desired_access
;
309 io
.in
.file_attributes
= file_attributes
;
310 io
.in
.create_disposition
= NTCREATEX_DISP_OPEN_IF
;
312 NTCREATEX_SHARE_ACCESS_DELETE
|
313 NTCREATEX_SHARE_ACCESS_READ
|
314 NTCREATEX_SHARE_ACCESS_WRITE
;
315 if (file_attributes
& FILE_ATTRIBUTE_DIRECTORY
) {
316 io
.in
.create_options
= NTCREATEX_OPTIONS_DIRECTORY
;
320 status
= smb2_create(tree
, mem_ctx
, &io
);
321 torture_assert_ntstatus_ok(torture
, status
, "file create");
323 *fh
= io
.out
.file
.handle
;
328 static bool test_setup_create_fill(struct torture_context
*torture
,
329 struct smb2_tree
*tree
, TALLOC_CTX
*mem_ctx
,
331 struct smb2_handle
*fh
,
333 uint32_t desired_access
,
334 uint32_t file_attributes
)
337 uint32_t initial_access
= desired_access
;
340 initial_access
|= SEC_FILE_APPEND_DATA
;
343 smb2_util_unlink(tree
, fname
);
345 ok
= test_setup_open(torture
, tree
, mem_ctx
,
350 torture_assert(torture
, ok
, "file create");
353 ok
= write_pattern(torture
, tree
, mem_ctx
, *fh
, 0, size
, 0);
354 torture_assert(torture
, ok
, "write pattern");
357 if (initial_access
!= desired_access
) {
358 smb2_util_close(tree
, *fh
);
359 ok
= test_setup_open(torture
, tree
, mem_ctx
,
364 torture_assert(torture
, ok
, "file open");
370 static bool test_setup_copy_chunk(struct torture_context
*torture
,
371 struct smb2_tree
*src_tree
,
372 struct smb2_tree
*dst_tree
,
375 const char *src_name
,
376 struct smb2_handle
*src_h
,
378 uint32_t src_desired_access
,
379 const char *dst_name
,
380 struct smb2_handle
*dest_h
,
382 uint32_t dest_desired_access
,
383 struct srv_copychunk_copy
*cc_copy
,
384 union smb_ioctl
*ioctl
)
386 struct req_resume_key_rsp res_key
;
389 enum ndr_err_code ndr_ret
;
391 ok
= test_setup_create_fill(torture
, src_tree
, mem_ctx
, src_name
,
392 src_h
, src_size
, src_desired_access
,
393 FILE_ATTRIBUTE_NORMAL
);
394 torture_assert(torture
, ok
, "src file create fill");
396 ok
= test_setup_create_fill(torture
, dst_tree
, mem_ctx
, dst_name
,
397 dest_h
, dest_size
, dest_desired_access
,
398 FILE_ATTRIBUTE_NORMAL
);
399 torture_assert(torture
, ok
, "dest file create fill");
401 ZERO_STRUCTPN(ioctl
);
402 ioctl
->smb2
.level
= RAW_IOCTL_SMB2
;
403 ioctl
->smb2
.in
.file
.handle
= *src_h
;
404 ioctl
->smb2
.in
.function
= FSCTL_SRV_REQUEST_RESUME_KEY
;
405 /* Allow for Key + ContextLength + Context */
406 ioctl
->smb2
.in
.max_response_size
= 32;
407 ioctl
->smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
409 status
= smb2_ioctl(src_tree
, mem_ctx
, &ioctl
->smb2
);
410 torture_assert_ntstatus_ok(torture
, status
,
411 "FSCTL_SRV_REQUEST_RESUME_KEY");
413 ndr_ret
= ndr_pull_struct_blob(&ioctl
->smb2
.out
.out
, mem_ctx
, &res_key
,
414 (ndr_pull_flags_fn_t
)ndr_pull_req_resume_key_rsp
);
416 torture_assert_ndr_success(torture
, ndr_ret
,
417 "ndr_pull_req_resume_key_rsp");
419 ZERO_STRUCTPN(ioctl
);
420 ioctl
->smb2
.level
= RAW_IOCTL_SMB2
;
421 ioctl
->smb2
.in
.file
.handle
= *dest_h
;
422 ioctl
->smb2
.in
.function
= FSCTL_SRV_COPYCHUNK
;
423 ioctl
->smb2
.in
.max_response_size
= sizeof(struct srv_copychunk_rsp
);
424 ioctl
->smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
426 ZERO_STRUCTPN(cc_copy
);
427 memcpy(cc_copy
->source_key
, res_key
.resume_key
, ARRAY_SIZE(cc_copy
->source_key
));
428 cc_copy
->chunk_count
= nchunks
;
429 cc_copy
->chunks
= talloc_zero_array(mem_ctx
, struct srv_copychunk
, nchunks
);
430 torture_assert(torture
, (cc_copy
->chunks
!= NULL
), "no memory for chunks");
436 static bool check_copy_chunk_rsp(struct torture_context
*torture
,
437 struct srv_copychunk_rsp
*cc_rsp
,
438 uint32_t ex_chunks_written
,
439 uint32_t ex_chunk_bytes_written
,
440 uint32_t ex_total_bytes_written
)
442 torture_assert_int_equal(torture
, cc_rsp
->chunks_written
,
443 ex_chunks_written
, "num chunks");
444 torture_assert_int_equal(torture
, cc_rsp
->chunk_bytes_written
,
445 ex_chunk_bytes_written
, "chunk bytes written");
446 torture_assert_int_equal(torture
, cc_rsp
->total_bytes_written
,
447 ex_total_bytes_written
, "chunk total bytes");
451 static bool test_ioctl_copy_chunk_simple(struct torture_context
*torture
,
452 struct smb2_tree
*tree
)
454 struct smb2_handle src_h
;
455 struct smb2_handle dest_h
;
457 union smb_ioctl ioctl
;
458 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
459 struct srv_copychunk_copy cc_copy
;
460 struct srv_copychunk_rsp cc_rsp
;
461 enum ndr_err_code ndr_ret
;
464 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
467 &src_h
, 4096, /* fill 4096 byte src file */
470 &dest_h
, 0, /* 0 byte dest file */
475 torture_fail(torture
, "setup copy chunk error");
478 /* copy all src file data (via a single chunk desc) */
479 cc_copy
.chunks
[0].source_off
= 0;
480 cc_copy
.chunks
[0].target_off
= 0;
481 cc_copy
.chunks
[0].length
= 4096;
483 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
485 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
486 torture_assert_ndr_success(torture
, ndr_ret
,
487 "ndr_push_srv_copychunk_copy");
489 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
490 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_COPYCHUNK");
492 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
494 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
495 torture_assert_ndr_success(torture
, ndr_ret
,
496 "ndr_pull_srv_copychunk_rsp");
498 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
499 1, /* chunks written */
500 0, /* chunk bytes unsuccessfully written */
501 4096); /* total bytes written */
503 torture_fail(torture
, "bad copy chunk response data");
506 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 0, 4096, 0);
508 torture_fail(torture
, "inconsistent file data");
511 smb2_util_close(tree
, src_h
);
512 smb2_util_close(tree
, dest_h
);
513 talloc_free(tmp_ctx
);
517 static bool test_ioctl_copy_chunk_multi(struct torture_context
*torture
,
518 struct smb2_tree
*tree
)
520 struct smb2_handle src_h
;
521 struct smb2_handle dest_h
;
523 union smb_ioctl ioctl
;
524 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
525 struct srv_copychunk_copy cc_copy
;
526 struct srv_copychunk_rsp cc_rsp
;
527 enum ndr_err_code ndr_ret
;
530 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
533 &src_h
, 8192, /* src file */
536 &dest_h
, 0, /* dest file */
541 torture_fail(torture
, "setup copy chunk error");
544 /* copy all src file data via two chunks */
545 cc_copy
.chunks
[0].source_off
= 0;
546 cc_copy
.chunks
[0].target_off
= 0;
547 cc_copy
.chunks
[0].length
= 4096;
549 cc_copy
.chunks
[1].source_off
= 4096;
550 cc_copy
.chunks
[1].target_off
= 4096;
551 cc_copy
.chunks
[1].length
= 4096;
553 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
555 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
556 torture_assert_ndr_success(torture
, ndr_ret
,
557 "ndr_push_srv_copychunk_copy");
559 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
560 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_COPYCHUNK");
562 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
564 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
565 torture_assert_ndr_success(torture
, ndr_ret
,
566 "ndr_pull_srv_copychunk_rsp");
568 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
569 2, /* chunks written */
570 0, /* chunk bytes unsuccessfully written */
571 8192); /* total bytes written */
573 torture_fail(torture
, "bad copy chunk response data");
576 smb2_util_close(tree
, src_h
);
577 smb2_util_close(tree
, dest_h
);
578 talloc_free(tmp_ctx
);
582 static bool test_ioctl_copy_chunk_tiny(struct torture_context
*torture
,
583 struct smb2_tree
*tree
)
585 struct smb2_handle src_h
;
586 struct smb2_handle dest_h
;
588 union smb_ioctl ioctl
;
589 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
590 struct srv_copychunk_copy cc_copy
;
591 struct srv_copychunk_rsp cc_rsp
;
592 enum ndr_err_code ndr_ret
;
595 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
598 &src_h
, 96, /* src file */
601 &dest_h
, 0, /* dest file */
606 torture_fail(torture
, "setup copy chunk error");
609 /* copy all src file data via two chunks, sub block size chunks */
610 cc_copy
.chunks
[0].source_off
= 0;
611 cc_copy
.chunks
[0].target_off
= 0;
612 cc_copy
.chunks
[0].length
= 48;
614 cc_copy
.chunks
[1].source_off
= 48;
615 cc_copy
.chunks
[1].target_off
= 48;
616 cc_copy
.chunks
[1].length
= 48;
618 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
620 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
621 torture_assert_ndr_success(torture
, ndr_ret
,
622 "ndr_push_srv_copychunk_copy");
624 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
625 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_COPYCHUNK");
627 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
629 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
630 torture_assert_ndr_success(torture
, ndr_ret
,
631 "ndr_pull_srv_copychunk_rsp");
633 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
634 2, /* chunks written */
635 0, /* chunk bytes unsuccessfully written */
636 96); /* total bytes written */
638 torture_fail(torture
, "bad copy chunk response data");
641 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 0, 96, 0);
643 torture_fail(torture
, "inconsistent file data");
646 smb2_util_close(tree
, src_h
);
647 smb2_util_close(tree
, dest_h
);
648 talloc_free(tmp_ctx
);
652 static bool test_ioctl_copy_chunk_over(struct torture_context
*torture
,
653 struct smb2_tree
*tree
)
655 struct smb2_handle src_h
;
656 struct smb2_handle dest_h
;
658 union smb_ioctl ioctl
;
659 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
660 struct srv_copychunk_copy cc_copy
;
661 struct srv_copychunk_rsp cc_rsp
;
662 enum ndr_err_code ndr_ret
;
665 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
668 &src_h
, 8192, /* src file */
671 &dest_h
, 4096, /* dest file */
676 torture_fail(torture
, "setup copy chunk error");
679 /* first chunk overwrites existing dest data */
680 cc_copy
.chunks
[0].source_off
= 0;
681 cc_copy
.chunks
[0].target_off
= 0;
682 cc_copy
.chunks
[0].length
= 4096;
684 /* second chunk overwrites the first */
685 cc_copy
.chunks
[1].source_off
= 4096;
686 cc_copy
.chunks
[1].target_off
= 0;
687 cc_copy
.chunks
[1].length
= 4096;
689 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
691 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
692 torture_assert_ndr_success(torture
, ndr_ret
,
693 "ndr_push_srv_copychunk_copy");
695 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
696 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_COPYCHUNK");
698 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
700 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
701 torture_assert_ndr_success(torture
, ndr_ret
,
702 "ndr_pull_srv_copychunk_rsp");
704 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
705 2, /* chunks written */
706 0, /* chunk bytes unsuccessfully written */
707 8192); /* total bytes written */
709 torture_fail(torture
, "bad copy chunk response data");
712 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 0, 4096, 4096);
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_append(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
, tree
, tmp_ctx
,
739 &src_h
, 4096, /* src file */
742 &dest_h
, 0, /* dest file */
747 torture_fail(torture
, "setup copy chunk error");
750 cc_copy
.chunks
[0].source_off
= 0;
751 cc_copy
.chunks
[0].target_off
= 0;
752 cc_copy
.chunks
[0].length
= 4096;
754 /* second chunk appends the same data to the first */
755 cc_copy
.chunks
[1].source_off
= 0;
756 cc_copy
.chunks
[1].target_off
= 4096;
757 cc_copy
.chunks
[1].length
= 4096;
759 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
761 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
762 torture_assert_ndr_success(torture
, ndr_ret
,
763 "ndr_push_srv_copychunk_copy");
765 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
766 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_COPYCHUNK");
768 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
770 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
771 torture_assert_ndr_success(torture
, ndr_ret
,
772 "ndr_pull_srv_copychunk_rsp");
774 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
775 2, /* chunks written */
776 0, /* chunk bytes unsuccessfully written */
777 8192); /* total bytes written */
779 torture_fail(torture
, "bad copy chunk response data");
782 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 0, 4096, 0);
784 torture_fail(torture
, "inconsistent file data");
787 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 4096, 4096, 0);
789 torture_fail(torture
, "inconsistent file data");
792 smb2_util_close(tree
, src_h
);
793 smb2_util_close(tree
, dest_h
);
794 talloc_free(tmp_ctx
);
798 static bool test_ioctl_copy_chunk_limits(struct torture_context
*torture
,
799 struct smb2_tree
*tree
)
801 struct smb2_handle src_h
;
802 struct smb2_handle dest_h
;
804 union smb_ioctl ioctl
;
805 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
806 struct srv_copychunk_copy cc_copy
;
807 struct srv_copychunk_rsp cc_rsp
;
808 enum ndr_err_code ndr_ret
;
811 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
814 &src_h
, 4096, /* src file */
817 &dest_h
, 0, /* dest file */
822 torture_fail(torture
, "setup copy chunk error");
825 /* send huge chunk length request */
826 cc_copy
.chunks
[0].source_off
= 0;
827 cc_copy
.chunks
[0].target_off
= 0;
828 cc_copy
.chunks
[0].length
= UINT_MAX
;
830 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
832 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
833 torture_assert_ndr_success(torture
, ndr_ret
, "marshalling request");
835 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
836 torture_assert_ntstatus_equal(torture
, status
,
837 NT_STATUS_INVALID_PARAMETER
,
838 "bad oversize chunk response");
840 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
842 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
843 torture_assert_ndr_success(torture
, ndr_ret
, "unmarshalling response");
845 torture_comment(torture
, "limit max chunks, got %u\n",
846 cc_rsp
.chunks_written
);
847 torture_comment(torture
, "limit max chunk len, got %u\n",
848 cc_rsp
.chunk_bytes_written
);
849 torture_comment(torture
, "limit max total bytes, got %u\n",
850 cc_rsp
.total_bytes_written
);
852 smb2_util_close(tree
, src_h
);
853 smb2_util_close(tree
, dest_h
);
854 talloc_free(tmp_ctx
);
858 static bool test_ioctl_copy_chunk_src_lck(struct torture_context
*torture
,
859 struct smb2_tree
*tree
)
861 struct smb2_handle src_h
;
862 struct smb2_handle src_h2
;
863 struct smb2_handle dest_h
;
865 union smb_ioctl ioctl
;
866 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
867 struct srv_copychunk_copy cc_copy
;
868 struct srv_copychunk_rsp cc_rsp
;
869 enum ndr_err_code ndr_ret
;
871 struct smb2_lock lck
;
872 struct smb2_lock_element el
[1];
874 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
877 &src_h
, 4096, /* src file */
880 &dest_h
, 0, /* dest file */
885 torture_fail(torture
, "setup copy chunk error");
888 cc_copy
.chunks
[0].source_off
= 0;
889 cc_copy
.chunks
[0].target_off
= 0;
890 cc_copy
.chunks
[0].length
= 4096;
892 /* open and lock the copychunk src file */
893 status
= torture_smb2_testfile(tree
, FNAME
, &src_h2
);
894 torture_assert_ntstatus_ok(torture
, status
, "2nd src open");
896 lck
.in
.lock_count
= 0x0001;
897 lck
.in
.lock_sequence
= 0x00000000;
898 lck
.in
.file
.handle
= src_h2
;
900 el
[0].offset
= cc_copy
.chunks
[0].source_off
;
901 el
[0].length
= cc_copy
.chunks
[0].length
;
903 el
[0].flags
= SMB2_LOCK_FLAG_EXCLUSIVE
;
905 status
= smb2_lock(tree
, &lck
);
906 torture_assert_ntstatus_ok(torture
, status
, "lock");
908 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
910 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
911 torture_assert_ndr_success(torture
, ndr_ret
,
912 "ndr_push_srv_copychunk_copy");
914 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
916 * 2k12 & Samba return lock_conflict, Windows 7 & 2k8 return success...
918 * Edgar Olougouna @ MS wrote:
919 * Regarding the FSCTL_SRV_COPYCHUNK and STATUS_FILE_LOCK_CONFLICT
920 * discrepancy observed between Windows versions, we confirm that the
921 * behavior change is expected.
923 * CopyChunk in Windows Server 2012 use regular Readfile/Writefile APIs
924 * to move the chunks from the source to the destination.
925 * These ReadFile/WriteFile APIs go through the byte-range lock checks,
926 * and this explains the observed STATUS_FILE_LOCK_CONFLICT error.
928 * Prior to Windows Server 2012, CopyChunk used mapped sections to move
929 * the data. And byte range locks are not enforced on mapped I/O, and
930 * this explains the STATUS_SUCCESS observed on Windows Server 2008 R2.
932 torture_assert_ntstatus_equal(torture
, status
,
933 NT_STATUS_FILE_LOCK_CONFLICT
,
934 "FSCTL_SRV_COPYCHUNK locked");
936 /* should get cc response data with the lock conflict status */
937 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
939 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
940 torture_assert_ndr_success(torture
, ndr_ret
,
941 "ndr_pull_srv_copychunk_rsp");
942 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
943 0, /* chunks written */
944 0, /* chunk bytes unsuccessfully written */
945 0); /* total bytes written */
947 lck
.in
.lock_count
= 0x0001;
948 lck
.in
.lock_sequence
= 0x00000001;
949 lck
.in
.file
.handle
= src_h2
;
951 el
[0].offset
= cc_copy
.chunks
[0].source_off
;
952 el
[0].length
= cc_copy
.chunks
[0].length
;
954 el
[0].flags
= SMB2_LOCK_FLAG_UNLOCK
;
955 status
= smb2_lock(tree
, &lck
);
956 torture_assert_ntstatus_ok(torture
, status
, "unlock");
958 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
959 torture_assert_ntstatus_ok(torture
, status
,
960 "FSCTL_SRV_COPYCHUNK unlocked");
962 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
964 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
965 torture_assert_ndr_success(torture
, ndr_ret
,
966 "ndr_pull_srv_copychunk_rsp");
968 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
969 1, /* chunks written */
970 0, /* chunk bytes unsuccessfully written */
971 4096); /* total bytes written */
973 torture_fail(torture
, "bad copy chunk response data");
976 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 0, 4096, 0);
978 torture_fail(torture
, "inconsistent file data");
981 smb2_util_close(tree
, src_h2
);
982 smb2_util_close(tree
, src_h
);
983 smb2_util_close(tree
, dest_h
);
984 talloc_free(tmp_ctx
);
988 static bool test_ioctl_copy_chunk_dest_lck(struct torture_context
*torture
,
989 struct smb2_tree
*tree
)
991 struct smb2_handle src_h
;
992 struct smb2_handle dest_h
;
993 struct smb2_handle dest_h2
;
995 union smb_ioctl ioctl
;
996 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
997 struct srv_copychunk_copy cc_copy
;
998 struct srv_copychunk_rsp cc_rsp
;
999 enum ndr_err_code ndr_ret
;
1001 struct smb2_lock lck
;
1002 struct smb2_lock_element el
[1];
1004 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
1007 &src_h
, 4096, /* src file */
1008 SEC_RIGHTS_FILE_ALL
,
1010 &dest_h
, 4096, /* dest file */
1011 SEC_RIGHTS_FILE_ALL
,
1015 torture_fail(torture
, "setup copy chunk error");
1018 cc_copy
.chunks
[0].source_off
= 0;
1019 cc_copy
.chunks
[0].target_off
= 0;
1020 cc_copy
.chunks
[0].length
= 4096;
1022 /* open and lock the copychunk dest file */
1023 status
= torture_smb2_testfile(tree
, FNAME2
, &dest_h2
);
1024 torture_assert_ntstatus_ok(torture
, status
, "2nd src open");
1026 lck
.in
.lock_count
= 0x0001;
1027 lck
.in
.lock_sequence
= 0x00000000;
1028 lck
.in
.file
.handle
= dest_h2
;
1030 el
[0].offset
= cc_copy
.chunks
[0].target_off
;
1031 el
[0].length
= cc_copy
.chunks
[0].length
;
1033 el
[0].flags
= SMB2_LOCK_FLAG_EXCLUSIVE
;
1035 status
= smb2_lock(tree
, &lck
);
1036 torture_assert_ntstatus_ok(torture
, status
, "lock");
1038 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1040 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1041 torture_assert_ndr_success(torture
, ndr_ret
,
1042 "ndr_push_srv_copychunk_copy");
1044 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1045 torture_assert_ntstatus_equal(torture
, status
,
1046 NT_STATUS_FILE_LOCK_CONFLICT
,
1047 "FSCTL_SRV_COPYCHUNK locked");
1049 lck
.in
.lock_count
= 0x0001;
1050 lck
.in
.lock_sequence
= 0x00000001;
1051 lck
.in
.file
.handle
= dest_h2
;
1053 el
[0].offset
= cc_copy
.chunks
[0].target_off
;
1054 el
[0].length
= cc_copy
.chunks
[0].length
;
1056 el
[0].flags
= SMB2_LOCK_FLAG_UNLOCK
;
1057 status
= smb2_lock(tree
, &lck
);
1058 torture_assert_ntstatus_ok(torture
, status
, "unlock");
1060 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1061 torture_assert_ntstatus_ok(torture
, status
,
1062 "FSCTL_SRV_COPYCHUNK unlocked");
1064 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
1066 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
1067 torture_assert_ndr_success(torture
, ndr_ret
,
1068 "ndr_pull_srv_copychunk_rsp");
1070 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
1071 1, /* chunks written */
1072 0, /* chunk bytes unsuccessfully written */
1073 4096); /* total bytes written */
1075 torture_fail(torture
, "bad copy chunk response data");
1078 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 0, 4096, 0);
1080 torture_fail(torture
, "inconsistent file data");
1083 smb2_util_close(tree
, dest_h2
);
1084 smb2_util_close(tree
, src_h
);
1085 smb2_util_close(tree
, dest_h
);
1086 talloc_free(tmp_ctx
);
1090 static bool test_ioctl_copy_chunk_bad_key(struct torture_context
*torture
,
1091 struct smb2_tree
*tree
)
1093 struct smb2_handle src_h
;
1094 struct smb2_handle dest_h
;
1096 union smb_ioctl ioctl
;
1097 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
1098 struct srv_copychunk_copy cc_copy
;
1099 enum ndr_err_code ndr_ret
;
1102 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
1106 SEC_RIGHTS_FILE_ALL
,
1109 SEC_RIGHTS_FILE_ALL
,
1113 torture_fail(torture
, "setup copy chunk error");
1116 /* overwrite the resume key with a bogus value */
1117 memcpy(cc_copy
.source_key
, "deadbeefdeadbeefdeadbeef", 24);
1119 cc_copy
.chunks
[0].source_off
= 0;
1120 cc_copy
.chunks
[0].target_off
= 0;
1121 cc_copy
.chunks
[0].length
= 4096;
1123 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1125 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1126 torture_assert_ndr_success(torture
, ndr_ret
,
1127 "ndr_push_srv_copychunk_copy");
1129 /* Server 2k12 returns NT_STATUS_OBJECT_NAME_NOT_FOUND */
1130 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1131 torture_assert_ntstatus_equal(torture
, status
,
1132 NT_STATUS_OBJECT_NAME_NOT_FOUND
,
1133 "FSCTL_SRV_COPYCHUNK");
1135 smb2_util_close(tree
, src_h
);
1136 smb2_util_close(tree
, dest_h
);
1137 talloc_free(tmp_ctx
);
1141 static bool test_ioctl_copy_chunk_src_is_dest(struct torture_context
*torture
,
1142 struct smb2_tree
*tree
)
1144 struct smb2_handle src_h
;
1145 struct smb2_handle dest_h
;
1147 union smb_ioctl ioctl
;
1148 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
1149 struct srv_copychunk_copy cc_copy
;
1150 struct srv_copychunk_rsp cc_rsp
;
1151 enum ndr_err_code ndr_ret
;
1154 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
1158 SEC_RIGHTS_FILE_ALL
,
1161 SEC_RIGHTS_FILE_ALL
,
1165 torture_fail(torture
, "setup copy chunk error");
1168 /* the source is also the destination */
1169 ioctl
.smb2
.in
.file
.handle
= src_h
;
1171 /* non-overlapping */
1172 cc_copy
.chunks
[0].source_off
= 0;
1173 cc_copy
.chunks
[0].target_off
= 4096;
1174 cc_copy
.chunks
[0].length
= 4096;
1176 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1178 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1179 torture_assert_ndr_success(torture
, ndr_ret
,
1180 "ndr_push_srv_copychunk_copy");
1182 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1183 torture_assert_ntstatus_ok(torture
, status
,
1184 "FSCTL_SRV_COPYCHUNK");
1186 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
1188 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
1189 torture_assert_ndr_success(torture
, ndr_ret
,
1190 "ndr_pull_srv_copychunk_rsp");
1192 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
1193 1, /* chunks written */
1194 0, /* chunk bytes unsuccessfully written */
1195 4096); /* total bytes written */
1197 torture_fail(torture
, "bad copy chunk response data");
1200 ok
= check_pattern(torture
, tree
, tmp_ctx
, src_h
, 0, 4096, 0);
1202 torture_fail(torture
, "inconsistent file data");
1204 ok
= check_pattern(torture
, tree
, tmp_ctx
, src_h
, 4096, 4096, 0);
1206 torture_fail(torture
, "inconsistent file data");
1209 smb2_util_close(tree
, src_h
);
1210 smb2_util_close(tree
, dest_h
);
1211 talloc_free(tmp_ctx
);
1216 * Test a single-chunk copychunk request, where the source and target ranges
1217 * overlap, and the SourceKey refers to the same target file. E.g:
1221 * File: src_and_dest
1222 * Offset: 0123456789
1227 * FSCTL_SRV_COPYCHUNK(src_and_dest)
1228 * SourceKey = SRV_REQUEST_RESUME_KEY(src_and_dest)
1230 * Chunks[0].SourceOffset = 0
1231 * Chunks[0].TargetOffset = 4
1232 * Chunks[0].Length = 6
1236 * File: src_and_dest
1237 * Offset: 0123456789
1240 * The resultant contents of src_and_dest is dependent on the server's
1241 * copy algorithm. In the above example, the server uses an IO buffer
1242 * large enough to hold the entire six-byte source data before writing
1243 * to TargetOffset. If the server were to use a four-byte IO buffer and
1244 * started reads/writes from the lowest offset, then the two overlapping
1245 * bytes in the above example would be overwritten before being read. The
1246 * resultant file contents would be abcdabcdab.
1248 * Windows 2008r2 appears to use a 2048 byte copy buffer, overlapping bytes
1249 * after this offset are written before being read. Windows 2012 on the
1250 * other hand appears to use a buffer large enough to hold its maximum
1251 * supported chunk size (1M). Samba currently uses a 64k copy buffer by
1252 * default (vfs_cc_state.buf).
1254 * This test uses an 8-byte overlap at 2040-2048, so that it passes against
1255 * Windows 2008r2, 2012 and Samba servers. Note, 2008GM fails, as it appears
1256 * to use a different copy algorithm to 2008r2.
1259 test_ioctl_copy_chunk_src_is_dest_overlap(struct torture_context
*torture
,
1260 struct smb2_tree
*tree
)
1262 struct smb2_handle src_h
;
1263 struct smb2_handle dest_h
;
1265 union smb_ioctl ioctl
;
1266 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
1267 struct srv_copychunk_copy cc_copy
;
1268 struct srv_copychunk_rsp cc_rsp
;
1269 enum ndr_err_code ndr_ret
;
1272 /* exceed the vfs_default copy buffer */
1273 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
1277 SEC_RIGHTS_FILE_ALL
,
1280 SEC_RIGHTS_FILE_ALL
,
1284 torture_fail(torture
, "setup copy chunk error");
1287 /* the source is also the destination */
1288 ioctl
.smb2
.in
.file
.handle
= src_h
;
1290 /* 8 bytes overlap between source and target ranges */
1291 cc_copy
.chunks
[0].source_off
= 0;
1292 cc_copy
.chunks
[0].target_off
= 2048 - 8;
1293 cc_copy
.chunks
[0].length
= 2048;
1295 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1297 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1298 torture_assert_ndr_success(torture
, ndr_ret
,
1299 "ndr_push_srv_copychunk_copy");
1301 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1302 torture_assert_ntstatus_ok(torture
, status
,
1303 "FSCTL_SRV_COPYCHUNK");
1305 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
1307 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
1308 torture_assert_ndr_success(torture
, ndr_ret
,
1309 "ndr_pull_srv_copychunk_rsp");
1311 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
1312 1, /* chunks written */
1313 0, /* chunk bytes unsuccessfully written */
1314 2048); /* total bytes written */
1316 torture_fail(torture
, "bad copy chunk response data");
1319 ok
= check_pattern(torture
, tree
, tmp_ctx
, src_h
, 0, 2048 - 8, 0);
1321 torture_fail(torture
, "inconsistent file data");
1323 ok
= check_pattern(torture
, tree
, tmp_ctx
, src_h
, 2048 - 8, 2048, 0);
1325 torture_fail(torture
, "inconsistent file data");
1328 smb2_util_close(tree
, src_h
);
1329 smb2_util_close(tree
, dest_h
);
1330 talloc_free(tmp_ctx
);
1334 static bool test_ioctl_copy_chunk_bad_access(struct torture_context
*torture
,
1335 struct smb2_tree
*tree
)
1337 struct smb2_handle src_h
;
1338 struct smb2_handle dest_h
;
1340 union smb_ioctl ioctl
;
1341 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
1342 struct srv_copychunk_copy cc_copy
;
1343 enum ndr_err_code ndr_ret
;
1345 /* read permission on src */
1346 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
, 1, /* 1 chunk */
1347 FNAME
, &src_h
, 4096, /* fill 4096 byte src file */
1348 SEC_FILE_READ_DATA
| SEC_FILE_READ_ATTRIBUTE
,
1349 FNAME2
, &dest_h
, 0, /* 0 byte dest file */
1350 SEC_RIGHTS_FILE_ALL
, &cc_copy
, &ioctl
);
1352 torture_fail(torture
, "setup copy chunk error");
1355 cc_copy
.chunks
[0].source_off
= 0;
1356 cc_copy
.chunks
[0].target_off
= 0;
1357 cc_copy
.chunks
[0].length
= 4096;
1359 ndr_ret
= ndr_push_struct_blob(
1360 &ioctl
.smb2
.in
.out
, tmp_ctx
, &cc_copy
,
1361 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1362 torture_assert_ndr_success(torture
, ndr_ret
,
1363 "ndr_push_srv_copychunk_copy");
1365 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1366 torture_assert_ntstatus_equal(torture
, status
, NT_STATUS_OK
,
1367 "FSCTL_SRV_COPYCHUNK");
1369 smb2_util_close(tree
, src_h
);
1370 smb2_util_close(tree
, dest_h
);
1372 /* execute permission on src */
1373 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
, 1, /* 1 chunk */
1374 FNAME
, &src_h
, 4096, /* fill 4096 byte src file */
1375 SEC_FILE_EXECUTE
| SEC_FILE_READ_ATTRIBUTE
,
1376 FNAME2
, &dest_h
, 0, /* 0 byte dest file */
1377 SEC_RIGHTS_FILE_ALL
, &cc_copy
, &ioctl
);
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(
1387 &ioctl
.smb2
.in
.out
, tmp_ctx
, &cc_copy
,
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");
1392 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1393 torture_assert_ntstatus_equal(torture
, status
, NT_STATUS_OK
,
1394 "FSCTL_SRV_COPYCHUNK");
1396 smb2_util_close(tree
, src_h
);
1397 smb2_util_close(tree
, dest_h
);
1399 /* neither read nor execute permission on src */
1400 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
, 1, /* 1 chunk */
1401 FNAME
, &src_h
, 4096, /* fill 4096 byte src file */
1402 SEC_FILE_READ_ATTRIBUTE
, FNAME2
, &dest_h
,
1403 0, /* 0 byte dest file */
1404 SEC_RIGHTS_FILE_ALL
, &cc_copy
, &ioctl
);
1406 torture_fail(torture
, "setup copy chunk error");
1409 cc_copy
.chunks
[0].source_off
= 0;
1410 cc_copy
.chunks
[0].target_off
= 0;
1411 cc_copy
.chunks
[0].length
= 4096;
1413 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1415 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1416 torture_assert_ndr_success(torture
, ndr_ret
,
1417 "ndr_push_srv_copychunk_copy");
1419 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1420 torture_assert_ntstatus_equal(torture
, status
,
1421 NT_STATUS_ACCESS_DENIED
,
1422 "FSCTL_SRV_COPYCHUNK");
1424 smb2_util_close(tree
, src_h
);
1425 smb2_util_close(tree
, dest_h
);
1427 /* no write permission on dest */
1428 ok
= test_setup_copy_chunk(
1429 torture
, tree
, tree
, tmp_ctx
, 1, /* 1 chunk */
1430 FNAME
, &src_h
, 4096, /* fill 4096 byte src file */
1431 SEC_FILE_READ_DATA
| SEC_FILE_READ_ATTRIBUTE
, FNAME2
, &dest_h
,
1432 0, /* 0 byte dest file */
1433 (SEC_RIGHTS_FILE_ALL
&
1434 ~(SEC_FILE_WRITE_DATA
| SEC_FILE_APPEND_DATA
)),
1437 torture_fail(torture
, "setup copy chunk error");
1440 cc_copy
.chunks
[0].source_off
= 0;
1441 cc_copy
.chunks
[0].target_off
= 0;
1442 cc_copy
.chunks
[0].length
= 4096;
1444 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1446 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1447 torture_assert_ndr_success(torture
, ndr_ret
,
1448 "ndr_push_srv_copychunk_copy");
1450 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1451 torture_assert_ntstatus_equal(torture
, status
,
1452 NT_STATUS_ACCESS_DENIED
,
1453 "FSCTL_SRV_COPYCHUNK");
1455 smb2_util_close(tree
, src_h
);
1456 smb2_util_close(tree
, dest_h
);
1458 /* no read permission on dest */
1459 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
, 1, /* 1 chunk */
1460 FNAME
, &src_h
, 4096, /* fill 4096 byte src file */
1461 SEC_FILE_READ_DATA
| SEC_FILE_READ_ATTRIBUTE
,
1462 FNAME2
, &dest_h
, 0, /* 0 byte dest file */
1463 (SEC_RIGHTS_FILE_ALL
& ~SEC_FILE_READ_DATA
),
1466 torture_fail(torture
, "setup copy chunk error");
1469 cc_copy
.chunks
[0].source_off
= 0;
1470 cc_copy
.chunks
[0].target_off
= 0;
1471 cc_copy
.chunks
[0].length
= 4096;
1473 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1475 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1476 torture_assert_ndr_success(torture
, ndr_ret
,
1477 "ndr_push_srv_copychunk_copy");
1480 * FSCTL_SRV_COPYCHUNK requires read permission on dest,
1481 * FSCTL_SRV_COPYCHUNK_WRITE on the other hand does not.
1483 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1484 torture_assert_ntstatus_equal(torture
, status
,
1485 NT_STATUS_ACCESS_DENIED
,
1486 "FSCTL_SRV_COPYCHUNK");
1488 smb2_util_close(tree
, src_h
);
1489 smb2_util_close(tree
, dest_h
);
1490 talloc_free(tmp_ctx
);
1495 static bool test_ioctl_copy_chunk_write_access(struct torture_context
*torture
,
1496 struct smb2_tree
*tree
)
1498 struct smb2_handle src_h
;
1499 struct smb2_handle dest_h
;
1501 union smb_ioctl ioctl
;
1502 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
1503 struct srv_copychunk_copy cc_copy
;
1504 enum ndr_err_code ndr_ret
;
1507 /* no read permission on dest with FSCTL_SRV_COPYCHUNK_WRITE */
1508 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
1511 &src_h
, 4096, /* fill 4096 byte src file */
1512 SEC_RIGHTS_FILE_ALL
,
1514 &dest_h
, 0, /* 0 byte dest file */
1515 (SEC_RIGHTS_FILE_WRITE
1516 | SEC_RIGHTS_FILE_EXECUTE
),
1520 torture_fail(torture
, "setup copy chunk error");
1523 ioctl
.smb2
.in
.function
= FSCTL_SRV_COPYCHUNK_WRITE
;
1524 cc_copy
.chunks
[0].source_off
= 0;
1525 cc_copy
.chunks
[0].target_off
= 0;
1526 cc_copy
.chunks
[0].length
= 4096;
1528 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1530 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1531 torture_assert_ndr_success(torture
, ndr_ret
,
1532 "ndr_push_srv_copychunk_copy");
1534 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1535 torture_assert_ntstatus_ok(torture
, status
,
1536 "FSCTL_SRV_COPYCHUNK_WRITE");
1538 smb2_util_close(tree
, src_h
);
1539 smb2_util_close(tree
, dest_h
);
1540 talloc_free(tmp_ctx
);
1545 static bool test_ioctl_copy_chunk_src_exceed(struct torture_context
*torture
,
1546 struct smb2_tree
*tree
)
1548 struct smb2_handle src_h
;
1549 struct smb2_handle dest_h
;
1551 union smb_ioctl ioctl
;
1552 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
1553 struct srv_copychunk_copy cc_copy
;
1554 struct srv_copychunk_rsp cc_rsp
;
1555 enum ndr_err_code ndr_ret
;
1558 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
1561 &src_h
, 4096, /* fill 4096 byte src file */
1562 SEC_RIGHTS_FILE_ALL
,
1564 &dest_h
, 0, /* 0 byte dest file */
1565 SEC_RIGHTS_FILE_ALL
,
1569 torture_fail(torture
, "setup copy chunk error");
1572 /* Request copy where off + length exceeds size of src */
1573 cc_copy
.chunks
[0].source_off
= 1024;
1574 cc_copy
.chunks
[0].target_off
= 0;
1575 cc_copy
.chunks
[0].length
= 4096;
1577 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1579 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1580 torture_assert_ndr_success(torture
, ndr_ret
,
1581 "ndr_push_srv_copychunk_copy");
1583 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1584 torture_assert_ntstatus_equal(torture
, status
,
1585 NT_STATUS_INVALID_VIEW_SIZE
,
1586 "FSCTL_SRV_COPYCHUNK oversize");
1588 /* Request copy where length exceeds size of src */
1589 cc_copy
.chunks
[0].source_off
= 1024;
1590 cc_copy
.chunks
[0].target_off
= 0;
1591 cc_copy
.chunks
[0].length
= 3072;
1593 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1595 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1596 torture_assert_ndr_success(torture
, ndr_ret
,
1597 "ndr_push_srv_copychunk_copy");
1599 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1600 torture_assert_ntstatus_ok(torture
, status
,
1601 "FSCTL_SRV_COPYCHUNK just right");
1603 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
1605 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
1606 torture_assert_ndr_success(torture
, ndr_ret
,
1607 "ndr_pull_srv_copychunk_rsp");
1609 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
1610 1, /* chunks written */
1611 0, /* chunk bytes unsuccessfully written */
1612 3072); /* total bytes written */
1614 torture_fail(torture
, "bad copy chunk response data");
1617 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 0, 3072, 1024);
1619 torture_fail(torture
, "inconsistent file data");
1622 smb2_util_close(tree
, src_h
);
1623 smb2_util_close(tree
, dest_h
);
1624 talloc_free(tmp_ctx
);
1629 test_ioctl_copy_chunk_src_exceed_multi(struct torture_context
*torture
,
1630 struct smb2_tree
*tree
)
1632 struct smb2_handle src_h
;
1633 struct smb2_handle dest_h
;
1635 union smb_ioctl ioctl
;
1636 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
1637 struct srv_copychunk_copy cc_copy
;
1638 struct srv_copychunk_rsp cc_rsp
;
1639 enum ndr_err_code ndr_ret
;
1642 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
1645 &src_h
, 8192, /* fill 8192 byte src file */
1646 SEC_RIGHTS_FILE_ALL
,
1648 &dest_h
, 0, /* 0 byte dest file */
1649 SEC_RIGHTS_FILE_ALL
,
1653 torture_fail(torture
, "setup copy chunk error");
1656 /* Request copy where off + length exceeds size of src */
1657 cc_copy
.chunks
[0].source_off
= 0;
1658 cc_copy
.chunks
[0].target_off
= 0;
1659 cc_copy
.chunks
[0].length
= 4096;
1661 cc_copy
.chunks
[1].source_off
= 4096;
1662 cc_copy
.chunks
[1].target_off
= 4096;
1663 cc_copy
.chunks
[1].length
= 8192;
1665 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1667 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1668 torture_assert_ndr_success(torture
, ndr_ret
,
1669 "ndr_push_srv_copychunk_copy");
1671 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1672 torture_assert_ntstatus_equal(torture
, status
,
1673 NT_STATUS_INVALID_VIEW_SIZE
,
1674 "FSCTL_SRV_COPYCHUNK oversize");
1675 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
1677 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
1678 torture_assert_ndr_success(torture
, ndr_ret
, "unmarshalling response");
1680 /* first chunk should still be written */
1681 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
1682 1, /* chunks written */
1683 0, /* chunk bytes unsuccessfully written */
1684 4096); /* total bytes written */
1686 torture_fail(torture
, "bad copy chunk response data");
1688 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 0, 4096, 0);
1690 torture_fail(torture
, "inconsistent file data");
1693 smb2_util_close(tree
, src_h
);
1694 smb2_util_close(tree
, dest_h
);
1695 talloc_free(tmp_ctx
);
1699 static bool test_ioctl_copy_chunk_sparse_dest(struct torture_context
*torture
,
1700 struct smb2_tree
*tree
)
1702 struct smb2_handle src_h
;
1703 struct smb2_handle dest_h
;
1705 union smb_ioctl ioctl
;
1707 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
1708 struct srv_copychunk_copy cc_copy
;
1709 struct srv_copychunk_rsp cc_rsp
;
1710 enum ndr_err_code ndr_ret
;
1714 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
1717 &src_h
, 4096, /* fill 4096 byte src file */
1718 SEC_RIGHTS_FILE_ALL
,
1720 &dest_h
, 0, /* 0 byte dest file */
1721 SEC_RIGHTS_FILE_ALL
,
1725 torture_fail(torture
, "setup copy chunk error");
1728 /* copy all src file data (via a single chunk desc) */
1729 cc_copy
.chunks
[0].source_off
= 0;
1730 cc_copy
.chunks
[0].target_off
= 4096;
1731 cc_copy
.chunks
[0].length
= 4096;
1733 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1735 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1736 torture_assert_ndr_success(torture
, ndr_ret
,
1737 "ndr_push_srv_copychunk_copy");
1739 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1740 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_COPYCHUNK");
1742 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
1744 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
1745 torture_assert_ndr_success(torture
, ndr_ret
,
1746 "ndr_pull_srv_copychunk_rsp");
1748 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
1749 1, /* chunks written */
1750 0, /* chunk bytes unsuccessfully written */
1751 4096); /* total bytes written */
1753 torture_fail(torture
, "bad copy chunk response data");
1756 /* check for zeros in first 4k */
1758 r
.in
.file
.handle
= dest_h
;
1761 status
= smb2_read(tree
, tmp_ctx
, &r
);
1762 torture_assert_ntstatus_ok(torture
, status
, "read");
1764 torture_assert_u64_equal(torture
, r
.out
.data
.length
, 4096,
1765 "read data len mismatch");
1767 for (i
= 0; i
< 4096; i
++) {
1768 torture_assert(torture
, (r
.out
.data
.data
[i
] == 0),
1769 "sparse did not pass class");
1772 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 4096, 4096, 0);
1774 torture_fail(torture
, "inconsistent file data");
1777 smb2_util_close(tree
, src_h
);
1778 smb2_util_close(tree
, dest_h
);
1779 talloc_free(tmp_ctx
);
1784 * set the ioctl MaxOutputResponse size to less than
1785 * sizeof(struct srv_copychunk_rsp)
1787 static bool test_ioctl_copy_chunk_max_output_sz(struct torture_context
*torture
,
1788 struct smb2_tree
*tree
)
1790 struct smb2_handle src_h
;
1791 struct smb2_handle dest_h
;
1793 union smb_ioctl ioctl
;
1794 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
1795 struct srv_copychunk_copy cc_copy
;
1796 enum ndr_err_code ndr_ret
;
1799 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
1802 &src_h
, 4096, /* fill 4096 byte src file */
1803 SEC_RIGHTS_FILE_ALL
,
1805 &dest_h
, 0, /* 0 byte dest file */
1806 SEC_RIGHTS_FILE_ALL
,
1810 torture_fail(torture
, "setup copy chunk error");
1813 cc_copy
.chunks
[0].source_off
= 0;
1814 cc_copy
.chunks
[0].target_off
= 0;
1815 cc_copy
.chunks
[0].length
= 4096;
1816 /* req is valid, but use undersize max_response_size */
1817 ioctl
.smb2
.in
.max_response_size
= sizeof(struct srv_copychunk_rsp
) - 1;
1819 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1821 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1822 torture_assert_ndr_success(torture
, ndr_ret
,
1823 "ndr_push_srv_copychunk_copy");
1825 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1826 torture_assert_ntstatus_equal(torture
, status
,
1827 NT_STATUS_INVALID_PARAMETER
,
1828 "FSCTL_SRV_COPYCHUNK");
1830 smb2_util_close(tree
, src_h
);
1831 smb2_util_close(tree
, dest_h
);
1832 talloc_free(tmp_ctx
);
1836 static bool test_ioctl_copy_chunk_zero_length(struct torture_context
*torture
,
1837 struct smb2_tree
*tree
)
1839 struct smb2_handle src_h
;
1840 struct smb2_handle dest_h
;
1842 union smb_ioctl ioctl
;
1843 union smb_fileinfo q
;
1844 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
1845 struct srv_copychunk_copy cc_copy
;
1846 struct srv_copychunk_rsp cc_rsp
;
1847 enum ndr_err_code ndr_ret
;
1850 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
1853 &src_h
, 4096, /* fill 4096 byte src file */
1854 SEC_RIGHTS_FILE_ALL
,
1856 &dest_h
, 0, /* 0 byte dest file */
1857 SEC_RIGHTS_FILE_ALL
,
1861 torture_fail(torture
, "setup copy chunk error");
1864 /* zero length server-side copy (via a single chunk desc) */
1865 cc_copy
.chunks
[0].source_off
= 0;
1866 cc_copy
.chunks
[0].target_off
= 0;
1867 cc_copy
.chunks
[0].length
= 0;
1869 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1871 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1872 torture_assert_ndr_success(torture
, ndr_ret
,
1873 "ndr_push_srv_copychunk_copy");
1875 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1876 torture_assert_ntstatus_equal(torture
, status
,
1877 NT_STATUS_INVALID_PARAMETER
,
1878 "bad zero-length chunk response");
1880 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
1882 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
1883 torture_assert_ndr_success(torture
, ndr_ret
, "unmarshalling response");
1886 q
.all_info2
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
1887 q
.all_info2
.in
.file
.handle
= dest_h
;
1888 status
= smb2_getinfo_file(tree
, torture
, &q
);
1889 torture_assert_ntstatus_ok(torture
, status
, "getinfo");
1891 torture_assert_int_equal(torture
, q
.all_info2
.out
.size
, 0,
1892 "size after zero len clone");
1894 smb2_util_close(tree
, src_h
);
1895 smb2_util_close(tree
, dest_h
);
1896 talloc_free(tmp_ctx
);
1900 static bool copy_one_stream(struct torture_context
*torture
,
1901 struct smb2_tree
*tree
,
1902 TALLOC_CTX
*tmp_ctx
,
1903 const char *src_sname
,
1904 const char *dst_sname
)
1906 struct smb2_handle src_h
= {{0}};
1907 struct smb2_handle dest_h
= {{0}};
1910 struct srv_copychunk_copy cc_copy
;
1911 struct srv_copychunk_rsp cc_rsp
;
1912 enum ndr_err_code ndr_ret
;
1915 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
1918 &src_h
, 256, /* fill 256 byte src file */
1919 SEC_FILE_READ_DATA
| SEC_FILE_WRITE_DATA
,
1921 &dest_h
, 0, /* 0 byte dest file */
1922 SEC_FILE_READ_DATA
| SEC_FILE_WRITE_DATA
,
1925 torture_assert_goto(torture
, ok
== true, ok
, done
,
1926 "setup copy chunk error\n");
1928 /* copy all src file data (via a single chunk desc) */
1929 cc_copy
.chunks
[0].source_off
= 0;
1930 cc_copy
.chunks
[0].target_off
= 0;
1931 cc_copy
.chunks
[0].length
= 256;
1933 ndr_ret
= ndr_push_struct_blob(
1934 &io
.smb2
.in
.out
, tmp_ctx
, &cc_copy
,
1935 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1937 torture_assert_ndr_success_goto(torture
, ndr_ret
, ok
, done
,
1938 "ndr_push_srv_copychunk_copy\n");
1940 status
= smb2_ioctl(tree
, tmp_ctx
, &io
.smb2
);
1941 torture_assert_ntstatus_ok_goto(torture
, status
, ok
, done
,
1942 "FSCTL_SRV_COPYCHUNK\n");
1944 ndr_ret
= ndr_pull_struct_blob(
1945 &io
.smb2
.out
.out
, tmp_ctx
, &cc_rsp
,
1946 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
1948 torture_assert_ndr_success_goto(torture
, ndr_ret
, ok
, done
,
1949 "ndr_pull_srv_copychunk_rsp\n");
1951 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
1952 1, /* chunks written */
1953 0, /* chunk bytes unsuccessfully written */
1954 256); /* total bytes written */
1955 torture_assert_goto(torture
, ok
== true, ok
, done
,
1956 "bad copy chunk response data\n");
1958 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 0, 256, 0);
1960 torture_fail(torture
, "inconsistent file data\n");
1964 if (!smb2_util_handle_empty(src_h
)) {
1965 smb2_util_close(tree
, src_h
);
1967 if (!smb2_util_handle_empty(dest_h
)) {
1968 smb2_util_close(tree
, dest_h
);
1977 static bool torture_setup_file(TALLOC_CTX
*mem_ctx
,
1978 struct smb2_tree
*tree
,
1981 struct smb2_create io
;
1984 smb2_util_unlink(tree
, name
);
1986 io
.in
.desired_access
= SEC_FLAG_MAXIMUM_ALLOWED
;
1987 io
.in
.file_attributes
= FILE_ATTRIBUTE_NORMAL
;
1988 io
.in
.create_disposition
= NTCREATEX_DISP_OVERWRITE_IF
;
1989 io
.in
.share_access
=
1990 NTCREATEX_SHARE_ACCESS_DELETE
|
1991 NTCREATEX_SHARE_ACCESS_READ
|
1992 NTCREATEX_SHARE_ACCESS_WRITE
;
1993 io
.in
.create_options
= 0;
1996 status
= smb2_create(tree
, mem_ctx
, &io
);
1997 if (!NT_STATUS_IS_OK(status
)) {
2001 status
= smb2_util_close(tree
, io
.out
.file
.handle
);
2002 if (!NT_STATUS_IS_OK(status
)) {
2009 static bool test_copy_chunk_streams(struct torture_context
*torture
,
2010 struct smb2_tree
*tree
)
2012 const char *src_name
= "src";
2013 const char *dst_name
= "dst";
2015 const char *src_sname
;
2016 const char *dst_sname
;
2018 { "src:foo", "dst:foo" }
2021 TALLOC_CTX
*tmp_ctx
= NULL
;
2024 tmp_ctx
= talloc_new(tree
);
2025 torture_assert_not_null_goto(torture
, tmp_ctx
, ok
, done
,
2026 "torture_setup_file\n");
2028 ok
= torture_setup_file(torture
, tree
, src_name
);
2029 torture_assert_goto(torture
, ok
== true, ok
, done
, "torture_setup_file\n");
2030 ok
= torture_setup_file(torture
, tree
, dst_name
);
2031 torture_assert_goto(torture
, ok
== true, ok
, done
, "torture_setup_file\n");
2033 for (i
= 0; i
< ARRAY_SIZE(names
); i
++) {
2034 ok
= copy_one_stream(torture
, tree
, tmp_ctx
,
2036 names
[i
].dst_sname
);
2037 torture_assert_goto(torture
, ok
== true, ok
, done
,
2038 "copy_one_stream failed\n");
2042 smb2_util_unlink(tree
, src_name
);
2043 smb2_util_unlink(tree
, dst_name
);
2044 talloc_free(tmp_ctx
);
2048 static bool test_copy_chunk_across_shares(struct torture_context
*tctx
,
2049 struct smb2_tree
*tree
)
2051 TALLOC_CTX
*mem_ctx
= NULL
;
2052 struct smb2_tree
*tree2
= NULL
;
2053 struct smb2_handle src_h
= {{0}};
2054 struct smb2_handle dest_h
= {{0}};
2055 union smb_ioctl ioctl
;
2056 struct srv_copychunk_copy cc_copy
;
2057 struct srv_copychunk_rsp cc_rsp
;
2058 enum ndr_err_code ndr_ret
;
2062 mem_ctx
= talloc_new(tctx
);
2063 torture_assert_not_null_goto(tctx
, mem_ctx
, ok
, done
,
2066 ok
= torture_smb2_tree_connect(tctx
, tree
->session
, tctx
, &tree2
);
2067 torture_assert_goto(tctx
, ok
== true, ok
, done
,
2068 "torture_smb2_tree_connect failed\n");
2070 ok
= test_setup_copy_chunk(tctx
, tree
, tree2
, mem_ctx
,
2073 &src_h
, 4096, /* fill 4096 byte src file */
2074 SEC_RIGHTS_FILE_ALL
,
2076 &dest_h
, 0, /* 0 byte dest file */
2077 SEC_RIGHTS_FILE_ALL
,
2080 torture_assert_goto(tctx
, ok
== true, ok
, done
,
2081 "test_setup_copy_chunk failed\n");
2083 cc_copy
.chunks
[0].source_off
= 0;
2084 cc_copy
.chunks
[0].target_off
= 0;
2085 cc_copy
.chunks
[0].length
= 4096;
2087 ndr_ret
= ndr_push_struct_blob(
2088 &ioctl
.smb2
.in
.out
, mem_ctx
, &cc_copy
,
2089 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
2090 torture_assert_ndr_success_goto(tctx
, ndr_ret
, ok
, done
,
2091 "ndr_push_srv_copychunk_copy\n");
2093 status
= smb2_ioctl(tree2
, mem_ctx
, &ioctl
.smb2
);
2094 torture_assert_ntstatus_ok_goto(tctx
, status
, ok
, done
,
2095 "FSCTL_SRV_COPYCHUNK\n");
2097 ndr_ret
= ndr_pull_struct_blob(
2098 &ioctl
.smb2
.out
.out
, mem_ctx
, &cc_rsp
,
2099 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
2101 torture_assert_ndr_success_goto(tctx
, ndr_ret
, ok
, done
,
2102 "ndr_pull_srv_copychunk_rsp\n");
2104 ok
= check_copy_chunk_rsp(tctx
, &cc_rsp
,
2105 1, /* chunks written */
2106 0, /* chunk bytes unsuccessfully written */
2107 4096); /* total bytes written */
2108 torture_assert_goto(tctx
, ok
== true, ok
, done
,
2109 "bad copy chunk response data\n");
2111 ok
= check_pattern(tctx
, tree2
, mem_ctx
, dest_h
, 0, 4096, 0);
2112 torture_assert_goto(tctx
, ok
== true, ok
, done
,
2113 "inconsistent file data\n");
2116 TALLOC_FREE(mem_ctx
);
2117 if (!smb2_util_handle_empty(src_h
)) {
2118 smb2_util_close(tree
, src_h
);
2120 if (!smb2_util_handle_empty(dest_h
)) {
2121 smb2_util_close(tree2
, dest_h
);
2123 smb2_util_unlink(tree
, FNAME
);
2124 smb2_util_unlink(tree2
, FNAME2
);
2125 if (tree2
!= NULL
) {
2131 /* Test closing the src handle */
2132 static bool test_copy_chunk_across_shares2(struct torture_context
*tctx
,
2133 struct smb2_tree
*tree
)
2135 TALLOC_CTX
*mem_ctx
= NULL
;
2136 struct smb2_tree
*tree2
= NULL
;
2137 struct smb2_handle src_h
= {{0}};
2138 struct smb2_handle dest_h
= {{0}};
2139 union smb_ioctl ioctl
;
2140 struct srv_copychunk_copy cc_copy
;
2141 enum ndr_err_code ndr_ret
;
2145 mem_ctx
= talloc_new(tctx
);
2146 torture_assert_not_null_goto(tctx
, mem_ctx
, ok
, done
,
2149 ok
= torture_smb2_tree_connect(tctx
, tree
->session
, tctx
, &tree2
);
2150 torture_assert_goto(tctx
, ok
== true, ok
, done
,
2151 "torture_smb2_tree_connect failed\n");
2153 ok
= test_setup_copy_chunk(tctx
, tree
, tree2
, mem_ctx
,
2156 &src_h
, 4096, /* fill 4096 byte src file */
2157 SEC_RIGHTS_FILE_ALL
,
2159 &dest_h
, 0, /* 0 byte dest file */
2160 SEC_RIGHTS_FILE_ALL
,
2163 torture_assert_goto(tctx
, ok
== true, ok
, done
,
2164 "test_setup_copy_chunk failed\n");
2166 status
= smb2_util_close(tree
, src_h
);
2167 torture_assert_ntstatus_ok_goto(tctx
, status
, ok
, done
,
2168 "smb2_util_close failed\n");
2171 cc_copy
.chunks
[0].source_off
= 0;
2172 cc_copy
.chunks
[0].target_off
= 0;
2173 cc_copy
.chunks
[0].length
= 4096;
2175 ndr_ret
= ndr_push_struct_blob(
2176 &ioctl
.smb2
.in
.out
, mem_ctx
, &cc_copy
,
2177 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
2178 torture_assert_ndr_success_goto(tctx
, ndr_ret
, ok
, done
,
2179 "ndr_push_srv_copychunk_copy\n");
2181 status
= smb2_ioctl(tree2
, mem_ctx
, &ioctl
.smb2
);
2182 torture_assert_ntstatus_equal_goto(tctx
, status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
,
2183 ok
, done
, "smb2_ioctl failed\n");
2186 TALLOC_FREE(mem_ctx
);
2187 if (!smb2_util_handle_empty(src_h
)) {
2188 smb2_util_close(tree
, src_h
);
2190 if (!smb2_util_handle_empty(dest_h
)) {
2191 smb2_util_close(tree2
, dest_h
);
2193 smb2_util_unlink(tree
, FNAME
);
2194 smb2_util_unlink(tree2
, FNAME2
);
2195 if (tree2
!= NULL
) {
2201 /* Test offset works */
2202 static bool test_copy_chunk_across_shares3(struct torture_context
*tctx
,
2203 struct smb2_tree
*tree
)
2205 TALLOC_CTX
*mem_ctx
= NULL
;
2206 struct smb2_tree
*tree2
= NULL
;
2207 struct smb2_handle src_h
= {{0}};
2208 struct smb2_handle dest_h
= {{0}};
2209 union smb_ioctl ioctl
;
2210 struct srv_copychunk_copy cc_copy
;
2211 struct srv_copychunk_rsp cc_rsp
;
2212 enum ndr_err_code ndr_ret
;
2216 mem_ctx
= talloc_new(tctx
);
2217 torture_assert_not_null_goto(tctx
, mem_ctx
, ok
, done
,
2220 ok
= torture_smb2_tree_connect(tctx
, tree
->session
, tctx
, &tree2
);
2221 torture_assert_goto(tctx
, ok
== true, ok
, done
,
2222 "torture_smb2_tree_connect failed\n");
2224 ok
= test_setup_copy_chunk(tctx
, tree
, tree2
, mem_ctx
,
2227 &src_h
, 4096, /* fill 4096 byte src file */
2228 SEC_RIGHTS_FILE_ALL
,
2230 &dest_h
, 0, /* 0 byte dest file */
2231 SEC_RIGHTS_FILE_ALL
,
2234 torture_assert_goto(tctx
, ok
== true, ok
, done
,
2235 "test_setup_copy_chunk failed\n");
2237 cc_copy
.chunks
[0].source_off
= 0;
2238 cc_copy
.chunks
[0].target_off
= 0;
2239 cc_copy
.chunks
[0].length
= 4096;
2241 /* second chunk appends the same data to the first */
2242 cc_copy
.chunks
[1].source_off
= 0;
2243 cc_copy
.chunks
[1].target_off
= 4096;
2244 cc_copy
.chunks
[1].length
= 4096;
2246 ndr_ret
= ndr_push_struct_blob(
2247 &ioctl
.smb2
.in
.out
, mem_ctx
, &cc_copy
,
2248 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
2249 torture_assert_ndr_success_goto(tctx
, ndr_ret
, ok
, done
,
2250 "ndr_push_srv_copychunk_copy\n");
2252 status
= smb2_ioctl(tree2
, mem_ctx
, &ioctl
.smb2
);
2253 torture_assert_ntstatus_ok_goto(tctx
, status
, ok
, done
, "smb2_ioctl failed\n");
2255 ndr_ret
= ndr_pull_struct_blob(
2256 &ioctl
.smb2
.out
.out
, mem_ctx
, &cc_rsp
,
2257 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
2259 torture_assert_ndr_success_goto(tctx
, ndr_ret
, ok
, done
,
2260 "ndr_pull_srv_copychunk_rsp\n");
2262 ok
= check_copy_chunk_rsp(tctx
, &cc_rsp
,
2263 2, /* chunks written */
2264 0, /* chunk bytes unsuccessfully written */
2265 8192); /* total bytes written */
2266 torture_assert_goto(tctx
, ok
== true, ok
, done
,
2267 "check_copy_chunk_rsp failed\n");
2269 ok
= check_pattern(tctx
, tree2
, mem_ctx
, dest_h
, 0, 4096, 0);
2270 torture_assert_goto(tctx
, ok
== true, ok
, done
,
2271 "check_pattern failed\n");
2273 ok
= check_pattern(tctx
, tree2
, mem_ctx
, dest_h
, 4096, 4096, 0);
2274 torture_assert_goto(tctx
, ok
== true, ok
, done
,
2275 "check_pattern failed\n");
2278 TALLOC_FREE(mem_ctx
);
2279 if (!smb2_util_handle_empty(src_h
)) {
2280 smb2_util_close(tree
, src_h
);
2282 if (!smb2_util_handle_empty(dest_h
)) {
2283 smb2_util_close(tree2
, dest_h
);
2285 smb2_util_unlink(tree
, FNAME
);
2286 smb2_util_unlink(tree2
, FNAME2
);
2287 if (tree2
!= NULL
) {
2293 static NTSTATUS
test_ioctl_compress_fs_supported(struct torture_context
*torture
,
2294 struct smb2_tree
*tree
,
2295 TALLOC_CTX
*mem_ctx
,
2296 struct smb2_handle
*fh
,
2297 bool *compress_support
)
2300 union smb_fsinfo info
;
2303 info
.generic
.level
= RAW_QFS_ATTRIBUTE_INFORMATION
;
2304 info
.generic
.handle
= *fh
;
2305 status
= smb2_getinfo_fs(tree
, tree
, &info
);
2306 if (!NT_STATUS_IS_OK(status
)) {
2310 if (info
.attribute_info
.out
.fs_attr
& FILE_FILE_COMPRESSION
) {
2311 *compress_support
= true;
2313 *compress_support
= false;
2315 return NT_STATUS_OK
;
2318 static NTSTATUS
test_ioctl_compress_get(struct torture_context
*torture
,
2319 TALLOC_CTX
*mem_ctx
,
2320 struct smb2_tree
*tree
,
2321 struct smb2_handle fh
,
2322 uint16_t *_compression_fmt
)
2324 union smb_ioctl ioctl
;
2325 struct compression_state cmpr_state
;
2326 enum ndr_err_code ndr_ret
;
2330 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
2331 ioctl
.smb2
.in
.file
.handle
= fh
;
2332 ioctl
.smb2
.in
.function
= FSCTL_GET_COMPRESSION
;
2333 ioctl
.smb2
.in
.max_response_size
= sizeof(struct compression_state
);
2334 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
2336 status
= smb2_ioctl(tree
, mem_ctx
, &ioctl
.smb2
);
2337 if (!NT_STATUS_IS_OK(status
)) {
2341 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, mem_ctx
,
2343 (ndr_pull_flags_fn_t
)ndr_pull_compression_state
);
2345 if (ndr_ret
!= NDR_ERR_SUCCESS
) {
2346 return NT_STATUS_INTERNAL_ERROR
;
2349 *_compression_fmt
= cmpr_state
.format
;
2350 return NT_STATUS_OK
;
2353 static NTSTATUS
test_ioctl_compress_set(struct torture_context
*torture
,
2354 TALLOC_CTX
*mem_ctx
,
2355 struct smb2_tree
*tree
,
2356 struct smb2_handle fh
,
2357 uint16_t compression_fmt
)
2359 union smb_ioctl ioctl
;
2360 struct compression_state cmpr_state
;
2361 enum ndr_err_code ndr_ret
;
2365 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
2366 ioctl
.smb2
.in
.file
.handle
= fh
;
2367 ioctl
.smb2
.in
.function
= FSCTL_SET_COMPRESSION
;
2368 ioctl
.smb2
.in
.max_response_size
= 0;
2369 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
2371 cmpr_state
.format
= compression_fmt
;
2372 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, mem_ctx
,
2374 (ndr_push_flags_fn_t
)ndr_push_compression_state
);
2375 if (ndr_ret
!= NDR_ERR_SUCCESS
) {
2376 return NT_STATUS_INTERNAL_ERROR
;
2379 status
= smb2_ioctl(tree
, mem_ctx
, &ioctl
.smb2
);
2383 static bool test_ioctl_compress_file_flag(struct torture_context
*torture
,
2384 struct smb2_tree
*tree
)
2386 struct smb2_handle fh
;
2388 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
2390 uint16_t compression_fmt
;
2392 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2393 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
2394 FILE_ATTRIBUTE_NORMAL
);
2395 torture_assert(torture
, ok
, "setup compression file");
2397 status
= test_ioctl_compress_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
2399 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
2401 smb2_util_close(tree
, fh
);
2402 torture_skip(torture
, "FS compression not supported\n");
2405 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, fh
,
2407 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
2409 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_NONE
),
2410 "initial compression state not NONE");
2412 status
= test_ioctl_compress_set(torture
, tmp_ctx
, tree
, fh
,
2413 COMPRESSION_FORMAT_DEFAULT
);
2414 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_COMPRESSION");
2416 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, fh
,
2418 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
2420 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_LZNT1
),
2421 "invalid compression state after set");
2423 smb2_util_close(tree
, fh
);
2424 talloc_free(tmp_ctx
);
2428 static bool test_ioctl_compress_dir_inherit(struct torture_context
*torture
,
2429 struct smb2_tree
*tree
)
2431 struct smb2_handle dirh
;
2432 struct smb2_handle fh
;
2434 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
2435 uint16_t compression_fmt
;
2437 char path_buf
[PATH_MAX
];
2439 smb2_deltree(tree
, DNAME
);
2440 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2441 DNAME
, &dirh
, 0, SEC_RIGHTS_FILE_ALL
,
2442 FILE_ATTRIBUTE_DIRECTORY
);
2443 torture_assert(torture
, ok
, "setup compression directory");
2445 status
= test_ioctl_compress_fs_supported(torture
, tree
, tmp_ctx
, &dirh
,
2447 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
2449 smb2_util_close(tree
, dirh
);
2450 smb2_deltree(tree
, DNAME
);
2451 torture_skip(torture
, "FS compression not supported\n");
2454 /* set compression on parent dir, then check for inheritance */
2455 status
= test_ioctl_compress_set(torture
, tmp_ctx
, tree
, dirh
,
2456 COMPRESSION_FORMAT_LZNT1
);
2457 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_COMPRESSION");
2459 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, dirh
,
2461 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
2463 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_LZNT1
),
2464 "invalid compression state after set");
2466 snprintf(path_buf
, PATH_MAX
, "%s\\%s", DNAME
, FNAME
);
2467 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2468 path_buf
, &fh
, 4096, SEC_RIGHTS_FILE_ALL
,
2469 FILE_ATTRIBUTE_NORMAL
);
2470 torture_assert(torture
, ok
, "setup compression file");
2472 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, fh
,
2474 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
2476 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_LZNT1
),
2477 "compression attr not inherited by new file");
2479 /* check compressed data is consistent */
2480 ok
= check_pattern(torture
, tree
, tmp_ctx
, fh
, 0, 4096, 0);
2482 /* disable dir compression attr, file should remain compressed */
2483 status
= test_ioctl_compress_set(torture
, tmp_ctx
, tree
, dirh
,
2484 COMPRESSION_FORMAT_NONE
);
2485 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_COMPRESSION");
2487 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, fh
,
2489 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
2491 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_LZNT1
),
2492 "file compression attr removed after dir change");
2493 smb2_util_close(tree
, fh
);
2495 /* new files should no longer inherit compression attr */
2496 snprintf(path_buf
, PATH_MAX
, "%s\\%s", DNAME
, FNAME2
);
2497 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2498 path_buf
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
2499 FILE_ATTRIBUTE_NORMAL
);
2500 torture_assert(torture
, ok
, "setup file");
2502 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, fh
,
2504 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
2506 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_NONE
),
2507 "compression attr present on new file");
2509 smb2_util_close(tree
, fh
);
2510 smb2_util_close(tree
, dirh
);
2511 smb2_deltree(tree
, DNAME
);
2512 talloc_free(tmp_ctx
);
2516 static bool test_ioctl_compress_invalid_format(struct torture_context
*torture
,
2517 struct smb2_tree
*tree
)
2519 struct smb2_handle fh
;
2521 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
2523 uint16_t compression_fmt
;
2525 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2526 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
2527 FILE_ATTRIBUTE_NORMAL
);
2528 torture_assert(torture
, ok
, "setup compression file");
2530 status
= test_ioctl_compress_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
2532 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
2534 smb2_util_close(tree
, fh
);
2535 torture_skip(torture
, "FS compression not supported\n");
2538 status
= test_ioctl_compress_set(torture
, tmp_ctx
, tree
, fh
,
2539 0x0042); /* bogus */
2540 torture_assert_ntstatus_equal(torture
, status
,
2541 NT_STATUS_INVALID_PARAMETER
,
2542 "invalid FSCTL_SET_COMPRESSION");
2544 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, fh
,
2546 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
2548 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_NONE
),
2549 "initial compression state not NONE");
2551 smb2_util_close(tree
, fh
);
2552 talloc_free(tmp_ctx
);
2556 static bool test_ioctl_compress_invalid_buf(struct torture_context
*torture
,
2557 struct smb2_tree
*tree
)
2559 struct smb2_handle fh
;
2561 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
2563 union smb_ioctl ioctl
;
2565 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2566 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
2567 FILE_ATTRIBUTE_NORMAL
);
2568 torture_assert(torture
, ok
, "setup compression file");
2570 status
= test_ioctl_compress_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
2572 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
2574 smb2_util_close(tree
, fh
);
2575 torture_skip(torture
, "FS compression not supported\n");
2579 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
2580 ioctl
.smb2
.in
.file
.handle
= fh
;
2581 ioctl
.smb2
.in
.function
= FSCTL_GET_COMPRESSION
;
2582 ioctl
.smb2
.in
.max_response_size
= 0; /* no room for rsp data */
2583 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
2585 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
2586 if (!NT_STATUS_EQUAL(status
, NT_STATUS_INVALID_USER_BUFFER
)
2587 && !NT_STATUS_EQUAL(status
, NT_STATUS_INVALID_PARAMETER
)) {
2588 /* neither Server 2k12 nor 2k8r2 response status */
2589 torture_assert(torture
, true,
2590 "invalid FSCTL_SET_COMPRESSION");
2593 smb2_util_close(tree
, fh
);
2594 talloc_free(tmp_ctx
);
2598 static bool test_ioctl_compress_query_file_attr(struct torture_context
*torture
,
2599 struct smb2_tree
*tree
)
2601 struct smb2_handle fh
;
2602 union smb_fileinfo io
;
2604 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
2607 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2608 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
2609 FILE_ATTRIBUTE_NORMAL
);
2610 torture_assert(torture
, ok
, "setup compression file");
2612 status
= test_ioctl_compress_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
2614 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
2616 smb2_util_close(tree
, fh
);
2617 torture_skip(torture
, "FS compression not supported\n");
2621 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
2622 io
.generic
.in
.file
.handle
= fh
;
2623 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
2624 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FILE");
2626 torture_assert(torture
,
2627 ((io
.all_info2
.out
.attrib
& FILE_ATTRIBUTE_COMPRESSED
) == 0),
2628 "compression attr before set");
2630 status
= test_ioctl_compress_set(torture
, tmp_ctx
, tree
, fh
,
2631 COMPRESSION_FORMAT_DEFAULT
);
2632 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_COMPRESSION");
2635 io
.generic
.level
= RAW_FILEINFO_BASIC_INFORMATION
;
2636 io
.generic
.in
.file
.handle
= fh
;
2637 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
2638 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FILE");
2640 torture_assert(torture
,
2641 (io
.basic_info
.out
.attrib
& FILE_ATTRIBUTE_COMPRESSED
),
2642 "no compression attr after set");
2644 smb2_util_close(tree
, fh
);
2645 talloc_free(tmp_ctx
);
2650 * Specify FILE_ATTRIBUTE_COMPRESSED on creation, Windows does not retain this
2653 static bool test_ioctl_compress_create_with_attr(struct torture_context
*torture
,
2654 struct smb2_tree
*tree
)
2656 struct smb2_handle fh2
;
2657 union smb_fileinfo io
;
2659 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
2660 uint16_t compression_fmt
;
2663 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2664 FNAME2
, &fh2
, 0, SEC_RIGHTS_FILE_ALL
,
2665 (FILE_ATTRIBUTE_NORMAL
| FILE_ATTRIBUTE_COMPRESSED
));
2666 torture_assert(torture
, ok
, "setup compression file");
2668 status
= test_ioctl_compress_fs_supported(torture
, tree
, tmp_ctx
, &fh2
,
2670 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
2672 smb2_util_close(tree
, fh2
);
2673 torture_skip(torture
, "FS compression not supported\n");
2676 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, fh2
,
2678 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
2680 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_NONE
),
2681 "initial compression state not NONE");
2684 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
2685 io
.generic
.in
.file
.handle
= fh2
;
2686 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
2687 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FILE");
2689 torture_assert(torture
,
2690 ((io
.all_info2
.out
.attrib
& FILE_ATTRIBUTE_COMPRESSED
) == 0),
2691 "incorrect compression attr");
2693 smb2_util_close(tree
, fh2
);
2694 talloc_free(tmp_ctx
);
2698 static bool test_ioctl_compress_inherit_disable(struct torture_context
*torture
,
2699 struct smb2_tree
*tree
)
2701 struct smb2_handle fh
;
2702 struct smb2_handle dirh
;
2703 char path_buf
[PATH_MAX
];
2705 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
2707 uint16_t compression_fmt
;
2709 struct smb2_create io
;
2711 smb2_deltree(tree
, DNAME
);
2712 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2713 DNAME
, &dirh
, 0, SEC_RIGHTS_FILE_ALL
,
2714 FILE_ATTRIBUTE_DIRECTORY
);
2715 torture_assert(torture
, ok
, "setup compression directory");
2717 status
= test_ioctl_compress_fs_supported(torture
, tree
, tmp_ctx
, &dirh
,
2719 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
2721 smb2_util_close(tree
, dirh
);
2722 smb2_deltree(tree
, DNAME
);
2723 torture_skip(torture
, "FS compression not supported\n");
2726 /* set compression on parent dir, then check for inheritance */
2727 status
= test_ioctl_compress_set(torture
, tmp_ctx
, tree
, dirh
,
2728 COMPRESSION_FORMAT_LZNT1
);
2729 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_COMPRESSION");
2731 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, dirh
,
2733 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
2735 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_LZNT1
),
2736 "invalid compression state after set");
2737 smb2_util_close(tree
, dirh
);
2739 snprintf(path_buf
, PATH_MAX
, "%s\\%s", DNAME
, FNAME
);
2740 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2741 path_buf
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
2742 FILE_ATTRIBUTE_NORMAL
);
2743 torture_assert(torture
, ok
, "setup compression file");
2745 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, fh
,
2747 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
2749 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_LZNT1
),
2750 "compression attr not inherited by new file");
2751 smb2_util_close(tree
, fh
);
2753 snprintf(path_buf
, PATH_MAX
, "%s\\%s", DNAME
, FNAME2
);
2755 /* NO_COMPRESSION option should block inheritance */
2757 io
.in
.desired_access
= SEC_RIGHTS_FILE_ALL
;
2758 io
.in
.file_attributes
= FILE_ATTRIBUTE_NORMAL
;
2759 io
.in
.create_disposition
= NTCREATEX_DISP_CREATE
;
2760 io
.in
.create_options
= NTCREATEX_OPTIONS_NO_COMPRESSION
;
2761 io
.in
.share_access
=
2762 NTCREATEX_SHARE_ACCESS_DELETE
|
2763 NTCREATEX_SHARE_ACCESS_READ
|
2764 NTCREATEX_SHARE_ACCESS_WRITE
;
2765 io
.in
.fname
= path_buf
;
2767 status
= smb2_create(tree
, tmp_ctx
, &io
);
2768 torture_assert_ntstatus_ok(torture
, status
, "file create");
2770 fh
= io
.out
.file
.handle
;
2772 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, fh
,
2774 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
2776 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_NONE
),
2777 "compression attr inherited by NO_COMPRESSION file");
2778 smb2_util_close(tree
, fh
);
2781 snprintf(path_buf
, PATH_MAX
, "%s\\%s", DNAME
, DNAME
);
2783 io
.in
.desired_access
= SEC_RIGHTS_FILE_ALL
;
2784 io
.in
.file_attributes
= FILE_ATTRIBUTE_DIRECTORY
;
2785 io
.in
.create_disposition
= NTCREATEX_DISP_CREATE
;
2786 io
.in
.create_options
= (NTCREATEX_OPTIONS_NO_COMPRESSION
2787 | NTCREATEX_OPTIONS_DIRECTORY
);
2788 io
.in
.share_access
=
2789 NTCREATEX_SHARE_ACCESS_DELETE
|
2790 NTCREATEX_SHARE_ACCESS_READ
|
2791 NTCREATEX_SHARE_ACCESS_WRITE
;
2792 io
.in
.fname
= path_buf
;
2794 status
= smb2_create(tree
, tmp_ctx
, &io
);
2795 torture_assert_ntstatus_ok(torture
, status
, "dir create");
2797 dirh
= io
.out
.file
.handle
;
2799 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, dirh
,
2801 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
2803 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_NONE
),
2804 "compression attr inherited by NO_COMPRESSION dir");
2805 smb2_util_close(tree
, dirh
);
2806 smb2_deltree(tree
, DNAME
);
2808 talloc_free(tmp_ctx
);
2812 /* attempting to set compression via SetInfo should not stick */
2813 static bool test_ioctl_compress_set_file_attr(struct torture_context
*torture
,
2814 struct smb2_tree
*tree
)
2816 struct smb2_handle fh
;
2817 struct smb2_handle dirh
;
2818 union smb_fileinfo io
;
2819 union smb_setfileinfo set_io
;
2820 uint16_t compression_fmt
;
2822 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
2825 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2826 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
2827 FILE_ATTRIBUTE_NORMAL
);
2828 torture_assert(torture
, ok
, "setup compression file");
2830 status
= test_ioctl_compress_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
2832 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
2834 smb2_util_close(tree
, fh
);
2835 torture_skip(torture
, "FS compression not supported\n");
2839 io
.generic
.level
= RAW_FILEINFO_BASIC_INFORMATION
;
2840 io
.generic
.in
.file
.handle
= fh
;
2841 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
2842 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FILE");
2844 torture_assert(torture
,
2845 ((io
.basic_info
.out
.attrib
& FILE_ATTRIBUTE_COMPRESSED
) == 0),
2846 "compression attr before set");
2848 ZERO_STRUCT(set_io
);
2849 set_io
.generic
.level
= RAW_FILEINFO_BASIC_INFORMATION
;
2850 set_io
.basic_info
.in
.file
.handle
= fh
;
2851 set_io
.basic_info
.in
.create_time
= io
.basic_info
.out
.create_time
;
2852 set_io
.basic_info
.in
.access_time
= io
.basic_info
.out
.access_time
;
2853 set_io
.basic_info
.in
.write_time
= io
.basic_info
.out
.write_time
;
2854 set_io
.basic_info
.in
.change_time
= io
.basic_info
.out
.change_time
;
2855 set_io
.basic_info
.in
.attrib
= (io
.basic_info
.out
.attrib
2856 | FILE_ATTRIBUTE_COMPRESSED
);
2857 status
= smb2_setinfo_file(tree
, &set_io
);
2858 torture_assert_ntstatus_ok(torture
, status
, "SMB2_SETINFO_FILE");
2861 io
.generic
.level
= RAW_FILEINFO_BASIC_INFORMATION
;
2862 io
.generic
.in
.file
.handle
= fh
;
2863 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
2864 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FILE");
2866 torture_assert(torture
,
2867 ((io
.basic_info
.out
.attrib
& FILE_ATTRIBUTE_COMPRESSED
) == 0),
2868 "compression attr after set");
2870 smb2_util_close(tree
, fh
);
2871 smb2_deltree(tree
, DNAME
);
2872 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2873 DNAME
, &dirh
, 0, SEC_RIGHTS_FILE_ALL
,
2874 FILE_ATTRIBUTE_DIRECTORY
);
2875 torture_assert(torture
, ok
, "setup compression directory");
2878 io
.generic
.level
= RAW_FILEINFO_BASIC_INFORMATION
;
2879 io
.generic
.in
.file
.handle
= dirh
;
2880 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
2881 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FILE");
2883 torture_assert(torture
,
2884 ((io
.basic_info
.out
.attrib
& FILE_ATTRIBUTE_COMPRESSED
) == 0),
2885 "compression attr before set");
2887 ZERO_STRUCT(set_io
);
2888 set_io
.generic
.level
= RAW_FILEINFO_BASIC_INFORMATION
;
2889 set_io
.basic_info
.in
.file
.handle
= dirh
;
2890 set_io
.basic_info
.in
.create_time
= io
.basic_info
.out
.create_time
;
2891 set_io
.basic_info
.in
.access_time
= io
.basic_info
.out
.access_time
;
2892 set_io
.basic_info
.in
.write_time
= io
.basic_info
.out
.write_time
;
2893 set_io
.basic_info
.in
.change_time
= io
.basic_info
.out
.change_time
;
2894 set_io
.basic_info
.in
.attrib
= (io
.basic_info
.out
.attrib
2895 | FILE_ATTRIBUTE_COMPRESSED
);
2896 status
= smb2_setinfo_file(tree
, &set_io
);
2897 torture_assert_ntstatus_ok(torture
, status
, "SMB2_SETINFO_FILE");
2899 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, dirh
,
2901 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
2903 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_NONE
),
2904 "dir compression set after SetInfo");
2906 smb2_util_close(tree
, dirh
);
2907 talloc_free(tmp_ctx
);
2911 static bool test_ioctl_compress_perms(struct torture_context
*torture
,
2912 struct smb2_tree
*tree
)
2914 struct smb2_handle fh
;
2915 uint16_t compression_fmt
;
2916 union smb_fileinfo io
;
2918 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
2921 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2922 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
2923 FILE_ATTRIBUTE_NORMAL
);
2924 torture_assert(torture
, ok
, "setup compression file");
2926 status
= test_ioctl_compress_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
2928 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
2929 smb2_util_close(tree
, fh
);
2931 torture_skip(torture
, "FS compression not supported\n");
2934 /* attempt get compression without READ_ATTR permission */
2935 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2937 (SEC_RIGHTS_FILE_READ
& ~(SEC_FILE_READ_ATTRIBUTE
2938 | SEC_STD_READ_CONTROL
2939 | SEC_FILE_READ_EA
)),
2940 FILE_ATTRIBUTE_NORMAL
);
2941 torture_assert(torture
, ok
, "setup compression file");
2943 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, fh
,
2945 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
2946 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_NONE
),
2947 "compression set after create");
2948 smb2_util_close(tree
, fh
);
2950 /* set compression without WRITE_ATTR permission should succeed */
2951 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2953 (SEC_RIGHTS_FILE_WRITE
& ~(SEC_FILE_WRITE_ATTRIBUTE
2955 | SEC_FILE_WRITE_EA
)),
2956 FILE_ATTRIBUTE_NORMAL
);
2957 torture_assert(torture
, ok
, "setup compression file");
2959 status
= test_ioctl_compress_set(torture
, tmp_ctx
, tree
, fh
,
2960 COMPRESSION_FORMAT_DEFAULT
);
2961 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_COMPRESSION");
2962 smb2_util_close(tree
, fh
);
2964 ok
= test_setup_open(torture
, tree
, tmp_ctx
,
2965 FNAME
, &fh
, SEC_RIGHTS_FILE_ALL
,
2966 FILE_ATTRIBUTE_NORMAL
);
2967 torture_assert(torture
, ok
, "setup compression file");
2969 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
2970 io
.generic
.in
.file
.handle
= fh
;
2971 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
2972 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FILE");
2974 torture_assert(torture
,
2975 (io
.all_info2
.out
.attrib
& FILE_ATTRIBUTE_COMPRESSED
),
2976 "incorrect compression attr");
2977 smb2_util_close(tree
, fh
);
2979 /* attempt get compression without READ_DATA permission */
2980 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2982 (SEC_RIGHTS_FILE_READ
& ~SEC_FILE_READ_DATA
),
2983 FILE_ATTRIBUTE_NORMAL
);
2984 torture_assert(torture
, ok
, "setup compression file");
2986 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, fh
,
2988 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
2989 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_NONE
),
2990 "compression enabled after set");
2991 smb2_util_close(tree
, fh
);
2993 /* attempt get compression with only SYNCHRONIZE permission */
2994 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2996 SEC_STD_SYNCHRONIZE
,
2997 FILE_ATTRIBUTE_NORMAL
);
2998 torture_assert(torture
, ok
, "setup compression file");
3000 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, fh
,
3002 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
3003 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_NONE
),
3004 "compression not enabled after set");
3005 smb2_util_close(tree
, fh
);
3007 /* attempt to set compression without WRITE_DATA permission */
3008 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
3010 (SEC_RIGHTS_FILE_WRITE
& (~SEC_FILE_WRITE_DATA
)),
3011 FILE_ATTRIBUTE_NORMAL
);
3012 torture_assert(torture
, ok
, "setup compression file");
3014 status
= test_ioctl_compress_set(torture
, tmp_ctx
, tree
, fh
,
3015 COMPRESSION_FORMAT_DEFAULT
);
3016 torture_assert_ntstatus_equal(torture
, status
,
3017 NT_STATUS_ACCESS_DENIED
,
3018 "FSCTL_SET_COMPRESSION permission");
3019 smb2_util_close(tree
, fh
);
3021 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
3023 (SEC_RIGHTS_FILE_WRITE
& (~SEC_FILE_WRITE_DATA
)),
3024 FILE_ATTRIBUTE_NORMAL
);
3025 torture_assert(torture
, ok
, "setup compression file");
3027 status
= test_ioctl_compress_set(torture
, tmp_ctx
, tree
, fh
,
3028 COMPRESSION_FORMAT_NONE
);
3029 torture_assert_ntstatus_equal(torture
, status
,
3030 NT_STATUS_ACCESS_DENIED
,
3031 "FSCTL_SET_COMPRESSION permission");
3032 smb2_util_close(tree
, fh
);
3034 talloc_free(tmp_ctx
);
3038 static bool test_ioctl_compress_notsup_get(struct torture_context
*torture
,
3039 struct smb2_tree
*tree
)
3041 struct smb2_handle fh
;
3043 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
3045 uint16_t compression_fmt
;
3047 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
3048 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
3049 FILE_ATTRIBUTE_NORMAL
);
3050 torture_assert(torture
, ok
, "setup compression file");
3052 /* skip if the server DOES support compression */
3053 status
= test_ioctl_compress_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
3055 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
3057 smb2_util_close(tree
, fh
);
3058 torture_skip(torture
, "FS compression supported\n");
3062 * Despite not supporting compression, we should get a successful
3063 * response indicating that the file is uncompressed - like WS2016.
3065 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, fh
,
3067 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
3069 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_NONE
),
3070 "initial compression state not NONE");
3072 smb2_util_close(tree
, fh
);
3073 talloc_free(tmp_ctx
);
3077 static bool test_ioctl_compress_notsup_set(struct torture_context
*torture
,
3078 struct smb2_tree
*tree
)
3080 struct smb2_handle fh
;
3082 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
3085 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
3086 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
3087 FILE_ATTRIBUTE_NORMAL
);
3088 torture_assert(torture
, ok
, "setup compression file");
3090 /* skip if the server DOES support compression */
3091 status
= test_ioctl_compress_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
3093 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
3095 smb2_util_close(tree
, fh
);
3096 torture_skip(torture
, "FS compression supported\n");
3099 status
= test_ioctl_compress_set(torture
, tmp_ctx
, tree
, fh
,
3100 COMPRESSION_FORMAT_DEFAULT
);
3101 torture_assert_ntstatus_equal(torture
, status
,
3102 NT_STATUS_NOT_SUPPORTED
,
3103 "FSCTL_SET_COMPRESSION default");
3106 * Despite not supporting compression, we should get a successful
3107 * response for set(COMPRESSION_FORMAT_NONE) - like WS2016 ReFS.
3109 status
= test_ioctl_compress_set(torture
, tmp_ctx
, tree
, fh
,
3110 COMPRESSION_FORMAT_NONE
);
3111 torture_assert_ntstatus_ok(torture
, status
,
3112 "FSCTL_SET_COMPRESSION none");
3114 smb2_util_close(tree
, fh
);
3115 talloc_free(tmp_ctx
);
3120 basic testing of the SMB2 FSCTL_QUERY_NETWORK_INTERFACE_INFO ioctl
3122 static bool test_ioctl_network_interface_info(struct torture_context
*torture
,
3123 struct smb2_tree
*tree
)
3125 union smb_ioctl ioctl
;
3126 struct smb2_handle fh
;
3128 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
3129 struct fsctl_net_iface_info net_iface
;
3130 enum ndr_err_code ndr_ret
;
3133 caps
= smb2cli_conn_server_capabilities(tree
->session
->transport
->conn
);
3134 if (!(caps
& SMB2_CAP_MULTI_CHANNEL
)) {
3135 torture_skip(torture
, "server doesn't support SMB2_CAP_MULTI_CHANNEL\n");
3139 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
3140 fh
.data
[0] = UINT64_MAX
;
3141 fh
.data
[1] = UINT64_MAX
;
3142 ioctl
.smb2
.in
.file
.handle
= fh
;
3143 ioctl
.smb2
.in
.function
= FSCTL_QUERY_NETWORK_INTERFACE_INFO
;
3144 ioctl
.smb2
.in
.max_response_size
= 0x10000; /* Windows client sets this to 64KiB */
3145 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
3147 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
3148 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_QUERY_NETWORK_INTERFACE_INFO");
3150 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
, &net_iface
,
3151 (ndr_pull_flags_fn_t
)ndr_pull_fsctl_net_iface_info
);
3152 torture_assert_ndr_success(torture
, ndr_ret
,
3153 "ndr_pull_fsctl_net_iface_info");
3155 ndr_print_debug((ndr_print_fn_t
)ndr_print_fsctl_net_iface_info
,
3156 "Network Interface Info", &net_iface
);
3158 talloc_free(tmp_ctx
);
3163 * Check whether all @fs_support_flags are set in the server's
3164 * RAW_QFS_ATTRIBUTE_INFORMATION FileSystemAttributes response.
3166 static NTSTATUS
test_ioctl_fs_supported(struct torture_context
*torture
,
3167 struct smb2_tree
*tree
,
3168 TALLOC_CTX
*mem_ctx
,
3169 struct smb2_handle
*fh
,
3170 uint64_t fs_support_flags
,
3174 union smb_fsinfo info
;
3177 info
.generic
.level
= RAW_QFS_ATTRIBUTE_INFORMATION
;
3178 info
.generic
.handle
= *fh
;
3179 status
= smb2_getinfo_fs(tree
, tree
, &info
);
3180 if (!NT_STATUS_IS_OK(status
)) {
3184 if ((info
.attribute_info
.out
.fs_attr
& fs_support_flags
)
3185 == fs_support_flags
) {
3190 return NT_STATUS_OK
;
3193 static NTSTATUS
test_ioctl_sparse_req(struct torture_context
*torture
,
3194 TALLOC_CTX
*mem_ctx
,
3195 struct smb2_tree
*tree
,
3196 struct smb2_handle fh
,
3199 union smb_ioctl ioctl
;
3204 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
3205 ioctl
.smb2
.in
.file
.handle
= fh
;
3206 ioctl
.smb2
.in
.function
= FSCTL_SET_SPARSE
;
3207 ioctl
.smb2
.in
.max_response_size
= 0;
3208 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
3209 set_sparse
= (set
? 0xFF : 0x0);
3210 ioctl
.smb2
.in
.out
.data
= &set_sparse
;
3211 ioctl
.smb2
.in
.out
.length
= sizeof(set_sparse
);
3213 status
= smb2_ioctl(tree
, mem_ctx
, &ioctl
.smb2
);
3217 static NTSTATUS
test_sparse_get(struct torture_context
*torture
,
3218 TALLOC_CTX
*mem_ctx
,
3219 struct smb2_tree
*tree
,
3220 struct smb2_handle fh
,
3223 union smb_fileinfo io
;
3227 io
.generic
.level
= RAW_FILEINFO_BASIC_INFORMATION
;
3228 io
.generic
.in
.file
.handle
= fh
;
3229 status
= smb2_getinfo_file(tree
, mem_ctx
, &io
);
3230 if (!NT_STATUS_IS_OK(status
)) {
3233 *_is_sparse
= !!(io
.basic_info
.out
.attrib
& FILE_ATTRIBUTE_SPARSE
);
3238 static bool test_ioctl_sparse_file_flag(struct torture_context
*torture
,
3239 struct smb2_tree
*tree
)
3241 struct smb2_handle fh
;
3242 union smb_fileinfo io
;
3244 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
3248 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
3249 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
3250 FILE_ATTRIBUTE_NORMAL
);
3251 torture_assert(torture
, ok
, "setup file");
3253 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
3254 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
3255 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
3257 smb2_util_close(tree
, fh
);
3258 torture_skip(torture
, "Sparse files not supported\n");
3262 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
3263 io
.generic
.in
.file
.handle
= fh
;
3264 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
3265 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FILE");
3267 torture_assert(torture
,
3268 ((io
.all_info2
.out
.attrib
& FILE_ATTRIBUTE_SPARSE
) == 0),
3269 "sparse attr before set");
3271 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, true);
3272 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
3274 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
3275 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
3276 torture_assert(torture
, is_sparse
, "no sparse attr after set");
3278 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, false);
3279 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
3281 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
3282 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
3283 torture_assert(torture
, !is_sparse
, "sparse attr after unset");
3285 smb2_util_close(tree
, fh
);
3286 talloc_free(tmp_ctx
);
3290 static bool test_ioctl_sparse_file_attr(struct torture_context
*torture
,
3291 struct smb2_tree
*tree
)
3293 struct smb2_handle fh
;
3295 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
3299 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
3300 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
3301 (FILE_ATTRIBUTE_NORMAL
| FILE_ATTRIBUTE_SPARSE
));
3302 torture_assert(torture
, ok
, "setup file");
3304 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
3305 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
3306 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
3308 smb2_util_close(tree
, fh
);
3309 torture_skip(torture
, "Sparse files not supported\n");
3312 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
3313 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
3314 torture_assert(torture
, !is_sparse
, "sparse attr on open");
3316 smb2_util_close(tree
, fh
);
3317 talloc_free(tmp_ctx
);
3321 static bool test_ioctl_sparse_dir_flag(struct torture_context
*torture
,
3322 struct smb2_tree
*tree
)
3324 struct smb2_handle dirh
;
3326 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
3329 smb2_deltree(tree
, DNAME
);
3330 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
3331 DNAME
, &dirh
, 0, SEC_RIGHTS_FILE_ALL
,
3332 FILE_ATTRIBUTE_DIRECTORY
);
3333 torture_assert(torture
, ok
, "setup sparse directory");
3335 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &dirh
,
3336 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
3337 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
3339 smb2_util_close(tree
, dirh
);
3340 smb2_deltree(tree
, DNAME
);
3341 torture_skip(torture
, "Sparse files not supported\n");
3344 /* set sparse dir should fail, check for 2k12 & 2k8 response */
3345 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, dirh
, true);
3346 torture_assert_ntstatus_equal(torture
, status
,
3347 NT_STATUS_INVALID_PARAMETER
,
3348 "dir FSCTL_SET_SPARSE status");
3350 smb2_util_close(tree
, dirh
);
3351 smb2_deltree(tree
, DNAME
);
3352 talloc_free(tmp_ctx
);
3357 * FSCTL_SET_SPARSE can be sent with (already tested) or without a SetSparse
3358 * buffer to indicate whether the flag should be set or cleared. When sent
3359 * without a buffer, it must be handled as if SetSparse=TRUE.
3361 static bool test_ioctl_sparse_set_nobuf(struct torture_context
*torture
,
3362 struct smb2_tree
*tree
)
3364 struct smb2_handle fh
;
3365 union smb_ioctl ioctl
;
3367 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
3371 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
3372 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
3373 FILE_ATTRIBUTE_NORMAL
);
3374 torture_assert(torture
, ok
, "setup file");
3376 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
3377 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
3378 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
3380 smb2_util_close(tree
, fh
);
3381 torture_skip(torture
, "Sparse files not supported\n");
3384 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
3385 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
3386 torture_assert(torture
, !is_sparse
, "sparse attr before set");
3389 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
3390 ioctl
.smb2
.in
.file
.handle
= fh
;
3391 ioctl
.smb2
.in
.function
= FSCTL_SET_SPARSE
;
3392 ioctl
.smb2
.in
.max_response_size
= 0;
3393 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
3394 /* ioctl.smb2.in.out is zeroed, no SetSparse buffer */
3396 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
3397 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
3399 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
3400 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
3401 torture_assert(torture
, is_sparse
, "no sparse attr after set");
3403 /* second non-SetSparse request shouldn't toggle sparse */
3405 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
3406 ioctl
.smb2
.in
.file
.handle
= fh
;
3407 ioctl
.smb2
.in
.function
= FSCTL_SET_SPARSE
;
3408 ioctl
.smb2
.in
.max_response_size
= 0;
3409 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
3411 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
3412 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
3414 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
3415 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
3416 torture_assert(torture
, is_sparse
, "no sparse attr after 2nd set");
3418 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, false);
3419 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
3421 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
3422 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
3423 torture_assert(torture
, !is_sparse
, "sparse attr after unset");
3425 smb2_util_close(tree
, fh
);
3426 talloc_free(tmp_ctx
);
3430 static bool test_ioctl_sparse_set_oversize(struct torture_context
*torture
,
3431 struct smb2_tree
*tree
)
3433 struct smb2_handle fh
;
3434 union smb_ioctl ioctl
;
3436 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
3441 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
3442 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
3443 FILE_ATTRIBUTE_NORMAL
);
3444 torture_assert(torture
, ok
, "setup file");
3446 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
3447 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
3448 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
3450 smb2_util_close(tree
, fh
);
3451 torture_skip(torture
, "Sparse files not supported\n");
3454 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
3455 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
3456 torture_assert(torture
, !is_sparse
, "sparse attr before set");
3459 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
3460 ioctl
.smb2
.in
.file
.handle
= fh
;
3461 ioctl
.smb2
.in
.function
= FSCTL_SET_SPARSE
;
3462 ioctl
.smb2
.in
.max_response_size
= 0;
3463 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
3466 * Attach a request buffer larger than FILE_SET_SPARSE_BUFFER
3467 * Windows still successfully processes the request.
3470 buf
[0] = 0xFF; /* attempt to set sparse */
3471 ioctl
.smb2
.in
.out
.data
= buf
;
3472 ioctl
.smb2
.in
.out
.length
= ARRAY_SIZE(buf
);
3474 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
3475 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
3477 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
3478 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
3479 torture_assert(torture
, is_sparse
, "no sparse attr after set");
3482 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
3483 ioctl
.smb2
.in
.file
.handle
= fh
;
3484 ioctl
.smb2
.in
.function
= FSCTL_SET_SPARSE
;
3485 ioctl
.smb2
.in
.max_response_size
= 0;
3486 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
3488 ZERO_ARRAY(buf
); /* clear sparse */
3489 ioctl
.smb2
.in
.out
.data
= buf
;
3490 ioctl
.smb2
.in
.out
.length
= ARRAY_SIZE(buf
);
3492 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
3493 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
3495 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
3496 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
3497 torture_assert(torture
, !is_sparse
, "sparse attr after clear");
3499 smb2_util_close(tree
, fh
);
3500 talloc_free(tmp_ctx
);
3504 static NTSTATUS
test_ioctl_qar_req(struct torture_context
*torture
,
3505 TALLOC_CTX
*mem_ctx
,
3506 struct smb2_tree
*tree
,
3507 struct smb2_handle fh
,
3510 struct file_alloced_range_buf
**_rsp
,
3511 uint64_t *_rsp_count
)
3513 union smb_ioctl ioctl
;
3515 enum ndr_err_code ndr_ret
;
3516 struct file_alloced_range_buf far_buf
;
3517 struct file_alloced_range_buf
*far_rsp
= NULL
;
3518 uint64_t far_count
= 0;
3520 TALLOC_CTX
*tmp_ctx
= talloc_new(mem_ctx
);
3521 if (tmp_ctx
== NULL
) {
3522 return NT_STATUS_NO_MEMORY
;
3526 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
3527 ioctl
.smb2
.in
.file
.handle
= fh
;
3528 ioctl
.smb2
.in
.function
= FSCTL_QUERY_ALLOCATED_RANGES
;
3529 ioctl
.smb2
.in
.max_response_size
= 1024;
3530 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
3532 far_buf
.file_off
= req_off
;
3533 far_buf
.len
= req_len
;
3535 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
3537 (ndr_push_flags_fn_t
)ndr_push_file_alloced_range_buf
);
3538 if (ndr_ret
!= NDR_ERR_SUCCESS
) {
3539 status
= NT_STATUS_UNSUCCESSFUL
;
3543 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
3544 if (!NT_STATUS_IS_OK(status
)) {
3548 if (ioctl
.smb2
.out
.out
.length
== 0) {
3552 if ((ioctl
.smb2
.out
.out
.length
% sizeof(far_buf
)) != 0) {
3553 torture_comment(torture
, "invalid qry_alloced rsp len: %zd:",
3554 ioctl
.smb2
.out
.out
.length
);
3555 status
= NT_STATUS_INVALID_VIEW_SIZE
;
3559 far_count
= (ioctl
.smb2
.out
.out
.length
/ sizeof(far_buf
));
3560 far_rsp
= talloc_array(mem_ctx
, struct file_alloced_range_buf
,
3562 if (far_rsp
== NULL
) {
3563 status
= NT_STATUS_NO_MEMORY
;
3567 for (i
= 0; i
< far_count
; i
++) {
3568 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
3570 (ndr_pull_flags_fn_t
)ndr_pull_file_alloced_range_buf
);
3571 if (ndr_ret
!= NDR_ERR_SUCCESS
) {
3572 status
= NT_STATUS_UNSUCCESSFUL
;
3575 /* move to next buffer */
3576 ioctl
.smb2
.out
.out
.data
+= sizeof(far_buf
);
3577 ioctl
.smb2
.out
.out
.length
-= sizeof(far_buf
);
3582 *_rsp_count
= far_count
;
3583 status
= NT_STATUS_OK
;
3585 talloc_free(tmp_ctx
);
3589 static bool test_ioctl_sparse_qar(struct torture_context
*torture
,
3590 struct smb2_tree
*tree
)
3592 struct smb2_handle fh
;
3594 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
3597 struct file_alloced_range_buf
*far_rsp
= NULL
;
3598 uint64_t far_count
= 0;
3600 /* zero length file, shouldn't have any ranges */
3601 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
3602 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
3603 FILE_ATTRIBUTE_NORMAL
);
3604 torture_assert(torture
, ok
, "setup file");
3606 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
3607 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
3608 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
3610 smb2_util_close(tree
, fh
);
3611 torture_skip(torture
, "Sparse files not supported\n");
3614 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
3615 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
3616 torture_assert(torture
, !is_sparse
, "sparse attr before set");
3618 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
3623 torture_assert_ntstatus_ok(torture
, status
,
3624 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3625 torture_assert_u64_equal(torture
, far_count
, 0,
3626 "unexpected response len");
3628 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
3633 torture_assert_ntstatus_ok(torture
, status
,
3634 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3635 torture_assert_u64_equal(torture
, far_count
, 0,
3636 "unexpected response len");
3638 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, true);
3639 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
3641 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
3642 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
3643 torture_assert(torture
, is_sparse
, "no sparse attr after set");
3645 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
3650 torture_assert_ntstatus_ok(torture
, status
,
3651 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3652 torture_assert_u64_equal(torture
, far_count
, 0,
3653 "unexpected response len");
3655 /* write into the (now) sparse file at 4k offset */
3656 ok
= write_pattern(torture
, tree
, tmp_ctx
, fh
,
3659 4096); /* pattern offset */
3660 torture_assert(torture
, ok
, "write pattern");
3663 * Query range before write off. Whether it's allocated or not is FS
3664 * dependent. NTFS deallocates chunks in 64K increments, but others
3665 * (e.g. XFS, Btrfs, etc.) may deallocate 4K chunks.
3667 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
3672 torture_assert_ntstatus_ok(torture
, status
,
3673 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3674 if (far_count
== 0) {
3675 torture_comment(torture
, "FS deallocated 4K chunk\n");
3677 /* expect fully allocated */
3678 torture_assert_u64_equal(torture
, far_count
, 1,
3679 "unexpected response len");
3680 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0, "far offset");
3681 torture_assert_u64_equal(torture
, far_rsp
[0].len
, 4096, "far len");
3685 * Query range before and past write, it should be allocated up to the
3688 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
3693 torture_assert_ntstatus_ok(torture
, status
,
3694 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3695 torture_assert_u64_equal(torture
, far_count
, 1,
3696 "unexpected response len");
3698 if (far_rsp
[0].file_off
== 4096) {
3699 /* 4K chunk unallocated */
3700 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 4096, "far offset");
3701 torture_assert_u64_equal(torture
, far_rsp
[0].len
, 1024, "far len");
3703 /* expect fully allocated */
3704 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0, "far offset");
3705 torture_assert_u64_equal(torture
, far_rsp
[0].len
, 5120, "far len");
3708 smb2_util_close(tree
, fh
);
3709 talloc_free(tmp_ctx
);
3713 static bool test_ioctl_sparse_qar_malformed(struct torture_context
*torture
,
3714 struct smb2_tree
*tree
)
3716 struct smb2_handle fh
;
3717 union smb_ioctl ioctl
;
3718 struct file_alloced_range_buf far_buf
;
3720 enum ndr_err_code ndr_ret
;
3721 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
3725 /* zero length file, shouldn't have any ranges */
3726 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
3727 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
3728 FILE_ATTRIBUTE_NORMAL
);
3729 torture_assert(torture
, ok
, "setup file");
3731 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
3732 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
3733 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
3735 smb2_util_close(tree
, fh
);
3736 torture_skip(torture
, "Sparse files not supported\n");
3739 /* no allocated ranges, no space for range response, should pass */
3741 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
3742 ioctl
.smb2
.in
.file
.handle
= fh
;
3743 ioctl
.smb2
.in
.function
= FSCTL_QUERY_ALLOCATED_RANGES
;
3744 ioctl
.smb2
.in
.max_response_size
= 0;
3745 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
3747 far_buf
.file_off
= 0;
3749 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
3751 (ndr_push_flags_fn_t
)ndr_push_file_alloced_range_buf
);
3752 torture_assert_ndr_success(torture
, ndr_ret
, "push far ndr buf");
3754 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
3755 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_QUERY_ALLOCATED_RANGES");
3757 /* write into the file at 4k offset */
3758 ok
= write_pattern(torture
, tree
, tmp_ctx
, fh
,
3761 0); /* pattern offset */
3762 torture_assert(torture
, ok
, "write pattern");
3764 /* allocated range, no space for range response, should fail */
3765 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
3766 torture_assert_ntstatus_equal(torture
, status
,
3767 NT_STATUS_BUFFER_TOO_SMALL
, "qar no space");
3769 /* oversize (2x) file_alloced_range_buf in request, should pass */
3770 ioctl
.smb2
.in
.max_response_size
= 1024;
3771 old_len
= ioctl
.smb2
.in
.out
.length
;
3772 ok
= data_blob_realloc(tmp_ctx
, &ioctl
.smb2
.in
.out
,
3773 (ioctl
.smb2
.in
.out
.length
* 2));
3774 torture_assert(torture
, ok
, "2x data buffer");
3775 memcpy(ioctl
.smb2
.in
.out
.data
+ old_len
, ioctl
.smb2
.in
.out
.data
,
3777 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
3778 torture_assert_ntstatus_ok(torture
, status
, "qar too big");
3780 /* no file_alloced_range_buf in request, should fail */
3781 data_blob_free(&ioctl
.smb2
.in
.out
);
3782 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
3783 torture_assert_ntstatus_equal(torture
, status
,
3784 NT_STATUS_INVALID_PARAMETER
, "qar empty");
3790 * 2.3.57 FSCTL_SET_ZERO_DATA Request
3792 * How an implementation zeros data within a file is implementation-dependent.
3793 * A file system MAY choose to deallocate regions of disk space that have been
3796 * ... NTFS might deallocate disk space in the file if the file is stored on an
3797 * NTFS volume, and the file is sparse or compressed. It will free any allocated
3798 * space in chunks of 64 kilobytes that begin at an offset that is a multiple of
3799 * 64 kilobytes. Other bytes in the file (prior to the first freed 64-kilobyte
3800 * chunk and after the last freed 64-kilobyte chunk) will be zeroed but not
3803 static NTSTATUS
test_ioctl_zdata_req(struct torture_context
*torture
,
3804 TALLOC_CTX
*mem_ctx
,
3805 struct smb2_tree
*tree
,
3806 struct smb2_handle fh
,
3808 int64_t beyond_final_zero
)
3810 union smb_ioctl ioctl
;
3812 enum ndr_err_code ndr_ret
;
3813 struct file_zero_data_info zdata_info
;
3814 TALLOC_CTX
*tmp_ctx
= talloc_new(mem_ctx
);
3815 if (tmp_ctx
== NULL
) {
3816 return NT_STATUS_NO_MEMORY
;
3820 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
3821 ioctl
.smb2
.in
.file
.handle
= fh
;
3822 ioctl
.smb2
.in
.function
= FSCTL_SET_ZERO_DATA
;
3823 ioctl
.smb2
.in
.max_response_size
= 0;
3824 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
3826 zdata_info
.file_off
= off
;
3827 zdata_info
.beyond_final_zero
= beyond_final_zero
;
3829 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
3831 (ndr_push_flags_fn_t
)ndr_push_file_zero_data_info
);
3832 if (ndr_ret
!= NDR_ERR_SUCCESS
) {
3833 status
= NT_STATUS_UNSUCCESSFUL
;
3837 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
3838 if (!NT_STATUS_IS_OK(status
)) {
3842 status
= NT_STATUS_OK
;
3844 talloc_free(tmp_ctx
);
3848 static bool test_ioctl_sparse_punch(struct torture_context
*torture
,
3849 struct smb2_tree
*tree
)
3851 struct smb2_handle fh
;
3853 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
3856 struct file_alloced_range_buf
*far_rsp
= NULL
;
3857 uint64_t far_count
= 0;
3859 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
3860 FNAME
, &fh
, 4096, SEC_RIGHTS_FILE_ALL
,
3861 FILE_ATTRIBUTE_NORMAL
);
3862 torture_assert(torture
, ok
, "setup file");
3864 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
3865 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
3866 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
3868 smb2_util_close(tree
, fh
);
3869 torture_skip(torture
, "Sparse files not supported\n");
3872 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
3873 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
3874 torture_assert(torture
, !is_sparse
, "sparse attr before set");
3876 /* zero (hole-punch) the data, without sparse flag */
3877 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
3879 4096); /* beyond_final_zero */
3880 torture_assert_ntstatus_ok(torture
, status
, "zero_data");
3882 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
3887 torture_assert_ntstatus_ok(torture
, status
,
3888 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3889 torture_assert_u64_equal(torture
, far_count
, 1,
3890 "unexpected response len");
3892 /* expect fully allocated */
3893 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0,
3894 "unexpected far off");
3895 torture_assert_u64_equal(torture
, far_rsp
[0].len
, 4096,
3896 "unexpected far len");
3897 /* check that the data is now zeroed */
3898 ok
= check_zero(torture
, tree
, tmp_ctx
, fh
, 0, 4096);
3899 torture_assert(torture
, ok
, "non-sparse zeroed range");
3902 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, true);
3903 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
3905 /* still fully allocated on NTFS, see note below for Samba */
3906 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
3911 torture_assert_ntstatus_ok(torture
, status
,
3912 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3914 * FS specific: Samba uses PUNCH_HOLE to zero the range, and
3915 * subsequently uses fallocate() to allocate the punched range if the
3916 * file is marked non-sparse and "strict allocate" is enabled. In both
3917 * cases, the zeroed range will not be detected by SEEK_DATA, so the
3918 * range won't be present in QAR responses until the file is marked
3921 if (far_count
== 0) {
3922 torture_comment(torture
, "non-sparse zeroed range disappeared "
3923 "after marking sparse\n");
3925 /* NTFS: range remains fully allocated */
3926 torture_assert_u64_equal(torture
, far_count
, 1,
3927 "unexpected response len");
3928 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0,
3929 "unexpected far off");
3930 torture_assert_u64_equal(torture
, far_rsp
[0].len
, 4096,
3931 "unexpected far len");
3934 /* zero (hole-punch) the data, _with_ sparse flag */
3935 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
3937 4096); /* beyond_final_zero */
3938 torture_assert_ntstatus_ok(torture
, status
, "zero_data");
3940 /* the range should no longer be alloced */
3941 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
3946 torture_assert_ntstatus_ok(torture
, status
,
3947 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3948 torture_assert_u64_equal(torture
, far_count
, 0,
3949 "unexpected response len");
3951 ok
= check_zero(torture
, tree
, tmp_ctx
, fh
, 0, 4096);
3952 torture_assert(torture
, ok
, "sparse zeroed range");
3954 /* remove sparse flag, this should "unsparse" the zeroed range */
3955 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, false);
3956 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
3958 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
3963 torture_assert_ntstatus_ok(torture
, status
,
3964 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3965 torture_assert_u64_equal(torture
, far_count
, 1,
3966 "unexpected response len");
3967 /* expect fully allocated */
3968 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0,
3969 "unexpected far off");
3970 torture_assert_u64_equal(torture
, far_rsp
[0].len
, 4096,
3971 "unexpected far len");
3973 ok
= check_zero(torture
, tree
, tmp_ctx
, fh
, 0, 4096);
3974 torture_assert(torture
, ok
, "sparse zeroed range");
3976 smb2_util_close(tree
, fh
);
3977 talloc_free(tmp_ctx
);
3982 * Find the point at which a zeroed range in a sparse file is deallocated by the
3983 * underlying filesystem. NTFS on Windows Server 2012 deallocates chunks in 64k
3984 * increments. Also check whether zeroed neighbours are merged for deallocation.
3986 static bool test_ioctl_sparse_hole_dealloc(struct torture_context
*torture
,
3987 struct smb2_tree
*tree
)
3989 struct smb2_handle fh
;
3991 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
3995 uint64_t dealloc_chunk_len
= 0;
3996 struct file_alloced_range_buf
*far_rsp
= NULL
;
3997 uint64_t far_count
= 0;
3999 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
4000 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
4001 FILE_ATTRIBUTE_NORMAL
);
4002 torture_assert(torture
, ok
, "setup file 1");
4004 /* check for FS sparse file */
4005 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
4006 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
4007 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
4009 smb2_util_close(tree
, fh
);
4010 torture_skip(torture
, "Sparse files not supported\n");
4014 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, true);
4015 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
4017 file_size
= 1024 * 1024;
4019 ok
= write_pattern(torture
, tree
, tmp_ctx
, fh
,
4021 file_size
, /* len */
4022 0); /* pattern offset */
4023 torture_assert(torture
, ok
, "write pattern");
4025 /* check allocated ranges, should be fully allocated */
4026 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
4028 file_size
, /* len */
4031 torture_assert_ntstatus_ok(torture
, status
,
4032 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4033 torture_assert_u64_equal(torture
, far_count
, 1,
4034 "unexpected response len");
4035 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0,
4036 "unexpected far off");
4037 torture_assert_u64_equal(torture
, far_rsp
[0].len
, file_size
,
4038 "unexpected far len");
4040 /* punch holes in sizes of 1k increments */
4041 for (hlen
= 0; hlen
<= file_size
; hlen
+= 4096) {
4043 /* punch a hole from zero to the current increment */
4044 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
4046 hlen
); /* beyond_final_zero */
4047 torture_assert_ntstatus_ok(torture
, status
, "zero_data");
4049 /* ensure hole is zeroed, and pattern is consistent */
4050 ok
= check_zero(torture
, tree
, tmp_ctx
, fh
, 0, hlen
);
4051 torture_assert(torture
, ok
, "sparse zeroed range");
4053 ok
= check_pattern(torture
, tree
, tmp_ctx
, fh
, hlen
,
4054 file_size
- hlen
, hlen
);
4055 torture_assert(torture
, ok
, "allocated pattern range");
4057 /* Check allocated ranges, hole might have been deallocated */
4058 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
4060 file_size
, /* len */
4063 torture_assert_ntstatus_ok(torture
, status
,
4064 "FSCTL_QUERY_ALLOCATED_RANGES");
4065 if ((hlen
== file_size
) && (far_count
== 0)) {
4066 /* hole covered entire file, deallocation occurred */
4067 dealloc_chunk_len
= file_size
;
4071 torture_assert_u64_equal(torture
, far_count
, 1,
4072 "unexpected response len");
4073 if (far_rsp
[0].file_off
!= 0) {
4075 * We now know the hole punch length needed to trigger a
4076 * deallocation on this FS...
4078 dealloc_chunk_len
= hlen
;
4079 torture_comment(torture
, "hole punch %ju@0 resulted in "
4080 "deallocation of %ju@0\n",
4082 (uintmax_t)far_rsp
[0].file_off
);
4083 torture_assert_u64_equal(torture
,
4084 file_size
- far_rsp
[0].len
,
4085 far_rsp
[0].file_off
,
4086 "invalid alloced range");
4091 if (dealloc_chunk_len
== 0) {
4092 torture_comment(torture
, "strange, this FS never deallocates"
4093 "zeroed ranges in sparse files\n");
4094 return true; /* FS specific, not a failure */
4098 * Check whether deallocation occurs when the (now known)
4099 * deallocation chunk size is punched via two ZERO_DATA requests.
4100 * I.e. Does the FS merge the two ranges and deallocate the chunk?
4101 * NTFS on Windows Server 2012 does not.
4103 ok
= write_pattern(torture
, tree
, tmp_ctx
, fh
,
4105 file_size
, /* len */
4106 0); /* pattern offset */
4107 torture_assert(torture
, ok
, "write pattern");
4109 /* divide dealloc chunk size by two, to use as punch length */
4110 hlen
= dealloc_chunk_len
>> 1;
4113 * /half of dealloc chunk size 1M\
4115 * /offset 0 | /dealloc chunk size |
4116 * |------------------ |-------------------|-------------------|
4117 * | zeroed, 1st punch | zeroed, 2nd punch | existing pattern |
4119 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
4121 hlen
); /* beyond final zero */
4122 torture_assert_ntstatus_ok(torture
, status
, "zero_data");
4124 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
4126 dealloc_chunk_len
); /* beyond final */
4127 torture_assert_ntstatus_ok(torture
, status
, "zero_data");
4129 /* ensure holes are zeroed, and pattern is consistent */
4130 ok
= check_zero(torture
, tree
, tmp_ctx
, fh
, 0, dealloc_chunk_len
);
4131 torture_assert(torture
, ok
, "sparse zeroed range");
4133 ok
= check_pattern(torture
, tree
, tmp_ctx
, fh
, dealloc_chunk_len
,
4134 file_size
- dealloc_chunk_len
, dealloc_chunk_len
);
4135 torture_assert(torture
, ok
, "allocated pattern range");
4137 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
4139 file_size
, /* len */
4142 torture_assert_ntstatus_ok(torture
, status
,
4143 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4145 if ((far_count
== 0) && (dealloc_chunk_len
== file_size
)) {
4146 torture_comment(torture
, "holes merged for deallocation of "
4150 torture_assert_u64_equal(torture
, far_count
, 1,
4151 "unexpected response len");
4152 if (far_rsp
[0].file_off
== dealloc_chunk_len
) {
4153 torture_comment(torture
, "holes merged for deallocation of "
4154 "%ju chunk\n", (uintmax_t)dealloc_chunk_len
);
4155 torture_assert_u64_equal(torture
,
4156 file_size
- far_rsp
[0].len
,
4157 far_rsp
[0].file_off
,
4158 "invalid alloced range");
4160 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0,
4161 "unexpected deallocation");
4162 torture_comment(torture
, "holes not merged for deallocation\n");
4165 smb2_util_close(tree
, fh
);
4168 * Check whether an unwritten range is allocated when a sparse file is
4169 * written to at an offset past the dealloc chunk size:
4171 * /dealloc chunk size
4173 * |------------------ |-------------------|
4174 * | unwritten | pattern |
4176 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
4177 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
4178 FILE_ATTRIBUTE_NORMAL
);
4179 torture_assert(torture
, ok
, "setup file 1");
4182 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, true);
4183 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
4185 ok
= write_pattern(torture
, tree
, tmp_ctx
, fh
,
4186 dealloc_chunk_len
, /* off */
4188 dealloc_chunk_len
); /* pattern offset */
4189 torture_assert(torture
, ok
, "write pattern");
4191 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
4193 dealloc_chunk_len
+ 1024, /* len */
4196 torture_assert_ntstatus_ok(torture
, status
,
4197 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4198 torture_assert_u64_equal(torture
, far_count
, 1,
4199 "unexpected response len");
4200 if (far_rsp
[0].file_off
== 0) {
4201 torture_assert_u64_equal(torture
, far_rsp
[0].len
,
4202 dealloc_chunk_len
+ 1024,
4203 "unexpected far len");
4204 torture_comment(torture
, "unwritten range fully allocated\n");
4206 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, dealloc_chunk_len
,
4207 "unexpected deallocation");
4208 torture_assert_u64_equal(torture
, far_rsp
[0].len
, 1024,
4209 "unexpected far len");
4210 torture_comment(torture
, "unwritten range not allocated\n");
4213 ok
= check_zero(torture
, tree
, tmp_ctx
, fh
, 0, dealloc_chunk_len
);
4214 torture_assert(torture
, ok
, "sparse zeroed range");
4216 ok
= check_pattern(torture
, tree
, tmp_ctx
, fh
, dealloc_chunk_len
,
4217 1024, dealloc_chunk_len
);
4218 torture_assert(torture
, ok
, "allocated pattern range");
4220 /* unsparse, should now be fully allocated */
4221 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, false);
4222 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
4224 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
4226 dealloc_chunk_len
+ 1024, /* len */
4229 torture_assert_ntstatus_ok(torture
, status
,
4230 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4231 torture_assert_u64_equal(torture
, far_count
, 1,
4232 "unexpected response len");
4233 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0,
4234 "unexpected deallocation");
4235 torture_assert_u64_equal(torture
, far_rsp
[0].len
,
4236 dealloc_chunk_len
+ 1024,
4237 "unexpected far len");
4239 smb2_util_close(tree
, fh
);
4240 talloc_free(tmp_ctx
);
4244 /* check whether a file with compression and sparse attrs can be deallocated */
4245 static bool test_ioctl_sparse_compressed(struct torture_context
*torture
,
4246 struct smb2_tree
*tree
)
4248 struct smb2_handle fh
;
4250 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
4252 uint64_t file_size
= 1024 * 1024;
4253 struct file_alloced_range_buf
*far_rsp
= NULL
;
4254 uint64_t far_count
= 0;
4256 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
4257 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
4258 FILE_ATTRIBUTE_NORMAL
);
4259 torture_assert(torture
, ok
, "setup file 1");
4261 /* check for FS sparse file and compression support */
4262 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
4263 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
4264 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
4266 smb2_util_close(tree
, fh
);
4267 torture_skip(torture
, "Sparse files not supported\n");
4270 status
= test_ioctl_compress_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
4272 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
4274 smb2_util_close(tree
, fh
);
4275 torture_skip(torture
, "FS compression not supported\n");
4278 /* set compression and write some data */
4279 status
= test_ioctl_compress_set(torture
, tmp_ctx
, tree
, fh
,
4280 COMPRESSION_FORMAT_DEFAULT
);
4281 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_COMPRESSION");
4283 ok
= write_pattern(torture
, tree
, tmp_ctx
, fh
,
4285 file_size
, /* len */
4286 0); /* pattern offset */
4287 torture_assert(torture
, ok
, "write pattern");
4289 /* set sparse - now sparse and compressed */
4290 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, true);
4291 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
4293 /* check allocated ranges, should be fully alloced */
4294 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
4296 file_size
, /* len */
4299 torture_assert_ntstatus_ok(torture
, status
,
4300 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4301 torture_assert_u64_equal(torture
, far_count
, 1,
4302 "unexpected response len");
4303 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0,
4304 "unexpected far off");
4305 torture_assert_u64_equal(torture
, far_rsp
[0].len
, file_size
,
4306 "unexpected far len");
4308 /* zero (hole-punch) all data, with sparse and compressed attrs */
4309 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
4311 file_size
); /* beyond_final_zero */
4312 torture_assert_ntstatus_ok(torture
, status
, "zero_data");
4315 * Windows Server 2012 still deallocates a zeroed range when a sparse
4316 * file carries the compression attribute.
4318 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
4320 file_size
, /* len */
4323 torture_assert_ntstatus_ok(torture
, status
,
4324 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4325 if (far_count
== 0) {
4326 torture_comment(torture
, "sparse & compressed file "
4327 "deallocated after hole-punch\n");
4329 torture_assert_u64_equal(torture
, far_count
, 1,
4330 "unexpected response len");
4331 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0,
4332 "unexpected far off");
4333 torture_assert_u64_equal(torture
, far_rsp
[0].len
, file_size
,
4334 "unexpected far len");
4335 torture_comment(torture
, "sparse & compressed file fully "
4336 "allocated after hole-punch\n");
4339 smb2_util_close(tree
, fh
);
4340 talloc_free(tmp_ctx
);
4345 * Create a sparse file, then attempt to copy unallocated and allocated ranges
4346 * into a target file using FSCTL_SRV_COPYCHUNK.
4348 static bool test_ioctl_sparse_copy_chunk(struct torture_context
*torture
,
4349 struct smb2_tree
*tree
)
4351 struct smb2_handle src_h
;
4352 struct smb2_handle dest_h
;
4354 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
4356 uint64_t dealloc_chunk_len
= 64 * 1024; /* Windows 2012 */
4357 struct file_alloced_range_buf
*far_rsp
= NULL
;
4358 uint64_t far_count
= 0;
4359 union smb_ioctl ioctl
;
4360 struct srv_copychunk_copy cc_copy
;
4361 struct srv_copychunk_rsp cc_rsp
;
4362 enum ndr_err_code ndr_ret
;
4364 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
4365 FNAME
, &src_h
, 0, SEC_RIGHTS_FILE_ALL
,
4366 FILE_ATTRIBUTE_NORMAL
);
4367 torture_assert(torture
, ok
, "setup file");
4369 /* check for FS sparse file support */
4370 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &src_h
,
4371 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
4372 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
4373 smb2_util_close(tree
, src_h
);
4375 torture_skip(torture
, "Sparse files not supported\n");
4378 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
4381 &src_h
, 0, /* src file */
4382 SEC_RIGHTS_FILE_ALL
,
4384 &dest_h
, 0, /* dest file */
4385 SEC_RIGHTS_FILE_ALL
,
4388 torture_assert(torture
, ok
, "setup copy chunk error");
4391 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, src_h
, true);
4392 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
4394 /* start after dealloc_chunk_len, to create an unwritten sparse range */
4395 ok
= write_pattern(torture
, tree
, tmp_ctx
, src_h
,
4396 dealloc_chunk_len
, /* off */
4398 dealloc_chunk_len
); /* pattern offset */
4399 torture_assert(torture
, ok
, "write pattern");
4401 /* Skip test if 64k chunk is allocated - FS specific */
4402 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, src_h
,
4404 dealloc_chunk_len
+ 1024, /* len */
4407 torture_assert_ntstatus_ok(torture
, status
,
4408 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4409 torture_assert_u64_equal(torture
, far_count
, 1,
4410 "unexpected response len");
4411 if (far_rsp
[0].file_off
== 0) {
4412 torture_skip(torture
, "unwritten range fully allocated\n");
4415 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, dealloc_chunk_len
,
4416 "unexpected allocation");
4417 torture_assert_u64_equal(torture
, far_rsp
[0].len
, 1024,
4418 "unexpected far len");
4420 /* copy-chunk unallocated + written ranges into non-sparse dest */
4422 cc_copy
.chunks
[0].source_off
= 0;
4423 cc_copy
.chunks
[0].target_off
= 0;
4424 cc_copy
.chunks
[0].length
= dealloc_chunk_len
+ 1024;
4426 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
4428 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
4429 torture_assert_ndr_success(torture
, ndr_ret
,
4430 "ndr_push_srv_copychunk_copy");
4432 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
4433 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_COPYCHUNK");
4435 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
4437 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
4438 torture_assert_ndr_success(torture
, ndr_ret
,
4439 "ndr_pull_srv_copychunk_rsp");
4441 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
4442 1, /* chunks written */
4443 0, /* chunk bytes unsuccessfully written */
4444 dealloc_chunk_len
+ 1024); /* bytes written */
4445 torture_assert(torture
, ok
, "bad copy chunk response data");
4447 ok
= check_zero(torture
, tree
, tmp_ctx
, dest_h
, 0, dealloc_chunk_len
);
4448 torture_assert(torture
, ok
, "sparse zeroed range");
4450 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, dealloc_chunk_len
,
4451 1024, dealloc_chunk_len
);
4452 torture_assert(torture
, ok
, "copychunked range");
4454 /* copied range should be allocated in non-sparse dest */
4455 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, dest_h
,
4457 dealloc_chunk_len
+ 1024, /* len */
4460 torture_assert_ntstatus_ok(torture
, status
,
4461 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4462 torture_assert_u64_equal(torture
, far_count
, 1,
4463 "unexpected response len");
4464 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0,
4465 "unexpected allocation");
4466 torture_assert_u64_equal(torture
, far_rsp
[0].len
,
4467 dealloc_chunk_len
+ 1024,
4468 "unexpected far len");
4470 /* set dest as sparse */
4471 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, dest_h
, true);
4472 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
4474 /* zero (hole-punch) all data */
4475 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, dest_h
,
4477 dealloc_chunk_len
+ 1024);
4478 torture_assert_ntstatus_ok(torture
, status
, "zero_data");
4480 /* zeroed range might be deallocated */
4481 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, dest_h
,
4483 dealloc_chunk_len
+ 1024, /* len */
4486 torture_assert_ntstatus_ok(torture
, status
,
4487 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4488 if (far_count
== 0) {
4489 /* FS specific (e.g. NTFS) */
4490 torture_comment(torture
, "FS deallocates file on full-range "
4493 /* FS specific (e.g. EXT4) */
4494 torture_comment(torture
, "FS doesn't deallocate file on "
4495 "full-range punch\n");
4497 ok
= check_zero(torture
, tree
, tmp_ctx
, dest_h
, 0,
4498 dealloc_chunk_len
+ 1024);
4499 torture_assert(torture
, ok
, "punched zeroed range");
4501 /* copy-chunk again, this time with sparse dest */
4502 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
4503 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_COPYCHUNK");
4505 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
4507 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
4508 torture_assert_ndr_success(torture
, ndr_ret
,
4509 "ndr_pull_srv_copychunk_rsp");
4511 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
4512 1, /* chunks written */
4513 0, /* chunk bytes unsuccessfully written */
4514 dealloc_chunk_len
+ 1024); /* bytes written */
4515 torture_assert(torture
, ok
, "bad copy chunk response data");
4517 ok
= check_zero(torture
, tree
, tmp_ctx
, dest_h
, 0, dealloc_chunk_len
);
4518 torture_assert(torture
, ok
, "sparse zeroed range");
4520 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, dealloc_chunk_len
,
4521 1024, dealloc_chunk_len
);
4522 torture_assert(torture
, ok
, "copychunked range");
4524 /* copied range may be allocated in sparse dest */
4525 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, dest_h
,
4527 dealloc_chunk_len
+ 1024, /* len */
4530 torture_assert_ntstatus_ok(torture
, status
,
4531 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4532 torture_assert_u64_equal(torture
, far_count
, 1,
4533 "unexpected response len");
4535 * FS specific: sparse region may be unallocated in dest if copy-chunk
4536 * is handled in a sparse preserving way - E.g. vfs_btrfs
4537 * with BTRFS_IOC_CLONE_RANGE.
4539 if (far_rsp
[0].file_off
== dealloc_chunk_len
) {
4540 torture_comment(torture
, "copy-chunk sparse range preserved\n");
4541 torture_assert_u64_equal(torture
, far_rsp
[0].len
, 1024,
4542 "unexpected far len");
4544 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0,
4545 "unexpected allocation");
4546 torture_assert_u64_equal(torture
, far_rsp
[0].len
,
4547 dealloc_chunk_len
+ 1024,
4548 "unexpected far len");
4551 smb2_util_close(tree
, src_h
);
4552 smb2_util_close(tree
, dest_h
);
4553 talloc_free(tmp_ctx
);
4557 static bool test_ioctl_sparse_punch_invalid(struct torture_context
*torture
,
4558 struct smb2_tree
*tree
)
4560 struct smb2_handle fh
;
4562 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
4567 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
4568 FNAME
, &fh
, 4096, SEC_RIGHTS_FILE_ALL
,
4569 FILE_ATTRIBUTE_NORMAL
);
4570 torture_assert(torture
, ok
, "setup file");
4572 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
4573 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
4574 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
4576 smb2_util_close(tree
, fh
);
4577 torture_skip(torture
, "Sparse files not supported\n");
4580 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
4581 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
4582 torture_assert(torture
, !is_sparse
, "sparse attr before set");
4584 /* loop twice, without and with sparse attrib */
4585 for (i
= 0; i
<= 1; i
++) {
4586 union smb_fileinfo io
;
4587 struct file_alloced_range_buf
*far_rsp
= NULL
;
4588 uint64_t far_count
= 0;
4590 /* get size before & after. zero data should never change it */
4592 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
4593 io
.generic
.in
.file
.handle
= fh
;
4594 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
4595 torture_assert_ntstatus_ok(torture
, status
, "getinfo");
4596 torture_assert_int_equal(torture
, (int)io
.all_info2
.out
.size
,
4597 4096, "size after IO");
4599 /* valid 8 byte zero data, but after EOF */
4600 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
4602 4104); /* beyond_final_zero */
4603 torture_assert_ntstatus_ok(torture
, status
, "zero_data");
4605 /* valid 8 byte zero data, but after EOF */
4606 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
4608 8200); /* beyond_final_zero */
4609 torture_assert_ntstatus_ok(torture
, status
, "zero_data");
4612 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
4613 io
.generic
.in
.file
.handle
= fh
;
4614 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
4615 torture_assert_ntstatus_ok(torture
, status
, "getinfo");
4616 torture_assert_int_equal(torture
, (int)io
.all_info2
.out
.size
,
4617 4096, "size after IO");
4619 /* valid 0 byte zero data, without sparse flag */
4620 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
4622 4095); /* beyond_final_zero */
4623 torture_assert_ntstatus_ok(torture
, status
, "zero_data");
4625 /* INVALID off is past beyond_final_zero */
4626 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
4628 4095); /* beyond_final_zero */
4629 torture_assert_ntstatus_equal(torture
, status
,
4630 NT_STATUS_INVALID_PARAMETER
,
4631 "invalid zero_data");
4633 /* zero length QAR - valid */
4634 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
4637 &far_rsp
, &far_count
);
4638 torture_assert_ntstatus_ok(torture
, status
,
4639 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4640 torture_assert_u64_equal(torture
, far_count
, 0,
4641 "unexpected response len");
4643 /* QAR after EOF - valid */
4644 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
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
, 0,
4651 "unexpected response len");
4654 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
,
4656 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
4659 smb2_util_close(tree
, fh
);
4660 talloc_free(tmp_ctx
);
4664 static bool test_ioctl_sparse_perms(struct torture_context
*torture
,
4665 struct smb2_tree
*tree
)
4667 struct smb2_handle fh
;
4669 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
4672 struct file_alloced_range_buf
*far_rsp
= NULL
;
4673 uint64_t far_count
= 0;
4675 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
4676 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
4677 FILE_ATTRIBUTE_NORMAL
);
4678 torture_assert(torture
, ok
, "setup file");
4680 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
4681 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
4682 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
4683 smb2_util_close(tree
, fh
);
4685 torture_skip(torture
, "Sparse files not supported\n");
4688 /* set sparse without WRITE_ATTR permission should succeed */
4689 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
4691 (SEC_RIGHTS_FILE_WRITE
& ~(SEC_FILE_WRITE_ATTRIBUTE
4693 | SEC_FILE_WRITE_EA
)),
4694 FILE_ATTRIBUTE_NORMAL
);
4695 torture_assert(torture
, ok
, "setup file");
4697 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, true);
4698 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
4699 smb2_util_close(tree
, fh
);
4701 ok
= test_setup_open(torture
, tree
, tmp_ctx
,
4702 FNAME
, &fh
, SEC_RIGHTS_FILE_ALL
,
4703 FILE_ATTRIBUTE_NORMAL
);
4704 torture_assert(torture
, ok
, "setup file");
4705 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
4706 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
4707 torture_assert(torture
, is_sparse
, "sparse after set");
4708 smb2_util_close(tree
, fh
);
4710 /* attempt get sparse without READ_DATA permission */
4711 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
4713 (SEC_RIGHTS_FILE_READ
& ~SEC_FILE_READ_DATA
),
4714 FILE_ATTRIBUTE_NORMAL
);
4715 torture_assert(torture
, ok
, "setup file");
4717 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
4718 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
4719 torture_assert(torture
, !is_sparse
, "sparse set");
4720 smb2_util_close(tree
, fh
);
4722 /* attempt to set sparse with only WRITE_ATTR permission */
4723 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
4725 SEC_FILE_WRITE_ATTRIBUTE
,
4726 FILE_ATTRIBUTE_NORMAL
);
4727 torture_assert(torture
, ok
, "setup file");
4729 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, true);
4730 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
4731 smb2_util_close(tree
, fh
);
4733 /* attempt to set sparse with only WRITE_DATA permission */
4734 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
4736 SEC_FILE_WRITE_DATA
,
4737 FILE_ATTRIBUTE_NORMAL
);
4738 torture_assert(torture
, ok
, "setup file");
4740 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, true);
4741 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
4742 smb2_util_close(tree
, fh
);
4744 ok
= test_setup_open(torture
, tree
, tmp_ctx
,
4745 FNAME
, &fh
, SEC_RIGHTS_FILE_ALL
,
4746 FILE_ATTRIBUTE_NORMAL
);
4747 torture_assert(torture
, ok
, "setup file");
4748 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
4749 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
4750 torture_assert(torture
, is_sparse
, "sparse after set");
4751 smb2_util_close(tree
, fh
);
4753 /* attempt to set sparse with only APPEND_DATA permission */
4754 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
4756 SEC_FILE_APPEND_DATA
,
4757 FILE_ATTRIBUTE_NORMAL
);
4758 torture_assert(torture
, ok
, "setup file");
4760 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, true);
4761 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
4762 smb2_util_close(tree
, fh
);
4764 ok
= test_setup_open(torture
, tree
, tmp_ctx
,
4765 FNAME
, &fh
, SEC_RIGHTS_FILE_ALL
,
4766 FILE_ATTRIBUTE_NORMAL
);
4767 torture_assert(torture
, ok
, "setup file");
4768 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
4769 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
4770 torture_assert(torture
, is_sparse
, "sparse after set");
4771 smb2_util_close(tree
, fh
);
4773 /* attempt to set sparse with only WRITE_EA permission - should fail */
4774 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
4777 FILE_ATTRIBUTE_NORMAL
);
4778 torture_assert(torture
, ok
, "setup file");
4780 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, true);
4781 torture_assert_ntstatus_equal(torture
, status
,
4782 NT_STATUS_ACCESS_DENIED
,
4783 "FSCTL_SET_SPARSE permission");
4784 smb2_util_close(tree
, fh
);
4786 ok
= test_setup_open(torture
, tree
, tmp_ctx
,
4787 FNAME
, &fh
, SEC_RIGHTS_FILE_ALL
,
4788 FILE_ATTRIBUTE_NORMAL
);
4789 torture_assert(torture
, ok
, "setup file");
4790 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
4791 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
4792 torture_assert(torture
, !is_sparse
, "sparse after set");
4793 smb2_util_close(tree
, fh
);
4795 /* attempt QAR with only READ_ATTR permission - should fail */
4796 ok
= test_setup_open(torture
, tree
, tmp_ctx
,
4797 FNAME
, &fh
, SEC_FILE_READ_ATTRIBUTE
,
4798 FILE_ATTRIBUTE_NORMAL
);
4799 torture_assert(torture
, ok
, "setup file");
4800 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
4803 &far_rsp
, &far_count
);
4804 torture_assert_ntstatus_equal(torture
, status
,
4805 NT_STATUS_ACCESS_DENIED
,
4806 "FSCTL_QUERY_ALLOCATED_RANGES req passed");
4807 smb2_util_close(tree
, fh
);
4809 /* attempt QAR with only READ_DATA permission */
4810 ok
= test_setup_open(torture
, tree
, tmp_ctx
,
4811 FNAME
, &fh
, SEC_FILE_READ_DATA
,
4812 FILE_ATTRIBUTE_NORMAL
);
4813 torture_assert(torture
, ok
, "setup file");
4814 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
4817 &far_rsp
, &far_count
);
4818 torture_assert_ntstatus_ok(torture
, status
,
4819 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4820 torture_assert_u64_equal(torture
, far_count
, 0,
4821 "unexpected response len");
4822 smb2_util_close(tree
, fh
);
4824 /* attempt QAR with only READ_EA permission - should fail */
4825 ok
= test_setup_open(torture
, tree
, tmp_ctx
,
4826 FNAME
, &fh
, SEC_FILE_READ_EA
,
4827 FILE_ATTRIBUTE_NORMAL
);
4828 torture_assert(torture
, ok
, "setup file");
4829 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
4832 &far_rsp
, &far_count
);
4833 torture_assert_ntstatus_equal(torture
, status
,
4834 NT_STATUS_ACCESS_DENIED
,
4835 "FSCTL_QUERY_ALLOCATED_RANGES req passed");
4836 smb2_util_close(tree
, fh
);
4838 /* setup file for ZERO_DATA permissions tests */
4839 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
4841 SEC_RIGHTS_FILE_ALL
,
4842 FILE_ATTRIBUTE_NORMAL
);
4843 torture_assert(torture
, ok
, "setup file");
4845 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, true);
4846 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
4847 smb2_util_close(tree
, fh
);
4849 /* attempt ZERO_DATA with only WRITE_ATTR permission - should fail */
4850 ok
= test_setup_open(torture
, tree
, tmp_ctx
,
4851 FNAME
, &fh
, SEC_FILE_WRITE_ATTRIBUTE
,
4852 FILE_ATTRIBUTE_NORMAL
);
4853 torture_assert(torture
, ok
, "setup file");
4854 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
4856 4096); /* beyond_final_zero */
4857 torture_assert_ntstatus_equal(torture
, status
,
4858 NT_STATUS_ACCESS_DENIED
,
4859 "zero_data permission");
4860 smb2_util_close(tree
, fh
);
4862 /* attempt ZERO_DATA with only WRITE_DATA permission */
4863 ok
= test_setup_open(torture
, tree
, tmp_ctx
,
4864 FNAME
, &fh
, SEC_FILE_WRITE_DATA
,
4865 FILE_ATTRIBUTE_NORMAL
);
4866 torture_assert(torture
, ok
, "setup file");
4867 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
4869 4096); /* beyond_final_zero */
4870 torture_assert_ntstatus_ok(torture
, status
, "zero_data");
4871 smb2_util_close(tree
, fh
);
4873 /* attempt ZERO_DATA with only APPEND_DATA permission - should fail */
4874 ok
= test_setup_open(torture
, tree
, tmp_ctx
,
4875 FNAME
, &fh
, SEC_FILE_APPEND_DATA
,
4876 FILE_ATTRIBUTE_NORMAL
);
4877 torture_assert(torture
, ok
, "setup file");
4878 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
4880 4096); /* beyond_final_zero */
4881 torture_assert_ntstatus_equal(torture
, status
,
4882 NT_STATUS_ACCESS_DENIED
,
4883 "zero_data permission");
4884 smb2_util_close(tree
, fh
);
4886 /* attempt ZERO_DATA with only WRITE_EA permission - should fail */
4887 ok
= test_setup_open(torture
, tree
, tmp_ctx
,
4888 FNAME
, &fh
, SEC_FILE_WRITE_EA
,
4889 FILE_ATTRIBUTE_NORMAL
);
4890 torture_assert(torture
, ok
, "setup file");
4891 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
4893 4096); /* beyond_final_zero */
4894 torture_assert_ntstatus_equal(torture
, status
,
4895 NT_STATUS_ACCESS_DENIED
,
4896 "zero_data permission");
4897 smb2_util_close(tree
, fh
);
4899 talloc_free(tmp_ctx
);
4903 static bool test_ioctl_sparse_lck(struct torture_context
*torture
,
4904 struct smb2_tree
*tree
)
4906 struct smb2_handle fh
;
4907 struct smb2_handle fh2
;
4909 uint64_t dealloc_chunk_len
= 64 * 1024; /* Windows 2012 */
4910 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
4913 struct smb2_lock lck
;
4914 struct smb2_lock_element el
[1];
4915 struct file_alloced_range_buf
*far_rsp
= NULL
;
4916 uint64_t far_count
= 0;
4918 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
, FNAME
, &fh
,
4919 dealloc_chunk_len
, SEC_RIGHTS_FILE_ALL
,
4920 FILE_ATTRIBUTE_NORMAL
);
4921 torture_assert(torture
, ok
, "setup file");
4923 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
4924 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
4925 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
4927 torture_skip(torture
, "Sparse files not supported\n");
4928 smb2_util_close(tree
, fh
);
4931 /* open and lock via separate fh2 */
4932 status
= torture_smb2_testfile(tree
, FNAME
, &fh2
);
4933 torture_assert_ntstatus_ok(torture
, status
, "2nd src open");
4935 lck
.in
.lock_count
= 0x0001;
4936 lck
.in
.lock_sequence
= 0x00000000;
4937 lck
.in
.file
.handle
= fh2
;
4940 el
[0].length
= dealloc_chunk_len
;
4942 el
[0].flags
= SMB2_LOCK_FLAG_EXCLUSIVE
;
4944 status
= smb2_lock(tree
, &lck
);
4945 torture_assert_ntstatus_ok(torture
, status
, "lock");
4947 /* set sparse while locked */
4948 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, true);
4949 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
4951 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
4952 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
4953 torture_assert(torture
, is_sparse
, "sparse attr after set");
4955 /* zero data over locked range should fail */
4956 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
4958 4096); /* beyond_final_zero */
4959 torture_assert_ntstatus_equal(torture
, status
,
4960 NT_STATUS_FILE_LOCK_CONFLICT
,
4961 "zero_data locked");
4963 /* QAR over locked range should pass */
4964 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
4967 &far_rsp
, &far_count
);
4968 torture_assert_ntstatus_ok(torture
, status
,
4969 "FSCTL_QUERY_ALLOCATED_RANGES locked");
4970 torture_assert_u64_equal(torture
, far_count
, 1,
4971 "unexpected response len");
4972 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0,
4973 "unexpected allocation");
4974 torture_assert_u64_equal(torture
, far_rsp
[0].len
,
4976 "unexpected far len");
4978 /* zero data over range past EOF should pass */
4979 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
4980 dealloc_chunk_len
, /* off */
4981 dealloc_chunk_len
+ 4096);
4982 torture_assert_ntstatus_ok(torture
, status
,
4983 "zero_data past EOF locked");
4985 /* QAR over range past EOF should pass */
4986 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
4987 dealloc_chunk_len
, /* off */
4989 &far_rsp
, &far_count
);
4990 torture_assert_ntstatus_ok(torture
, status
,
4991 "FSCTL_QUERY_ALLOCATED_RANGES past EOF locked");
4992 torture_assert_u64_equal(torture
, far_count
, 0,
4993 "unexpected response len");
4995 lck
.in
.lock_count
= 0x0001;
4996 lck
.in
.lock_sequence
= 0x00000001;
4997 lck
.in
.file
.handle
= fh2
;
5000 el
[0].length
= dealloc_chunk_len
;
5002 el
[0].flags
= SMB2_LOCK_FLAG_UNLOCK
;
5003 status
= smb2_lock(tree
, &lck
);
5004 torture_assert_ntstatus_ok(torture
, status
, "unlock");
5006 smb2_util_close(tree
, fh2
);
5007 smb2_util_close(tree
, fh
);
5008 talloc_free(tmp_ctx
);
5012 /* alleviate QAR off-by-one bug paranoia - help me ob1 */
5013 static bool test_ioctl_sparse_qar_ob1(struct torture_context
*torture
,
5014 struct smb2_tree
*tree
)
5016 struct smb2_handle fh
;
5018 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
5020 uint64_t dealloc_chunk_len
= 64 * 1024; /* Windows 2012 */
5021 struct file_alloced_range_buf
*far_rsp
= NULL
;
5022 uint64_t far_count
= 0;
5024 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
5025 FNAME
, &fh
, dealloc_chunk_len
* 2,
5026 SEC_RIGHTS_FILE_ALL
,
5027 FILE_ATTRIBUTE_NORMAL
);
5028 torture_assert(torture
, ok
, "setup file");
5030 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
5031 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
5032 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
5034 torture_skip(torture
, "Sparse files not supported\n");
5035 smb2_util_close(tree
, fh
);
5038 /* non-sparse QAR with range one before EOF */
5039 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
5041 dealloc_chunk_len
* 2 - 1, /* len */
5042 &far_rsp
, &far_count
);
5043 torture_assert_ntstatus_ok(torture
, status
,
5044 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5045 torture_assert_u64_equal(torture
, far_count
, 1,
5046 "unexpected response len");
5047 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0,
5048 "unexpected allocation");
5049 torture_assert_u64_equal(torture
, far_rsp
[0].len
,
5050 dealloc_chunk_len
* 2 - 1,
5051 "unexpected far len");
5053 /* non-sparse QAR with range one after EOF */
5054 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
5056 dealloc_chunk_len
* 2 + 1, /* len */
5057 &far_rsp
, &far_count
);
5058 torture_assert_ntstatus_ok(torture
, status
,
5059 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5060 torture_assert_u64_equal(torture
, far_count
, 1,
5061 "unexpected response len");
5062 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0,
5063 "unexpected allocation");
5064 torture_assert_u64_equal(torture
, far_rsp
[0].len
,
5065 dealloc_chunk_len
* 2,
5066 "unexpected far len");
5068 /* non-sparse QAR with range one after EOF from off=1 */
5069 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
5071 dealloc_chunk_len
* 2, /* len */
5072 &far_rsp
, &far_count
);
5073 torture_assert_ntstatus_ok(torture
, status
,
5074 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5075 torture_assert_u64_equal(torture
, far_count
, 1,
5076 "unexpected response len");
5077 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 1,
5078 "unexpected allocation");
5079 torture_assert_u64_equal(torture
, far_rsp
[0].len
,
5080 dealloc_chunk_len
* 2 - 1,
5081 "unexpected far len");
5083 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, true);
5084 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
5086 /* punch out second chunk */
5087 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
5088 dealloc_chunk_len
, /* off */
5089 dealloc_chunk_len
* 2);
5090 torture_assert_ntstatus_ok(torture
, status
, "zero_data");
5092 /* sparse QAR with range one before hole */
5093 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
5095 dealloc_chunk_len
- 1, /* len */
5096 &far_rsp
, &far_count
);
5097 torture_assert_ntstatus_ok(torture
, status
,
5098 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5099 torture_assert_u64_equal(torture
, far_count
, 1,
5100 "unexpected response len");
5101 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0,
5102 "unexpected allocation");
5103 torture_assert_u64_equal(torture
, far_rsp
[0].len
,
5104 dealloc_chunk_len
- 1,
5105 "unexpected far len");
5107 /* sparse QAR with range one after hole */
5108 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
5110 dealloc_chunk_len
+ 1, /* len */
5111 &far_rsp
, &far_count
);
5112 torture_assert_ntstatus_ok(torture
, status
,
5113 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5114 torture_assert_u64_equal(torture
, far_count
, 1,
5115 "unexpected response len");
5116 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0,
5117 "unexpected allocation");
5118 torture_assert_u64_equal(torture
, far_rsp
[0].len
,
5120 "unexpected far len");
5122 /* sparse QAR with range one after hole from off=1 */
5123 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
5125 dealloc_chunk_len
, /* len */
5126 &far_rsp
, &far_count
);
5127 torture_assert_ntstatus_ok(torture
, status
,
5128 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5129 torture_assert_u64_equal(torture
, far_count
, 1,
5130 "unexpected response len");
5131 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 1,
5132 "unexpected allocation");
5133 torture_assert_u64_equal(torture
, far_rsp
[0].len
,
5134 dealloc_chunk_len
- 1,
5135 "unexpected far len");
5137 /* sparse QAR with range one before EOF from off=chunk_len-1 */
5138 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
5139 dealloc_chunk_len
- 1, /* off */
5140 dealloc_chunk_len
, /* len */
5141 &far_rsp
, &far_count
);
5142 torture_assert_ntstatus_ok(torture
, status
,
5143 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5144 torture_assert_u64_equal(torture
, far_count
, 1,
5145 "unexpected response len");
5146 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
,
5147 dealloc_chunk_len
- 1,
5148 "unexpected allocation");
5149 torture_assert_u64_equal(torture
, far_rsp
[0].len
,
5150 1, "unexpected far len");
5152 /* sparse QAR with range one after EOF from off=chunk_len+1 */
5153 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
5154 dealloc_chunk_len
+ 1, /* off */
5155 dealloc_chunk_len
, /* len */
5156 &far_rsp
, &far_count
);
5157 torture_assert_ntstatus_ok(torture
, status
,
5158 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5159 torture_assert_u64_equal(torture
, far_count
, 0,
5160 "unexpected response len");
5161 smb2_util_close(tree
, fh
);
5162 talloc_free(tmp_ctx
);
5166 /* test QAR with multi-range responses */
5167 static bool test_ioctl_sparse_qar_multi(struct torture_context
*torture
,
5168 struct smb2_tree
*tree
)
5170 struct smb2_handle fh
;
5172 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
5174 uint64_t dealloc_chunk_len
= 64 * 1024; /* Windows 2012 */
5177 struct file_alloced_range_buf
*far_rsp
= NULL
;
5178 uint64_t far_count
= 0;
5180 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
5181 FNAME
, &fh
, dealloc_chunk_len
* 2,
5182 SEC_RIGHTS_FILE_ALL
,
5183 FILE_ATTRIBUTE_NORMAL
);
5184 torture_assert(torture
, ok
, "setup file");
5186 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
5187 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
5188 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
5190 torture_skip(torture
, "Sparse files not supported\n");
5191 smb2_util_close(tree
, fh
);
5194 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, true);
5195 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
5197 /* each loop, write out two chunks and punch the first out */
5198 for (i
= 0; i
< 10; i
++) {
5199 this_off
= i
* dealloc_chunk_len
* 2;
5201 ok
= write_pattern(torture
, tree
, tmp_ctx
, fh
,
5203 dealloc_chunk_len
* 2, /* len */
5204 this_off
); /* pattern offset */
5205 torture_assert(torture
, ok
, "write pattern");
5207 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
5209 this_off
+ dealloc_chunk_len
);
5210 torture_assert_ntstatus_ok(torture
, status
, "zero_data");
5213 /* should now have one separate region for each iteration */
5214 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
5216 10 * dealloc_chunk_len
* 2,
5217 &far_rsp
, &far_count
);
5218 torture_assert_ntstatus_ok(torture
, status
,
5219 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5220 if (far_count
== 1) {
5221 torture_comment(torture
, "this FS doesn't deallocate 64K"
5222 "zeroed ranges in sparse files\n");
5223 return true; /* FS specific, not a failure */
5225 torture_assert_u64_equal(torture
, far_count
, 10,
5226 "unexpected response len");
5227 for (i
= 0; i
< 10; i
++) {
5228 this_off
= i
* dealloc_chunk_len
* 2;
5230 torture_assert_u64_equal(torture
, far_rsp
[i
].file_off
,
5231 this_off
+ dealloc_chunk_len
,
5232 "unexpected allocation");
5233 torture_assert_u64_equal(torture
, far_rsp
[i
].len
,
5235 "unexpected far len");
5238 smb2_util_close(tree
, fh
);
5239 talloc_free(tmp_ctx
);
5243 static bool test_ioctl_sparse_qar_overflow(struct torture_context
*torture
,
5244 struct smb2_tree
*tree
)
5246 struct smb2_handle fh
;
5247 union smb_ioctl ioctl
;
5248 struct file_alloced_range_buf far_buf
;
5250 enum ndr_err_code ndr_ret
;
5251 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
5254 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
5255 FNAME
, &fh
, 1024, SEC_RIGHTS_FILE_ALL
,
5256 FILE_ATTRIBUTE_NORMAL
);
5257 torture_assert(torture
, ok
, "setup file");
5259 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
5260 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
5261 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
5263 smb2_util_close(tree
, fh
);
5264 torture_skip(torture
, "Sparse files not supported\n");
5267 /* no allocated ranges, no space for range response, should pass */
5269 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
5270 ioctl
.smb2
.in
.file
.handle
= fh
;
5271 ioctl
.smb2
.in
.function
= FSCTL_QUERY_ALLOCATED_RANGES
;
5272 ioctl
.smb2
.in
.max_response_size
= 1024;
5273 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
5275 /* off + length wraps around to 511 */
5276 far_buf
.file_off
= 512;
5277 far_buf
.len
= 0xffffffffffffffffLL
;
5278 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
5280 (ndr_push_flags_fn_t
)ndr_push_file_alloced_range_buf
);
5281 torture_assert_ndr_success(torture
, ndr_ret
, "push far ndr buf");
5283 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
5284 torture_assert_ntstatus_equal(torture
, status
,
5285 NT_STATUS_INVALID_PARAMETER
,
5286 "FSCTL_QUERY_ALLOCATED_RANGES overflow");
5291 static NTSTATUS
test_ioctl_trim_supported(struct torture_context
*torture
,
5292 struct smb2_tree
*tree
,
5293 TALLOC_CTX
*mem_ctx
,
5294 struct smb2_handle
*fh
,
5298 union smb_fsinfo info
;
5301 info
.generic
.level
= RAW_QFS_SECTOR_SIZE_INFORMATION
;
5302 info
.generic
.handle
= *fh
;
5303 status
= smb2_getinfo_fs(tree
, tree
, &info
);
5304 if (NT_STATUS_EQUAL(status
, NT_STATUS_INVALID_INFO_CLASS
)) {
5306 * Windows < Server 2012, 8 etc. don't support this info level
5307 * or the trim ioctl. Ignore the error and let the caller skip.
5309 *trim_support
= false;
5310 return NT_STATUS_OK
;
5311 } else if (!NT_STATUS_IS_OK(status
)) {
5315 torture_comment(torture
, "sector size info: lb/s=%u, pb/sA=%u, "
5316 "pb/sP=%u, fse/sA=%u, flags=0x%x, bosa=%u, bopa=%u\n",
5317 (unsigned)info
.sector_size_info
.out
.logical_bytes_per_sector
,
5318 (unsigned)info
.sector_size_info
.out
.phys_bytes_per_sector_atomic
,
5319 (unsigned)info
.sector_size_info
.out
.phys_bytes_per_sector_perf
,
5320 (unsigned)info
.sector_size_info
.out
.fs_effective_phys_bytes_per_sector_atomic
,
5321 (unsigned)info
.sector_size_info
.out
.flags
,
5322 (unsigned)info
.sector_size_info
.out
.byte_off_sector_align
,
5323 (unsigned)info
.sector_size_info
.out
.byte_off_partition_align
);
5325 if (info
.sector_size_info
.out
.flags
& QFS_SSINFO_FLAGS_TRIM_ENABLED
) {
5326 *trim_support
= true;
5328 *trim_support
= false;
5330 return NT_STATUS_OK
;
5333 static bool test_setup_trim(struct torture_context
*torture
,
5334 struct smb2_tree
*tree
,
5335 TALLOC_CTX
*mem_ctx
,
5336 uint32_t num_ranges
,
5337 struct smb2_handle
*fh
,
5339 uint32_t desired_access
,
5340 struct fsctl_file_level_trim_req
*trim_req
,
5341 union smb_ioctl
*ioctl
)
5345 ok
= test_setup_create_fill(torture
, tree
, mem_ctx
, FNAME
,
5346 fh
, file_size
, desired_access
,
5347 FILE_ATTRIBUTE_NORMAL
);
5348 torture_assert(torture
, ok
, "src file create fill");
5350 ZERO_STRUCTPN(ioctl
);
5351 ioctl
->smb2
.level
= RAW_IOCTL_SMB2
;
5352 ioctl
->smb2
.in
.file
.handle
= *fh
;
5353 ioctl
->smb2
.in
.function
= FSCTL_FILE_LEVEL_TRIM
;
5354 ioctl
->smb2
.in
.max_response_size
5355 = sizeof(struct fsctl_file_level_trim_rsp
);
5356 ioctl
->smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
5358 ZERO_STRUCTPN(trim_req
);
5359 /* leave key as zero for now. TODO test locking with differing keys */
5360 trim_req
->num_ranges
= num_ranges
;
5361 trim_req
->ranges
= talloc_zero_array(mem_ctx
,
5362 struct file_level_trim_range
,
5364 torture_assert(torture
, (trim_req
->ranges
!= NULL
), "no memory for ranges");
5369 static bool test_ioctl_trim_simple(struct torture_context
*torture
,
5370 struct smb2_tree
*tree
)
5372 struct smb2_handle fh
;
5374 union smb_ioctl ioctl
;
5375 bool trim_supported
;
5376 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
5377 struct fsctl_file_level_trim_req trim_req
;
5378 struct fsctl_file_level_trim_rsp trim_rsp
;
5379 uint64_t trim_chunk_len
= 64 * 1024; /* trim 64K chunks */
5380 enum ndr_err_code ndr_ret
;
5383 ok
= test_setup_trim(torture
, tree
, tmp_ctx
,
5385 &fh
, 2 * trim_chunk_len
, /* fill 128K file */
5386 SEC_RIGHTS_FILE_ALL
,
5390 torture_fail(torture
, "setup trim error");
5393 status
= test_ioctl_trim_supported(torture
, tree
, tmp_ctx
, &fh
,
5395 torture_assert_ntstatus_ok(torture
, status
, "fsinfo");
5396 if (!trim_supported
) {
5397 smb2_util_close(tree
, fh
);
5398 talloc_free(tmp_ctx
);
5399 torture_skip(torture
, "trim not supported\n");
5402 /* trim first chunk, leave second */
5403 trim_req
.ranges
[0].off
= 0;
5404 trim_req
.ranges
[0].len
= trim_chunk_len
;
5406 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
, &trim_req
,
5407 (ndr_push_flags_fn_t
)ndr_push_fsctl_file_level_trim_req
);
5408 torture_assert_ndr_success(torture
, ndr_ret
,
5409 "ndr_push_fsctl_file_level_trim_req");
5411 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
5412 torture_assert_ntstatus_ok(torture
, status
, "FILE_LEVEL_TRIM_RANGE");
5414 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
5416 (ndr_pull_flags_fn_t
)ndr_pull_fsctl_file_level_trim_rsp
);
5417 torture_assert_ndr_success(torture
, ndr_ret
,
5418 "ndr_pull_fsctl_file_level_trim_rsp");
5420 torture_assert_int_equal(torture
, trim_rsp
.num_ranges_processed
, 1, "");
5422 /* second half of the file should remain consitent */
5423 ok
= check_pattern(torture
, tree
, tmp_ctx
, fh
, trim_chunk_len
,
5424 trim_chunk_len
, trim_chunk_len
);
5425 torture_assert(torture
, ok
, "non-trimmed range inconsistent");
5430 static bool test_setup_dup_extents(struct torture_context
*tctx
,
5431 struct smb2_tree
*tree
,
5432 TALLOC_CTX
*mem_ctx
,
5433 struct smb2_handle
*src_h
,
5435 uint32_t src_desired_access
,
5436 struct smb2_handle
*dest_h
,
5438 uint32_t dest_desired_access
,
5439 struct fsctl_dup_extents_to_file
*dup_ext_buf
,
5440 union smb_ioctl
*ioctl
)
5444 ok
= test_setup_create_fill(tctx
, tree
, mem_ctx
, FNAME
,
5445 src_h
, src_size
, src_desired_access
,
5446 FILE_ATTRIBUTE_NORMAL
);
5447 torture_assert(tctx
, ok
, "src file create fill");
5449 ok
= test_setup_create_fill(tctx
, tree
, mem_ctx
, FNAME2
,
5450 dest_h
, dest_size
, dest_desired_access
,
5451 FILE_ATTRIBUTE_NORMAL
);
5452 torture_assert(tctx
, ok
, "dest file create fill");
5454 ZERO_STRUCTPN(ioctl
);
5455 ioctl
->smb2
.level
= RAW_IOCTL_SMB2
;
5456 ioctl
->smb2
.in
.file
.handle
= *dest_h
;
5457 ioctl
->smb2
.in
.function
= FSCTL_DUP_EXTENTS_TO_FILE
;
5458 ioctl
->smb2
.in
.max_response_size
= 0;
5459 ioctl
->smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
5461 ZERO_STRUCTPN(dup_ext_buf
);
5462 smb2_push_handle(dup_ext_buf
->source_fid
, src_h
);
5467 static bool test_ioctl_dup_extents_simple(struct torture_context
*tctx
,
5468 struct smb2_tree
*tree
)
5470 struct smb2_handle src_h
;
5471 struct smb2_handle dest_h
;
5473 union smb_ioctl ioctl
;
5474 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
5475 struct fsctl_dup_extents_to_file dup_ext_buf
;
5476 enum ndr_err_code ndr_ret
;
5477 union smb_fileinfo io
;
5478 union smb_setfileinfo sinfo
;
5481 ok
= test_setup_dup_extents(tctx
, tree
, tmp_ctx
,
5482 &src_h
, 4096, /* fill 4096 byte src file */
5483 SEC_RIGHTS_FILE_ALL
,
5484 &dest_h
, 0, /* 0 byte dest file */
5485 SEC_RIGHTS_FILE_ALL
,
5489 torture_fail(tctx
, "setup dup extents error");
5492 status
= test_ioctl_fs_supported(tctx
, tree
, tmp_ctx
, &src_h
,
5493 FILE_SUPPORTS_BLOCK_REFCOUNTING
, &ok
);
5494 torture_assert_ntstatus_ok(tctx
, status
, "SMB2_GETINFO_FS");
5496 smb2_util_close(tree
, src_h
);
5497 smb2_util_close(tree
, dest_h
);
5498 talloc_free(tmp_ctx
);
5499 torture_skip(tctx
, "block refcounting not supported\n");
5502 /* extend dest to match src len */
5504 sinfo
.end_of_file_info
.level
=
5505 RAW_SFILEINFO_END_OF_FILE_INFORMATION
;
5506 sinfo
.end_of_file_info
.in
.file
.handle
= dest_h
;
5507 sinfo
.end_of_file_info
.in
.size
= 4096;
5508 status
= smb2_setinfo_file(tree
, &sinfo
);
5509 torture_assert_ntstatus_ok(tctx
, status
, "smb2_setinfo_file");
5511 /* copy all src file data */
5512 dup_ext_buf
.source_off
= 0;
5513 dup_ext_buf
.target_off
= 0;
5514 dup_ext_buf
.byte_count
= 4096;
5516 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
5518 (ndr_push_flags_fn_t
)ndr_push_fsctl_dup_extents_to_file
);
5519 torture_assert_ndr_success(tctx
, ndr_ret
,
5520 "ndr_push_fsctl_dup_extents_to_file");
5522 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
5523 torture_assert_ntstatus_ok(tctx
, status
,
5524 "FSCTL_DUP_EXTENTS_TO_FILE");
5526 /* the file size shouldn't have been changed by this operation! */
5528 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
5529 io
.generic
.in
.file
.handle
= dest_h
;
5530 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
5531 torture_assert_ntstatus_ok(tctx
, status
, "getinfo");
5532 torture_assert_int_equal(tctx
, (int)io
.all_info2
.out
.size
,
5533 4096, "size after IO");
5535 smb2_util_close(tree
, src_h
);
5536 smb2_util_close(tree
, dest_h
);
5538 /* reopen for pattern check */
5539 ok
= test_setup_open(tctx
, tree
, tmp_ctx
, FNAME
, &src_h
,
5540 SEC_RIGHTS_FILE_ALL
, FILE_ATTRIBUTE_NORMAL
);
5541 torture_assert_ntstatus_ok(tctx
, status
, "src open after dup");
5542 ok
= test_setup_open(tctx
, tree
, tmp_ctx
, FNAME2
, &dest_h
,
5543 SEC_RIGHTS_FILE_ALL
, FILE_ATTRIBUTE_NORMAL
);
5544 torture_assert_ntstatus_ok(tctx
, status
, "dest open after dup");
5546 ok
= check_pattern(tctx
, tree
, tmp_ctx
, src_h
, 0, 4096, 0);
5548 torture_fail(tctx
, "inconsistent src file data");
5551 ok
= check_pattern(tctx
, tree
, tmp_ctx
, dest_h
, 0, 4096, 0);
5553 torture_fail(tctx
, "inconsistent dest file data");
5556 smb2_util_close(tree
, src_h
);
5557 smb2_util_close(tree
, dest_h
);
5558 talloc_free(tmp_ctx
);
5562 static bool test_ioctl_dup_extents_len_beyond_dest(struct torture_context
*tctx
,
5563 struct smb2_tree
*tree
)
5565 struct smb2_handle src_h
;
5566 struct smb2_handle dest_h
;
5568 union smb_ioctl ioctl
;
5569 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
5570 struct fsctl_dup_extents_to_file dup_ext_buf
;
5571 enum ndr_err_code ndr_ret
;
5572 union smb_fileinfo io
;
5573 union smb_setfileinfo sinfo
;
5576 ok
= test_setup_dup_extents(tctx
, tree
, tmp_ctx
,
5577 &src_h
, 32768, /* fill 32768 byte src file */
5578 SEC_RIGHTS_FILE_ALL
,
5579 &dest_h
, 0, /* 0 byte dest file */
5580 SEC_RIGHTS_FILE_ALL
,
5584 torture_fail(tctx
, "setup dup extents error");
5587 status
= test_ioctl_fs_supported(tctx
, tree
, tmp_ctx
, &src_h
,
5588 FILE_SUPPORTS_BLOCK_REFCOUNTING
, &ok
);
5589 torture_assert_ntstatus_ok(tctx
, status
, "SMB2_GETINFO_FS");
5591 smb2_util_close(tree
, src_h
);
5592 smb2_util_close(tree
, dest_h
);
5593 talloc_free(tmp_ctx
);
5594 torture_skip(tctx
, "block refcounting not supported\n");
5598 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
5599 io
.generic
.in
.file
.handle
= dest_h
;
5600 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
5601 torture_assert_ntstatus_ok(tctx
, status
, "getinfo");
5602 torture_assert_int_equal(tctx
, (int)io
.all_info2
.out
.size
,
5603 0, "size after IO");
5605 /* copy all src file data */
5606 dup_ext_buf
.source_off
= 0;
5607 dup_ext_buf
.target_off
= 0;
5608 dup_ext_buf
.byte_count
= 32768;
5610 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
5612 (ndr_push_flags_fn_t
)ndr_push_fsctl_dup_extents_to_file
);
5613 torture_assert_ndr_success(tctx
, ndr_ret
,
5614 "ndr_push_fsctl_dup_extents_to_file");
5616 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
5619 * 2.3.8 FSCTL_DUPLICATE_EXTENTS_TO_FILE Reply - this should fail, but
5620 * passes against WS2016 RTM!
5622 torture_assert_ntstatus_equal(tctx
, status
, NT_STATUS_NOT_SUPPORTED
,
5623 "FSCTL_DUP_EXTENTS_TO_FILE");
5626 /* the file sizes shouldn't have been changed */
5628 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
5629 io
.generic
.in
.file
.handle
= src_h
;
5630 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
5631 torture_assert_ntstatus_ok(tctx
, status
, "getinfo");
5632 torture_assert_int_equal(tctx
, (int)io
.all_info2
.out
.size
,
5633 32768, "size after IO");
5636 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
5637 io
.generic
.in
.file
.handle
= dest_h
;
5638 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
5639 torture_assert_ntstatus_ok(tctx
, status
, "getinfo");
5640 torture_assert_int_equal(tctx
, (int)io
.all_info2
.out
.size
,
5641 0, "size after IO");
5645 sinfo
.end_of_file_info
.level
= RAW_SFILEINFO_END_OF_FILE_INFORMATION
;
5646 sinfo
.end_of_file_info
.in
.file
.handle
= dest_h
;
5647 sinfo
.end_of_file_info
.in
.size
= 32768;
5648 status
= smb2_setinfo_file(tree
, &sinfo
);
5649 torture_assert_ntstatus_ok(tctx
, status
, "smb2_setinfo_file");
5651 ok
= check_zero(tctx
, tree
, tmp_ctx
, dest_h
, 0, 32768);
5653 torture_fail(tctx
, "inconsistent file data");
5656 /* reissue ioctl, now with enough space */
5657 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
5658 torture_assert_ntstatus_ok(tctx
, status
,
5659 "FSCTL_DUP_EXTENTS_TO_FILE");
5661 ok
= check_pattern(tctx
, tree
, tmp_ctx
, dest_h
, 0, 32768, 0);
5663 torture_fail(tctx
, "inconsistent file data");
5666 smb2_util_close(tree
, src_h
);
5667 smb2_util_close(tree
, dest_h
);
5668 talloc_free(tmp_ctx
);
5672 static bool test_ioctl_dup_extents_len_beyond_src(struct torture_context
*tctx
,
5673 struct smb2_tree
*tree
)
5675 struct smb2_handle src_h
;
5676 struct smb2_handle dest_h
;
5678 union smb_ioctl ioctl
;
5679 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
5680 struct fsctl_dup_extents_to_file dup_ext_buf
;
5681 enum ndr_err_code ndr_ret
;
5682 union smb_fileinfo io
;
5685 ok
= test_setup_dup_extents(tctx
, tree
, tmp_ctx
,
5686 &src_h
, 32768, /* fill 32768 byte src file */
5687 SEC_RIGHTS_FILE_ALL
,
5688 &dest_h
, 0, /* 0 byte dest file */
5689 SEC_RIGHTS_FILE_ALL
,
5693 torture_fail(tctx
, "setup dup extents error");
5696 status
= test_ioctl_fs_supported(tctx
, tree
, tmp_ctx
, &src_h
,
5697 FILE_SUPPORTS_BLOCK_REFCOUNTING
, &ok
);
5698 torture_assert_ntstatus_ok(tctx
, status
, "SMB2_GETINFO_FS");
5700 smb2_util_close(tree
, src_h
);
5701 smb2_util_close(tree
, dest_h
);
5702 talloc_free(tmp_ctx
);
5703 torture_skip(tctx
, "block refcounting not supported\n");
5707 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
5708 io
.generic
.in
.file
.handle
= dest_h
;
5709 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
5710 torture_assert_ntstatus_ok(tctx
, status
, "getinfo");
5711 torture_assert_int_equal(tctx
, (int)io
.all_info2
.out
.size
,
5712 0, "size after IO");
5714 /* exceed src file len */
5715 dup_ext_buf
.source_off
= 0;
5716 dup_ext_buf
.target_off
= 0;
5717 dup_ext_buf
.byte_count
= 32768 * 2;
5719 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
5721 (ndr_push_flags_fn_t
)ndr_push_fsctl_dup_extents_to_file
);
5722 torture_assert_ndr_success(tctx
, ndr_ret
,
5723 "ndr_push_fsctl_dup_extents_to_file");
5725 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
5726 torture_assert_ntstatus_equal(tctx
, status
, NT_STATUS_NOT_SUPPORTED
,
5727 "FSCTL_DUP_EXTENTS_TO_FILE");
5729 /* the file sizes shouldn't have been changed */
5731 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
5732 io
.generic
.in
.file
.handle
= src_h
;
5733 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
5734 torture_assert_ntstatus_ok(tctx
, status
, "getinfo");
5735 torture_assert_int_equal(tctx
, (int)io
.all_info2
.out
.size
,
5736 32768, "size after IO");
5739 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
5740 io
.generic
.in
.file
.handle
= dest_h
;
5741 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
5742 torture_assert_ntstatus_ok(tctx
, status
, "getinfo");
5743 torture_assert_int_equal(tctx
, (int)io
.all_info2
.out
.size
,
5744 0, "size after IO");
5746 smb2_util_close(tree
, src_h
);
5747 smb2_util_close(tree
, dest_h
);
5748 talloc_free(tmp_ctx
);
5752 static bool test_ioctl_dup_extents_len_zero(struct torture_context
*tctx
,
5753 struct smb2_tree
*tree
)
5755 struct smb2_handle src_h
;
5756 struct smb2_handle dest_h
;
5758 union smb_ioctl ioctl
;
5759 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
5760 struct fsctl_dup_extents_to_file dup_ext_buf
;
5761 enum ndr_err_code ndr_ret
;
5762 union smb_fileinfo io
;
5765 ok
= test_setup_dup_extents(tctx
, tree
, tmp_ctx
,
5766 &src_h
, 32768, /* fill 32768 byte src file */
5767 SEC_RIGHTS_FILE_ALL
,
5768 &dest_h
, 0, /* 0 byte dest file */
5769 SEC_RIGHTS_FILE_ALL
,
5773 torture_fail(tctx
, "setup dup extents error");
5776 status
= test_ioctl_fs_supported(tctx
, tree
, tmp_ctx
, &src_h
,
5777 FILE_SUPPORTS_BLOCK_REFCOUNTING
, &ok
);
5778 torture_assert_ntstatus_ok(tctx
, status
, "SMB2_GETINFO_FS");
5780 smb2_util_close(tree
, src_h
);
5781 smb2_util_close(tree
, dest_h
);
5782 talloc_free(tmp_ctx
);
5783 torture_skip(tctx
, "block refcounting not supported\n");
5787 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
5788 io
.generic
.in
.file
.handle
= dest_h
;
5789 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
5790 torture_assert_ntstatus_ok(tctx
, status
, "getinfo");
5791 torture_assert_int_equal(tctx
, (int)io
.all_info2
.out
.size
,
5792 0, "size after IO");
5794 dup_ext_buf
.source_off
= 0;
5795 dup_ext_buf
.target_off
= 0;
5796 dup_ext_buf
.byte_count
= 0;
5798 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
5800 (ndr_push_flags_fn_t
)ndr_push_fsctl_dup_extents_to_file
);
5801 torture_assert_ndr_success(tctx
, ndr_ret
,
5802 "ndr_push_fsctl_dup_extents_to_file");
5804 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
5805 torture_assert_ntstatus_ok(tctx
, status
, "FSCTL_DUP_EXTENTS_TO_FILE");
5807 /* the file sizes shouldn't have been changed */
5809 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
5810 io
.generic
.in
.file
.handle
= src_h
;
5811 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
5812 torture_assert_ntstatus_ok(tctx
, status
, "getinfo");
5813 torture_assert_int_equal(tctx
, (int)io
.all_info2
.out
.size
,
5814 32768, "size after IO");
5817 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
5818 io
.generic
.in
.file
.handle
= dest_h
;
5819 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
5820 torture_assert_ntstatus_ok(tctx
, status
, "getinfo");
5821 torture_assert_int_equal(tctx
, (int)io
.all_info2
.out
.size
,
5822 0, "size after IO");
5824 smb2_util_close(tree
, src_h
);
5825 smb2_util_close(tree
, dest_h
);
5826 talloc_free(tmp_ctx
);
5830 static bool test_ioctl_dup_extents_sparse_src(struct torture_context
*tctx
,
5831 struct smb2_tree
*tree
)
5833 struct smb2_handle src_h
;
5834 struct smb2_handle dest_h
;
5836 union smb_ioctl ioctl
;
5837 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
5838 struct fsctl_dup_extents_to_file dup_ext_buf
;
5839 enum ndr_err_code ndr_ret
;
5840 union smb_setfileinfo sinfo
;
5843 ok
= test_setup_dup_extents(tctx
, tree
, tmp_ctx
,
5844 &src_h
, 0, /* filled after sparse flag */
5845 SEC_RIGHTS_FILE_ALL
,
5846 &dest_h
, 0, /* 0 byte dest file */
5847 SEC_RIGHTS_FILE_ALL
,
5851 torture_fail(tctx
, "setup dup extents error");
5854 status
= test_ioctl_fs_supported(tctx
, tree
, tmp_ctx
, &src_h
,
5855 FILE_SUPPORTS_BLOCK_REFCOUNTING
5856 | FILE_SUPPORTS_SPARSE_FILES
, &ok
);
5857 torture_assert_ntstatus_ok(tctx
, status
, "SMB2_GETINFO_FS");
5859 smb2_util_close(tree
, src_h
);
5860 smb2_util_close(tree
, dest_h
);
5861 talloc_free(tmp_ctx
);
5863 "block refcounting and sparse files not supported\n");
5866 /* set sparse flag on src */
5867 status
= test_ioctl_sparse_req(tctx
, tmp_ctx
, tree
, src_h
, true);
5868 torture_assert_ntstatus_ok(tctx
, status
, "FSCTL_SET_SPARSE");
5870 ok
= write_pattern(tctx
, tree
, tmp_ctx
, src_h
, 0, 4096, 0);
5871 torture_assert(tctx
, ok
, "write pattern");
5875 sinfo
.end_of_file_info
.level
= RAW_SFILEINFO_END_OF_FILE_INFORMATION
;
5876 sinfo
.end_of_file_info
.in
.file
.handle
= dest_h
;
5877 sinfo
.end_of_file_info
.in
.size
= 4096;
5878 status
= smb2_setinfo_file(tree
, &sinfo
);
5879 torture_assert_ntstatus_ok(tctx
, status
, "smb2_setinfo_file");
5881 /* copy all src file data */
5882 dup_ext_buf
.source_off
= 0;
5883 dup_ext_buf
.target_off
= 0;
5884 dup_ext_buf
.byte_count
= 4096;
5886 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
5888 (ndr_push_flags_fn_t
)ndr_push_fsctl_dup_extents_to_file
);
5889 torture_assert_ndr_success(tctx
, ndr_ret
,
5890 "ndr_push_fsctl_dup_extents_to_file");
5893 * src is sparse, but spec says: 2.3.8 FSCTL_DUPLICATE_EXTENTS_TO_FILE
5894 * Reply... STATUS_NOT_SUPPORTED: Target file is sparse, while source
5895 * is a non-sparse file.
5897 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
5898 torture_assert_ntstatus_equal(tctx
, status
, NT_STATUS_NOT_SUPPORTED
,
5899 "FSCTL_DUP_EXTENTS_TO_FILE");
5901 smb2_util_close(tree
, src_h
);
5902 smb2_util_close(tree
, dest_h
);
5903 talloc_free(tmp_ctx
);
5907 static bool test_ioctl_dup_extents_sparse_dest(struct torture_context
*tctx
,
5908 struct smb2_tree
*tree
)
5910 struct smb2_handle src_h
;
5911 struct smb2_handle dest_h
;
5913 union smb_ioctl ioctl
;
5914 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
5915 struct fsctl_dup_extents_to_file dup_ext_buf
;
5916 enum ndr_err_code ndr_ret
;
5917 union smb_setfileinfo sinfo
;
5920 ok
= test_setup_dup_extents(tctx
, tree
, tmp_ctx
,
5921 &src_h
, 4096, /* fill 4096 byte src file */
5922 SEC_RIGHTS_FILE_ALL
,
5923 &dest_h
, 0, /* 0 byte dest file */
5924 SEC_RIGHTS_FILE_ALL
,
5928 torture_fail(tctx
, "setup dup extents error");
5931 status
= test_ioctl_fs_supported(tctx
, tree
, tmp_ctx
, &src_h
,
5932 FILE_SUPPORTS_BLOCK_REFCOUNTING
5933 | FILE_SUPPORTS_SPARSE_FILES
, &ok
);
5934 torture_assert_ntstatus_ok(tctx
, status
, "SMB2_GETINFO_FS");
5936 smb2_util_close(tree
, src_h
);
5937 smb2_util_close(tree
, dest_h
);
5938 talloc_free(tmp_ctx
);
5940 "block refcounting and sparse files not supported\n");
5943 /* set sparse flag on dest */
5944 status
= test_ioctl_sparse_req(tctx
, tmp_ctx
, tree
, dest_h
, true);
5945 torture_assert_ntstatus_ok(tctx
, status
, "FSCTL_SET_SPARSE");
5949 sinfo
.end_of_file_info
.level
= RAW_SFILEINFO_END_OF_FILE_INFORMATION
;
5950 sinfo
.end_of_file_info
.in
.file
.handle
= dest_h
;
5951 sinfo
.end_of_file_info
.in
.size
= dup_ext_buf
.byte_count
;
5952 status
= smb2_setinfo_file(tree
, &sinfo
);
5953 torture_assert_ntstatus_ok(tctx
, status
, "smb2_setinfo_file");
5955 /* copy all src file data */
5956 dup_ext_buf
.source_off
= 0;
5957 dup_ext_buf
.target_off
= 0;
5958 dup_ext_buf
.byte_count
= 4096;
5960 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
5962 (ndr_push_flags_fn_t
)ndr_push_fsctl_dup_extents_to_file
);
5963 torture_assert_ndr_success(tctx
, ndr_ret
,
5964 "ndr_push_fsctl_dup_extents_to_file");
5967 * dest is sparse, but spec says: 2.3.8 FSCTL_DUPLICATE_EXTENTS_TO_FILE
5968 * Reply... STATUS_NOT_SUPPORTED: Target file is sparse, while source
5969 * is a non-sparse file.
5971 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
5972 torture_assert_ntstatus_ok(tctx
, status
, "FSCTL_DUP_EXTENTS_TO_FILE");
5974 smb2_util_close(tree
, src_h
);
5975 smb2_util_close(tree
, dest_h
);
5976 talloc_free(tmp_ctx
);
5980 static bool test_ioctl_dup_extents_sparse_both(struct torture_context
*tctx
,
5981 struct smb2_tree
*tree
)
5983 struct smb2_handle src_h
;
5984 struct smb2_handle dest_h
;
5986 union smb_ioctl ioctl
;
5987 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
5988 struct fsctl_dup_extents_to_file dup_ext_buf
;
5989 enum ndr_err_code ndr_ret
;
5990 union smb_setfileinfo sinfo
;
5993 ok
= test_setup_dup_extents(tctx
, tree
, tmp_ctx
,
5994 &src_h
, 0, /* fill 4096 byte src file */
5995 SEC_RIGHTS_FILE_ALL
,
5996 &dest_h
, 0, /* 0 byte dest file */
5997 SEC_RIGHTS_FILE_ALL
,
6001 torture_fail(tctx
, "setup dup extents error");
6004 status
= test_ioctl_fs_supported(tctx
, tree
, tmp_ctx
, &src_h
,
6005 FILE_SUPPORTS_BLOCK_REFCOUNTING
6006 | FILE_SUPPORTS_SPARSE_FILES
, &ok
);
6007 torture_assert_ntstatus_ok(tctx
, status
, "SMB2_GETINFO_FS");
6009 smb2_util_close(tree
, src_h
);
6010 smb2_util_close(tree
, dest_h
);
6011 talloc_free(tmp_ctx
);
6013 "block refcounting and sparse files not supported\n");
6016 /* set sparse flag on src and dest */
6017 status
= test_ioctl_sparse_req(tctx
, tmp_ctx
, tree
, src_h
, true);
6018 torture_assert_ntstatus_ok(tctx
, status
, "FSCTL_SET_SPARSE");
6019 status
= test_ioctl_sparse_req(tctx
, tmp_ctx
, tree
, dest_h
, true);
6020 torture_assert_ntstatus_ok(tctx
, status
, "FSCTL_SET_SPARSE");
6022 ok
= write_pattern(tctx
, tree
, tmp_ctx
, src_h
, 0, 4096, 0);
6023 torture_assert(tctx
, ok
, "write pattern");
6027 sinfo
.end_of_file_info
.level
= RAW_SFILEINFO_END_OF_FILE_INFORMATION
;
6028 sinfo
.end_of_file_info
.in
.file
.handle
= dest_h
;
6029 sinfo
.end_of_file_info
.in
.size
= 4096;
6030 status
= smb2_setinfo_file(tree
, &sinfo
);
6031 torture_assert_ntstatus_ok(tctx
, status
, "smb2_setinfo_file");
6033 /* copy all src file data */
6034 dup_ext_buf
.source_off
= 0;
6035 dup_ext_buf
.target_off
= 0;
6036 dup_ext_buf
.byte_count
= 4096;
6038 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
6040 (ndr_push_flags_fn_t
)ndr_push_fsctl_dup_extents_to_file
);
6041 torture_assert_ndr_success(tctx
, ndr_ret
,
6042 "ndr_push_fsctl_dup_extents_to_file");
6044 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
6045 torture_assert_ntstatus_ok(tctx
, status
, "FSCTL_DUP_EXTENTS_TO_FILE");
6047 smb2_util_close(tree
, src_h
);
6048 smb2_util_close(tree
, dest_h
);
6050 /* reopen for pattern check */
6051 ok
= test_setup_open(tctx
, tree
, tmp_ctx
, FNAME2
, &dest_h
,
6052 SEC_RIGHTS_FILE_ALL
, FILE_ATTRIBUTE_NORMAL
);
6053 torture_assert_ntstatus_ok(tctx
, status
, "dest open ater dup");
6055 ok
= check_pattern(tctx
, tree
, tmp_ctx
, dest_h
, 0, 4096, 0);
6057 torture_fail(tctx
, "inconsistent file data");
6060 smb2_util_close(tree
, dest_h
);
6061 talloc_free(tmp_ctx
);
6065 static bool test_ioctl_dup_extents_src_is_dest(struct torture_context
*tctx
,
6066 struct smb2_tree
*tree
)
6068 struct smb2_handle src_h
;
6069 struct smb2_handle dest_h
;
6071 union smb_ioctl ioctl
;
6072 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
6073 struct fsctl_dup_extents_to_file dup_ext_buf
;
6074 enum ndr_err_code ndr_ret
;
6075 union smb_fileinfo io
;
6078 ok
= test_setup_dup_extents(tctx
, tree
, tmp_ctx
,
6079 &src_h
, 32768, /* fill 32768 byte src file */
6080 SEC_RIGHTS_FILE_ALL
,
6082 SEC_RIGHTS_FILE_ALL
,
6086 torture_fail(tctx
, "setup dup extents error");
6088 /* dest_h not needed for this test */
6089 smb2_util_close(tree
, dest_h
);
6091 status
= test_ioctl_fs_supported(tctx
, tree
, tmp_ctx
, &src_h
,
6092 FILE_SUPPORTS_BLOCK_REFCOUNTING
, &ok
);
6093 torture_assert_ntstatus_ok(tctx
, status
, "SMB2_GETINFO_FS");
6095 smb2_util_close(tree
, src_h
);
6096 talloc_free(tmp_ctx
);
6097 torture_skip(tctx
, "block refcounting not supported\n");
6100 /* src and dest are the same file handle */
6101 ioctl
.smb2
.in
.file
.handle
= src_h
;
6103 /* no overlap between src and tgt */
6104 dup_ext_buf
.source_off
= 0;
6105 dup_ext_buf
.target_off
= 16384;
6106 dup_ext_buf
.byte_count
= 16384;
6108 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
6110 (ndr_push_flags_fn_t
)ndr_push_fsctl_dup_extents_to_file
);
6111 torture_assert_ndr_success(tctx
, ndr_ret
,
6112 "ndr_push_fsctl_dup_extents_to_file");
6114 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
6115 torture_assert_ntstatus_ok(tctx
, status
, "FSCTL_DUP_EXTENTS_TO_FILE");
6117 /* the file size shouldn't have been changed */
6119 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
6120 io
.generic
.in
.file
.handle
= src_h
;
6121 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
6122 torture_assert_ntstatus_ok(tctx
, status
, "getinfo");
6123 torture_assert_int_equal(tctx
, (int)io
.all_info2
.out
.size
,
6124 32768, "size after IO");
6126 ok
= check_pattern(tctx
, tree
, tmp_ctx
, src_h
, 0, 16384, 0);
6128 torture_fail(tctx
, "inconsistent file data");
6130 ok
= check_pattern(tctx
, tree
, tmp_ctx
, src_h
, 16384, 16384, 0);
6132 torture_fail(tctx
, "inconsistent file data");
6135 smb2_util_close(tree
, src_h
);
6136 talloc_free(tmp_ctx
);
6141 * unlike copy-chunk, dup extents doesn't support overlapping ranges between
6142 * source and target. This makes it a *lot* cleaner to implement on the server.
6145 test_ioctl_dup_extents_src_is_dest_overlap(struct torture_context
*tctx
,
6146 struct smb2_tree
*tree
)
6148 struct smb2_handle src_h
;
6149 struct smb2_handle dest_h
;
6151 union smb_ioctl ioctl
;
6152 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
6153 struct fsctl_dup_extents_to_file dup_ext_buf
;
6154 enum ndr_err_code ndr_ret
;
6155 union smb_fileinfo io
;
6158 ok
= test_setup_dup_extents(tctx
, tree
, tmp_ctx
,
6159 &src_h
, 32768, /* fill 32768 byte src file */
6160 SEC_RIGHTS_FILE_ALL
,
6162 SEC_RIGHTS_FILE_ALL
,
6166 torture_fail(tctx
, "setup dup extents error");
6168 /* dest_h not needed for this test */
6169 smb2_util_close(tree
, dest_h
);
6171 status
= test_ioctl_fs_supported(tctx
, tree
, tmp_ctx
, &src_h
,
6172 FILE_SUPPORTS_BLOCK_REFCOUNTING
, &ok
);
6173 torture_assert_ntstatus_ok(tctx
, status
, "SMB2_GETINFO_FS");
6175 smb2_util_close(tree
, src_h
);
6176 talloc_free(tmp_ctx
);
6177 torture_skip(tctx
, "block refcounting not supported\n");
6180 /* src and dest are the same file handle */
6181 ioctl
.smb2
.in
.file
.handle
= src_h
;
6183 /* 8K overlap between src and tgt */
6184 dup_ext_buf
.source_off
= 0;
6185 dup_ext_buf
.target_off
= 8192;
6186 dup_ext_buf
.byte_count
= 16384;
6188 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
6190 (ndr_push_flags_fn_t
)ndr_push_fsctl_dup_extents_to_file
);
6191 torture_assert_ndr_success(tctx
, ndr_ret
,
6192 "ndr_push_fsctl_dup_extents_to_file");
6194 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
6195 torture_assert_ntstatus_equal(tctx
, status
, NT_STATUS_NOT_SUPPORTED
,
6196 "FSCTL_DUP_EXTENTS_TO_FILE");
6198 /* the file size and data should match beforehand */
6200 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
6201 io
.generic
.in
.file
.handle
= src_h
;
6202 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
6203 torture_assert_ntstatus_ok(tctx
, status
, "getinfo");
6204 torture_assert_int_equal(tctx
, (int)io
.all_info2
.out
.size
,
6205 32768, "size after IO");
6207 ok
= check_pattern(tctx
, tree
, tmp_ctx
, src_h
, 0, 32768, 0);
6209 torture_fail(tctx
, "inconsistent file data");
6212 smb2_util_close(tree
, src_h
);
6213 talloc_free(tmp_ctx
);
6218 * The compression tests won't run against Windows servers yet - ReFS doesn't
6219 * (yet) offer support for compression.
6221 static bool test_ioctl_dup_extents_compressed_src(struct torture_context
*tctx
,
6222 struct smb2_tree
*tree
)
6224 struct smb2_handle src_h
;
6225 struct smb2_handle dest_h
;
6227 union smb_ioctl ioctl
;
6228 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
6229 struct fsctl_dup_extents_to_file dup_ext_buf
;
6230 enum ndr_err_code ndr_ret
;
6231 union smb_setfileinfo sinfo
;
6234 ok
= test_setup_dup_extents(tctx
, tree
, tmp_ctx
,
6235 &src_h
, 0, /* filled after compressed flag */
6236 SEC_RIGHTS_FILE_ALL
,
6238 SEC_RIGHTS_FILE_ALL
,
6242 torture_fail(tctx
, "setup dup extents error");
6245 status
= test_ioctl_fs_supported(tctx
, tree
, tmp_ctx
, &src_h
,
6246 FILE_SUPPORTS_BLOCK_REFCOUNTING
6247 | FILE_FILE_COMPRESSION
, &ok
);
6248 torture_assert_ntstatus_ok(tctx
, status
, "SMB2_GETINFO_FS");
6250 smb2_util_close(tree
, src_h
);
6251 smb2_util_close(tree
, dest_h
);
6252 talloc_free(tmp_ctx
);
6254 "block refcounting and compressed files not supported\n");
6257 /* set compressed flag on src */
6258 status
= test_ioctl_compress_set(tctx
, tmp_ctx
, tree
, src_h
,
6259 COMPRESSION_FORMAT_DEFAULT
);
6260 torture_assert_ntstatus_ok(tctx
, status
, "FSCTL_SET_COMPRESSION");
6262 ok
= write_pattern(tctx
, tree
, tmp_ctx
, src_h
, 0, 4096, 0);
6263 torture_assert(tctx
, ok
, "write pattern");
6267 sinfo
.end_of_file_info
.level
= RAW_SFILEINFO_END_OF_FILE_INFORMATION
;
6268 sinfo
.end_of_file_info
.in
.file
.handle
= dest_h
;
6269 sinfo
.end_of_file_info
.in
.size
= 4096;
6270 status
= smb2_setinfo_file(tree
, &sinfo
);
6271 torture_assert_ntstatus_ok(tctx
, status
, "smb2_setinfo_file");
6273 /* copy all src file data */
6274 dup_ext_buf
.source_off
= 0;
6275 dup_ext_buf
.target_off
= 0;
6276 dup_ext_buf
.byte_count
= 4096;
6278 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
6280 (ndr_push_flags_fn_t
)ndr_push_fsctl_dup_extents_to_file
);
6281 torture_assert_ndr_success(tctx
, ndr_ret
,
6282 "ndr_push_fsctl_dup_extents_to_file");
6284 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
6285 torture_assert_ntstatus_ok(tctx
, status
,
6286 "FSCTL_DUP_EXTENTS_TO_FILE");
6288 ok
= check_pattern(tctx
, tree
, tmp_ctx
, dest_h
, 0, 4096, 0);
6290 torture_fail(tctx
, "inconsistent file data");
6293 smb2_util_close(tree
, src_h
);
6294 smb2_util_close(tree
, dest_h
);
6295 talloc_free(tmp_ctx
);
6299 static bool test_ioctl_dup_extents_compressed_dest(struct torture_context
*tctx
,
6300 struct smb2_tree
*tree
)
6302 struct smb2_handle src_h
;
6303 struct smb2_handle dest_h
;
6305 union smb_ioctl ioctl
;
6306 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
6307 struct fsctl_dup_extents_to_file dup_ext_buf
;
6308 enum ndr_err_code ndr_ret
;
6309 union smb_setfileinfo sinfo
;
6312 ok
= test_setup_dup_extents(tctx
, tree
, tmp_ctx
,
6314 SEC_RIGHTS_FILE_ALL
,
6316 SEC_RIGHTS_FILE_ALL
,
6320 torture_fail(tctx
, "setup dup extents error");
6323 status
= test_ioctl_fs_supported(tctx
, tree
, tmp_ctx
, &src_h
,
6324 FILE_SUPPORTS_BLOCK_REFCOUNTING
6325 | FILE_FILE_COMPRESSION
, &ok
);
6326 torture_assert_ntstatus_ok(tctx
, status
, "SMB2_GETINFO_FS");
6328 smb2_util_close(tree
, src_h
);
6329 smb2_util_close(tree
, dest_h
);
6330 talloc_free(tmp_ctx
);
6332 "block refcounting and compressed files not supported\n");
6335 /* set compressed flag on dest */
6336 status
= test_ioctl_compress_set(tctx
, tmp_ctx
, tree
, dest_h
,
6337 COMPRESSION_FORMAT_DEFAULT
);
6338 torture_assert_ntstatus_ok(tctx
, status
, "FSCTL_SET_COMPRESSION");
6342 sinfo
.end_of_file_info
.level
= RAW_SFILEINFO_END_OF_FILE_INFORMATION
;
6343 sinfo
.end_of_file_info
.in
.file
.handle
= dest_h
;
6344 sinfo
.end_of_file_info
.in
.size
= 4096;
6345 status
= smb2_setinfo_file(tree
, &sinfo
);
6346 torture_assert_ntstatus_ok(tctx
, status
, "smb2_setinfo_file");
6348 /* copy all src file data */
6349 dup_ext_buf
.source_off
= 0;
6350 dup_ext_buf
.target_off
= 0;
6351 dup_ext_buf
.byte_count
= 4096;
6353 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
6355 (ndr_push_flags_fn_t
)ndr_push_fsctl_dup_extents_to_file
);
6356 torture_assert_ndr_success(tctx
, ndr_ret
,
6357 "ndr_push_fsctl_dup_extents_to_file");
6359 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
6360 torture_assert_ntstatus_ok(tctx
, status
,
6361 "FSCTL_DUP_EXTENTS_TO_FILE");
6363 ok
= check_pattern(tctx
, tree
, tmp_ctx
, dest_h
, 0, 4096, 0);
6365 torture_fail(tctx
, "inconsistent file data");
6368 smb2_util_close(tree
, src_h
);
6369 smb2_util_close(tree
, dest_h
);
6370 talloc_free(tmp_ctx
);
6374 static bool test_ioctl_dup_extents_bad_handle(struct torture_context
*tctx
,
6375 struct smb2_tree
*tree
)
6377 struct smb2_handle src_h
;
6378 struct smb2_handle dest_h
;
6379 struct smb2_handle bogus_h
;
6381 union smb_ioctl ioctl
;
6382 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
6383 struct fsctl_dup_extents_to_file dup_ext_buf
;
6384 enum ndr_err_code ndr_ret
;
6387 ok
= test_setup_dup_extents(tctx
, tree
, tmp_ctx
,
6388 &src_h
, 32768, /* fill 32768 byte src file */
6389 SEC_RIGHTS_FILE_ALL
,
6391 SEC_RIGHTS_FILE_ALL
,
6395 torture_fail(tctx
, "setup dup extents error");
6398 status
= test_ioctl_fs_supported(tctx
, tree
, tmp_ctx
, &src_h
,
6399 FILE_SUPPORTS_BLOCK_REFCOUNTING
, &ok
);
6400 torture_assert_ntstatus_ok(tctx
, status
, "SMB2_GETINFO_FS");
6402 smb2_util_close(tree
, src_h
);
6403 smb2_util_close(tree
, dest_h
);
6404 talloc_free(tmp_ctx
);
6405 torture_skip(tctx
, "block refcounting not supported\n");
6408 /* open and close a file, keeping the handle as now a "bogus" handle */
6409 ok
= test_setup_create_fill(tctx
, tree
, tmp_ctx
, "bogus_file",
6410 &bogus_h
, 0, SEC_RIGHTS_FILE_ALL
,
6411 FILE_ATTRIBUTE_NORMAL
);
6412 torture_assert(tctx
, ok
, "bogus file create fill");
6413 smb2_util_close(tree
, bogus_h
);
6415 /* bogus dest file handle */
6416 ioctl
.smb2
.in
.file
.handle
= bogus_h
;
6418 dup_ext_buf
.source_off
= 0;
6419 dup_ext_buf
.target_off
= 0;
6420 dup_ext_buf
.byte_count
= 32768;
6422 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
6424 (ndr_push_flags_fn_t
)ndr_push_fsctl_dup_extents_to_file
);
6425 torture_assert_ndr_success(tctx
, ndr_ret
,
6426 "ndr_push_fsctl_dup_extents_to_file");
6428 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
6429 torture_assert_ntstatus_equal(tctx
, status
, NT_STATUS_FILE_CLOSED
,
6430 "FSCTL_DUP_EXTENTS_TO_FILE");
6432 ok
= check_pattern(tctx
, tree
, tmp_ctx
, src_h
, 0, 32768, 0);
6434 torture_fail(tctx
, "inconsistent file data");
6436 ok
= check_pattern(tctx
, tree
, tmp_ctx
, dest_h
, 0, 32768, 0);
6438 torture_fail(tctx
, "inconsistent file data");
6441 /* reinstate dest, add bogus src file handle */
6442 ioctl
.smb2
.in
.file
.handle
= dest_h
;
6443 smb2_push_handle(dup_ext_buf
.source_fid
, &bogus_h
);
6445 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
6447 (ndr_push_flags_fn_t
)ndr_push_fsctl_dup_extents_to_file
);
6448 torture_assert_ndr_success(tctx
, ndr_ret
,
6449 "ndr_push_fsctl_dup_extents_to_file");
6451 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
6452 torture_assert_ntstatus_equal(tctx
, status
, NT_STATUS_INVALID_HANDLE
,
6453 "FSCTL_DUP_EXTENTS_TO_FILE");
6455 ok
= check_pattern(tctx
, tree
, tmp_ctx
, src_h
, 0, 32768, 0);
6457 torture_fail(tctx
, "inconsistent file data");
6459 ok
= check_pattern(tctx
, tree
, tmp_ctx
, dest_h
, 0, 32768, 0);
6461 torture_fail(tctx
, "inconsistent file data");
6464 smb2_util_close(tree
, src_h
);
6465 smb2_util_close(tree
, dest_h
);
6466 talloc_free(tmp_ctx
);
6470 static bool test_ioctl_dup_extents_src_lck(struct torture_context
*tctx
,
6471 struct smb2_tree
*tree
)
6473 struct smb2_handle src_h
;
6474 struct smb2_handle src_h2
;
6475 struct smb2_handle dest_h
;
6477 union smb_ioctl ioctl
;
6478 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
6479 struct fsctl_dup_extents_to_file dup_ext_buf
;
6480 enum ndr_err_code ndr_ret
;
6482 struct smb2_lock lck
;
6483 struct smb2_lock_element el
[1];
6485 ok
= test_setup_dup_extents(tctx
, tree
, tmp_ctx
,
6486 &src_h
, 32768, /* fill 32768 byte src file */
6487 SEC_RIGHTS_FILE_ALL
,
6489 SEC_RIGHTS_FILE_ALL
,
6493 torture_fail(tctx
, "setup dup extents error");
6496 status
= test_ioctl_fs_supported(tctx
, tree
, tmp_ctx
, &src_h
,
6497 FILE_SUPPORTS_BLOCK_REFCOUNTING
, &ok
);
6498 torture_assert_ntstatus_ok(tctx
, status
, "SMB2_GETINFO_FS");
6500 smb2_util_close(tree
, src_h
);
6501 smb2_util_close(tree
, dest_h
);
6502 talloc_free(tmp_ctx
);
6503 torture_skip(tctx
, "block refcounting not supported\n");
6506 /* dest pattern is different to src */
6507 ok
= write_pattern(tctx
, tree
, tmp_ctx
, dest_h
, 0, 32768, 32768);
6508 torture_assert(tctx
, ok
, "write pattern");
6510 /* setup dup ext req, values used for locking */
6511 dup_ext_buf
.source_off
= 0;
6512 dup_ext_buf
.target_off
= 0;
6513 dup_ext_buf
.byte_count
= 32768;
6515 /* open and lock the dup extents src file */
6516 status
= torture_smb2_testfile(tree
, FNAME
, &src_h2
);
6517 torture_assert_ntstatus_ok(tctx
, status
, "2nd src open");
6519 lck
.in
.lock_count
= 0x0001;
6520 lck
.in
.lock_sequence
= 0x00000000;
6521 lck
.in
.file
.handle
= src_h2
;
6523 el
[0].offset
= dup_ext_buf
.source_off
;
6524 el
[0].length
= dup_ext_buf
.byte_count
;
6526 el
[0].flags
= SMB2_LOCK_FLAG_EXCLUSIVE
;
6528 status
= smb2_lock(tree
, &lck
);
6529 torture_assert_ntstatus_ok(tctx
, status
, "lock");
6531 status
= smb2_util_write(tree
, src_h
,
6532 "conflicted", 0, sizeof("conflicted"));
6533 torture_assert_ntstatus_equal(tctx
, status
,
6534 NT_STATUS_FILE_LOCK_CONFLICT
, "file write");
6536 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
6538 (ndr_push_flags_fn_t
)ndr_push_fsctl_dup_extents_to_file
);
6539 torture_assert_ndr_success(tctx
, ndr_ret
,
6540 "ndr_push_fsctl_dup_extents_to_file");
6543 * In contrast to copy-chunk, dup extents doesn't cause a lock conflict
6546 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
6547 torture_assert_ntstatus_ok(tctx
, status
, "FSCTL_DUP_EXTENTS_TO_FILE");
6549 ok
= check_pattern(tctx
, tree
, tmp_ctx
, dest_h
, 0, 32768, 0);
6551 torture_fail(tctx
, "inconsistent file data");
6554 lck
.in
.lock_count
= 0x0001;
6555 lck
.in
.lock_sequence
= 0x00000001;
6556 lck
.in
.file
.handle
= src_h2
;
6558 el
[0].offset
= dup_ext_buf
.source_off
;
6559 el
[0].length
= dup_ext_buf
.byte_count
;
6561 el
[0].flags
= SMB2_LOCK_FLAG_UNLOCK
;
6562 status
= smb2_lock(tree
, &lck
);
6563 torture_assert_ntstatus_ok(tctx
, status
, "unlock");
6565 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
6566 torture_assert_ntstatus_ok(tctx
, status
,
6567 "FSCTL_DUP_EXTENTS_TO_FILE unlocked");
6569 ok
= check_pattern(tctx
, tree
, tmp_ctx
, dest_h
, 0, 32768, 0);
6571 torture_fail(tctx
, "inconsistent file data");
6574 smb2_util_close(tree
, src_h2
);
6575 smb2_util_close(tree
, src_h
);
6576 smb2_util_close(tree
, dest_h
);
6577 talloc_free(tmp_ctx
);
6581 static bool test_ioctl_dup_extents_dest_lck(struct torture_context
*tctx
,
6582 struct smb2_tree
*tree
)
6584 struct smb2_handle src_h
;
6585 struct smb2_handle dest_h
;
6586 struct smb2_handle dest_h2
;
6588 union smb_ioctl ioctl
;
6589 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
6590 struct fsctl_dup_extents_to_file dup_ext_buf
;
6591 enum ndr_err_code ndr_ret
;
6593 struct smb2_lock lck
;
6594 struct smb2_lock_element el
[1];
6596 ok
= test_setup_dup_extents(tctx
, tree
, tmp_ctx
,
6597 &src_h
, 32768, /* fill 32768 byte src file */
6598 SEC_RIGHTS_FILE_ALL
,
6600 SEC_RIGHTS_FILE_ALL
,
6604 torture_fail(tctx
, "setup dup extents error");
6607 status
= test_ioctl_fs_supported(tctx
, tree
, tmp_ctx
, &src_h
,
6608 FILE_SUPPORTS_BLOCK_REFCOUNTING
, &ok
);
6609 torture_assert_ntstatus_ok(tctx
, status
, "SMB2_GETINFO_FS");
6611 smb2_util_close(tree
, src_h
);
6612 smb2_util_close(tree
, dest_h
);
6613 talloc_free(tmp_ctx
);
6614 torture_skip(tctx
, "block refcounting not supported\n");
6617 /* dest pattern is different to src */
6618 ok
= write_pattern(tctx
, tree
, tmp_ctx
, dest_h
, 0, 32768, 32768);
6619 torture_assert(tctx
, ok
, "write pattern");
6621 /* setup dup ext req, values used for locking */
6622 dup_ext_buf
.source_off
= 0;
6623 dup_ext_buf
.target_off
= 0;
6624 dup_ext_buf
.byte_count
= 32768;
6626 /* open and lock the dup extents dest file */
6627 status
= torture_smb2_testfile(tree
, FNAME2
, &dest_h2
);
6628 torture_assert_ntstatus_ok(tctx
, status
, "2nd src open");
6630 lck
.in
.lock_count
= 0x0001;
6631 lck
.in
.lock_sequence
= 0x00000000;
6632 lck
.in
.file
.handle
= dest_h2
;
6634 el
[0].offset
= dup_ext_buf
.source_off
;
6635 el
[0].length
= dup_ext_buf
.byte_count
;
6637 el
[0].flags
= SMB2_LOCK_FLAG_EXCLUSIVE
;
6639 status
= smb2_lock(tree
, &lck
);
6640 torture_assert_ntstatus_ok(tctx
, status
, "lock");
6642 status
= smb2_util_write(tree
, dest_h
,
6643 "conflicted", 0, sizeof("conflicted"));
6644 torture_assert_ntstatus_equal(tctx
, status
,
6645 NT_STATUS_FILE_LOCK_CONFLICT
, "file write");
6647 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
6649 (ndr_push_flags_fn_t
)ndr_push_fsctl_dup_extents_to_file
);
6650 torture_assert_ndr_success(tctx
, ndr_ret
,
6651 "ndr_push_fsctl_dup_extents_to_file");
6654 * In contrast to copy-chunk, dup extents doesn't cause a lock conflict
6657 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
6658 torture_assert_ntstatus_ok(tctx
, status
, "FSCTL_DUP_EXTENTS_TO_FILE");
6660 lck
.in
.lock_count
= 0x0001;
6661 lck
.in
.lock_sequence
= 0x00000001;
6662 lck
.in
.file
.handle
= dest_h2
;
6664 el
[0].offset
= dup_ext_buf
.source_off
;
6665 el
[0].length
= dup_ext_buf
.byte_count
;
6667 el
[0].flags
= SMB2_LOCK_FLAG_UNLOCK
;
6668 status
= smb2_lock(tree
, &lck
);
6669 torture_assert_ntstatus_ok(tctx
, status
, "unlock");
6671 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
6672 torture_assert_ntstatus_ok(tctx
, status
,
6673 "FSCTL_DUP_EXTENTS_TO_FILE unlocked");
6675 ok
= check_pattern(tctx
, tree
, tmp_ctx
, dest_h
, 0, 32768, 0);
6677 torture_fail(tctx
, "inconsistent file data");
6680 smb2_util_close(tree
, src_h
);
6681 smb2_util_close(tree
, dest_h
);
6682 smb2_util_close(tree
, dest_h2
);
6683 talloc_free(tmp_ctx
);
6688 * testing of SMB2 ioctls
6690 struct torture_suite
*torture_smb2_ioctl_init(TALLOC_CTX
*ctx
)
6692 struct torture_suite
*suite
= torture_suite_create(ctx
, "ioctl");
6694 torture_suite_add_1smb2_test(suite
, "shadow_copy",
6695 test_ioctl_get_shadow_copy
);
6696 torture_suite_add_1smb2_test(suite
, "req_resume_key",
6697 test_ioctl_req_resume_key
);
6698 torture_suite_add_1smb2_test(suite
, "req_two_resume_keys",
6699 test_ioctl_req_two_resume_keys
);
6700 torture_suite_add_1smb2_test(suite
, "copy_chunk_simple",
6701 test_ioctl_copy_chunk_simple
);
6702 torture_suite_add_1smb2_test(suite
, "copy_chunk_multi",
6703 test_ioctl_copy_chunk_multi
);
6704 torture_suite_add_1smb2_test(suite
, "copy_chunk_tiny",
6705 test_ioctl_copy_chunk_tiny
);
6706 torture_suite_add_1smb2_test(suite
, "copy_chunk_overwrite",
6707 test_ioctl_copy_chunk_over
);
6708 torture_suite_add_1smb2_test(suite
, "copy_chunk_append",
6709 test_ioctl_copy_chunk_append
);
6710 torture_suite_add_1smb2_test(suite
, "copy_chunk_limits",
6711 test_ioctl_copy_chunk_limits
);
6712 torture_suite_add_1smb2_test(suite
, "copy_chunk_src_lock",
6713 test_ioctl_copy_chunk_src_lck
);
6714 torture_suite_add_1smb2_test(suite
, "copy_chunk_dest_lock",
6715 test_ioctl_copy_chunk_dest_lck
);
6716 torture_suite_add_1smb2_test(suite
, "copy_chunk_bad_key",
6717 test_ioctl_copy_chunk_bad_key
);
6718 torture_suite_add_1smb2_test(suite
, "copy_chunk_src_is_dest",
6719 test_ioctl_copy_chunk_src_is_dest
);
6720 torture_suite_add_1smb2_test(suite
, "copy_chunk_src_is_dest_overlap",
6721 test_ioctl_copy_chunk_src_is_dest_overlap
);
6722 torture_suite_add_1smb2_test(suite
, "copy_chunk_bad_access",
6723 test_ioctl_copy_chunk_bad_access
);
6724 torture_suite_add_1smb2_test(suite
, "copy_chunk_write_access",
6725 test_ioctl_copy_chunk_write_access
);
6726 torture_suite_add_1smb2_test(suite
, "copy_chunk_src_exceed",
6727 test_ioctl_copy_chunk_src_exceed
);
6728 torture_suite_add_1smb2_test(suite
, "copy_chunk_src_exceed_multi",
6729 test_ioctl_copy_chunk_src_exceed_multi
);
6730 torture_suite_add_1smb2_test(suite
, "copy_chunk_sparse_dest",
6731 test_ioctl_copy_chunk_sparse_dest
);
6732 torture_suite_add_1smb2_test(suite
, "copy_chunk_max_output_sz",
6733 test_ioctl_copy_chunk_max_output_sz
);
6734 torture_suite_add_1smb2_test(suite
, "copy_chunk_zero_length",
6735 test_ioctl_copy_chunk_zero_length
);
6736 torture_suite_add_1smb2_test(suite
, "copy-chunk streams",
6737 test_copy_chunk_streams
);
6738 torture_suite_add_1smb2_test(suite
, "copy_chunk_across_shares",
6739 test_copy_chunk_across_shares
);
6740 torture_suite_add_1smb2_test(suite
, "copy_chunk_across_shares2",
6741 test_copy_chunk_across_shares2
);
6742 torture_suite_add_1smb2_test(suite
, "copy_chunk_across_shares3",
6743 test_copy_chunk_across_shares3
);
6744 torture_suite_add_1smb2_test(suite
, "compress_file_flag",
6745 test_ioctl_compress_file_flag
);
6746 torture_suite_add_1smb2_test(suite
, "compress_dir_inherit",
6747 test_ioctl_compress_dir_inherit
);
6748 torture_suite_add_1smb2_test(suite
, "compress_invalid_format",
6749 test_ioctl_compress_invalid_format
);
6750 torture_suite_add_1smb2_test(suite
, "compress_invalid_buf",
6751 test_ioctl_compress_invalid_buf
);
6752 torture_suite_add_1smb2_test(suite
, "compress_query_file_attr",
6753 test_ioctl_compress_query_file_attr
);
6754 torture_suite_add_1smb2_test(suite
, "compress_create_with_attr",
6755 test_ioctl_compress_create_with_attr
);
6756 torture_suite_add_1smb2_test(suite
, "compress_inherit_disable",
6757 test_ioctl_compress_inherit_disable
);
6758 torture_suite_add_1smb2_test(suite
, "compress_set_file_attr",
6759 test_ioctl_compress_set_file_attr
);
6760 torture_suite_add_1smb2_test(suite
, "compress_perms",
6761 test_ioctl_compress_perms
);
6762 torture_suite_add_1smb2_test(suite
, "compress_notsup_get",
6763 test_ioctl_compress_notsup_get
);
6764 torture_suite_add_1smb2_test(suite
, "compress_notsup_set",
6765 test_ioctl_compress_notsup_set
);
6766 torture_suite_add_1smb2_test(suite
, "network_interface_info",
6767 test_ioctl_network_interface_info
);
6768 torture_suite_add_1smb2_test(suite
, "sparse_file_flag",
6769 test_ioctl_sparse_file_flag
);
6770 torture_suite_add_1smb2_test(suite
, "sparse_file_attr",
6771 test_ioctl_sparse_file_attr
);
6772 torture_suite_add_1smb2_test(suite
, "sparse_dir_flag",
6773 test_ioctl_sparse_dir_flag
);
6774 torture_suite_add_1smb2_test(suite
, "sparse_set_nobuf",
6775 test_ioctl_sparse_set_nobuf
);
6776 torture_suite_add_1smb2_test(suite
, "sparse_set_oversize",
6777 test_ioctl_sparse_set_oversize
);
6778 torture_suite_add_1smb2_test(suite
, "sparse_qar",
6779 test_ioctl_sparse_qar
);
6780 torture_suite_add_1smb2_test(suite
, "sparse_qar_malformed",
6781 test_ioctl_sparse_qar_malformed
);
6782 torture_suite_add_1smb2_test(suite
, "sparse_punch",
6783 test_ioctl_sparse_punch
);
6784 torture_suite_add_1smb2_test(suite
, "sparse_hole_dealloc",
6785 test_ioctl_sparse_hole_dealloc
);
6786 torture_suite_add_1smb2_test(suite
, "sparse_compressed",
6787 test_ioctl_sparse_compressed
);
6788 torture_suite_add_1smb2_test(suite
, "sparse_copy_chunk",
6789 test_ioctl_sparse_copy_chunk
);
6790 torture_suite_add_1smb2_test(suite
, "sparse_punch_invalid",
6791 test_ioctl_sparse_punch_invalid
);
6792 torture_suite_add_1smb2_test(suite
, "sparse_perms",
6793 test_ioctl_sparse_perms
);
6794 torture_suite_add_1smb2_test(suite
, "sparse_lock",
6795 test_ioctl_sparse_lck
);
6796 torture_suite_add_1smb2_test(suite
, "sparse_qar_ob1",
6797 test_ioctl_sparse_qar_ob1
);
6798 torture_suite_add_1smb2_test(suite
, "sparse_qar_multi",
6799 test_ioctl_sparse_qar_multi
);
6800 torture_suite_add_1smb2_test(suite
, "sparse_qar_overflow",
6801 test_ioctl_sparse_qar_overflow
);
6802 torture_suite_add_1smb2_test(suite
, "trim_simple",
6803 test_ioctl_trim_simple
);
6804 torture_suite_add_1smb2_test(suite
, "dup_extents_simple",
6805 test_ioctl_dup_extents_simple
);
6806 torture_suite_add_1smb2_test(suite
, "dup_extents_len_beyond_dest",
6807 test_ioctl_dup_extents_len_beyond_dest
);
6808 torture_suite_add_1smb2_test(suite
, "dup_extents_len_beyond_src",
6809 test_ioctl_dup_extents_len_beyond_src
);
6810 torture_suite_add_1smb2_test(suite
, "dup_extents_len_zero",
6811 test_ioctl_dup_extents_len_zero
);
6812 torture_suite_add_1smb2_test(suite
, "dup_extents_sparse_src",
6813 test_ioctl_dup_extents_sparse_src
);
6814 torture_suite_add_1smb2_test(suite
, "dup_extents_sparse_dest",
6815 test_ioctl_dup_extents_sparse_dest
);
6816 torture_suite_add_1smb2_test(suite
, "dup_extents_sparse_both",
6817 test_ioctl_dup_extents_sparse_both
);
6818 torture_suite_add_1smb2_test(suite
, "dup_extents_src_is_dest",
6819 test_ioctl_dup_extents_src_is_dest
);
6820 torture_suite_add_1smb2_test(suite
, "dup_extents_src_is_dest_overlap",
6821 test_ioctl_dup_extents_src_is_dest_overlap
);
6822 torture_suite_add_1smb2_test(suite
, "dup_extents_compressed_src",
6823 test_ioctl_dup_extents_compressed_src
);
6824 torture_suite_add_1smb2_test(suite
, "dup_extents_compressed_dest",
6825 test_ioctl_dup_extents_compressed_dest
);
6826 torture_suite_add_1smb2_test(suite
, "dup_extents_bad_handle",
6827 test_ioctl_dup_extents_bad_handle
);
6828 torture_suite_add_1smb2_test(suite
, "dup_extents_src_lock",
6829 test_ioctl_dup_extents_src_lck
);
6830 torture_suite_add_1smb2_test(suite
, "dup_extents_dest_lock",
6831 test_ioctl_dup_extents_dest_lck
);
6833 suite
->description
= talloc_strdup(suite
, "SMB2-IOCTL tests");