2 Unix SMB/CIFS implementation.
4 test suite for SMB2 ioctl operations
6 Copyright (C) David Disseldorp 2011-2024
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"
30 #include "lib/cmdline/cmdline.h"
31 #include "libcli/resolve/resolve.h"
32 #include "lib/param/param.h"
33 #include "lib/util/tevent_ntstatus.h"
35 #define FNAME "testfsctl.dat"
36 #define FNAME2 "testfsctl2.dat"
37 #define DNAME "testfsctl_dir"
40 basic testing of SMB2 shadow copy calls
42 static bool test_ioctl_get_shadow_copy(struct torture_context
*torture
,
43 struct smb2_tree
*tree
)
48 union smb_ioctl ioctl
;
49 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
51 smb2_util_unlink(tree
, FNAME
);
53 status
= torture_smb2_testfile(tree
, FNAME
, &h
);
54 torture_assert_ntstatus_ok(torture
, status
, "create write");
57 status
= smb2_util_write(tree
, h
, buf
, 0, ARRAY_SIZE(buf
));
58 torture_assert_ntstatus_ok(torture
, status
, "write");
61 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
62 ioctl
.smb2
.in
.file
.handle
= h
;
63 ioctl
.smb2
.in
.function
= FSCTL_SRV_ENUM_SNAPS
;
64 ioctl
.smb2
.in
.max_output_response
= 16;
65 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
67 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
68 if (NT_STATUS_EQUAL(status
, NT_STATUS_NOT_SUPPORTED
)
69 || NT_STATUS_EQUAL(status
, NT_STATUS_INVALID_DEVICE_REQUEST
)) {
70 torture_skip(torture
, "FSCTL_SRV_ENUM_SNAPS not supported\n");
72 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_ENUM_SNAPS");
78 basic testing of the SMB2 server side copy ioctls
80 static bool test_ioctl_req_resume_key(struct torture_context
*torture
,
81 struct smb2_tree
*tree
)
86 union smb_ioctl ioctl
;
87 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
88 struct req_resume_key_rsp res_key
;
89 enum ndr_err_code ndr_ret
;
91 smb2_util_unlink(tree
, FNAME
);
93 status
= torture_smb2_testfile(tree
, FNAME
, &h
);
94 torture_assert_ntstatus_ok(torture
, status
, "create write");
97 status
= smb2_util_write(tree
, h
, buf
, 0, ARRAY_SIZE(buf
));
98 torture_assert_ntstatus_ok(torture
, status
, "write");
101 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
102 ioctl
.smb2
.in
.file
.handle
= h
;
103 ioctl
.smb2
.in
.function
= FSCTL_SRV_REQUEST_RESUME_KEY
;
104 ioctl
.smb2
.in
.max_output_response
= 32;
105 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
107 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
108 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_REQUEST_RESUME_KEY");
110 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
, &res_key
,
111 (ndr_pull_flags_fn_t
)ndr_pull_req_resume_key_rsp
);
112 torture_assert_ndr_success(torture
, ndr_ret
,
113 "ndr_pull_req_resume_key_rsp");
115 NDR_PRINT_DEBUG(req_resume_key_rsp
, &res_key
);
117 talloc_free(tmp_ctx
);
122 testing fetching a resume key twice for one file handle
124 static bool test_ioctl_req_two_resume_keys(struct torture_context
*torture
,
125 struct smb2_tree
*tree
)
127 struct smb2_handle h
;
130 union smb_ioctl ioctl
;
131 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
132 struct req_resume_key_rsp res_key
;
133 enum ndr_err_code ndr_ret
;
135 smb2_util_unlink(tree
, FNAME
);
137 status
= torture_smb2_testfile(tree
, FNAME
, &h
);
138 torture_assert_ntstatus_ok(torture
, status
, "create write");
141 status
= smb2_util_write(tree
, h
, buf
, 0, ARRAY_SIZE(buf
));
142 torture_assert_ntstatus_ok(torture
, status
, "write");
145 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
146 ioctl
.smb2
.in
.file
.handle
= h
;
147 ioctl
.smb2
.in
.function
= FSCTL_SRV_REQUEST_RESUME_KEY
;
148 ioctl
.smb2
.in
.max_output_response
= 32;
149 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
151 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
152 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_REQUEST_RESUME_KEY");
154 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
, &res_key
,
155 (ndr_pull_flags_fn_t
)ndr_pull_req_resume_key_rsp
);
156 torture_assert_ndr_success(torture
, ndr_ret
,
157 "ndr_pull_req_resume_key_rsp");
159 NDR_PRINT_DEBUG(req_resume_key_rsp
, &res_key
);
162 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
163 ioctl
.smb2
.in
.file
.handle
= h
;
164 ioctl
.smb2
.in
.function
= FSCTL_SRV_REQUEST_RESUME_KEY
;
165 ioctl
.smb2
.in
.max_output_response
= 32;
166 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
168 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
169 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_REQUEST_RESUME_KEY");
171 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
, &res_key
,
172 (ndr_pull_flags_fn_t
)ndr_pull_req_resume_key_rsp
);
173 torture_assert_ndr_success(torture
, ndr_ret
,
174 "ndr_pull_req_resume_key_rsp");
176 NDR_PRINT_DEBUG(req_resume_key_rsp
, &res_key
);
178 talloc_free(tmp_ctx
);
182 static uint64_t patt_hash(uint64_t off
)
187 static bool write_pattern(struct torture_context
*torture
,
188 struct smb2_tree
*tree
, TALLOC_CTX
*mem_ctx
,
189 struct smb2_handle h
, uint64_t off
, uint64_t len
,
195 uint64_t io_sz
= MIN(1024 * 64, len
);
201 torture_assert(torture
, (len
% 8) == 0, "invalid write len");
203 buf
= talloc_zero_size(mem_ctx
, io_sz
);
204 torture_assert(torture
, (buf
!= NULL
), "no memory for file data buf");
207 for (i
= 0; i
<= io_sz
- 8; i
+= 8) {
208 SBVAL(buf
, i
, patt_hash(patt_off
));
212 status
= smb2_util_write(tree
, h
,
214 torture_assert_ntstatus_ok(torture
, status
, "file write");
225 static bool check_pattern(struct torture_context
*torture
,
226 struct smb2_tree
*tree
, TALLOC_CTX
*mem_ctx
,
227 struct smb2_handle h
, uint64_t off
, uint64_t len
,
234 torture_assert(torture
, (len
% 8) == 0, "invalid read len");
240 uint64_t io_sz
= MIN(1024 * 64, len
);
243 r
.in
.file
.handle
= h
;
246 status
= smb2_read(tree
, mem_ctx
, &r
);
247 torture_assert_ntstatus_ok(torture
, status
, "read");
249 torture_assert_u64_equal(torture
, r
.out
.data
.length
, io_sz
,
250 "read data len mismatch");
252 for (i
= 0; i
<= io_sz
- 8; i
+= 8, patt_off
+= 8) {
253 uint64_t data
= BVAL(r
.out
.data
.data
, i
);
254 torture_assert_u64_equal(torture
, data
, patt_hash(patt_off
),
255 talloc_asprintf(torture
, "read data "
256 "pattern bad at %llu\n",
257 (unsigned long long)off
+ i
));
259 talloc_free(r
.out
.data
.data
);
267 static bool check_zero(struct torture_context
*torture
,
268 struct smb2_tree
*tree
, TALLOC_CTX
*mem_ctx
,
269 struct smb2_handle h
, uint64_t off
, uint64_t len
)
280 r
.in
.file
.handle
= h
;
283 status
= smb2_read(tree
, mem_ctx
, &r
);
284 torture_assert_ntstatus_ok(torture
, status
, "read");
286 torture_assert_u64_equal(torture
, r
.out
.data
.length
, len
,
287 "read data len mismatch");
289 for (i
= 0; i
<= len
- 8; i
+= 8) {
290 uint64_t data
= BVAL(r
.out
.data
.data
, i
);
291 torture_assert_u64_equal(torture
, data
, 0,
292 talloc_asprintf(mem_ctx
, "read zero "
294 (unsigned long long)i
));
297 talloc_free(r
.out
.data
.data
);
301 static bool test_setup_open(struct torture_context
*torture
,
302 struct smb2_tree
*tree
, TALLOC_CTX
*mem_ctx
,
304 struct smb2_handle
*fh
,
305 uint32_t desired_access
,
306 uint32_t file_attributes
)
308 struct smb2_create io
;
312 io
.in
.desired_access
= desired_access
;
313 io
.in
.file_attributes
= file_attributes
;
314 io
.in
.create_disposition
= NTCREATEX_DISP_OPEN_IF
;
316 NTCREATEX_SHARE_ACCESS_DELETE
|
317 NTCREATEX_SHARE_ACCESS_READ
|
318 NTCREATEX_SHARE_ACCESS_WRITE
;
319 if (file_attributes
& FILE_ATTRIBUTE_DIRECTORY
) {
320 io
.in
.create_options
= NTCREATEX_OPTIONS_DIRECTORY
;
324 status
= smb2_create(tree
, mem_ctx
, &io
);
325 torture_assert_ntstatus_ok(torture
, status
, "file create");
327 *fh
= io
.out
.file
.handle
;
332 static bool test_setup_create_fill(struct torture_context
*torture
,
333 struct smb2_tree
*tree
, TALLOC_CTX
*mem_ctx
,
335 struct smb2_handle
*fh
,
337 uint32_t desired_access
,
338 uint32_t file_attributes
)
341 uint32_t initial_access
= desired_access
;
344 initial_access
|= SEC_FILE_APPEND_DATA
;
347 smb2_util_unlink(tree
, fname
);
349 ok
= test_setup_open(torture
, tree
, mem_ctx
,
354 torture_assert(torture
, ok
, "file create");
357 ok
= write_pattern(torture
, tree
, mem_ctx
, *fh
, 0, size
, 0);
358 torture_assert(torture
, ok
, "write pattern");
361 if (initial_access
!= desired_access
) {
362 smb2_util_close(tree
, *fh
);
363 ok
= test_setup_open(torture
, tree
, mem_ctx
,
368 torture_assert(torture
, ok
, "file open");
374 static bool test_setup_copy_chunk(struct torture_context
*torture
,
375 struct smb2_tree
*src_tree
,
376 struct smb2_tree
*dst_tree
,
379 const char *src_name
,
380 struct smb2_handle
*src_h
,
382 uint32_t src_desired_access
,
383 const char *dst_name
,
384 struct smb2_handle
*dest_h
,
386 uint32_t dest_desired_access
,
387 struct srv_copychunk_copy
*cc_copy
,
388 union smb_ioctl
*ioctl
)
390 struct req_resume_key_rsp res_key
;
393 enum ndr_err_code ndr_ret
;
395 ok
= test_setup_create_fill(torture
, src_tree
, mem_ctx
, src_name
,
396 src_h
, src_size
, src_desired_access
,
397 FILE_ATTRIBUTE_NORMAL
);
398 torture_assert(torture
, ok
, "src file create fill");
400 ok
= test_setup_create_fill(torture
, dst_tree
, mem_ctx
, dst_name
,
401 dest_h
, dest_size
, dest_desired_access
,
402 FILE_ATTRIBUTE_NORMAL
);
403 torture_assert(torture
, ok
, "dest file create fill");
405 ZERO_STRUCTPN(ioctl
);
406 ioctl
->smb2
.level
= RAW_IOCTL_SMB2
;
407 ioctl
->smb2
.in
.file
.handle
= *src_h
;
408 ioctl
->smb2
.in
.function
= FSCTL_SRV_REQUEST_RESUME_KEY
;
409 /* Allow for Key + ContextLength + Context */
410 ioctl
->smb2
.in
.max_output_response
= 32;
411 ioctl
->smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
413 status
= smb2_ioctl(src_tree
, mem_ctx
, &ioctl
->smb2
);
414 torture_assert_ntstatus_ok(torture
, status
,
415 "FSCTL_SRV_REQUEST_RESUME_KEY");
417 ndr_ret
= ndr_pull_struct_blob(&ioctl
->smb2
.out
.out
, mem_ctx
, &res_key
,
418 (ndr_pull_flags_fn_t
)ndr_pull_req_resume_key_rsp
);
420 torture_assert_ndr_success(torture
, ndr_ret
,
421 "ndr_pull_req_resume_key_rsp");
423 ZERO_STRUCTPN(ioctl
);
424 ioctl
->smb2
.level
= RAW_IOCTL_SMB2
;
425 ioctl
->smb2
.in
.file
.handle
= *dest_h
;
426 ioctl
->smb2
.in
.function
= FSCTL_SRV_COPYCHUNK
;
427 ioctl
->smb2
.in
.max_output_response
= sizeof(struct srv_copychunk_rsp
);
428 ioctl
->smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
430 ZERO_STRUCTPN(cc_copy
);
431 memcpy(cc_copy
->source_key
, res_key
.resume_key
, ARRAY_SIZE(cc_copy
->source_key
));
432 cc_copy
->chunk_count
= nchunks
;
433 cc_copy
->chunks
= talloc_zero_array(mem_ctx
, struct srv_copychunk
, nchunks
);
434 torture_assert(torture
, (cc_copy
->chunks
!= NULL
), "no memory for chunks");
440 static bool check_copy_chunk_rsp(struct torture_context
*torture
,
441 struct srv_copychunk_rsp
*cc_rsp
,
442 uint32_t ex_chunks_written
,
443 uint32_t ex_chunk_bytes_written
,
444 uint32_t ex_total_bytes_written
)
446 torture_assert_int_equal(torture
, cc_rsp
->chunks_written
,
447 ex_chunks_written
, "num chunks");
448 torture_assert_int_equal(torture
, cc_rsp
->chunk_bytes_written
,
449 ex_chunk_bytes_written
, "chunk bytes written");
450 torture_assert_int_equal(torture
, cc_rsp
->total_bytes_written
,
451 ex_total_bytes_written
, "chunk total bytes");
455 static bool test_ioctl_copy_chunk_simple(struct torture_context
*torture
,
456 struct smb2_tree
*tree
)
458 struct smb2_handle src_h
;
459 struct smb2_handle dest_h
;
461 union smb_ioctl ioctl
;
462 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
463 struct srv_copychunk_copy cc_copy
;
464 struct srv_copychunk_rsp cc_rsp
;
465 enum ndr_err_code ndr_ret
;
468 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
471 &src_h
, 4096, /* fill 4096 byte src file */
474 &dest_h
, 0, /* 0 byte dest file */
479 torture_fail(torture
, "setup copy chunk error");
482 /* copy all src file data (via a single chunk desc) */
483 cc_copy
.chunks
[0].source_off
= 0;
484 cc_copy
.chunks
[0].target_off
= 0;
485 cc_copy
.chunks
[0].length
= 4096;
487 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
489 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
490 torture_assert_ndr_success(torture
, ndr_ret
,
491 "ndr_push_srv_copychunk_copy");
493 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
494 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_COPYCHUNK");
496 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
498 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
499 torture_assert_ndr_success(torture
, ndr_ret
,
500 "ndr_pull_srv_copychunk_rsp");
502 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
503 1, /* chunks written */
504 0, /* chunk bytes unsuccessfully written */
505 4096); /* total bytes written */
507 torture_fail(torture
, "bad copy chunk response data");
510 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 0, 4096, 0);
512 torture_fail(torture
, "inconsistent file 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_multi(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
, 8192, /* 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 */
549 cc_copy
.chunks
[0].source_off
= 0;
550 cc_copy
.chunks
[0].target_off
= 0;
551 cc_copy
.chunks
[0].length
= 4096;
553 cc_copy
.chunks
[1].source_off
= 4096;
554 cc_copy
.chunks
[1].target_off
= 4096;
555 cc_copy
.chunks
[1].length
= 4096;
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 8192); /* total bytes written */
577 torture_fail(torture
, "bad copy chunk response data");
580 smb2_util_close(tree
, src_h
);
581 smb2_util_close(tree
, dest_h
);
582 talloc_free(tmp_ctx
);
586 static bool test_ioctl_copy_chunk_tiny(struct torture_context
*torture
,
587 struct smb2_tree
*tree
)
589 struct smb2_handle src_h
;
590 struct smb2_handle dest_h
;
592 union smb_ioctl ioctl
;
593 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
594 struct srv_copychunk_copy cc_copy
;
595 struct srv_copychunk_rsp cc_rsp
;
596 enum ndr_err_code ndr_ret
;
599 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
602 &src_h
, 96, /* src file */
605 &dest_h
, 0, /* dest file */
610 torture_fail(torture
, "setup copy chunk error");
613 /* copy all src file data via two chunks, sub block size chunks */
614 cc_copy
.chunks
[0].source_off
= 0;
615 cc_copy
.chunks
[0].target_off
= 0;
616 cc_copy
.chunks
[0].length
= 48;
618 cc_copy
.chunks
[1].source_off
= 48;
619 cc_copy
.chunks
[1].target_off
= 48;
620 cc_copy
.chunks
[1].length
= 48;
622 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
624 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
625 torture_assert_ndr_success(torture
, ndr_ret
,
626 "ndr_push_srv_copychunk_copy");
628 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
629 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_COPYCHUNK");
631 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
633 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
634 torture_assert_ndr_success(torture
, ndr_ret
,
635 "ndr_pull_srv_copychunk_rsp");
637 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
638 2, /* chunks written */
639 0, /* chunk bytes unsuccessfully written */
640 96); /* total bytes written */
642 torture_fail(torture
, "bad copy chunk response data");
645 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 0, 96, 0);
647 torture_fail(torture
, "inconsistent file data");
650 smb2_util_close(tree
, src_h
);
651 smb2_util_close(tree
, dest_h
);
652 talloc_free(tmp_ctx
);
656 static bool test_ioctl_copy_chunk_over(struct torture_context
*torture
,
657 struct smb2_tree
*tree
)
659 struct smb2_handle src_h
;
660 struct smb2_handle dest_h
;
662 union smb_ioctl ioctl
;
663 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
664 struct srv_copychunk_copy cc_copy
;
665 struct srv_copychunk_rsp cc_rsp
;
666 enum ndr_err_code ndr_ret
;
669 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
672 &src_h
, 8192, /* src file */
675 &dest_h
, 4096, /* dest file */
680 torture_fail(torture
, "setup copy chunk error");
683 /* first chunk overwrites existing dest data */
684 cc_copy
.chunks
[0].source_off
= 0;
685 cc_copy
.chunks
[0].target_off
= 0;
686 cc_copy
.chunks
[0].length
= 4096;
688 /* second chunk overwrites the first */
689 cc_copy
.chunks
[1].source_off
= 4096;
690 cc_copy
.chunks
[1].target_off
= 0;
691 cc_copy
.chunks
[1].length
= 4096;
693 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
695 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
696 torture_assert_ndr_success(torture
, ndr_ret
,
697 "ndr_push_srv_copychunk_copy");
699 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
700 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_COPYCHUNK");
702 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
704 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
705 torture_assert_ndr_success(torture
, ndr_ret
,
706 "ndr_pull_srv_copychunk_rsp");
708 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
709 2, /* chunks written */
710 0, /* chunk bytes unsuccessfully written */
711 8192); /* total bytes written */
713 torture_fail(torture
, "bad copy chunk response data");
716 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 0, 4096, 4096);
718 torture_fail(torture
, "inconsistent file data");
721 smb2_util_close(tree
, src_h
);
722 smb2_util_close(tree
, dest_h
);
723 talloc_free(tmp_ctx
);
727 static bool test_ioctl_copy_chunk_append(struct torture_context
*torture
,
728 struct smb2_tree
*tree
)
730 struct smb2_handle src_h
;
731 struct smb2_handle dest_h
;
733 union smb_ioctl ioctl
;
734 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
735 struct srv_copychunk_copy cc_copy
;
736 struct srv_copychunk_rsp cc_rsp
;
737 enum ndr_err_code ndr_ret
;
740 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
743 &src_h
, 4096, /* src file */
746 &dest_h
, 0, /* dest file */
751 torture_fail(torture
, "setup copy chunk error");
754 cc_copy
.chunks
[0].source_off
= 0;
755 cc_copy
.chunks
[0].target_off
= 0;
756 cc_copy
.chunks
[0].length
= 4096;
758 /* second chunk appends the same data to the first */
759 cc_copy
.chunks
[1].source_off
= 0;
760 cc_copy
.chunks
[1].target_off
= 4096;
761 cc_copy
.chunks
[1].length
= 4096;
763 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
765 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
766 torture_assert_ndr_success(torture
, ndr_ret
,
767 "ndr_push_srv_copychunk_copy");
769 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
770 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_COPYCHUNK");
772 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
774 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
775 torture_assert_ndr_success(torture
, ndr_ret
,
776 "ndr_pull_srv_copychunk_rsp");
778 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
779 2, /* chunks written */
780 0, /* chunk bytes unsuccessfully written */
781 8192); /* total bytes written */
783 torture_fail(torture
, "bad copy chunk response data");
786 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 0, 4096, 0);
788 torture_fail(torture
, "inconsistent file data");
791 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 4096, 4096, 0);
793 torture_fail(torture
, "inconsistent file data");
796 smb2_util_close(tree
, src_h
);
797 smb2_util_close(tree
, dest_h
);
798 talloc_free(tmp_ctx
);
802 static bool test_ioctl_copy_chunk_limits(struct torture_context
*torture
,
803 struct smb2_tree
*tree
)
805 struct smb2_handle src_h
;
806 struct smb2_handle dest_h
;
808 union smb_ioctl ioctl
;
809 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
810 struct srv_copychunk_copy cc_copy
;
811 struct srv_copychunk_rsp cc_rsp
;
812 enum ndr_err_code ndr_ret
;
815 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
818 &src_h
, 4096, /* src file */
821 &dest_h
, 0, /* dest file */
826 torture_fail(torture
, "setup copy chunk error");
829 /* send huge chunk length request */
830 cc_copy
.chunks
[0].source_off
= 0;
831 cc_copy
.chunks
[0].target_off
= 0;
832 cc_copy
.chunks
[0].length
= UINT_MAX
;
834 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
836 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
837 torture_assert_ndr_success(torture
, ndr_ret
, "marshalling request");
839 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
840 torture_assert_ntstatus_equal(torture
, status
,
841 NT_STATUS_INVALID_PARAMETER
,
842 "bad oversize chunk response");
844 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
846 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
847 torture_assert_ndr_success(torture
, ndr_ret
, "unmarshalling response");
849 torture_comment(torture
, "limit max chunks, got %u\n",
850 cc_rsp
.chunks_written
);
851 torture_comment(torture
, "limit max chunk len, got %u\n",
852 cc_rsp
.chunk_bytes_written
);
853 torture_comment(torture
, "limit max total bytes, got %u\n",
854 cc_rsp
.total_bytes_written
);
856 smb2_util_close(tree
, src_h
);
857 smb2_util_close(tree
, dest_h
);
858 talloc_free(tmp_ctx
);
862 static bool test_ioctl_copy_chunk_src_lck(struct torture_context
*torture
,
863 struct smb2_tree
*tree
)
865 struct smb2_handle src_h
;
866 struct smb2_handle src_h2
;
867 struct smb2_handle dest_h
;
869 union smb_ioctl ioctl
;
870 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
871 struct srv_copychunk_copy cc_copy
;
872 struct srv_copychunk_rsp cc_rsp
;
873 enum ndr_err_code ndr_ret
;
875 struct smb2_lock lck
;
876 struct smb2_lock_element el
[1];
878 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
881 &src_h
, 4096, /* src file */
884 &dest_h
, 0, /* dest file */
889 torture_fail(torture
, "setup copy chunk error");
892 cc_copy
.chunks
[0].source_off
= 0;
893 cc_copy
.chunks
[0].target_off
= 0;
894 cc_copy
.chunks
[0].length
= 4096;
896 /* open and lock the copychunk src file */
897 status
= torture_smb2_testfile(tree
, FNAME
, &src_h2
);
898 torture_assert_ntstatus_ok(torture
, status
, "2nd src open");
900 lck
.in
.lock_count
= 0x0001;
901 lck
.in
.lock_sequence
= 0x00000000;
902 lck
.in
.file
.handle
= src_h2
;
904 el
[0].offset
= cc_copy
.chunks
[0].source_off
;
905 el
[0].length
= cc_copy
.chunks
[0].length
;
907 el
[0].flags
= SMB2_LOCK_FLAG_EXCLUSIVE
;
909 status
= smb2_lock(tree
, &lck
);
910 torture_assert_ntstatus_ok(torture
, status
, "lock");
912 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
914 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
915 torture_assert_ndr_success(torture
, ndr_ret
,
916 "ndr_push_srv_copychunk_copy");
918 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
920 * 2k12 & Samba return lock_conflict, Windows 7 & 2k8 return success...
922 * Edgar Olougouna @ MS wrote:
923 * Regarding the FSCTL_SRV_COPYCHUNK and STATUS_FILE_LOCK_CONFLICT
924 * discrepancy observed between Windows versions, we confirm that the
925 * behavior change is expected.
927 * CopyChunk in Windows Server 2012 use regular Readfile/Writefile APIs
928 * to move the chunks from the source to the destination.
929 * These ReadFile/WriteFile APIs go through the byte-range lock checks,
930 * and this explains the observed STATUS_FILE_LOCK_CONFLICT error.
932 * Prior to Windows Server 2012, CopyChunk used mapped sections to move
933 * the data. And byte range locks are not enforced on mapped I/O, and
934 * this explains the STATUS_SUCCESS observed on Windows Server 2008 R2.
936 torture_assert_ntstatus_equal(torture
, status
,
937 NT_STATUS_FILE_LOCK_CONFLICT
,
938 "FSCTL_SRV_COPYCHUNK locked");
940 /* should get cc response data with the lock conflict status */
941 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
943 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
944 torture_assert_ndr_success(torture
, ndr_ret
,
945 "ndr_pull_srv_copychunk_rsp");
946 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
947 0, /* chunks written */
948 0, /* chunk bytes unsuccessfully written */
949 0); /* total bytes written */
951 lck
.in
.lock_count
= 0x0001;
952 lck
.in
.lock_sequence
= 0x00000001;
953 lck
.in
.file
.handle
= src_h2
;
955 el
[0].offset
= cc_copy
.chunks
[0].source_off
;
956 el
[0].length
= cc_copy
.chunks
[0].length
;
958 el
[0].flags
= SMB2_LOCK_FLAG_UNLOCK
;
959 status
= smb2_lock(tree
, &lck
);
960 torture_assert_ntstatus_ok(torture
, status
, "unlock");
962 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
963 torture_assert_ntstatus_ok(torture
, status
,
964 "FSCTL_SRV_COPYCHUNK unlocked");
966 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
968 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
969 torture_assert_ndr_success(torture
, ndr_ret
,
970 "ndr_pull_srv_copychunk_rsp");
972 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
973 1, /* chunks written */
974 0, /* chunk bytes unsuccessfully written */
975 4096); /* total bytes written */
977 torture_fail(torture
, "bad copy chunk response data");
980 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 0, 4096, 0);
982 torture_fail(torture
, "inconsistent file data");
985 smb2_util_close(tree
, src_h2
);
986 smb2_util_close(tree
, src_h
);
987 smb2_util_close(tree
, dest_h
);
988 talloc_free(tmp_ctx
);
992 static bool test_ioctl_copy_chunk_dest_lck(struct torture_context
*torture
,
993 struct smb2_tree
*tree
)
995 struct smb2_handle src_h
;
996 struct smb2_handle dest_h
;
997 struct smb2_handle dest_h2
;
999 union smb_ioctl ioctl
;
1000 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
1001 struct srv_copychunk_copy cc_copy
;
1002 struct srv_copychunk_rsp cc_rsp
;
1003 enum ndr_err_code ndr_ret
;
1005 struct smb2_lock lck
;
1006 struct smb2_lock_element el
[1];
1008 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
1011 &src_h
, 4096, /* src file */
1012 SEC_RIGHTS_FILE_ALL
,
1014 &dest_h
, 4096, /* dest file */
1015 SEC_RIGHTS_FILE_ALL
,
1019 torture_fail(torture
, "setup copy chunk error");
1022 cc_copy
.chunks
[0].source_off
= 0;
1023 cc_copy
.chunks
[0].target_off
= 0;
1024 cc_copy
.chunks
[0].length
= 4096;
1026 /* open and lock the copychunk dest file */
1027 status
= torture_smb2_testfile(tree
, FNAME2
, &dest_h2
);
1028 torture_assert_ntstatus_ok(torture
, status
, "2nd src open");
1030 lck
.in
.lock_count
= 0x0001;
1031 lck
.in
.lock_sequence
= 0x00000000;
1032 lck
.in
.file
.handle
= dest_h2
;
1034 el
[0].offset
= cc_copy
.chunks
[0].target_off
;
1035 el
[0].length
= cc_copy
.chunks
[0].length
;
1037 el
[0].flags
= SMB2_LOCK_FLAG_EXCLUSIVE
;
1039 status
= smb2_lock(tree
, &lck
);
1040 torture_assert_ntstatus_ok(torture
, status
, "lock");
1042 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1044 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1045 torture_assert_ndr_success(torture
, ndr_ret
,
1046 "ndr_push_srv_copychunk_copy");
1048 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1049 torture_assert_ntstatus_equal(torture
, status
,
1050 NT_STATUS_FILE_LOCK_CONFLICT
,
1051 "FSCTL_SRV_COPYCHUNK locked");
1053 lck
.in
.lock_count
= 0x0001;
1054 lck
.in
.lock_sequence
= 0x00000001;
1055 lck
.in
.file
.handle
= dest_h2
;
1057 el
[0].offset
= cc_copy
.chunks
[0].target_off
;
1058 el
[0].length
= cc_copy
.chunks
[0].length
;
1060 el
[0].flags
= SMB2_LOCK_FLAG_UNLOCK
;
1061 status
= smb2_lock(tree
, &lck
);
1062 torture_assert_ntstatus_ok(torture
, status
, "unlock");
1064 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1065 torture_assert_ntstatus_ok(torture
, status
,
1066 "FSCTL_SRV_COPYCHUNK unlocked");
1068 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
1070 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
1071 torture_assert_ndr_success(torture
, ndr_ret
,
1072 "ndr_pull_srv_copychunk_rsp");
1074 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
1075 1, /* chunks written */
1076 0, /* chunk bytes unsuccessfully written */
1077 4096); /* total bytes written */
1079 torture_fail(torture
, "bad copy chunk response data");
1082 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 0, 4096, 0);
1084 torture_fail(torture
, "inconsistent file data");
1087 smb2_util_close(tree
, dest_h2
);
1088 smb2_util_close(tree
, src_h
);
1089 smb2_util_close(tree
, dest_h
);
1090 talloc_free(tmp_ctx
);
1094 static bool test_ioctl_copy_chunk_bad_key(struct torture_context
*torture
,
1095 struct smb2_tree
*tree
)
1097 struct smb2_handle src_h
;
1098 struct smb2_handle dest_h
;
1100 union smb_ioctl ioctl
;
1101 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
1102 struct srv_copychunk_copy cc_copy
;
1103 enum ndr_err_code ndr_ret
;
1106 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
1110 SEC_RIGHTS_FILE_ALL
,
1113 SEC_RIGHTS_FILE_ALL
,
1117 torture_fail(torture
, "setup copy chunk error");
1120 /* overwrite the resume key with a bogus value */
1121 memcpy(cc_copy
.source_key
, "deadbeefdeadbeefdeadbeef", 24);
1123 cc_copy
.chunks
[0].source_off
= 0;
1124 cc_copy
.chunks
[0].target_off
= 0;
1125 cc_copy
.chunks
[0].length
= 4096;
1127 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1129 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1130 torture_assert_ndr_success(torture
, ndr_ret
,
1131 "ndr_push_srv_copychunk_copy");
1133 /* Server 2k12 returns NT_STATUS_OBJECT_NAME_NOT_FOUND */
1134 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1135 torture_assert_ntstatus_equal(torture
, status
,
1136 NT_STATUS_OBJECT_NAME_NOT_FOUND
,
1137 "FSCTL_SRV_COPYCHUNK");
1139 smb2_util_close(tree
, src_h
);
1140 smb2_util_close(tree
, dest_h
);
1141 talloc_free(tmp_ctx
);
1145 static bool test_ioctl_copy_chunk_src_is_dest(struct torture_context
*torture
,
1146 struct smb2_tree
*tree
)
1148 struct smb2_handle src_h
;
1149 struct smb2_handle dest_h
;
1151 union smb_ioctl ioctl
;
1152 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
1153 struct srv_copychunk_copy cc_copy
;
1154 struct srv_copychunk_rsp cc_rsp
;
1155 enum ndr_err_code ndr_ret
;
1158 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
1162 SEC_RIGHTS_FILE_ALL
,
1165 SEC_RIGHTS_FILE_ALL
,
1169 torture_fail(torture
, "setup copy chunk error");
1172 /* the source is also the destination */
1173 ioctl
.smb2
.in
.file
.handle
= src_h
;
1175 /* non-overlapping */
1176 cc_copy
.chunks
[0].source_off
= 0;
1177 cc_copy
.chunks
[0].target_off
= 4096;
1178 cc_copy
.chunks
[0].length
= 4096;
1180 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1182 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1183 torture_assert_ndr_success(torture
, ndr_ret
,
1184 "ndr_push_srv_copychunk_copy");
1186 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1187 torture_assert_ntstatus_ok(torture
, status
,
1188 "FSCTL_SRV_COPYCHUNK");
1190 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
1192 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
1193 torture_assert_ndr_success(torture
, ndr_ret
,
1194 "ndr_pull_srv_copychunk_rsp");
1196 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
1197 1, /* chunks written */
1198 0, /* chunk bytes unsuccessfully written */
1199 4096); /* total bytes written */
1201 torture_fail(torture
, "bad copy chunk response data");
1204 ok
= check_pattern(torture
, tree
, tmp_ctx
, src_h
, 0, 4096, 0);
1206 torture_fail(torture
, "inconsistent file data");
1208 ok
= check_pattern(torture
, tree
, tmp_ctx
, src_h
, 4096, 4096, 0);
1210 torture_fail(torture
, "inconsistent file data");
1213 smb2_util_close(tree
, src_h
);
1214 smb2_util_close(tree
, dest_h
);
1215 talloc_free(tmp_ctx
);
1220 * Test a single-chunk copychunk request, where the source and target ranges
1221 * overlap, and the SourceKey refers to the same target file. E.g:
1225 * File: src_and_dest
1226 * Offset: 0123456789
1231 * FSCTL_SRV_COPYCHUNK(src_and_dest)
1232 * SourceKey = SRV_REQUEST_RESUME_KEY(src_and_dest)
1234 * Chunks[0].SourceOffset = 0
1235 * Chunks[0].TargetOffset = 4
1236 * Chunks[0].Length = 6
1240 * File: src_and_dest
1241 * Offset: 0123456789
1244 * The resultant contents of src_and_dest is dependent on the server's
1245 * copy algorithm. In the above example, the server uses an IO buffer
1246 * large enough to hold the entire six-byte source data before writing
1247 * to TargetOffset. If the server were to use a four-byte IO buffer and
1248 * started reads/writes from the lowest offset, then the two overlapping
1249 * bytes in the above example would be overwritten before being read. The
1250 * resultant file contents would be abcdabcdab.
1252 * Windows 2008r2 appears to use a 2048 byte copy buffer, overlapping bytes
1253 * after this offset are written before being read. Windows 2012 on the
1254 * other hand appears to use a buffer large enough to hold its maximum
1255 * supported chunk size (1M). Samba currently uses a 64k copy buffer by
1256 * default (vfs_cc_state.buf).
1258 * This test uses an 8-byte overlap at 2040-2048, so that it passes against
1259 * Windows 2008r2, 2012 and Samba servers. Note, 2008GM fails, as it appears
1260 * to use a different copy algorithm to 2008r2.
1263 test_ioctl_copy_chunk_src_is_dest_overlap(struct torture_context
*torture
,
1264 struct smb2_tree
*tree
)
1266 struct smb2_handle src_h
;
1267 struct smb2_handle dest_h
;
1269 union smb_ioctl ioctl
;
1270 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
1271 struct srv_copychunk_copy cc_copy
;
1272 struct srv_copychunk_rsp cc_rsp
;
1273 enum ndr_err_code ndr_ret
;
1276 /* exceed the vfs_default copy buffer */
1277 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
1281 SEC_RIGHTS_FILE_ALL
,
1284 SEC_RIGHTS_FILE_ALL
,
1288 torture_fail(torture
, "setup copy chunk error");
1291 /* the source is also the destination */
1292 ioctl
.smb2
.in
.file
.handle
= src_h
;
1294 /* 8 bytes overlap between source and target ranges */
1295 cc_copy
.chunks
[0].source_off
= 0;
1296 cc_copy
.chunks
[0].target_off
= 2048 - 8;
1297 cc_copy
.chunks
[0].length
= 2048;
1299 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1301 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1302 torture_assert_ndr_success(torture
, ndr_ret
,
1303 "ndr_push_srv_copychunk_copy");
1305 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1306 torture_assert_ntstatus_ok(torture
, status
,
1307 "FSCTL_SRV_COPYCHUNK");
1309 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
1311 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
1312 torture_assert_ndr_success(torture
, ndr_ret
,
1313 "ndr_pull_srv_copychunk_rsp");
1315 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
1316 1, /* chunks written */
1317 0, /* chunk bytes unsuccessfully written */
1318 2048); /* total bytes written */
1320 torture_fail(torture
, "bad copy chunk response data");
1323 ok
= check_pattern(torture
, tree
, tmp_ctx
, src_h
, 0, 2048 - 8, 0);
1325 torture_fail(torture
, "inconsistent file data");
1327 ok
= check_pattern(torture
, tree
, tmp_ctx
, src_h
, 2048 - 8, 2048, 0);
1329 torture_fail(torture
, "inconsistent file data");
1332 smb2_util_close(tree
, src_h
);
1333 smb2_util_close(tree
, dest_h
);
1334 talloc_free(tmp_ctx
);
1338 static bool test_ioctl_copy_chunk_bad_access(struct torture_context
*torture
,
1339 struct smb2_tree
*tree
)
1341 struct smb2_handle src_h
;
1342 struct smb2_handle dest_h
;
1344 union smb_ioctl ioctl
;
1345 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
1346 struct srv_copychunk_copy cc_copy
;
1347 enum ndr_err_code ndr_ret
;
1349 /* read permission on src */
1350 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
, 1, /* 1 chunk */
1351 FNAME
, &src_h
, 4096, /* fill 4096 byte src file */
1352 SEC_FILE_READ_DATA
| SEC_FILE_READ_ATTRIBUTE
,
1353 FNAME2
, &dest_h
, 0, /* 0 byte dest file */
1354 SEC_RIGHTS_FILE_ALL
, &cc_copy
, &ioctl
);
1356 torture_fail(torture
, "setup copy chunk error");
1359 cc_copy
.chunks
[0].source_off
= 0;
1360 cc_copy
.chunks
[0].target_off
= 0;
1361 cc_copy
.chunks
[0].length
= 4096;
1363 ndr_ret
= ndr_push_struct_blob(
1364 &ioctl
.smb2
.in
.out
, tmp_ctx
, &cc_copy
,
1365 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1366 torture_assert_ndr_success(torture
, ndr_ret
,
1367 "ndr_push_srv_copychunk_copy");
1369 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1370 torture_assert_ntstatus_equal(torture
, status
, NT_STATUS_OK
,
1371 "FSCTL_SRV_COPYCHUNK");
1373 smb2_util_close(tree
, src_h
);
1374 smb2_util_close(tree
, dest_h
);
1376 /* execute permission on src */
1377 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
, 1, /* 1 chunk */
1378 FNAME
, &src_h
, 4096, /* fill 4096 byte src file */
1379 SEC_FILE_EXECUTE
| SEC_FILE_READ_ATTRIBUTE
,
1380 FNAME2
, &dest_h
, 0, /* 0 byte dest file */
1381 SEC_RIGHTS_FILE_ALL
, &cc_copy
, &ioctl
);
1383 torture_fail(torture
, "setup copy chunk error");
1386 cc_copy
.chunks
[0].source_off
= 0;
1387 cc_copy
.chunks
[0].target_off
= 0;
1388 cc_copy
.chunks
[0].length
= 4096;
1390 ndr_ret
= ndr_push_struct_blob(
1391 &ioctl
.smb2
.in
.out
, tmp_ctx
, &cc_copy
,
1392 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1393 torture_assert_ndr_success(torture
, ndr_ret
,
1394 "ndr_push_srv_copychunk_copy");
1396 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1397 torture_assert_ntstatus_equal(torture
, status
, NT_STATUS_OK
,
1398 "FSCTL_SRV_COPYCHUNK");
1400 smb2_util_close(tree
, src_h
);
1401 smb2_util_close(tree
, dest_h
);
1403 /* neither read nor execute permission on src */
1404 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
, 1, /* 1 chunk */
1405 FNAME
, &src_h
, 4096, /* fill 4096 byte src file */
1406 SEC_FILE_READ_ATTRIBUTE
, FNAME2
, &dest_h
,
1407 0, /* 0 byte dest file */
1408 SEC_RIGHTS_FILE_ALL
, &cc_copy
, &ioctl
);
1410 torture_fail(torture
, "setup copy chunk error");
1413 cc_copy
.chunks
[0].source_off
= 0;
1414 cc_copy
.chunks
[0].target_off
= 0;
1415 cc_copy
.chunks
[0].length
= 4096;
1417 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1419 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1420 torture_assert_ndr_success(torture
, ndr_ret
,
1421 "ndr_push_srv_copychunk_copy");
1423 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1424 torture_assert_ntstatus_equal(torture
, status
,
1425 NT_STATUS_ACCESS_DENIED
,
1426 "FSCTL_SRV_COPYCHUNK");
1428 smb2_util_close(tree
, src_h
);
1429 smb2_util_close(tree
, dest_h
);
1431 /* no write permission on dest */
1432 ok
= test_setup_copy_chunk(
1433 torture
, tree
, tree
, tmp_ctx
, 1, /* 1 chunk */
1434 FNAME
, &src_h
, 4096, /* fill 4096 byte src file */
1435 SEC_FILE_READ_DATA
| SEC_FILE_READ_ATTRIBUTE
, FNAME2
, &dest_h
,
1436 0, /* 0 byte dest file */
1437 (SEC_RIGHTS_FILE_ALL
&
1438 ~(SEC_FILE_WRITE_DATA
| SEC_FILE_APPEND_DATA
)),
1441 torture_fail(torture
, "setup copy chunk error");
1444 cc_copy
.chunks
[0].source_off
= 0;
1445 cc_copy
.chunks
[0].target_off
= 0;
1446 cc_copy
.chunks
[0].length
= 4096;
1448 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1450 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1451 torture_assert_ndr_success(torture
, ndr_ret
,
1452 "ndr_push_srv_copychunk_copy");
1454 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1455 torture_assert_ntstatus_equal(torture
, status
,
1456 NT_STATUS_ACCESS_DENIED
,
1457 "FSCTL_SRV_COPYCHUNK");
1459 smb2_util_close(tree
, src_h
);
1460 smb2_util_close(tree
, dest_h
);
1462 /* no read permission on dest */
1463 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
, 1, /* 1 chunk */
1464 FNAME
, &src_h
, 4096, /* fill 4096 byte src file */
1465 SEC_FILE_READ_DATA
| SEC_FILE_READ_ATTRIBUTE
,
1466 FNAME2
, &dest_h
, 0, /* 0 byte dest file */
1467 (SEC_RIGHTS_FILE_ALL
& ~SEC_FILE_READ_DATA
),
1470 torture_fail(torture
, "setup copy chunk error");
1473 cc_copy
.chunks
[0].source_off
= 0;
1474 cc_copy
.chunks
[0].target_off
= 0;
1475 cc_copy
.chunks
[0].length
= 4096;
1477 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1479 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1480 torture_assert_ndr_success(torture
, ndr_ret
,
1481 "ndr_push_srv_copychunk_copy");
1484 * FSCTL_SRV_COPYCHUNK requires read permission on dest,
1485 * FSCTL_SRV_COPYCHUNK_WRITE on the other hand does not.
1487 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1488 torture_assert_ntstatus_equal(torture
, status
,
1489 NT_STATUS_ACCESS_DENIED
,
1490 "FSCTL_SRV_COPYCHUNK");
1492 smb2_util_close(tree
, src_h
);
1493 smb2_util_close(tree
, dest_h
);
1494 talloc_free(tmp_ctx
);
1499 static bool test_ioctl_copy_chunk_write_access(struct torture_context
*torture
,
1500 struct smb2_tree
*tree
)
1502 struct smb2_handle src_h
;
1503 struct smb2_handle dest_h
;
1505 union smb_ioctl ioctl
;
1506 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
1507 struct srv_copychunk_copy cc_copy
;
1508 enum ndr_err_code ndr_ret
;
1511 /* no read permission on dest with FSCTL_SRV_COPYCHUNK_WRITE */
1512 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
1515 &src_h
, 4096, /* fill 4096 byte src file */
1516 SEC_RIGHTS_FILE_ALL
,
1518 &dest_h
, 0, /* 0 byte dest file */
1519 (SEC_RIGHTS_FILE_WRITE
1520 | SEC_RIGHTS_FILE_EXECUTE
),
1524 torture_fail(torture
, "setup copy chunk error");
1527 ioctl
.smb2
.in
.function
= FSCTL_SRV_COPYCHUNK_WRITE
;
1528 cc_copy
.chunks
[0].source_off
= 0;
1529 cc_copy
.chunks
[0].target_off
= 0;
1530 cc_copy
.chunks
[0].length
= 4096;
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_WRITE");
1542 smb2_util_close(tree
, src_h
);
1543 smb2_util_close(tree
, dest_h
);
1544 talloc_free(tmp_ctx
);
1549 static bool test_ioctl_copy_chunk_src_exceed(struct torture_context
*torture
,
1550 struct smb2_tree
*tree
)
1552 struct smb2_handle src_h
;
1553 struct smb2_handle dest_h
;
1555 union smb_ioctl ioctl
;
1556 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
1557 struct srv_copychunk_copy cc_copy
;
1558 struct srv_copychunk_rsp cc_rsp
;
1559 enum ndr_err_code ndr_ret
;
1562 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
1565 &src_h
, 4096, /* fill 4096 byte src file */
1566 SEC_RIGHTS_FILE_ALL
,
1568 &dest_h
, 0, /* 0 byte dest file */
1569 SEC_RIGHTS_FILE_ALL
,
1573 torture_fail(torture
, "setup copy chunk error");
1576 /* Request copy where off + length exceeds size of src */
1577 cc_copy
.chunks
[0].source_off
= 1024;
1578 cc_copy
.chunks
[0].target_off
= 0;
1579 cc_copy
.chunks
[0].length
= 4096;
1581 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1583 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1584 torture_assert_ndr_success(torture
, ndr_ret
,
1585 "ndr_push_srv_copychunk_copy");
1587 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1588 torture_assert_ntstatus_equal(torture
, status
,
1589 NT_STATUS_INVALID_VIEW_SIZE
,
1590 "FSCTL_SRV_COPYCHUNK oversize");
1592 /* Request copy where length exceeds size of src */
1593 cc_copy
.chunks
[0].source_off
= 1024;
1594 cc_copy
.chunks
[0].target_off
= 0;
1595 cc_copy
.chunks
[0].length
= 3072;
1597 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1599 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1600 torture_assert_ndr_success(torture
, ndr_ret
,
1601 "ndr_push_srv_copychunk_copy");
1603 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1604 torture_assert_ntstatus_ok(torture
, status
,
1605 "FSCTL_SRV_COPYCHUNK just right");
1607 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
1609 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
1610 torture_assert_ndr_success(torture
, ndr_ret
,
1611 "ndr_pull_srv_copychunk_rsp");
1613 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
1614 1, /* chunks written */
1615 0, /* chunk bytes unsuccessfully written */
1616 3072); /* total bytes written */
1618 torture_fail(torture
, "bad copy chunk response data");
1621 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 0, 3072, 1024);
1623 torture_fail(torture
, "inconsistent file data");
1626 smb2_util_close(tree
, src_h
);
1627 smb2_util_close(tree
, dest_h
);
1628 talloc_free(tmp_ctx
);
1633 test_ioctl_copy_chunk_src_exceed_multi(struct torture_context
*torture
,
1634 struct smb2_tree
*tree
)
1636 struct smb2_handle src_h
;
1637 struct smb2_handle dest_h
;
1639 union smb_ioctl ioctl
;
1640 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
1641 struct srv_copychunk_copy cc_copy
;
1642 struct srv_copychunk_rsp cc_rsp
;
1643 enum ndr_err_code ndr_ret
;
1646 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
1649 &src_h
, 8192, /* fill 8192 byte src file */
1650 SEC_RIGHTS_FILE_ALL
,
1652 &dest_h
, 0, /* 0 byte dest file */
1653 SEC_RIGHTS_FILE_ALL
,
1657 torture_fail(torture
, "setup copy chunk error");
1660 /* Request copy where off + length exceeds size of src */
1661 cc_copy
.chunks
[0].source_off
= 0;
1662 cc_copy
.chunks
[0].target_off
= 0;
1663 cc_copy
.chunks
[0].length
= 4096;
1665 cc_copy
.chunks
[1].source_off
= 4096;
1666 cc_copy
.chunks
[1].target_off
= 4096;
1667 cc_copy
.chunks
[1].length
= 8192;
1669 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1671 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1672 torture_assert_ndr_success(torture
, ndr_ret
,
1673 "ndr_push_srv_copychunk_copy");
1675 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1676 torture_assert_ntstatus_equal(torture
, status
,
1677 NT_STATUS_INVALID_VIEW_SIZE
,
1678 "FSCTL_SRV_COPYCHUNK oversize");
1679 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
1681 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
1682 torture_assert_ndr_success(torture
, ndr_ret
, "unmarshalling response");
1684 /* first chunk should still be written */
1685 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
1686 1, /* chunks written */
1687 0, /* chunk bytes unsuccessfully written */
1688 4096); /* total bytes written */
1690 torture_fail(torture
, "bad copy chunk response data");
1692 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 0, 4096, 0);
1694 torture_fail(torture
, "inconsistent file data");
1697 smb2_util_close(tree
, src_h
);
1698 smb2_util_close(tree
, dest_h
);
1699 talloc_free(tmp_ctx
);
1703 static bool test_ioctl_copy_chunk_sparse_dest(struct torture_context
*torture
,
1704 struct smb2_tree
*tree
)
1706 struct smb2_handle src_h
;
1707 struct smb2_handle dest_h
;
1709 union smb_ioctl ioctl
;
1711 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
1712 struct srv_copychunk_copy cc_copy
;
1713 struct srv_copychunk_rsp cc_rsp
;
1714 enum ndr_err_code ndr_ret
;
1718 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
1721 &src_h
, 4096, /* fill 4096 byte src file */
1722 SEC_RIGHTS_FILE_ALL
,
1724 &dest_h
, 0, /* 0 byte dest file */
1725 SEC_RIGHTS_FILE_ALL
,
1729 torture_fail(torture
, "setup copy chunk error");
1732 /* copy all src file data (via a single chunk desc) */
1733 cc_copy
.chunks
[0].source_off
= 0;
1734 cc_copy
.chunks
[0].target_off
= 4096;
1735 cc_copy
.chunks
[0].length
= 4096;
1737 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1739 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1740 torture_assert_ndr_success(torture
, ndr_ret
,
1741 "ndr_push_srv_copychunk_copy");
1743 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1744 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_COPYCHUNK");
1746 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
1748 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
1749 torture_assert_ndr_success(torture
, ndr_ret
,
1750 "ndr_pull_srv_copychunk_rsp");
1752 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
1753 1, /* chunks written */
1754 0, /* chunk bytes unsuccessfully written */
1755 4096); /* total bytes written */
1757 torture_fail(torture
, "bad copy chunk response data");
1760 /* check for zeros in first 4k */
1762 r
.in
.file
.handle
= dest_h
;
1765 status
= smb2_read(tree
, tmp_ctx
, &r
);
1766 torture_assert_ntstatus_ok(torture
, status
, "read");
1768 torture_assert_u64_equal(torture
, r
.out
.data
.length
, 4096,
1769 "read data len mismatch");
1771 for (i
= 0; i
< 4096; i
++) {
1772 torture_assert(torture
, (r
.out
.data
.data
[i
] == 0),
1773 "sparse did not pass class");
1776 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 4096, 4096, 0);
1778 torture_fail(torture
, "inconsistent file data");
1781 smb2_util_close(tree
, src_h
);
1782 smb2_util_close(tree
, dest_h
);
1783 talloc_free(tmp_ctx
);
1788 * set the ioctl MaxOutputResponse size to less than
1789 * sizeof(struct srv_copychunk_rsp)
1791 static bool test_ioctl_copy_chunk_max_output_sz(struct torture_context
*torture
,
1792 struct smb2_tree
*tree
)
1794 struct smb2_handle src_h
;
1795 struct smb2_handle dest_h
;
1797 union smb_ioctl ioctl
;
1798 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
1799 struct srv_copychunk_copy cc_copy
;
1800 enum ndr_err_code ndr_ret
;
1803 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
1806 &src_h
, 4096, /* fill 4096 byte src file */
1807 SEC_RIGHTS_FILE_ALL
,
1809 &dest_h
, 0, /* 0 byte dest file */
1810 SEC_RIGHTS_FILE_ALL
,
1814 torture_fail(torture
, "setup copy chunk error");
1817 cc_copy
.chunks
[0].source_off
= 0;
1818 cc_copy
.chunks
[0].target_off
= 0;
1819 cc_copy
.chunks
[0].length
= 4096;
1820 /* req is valid, but use undersize max_output_response */
1821 ioctl
.smb2
.in
.max_output_response
= sizeof(struct srv_copychunk_rsp
) - 1;
1823 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1825 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1826 torture_assert_ndr_success(torture
, ndr_ret
,
1827 "ndr_push_srv_copychunk_copy");
1829 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1830 torture_assert_ntstatus_equal(torture
, status
,
1831 NT_STATUS_INVALID_PARAMETER
,
1832 "FSCTL_SRV_COPYCHUNK");
1834 smb2_util_close(tree
, src_h
);
1835 smb2_util_close(tree
, dest_h
);
1836 talloc_free(tmp_ctx
);
1840 static bool test_ioctl_copy_chunk_zero_length(struct torture_context
*torture
,
1841 struct smb2_tree
*tree
)
1843 struct smb2_handle src_h
;
1844 struct smb2_handle dest_h
;
1846 union smb_ioctl ioctl
;
1847 union smb_fileinfo q
;
1848 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
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
, 4096, /* fill 4096 byte src file */
1858 SEC_RIGHTS_FILE_ALL
,
1860 &dest_h
, 0, /* 0 byte dest file */
1861 SEC_RIGHTS_FILE_ALL
,
1865 torture_fail(torture
, "setup copy chunk error");
1868 /* zero length server-side copy (via a single chunk desc) */
1869 cc_copy
.chunks
[0].source_off
= 0;
1870 cc_copy
.chunks
[0].target_off
= 0;
1871 cc_copy
.chunks
[0].length
= 0;
1873 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1875 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1876 torture_assert_ndr_success(torture
, ndr_ret
,
1877 "ndr_push_srv_copychunk_copy");
1879 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1880 torture_assert_ntstatus_equal(torture
, status
,
1881 NT_STATUS_INVALID_PARAMETER
,
1882 "bad zero-length chunk response");
1884 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
1886 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
1887 torture_assert_ndr_success(torture
, ndr_ret
, "unmarshalling response");
1890 q
.all_info2
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
1891 q
.all_info2
.in
.file
.handle
= dest_h
;
1892 status
= smb2_getinfo_file(tree
, torture
, &q
);
1893 torture_assert_ntstatus_ok(torture
, status
, "getinfo");
1895 torture_assert_int_equal(torture
, q
.all_info2
.out
.size
, 0,
1896 "size after zero len clone");
1898 smb2_util_close(tree
, src_h
);
1899 smb2_util_close(tree
, dest_h
);
1900 talloc_free(tmp_ctx
);
1904 static bool copy_one_stream(struct torture_context
*torture
,
1905 struct smb2_tree
*tree
,
1906 TALLOC_CTX
*tmp_ctx
,
1907 const char *src_sname
,
1908 const char *dst_sname
)
1910 struct smb2_handle src_h
= {{0}};
1911 struct smb2_handle dest_h
= {{0}};
1914 struct srv_copychunk_copy cc_copy
;
1915 struct srv_copychunk_rsp cc_rsp
;
1916 enum ndr_err_code ndr_ret
;
1919 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
1922 &src_h
, 256, /* fill 256 byte src file */
1923 SEC_FILE_READ_DATA
| SEC_FILE_WRITE_DATA
,
1925 &dest_h
, 0, /* 0 byte dest file */
1926 SEC_FILE_READ_DATA
| SEC_FILE_WRITE_DATA
,
1929 torture_assert_goto(torture
, ok
== true, ok
, done
,
1930 "setup copy chunk error\n");
1932 /* copy all src file data (via a single chunk desc) */
1933 cc_copy
.chunks
[0].source_off
= 0;
1934 cc_copy
.chunks
[0].target_off
= 0;
1935 cc_copy
.chunks
[0].length
= 256;
1937 ndr_ret
= ndr_push_struct_blob(
1938 &io
.smb2
.in
.out
, tmp_ctx
, &cc_copy
,
1939 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1941 torture_assert_ndr_success_goto(torture
, ndr_ret
, ok
, done
,
1942 "ndr_push_srv_copychunk_copy\n");
1944 status
= smb2_ioctl(tree
, tmp_ctx
, &io
.smb2
);
1945 torture_assert_ntstatus_ok_goto(torture
, status
, ok
, done
,
1946 "FSCTL_SRV_COPYCHUNK\n");
1948 ndr_ret
= ndr_pull_struct_blob(
1949 &io
.smb2
.out
.out
, tmp_ctx
, &cc_rsp
,
1950 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
1952 torture_assert_ndr_success_goto(torture
, ndr_ret
, ok
, done
,
1953 "ndr_pull_srv_copychunk_rsp\n");
1955 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
1956 1, /* chunks written */
1957 0, /* chunk bytes unsuccessfully written */
1958 256); /* total bytes written */
1959 torture_assert_goto(torture
, ok
== true, ok
, done
,
1960 "bad copy chunk response data\n");
1962 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 0, 256, 0);
1964 torture_fail(torture
, "inconsistent file data\n");
1968 if (!smb2_util_handle_empty(src_h
)) {
1969 smb2_util_close(tree
, src_h
);
1971 if (!smb2_util_handle_empty(dest_h
)) {
1972 smb2_util_close(tree
, dest_h
);
1981 static bool torture_setup_file(TALLOC_CTX
*mem_ctx
,
1982 struct smb2_tree
*tree
,
1985 struct smb2_create io
;
1988 smb2_util_unlink(tree
, name
);
1990 io
.in
.desired_access
= SEC_FLAG_MAXIMUM_ALLOWED
;
1991 io
.in
.file_attributes
= FILE_ATTRIBUTE_NORMAL
;
1992 io
.in
.create_disposition
= NTCREATEX_DISP_OVERWRITE_IF
;
1993 io
.in
.share_access
=
1994 NTCREATEX_SHARE_ACCESS_DELETE
|
1995 NTCREATEX_SHARE_ACCESS_READ
|
1996 NTCREATEX_SHARE_ACCESS_WRITE
;
1997 io
.in
.create_options
= 0;
2000 status
= smb2_create(tree
, mem_ctx
, &io
);
2001 if (!NT_STATUS_IS_OK(status
)) {
2005 status
= smb2_util_close(tree
, io
.out
.file
.handle
);
2006 if (!NT_STATUS_IS_OK(status
)) {
2013 static bool test_copy_chunk_streams(struct torture_context
*torture
,
2014 struct smb2_tree
*tree
)
2016 const char *src_name
= "src";
2017 const char *dst_name
= "dst";
2019 const char *src_sname
;
2020 const char *dst_sname
;
2022 { "src:foo", "dst:foo" }
2025 TALLOC_CTX
*tmp_ctx
= NULL
;
2028 tmp_ctx
= talloc_new(tree
);
2029 torture_assert_not_null_goto(torture
, tmp_ctx
, ok
, done
,
2030 "torture_setup_file\n");
2032 ok
= torture_setup_file(torture
, tree
, src_name
);
2033 torture_assert_goto(torture
, ok
== true, ok
, done
, "torture_setup_file\n");
2034 ok
= torture_setup_file(torture
, tree
, dst_name
);
2035 torture_assert_goto(torture
, ok
== true, ok
, done
, "torture_setup_file\n");
2037 for (i
= 0; i
< ARRAY_SIZE(names
); i
++) {
2038 ok
= copy_one_stream(torture
, tree
, tmp_ctx
,
2040 names
[i
].dst_sname
);
2041 torture_assert_goto(torture
, ok
== true, ok
, done
,
2042 "copy_one_stream failed\n");
2046 smb2_util_unlink(tree
, src_name
);
2047 smb2_util_unlink(tree
, dst_name
);
2048 talloc_free(tmp_ctx
);
2052 static bool test_copy_chunk_across_shares(struct torture_context
*tctx
,
2053 struct smb2_tree
*tree
)
2055 TALLOC_CTX
*mem_ctx
= NULL
;
2056 struct smb2_tree
*tree2
= NULL
;
2057 struct smb2_handle src_h
= {{0}};
2058 struct smb2_handle dest_h
= {{0}};
2059 union smb_ioctl ioctl
;
2060 struct srv_copychunk_copy cc_copy
;
2061 struct srv_copychunk_rsp cc_rsp
;
2062 enum ndr_err_code ndr_ret
;
2066 mem_ctx
= talloc_new(tctx
);
2067 torture_assert_not_null_goto(tctx
, mem_ctx
, ok
, done
,
2070 ok
= torture_smb2_tree_connect(tctx
, tree
->session
, tctx
, &tree2
);
2071 torture_assert_goto(tctx
, ok
== true, ok
, done
,
2072 "torture_smb2_tree_connect failed\n");
2074 ok
= test_setup_copy_chunk(tctx
, tree
, tree2
, mem_ctx
,
2077 &src_h
, 4096, /* fill 4096 byte src file */
2078 SEC_RIGHTS_FILE_ALL
,
2080 &dest_h
, 0, /* 0 byte dest file */
2081 SEC_RIGHTS_FILE_ALL
,
2084 torture_assert_goto(tctx
, ok
== true, ok
, done
,
2085 "test_setup_copy_chunk failed\n");
2087 cc_copy
.chunks
[0].source_off
= 0;
2088 cc_copy
.chunks
[0].target_off
= 0;
2089 cc_copy
.chunks
[0].length
= 4096;
2091 ndr_ret
= ndr_push_struct_blob(
2092 &ioctl
.smb2
.in
.out
, mem_ctx
, &cc_copy
,
2093 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
2094 torture_assert_ndr_success_goto(tctx
, ndr_ret
, ok
, done
,
2095 "ndr_push_srv_copychunk_copy\n");
2097 status
= smb2_ioctl(tree2
, mem_ctx
, &ioctl
.smb2
);
2098 torture_assert_ntstatus_ok_goto(tctx
, status
, ok
, done
,
2099 "FSCTL_SRV_COPYCHUNK\n");
2101 ndr_ret
= ndr_pull_struct_blob(
2102 &ioctl
.smb2
.out
.out
, mem_ctx
, &cc_rsp
,
2103 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
2105 torture_assert_ndr_success_goto(tctx
, ndr_ret
, ok
, done
,
2106 "ndr_pull_srv_copychunk_rsp\n");
2108 ok
= check_copy_chunk_rsp(tctx
, &cc_rsp
,
2109 1, /* chunks written */
2110 0, /* chunk bytes unsuccessfully written */
2111 4096); /* total bytes written */
2112 torture_assert_goto(tctx
, ok
== true, ok
, done
,
2113 "bad copy chunk response data\n");
2115 ok
= check_pattern(tctx
, tree2
, mem_ctx
, dest_h
, 0, 4096, 0);
2116 torture_assert_goto(tctx
, ok
== true, ok
, done
,
2117 "inconsistent file data\n");
2120 TALLOC_FREE(mem_ctx
);
2121 if (!smb2_util_handle_empty(src_h
)) {
2122 smb2_util_close(tree
, src_h
);
2124 if (!smb2_util_handle_empty(dest_h
)) {
2125 smb2_util_close(tree2
, dest_h
);
2127 smb2_util_unlink(tree
, FNAME
);
2128 smb2_util_unlink(tree2
, FNAME2
);
2129 if (tree2
!= NULL
) {
2135 /* Test closing the src handle */
2136 static bool test_copy_chunk_across_shares2(struct torture_context
*tctx
,
2137 struct smb2_tree
*tree
)
2139 TALLOC_CTX
*mem_ctx
= NULL
;
2140 struct smb2_tree
*tree2
= NULL
;
2141 struct smb2_handle src_h
= {{0}};
2142 struct smb2_handle dest_h
= {{0}};
2143 union smb_ioctl ioctl
;
2144 struct srv_copychunk_copy cc_copy
;
2145 enum ndr_err_code ndr_ret
;
2149 mem_ctx
= talloc_new(tctx
);
2150 torture_assert_not_null_goto(tctx
, mem_ctx
, ok
, done
,
2153 ok
= torture_smb2_tree_connect(tctx
, tree
->session
, tctx
, &tree2
);
2154 torture_assert_goto(tctx
, ok
== true, ok
, done
,
2155 "torture_smb2_tree_connect failed\n");
2157 ok
= test_setup_copy_chunk(tctx
, tree
, tree2
, mem_ctx
,
2160 &src_h
, 4096, /* fill 4096 byte src file */
2161 SEC_RIGHTS_FILE_ALL
,
2163 &dest_h
, 0, /* 0 byte dest file */
2164 SEC_RIGHTS_FILE_ALL
,
2167 torture_assert_goto(tctx
, ok
== true, ok
, done
,
2168 "test_setup_copy_chunk failed\n");
2170 status
= smb2_util_close(tree
, src_h
);
2171 torture_assert_ntstatus_ok_goto(tctx
, status
, ok
, done
,
2172 "smb2_util_close failed\n");
2175 cc_copy
.chunks
[0].source_off
= 0;
2176 cc_copy
.chunks
[0].target_off
= 0;
2177 cc_copy
.chunks
[0].length
= 4096;
2179 ndr_ret
= ndr_push_struct_blob(
2180 &ioctl
.smb2
.in
.out
, mem_ctx
, &cc_copy
,
2181 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
2182 torture_assert_ndr_success_goto(tctx
, ndr_ret
, ok
, done
,
2183 "ndr_push_srv_copychunk_copy\n");
2185 status
= smb2_ioctl(tree2
, mem_ctx
, &ioctl
.smb2
);
2186 torture_assert_ntstatus_equal_goto(tctx
, status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
,
2187 ok
, done
, "smb2_ioctl failed\n");
2190 TALLOC_FREE(mem_ctx
);
2191 if (!smb2_util_handle_empty(src_h
)) {
2192 smb2_util_close(tree
, src_h
);
2194 if (!smb2_util_handle_empty(dest_h
)) {
2195 smb2_util_close(tree2
, dest_h
);
2197 smb2_util_unlink(tree
, FNAME
);
2198 smb2_util_unlink(tree2
, FNAME2
);
2199 if (tree2
!= NULL
) {
2205 /* Test offset works */
2206 static bool test_copy_chunk_across_shares3(struct torture_context
*tctx
,
2207 struct smb2_tree
*tree
)
2209 TALLOC_CTX
*mem_ctx
= NULL
;
2210 struct smb2_tree
*tree2
= NULL
;
2211 struct smb2_handle src_h
= {{0}};
2212 struct smb2_handle dest_h
= {{0}};
2213 union smb_ioctl ioctl
;
2214 struct srv_copychunk_copy cc_copy
;
2215 struct srv_copychunk_rsp cc_rsp
;
2216 enum ndr_err_code ndr_ret
;
2220 mem_ctx
= talloc_new(tctx
);
2221 torture_assert_not_null_goto(tctx
, mem_ctx
, ok
, done
,
2224 ok
= torture_smb2_tree_connect(tctx
, tree
->session
, tctx
, &tree2
);
2225 torture_assert_goto(tctx
, ok
== true, ok
, done
,
2226 "torture_smb2_tree_connect failed\n");
2228 ok
= test_setup_copy_chunk(tctx
, tree
, tree2
, mem_ctx
,
2231 &src_h
, 4096, /* fill 4096 byte src file */
2232 SEC_RIGHTS_FILE_ALL
,
2234 &dest_h
, 0, /* 0 byte dest file */
2235 SEC_RIGHTS_FILE_ALL
,
2238 torture_assert_goto(tctx
, ok
== true, ok
, done
,
2239 "test_setup_copy_chunk failed\n");
2241 cc_copy
.chunks
[0].source_off
= 0;
2242 cc_copy
.chunks
[0].target_off
= 0;
2243 cc_copy
.chunks
[0].length
= 4096;
2245 /* second chunk appends the same data to the first */
2246 cc_copy
.chunks
[1].source_off
= 0;
2247 cc_copy
.chunks
[1].target_off
= 4096;
2248 cc_copy
.chunks
[1].length
= 4096;
2250 ndr_ret
= ndr_push_struct_blob(
2251 &ioctl
.smb2
.in
.out
, mem_ctx
, &cc_copy
,
2252 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
2253 torture_assert_ndr_success_goto(tctx
, ndr_ret
, ok
, done
,
2254 "ndr_push_srv_copychunk_copy\n");
2256 status
= smb2_ioctl(tree2
, mem_ctx
, &ioctl
.smb2
);
2257 torture_assert_ntstatus_ok_goto(tctx
, status
, ok
, done
, "smb2_ioctl failed\n");
2259 ndr_ret
= ndr_pull_struct_blob(
2260 &ioctl
.smb2
.out
.out
, mem_ctx
, &cc_rsp
,
2261 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
2263 torture_assert_ndr_success_goto(tctx
, ndr_ret
, ok
, done
,
2264 "ndr_pull_srv_copychunk_rsp\n");
2266 ok
= check_copy_chunk_rsp(tctx
, &cc_rsp
,
2267 2, /* chunks written */
2268 0, /* chunk bytes unsuccessfully written */
2269 8192); /* total bytes written */
2270 torture_assert_goto(tctx
, ok
== true, ok
, done
,
2271 "check_copy_chunk_rsp failed\n");
2273 ok
= check_pattern(tctx
, tree2
, mem_ctx
, dest_h
, 0, 4096, 0);
2274 torture_assert_goto(tctx
, ok
== true, ok
, done
,
2275 "check_pattern failed\n");
2277 ok
= check_pattern(tctx
, tree2
, mem_ctx
, dest_h
, 4096, 4096, 0);
2278 torture_assert_goto(tctx
, ok
== true, ok
, done
,
2279 "check_pattern failed\n");
2282 TALLOC_FREE(mem_ctx
);
2283 if (!smb2_util_handle_empty(src_h
)) {
2284 smb2_util_close(tree
, src_h
);
2286 if (!smb2_util_handle_empty(dest_h
)) {
2287 smb2_util_close(tree2
, dest_h
);
2289 smb2_util_unlink(tree
, FNAME
);
2290 smb2_util_unlink(tree2
, FNAME2
);
2291 if (tree2
!= NULL
) {
2297 static NTSTATUS
test_ioctl_compress_fs_supported(struct torture_context
*torture
,
2298 struct smb2_tree
*tree
,
2299 TALLOC_CTX
*mem_ctx
,
2300 struct smb2_handle
*fh
,
2301 bool *compress_support
)
2304 union smb_fsinfo info
;
2307 info
.generic
.level
= RAW_QFS_ATTRIBUTE_INFORMATION
;
2308 info
.generic
.handle
= *fh
;
2309 status
= smb2_getinfo_fs(tree
, tree
, &info
);
2310 if (!NT_STATUS_IS_OK(status
)) {
2314 if (info
.attribute_info
.out
.fs_attr
& FILE_FILE_COMPRESSION
) {
2315 *compress_support
= true;
2317 *compress_support
= false;
2319 return NT_STATUS_OK
;
2322 static NTSTATUS
test_ioctl_compress_get(struct torture_context
*torture
,
2323 TALLOC_CTX
*mem_ctx
,
2324 struct smb2_tree
*tree
,
2325 struct smb2_handle fh
,
2326 uint16_t *_compression_fmt
)
2328 union smb_ioctl ioctl
;
2329 struct compression_state cmpr_state
;
2330 enum ndr_err_code ndr_ret
;
2334 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
2335 ioctl
.smb2
.in
.file
.handle
= fh
;
2336 ioctl
.smb2
.in
.function
= FSCTL_GET_COMPRESSION
;
2337 ioctl
.smb2
.in
.max_output_response
= sizeof(struct compression_state
);
2338 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
2340 status
= smb2_ioctl(tree
, mem_ctx
, &ioctl
.smb2
);
2341 if (!NT_STATUS_IS_OK(status
)) {
2345 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, mem_ctx
,
2347 (ndr_pull_flags_fn_t
)ndr_pull_compression_state
);
2349 if (ndr_ret
!= NDR_ERR_SUCCESS
) {
2350 return NT_STATUS_INTERNAL_ERROR
;
2353 *_compression_fmt
= cmpr_state
.format
;
2354 return NT_STATUS_OK
;
2357 static NTSTATUS
test_ioctl_compress_set(struct torture_context
*torture
,
2358 TALLOC_CTX
*mem_ctx
,
2359 struct smb2_tree
*tree
,
2360 struct smb2_handle fh
,
2361 uint16_t compression_fmt
)
2363 union smb_ioctl ioctl
;
2364 struct compression_state cmpr_state
;
2365 enum ndr_err_code ndr_ret
;
2369 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
2370 ioctl
.smb2
.in
.file
.handle
= fh
;
2371 ioctl
.smb2
.in
.function
= FSCTL_SET_COMPRESSION
;
2372 ioctl
.smb2
.in
.max_output_response
= 0;
2373 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
2375 cmpr_state
.format
= compression_fmt
;
2376 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, mem_ctx
,
2378 (ndr_push_flags_fn_t
)ndr_push_compression_state
);
2379 if (ndr_ret
!= NDR_ERR_SUCCESS
) {
2380 return NT_STATUS_INTERNAL_ERROR
;
2383 status
= smb2_ioctl(tree
, mem_ctx
, &ioctl
.smb2
);
2387 static bool test_ioctl_compress_file_flag(struct torture_context
*torture
,
2388 struct smb2_tree
*tree
)
2390 struct smb2_handle fh
;
2392 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
2394 uint16_t compression_fmt
;
2396 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2397 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
2398 FILE_ATTRIBUTE_NORMAL
);
2399 torture_assert(torture
, ok
, "setup compression file");
2401 status
= test_ioctl_compress_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
2403 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
2405 smb2_util_close(tree
, fh
);
2406 torture_skip(torture
, "FS compression not supported\n");
2409 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, fh
,
2411 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
2413 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_NONE
),
2414 "initial compression state not NONE");
2416 status
= test_ioctl_compress_set(torture
, tmp_ctx
, tree
, fh
,
2417 COMPRESSION_FORMAT_DEFAULT
);
2418 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_COMPRESSION");
2420 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, fh
,
2422 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
2424 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_LZNT1
),
2425 "invalid compression state after set");
2427 smb2_util_close(tree
, fh
);
2428 talloc_free(tmp_ctx
);
2432 static bool test_ioctl_compress_dir_inherit(struct torture_context
*torture
,
2433 struct smb2_tree
*tree
)
2435 struct smb2_handle dirh
;
2436 struct smb2_handle fh
;
2438 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
2439 uint16_t compression_fmt
;
2441 char path_buf
[PATH_MAX
];
2443 smb2_deltree(tree
, DNAME
);
2444 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2445 DNAME
, &dirh
, 0, SEC_RIGHTS_FILE_ALL
,
2446 FILE_ATTRIBUTE_DIRECTORY
);
2447 torture_assert(torture
, ok
, "setup compression directory");
2449 status
= test_ioctl_compress_fs_supported(torture
, tree
, tmp_ctx
, &dirh
,
2451 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
2453 smb2_util_close(tree
, dirh
);
2454 smb2_deltree(tree
, DNAME
);
2455 torture_skip(torture
, "FS compression not supported\n");
2458 /* set compression on parent dir, then check for inheritance */
2459 status
= test_ioctl_compress_set(torture
, tmp_ctx
, tree
, dirh
,
2460 COMPRESSION_FORMAT_LZNT1
);
2461 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_COMPRESSION");
2463 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, dirh
,
2465 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
2467 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_LZNT1
),
2468 "invalid compression state after set");
2470 snprintf(path_buf
, PATH_MAX
, "%s\\%s", DNAME
, FNAME
);
2471 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2472 path_buf
, &fh
, 4096, SEC_RIGHTS_FILE_ALL
,
2473 FILE_ATTRIBUTE_NORMAL
);
2474 torture_assert(torture
, ok
, "setup compression file");
2476 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, fh
,
2478 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
2480 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_LZNT1
),
2481 "compression attr not inherited by new file");
2483 /* check compressed data is consistent */
2484 ok
= check_pattern(torture
, tree
, tmp_ctx
, fh
, 0, 4096, 0);
2486 /* disable dir compression attr, file should remain compressed */
2487 status
= test_ioctl_compress_set(torture
, tmp_ctx
, tree
, dirh
,
2488 COMPRESSION_FORMAT_NONE
);
2489 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_COMPRESSION");
2491 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, fh
,
2493 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
2495 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_LZNT1
),
2496 "file compression attr removed after dir change");
2497 smb2_util_close(tree
, fh
);
2499 /* new files should no longer inherit compression attr */
2500 snprintf(path_buf
, PATH_MAX
, "%s\\%s", DNAME
, FNAME2
);
2501 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2502 path_buf
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
2503 FILE_ATTRIBUTE_NORMAL
);
2504 torture_assert(torture
, ok
, "setup file");
2506 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, fh
,
2508 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
2510 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_NONE
),
2511 "compression attr present on new file");
2513 smb2_util_close(tree
, fh
);
2514 smb2_util_close(tree
, dirh
);
2515 smb2_deltree(tree
, DNAME
);
2516 talloc_free(tmp_ctx
);
2520 static bool test_ioctl_compress_invalid_format(struct torture_context
*torture
,
2521 struct smb2_tree
*tree
)
2523 struct smb2_handle fh
;
2525 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
2527 uint16_t compression_fmt
;
2529 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2530 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
2531 FILE_ATTRIBUTE_NORMAL
);
2532 torture_assert(torture
, ok
, "setup compression file");
2534 status
= test_ioctl_compress_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
2536 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
2538 smb2_util_close(tree
, fh
);
2539 torture_skip(torture
, "FS compression not supported\n");
2542 status
= test_ioctl_compress_set(torture
, tmp_ctx
, tree
, fh
,
2543 0x0042); /* bogus */
2544 torture_assert_ntstatus_equal(torture
, status
,
2545 NT_STATUS_INVALID_PARAMETER
,
2546 "invalid FSCTL_SET_COMPRESSION");
2548 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, fh
,
2550 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
2552 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_NONE
),
2553 "initial compression state not NONE");
2555 smb2_util_close(tree
, fh
);
2556 talloc_free(tmp_ctx
);
2560 static bool test_ioctl_compress_invalid_buf(struct torture_context
*torture
,
2561 struct smb2_tree
*tree
)
2563 struct smb2_handle fh
;
2565 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
2567 union smb_ioctl ioctl
;
2569 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2570 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
2571 FILE_ATTRIBUTE_NORMAL
);
2572 torture_assert(torture
, ok
, "setup compression file");
2574 status
= test_ioctl_compress_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
2576 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
2578 smb2_util_close(tree
, fh
);
2579 torture_skip(torture
, "FS compression not supported\n");
2583 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
2584 ioctl
.smb2
.in
.file
.handle
= fh
;
2585 ioctl
.smb2
.in
.function
= FSCTL_GET_COMPRESSION
;
2586 ioctl
.smb2
.in
.max_output_response
= 0; /* no room for rsp data */
2587 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
2589 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
2590 if (!NT_STATUS_EQUAL(status
, NT_STATUS_INVALID_USER_BUFFER
)
2591 && !NT_STATUS_EQUAL(status
, NT_STATUS_INVALID_PARAMETER
)) {
2592 /* neither Server 2k12 nor 2k8r2 response status */
2593 torture_assert(torture
, true,
2594 "invalid FSCTL_SET_COMPRESSION");
2597 smb2_util_close(tree
, fh
);
2598 talloc_free(tmp_ctx
);
2602 static bool test_ioctl_compress_query_file_attr(struct torture_context
*torture
,
2603 struct smb2_tree
*tree
)
2605 struct smb2_handle fh
;
2606 union smb_fileinfo io
;
2608 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
2611 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2612 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
2613 FILE_ATTRIBUTE_NORMAL
);
2614 torture_assert(torture
, ok
, "setup compression file");
2616 status
= test_ioctl_compress_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
2618 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
2620 smb2_util_close(tree
, fh
);
2621 torture_skip(torture
, "FS compression not supported\n");
2625 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
2626 io
.generic
.in
.file
.handle
= fh
;
2627 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
2628 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FILE");
2630 torture_assert(torture
,
2631 ((io
.all_info2
.out
.attrib
& FILE_ATTRIBUTE_COMPRESSED
) == 0),
2632 "compression attr before set");
2634 status
= test_ioctl_compress_set(torture
, tmp_ctx
, tree
, fh
,
2635 COMPRESSION_FORMAT_DEFAULT
);
2636 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_COMPRESSION");
2639 io
.generic
.level
= RAW_FILEINFO_BASIC_INFORMATION
;
2640 io
.generic
.in
.file
.handle
= fh
;
2641 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
2642 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FILE");
2644 torture_assert(torture
,
2645 (io
.basic_info
.out
.attrib
& FILE_ATTRIBUTE_COMPRESSED
),
2646 "no compression attr after set");
2648 smb2_util_close(tree
, fh
);
2649 talloc_free(tmp_ctx
);
2654 * Specify FILE_ATTRIBUTE_COMPRESSED on creation, Windows does not retain this
2657 static bool test_ioctl_compress_create_with_attr(struct torture_context
*torture
,
2658 struct smb2_tree
*tree
)
2660 struct smb2_handle fh2
;
2661 union smb_fileinfo io
;
2663 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
2664 uint16_t compression_fmt
;
2667 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2668 FNAME2
, &fh2
, 0, SEC_RIGHTS_FILE_ALL
,
2669 (FILE_ATTRIBUTE_NORMAL
| FILE_ATTRIBUTE_COMPRESSED
));
2670 torture_assert(torture
, ok
, "setup compression file");
2672 status
= test_ioctl_compress_fs_supported(torture
, tree
, tmp_ctx
, &fh2
,
2674 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
2676 smb2_util_close(tree
, fh2
);
2677 torture_skip(torture
, "FS compression not supported\n");
2680 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, fh2
,
2682 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
2684 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_NONE
),
2685 "initial compression state not NONE");
2688 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
2689 io
.generic
.in
.file
.handle
= fh2
;
2690 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
2691 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FILE");
2693 torture_assert(torture
,
2694 ((io
.all_info2
.out
.attrib
& FILE_ATTRIBUTE_COMPRESSED
) == 0),
2695 "incorrect compression attr");
2697 smb2_util_close(tree
, fh2
);
2698 talloc_free(tmp_ctx
);
2702 static bool test_ioctl_compress_inherit_disable(struct torture_context
*torture
,
2703 struct smb2_tree
*tree
)
2705 struct smb2_handle fh
;
2706 struct smb2_handle dirh
;
2707 char path_buf
[PATH_MAX
];
2709 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
2711 uint16_t compression_fmt
;
2713 struct smb2_create io
;
2715 smb2_deltree(tree
, DNAME
);
2716 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2717 DNAME
, &dirh
, 0, SEC_RIGHTS_FILE_ALL
,
2718 FILE_ATTRIBUTE_DIRECTORY
);
2719 torture_assert(torture
, ok
, "setup compression directory");
2721 status
= test_ioctl_compress_fs_supported(torture
, tree
, tmp_ctx
, &dirh
,
2723 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
2725 smb2_util_close(tree
, dirh
);
2726 smb2_deltree(tree
, DNAME
);
2727 torture_skip(torture
, "FS compression not supported\n");
2730 /* set compression on parent dir, then check for inheritance */
2731 status
= test_ioctl_compress_set(torture
, tmp_ctx
, tree
, dirh
,
2732 COMPRESSION_FORMAT_LZNT1
);
2733 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_COMPRESSION");
2735 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, dirh
,
2737 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
2739 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_LZNT1
),
2740 "invalid compression state after set");
2741 smb2_util_close(tree
, dirh
);
2743 snprintf(path_buf
, PATH_MAX
, "%s\\%s", DNAME
, FNAME
);
2744 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2745 path_buf
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
2746 FILE_ATTRIBUTE_NORMAL
);
2747 torture_assert(torture
, ok
, "setup compression file");
2749 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, fh
,
2751 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
2753 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_LZNT1
),
2754 "compression attr not inherited by new file");
2755 smb2_util_close(tree
, fh
);
2757 snprintf(path_buf
, PATH_MAX
, "%s\\%s", DNAME
, FNAME2
);
2759 /* NO_COMPRESSION option should block inheritance */
2761 io
.in
.desired_access
= SEC_RIGHTS_FILE_ALL
;
2762 io
.in
.file_attributes
= FILE_ATTRIBUTE_NORMAL
;
2763 io
.in
.create_disposition
= NTCREATEX_DISP_CREATE
;
2764 io
.in
.create_options
= NTCREATEX_OPTIONS_NO_COMPRESSION
;
2765 io
.in
.share_access
=
2766 NTCREATEX_SHARE_ACCESS_DELETE
|
2767 NTCREATEX_SHARE_ACCESS_READ
|
2768 NTCREATEX_SHARE_ACCESS_WRITE
;
2769 io
.in
.fname
= path_buf
;
2771 status
= smb2_create(tree
, tmp_ctx
, &io
);
2772 torture_assert_ntstatus_ok(torture
, status
, "file create");
2774 fh
= io
.out
.file
.handle
;
2776 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, fh
,
2778 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
2780 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_NONE
),
2781 "compression attr inherited by NO_COMPRESSION file");
2782 smb2_util_close(tree
, fh
);
2785 snprintf(path_buf
, PATH_MAX
, "%s\\%s", DNAME
, DNAME
);
2787 io
.in
.desired_access
= SEC_RIGHTS_FILE_ALL
;
2788 io
.in
.file_attributes
= FILE_ATTRIBUTE_DIRECTORY
;
2789 io
.in
.create_disposition
= NTCREATEX_DISP_CREATE
;
2790 io
.in
.create_options
= (NTCREATEX_OPTIONS_NO_COMPRESSION
2791 | NTCREATEX_OPTIONS_DIRECTORY
);
2792 io
.in
.share_access
=
2793 NTCREATEX_SHARE_ACCESS_DELETE
|
2794 NTCREATEX_SHARE_ACCESS_READ
|
2795 NTCREATEX_SHARE_ACCESS_WRITE
;
2796 io
.in
.fname
= path_buf
;
2798 status
= smb2_create(tree
, tmp_ctx
, &io
);
2799 torture_assert_ntstatus_ok(torture
, status
, "dir create");
2801 dirh
= io
.out
.file
.handle
;
2803 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, dirh
,
2805 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
2807 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_NONE
),
2808 "compression attr inherited by NO_COMPRESSION dir");
2809 smb2_util_close(tree
, dirh
);
2810 smb2_deltree(tree
, DNAME
);
2812 talloc_free(tmp_ctx
);
2816 /* attempting to set compression via SetInfo should not stick */
2817 static bool test_ioctl_compress_set_file_attr(struct torture_context
*torture
,
2818 struct smb2_tree
*tree
)
2820 struct smb2_handle fh
;
2821 struct smb2_handle dirh
;
2822 union smb_fileinfo io
;
2823 union smb_setfileinfo set_io
;
2824 uint16_t compression_fmt
;
2826 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
2829 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2830 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
2831 FILE_ATTRIBUTE_NORMAL
);
2832 torture_assert(torture
, ok
, "setup compression file");
2834 status
= test_ioctl_compress_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
2836 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
2838 smb2_util_close(tree
, fh
);
2839 torture_skip(torture
, "FS compression not supported\n");
2843 io
.generic
.level
= RAW_FILEINFO_BASIC_INFORMATION
;
2844 io
.generic
.in
.file
.handle
= fh
;
2845 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
2846 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FILE");
2848 torture_assert(torture
,
2849 ((io
.basic_info
.out
.attrib
& FILE_ATTRIBUTE_COMPRESSED
) == 0),
2850 "compression attr before set");
2852 ZERO_STRUCT(set_io
);
2853 set_io
.generic
.level
= RAW_SFILEINFO_BASIC_INFORMATION
;
2854 set_io
.basic_info
.in
.file
.handle
= fh
;
2855 set_io
.basic_info
.in
.create_time
= io
.basic_info
.out
.create_time
;
2856 set_io
.basic_info
.in
.access_time
= io
.basic_info
.out
.access_time
;
2857 set_io
.basic_info
.in
.write_time
= io
.basic_info
.out
.write_time
;
2858 set_io
.basic_info
.in
.change_time
= io
.basic_info
.out
.change_time
;
2859 set_io
.basic_info
.in
.attrib
= (io
.basic_info
.out
.attrib
2860 | FILE_ATTRIBUTE_COMPRESSED
);
2861 status
= smb2_setinfo_file(tree
, &set_io
);
2862 torture_assert_ntstatus_ok(torture
, status
, "SMB2_SETINFO_FILE");
2865 io
.generic
.level
= RAW_FILEINFO_BASIC_INFORMATION
;
2866 io
.generic
.in
.file
.handle
= fh
;
2867 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
2868 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FILE");
2870 torture_assert(torture
,
2871 ((io
.basic_info
.out
.attrib
& FILE_ATTRIBUTE_COMPRESSED
) == 0),
2872 "compression attr after set");
2874 smb2_util_close(tree
, fh
);
2875 smb2_deltree(tree
, DNAME
);
2876 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2877 DNAME
, &dirh
, 0, SEC_RIGHTS_FILE_ALL
,
2878 FILE_ATTRIBUTE_DIRECTORY
);
2879 torture_assert(torture
, ok
, "setup compression directory");
2882 io
.generic
.level
= RAW_FILEINFO_BASIC_INFORMATION
;
2883 io
.generic
.in
.file
.handle
= dirh
;
2884 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
2885 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FILE");
2887 torture_assert(torture
,
2888 ((io
.basic_info
.out
.attrib
& FILE_ATTRIBUTE_COMPRESSED
) == 0),
2889 "compression attr before set");
2891 ZERO_STRUCT(set_io
);
2892 set_io
.generic
.level
= RAW_SFILEINFO_BASIC_INFORMATION
;
2893 set_io
.basic_info
.in
.file
.handle
= dirh
;
2894 set_io
.basic_info
.in
.create_time
= io
.basic_info
.out
.create_time
;
2895 set_io
.basic_info
.in
.access_time
= io
.basic_info
.out
.access_time
;
2896 set_io
.basic_info
.in
.write_time
= io
.basic_info
.out
.write_time
;
2897 set_io
.basic_info
.in
.change_time
= io
.basic_info
.out
.change_time
;
2898 set_io
.basic_info
.in
.attrib
= (io
.basic_info
.out
.attrib
2899 | FILE_ATTRIBUTE_COMPRESSED
);
2900 status
= smb2_setinfo_file(tree
, &set_io
);
2901 torture_assert_ntstatus_ok(torture
, status
, "SMB2_SETINFO_FILE");
2903 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, dirh
,
2905 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
2907 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_NONE
),
2908 "dir compression set after SetInfo");
2910 smb2_util_close(tree
, dirh
);
2911 talloc_free(tmp_ctx
);
2915 static bool test_ioctl_compress_perms(struct torture_context
*torture
,
2916 struct smb2_tree
*tree
)
2918 struct smb2_handle fh
;
2919 uint16_t compression_fmt
;
2920 union smb_fileinfo io
;
2922 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
2925 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2926 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
2927 FILE_ATTRIBUTE_NORMAL
);
2928 torture_assert(torture
, ok
, "setup compression file");
2930 status
= test_ioctl_compress_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
2932 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
2933 smb2_util_close(tree
, fh
);
2935 torture_skip(torture
, "FS compression not supported\n");
2938 /* attempt get compression without READ_ATTR permission */
2939 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2941 (SEC_RIGHTS_FILE_READ
& ~(SEC_FILE_READ_ATTRIBUTE
2942 | SEC_STD_READ_CONTROL
2943 | SEC_FILE_READ_EA
)),
2944 FILE_ATTRIBUTE_NORMAL
);
2945 torture_assert(torture
, ok
, "setup compression file");
2947 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, fh
,
2949 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
2950 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_NONE
),
2951 "compression set after create");
2952 smb2_util_close(tree
, fh
);
2954 /* set compression without WRITE_ATTR permission should succeed */
2955 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2957 (SEC_RIGHTS_FILE_WRITE
& ~(SEC_FILE_WRITE_ATTRIBUTE
2959 | SEC_FILE_WRITE_EA
)),
2960 FILE_ATTRIBUTE_NORMAL
);
2961 torture_assert(torture
, ok
, "setup compression file");
2963 status
= test_ioctl_compress_set(torture
, tmp_ctx
, tree
, fh
,
2964 COMPRESSION_FORMAT_DEFAULT
);
2965 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_COMPRESSION");
2966 smb2_util_close(tree
, fh
);
2968 ok
= test_setup_open(torture
, tree
, tmp_ctx
,
2969 FNAME
, &fh
, SEC_RIGHTS_FILE_ALL
,
2970 FILE_ATTRIBUTE_NORMAL
);
2971 torture_assert(torture
, ok
, "setup compression file");
2973 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
2974 io
.generic
.in
.file
.handle
= fh
;
2975 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
2976 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FILE");
2978 torture_assert(torture
,
2979 (io
.all_info2
.out
.attrib
& FILE_ATTRIBUTE_COMPRESSED
),
2980 "incorrect compression attr");
2981 smb2_util_close(tree
, fh
);
2983 /* attempt get compression without READ_DATA permission */
2984 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
2986 (SEC_RIGHTS_FILE_READ
& ~SEC_FILE_READ_DATA
),
2987 FILE_ATTRIBUTE_NORMAL
);
2988 torture_assert(torture
, ok
, "setup compression file");
2990 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, fh
,
2992 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
2993 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_NONE
),
2994 "compression enabled after set");
2995 smb2_util_close(tree
, fh
);
2997 /* attempt get compression with only SYNCHRONIZE permission */
2998 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
3000 SEC_STD_SYNCHRONIZE
,
3001 FILE_ATTRIBUTE_NORMAL
);
3002 torture_assert(torture
, ok
, "setup compression file");
3004 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, fh
,
3006 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
3007 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_NONE
),
3008 "compression not enabled after set");
3009 smb2_util_close(tree
, fh
);
3011 /* attempt to set compression without WRITE_DATA permission */
3012 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
3014 (SEC_RIGHTS_FILE_WRITE
& (~SEC_FILE_WRITE_DATA
)),
3015 FILE_ATTRIBUTE_NORMAL
);
3016 torture_assert(torture
, ok
, "setup compression file");
3018 status
= test_ioctl_compress_set(torture
, tmp_ctx
, tree
, fh
,
3019 COMPRESSION_FORMAT_DEFAULT
);
3020 torture_assert_ntstatus_equal(torture
, status
,
3021 NT_STATUS_ACCESS_DENIED
,
3022 "FSCTL_SET_COMPRESSION permission");
3023 smb2_util_close(tree
, fh
);
3025 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
3027 (SEC_RIGHTS_FILE_WRITE
& (~SEC_FILE_WRITE_DATA
)),
3028 FILE_ATTRIBUTE_NORMAL
);
3029 torture_assert(torture
, ok
, "setup compression file");
3031 status
= test_ioctl_compress_set(torture
, tmp_ctx
, tree
, fh
,
3032 COMPRESSION_FORMAT_NONE
);
3033 torture_assert_ntstatus_equal(torture
, status
,
3034 NT_STATUS_ACCESS_DENIED
,
3035 "FSCTL_SET_COMPRESSION permission");
3036 smb2_util_close(tree
, fh
);
3038 talloc_free(tmp_ctx
);
3042 static bool test_ioctl_compress_notsup_get(struct torture_context
*torture
,
3043 struct smb2_tree
*tree
)
3045 struct smb2_handle fh
;
3047 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
3049 uint16_t compression_fmt
;
3051 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
3052 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
3053 FILE_ATTRIBUTE_NORMAL
);
3054 torture_assert(torture
, ok
, "setup compression file");
3056 /* skip if the server DOES support compression */
3057 status
= test_ioctl_compress_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
3059 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
3061 smb2_util_close(tree
, fh
);
3062 torture_skip(torture
, "FS compression supported\n");
3066 * Despite not supporting compression, we should get a successful
3067 * response indicating that the file is uncompressed - like WS2016.
3069 status
= test_ioctl_compress_get(torture
, tmp_ctx
, tree
, fh
,
3071 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_GET_COMPRESSION");
3073 torture_assert(torture
, (compression_fmt
== COMPRESSION_FORMAT_NONE
),
3074 "initial compression state not NONE");
3076 smb2_util_close(tree
, fh
);
3077 talloc_free(tmp_ctx
);
3081 static bool test_ioctl_compress_notsup_set(struct torture_context
*torture
,
3082 struct smb2_tree
*tree
)
3084 struct smb2_handle fh
;
3086 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
3089 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
3090 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
3091 FILE_ATTRIBUTE_NORMAL
);
3092 torture_assert(torture
, ok
, "setup compression file");
3094 /* skip if the server DOES support compression */
3095 status
= test_ioctl_compress_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
3097 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
3099 smb2_util_close(tree
, fh
);
3100 torture_skip(torture
, "FS compression supported\n");
3103 status
= test_ioctl_compress_set(torture
, tmp_ctx
, tree
, fh
,
3104 COMPRESSION_FORMAT_DEFAULT
);
3105 torture_assert_ntstatus_equal(torture
, status
,
3106 NT_STATUS_NOT_SUPPORTED
,
3107 "FSCTL_SET_COMPRESSION default");
3110 * Despite not supporting compression, we should get a successful
3111 * response for set(COMPRESSION_FORMAT_NONE) - like WS2016 ReFS.
3113 status
= test_ioctl_compress_set(torture
, tmp_ctx
, tree
, fh
,
3114 COMPRESSION_FORMAT_NONE
);
3115 torture_assert_ntstatus_ok(torture
, status
,
3116 "FSCTL_SET_COMPRESSION none");
3118 smb2_util_close(tree
, fh
);
3119 talloc_free(tmp_ctx
);
3124 basic testing of the SMB2 FSCTL_QUERY_NETWORK_INTERFACE_INFO ioctl
3126 static bool test_ioctl_network_interface_info(struct torture_context
*torture
,
3127 struct smb2_tree
*tree
)
3129 union smb_ioctl ioctl
;
3130 struct smb2_handle fh
;
3132 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
3133 struct fsctl_net_iface_info net_iface
;
3134 enum ndr_err_code ndr_ret
;
3137 caps
= smb2cli_conn_server_capabilities(tree
->session
->transport
->conn
);
3138 if (!(caps
& SMB2_CAP_MULTI_CHANNEL
)) {
3139 torture_skip(torture
, "server doesn't support SMB2_CAP_MULTI_CHANNEL\n");
3143 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
3144 fh
.data
[0] = UINT64_MAX
;
3145 fh
.data
[1] = UINT64_MAX
;
3146 ioctl
.smb2
.in
.file
.handle
= fh
;
3147 ioctl
.smb2
.in
.function
= FSCTL_QUERY_NETWORK_INTERFACE_INFO
;
3148 ioctl
.smb2
.in
.max_output_response
= 0x10000; /* Windows client sets this to 64KiB */
3149 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
3151 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
3152 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_QUERY_NETWORK_INTERFACE_INFO");
3154 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
, &net_iface
,
3155 (ndr_pull_flags_fn_t
)ndr_pull_fsctl_net_iface_info
);
3156 torture_assert_ndr_success(torture
, ndr_ret
,
3157 "ndr_pull_fsctl_net_iface_info");
3159 NDR_PRINT_DEBUG(fsctl_net_iface_info
, &net_iface
);
3161 talloc_free(tmp_ctx
);
3166 * Check whether all @fs_support_flags are set in the server's
3167 * RAW_QFS_ATTRIBUTE_INFORMATION FileSystemAttributes response.
3169 static NTSTATUS
test_ioctl_fs_supported(struct torture_context
*torture
,
3170 struct smb2_tree
*tree
,
3171 TALLOC_CTX
*mem_ctx
,
3172 struct smb2_handle
*fh
,
3173 uint64_t fs_support_flags
,
3177 union smb_fsinfo info
;
3180 info
.generic
.level
= RAW_QFS_ATTRIBUTE_INFORMATION
;
3181 info
.generic
.handle
= *fh
;
3182 status
= smb2_getinfo_fs(tree
, tree
, &info
);
3183 if (!NT_STATUS_IS_OK(status
)) {
3187 if ((info
.attribute_info
.out
.fs_attr
& fs_support_flags
)
3188 == fs_support_flags
) {
3193 return NT_STATUS_OK
;
3196 static NTSTATUS
test_ioctl_sparse_req(struct torture_context
*torture
,
3197 TALLOC_CTX
*mem_ctx
,
3198 struct smb2_tree
*tree
,
3199 struct smb2_handle fh
,
3202 union smb_ioctl ioctl
;
3207 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
3208 ioctl
.smb2
.in
.file
.handle
= fh
;
3209 ioctl
.smb2
.in
.function
= FSCTL_SET_SPARSE
;
3210 ioctl
.smb2
.in
.max_output_response
= 0;
3211 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
3212 set_sparse
= (set
? 0xFF : 0x0);
3213 ioctl
.smb2
.in
.out
.data
= &set_sparse
;
3214 ioctl
.smb2
.in
.out
.length
= sizeof(set_sparse
);
3216 status
= smb2_ioctl(tree
, mem_ctx
, &ioctl
.smb2
);
3220 static NTSTATUS
test_sparse_get(struct torture_context
*torture
,
3221 TALLOC_CTX
*mem_ctx
,
3222 struct smb2_tree
*tree
,
3223 struct smb2_handle fh
,
3226 union smb_fileinfo io
;
3230 io
.generic
.level
= RAW_FILEINFO_BASIC_INFORMATION
;
3231 io
.generic
.in
.file
.handle
= fh
;
3232 status
= smb2_getinfo_file(tree
, mem_ctx
, &io
);
3233 if (!NT_STATUS_IS_OK(status
)) {
3236 *_is_sparse
= !!(io
.basic_info
.out
.attrib
& FILE_ATTRIBUTE_SPARSE
);
3242 * Manually test setting and clearing sparse flag. Intended for file system
3243 * specific tests to toggle the flag through SMB and check the status in the
3246 bool test_ioctl_set_sparse(struct torture_context
*tctx
)
3248 bool set
, ret
= true;
3249 const char *filename
= NULL
;
3250 struct smb2_create create
= { };
3251 struct smb2_tree
*tree
= NULL
;
3254 set
= torture_setting_bool(tctx
, "set_sparse", true);
3255 filename
= torture_setting_string(tctx
, "filename", NULL
);
3257 if (filename
== NULL
) {
3258 torture_fail(tctx
, "Need to provide filename through "
3259 "--option=torture:filename=testfile\n");
3263 if (!torture_smb2_connection(tctx
, &tree
)) {
3264 torture_comment(tctx
, "Initializing smb2 connection failed.\n");
3268 create
.in
.desired_access
= SEC_RIGHTS_DIR_ALL
;
3269 create
.in
.create_options
= 0;
3270 create
.in
.file_attributes
= FILE_ATTRIBUTE_NORMAL
;
3271 create
.in
.share_access
= NTCREATEX_SHARE_ACCESS_READ
|
3272 NTCREATEX_SHARE_ACCESS_WRITE
|
3273 NTCREATEX_SHARE_ACCESS_DELETE
;
3274 create
.in
.create_disposition
= NTCREATEX_DISP_OPEN_IF
;
3275 create
.in
.fname
= filename
;
3277 status
= smb2_create(tree
, tctx
, &create
);
3278 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
,
3279 "CREATE failed.\n");
3281 status
= test_ioctl_sparse_req(tctx
, tctx
, tree
,
3282 create
.out
.file
.handle
, set
);
3283 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
,
3284 "FSCTL_SET_SPARSE failed.\n");
3290 static bool test_ioctl_sparse_file_flag(struct torture_context
*torture
,
3291 struct smb2_tree
*tree
)
3293 struct smb2_handle fh
;
3294 union smb_fileinfo io
;
3296 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
3300 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
3301 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
3302 FILE_ATTRIBUTE_NORMAL
);
3303 torture_assert(torture
, ok
, "setup file");
3305 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
3306 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
3307 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
3309 smb2_util_close(tree
, fh
);
3310 torture_skip(torture
, "Sparse files not supported\n");
3314 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
3315 io
.generic
.in
.file
.handle
= fh
;
3316 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
3317 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FILE");
3319 torture_assert(torture
,
3320 ((io
.all_info2
.out
.attrib
& FILE_ATTRIBUTE_SPARSE
) == 0),
3321 "sparse attr before set");
3323 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, true);
3324 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
3326 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
3327 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
3328 torture_assert(torture
, is_sparse
, "no sparse attr after set");
3330 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, false);
3331 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
3333 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
3334 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
3335 torture_assert(torture
, !is_sparse
, "sparse attr after unset");
3337 smb2_util_close(tree
, fh
);
3338 talloc_free(tmp_ctx
);
3342 static bool test_ioctl_sparse_file_attr(struct torture_context
*torture
,
3343 struct smb2_tree
*tree
)
3345 struct smb2_handle fh
;
3347 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
3351 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
3352 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
3353 (FILE_ATTRIBUTE_NORMAL
| FILE_ATTRIBUTE_SPARSE
));
3354 torture_assert(torture
, ok
, "setup file");
3356 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
3357 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
3358 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
3360 smb2_util_close(tree
, fh
);
3361 torture_skip(torture
, "Sparse files not supported\n");
3364 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
3365 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
3366 torture_assert(torture
, !is_sparse
, "sparse attr on open");
3368 smb2_util_close(tree
, fh
);
3369 talloc_free(tmp_ctx
);
3373 static bool test_ioctl_sparse_dir_flag(struct torture_context
*torture
,
3374 struct smb2_tree
*tree
)
3376 struct smb2_handle dirh
;
3378 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
3381 smb2_deltree(tree
, DNAME
);
3382 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
3383 DNAME
, &dirh
, 0, SEC_RIGHTS_FILE_ALL
,
3384 FILE_ATTRIBUTE_DIRECTORY
);
3385 torture_assert(torture
, ok
, "setup sparse directory");
3387 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &dirh
,
3388 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
3389 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
3391 smb2_util_close(tree
, dirh
);
3392 smb2_deltree(tree
, DNAME
);
3393 torture_skip(torture
, "Sparse files not supported\n");
3396 /* set sparse dir should fail, check for 2k12 & 2k8 response */
3397 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, dirh
, true);
3398 torture_assert_ntstatus_equal(torture
, status
,
3399 NT_STATUS_INVALID_PARAMETER
,
3400 "dir FSCTL_SET_SPARSE status");
3402 smb2_util_close(tree
, dirh
);
3403 smb2_deltree(tree
, DNAME
);
3404 talloc_free(tmp_ctx
);
3409 * FSCTL_SET_SPARSE can be sent with (already tested) or without a SetSparse
3410 * buffer to indicate whether the flag should be set or cleared. When sent
3411 * without a buffer, it must be handled as if SetSparse=TRUE.
3413 static bool test_ioctl_sparse_set_nobuf(struct torture_context
*torture
,
3414 struct smb2_tree
*tree
)
3416 struct smb2_handle fh
;
3417 union smb_ioctl ioctl
;
3419 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
3423 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
3424 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
3425 FILE_ATTRIBUTE_NORMAL
);
3426 torture_assert(torture
, ok
, "setup file");
3428 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
3429 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
3430 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
3432 smb2_util_close(tree
, fh
);
3433 torture_skip(torture
, "Sparse files not supported\n");
3436 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
3437 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
3438 torture_assert(torture
, !is_sparse
, "sparse attr before set");
3441 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
3442 ioctl
.smb2
.in
.file
.handle
= fh
;
3443 ioctl
.smb2
.in
.function
= FSCTL_SET_SPARSE
;
3444 ioctl
.smb2
.in
.max_output_response
= 0;
3445 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
3446 /* ioctl.smb2.in.out is zeroed, no SetSparse buffer */
3448 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
3449 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
3451 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
3452 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
3453 torture_assert(torture
, is_sparse
, "no sparse attr after set");
3455 /* second non-SetSparse request shouldn't toggle sparse */
3457 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
3458 ioctl
.smb2
.in
.file
.handle
= fh
;
3459 ioctl
.smb2
.in
.function
= FSCTL_SET_SPARSE
;
3460 ioctl
.smb2
.in
.max_output_response
= 0;
3461 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
3463 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
3464 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
3466 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
3467 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
3468 torture_assert(torture
, is_sparse
, "no sparse attr after 2nd set");
3470 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, false);
3471 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
3473 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
3474 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
3475 torture_assert(torture
, !is_sparse
, "sparse attr after unset");
3477 smb2_util_close(tree
, fh
);
3478 talloc_free(tmp_ctx
);
3482 static bool test_ioctl_sparse_set_oversize(struct torture_context
*torture
,
3483 struct smb2_tree
*tree
)
3485 struct smb2_handle fh
;
3486 union smb_ioctl ioctl
;
3488 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
3493 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
3494 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
3495 FILE_ATTRIBUTE_NORMAL
);
3496 torture_assert(torture
, ok
, "setup file");
3498 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
3499 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
3500 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
3502 smb2_util_close(tree
, fh
);
3503 torture_skip(torture
, "Sparse files not supported\n");
3506 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
3507 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
3508 torture_assert(torture
, !is_sparse
, "sparse attr before set");
3511 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
3512 ioctl
.smb2
.in
.file
.handle
= fh
;
3513 ioctl
.smb2
.in
.function
= FSCTL_SET_SPARSE
;
3514 ioctl
.smb2
.in
.max_output_response
= 0;
3515 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
3518 * Attach a request buffer larger than FILE_SET_SPARSE_BUFFER
3519 * Windows still successfully processes the request.
3522 buf
[0] = 0xFF; /* attempt to set sparse */
3523 ioctl
.smb2
.in
.out
.data
= buf
;
3524 ioctl
.smb2
.in
.out
.length
= ARRAY_SIZE(buf
);
3526 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
3527 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
3529 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
3530 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
3531 torture_assert(torture
, is_sparse
, "no sparse attr after set");
3534 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
3535 ioctl
.smb2
.in
.file
.handle
= fh
;
3536 ioctl
.smb2
.in
.function
= FSCTL_SET_SPARSE
;
3537 ioctl
.smb2
.in
.max_output_response
= 0;
3538 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
3540 ZERO_ARRAY(buf
); /* clear sparse */
3541 ioctl
.smb2
.in
.out
.data
= buf
;
3542 ioctl
.smb2
.in
.out
.length
= ARRAY_SIZE(buf
);
3544 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
3545 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
3547 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
3548 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
3549 torture_assert(torture
, !is_sparse
, "sparse attr after clear");
3551 smb2_util_close(tree
, fh
);
3552 talloc_free(tmp_ctx
);
3556 static NTSTATUS
test_ioctl_qar_req(struct torture_context
*torture
,
3557 TALLOC_CTX
*mem_ctx
,
3558 struct smb2_tree
*tree
,
3559 struct smb2_handle fh
,
3562 struct file_alloced_range_buf
**_rsp
,
3563 uint64_t *_rsp_count
)
3565 union smb_ioctl ioctl
;
3567 enum ndr_err_code ndr_ret
;
3568 struct file_alloced_range_buf far_buf
;
3569 struct file_alloced_range_buf
*far_rsp
= NULL
;
3570 uint64_t far_count
= 0;
3572 TALLOC_CTX
*tmp_ctx
= talloc_new(mem_ctx
);
3573 if (tmp_ctx
== NULL
) {
3574 return NT_STATUS_NO_MEMORY
;
3578 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
3579 ioctl
.smb2
.in
.file
.handle
= fh
;
3580 ioctl
.smb2
.in
.function
= FSCTL_QUERY_ALLOCATED_RANGES
;
3581 ioctl
.smb2
.in
.max_output_response
= 1024;
3582 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
3584 far_buf
.file_off
= req_off
;
3585 far_buf
.len
= req_len
;
3587 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
3589 (ndr_push_flags_fn_t
)ndr_push_file_alloced_range_buf
);
3590 if (ndr_ret
!= NDR_ERR_SUCCESS
) {
3591 status
= NT_STATUS_UNSUCCESSFUL
;
3595 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
3596 if (!NT_STATUS_IS_OK(status
)) {
3600 if (ioctl
.smb2
.out
.out
.length
== 0) {
3604 if ((ioctl
.smb2
.out
.out
.length
% sizeof(far_buf
)) != 0) {
3605 torture_comment(torture
, "invalid qry_alloced rsp len: %zd:",
3606 ioctl
.smb2
.out
.out
.length
);
3607 status
= NT_STATUS_INVALID_VIEW_SIZE
;
3611 far_count
= (ioctl
.smb2
.out
.out
.length
/ sizeof(far_buf
));
3612 far_rsp
= talloc_array(mem_ctx
, struct file_alloced_range_buf
,
3614 if (far_rsp
== NULL
) {
3615 status
= NT_STATUS_NO_MEMORY
;
3619 for (i
= 0; i
< far_count
; i
++) {
3620 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
3622 (ndr_pull_flags_fn_t
)ndr_pull_file_alloced_range_buf
);
3623 if (ndr_ret
!= NDR_ERR_SUCCESS
) {
3624 status
= NT_STATUS_UNSUCCESSFUL
;
3627 /* move to next buffer */
3628 ioctl
.smb2
.out
.out
.data
+= sizeof(far_buf
);
3629 ioctl
.smb2
.out
.out
.length
-= sizeof(far_buf
);
3634 *_rsp_count
= far_count
;
3635 status
= NT_STATUS_OK
;
3637 talloc_free(tmp_ctx
);
3641 static bool test_ioctl_sparse_qar(struct torture_context
*torture
,
3642 struct smb2_tree
*tree
)
3644 struct smb2_handle fh
;
3646 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
3649 struct file_alloced_range_buf
*far_rsp
= NULL
;
3650 uint64_t far_count
= 0;
3652 /* zero length file, shouldn't have any ranges */
3653 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
3654 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
3655 FILE_ATTRIBUTE_NORMAL
);
3656 torture_assert(torture
, ok
, "setup file");
3658 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
3659 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
3660 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
3662 smb2_util_close(tree
, fh
);
3663 torture_skip(torture
, "Sparse files not supported\n");
3666 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
3667 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
3668 torture_assert(torture
, !is_sparse
, "sparse attr before set");
3670 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
3675 torture_assert_ntstatus_ok(torture
, status
,
3676 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3677 torture_assert_u64_equal(torture
, far_count
, 0,
3678 "unexpected response len");
3680 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
3685 torture_assert_ntstatus_ok(torture
, status
,
3686 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3687 torture_assert_u64_equal(torture
, far_count
, 0,
3688 "unexpected response len");
3690 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, true);
3691 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
3693 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
3694 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
3695 torture_assert(torture
, is_sparse
, "no sparse attr after set");
3697 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
3702 torture_assert_ntstatus_ok(torture
, status
,
3703 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3704 torture_assert_u64_equal(torture
, far_count
, 0,
3705 "unexpected response len");
3707 /* write into the (now) sparse file at 4k offset */
3708 ok
= write_pattern(torture
, tree
, tmp_ctx
, fh
,
3711 4096); /* pattern offset */
3712 torture_assert(torture
, ok
, "write pattern");
3715 * Query range before write off. Whether it's allocated or not is FS
3716 * dependent. NTFS deallocates chunks in 64K increments, but others
3717 * (e.g. XFS, Btrfs, etc.) may deallocate 4K chunks.
3719 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
3724 torture_assert_ntstatus_ok(torture
, status
,
3725 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3726 if (far_count
== 0) {
3727 torture_comment(torture
, "FS deallocated 4K chunk\n");
3729 /* expect fully allocated */
3730 torture_assert_u64_equal(torture
, far_count
, 1,
3731 "unexpected response len");
3732 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0, "far offset");
3733 torture_assert_u64_equal(torture
, far_rsp
[0].len
, 4096, "far len");
3737 * Query range before and past write, it should be allocated up to the
3740 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
3745 torture_assert_ntstatus_ok(torture
, status
,
3746 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3747 torture_assert_u64_equal(torture
, far_count
, 1,
3748 "unexpected response len");
3750 if (far_rsp
[0].file_off
== 4096) {
3751 /* 4K chunk unallocated */
3752 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 4096, "far offset");
3753 torture_assert_u64_equal(torture
, far_rsp
[0].len
, 1024, "far len");
3755 /* expect fully allocated */
3756 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0, "far offset");
3757 torture_assert_u64_equal(torture
, far_rsp
[0].len
, 5120, "far len");
3760 smb2_util_close(tree
, fh
);
3761 talloc_free(tmp_ctx
);
3765 static bool test_ioctl_sparse_qar_malformed(struct torture_context
*torture
,
3766 struct smb2_tree
*tree
)
3768 struct smb2_handle fh
;
3769 union smb_ioctl ioctl
;
3770 struct file_alloced_range_buf far_buf
;
3772 enum ndr_err_code ndr_ret
;
3773 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
3777 /* zero length file, shouldn't have any ranges */
3778 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
3779 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
3780 FILE_ATTRIBUTE_NORMAL
);
3781 torture_assert(torture
, ok
, "setup file");
3783 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
3784 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
3785 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
3787 smb2_util_close(tree
, fh
);
3788 torture_skip(torture
, "Sparse files not supported\n");
3791 /* no allocated ranges, no space for range response, should pass */
3793 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
3794 ioctl
.smb2
.in
.file
.handle
= fh
;
3795 ioctl
.smb2
.in
.function
= FSCTL_QUERY_ALLOCATED_RANGES
;
3796 ioctl
.smb2
.in
.max_output_response
= 0;
3797 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
3799 far_buf
.file_off
= 0;
3801 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
3803 (ndr_push_flags_fn_t
)ndr_push_file_alloced_range_buf
);
3804 torture_assert_ndr_success(torture
, ndr_ret
, "push far ndr buf");
3806 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
3807 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_QUERY_ALLOCATED_RANGES");
3809 /* write into the file at 4k offset */
3810 ok
= write_pattern(torture
, tree
, tmp_ctx
, fh
,
3813 0); /* pattern offset */
3814 torture_assert(torture
, ok
, "write pattern");
3816 /* allocated range, no space for range response, should fail */
3817 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
3818 torture_assert_ntstatus_equal(torture
, status
,
3819 NT_STATUS_BUFFER_TOO_SMALL
, "qar no space");
3821 /* oversize (2x) file_alloced_range_buf in request, should pass */
3822 ioctl
.smb2
.in
.max_output_response
= 1024;
3823 old_len
= ioctl
.smb2
.in
.out
.length
;
3824 ok
= data_blob_realloc(tmp_ctx
, &ioctl
.smb2
.in
.out
,
3825 (ioctl
.smb2
.in
.out
.length
* 2));
3826 torture_assert(torture
, ok
, "2x data buffer");
3827 memcpy(ioctl
.smb2
.in
.out
.data
+ old_len
, ioctl
.smb2
.in
.out
.data
,
3829 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
3830 torture_assert_ntstatus_ok(torture
, status
, "qar too big");
3832 /* no file_alloced_range_buf in request, should fail */
3833 data_blob_free(&ioctl
.smb2
.in
.out
);
3834 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
3835 torture_assert_ntstatus_equal(torture
, status
,
3836 NT_STATUS_INVALID_PARAMETER
, "qar empty");
3841 static bool test_ioctl_sparse_qar_truncated(struct torture_context
*torture
,
3842 struct smb2_tree
*tree
)
3844 struct smb2_handle fh
;
3845 union smb_ioctl ioctl
;
3846 struct file_alloced_range_buf far_buf
;
3848 enum ndr_err_code ndr_ret
;
3849 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
3851 struct file_alloced_range_buf far_rsp
;
3853 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
3854 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
3855 FILE_ATTRIBUTE_NORMAL
);
3856 torture_assert(torture
, ok
, "setup file");
3858 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
3859 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
3860 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
3862 smb2_util_close(tree
, fh
);
3863 torture_skip(torture
, "Sparse files not supported\n");
3866 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, true);
3867 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
3870 * Write 0 and 1M offsets as (hopefully) two separate extents.
3871 * XXX this test assumes that these ranges will be recorded as separate
3872 * FSCTL_QUERY_ALLOCATED_RANGES extents, which isn't strictly required:
3873 * the spec basically says the FS can do what it wants as long as
3874 * non-zeroed data ranges aren't reported as sparse holes.
3876 ok
= write_pattern(torture
, tree
, tmp_ctx
, fh
,
3879 0); /* pattern offset */
3880 torture_assert(torture
, ok
, "write pattern");
3881 ok
= write_pattern(torture
, tree
, tmp_ctx
, fh
,
3882 1024 * 1024, /* off */
3884 0); /* pattern offset */
3885 torture_assert(torture
, ok
, "write pattern");
3887 /* qar max output enough to carry one range, should be truncated */
3889 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
3890 ioctl
.smb2
.in
.file
.handle
= fh
;
3891 ioctl
.smb2
.in
.function
= FSCTL_QUERY_ALLOCATED_RANGES
;
3892 ioctl
.smb2
.in
.max_output_response
= sizeof(struct file_alloced_range_buf
);
3893 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
3895 far_buf
.file_off
= 0;
3896 far_buf
.len
= 2048 * 1024;
3897 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
3899 (ndr_push_flags_fn_t
)ndr_push_file_alloced_range_buf
);
3900 torture_assert_ndr_success(torture
, ndr_ret
, "push far ndr buf");
3902 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
3903 torture_assert_ntstatus_equal(torture
, status
,
3904 STATUS_BUFFER_OVERFLOW
, "qar truncated");
3905 torture_assert_size_equal(torture
,
3906 ioctl
.smb2
.out
.out
.length
, sizeof(far_buf
),
3908 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
3910 (ndr_pull_flags_fn_t
)ndr_pull_file_alloced_range_buf
);
3911 torture_assert_ndr_success(torture
, ndr_ret
, "pull far range");
3912 torture_assert_u64_equal(torture
, far_rsp
.file_off
, 0, "far offset");
3913 /* length depends on allocation behaviour of FS, so allow range */
3914 torture_assert(torture
, far_rsp
.len
>= 1024, "far len");
3916 /* qar max output for just under 2 ranges, should be truncated */
3918 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
3919 ioctl
.smb2
.in
.file
.handle
= fh
;
3920 ioctl
.smb2
.in
.function
= FSCTL_QUERY_ALLOCATED_RANGES
;
3921 ioctl
.smb2
.in
.max_output_response
= 31;
3922 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
3924 far_buf
.file_off
= 0;
3925 far_buf
.len
= 2048 * 1024;
3926 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
3928 (ndr_push_flags_fn_t
)ndr_push_file_alloced_range_buf
);
3929 torture_assert_ndr_success(torture
, ndr_ret
, "push far ndr buf");
3931 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
3932 torture_assert_ntstatus_equal(torture
, status
,
3933 STATUS_BUFFER_OVERFLOW
, "qar truncated");
3934 torture_assert_size_equal(torture
,
3935 ioctl
.smb2
.out
.out
.length
, sizeof(far_buf
),
3937 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
3939 (ndr_pull_flags_fn_t
)ndr_pull_file_alloced_range_buf
);
3940 torture_assert_ndr_success(torture
, ndr_ret
, "pull far range");
3941 torture_assert_u64_equal(torture
, far_rsp
.file_off
, 0, "far offset");
3942 torture_assert(torture
, far_rsp
.len
>= 1024, "far len");
3944 /* qar max output for 2 ranges, should pass */
3946 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
3947 ioctl
.smb2
.in
.file
.handle
= fh
;
3948 ioctl
.smb2
.in
.function
= FSCTL_QUERY_ALLOCATED_RANGES
;
3949 ioctl
.smb2
.in
.max_output_response
= 32;
3950 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
3952 far_buf
.file_off
= 0;
3953 far_buf
.len
= 2048 * 1024;
3954 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
3956 (ndr_push_flags_fn_t
)ndr_push_file_alloced_range_buf
);
3957 torture_assert_ndr_success(torture
, ndr_ret
, "push far ndr buf");
3959 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
3960 torture_assert_ntstatus_ok(torture
, status
, "qar non-truncated");
3961 torture_assert_size_equal(torture
,
3962 ioctl
.smb2
.out
.out
.length
,
3963 2 * sizeof(far_buf
), "qar outlen");
3964 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
3966 (ndr_pull_flags_fn_t
)ndr_pull_file_alloced_range_buf
);
3967 torture_assert_ndr_success(torture
, ndr_ret
, "pull far range");
3968 torture_assert_u64_equal(torture
, far_rsp
.file_off
, 0, "far offset");
3969 torture_assert(torture
, far_rsp
.len
>= 1024, "far len");
3970 /* move to next buffer */
3971 ioctl
.smb2
.out
.out
.data
+= sizeof(far_buf
);
3972 ioctl
.smb2
.out
.out
.length
-= sizeof(far_buf
);
3973 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
3975 (ndr_pull_flags_fn_t
)ndr_pull_file_alloced_range_buf
);
3976 torture_assert_ndr_success(torture
, ndr_ret
, "pull far range");
3977 torture_assert_u64_equal(torture
, far_rsp
.file_off
, 1024 * 1024,
3979 torture_assert(torture
, far_rsp
.len
>= 1024, "far len");
3981 smb2_util_close(tree
, fh
);
3982 talloc_free(tmp_ctx
);
3986 bool test_ioctl_alternate_data_stream(struct torture_context
*tctx
)
3989 const char *fname
= DNAME
"\\test_stream_ioctl_dir";
3990 const char *sname
= DNAME
"\\test_stream_ioctl_dir:stream";
3992 struct smb2_create create
= {};
3993 struct smb2_tree
*tree
= NULL
;
3994 struct smb2_handle h1
= {{0}};
3995 union smb_ioctl ioctl
;
3997 if (!torture_smb2_connection(tctx
, &tree
)) {
3998 torture_comment(tctx
, "Initializing smb2 connection failed.\n");
4002 smb2_deltree(tree
, DNAME
);
4004 status
= torture_smb2_testdir(tree
, DNAME
, &h1
);
4005 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
,
4006 "torture_smb2_testdir failed\n");
4008 status
= smb2_util_close(tree
, h1
);
4009 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
,
4010 "smb2_util_close failed\n");
4011 create
= (struct smb2_create
) {
4012 .in
.desired_access
= SEC_FILE_ALL
,
4013 .in
.share_access
= NTCREATEX_SHARE_ACCESS_MASK
,
4014 .in
.file_attributes
= FILE_ATTRIBUTE_HIDDEN
,
4015 .in
.create_disposition
= NTCREATEX_DISP_CREATE
,
4016 .in
.impersonation_level
= SMB2_IMPERSONATION_IMPERSONATION
,
4020 status
= smb2_create(tree
, tctx
, &create
);
4021 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
,
4022 "smb2_create failed\n");
4024 h1
= create
.out
.file
.handle
;
4025 status
= smb2_util_close(tree
, h1
);
4026 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
,
4027 "smb2_util_close failed\n");
4029 create
= (struct smb2_create
) {
4030 .in
.desired_access
= SEC_FILE_ALL
,
4031 .in
.share_access
= NTCREATEX_SHARE_ACCESS_MASK
,
4032 .in
.file_attributes
= FILE_ATTRIBUTE_NORMAL
,
4033 .in
.create_disposition
= NTCREATEX_DISP_CREATE
,
4034 .in
.impersonation_level
= SMB2_IMPERSONATION_IMPERSONATION
,
4037 status
= smb2_create(tree
, tctx
, &create
);
4038 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
,
4039 "smb2_create failed\n");
4040 h1
= create
.out
.file
.handle
;
4043 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
4044 ioctl
.smb2
.in
.file
.handle
= h1
;
4045 ioctl
.smb2
.in
.function
= FSCTL_CREATE_OR_GET_OBJECT_ID
,
4046 ioctl
.smb2
.in
.max_output_response
= 64;
4047 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
4048 status
= smb2_ioctl(tree
, tctx
, &ioctl
.smb2
);
4049 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
,
4050 "smb2_ioctl failed\n");
4055 smb2_util_close(tree
, h1
);
4056 smb2_deltree(tree
, DNAME
);
4061 * 2.3.57 FSCTL_SET_ZERO_DATA Request
4063 * How an implementation zeros data within a file is implementation-dependent.
4064 * A file system MAY choose to deallocate regions of disk space that have been
4067 * ... NTFS might deallocate disk space in the file if the file is stored on an
4068 * NTFS volume, and the file is sparse or compressed. It will free any allocated
4069 * space in chunks of 64 kilobytes that begin at an offset that is a multiple of
4070 * 64 kilobytes. Other bytes in the file (prior to the first freed 64-kilobyte
4071 * chunk and after the last freed 64-kilobyte chunk) will be zeroed but not
4074 static NTSTATUS
test_ioctl_zdata_req(struct torture_context
*torture
,
4075 TALLOC_CTX
*mem_ctx
,
4076 struct smb2_tree
*tree
,
4077 struct smb2_handle fh
,
4079 int64_t beyond_final_zero
)
4081 union smb_ioctl ioctl
;
4083 enum ndr_err_code ndr_ret
;
4084 struct file_zero_data_info zdata_info
;
4085 TALLOC_CTX
*tmp_ctx
= talloc_new(mem_ctx
);
4086 if (tmp_ctx
== NULL
) {
4087 return NT_STATUS_NO_MEMORY
;
4091 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
4092 ioctl
.smb2
.in
.file
.handle
= fh
;
4093 ioctl
.smb2
.in
.function
= FSCTL_SET_ZERO_DATA
;
4094 ioctl
.smb2
.in
.max_output_response
= 0;
4095 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
4097 zdata_info
.file_off
= off
;
4098 zdata_info
.beyond_final_zero
= beyond_final_zero
;
4100 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
4102 (ndr_push_flags_fn_t
)ndr_push_file_zero_data_info
);
4103 if (ndr_ret
!= NDR_ERR_SUCCESS
) {
4104 status
= NT_STATUS_UNSUCCESSFUL
;
4108 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
4109 if (!NT_STATUS_IS_OK(status
)) {
4113 status
= NT_STATUS_OK
;
4115 talloc_free(tmp_ctx
);
4119 bool test_ioctl_zero_data(struct torture_context
*tctx
)
4122 int offset
, beyond_final_zero
;
4123 const char *filename
;
4125 struct smb2_create create
= { };
4126 struct smb2_tree
*tree
= NULL
;
4128 offset
= torture_setting_int(tctx
, "offset", -1);
4131 torture_fail(tctx
, "Need to provide non-negative offset "
4132 "through --option=torture:offset=NNN\n");
4136 beyond_final_zero
= torture_setting_int(tctx
, "beyond_final_zero",
4138 if (beyond_final_zero
< 0) {
4139 torture_fail(tctx
, "Need to provide non-negative "
4140 "'beyond final zero' through "
4141 "--option=torture:beyond_final_zero=NNN\n");
4144 filename
= torture_setting_string(tctx
, "filename", NULL
);
4145 if (filename
== NULL
) {
4146 torture_fail(tctx
, "Need to provide filename through "
4147 "--option=torture:filename=testfile\n");
4151 if (!torture_smb2_connection(tctx
, &tree
)) {
4152 torture_comment(tctx
, "Initializing smb2 connection failed.\n");
4156 create
.in
.desired_access
= SEC_RIGHTS_DIR_ALL
;
4157 create
.in
.create_options
= 0;
4158 create
.in
.file_attributes
= FILE_ATTRIBUTE_NORMAL
;
4159 create
.in
.share_access
= NTCREATEX_SHARE_ACCESS_READ
|
4160 NTCREATEX_SHARE_ACCESS_WRITE
|
4161 NTCREATEX_SHARE_ACCESS_DELETE
;
4162 create
.in
.create_disposition
= NTCREATEX_DISP_OPEN
;
4163 create
.in
.fname
= filename
;
4165 status
= smb2_create(tree
, tctx
, &create
);
4166 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
,
4167 "CREATE failed.\n");
4169 status
= test_ioctl_zdata_req(tctx
, tctx
, tree
,
4170 create
.out
.file
.handle
,
4173 torture_assert_ntstatus_ok_goto(tctx
,
4177 "FSCTL_SET_ZERO_DATA failed.\n");
4183 static bool test_ioctl_sparse_punch(struct torture_context
*torture
,
4184 struct smb2_tree
*tree
)
4186 struct smb2_handle fh
;
4188 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
4191 struct file_alloced_range_buf
*far_rsp
= NULL
;
4192 uint64_t far_count
= 0;
4194 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
4195 FNAME
, &fh
, 4096, SEC_RIGHTS_FILE_ALL
,
4196 FILE_ATTRIBUTE_NORMAL
);
4197 torture_assert(torture
, ok
, "setup file");
4199 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
4200 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
4201 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
4203 smb2_util_close(tree
, fh
);
4204 torture_skip(torture
, "Sparse files not supported\n");
4207 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
4208 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
4209 torture_assert(torture
, !is_sparse
, "sparse attr before set");
4211 /* zero (hole-punch) the data, without sparse flag */
4212 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
4214 4096); /* beyond_final_zero */
4215 torture_assert_ntstatus_ok(torture
, status
, "zero_data");
4217 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
4222 torture_assert_ntstatus_ok(torture
, status
,
4223 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4224 torture_assert_u64_equal(torture
, far_count
, 1,
4225 "unexpected response len");
4227 /* expect fully allocated */
4228 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0,
4229 "unexpected far off");
4230 torture_assert_u64_equal(torture
, far_rsp
[0].len
, 4096,
4231 "unexpected far len");
4232 /* check that the data is now zeroed */
4233 ok
= check_zero(torture
, tree
, tmp_ctx
, fh
, 0, 4096);
4234 torture_assert(torture
, ok
, "non-sparse zeroed range");
4237 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, true);
4238 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
4240 /* still fully allocated on NTFS, see note below for Samba */
4241 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
4246 torture_assert_ntstatus_ok(torture
, status
,
4247 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4249 * FS specific: Samba uses PUNCH_HOLE to zero the range, and
4250 * subsequently uses fallocate() to allocate the punched range if the
4251 * file is marked non-sparse and "strict allocate" is enabled. In both
4252 * cases, the zeroed range will not be detected by SEEK_DATA, so the
4253 * range won't be present in QAR responses until the file is marked
4256 if (far_count
== 0) {
4257 torture_comment(torture
, "non-sparse zeroed range disappeared "
4258 "after marking sparse\n");
4260 /* NTFS: range remains fully allocated */
4261 torture_assert_u64_equal(torture
, far_count
, 1,
4262 "unexpected response len");
4263 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0,
4264 "unexpected far off");
4265 torture_assert_u64_equal(torture
, far_rsp
[0].len
, 4096,
4266 "unexpected far len");
4269 /* zero (hole-punch) the data, _with_ sparse flag */
4270 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
4272 4096); /* beyond_final_zero */
4273 torture_assert_ntstatus_ok(torture
, status
, "zero_data");
4275 /* the range should no longer be alloced */
4276 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
4281 torture_assert_ntstatus_ok(torture
, status
,
4282 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4283 torture_assert_u64_equal(torture
, far_count
, 0,
4284 "unexpected response len");
4286 ok
= check_zero(torture
, tree
, tmp_ctx
, fh
, 0, 4096);
4287 torture_assert(torture
, ok
, "sparse zeroed range");
4289 /* remove sparse flag, this should "unsparse" the zeroed range */
4290 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, false);
4291 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
4293 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
4298 torture_assert_ntstatus_ok(torture
, status
,
4299 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4300 torture_assert_u64_equal(torture
, far_count
, 1,
4301 "unexpected response len");
4302 /* expect fully allocated */
4303 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0,
4304 "unexpected far off");
4305 torture_assert_u64_equal(torture
, far_rsp
[0].len
, 4096,
4306 "unexpected far len");
4308 ok
= check_zero(torture
, tree
, tmp_ctx
, fh
, 0, 4096);
4309 torture_assert(torture
, ok
, "sparse zeroed range");
4311 smb2_util_close(tree
, fh
);
4312 talloc_free(tmp_ctx
);
4317 * Find the point at which a zeroed range in a sparse file is deallocated by the
4318 * underlying filesystem. NTFS on Windows Server 2012 deallocates chunks in 64k
4319 * increments. Also check whether zeroed neighbours are merged for deallocation.
4321 static bool test_ioctl_sparse_hole_dealloc(struct torture_context
*torture
,
4322 struct smb2_tree
*tree
)
4324 struct smb2_handle fh
;
4326 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
4330 uint64_t dealloc_chunk_len
= 0;
4331 struct file_alloced_range_buf
*far_rsp
= NULL
;
4332 uint64_t far_count
= 0;
4334 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
4335 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
4336 FILE_ATTRIBUTE_NORMAL
);
4337 torture_assert(torture
, ok
, "setup file 1");
4339 /* check for FS sparse file */
4340 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
4341 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
4342 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
4344 smb2_util_close(tree
, fh
);
4345 torture_skip(torture
, "Sparse files not supported\n");
4349 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, true);
4350 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
4352 file_size
= 1024 * 1024;
4354 ok
= write_pattern(torture
, tree
, tmp_ctx
, fh
,
4356 file_size
, /* len */
4357 0); /* pattern offset */
4358 torture_assert(torture
, ok
, "write pattern");
4360 /* check allocated ranges, should be fully allocated */
4361 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
4363 file_size
, /* len */
4366 torture_assert_ntstatus_ok(torture
, status
,
4367 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4368 torture_assert_u64_equal(torture
, far_count
, 1,
4369 "unexpected response len");
4370 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0,
4371 "unexpected far off");
4372 torture_assert_u64_equal(torture
, far_rsp
[0].len
, file_size
,
4373 "unexpected far len");
4375 /* punch holes in sizes of 1k increments */
4376 for (hlen
= 0; hlen
<= file_size
; hlen
+= 4096) {
4378 /* punch a hole from zero to the current increment */
4379 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
4381 hlen
); /* beyond_final_zero */
4382 torture_assert_ntstatus_ok(torture
, status
, "zero_data");
4384 /* ensure hole is zeroed, and pattern is consistent */
4385 ok
= check_zero(torture
, tree
, tmp_ctx
, fh
, 0, hlen
);
4386 torture_assert(torture
, ok
, "sparse zeroed range");
4388 ok
= check_pattern(torture
, tree
, tmp_ctx
, fh
, hlen
,
4389 file_size
- hlen
, hlen
);
4390 torture_assert(torture
, ok
, "allocated pattern range");
4392 /* Check allocated ranges, hole might have been deallocated */
4393 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
4395 file_size
, /* len */
4398 torture_assert_ntstatus_ok(torture
, status
,
4399 "FSCTL_QUERY_ALLOCATED_RANGES");
4400 if ((hlen
== file_size
) && (far_count
== 0)) {
4401 /* hole covered entire file, deallocation occurred */
4402 dealloc_chunk_len
= file_size
;
4406 torture_assert_u64_equal(torture
, far_count
, 1,
4407 "unexpected response len");
4408 if (far_rsp
[0].file_off
!= 0) {
4410 * We now know the hole punch length needed to trigger a
4411 * deallocation on this FS...
4413 dealloc_chunk_len
= hlen
;
4414 torture_comment(torture
, "hole punch %ju@0 resulted in "
4415 "deallocation of %ju@0\n",
4417 (uintmax_t)far_rsp
[0].file_off
);
4418 torture_assert_u64_equal(torture
,
4419 file_size
- far_rsp
[0].len
,
4420 far_rsp
[0].file_off
,
4421 "invalid alloced range");
4426 if (dealloc_chunk_len
== 0) {
4427 torture_comment(torture
, "strange, this FS never deallocates"
4428 "zeroed ranges in sparse files\n");
4429 return true; /* FS specific, not a failure */
4433 * Check whether deallocation occurs when the (now known)
4434 * deallocation chunk size is punched via two ZERO_DATA requests.
4435 * I.e. Does the FS merge the two ranges and deallocate the chunk?
4436 * NTFS on Windows Server 2012 does not.
4438 ok
= write_pattern(torture
, tree
, tmp_ctx
, fh
,
4440 file_size
, /* len */
4441 0); /* pattern offset */
4442 torture_assert(torture
, ok
, "write pattern");
4444 /* divide dealloc chunk size by two, to use as punch length */
4445 hlen
= dealloc_chunk_len
>> 1;
4448 * /half of dealloc chunk size 1M\
4450 * /offset 0 | /dealloc chunk size |
4451 * |------------------ |-------------------|-------------------|
4452 * | zeroed, 1st punch | zeroed, 2nd punch | existing pattern |
4454 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
4456 hlen
); /* beyond final zero */
4457 torture_assert_ntstatus_ok(torture
, status
, "zero_data");
4459 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
4461 dealloc_chunk_len
); /* beyond final */
4462 torture_assert_ntstatus_ok(torture
, status
, "zero_data");
4464 /* ensure holes are zeroed, and pattern is consistent */
4465 ok
= check_zero(torture
, tree
, tmp_ctx
, fh
, 0, dealloc_chunk_len
);
4466 torture_assert(torture
, ok
, "sparse zeroed range");
4468 ok
= check_pattern(torture
, tree
, tmp_ctx
, fh
, dealloc_chunk_len
,
4469 file_size
- dealloc_chunk_len
, dealloc_chunk_len
);
4470 torture_assert(torture
, ok
, "allocated pattern range");
4472 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
4474 file_size
, /* len */
4477 torture_assert_ntstatus_ok(torture
, status
,
4478 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4480 if ((far_count
== 0) && (dealloc_chunk_len
== file_size
)) {
4481 torture_comment(torture
, "holes merged for deallocation of "
4485 torture_assert_u64_equal(torture
, far_count
, 1,
4486 "unexpected response len");
4487 if (far_rsp
[0].file_off
== dealloc_chunk_len
) {
4488 torture_comment(torture
, "holes merged for deallocation of "
4489 "%ju chunk\n", (uintmax_t)dealloc_chunk_len
);
4490 torture_assert_u64_equal(torture
,
4491 file_size
- far_rsp
[0].len
,
4492 far_rsp
[0].file_off
,
4493 "invalid alloced range");
4495 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0,
4496 "unexpected deallocation");
4497 torture_comment(torture
, "holes not merged for deallocation\n");
4500 smb2_util_close(tree
, fh
);
4503 * Check whether an unwritten range is allocated when a sparse file is
4504 * written to at an offset past the dealloc chunk size:
4506 * /dealloc chunk size
4508 * |------------------ |-------------------|
4509 * | unwritten | pattern |
4511 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
4512 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
4513 FILE_ATTRIBUTE_NORMAL
);
4514 torture_assert(torture
, ok
, "setup file 1");
4517 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, true);
4518 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
4520 ok
= write_pattern(torture
, tree
, tmp_ctx
, fh
,
4521 dealloc_chunk_len
, /* off */
4523 dealloc_chunk_len
); /* pattern offset */
4524 torture_assert(torture
, ok
, "write pattern");
4526 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
4528 dealloc_chunk_len
+ 1024, /* len */
4531 torture_assert_ntstatus_ok(torture
, status
,
4532 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4533 torture_assert_u64_equal(torture
, far_count
, 1,
4534 "unexpected response len");
4535 if (far_rsp
[0].file_off
== 0) {
4536 torture_assert_u64_equal(torture
, far_rsp
[0].len
,
4537 dealloc_chunk_len
+ 1024,
4538 "unexpected far len");
4539 torture_comment(torture
, "unwritten range fully allocated\n");
4541 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, dealloc_chunk_len
,
4542 "unexpected deallocation");
4543 torture_assert_u64_equal(torture
, far_rsp
[0].len
, 1024,
4544 "unexpected far len");
4545 torture_comment(torture
, "unwritten range not allocated\n");
4548 ok
= check_zero(torture
, tree
, tmp_ctx
, fh
, 0, dealloc_chunk_len
);
4549 torture_assert(torture
, ok
, "sparse zeroed range");
4551 ok
= check_pattern(torture
, tree
, tmp_ctx
, fh
, dealloc_chunk_len
,
4552 1024, dealloc_chunk_len
);
4553 torture_assert(torture
, ok
, "allocated pattern range");
4555 /* unsparse, should now be fully allocated */
4556 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, false);
4557 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
4559 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
4561 dealloc_chunk_len
+ 1024, /* len */
4564 torture_assert_ntstatus_ok(torture
, status
,
4565 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4566 torture_assert_u64_equal(torture
, far_count
, 1,
4567 "unexpected response len");
4568 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0,
4569 "unexpected deallocation");
4570 torture_assert_u64_equal(torture
, far_rsp
[0].len
,
4571 dealloc_chunk_len
+ 1024,
4572 "unexpected far len");
4574 smb2_util_close(tree
, fh
);
4575 talloc_free(tmp_ctx
);
4579 /* check whether a file with compression and sparse attrs can be deallocated */
4580 static bool test_ioctl_sparse_compressed(struct torture_context
*torture
,
4581 struct smb2_tree
*tree
)
4583 struct smb2_handle fh
;
4585 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
4587 uint64_t file_size
= 1024 * 1024;
4588 struct file_alloced_range_buf
*far_rsp
= NULL
;
4589 uint64_t far_count
= 0;
4591 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
4592 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
4593 FILE_ATTRIBUTE_NORMAL
);
4594 torture_assert(torture
, ok
, "setup file 1");
4596 /* check for FS sparse file and compression support */
4597 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
4598 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
4599 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
4601 smb2_util_close(tree
, fh
);
4602 torture_skip(torture
, "Sparse files not supported\n");
4605 status
= test_ioctl_compress_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
4607 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
4609 smb2_util_close(tree
, fh
);
4610 torture_skip(torture
, "FS compression not supported\n");
4613 /* set compression and write some data */
4614 status
= test_ioctl_compress_set(torture
, tmp_ctx
, tree
, fh
,
4615 COMPRESSION_FORMAT_DEFAULT
);
4616 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_COMPRESSION");
4618 ok
= write_pattern(torture
, tree
, tmp_ctx
, fh
,
4620 file_size
, /* len */
4621 0); /* pattern offset */
4622 torture_assert(torture
, ok
, "write pattern");
4624 /* set sparse - now sparse and compressed */
4625 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, true);
4626 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
4628 /* check allocated ranges, should be fully alloced */
4629 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
4631 file_size
, /* len */
4634 torture_assert_ntstatus_ok(torture
, status
,
4635 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4636 torture_assert_u64_equal(torture
, far_count
, 1,
4637 "unexpected response len");
4638 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0,
4639 "unexpected far off");
4640 torture_assert_u64_equal(torture
, far_rsp
[0].len
, file_size
,
4641 "unexpected far len");
4643 /* zero (hole-punch) all data, with sparse and compressed attrs */
4644 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
4646 file_size
); /* beyond_final_zero */
4647 torture_assert_ntstatus_ok(torture
, status
, "zero_data");
4650 * Windows Server 2012 still deallocates a zeroed range when a sparse
4651 * file carries the compression attribute.
4653 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
4655 file_size
, /* len */
4658 torture_assert_ntstatus_ok(torture
, status
,
4659 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4660 if (far_count
== 0) {
4661 torture_comment(torture
, "sparse & compressed file "
4662 "deallocated after hole-punch\n");
4664 torture_assert_u64_equal(torture
, far_count
, 1,
4665 "unexpected response len");
4666 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0,
4667 "unexpected far off");
4668 torture_assert_u64_equal(torture
, far_rsp
[0].len
, file_size
,
4669 "unexpected far len");
4670 torture_comment(torture
, "sparse & compressed file fully "
4671 "allocated after hole-punch\n");
4674 smb2_util_close(tree
, fh
);
4675 talloc_free(tmp_ctx
);
4680 * Create a sparse file, then attempt to copy unallocated and allocated ranges
4681 * into a target file using FSCTL_SRV_COPYCHUNK.
4683 static bool test_ioctl_sparse_copy_chunk(struct torture_context
*torture
,
4684 struct smb2_tree
*tree
)
4686 struct smb2_handle src_h
;
4687 struct smb2_handle dest_h
;
4689 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
4691 uint64_t dealloc_chunk_len
= 64 * 1024; /* Windows 2012 */
4692 struct file_alloced_range_buf
*far_rsp
= NULL
;
4693 uint64_t far_count
= 0;
4694 union smb_ioctl ioctl
;
4695 struct srv_copychunk_copy cc_copy
;
4696 struct srv_copychunk_rsp cc_rsp
;
4697 enum ndr_err_code ndr_ret
;
4699 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
4700 FNAME
, &src_h
, 0, SEC_RIGHTS_FILE_ALL
,
4701 FILE_ATTRIBUTE_NORMAL
);
4702 torture_assert(torture
, ok
, "setup file");
4704 /* check for FS sparse file support */
4705 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &src_h
,
4706 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
4707 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
4708 smb2_util_close(tree
, src_h
);
4710 torture_skip(torture
, "Sparse files not supported\n");
4713 ok
= test_setup_copy_chunk(torture
, tree
, tree
, tmp_ctx
,
4716 &src_h
, 0, /* src file */
4717 SEC_RIGHTS_FILE_ALL
,
4719 &dest_h
, 0, /* dest file */
4720 SEC_RIGHTS_FILE_ALL
,
4723 torture_assert(torture
, ok
, "setup copy chunk error");
4726 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, src_h
, true);
4727 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
4729 /* start after dealloc_chunk_len, to create an unwritten sparse range */
4730 ok
= write_pattern(torture
, tree
, tmp_ctx
, src_h
,
4731 dealloc_chunk_len
, /* off */
4733 dealloc_chunk_len
); /* pattern offset */
4734 torture_assert(torture
, ok
, "write pattern");
4736 /* Skip test if 64k chunk is allocated - FS specific */
4737 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, src_h
,
4739 dealloc_chunk_len
+ 1024, /* len */
4742 torture_assert_ntstatus_ok(torture
, status
,
4743 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4744 torture_assert_u64_equal(torture
, far_count
, 1,
4745 "unexpected response len");
4746 if (far_rsp
[0].file_off
== 0) {
4747 torture_skip(torture
, "unwritten range fully allocated\n");
4750 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, dealloc_chunk_len
,
4751 "unexpected allocation");
4752 torture_assert_u64_equal(torture
, far_rsp
[0].len
, 1024,
4753 "unexpected far len");
4755 /* copy-chunk unallocated + written ranges into non-sparse dest */
4757 cc_copy
.chunks
[0].source_off
= 0;
4758 cc_copy
.chunks
[0].target_off
= 0;
4759 cc_copy
.chunks
[0].length
= dealloc_chunk_len
+ 1024;
4761 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
4763 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
4764 torture_assert_ndr_success(torture
, ndr_ret
,
4765 "ndr_push_srv_copychunk_copy");
4767 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
4768 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_COPYCHUNK");
4770 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
4772 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
4773 torture_assert_ndr_success(torture
, ndr_ret
,
4774 "ndr_pull_srv_copychunk_rsp");
4776 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
4777 1, /* chunks written */
4778 0, /* chunk bytes unsuccessfully written */
4779 dealloc_chunk_len
+ 1024); /* bytes written */
4780 torture_assert(torture
, ok
, "bad copy chunk response data");
4782 ok
= check_zero(torture
, tree
, tmp_ctx
, dest_h
, 0, dealloc_chunk_len
);
4783 torture_assert(torture
, ok
, "sparse zeroed range");
4785 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, dealloc_chunk_len
,
4786 1024, dealloc_chunk_len
);
4787 torture_assert(torture
, ok
, "copychunked range");
4789 /* copied range should be allocated in non-sparse dest */
4790 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, dest_h
,
4792 dealloc_chunk_len
+ 1024, /* len */
4795 torture_assert_ntstatus_ok(torture
, status
,
4796 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4797 torture_assert_u64_equal(torture
, far_count
, 1,
4798 "unexpected response len");
4799 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0,
4800 "unexpected allocation");
4801 torture_assert_u64_equal(torture
, far_rsp
[0].len
,
4802 dealloc_chunk_len
+ 1024,
4803 "unexpected far len");
4805 /* set dest as sparse */
4806 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, dest_h
, true);
4807 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
4809 /* zero (hole-punch) all data */
4810 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, dest_h
,
4812 dealloc_chunk_len
+ 1024);
4813 torture_assert_ntstatus_ok(torture
, status
, "zero_data");
4815 /* zeroed range might be deallocated */
4816 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, dest_h
,
4818 dealloc_chunk_len
+ 1024, /* len */
4821 torture_assert_ntstatus_ok(torture
, status
,
4822 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4823 if (far_count
== 0) {
4824 /* FS specific (e.g. NTFS) */
4825 torture_comment(torture
, "FS deallocates file on full-range "
4828 /* FS specific (e.g. EXT4) */
4829 torture_comment(torture
, "FS doesn't deallocate file on "
4830 "full-range punch\n");
4832 ok
= check_zero(torture
, tree
, tmp_ctx
, dest_h
, 0,
4833 dealloc_chunk_len
+ 1024);
4834 torture_assert(torture
, ok
, "punched zeroed range");
4836 /* copy-chunk again, this time with sparse dest */
4837 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
4838 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_COPYCHUNK");
4840 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
4842 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
4843 torture_assert_ndr_success(torture
, ndr_ret
,
4844 "ndr_pull_srv_copychunk_rsp");
4846 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
4847 1, /* chunks written */
4848 0, /* chunk bytes unsuccessfully written */
4849 dealloc_chunk_len
+ 1024); /* bytes written */
4850 torture_assert(torture
, ok
, "bad copy chunk response data");
4852 ok
= check_zero(torture
, tree
, tmp_ctx
, dest_h
, 0, dealloc_chunk_len
);
4853 torture_assert(torture
, ok
, "sparse zeroed range");
4855 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, dealloc_chunk_len
,
4856 1024, dealloc_chunk_len
);
4857 torture_assert(torture
, ok
, "copychunked range");
4859 /* copied range may be allocated in sparse dest */
4860 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, dest_h
,
4862 dealloc_chunk_len
+ 1024, /* len */
4865 torture_assert_ntstatus_ok(torture
, status
,
4866 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4867 torture_assert_u64_equal(torture
, far_count
, 1,
4868 "unexpected response len");
4870 * FS specific: sparse region may be unallocated in dest if copy-chunk
4871 * is handled in a sparse preserving way - E.g. vfs_btrfs
4872 * with BTRFS_IOC_CLONE_RANGE.
4874 if (far_rsp
[0].file_off
== dealloc_chunk_len
) {
4875 torture_comment(torture
, "copy-chunk sparse range preserved\n");
4876 torture_assert_u64_equal(torture
, far_rsp
[0].len
, 1024,
4877 "unexpected far len");
4879 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0,
4880 "unexpected allocation");
4881 torture_assert_u64_equal(torture
, far_rsp
[0].len
,
4882 dealloc_chunk_len
+ 1024,
4883 "unexpected far len");
4886 smb2_util_close(tree
, src_h
);
4887 smb2_util_close(tree
, dest_h
);
4888 talloc_free(tmp_ctx
);
4892 static bool test_ioctl_sparse_punch_invalid(struct torture_context
*torture
,
4893 struct smb2_tree
*tree
)
4895 struct smb2_handle fh
;
4897 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
4902 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
4903 FNAME
, &fh
, 4096, SEC_RIGHTS_FILE_ALL
,
4904 FILE_ATTRIBUTE_NORMAL
);
4905 torture_assert(torture
, ok
, "setup file");
4907 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
4908 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
4909 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
4911 smb2_util_close(tree
, fh
);
4912 torture_skip(torture
, "Sparse files not supported\n");
4915 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
4916 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
4917 torture_assert(torture
, !is_sparse
, "sparse attr before set");
4919 /* loop twice, without and with sparse attrib */
4920 for (i
= 0; i
<= 1; i
++) {
4921 union smb_fileinfo io
;
4922 struct file_alloced_range_buf
*far_rsp
= NULL
;
4923 uint64_t far_count
= 0;
4925 /* get size before & after. zero data should never change it */
4927 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
4928 io
.generic
.in
.file
.handle
= fh
;
4929 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
4930 torture_assert_ntstatus_ok(torture
, status
, "getinfo");
4931 torture_assert_int_equal(torture
, (int)io
.all_info2
.out
.size
,
4932 4096, "size after IO");
4934 /* valid 8 byte zero data, but after EOF */
4935 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
4937 4104); /* beyond_final_zero */
4938 torture_assert_ntstatus_ok(torture
, status
, "zero_data");
4940 /* valid 8 byte zero data, but after EOF */
4941 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
4943 8200); /* beyond_final_zero */
4944 torture_assert_ntstatus_ok(torture
, status
, "zero_data");
4947 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
4948 io
.generic
.in
.file
.handle
= fh
;
4949 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
4950 torture_assert_ntstatus_ok(torture
, status
, "getinfo");
4951 torture_assert_int_equal(torture
, (int)io
.all_info2
.out
.size
,
4952 4096, "size after IO");
4954 /* valid 0 byte zero data, without sparse flag */
4955 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
4957 4095); /* beyond_final_zero */
4958 torture_assert_ntstatus_ok(torture
, status
, "zero_data");
4960 /* INVALID off is past beyond_final_zero */
4961 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
4963 4095); /* beyond_final_zero */
4964 torture_assert_ntstatus_equal(torture
, status
,
4965 NT_STATUS_INVALID_PARAMETER
,
4966 "invalid zero_data");
4968 /* zero length QAR - valid */
4969 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
4972 &far_rsp
, &far_count
);
4973 torture_assert_ntstatus_ok(torture
, status
,
4974 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4975 torture_assert_u64_equal(torture
, far_count
, 0,
4976 "unexpected response len");
4978 /* QAR after EOF - valid */
4979 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
4982 &far_rsp
, &far_count
);
4983 torture_assert_ntstatus_ok(torture
, status
,
4984 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4985 torture_assert_u64_equal(torture
, far_count
, 0,
4986 "unexpected response len");
4989 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
,
4991 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
4994 smb2_util_close(tree
, fh
);
4995 talloc_free(tmp_ctx
);
4999 static bool test_ioctl_sparse_perms(struct torture_context
*torture
,
5000 struct smb2_tree
*tree
)
5002 struct smb2_handle fh
;
5004 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
5007 struct file_alloced_range_buf
*far_rsp
= NULL
;
5008 uint64_t far_count
= 0;
5010 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
5011 FNAME
, &fh
, 0, SEC_RIGHTS_FILE_ALL
,
5012 FILE_ATTRIBUTE_NORMAL
);
5013 torture_assert(torture
, ok
, "setup file");
5015 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
5016 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
5017 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
5018 smb2_util_close(tree
, fh
);
5020 torture_skip(torture
, "Sparse files not supported\n");
5023 /* set sparse without WRITE_ATTR permission should succeed */
5024 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
5026 (SEC_RIGHTS_FILE_WRITE
& ~(SEC_FILE_WRITE_ATTRIBUTE
5028 | SEC_FILE_WRITE_EA
)),
5029 FILE_ATTRIBUTE_NORMAL
);
5030 torture_assert(torture
, ok
, "setup file");
5032 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, true);
5033 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
5034 smb2_util_close(tree
, fh
);
5036 ok
= test_setup_open(torture
, tree
, tmp_ctx
,
5037 FNAME
, &fh
, SEC_RIGHTS_FILE_ALL
,
5038 FILE_ATTRIBUTE_NORMAL
);
5039 torture_assert(torture
, ok
, "setup file");
5040 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
5041 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
5042 torture_assert(torture
, is_sparse
, "sparse after set");
5043 smb2_util_close(tree
, fh
);
5045 /* attempt get sparse without READ_DATA permission */
5046 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
5048 (SEC_RIGHTS_FILE_READ
& ~SEC_FILE_READ_DATA
),
5049 FILE_ATTRIBUTE_NORMAL
);
5050 torture_assert(torture
, ok
, "setup file");
5052 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
5053 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
5054 torture_assert(torture
, !is_sparse
, "sparse set");
5055 smb2_util_close(tree
, fh
);
5057 /* attempt to set sparse with only WRITE_ATTR permission */
5058 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
5060 SEC_FILE_WRITE_ATTRIBUTE
,
5061 FILE_ATTRIBUTE_NORMAL
);
5062 torture_assert(torture
, ok
, "setup file");
5064 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, true);
5065 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
5066 smb2_util_close(tree
, fh
);
5068 /* attempt to set sparse with only WRITE_DATA permission */
5069 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
5071 SEC_FILE_WRITE_DATA
,
5072 FILE_ATTRIBUTE_NORMAL
);
5073 torture_assert(torture
, ok
, "setup file");
5075 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, true);
5076 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
5077 smb2_util_close(tree
, fh
);
5079 ok
= test_setup_open(torture
, tree
, tmp_ctx
,
5080 FNAME
, &fh
, SEC_RIGHTS_FILE_ALL
,
5081 FILE_ATTRIBUTE_NORMAL
);
5082 torture_assert(torture
, ok
, "setup file");
5083 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
5084 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
5085 torture_assert(torture
, is_sparse
, "sparse after set");
5086 smb2_util_close(tree
, fh
);
5088 /* attempt to set sparse with only APPEND_DATA permission */
5089 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
5091 SEC_FILE_APPEND_DATA
,
5092 FILE_ATTRIBUTE_NORMAL
);
5093 torture_assert(torture
, ok
, "setup file");
5095 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, true);
5096 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
5097 smb2_util_close(tree
, fh
);
5099 ok
= test_setup_open(torture
, tree
, tmp_ctx
,
5100 FNAME
, &fh
, SEC_RIGHTS_FILE_ALL
,
5101 FILE_ATTRIBUTE_NORMAL
);
5102 torture_assert(torture
, ok
, "setup file");
5103 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
5104 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
5105 torture_assert(torture
, is_sparse
, "sparse after set");
5106 smb2_util_close(tree
, fh
);
5108 /* attempt to set sparse with only WRITE_EA permission - should fail */
5109 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
5112 FILE_ATTRIBUTE_NORMAL
);
5113 torture_assert(torture
, ok
, "setup file");
5115 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, true);
5116 torture_assert_ntstatus_equal(torture
, status
,
5117 NT_STATUS_ACCESS_DENIED
,
5118 "FSCTL_SET_SPARSE permission");
5119 smb2_util_close(tree
, fh
);
5121 ok
= test_setup_open(torture
, tree
, tmp_ctx
,
5122 FNAME
, &fh
, SEC_RIGHTS_FILE_ALL
,
5123 FILE_ATTRIBUTE_NORMAL
);
5124 torture_assert(torture
, ok
, "setup file");
5125 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
5126 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
5127 torture_assert(torture
, !is_sparse
, "sparse after set");
5128 smb2_util_close(tree
, fh
);
5130 /* attempt QAR with only READ_ATTR permission - should fail */
5131 ok
= test_setup_open(torture
, tree
, tmp_ctx
,
5132 FNAME
, &fh
, SEC_FILE_READ_ATTRIBUTE
,
5133 FILE_ATTRIBUTE_NORMAL
);
5134 torture_assert(torture
, ok
, "setup file");
5135 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
5138 &far_rsp
, &far_count
);
5139 torture_assert_ntstatus_equal(torture
, status
,
5140 NT_STATUS_ACCESS_DENIED
,
5141 "FSCTL_QUERY_ALLOCATED_RANGES req passed");
5142 smb2_util_close(tree
, fh
);
5144 /* attempt QAR with only READ_DATA permission */
5145 ok
= test_setup_open(torture
, tree
, tmp_ctx
,
5146 FNAME
, &fh
, SEC_FILE_READ_DATA
,
5147 FILE_ATTRIBUTE_NORMAL
);
5148 torture_assert(torture
, ok
, "setup file");
5149 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
5152 &far_rsp
, &far_count
);
5153 torture_assert_ntstatus_ok(torture
, status
,
5154 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5155 torture_assert_u64_equal(torture
, far_count
, 0,
5156 "unexpected response len");
5157 smb2_util_close(tree
, fh
);
5159 /* attempt QAR with only READ_EA permission - should fail */
5160 ok
= test_setup_open(torture
, tree
, tmp_ctx
,
5161 FNAME
, &fh
, SEC_FILE_READ_EA
,
5162 FILE_ATTRIBUTE_NORMAL
);
5163 torture_assert(torture
, ok
, "setup file");
5164 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
5167 &far_rsp
, &far_count
);
5168 torture_assert_ntstatus_equal(torture
, status
,
5169 NT_STATUS_ACCESS_DENIED
,
5170 "FSCTL_QUERY_ALLOCATED_RANGES req passed");
5171 smb2_util_close(tree
, fh
);
5173 /* setup file for ZERO_DATA permissions tests */
5174 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
5176 SEC_RIGHTS_FILE_ALL
,
5177 FILE_ATTRIBUTE_NORMAL
);
5178 torture_assert(torture
, ok
, "setup file");
5180 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, true);
5181 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
5182 smb2_util_close(tree
, fh
);
5184 /* attempt ZERO_DATA with only WRITE_ATTR permission - should fail */
5185 ok
= test_setup_open(torture
, tree
, tmp_ctx
,
5186 FNAME
, &fh
, SEC_FILE_WRITE_ATTRIBUTE
,
5187 FILE_ATTRIBUTE_NORMAL
);
5188 torture_assert(torture
, ok
, "setup file");
5189 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
5191 4096); /* beyond_final_zero */
5192 torture_assert_ntstatus_equal(torture
, status
,
5193 NT_STATUS_ACCESS_DENIED
,
5194 "zero_data permission");
5195 smb2_util_close(tree
, fh
);
5197 /* attempt ZERO_DATA with only WRITE_DATA permission */
5198 ok
= test_setup_open(torture
, tree
, tmp_ctx
,
5199 FNAME
, &fh
, SEC_FILE_WRITE_DATA
,
5200 FILE_ATTRIBUTE_NORMAL
);
5201 torture_assert(torture
, ok
, "setup file");
5202 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
5204 4096); /* beyond_final_zero */
5205 torture_assert_ntstatus_ok(torture
, status
, "zero_data");
5206 smb2_util_close(tree
, fh
);
5208 /* attempt ZERO_DATA with only APPEND_DATA permission - should fail */
5209 ok
= test_setup_open(torture
, tree
, tmp_ctx
,
5210 FNAME
, &fh
, SEC_FILE_APPEND_DATA
,
5211 FILE_ATTRIBUTE_NORMAL
);
5212 torture_assert(torture
, ok
, "setup file");
5213 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
5215 4096); /* beyond_final_zero */
5216 torture_assert_ntstatus_equal(torture
, status
,
5217 NT_STATUS_ACCESS_DENIED
,
5218 "zero_data permission");
5219 smb2_util_close(tree
, fh
);
5221 /* attempt ZERO_DATA with only WRITE_EA permission - should fail */
5222 ok
= test_setup_open(torture
, tree
, tmp_ctx
,
5223 FNAME
, &fh
, SEC_FILE_WRITE_EA
,
5224 FILE_ATTRIBUTE_NORMAL
);
5225 torture_assert(torture
, ok
, "setup file");
5226 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
5228 4096); /* beyond_final_zero */
5229 torture_assert_ntstatus_equal(torture
, status
,
5230 NT_STATUS_ACCESS_DENIED
,
5231 "zero_data permission");
5232 smb2_util_close(tree
, fh
);
5234 talloc_free(tmp_ctx
);
5238 static bool test_ioctl_sparse_lck(struct torture_context
*torture
,
5239 struct smb2_tree
*tree
)
5241 struct smb2_handle fh
;
5242 struct smb2_handle fh2
;
5244 uint64_t dealloc_chunk_len
= 64 * 1024; /* Windows 2012 */
5245 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
5248 struct smb2_lock lck
;
5249 struct smb2_lock_element el
[1];
5250 struct file_alloced_range_buf
*far_rsp
= NULL
;
5251 uint64_t far_count
= 0;
5253 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
, FNAME
, &fh
,
5254 dealloc_chunk_len
, SEC_RIGHTS_FILE_ALL
,
5255 FILE_ATTRIBUTE_NORMAL
);
5256 torture_assert(torture
, ok
, "setup file");
5258 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
5259 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
5260 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
5262 torture_skip(torture
, "Sparse files not supported\n");
5263 smb2_util_close(tree
, fh
);
5266 /* open and lock via separate fh2 */
5267 status
= torture_smb2_testfile(tree
, FNAME
, &fh2
);
5268 torture_assert_ntstatus_ok(torture
, status
, "2nd src open");
5270 lck
.in
.lock_count
= 0x0001;
5271 lck
.in
.lock_sequence
= 0x00000000;
5272 lck
.in
.file
.handle
= fh2
;
5275 el
[0].length
= dealloc_chunk_len
;
5277 el
[0].flags
= SMB2_LOCK_FLAG_EXCLUSIVE
;
5279 status
= smb2_lock(tree
, &lck
);
5280 torture_assert_ntstatus_ok(torture
, status
, "lock");
5282 /* set sparse while locked */
5283 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, true);
5284 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
5286 status
= test_sparse_get(torture
, tmp_ctx
, tree
, fh
, &is_sparse
);
5287 torture_assert_ntstatus_ok(torture
, status
, "test_sparse_get");
5288 torture_assert(torture
, is_sparse
, "sparse attr after set");
5290 /* zero data over locked range should fail */
5291 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
5293 4096); /* beyond_final_zero */
5294 torture_assert_ntstatus_equal(torture
, status
,
5295 NT_STATUS_FILE_LOCK_CONFLICT
,
5296 "zero_data locked");
5298 /* QAR over locked range should pass */
5299 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
5302 &far_rsp
, &far_count
);
5303 torture_assert_ntstatus_ok(torture
, status
,
5304 "FSCTL_QUERY_ALLOCATED_RANGES locked");
5305 torture_assert_u64_equal(torture
, far_count
, 1,
5306 "unexpected response len");
5307 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0,
5308 "unexpected allocation");
5309 torture_assert_u64_equal(torture
, far_rsp
[0].len
,
5311 "unexpected far len");
5313 /* zero data over range past EOF should pass */
5314 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
5315 dealloc_chunk_len
, /* off */
5316 dealloc_chunk_len
+ 4096);
5317 torture_assert_ntstatus_ok(torture
, status
,
5318 "zero_data past EOF locked");
5320 /* QAR over range past EOF should pass */
5321 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
5322 dealloc_chunk_len
, /* off */
5324 &far_rsp
, &far_count
);
5325 torture_assert_ntstatus_ok(torture
, status
,
5326 "FSCTL_QUERY_ALLOCATED_RANGES past EOF locked");
5327 torture_assert_u64_equal(torture
, far_count
, 0,
5328 "unexpected response len");
5330 lck
.in
.lock_count
= 0x0001;
5331 lck
.in
.lock_sequence
= 0x00000001;
5332 lck
.in
.file
.handle
= fh2
;
5335 el
[0].length
= dealloc_chunk_len
;
5337 el
[0].flags
= SMB2_LOCK_FLAG_UNLOCK
;
5338 status
= smb2_lock(tree
, &lck
);
5339 torture_assert_ntstatus_ok(torture
, status
, "unlock");
5341 smb2_util_close(tree
, fh2
);
5342 smb2_util_close(tree
, fh
);
5343 talloc_free(tmp_ctx
);
5347 /* alleviate QAR off-by-one bug paranoia - help me ob1 */
5348 static bool test_ioctl_sparse_qar_ob1(struct torture_context
*torture
,
5349 struct smb2_tree
*tree
)
5351 struct smb2_handle fh
;
5353 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
5355 uint64_t dealloc_chunk_len
= 64 * 1024; /* Windows 2012 */
5356 struct file_alloced_range_buf
*far_rsp
= NULL
;
5357 uint64_t far_count
= 0;
5359 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
5360 FNAME
, &fh
, dealloc_chunk_len
* 2,
5361 SEC_RIGHTS_FILE_ALL
,
5362 FILE_ATTRIBUTE_NORMAL
);
5363 torture_assert(torture
, ok
, "setup file");
5365 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
5366 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
5367 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
5369 torture_skip(torture
, "Sparse files not supported\n");
5370 smb2_util_close(tree
, fh
);
5373 /* non-sparse QAR with range one before EOF */
5374 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
5376 dealloc_chunk_len
* 2 - 1, /* len */
5377 &far_rsp
, &far_count
);
5378 torture_assert_ntstatus_ok(torture
, status
,
5379 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5380 torture_assert_u64_equal(torture
, far_count
, 1,
5381 "unexpected response len");
5382 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0,
5383 "unexpected allocation");
5384 torture_assert_u64_equal(torture
, far_rsp
[0].len
,
5385 dealloc_chunk_len
* 2 - 1,
5386 "unexpected far len");
5388 /* non-sparse QAR with range one after EOF */
5389 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
5391 dealloc_chunk_len
* 2 + 1, /* len */
5392 &far_rsp
, &far_count
);
5393 torture_assert_ntstatus_ok(torture
, status
,
5394 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5395 torture_assert_u64_equal(torture
, far_count
, 1,
5396 "unexpected response len");
5397 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0,
5398 "unexpected allocation");
5399 torture_assert_u64_equal(torture
, far_rsp
[0].len
,
5400 dealloc_chunk_len
* 2,
5401 "unexpected far len");
5403 /* non-sparse QAR with range one after EOF from off=1 */
5404 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
5406 dealloc_chunk_len
* 2, /* len */
5407 &far_rsp
, &far_count
);
5408 torture_assert_ntstatus_ok(torture
, status
,
5409 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5410 torture_assert_u64_equal(torture
, far_count
, 1,
5411 "unexpected response len");
5412 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 1,
5413 "unexpected allocation");
5414 torture_assert_u64_equal(torture
, far_rsp
[0].len
,
5415 dealloc_chunk_len
* 2 - 1,
5416 "unexpected far len");
5418 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, true);
5419 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
5421 /* punch out second chunk */
5422 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
5423 dealloc_chunk_len
, /* off */
5424 dealloc_chunk_len
* 2);
5425 torture_assert_ntstatus_ok(torture
, status
, "zero_data");
5427 /* sparse QAR with range one before hole */
5428 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
5430 dealloc_chunk_len
- 1, /* len */
5431 &far_rsp
, &far_count
);
5432 torture_assert_ntstatus_ok(torture
, status
,
5433 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5434 torture_assert_u64_equal(torture
, far_count
, 1,
5435 "unexpected response len");
5436 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0,
5437 "unexpected allocation");
5438 torture_assert_u64_equal(torture
, far_rsp
[0].len
,
5439 dealloc_chunk_len
- 1,
5440 "unexpected far len");
5442 /* sparse QAR with range one after hole */
5443 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
5445 dealloc_chunk_len
+ 1, /* len */
5446 &far_rsp
, &far_count
);
5447 torture_assert_ntstatus_ok(torture
, status
,
5448 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5449 torture_assert_u64_equal(torture
, far_count
, 1,
5450 "unexpected response len");
5451 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 0,
5452 "unexpected allocation");
5453 torture_assert_u64_equal(torture
, far_rsp
[0].len
,
5455 "unexpected far len");
5457 /* sparse QAR with range one after hole from off=1 */
5458 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
5460 dealloc_chunk_len
, /* len */
5461 &far_rsp
, &far_count
);
5462 torture_assert_ntstatus_ok(torture
, status
,
5463 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5464 torture_assert_u64_equal(torture
, far_count
, 1,
5465 "unexpected response len");
5466 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
, 1,
5467 "unexpected allocation");
5468 torture_assert_u64_equal(torture
, far_rsp
[0].len
,
5469 dealloc_chunk_len
- 1,
5470 "unexpected far len");
5472 /* sparse QAR with range one before EOF from off=chunk_len-1 */
5473 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
5474 dealloc_chunk_len
- 1, /* off */
5475 dealloc_chunk_len
, /* len */
5476 &far_rsp
, &far_count
);
5477 torture_assert_ntstatus_ok(torture
, status
,
5478 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5479 torture_assert_u64_equal(torture
, far_count
, 1,
5480 "unexpected response len");
5481 torture_assert_u64_equal(torture
, far_rsp
[0].file_off
,
5482 dealloc_chunk_len
- 1,
5483 "unexpected allocation");
5484 torture_assert_u64_equal(torture
, far_rsp
[0].len
,
5485 1, "unexpected far len");
5487 /* sparse QAR with range one after EOF from off=chunk_len+1 */
5488 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
5489 dealloc_chunk_len
+ 1, /* off */
5490 dealloc_chunk_len
, /* len */
5491 &far_rsp
, &far_count
);
5492 torture_assert_ntstatus_ok(torture
, status
,
5493 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5494 torture_assert_u64_equal(torture
, far_count
, 0,
5495 "unexpected response len");
5496 smb2_util_close(tree
, fh
);
5497 talloc_free(tmp_ctx
);
5501 /* test QAR with multi-range responses */
5502 static bool test_ioctl_sparse_qar_multi(struct torture_context
*torture
,
5503 struct smb2_tree
*tree
)
5505 struct smb2_handle fh
;
5507 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
5509 uint64_t dealloc_chunk_len
= 64 * 1024; /* Windows 2012 */
5512 struct file_alloced_range_buf
*far_rsp
= NULL
;
5513 uint64_t far_count
= 0;
5515 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
5516 FNAME
, &fh
, dealloc_chunk_len
* 2,
5517 SEC_RIGHTS_FILE_ALL
,
5518 FILE_ATTRIBUTE_NORMAL
);
5519 torture_assert(torture
, ok
, "setup file");
5521 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
5522 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
5523 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
5525 torture_skip(torture
, "Sparse files not supported\n");
5526 smb2_util_close(tree
, fh
);
5529 status
= test_ioctl_sparse_req(torture
, tmp_ctx
, tree
, fh
, true);
5530 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SET_SPARSE");
5532 /* each loop, write out two chunks and punch the first out */
5533 for (i
= 0; i
< 10; i
++) {
5534 this_off
= i
* dealloc_chunk_len
* 2;
5536 ok
= write_pattern(torture
, tree
, tmp_ctx
, fh
,
5538 dealloc_chunk_len
* 2, /* len */
5539 this_off
); /* pattern offset */
5540 torture_assert(torture
, ok
, "write pattern");
5542 status
= test_ioctl_zdata_req(torture
, tmp_ctx
, tree
, fh
,
5544 this_off
+ dealloc_chunk_len
);
5545 torture_assert_ntstatus_ok(torture
, status
, "zero_data");
5548 /* should now have one separate region for each iteration */
5549 status
= test_ioctl_qar_req(torture
, tmp_ctx
, tree
, fh
,
5551 10 * dealloc_chunk_len
* 2,
5552 &far_rsp
, &far_count
);
5553 torture_assert_ntstatus_ok(torture
, status
,
5554 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5555 if (far_count
== 1) {
5556 torture_comment(torture
, "this FS doesn't deallocate 64K"
5557 "zeroed ranges in sparse files\n");
5558 return true; /* FS specific, not a failure */
5560 torture_assert_u64_equal(torture
, far_count
, 10,
5561 "unexpected response len");
5562 for (i
= 0; i
< 10; i
++) {
5563 this_off
= i
* dealloc_chunk_len
* 2;
5565 torture_assert_u64_equal(torture
, far_rsp
[i
].file_off
,
5566 this_off
+ dealloc_chunk_len
,
5567 "unexpected allocation");
5568 torture_assert_u64_equal(torture
, far_rsp
[i
].len
,
5570 "unexpected far len");
5573 smb2_util_close(tree
, fh
);
5574 talloc_free(tmp_ctx
);
5578 static bool test_ioctl_sparse_qar_overflow(struct torture_context
*torture
,
5579 struct smb2_tree
*tree
)
5581 struct smb2_handle fh
;
5582 union smb_ioctl ioctl
;
5583 struct file_alloced_range_buf far_buf
;
5585 enum ndr_err_code ndr_ret
;
5586 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
5589 ok
= test_setup_create_fill(torture
, tree
, tmp_ctx
,
5590 FNAME
, &fh
, 1024, SEC_RIGHTS_FILE_ALL
,
5591 FILE_ATTRIBUTE_NORMAL
);
5592 torture_assert(torture
, ok
, "setup file");
5594 status
= test_ioctl_fs_supported(torture
, tree
, tmp_ctx
, &fh
,
5595 FILE_SUPPORTS_SPARSE_FILES
, &ok
);
5596 torture_assert_ntstatus_ok(torture
, status
, "SMB2_GETINFO_FS");
5598 smb2_util_close(tree
, fh
);
5599 torture_skip(torture
, "Sparse files not supported\n");
5602 /* no allocated ranges, no space for range response, should pass */
5604 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
5605 ioctl
.smb2
.in
.file
.handle
= fh
;
5606 ioctl
.smb2
.in
.function
= FSCTL_QUERY_ALLOCATED_RANGES
;
5607 ioctl
.smb2
.in
.max_output_response
= 1024;
5608 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
5610 /* off + length wraps around to 511 */
5611 far_buf
.file_off
= 512;
5612 far_buf
.len
= 0xffffffffffffffffLL
;
5613 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
5615 (ndr_push_flags_fn_t
)ndr_push_file_alloced_range_buf
);
5616 torture_assert_ndr_success(torture
, ndr_ret
, "push far ndr buf");
5618 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
5619 torture_assert_ntstatus_equal(torture
, status
,
5620 NT_STATUS_INVALID_PARAMETER
,
5621 "FSCTL_QUERY_ALLOCATED_RANGES overflow");
5626 static NTSTATUS
test_ioctl_trim_supported(struct torture_context
*torture
,
5627 struct smb2_tree
*tree
,
5628 TALLOC_CTX
*mem_ctx
,
5629 struct smb2_handle
*fh
,
5633 union smb_fsinfo info
;
5636 info
.generic
.level
= RAW_QFS_SECTOR_SIZE_INFORMATION
;
5637 info
.generic
.handle
= *fh
;
5638 status
= smb2_getinfo_fs(tree
, tree
, &info
);
5639 if (NT_STATUS_EQUAL(status
, NT_STATUS_INVALID_INFO_CLASS
)) {
5641 * Windows < Server 2012, 8 etc. don't support this info level
5642 * or the trim ioctl. Ignore the error and let the caller skip.
5644 *trim_support
= false;
5645 return NT_STATUS_OK
;
5646 } else if (!NT_STATUS_IS_OK(status
)) {
5650 torture_comment(torture
, "sector size info: lb/s=%u, pb/sA=%u, "
5651 "pb/sP=%u, fse/sA=%u, flags=0x%x, bosa=%u, bopa=%u\n",
5652 (unsigned)info
.sector_size_info
.out
.logical_bytes_per_sector
,
5653 (unsigned)info
.sector_size_info
.out
.phys_bytes_per_sector_atomic
,
5654 (unsigned)info
.sector_size_info
.out
.phys_bytes_per_sector_perf
,
5655 (unsigned)info
.sector_size_info
.out
.fs_effective_phys_bytes_per_sector_atomic
,
5656 (unsigned)info
.sector_size_info
.out
.flags
,
5657 (unsigned)info
.sector_size_info
.out
.byte_off_sector_align
,
5658 (unsigned)info
.sector_size_info
.out
.byte_off_partition_align
);
5660 if (info
.sector_size_info
.out
.flags
& QFS_SSINFO_FLAGS_TRIM_ENABLED
) {
5661 *trim_support
= true;
5663 *trim_support
= false;
5665 return NT_STATUS_OK
;
5668 static bool test_setup_trim(struct torture_context
*torture
,
5669 struct smb2_tree
*tree
,
5670 TALLOC_CTX
*mem_ctx
,
5671 uint32_t num_ranges
,
5672 struct smb2_handle
*fh
,
5674 uint32_t desired_access
,
5675 struct fsctl_file_level_trim_req
*trim_req
,
5676 union smb_ioctl
*ioctl
)
5680 ok
= test_setup_create_fill(torture
, tree
, mem_ctx
, FNAME
,
5681 fh
, file_size
, desired_access
,
5682 FILE_ATTRIBUTE_NORMAL
);
5683 torture_assert(torture
, ok
, "src file create fill");
5685 ZERO_STRUCTPN(ioctl
);
5686 ioctl
->smb2
.level
= RAW_IOCTL_SMB2
;
5687 ioctl
->smb2
.in
.file
.handle
= *fh
;
5688 ioctl
->smb2
.in
.function
= FSCTL_FILE_LEVEL_TRIM
;
5689 ioctl
->smb2
.in
.max_output_response
5690 = sizeof(struct fsctl_file_level_trim_rsp
);
5691 ioctl
->smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
5693 ZERO_STRUCTPN(trim_req
);
5694 /* leave key as zero for now. TODO test locking with differing keys */
5695 trim_req
->num_ranges
= num_ranges
;
5696 trim_req
->ranges
= talloc_zero_array(mem_ctx
,
5697 struct file_level_trim_range
,
5699 torture_assert(torture
, (trim_req
->ranges
!= NULL
), "no memory for ranges");
5704 static bool test_ioctl_trim_simple(struct torture_context
*torture
,
5705 struct smb2_tree
*tree
)
5707 struct smb2_handle fh
;
5709 union smb_ioctl ioctl
;
5710 bool trim_supported
;
5711 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
5712 struct fsctl_file_level_trim_req trim_req
;
5713 struct fsctl_file_level_trim_rsp trim_rsp
;
5714 uint64_t trim_chunk_len
= 64 * 1024; /* trim 64K chunks */
5715 enum ndr_err_code ndr_ret
;
5718 ok
= test_setup_trim(torture
, tree
, tmp_ctx
,
5720 &fh
, 2 * trim_chunk_len
, /* fill 128K file */
5721 SEC_RIGHTS_FILE_ALL
,
5725 torture_fail(torture
, "setup trim error");
5728 status
= test_ioctl_trim_supported(torture
, tree
, tmp_ctx
, &fh
,
5730 torture_assert_ntstatus_ok(torture
, status
, "fsinfo");
5731 if (!trim_supported
) {
5732 smb2_util_close(tree
, fh
);
5733 talloc_free(tmp_ctx
);
5734 torture_skip(torture
, "trim not supported\n");
5737 /* trim first chunk, leave second */
5738 trim_req
.ranges
[0].off
= 0;
5739 trim_req
.ranges
[0].len
= trim_chunk_len
;
5741 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
, &trim_req
,
5742 (ndr_push_flags_fn_t
)ndr_push_fsctl_file_level_trim_req
);
5743 torture_assert_ndr_success(torture
, ndr_ret
,
5744 "ndr_push_fsctl_file_level_trim_req");
5746 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
5747 torture_assert_ntstatus_ok(torture
, status
, "FILE_LEVEL_TRIM_RANGE");
5749 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
5751 (ndr_pull_flags_fn_t
)ndr_pull_fsctl_file_level_trim_rsp
);
5752 torture_assert_ndr_success(torture
, ndr_ret
,
5753 "ndr_pull_fsctl_file_level_trim_rsp");
5755 torture_assert_int_equal(torture
, trim_rsp
.num_ranges_processed
, 1, "");
5757 /* second half of the file should remain consistent */
5758 ok
= check_pattern(torture
, tree
, tmp_ctx
, fh
, trim_chunk_len
,
5759 trim_chunk_len
, trim_chunk_len
);
5760 torture_assert(torture
, ok
, "non-trimmed range inconsistent");
5765 static bool test_setup_dup_extents(struct torture_context
*tctx
,
5766 struct smb2_tree
*tree
,
5767 TALLOC_CTX
*mem_ctx
,
5768 struct smb2_handle
*src_h
,
5770 uint32_t src_desired_access
,
5771 struct smb2_handle
*dest_h
,
5773 uint32_t dest_desired_access
,
5774 struct fsctl_dup_extents_to_file
*dup_ext_buf
,
5775 union smb_ioctl
*ioctl
)
5779 ok
= test_setup_create_fill(tctx
, tree
, mem_ctx
, FNAME
,
5780 src_h
, src_size
, src_desired_access
,
5781 FILE_ATTRIBUTE_NORMAL
);
5782 torture_assert(tctx
, ok
, "src file create fill");
5784 ok
= test_setup_create_fill(tctx
, tree
, mem_ctx
, FNAME2
,
5785 dest_h
, dest_size
, dest_desired_access
,
5786 FILE_ATTRIBUTE_NORMAL
);
5787 torture_assert(tctx
, ok
, "dest file create fill");
5789 ZERO_STRUCTPN(ioctl
);
5790 ioctl
->smb2
.level
= RAW_IOCTL_SMB2
;
5791 ioctl
->smb2
.in
.file
.handle
= *dest_h
;
5792 ioctl
->smb2
.in
.function
= FSCTL_DUP_EXTENTS_TO_FILE
;
5793 ioctl
->smb2
.in
.max_output_response
= 0;
5794 ioctl
->smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
5796 ZERO_STRUCTPN(dup_ext_buf
);
5797 smb2_push_handle(dup_ext_buf
->source_fid
, src_h
);
5802 static bool test_ioctl_dup_extents_simple(struct torture_context
*tctx
,
5803 struct smb2_tree
*tree
)
5805 struct smb2_handle src_h
;
5806 struct smb2_handle dest_h
;
5808 union smb_ioctl ioctl
;
5809 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
5810 struct fsctl_dup_extents_to_file dup_ext_buf
;
5811 enum ndr_err_code ndr_ret
;
5812 union smb_fileinfo io
;
5813 union smb_setfileinfo sinfo
;
5816 ok
= test_setup_dup_extents(tctx
, tree
, tmp_ctx
,
5817 &src_h
, 4096, /* fill 4096 byte src file */
5818 SEC_RIGHTS_FILE_ALL
,
5819 &dest_h
, 0, /* 0 byte dest file */
5820 SEC_RIGHTS_FILE_ALL
,
5824 torture_fail(tctx
, "setup dup extents error");
5827 status
= test_ioctl_fs_supported(tctx
, tree
, tmp_ctx
, &src_h
,
5828 FILE_SUPPORTS_BLOCK_REFCOUNTING
, &ok
);
5829 torture_assert_ntstatus_ok(tctx
, status
, "SMB2_GETINFO_FS");
5831 smb2_util_close(tree
, src_h
);
5832 smb2_util_close(tree
, dest_h
);
5833 talloc_free(tmp_ctx
);
5834 torture_skip(tctx
, "block refcounting not supported\n");
5837 /* extend dest to match src len */
5839 sinfo
.end_of_file_info
.level
=
5840 RAW_SFILEINFO_END_OF_FILE_INFORMATION
;
5841 sinfo
.end_of_file_info
.in
.file
.handle
= dest_h
;
5842 sinfo
.end_of_file_info
.in
.size
= 4096;
5843 status
= smb2_setinfo_file(tree
, &sinfo
);
5844 torture_assert_ntstatus_ok(tctx
, status
, "smb2_setinfo_file");
5846 /* copy all src file data */
5847 dup_ext_buf
.source_off
= 0;
5848 dup_ext_buf
.target_off
= 0;
5849 dup_ext_buf
.byte_count
= 4096;
5851 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
5853 (ndr_push_flags_fn_t
)ndr_push_fsctl_dup_extents_to_file
);
5854 torture_assert_ndr_success(tctx
, ndr_ret
,
5855 "ndr_push_fsctl_dup_extents_to_file");
5857 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
5858 torture_assert_ntstatus_ok(tctx
, status
,
5859 "FSCTL_DUP_EXTENTS_TO_FILE");
5861 /* the file size shouldn't have been changed by this operation! */
5863 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
5864 io
.generic
.in
.file
.handle
= dest_h
;
5865 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
5866 torture_assert_ntstatus_ok(tctx
, status
, "getinfo");
5867 torture_assert_int_equal(tctx
, (int)io
.all_info2
.out
.size
,
5868 4096, "size after IO");
5870 smb2_util_close(tree
, src_h
);
5871 smb2_util_close(tree
, dest_h
);
5873 /* reopen for pattern check */
5874 ok
= test_setup_open(tctx
, tree
, tmp_ctx
, FNAME
, &src_h
,
5875 SEC_RIGHTS_FILE_ALL
, FILE_ATTRIBUTE_NORMAL
);
5876 torture_assert_ntstatus_ok(tctx
, status
, "src open after dup");
5877 ok
= test_setup_open(tctx
, tree
, tmp_ctx
, FNAME2
, &dest_h
,
5878 SEC_RIGHTS_FILE_ALL
, FILE_ATTRIBUTE_NORMAL
);
5879 torture_assert_ntstatus_ok(tctx
, status
, "dest open after dup");
5881 ok
= check_pattern(tctx
, tree
, tmp_ctx
, src_h
, 0, 4096, 0);
5883 torture_fail(tctx
, "inconsistent src file data");
5886 ok
= check_pattern(tctx
, tree
, tmp_ctx
, dest_h
, 0, 4096, 0);
5888 torture_fail(tctx
, "inconsistent dest file data");
5891 smb2_util_close(tree
, src_h
);
5892 smb2_util_close(tree
, dest_h
);
5893 talloc_free(tmp_ctx
);
5897 static bool test_ioctl_dup_extents_len_beyond_dest(struct torture_context
*tctx
,
5898 struct smb2_tree
*tree
)
5900 struct smb2_handle src_h
;
5901 struct smb2_handle dest_h
;
5903 union smb_ioctl ioctl
;
5904 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
5905 struct fsctl_dup_extents_to_file dup_ext_buf
;
5906 enum ndr_err_code ndr_ret
;
5907 union smb_fileinfo io
;
5908 union smb_setfileinfo sinfo
;
5911 ok
= test_setup_dup_extents(tctx
, tree
, tmp_ctx
,
5912 &src_h
, 32768, /* fill 32768 byte src file */
5913 SEC_RIGHTS_FILE_ALL
,
5914 &dest_h
, 0, /* 0 byte dest file */
5915 SEC_RIGHTS_FILE_ALL
,
5919 torture_fail(tctx
, "setup dup extents error");
5922 status
= test_ioctl_fs_supported(tctx
, tree
, tmp_ctx
, &src_h
,
5923 FILE_SUPPORTS_BLOCK_REFCOUNTING
, &ok
);
5924 torture_assert_ntstatus_ok(tctx
, status
, "SMB2_GETINFO_FS");
5926 smb2_util_close(tree
, src_h
);
5927 smb2_util_close(tree
, dest_h
);
5928 talloc_free(tmp_ctx
);
5929 torture_skip(tctx
, "block refcounting not supported\n");
5933 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
5934 io
.generic
.in
.file
.handle
= dest_h
;
5935 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
5936 torture_assert_ntstatus_ok(tctx
, status
, "getinfo");
5937 torture_assert_int_equal(tctx
, (int)io
.all_info2
.out
.size
,
5938 0, "size after IO");
5940 /* copy all src file data */
5941 dup_ext_buf
.source_off
= 0;
5942 dup_ext_buf
.target_off
= 0;
5943 dup_ext_buf
.byte_count
= 32768;
5945 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
5947 (ndr_push_flags_fn_t
)ndr_push_fsctl_dup_extents_to_file
);
5948 torture_assert_ndr_success(tctx
, ndr_ret
,
5949 "ndr_push_fsctl_dup_extents_to_file");
5951 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
5954 * 2.3.8 FSCTL_DUPLICATE_EXTENTS_TO_FILE Reply - this should fail, but
5955 * passes against WS2016 RTM!
5957 torture_assert_ntstatus_equal(tctx
, status
, NT_STATUS_NOT_SUPPORTED
,
5958 "FSCTL_DUP_EXTENTS_TO_FILE");
5961 /* the file sizes shouldn't have been changed */
5963 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
5964 io
.generic
.in
.file
.handle
= src_h
;
5965 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
5966 torture_assert_ntstatus_ok(tctx
, status
, "getinfo");
5967 torture_assert_int_equal(tctx
, (int)io
.all_info2
.out
.size
,
5968 32768, "size after IO");
5971 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
5972 io
.generic
.in
.file
.handle
= dest_h
;
5973 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
5974 torture_assert_ntstatus_ok(tctx
, status
, "getinfo");
5975 torture_assert_int_equal(tctx
, (int)io
.all_info2
.out
.size
,
5976 0, "size after IO");
5980 sinfo
.end_of_file_info
.level
= RAW_SFILEINFO_END_OF_FILE_INFORMATION
;
5981 sinfo
.end_of_file_info
.in
.file
.handle
= dest_h
;
5982 sinfo
.end_of_file_info
.in
.size
= 32768;
5983 status
= smb2_setinfo_file(tree
, &sinfo
);
5984 torture_assert_ntstatus_ok(tctx
, status
, "smb2_setinfo_file");
5986 ok
= check_zero(tctx
, tree
, tmp_ctx
, dest_h
, 0, 32768);
5988 torture_fail(tctx
, "inconsistent file data");
5991 /* reissue ioctl, now with enough space */
5992 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
5993 torture_assert_ntstatus_ok(tctx
, status
,
5994 "FSCTL_DUP_EXTENTS_TO_FILE");
5996 ok
= check_pattern(tctx
, tree
, tmp_ctx
, dest_h
, 0, 32768, 0);
5998 torture_fail(tctx
, "inconsistent file data");
6001 smb2_util_close(tree
, src_h
);
6002 smb2_util_close(tree
, dest_h
);
6003 talloc_free(tmp_ctx
);
6007 static bool test_ioctl_dup_extents_len_beyond_src(struct torture_context
*tctx
,
6008 struct smb2_tree
*tree
)
6010 struct smb2_handle src_h
;
6011 struct smb2_handle dest_h
;
6013 union smb_ioctl ioctl
;
6014 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
6015 struct fsctl_dup_extents_to_file dup_ext_buf
;
6016 enum ndr_err_code ndr_ret
;
6017 union smb_fileinfo io
;
6020 ok
= test_setup_dup_extents(tctx
, tree
, tmp_ctx
,
6021 &src_h
, 32768, /* fill 32768 byte src file */
6022 SEC_RIGHTS_FILE_ALL
,
6023 &dest_h
, 0, /* 0 byte dest file */
6024 SEC_RIGHTS_FILE_ALL
,
6028 torture_fail(tctx
, "setup dup extents error");
6031 status
= test_ioctl_fs_supported(tctx
, tree
, tmp_ctx
, &src_h
,
6032 FILE_SUPPORTS_BLOCK_REFCOUNTING
, &ok
);
6033 torture_assert_ntstatus_ok(tctx
, status
, "SMB2_GETINFO_FS");
6035 smb2_util_close(tree
, src_h
);
6036 smb2_util_close(tree
, dest_h
);
6037 talloc_free(tmp_ctx
);
6038 torture_skip(tctx
, "block refcounting not supported\n");
6042 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
6043 io
.generic
.in
.file
.handle
= dest_h
;
6044 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
6045 torture_assert_ntstatus_ok(tctx
, status
, "getinfo");
6046 torture_assert_int_equal(tctx
, (int)io
.all_info2
.out
.size
,
6047 0, "size after IO");
6049 /* exceed src file len */
6050 dup_ext_buf
.source_off
= 0;
6051 dup_ext_buf
.target_off
= 0;
6052 dup_ext_buf
.byte_count
= 32768 * 2;
6054 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
6056 (ndr_push_flags_fn_t
)ndr_push_fsctl_dup_extents_to_file
);
6057 torture_assert_ndr_success(tctx
, ndr_ret
,
6058 "ndr_push_fsctl_dup_extents_to_file");
6060 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
6061 torture_assert_ntstatus_equal(tctx
, status
, NT_STATUS_NOT_SUPPORTED
,
6062 "FSCTL_DUP_EXTENTS_TO_FILE");
6064 /* the file sizes shouldn't have been changed */
6066 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
6067 io
.generic
.in
.file
.handle
= src_h
;
6068 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
6069 torture_assert_ntstatus_ok(tctx
, status
, "getinfo");
6070 torture_assert_int_equal(tctx
, (int)io
.all_info2
.out
.size
,
6071 32768, "size after IO");
6074 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
6075 io
.generic
.in
.file
.handle
= dest_h
;
6076 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
6077 torture_assert_ntstatus_ok(tctx
, status
, "getinfo");
6078 torture_assert_int_equal(tctx
, (int)io
.all_info2
.out
.size
,
6079 0, "size after IO");
6081 smb2_util_close(tree
, src_h
);
6082 smb2_util_close(tree
, dest_h
);
6083 talloc_free(tmp_ctx
);
6087 static bool test_ioctl_dup_extents_len_zero(struct torture_context
*tctx
,
6088 struct smb2_tree
*tree
)
6090 struct smb2_handle src_h
;
6091 struct smb2_handle dest_h
;
6093 union smb_ioctl ioctl
;
6094 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
6095 struct fsctl_dup_extents_to_file dup_ext_buf
;
6096 enum ndr_err_code ndr_ret
;
6097 union smb_fileinfo io
;
6100 ok
= test_setup_dup_extents(tctx
, tree
, tmp_ctx
,
6101 &src_h
, 32768, /* fill 32768 byte src file */
6102 SEC_RIGHTS_FILE_ALL
,
6103 &dest_h
, 0, /* 0 byte dest file */
6104 SEC_RIGHTS_FILE_ALL
,
6108 torture_fail(tctx
, "setup dup extents error");
6111 status
= test_ioctl_fs_supported(tctx
, tree
, tmp_ctx
, &src_h
,
6112 FILE_SUPPORTS_BLOCK_REFCOUNTING
, &ok
);
6113 torture_assert_ntstatus_ok(tctx
, status
, "SMB2_GETINFO_FS");
6115 smb2_util_close(tree
, src_h
);
6116 smb2_util_close(tree
, dest_h
);
6117 talloc_free(tmp_ctx
);
6118 torture_skip(tctx
, "block refcounting not supported\n");
6122 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
6123 io
.generic
.in
.file
.handle
= dest_h
;
6124 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
6125 torture_assert_ntstatus_ok(tctx
, status
, "getinfo");
6126 torture_assert_int_equal(tctx
, (int)io
.all_info2
.out
.size
,
6127 0, "size after IO");
6129 dup_ext_buf
.source_off
= 0;
6130 dup_ext_buf
.target_off
= 0;
6131 dup_ext_buf
.byte_count
= 0;
6133 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
6135 (ndr_push_flags_fn_t
)ndr_push_fsctl_dup_extents_to_file
);
6136 torture_assert_ndr_success(tctx
, ndr_ret
,
6137 "ndr_push_fsctl_dup_extents_to_file");
6139 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
6140 torture_assert_ntstatus_ok(tctx
, status
, "FSCTL_DUP_EXTENTS_TO_FILE");
6142 /* the file sizes shouldn't have been changed */
6144 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
6145 io
.generic
.in
.file
.handle
= src_h
;
6146 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
6147 torture_assert_ntstatus_ok(tctx
, status
, "getinfo");
6148 torture_assert_int_equal(tctx
, (int)io
.all_info2
.out
.size
,
6149 32768, "size after IO");
6152 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
6153 io
.generic
.in
.file
.handle
= dest_h
;
6154 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
6155 torture_assert_ntstatus_ok(tctx
, status
, "getinfo");
6156 torture_assert_int_equal(tctx
, (int)io
.all_info2
.out
.size
,
6157 0, "size after IO");
6159 smb2_util_close(tree
, src_h
);
6160 smb2_util_close(tree
, dest_h
);
6161 talloc_free(tmp_ctx
);
6165 static bool test_ioctl_dup_extents_sparse_src(struct torture_context
*tctx
,
6166 struct smb2_tree
*tree
)
6168 struct smb2_handle src_h
;
6169 struct smb2_handle dest_h
;
6171 union smb_ioctl ioctl
;
6172 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
6173 struct fsctl_dup_extents_to_file dup_ext_buf
;
6174 enum ndr_err_code ndr_ret
;
6175 union smb_setfileinfo sinfo
;
6178 ok
= test_setup_dup_extents(tctx
, tree
, tmp_ctx
,
6179 &src_h
, 0, /* filled after sparse flag */
6180 SEC_RIGHTS_FILE_ALL
,
6181 &dest_h
, 0, /* 0 byte dest file */
6182 SEC_RIGHTS_FILE_ALL
,
6186 torture_fail(tctx
, "setup dup extents error");
6189 status
= test_ioctl_fs_supported(tctx
, tree
, tmp_ctx
, &src_h
,
6190 FILE_SUPPORTS_BLOCK_REFCOUNTING
6191 | FILE_SUPPORTS_SPARSE_FILES
, &ok
);
6192 torture_assert_ntstatus_ok(tctx
, status
, "SMB2_GETINFO_FS");
6194 smb2_util_close(tree
, src_h
);
6195 smb2_util_close(tree
, dest_h
);
6196 talloc_free(tmp_ctx
);
6198 "block refcounting and sparse files not supported\n");
6201 /* set sparse flag on src */
6202 status
= test_ioctl_sparse_req(tctx
, tmp_ctx
, tree
, src_h
, true);
6203 torture_assert_ntstatus_ok(tctx
, status
, "FSCTL_SET_SPARSE");
6205 ok
= write_pattern(tctx
, tree
, tmp_ctx
, src_h
, 0, 4096, 0);
6206 torture_assert(tctx
, ok
, "write pattern");
6210 sinfo
.end_of_file_info
.level
= RAW_SFILEINFO_END_OF_FILE_INFORMATION
;
6211 sinfo
.end_of_file_info
.in
.file
.handle
= dest_h
;
6212 sinfo
.end_of_file_info
.in
.size
= 4096;
6213 status
= smb2_setinfo_file(tree
, &sinfo
);
6214 torture_assert_ntstatus_ok(tctx
, status
, "smb2_setinfo_file");
6216 /* copy all src file data */
6217 dup_ext_buf
.source_off
= 0;
6218 dup_ext_buf
.target_off
= 0;
6219 dup_ext_buf
.byte_count
= 4096;
6221 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
6223 (ndr_push_flags_fn_t
)ndr_push_fsctl_dup_extents_to_file
);
6224 torture_assert_ndr_success(tctx
, ndr_ret
,
6225 "ndr_push_fsctl_dup_extents_to_file");
6228 * src is sparse, but spec says: 2.3.8 FSCTL_DUPLICATE_EXTENTS_TO_FILE
6229 * Reply... STATUS_NOT_SUPPORTED: Target file is sparse, while source
6230 * is a non-sparse file.
6232 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
6233 torture_assert_ntstatus_equal(tctx
, status
, NT_STATUS_NOT_SUPPORTED
,
6234 "FSCTL_DUP_EXTENTS_TO_FILE");
6236 smb2_util_close(tree
, src_h
);
6237 smb2_util_close(tree
, dest_h
);
6238 talloc_free(tmp_ctx
);
6242 static bool test_ioctl_dup_extents_sparse_dest(struct torture_context
*tctx
,
6243 struct smb2_tree
*tree
)
6245 struct smb2_handle src_h
;
6246 struct smb2_handle dest_h
;
6248 union smb_ioctl ioctl
;
6249 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
6250 struct fsctl_dup_extents_to_file dup_ext_buf
;
6251 enum ndr_err_code ndr_ret
;
6252 union smb_setfileinfo sinfo
;
6255 ok
= test_setup_dup_extents(tctx
, tree
, tmp_ctx
,
6256 &src_h
, 4096, /* fill 4096 byte src file */
6257 SEC_RIGHTS_FILE_ALL
,
6258 &dest_h
, 0, /* 0 byte dest file */
6259 SEC_RIGHTS_FILE_ALL
,
6263 torture_fail(tctx
, "setup dup extents error");
6266 status
= test_ioctl_fs_supported(tctx
, tree
, tmp_ctx
, &src_h
,
6267 FILE_SUPPORTS_BLOCK_REFCOUNTING
6268 | FILE_SUPPORTS_SPARSE_FILES
, &ok
);
6269 torture_assert_ntstatus_ok(tctx
, status
, "SMB2_GETINFO_FS");
6271 smb2_util_close(tree
, src_h
);
6272 smb2_util_close(tree
, dest_h
);
6273 talloc_free(tmp_ctx
);
6275 "block refcounting and sparse files not supported\n");
6278 /* set sparse flag on dest */
6279 status
= test_ioctl_sparse_req(tctx
, tmp_ctx
, tree
, dest_h
, true);
6280 torture_assert_ntstatus_ok(tctx
, status
, "FSCTL_SET_SPARSE");
6284 sinfo
.end_of_file_info
.level
= RAW_SFILEINFO_END_OF_FILE_INFORMATION
;
6285 sinfo
.end_of_file_info
.in
.file
.handle
= dest_h
;
6286 sinfo
.end_of_file_info
.in
.size
= dup_ext_buf
.byte_count
;
6287 status
= smb2_setinfo_file(tree
, &sinfo
);
6288 torture_assert_ntstatus_ok(tctx
, status
, "smb2_setinfo_file");
6290 /* copy all src file data */
6291 dup_ext_buf
.source_off
= 0;
6292 dup_ext_buf
.target_off
= 0;
6293 dup_ext_buf
.byte_count
= 4096;
6295 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
6297 (ndr_push_flags_fn_t
)ndr_push_fsctl_dup_extents_to_file
);
6298 torture_assert_ndr_success(tctx
, ndr_ret
,
6299 "ndr_push_fsctl_dup_extents_to_file");
6302 * dest is sparse, but spec says: 2.3.8 FSCTL_DUPLICATE_EXTENTS_TO_FILE
6303 * Reply... STATUS_NOT_SUPPORTED: Target file is sparse, while source
6304 * is a non-sparse file.
6306 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
6307 torture_assert_ntstatus_ok(tctx
, status
, "FSCTL_DUP_EXTENTS_TO_FILE");
6309 smb2_util_close(tree
, src_h
);
6310 smb2_util_close(tree
, dest_h
);
6311 talloc_free(tmp_ctx
);
6315 static bool test_ioctl_dup_extents_sparse_both(struct torture_context
*tctx
,
6316 struct smb2_tree
*tree
)
6318 struct smb2_handle src_h
;
6319 struct smb2_handle dest_h
;
6321 union smb_ioctl ioctl
;
6322 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
6323 struct fsctl_dup_extents_to_file dup_ext_buf
;
6324 enum ndr_err_code ndr_ret
;
6325 union smb_setfileinfo sinfo
;
6328 ok
= test_setup_dup_extents(tctx
, tree
, tmp_ctx
,
6329 &src_h
, 0, /* fill 4096 byte src file */
6330 SEC_RIGHTS_FILE_ALL
,
6331 &dest_h
, 0, /* 0 byte dest file */
6332 SEC_RIGHTS_FILE_ALL
,
6336 torture_fail(tctx
, "setup dup extents error");
6339 status
= test_ioctl_fs_supported(tctx
, tree
, tmp_ctx
, &src_h
,
6340 FILE_SUPPORTS_BLOCK_REFCOUNTING
6341 | FILE_SUPPORTS_SPARSE_FILES
, &ok
);
6342 torture_assert_ntstatus_ok(tctx
, status
, "SMB2_GETINFO_FS");
6344 smb2_util_close(tree
, src_h
);
6345 smb2_util_close(tree
, dest_h
);
6346 talloc_free(tmp_ctx
);
6348 "block refcounting and sparse files not supported\n");
6351 /* set sparse flag on src and dest */
6352 status
= test_ioctl_sparse_req(tctx
, tmp_ctx
, tree
, src_h
, true);
6353 torture_assert_ntstatus_ok(tctx
, status
, "FSCTL_SET_SPARSE");
6354 status
= test_ioctl_sparse_req(tctx
, tmp_ctx
, tree
, dest_h
, true);
6355 torture_assert_ntstatus_ok(tctx
, status
, "FSCTL_SET_SPARSE");
6357 ok
= write_pattern(tctx
, tree
, tmp_ctx
, src_h
, 0, 4096, 0);
6358 torture_assert(tctx
, ok
, "write pattern");
6362 sinfo
.end_of_file_info
.level
= RAW_SFILEINFO_END_OF_FILE_INFORMATION
;
6363 sinfo
.end_of_file_info
.in
.file
.handle
= dest_h
;
6364 sinfo
.end_of_file_info
.in
.size
= 4096;
6365 status
= smb2_setinfo_file(tree
, &sinfo
);
6366 torture_assert_ntstatus_ok(tctx
, status
, "smb2_setinfo_file");
6368 /* copy all src file data */
6369 dup_ext_buf
.source_off
= 0;
6370 dup_ext_buf
.target_off
= 0;
6371 dup_ext_buf
.byte_count
= 4096;
6373 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
6375 (ndr_push_flags_fn_t
)ndr_push_fsctl_dup_extents_to_file
);
6376 torture_assert_ndr_success(tctx
, ndr_ret
,
6377 "ndr_push_fsctl_dup_extents_to_file");
6379 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
6380 torture_assert_ntstatus_ok(tctx
, status
, "FSCTL_DUP_EXTENTS_TO_FILE");
6382 smb2_util_close(tree
, src_h
);
6383 smb2_util_close(tree
, dest_h
);
6385 /* reopen for pattern check */
6386 ok
= test_setup_open(tctx
, tree
, tmp_ctx
, FNAME2
, &dest_h
,
6387 SEC_RIGHTS_FILE_ALL
, FILE_ATTRIBUTE_NORMAL
);
6388 torture_assert_ntstatus_ok(tctx
, status
, "dest open ater dup");
6390 ok
= check_pattern(tctx
, tree
, tmp_ctx
, dest_h
, 0, 4096, 0);
6392 torture_fail(tctx
, "inconsistent file data");
6395 smb2_util_close(tree
, dest_h
);
6396 talloc_free(tmp_ctx
);
6400 static bool test_ioctl_dup_extents_src_is_dest(struct torture_context
*tctx
,
6401 struct smb2_tree
*tree
)
6403 struct smb2_handle src_h
;
6404 struct smb2_handle dest_h
;
6406 union smb_ioctl ioctl
;
6407 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
6408 struct fsctl_dup_extents_to_file dup_ext_buf
;
6409 enum ndr_err_code ndr_ret
;
6410 union smb_fileinfo io
;
6413 ok
= test_setup_dup_extents(tctx
, tree
, tmp_ctx
,
6414 &src_h
, 32768, /* fill 32768 byte src file */
6415 SEC_RIGHTS_FILE_ALL
,
6417 SEC_RIGHTS_FILE_ALL
,
6421 torture_fail(tctx
, "setup dup extents error");
6423 /* dest_h not needed for this test */
6424 smb2_util_close(tree
, dest_h
);
6426 status
= test_ioctl_fs_supported(tctx
, tree
, tmp_ctx
, &src_h
,
6427 FILE_SUPPORTS_BLOCK_REFCOUNTING
, &ok
);
6428 torture_assert_ntstatus_ok(tctx
, status
, "SMB2_GETINFO_FS");
6430 smb2_util_close(tree
, src_h
);
6431 talloc_free(tmp_ctx
);
6432 torture_skip(tctx
, "block refcounting not supported\n");
6435 /* src and dest are the same file handle */
6436 ioctl
.smb2
.in
.file
.handle
= src_h
;
6438 /* no overlap between src and tgt */
6439 dup_ext_buf
.source_off
= 0;
6440 dup_ext_buf
.target_off
= 16384;
6441 dup_ext_buf
.byte_count
= 16384;
6443 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
6445 (ndr_push_flags_fn_t
)ndr_push_fsctl_dup_extents_to_file
);
6446 torture_assert_ndr_success(tctx
, ndr_ret
,
6447 "ndr_push_fsctl_dup_extents_to_file");
6449 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
6450 torture_assert_ntstatus_ok(tctx
, status
, "FSCTL_DUP_EXTENTS_TO_FILE");
6452 /* the file size shouldn't have been changed */
6454 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
6455 io
.generic
.in
.file
.handle
= src_h
;
6456 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
6457 torture_assert_ntstatus_ok(tctx
, status
, "getinfo");
6458 torture_assert_int_equal(tctx
, (int)io
.all_info2
.out
.size
,
6459 32768, "size after IO");
6461 ok
= check_pattern(tctx
, tree
, tmp_ctx
, src_h
, 0, 16384, 0);
6463 torture_fail(tctx
, "inconsistent file data");
6465 ok
= check_pattern(tctx
, tree
, tmp_ctx
, src_h
, 16384, 16384, 0);
6467 torture_fail(tctx
, "inconsistent file data");
6470 smb2_util_close(tree
, src_h
);
6471 talloc_free(tmp_ctx
);
6476 * unlike copy-chunk, dup extents doesn't support overlapping ranges between
6477 * source and target. This makes it a *lot* cleaner to implement on the server.
6480 test_ioctl_dup_extents_src_is_dest_overlap(struct torture_context
*tctx
,
6481 struct smb2_tree
*tree
)
6483 struct smb2_handle src_h
;
6484 struct smb2_handle dest_h
;
6486 union smb_ioctl ioctl
;
6487 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
6488 struct fsctl_dup_extents_to_file dup_ext_buf
;
6489 enum ndr_err_code ndr_ret
;
6490 union smb_fileinfo io
;
6493 ok
= test_setup_dup_extents(tctx
, tree
, tmp_ctx
,
6494 &src_h
, 32768, /* fill 32768 byte src file */
6495 SEC_RIGHTS_FILE_ALL
,
6497 SEC_RIGHTS_FILE_ALL
,
6501 torture_fail(tctx
, "setup dup extents error");
6503 /* dest_h not needed for this test */
6504 smb2_util_close(tree
, dest_h
);
6506 status
= test_ioctl_fs_supported(tctx
, tree
, tmp_ctx
, &src_h
,
6507 FILE_SUPPORTS_BLOCK_REFCOUNTING
, &ok
);
6508 torture_assert_ntstatus_ok(tctx
, status
, "SMB2_GETINFO_FS");
6510 smb2_util_close(tree
, src_h
);
6511 talloc_free(tmp_ctx
);
6512 torture_skip(tctx
, "block refcounting not supported\n");
6515 /* src and dest are the same file handle */
6516 ioctl
.smb2
.in
.file
.handle
= src_h
;
6518 /* 8K overlap between src and tgt */
6519 dup_ext_buf
.source_off
= 0;
6520 dup_ext_buf
.target_off
= 8192;
6521 dup_ext_buf
.byte_count
= 16384;
6523 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
6525 (ndr_push_flags_fn_t
)ndr_push_fsctl_dup_extents_to_file
);
6526 torture_assert_ndr_success(tctx
, ndr_ret
,
6527 "ndr_push_fsctl_dup_extents_to_file");
6529 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
6530 torture_assert_ntstatus_equal(tctx
, status
, NT_STATUS_NOT_SUPPORTED
,
6531 "FSCTL_DUP_EXTENTS_TO_FILE");
6533 /* the file size and data should match beforehand */
6535 io
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
6536 io
.generic
.in
.file
.handle
= src_h
;
6537 status
= smb2_getinfo_file(tree
, tmp_ctx
, &io
);
6538 torture_assert_ntstatus_ok(tctx
, status
, "getinfo");
6539 torture_assert_int_equal(tctx
, (int)io
.all_info2
.out
.size
,
6540 32768, "size after IO");
6542 ok
= check_pattern(tctx
, tree
, tmp_ctx
, src_h
, 0, 32768, 0);
6544 torture_fail(tctx
, "inconsistent file data");
6547 smb2_util_close(tree
, src_h
);
6548 talloc_free(tmp_ctx
);
6553 * The compression tests won't run against Windows servers yet - ReFS doesn't
6554 * (yet) offer support for compression.
6556 static bool test_ioctl_dup_extents_compressed_src(struct torture_context
*tctx
,
6557 struct smb2_tree
*tree
)
6559 struct smb2_handle src_h
;
6560 struct smb2_handle dest_h
;
6562 union smb_ioctl ioctl
;
6563 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
6564 struct fsctl_dup_extents_to_file dup_ext_buf
;
6565 enum ndr_err_code ndr_ret
;
6566 union smb_setfileinfo sinfo
;
6569 ok
= test_setup_dup_extents(tctx
, tree
, tmp_ctx
,
6570 &src_h
, 0, /* filled after compressed flag */
6571 SEC_RIGHTS_FILE_ALL
,
6573 SEC_RIGHTS_FILE_ALL
,
6577 torture_fail(tctx
, "setup dup extents error");
6580 status
= test_ioctl_fs_supported(tctx
, tree
, tmp_ctx
, &src_h
,
6581 FILE_SUPPORTS_BLOCK_REFCOUNTING
6582 | FILE_FILE_COMPRESSION
, &ok
);
6583 torture_assert_ntstatus_ok(tctx
, status
, "SMB2_GETINFO_FS");
6585 smb2_util_close(tree
, src_h
);
6586 smb2_util_close(tree
, dest_h
);
6587 talloc_free(tmp_ctx
);
6589 "block refcounting and compressed files not supported\n");
6592 /* set compressed flag on src */
6593 status
= test_ioctl_compress_set(tctx
, tmp_ctx
, tree
, src_h
,
6594 COMPRESSION_FORMAT_DEFAULT
);
6595 torture_assert_ntstatus_ok(tctx
, status
, "FSCTL_SET_COMPRESSION");
6597 ok
= write_pattern(tctx
, tree
, tmp_ctx
, src_h
, 0, 4096, 0);
6598 torture_assert(tctx
, ok
, "write pattern");
6602 sinfo
.end_of_file_info
.level
= RAW_SFILEINFO_END_OF_FILE_INFORMATION
;
6603 sinfo
.end_of_file_info
.in
.file
.handle
= dest_h
;
6604 sinfo
.end_of_file_info
.in
.size
= 4096;
6605 status
= smb2_setinfo_file(tree
, &sinfo
);
6606 torture_assert_ntstatus_ok(tctx
, status
, "smb2_setinfo_file");
6608 /* copy all src file data */
6609 dup_ext_buf
.source_off
= 0;
6610 dup_ext_buf
.target_off
= 0;
6611 dup_ext_buf
.byte_count
= 4096;
6613 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
6615 (ndr_push_flags_fn_t
)ndr_push_fsctl_dup_extents_to_file
);
6616 torture_assert_ndr_success(tctx
, ndr_ret
,
6617 "ndr_push_fsctl_dup_extents_to_file");
6619 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
6620 torture_assert_ntstatus_ok(tctx
, status
,
6621 "FSCTL_DUP_EXTENTS_TO_FILE");
6623 ok
= check_pattern(tctx
, tree
, tmp_ctx
, dest_h
, 0, 4096, 0);
6625 torture_fail(tctx
, "inconsistent file data");
6628 smb2_util_close(tree
, src_h
);
6629 smb2_util_close(tree
, dest_h
);
6630 talloc_free(tmp_ctx
);
6634 static bool test_ioctl_dup_extents_compressed_dest(struct torture_context
*tctx
,
6635 struct smb2_tree
*tree
)
6637 struct smb2_handle src_h
;
6638 struct smb2_handle dest_h
;
6640 union smb_ioctl ioctl
;
6641 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
6642 struct fsctl_dup_extents_to_file dup_ext_buf
;
6643 enum ndr_err_code ndr_ret
;
6644 union smb_setfileinfo sinfo
;
6647 ok
= test_setup_dup_extents(tctx
, tree
, tmp_ctx
,
6649 SEC_RIGHTS_FILE_ALL
,
6651 SEC_RIGHTS_FILE_ALL
,
6655 torture_fail(tctx
, "setup dup extents error");
6658 status
= test_ioctl_fs_supported(tctx
, tree
, tmp_ctx
, &src_h
,
6659 FILE_SUPPORTS_BLOCK_REFCOUNTING
6660 | FILE_FILE_COMPRESSION
, &ok
);
6661 torture_assert_ntstatus_ok(tctx
, status
, "SMB2_GETINFO_FS");
6663 smb2_util_close(tree
, src_h
);
6664 smb2_util_close(tree
, dest_h
);
6665 talloc_free(tmp_ctx
);
6667 "block refcounting and compressed files not supported\n");
6670 /* set compressed flag on dest */
6671 status
= test_ioctl_compress_set(tctx
, tmp_ctx
, tree
, dest_h
,
6672 COMPRESSION_FORMAT_DEFAULT
);
6673 torture_assert_ntstatus_ok(tctx
, status
, "FSCTL_SET_COMPRESSION");
6677 sinfo
.end_of_file_info
.level
= RAW_SFILEINFO_END_OF_FILE_INFORMATION
;
6678 sinfo
.end_of_file_info
.in
.file
.handle
= dest_h
;
6679 sinfo
.end_of_file_info
.in
.size
= 4096;
6680 status
= smb2_setinfo_file(tree
, &sinfo
);
6681 torture_assert_ntstatus_ok(tctx
, status
, "smb2_setinfo_file");
6683 /* copy all src file data */
6684 dup_ext_buf
.source_off
= 0;
6685 dup_ext_buf
.target_off
= 0;
6686 dup_ext_buf
.byte_count
= 4096;
6688 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
6690 (ndr_push_flags_fn_t
)ndr_push_fsctl_dup_extents_to_file
);
6691 torture_assert_ndr_success(tctx
, ndr_ret
,
6692 "ndr_push_fsctl_dup_extents_to_file");
6694 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
6695 torture_assert_ntstatus_ok(tctx
, status
,
6696 "FSCTL_DUP_EXTENTS_TO_FILE");
6698 ok
= check_pattern(tctx
, tree
, tmp_ctx
, dest_h
, 0, 4096, 0);
6700 torture_fail(tctx
, "inconsistent file data");
6703 smb2_util_close(tree
, src_h
);
6704 smb2_util_close(tree
, dest_h
);
6705 talloc_free(tmp_ctx
);
6709 static bool test_ioctl_dup_extents_bad_handle(struct torture_context
*tctx
,
6710 struct smb2_tree
*tree
)
6712 struct smb2_handle src_h
;
6713 struct smb2_handle dest_h
;
6714 struct smb2_handle bogus_h
;
6716 union smb_ioctl ioctl
;
6717 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
6718 struct fsctl_dup_extents_to_file dup_ext_buf
;
6719 enum ndr_err_code ndr_ret
;
6722 ok
= test_setup_dup_extents(tctx
, tree
, tmp_ctx
,
6723 &src_h
, 32768, /* fill 32768 byte src file */
6724 SEC_RIGHTS_FILE_ALL
,
6726 SEC_RIGHTS_FILE_ALL
,
6730 torture_fail(tctx
, "setup dup extents error");
6733 status
= test_ioctl_fs_supported(tctx
, tree
, tmp_ctx
, &src_h
,
6734 FILE_SUPPORTS_BLOCK_REFCOUNTING
, &ok
);
6735 torture_assert_ntstatus_ok(tctx
, status
, "SMB2_GETINFO_FS");
6737 smb2_util_close(tree
, src_h
);
6738 smb2_util_close(tree
, dest_h
);
6739 talloc_free(tmp_ctx
);
6740 torture_skip(tctx
, "block refcounting not supported\n");
6743 /* open and close a file, keeping the handle as now a "bogus" handle */
6744 ok
= test_setup_create_fill(tctx
, tree
, tmp_ctx
, "bogus_file",
6745 &bogus_h
, 0, SEC_RIGHTS_FILE_ALL
,
6746 FILE_ATTRIBUTE_NORMAL
);
6747 torture_assert(tctx
, ok
, "bogus file create fill");
6748 smb2_util_close(tree
, bogus_h
);
6750 /* bogus dest file handle */
6751 ioctl
.smb2
.in
.file
.handle
= bogus_h
;
6753 dup_ext_buf
.source_off
= 0;
6754 dup_ext_buf
.target_off
= 0;
6755 dup_ext_buf
.byte_count
= 32768;
6757 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
6759 (ndr_push_flags_fn_t
)ndr_push_fsctl_dup_extents_to_file
);
6760 torture_assert_ndr_success(tctx
, ndr_ret
,
6761 "ndr_push_fsctl_dup_extents_to_file");
6763 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
6764 torture_assert_ntstatus_equal(tctx
, status
, NT_STATUS_FILE_CLOSED
,
6765 "FSCTL_DUP_EXTENTS_TO_FILE");
6767 ok
= check_pattern(tctx
, tree
, tmp_ctx
, src_h
, 0, 32768, 0);
6769 torture_fail(tctx
, "inconsistent file data");
6771 ok
= check_pattern(tctx
, tree
, tmp_ctx
, dest_h
, 0, 32768, 0);
6773 torture_fail(tctx
, "inconsistent file data");
6776 /* reinstate dest, add bogus src file handle */
6777 ioctl
.smb2
.in
.file
.handle
= dest_h
;
6778 smb2_push_handle(dup_ext_buf
.source_fid
, &bogus_h
);
6780 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
6782 (ndr_push_flags_fn_t
)ndr_push_fsctl_dup_extents_to_file
);
6783 torture_assert_ndr_success(tctx
, ndr_ret
,
6784 "ndr_push_fsctl_dup_extents_to_file");
6786 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
6787 torture_assert_ntstatus_equal(tctx
, status
, NT_STATUS_INVALID_HANDLE
,
6788 "FSCTL_DUP_EXTENTS_TO_FILE");
6790 ok
= check_pattern(tctx
, tree
, tmp_ctx
, src_h
, 0, 32768, 0);
6792 torture_fail(tctx
, "inconsistent file data");
6794 ok
= check_pattern(tctx
, tree
, tmp_ctx
, dest_h
, 0, 32768, 0);
6796 torture_fail(tctx
, "inconsistent file data");
6799 smb2_util_close(tree
, src_h
);
6800 smb2_util_close(tree
, dest_h
);
6801 talloc_free(tmp_ctx
);
6805 static bool test_ioctl_dup_extents_src_lck(struct torture_context
*tctx
,
6806 struct smb2_tree
*tree
)
6808 struct smb2_handle src_h
;
6809 struct smb2_handle src_h2
;
6810 struct smb2_handle dest_h
;
6812 union smb_ioctl ioctl
;
6813 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
6814 struct fsctl_dup_extents_to_file dup_ext_buf
;
6815 enum ndr_err_code ndr_ret
;
6817 struct smb2_lock lck
;
6818 struct smb2_lock_element el
[1];
6820 ok
= test_setup_dup_extents(tctx
, tree
, tmp_ctx
,
6821 &src_h
, 32768, /* fill 32768 byte src file */
6822 SEC_RIGHTS_FILE_ALL
,
6824 SEC_RIGHTS_FILE_ALL
,
6828 torture_fail(tctx
, "setup dup extents error");
6831 status
= test_ioctl_fs_supported(tctx
, tree
, tmp_ctx
, &src_h
,
6832 FILE_SUPPORTS_BLOCK_REFCOUNTING
, &ok
);
6833 torture_assert_ntstatus_ok(tctx
, status
, "SMB2_GETINFO_FS");
6835 smb2_util_close(tree
, src_h
);
6836 smb2_util_close(tree
, dest_h
);
6837 talloc_free(tmp_ctx
);
6838 torture_skip(tctx
, "block refcounting not supported\n");
6841 /* dest pattern is different to src */
6842 ok
= write_pattern(tctx
, tree
, tmp_ctx
, dest_h
, 0, 32768, 32768);
6843 torture_assert(tctx
, ok
, "write pattern");
6845 /* setup dup ext req, values used for locking */
6846 dup_ext_buf
.source_off
= 0;
6847 dup_ext_buf
.target_off
= 0;
6848 dup_ext_buf
.byte_count
= 32768;
6850 /* open and lock the dup extents src file */
6851 status
= torture_smb2_testfile(tree
, FNAME
, &src_h2
);
6852 torture_assert_ntstatus_ok(tctx
, status
, "2nd src open");
6854 lck
.in
.lock_count
= 0x0001;
6855 lck
.in
.lock_sequence
= 0x00000000;
6856 lck
.in
.file
.handle
= src_h2
;
6858 el
[0].offset
= dup_ext_buf
.source_off
;
6859 el
[0].length
= dup_ext_buf
.byte_count
;
6861 el
[0].flags
= SMB2_LOCK_FLAG_EXCLUSIVE
;
6863 status
= smb2_lock(tree
, &lck
);
6864 torture_assert_ntstatus_ok(tctx
, status
, "lock");
6866 status
= smb2_util_write(tree
, src_h
,
6867 "conflicted", 0, sizeof("conflicted"));
6868 torture_assert_ntstatus_equal(tctx
, status
,
6869 NT_STATUS_FILE_LOCK_CONFLICT
, "file write");
6871 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
6873 (ndr_push_flags_fn_t
)ndr_push_fsctl_dup_extents_to_file
);
6874 torture_assert_ndr_success(tctx
, ndr_ret
,
6875 "ndr_push_fsctl_dup_extents_to_file");
6878 * In contrast to copy-chunk, dup extents doesn't cause a lock conflict
6881 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
6882 torture_assert_ntstatus_ok(tctx
, status
, "FSCTL_DUP_EXTENTS_TO_FILE");
6884 ok
= check_pattern(tctx
, tree
, tmp_ctx
, dest_h
, 0, 32768, 0);
6886 torture_fail(tctx
, "inconsistent file data");
6889 lck
.in
.lock_count
= 0x0001;
6890 lck
.in
.lock_sequence
= 0x00000001;
6891 lck
.in
.file
.handle
= src_h2
;
6893 el
[0].offset
= dup_ext_buf
.source_off
;
6894 el
[0].length
= dup_ext_buf
.byte_count
;
6896 el
[0].flags
= SMB2_LOCK_FLAG_UNLOCK
;
6897 status
= smb2_lock(tree
, &lck
);
6898 torture_assert_ntstatus_ok(tctx
, status
, "unlock");
6900 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
6901 torture_assert_ntstatus_ok(tctx
, status
,
6902 "FSCTL_DUP_EXTENTS_TO_FILE unlocked");
6904 ok
= check_pattern(tctx
, tree
, tmp_ctx
, dest_h
, 0, 32768, 0);
6906 torture_fail(tctx
, "inconsistent file data");
6909 smb2_util_close(tree
, src_h2
);
6910 smb2_util_close(tree
, src_h
);
6911 smb2_util_close(tree
, dest_h
);
6912 talloc_free(tmp_ctx
);
6916 static bool test_ioctl_dup_extents_dest_lck(struct torture_context
*tctx
,
6917 struct smb2_tree
*tree
)
6919 struct smb2_handle src_h
;
6920 struct smb2_handle dest_h
;
6921 struct smb2_handle dest_h2
;
6923 union smb_ioctl ioctl
;
6924 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
6925 struct fsctl_dup_extents_to_file dup_ext_buf
;
6926 enum ndr_err_code ndr_ret
;
6928 struct smb2_lock lck
;
6929 struct smb2_lock_element el
[1];
6931 ok
= test_setup_dup_extents(tctx
, tree
, tmp_ctx
,
6932 &src_h
, 32768, /* fill 32768 byte src file */
6933 SEC_RIGHTS_FILE_ALL
,
6935 SEC_RIGHTS_FILE_ALL
,
6939 torture_fail(tctx
, "setup dup extents error");
6942 status
= test_ioctl_fs_supported(tctx
, tree
, tmp_ctx
, &src_h
,
6943 FILE_SUPPORTS_BLOCK_REFCOUNTING
, &ok
);
6944 torture_assert_ntstatus_ok(tctx
, status
, "SMB2_GETINFO_FS");
6946 smb2_util_close(tree
, src_h
);
6947 smb2_util_close(tree
, dest_h
);
6948 talloc_free(tmp_ctx
);
6949 torture_skip(tctx
, "block refcounting not supported\n");
6952 /* dest pattern is different to src */
6953 ok
= write_pattern(tctx
, tree
, tmp_ctx
, dest_h
, 0, 32768, 32768);
6954 torture_assert(tctx
, ok
, "write pattern");
6956 /* setup dup ext req, values used for locking */
6957 dup_ext_buf
.source_off
= 0;
6958 dup_ext_buf
.target_off
= 0;
6959 dup_ext_buf
.byte_count
= 32768;
6961 /* open and lock the dup extents dest file */
6962 status
= torture_smb2_testfile(tree
, FNAME2
, &dest_h2
);
6963 torture_assert_ntstatus_ok(tctx
, status
, "2nd src open");
6965 lck
.in
.lock_count
= 0x0001;
6966 lck
.in
.lock_sequence
= 0x00000000;
6967 lck
.in
.file
.handle
= dest_h2
;
6969 el
[0].offset
= dup_ext_buf
.source_off
;
6970 el
[0].length
= dup_ext_buf
.byte_count
;
6972 el
[0].flags
= SMB2_LOCK_FLAG_EXCLUSIVE
;
6974 status
= smb2_lock(tree
, &lck
);
6975 torture_assert_ntstatus_ok(tctx
, status
, "lock");
6977 status
= smb2_util_write(tree
, dest_h
,
6978 "conflicted", 0, sizeof("conflicted"));
6979 torture_assert_ntstatus_equal(tctx
, status
,
6980 NT_STATUS_FILE_LOCK_CONFLICT
, "file write");
6982 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
6984 (ndr_push_flags_fn_t
)ndr_push_fsctl_dup_extents_to_file
);
6985 torture_assert_ndr_success(tctx
, ndr_ret
,
6986 "ndr_push_fsctl_dup_extents_to_file");
6989 * In contrast to copy-chunk, dup extents doesn't cause a lock conflict
6992 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
6993 torture_assert_ntstatus_ok(tctx
, status
, "FSCTL_DUP_EXTENTS_TO_FILE");
6995 lck
.in
.lock_count
= 0x0001;
6996 lck
.in
.lock_sequence
= 0x00000001;
6997 lck
.in
.file
.handle
= dest_h2
;
6999 el
[0].offset
= dup_ext_buf
.source_off
;
7000 el
[0].length
= dup_ext_buf
.byte_count
;
7002 el
[0].flags
= SMB2_LOCK_FLAG_UNLOCK
;
7003 status
= smb2_lock(tree
, &lck
);
7004 torture_assert_ntstatus_ok(tctx
, status
, "unlock");
7006 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
7007 torture_assert_ntstatus_ok(tctx
, status
,
7008 "FSCTL_DUP_EXTENTS_TO_FILE unlocked");
7010 ok
= check_pattern(tctx
, tree
, tmp_ctx
, dest_h
, 0, 32768, 0);
7012 torture_fail(tctx
, "inconsistent file data");
7015 smb2_util_close(tree
, src_h
);
7016 smb2_util_close(tree
, dest_h
);
7017 smb2_util_close(tree
, dest_h2
);
7018 talloc_free(tmp_ctx
);
7023 basic regression test for BUG 14607
7024 https://bugzilla.samba.org/show_bug.cgi?id=14607
7026 static bool test_ioctl_bug14607(struct torture_context
*torture
,
7027 struct smb2_tree
*tree
)
7029 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
7030 uint32_t timeout_msec
;
7032 DATA_BLOB out_input_buffer
= data_blob_null
;
7033 DATA_BLOB out_output_buffer
= data_blob_null
;
7035 timeout_msec
= tree
->session
->transport
->options
.request_timeout
* 1000;
7037 status
= smb2cli_ioctl(tree
->session
->transport
->conn
,
7039 tree
->session
->smbXcli
,
7041 UINT64_MAX
, /* in_fid_persistent */
7042 UINT64_MAX
, /* in_fid_volatile */
7043 FSCTL_SMBTORTURE_IOCTL_RESPONSE_BODY_PADDING8
,
7044 0, /* in_max_input_length */
7045 NULL
, /* in_input_buffer */
7046 1, /* in_max_output_length */
7047 NULL
, /* in_output_buffer */
7048 SMB2_IOCTL_FLAG_IS_FSCTL
,
7051 &out_output_buffer
);
7052 if (NT_STATUS_EQUAL(status
, NT_STATUS_NOT_SUPPORTED
) ||
7053 NT_STATUS_EQUAL(status
, NT_STATUS_FILE_CLOSED
) ||
7054 NT_STATUS_EQUAL(status
, NT_STATUS_FS_DRIVER_REQUIRED
) ||
7055 NT_STATUS_EQUAL(status
, NT_STATUS_INVALID_DEVICE_REQUEST
))
7057 torture_comment(torture
,
7058 "FSCTL_SMBTORTURE_IOCTL_RESPONSE_BODY_PADDING8: %s\n",
7060 torture_skip(torture
, "server doesn't support FSCTL_SMBTORTURE_IOCTL_RESPONSE_BODY_PADDING8\n");
7062 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SMBTORTURE_IOCTL_RESPONSE_BODY_PADDING8");
7064 torture_assert_int_equal(torture
, out_output_buffer
.length
, 1,
7066 torture_assert_int_equal(torture
, out_output_buffer
.data
[0], 8,
7067 "output buffer byte should be 8");
7069 talloc_free(tmp_ctx
);
7074 basic regression test for BUG 14769
7075 https://bugzilla.samba.org/show_bug.cgi?id=14769
7077 static bool test_ioctl_bug14769(struct torture_context
*torture
,
7078 struct smb2_tree
*tree
)
7081 const char *fname
= "bug14769";
7083 struct smb2_handle h
;
7084 struct smb2_ioctl ioctl
;
7085 struct smb2_close cl
;
7086 struct smb2_request
*smb2arr
[2] = { 0 };
7087 uint8_t tosend_msec
= 200;
7088 DATA_BLOB send_buf
= { &tosend_msec
, 1 };
7090 /* Create a test file. */
7091 smb2_util_unlink(tree
, fname
);
7092 status
= torture_smb2_testfile(tree
, fname
, &h
);
7093 torture_assert_ntstatus_ok(torture
, status
, "create bug14769");
7096 * Send (not receive) the FSCTL.
7097 * This should go async with a wait time of 200 msec.
7100 ioctl
.in
.file
.handle
= h
;
7101 ioctl
.in
.function
= FSCTL_SMBTORTURE_FSP_ASYNC_SLEEP
;
7102 ioctl
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
7103 ioctl
.in
.out
= send_buf
;
7105 smb2arr
[0] = smb2_ioctl_send(tree
, &ioctl
);
7106 torture_assert_goto(torture
,
7110 "smb2_ioctl_send failed\n");
7111 /* Immediately send the close. */
7113 cl
.in
.file
.handle
= h
;
7115 smb2arr
[1] = smb2_close_send(tree
, &cl
);
7116 torture_assert_goto(torture
,
7120 "smb2_close_send failed\n");
7122 /* Now get the FSCTL reply. */
7124 * If we suffer from bug #14769 this will fail as
7125 * the ioctl will return with NT_STATUS_FILE_CLOSED,
7126 * as the close will have closed the handle without
7127 * waiting for the ioctl to complete. The server shouldn't
7128 * complete the close until the ioctl finishes.
7130 status
= smb2_ioctl_recv(smb2arr
[0], tree
, &ioctl
);
7131 torture_assert_ntstatus_ok_goto(torture
,
7135 "smb2_ioctl_recv failed\n");
7137 /* Followed by the close reply. */
7138 status
= smb2_close_recv(smb2arr
[1], &cl
);
7139 torture_assert_ntstatus_ok_goto(torture
,
7143 "smb2_ioctl_close failed\n");
7147 smb2_util_unlink(tree
, fname
);
7152 basic regression test for BUG 14788,
7153 with FSCTL_VALIDATE_NEGOTIATE_INFO
7154 https://bugzilla.samba.org/show_bug.cgi?id=14788
7156 static bool test_ioctl_bug14788_VALIDATE_NEGOTIATE(struct torture_context
*torture
,
7157 struct smb2_tree
*tree0
)
7159 const char *host
= torture_setting_string(torture
, "host", NULL
);
7160 const char *share
= torture_setting_string(torture
, "share", NULL
);
7161 const char *noperm_share
= torture_setting_string(torture
, "noperm_share", "noperm");
7162 struct smb2_transport
*transport0
= tree0
->session
->transport
;
7163 struct smbcli_options options
;
7164 struct smb2_transport
*transport
= NULL
;
7165 struct smb2_tree
*tree
= NULL
;
7166 struct smb2_session
*session
= NULL
;
7167 uint16_t noperm_flags
= 0;
7168 const char *noperm_unc
= NULL
;
7169 struct smb2_tree
*noperm_tree
= NULL
;
7170 uint32_t timeout_msec
;
7171 struct tevent_req
*subreq
= NULL
;
7172 struct cli_credentials
*credentials
= samba_cmdline_get_creds();
7175 if (smbXcli_conn_protocol(transport0
->conn
) < PROTOCOL_SMB3_00
) {
7176 torture_skip(torture
, "Can't test without SMB 3 support");
7179 options
= transport0
->options
;
7180 options
.client_guid
= GUID_random();
7181 options
.min_protocol
= PROTOCOL_SMB3_00
;
7182 options
.max_protocol
= PROTOCOL_SMB3_02
;
7184 status
= smb2_connect(torture
,
7186 lpcfg_smb_ports(torture
->lp_ctx
),
7188 lpcfg_resolve_context(torture
->lp_ctx
),
7193 lpcfg_socket_options(torture
->lp_ctx
),
7194 lpcfg_gensec_settings(torture
, torture
->lp_ctx
)
7196 torture_assert_ntstatus_ok(torture
, status
, "smb2_connect options failed");
7197 session
= tree
->session
;
7198 transport
= session
->transport
;
7200 timeout_msec
= tree
->session
->transport
->options
.request_timeout
* 1000;
7202 subreq
= smb2cli_validate_negotiate_info_send(torture
,
7208 torture_assert(torture
,
7209 tevent_req_poll_ntstatus(subreq
, torture
->ev
, &status
),
7210 "tevent_req_poll_ntstatus");
7211 status
= smb2cli_validate_negotiate_info_recv(subreq
);
7212 torture_assert_ntstatus_ok(torture
, status
, "smb2cli_validate_negotiate_info");
7214 noperm_unc
= talloc_asprintf(torture
, "\\\\%s\\%s", host
, noperm_share
);
7215 torture_assert(torture
, noperm_unc
!= NULL
, "talloc_asprintf");
7217 noperm_tree
= smb2_tree_init(session
, torture
, false);
7218 torture_assert(torture
, noperm_tree
!= NULL
, "smb2_tree_init");
7220 status
= smb2cli_raw_tcon(transport
->conn
,
7221 SMB2_HDR_FLAG_SIGNED
,
7222 0, /* clear_flags */
7225 noperm_tree
->smbXcli
,
7228 if (NT_STATUS_EQUAL(status
, NT_STATUS_BAD_NETWORK_NAME
)) {
7229 torture_skip(torture
, talloc_asprintf(torture
,
7230 "noperm_unc[%s] %s",
7231 noperm_unc
, nt_errstr(status
)));
7233 torture_assert_ntstatus_ok(torture
, status
,
7234 talloc_asprintf(torture
,
7238 subreq
= smb2cli_validate_negotiate_info_send(torture
,
7243 noperm_tree
->smbXcli
);
7244 torture_assert(torture
,
7245 tevent_req_poll_ntstatus(subreq
, torture
->ev
, &status
),
7246 "tevent_req_poll_ntstatus");
7247 status
= smb2cli_validate_negotiate_info_recv(subreq
);
7248 torture_assert_ntstatus_ok(torture
, status
, "smb2cli_validate_negotiate_info noperm");
7254 basic regression test for BUG 14788,
7255 with FSCTL_QUERY_NETWORK_INTERFACE_INFO
7256 https://bugzilla.samba.org/show_bug.cgi?id=14788
7258 static bool test_ioctl_bug14788_NETWORK_INTERFACE(struct torture_context
*torture
,
7259 struct smb2_tree
*tree0
)
7261 const char *host
= torture_setting_string(torture
, "host", NULL
);
7262 const char *share
= torture_setting_string(torture
, "share", NULL
);
7263 const char *noperm_share
= torture_setting_string(torture
, "noperm_share", "noperm");
7264 struct smb2_transport
*transport0
= tree0
->session
->transport
;
7265 struct smbcli_options options
;
7266 struct smb2_transport
*transport
= NULL
;
7267 struct smb2_tree
*tree
= NULL
;
7268 struct smb2_session
*session
= NULL
;
7269 uint16_t noperm_flags
= 0;
7270 const char *noperm_unc
= NULL
;
7271 struct smb2_tree
*noperm_tree
= NULL
;
7272 uint32_t timeout_msec
;
7273 DATA_BLOB out_input_buffer
= data_blob_null
;
7274 DATA_BLOB out_output_buffer
= data_blob_null
;
7275 struct cli_credentials
*credentials
= samba_cmdline_get_creds();
7278 if (smbXcli_conn_protocol(transport0
->conn
) < PROTOCOL_SMB3_00
) {
7279 torture_skip(torture
, "Can't test without SMB 3 support");
7282 options
= transport0
->options
;
7283 options
.client_guid
= GUID_random();
7284 options
.min_protocol
= PROTOCOL_SMB3_00
;
7285 options
.max_protocol
= PROTOCOL_SMB3_02
;
7287 status
= smb2_connect(torture
,
7289 lpcfg_smb_ports(torture
->lp_ctx
),
7291 lpcfg_resolve_context(torture
->lp_ctx
),
7296 lpcfg_socket_options(torture
->lp_ctx
),
7297 lpcfg_gensec_settings(torture
, torture
->lp_ctx
)
7299 torture_assert_ntstatus_ok(torture
, status
, "smb2_connect options failed");
7300 session
= tree
->session
;
7301 transport
= session
->transport
;
7303 timeout_msec
= tree
->session
->transport
->options
.request_timeout
* 1000;
7306 * A valid FSCTL_QUERY_NETWORK_INTERFACE_INFO
7308 status
= smb2cli_ioctl(transport
->conn
,
7312 UINT64_MAX
, /* in_fid_persistent */
7313 UINT64_MAX
, /* in_fid_volatile */
7314 FSCTL_QUERY_NETWORK_INTERFACE_INFO
,
7315 0, /* in_max_input_length */
7316 NULL
, /* in_input_buffer */
7317 UINT16_MAX
, /* in_max_output_length */
7318 NULL
, /* in_output_buffer */
7319 SMB2_IOCTL_FLAG_IS_FSCTL
,
7322 &out_output_buffer
);
7323 torture_assert_ntstatus_ok(torture
, status
,
7324 "FSCTL_QUERY_NETWORK_INTERFACE_INFO");
7327 * An invalid FSCTL_QUERY_NETWORK_INTERFACE_INFO,
7328 * with file_id_* is being UINT64_MAX and
7329 * in_max_output_length = 1.
7331 * This demonstrates NT_STATUS_BUFFER_TOO_SMALL
7332 * if the server is not able to return the
7333 * whole response buffer to the client.
7335 status
= smb2cli_ioctl(transport
->conn
,
7339 UINT64_MAX
, /* in_fid_persistent */
7340 UINT64_MAX
, /* in_fid_volatile */
7341 FSCTL_QUERY_NETWORK_INTERFACE_INFO
,
7342 0, /* in_max_input_length */
7343 NULL
, /* in_input_buffer */
7344 1, /* in_max_output_length */
7345 NULL
, /* in_output_buffer */
7346 SMB2_IOCTL_FLAG_IS_FSCTL
,
7349 &out_output_buffer
);
7350 torture_assert_ntstatus_equal(torture
, status
,
7351 NT_STATUS_BUFFER_TOO_SMALL
,
7352 "FSCTL_QUERY_NETWORK_INTERFACE_INFO");
7355 * An invalid FSCTL_QUERY_NETWORK_INTERFACE_INFO,
7356 * with file_id_* not being UINT64_MAX.
7358 * This gives INVALID_PARAMETER instead
7361 status
= smb2cli_ioctl(transport
->conn
,
7365 INT64_MAX
, /* in_fid_persistent */
7366 INT64_MAX
, /* in_fid_volatile */
7367 FSCTL_QUERY_NETWORK_INTERFACE_INFO
,
7368 0, /* in_max_input_length */
7369 NULL
, /* in_input_buffer */
7370 UINT16_MAX
, /* in_max_output_length */
7371 NULL
, /* in_output_buffer */
7372 SMB2_IOCTL_FLAG_IS_FSCTL
,
7375 &out_output_buffer
);
7376 torture_assert_ntstatus_equal(torture
, status
,
7377 NT_STATUS_INVALID_PARAMETER
,
7378 "FSCTL_QUERY_NETWORK_INTERFACE_INFO");
7381 * An invalid FSCTL_QUERY_NETWORK_INTERFACE_INFO,
7382 * with file_id_* not being UINT64_MAX and
7383 * in_max_output_length = 1.
7385 * This proves INVALID_PARAMETER instead
7386 * of BUFFER_TOO_SMALL.
7388 status
= smb2cli_ioctl(transport
->conn
,
7392 INT64_MAX
, /* in_fid_persistent */
7393 INT64_MAX
, /* in_fid_volatile */
7394 FSCTL_QUERY_NETWORK_INTERFACE_INFO
,
7395 0, /* in_max_input_length */
7396 NULL
, /* in_input_buffer */
7397 1, /* in_max_output_length */
7398 NULL
, /* in_output_buffer */
7399 SMB2_IOCTL_FLAG_IS_FSCTL
,
7402 &out_output_buffer
);
7403 torture_assert_ntstatus_equal(torture
, status
,
7404 NT_STATUS_INVALID_PARAMETER
,
7405 "FSCTL_QUERY_NETWORK_INTERFACE_INFO");
7407 noperm_unc
= talloc_asprintf(torture
, "\\\\%s\\%s", host
, noperm_share
);
7408 torture_assert(torture
, noperm_unc
!= NULL
, "talloc_asprintf");
7410 noperm_tree
= smb2_tree_init(session
, torture
, false);
7411 torture_assert(torture
, noperm_tree
!= NULL
, "smb2_tree_init");
7413 status
= smb2cli_raw_tcon(transport
->conn
,
7414 SMB2_HDR_FLAG_SIGNED
,
7415 0, /* clear_flags */
7418 noperm_tree
->smbXcli
,
7421 if (NT_STATUS_EQUAL(status
, NT_STATUS_BAD_NETWORK_NAME
)) {
7422 torture_skip(torture
, talloc_asprintf(torture
,
7423 "noperm_unc[%s] %s",
7424 noperm_unc
, nt_errstr(status
)));
7426 torture_assert_ntstatus_ok(torture
, status
,
7427 talloc_asprintf(torture
,
7432 * A valid FSCTL_QUERY_NETWORK_INTERFACE_INFO
7434 status
= smb2cli_ioctl(transport
->conn
,
7437 noperm_tree
->smbXcli
,
7438 UINT64_MAX
, /* in_fid_persistent */
7439 UINT64_MAX
, /* in_fid_volatile */
7440 FSCTL_QUERY_NETWORK_INTERFACE_INFO
,
7441 0, /* in_max_input_length */
7442 NULL
, /* in_input_buffer */
7443 UINT16_MAX
, /* in_max_output_length */
7444 NULL
, /* in_output_buffer */
7445 SMB2_IOCTL_FLAG_IS_FSCTL
,
7448 &out_output_buffer
);
7449 torture_assert_ntstatus_ok(torture
, status
,
7450 "FSCTL_QUERY_NETWORK_INTERFACE_INFO");
7453 * An invalid FSCTL_QUERY_NETWORK_INTERFACE_INFO,
7454 * with file_id_* is being UINT64_MAX and
7455 * in_max_output_length = 1.
7457 * This demonstrates NT_STATUS_BUFFER_TOO_SMALL
7458 * if the server is not able to return the
7459 * whole response buffer to the client.
7461 status
= smb2cli_ioctl(transport
->conn
,
7464 noperm_tree
->smbXcli
,
7465 UINT64_MAX
, /* in_fid_persistent */
7466 UINT64_MAX
, /* in_fid_volatile */
7467 FSCTL_QUERY_NETWORK_INTERFACE_INFO
,
7468 0, /* in_max_input_length */
7469 NULL
, /* in_input_buffer */
7470 1, /* in_max_output_length */
7471 NULL
, /* in_output_buffer */
7472 SMB2_IOCTL_FLAG_IS_FSCTL
,
7475 &out_output_buffer
);
7476 torture_assert_ntstatus_equal(torture
, status
,
7477 NT_STATUS_BUFFER_TOO_SMALL
,
7478 "FSCTL_QUERY_NETWORK_INTERFACE_INFO");
7481 * An invalid FSCTL_QUERY_NETWORK_INTERFACE_INFO,
7482 * with file_id_* not being UINT64_MAX.
7484 * This gives INVALID_PARAMETER instead
7487 status
= smb2cli_ioctl(transport
->conn
,
7490 noperm_tree
->smbXcli
,
7491 INT64_MAX
, /* in_fid_persistent */
7492 INT64_MAX
, /* in_fid_volatile */
7493 FSCTL_QUERY_NETWORK_INTERFACE_INFO
,
7494 0, /* in_max_input_length */
7495 NULL
, /* in_input_buffer */
7496 UINT16_MAX
, /* in_max_output_length */
7497 NULL
, /* in_output_buffer */
7498 SMB2_IOCTL_FLAG_IS_FSCTL
,
7501 &out_output_buffer
);
7502 torture_assert_ntstatus_equal(torture
, status
,
7503 NT_STATUS_INVALID_PARAMETER
,
7504 "FSCTL_QUERY_NETWORK_INTERFACE_INFO");
7507 * An invalid FSCTL_QUERY_NETWORK_INTERFACE_INFO,
7508 * with file_id_* not being UINT64_MAX and
7509 * in_max_output_length = 1.
7511 * This proves INVALID_PARAMETER instead
7512 * of BUFFER_TOO_SMALL.
7514 status
= smb2cli_ioctl(transport
->conn
,
7517 noperm_tree
->smbXcli
,
7518 INT64_MAX
, /* in_fid_persistent */
7519 INT64_MAX
, /* in_fid_volatile */
7520 FSCTL_QUERY_NETWORK_INTERFACE_INFO
,
7521 0, /* in_max_input_length */
7522 NULL
, /* in_input_buffer */
7523 1, /* in_max_output_length */
7524 NULL
, /* in_output_buffer */
7525 SMB2_IOCTL_FLAG_IS_FSCTL
,
7528 &out_output_buffer
);
7529 torture_assert_ntstatus_equal(torture
, status
,
7530 NT_STATUS_INVALID_PARAMETER
,
7531 "FSCTL_QUERY_NETWORK_INTERFACE_INFO");
7537 * basic regression test for BUG 15664
7538 * https://bugzilla.samba.org/show_bug.cgi?id=15664
7540 static bool test_ioctl_copy_chunk_bug15644(struct torture_context
*torture
,
7541 struct smb2_tree
*tree
)
7543 struct smb2_handle dest_h
;
7545 union smb_ioctl ioctl
;
7546 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
7547 struct srv_copychunk chunk
;
7548 struct srv_copychunk_copy cc_copy
;
7549 enum ndr_err_code ndr_ret
;
7552 ok
= test_setup_create_fill(torture
,
7558 SEC_RIGHTS_FILE_ALL
,
7559 FILE_ATTRIBUTE_NORMAL
);
7560 torture_assert(torture
, ok
, "dest file create fill");
7563 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
7564 ioctl
.smb2
.in
.file
.handle
= dest_h
;
7565 ioctl
.smb2
.in
.function
= FSCTL_SRV_COPYCHUNK
;
7566 ioctl
.smb2
.in
.max_output_response
= sizeof(struct srv_copychunk_rsp
);
7567 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
7570 ZERO_STRUCT(cc_copy
);
7571 /* overwrite the resume key with a bogus value */
7572 memcpy(cc_copy
.source_key
, "deadbeefdeadbeefdeadbeef", 24);
7573 cc_copy
.chunk_count
= 1;
7574 cc_copy
.chunks
= &chunk
;
7575 cc_copy
.chunks
[0].source_off
= 0;
7576 cc_copy
.chunks
[0].target_off
= 0;
7577 cc_copy
.chunks
[0].length
= 4096;
7579 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
7581 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
7582 torture_assert_ndr_success(torture
, ndr_ret
,
7583 "ndr_push_srv_copychunk_copy");
7585 /* Server 2k12 returns NT_STATUS_OBJECT_NAME_NOT_FOUND */
7586 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
7587 torture_assert_ntstatus_equal(torture
, status
,
7588 NT_STATUS_OBJECT_NAME_NOT_FOUND
,
7589 "FSCTL_SRV_COPYCHUNK");
7591 status
= smb2_util_close(tree
, dest_h
);
7592 torture_assert_ntstatus_ok(torture
, status
, "close");
7594 talloc_free(tmp_ctx
);
7599 * testing of SMB2 ioctls
7601 struct torture_suite
*torture_smb2_ioctl_init(TALLOC_CTX
*ctx
)
7603 struct torture_suite
*suite
= torture_suite_create(ctx
, "ioctl");
7604 struct torture_suite
*bug14788
= torture_suite_create(ctx
, "bug14788");
7606 torture_suite_add_1smb2_test(suite
, "shadow_copy",
7607 test_ioctl_get_shadow_copy
);
7608 torture_suite_add_1smb2_test(suite
, "req_resume_key",
7609 test_ioctl_req_resume_key
);
7610 torture_suite_add_1smb2_test(suite
, "req_two_resume_keys",
7611 test_ioctl_req_two_resume_keys
);
7612 torture_suite_add_1smb2_test(suite
, "copy_chunk_simple",
7613 test_ioctl_copy_chunk_simple
);
7614 torture_suite_add_1smb2_test(suite
, "copy_chunk_multi",
7615 test_ioctl_copy_chunk_multi
);
7616 torture_suite_add_1smb2_test(suite
, "copy_chunk_tiny",
7617 test_ioctl_copy_chunk_tiny
);
7618 torture_suite_add_1smb2_test(suite
, "copy_chunk_overwrite",
7619 test_ioctl_copy_chunk_over
);
7620 torture_suite_add_1smb2_test(suite
, "copy_chunk_append",
7621 test_ioctl_copy_chunk_append
);
7622 torture_suite_add_1smb2_test(suite
, "copy_chunk_limits",
7623 test_ioctl_copy_chunk_limits
);
7624 torture_suite_add_1smb2_test(suite
, "copy_chunk_src_lock",
7625 test_ioctl_copy_chunk_src_lck
);
7626 torture_suite_add_1smb2_test(suite
, "copy_chunk_dest_lock",
7627 test_ioctl_copy_chunk_dest_lck
);
7628 torture_suite_add_1smb2_test(suite
, "copy_chunk_bad_key",
7629 test_ioctl_copy_chunk_bad_key
);
7630 torture_suite_add_1smb2_test(suite
, "copy_chunk_bug15644",
7631 test_ioctl_copy_chunk_bug15644
);
7632 torture_suite_add_1smb2_test(suite
, "copy_chunk_src_is_dest",
7633 test_ioctl_copy_chunk_src_is_dest
);
7634 torture_suite_add_1smb2_test(suite
, "copy_chunk_src_is_dest_overlap",
7635 test_ioctl_copy_chunk_src_is_dest_overlap
);
7636 torture_suite_add_1smb2_test(suite
, "copy_chunk_bad_access",
7637 test_ioctl_copy_chunk_bad_access
);
7638 torture_suite_add_1smb2_test(suite
, "copy_chunk_write_access",
7639 test_ioctl_copy_chunk_write_access
);
7640 torture_suite_add_1smb2_test(suite
, "copy_chunk_src_exceed",
7641 test_ioctl_copy_chunk_src_exceed
);
7642 torture_suite_add_1smb2_test(suite
, "copy_chunk_src_exceed_multi",
7643 test_ioctl_copy_chunk_src_exceed_multi
);
7644 torture_suite_add_1smb2_test(suite
, "copy_chunk_sparse_dest",
7645 test_ioctl_copy_chunk_sparse_dest
);
7646 torture_suite_add_1smb2_test(suite
, "copy_chunk_max_output_sz",
7647 test_ioctl_copy_chunk_max_output_sz
);
7648 torture_suite_add_1smb2_test(suite
, "copy_chunk_zero_length",
7649 test_ioctl_copy_chunk_zero_length
);
7650 torture_suite_add_1smb2_test(suite
, "copy-chunk streams",
7651 test_copy_chunk_streams
);
7652 torture_suite_add_1smb2_test(suite
, "copy_chunk_across_shares",
7653 test_copy_chunk_across_shares
);
7654 torture_suite_add_1smb2_test(suite
, "copy_chunk_across_shares2",
7655 test_copy_chunk_across_shares2
);
7656 torture_suite_add_1smb2_test(suite
, "copy_chunk_across_shares3",
7657 test_copy_chunk_across_shares3
);
7658 torture_suite_add_1smb2_test(suite
, "compress_file_flag",
7659 test_ioctl_compress_file_flag
);
7660 torture_suite_add_1smb2_test(suite
, "compress_dir_inherit",
7661 test_ioctl_compress_dir_inherit
);
7662 torture_suite_add_1smb2_test(suite
, "compress_invalid_format",
7663 test_ioctl_compress_invalid_format
);
7664 torture_suite_add_1smb2_test(suite
, "compress_invalid_buf",
7665 test_ioctl_compress_invalid_buf
);
7666 torture_suite_add_1smb2_test(suite
, "compress_query_file_attr",
7667 test_ioctl_compress_query_file_attr
);
7668 torture_suite_add_1smb2_test(suite
, "compress_create_with_attr",
7669 test_ioctl_compress_create_with_attr
);
7670 torture_suite_add_1smb2_test(suite
, "compress_inherit_disable",
7671 test_ioctl_compress_inherit_disable
);
7672 torture_suite_add_1smb2_test(suite
, "compress_set_file_attr",
7673 test_ioctl_compress_set_file_attr
);
7674 torture_suite_add_1smb2_test(suite
, "compress_perms",
7675 test_ioctl_compress_perms
);
7676 torture_suite_add_1smb2_test(suite
, "compress_notsup_get",
7677 test_ioctl_compress_notsup_get
);
7678 torture_suite_add_1smb2_test(suite
, "compress_notsup_set",
7679 test_ioctl_compress_notsup_set
);
7680 torture_suite_add_1smb2_test(suite
, "network_interface_info",
7681 test_ioctl_network_interface_info
);
7682 torture_suite_add_1smb2_test(suite
, "sparse_file_flag",
7683 test_ioctl_sparse_file_flag
);
7684 torture_suite_add_1smb2_test(suite
, "sparse_file_attr",
7685 test_ioctl_sparse_file_attr
);
7686 torture_suite_add_1smb2_test(suite
, "sparse_dir_flag",
7687 test_ioctl_sparse_dir_flag
);
7688 torture_suite_add_1smb2_test(suite
, "sparse_set_nobuf",
7689 test_ioctl_sparse_set_nobuf
);
7690 torture_suite_add_1smb2_test(suite
, "sparse_set_oversize",
7691 test_ioctl_sparse_set_oversize
);
7692 torture_suite_add_1smb2_test(suite
, "sparse_qar",
7693 test_ioctl_sparse_qar
);
7694 torture_suite_add_1smb2_test(suite
, "sparse_qar_malformed",
7695 test_ioctl_sparse_qar_malformed
);
7696 torture_suite_add_1smb2_test(suite
, "sparse_qar_truncated",
7697 test_ioctl_sparse_qar_truncated
);
7698 torture_suite_add_1smb2_test(suite
, "sparse_punch",
7699 test_ioctl_sparse_punch
);
7700 torture_suite_add_1smb2_test(suite
, "sparse_hole_dealloc",
7701 test_ioctl_sparse_hole_dealloc
);
7702 torture_suite_add_1smb2_test(suite
, "sparse_compressed",
7703 test_ioctl_sparse_compressed
);
7704 torture_suite_add_1smb2_test(suite
, "sparse_copy_chunk",
7705 test_ioctl_sparse_copy_chunk
);
7706 torture_suite_add_1smb2_test(suite
, "sparse_punch_invalid",
7707 test_ioctl_sparse_punch_invalid
);
7708 torture_suite_add_1smb2_test(suite
, "sparse_perms",
7709 test_ioctl_sparse_perms
);
7710 torture_suite_add_1smb2_test(suite
, "sparse_lock",
7711 test_ioctl_sparse_lck
);
7712 torture_suite_add_1smb2_test(suite
, "sparse_qar_ob1",
7713 test_ioctl_sparse_qar_ob1
);
7714 torture_suite_add_1smb2_test(suite
, "sparse_qar_multi",
7715 test_ioctl_sparse_qar_multi
);
7716 torture_suite_add_1smb2_test(suite
, "sparse_qar_overflow",
7717 test_ioctl_sparse_qar_overflow
);
7718 torture_suite_add_1smb2_test(suite
, "trim_simple",
7719 test_ioctl_trim_simple
);
7720 torture_suite_add_1smb2_test(suite
, "dup_extents_simple",
7721 test_ioctl_dup_extents_simple
);
7722 torture_suite_add_1smb2_test(suite
, "dup_extents_len_beyond_dest",
7723 test_ioctl_dup_extents_len_beyond_dest
);
7724 torture_suite_add_1smb2_test(suite
, "dup_extents_len_beyond_src",
7725 test_ioctl_dup_extents_len_beyond_src
);
7726 torture_suite_add_1smb2_test(suite
, "dup_extents_len_zero",
7727 test_ioctl_dup_extents_len_zero
);
7728 torture_suite_add_1smb2_test(suite
, "dup_extents_sparse_src",
7729 test_ioctl_dup_extents_sparse_src
);
7730 torture_suite_add_1smb2_test(suite
, "dup_extents_sparse_dest",
7731 test_ioctl_dup_extents_sparse_dest
);
7732 torture_suite_add_1smb2_test(suite
, "dup_extents_sparse_both",
7733 test_ioctl_dup_extents_sparse_both
);
7734 torture_suite_add_1smb2_test(suite
, "dup_extents_src_is_dest",
7735 test_ioctl_dup_extents_src_is_dest
);
7736 torture_suite_add_1smb2_test(suite
, "dup_extents_src_is_dest_overlap",
7737 test_ioctl_dup_extents_src_is_dest_overlap
);
7738 torture_suite_add_1smb2_test(suite
, "dup_extents_compressed_src",
7739 test_ioctl_dup_extents_compressed_src
);
7740 torture_suite_add_1smb2_test(suite
, "dup_extents_compressed_dest",
7741 test_ioctl_dup_extents_compressed_dest
);
7742 torture_suite_add_1smb2_test(suite
, "dup_extents_bad_handle",
7743 test_ioctl_dup_extents_bad_handle
);
7744 torture_suite_add_1smb2_test(suite
, "dup_extents_src_lock",
7745 test_ioctl_dup_extents_src_lck
);
7746 torture_suite_add_1smb2_test(suite
, "dup_extents_dest_lock",
7747 test_ioctl_dup_extents_dest_lck
);
7748 torture_suite_add_1smb2_test(suite
, "bug14607",
7749 test_ioctl_bug14607
);
7750 torture_suite_add_1smb2_test(suite
, "bug14769",
7751 test_ioctl_bug14769
);
7753 torture_suite_add_1smb2_test(bug14788
, "VALIDATE_NEGOTIATE",
7754 test_ioctl_bug14788_VALIDATE_NEGOTIATE
);
7755 torture_suite_add_1smb2_test(bug14788
, "NETWORK_INTERFACE",
7756 test_ioctl_bug14788_NETWORK_INTERFACE
);
7757 torture_suite_add_suite(suite
, bug14788
);
7759 suite
->description
= talloc_strdup(suite
, "SMB2-IOCTL tests");