2 Unix SMB/CIFS implementation.
4 test suite for SMB2 ioctl operations
6 Copyright (C) David Disseldorp 2011-2016
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "librpc/gen_ndr/security.h"
24 #include "libcli/smb2/smb2.h"
25 #include "libcli/smb2/smb2_calls.h"
26 #include "torture/torture.h"
27 #include "torture/smb2/proto.h"
28 #include "../libcli/smb/smbXcli_base.h"
29 #include "librpc/gen_ndr/ndr_ioctl.h"
31 #define FNAME "testfsctl.dat"
32 #define FNAME2 "testfsctl2.dat"
33 #define DNAME "testfsctl_dir"
36 basic testing of SMB2 shadow copy calls
38 static bool test_ioctl_get_shadow_copy(struct torture_context
*torture
,
39 struct smb2_tree
*tree
)
44 union smb_ioctl ioctl
;
45 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
47 smb2_util_unlink(tree
, FNAME
);
49 status
= torture_smb2_testfile(tree
, FNAME
, &h
);
50 torture_assert_ntstatus_ok(torture
, status
, "create write");
53 status
= smb2_util_write(tree
, h
, buf
, 0, ARRAY_SIZE(buf
));
54 torture_assert_ntstatus_ok(torture
, status
, "write");
57 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
58 ioctl
.smb2
.in
.file
.handle
= h
;
59 ioctl
.smb2
.in
.function
= FSCTL_SRV_ENUM_SNAPS
;
60 ioctl
.smb2
.in
.max_response_size
= 16;
61 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
63 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
64 if (NT_STATUS_EQUAL(status
, NT_STATUS_NOT_SUPPORTED
)
65 || NT_STATUS_EQUAL(status
, NT_STATUS_INVALID_DEVICE_REQUEST
)) {
66 torture_skip(torture
, "FSCTL_SRV_ENUM_SNAPS not supported\n");
68 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_ENUM_SNAPS");
74 basic testing of the SMB2 server side copy ioctls
76 static bool test_ioctl_req_resume_key(struct torture_context
*torture
,
77 struct smb2_tree
*tree
)
82 union smb_ioctl ioctl
;
83 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
84 struct req_resume_key_rsp res_key
;
85 enum ndr_err_code ndr_ret
;
87 smb2_util_unlink(tree
, FNAME
);
89 status
= torture_smb2_testfile(tree
, FNAME
, &h
);
90 torture_assert_ntstatus_ok(torture
, status
, "create write");
93 status
= smb2_util_write(tree
, h
, buf
, 0, ARRAY_SIZE(buf
));
94 torture_assert_ntstatus_ok(torture
, status
, "write");
97 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
98 ioctl
.smb2
.in
.file
.handle
= h
;
99 ioctl
.smb2
.in
.function
= FSCTL_SRV_REQUEST_RESUME_KEY
;
100 ioctl
.smb2
.in
.max_response_size
= 32;
101 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
103 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
104 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_REQUEST_RESUME_KEY");
106 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
, &res_key
,
107 (ndr_pull_flags_fn_t
)ndr_pull_req_resume_key_rsp
);
108 torture_assert_ndr_success(torture
, ndr_ret
,
109 "ndr_pull_req_resume_key_rsp");
111 ndr_print_debug((ndr_print_fn_t
)ndr_print_req_resume_key_rsp
, "yo", &res_key
);
113 talloc_free(tmp_ctx
);
117 static uint64_t patt_hash(uint64_t off
)
122 static bool write_pattern(struct torture_context
*torture
,
123 struct smb2_tree
*tree
, TALLOC_CTX
*mem_ctx
,
124 struct smb2_handle h
, uint64_t off
, uint64_t len
,
130 uint64_t io_sz
= MIN(1024 * 64, len
);
136 torture_assert(torture
, (len
% 8) == 0, "invalid write len");
138 buf
= talloc_zero_size(mem_ctx
, io_sz
);
139 torture_assert(torture
, (buf
!= NULL
), "no memory for file data buf");
142 for (i
= 0; i
<= io_sz
- 8; i
+= 8) {
143 SBVAL(buf
, i
, patt_hash(patt_off
));
147 status
= smb2_util_write(tree
, h
,
149 torture_assert_ntstatus_ok(torture
, status
, "file write");
160 static bool check_pattern(struct torture_context
*torture
,
161 struct smb2_tree
*tree
, TALLOC_CTX
*mem_ctx
,
162 struct smb2_handle h
, uint64_t off
, uint64_t len
,
169 torture_assert(torture
, (len
% 8) == 0, "invalid read len");
175 uint64_t io_sz
= MIN(1024 * 64, len
);
178 r
.in
.file
.handle
= h
;
181 status
= smb2_read(tree
, mem_ctx
, &r
);
182 torture_assert_ntstatus_ok(torture
, status
, "read");
184 torture_assert_u64_equal(torture
, r
.out
.data
.length
, io_sz
,
185 "read data len mismatch");
187 for (i
= 0; i
<= io_sz
- 8; i
+= 8, patt_off
+= 8) {
188 uint64_t data
= BVAL(r
.out
.data
.data
, i
);
189 torture_assert_u64_equal(torture
, data
, patt_hash(patt_off
),
190 talloc_asprintf(torture
, "read data "
191 "pattern bad at %llu\n",
192 (unsigned long long)off
+ i
));
194 talloc_free(r
.out
.data
.data
);
202 static bool check_zero(struct torture_context
*torture
,
203 struct smb2_tree
*tree
, TALLOC_CTX
*mem_ctx
,
204 struct smb2_handle h
, uint64_t off
, uint64_t len
)
215 r
.in
.file
.handle
= h
;
218 status
= smb2_read(tree
, mem_ctx
, &r
);
219 torture_assert_ntstatus_ok(torture
, status
, "read");
221 torture_assert_u64_equal(torture
, r
.out
.data
.length
, len
,
222 "read data len mismatch");
224 for (i
= 0; i
<= len
- 8; i
+= 8) {
225 uint64_t data
= BVAL(r
.out
.data
.data
, i
);
226 torture_assert_u64_equal(torture
, data
, 0,
227 talloc_asprintf(mem_ctx
, "read zero "
229 (unsigned long long)i
));
232 talloc_free(r
.out
.data
.data
);
236 static bool test_setup_open(struct torture_context
*torture
,
237 struct smb2_tree
*tree
, TALLOC_CTX
*mem_ctx
,
239 struct smb2_handle
*fh
,
240 uint32_t desired_access
,
241 uint32_t file_attributes
)
243 struct smb2_create io
;
247 io
.in
.desired_access
= desired_access
;
248 io
.in
.file_attributes
= file_attributes
;
249 io
.in
.create_disposition
= NTCREATEX_DISP_OPEN_IF
;
251 NTCREATEX_SHARE_ACCESS_DELETE
|
252 NTCREATEX_SHARE_ACCESS_READ
|
253 NTCREATEX_SHARE_ACCESS_WRITE
;
254 if (file_attributes
& FILE_ATTRIBUTE_DIRECTORY
) {
255 io
.in
.create_options
= NTCREATEX_OPTIONS_DIRECTORY
;
259 status
= smb2_create(tree
, mem_ctx
, &io
);
260 torture_assert_ntstatus_ok(torture
, status
, "file create");
262 *fh
= io
.out
.file
.handle
;
267 static bool test_setup_create_fill(struct torture_context
*torture
,
268 struct smb2_tree
*tree
, TALLOC_CTX
*mem_ctx
,
270 struct smb2_handle
*fh
,
272 uint32_t desired_access
,
273 uint32_t file_attributes
)
276 uint32_t initial_access
= desired_access
;
279 initial_access
|= SEC_FILE_APPEND_DATA
;
282 smb2_util_unlink(tree
, fname
);
284 ok
= test_setup_open(torture
, tree
, mem_ctx
,
289 torture_assert(torture
, ok
, "file create");
292 ok
= write_pattern(torture
, tree
, mem_ctx
, *fh
, 0, size
, 0);
293 torture_assert(torture
, ok
, "write pattern");
296 if (initial_access
!= desired_access
) {
297 smb2_util_close(tree
, *fh
);
298 ok
= test_setup_open(torture
, tree
, mem_ctx
,
303 torture_assert(torture
, ok
, "file open");
309 static bool test_setup_copy_chunk(struct torture_context
*torture
,
310 struct smb2_tree
*src_tree
,
311 struct smb2_tree
*dst_tree
,
314 const char *src_name
,
315 struct smb2_handle
*src_h
,
317 uint32_t src_desired_access
,
318 const char *dst_name
,
319 struct smb2_handle
*dest_h
,
321 uint32_t dest_desired_access
,
322 struct srv_copychunk_copy
*cc_copy
,
323 union smb_ioctl
*ioctl
)
325 struct req_resume_key_rsp res_key
;
328 enum ndr_err_code ndr_ret
;
330 ok
= test_setup_create_fill(torture
, src_tree
, mem_ctx
, src_name
,
331 src_h
, src_size
, src_desired_access
,
332 FILE_ATTRIBUTE_NORMAL
);
333 torture_assert(torture
, ok
, "src file create fill");
335 ok
= test_setup_create_fill(torture
, dst_tree
, mem_ctx
, dst_name
,
336 dest_h
, dest_size
, dest_desired_access
,
337 FILE_ATTRIBUTE_NORMAL
);
338 torture_assert(torture
, ok
, "dest file create fill");
340 ZERO_STRUCTPN(ioctl
);
341 ioctl
->smb2
.level
= RAW_IOCTL_SMB2
;
342 ioctl
->smb2
.in
.file
.handle
= *src_h
;
343 ioctl
->smb2
.in
.function
= FSCTL_SRV_REQUEST_RESUME_KEY
;
344 /* Allow for Key + ContextLength + Context */
345 ioctl
->smb2
.in
.max_response_size
= 32;
346 ioctl
->smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
348 status
= smb2_ioctl(src_tree
, mem_ctx
, &ioctl
->smb2
);
349 torture_assert_ntstatus_ok(torture
, status
,
350 "FSCTL_SRV_REQUEST_RESUME_KEY");
352 ndr_ret
= ndr_pull_struct_blob(&ioctl
->smb2
.out
.out
, mem_ctx
, &res_key
,
353 (ndr_pull_flags_fn_t
)ndr_pull_req_resume_key_rsp
);
355 torture_assert_ndr_success(torture
, ndr_ret
,
356 "ndr_pull_req_resume_key_rsp");
358 ZERO_STRUCTPN(ioctl
);
359 ioctl
->smb2
.level
= RAW_IOCTL_SMB2
;
360 ioctl
->smb2
.in
.file
.handle
= *dest_h
;
361 ioctl
->smb2
.in
.function
= FSCTL_SRV_COPYCHUNK
;
362 ioctl
->smb2
.in
.max_response_size
= sizeof(struct srv_copychunk_rsp
);
363 ioctl
->smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
365 ZERO_STRUCTPN(cc_copy
);
366 memcpy(cc_copy
->source_key
, res_key
.resume_key
, ARRAY_SIZE(cc_copy
->source_key
));
367 cc_copy
->chunk_count
= nchunks
;
368 cc_copy
->chunks
= talloc_zero_array(mem_ctx
, struct srv_copychunk
, nchunks
);
369 torture_assert(torture
, (cc_copy
->chunks
!= NULL
), "no memory for chunks");
375 static bool check_copy_chunk_rsp(struct torture_context
*torture
,
376 struct srv_copychunk_rsp
*cc_rsp
,
377 uint32_t ex_chunks_written
,
378 uint32_t ex_chunk_bytes_written
,
379 uint32_t ex_total_bytes_written
)
381 torture_assert_int_equal(torture
, cc_rsp
->chunks_written
,
382 ex_chunks_written
, "num chunks");
383 torture_assert_int_equal(torture
, cc_rsp
->chunk_bytes_written
,
384 ex_chunk_bytes_written
, "chunk bytes written");
385 torture_assert_int_equal(torture
, cc_rsp
->total_bytes_written
,
386 ex_total_bytes_written
, "chunk total bytes");
390 static bool test_ioctl_copy_chunk_simple(struct torture_context
*torture
,
391 struct smb2_tree
*tree
)
393 struct smb2_handle src_h
;
394 struct smb2_handle dest_h
;
396 union smb_ioctl ioctl
;
397 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
398 struct srv_copychunk_copy cc_copy
;
399 struct srv_copychunk_rsp cc_rsp
;
400 enum ndr_err_code ndr_ret
;
403 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
406 &src_h
, 4096, /* fill 4096 byte src file */
409 &dest_h
, 0, /* 0 byte dest file */
414 torture_fail(torture
, "setup copy chunk error");
417 /* copy all src file data (via a single chunk desc) */
418 cc_copy
.chunks
[0].source_off
= 0;
419 cc_copy
.chunks
[0].target_off
= 0;
420 cc_copy
.chunks
[0].length
= 4096;
422 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
424 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
425 torture_assert_ndr_success(torture
, ndr_ret
,
426 "ndr_push_srv_copychunk_copy");
428 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
429 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_COPYCHUNK");
431 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
433 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
434 torture_assert_ndr_success(torture
, ndr_ret
,
435 "ndr_pull_srv_copychunk_rsp");
437 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
438 1, /* chunks written */
439 0, /* chunk bytes unsuccessfully written */
440 4096); /* total bytes written */
442 torture_fail(torture
, "bad copy chunk response data");
445 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 0, 4096, 0);
447 torture_fail(torture
, "inconsistent file data");
450 smb2_util_close(tree
, src_h
);
451 smb2_util_close(tree
, dest_h
);
452 talloc_free(tmp_ctx
);
456 static bool test_ioctl_copy_chunk_multi(struct torture_context
*torture
,
457 struct smb2_tree
*tree
)
459 struct smb2_handle src_h
;
460 struct smb2_handle dest_h
;
462 union smb_ioctl ioctl
;
463 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
464 struct srv_copychunk_copy cc_copy
;
465 struct srv_copychunk_rsp cc_rsp
;
466 enum ndr_err_code ndr_ret
;
469 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
472 &src_h
, 8192, /* src file */
475 &dest_h
, 0, /* dest file */
480 torture_fail(torture
, "setup copy chunk error");
483 /* copy all src file data via two chunks */
484 cc_copy
.chunks
[0].source_off
= 0;
485 cc_copy
.chunks
[0].target_off
= 0;
486 cc_copy
.chunks
[0].length
= 4096;
488 cc_copy
.chunks
[1].source_off
= 4096;
489 cc_copy
.chunks
[1].target_off
= 4096;
490 cc_copy
.chunks
[1].length
= 4096;
492 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
494 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
495 torture_assert_ndr_success(torture
, ndr_ret
,
496 "ndr_push_srv_copychunk_copy");
498 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
499 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_COPYCHUNK");
501 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
503 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
504 torture_assert_ndr_success(torture
, ndr_ret
,
505 "ndr_pull_srv_copychunk_rsp");
507 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
508 2, /* chunks written */
509 0, /* chunk bytes unsuccessfully written */
510 8192); /* total bytes written */
512 torture_fail(torture
, "bad copy chunk response data");
515 smb2_util_close(tree
, src_h
);
516 smb2_util_close(tree
, dest_h
);
517 talloc_free(tmp_ctx
);
521 static bool test_ioctl_copy_chunk_tiny(struct torture_context
*torture
,
522 struct smb2_tree
*tree
)
524 struct smb2_handle src_h
;
525 struct smb2_handle dest_h
;
527 union smb_ioctl ioctl
;
528 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
529 struct srv_copychunk_copy cc_copy
;
530 struct srv_copychunk_rsp cc_rsp
;
531 enum ndr_err_code ndr_ret
;
534 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
537 &src_h
, 96, /* src file */
540 &dest_h
, 0, /* dest file */
545 torture_fail(torture
, "setup copy chunk error");
548 /* copy all src file data via two chunks, sub block size chunks */
549 cc_copy
.chunks
[0].source_off
= 0;
550 cc_copy
.chunks
[0].target_off
= 0;
551 cc_copy
.chunks
[0].length
= 48;
553 cc_copy
.chunks
[1].source_off
= 48;
554 cc_copy
.chunks
[1].target_off
= 48;
555 cc_copy
.chunks
[1].length
= 48;
557 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
559 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
560 torture_assert_ndr_success(torture
, ndr_ret
,
561 "ndr_push_srv_copychunk_copy");
563 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
564 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_COPYCHUNK");
566 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
568 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
569 torture_assert_ndr_success(torture
, ndr_ret
,
570 "ndr_pull_srv_copychunk_rsp");
572 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
573 2, /* chunks written */
574 0, /* chunk bytes unsuccessfully written */
575 96); /* total bytes written */
577 torture_fail(torture
, "bad copy chunk response data");
580 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 0, 96, 0);
582 torture_fail(torture
, "inconsistent file data");
585 smb2_util_close(tree
, src_h
);
586 smb2_util_close(tree
, dest_h
);
587 talloc_free(tmp_ctx
);
591 static bool test_ioctl_copy_chunk_over(struct torture_context
*torture
,
592 struct smb2_tree
*tree
)
594 struct smb2_handle src_h
;
595 struct smb2_handle dest_h
;
597 union smb_ioctl ioctl
;
598 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
599 struct srv_copychunk_copy cc_copy
;
600 struct srv_copychunk_rsp cc_rsp
;
601 enum ndr_err_code ndr_ret
;
604 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
607 &src_h
, 8192, /* src file */
610 &dest_h
, 4096, /* dest file */
615 torture_fail(torture
, "setup copy chunk error");
618 /* first chunk overwrites existing dest data */
619 cc_copy
.chunks
[0].source_off
= 0;
620 cc_copy
.chunks
[0].target_off
= 0;
621 cc_copy
.chunks
[0].length
= 4096;
623 /* second chunk overwrites the first */
624 cc_copy
.chunks
[1].source_off
= 4096;
625 cc_copy
.chunks
[1].target_off
= 0;
626 cc_copy
.chunks
[1].length
= 4096;
628 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
630 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
631 torture_assert_ndr_success(torture
, ndr_ret
,
632 "ndr_push_srv_copychunk_copy");
634 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
635 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_COPYCHUNK");
637 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
639 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
640 torture_assert_ndr_success(torture
, ndr_ret
,
641 "ndr_pull_srv_copychunk_rsp");
643 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
644 2, /* chunks written */
645 0, /* chunk bytes unsuccessfully written */
646 8192); /* total bytes written */
648 torture_fail(torture
, "bad copy chunk response data");
651 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 0, 4096, 4096);
653 torture_fail(torture
, "inconsistent file data");
656 smb2_util_close(tree
, src_h
);
657 smb2_util_close(tree
, dest_h
);
658 talloc_free(tmp_ctx
);
662 static bool test_ioctl_copy_chunk_append(struct torture_context
*torture
,
663 struct smb2_tree
*tree
)
665 struct smb2_handle src_h
;
666 struct smb2_handle dest_h
;
668 union smb_ioctl ioctl
;
669 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
670 struct srv_copychunk_copy cc_copy
;
671 struct srv_copychunk_rsp cc_rsp
;
672 enum ndr_err_code ndr_ret
;
675 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
678 &src_h
, 4096, /* src file */
681 &dest_h
, 0, /* dest file */
686 torture_fail(torture
, "setup copy chunk error");
689 cc_copy
.chunks
[0].source_off
= 0;
690 cc_copy
.chunks
[0].target_off
= 0;
691 cc_copy
.chunks
[0].length
= 4096;
693 /* second chunk appends the same data to the first */
694 cc_copy
.chunks
[1].source_off
= 0;
695 cc_copy
.chunks
[1].target_off
= 4096;
696 cc_copy
.chunks
[1].length
= 4096;
698 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
700 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
701 torture_assert_ndr_success(torture
, ndr_ret
,
702 "ndr_push_srv_copychunk_copy");
704 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
705 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_COPYCHUNK");
707 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
709 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
710 torture_assert_ndr_success(torture
, ndr_ret
,
711 "ndr_pull_srv_copychunk_rsp");
713 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
714 2, /* chunks written */
715 0, /* chunk bytes unsuccessfully written */
716 8192); /* total bytes written */
718 torture_fail(torture
, "bad copy chunk response data");
721 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 0, 4096, 0);
723 torture_fail(torture
, "inconsistent file data");
726 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 4096, 4096, 0);
728 torture_fail(torture
, "inconsistent file data");
731 smb2_util_close(tree
, src_h
);
732 smb2_util_close(tree
, dest_h
);
733 talloc_free(tmp_ctx
);
737 static bool test_ioctl_copy_chunk_limits(struct torture_context
*torture
,
738 struct smb2_tree
*tree
)
740 struct smb2_handle src_h
;
741 struct smb2_handle dest_h
;
743 union smb_ioctl ioctl
;
744 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
745 struct srv_copychunk_copy cc_copy
;
746 struct srv_copychunk_rsp cc_rsp
;
747 enum ndr_err_code ndr_ret
;
750 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
753 &src_h
, 4096, /* src file */
756 &dest_h
, 0, /* dest file */
761 torture_fail(torture
, "setup copy chunk error");
764 /* send huge chunk length request */
765 cc_copy
.chunks
[0].source_off
= 0;
766 cc_copy
.chunks
[0].target_off
= 0;
767 cc_copy
.chunks
[0].length
= UINT_MAX
;
769 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
771 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
772 torture_assert_ndr_success(torture
, ndr_ret
, "marshalling request");
774 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
775 torture_assert_ntstatus_equal(torture
, status
,
776 NT_STATUS_INVALID_PARAMETER
,
777 "bad oversize chunk response");
779 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
781 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
782 torture_assert_ndr_success(torture
, ndr_ret
, "unmarshalling response");
784 torture_comment(torture
, "limit max chunks, got %u\n",
785 cc_rsp
.chunks_written
);
786 torture_comment(torture
, "limit max chunk len, got %u\n",
787 cc_rsp
.chunk_bytes_written
);
788 torture_comment(torture
, "limit max total bytes, got %u\n",
789 cc_rsp
.total_bytes_written
);
791 smb2_util_close(tree
, src_h
);
792 smb2_util_close(tree
, dest_h
);
793 talloc_free(tmp_ctx
);
797 static bool test_ioctl_copy_chunk_src_lck(struct torture_context
*torture
,
798 struct smb2_tree
*tree
)
800 struct smb2_handle src_h
;
801 struct smb2_handle src_h2
;
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
;
810 struct smb2_lock lck
;
811 struct smb2_lock_element el
[1];
813 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
816 &src_h
, 4096, /* src file */
819 &dest_h
, 0, /* dest file */
824 torture_fail(torture
, "setup copy chunk error");
827 cc_copy
.chunks
[0].source_off
= 0;
828 cc_copy
.chunks
[0].target_off
= 0;
829 cc_copy
.chunks
[0].length
= 4096;
831 /* open and lock the copychunk src file */
832 status
= torture_smb2_testfile(tree
, FNAME
, &src_h2
);
833 torture_assert_ntstatus_ok(torture
, status
, "2nd src open");
835 lck
.in
.lock_count
= 0x0001;
836 lck
.in
.lock_sequence
= 0x00000000;
837 lck
.in
.file
.handle
= src_h2
;
839 el
[0].offset
= cc_copy
.chunks
[0].source_off
;
840 el
[0].length
= cc_copy
.chunks
[0].length
;
842 el
[0].flags
= SMB2_LOCK_FLAG_EXCLUSIVE
;
844 status
= smb2_lock(tree
, &lck
);
845 torture_assert_ntstatus_ok(torture
, status
, "lock");
847 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
849 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
850 torture_assert_ndr_success(torture
, ndr_ret
,
851 "ndr_push_srv_copychunk_copy");
853 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
855 * 2k12 & Samba return lock_conflict, Windows 7 & 2k8 return success...
857 * Edgar Olougouna @ MS wrote:
858 * Regarding the FSCTL_SRV_COPYCHUNK and STATUS_FILE_LOCK_CONFLICT
859 * discrepancy observed between Windows versions, we confirm that the
860 * behavior change is expected.
862 * CopyChunk in Windows Server 2012 use regular Readfile/Writefile APIs
863 * to move the chunks from the source to the destination.
864 * These ReadFile/WriteFile APIs go through the byte-range lock checks,
865 * and this explains the observed STATUS_FILE_LOCK_CONFLICT error.
867 * Prior to Windows Server 2012, CopyChunk used mapped sections to move
868 * the data. And byte range locks are not enforced on mapped I/O, and
869 * this explains the STATUS_SUCCESS observed on Windows Server 2008 R2.
871 torture_assert_ntstatus_equal(torture
, status
,
872 NT_STATUS_FILE_LOCK_CONFLICT
,
873 "FSCTL_SRV_COPYCHUNK locked");
875 /* should get cc response data with the lock conflict status */
876 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
878 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
879 torture_assert_ndr_success(torture
, ndr_ret
,
880 "ndr_pull_srv_copychunk_rsp");
881 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
882 0, /* chunks written */
883 0, /* chunk bytes unsuccessfully written */
884 0); /* total bytes written */
886 lck
.in
.lock_count
= 0x0001;
887 lck
.in
.lock_sequence
= 0x00000001;
888 lck
.in
.file
.handle
= src_h2
;
890 el
[0].offset
= cc_copy
.chunks
[0].source_off
;
891 el
[0].length
= cc_copy
.chunks
[0].length
;
893 el
[0].flags
= SMB2_LOCK_FLAG_UNLOCK
;
894 status
= smb2_lock(tree
, &lck
);
895 torture_assert_ntstatus_ok(torture
, status
, "unlock");
897 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
898 torture_assert_ntstatus_ok(torture
, status
,
899 "FSCTL_SRV_COPYCHUNK unlocked");
901 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
903 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
904 torture_assert_ndr_success(torture
, ndr_ret
,
905 "ndr_pull_srv_copychunk_rsp");
907 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
908 1, /* chunks written */
909 0, /* chunk bytes unsuccessfully written */
910 4096); /* total bytes written */
912 torture_fail(torture
, "bad copy chunk response data");
915 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 0, 4096, 0);
917 torture_fail(torture
, "inconsistent file data");
920 smb2_util_close(tree
, src_h2
);
921 smb2_util_close(tree
, src_h
);
922 smb2_util_close(tree
, dest_h
);
923 talloc_free(tmp_ctx
);
927 static bool test_ioctl_copy_chunk_dest_lck(struct torture_context
*torture
,
928 struct smb2_tree
*tree
)
930 struct smb2_handle src_h
;
931 struct smb2_handle dest_h
;
932 struct smb2_handle dest_h2
;
934 union smb_ioctl ioctl
;
935 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
936 struct srv_copychunk_copy cc_copy
;
937 struct srv_copychunk_rsp cc_rsp
;
938 enum ndr_err_code ndr_ret
;
940 struct smb2_lock lck
;
941 struct smb2_lock_element el
[1];
943 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
946 &src_h
, 4096, /* src file */
949 &dest_h
, 4096, /* dest file */
954 torture_fail(torture
, "setup copy chunk error");
957 cc_copy
.chunks
[0].source_off
= 0;
958 cc_copy
.chunks
[0].target_off
= 0;
959 cc_copy
.chunks
[0].length
= 4096;
961 /* open and lock the copychunk dest file */
962 status
= torture_smb2_testfile(tree
, FNAME2
, &dest_h2
);
963 torture_assert_ntstatus_ok(torture
, status
, "2nd src open");
965 lck
.in
.lock_count
= 0x0001;
966 lck
.in
.lock_sequence
= 0x00000000;
967 lck
.in
.file
.handle
= dest_h2
;
969 el
[0].offset
= cc_copy
.chunks
[0].target_off
;
970 el
[0].length
= cc_copy
.chunks
[0].length
;
972 el
[0].flags
= SMB2_LOCK_FLAG_EXCLUSIVE
;
974 status
= smb2_lock(tree
, &lck
);
975 torture_assert_ntstatus_ok(torture
, status
, "lock");
977 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
979 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
980 torture_assert_ndr_success(torture
, ndr_ret
,
981 "ndr_push_srv_copychunk_copy");
983 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
984 torture_assert_ntstatus_equal(torture
, status
,
985 NT_STATUS_FILE_LOCK_CONFLICT
,
986 "FSCTL_SRV_COPYCHUNK locked");
988 lck
.in
.lock_count
= 0x0001;
989 lck
.in
.lock_sequence
= 0x00000001;
990 lck
.in
.file
.handle
= dest_h2
;
992 el
[0].offset
= cc_copy
.chunks
[0].target_off
;
993 el
[0].length
= cc_copy
.chunks
[0].length
;
995 el
[0].flags
= SMB2_LOCK_FLAG_UNLOCK
;
996 status
= smb2_lock(tree
, &lck
);
997 torture_assert_ntstatus_ok(torture
, status
, "unlock");
999 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1000 torture_assert_ntstatus_ok(torture
, status
,
1001 "FSCTL_SRV_COPYCHUNK unlocked");
1003 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
1005 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
1006 torture_assert_ndr_success(torture
, ndr_ret
,
1007 "ndr_pull_srv_copychunk_rsp");
1009 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
1010 1, /* chunks written */
1011 0, /* chunk bytes unsuccessfully written */
1012 4096); /* total bytes written */
1014 torture_fail(torture
, "bad copy chunk response data");
1017 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 0, 4096, 0);
1019 torture_fail(torture
, "inconsistent file data");
1022 smb2_util_close(tree
, dest_h2
);
1023 smb2_util_close(tree
, src_h
);
1024 smb2_util_close(tree
, dest_h
);
1025 talloc_free(tmp_ctx
);
1029 static bool test_ioctl_copy_chunk_bad_key(struct torture_context
*torture
,
1030 struct smb2_tree
*tree
)
1032 struct smb2_handle src_h
;
1033 struct smb2_handle dest_h
;
1035 union smb_ioctl ioctl
;
1036 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
1037 struct srv_copychunk_copy cc_copy
;
1038 enum ndr_err_code ndr_ret
;
1041 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
1045 SEC_RIGHTS_FILE_ALL
,
1048 SEC_RIGHTS_FILE_ALL
,
1052 torture_fail(torture
, "setup copy chunk error");
1055 /* overwrite the resume key with a bogus value */
1056 memcpy(cc_copy
.source_key
, "deadbeefdeadbeefdeadbeef", 24);
1058 cc_copy
.chunks
[0].source_off
= 0;
1059 cc_copy
.chunks
[0].target_off
= 0;
1060 cc_copy
.chunks
[0].length
= 4096;
1062 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1064 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1065 torture_assert_ndr_success(torture
, ndr_ret
,
1066 "ndr_push_srv_copychunk_copy");
1068 /* Server 2k12 returns NT_STATUS_OBJECT_NAME_NOT_FOUND */
1069 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1070 torture_assert_ntstatus_equal(torture
, status
,
1071 NT_STATUS_OBJECT_NAME_NOT_FOUND
,
1072 "FSCTL_SRV_COPYCHUNK");
1074 smb2_util_close(tree
, src_h
);
1075 smb2_util_close(tree
, dest_h
);
1076 talloc_free(tmp_ctx
);
1080 static bool test_ioctl_copy_chunk_src_is_dest(struct torture_context
*torture
,
1081 struct smb2_tree
*tree
)
1083 struct smb2_handle src_h
;
1084 struct smb2_handle dest_h
;
1086 union smb_ioctl ioctl
;
1087 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
1088 struct srv_copychunk_copy cc_copy
;
1089 struct srv_copychunk_rsp cc_rsp
;
1090 enum ndr_err_code ndr_ret
;
1093 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
1097 SEC_RIGHTS_FILE_ALL
,
1100 SEC_RIGHTS_FILE_ALL
,
1104 torture_fail(torture
, "setup copy chunk error");
1107 /* the source is also the destination */
1108 ioctl
.smb2
.in
.file
.handle
= src_h
;
1110 /* non-overlapping */
1111 cc_copy
.chunks
[0].source_off
= 0;
1112 cc_copy
.chunks
[0].target_off
= 4096;
1113 cc_copy
.chunks
[0].length
= 4096;
1115 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1117 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1118 torture_assert_ndr_success(torture
, ndr_ret
,
1119 "ndr_push_srv_copychunk_copy");
1121 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1122 torture_assert_ntstatus_ok(torture
, status
,
1123 "FSCTL_SRV_COPYCHUNK");
1125 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
1127 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
1128 torture_assert_ndr_success(torture
, ndr_ret
,
1129 "ndr_pull_srv_copychunk_rsp");
1131 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
1132 1, /* chunks written */
1133 0, /* chunk bytes unsuccessfully written */
1134 4096); /* total bytes written */
1136 torture_fail(torture
, "bad copy chunk response data");
1139 ok
= check_pattern(torture
, tree
, tmp_ctx
, src_h
, 0, 4096, 0);
1141 torture_fail(torture
, "inconsistent file data");
1143 ok
= check_pattern(torture
, tree
, tmp_ctx
, src_h
, 4096, 4096, 0);
1145 torture_fail(torture
, "inconsistent file data");
1148 smb2_util_close(tree
, src_h
);
1149 smb2_util_close(tree
, dest_h
);
1150 talloc_free(tmp_ctx
);
1155 * Test a single-chunk copychunk request, where the source and target ranges
1156 * overlap, and the SourceKey refers to the same target file. E.g:
1160 * File: src_and_dest
1161 * Offset: 0123456789
1166 * FSCTL_SRV_COPYCHUNK(src_and_dest)
1167 * SourceKey = SRV_REQUEST_RESUME_KEY(src_and_dest)
1169 * Chunks[0].SourceOffset = 0
1170 * Chunks[0].TargetOffset = 4
1171 * Chunks[0].Length = 6
1175 * File: src_and_dest
1176 * Offset: 0123456789
1179 * The resultant contents of src_and_dest is dependent on the server's
1180 * copy algorithm. In the above example, the server uses an IO buffer
1181 * large enough to hold the entire six-byte source data before writing
1182 * to TargetOffset. If the server were to use a four-byte IO buffer and
1183 * started reads/writes from the lowest offset, then the two overlapping
1184 * bytes in the above example would be overwritten before being read. The
1185 * resultant file contents would be abcdabcdab.
1187 * Windows 2008r2 appears to use a 2048 byte copy buffer, overlapping bytes
1188 * after this offset are written before being read. Windows 2012 on the
1189 * other hand appears to use a buffer large enough to hold its maximum
1190 * supported chunk size (1M). Samba currently uses a 64k copy buffer by
1191 * default (vfs_cc_state.buf).
1193 * This test uses an 8-byte overlap at 2040-2048, so that it passes against
1194 * Windows 2008r2, 2012 and Samba servers. Note, 2008GM fails, as it appears
1195 * to use a different copy algorithm to 2008r2.
1198 test_ioctl_copy_chunk_src_is_dest_overlap(struct torture_context
*torture
,
1199 struct smb2_tree
*tree
)
1201 struct smb2_handle src_h
;
1202 struct smb2_handle dest_h
;
1204 union smb_ioctl ioctl
;
1205 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
1206 struct srv_copychunk_copy cc_copy
;
1207 struct srv_copychunk_rsp cc_rsp
;
1208 enum ndr_err_code ndr_ret
;
1211 /* exceed the vfs_default copy buffer */
1212 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
1216 SEC_RIGHTS_FILE_ALL
,
1219 SEC_RIGHTS_FILE_ALL
,
1223 torture_fail(torture
, "setup copy chunk error");
1226 /* the source is also the destination */
1227 ioctl
.smb2
.in
.file
.handle
= src_h
;
1229 /* 8 bytes overlap between source and target ranges */
1230 cc_copy
.chunks
[0].source_off
= 0;
1231 cc_copy
.chunks
[0].target_off
= 2048 - 8;
1232 cc_copy
.chunks
[0].length
= 2048;
1234 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1236 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1237 torture_assert_ndr_success(torture
, ndr_ret
,
1238 "ndr_push_srv_copychunk_copy");
1240 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1241 torture_assert_ntstatus_ok(torture
, status
,
1242 "FSCTL_SRV_COPYCHUNK");
1244 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
1246 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
1247 torture_assert_ndr_success(torture
, ndr_ret
,
1248 "ndr_pull_srv_copychunk_rsp");
1250 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
1251 1, /* chunks written */
1252 0, /* chunk bytes unsuccessfully written */
1253 2048); /* total bytes written */
1255 torture_fail(torture
, "bad copy chunk response data");
1258 ok
= check_pattern(torture
, tree
, tmp_ctx
, src_h
, 0, 2048 - 8, 0);
1260 torture_fail(torture
, "inconsistent file data");
1262 ok
= check_pattern(torture
, tree
, tmp_ctx
, src_h
, 2048 - 8, 2048, 0);
1264 torture_fail(torture
, "inconsistent file data");
1267 smb2_util_close(tree
, src_h
);
1268 smb2_util_close(tree
, dest_h
);
1269 talloc_free(tmp_ctx
);
1273 static bool test_ioctl_copy_chunk_bad_access(struct torture_context
*torture
,
1274 struct smb2_tree
*tree
)
1276 struct smb2_handle src_h
;
1277 struct smb2_handle dest_h
;
1279 union smb_ioctl ioctl
;
1280 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
1281 struct srv_copychunk_copy cc_copy
;
1282 enum ndr_err_code ndr_ret
;
1284 /* read permission on src */
1285 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
, 1, /* 1 chunk */
1286 FNAME
, &src_h
, 4096, /* fill 4096 byte src file */
1287 SEC_FILE_READ_DATA
| SEC_FILE_READ_ATTRIBUTE
,
1288 FNAME2
, &dest_h
, 0, /* 0 byte dest file */
1289 SEC_RIGHTS_FILE_ALL
, &cc_copy
, &ioctl
);
1291 torture_fail(torture
, "setup copy chunk error");
1294 cc_copy
.chunks
[0].source_off
= 0;
1295 cc_copy
.chunks
[0].target_off
= 0;
1296 cc_copy
.chunks
[0].length
= 4096;
1298 ndr_ret
= ndr_push_struct_blob(
1299 &ioctl
.smb2
.in
.out
, tmp_ctx
, &cc_copy
,
1300 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1301 torture_assert_ndr_success(torture
, ndr_ret
,
1302 "ndr_push_srv_copychunk_copy");
1304 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1305 torture_assert_ntstatus_equal(torture
, status
, NT_STATUS_OK
,
1306 "FSCTL_SRV_COPYCHUNK");
1308 smb2_util_close(tree
, src_h
);
1309 smb2_util_close(tree
, dest_h
);
1311 /* execute permission on src */
1312 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
, 1, /* 1 chunk */
1313 FNAME
, &src_h
, 4096, /* fill 4096 byte src file */
1314 SEC_FILE_EXECUTE
| SEC_FILE_READ_ATTRIBUTE
,
1315 FNAME2
, &dest_h
, 0, /* 0 byte dest file */
1316 SEC_RIGHTS_FILE_ALL
, &cc_copy
, &ioctl
);
1318 torture_fail(torture
, "setup copy chunk error");
1321 cc_copy
.chunks
[0].source_off
= 0;
1322 cc_copy
.chunks
[0].target_off
= 0;
1323 cc_copy
.chunks
[0].length
= 4096;
1325 ndr_ret
= ndr_push_struct_blob(
1326 &ioctl
.smb2
.in
.out
, tmp_ctx
, &cc_copy
,
1327 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1328 torture_assert_ndr_success(torture
, ndr_ret
,
1329 "ndr_push_srv_copychunk_copy");
1331 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1332 torture_assert_ntstatus_equal(torture
, status
, NT_STATUS_OK
,
1333 "FSCTL_SRV_COPYCHUNK");
1335 smb2_util_close(tree
, src_h
);
1336 smb2_util_close(tree
, dest_h
);
1338 /* neither read nor execute permission on src */
1339 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
, 1, /* 1 chunk */
1340 FNAME
, &src_h
, 4096, /* fill 4096 byte src file */
1341 SEC_FILE_READ_ATTRIBUTE
, FNAME2
, &dest_h
,
1342 0, /* 0 byte dest file */
1343 SEC_RIGHTS_FILE_ALL
, &cc_copy
, &ioctl
);
1345 torture_fail(torture
, "setup copy chunk error");
1348 cc_copy
.chunks
[0].source_off
= 0;
1349 cc_copy
.chunks
[0].target_off
= 0;
1350 cc_copy
.chunks
[0].length
= 4096;
1352 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1354 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1355 torture_assert_ndr_success(torture
, ndr_ret
,
1356 "ndr_push_srv_copychunk_copy");
1358 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1359 torture_assert_ntstatus_equal(torture
, status
,
1360 NT_STATUS_ACCESS_DENIED
,
1361 "FSCTL_SRV_COPYCHUNK");
1363 smb2_util_close(tree
, src_h
);
1364 smb2_util_close(tree
, dest_h
);
1366 /* no write permission on dest */
1367 ok
= test_setup_copy_chunk(
1368 torture
, tree
, tree
, tmp_ctx
, 1, /* 1 chunk */
1369 FNAME
, &src_h
, 4096, /* fill 4096 byte src file */
1370 SEC_FILE_READ_DATA
| SEC_FILE_READ_ATTRIBUTE
, FNAME2
, &dest_h
,
1371 0, /* 0 byte dest file */
1372 (SEC_RIGHTS_FILE_ALL
&
1373 ~(SEC_FILE_WRITE_DATA
| SEC_FILE_APPEND_DATA
)),
1376 torture_fail(torture
, "setup copy chunk error");
1379 cc_copy
.chunks
[0].source_off
= 0;
1380 cc_copy
.chunks
[0].target_off
= 0;
1381 cc_copy
.chunks
[0].length
= 4096;
1383 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1385 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1386 torture_assert_ndr_success(torture
, ndr_ret
,
1387 "ndr_push_srv_copychunk_copy");
1389 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1390 torture_assert_ntstatus_equal(torture
, status
,
1391 NT_STATUS_ACCESS_DENIED
,
1392 "FSCTL_SRV_COPYCHUNK");
1394 smb2_util_close(tree
, src_h
);
1395 smb2_util_close(tree
, dest_h
);
1397 /* no read permission on dest */
1398 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
, 1, /* 1 chunk */
1399 FNAME
, &src_h
, 4096, /* fill 4096 byte src file */
1400 SEC_FILE_READ_DATA
| SEC_FILE_READ_ATTRIBUTE
,
1401 FNAME2
, &dest_h
, 0, /* 0 byte dest file */
1402 (SEC_RIGHTS_FILE_ALL
& ~SEC_FILE_READ_DATA
),
1405 torture_fail(torture
, "setup copy chunk error");
1408 cc_copy
.chunks
[0].source_off
= 0;
1409 cc_copy
.chunks
[0].target_off
= 0;
1410 cc_copy
.chunks
[0].length
= 4096;
1412 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1414 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1415 torture_assert_ndr_success(torture
, ndr_ret
,
1416 "ndr_push_srv_copychunk_copy");
1419 * FSCTL_SRV_COPYCHUNK requires read permission on dest,
1420 * FSCTL_SRV_COPYCHUNK_WRITE on the other hand does not.
1422 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1423 torture_assert_ntstatus_equal(torture
, status
,
1424 NT_STATUS_ACCESS_DENIED
,
1425 "FSCTL_SRV_COPYCHUNK");
1427 smb2_util_close(tree
, src_h
);
1428 smb2_util_close(tree
, dest_h
);
1429 talloc_free(tmp_ctx
);
1434 static bool test_ioctl_copy_chunk_write_access(struct torture_context
*torture
,
1435 struct smb2_tree
*tree
)
1437 struct smb2_handle src_h
;
1438 struct smb2_handle dest_h
;
1440 union smb_ioctl ioctl
;
1441 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
1442 struct srv_copychunk_copy cc_copy
;
1443 enum ndr_err_code ndr_ret
;
1446 /* no read permission on dest with FSCTL_SRV_COPYCHUNK_WRITE */
1447 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
1450 &src_h
, 4096, /* fill 4096 byte src file */
1451 SEC_RIGHTS_FILE_ALL
,
1453 &dest_h
, 0, /* 0 byte dest file */
1454 (SEC_RIGHTS_FILE_WRITE
1455 | SEC_RIGHTS_FILE_EXECUTE
),
1459 torture_fail(torture
, "setup copy chunk error");
1462 ioctl
.smb2
.in
.function
= FSCTL_SRV_COPYCHUNK_WRITE
;
1463 cc_copy
.chunks
[0].source_off
= 0;
1464 cc_copy
.chunks
[0].target_off
= 0;
1465 cc_copy
.chunks
[0].length
= 4096;
1467 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1469 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1470 torture_assert_ndr_success(torture
, ndr_ret
,
1471 "ndr_push_srv_copychunk_copy");
1473 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1474 torture_assert_ntstatus_ok(torture
, status
,
1475 "FSCTL_SRV_COPYCHUNK_WRITE");
1477 smb2_util_close(tree
, src_h
);
1478 smb2_util_close(tree
, dest_h
);
1479 talloc_free(tmp_ctx
);
1484 static bool test_ioctl_copy_chunk_src_exceed(struct torture_context
*torture
,
1485 struct smb2_tree
*tree
)
1487 struct smb2_handle src_h
;
1488 struct smb2_handle dest_h
;
1490 union smb_ioctl ioctl
;
1491 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
1492 struct srv_copychunk_copy cc_copy
;
1493 struct srv_copychunk_rsp cc_rsp
;
1494 enum ndr_err_code ndr_ret
;
1497 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
1500 &src_h
, 4096, /* fill 4096 byte src file */
1501 SEC_RIGHTS_FILE_ALL
,
1503 &dest_h
, 0, /* 0 byte dest file */
1504 SEC_RIGHTS_FILE_ALL
,
1508 torture_fail(torture
, "setup copy chunk error");
1511 /* Request copy where off + length exceeds size of src */
1512 cc_copy
.chunks
[0].source_off
= 1024;
1513 cc_copy
.chunks
[0].target_off
= 0;
1514 cc_copy
.chunks
[0].length
= 4096;
1516 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1518 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1519 torture_assert_ndr_success(torture
, ndr_ret
,
1520 "ndr_push_srv_copychunk_copy");
1522 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1523 torture_assert_ntstatus_equal(torture
, status
,
1524 NT_STATUS_INVALID_VIEW_SIZE
,
1525 "FSCTL_SRV_COPYCHUNK oversize");
1527 /* Request copy where length exceeds size of src */
1528 cc_copy
.chunks
[0].source_off
= 1024;
1529 cc_copy
.chunks
[0].target_off
= 0;
1530 cc_copy
.chunks
[0].length
= 3072;
1532 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1534 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1535 torture_assert_ndr_success(torture
, ndr_ret
,
1536 "ndr_push_srv_copychunk_copy");
1538 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1539 torture_assert_ntstatus_ok(torture
, status
,
1540 "FSCTL_SRV_COPYCHUNK just right");
1542 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
1544 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
1545 torture_assert_ndr_success(torture
, ndr_ret
,
1546 "ndr_pull_srv_copychunk_rsp");
1548 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
1549 1, /* chunks written */
1550 0, /* chunk bytes unsuccessfully written */
1551 3072); /* total bytes written */
1553 torture_fail(torture
, "bad copy chunk response data");
1556 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 0, 3072, 1024);
1558 torture_fail(torture
, "inconsistent file data");
1561 smb2_util_close(tree
, src_h
);
1562 smb2_util_close(tree
, dest_h
);
1563 talloc_free(tmp_ctx
);
1568 test_ioctl_copy_chunk_src_exceed_multi(struct torture_context
*torture
,
1569 struct smb2_tree
*tree
)
1571 struct smb2_handle src_h
;
1572 struct smb2_handle dest_h
;
1574 union smb_ioctl ioctl
;
1575 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
1576 struct srv_copychunk_copy cc_copy
;
1577 struct srv_copychunk_rsp cc_rsp
;
1578 enum ndr_err_code ndr_ret
;
1581 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
1584 &src_h
, 8192, /* fill 8192 byte src file */
1585 SEC_RIGHTS_FILE_ALL
,
1587 &dest_h
, 0, /* 0 byte dest file */
1588 SEC_RIGHTS_FILE_ALL
,
1592 torture_fail(torture
, "setup copy chunk error");
1595 /* Request copy where off + length exceeds size of src */
1596 cc_copy
.chunks
[0].source_off
= 0;
1597 cc_copy
.chunks
[0].target_off
= 0;
1598 cc_copy
.chunks
[0].length
= 4096;
1600 cc_copy
.chunks
[1].source_off
= 4096;
1601 cc_copy
.chunks
[1].target_off
= 4096;
1602 cc_copy
.chunks
[1].length
= 8192;
1604 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1606 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1607 torture_assert_ndr_success(torture
, ndr_ret
,
1608 "ndr_push_srv_copychunk_copy");
1610 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1611 torture_assert_ntstatus_equal(torture
, status
,
1612 NT_STATUS_INVALID_VIEW_SIZE
,
1613 "FSCTL_SRV_COPYCHUNK oversize");
1614 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
1616 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
1617 torture_assert_ndr_success(torture
, ndr_ret
, "unmarshalling response");
1619 /* first chunk should still be written */
1620 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
1621 1, /* chunks written */
1622 0, /* chunk bytes unsuccessfully written */
1623 4096); /* total bytes written */
1625 torture_fail(torture
, "bad copy chunk response data");
1627 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 0, 4096, 0);
1629 torture_fail(torture
, "inconsistent file data");
1632 smb2_util_close(tree
, src_h
);
1633 smb2_util_close(tree
, dest_h
);
1634 talloc_free(tmp_ctx
);
1638 static bool test_ioctl_copy_chunk_sparse_dest(struct torture_context
*torture
,
1639 struct smb2_tree
*tree
)
1641 struct smb2_handle src_h
;
1642 struct smb2_handle dest_h
;
1644 union smb_ioctl ioctl
;
1646 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
1647 struct srv_copychunk_copy cc_copy
;
1648 struct srv_copychunk_rsp cc_rsp
;
1649 enum ndr_err_code ndr_ret
;
1653 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
1656 &src_h
, 4096, /* fill 4096 byte src file */
1657 SEC_RIGHTS_FILE_ALL
,
1659 &dest_h
, 0, /* 0 byte dest file */
1660 SEC_RIGHTS_FILE_ALL
,
1664 torture_fail(torture
, "setup copy chunk error");
1667 /* copy all src file data (via a single chunk desc) */
1668 cc_copy
.chunks
[0].source_off
= 0;
1669 cc_copy
.chunks
[0].target_off
= 4096;
1670 cc_copy
.chunks
[0].length
= 4096;
1672 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1674 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1675 torture_assert_ndr_success(torture
, ndr_ret
,
1676 "ndr_push_srv_copychunk_copy");
1678 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1679 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_COPYCHUNK");
1681 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
1683 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
1684 torture_assert_ndr_success(torture
, ndr_ret
,
1685 "ndr_pull_srv_copychunk_rsp");
1687 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
1688 1, /* chunks written */
1689 0, /* chunk bytes unsuccessfully written */
1690 4096); /* total bytes written */
1692 torture_fail(torture
, "bad copy chunk response data");
1695 /* check for zeros in first 4k */
1697 r
.in
.file
.handle
= dest_h
;
1700 status
= smb2_read(tree
, tmp_ctx
, &r
);
1701 torture_assert_ntstatus_ok(torture
, status
, "read");
1703 torture_assert_u64_equal(torture
, r
.out
.data
.length
, 4096,
1704 "read data len mismatch");
1706 for (i
= 0; i
< 4096; i
++) {
1707 torture_assert(torture
, (r
.out
.data
.data
[i
] == 0),
1708 "sparse did not pass class");
1711 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 4096, 4096, 0);
1713 torture_fail(torture
, "inconsistent file data");
1716 smb2_util_close(tree
, src_h
);
1717 smb2_util_close(tree
, dest_h
);
1718 talloc_free(tmp_ctx
);
1723 * set the ioctl MaxOutputResponse size to less than
1724 * sizeof(struct srv_copychunk_rsp)
1726 static bool test_ioctl_copy_chunk_max_output_sz(struct torture_context
*torture
,
1727 struct smb2_tree
*tree
)
1729 struct smb2_handle src_h
;
1730 struct smb2_handle dest_h
;
1732 union smb_ioctl ioctl
;
1733 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
1734 struct srv_copychunk_copy cc_copy
;
1735 enum ndr_err_code ndr_ret
;
1738 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
1741 &src_h
, 4096, /* fill 4096 byte src file */
1742 SEC_RIGHTS_FILE_ALL
,
1744 &dest_h
, 0, /* 0 byte dest file */
1745 SEC_RIGHTS_FILE_ALL
,
1749 torture_fail(torture
, "setup copy chunk error");
1752 cc_copy
.chunks
[0].source_off
= 0;
1753 cc_copy
.chunks
[0].target_off
= 0;
1754 cc_copy
.chunks
[0].length
= 4096;
1755 /* req is valid, but use undersize max_response_size */
1756 ioctl
.smb2
.in
.max_response_size
= sizeof(struct srv_copychunk_rsp
) - 1;
1758 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1760 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1761 torture_assert_ndr_success(torture
, ndr_ret
,
1762 "ndr_push_srv_copychunk_copy");
1764 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1765 torture_assert_ntstatus_equal(torture
, status
,
1766 NT_STATUS_INVALID_PARAMETER
,
1767 "FSCTL_SRV_COPYCHUNK");
1769 smb2_util_close(tree
, src_h
);
1770 smb2_util_close(tree
, dest_h
);
1771 talloc_free(tmp_ctx
);
1775 static bool test_ioctl_copy_chunk_zero_length(struct torture_context
*torture
,
1776 struct smb2_tree
*tree
)
1778 struct smb2_handle src_h
;
1779 struct smb2_handle dest_h
;
1781 union smb_ioctl ioctl
;
1782 union smb_fileinfo q
;
1783 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
1784 struct srv_copychunk_copy cc_copy
;
1785 struct srv_copychunk_rsp cc_rsp
;
1786 enum ndr_err_code ndr_ret
;
1789 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
1792 &src_h
, 4096, /* fill 4096 byte src file */
1793 SEC_RIGHTS_FILE_ALL
,
1795 &dest_h
, 0, /* 0 byte dest file */
1796 SEC_RIGHTS_FILE_ALL
,
1800 torture_fail(torture
, "setup copy chunk error");
1803 /* zero length server-side copy (via a single chunk desc) */
1804 cc_copy
.chunks
[0].source_off
= 0;
1805 cc_copy
.chunks
[0].target_off
= 0;
1806 cc_copy
.chunks
[0].length
= 0;
1808 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1810 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1811 torture_assert_ndr_success(torture
, ndr_ret
,
1812 "ndr_push_srv_copychunk_copy");
1814 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1815 torture_assert_ntstatus_equal(torture
, status
,
1816 NT_STATUS_INVALID_PARAMETER
,
1817 "bad zero-length chunk response");
1819 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
1821 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
1822 torture_assert_ndr_success(torture
, ndr_ret
, "unmarshalling response");
1825 q
.all_info2
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
1826 q
.all_info2
.in
.file
.handle
= dest_h
;
1827 status
= smb2_getinfo_file(tree
, torture
, &q
);
1828 torture_assert_ntstatus_ok(torture
, status
, "getinfo");
1830 torture_assert_int_equal(torture
, q
.all_info2
.out
.size
, 0,
1831 "size after zero len clone");
1833 smb2_util_close(tree
, src_h
);
1834 smb2_util_close(tree
, dest_h
);
1835 talloc_free(tmp_ctx
);
1839 static bool copy_one_stream(struct torture_context
*torture
,
1840 struct smb2_tree
*tree
,
1841 TALLOC_CTX
*tmp_ctx
,
1842 const char *src_sname
,
1843 const char *dst_sname
)
1845 struct smb2_handle src_h
= {{0}};
1846 struct smb2_handle dest_h
= {{0}};
1849 struct srv_copychunk_copy cc_copy
;
1850 struct srv_copychunk_rsp cc_rsp
;
1851 enum ndr_err_code ndr_ret
;
1854 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
1857 &src_h
, 256, /* fill 256 byte src file */
1858 SEC_FILE_READ_DATA
| SEC_FILE_WRITE_DATA
,
1860 &dest_h
, 0, /* 0 byte dest file */
1861 SEC_FILE_READ_DATA
| SEC_FILE_WRITE_DATA
,
1864 torture_assert_goto(torture
, ok
== true, ok
, done
,
1865 "setup copy chunk error\n");
1867 /* copy all src file data (via a single chunk desc) */
1868 cc_copy
.chunks
[0].source_off
= 0;
1869 cc_copy
.chunks
[0].target_off
= 0;
1870 cc_copy
.chunks
[0].length
= 256;
1872 ndr_ret
= ndr_push_struct_blob(
1873 &io
.smb2
.in
.out
, tmp_ctx
, &cc_copy
,
1874 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1876 torture_assert_ndr_success_goto(torture
, ndr_ret
, ok
, done
,
1877 "ndr_push_srv_copychunk_copy\n");
1879 status
= smb2_ioctl(tree
, tmp_ctx
, &io
.smb2
);
1880 torture_assert_ntstatus_ok_goto(torture
, status
, ok
, done
,
1881 "FSCTL_SRV_COPYCHUNK\n");
1883 ndr_ret
= ndr_pull_struct_blob(
1884 &io
.smb2
.out
.out
, tmp_ctx
, &cc_rsp
,
1885 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
1887 torture_assert_ndr_success_goto(torture
, ndr_ret
, ok
, done
,
1888 "ndr_pull_srv_copychunk_rsp\n");
1890 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
1891 1, /* chunks written */
1892 0, /* chunk bytes unsuccessfully written */
1893 256); /* total bytes written */
1894 torture_assert_goto(torture
, ok
== true, ok
, done
,
1895 "bad copy chunk response data\n");
1897 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 0, 256, 0);
1899 torture_fail(torture
, "inconsistent file data\n");
1903 if (!smb2_util_handle_empty(src_h
)) {
1904 smb2_util_close(tree
, src_h
);
1906 if (!smb2_util_handle_empty(dest_h
)) {
1907 smb2_util_close(tree
, dest_h
);
1916 static bool torture_setup_file(TALLOC_CTX
*mem_ctx
,
1917 struct smb2_tree
*tree
,
1920 struct smb2_create io
;
1923 smb2_util_unlink(tree
, name
);
1925 io
.in
.desired_access
= SEC_FLAG_MAXIMUM_ALLOWED
;
1926 io
.in
.file_attributes
= FILE_ATTRIBUTE_NORMAL
;
1927 io
.in
.create_disposition
= NTCREATEX_DISP_OVERWRITE_IF
;
1928 io
.in
.share_access
=
1929 NTCREATEX_SHARE_ACCESS_DELETE
|
1930 NTCREATEX_SHARE_ACCESS_READ
|
1931 NTCREATEX_SHARE_ACCESS_WRITE
;
1932 io
.in
.create_options
= 0;
1935 status
= smb2_create(tree
, mem_ctx
, &io
);
1936 if (!NT_STATUS_IS_OK(status
)) {
1940 status
= smb2_util_close(tree
, io
.out
.file
.handle
);
1941 if (!NT_STATUS_IS_OK(status
)) {
1948 static bool test_copy_chunk_streams(struct torture_context
*torture
,
1949 struct smb2_tree
*tree
)
1951 const char *src_name
= "src";
1952 const char *dst_name
= "dst";
1954 const char *src_sname
;
1955 const char *dst_sname
;
1957 { "src:foo", "dst:foo" }
1960 TALLOC_CTX
*tmp_ctx
= NULL
;
1963 tmp_ctx
= talloc_new(tree
);
1964 torture_assert_not_null_goto(torture
, tmp_ctx
, ok
, done
,
1965 "torture_setup_file\n");
1967 ok
= torture_setup_file(torture
, tree
, src_name
);
1968 torture_assert_goto(torture
, ok
== true, ok
, done
, "torture_setup_file\n");
1969 ok
= torture_setup_file(torture
, tree
, dst_name
);
1970 torture_assert_goto(torture
, ok
== true, ok
, done
, "torture_setup_file\n");
1972 for (i
= 0; i
< ARRAY_SIZE(names
); i
++) {
1973 ok
= copy_one_stream(torture
, tree
, tmp_ctx
,
1975 names
[i
].dst_sname
);
1976 torture_assert_goto(torture
, ok
== true, ok
, done
,
1977 "copy_one_stream failed\n");
1981 smb2_util_unlink(tree
, src_name
);
1982 smb2_util_unlink(tree
, dst_name
);
1983 talloc_free(tmp_ctx
);
1987 static bool test_copy_chunk_across_shares(struct torture_context
*tctx
,
1988 struct smb2_tree
*tree
)
1990 TALLOC_CTX
*mem_ctx
= NULL
;
1991 struct smb2_tree
*tree2
= NULL
;
1992 struct smb2_handle src_h
= {{0}};
1993 struct smb2_handle dest_h
= {{0}};
1994 union smb_ioctl ioctl
;
1995 struct srv_copychunk_copy cc_copy
;
1996 struct srv_copychunk_rsp cc_rsp
;
1997 enum ndr_err_code ndr_ret
;
2001 mem_ctx
= talloc_new(tctx
);
2002 torture_assert_not_null_goto(tctx
, mem_ctx
, ok
, done
,
2005 ok
= torture_smb2_tree_connect(tctx
, tree
->session
, tctx
, &tree2
);
2006 torture_assert_goto(tctx
, ok
== true, ok
, done
,
2007 "torture_smb2_tree_connect failed\n");
2009 ok
= test_setup_copy_chunk(tctx
, tree
, tree2
, mem_ctx
,
2012 &src_h
, 4096, /* fill 4096 byte src file */
2013 SEC_RIGHTS_FILE_ALL
,
2015 &dest_h
, 0, /* 0 byte dest file */
2016 SEC_RIGHTS_FILE_ALL
,
2019 torture_assert_goto(tctx
, ok
== true, ok
, done
,
2020 "test_setup_copy_chunk failed\n");
2022 cc_copy
.chunks
[0].source_off
= 0;
2023 cc_copy
.chunks
[0].target_off
= 0;
2024 cc_copy
.chunks
[0].length
= 4096;
2026 ndr_ret
= ndr_push_struct_blob(
2027 &ioctl
.smb2
.in
.out
, mem_ctx
, &cc_copy
,
2028 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
2029 torture_assert_ndr_success_goto(tctx
, ndr_ret
, ok
, done
,
2030 "ndr_push_srv_copychunk_copy\n");
2032 status
= smb2_ioctl(tree2
, mem_ctx
, &ioctl
.smb2
);
2033 torture_assert_ntstatus_ok_goto(tctx
, status
, ok
, done
,
2034 "FSCTL_SRV_COPYCHUNK\n");
2036 ndr_ret
= ndr_pull_struct_blob(
2037 &ioctl
.smb2
.out
.out
, mem_ctx
, &cc_rsp
,
2038 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
2040 torture_assert_ndr_success_goto(tctx
, ndr_ret
, ok
, done
,
2041 "ndr_pull_srv_copychunk_rsp\n");
2043 ok
= check_copy_chunk_rsp(tctx
, &cc_rsp
,
2044 1, /* chunks written */
2045 0, /* chunk bytes unsuccessfully written */
2046 4096); /* total bytes written */
2047 torture_assert_goto(tctx
, ok
== true, ok
, done
,
2048 "bad copy chunk response data\n");
2050 ok
= check_pattern(tctx
, tree2
, mem_ctx
, dest_h
, 0, 4096, 0);
2051 torture_assert_goto(tctx
, ok
== true, ok
, done
,
2052 "inconsistent file data\n");
2055 TALLOC_FREE(mem_ctx
);
2056 if (!smb2_util_handle_empty(src_h
)) {
2057 smb2_util_close(tree
, src_h
);
2059 if (!smb2_util_handle_empty(dest_h
)) {
2060 smb2_util_close(tree2
, dest_h
);
2062 smb2_util_unlink(tree
, FNAME
);
2063 smb2_util_unlink(tree2
, FNAME2
);
2064 if (tree2
!= NULL
) {
2070 static NTSTATUS
test_ioctl_compress_fs_supported(struct torture_context
*torture
,
2071 struct smb2_tree
*tree
,
2072 TALLOC_CTX
*mem_ctx
,
2073 struct smb2_handle
*fh
,
2074 bool *compress_support
)
2077 union smb_fsinfo info
;
2080 info
.generic
.level
= RAW_QFS_ATTRIBUTE_INFORMATION
;
2081 info
.generic
.handle
= *fh
;
2082 status
= smb2_getinfo_fs(tree
, tree
, &info
);
2083 if (!NT_STATUS_IS_OK(status
)) {
2087 if (info
.attribute_info
.out
.fs_attr
& FILE_FILE_COMPRESSION
) {
2088 *compress_support
= true;
2090 *compress_support
= false;
2092 return NT_STATUS_OK
;
2095 static NTSTATUS
test_ioctl_compress_get(struct torture_context
*torture
,
2096 TALLOC_CTX
*mem_ctx
,
2097 struct smb2_tree
*tree
,
2098 struct smb2_handle fh
,
2099 uint16_t *_compression_fmt
)
2101 union smb_ioctl ioctl
;
2102 struct compression_state cmpr_state
;
2103 enum ndr_err_code ndr_ret
;
2107 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
2108 ioctl
.smb2
.in
.file
.handle
= fh
;
2109 ioctl
.smb2
.in
.function
= FSCTL_GET_COMPRESSION
;
2110 ioctl
.smb2
.in
.max_response_size
= sizeof(struct compression_state
);
2111 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
2113 status
= smb2_ioctl(tree
, mem_ctx
, &ioctl
.smb2
);
2114 if (!NT_STATUS_IS_OK(status
)) {
2118 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, mem_ctx
,
2120 (ndr_pull_flags_fn_t
)ndr_pull_compression_state
);
2122 if (ndr_ret
!= NDR_ERR_SUCCESS
) {
2123 return NT_STATUS_INTERNAL_ERROR
;
2126 *_compression_fmt
= cmpr_state
.format
;
2127 return NT_STATUS_OK
;
2130 static NTSTATUS
test_ioctl_compress_set(struct torture_context
*torture
,
2131 TALLOC_CTX
*mem_ctx
,
2132 struct smb2_tree
*tree
,
2133 struct smb2_handle fh
,
2134 uint16_t compression_fmt
)
2136 union smb_ioctl ioctl
;
2137 struct compression_state cmpr_state
;
2138 enum ndr_err_code ndr_ret
;
2142 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
2143 ioctl
.smb2
.in
.file
.handle
= fh
;
2144 ioctl
.smb2
.in
.function
= FSCTL_SET_COMPRESSION
;
2145 ioctl
.smb2
.in
.max_response_size
= 0;
2146 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
2148 cmpr_state
.format
= compression_fmt
;
2149 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, mem_ctx
,
2151 (ndr_push_flags_fn_t
)ndr_push_compression_state
);
2152 if (ndr_ret
!= NDR_ERR_SUCCESS
) {
2153 return NT_STATUS_INTERNAL_ERROR
;
2156 status
= smb2_ioctl(tree
, mem_ctx
, &ioctl
.smb2
);
2160 static bool test_ioctl_compress_file_flag(struct torture_context
*torture
,
2161 struct smb2_tree
*tree
)
2163 struct smb2_handle fh
;
2165 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
2167 uint16_t compression_fmt
;
2169 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2170 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
2171 FILE_ATTRIBUTE_NORMAL
);
2172 torture_assert(torture
, ok
, "setup compression file");
2174 status
= test_ioctl_compress_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
2176 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
2178 smb2_util_close(tree
, fh
);
2179 torture_skip(torture
, "FS compression not supported\n");
2182 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, fh
,
2184 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
2186 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_NONE
),
2187 "initial compression state not NONE");
2189 status
= test_ioctl_compress_set(torture
, tmp_ctx
, tree
, fh
,
2190 COMPRESSION_FORMAT_DEFAULT
);
2191 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_COMPRESSION");
2193 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, fh
,
2195 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
2197 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_LZNT1
),
2198 "invalid compression state after set");
2200 smb2_util_close(tree
, fh
);
2201 talloc_free(tmp_ctx
);
2205 static bool test_ioctl_compress_dir_inherit(struct torture_context
*torture
,
2206 struct smb2_tree
*tree
)
2208 struct smb2_handle dirh
;
2209 struct smb2_handle fh
;
2211 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
2212 uint16_t compression_fmt
;
2214 char path_buf
[PATH_MAX
];
2216 smb2_deltree(tree
, DNAME
);
2217 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2218 DNAME
, &dirh
, 0, SEC_RIGHTS_FILE_ALL
,
2219 FILE_ATTRIBUTE_DIRECTORY
);
2220 torture_assert(torture
, ok
, "setup compression directory");
2222 status
= test_ioctl_compress_fs_supported(torture
, tree
, tmp_ctx
, &dirh
,
2224 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
2226 smb2_util_close(tree
, dirh
);
2227 smb2_deltree(tree
, DNAME
);
2228 torture_skip(torture
, "FS compression not supported\n");
2231 /* set compression on parent dir, then check for inheritance */
2232 status
= test_ioctl_compress_set(torture
, tmp_ctx
, tree
, dirh
,
2233 COMPRESSION_FORMAT_LZNT1
);
2234 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_COMPRESSION");
2236 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, dirh
,
2238 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
2240 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_LZNT1
),
2241 "invalid compression state after set");
2243 snprintf(path_buf
, PATH_MAX
, "%s\\%s", DNAME
, FNAME
);
2244 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2245 path_buf
, &fh
, 4096, SEC_RIGHTS_FILE_ALL
,
2246 FILE_ATTRIBUTE_NORMAL
);
2247 torture_assert(torture
, ok
, "setup compression file");
2249 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, fh
,
2251 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
2253 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_LZNT1
),
2254 "compression attr not inherited by new file");
2256 /* check compressed data is consistent */
2257 ok
= check_pattern(torture
, tree
, tmp_ctx
, fh
, 0, 4096, 0);
2259 /* disable dir compression attr, file should remain compressed */
2260 status
= test_ioctl_compress_set(torture
, tmp_ctx
, tree
, dirh
,
2261 COMPRESSION_FORMAT_NONE
);
2262 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_COMPRESSION");
2264 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, fh
,
2266 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
2268 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_LZNT1
),
2269 "file compression attr removed after dir change");
2270 smb2_util_close(tree
, fh
);
2272 /* new files should no longer inherit compression attr */
2273 snprintf(path_buf
, PATH_MAX
, "%s\\%s", DNAME
, FNAME2
);
2274 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2275 path_buf
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
2276 FILE_ATTRIBUTE_NORMAL
);
2277 torture_assert(torture
, ok
, "setup file");
2279 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, fh
,
2281 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
2283 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_NONE
),
2284 "compression attr present on new file");
2286 smb2_util_close(tree
, fh
);
2287 smb2_util_close(tree
, dirh
);
2288 smb2_deltree(tree
, DNAME
);
2289 talloc_free(tmp_ctx
);
2293 static bool test_ioctl_compress_invalid_format(struct torture_context
*torture
,
2294 struct smb2_tree
*tree
)
2296 struct smb2_handle fh
;
2298 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
2300 uint16_t compression_fmt
;
2302 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2303 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
2304 FILE_ATTRIBUTE_NORMAL
);
2305 torture_assert(torture
, ok
, "setup compression file");
2307 status
= test_ioctl_compress_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
2309 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
2311 smb2_util_close(tree
, fh
);
2312 torture_skip(torture
, "FS compression not supported\n");
2315 status
= test_ioctl_compress_set(torture
, tmp_ctx
, tree
, fh
,
2316 0x0042); /* bogus */
2317 torture_assert_ntstatus_equal(torture
, status
,
2318 NT_STATUS_INVALID_PARAMETER
,
2319 "invalid FSCTL_SET_COMPRESSION");
2321 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, fh
,
2323 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
2325 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_NONE
),
2326 "initial compression state not NONE");
2328 smb2_util_close(tree
, fh
);
2329 talloc_free(tmp_ctx
);
2333 static bool test_ioctl_compress_invalid_buf(struct torture_context
*torture
,
2334 struct smb2_tree
*tree
)
2336 struct smb2_handle fh
;
2338 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
2340 union smb_ioctl ioctl
;
2342 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2343 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
2344 FILE_ATTRIBUTE_NORMAL
);
2345 torture_assert(torture
, ok
, "setup compression file");
2347 status
= test_ioctl_compress_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
2349 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
2351 smb2_util_close(tree
, fh
);
2352 torture_skip(torture
, "FS compression not supported\n");
2356 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
2357 ioctl
.smb2
.in
.file
.handle
= fh
;
2358 ioctl
.smb2
.in
.function
= FSCTL_GET_COMPRESSION
;
2359 ioctl
.smb2
.in
.max_response_size
= 0; /* no room for rsp data */
2360 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
2362 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
2363 if (!NT_STATUS_EQUAL(status
, NT_STATUS_INVALID_USER_BUFFER
)
2364 && !NT_STATUS_EQUAL(status
, NT_STATUS_INVALID_PARAMETER
)) {
2365 /* neither Server 2k12 nor 2k8r2 response status */
2366 torture_assert(torture
, true,
2367 "invalid FSCTL_SET_COMPRESSION");
2370 smb2_util_close(tree
, fh
);
2371 talloc_free(tmp_ctx
);
2375 static bool test_ioctl_compress_query_file_attr(struct torture_context
*torture
,
2376 struct smb2_tree
*tree
)
2378 struct smb2_handle fh
;
2379 union smb_fileinfo io
;
2381 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
2384 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2385 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
2386 FILE_ATTRIBUTE_NORMAL
);
2387 torture_assert(torture
, ok
, "setup compression file");
2389 status
= test_ioctl_compress_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
2391 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
2393 smb2_util_close(tree
, fh
);
2394 torture_skip(torture
, "FS compression not supported\n");
2398 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
2399 io
.generic
.in
.file
.handle
= fh
;
2400 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
2401 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FILE");
2403 torture_assert(torture
,
2404 ((io
.all_info2
.out
.attrib
& FILE_ATTRIBUTE_COMPRESSED
) == 0),
2405 "compression attr before set");
2407 status
= test_ioctl_compress_set(torture
, tmp_ctx
, tree
, fh
,
2408 COMPRESSION_FORMAT_DEFAULT
);
2409 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_COMPRESSION");
2412 io
.generic
.level
= RAW_FILEINFO_BASIC_INFORMATION
;
2413 io
.generic
.in
.file
.handle
= fh
;
2414 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
2415 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FILE");
2417 torture_assert(torture
,
2418 (io
.basic_info
.out
.attrib
& FILE_ATTRIBUTE_COMPRESSED
),
2419 "no compression attr after set");
2421 smb2_util_close(tree
, fh
);
2422 talloc_free(tmp_ctx
);
2427 * Specify FILE_ATTRIBUTE_COMPRESSED on creation, Windows does not retain this
2430 static bool test_ioctl_compress_create_with_attr(struct torture_context
*torture
,
2431 struct smb2_tree
*tree
)
2433 struct smb2_handle fh2
;
2434 union smb_fileinfo io
;
2436 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
2437 uint16_t compression_fmt
;
2440 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2441 FNAME2
, &fh2
, 0, SEC_RIGHTS_FILE_ALL
,
2442 (FILE_ATTRIBUTE_NORMAL
| FILE_ATTRIBUTE_COMPRESSED
));
2443 torture_assert(torture
, ok
, "setup compression file");
2445 status
= test_ioctl_compress_fs_supported(torture
, tree
, tmp_ctx
, &fh2
,
2447 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
2449 smb2_util_close(tree
, fh2
);
2450 torture_skip(torture
, "FS compression not supported\n");
2453 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, fh2
,
2455 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
2457 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_NONE
),
2458 "initial compression state not NONE");
2461 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
2462 io
.generic
.in
.file
.handle
= fh2
;
2463 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
2464 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FILE");
2466 torture_assert(torture
,
2467 ((io
.all_info2
.out
.attrib
& FILE_ATTRIBUTE_COMPRESSED
) == 0),
2468 "incorrect compression attr");
2470 smb2_util_close(tree
, fh2
);
2471 talloc_free(tmp_ctx
);
2475 static bool test_ioctl_compress_inherit_disable(struct torture_context
*torture
,
2476 struct smb2_tree
*tree
)
2478 struct smb2_handle fh
;
2479 struct smb2_handle dirh
;
2480 char path_buf
[PATH_MAX
];
2482 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
2484 uint16_t compression_fmt
;
2486 struct smb2_create io
;
2488 smb2_deltree(tree
, DNAME
);
2489 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2490 DNAME
, &dirh
, 0, SEC_RIGHTS_FILE_ALL
,
2491 FILE_ATTRIBUTE_DIRECTORY
);
2492 torture_assert(torture
, ok
, "setup compression directory");
2494 status
= test_ioctl_compress_fs_supported(torture
, tree
, tmp_ctx
, &dirh
,
2496 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
2498 smb2_util_close(tree
, dirh
);
2499 smb2_deltree(tree
, DNAME
);
2500 torture_skip(torture
, "FS compression not supported\n");
2503 /* set compression on parent dir, then check for inheritance */
2504 status
= test_ioctl_compress_set(torture
, tmp_ctx
, tree
, dirh
,
2505 COMPRESSION_FORMAT_LZNT1
);
2506 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_COMPRESSION");
2508 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, dirh
,
2510 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
2512 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_LZNT1
),
2513 "invalid compression state after set");
2514 smb2_util_close(tree
, dirh
);
2516 snprintf(path_buf
, PATH_MAX
, "%s\\%s", DNAME
, FNAME
);
2517 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2518 path_buf
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
2519 FILE_ATTRIBUTE_NORMAL
);
2520 torture_assert(torture
, ok
, "setup compression file");
2522 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, fh
,
2524 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
2526 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_LZNT1
),
2527 "compression attr not inherited by new file");
2528 smb2_util_close(tree
, fh
);
2530 snprintf(path_buf
, PATH_MAX
, "%s\\%s", DNAME
, FNAME2
);
2532 /* NO_COMPRESSION option should block inheritance */
2534 io
.in
.desired_access
= SEC_RIGHTS_FILE_ALL
;
2535 io
.in
.file_attributes
= FILE_ATTRIBUTE_NORMAL
;
2536 io
.in
.create_disposition
= NTCREATEX_DISP_CREATE
;
2537 io
.in
.create_options
= NTCREATEX_OPTIONS_NO_COMPRESSION
;
2538 io
.in
.share_access
=
2539 NTCREATEX_SHARE_ACCESS_DELETE
|
2540 NTCREATEX_SHARE_ACCESS_READ
|
2541 NTCREATEX_SHARE_ACCESS_WRITE
;
2542 io
.in
.fname
= path_buf
;
2544 status
= smb2_create(tree
, tmp_ctx
, &io
);
2545 torture_assert_ntstatus_ok(torture
, status
, "file create");
2547 fh
= io
.out
.file
.handle
;
2549 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, fh
,
2551 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
2553 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_NONE
),
2554 "compression attr inherited by NO_COMPRESSION file");
2555 smb2_util_close(tree
, fh
);
2558 snprintf(path_buf
, PATH_MAX
, "%s\\%s", DNAME
, DNAME
);
2560 io
.in
.desired_access
= SEC_RIGHTS_FILE_ALL
;
2561 io
.in
.file_attributes
= FILE_ATTRIBUTE_DIRECTORY
;
2562 io
.in
.create_disposition
= NTCREATEX_DISP_CREATE
;
2563 io
.in
.create_options
= (NTCREATEX_OPTIONS_NO_COMPRESSION
2564 | NTCREATEX_OPTIONS_DIRECTORY
);
2565 io
.in
.share_access
=
2566 NTCREATEX_SHARE_ACCESS_DELETE
|
2567 NTCREATEX_SHARE_ACCESS_READ
|
2568 NTCREATEX_SHARE_ACCESS_WRITE
;
2569 io
.in
.fname
= path_buf
;
2571 status
= smb2_create(tree
, tmp_ctx
, &io
);
2572 torture_assert_ntstatus_ok(torture
, status
, "dir create");
2574 dirh
= io
.out
.file
.handle
;
2576 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, dirh
,
2578 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
2580 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_NONE
),
2581 "compression attr inherited by NO_COMPRESSION dir");
2582 smb2_util_close(tree
, dirh
);
2583 smb2_deltree(tree
, DNAME
);
2585 talloc_free(tmp_ctx
);
2589 /* attempting to set compression via SetInfo should not stick */
2590 static bool test_ioctl_compress_set_file_attr(struct torture_context
*torture
,
2591 struct smb2_tree
*tree
)
2593 struct smb2_handle fh
;
2594 struct smb2_handle dirh
;
2595 union smb_fileinfo io
;
2596 union smb_setfileinfo set_io
;
2597 uint16_t compression_fmt
;
2599 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
2602 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2603 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
2604 FILE_ATTRIBUTE_NORMAL
);
2605 torture_assert(torture
, ok
, "setup compression file");
2607 status
= test_ioctl_compress_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
2609 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
2611 smb2_util_close(tree
, fh
);
2612 torture_skip(torture
, "FS compression not supported\n");
2616 io
.generic
.level
= RAW_FILEINFO_BASIC_INFORMATION
;
2617 io
.generic
.in
.file
.handle
= fh
;
2618 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
2619 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FILE");
2621 torture_assert(torture
,
2622 ((io
.basic_info
.out
.attrib
& FILE_ATTRIBUTE_COMPRESSED
) == 0),
2623 "compression attr before set");
2625 ZERO_STRUCT(set_io
);
2626 set_io
.generic
.level
= RAW_FILEINFO_BASIC_INFORMATION
;
2627 set_io
.basic_info
.in
.file
.handle
= fh
;
2628 set_io
.basic_info
.in
.create_time
= io
.basic_info
.out
.create_time
;
2629 set_io
.basic_info
.in
.access_time
= io
.basic_info
.out
.access_time
;
2630 set_io
.basic_info
.in
.write_time
= io
.basic_info
.out
.write_time
;
2631 set_io
.basic_info
.in
.change_time
= io
.basic_info
.out
.change_time
;
2632 set_io
.basic_info
.in
.attrib
= (io
.basic_info
.out
.attrib
2633 | FILE_ATTRIBUTE_COMPRESSED
);
2634 status
= smb2_setinfo_file(tree
, &set_io
);
2635 torture_assert_ntstatus_ok(torture
, status
, "SMB2_SETINFO_FILE");
2638 io
.generic
.level
= RAW_FILEINFO_BASIC_INFORMATION
;
2639 io
.generic
.in
.file
.handle
= fh
;
2640 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
2641 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FILE");
2643 torture_assert(torture
,
2644 ((io
.basic_info
.out
.attrib
& FILE_ATTRIBUTE_COMPRESSED
) == 0),
2645 "compression attr after set");
2647 smb2_util_close(tree
, fh
);
2648 smb2_deltree(tree
, DNAME
);
2649 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2650 DNAME
, &dirh
, 0, SEC_RIGHTS_FILE_ALL
,
2651 FILE_ATTRIBUTE_DIRECTORY
);
2652 torture_assert(torture
, ok
, "setup compression directory");
2655 io
.generic
.level
= RAW_FILEINFO_BASIC_INFORMATION
;
2656 io
.generic
.in
.file
.handle
= dirh
;
2657 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
2658 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FILE");
2660 torture_assert(torture
,
2661 ((io
.basic_info
.out
.attrib
& FILE_ATTRIBUTE_COMPRESSED
) == 0),
2662 "compression attr before set");
2664 ZERO_STRUCT(set_io
);
2665 set_io
.generic
.level
= RAW_FILEINFO_BASIC_INFORMATION
;
2666 set_io
.basic_info
.in
.file
.handle
= dirh
;
2667 set_io
.basic_info
.in
.create_time
= io
.basic_info
.out
.create_time
;
2668 set_io
.basic_info
.in
.access_time
= io
.basic_info
.out
.access_time
;
2669 set_io
.basic_info
.in
.write_time
= io
.basic_info
.out
.write_time
;
2670 set_io
.basic_info
.in
.change_time
= io
.basic_info
.out
.change_time
;
2671 set_io
.basic_info
.in
.attrib
= (io
.basic_info
.out
.attrib
2672 | FILE_ATTRIBUTE_COMPRESSED
);
2673 status
= smb2_setinfo_file(tree
, &set_io
);
2674 torture_assert_ntstatus_ok(torture
, status
, "SMB2_SETINFO_FILE");
2676 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, dirh
,
2678 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
2680 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_NONE
),
2681 "dir compression set after SetInfo");
2683 smb2_util_close(tree
, dirh
);
2684 talloc_free(tmp_ctx
);
2688 static bool test_ioctl_compress_perms(struct torture_context
*torture
,
2689 struct smb2_tree
*tree
)
2691 struct smb2_handle fh
;
2692 uint16_t compression_fmt
;
2693 union smb_fileinfo io
;
2695 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
2698 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2699 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
2700 FILE_ATTRIBUTE_NORMAL
);
2701 torture_assert(torture
, ok
, "setup compression file");
2703 status
= test_ioctl_compress_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
2705 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
2706 smb2_util_close(tree
, fh
);
2708 torture_skip(torture
, "FS compression not supported\n");
2711 /* attempt get compression without READ_ATTR permission */
2712 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2714 (SEC_RIGHTS_FILE_READ
& ~(SEC_FILE_READ_ATTRIBUTE
2715 | SEC_STD_READ_CONTROL
2716 | SEC_FILE_READ_EA
)),
2717 FILE_ATTRIBUTE_NORMAL
);
2718 torture_assert(torture
, ok
, "setup compression file");
2720 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, fh
,
2722 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
2723 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_NONE
),
2724 "compression set after create");
2725 smb2_util_close(tree
, fh
);
2727 /* set compression without WRITE_ATTR permission should succeed */
2728 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2730 (SEC_RIGHTS_FILE_WRITE
& ~(SEC_FILE_WRITE_ATTRIBUTE
2732 | SEC_FILE_WRITE_EA
)),
2733 FILE_ATTRIBUTE_NORMAL
);
2734 torture_assert(torture
, ok
, "setup compression file");
2736 status
= test_ioctl_compress_set(torture
, tmp_ctx
, tree
, fh
,
2737 COMPRESSION_FORMAT_DEFAULT
);
2738 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_COMPRESSION");
2739 smb2_util_close(tree
, fh
);
2741 ok
= test_setup_open(torture
, tree
, tmp_ctx
,
2742 FNAME
, &fh
, SEC_RIGHTS_FILE_ALL
,
2743 FILE_ATTRIBUTE_NORMAL
);
2744 torture_assert(torture
, ok
, "setup compression file");
2746 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
2747 io
.generic
.in
.file
.handle
= fh
;
2748 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
2749 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FILE");
2751 torture_assert(torture
,
2752 (io
.all_info2
.out
.attrib
& FILE_ATTRIBUTE_COMPRESSED
),
2753 "incorrect compression attr");
2754 smb2_util_close(tree
, fh
);
2756 /* attempt get compression without READ_DATA permission */
2757 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2759 (SEC_RIGHTS_FILE_READ
& ~SEC_FILE_READ_DATA
),
2760 FILE_ATTRIBUTE_NORMAL
);
2761 torture_assert(torture
, ok
, "setup compression file");
2763 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, fh
,
2765 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
2766 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_NONE
),
2767 "compression enabled after set");
2768 smb2_util_close(tree
, fh
);
2770 /* attempt get compression with only SYNCHRONIZE permission */
2771 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2773 SEC_STD_SYNCHRONIZE
,
2774 FILE_ATTRIBUTE_NORMAL
);
2775 torture_assert(torture
, ok
, "setup compression file");
2777 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, fh
,
2779 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
2780 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_NONE
),
2781 "compression not enabled after set");
2782 smb2_util_close(tree
, fh
);
2784 /* attempt to set compression without WRITE_DATA permission */
2785 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2787 (SEC_RIGHTS_FILE_WRITE
& (~SEC_FILE_WRITE_DATA
)),
2788 FILE_ATTRIBUTE_NORMAL
);
2789 torture_assert(torture
, ok
, "setup compression file");
2791 status
= test_ioctl_compress_set(torture
, tmp_ctx
, tree
, fh
,
2792 COMPRESSION_FORMAT_DEFAULT
);
2793 torture_assert_ntstatus_equal(torture
, status
,
2794 NT_STATUS_ACCESS_DENIED
,
2795 "FSCTL_SET_COMPRESSION permission");
2796 smb2_util_close(tree
, fh
);
2798 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2800 (SEC_RIGHTS_FILE_WRITE
& (~SEC_FILE_WRITE_DATA
)),
2801 FILE_ATTRIBUTE_NORMAL
);
2802 torture_assert(torture
, ok
, "setup compression file");
2804 status
= test_ioctl_compress_set(torture
, tmp_ctx
, tree
, fh
,
2805 COMPRESSION_FORMAT_NONE
);
2806 torture_assert_ntstatus_equal(torture
, status
,
2807 NT_STATUS_ACCESS_DENIED
,
2808 "FSCTL_SET_COMPRESSION permission");
2809 smb2_util_close(tree
, fh
);
2811 talloc_free(tmp_ctx
);
2815 static bool test_ioctl_compress_notsup_get(struct torture_context
*torture
,
2816 struct smb2_tree
*tree
)
2818 struct smb2_handle fh
;
2820 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
2822 uint16_t compression_fmt
;
2824 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2825 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
2826 FILE_ATTRIBUTE_NORMAL
);
2827 torture_assert(torture
, ok
, "setup compression file");
2829 /* skip if the server DOES support compression */
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 supported\n");
2839 * Despite not supporting compression, we should get a successful
2840 * response indicating that the file is uncompressed - like WS2016.
2842 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, fh
,
2844 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
2846 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_NONE
),
2847 "initial compression state not NONE");
2849 smb2_util_close(tree
, fh
);
2850 talloc_free(tmp_ctx
);
2854 static bool test_ioctl_compress_notsup_set(struct torture_context
*torture
,
2855 struct smb2_tree
*tree
)
2857 struct smb2_handle fh
;
2859 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
2862 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2863 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
2864 FILE_ATTRIBUTE_NORMAL
);
2865 torture_assert(torture
, ok
, "setup compression file");
2867 /* skip if the server DOES support compression */
2868 status
= test_ioctl_compress_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
2870 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
2872 smb2_util_close(tree
, fh
);
2873 torture_skip(torture
, "FS compression supported\n");
2876 status
= test_ioctl_compress_set(torture
, tmp_ctx
, tree
, fh
,
2877 COMPRESSION_FORMAT_DEFAULT
);
2878 torture_assert_ntstatus_equal(torture
, status
,
2879 NT_STATUS_NOT_SUPPORTED
,
2880 "FSCTL_SET_COMPRESSION default");
2883 * Despite not supporting compression, we should get a successful
2884 * response for set(COMPRESSION_FORMAT_NONE) - like WS2016 ReFS.
2886 status
= test_ioctl_compress_set(torture
, tmp_ctx
, tree
, fh
,
2887 COMPRESSION_FORMAT_NONE
);
2888 torture_assert_ntstatus_ok(torture
, status
,
2889 "FSCTL_SET_COMPRESSION none");
2891 smb2_util_close(tree
, fh
);
2892 talloc_free(tmp_ctx
);
2897 basic testing of the SMB2 FSCTL_QUERY_NETWORK_INTERFACE_INFO ioctl
2899 static bool test_ioctl_network_interface_info(struct torture_context
*torture
,
2900 struct smb2_tree
*tree
)
2902 union smb_ioctl ioctl
;
2903 struct smb2_handle fh
;
2905 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
2906 struct fsctl_net_iface_info net_iface
;
2907 enum ndr_err_code ndr_ret
;
2910 caps
= smb2cli_conn_server_capabilities(tree
->session
->transport
->conn
);
2911 if (!(caps
& SMB2_CAP_MULTI_CHANNEL
)) {
2912 torture_skip(torture
, "server doesn't support SMB2_CAP_MULTI_CHANNEL\n");
2916 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
2917 fh
.data
[0] = UINT64_MAX
;
2918 fh
.data
[1] = UINT64_MAX
;
2919 ioctl
.smb2
.in
.file
.handle
= fh
;
2920 ioctl
.smb2
.in
.function
= FSCTL_QUERY_NETWORK_INTERFACE_INFO
;
2921 ioctl
.smb2
.in
.max_response_size
= 0x10000; /* Windows client sets this to 64KiB */
2922 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
2924 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
2925 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_QUERY_NETWORK_INTERFACE_INFO");
2927 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
, &net_iface
,
2928 (ndr_pull_flags_fn_t
)ndr_pull_fsctl_net_iface_info
);
2929 torture_assert_ndr_success(torture
, ndr_ret
,
2930 "ndr_pull_fsctl_net_iface_info");
2932 ndr_print_debug((ndr_print_fn_t
)ndr_print_fsctl_net_iface_info
,
2933 "Network Interface Info", &net_iface
);
2935 talloc_free(tmp_ctx
);
2940 * Check whether all @fs_support_flags are set in the server's
2941 * RAW_QFS_ATTRIBUTE_INFORMATION FileSystemAttributes response.
2943 static NTSTATUS
test_ioctl_fs_supported(struct torture_context
*torture
,
2944 struct smb2_tree
*tree
,
2945 TALLOC_CTX
*mem_ctx
,
2946 struct smb2_handle
*fh
,
2947 uint64_t fs_support_flags
,
2951 union smb_fsinfo info
;
2954 info
.generic
.level
= RAW_QFS_ATTRIBUTE_INFORMATION
;
2955 info
.generic
.handle
= *fh
;
2956 status
= smb2_getinfo_fs(tree
, tree
, &info
);
2957 if (!NT_STATUS_IS_OK(status
)) {
2961 if ((info
.attribute_info
.out
.fs_attr
& fs_support_flags
)
2962 == fs_support_flags
) {
2967 return NT_STATUS_OK
;
2970 static NTSTATUS
test_ioctl_sparse_req(struct torture_context
*torture
,
2971 TALLOC_CTX
*mem_ctx
,
2972 struct smb2_tree
*tree
,
2973 struct smb2_handle fh
,
2976 union smb_ioctl ioctl
;
2981 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
2982 ioctl
.smb2
.in
.file
.handle
= fh
;
2983 ioctl
.smb2
.in
.function
= FSCTL_SET_SPARSE
;
2984 ioctl
.smb2
.in
.max_response_size
= 0;
2985 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
2986 set_sparse
= (set
? 0xFF : 0x0);
2987 ioctl
.smb2
.in
.out
.data
= &set_sparse
;
2988 ioctl
.smb2
.in
.out
.length
= sizeof(set_sparse
);
2990 status
= smb2_ioctl(tree
, mem_ctx
, &ioctl
.smb2
);
2994 static NTSTATUS
test_sparse_get(struct torture_context
*torture
,
2995 TALLOC_CTX
*mem_ctx
,
2996 struct smb2_tree
*tree
,
2997 struct smb2_handle fh
,
3000 union smb_fileinfo io
;
3004 io
.generic
.level
= RAW_FILEINFO_BASIC_INFORMATION
;
3005 io
.generic
.in
.file
.handle
= fh
;
3006 status
= smb2_getinfo_file(tree
, mem_ctx
, &io
);
3007 if (!NT_STATUS_IS_OK(status
)) {
3010 *_is_sparse
= !!(io
.basic_info
.out
.attrib
& FILE_ATTRIBUTE_SPARSE
);
3015 static bool test_ioctl_sparse_file_flag(struct torture_context
*torture
,
3016 struct smb2_tree
*tree
)
3018 struct smb2_handle fh
;
3019 union smb_fileinfo io
;
3021 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
3025 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
3026 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
3027 FILE_ATTRIBUTE_NORMAL
);
3028 torture_assert(torture
, ok
, "setup file");
3030 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
3031 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
3032 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
3034 smb2_util_close(tree
, fh
);
3035 torture_skip(torture
, "Sparse files not supported\n");
3039 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
3040 io
.generic
.in
.file
.handle
= fh
;
3041 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
3042 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FILE");
3044 torture_assert(torture
,
3045 ((io
.all_info2
.out
.attrib
& FILE_ATTRIBUTE_SPARSE
) == 0),
3046 "sparse attr before set");
3048 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, true);
3049 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
3051 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
3052 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
3053 torture_assert(torture
, is_sparse
, "no sparse attr after set");
3055 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, false);
3056 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
3058 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
3059 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
3060 torture_assert(torture
, !is_sparse
, "sparse attr after unset");
3062 smb2_util_close(tree
, fh
);
3063 talloc_free(tmp_ctx
);
3067 static bool test_ioctl_sparse_file_attr(struct torture_context
*torture
,
3068 struct smb2_tree
*tree
)
3070 struct smb2_handle fh
;
3072 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
3076 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
3077 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
3078 (FILE_ATTRIBUTE_NORMAL
| FILE_ATTRIBUTE_SPARSE
));
3079 torture_assert(torture
, ok
, "setup file");
3081 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
3082 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
3083 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
3085 smb2_util_close(tree
, fh
);
3086 torture_skip(torture
, "Sparse files not supported\n");
3089 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
3090 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
3091 torture_assert(torture
, !is_sparse
, "sparse attr on open");
3093 smb2_util_close(tree
, fh
);
3094 talloc_free(tmp_ctx
);
3098 static bool test_ioctl_sparse_dir_flag(struct torture_context
*torture
,
3099 struct smb2_tree
*tree
)
3101 struct smb2_handle dirh
;
3103 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
3106 smb2_deltree(tree
, DNAME
);
3107 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
3108 DNAME
, &dirh
, 0, SEC_RIGHTS_FILE_ALL
,
3109 FILE_ATTRIBUTE_DIRECTORY
);
3110 torture_assert(torture
, ok
, "setup sparse directory");
3112 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &dirh
,
3113 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
3114 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
3116 smb2_util_close(tree
, dirh
);
3117 smb2_deltree(tree
, DNAME
);
3118 torture_skip(torture
, "Sparse files not supported\n");
3121 /* set sparse dir should fail, check for 2k12 & 2k8 response */
3122 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, dirh
, true);
3123 torture_assert_ntstatus_equal(torture
, status
,
3124 NT_STATUS_INVALID_PARAMETER
,
3125 "dir FSCTL_SET_SPARSE status");
3127 smb2_util_close(tree
, dirh
);
3128 smb2_deltree(tree
, DNAME
);
3129 talloc_free(tmp_ctx
);
3134 * FSCTL_SET_SPARSE can be sent with (already tested) or without a SetSparse
3135 * buffer to indicate whether the flag should be set or cleared. When sent
3136 * without a buffer, it must be handled as if SetSparse=TRUE.
3138 static bool test_ioctl_sparse_set_nobuf(struct torture_context
*torture
,
3139 struct smb2_tree
*tree
)
3141 struct smb2_handle fh
;
3142 union smb_ioctl ioctl
;
3144 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
3148 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
3149 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
3150 FILE_ATTRIBUTE_NORMAL
);
3151 torture_assert(torture
, ok
, "setup file");
3153 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
3154 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
3155 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
3157 smb2_util_close(tree
, fh
);
3158 torture_skip(torture
, "Sparse files not supported\n");
3161 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
3162 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
3163 torture_assert(torture
, !is_sparse
, "sparse attr before set");
3166 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
3167 ioctl
.smb2
.in
.file
.handle
= fh
;
3168 ioctl
.smb2
.in
.function
= FSCTL_SET_SPARSE
;
3169 ioctl
.smb2
.in
.max_response_size
= 0;
3170 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
3171 /* ioctl.smb2.in.out is zeroed, no SetSparse buffer */
3173 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
3174 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
3176 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
3177 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
3178 torture_assert(torture
, is_sparse
, "no sparse attr after set");
3180 /* second non-SetSparse request shouldn't toggle sparse */
3182 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
3183 ioctl
.smb2
.in
.file
.handle
= fh
;
3184 ioctl
.smb2
.in
.function
= FSCTL_SET_SPARSE
;
3185 ioctl
.smb2
.in
.max_response_size
= 0;
3186 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
3188 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
3189 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
3191 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
3192 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
3193 torture_assert(torture
, is_sparse
, "no sparse attr after 2nd set");
3195 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, false);
3196 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
3198 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
3199 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
3200 torture_assert(torture
, !is_sparse
, "sparse attr after unset");
3202 smb2_util_close(tree
, fh
);
3203 talloc_free(tmp_ctx
);
3207 static bool test_ioctl_sparse_set_oversize(struct torture_context
*torture
,
3208 struct smb2_tree
*tree
)
3210 struct smb2_handle fh
;
3211 union smb_ioctl ioctl
;
3213 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
3218 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
3219 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
3220 FILE_ATTRIBUTE_NORMAL
);
3221 torture_assert(torture
, ok
, "setup file");
3223 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
3224 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
3225 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
3227 smb2_util_close(tree
, fh
);
3228 torture_skip(torture
, "Sparse files not supported\n");
3231 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
3232 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
3233 torture_assert(torture
, !is_sparse
, "sparse attr before set");
3236 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
3237 ioctl
.smb2
.in
.file
.handle
= fh
;
3238 ioctl
.smb2
.in
.function
= FSCTL_SET_SPARSE
;
3239 ioctl
.smb2
.in
.max_response_size
= 0;
3240 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
3243 * Attach a request buffer larger than FILE_SET_SPARSE_BUFFER
3244 * Windows still successfully processes the request.
3247 buf
[0] = 0xFF; /* attempt to set sparse */
3248 ioctl
.smb2
.in
.out
.data
= buf
;
3249 ioctl
.smb2
.in
.out
.length
= ARRAY_SIZE(buf
);
3251 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
3252 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
3254 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
3255 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
3256 torture_assert(torture
, is_sparse
, "no sparse attr after set");
3259 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
3260 ioctl
.smb2
.in
.file
.handle
= fh
;
3261 ioctl
.smb2
.in
.function
= FSCTL_SET_SPARSE
;
3262 ioctl
.smb2
.in
.max_response_size
= 0;
3263 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
3265 ZERO_ARRAY(buf
); /* clear sparse */
3266 ioctl
.smb2
.in
.out
.data
= buf
;
3267 ioctl
.smb2
.in
.out
.length
= ARRAY_SIZE(buf
);
3269 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
3270 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
3272 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
3273 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
3274 torture_assert(torture
, !is_sparse
, "sparse attr after clear");
3276 smb2_util_close(tree
, fh
);
3277 talloc_free(tmp_ctx
);
3281 static NTSTATUS
test_ioctl_qar_req(struct torture_context
*torture
,
3282 TALLOC_CTX
*mem_ctx
,
3283 struct smb2_tree
*tree
,
3284 struct smb2_handle fh
,
3287 struct file_alloced_range_buf
**_rsp
,
3288 uint64_t *_rsp_count
)
3290 union smb_ioctl ioctl
;
3292 enum ndr_err_code ndr_ret
;
3293 struct file_alloced_range_buf far_buf
;
3294 struct file_alloced_range_buf
*far_rsp
= NULL
;
3295 uint64_t far_count
= 0;
3297 TALLOC_CTX
*tmp_ctx
= talloc_new(mem_ctx
);
3298 if (tmp_ctx
== NULL
) {
3299 return NT_STATUS_NO_MEMORY
;
3303 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
3304 ioctl
.smb2
.in
.file
.handle
= fh
;
3305 ioctl
.smb2
.in
.function
= FSCTL_QUERY_ALLOCATED_RANGES
;
3306 ioctl
.smb2
.in
.max_response_size
= 1024;
3307 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
3309 far_buf
.file_off
= req_off
;
3310 far_buf
.len
= req_len
;
3312 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
3314 (ndr_push_flags_fn_t
)ndr_push_file_alloced_range_buf
);
3315 if (ndr_ret
!= NDR_ERR_SUCCESS
) {
3316 status
= NT_STATUS_UNSUCCESSFUL
;
3320 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
3321 if (!NT_STATUS_IS_OK(status
)) {
3325 if (ioctl
.smb2
.out
.out
.length
== 0) {
3329 if ((ioctl
.smb2
.out
.out
.length
% sizeof(far_buf
)) != 0) {
3330 torture_comment(torture
, "invalid qry_alloced rsp len: %zd:",
3331 ioctl
.smb2
.out
.out
.length
);
3332 status
= NT_STATUS_INVALID_VIEW_SIZE
;
3336 far_count
= (ioctl
.smb2
.out
.out
.length
/ sizeof(far_buf
));
3337 far_rsp
= talloc_array(mem_ctx
, struct file_alloced_range_buf
,
3339 if (far_rsp
== NULL
) {
3340 status
= NT_STATUS_NO_MEMORY
;
3344 for (i
= 0; i
< far_count
; i
++) {
3345 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
3347 (ndr_pull_flags_fn_t
)ndr_pull_file_alloced_range_buf
);
3348 if (ndr_ret
!= NDR_ERR_SUCCESS
) {
3349 status
= NT_STATUS_UNSUCCESSFUL
;
3352 /* move to next buffer */
3353 ioctl
.smb2
.out
.out
.data
+= sizeof(far_buf
);
3354 ioctl
.smb2
.out
.out
.length
-= sizeof(far_buf
);
3359 *_rsp_count
= far_count
;
3360 status
= NT_STATUS_OK
;
3362 talloc_free(tmp_ctx
);
3366 static bool test_ioctl_sparse_qar(struct torture_context
*torture
,
3367 struct smb2_tree
*tree
)
3369 struct smb2_handle fh
;
3371 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
3374 struct file_alloced_range_buf
*far_rsp
= NULL
;
3375 uint64_t far_count
= 0;
3377 /* zero length file, shouldn't have any ranges */
3378 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
3379 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
3380 FILE_ATTRIBUTE_NORMAL
);
3381 torture_assert(torture
, ok
, "setup file");
3383 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
3384 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
3385 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
3387 smb2_util_close(tree
, fh
);
3388 torture_skip(torture
, "Sparse files not supported\n");
3391 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
3392 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
3393 torture_assert(torture
, !is_sparse
, "sparse attr before set");
3395 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
3400 torture_assert_ntstatus_ok(torture
, status
,
3401 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3402 torture_assert_u64_equal(torture
, far_count
, 0,
3403 "unexpected response len");
3405 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
3410 torture_assert_ntstatus_ok(torture
, status
,
3411 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3412 torture_assert_u64_equal(torture
, far_count
, 0,
3413 "unexpected response len");
3415 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, true);
3416 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
3418 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
3419 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
3420 torture_assert(torture
, is_sparse
, "no sparse attr after set");
3422 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
3427 torture_assert_ntstatus_ok(torture
, status
,
3428 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3429 torture_assert_u64_equal(torture
, far_count
, 0,
3430 "unexpected response len");
3432 /* write into the (now) sparse file at 4k offset */
3433 ok
= write_pattern(torture
, tree
, tmp_ctx
, fh
,
3436 4096); /* pattern offset */
3437 torture_assert(torture
, ok
, "write pattern");
3440 * Query range before write off. Whether it's allocated or not is FS
3441 * dependent. NTFS deallocates chunks in 64K increments, but others
3442 * (e.g. XFS, Btrfs, etc.) may deallocate 4K chunks.
3444 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
3449 torture_assert_ntstatus_ok(torture
, status
,
3450 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3451 if (far_count
== 0) {
3452 torture_comment(torture
, "FS deallocated 4K chunk\n");
3454 /* expect fully allocated */
3455 torture_assert_u64_equal(torture
, far_count
, 1,
3456 "unexpected response len");
3457 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0, "far offset");
3458 torture_assert_u64_equal(torture
, far_rsp
[0].len
, 4096, "far len");
3462 * Query range before and past write, it should be allocated up to the
3465 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
3470 torture_assert_ntstatus_ok(torture
, status
,
3471 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3472 torture_assert_u64_equal(torture
, far_count
, 1,
3473 "unexpected response len");
3475 if (far_rsp
[0].file_off
== 4096) {
3476 /* 4K chunk unallocated */
3477 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 4096, "far offset");
3478 torture_assert_u64_equal(torture
, far_rsp
[0].len
, 1024, "far len");
3480 /* expect fully allocated */
3481 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0, "far offset");
3482 torture_assert_u64_equal(torture
, far_rsp
[0].len
, 5120, "far len");
3485 smb2_util_close(tree
, fh
);
3486 talloc_free(tmp_ctx
);
3490 static bool test_ioctl_sparse_qar_malformed(struct torture_context
*torture
,
3491 struct smb2_tree
*tree
)
3493 struct smb2_handle fh
;
3494 union smb_ioctl ioctl
;
3495 struct file_alloced_range_buf far_buf
;
3497 enum ndr_err_code ndr_ret
;
3498 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
3502 /* zero length file, shouldn't have any ranges */
3503 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
3504 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
3505 FILE_ATTRIBUTE_NORMAL
);
3506 torture_assert(torture
, ok
, "setup file");
3508 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
3509 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
3510 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
3512 smb2_util_close(tree
, fh
);
3513 torture_skip(torture
, "Sparse files not supported\n");
3516 /* no allocated ranges, no space for range response, should pass */
3518 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
3519 ioctl
.smb2
.in
.file
.handle
= fh
;
3520 ioctl
.smb2
.in
.function
= FSCTL_QUERY_ALLOCATED_RANGES
;
3521 ioctl
.smb2
.in
.max_response_size
= 0;
3522 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
3524 far_buf
.file_off
= 0;
3526 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
3528 (ndr_push_flags_fn_t
)ndr_push_file_alloced_range_buf
);
3529 torture_assert_ndr_success(torture
, ndr_ret
, "push far ndr buf");
3531 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
3532 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_QUERY_ALLOCATED_RANGES");
3534 /* write into the file at 4k offset */
3535 ok
= write_pattern(torture
, tree
, tmp_ctx
, fh
,
3538 0); /* pattern offset */
3539 torture_assert(torture
, ok
, "write pattern");
3541 /* allocated range, no space for range response, should fail */
3542 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
3543 torture_assert_ntstatus_equal(torture
, status
,
3544 NT_STATUS_BUFFER_TOO_SMALL
, "qar no space");
3546 /* oversize (2x) file_alloced_range_buf in request, should pass */
3547 ioctl
.smb2
.in
.max_response_size
= 1024;
3548 old_len
= ioctl
.smb2
.in
.out
.length
;
3549 ok
= data_blob_realloc(tmp_ctx
, &ioctl
.smb2
.in
.out
,
3550 (ioctl
.smb2
.in
.out
.length
* 2));
3551 torture_assert(torture
, ok
, "2x data buffer");
3552 memcpy(ioctl
.smb2
.in
.out
.data
+ old_len
, ioctl
.smb2
.in
.out
.data
,
3554 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
3555 torture_assert_ntstatus_ok(torture
, status
, "qar too big");
3557 /* no file_alloced_range_buf in request, should fail */
3558 data_blob_free(&ioctl
.smb2
.in
.out
);
3559 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
3560 torture_assert_ntstatus_equal(torture
, status
,
3561 NT_STATUS_INVALID_PARAMETER
, "qar empty");
3567 * 2.3.57 FSCTL_SET_ZERO_DATA Request
3569 * How an implementation zeros data within a file is implementation-dependent.
3570 * A file system MAY choose to deallocate regions of disk space that have been
3573 * ... NTFS might deallocate disk space in the file if the file is stored on an
3574 * NTFS volume, and the file is sparse or compressed. It will free any allocated
3575 * space in chunks of 64 kilobytes that begin at an offset that is a multiple of
3576 * 64 kilobytes. Other bytes in the file (prior to the first freed 64-kilobyte
3577 * chunk and after the last freed 64-kilobyte chunk) will be zeroed but not
3580 static NTSTATUS
test_ioctl_zdata_req(struct torture_context
*torture
,
3581 TALLOC_CTX
*mem_ctx
,
3582 struct smb2_tree
*tree
,
3583 struct smb2_handle fh
,
3585 int64_t beyond_final_zero
)
3587 union smb_ioctl ioctl
;
3589 enum ndr_err_code ndr_ret
;
3590 struct file_zero_data_info zdata_info
;
3591 TALLOC_CTX
*tmp_ctx
= talloc_new(mem_ctx
);
3592 if (tmp_ctx
== NULL
) {
3593 return NT_STATUS_NO_MEMORY
;
3597 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
3598 ioctl
.smb2
.in
.file
.handle
= fh
;
3599 ioctl
.smb2
.in
.function
= FSCTL_SET_ZERO_DATA
;
3600 ioctl
.smb2
.in
.max_response_size
= 0;
3601 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
3603 zdata_info
.file_off
= off
;
3604 zdata_info
.beyond_final_zero
= beyond_final_zero
;
3606 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
3608 (ndr_push_flags_fn_t
)ndr_push_file_zero_data_info
);
3609 if (ndr_ret
!= NDR_ERR_SUCCESS
) {
3610 status
= NT_STATUS_UNSUCCESSFUL
;
3614 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
3615 if (!NT_STATUS_IS_OK(status
)) {
3619 status
= NT_STATUS_OK
;
3621 talloc_free(tmp_ctx
);
3625 static bool test_ioctl_sparse_punch(struct torture_context
*torture
,
3626 struct smb2_tree
*tree
)
3628 struct smb2_handle fh
;
3630 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
3633 struct file_alloced_range_buf
*far_rsp
= NULL
;
3634 uint64_t far_count
= 0;
3636 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
3637 FNAME
, &fh
, 4096, SEC_RIGHTS_FILE_ALL
,
3638 FILE_ATTRIBUTE_NORMAL
);
3639 torture_assert(torture
, ok
, "setup file");
3641 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
3642 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
3643 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
3645 smb2_util_close(tree
, fh
);
3646 torture_skip(torture
, "Sparse files not supported\n");
3649 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
3650 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
3651 torture_assert(torture
, !is_sparse
, "sparse attr before set");
3653 /* zero (hole-punch) the data, without sparse flag */
3654 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
3656 4096); /* beyond_final_zero */
3657 torture_assert_ntstatus_ok(torture
, status
, "zero_data");
3659 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
3664 torture_assert_ntstatus_ok(torture
, status
,
3665 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3666 torture_assert_u64_equal(torture
, far_count
, 1,
3667 "unexpected response len");
3669 /* expect fully allocated */
3670 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0,
3671 "unexpected far off");
3672 torture_assert_u64_equal(torture
, far_rsp
[0].len
, 4096,
3673 "unexpected far len");
3674 /* check that the data is now zeroed */
3675 ok
= check_zero(torture
, tree
, tmp_ctx
, fh
, 0, 4096);
3676 torture_assert(torture
, ok
, "non-sparse zeroed range");
3679 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, true);
3680 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
3682 /* still fully allocated on NTFS, see note below for Samba */
3683 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
3688 torture_assert_ntstatus_ok(torture
, status
,
3689 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3691 * FS specific: Samba uses PUNCH_HOLE to zero the range, and
3692 * subsequently uses fallocate() to allocate the punched range if the
3693 * file is marked non-sparse and "strict allocate" is enabled. In both
3694 * cases, the zeroed range will not be detected by SEEK_DATA, so the
3695 * range won't be present in QAR responses until the file is marked
3698 if (far_count
== 0) {
3699 torture_comment(torture
, "non-sparse zeroed range disappeared "
3700 "after marking sparse\n");
3702 /* NTFS: range remains fully allocated */
3703 torture_assert_u64_equal(torture
, far_count
, 1,
3704 "unexpected response len");
3705 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0,
3706 "unexpected far off");
3707 torture_assert_u64_equal(torture
, far_rsp
[0].len
, 4096,
3708 "unexpected far len");
3711 /* zero (hole-punch) the data, _with_ sparse flag */
3712 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
3714 4096); /* beyond_final_zero */
3715 torture_assert_ntstatus_ok(torture
, status
, "zero_data");
3717 /* the range should no longer be alloced */
3718 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
3723 torture_assert_ntstatus_ok(torture
, status
,
3724 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3725 torture_assert_u64_equal(torture
, far_count
, 0,
3726 "unexpected response len");
3728 ok
= check_zero(torture
, tree
, tmp_ctx
, fh
, 0, 4096);
3729 torture_assert(torture
, ok
, "sparse zeroed range");
3731 /* remove sparse flag, this should "unsparse" the zeroed range */
3732 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, false);
3733 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
3735 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
3740 torture_assert_ntstatus_ok(torture
, status
,
3741 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3742 torture_assert_u64_equal(torture
, far_count
, 1,
3743 "unexpected response len");
3744 /* expect fully allocated */
3745 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0,
3746 "unexpected far off");
3747 torture_assert_u64_equal(torture
, far_rsp
[0].len
, 4096,
3748 "unexpected far len");
3750 ok
= check_zero(torture
, tree
, tmp_ctx
, fh
, 0, 4096);
3751 torture_assert(torture
, ok
, "sparse zeroed range");
3753 smb2_util_close(tree
, fh
);
3754 talloc_free(tmp_ctx
);
3759 * Find the point at which a zeroed range in a sparse file is deallocated by the
3760 * underlying filesystem. NTFS on Windows Server 2012 deallocates chunks in 64k
3761 * increments. Also check whether zeroed neighbours are merged for deallocation.
3763 static bool test_ioctl_sparse_hole_dealloc(struct torture_context
*torture
,
3764 struct smb2_tree
*tree
)
3766 struct smb2_handle fh
;
3768 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
3772 uint64_t dealloc_chunk_len
= 0;
3773 struct file_alloced_range_buf
*far_rsp
= NULL
;
3774 uint64_t far_count
= 0;
3776 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
3777 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
3778 FILE_ATTRIBUTE_NORMAL
);
3779 torture_assert(torture
, ok
, "setup file 1");
3781 /* check for FS sparse file */
3782 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
3783 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
3784 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
3786 smb2_util_close(tree
, fh
);
3787 torture_skip(torture
, "Sparse files not supported\n");
3791 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, true);
3792 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
3794 file_size
= 1024 * 1024;
3796 ok
= write_pattern(torture
, tree
, tmp_ctx
, fh
,
3798 file_size
, /* len */
3799 0); /* pattern offset */
3800 torture_assert(torture
, ok
, "write pattern");
3802 /* check allocated ranges, should be fully allocated */
3803 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
3805 file_size
, /* len */
3808 torture_assert_ntstatus_ok(torture
, status
,
3809 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3810 torture_assert_u64_equal(torture
, far_count
, 1,
3811 "unexpected response len");
3812 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0,
3813 "unexpected far off");
3814 torture_assert_u64_equal(torture
, far_rsp
[0].len
, file_size
,
3815 "unexpected far len");
3817 /* punch holes in sizes of 1k increments */
3818 for (hlen
= 0; hlen
<= file_size
; hlen
+= 4096) {
3820 /* punch a hole from zero to the current increment */
3821 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
3823 hlen
); /* beyond_final_zero */
3824 torture_assert_ntstatus_ok(torture
, status
, "zero_data");
3826 /* ensure hole is zeroed, and pattern is consistent */
3827 ok
= check_zero(torture
, tree
, tmp_ctx
, fh
, 0, hlen
);
3828 torture_assert(torture
, ok
, "sparse zeroed range");
3830 ok
= check_pattern(torture
, tree
, tmp_ctx
, fh
, hlen
,
3831 file_size
- hlen
, hlen
);
3832 torture_assert(torture
, ok
, "allocated pattern range");
3834 /* Check allocated ranges, hole might have been deallocated */
3835 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
3837 file_size
, /* len */
3840 torture_assert_ntstatus_ok(torture
, status
,
3841 "FSCTL_QUERY_ALLOCATED_RANGES");
3842 if ((hlen
== file_size
) && (far_count
== 0)) {
3843 /* hole covered entire file, deallocation occurred */
3844 dealloc_chunk_len
= file_size
;
3848 torture_assert_u64_equal(torture
, far_count
, 1,
3849 "unexpected response len");
3850 if (far_rsp
[0].file_off
!= 0) {
3852 * We now know the hole punch length needed to trigger a
3853 * deallocation on this FS...
3855 dealloc_chunk_len
= hlen
;
3856 torture_comment(torture
, "hole punch %ju@0 resulted in "
3857 "deallocation of %ju@0\n",
3859 (uintmax_t)far_rsp
[0].file_off
);
3860 torture_assert_u64_equal(torture
,
3861 file_size
- far_rsp
[0].len
,
3862 far_rsp
[0].file_off
,
3863 "invalid alloced range");
3868 if (dealloc_chunk_len
== 0) {
3869 torture_comment(torture
, "strange, this FS never deallocates"
3870 "zeroed ranges in sparse files\n");
3871 return true; /* FS specific, not a failure */
3875 * Check whether deallocation occurs when the (now known)
3876 * deallocation chunk size is punched via two ZERO_DATA requests.
3877 * I.e. Does the FS merge the two ranges and deallocate the chunk?
3878 * NTFS on Windows Server 2012 does not.
3880 ok
= write_pattern(torture
, tree
, tmp_ctx
, fh
,
3882 file_size
, /* len */
3883 0); /* pattern offset */
3884 torture_assert(torture
, ok
, "write pattern");
3886 /* divide dealloc chunk size by two, to use as punch length */
3887 hlen
= dealloc_chunk_len
>> 1;
3890 * /half of dealloc chunk size 1M\
3892 * /offset 0 | /dealloc chunk size |
3893 * |------------------ |-------------------|-------------------|
3894 * | zeroed, 1st punch | zeroed, 2nd punch | existing pattern |
3896 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
3898 hlen
); /* beyond final zero */
3899 torture_assert_ntstatus_ok(torture
, status
, "zero_data");
3901 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
3903 dealloc_chunk_len
); /* beyond final */
3904 torture_assert_ntstatus_ok(torture
, status
, "zero_data");
3906 /* ensure holes are zeroed, and pattern is consistent */
3907 ok
= check_zero(torture
, tree
, tmp_ctx
, fh
, 0, dealloc_chunk_len
);
3908 torture_assert(torture
, ok
, "sparse zeroed range");
3910 ok
= check_pattern(torture
, tree
, tmp_ctx
, fh
, dealloc_chunk_len
,
3911 file_size
- dealloc_chunk_len
, dealloc_chunk_len
);
3912 torture_assert(torture
, ok
, "allocated pattern range");
3914 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
3916 file_size
, /* len */
3919 torture_assert_ntstatus_ok(torture
, status
,
3920 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3922 if ((far_count
== 0) && (dealloc_chunk_len
== file_size
)) {
3923 torture_comment(torture
, "holes merged for deallocation of "
3927 torture_assert_u64_equal(torture
, far_count
, 1,
3928 "unexpected response len");
3929 if (far_rsp
[0].file_off
== dealloc_chunk_len
) {
3930 torture_comment(torture
, "holes merged for deallocation of "
3931 "%ju chunk\n", (uintmax_t)dealloc_chunk_len
);
3932 torture_assert_u64_equal(torture
,
3933 file_size
- far_rsp
[0].len
,
3934 far_rsp
[0].file_off
,
3935 "invalid alloced range");
3937 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0,
3938 "unexpected deallocation");
3939 torture_comment(torture
, "holes not merged for deallocation\n");
3942 smb2_util_close(tree
, fh
);
3945 * Check whether an unwritten range is allocated when a sparse file is
3946 * written to at an offset past the dealloc chunk size:
3948 * /dealloc chunk size
3950 * |------------------ |-------------------|
3951 * | unwritten | pattern |
3953 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
3954 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
3955 FILE_ATTRIBUTE_NORMAL
);
3956 torture_assert(torture
, ok
, "setup file 1");
3959 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, true);
3960 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
3962 ok
= write_pattern(torture
, tree
, tmp_ctx
, fh
,
3963 dealloc_chunk_len
, /* off */
3965 dealloc_chunk_len
); /* pattern offset */
3966 torture_assert(torture
, ok
, "write pattern");
3968 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
3970 dealloc_chunk_len
+ 1024, /* len */
3973 torture_assert_ntstatus_ok(torture
, status
,
3974 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3975 torture_assert_u64_equal(torture
, far_count
, 1,
3976 "unexpected response len");
3977 if (far_rsp
[0].file_off
== 0) {
3978 torture_assert_u64_equal(torture
, far_rsp
[0].len
,
3979 dealloc_chunk_len
+ 1024,
3980 "unexpected far len");
3981 torture_comment(torture
, "unwritten range fully allocated\n");
3983 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, dealloc_chunk_len
,
3984 "unexpected deallocation");
3985 torture_assert_u64_equal(torture
, far_rsp
[0].len
, 1024,
3986 "unexpected far len");
3987 torture_comment(torture
, "unwritten range not allocated\n");
3990 ok
= check_zero(torture
, tree
, tmp_ctx
, fh
, 0, dealloc_chunk_len
);
3991 torture_assert(torture
, ok
, "sparse zeroed range");
3993 ok
= check_pattern(torture
, tree
, tmp_ctx
, fh
, dealloc_chunk_len
,
3994 1024, dealloc_chunk_len
);
3995 torture_assert(torture
, ok
, "allocated pattern range");
3997 /* unsparse, should now be fully allocated */
3998 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, false);
3999 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
4001 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
4003 dealloc_chunk_len
+ 1024, /* len */
4006 torture_assert_ntstatus_ok(torture
, status
,
4007 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4008 torture_assert_u64_equal(torture
, far_count
, 1,
4009 "unexpected response len");
4010 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0,
4011 "unexpected deallocation");
4012 torture_assert_u64_equal(torture
, far_rsp
[0].len
,
4013 dealloc_chunk_len
+ 1024,
4014 "unexpected far len");
4016 smb2_util_close(tree
, fh
);
4017 talloc_free(tmp_ctx
);
4021 /* check whether a file with compression and sparse attrs can be deallocated */
4022 static bool test_ioctl_sparse_compressed(struct torture_context
*torture
,
4023 struct smb2_tree
*tree
)
4025 struct smb2_handle fh
;
4027 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
4029 uint64_t file_size
= 1024 * 1024;
4030 struct file_alloced_range_buf
*far_rsp
= NULL
;
4031 uint64_t far_count
= 0;
4033 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
4034 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
4035 FILE_ATTRIBUTE_NORMAL
);
4036 torture_assert(torture
, ok
, "setup file 1");
4038 /* check for FS sparse file and compression support */
4039 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
4040 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
4041 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
4043 smb2_util_close(tree
, fh
);
4044 torture_skip(torture
, "Sparse files not supported\n");
4047 status
= test_ioctl_compress_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
4049 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
4051 smb2_util_close(tree
, fh
);
4052 torture_skip(torture
, "FS compression not supported\n");
4055 /* set compression and write some data */
4056 status
= test_ioctl_compress_set(torture
, tmp_ctx
, tree
, fh
,
4057 COMPRESSION_FORMAT_DEFAULT
);
4058 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_COMPRESSION");
4060 ok
= write_pattern(torture
, tree
, tmp_ctx
, fh
,
4062 file_size
, /* len */
4063 0); /* pattern offset */
4064 torture_assert(torture
, ok
, "write pattern");
4066 /* set sparse - now sparse and compressed */
4067 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, true);
4068 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
4070 /* check allocated ranges, should be fully alloced */
4071 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
4073 file_size
, /* len */
4076 torture_assert_ntstatus_ok(torture
, status
,
4077 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4078 torture_assert_u64_equal(torture
, far_count
, 1,
4079 "unexpected response len");
4080 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0,
4081 "unexpected far off");
4082 torture_assert_u64_equal(torture
, far_rsp
[0].len
, file_size
,
4083 "unexpected far len");
4085 /* zero (hole-punch) all data, with sparse and compressed attrs */
4086 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
4088 file_size
); /* beyond_final_zero */
4089 torture_assert_ntstatus_ok(torture
, status
, "zero_data");
4092 * Windows Server 2012 still deallocates a zeroed range when a sparse
4093 * file carries the compression attribute.
4095 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
4097 file_size
, /* len */
4100 torture_assert_ntstatus_ok(torture
, status
,
4101 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4102 if (far_count
== 0) {
4103 torture_comment(torture
, "sparse & compressed file "
4104 "deallocated after hole-punch\n");
4106 torture_assert_u64_equal(torture
, far_count
, 1,
4107 "unexpected response len");
4108 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0,
4109 "unexpected far off");
4110 torture_assert_u64_equal(torture
, far_rsp
[0].len
, file_size
,
4111 "unexpected far len");
4112 torture_comment(torture
, "sparse & compressed file fully "
4113 "allocated after hole-punch\n");
4116 smb2_util_close(tree
, fh
);
4117 talloc_free(tmp_ctx
);
4122 * Create a sparse file, then attempt to copy unallocated and allocated ranges
4123 * into a target file using FSCTL_SRV_COPYCHUNK.
4125 static bool test_ioctl_sparse_copy_chunk(struct torture_context
*torture
,
4126 struct smb2_tree
*tree
)
4128 struct smb2_handle src_h
;
4129 struct smb2_handle dest_h
;
4131 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
4133 uint64_t dealloc_chunk_len
= 64 * 1024; /* Windows 2012 */
4134 struct file_alloced_range_buf
*far_rsp
= NULL
;
4135 uint64_t far_count
= 0;
4136 union smb_ioctl ioctl
;
4137 struct srv_copychunk_copy cc_copy
;
4138 struct srv_copychunk_rsp cc_rsp
;
4139 enum ndr_err_code ndr_ret
;
4141 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
4142 FNAME
, &src_h
, 0, SEC_RIGHTS_FILE_ALL
,
4143 FILE_ATTRIBUTE_NORMAL
);
4144 torture_assert(torture
, ok
, "setup file");
4146 /* check for FS sparse file support */
4147 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &src_h
,
4148 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
4149 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
4150 smb2_util_close(tree
, src_h
);
4152 torture_skip(torture
, "Sparse files not supported\n");
4155 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
4158 &src_h
, 0, /* src file */
4159 SEC_RIGHTS_FILE_ALL
,
4161 &dest_h
, 0, /* dest file */
4162 SEC_RIGHTS_FILE_ALL
,
4165 torture_assert(torture
, ok
, "setup copy chunk error");
4168 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, src_h
, true);
4169 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
4171 /* start after dealloc_chunk_len, to create an unwritten sparse range */
4172 ok
= write_pattern(torture
, tree
, tmp_ctx
, src_h
,
4173 dealloc_chunk_len
, /* off */
4175 dealloc_chunk_len
); /* pattern offset */
4176 torture_assert(torture
, ok
, "write pattern");
4178 /* Skip test if 64k chunk is allocated - FS specific */
4179 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, src_h
,
4181 dealloc_chunk_len
+ 1024, /* len */
4184 torture_assert_ntstatus_ok(torture
, status
,
4185 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4186 torture_assert_u64_equal(torture
, far_count
, 1,
4187 "unexpected response len");
4188 if (far_rsp
[0].file_off
== 0) {
4189 torture_skip(torture
, "unwritten range fully allocated\n");
4192 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, dealloc_chunk_len
,
4193 "unexpected allocation");
4194 torture_assert_u64_equal(torture
, far_rsp
[0].len
, 1024,
4195 "unexpected far len");
4197 /* copy-chunk unallocated + written ranges into non-sparse dest */
4199 cc_copy
.chunks
[0].source_off
= 0;
4200 cc_copy
.chunks
[0].target_off
= 0;
4201 cc_copy
.chunks
[0].length
= dealloc_chunk_len
+ 1024;
4203 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
4205 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
4206 torture_assert_ndr_success(torture
, ndr_ret
,
4207 "ndr_push_srv_copychunk_copy");
4209 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
4210 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_COPYCHUNK");
4212 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
4214 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
4215 torture_assert_ndr_success(torture
, ndr_ret
,
4216 "ndr_pull_srv_copychunk_rsp");
4218 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
4219 1, /* chunks written */
4220 0, /* chunk bytes unsuccessfully written */
4221 dealloc_chunk_len
+ 1024); /* bytes written */
4222 torture_assert(torture
, ok
, "bad copy chunk response data");
4224 ok
= check_zero(torture
, tree
, tmp_ctx
, dest_h
, 0, dealloc_chunk_len
);
4225 torture_assert(torture
, ok
, "sparse zeroed range");
4227 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, dealloc_chunk_len
,
4228 1024, dealloc_chunk_len
);
4229 torture_assert(torture
, ok
, "copychunked range");
4231 /* copied range should be allocated in non-sparse dest */
4232 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, dest_h
,
4234 dealloc_chunk_len
+ 1024, /* len */
4237 torture_assert_ntstatus_ok(torture
, status
,
4238 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4239 torture_assert_u64_equal(torture
, far_count
, 1,
4240 "unexpected response len");
4241 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0,
4242 "unexpected allocation");
4243 torture_assert_u64_equal(torture
, far_rsp
[0].len
,
4244 dealloc_chunk_len
+ 1024,
4245 "unexpected far len");
4247 /* set dest as sparse */
4248 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, dest_h
, true);
4249 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
4251 /* zero (hole-punch) all data */
4252 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, dest_h
,
4254 dealloc_chunk_len
+ 1024);
4255 torture_assert_ntstatus_ok(torture
, status
, "zero_data");
4257 /* zeroed range might be deallocated */
4258 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, dest_h
,
4260 dealloc_chunk_len
+ 1024, /* len */
4263 torture_assert_ntstatus_ok(torture
, status
,
4264 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4265 if (far_count
== 0) {
4266 /* FS specific (e.g. NTFS) */
4267 torture_comment(torture
, "FS deallocates file on full-range "
4270 /* FS specific (e.g. EXT4) */
4271 torture_comment(torture
, "FS doesn't deallocate file on "
4272 "full-range punch\n");
4274 ok
= check_zero(torture
, tree
, tmp_ctx
, dest_h
, 0,
4275 dealloc_chunk_len
+ 1024);
4276 torture_assert(torture
, ok
, "punched zeroed range");
4278 /* copy-chunk again, this time with sparse dest */
4279 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
4280 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_COPYCHUNK");
4282 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
4284 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
4285 torture_assert_ndr_success(torture
, ndr_ret
,
4286 "ndr_pull_srv_copychunk_rsp");
4288 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
4289 1, /* chunks written */
4290 0, /* chunk bytes unsuccessfully written */
4291 dealloc_chunk_len
+ 1024); /* bytes written */
4292 torture_assert(torture
, ok
, "bad copy chunk response data");
4294 ok
= check_zero(torture
, tree
, tmp_ctx
, dest_h
, 0, dealloc_chunk_len
);
4295 torture_assert(torture
, ok
, "sparse zeroed range");
4297 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, dealloc_chunk_len
,
4298 1024, dealloc_chunk_len
);
4299 torture_assert(torture
, ok
, "copychunked range");
4301 /* copied range may be allocated in sparse dest */
4302 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, dest_h
,
4304 dealloc_chunk_len
+ 1024, /* len */
4307 torture_assert_ntstatus_ok(torture
, status
,
4308 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4309 torture_assert_u64_equal(torture
, far_count
, 1,
4310 "unexpected response len");
4312 * FS specific: sparse region may be unallocated in dest if copy-chunk
4313 * is handled in a sparse preserving way - E.g. vfs_btrfs
4314 * with BTRFS_IOC_CLONE_RANGE.
4316 if (far_rsp
[0].file_off
== dealloc_chunk_len
) {
4317 torture_comment(torture
, "copy-chunk sparse range preserved\n");
4318 torture_assert_u64_equal(torture
, far_rsp
[0].len
, 1024,
4319 "unexpected far len");
4321 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0,
4322 "unexpected allocation");
4323 torture_assert_u64_equal(torture
, far_rsp
[0].len
,
4324 dealloc_chunk_len
+ 1024,
4325 "unexpected far len");
4328 smb2_util_close(tree
, src_h
);
4329 smb2_util_close(tree
, dest_h
);
4330 talloc_free(tmp_ctx
);
4334 static bool test_ioctl_sparse_punch_invalid(struct torture_context
*torture
,
4335 struct smb2_tree
*tree
)
4337 struct smb2_handle fh
;
4339 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
4344 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
4345 FNAME
, &fh
, 4096, SEC_RIGHTS_FILE_ALL
,
4346 FILE_ATTRIBUTE_NORMAL
);
4347 torture_assert(torture
, ok
, "setup file");
4349 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
4350 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
4351 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
4353 smb2_util_close(tree
, fh
);
4354 torture_skip(torture
, "Sparse files not supported\n");
4357 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
4358 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
4359 torture_assert(torture
, !is_sparse
, "sparse attr before set");
4361 /* loop twice, without and with sparse attrib */
4362 for (i
= 0; i
<= 1; i
++) {
4363 union smb_fileinfo io
;
4364 struct file_alloced_range_buf
*far_rsp
= NULL
;
4365 uint64_t far_count
= 0;
4367 /* get size before & after. zero data should never change it */
4369 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
4370 io
.generic
.in
.file
.handle
= fh
;
4371 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
4372 torture_assert_ntstatus_ok(torture
, status
, "getinfo");
4373 torture_assert_int_equal(torture
, (int)io
.all_info2
.out
.size
,
4374 4096, "size after IO");
4376 /* valid 8 byte zero data, but after EOF */
4377 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
4379 4104); /* beyond_final_zero */
4380 torture_assert_ntstatus_ok(torture
, status
, "zero_data");
4382 /* valid 8 byte zero data, but after EOF */
4383 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
4385 8200); /* beyond_final_zero */
4386 torture_assert_ntstatus_ok(torture
, status
, "zero_data");
4389 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
4390 io
.generic
.in
.file
.handle
= fh
;
4391 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
4392 torture_assert_ntstatus_ok(torture
, status
, "getinfo");
4393 torture_assert_int_equal(torture
, (int)io
.all_info2
.out
.size
,
4394 4096, "size after IO");
4396 /* valid 0 byte zero data, without sparse flag */
4397 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
4399 4095); /* beyond_final_zero */
4400 torture_assert_ntstatus_ok(torture
, status
, "zero_data");
4402 /* INVALID off is past beyond_final_zero */
4403 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
4405 4095); /* beyond_final_zero */
4406 torture_assert_ntstatus_equal(torture
, status
,
4407 NT_STATUS_INVALID_PARAMETER
,
4408 "invalid zero_data");
4410 /* zero length QAR - valid */
4411 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
4414 &far_rsp
, &far_count
);
4415 torture_assert_ntstatus_ok(torture
, status
,
4416 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4417 torture_assert_u64_equal(torture
, far_count
, 0,
4418 "unexpected response len");
4420 /* QAR after EOF - valid */
4421 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
4424 &far_rsp
, &far_count
);
4425 torture_assert_ntstatus_ok(torture
, status
,
4426 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4427 torture_assert_u64_equal(torture
, far_count
, 0,
4428 "unexpected response len");
4431 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
,
4433 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
4436 smb2_util_close(tree
, fh
);
4437 talloc_free(tmp_ctx
);
4441 static bool test_ioctl_sparse_perms(struct torture_context
*torture
,
4442 struct smb2_tree
*tree
)
4444 struct smb2_handle fh
;
4446 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
4449 struct file_alloced_range_buf
*far_rsp
= NULL
;
4450 uint64_t far_count
= 0;
4452 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
4453 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
4454 FILE_ATTRIBUTE_NORMAL
);
4455 torture_assert(torture
, ok
, "setup file");
4457 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
4458 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
4459 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
4460 smb2_util_close(tree
, fh
);
4462 torture_skip(torture
, "Sparse files not supported\n");
4465 /* set sparse without WRITE_ATTR permission should succeed */
4466 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
4468 (SEC_RIGHTS_FILE_WRITE
& ~(SEC_FILE_WRITE_ATTRIBUTE
4470 | SEC_FILE_WRITE_EA
)),
4471 FILE_ATTRIBUTE_NORMAL
);
4472 torture_assert(torture
, ok
, "setup file");
4474 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, true);
4475 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
4476 smb2_util_close(tree
, fh
);
4478 ok
= test_setup_open(torture
, tree
, tmp_ctx
,
4479 FNAME
, &fh
, SEC_RIGHTS_FILE_ALL
,
4480 FILE_ATTRIBUTE_NORMAL
);
4481 torture_assert(torture
, ok
, "setup file");
4482 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
4483 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
4484 torture_assert(torture
, is_sparse
, "sparse after set");
4485 smb2_util_close(tree
, fh
);
4487 /* attempt get sparse without READ_DATA permission */
4488 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
4490 (SEC_RIGHTS_FILE_READ
& ~SEC_FILE_READ_DATA
),
4491 FILE_ATTRIBUTE_NORMAL
);
4492 torture_assert(torture
, ok
, "setup file");
4494 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
4495 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
4496 torture_assert(torture
, !is_sparse
, "sparse set");
4497 smb2_util_close(tree
, fh
);
4499 /* attempt to set sparse with only WRITE_ATTR permission */
4500 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
4502 SEC_FILE_WRITE_ATTRIBUTE
,
4503 FILE_ATTRIBUTE_NORMAL
);
4504 torture_assert(torture
, ok
, "setup file");
4506 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, true);
4507 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
4508 smb2_util_close(tree
, fh
);
4510 /* attempt to set sparse with only WRITE_DATA permission */
4511 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
4513 SEC_FILE_WRITE_DATA
,
4514 FILE_ATTRIBUTE_NORMAL
);
4515 torture_assert(torture
, ok
, "setup file");
4517 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, true);
4518 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
4519 smb2_util_close(tree
, fh
);
4521 ok
= test_setup_open(torture
, tree
, tmp_ctx
,
4522 FNAME
, &fh
, SEC_RIGHTS_FILE_ALL
,
4523 FILE_ATTRIBUTE_NORMAL
);
4524 torture_assert(torture
, ok
, "setup file");
4525 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
4526 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
4527 torture_assert(torture
, is_sparse
, "sparse after set");
4528 smb2_util_close(tree
, fh
);
4530 /* attempt to set sparse with only APPEND_DATA permission */
4531 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
4533 SEC_FILE_APPEND_DATA
,
4534 FILE_ATTRIBUTE_NORMAL
);
4535 torture_assert(torture
, ok
, "setup file");
4537 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, true);
4538 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
4539 smb2_util_close(tree
, fh
);
4541 ok
= test_setup_open(torture
, tree
, tmp_ctx
,
4542 FNAME
, &fh
, SEC_RIGHTS_FILE_ALL
,
4543 FILE_ATTRIBUTE_NORMAL
);
4544 torture_assert(torture
, ok
, "setup file");
4545 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
4546 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
4547 torture_assert(torture
, is_sparse
, "sparse after set");
4548 smb2_util_close(tree
, fh
);
4550 /* attempt to set sparse with only WRITE_EA permission - should fail */
4551 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
4554 FILE_ATTRIBUTE_NORMAL
);
4555 torture_assert(torture
, ok
, "setup file");
4557 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, true);
4558 torture_assert_ntstatus_equal(torture
, status
,
4559 NT_STATUS_ACCESS_DENIED
,
4560 "FSCTL_SET_SPARSE permission");
4561 smb2_util_close(tree
, fh
);
4563 ok
= test_setup_open(torture
, tree
, tmp_ctx
,
4564 FNAME
, &fh
, SEC_RIGHTS_FILE_ALL
,
4565 FILE_ATTRIBUTE_NORMAL
);
4566 torture_assert(torture
, ok
, "setup file");
4567 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
4568 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
4569 torture_assert(torture
, !is_sparse
, "sparse after set");
4570 smb2_util_close(tree
, fh
);
4572 /* attempt QAR with only READ_ATTR permission - should fail */
4573 ok
= test_setup_open(torture
, tree
, tmp_ctx
,
4574 FNAME
, &fh
, SEC_FILE_READ_ATTRIBUTE
,
4575 FILE_ATTRIBUTE_NORMAL
);
4576 torture_assert(torture
, ok
, "setup file");
4577 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
4580 &far_rsp
, &far_count
);
4581 torture_assert_ntstatus_equal(torture
, status
,
4582 NT_STATUS_ACCESS_DENIED
,
4583 "FSCTL_QUERY_ALLOCATED_RANGES req passed");
4584 smb2_util_close(tree
, fh
);
4586 /* attempt QAR with only READ_DATA permission */
4587 ok
= test_setup_open(torture
, tree
, tmp_ctx
,
4588 FNAME
, &fh
, SEC_FILE_READ_DATA
,
4589 FILE_ATTRIBUTE_NORMAL
);
4590 torture_assert(torture
, ok
, "setup file");
4591 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
4594 &far_rsp
, &far_count
);
4595 torture_assert_ntstatus_ok(torture
, status
,
4596 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4597 torture_assert_u64_equal(torture
, far_count
, 0,
4598 "unexpected response len");
4599 smb2_util_close(tree
, fh
);
4601 /* attempt QAR with only READ_EA permission - should fail */
4602 ok
= test_setup_open(torture
, tree
, tmp_ctx
,
4603 FNAME
, &fh
, SEC_FILE_READ_EA
,
4604 FILE_ATTRIBUTE_NORMAL
);
4605 torture_assert(torture
, ok
, "setup file");
4606 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
4609 &far_rsp
, &far_count
);
4610 torture_assert_ntstatus_equal(torture
, status
,
4611 NT_STATUS_ACCESS_DENIED
,
4612 "FSCTL_QUERY_ALLOCATED_RANGES req passed");
4613 smb2_util_close(tree
, fh
);
4615 /* setup file for ZERO_DATA permissions tests */
4616 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
4618 SEC_RIGHTS_FILE_ALL
,
4619 FILE_ATTRIBUTE_NORMAL
);
4620 torture_assert(torture
, ok
, "setup file");
4622 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, true);
4623 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
4624 smb2_util_close(tree
, fh
);
4626 /* attempt ZERO_DATA with only WRITE_ATTR permission - should fail */
4627 ok
= test_setup_open(torture
, tree
, tmp_ctx
,
4628 FNAME
, &fh
, SEC_FILE_WRITE_ATTRIBUTE
,
4629 FILE_ATTRIBUTE_NORMAL
);
4630 torture_assert(torture
, ok
, "setup file");
4631 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
4633 4096); /* beyond_final_zero */
4634 torture_assert_ntstatus_equal(torture
, status
,
4635 NT_STATUS_ACCESS_DENIED
,
4636 "zero_data permission");
4637 smb2_util_close(tree
, fh
);
4639 /* attempt ZERO_DATA with only WRITE_DATA permission */
4640 ok
= test_setup_open(torture
, tree
, tmp_ctx
,
4641 FNAME
, &fh
, SEC_FILE_WRITE_DATA
,
4642 FILE_ATTRIBUTE_NORMAL
);
4643 torture_assert(torture
, ok
, "setup file");
4644 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
4646 4096); /* beyond_final_zero */
4647 torture_assert_ntstatus_ok(torture
, status
, "zero_data");
4648 smb2_util_close(tree
, fh
);
4650 /* attempt ZERO_DATA with only APPEND_DATA permission - should fail */
4651 ok
= test_setup_open(torture
, tree
, tmp_ctx
,
4652 FNAME
, &fh
, SEC_FILE_APPEND_DATA
,
4653 FILE_ATTRIBUTE_NORMAL
);
4654 torture_assert(torture
, ok
, "setup file");
4655 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
4657 4096); /* beyond_final_zero */
4658 torture_assert_ntstatus_equal(torture
, status
,
4659 NT_STATUS_ACCESS_DENIED
,
4660 "zero_data permission");
4661 smb2_util_close(tree
, fh
);
4663 /* attempt ZERO_DATA with only WRITE_EA permission - should fail */
4664 ok
= test_setup_open(torture
, tree
, tmp_ctx
,
4665 FNAME
, &fh
, SEC_FILE_WRITE_EA
,
4666 FILE_ATTRIBUTE_NORMAL
);
4667 torture_assert(torture
, ok
, "setup file");
4668 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
4670 4096); /* beyond_final_zero */
4671 torture_assert_ntstatus_equal(torture
, status
,
4672 NT_STATUS_ACCESS_DENIED
,
4673 "zero_data permission");
4674 smb2_util_close(tree
, fh
);
4676 talloc_free(tmp_ctx
);
4680 static bool test_ioctl_sparse_lck(struct torture_context
*torture
,
4681 struct smb2_tree
*tree
)
4683 struct smb2_handle fh
;
4684 struct smb2_handle fh2
;
4686 uint64_t dealloc_chunk_len
= 64 * 1024; /* Windows 2012 */
4687 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
4690 struct smb2_lock lck
;
4691 struct smb2_lock_element el
[1];
4692 struct file_alloced_range_buf
*far_rsp
= NULL
;
4693 uint64_t far_count
= 0;
4695 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
, FNAME
, &fh
,
4696 dealloc_chunk_len
, SEC_RIGHTS_FILE_ALL
,
4697 FILE_ATTRIBUTE_NORMAL
);
4698 torture_assert(torture
, ok
, "setup file");
4700 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
4701 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
4702 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
4704 torture_skip(torture
, "Sparse files not supported\n");
4705 smb2_util_close(tree
, fh
);
4708 /* open and lock via separate fh2 */
4709 status
= torture_smb2_testfile(tree
, FNAME
, &fh2
);
4710 torture_assert_ntstatus_ok(torture
, status
, "2nd src open");
4712 lck
.in
.lock_count
= 0x0001;
4713 lck
.in
.lock_sequence
= 0x00000000;
4714 lck
.in
.file
.handle
= fh2
;
4717 el
[0].length
= dealloc_chunk_len
;
4719 el
[0].flags
= SMB2_LOCK_FLAG_EXCLUSIVE
;
4721 status
= smb2_lock(tree
, &lck
);
4722 torture_assert_ntstatus_ok(torture
, status
, "lock");
4724 /* set sparse while locked */
4725 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, true);
4726 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
4728 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
4729 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
4730 torture_assert(torture
, is_sparse
, "sparse attr after set");
4732 /* zero data over locked range should fail */
4733 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
4735 4096); /* beyond_final_zero */
4736 torture_assert_ntstatus_equal(torture
, status
,
4737 NT_STATUS_FILE_LOCK_CONFLICT
,
4738 "zero_data locked");
4740 /* QAR over locked range should pass */
4741 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
4744 &far_rsp
, &far_count
);
4745 torture_assert_ntstatus_ok(torture
, status
,
4746 "FSCTL_QUERY_ALLOCATED_RANGES locked");
4747 torture_assert_u64_equal(torture
, far_count
, 1,
4748 "unexpected response len");
4749 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0,
4750 "unexpected allocation");
4751 torture_assert_u64_equal(torture
, far_rsp
[0].len
,
4753 "unexpected far len");
4755 /* zero data over range past EOF should pass */
4756 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
4757 dealloc_chunk_len
, /* off */
4758 dealloc_chunk_len
+ 4096);
4759 torture_assert_ntstatus_ok(torture
, status
,
4760 "zero_data past EOF locked");
4762 /* QAR over range past EOF should pass */
4763 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
4764 dealloc_chunk_len
, /* off */
4766 &far_rsp
, &far_count
);
4767 torture_assert_ntstatus_ok(torture
, status
,
4768 "FSCTL_QUERY_ALLOCATED_RANGES past EOF locked");
4769 torture_assert_u64_equal(torture
, far_count
, 0,
4770 "unexpected response len");
4772 lck
.in
.lock_count
= 0x0001;
4773 lck
.in
.lock_sequence
= 0x00000001;
4774 lck
.in
.file
.handle
= fh2
;
4777 el
[0].length
= dealloc_chunk_len
;
4779 el
[0].flags
= SMB2_LOCK_FLAG_UNLOCK
;
4780 status
= smb2_lock(tree
, &lck
);
4781 torture_assert_ntstatus_ok(torture
, status
, "unlock");
4783 smb2_util_close(tree
, fh2
);
4784 smb2_util_close(tree
, fh
);
4785 talloc_free(tmp_ctx
);
4789 /* alleviate QAR off-by-one bug paranoia - help me ob1 */
4790 static bool test_ioctl_sparse_qar_ob1(struct torture_context
*torture
,
4791 struct smb2_tree
*tree
)
4793 struct smb2_handle fh
;
4795 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
4797 uint64_t dealloc_chunk_len
= 64 * 1024; /* Windows 2012 */
4798 struct file_alloced_range_buf
*far_rsp
= NULL
;
4799 uint64_t far_count
= 0;
4801 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
4802 FNAME
, &fh
, dealloc_chunk_len
* 2,
4803 SEC_RIGHTS_FILE_ALL
,
4804 FILE_ATTRIBUTE_NORMAL
);
4805 torture_assert(torture
, ok
, "setup file");
4807 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
4808 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
4809 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
4811 torture_skip(torture
, "Sparse files not supported\n");
4812 smb2_util_close(tree
, fh
);
4815 /* non-sparse QAR with range one before EOF */
4816 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
4818 dealloc_chunk_len
* 2 - 1, /* len */
4819 &far_rsp
, &far_count
);
4820 torture_assert_ntstatus_ok(torture
, status
,
4821 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4822 torture_assert_u64_equal(torture
, far_count
, 1,
4823 "unexpected response len");
4824 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0,
4825 "unexpected allocation");
4826 torture_assert_u64_equal(torture
, far_rsp
[0].len
,
4827 dealloc_chunk_len
* 2 - 1,
4828 "unexpected far len");
4830 /* non-sparse QAR with range one after EOF */
4831 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
4833 dealloc_chunk_len
* 2 + 1, /* len */
4834 &far_rsp
, &far_count
);
4835 torture_assert_ntstatus_ok(torture
, status
,
4836 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4837 torture_assert_u64_equal(torture
, far_count
, 1,
4838 "unexpected response len");
4839 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0,
4840 "unexpected allocation");
4841 torture_assert_u64_equal(torture
, far_rsp
[0].len
,
4842 dealloc_chunk_len
* 2,
4843 "unexpected far len");
4845 /* non-sparse QAR with range one after EOF from off=1 */
4846 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
4848 dealloc_chunk_len
* 2, /* len */
4849 &far_rsp
, &far_count
);
4850 torture_assert_ntstatus_ok(torture
, status
,
4851 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4852 torture_assert_u64_equal(torture
, far_count
, 1,
4853 "unexpected response len");
4854 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 1,
4855 "unexpected allocation");
4856 torture_assert_u64_equal(torture
, far_rsp
[0].len
,
4857 dealloc_chunk_len
* 2 - 1,
4858 "unexpected far len");
4860 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, true);
4861 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
4863 /* punch out second chunk */
4864 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
4865 dealloc_chunk_len
, /* off */
4866 dealloc_chunk_len
* 2);
4867 torture_assert_ntstatus_ok(torture
, status
, "zero_data");
4869 /* sparse QAR with range one before hole */
4870 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
4872 dealloc_chunk_len
- 1, /* len */
4873 &far_rsp
, &far_count
);
4874 torture_assert_ntstatus_ok(torture
, status
,
4875 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4876 torture_assert_u64_equal(torture
, far_count
, 1,
4877 "unexpected response len");
4878 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0,
4879 "unexpected allocation");
4880 torture_assert_u64_equal(torture
, far_rsp
[0].len
,
4881 dealloc_chunk_len
- 1,
4882 "unexpected far len");
4884 /* sparse QAR with range one after hole */
4885 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
4887 dealloc_chunk_len
+ 1, /* len */
4888 &far_rsp
, &far_count
);
4889 torture_assert_ntstatus_ok(torture
, status
,
4890 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4891 torture_assert_u64_equal(torture
, far_count
, 1,
4892 "unexpected response len");
4893 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0,
4894 "unexpected allocation");
4895 torture_assert_u64_equal(torture
, far_rsp
[0].len
,
4897 "unexpected far len");
4899 /* sparse QAR with range one after hole from off=1 */
4900 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
4902 dealloc_chunk_len
, /* len */
4903 &far_rsp
, &far_count
);
4904 torture_assert_ntstatus_ok(torture
, status
,
4905 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4906 torture_assert_u64_equal(torture
, far_count
, 1,
4907 "unexpected response len");
4908 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 1,
4909 "unexpected allocation");
4910 torture_assert_u64_equal(torture
, far_rsp
[0].len
,
4911 dealloc_chunk_len
- 1,
4912 "unexpected far len");
4914 /* sparse QAR with range one before EOF from off=chunk_len-1 */
4915 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
4916 dealloc_chunk_len
- 1, /* off */
4917 dealloc_chunk_len
, /* len */
4918 &far_rsp
, &far_count
);
4919 torture_assert_ntstatus_ok(torture
, status
,
4920 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4921 torture_assert_u64_equal(torture
, far_count
, 1,
4922 "unexpected response len");
4923 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
,
4924 dealloc_chunk_len
- 1,
4925 "unexpected allocation");
4926 torture_assert_u64_equal(torture
, far_rsp
[0].len
,
4927 1, "unexpected far len");
4929 /* sparse QAR with range one after EOF from off=chunk_len+1 */
4930 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
4931 dealloc_chunk_len
+ 1, /* off */
4932 dealloc_chunk_len
, /* len */
4933 &far_rsp
, &far_count
);
4934 torture_assert_ntstatus_ok(torture
, status
,
4935 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4936 torture_assert_u64_equal(torture
, far_count
, 0,
4937 "unexpected response len");
4938 smb2_util_close(tree
, fh
);
4939 talloc_free(tmp_ctx
);
4943 /* test QAR with multi-range responses */
4944 static bool test_ioctl_sparse_qar_multi(struct torture_context
*torture
,
4945 struct smb2_tree
*tree
)
4947 struct smb2_handle fh
;
4949 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
4951 uint64_t dealloc_chunk_len
= 64 * 1024; /* Windows 2012 */
4954 struct file_alloced_range_buf
*far_rsp
= NULL
;
4955 uint64_t far_count
= 0;
4957 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
4958 FNAME
, &fh
, dealloc_chunk_len
* 2,
4959 SEC_RIGHTS_FILE_ALL
,
4960 FILE_ATTRIBUTE_NORMAL
);
4961 torture_assert(torture
, ok
, "setup file");
4963 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
4964 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
4965 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
4967 torture_skip(torture
, "Sparse files not supported\n");
4968 smb2_util_close(tree
, fh
);
4971 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, true);
4972 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
4974 /* each loop, write out two chunks and punch the first out */
4975 for (i
= 0; i
< 10; i
++) {
4976 this_off
= i
* dealloc_chunk_len
* 2;
4978 ok
= write_pattern(torture
, tree
, tmp_ctx
, fh
,
4980 dealloc_chunk_len
* 2, /* len */
4981 this_off
); /* pattern offset */
4982 torture_assert(torture
, ok
, "write pattern");
4984 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
4986 this_off
+ dealloc_chunk_len
);
4987 torture_assert_ntstatus_ok(torture
, status
, "zero_data");
4990 /* should now have one separate region for each iteration */
4991 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
4993 10 * dealloc_chunk_len
* 2,
4994 &far_rsp
, &far_count
);
4995 torture_assert_ntstatus_ok(torture
, status
,
4996 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4997 if (far_count
== 1) {
4998 torture_comment(torture
, "this FS doesn't deallocate 64K"
4999 "zeroed ranges in sparse files\n");
5000 return true; /* FS specific, not a failure */
5002 torture_assert_u64_equal(torture
, far_count
, 10,
5003 "unexpected response len");
5004 for (i
= 0; i
< 10; i
++) {
5005 this_off
= i
* dealloc_chunk_len
* 2;
5007 torture_assert_u64_equal(torture
, far_rsp
[i
].file_off
,
5008 this_off
+ dealloc_chunk_len
,
5009 "unexpected allocation");
5010 torture_assert_u64_equal(torture
, far_rsp
[i
].len
,
5012 "unexpected far len");
5015 smb2_util_close(tree
, fh
);
5016 talloc_free(tmp_ctx
);
5020 static bool test_ioctl_sparse_qar_overflow(struct torture_context
*torture
,
5021 struct smb2_tree
*tree
)
5023 struct smb2_handle fh
;
5024 union smb_ioctl ioctl
;
5025 struct file_alloced_range_buf far_buf
;
5027 enum ndr_err_code ndr_ret
;
5028 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
5031 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
5032 FNAME
, &fh
, 1024, SEC_RIGHTS_FILE_ALL
,
5033 FILE_ATTRIBUTE_NORMAL
);
5034 torture_assert(torture
, ok
, "setup file");
5036 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
5037 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
5038 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
5040 smb2_util_close(tree
, fh
);
5041 torture_skip(torture
, "Sparse files not supported\n");
5044 /* no allocated ranges, no space for range response, should pass */
5046 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
5047 ioctl
.smb2
.in
.file
.handle
= fh
;
5048 ioctl
.smb2
.in
.function
= FSCTL_QUERY_ALLOCATED_RANGES
;
5049 ioctl
.smb2
.in
.max_response_size
= 1024;
5050 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
5052 /* off + length wraps around to 511 */
5053 far_buf
.file_off
= 512;
5054 far_buf
.len
= 0xffffffffffffffffLL
;
5055 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
5057 (ndr_push_flags_fn_t
)ndr_push_file_alloced_range_buf
);
5058 torture_assert_ndr_success(torture
, ndr_ret
, "push far ndr buf");
5060 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
5061 torture_assert_ntstatus_equal(torture
, status
,
5062 NT_STATUS_INVALID_PARAMETER
,
5063 "FSCTL_QUERY_ALLOCATED_RANGES overflow");
5068 static NTSTATUS
test_ioctl_trim_supported(struct torture_context
*torture
,
5069 struct smb2_tree
*tree
,
5070 TALLOC_CTX
*mem_ctx
,
5071 struct smb2_handle
*fh
,
5075 union smb_fsinfo info
;
5078 info
.generic
.level
= RAW_QFS_SECTOR_SIZE_INFORMATION
;
5079 info
.generic
.handle
= *fh
;
5080 status
= smb2_getinfo_fs(tree
, tree
, &info
);
5081 if (NT_STATUS_EQUAL(status
, NT_STATUS_INVALID_INFO_CLASS
)) {
5083 * Windows < Server 2012, 8 etc. don't support this info level
5084 * or the trim ioctl. Ignore the error and let the caller skip.
5086 *trim_support
= false;
5087 return NT_STATUS_OK
;
5088 } else if (!NT_STATUS_IS_OK(status
)) {
5092 torture_comment(torture
, "sector size info: lb/s=%u, pb/sA=%u, "
5093 "pb/sP=%u, fse/sA=%u, flags=0x%x, bosa=%u, bopa=%u\n",
5094 (unsigned)info
.sector_size_info
.out
.logical_bytes_per_sector
,
5095 (unsigned)info
.sector_size_info
.out
.phys_bytes_per_sector_atomic
,
5096 (unsigned)info
.sector_size_info
.out
.phys_bytes_per_sector_perf
,
5097 (unsigned)info
.sector_size_info
.out
.fs_effective_phys_bytes_per_sector_atomic
,
5098 (unsigned)info
.sector_size_info
.out
.flags
,
5099 (unsigned)info
.sector_size_info
.out
.byte_off_sector_align
,
5100 (unsigned)info
.sector_size_info
.out
.byte_off_partition_align
);
5102 if (info
.sector_size_info
.out
.flags
& QFS_SSINFO_FLAGS_TRIM_ENABLED
) {
5103 *trim_support
= true;
5105 *trim_support
= false;
5107 return NT_STATUS_OK
;
5110 static bool test_setup_trim(struct torture_context
*torture
,
5111 struct smb2_tree
*tree
,
5112 TALLOC_CTX
*mem_ctx
,
5113 uint32_t num_ranges
,
5114 struct smb2_handle
*fh
,
5116 uint32_t desired_access
,
5117 struct fsctl_file_level_trim_req
*trim_req
,
5118 union smb_ioctl
*ioctl
)
5122 ok
= test_setup_create_fill(torture
, tree
, mem_ctx
, FNAME
,
5123 fh
, file_size
, desired_access
,
5124 FILE_ATTRIBUTE_NORMAL
);
5125 torture_assert(torture
, ok
, "src file create fill");
5127 ZERO_STRUCTPN(ioctl
);
5128 ioctl
->smb2
.level
= RAW_IOCTL_SMB2
;
5129 ioctl
->smb2
.in
.file
.handle
= *fh
;
5130 ioctl
->smb2
.in
.function
= FSCTL_FILE_LEVEL_TRIM
;
5131 ioctl
->smb2
.in
.max_response_size
5132 = sizeof(struct fsctl_file_level_trim_rsp
);
5133 ioctl
->smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
5135 ZERO_STRUCTPN(trim_req
);
5136 /* leave key as zero for now. TODO test locking with differing keys */
5137 trim_req
->num_ranges
= num_ranges
;
5138 trim_req
->ranges
= talloc_zero_array(mem_ctx
,
5139 struct file_level_trim_range
,
5141 torture_assert(torture
, (trim_req
->ranges
!= NULL
), "no memory for ranges");
5146 static bool test_ioctl_trim_simple(struct torture_context
*torture
,
5147 struct smb2_tree
*tree
)
5149 struct smb2_handle fh
;
5151 union smb_ioctl ioctl
;
5152 bool trim_supported
;
5153 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
5154 struct fsctl_file_level_trim_req trim_req
;
5155 struct fsctl_file_level_trim_rsp trim_rsp
;
5156 uint64_t trim_chunk_len
= 64 * 1024; /* trim 64K chunks */
5157 enum ndr_err_code ndr_ret
;
5160 ok
= test_setup_trim(torture
, tree
, tmp_ctx
,
5162 &fh
, 2 * trim_chunk_len
, /* fill 128K file */
5163 SEC_RIGHTS_FILE_ALL
,
5167 torture_fail(torture
, "setup trim error");
5170 status
= test_ioctl_trim_supported(torture
, tree
, tmp_ctx
, &fh
,
5172 torture_assert_ntstatus_ok(torture
, status
, "fsinfo");
5173 if (!trim_supported
) {
5174 smb2_util_close(tree
, fh
);
5175 talloc_free(tmp_ctx
);
5176 torture_skip(torture
, "trim not supported\n");
5179 /* trim first chunk, leave second */
5180 trim_req
.ranges
[0].off
= 0;
5181 trim_req
.ranges
[0].len
= trim_chunk_len
;
5183 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
, &trim_req
,
5184 (ndr_push_flags_fn_t
)ndr_push_fsctl_file_level_trim_req
);
5185 torture_assert_ndr_success(torture
, ndr_ret
,
5186 "ndr_push_fsctl_file_level_trim_req");
5188 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
5189 torture_assert_ntstatus_ok(torture
, status
, "FILE_LEVEL_TRIM_RANGE");
5191 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
5193 (ndr_pull_flags_fn_t
)ndr_pull_fsctl_file_level_trim_rsp
);
5194 torture_assert_ndr_success(torture
, ndr_ret
,
5195 "ndr_pull_fsctl_file_level_trim_rsp");
5197 torture_assert_int_equal(torture
, trim_rsp
.num_ranges_processed
, 1, "");
5199 /* second half of the file should remain consitent */
5200 ok
= check_pattern(torture
, tree
, tmp_ctx
, fh
, trim_chunk_len
,
5201 trim_chunk_len
, trim_chunk_len
);
5202 torture_assert(torture
, ok
, "non-trimmed range inconsistent");
5207 static bool test_setup_dup_extents(struct torture_context
*tctx
,
5208 struct smb2_tree
*tree
,
5209 TALLOC_CTX
*mem_ctx
,
5210 struct smb2_handle
*src_h
,
5212 uint32_t src_desired_access
,
5213 struct smb2_handle
*dest_h
,
5215 uint32_t dest_desired_access
,
5216 struct fsctl_dup_extents_to_file
*dup_ext_buf
,
5217 union smb_ioctl
*ioctl
)
5221 ok
= test_setup_create_fill(tctx
, tree
, mem_ctx
, FNAME
,
5222 src_h
, src_size
, src_desired_access
,
5223 FILE_ATTRIBUTE_NORMAL
);
5224 torture_assert(tctx
, ok
, "src file create fill");
5226 ok
= test_setup_create_fill(tctx
, tree
, mem_ctx
, FNAME2
,
5227 dest_h
, dest_size
, dest_desired_access
,
5228 FILE_ATTRIBUTE_NORMAL
);
5229 torture_assert(tctx
, ok
, "dest file create fill");
5231 ZERO_STRUCTPN(ioctl
);
5232 ioctl
->smb2
.level
= RAW_IOCTL_SMB2
;
5233 ioctl
->smb2
.in
.file
.handle
= *dest_h
;
5234 ioctl
->smb2
.in
.function
= FSCTL_DUP_EXTENTS_TO_FILE
;
5235 ioctl
->smb2
.in
.max_response_size
= 0;
5236 ioctl
->smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
5238 ZERO_STRUCTPN(dup_ext_buf
);
5239 smb2_push_handle(dup_ext_buf
->source_fid
, src_h
);
5244 static bool test_ioctl_dup_extents_simple(struct torture_context
*tctx
,
5245 struct smb2_tree
*tree
)
5247 struct smb2_handle src_h
;
5248 struct smb2_handle dest_h
;
5250 union smb_ioctl ioctl
;
5251 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
5252 struct fsctl_dup_extents_to_file dup_ext_buf
;
5253 enum ndr_err_code ndr_ret
;
5254 union smb_fileinfo io
;
5255 union smb_setfileinfo sinfo
;
5258 ok
= test_setup_dup_extents(tctx
, tree
, tmp_ctx
,
5259 &src_h
, 4096, /* fill 4096 byte src file */
5260 SEC_RIGHTS_FILE_ALL
,
5261 &dest_h
, 0, /* 0 byte dest file */
5262 SEC_RIGHTS_FILE_ALL
,
5266 torture_fail(tctx
, "setup dup extents error");
5269 status
= test_ioctl_fs_supported(tctx
, tree
, tmp_ctx
, &src_h
,
5270 FILE_SUPPORTS_BLOCK_REFCOUNTING
, &ok
);
5271 torture_assert_ntstatus_ok(tctx
, status
, "SMB2_GETINFO_FS");
5273 smb2_util_close(tree
, src_h
);
5274 smb2_util_close(tree
, dest_h
);
5275 talloc_free(tmp_ctx
);
5276 torture_skip(tctx
, "block refcounting not supported\n");
5279 /* extend dest to match src len */
5281 sinfo
.end_of_file_info
.level
=
5282 RAW_SFILEINFO_END_OF_FILE_INFORMATION
;
5283 sinfo
.end_of_file_info
.in
.file
.handle
= dest_h
;
5284 sinfo
.end_of_file_info
.in
.size
= 4096;
5285 status
= smb2_setinfo_file(tree
, &sinfo
);
5286 torture_assert_ntstatus_ok(tctx
, status
, "smb2_setinfo_file");
5288 /* copy all src file data */
5289 dup_ext_buf
.source_off
= 0;
5290 dup_ext_buf
.target_off
= 0;
5291 dup_ext_buf
.byte_count
= 4096;
5293 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
5295 (ndr_push_flags_fn_t
)ndr_push_fsctl_dup_extents_to_file
);
5296 torture_assert_ndr_success(tctx
, ndr_ret
,
5297 "ndr_push_fsctl_dup_extents_to_file");
5299 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
5300 torture_assert_ntstatus_ok(tctx
, status
,
5301 "FSCTL_DUP_EXTENTS_TO_FILE");
5303 /* the file size shouldn't have been changed by this operation! */
5305 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
5306 io
.generic
.in
.file
.handle
= dest_h
;
5307 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
5308 torture_assert_ntstatus_ok(tctx
, status
, "getinfo");
5309 torture_assert_int_equal(tctx
, (int)io
.all_info2
.out
.size
,
5310 4096, "size after IO");
5312 smb2_util_close(tree
, src_h
);
5313 smb2_util_close(tree
, dest_h
);
5315 /* reopen for pattern check */
5316 ok
= test_setup_open(tctx
, tree
, tmp_ctx
, FNAME
, &src_h
,
5317 SEC_RIGHTS_FILE_ALL
, FILE_ATTRIBUTE_NORMAL
);
5318 torture_assert_ntstatus_ok(tctx
, status
, "src open after dup");
5319 ok
= test_setup_open(tctx
, tree
, tmp_ctx
, FNAME2
, &dest_h
,
5320 SEC_RIGHTS_FILE_ALL
, FILE_ATTRIBUTE_NORMAL
);
5321 torture_assert_ntstatus_ok(tctx
, status
, "dest open after dup");
5323 ok
= check_pattern(tctx
, tree
, tmp_ctx
, src_h
, 0, 4096, 0);
5325 torture_fail(tctx
, "inconsistent src file data");
5328 ok
= check_pattern(tctx
, tree
, tmp_ctx
, dest_h
, 0, 4096, 0);
5330 torture_fail(tctx
, "inconsistent dest file data");
5333 smb2_util_close(tree
, src_h
);
5334 smb2_util_close(tree
, dest_h
);
5335 talloc_free(tmp_ctx
);
5339 static bool test_ioctl_dup_extents_len_beyond_dest(struct torture_context
*tctx
,
5340 struct smb2_tree
*tree
)
5342 struct smb2_handle src_h
;
5343 struct smb2_handle dest_h
;
5345 union smb_ioctl ioctl
;
5346 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
5347 struct fsctl_dup_extents_to_file dup_ext_buf
;
5348 enum ndr_err_code ndr_ret
;
5349 union smb_fileinfo io
;
5350 union smb_setfileinfo sinfo
;
5353 ok
= test_setup_dup_extents(tctx
, tree
, tmp_ctx
,
5354 &src_h
, 32768, /* fill 32768 byte src file */
5355 SEC_RIGHTS_FILE_ALL
,
5356 &dest_h
, 0, /* 0 byte dest file */
5357 SEC_RIGHTS_FILE_ALL
,
5361 torture_fail(tctx
, "setup dup extents error");
5364 status
= test_ioctl_fs_supported(tctx
, tree
, tmp_ctx
, &src_h
,
5365 FILE_SUPPORTS_BLOCK_REFCOUNTING
, &ok
);
5366 torture_assert_ntstatus_ok(tctx
, status
, "SMB2_GETINFO_FS");
5368 smb2_util_close(tree
, src_h
);
5369 smb2_util_close(tree
, dest_h
);
5370 talloc_free(tmp_ctx
);
5371 torture_skip(tctx
, "block refcounting not supported\n");
5375 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
5376 io
.generic
.in
.file
.handle
= dest_h
;
5377 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
5378 torture_assert_ntstatus_ok(tctx
, status
, "getinfo");
5379 torture_assert_int_equal(tctx
, (int)io
.all_info2
.out
.size
,
5380 0, "size after IO");
5382 /* copy all src file data */
5383 dup_ext_buf
.source_off
= 0;
5384 dup_ext_buf
.target_off
= 0;
5385 dup_ext_buf
.byte_count
= 32768;
5387 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
5389 (ndr_push_flags_fn_t
)ndr_push_fsctl_dup_extents_to_file
);
5390 torture_assert_ndr_success(tctx
, ndr_ret
,
5391 "ndr_push_fsctl_dup_extents_to_file");
5393 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
5396 * 2.3.8 FSCTL_DUPLICATE_EXTENTS_TO_FILE Reply - this should fail, but
5397 * passes against WS2016 RTM!
5399 torture_assert_ntstatus_equal(tctx
, status
, NT_STATUS_NOT_SUPPORTED
,
5400 "FSCTL_DUP_EXTENTS_TO_FILE");
5403 /* the file sizes shouldn't have been changed */
5405 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
5406 io
.generic
.in
.file
.handle
= src_h
;
5407 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
5408 torture_assert_ntstatus_ok(tctx
, status
, "getinfo");
5409 torture_assert_int_equal(tctx
, (int)io
.all_info2
.out
.size
,
5410 32768, "size after IO");
5413 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
5414 io
.generic
.in
.file
.handle
= dest_h
;
5415 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
5416 torture_assert_ntstatus_ok(tctx
, status
, "getinfo");
5417 torture_assert_int_equal(tctx
, (int)io
.all_info2
.out
.size
,
5418 0, "size after IO");
5422 sinfo
.end_of_file_info
.level
= RAW_SFILEINFO_END_OF_FILE_INFORMATION
;
5423 sinfo
.end_of_file_info
.in
.file
.handle
= dest_h
;
5424 sinfo
.end_of_file_info
.in
.size
= 32768;
5425 status
= smb2_setinfo_file(tree
, &sinfo
);
5426 torture_assert_ntstatus_ok(tctx
, status
, "smb2_setinfo_file");
5428 ok
= check_zero(tctx
, tree
, tmp_ctx
, dest_h
, 0, 32768);
5430 torture_fail(tctx
, "inconsistent file data");
5433 /* reissue ioctl, now with enough space */
5434 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
5435 torture_assert_ntstatus_ok(tctx
, status
,
5436 "FSCTL_DUP_EXTENTS_TO_FILE");
5438 ok
= check_pattern(tctx
, tree
, tmp_ctx
, dest_h
, 0, 32768, 0);
5440 torture_fail(tctx
, "inconsistent file data");
5443 smb2_util_close(tree
, src_h
);
5444 smb2_util_close(tree
, dest_h
);
5445 talloc_free(tmp_ctx
);
5449 static bool test_ioctl_dup_extents_len_beyond_src(struct torture_context
*tctx
,
5450 struct smb2_tree
*tree
)
5452 struct smb2_handle src_h
;
5453 struct smb2_handle dest_h
;
5455 union smb_ioctl ioctl
;
5456 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
5457 struct fsctl_dup_extents_to_file dup_ext_buf
;
5458 enum ndr_err_code ndr_ret
;
5459 union smb_fileinfo io
;
5462 ok
= test_setup_dup_extents(tctx
, tree
, tmp_ctx
,
5463 &src_h
, 32768, /* fill 32768 byte src file */
5464 SEC_RIGHTS_FILE_ALL
,
5465 &dest_h
, 0, /* 0 byte dest file */
5466 SEC_RIGHTS_FILE_ALL
,
5470 torture_fail(tctx
, "setup dup extents error");
5473 status
= test_ioctl_fs_supported(tctx
, tree
, tmp_ctx
, &src_h
,
5474 FILE_SUPPORTS_BLOCK_REFCOUNTING
, &ok
);
5475 torture_assert_ntstatus_ok(tctx
, status
, "SMB2_GETINFO_FS");
5477 smb2_util_close(tree
, src_h
);
5478 smb2_util_close(tree
, dest_h
);
5479 talloc_free(tmp_ctx
);
5480 torture_skip(tctx
, "block refcounting not supported\n");
5484 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
5485 io
.generic
.in
.file
.handle
= dest_h
;
5486 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
5487 torture_assert_ntstatus_ok(tctx
, status
, "getinfo");
5488 torture_assert_int_equal(tctx
, (int)io
.all_info2
.out
.size
,
5489 0, "size after IO");
5491 /* exceed src file len */
5492 dup_ext_buf
.source_off
= 0;
5493 dup_ext_buf
.target_off
= 0;
5494 dup_ext_buf
.byte_count
= 32768 * 2;
5496 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
5498 (ndr_push_flags_fn_t
)ndr_push_fsctl_dup_extents_to_file
);
5499 torture_assert_ndr_success(tctx
, ndr_ret
,
5500 "ndr_push_fsctl_dup_extents_to_file");
5502 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
5503 torture_assert_ntstatus_equal(tctx
, status
, NT_STATUS_NOT_SUPPORTED
,
5504 "FSCTL_DUP_EXTENTS_TO_FILE");
5506 /* the file sizes shouldn't have been changed */
5508 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
5509 io
.generic
.in
.file
.handle
= src_h
;
5510 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
5511 torture_assert_ntstatus_ok(tctx
, status
, "getinfo");
5512 torture_assert_int_equal(tctx
, (int)io
.all_info2
.out
.size
,
5513 32768, "size after IO");
5516 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
5517 io
.generic
.in
.file
.handle
= dest_h
;
5518 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
5519 torture_assert_ntstatus_ok(tctx
, status
, "getinfo");
5520 torture_assert_int_equal(tctx
, (int)io
.all_info2
.out
.size
,
5521 0, "size after IO");
5523 smb2_util_close(tree
, src_h
);
5524 smb2_util_close(tree
, dest_h
);
5525 talloc_free(tmp_ctx
);
5529 static bool test_ioctl_dup_extents_len_zero(struct torture_context
*tctx
,
5530 struct smb2_tree
*tree
)
5532 struct smb2_handle src_h
;
5533 struct smb2_handle dest_h
;
5535 union smb_ioctl ioctl
;
5536 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
5537 struct fsctl_dup_extents_to_file dup_ext_buf
;
5538 enum ndr_err_code ndr_ret
;
5539 union smb_fileinfo io
;
5542 ok
= test_setup_dup_extents(tctx
, tree
, tmp_ctx
,
5543 &src_h
, 32768, /* fill 32768 byte src file */
5544 SEC_RIGHTS_FILE_ALL
,
5545 &dest_h
, 0, /* 0 byte dest file */
5546 SEC_RIGHTS_FILE_ALL
,
5550 torture_fail(tctx
, "setup dup extents error");
5553 status
= test_ioctl_fs_supported(tctx
, tree
, tmp_ctx
, &src_h
,
5554 FILE_SUPPORTS_BLOCK_REFCOUNTING
, &ok
);
5555 torture_assert_ntstatus_ok(tctx
, status
, "SMB2_GETINFO_FS");
5557 smb2_util_close(tree
, src_h
);
5558 smb2_util_close(tree
, dest_h
);
5559 talloc_free(tmp_ctx
);
5560 torture_skip(tctx
, "block refcounting not supported\n");
5564 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
5565 io
.generic
.in
.file
.handle
= dest_h
;
5566 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
5567 torture_assert_ntstatus_ok(tctx
, status
, "getinfo");
5568 torture_assert_int_equal(tctx
, (int)io
.all_info2
.out
.size
,
5569 0, "size after IO");
5571 dup_ext_buf
.source_off
= 0;
5572 dup_ext_buf
.target_off
= 0;
5573 dup_ext_buf
.byte_count
= 0;
5575 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
5577 (ndr_push_flags_fn_t
)ndr_push_fsctl_dup_extents_to_file
);
5578 torture_assert_ndr_success(tctx
, ndr_ret
,
5579 "ndr_push_fsctl_dup_extents_to_file");
5581 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
5582 torture_assert_ntstatus_ok(tctx
, status
, "FSCTL_DUP_EXTENTS_TO_FILE");
5584 /* the file sizes shouldn't have been changed */
5586 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
5587 io
.generic
.in
.file
.handle
= src_h
;
5588 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
5589 torture_assert_ntstatus_ok(tctx
, status
, "getinfo");
5590 torture_assert_int_equal(tctx
, (int)io
.all_info2
.out
.size
,
5591 32768, "size after IO");
5594 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
5595 io
.generic
.in
.file
.handle
= dest_h
;
5596 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
5597 torture_assert_ntstatus_ok(tctx
, status
, "getinfo");
5598 torture_assert_int_equal(tctx
, (int)io
.all_info2
.out
.size
,
5599 0, "size after IO");
5601 smb2_util_close(tree
, src_h
);
5602 smb2_util_close(tree
, dest_h
);
5603 talloc_free(tmp_ctx
);
5607 static bool test_ioctl_dup_extents_sparse_src(struct torture_context
*tctx
,
5608 struct smb2_tree
*tree
)
5610 struct smb2_handle src_h
;
5611 struct smb2_handle dest_h
;
5613 union smb_ioctl ioctl
;
5614 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
5615 struct fsctl_dup_extents_to_file dup_ext_buf
;
5616 enum ndr_err_code ndr_ret
;
5617 union smb_setfileinfo sinfo
;
5620 ok
= test_setup_dup_extents(tctx
, tree
, tmp_ctx
,
5621 &src_h
, 0, /* filled after sparse flag */
5622 SEC_RIGHTS_FILE_ALL
,
5623 &dest_h
, 0, /* 0 byte dest file */
5624 SEC_RIGHTS_FILE_ALL
,
5628 torture_fail(tctx
, "setup dup extents error");
5631 status
= test_ioctl_fs_supported(tctx
, tree
, tmp_ctx
, &src_h
,
5632 FILE_SUPPORTS_BLOCK_REFCOUNTING
5633 | FILE_SUPPORTS_SPARSE_FILES
, &ok
);
5634 torture_assert_ntstatus_ok(tctx
, status
, "SMB2_GETINFO_FS");
5636 smb2_util_close(tree
, src_h
);
5637 smb2_util_close(tree
, dest_h
);
5638 talloc_free(tmp_ctx
);
5640 "block refcounting and sparse files not supported\n");
5643 /* set sparse flag on src */
5644 status
= test_ioctl_sparse_req(tctx
, tmp_ctx
, tree
, src_h
, true);
5645 torture_assert_ntstatus_ok(tctx
, status
, "FSCTL_SET_SPARSE");
5647 ok
= write_pattern(tctx
, tree
, tmp_ctx
, src_h
, 0, 4096, 0);
5648 torture_assert(tctx
, ok
, "write pattern");
5652 sinfo
.end_of_file_info
.level
= RAW_SFILEINFO_END_OF_FILE_INFORMATION
;
5653 sinfo
.end_of_file_info
.in
.file
.handle
= dest_h
;
5654 sinfo
.end_of_file_info
.in
.size
= 4096;
5655 status
= smb2_setinfo_file(tree
, &sinfo
);
5656 torture_assert_ntstatus_ok(tctx
, status
, "smb2_setinfo_file");
5658 /* copy all src file data */
5659 dup_ext_buf
.source_off
= 0;
5660 dup_ext_buf
.target_off
= 0;
5661 dup_ext_buf
.byte_count
= 4096;
5663 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
5665 (ndr_push_flags_fn_t
)ndr_push_fsctl_dup_extents_to_file
);
5666 torture_assert_ndr_success(tctx
, ndr_ret
,
5667 "ndr_push_fsctl_dup_extents_to_file");
5670 * src is sparse, but spec says: 2.3.8 FSCTL_DUPLICATE_EXTENTS_TO_FILE
5671 * Reply... STATUS_NOT_SUPPORTED: Target file is sparse, while source
5672 * is a non-sparse file.
5674 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
5675 torture_assert_ntstatus_equal(tctx
, status
, NT_STATUS_NOT_SUPPORTED
,
5676 "FSCTL_DUP_EXTENTS_TO_FILE");
5678 smb2_util_close(tree
, src_h
);
5679 smb2_util_close(tree
, dest_h
);
5680 talloc_free(tmp_ctx
);
5684 static bool test_ioctl_dup_extents_sparse_dest(struct torture_context
*tctx
,
5685 struct smb2_tree
*tree
)
5687 struct smb2_handle src_h
;
5688 struct smb2_handle dest_h
;
5690 union smb_ioctl ioctl
;
5691 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
5692 struct fsctl_dup_extents_to_file dup_ext_buf
;
5693 enum ndr_err_code ndr_ret
;
5694 union smb_setfileinfo sinfo
;
5697 ok
= test_setup_dup_extents(tctx
, tree
, tmp_ctx
,
5698 &src_h
, 4096, /* fill 4096 byte src file */
5699 SEC_RIGHTS_FILE_ALL
,
5700 &dest_h
, 0, /* 0 byte dest file */
5701 SEC_RIGHTS_FILE_ALL
,
5705 torture_fail(tctx
, "setup dup extents error");
5708 status
= test_ioctl_fs_supported(tctx
, tree
, tmp_ctx
, &src_h
,
5709 FILE_SUPPORTS_BLOCK_REFCOUNTING
5710 | FILE_SUPPORTS_SPARSE_FILES
, &ok
);
5711 torture_assert_ntstatus_ok(tctx
, status
, "SMB2_GETINFO_FS");
5713 smb2_util_close(tree
, src_h
);
5714 smb2_util_close(tree
, dest_h
);
5715 talloc_free(tmp_ctx
);
5717 "block refcounting and sparse files not supported\n");
5720 /* set sparse flag on dest */
5721 status
= test_ioctl_sparse_req(tctx
, tmp_ctx
, tree
, dest_h
, true);
5722 torture_assert_ntstatus_ok(tctx
, status
, "FSCTL_SET_SPARSE");
5726 sinfo
.end_of_file_info
.level
= RAW_SFILEINFO_END_OF_FILE_INFORMATION
;
5727 sinfo
.end_of_file_info
.in
.file
.handle
= dest_h
;
5728 sinfo
.end_of_file_info
.in
.size
= dup_ext_buf
.byte_count
;
5729 status
= smb2_setinfo_file(tree
, &sinfo
);
5730 torture_assert_ntstatus_ok(tctx
, status
, "smb2_setinfo_file");
5732 /* copy all src file data */
5733 dup_ext_buf
.source_off
= 0;
5734 dup_ext_buf
.target_off
= 0;
5735 dup_ext_buf
.byte_count
= 4096;
5737 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
5739 (ndr_push_flags_fn_t
)ndr_push_fsctl_dup_extents_to_file
);
5740 torture_assert_ndr_success(tctx
, ndr_ret
,
5741 "ndr_push_fsctl_dup_extents_to_file");
5744 * dest is sparse, but spec says: 2.3.8 FSCTL_DUPLICATE_EXTENTS_TO_FILE
5745 * Reply... STATUS_NOT_SUPPORTED: Target file is sparse, while source
5746 * is a non-sparse file.
5748 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
5749 torture_assert_ntstatus_ok(tctx
, status
, "FSCTL_DUP_EXTENTS_TO_FILE");
5751 smb2_util_close(tree
, src_h
);
5752 smb2_util_close(tree
, dest_h
);
5753 talloc_free(tmp_ctx
);
5757 static bool test_ioctl_dup_extents_sparse_both(struct torture_context
*tctx
,
5758 struct smb2_tree
*tree
)
5760 struct smb2_handle src_h
;
5761 struct smb2_handle dest_h
;
5763 union smb_ioctl ioctl
;
5764 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
5765 struct fsctl_dup_extents_to_file dup_ext_buf
;
5766 enum ndr_err_code ndr_ret
;
5767 union smb_setfileinfo sinfo
;
5770 ok
= test_setup_dup_extents(tctx
, tree
, tmp_ctx
,
5771 &src_h
, 0, /* fill 4096 byte src file */
5772 SEC_RIGHTS_FILE_ALL
,
5773 &dest_h
, 0, /* 0 byte dest file */
5774 SEC_RIGHTS_FILE_ALL
,
5778 torture_fail(tctx
, "setup dup extents error");
5781 status
= test_ioctl_fs_supported(tctx
, tree
, tmp_ctx
, &src_h
,
5782 FILE_SUPPORTS_BLOCK_REFCOUNTING
5783 | FILE_SUPPORTS_SPARSE_FILES
, &ok
);
5784 torture_assert_ntstatus_ok(tctx
, status
, "SMB2_GETINFO_FS");
5786 smb2_util_close(tree
, src_h
);
5787 smb2_util_close(tree
, dest_h
);
5788 talloc_free(tmp_ctx
);
5790 "block refcounting and sparse files not supported\n");
5793 /* set sparse flag on src and dest */
5794 status
= test_ioctl_sparse_req(tctx
, tmp_ctx
, tree
, src_h
, true);
5795 torture_assert_ntstatus_ok(tctx
, status
, "FSCTL_SET_SPARSE");
5796 status
= test_ioctl_sparse_req(tctx
, tmp_ctx
, tree
, dest_h
, true);
5797 torture_assert_ntstatus_ok(tctx
, status
, "FSCTL_SET_SPARSE");
5799 ok
= write_pattern(tctx
, tree
, tmp_ctx
, src_h
, 0, 4096, 0);
5800 torture_assert(tctx
, ok
, "write pattern");
5804 sinfo
.end_of_file_info
.level
= RAW_SFILEINFO_END_OF_FILE_INFORMATION
;
5805 sinfo
.end_of_file_info
.in
.file
.handle
= dest_h
;
5806 sinfo
.end_of_file_info
.in
.size
= 4096;
5807 status
= smb2_setinfo_file(tree
, &sinfo
);
5808 torture_assert_ntstatus_ok(tctx
, status
, "smb2_setinfo_file");
5810 /* copy all src file data */
5811 dup_ext_buf
.source_off
= 0;
5812 dup_ext_buf
.target_off
= 0;
5813 dup_ext_buf
.byte_count
= 4096;
5815 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
5817 (ndr_push_flags_fn_t
)ndr_push_fsctl_dup_extents_to_file
);
5818 torture_assert_ndr_success(tctx
, ndr_ret
,
5819 "ndr_push_fsctl_dup_extents_to_file");
5821 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
5822 torture_assert_ntstatus_ok(tctx
, status
, "FSCTL_DUP_EXTENTS_TO_FILE");
5824 smb2_util_close(tree
, src_h
);
5825 smb2_util_close(tree
, dest_h
);
5827 /* reopen for pattern check */
5828 ok
= test_setup_open(tctx
, tree
, tmp_ctx
, FNAME2
, &dest_h
,
5829 SEC_RIGHTS_FILE_ALL
, FILE_ATTRIBUTE_NORMAL
);
5830 torture_assert_ntstatus_ok(tctx
, status
, "dest open ater dup");
5832 ok
= check_pattern(tctx
, tree
, tmp_ctx
, dest_h
, 0, 4096, 0);
5834 torture_fail(tctx
, "inconsistent file data");
5837 smb2_util_close(tree
, dest_h
);
5838 talloc_free(tmp_ctx
);
5842 static bool test_ioctl_dup_extents_src_is_dest(struct torture_context
*tctx
,
5843 struct smb2_tree
*tree
)
5845 struct smb2_handle src_h
;
5846 struct smb2_handle dest_h
;
5848 union smb_ioctl ioctl
;
5849 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
5850 struct fsctl_dup_extents_to_file dup_ext_buf
;
5851 enum ndr_err_code ndr_ret
;
5852 union smb_fileinfo io
;
5855 ok
= test_setup_dup_extents(tctx
, tree
, tmp_ctx
,
5856 &src_h
, 32768, /* fill 32768 byte src file */
5857 SEC_RIGHTS_FILE_ALL
,
5859 SEC_RIGHTS_FILE_ALL
,
5863 torture_fail(tctx
, "setup dup extents error");
5865 /* dest_h not needed for this test */
5866 smb2_util_close(tree
, dest_h
);
5868 status
= test_ioctl_fs_supported(tctx
, tree
, tmp_ctx
, &src_h
,
5869 FILE_SUPPORTS_BLOCK_REFCOUNTING
, &ok
);
5870 torture_assert_ntstatus_ok(tctx
, status
, "SMB2_GETINFO_FS");
5872 smb2_util_close(tree
, src_h
);
5873 talloc_free(tmp_ctx
);
5874 torture_skip(tctx
, "block refcounting not supported\n");
5877 /* src and dest are the same file handle */
5878 ioctl
.smb2
.in
.file
.handle
= src_h
;
5880 /* no overlap between src and tgt */
5881 dup_ext_buf
.source_off
= 0;
5882 dup_ext_buf
.target_off
= 16384;
5883 dup_ext_buf
.byte_count
= 16384;
5885 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
5887 (ndr_push_flags_fn_t
)ndr_push_fsctl_dup_extents_to_file
);
5888 torture_assert_ndr_success(tctx
, ndr_ret
,
5889 "ndr_push_fsctl_dup_extents_to_file");
5891 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
5892 torture_assert_ntstatus_ok(tctx
, status
, "FSCTL_DUP_EXTENTS_TO_FILE");
5894 /* the file size shouldn't have been changed */
5896 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
5897 io
.generic
.in
.file
.handle
= src_h
;
5898 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
5899 torture_assert_ntstatus_ok(tctx
, status
, "getinfo");
5900 torture_assert_int_equal(tctx
, (int)io
.all_info2
.out
.size
,
5901 32768, "size after IO");
5903 ok
= check_pattern(tctx
, tree
, tmp_ctx
, src_h
, 0, 16384, 0);
5905 torture_fail(tctx
, "inconsistent file data");
5907 ok
= check_pattern(tctx
, tree
, tmp_ctx
, src_h
, 16384, 16384, 0);
5909 torture_fail(tctx
, "inconsistent file data");
5912 smb2_util_close(tree
, src_h
);
5913 talloc_free(tmp_ctx
);
5918 * unlike copy-chunk, dup extents doesn't support overlapping ranges between
5919 * source and target. This makes it a *lot* cleaner to implement on the server.
5922 test_ioctl_dup_extents_src_is_dest_overlap(struct torture_context
*tctx
,
5923 struct smb2_tree
*tree
)
5925 struct smb2_handle src_h
;
5926 struct smb2_handle dest_h
;
5928 union smb_ioctl ioctl
;
5929 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
5930 struct fsctl_dup_extents_to_file dup_ext_buf
;
5931 enum ndr_err_code ndr_ret
;
5932 union smb_fileinfo io
;
5935 ok
= test_setup_dup_extents(tctx
, tree
, tmp_ctx
,
5936 &src_h
, 32768, /* fill 32768 byte src file */
5937 SEC_RIGHTS_FILE_ALL
,
5939 SEC_RIGHTS_FILE_ALL
,
5943 torture_fail(tctx
, "setup dup extents error");
5945 /* dest_h not needed for this test */
5946 smb2_util_close(tree
, dest_h
);
5948 status
= test_ioctl_fs_supported(tctx
, tree
, tmp_ctx
, &src_h
,
5949 FILE_SUPPORTS_BLOCK_REFCOUNTING
, &ok
);
5950 torture_assert_ntstatus_ok(tctx
, status
, "SMB2_GETINFO_FS");
5952 smb2_util_close(tree
, src_h
);
5953 talloc_free(tmp_ctx
);
5954 torture_skip(tctx
, "block refcounting not supported\n");
5957 /* src and dest are the same file handle */
5958 ioctl
.smb2
.in
.file
.handle
= src_h
;
5960 /* 8K overlap between src and tgt */
5961 dup_ext_buf
.source_off
= 0;
5962 dup_ext_buf
.target_off
= 8192;
5963 dup_ext_buf
.byte_count
= 16384;
5965 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
5967 (ndr_push_flags_fn_t
)ndr_push_fsctl_dup_extents_to_file
);
5968 torture_assert_ndr_success(tctx
, ndr_ret
,
5969 "ndr_push_fsctl_dup_extents_to_file");
5971 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
5972 torture_assert_ntstatus_equal(tctx
, status
, NT_STATUS_NOT_SUPPORTED
,
5973 "FSCTL_DUP_EXTENTS_TO_FILE");
5975 /* the file size and data should match beforehand */
5977 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
5978 io
.generic
.in
.file
.handle
= src_h
;
5979 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
5980 torture_assert_ntstatus_ok(tctx
, status
, "getinfo");
5981 torture_assert_int_equal(tctx
, (int)io
.all_info2
.out
.size
,
5982 32768, "size after IO");
5984 ok
= check_pattern(tctx
, tree
, tmp_ctx
, src_h
, 0, 32768, 0);
5986 torture_fail(tctx
, "inconsistent file data");
5989 smb2_util_close(tree
, src_h
);
5990 talloc_free(tmp_ctx
);
5995 * The compression tests won't run against Windows servers yet - ReFS doesn't
5996 * (yet) offer support for compression.
5998 static bool test_ioctl_dup_extents_compressed_src(struct torture_context
*tctx
,
5999 struct smb2_tree
*tree
)
6001 struct smb2_handle src_h
;
6002 struct smb2_handle dest_h
;
6004 union smb_ioctl ioctl
;
6005 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
6006 struct fsctl_dup_extents_to_file dup_ext_buf
;
6007 enum ndr_err_code ndr_ret
;
6008 union smb_setfileinfo sinfo
;
6011 ok
= test_setup_dup_extents(tctx
, tree
, tmp_ctx
,
6012 &src_h
, 0, /* filled after compressed flag */
6013 SEC_RIGHTS_FILE_ALL
,
6015 SEC_RIGHTS_FILE_ALL
,
6019 torture_fail(tctx
, "setup dup extents error");
6022 status
= test_ioctl_fs_supported(tctx
, tree
, tmp_ctx
, &src_h
,
6023 FILE_SUPPORTS_BLOCK_REFCOUNTING
6024 | FILE_FILE_COMPRESSION
, &ok
);
6025 torture_assert_ntstatus_ok(tctx
, status
, "SMB2_GETINFO_FS");
6027 smb2_util_close(tree
, src_h
);
6028 smb2_util_close(tree
, dest_h
);
6029 talloc_free(tmp_ctx
);
6031 "block refcounting and compressed files not supported\n");
6034 /* set compressed flag on src */
6035 status
= test_ioctl_compress_set(tctx
, tmp_ctx
, tree
, src_h
,
6036 COMPRESSION_FORMAT_DEFAULT
);
6037 torture_assert_ntstatus_ok(tctx
, status
, "FSCTL_SET_COMPRESSION");
6039 ok
= write_pattern(tctx
, tree
, tmp_ctx
, src_h
, 0, 4096, 0);
6040 torture_assert(tctx
, ok
, "write pattern");
6044 sinfo
.end_of_file_info
.level
= RAW_SFILEINFO_END_OF_FILE_INFORMATION
;
6045 sinfo
.end_of_file_info
.in
.file
.handle
= dest_h
;
6046 sinfo
.end_of_file_info
.in
.size
= 4096;
6047 status
= smb2_setinfo_file(tree
, &sinfo
);
6048 torture_assert_ntstatus_ok(tctx
, status
, "smb2_setinfo_file");
6050 /* copy all src file data */
6051 dup_ext_buf
.source_off
= 0;
6052 dup_ext_buf
.target_off
= 0;
6053 dup_ext_buf
.byte_count
= 4096;
6055 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
6057 (ndr_push_flags_fn_t
)ndr_push_fsctl_dup_extents_to_file
);
6058 torture_assert_ndr_success(tctx
, ndr_ret
,
6059 "ndr_push_fsctl_dup_extents_to_file");
6061 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
6062 torture_assert_ntstatus_ok(tctx
, status
,
6063 "FSCTL_DUP_EXTENTS_TO_FILE");
6065 ok
= check_pattern(tctx
, tree
, tmp_ctx
, dest_h
, 0, 4096, 0);
6067 torture_fail(tctx
, "inconsistent file data");
6070 smb2_util_close(tree
, src_h
);
6071 smb2_util_close(tree
, dest_h
);
6072 talloc_free(tmp_ctx
);
6076 static bool test_ioctl_dup_extents_compressed_dest(struct torture_context
*tctx
,
6077 struct smb2_tree
*tree
)
6079 struct smb2_handle src_h
;
6080 struct smb2_handle dest_h
;
6082 union smb_ioctl ioctl
;
6083 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
6084 struct fsctl_dup_extents_to_file dup_ext_buf
;
6085 enum ndr_err_code ndr_ret
;
6086 union smb_setfileinfo sinfo
;
6089 ok
= test_setup_dup_extents(tctx
, tree
, tmp_ctx
,
6091 SEC_RIGHTS_FILE_ALL
,
6093 SEC_RIGHTS_FILE_ALL
,
6097 torture_fail(tctx
, "setup dup extents error");
6100 status
= test_ioctl_fs_supported(tctx
, tree
, tmp_ctx
, &src_h
,
6101 FILE_SUPPORTS_BLOCK_REFCOUNTING
6102 | FILE_FILE_COMPRESSION
, &ok
);
6103 torture_assert_ntstatus_ok(tctx
, status
, "SMB2_GETINFO_FS");
6105 smb2_util_close(tree
, src_h
);
6106 smb2_util_close(tree
, dest_h
);
6107 talloc_free(tmp_ctx
);
6109 "block refcounting and compressed files not supported\n");
6112 /* set compressed flag on dest */
6113 status
= test_ioctl_compress_set(tctx
, tmp_ctx
, tree
, dest_h
,
6114 COMPRESSION_FORMAT_DEFAULT
);
6115 torture_assert_ntstatus_ok(tctx
, status
, "FSCTL_SET_COMPRESSION");
6119 sinfo
.end_of_file_info
.level
= RAW_SFILEINFO_END_OF_FILE_INFORMATION
;
6120 sinfo
.end_of_file_info
.in
.file
.handle
= dest_h
;
6121 sinfo
.end_of_file_info
.in
.size
= 4096;
6122 status
= smb2_setinfo_file(tree
, &sinfo
);
6123 torture_assert_ntstatus_ok(tctx
, status
, "smb2_setinfo_file");
6125 /* copy all src file data */
6126 dup_ext_buf
.source_off
= 0;
6127 dup_ext_buf
.target_off
= 0;
6128 dup_ext_buf
.byte_count
= 4096;
6130 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
6132 (ndr_push_flags_fn_t
)ndr_push_fsctl_dup_extents_to_file
);
6133 torture_assert_ndr_success(tctx
, ndr_ret
,
6134 "ndr_push_fsctl_dup_extents_to_file");
6136 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
6137 torture_assert_ntstatus_ok(tctx
, status
,
6138 "FSCTL_DUP_EXTENTS_TO_FILE");
6140 ok
= check_pattern(tctx
, tree
, tmp_ctx
, dest_h
, 0, 4096, 0);
6142 torture_fail(tctx
, "inconsistent file data");
6145 smb2_util_close(tree
, src_h
);
6146 smb2_util_close(tree
, dest_h
);
6147 talloc_free(tmp_ctx
);
6151 static bool test_ioctl_dup_extents_bad_handle(struct torture_context
*tctx
,
6152 struct smb2_tree
*tree
)
6154 struct smb2_handle src_h
;
6155 struct smb2_handle dest_h
;
6156 struct smb2_handle bogus_h
;
6158 union smb_ioctl ioctl
;
6159 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
6160 struct fsctl_dup_extents_to_file dup_ext_buf
;
6161 enum ndr_err_code ndr_ret
;
6164 ok
= test_setup_dup_extents(tctx
, tree
, tmp_ctx
,
6165 &src_h
, 32768, /* fill 32768 byte src file */
6166 SEC_RIGHTS_FILE_ALL
,
6168 SEC_RIGHTS_FILE_ALL
,
6172 torture_fail(tctx
, "setup dup extents error");
6175 status
= test_ioctl_fs_supported(tctx
, tree
, tmp_ctx
, &src_h
,
6176 FILE_SUPPORTS_BLOCK_REFCOUNTING
, &ok
);
6177 torture_assert_ntstatus_ok(tctx
, status
, "SMB2_GETINFO_FS");
6179 smb2_util_close(tree
, src_h
);
6180 smb2_util_close(tree
, dest_h
);
6181 talloc_free(tmp_ctx
);
6182 torture_skip(tctx
, "block refcounting not supported\n");
6185 /* open and close a file, keeping the handle as now a "bogus" handle */
6186 ok
= test_setup_create_fill(tctx
, tree
, tmp_ctx
, "bogus_file",
6187 &bogus_h
, 0, SEC_RIGHTS_FILE_ALL
,
6188 FILE_ATTRIBUTE_NORMAL
);
6189 torture_assert(tctx
, ok
, "bogus file create fill");
6190 smb2_util_close(tree
, bogus_h
);
6192 /* bogus dest file handle */
6193 ioctl
.smb2
.in
.file
.handle
= bogus_h
;
6195 dup_ext_buf
.source_off
= 0;
6196 dup_ext_buf
.target_off
= 0;
6197 dup_ext_buf
.byte_count
= 32768;
6199 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
6201 (ndr_push_flags_fn_t
)ndr_push_fsctl_dup_extents_to_file
);
6202 torture_assert_ndr_success(tctx
, ndr_ret
,
6203 "ndr_push_fsctl_dup_extents_to_file");
6205 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
6206 torture_assert_ntstatus_equal(tctx
, status
, NT_STATUS_FILE_CLOSED
,
6207 "FSCTL_DUP_EXTENTS_TO_FILE");
6209 ok
= check_pattern(tctx
, tree
, tmp_ctx
, src_h
, 0, 32768, 0);
6211 torture_fail(tctx
, "inconsistent file data");
6213 ok
= check_pattern(tctx
, tree
, tmp_ctx
, dest_h
, 0, 32768, 0);
6215 torture_fail(tctx
, "inconsistent file data");
6218 /* reinstate dest, add bogus src file handle */
6219 ioctl
.smb2
.in
.file
.handle
= dest_h
;
6220 smb2_push_handle(dup_ext_buf
.source_fid
, &bogus_h
);
6222 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
6224 (ndr_push_flags_fn_t
)ndr_push_fsctl_dup_extents_to_file
);
6225 torture_assert_ndr_success(tctx
, ndr_ret
,
6226 "ndr_push_fsctl_dup_extents_to_file");
6228 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
6229 torture_assert_ntstatus_equal(tctx
, status
, NT_STATUS_INVALID_HANDLE
,
6230 "FSCTL_DUP_EXTENTS_TO_FILE");
6232 ok
= check_pattern(tctx
, tree
, tmp_ctx
, src_h
, 0, 32768, 0);
6234 torture_fail(tctx
, "inconsistent file data");
6236 ok
= check_pattern(tctx
, tree
, tmp_ctx
, dest_h
, 0, 32768, 0);
6238 torture_fail(tctx
, "inconsistent file data");
6241 smb2_util_close(tree
, src_h
);
6242 smb2_util_close(tree
, dest_h
);
6243 talloc_free(tmp_ctx
);
6247 static bool test_ioctl_dup_extents_src_lck(struct torture_context
*tctx
,
6248 struct smb2_tree
*tree
)
6250 struct smb2_handle src_h
;
6251 struct smb2_handle src_h2
;
6252 struct smb2_handle dest_h
;
6254 union smb_ioctl ioctl
;
6255 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
6256 struct fsctl_dup_extents_to_file dup_ext_buf
;
6257 enum ndr_err_code ndr_ret
;
6259 struct smb2_lock lck
;
6260 struct smb2_lock_element el
[1];
6262 ok
= test_setup_dup_extents(tctx
, tree
, tmp_ctx
,
6263 &src_h
, 32768, /* fill 32768 byte src file */
6264 SEC_RIGHTS_FILE_ALL
,
6266 SEC_RIGHTS_FILE_ALL
,
6270 torture_fail(tctx
, "setup dup extents error");
6273 status
= test_ioctl_fs_supported(tctx
, tree
, tmp_ctx
, &src_h
,
6274 FILE_SUPPORTS_BLOCK_REFCOUNTING
, &ok
);
6275 torture_assert_ntstatus_ok(tctx
, status
, "SMB2_GETINFO_FS");
6277 smb2_util_close(tree
, src_h
);
6278 smb2_util_close(tree
, dest_h
);
6279 talloc_free(tmp_ctx
);
6280 torture_skip(tctx
, "block refcounting not supported\n");
6283 /* dest pattern is different to src */
6284 ok
= write_pattern(tctx
, tree
, tmp_ctx
, dest_h
, 0, 32768, 32768);
6285 torture_assert(tctx
, ok
, "write pattern");
6287 /* setup dup ext req, values used for locking */
6288 dup_ext_buf
.source_off
= 0;
6289 dup_ext_buf
.target_off
= 0;
6290 dup_ext_buf
.byte_count
= 32768;
6292 /* open and lock the dup extents src file */
6293 status
= torture_smb2_testfile(tree
, FNAME
, &src_h2
);
6294 torture_assert_ntstatus_ok(tctx
, status
, "2nd src open");
6296 lck
.in
.lock_count
= 0x0001;
6297 lck
.in
.lock_sequence
= 0x00000000;
6298 lck
.in
.file
.handle
= src_h2
;
6300 el
[0].offset
= dup_ext_buf
.source_off
;
6301 el
[0].length
= dup_ext_buf
.byte_count
;
6303 el
[0].flags
= SMB2_LOCK_FLAG_EXCLUSIVE
;
6305 status
= smb2_lock(tree
, &lck
);
6306 torture_assert_ntstatus_ok(tctx
, status
, "lock");
6308 status
= smb2_util_write(tree
, src_h
,
6309 "conflicted", 0, sizeof("conflicted"));
6310 torture_assert_ntstatus_equal(tctx
, status
,
6311 NT_STATUS_FILE_LOCK_CONFLICT
, "file write");
6313 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
6315 (ndr_push_flags_fn_t
)ndr_push_fsctl_dup_extents_to_file
);
6316 torture_assert_ndr_success(tctx
, ndr_ret
,
6317 "ndr_push_fsctl_dup_extents_to_file");
6320 * In contrast to copy-chunk, dup extents doesn't cause a lock conflict
6323 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
6324 torture_assert_ntstatus_ok(tctx
, status
, "FSCTL_DUP_EXTENTS_TO_FILE");
6326 ok
= check_pattern(tctx
, tree
, tmp_ctx
, dest_h
, 0, 32768, 0);
6328 torture_fail(tctx
, "inconsistent file data");
6331 lck
.in
.lock_count
= 0x0001;
6332 lck
.in
.lock_sequence
= 0x00000001;
6333 lck
.in
.file
.handle
= src_h2
;
6335 el
[0].offset
= dup_ext_buf
.source_off
;
6336 el
[0].length
= dup_ext_buf
.byte_count
;
6338 el
[0].flags
= SMB2_LOCK_FLAG_UNLOCK
;
6339 status
= smb2_lock(tree
, &lck
);
6340 torture_assert_ntstatus_ok(tctx
, status
, "unlock");
6342 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
6343 torture_assert_ntstatus_ok(tctx
, status
,
6344 "FSCTL_DUP_EXTENTS_TO_FILE unlocked");
6346 ok
= check_pattern(tctx
, tree
, tmp_ctx
, dest_h
, 0, 32768, 0);
6348 torture_fail(tctx
, "inconsistent file data");
6351 smb2_util_close(tree
, src_h2
);
6352 smb2_util_close(tree
, src_h
);
6353 smb2_util_close(tree
, dest_h
);
6354 talloc_free(tmp_ctx
);
6358 static bool test_ioctl_dup_extents_dest_lck(struct torture_context
*tctx
,
6359 struct smb2_tree
*tree
)
6361 struct smb2_handle src_h
;
6362 struct smb2_handle dest_h
;
6363 struct smb2_handle dest_h2
;
6365 union smb_ioctl ioctl
;
6366 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
6367 struct fsctl_dup_extents_to_file dup_ext_buf
;
6368 enum ndr_err_code ndr_ret
;
6370 struct smb2_lock lck
;
6371 struct smb2_lock_element el
[1];
6373 ok
= test_setup_dup_extents(tctx
, tree
, tmp_ctx
,
6374 &src_h
, 32768, /* fill 32768 byte src file */
6375 SEC_RIGHTS_FILE_ALL
,
6377 SEC_RIGHTS_FILE_ALL
,
6381 torture_fail(tctx
, "setup dup extents error");
6384 status
= test_ioctl_fs_supported(tctx
, tree
, tmp_ctx
, &src_h
,
6385 FILE_SUPPORTS_BLOCK_REFCOUNTING
, &ok
);
6386 torture_assert_ntstatus_ok(tctx
, status
, "SMB2_GETINFO_FS");
6388 smb2_util_close(tree
, src_h
);
6389 smb2_util_close(tree
, dest_h
);
6390 talloc_free(tmp_ctx
);
6391 torture_skip(tctx
, "block refcounting not supported\n");
6394 /* dest pattern is different to src */
6395 ok
= write_pattern(tctx
, tree
, tmp_ctx
, dest_h
, 0, 32768, 32768);
6396 torture_assert(tctx
, ok
, "write pattern");
6398 /* setup dup ext req, values used for locking */
6399 dup_ext_buf
.source_off
= 0;
6400 dup_ext_buf
.target_off
= 0;
6401 dup_ext_buf
.byte_count
= 32768;
6403 /* open and lock the dup extents dest file */
6404 status
= torture_smb2_testfile(tree
, FNAME2
, &dest_h2
);
6405 torture_assert_ntstatus_ok(tctx
, status
, "2nd src open");
6407 lck
.in
.lock_count
= 0x0001;
6408 lck
.in
.lock_sequence
= 0x00000000;
6409 lck
.in
.file
.handle
= dest_h2
;
6411 el
[0].offset
= dup_ext_buf
.source_off
;
6412 el
[0].length
= dup_ext_buf
.byte_count
;
6414 el
[0].flags
= SMB2_LOCK_FLAG_EXCLUSIVE
;
6416 status
= smb2_lock(tree
, &lck
);
6417 torture_assert_ntstatus_ok(tctx
, status
, "lock");
6419 status
= smb2_util_write(tree
, dest_h
,
6420 "conflicted", 0, sizeof("conflicted"));
6421 torture_assert_ntstatus_equal(tctx
, status
,
6422 NT_STATUS_FILE_LOCK_CONFLICT
, "file write");
6424 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
6426 (ndr_push_flags_fn_t
)ndr_push_fsctl_dup_extents_to_file
);
6427 torture_assert_ndr_success(tctx
, ndr_ret
,
6428 "ndr_push_fsctl_dup_extents_to_file");
6431 * In contrast to copy-chunk, dup extents doesn't cause a lock conflict
6434 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
6435 torture_assert_ntstatus_ok(tctx
, status
, "FSCTL_DUP_EXTENTS_TO_FILE");
6437 lck
.in
.lock_count
= 0x0001;
6438 lck
.in
.lock_sequence
= 0x00000001;
6439 lck
.in
.file
.handle
= dest_h2
;
6441 el
[0].offset
= dup_ext_buf
.source_off
;
6442 el
[0].length
= dup_ext_buf
.byte_count
;
6444 el
[0].flags
= SMB2_LOCK_FLAG_UNLOCK
;
6445 status
= smb2_lock(tree
, &lck
);
6446 torture_assert_ntstatus_ok(tctx
, status
, "unlock");
6448 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
6449 torture_assert_ntstatus_ok(tctx
, status
,
6450 "FSCTL_DUP_EXTENTS_TO_FILE unlocked");
6452 ok
= check_pattern(tctx
, tree
, tmp_ctx
, dest_h
, 0, 32768, 0);
6454 torture_fail(tctx
, "inconsistent file data");
6457 smb2_util_close(tree
, src_h
);
6458 smb2_util_close(tree
, dest_h
);
6459 smb2_util_close(tree
, dest_h2
);
6460 talloc_free(tmp_ctx
);
6465 * testing of SMB2 ioctls
6467 struct torture_suite
*torture_smb2_ioctl_init(TALLOC_CTX
*ctx
)
6469 struct torture_suite
*suite
= torture_suite_create(ctx
, "ioctl");
6471 torture_suite_add_1smb2_test(suite
, "shadow_copy",
6472 test_ioctl_get_shadow_copy
);
6473 torture_suite_add_1smb2_test(suite
, "req_resume_key",
6474 test_ioctl_req_resume_key
);
6475 torture_suite_add_1smb2_test(suite
, "copy_chunk_simple",
6476 test_ioctl_copy_chunk_simple
);
6477 torture_suite_add_1smb2_test(suite
, "copy_chunk_multi",
6478 test_ioctl_copy_chunk_multi
);
6479 torture_suite_add_1smb2_test(suite
, "copy_chunk_tiny",
6480 test_ioctl_copy_chunk_tiny
);
6481 torture_suite_add_1smb2_test(suite
, "copy_chunk_overwrite",
6482 test_ioctl_copy_chunk_over
);
6483 torture_suite_add_1smb2_test(suite
, "copy_chunk_append",
6484 test_ioctl_copy_chunk_append
);
6485 torture_suite_add_1smb2_test(suite
, "copy_chunk_limits",
6486 test_ioctl_copy_chunk_limits
);
6487 torture_suite_add_1smb2_test(suite
, "copy_chunk_src_lock",
6488 test_ioctl_copy_chunk_src_lck
);
6489 torture_suite_add_1smb2_test(suite
, "copy_chunk_dest_lock",
6490 test_ioctl_copy_chunk_dest_lck
);
6491 torture_suite_add_1smb2_test(suite
, "copy_chunk_bad_key",
6492 test_ioctl_copy_chunk_bad_key
);
6493 torture_suite_add_1smb2_test(suite
, "copy_chunk_src_is_dest",
6494 test_ioctl_copy_chunk_src_is_dest
);
6495 torture_suite_add_1smb2_test(suite
, "copy_chunk_src_is_dest_overlap",
6496 test_ioctl_copy_chunk_src_is_dest_overlap
);
6497 torture_suite_add_1smb2_test(suite
, "copy_chunk_bad_access",
6498 test_ioctl_copy_chunk_bad_access
);
6499 torture_suite_add_1smb2_test(suite
, "copy_chunk_write_access",
6500 test_ioctl_copy_chunk_write_access
);
6501 torture_suite_add_1smb2_test(suite
, "copy_chunk_src_exceed",
6502 test_ioctl_copy_chunk_src_exceed
);
6503 torture_suite_add_1smb2_test(suite
, "copy_chunk_src_exceed_multi",
6504 test_ioctl_copy_chunk_src_exceed_multi
);
6505 torture_suite_add_1smb2_test(suite
, "copy_chunk_sparse_dest",
6506 test_ioctl_copy_chunk_sparse_dest
);
6507 torture_suite_add_1smb2_test(suite
, "copy_chunk_max_output_sz",
6508 test_ioctl_copy_chunk_max_output_sz
);
6509 torture_suite_add_1smb2_test(suite
, "copy_chunk_zero_length",
6510 test_ioctl_copy_chunk_zero_length
);
6511 torture_suite_add_1smb2_test(suite
, "copy-chunk streams",
6512 test_copy_chunk_streams
);
6513 torture_suite_add_1smb2_test(suite
, "copy_chunk_across_shares",
6514 test_copy_chunk_across_shares
);
6515 torture_suite_add_1smb2_test(suite
, "compress_file_flag",
6516 test_ioctl_compress_file_flag
);
6517 torture_suite_add_1smb2_test(suite
, "compress_dir_inherit",
6518 test_ioctl_compress_dir_inherit
);
6519 torture_suite_add_1smb2_test(suite
, "compress_invalid_format",
6520 test_ioctl_compress_invalid_format
);
6521 torture_suite_add_1smb2_test(suite
, "compress_invalid_buf",
6522 test_ioctl_compress_invalid_buf
);
6523 torture_suite_add_1smb2_test(suite
, "compress_query_file_attr",
6524 test_ioctl_compress_query_file_attr
);
6525 torture_suite_add_1smb2_test(suite
, "compress_create_with_attr",
6526 test_ioctl_compress_create_with_attr
);
6527 torture_suite_add_1smb2_test(suite
, "compress_inherit_disable",
6528 test_ioctl_compress_inherit_disable
);
6529 torture_suite_add_1smb2_test(suite
, "compress_set_file_attr",
6530 test_ioctl_compress_set_file_attr
);
6531 torture_suite_add_1smb2_test(suite
, "compress_perms",
6532 test_ioctl_compress_perms
);
6533 torture_suite_add_1smb2_test(suite
, "compress_notsup_get",
6534 test_ioctl_compress_notsup_get
);
6535 torture_suite_add_1smb2_test(suite
, "compress_notsup_set",
6536 test_ioctl_compress_notsup_set
);
6537 torture_suite_add_1smb2_test(suite
, "network_interface_info",
6538 test_ioctl_network_interface_info
);
6539 torture_suite_add_1smb2_test(suite
, "sparse_file_flag",
6540 test_ioctl_sparse_file_flag
);
6541 torture_suite_add_1smb2_test(suite
, "sparse_file_attr",
6542 test_ioctl_sparse_file_attr
);
6543 torture_suite_add_1smb2_test(suite
, "sparse_dir_flag",
6544 test_ioctl_sparse_dir_flag
);
6545 torture_suite_add_1smb2_test(suite
, "sparse_set_nobuf",
6546 test_ioctl_sparse_set_nobuf
);
6547 torture_suite_add_1smb2_test(suite
, "sparse_set_oversize",
6548 test_ioctl_sparse_set_oversize
);
6549 torture_suite_add_1smb2_test(suite
, "sparse_qar",
6550 test_ioctl_sparse_qar
);
6551 torture_suite_add_1smb2_test(suite
, "sparse_qar_malformed",
6552 test_ioctl_sparse_qar_malformed
);
6553 torture_suite_add_1smb2_test(suite
, "sparse_punch",
6554 test_ioctl_sparse_punch
);
6555 torture_suite_add_1smb2_test(suite
, "sparse_hole_dealloc",
6556 test_ioctl_sparse_hole_dealloc
);
6557 torture_suite_add_1smb2_test(suite
, "sparse_compressed",
6558 test_ioctl_sparse_compressed
);
6559 torture_suite_add_1smb2_test(suite
, "sparse_copy_chunk",
6560 test_ioctl_sparse_copy_chunk
);
6561 torture_suite_add_1smb2_test(suite
, "sparse_punch_invalid",
6562 test_ioctl_sparse_punch_invalid
);
6563 torture_suite_add_1smb2_test(suite
, "sparse_perms",
6564 test_ioctl_sparse_perms
);
6565 torture_suite_add_1smb2_test(suite
, "sparse_lock",
6566 test_ioctl_sparse_lck
);
6567 torture_suite_add_1smb2_test(suite
, "sparse_qar_ob1",
6568 test_ioctl_sparse_qar_ob1
);
6569 torture_suite_add_1smb2_test(suite
, "sparse_qar_multi",
6570 test_ioctl_sparse_qar_multi
);
6571 torture_suite_add_1smb2_test(suite
, "sparse_qar_overflow",
6572 test_ioctl_sparse_qar_overflow
);
6573 torture_suite_add_1smb2_test(suite
, "trim_simple",
6574 test_ioctl_trim_simple
);
6575 torture_suite_add_1smb2_test(suite
, "dup_extents_simple",
6576 test_ioctl_dup_extents_simple
);
6577 torture_suite_add_1smb2_test(suite
, "dup_extents_len_beyond_dest",
6578 test_ioctl_dup_extents_len_beyond_dest
);
6579 torture_suite_add_1smb2_test(suite
, "dup_extents_len_beyond_src",
6580 test_ioctl_dup_extents_len_beyond_src
);
6581 torture_suite_add_1smb2_test(suite
, "dup_extents_len_zero",
6582 test_ioctl_dup_extents_len_zero
);
6583 torture_suite_add_1smb2_test(suite
, "dup_extents_sparse_src",
6584 test_ioctl_dup_extents_sparse_src
);
6585 torture_suite_add_1smb2_test(suite
, "dup_extents_sparse_dest",
6586 test_ioctl_dup_extents_sparse_dest
);
6587 torture_suite_add_1smb2_test(suite
, "dup_extents_sparse_both",
6588 test_ioctl_dup_extents_sparse_both
);
6589 torture_suite_add_1smb2_test(suite
, "dup_extents_src_is_dest",
6590 test_ioctl_dup_extents_src_is_dest
);
6591 torture_suite_add_1smb2_test(suite
, "dup_extents_src_is_dest_overlap",
6592 test_ioctl_dup_extents_src_is_dest_overlap
);
6593 torture_suite_add_1smb2_test(suite
, "dup_extents_compressed_src",
6594 test_ioctl_dup_extents_compressed_src
);
6595 torture_suite_add_1smb2_test(suite
, "dup_extents_compressed_dest",
6596 test_ioctl_dup_extents_compressed_dest
);
6597 torture_suite_add_1smb2_test(suite
, "dup_extents_bad_handle",
6598 test_ioctl_dup_extents_bad_handle
);
6599 torture_suite_add_1smb2_test(suite
, "dup_extents_src_lock",
6600 test_ioctl_dup_extents_src_lck
);
6601 torture_suite_add_1smb2_test(suite
, "dup_extents_dest_lock",
6602 test_ioctl_dup_extents_dest_lck
);
6604 suite
->description
= talloc_strdup(suite
, "SMB2-IOCTL tests");