2 Unix SMB/CIFS implementation.
4 test suite for SMB2 ioctl operations
6 Copyright (C) David Disseldorp 2011
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 "librpc/gen_ndr/ndr_ioctl.h"
30 #define FNAME "testfsctl.dat"
31 #define FNAME2 "testfsctl2.dat"
34 basic testing of SMB2 shadow copy calls
36 static bool test_ioctl_get_shadow_copy(struct torture_context
*torture
,
37 struct smb2_tree
*tree
)
42 union smb_ioctl ioctl
;
43 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
45 smb2_util_unlink(tree
, FNAME
);
47 status
= torture_smb2_testfile(tree
, FNAME
, &h
);
48 torture_assert_ntstatus_ok(torture
, status
, "create write");
51 status
= smb2_util_write(tree
, h
, buf
, 0, ARRAY_SIZE(buf
));
52 torture_assert_ntstatus_ok(torture
, status
, "write");
55 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
56 ioctl
.smb2
.in
.file
.handle
= h
;
57 ioctl
.smb2
.in
.function
= FSCTL_SRV_ENUM_SNAPS
;
58 ioctl
.smb2
.in
.max_response_size
= 16;
59 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
61 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
62 if (NT_STATUS_EQUAL(status
, NT_STATUS_NOT_SUPPORTED
)
63 || NT_STATUS_EQUAL(status
, NT_STATUS_INVALID_DEVICE_REQUEST
)) {
64 torture_skip(torture
, "FSCTL_SRV_ENUM_SNAPS not supported\n");
66 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_ENUM_SNAPS");
72 basic testing of the SMB2 server side copy ioctls
74 static bool test_ioctl_req_resume_key(struct torture_context
*torture
,
75 struct smb2_tree
*tree
)
80 union smb_ioctl ioctl
;
81 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
82 struct req_resume_key_rsp res_key
;
83 enum ndr_err_code ndr_ret
;
85 smb2_util_unlink(tree
, FNAME
);
87 status
= torture_smb2_testfile(tree
, FNAME
, &h
);
88 torture_assert_ntstatus_ok(torture
, status
, "create write");
91 status
= smb2_util_write(tree
, h
, buf
, 0, ARRAY_SIZE(buf
));
92 torture_assert_ntstatus_ok(torture
, status
, "write");
95 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
96 ioctl
.smb2
.in
.file
.handle
= h
;
97 ioctl
.smb2
.in
.function
= FSCTL_SRV_REQUEST_RESUME_KEY
;
98 ioctl
.smb2
.in
.max_response_size
= 32;
99 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
101 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
102 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_REQUEST_RESUME_KEY");
104 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
, &res_key
,
105 (ndr_pull_flags_fn_t
)ndr_pull_req_resume_key_rsp
);
106 torture_assert_ndr_success(torture
, ndr_ret
,
107 "ndr_pull_req_resume_key_rsp");
109 ndr_print_debug((ndr_print_fn_t
)ndr_print_req_resume_key_rsp
, "yo", &res_key
);
111 talloc_free(tmp_ctx
);
115 static uint64_t patt_hash(uint64_t off
)
120 static bool check_pattern(struct torture_context
*torture
,
121 struct smb2_tree
*tree
, TALLOC_CTX
*mem_ctx
,
122 struct smb2_handle h
, uint64_t off
, uint64_t len
,
130 r
.in
.file
.handle
= h
;
133 status
= smb2_read(tree
, mem_ctx
, &r
);
134 torture_assert_ntstatus_ok(torture
, status
, "read");
136 torture_assert_u64_equal(torture
, r
.out
.data
.length
, len
,
137 "read data len mismatch");
139 for (i
= 0; i
<= len
- 8; i
+= 8, patt_off
+= 8) {
140 uint64_t data
= BVAL(r
.out
.data
.data
, i
);
141 torture_assert_u64_equal(torture
, data
, patt_hash(patt_off
),
142 talloc_asprintf(torture
, "read data "
143 "pattern bad at %llu\n",
144 (unsigned long long)i
));
147 talloc_free(r
.out
.data
.data
);
151 static bool test_setup_create_fill(struct torture_context
*torture
,
152 struct smb2_tree
*tree
, TALLOC_CTX
*mem_ctx
,
154 struct smb2_handle
*fh
,
156 uint32_t desired_access
)
158 struct smb2_create io
;
161 uint8_t *buf
= talloc_zero_size(mem_ctx
, size
);
162 torture_assert(torture
, (buf
!= NULL
), "no memory for file data buf");
164 smb2_util_unlink(tree
, fname
);
167 io
.in
.desired_access
= desired_access
;
168 io
.in
.file_attributes
= FILE_ATTRIBUTE_NORMAL
;
169 io
.in
.create_disposition
= NTCREATEX_DISP_OPEN_IF
;
171 NTCREATEX_SHARE_ACCESS_DELETE
|
172 NTCREATEX_SHARE_ACCESS_READ
|
173 NTCREATEX_SHARE_ACCESS_WRITE
;
176 status
= smb2_create(tree
, mem_ctx
, &io
);
177 torture_assert_ntstatus_ok(torture
, status
, "file create");
179 *fh
= io
.out
.file
.handle
;
182 uint64_t cur_off
= 0;
183 for (i
= 0; i
<= size
- 8; i
+= 8) {
184 SBVAL(buf
, i
, patt_hash(i
));
187 uint64_t io_sz
= MIN(1024 * 1024, size
);
188 status
= smb2_util_write(tree
, *fh
,
189 buf
+ cur_off
, cur_off
, io_sz
);
190 torture_assert_ntstatus_ok(torture
, status
, "file write");
199 static bool test_setup_copy_chunk(struct torture_context
*torture
,
200 struct smb2_tree
*tree
, TALLOC_CTX
*mem_ctx
,
202 struct smb2_handle
*src_h
,
204 uint32_t src_desired_access
,
205 struct smb2_handle
*dest_h
,
207 uint32_t dest_desired_access
,
208 struct srv_copychunk_copy
*cc_copy
,
209 union smb_ioctl
*ioctl
)
211 struct req_resume_key_rsp res_key
;
214 enum ndr_err_code ndr_ret
;
216 ok
= test_setup_create_fill(torture
, tree
, mem_ctx
, FNAME
,
217 src_h
, src_size
, src_desired_access
);
218 torture_assert(torture
, ok
, "src file create fill");
220 ok
= test_setup_create_fill(torture
, tree
, mem_ctx
, FNAME2
,
221 dest_h
, dest_size
, dest_desired_access
);
222 torture_assert(torture
, ok
, "dest file create fill");
224 ZERO_STRUCTPN(ioctl
);
225 ioctl
->smb2
.level
= RAW_IOCTL_SMB2
;
226 ioctl
->smb2
.in
.file
.handle
= *src_h
;
227 ioctl
->smb2
.in
.function
= FSCTL_SRV_REQUEST_RESUME_KEY
;
228 /* Allow for Key + ContextLength + Context */
229 ioctl
->smb2
.in
.max_response_size
= 32;
230 ioctl
->smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
232 status
= smb2_ioctl(tree
, mem_ctx
, &ioctl
->smb2
);
233 torture_assert_ntstatus_ok(torture
, status
,
234 "FSCTL_SRV_REQUEST_RESUME_KEY");
236 ndr_ret
= ndr_pull_struct_blob(&ioctl
->smb2
.out
.out
, mem_ctx
, &res_key
,
237 (ndr_pull_flags_fn_t
)ndr_pull_req_resume_key_rsp
);
239 torture_assert_ndr_success(torture
, ndr_ret
,
240 "ndr_pull_req_resume_key_rsp");
242 ZERO_STRUCTPN(ioctl
);
243 ioctl
->smb2
.level
= RAW_IOCTL_SMB2
;
244 ioctl
->smb2
.in
.file
.handle
= *dest_h
;
245 ioctl
->smb2
.in
.function
= FSCTL_SRV_COPYCHUNK
;
246 ioctl
->smb2
.in
.max_response_size
= sizeof(struct srv_copychunk_rsp
);
247 ioctl
->smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
249 ZERO_STRUCTPN(cc_copy
);
250 memcpy(cc_copy
->source_key
, res_key
.resume_key
, ARRAY_SIZE(cc_copy
->source_key
));
251 cc_copy
->chunk_count
= nchunks
;
252 cc_copy
->chunks
= talloc_zero_array(mem_ctx
, struct srv_copychunk
, nchunks
);
253 torture_assert(torture
, (cc_copy
->chunks
!= NULL
), "no memory for chunks");
259 static bool check_copy_chunk_rsp(struct torture_context
*torture
,
260 struct srv_copychunk_rsp
*cc_rsp
,
261 uint32_t ex_chunks_written
,
262 uint32_t ex_chunk_bytes_written
,
263 uint32_t ex_total_bytes_written
)
265 torture_assert_int_equal(torture
, cc_rsp
->chunks_written
,
266 ex_chunks_written
, "num chunks");
267 torture_assert_int_equal(torture
, cc_rsp
->chunk_bytes_written
,
268 ex_chunk_bytes_written
, "chunk bytes written");
269 torture_assert_int_equal(torture
, cc_rsp
->total_bytes_written
,
270 ex_total_bytes_written
, "chunk total bytes");
274 static bool test_ioctl_copy_chunk_simple(struct torture_context
*torture
,
275 struct smb2_tree
*tree
)
277 struct smb2_handle src_h
;
278 struct smb2_handle dest_h
;
280 union smb_ioctl ioctl
;
281 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
282 struct srv_copychunk_copy cc_copy
;
283 struct srv_copychunk_rsp cc_rsp
;
284 enum ndr_err_code ndr_ret
;
287 ok
= test_setup_copy_chunk(torture
, tree
, tmp_ctx
,
289 &src_h
, 4096, /* fill 4096 byte src file */
291 &dest_h
, 0, /* 0 byte dest file */
296 torture_fail(torture
, "setup copy chunk error");
299 /* copy all src file data (via a single chunk desc) */
300 cc_copy
.chunks
[0].source_off
= 0;
301 cc_copy
.chunks
[0].target_off
= 0;
302 cc_copy
.chunks
[0].length
= 4096;
304 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
306 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
307 torture_assert_ndr_success(torture
, ndr_ret
,
308 "ndr_push_srv_copychunk_copy");
310 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
311 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_COPYCHUNK");
313 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
315 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
316 torture_assert_ndr_success(torture
, ndr_ret
,
317 "ndr_pull_srv_copychunk_rsp");
319 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
320 1, /* chunks written */
321 0, /* chunk bytes unsuccessfully written */
322 4096); /* total bytes written */
324 torture_fail(torture
, "bad copy chunk response data");
327 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 0, 4096, 0);
329 torture_fail(torture
, "inconsistent file data");
332 smb2_util_close(tree
, src_h
);
333 smb2_util_close(tree
, dest_h
);
334 talloc_free(tmp_ctx
);
338 static bool test_ioctl_copy_chunk_multi(struct torture_context
*torture
,
339 struct smb2_tree
*tree
)
341 struct smb2_handle src_h
;
342 struct smb2_handle dest_h
;
344 union smb_ioctl ioctl
;
345 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
346 struct srv_copychunk_copy cc_copy
;
347 struct srv_copychunk_rsp cc_rsp
;
348 enum ndr_err_code ndr_ret
;
351 ok
= test_setup_copy_chunk(torture
, tree
, tmp_ctx
,
353 &src_h
, 8192, /* src file */
355 &dest_h
, 0, /* dest file */
360 torture_fail(torture
, "setup copy chunk error");
363 /* copy all src file data via two chunks */
364 cc_copy
.chunks
[0].source_off
= 0;
365 cc_copy
.chunks
[0].target_off
= 0;
366 cc_copy
.chunks
[0].length
= 4096;
368 cc_copy
.chunks
[1].source_off
= 4096;
369 cc_copy
.chunks
[1].target_off
= 4096;
370 cc_copy
.chunks
[1].length
= 4096;
372 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
374 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
375 torture_assert_ndr_success(torture
, ndr_ret
,
376 "ndr_push_srv_copychunk_copy");
378 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
379 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_COPYCHUNK");
381 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
383 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
384 torture_assert_ndr_success(torture
, ndr_ret
,
385 "ndr_pull_srv_copychunk_rsp");
387 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
388 2, /* chunks written */
389 0, /* chunk bytes unsuccessfully written */
390 8192); /* total bytes written */
392 torture_fail(torture
, "bad copy chunk response data");
395 smb2_util_close(tree
, src_h
);
396 smb2_util_close(tree
, dest_h
);
397 talloc_free(tmp_ctx
);
401 static bool test_ioctl_copy_chunk_tiny(struct torture_context
*torture
,
402 struct smb2_tree
*tree
)
404 struct smb2_handle src_h
;
405 struct smb2_handle dest_h
;
407 union smb_ioctl ioctl
;
408 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
409 struct srv_copychunk_copy cc_copy
;
410 struct srv_copychunk_rsp cc_rsp
;
411 enum ndr_err_code ndr_ret
;
414 ok
= test_setup_copy_chunk(torture
, tree
, tmp_ctx
,
416 &src_h
, 100, /* src file */
418 &dest_h
, 0, /* dest file */
423 torture_fail(torture
, "setup copy chunk error");
426 /* copy all src file data via two chunks, sub block size chunks */
427 cc_copy
.chunks
[0].source_off
= 0;
428 cc_copy
.chunks
[0].target_off
= 0;
429 cc_copy
.chunks
[0].length
= 50;
431 cc_copy
.chunks
[1].source_off
= 50;
432 cc_copy
.chunks
[1].target_off
= 50;
433 cc_copy
.chunks
[1].length
= 50;
435 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
437 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
438 torture_assert_ndr_success(torture
, ndr_ret
,
439 "ndr_push_srv_copychunk_copy");
441 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
442 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_COPYCHUNK");
444 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
446 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
447 torture_assert_ndr_success(torture
, ndr_ret
,
448 "ndr_pull_srv_copychunk_rsp");
450 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
451 2, /* chunks written */
452 0, /* chunk bytes unsuccessfully written */
453 100); /* total bytes written */
455 torture_fail(torture
, "bad copy chunk response data");
458 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 0, 100, 0);
460 torture_fail(torture
, "inconsistent file data");
463 smb2_util_close(tree
, src_h
);
464 smb2_util_close(tree
, dest_h
);
465 talloc_free(tmp_ctx
);
469 static bool test_ioctl_copy_chunk_over(struct torture_context
*torture
,
470 struct smb2_tree
*tree
)
472 struct smb2_handle src_h
;
473 struct smb2_handle dest_h
;
475 union smb_ioctl ioctl
;
476 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
477 struct srv_copychunk_copy cc_copy
;
478 struct srv_copychunk_rsp cc_rsp
;
479 enum ndr_err_code ndr_ret
;
482 ok
= test_setup_copy_chunk(torture
, tree
, tmp_ctx
,
484 &src_h
, 8192, /* src file */
486 &dest_h
, 4096, /* dest file */
491 torture_fail(torture
, "setup copy chunk error");
494 /* first chunk overwrites existing dest data */
495 cc_copy
.chunks
[0].source_off
= 0;
496 cc_copy
.chunks
[0].target_off
= 0;
497 cc_copy
.chunks
[0].length
= 4096;
499 /* second chunk overwrites the first */
500 cc_copy
.chunks
[1].source_off
= 4096;
501 cc_copy
.chunks
[1].target_off
= 0;
502 cc_copy
.chunks
[1].length
= 4096;
504 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
506 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
507 torture_assert_ndr_success(torture
, ndr_ret
,
508 "ndr_push_srv_copychunk_copy");
510 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
511 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_COPYCHUNK");
513 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
515 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
516 torture_assert_ndr_success(torture
, ndr_ret
,
517 "ndr_pull_srv_copychunk_rsp");
519 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
520 2, /* chunks written */
521 0, /* chunk bytes unsuccessfully written */
522 8192); /* total bytes written */
524 torture_fail(torture
, "bad copy chunk response data");
527 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 0, 4096, 4096);
529 torture_fail(torture
, "inconsistent file data");
532 smb2_util_close(tree
, src_h
);
533 smb2_util_close(tree
, dest_h
);
534 talloc_free(tmp_ctx
);
538 static bool test_ioctl_copy_chunk_append(struct torture_context
*torture
,
539 struct smb2_tree
*tree
)
541 struct smb2_handle src_h
;
542 struct smb2_handle dest_h
;
544 union smb_ioctl ioctl
;
545 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
546 struct srv_copychunk_copy cc_copy
;
547 struct srv_copychunk_rsp cc_rsp
;
548 enum ndr_err_code ndr_ret
;
551 ok
= test_setup_copy_chunk(torture
, tree
, tmp_ctx
,
553 &src_h
, 4096, /* src file */
555 &dest_h
, 0, /* dest file */
560 torture_fail(torture
, "setup copy chunk error");
563 cc_copy
.chunks
[0].source_off
= 0;
564 cc_copy
.chunks
[0].target_off
= 0;
565 cc_copy
.chunks
[0].length
= 4096;
567 /* second chunk appends the same data to the first */
568 cc_copy
.chunks
[1].source_off
= 0;
569 cc_copy
.chunks
[1].target_off
= 4096;
570 cc_copy
.chunks
[1].length
= 4096;
572 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
574 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
575 torture_assert_ndr_success(torture
, ndr_ret
,
576 "ndr_push_srv_copychunk_copy");
578 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
579 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_COPYCHUNK");
581 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
583 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
584 torture_assert_ndr_success(torture
, ndr_ret
,
585 "ndr_pull_srv_copychunk_rsp");
587 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
588 2, /* chunks written */
589 0, /* chunk bytes unsuccessfully written */
590 8192); /* total bytes written */
592 torture_fail(torture
, "bad copy chunk response data");
595 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 0, 4096, 0);
597 torture_fail(torture
, "inconsistent file data");
600 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 4096, 4096, 0);
602 torture_fail(torture
, "inconsistent file data");
605 smb2_util_close(tree
, src_h
);
606 smb2_util_close(tree
, dest_h
);
607 talloc_free(tmp_ctx
);
611 static bool test_ioctl_copy_chunk_limits(struct torture_context
*torture
,
612 struct smb2_tree
*tree
)
614 struct smb2_handle src_h
;
615 struct smb2_handle dest_h
;
617 union smb_ioctl ioctl
;
618 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
619 struct srv_copychunk_copy cc_copy
;
620 struct srv_copychunk_rsp cc_rsp
;
621 enum ndr_err_code ndr_ret
;
624 ok
= test_setup_copy_chunk(torture
, tree
, tmp_ctx
,
626 &src_h
, 4096, /* src file */
628 &dest_h
, 0, /* dest file */
633 torture_fail(torture
, "setup copy chunk error");
636 /* send huge chunk length request */
637 cc_copy
.chunks
[0].source_off
= 0;
638 cc_copy
.chunks
[0].target_off
= 0;
639 cc_copy
.chunks
[0].length
= UINT_MAX
;
641 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
643 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
644 torture_assert_ndr_success(torture
, ndr_ret
, "marshalling request");
646 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
647 torture_assert_ntstatus_equal(torture
, status
,
648 NT_STATUS_INVALID_PARAMETER
,
649 "bad oversize chunk response");
651 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
653 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
654 torture_assert_ndr_success(torture
, ndr_ret
, "unmarshalling response");
656 torture_comment(torture
, "limit max chunks, got %u\n",
657 cc_rsp
.chunks_written
);
658 torture_comment(torture
, "limit max chunk len, got %u\n",
659 cc_rsp
.chunk_bytes_written
);
660 torture_comment(torture
, "limit max total bytes, got %u\n",
661 cc_rsp
.total_bytes_written
);
663 smb2_util_close(tree
, src_h
);
664 smb2_util_close(tree
, dest_h
);
665 talloc_free(tmp_ctx
);
669 static bool test_ioctl_copy_chunk_src_lck(struct torture_context
*torture
,
670 struct smb2_tree
*tree
)
672 struct smb2_handle src_h
;
673 struct smb2_handle src_h2
;
674 struct smb2_handle dest_h
;
676 union smb_ioctl ioctl
;
677 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
678 struct srv_copychunk_copy cc_copy
;
679 struct srv_copychunk_rsp cc_rsp
;
680 enum ndr_err_code ndr_ret
;
682 struct smb2_lock lck
;
683 struct smb2_lock_element el
[1];
685 ok
= test_setup_copy_chunk(torture
, tree
, tmp_ctx
,
687 &src_h
, 4096, /* src file */
689 &dest_h
, 0, /* dest file */
694 torture_fail(torture
, "setup copy chunk error");
697 cc_copy
.chunks
[0].source_off
= 0;
698 cc_copy
.chunks
[0].target_off
= 0;
699 cc_copy
.chunks
[0].length
= 4096;
701 /* open and lock the copychunk src file */
702 status
= torture_smb2_testfile(tree
, FNAME
, &src_h2
);
703 torture_assert_ntstatus_ok(torture
, status
, "2nd src open");
705 lck
.in
.lock_count
= 0x0001;
706 lck
.in
.lock_sequence
= 0x00000000;
707 lck
.in
.file
.handle
= src_h2
;
709 el
[0].offset
= cc_copy
.chunks
[0].source_off
;
710 el
[0].length
= cc_copy
.chunks
[0].length
;
712 el
[0].flags
= SMB2_LOCK_FLAG_EXCLUSIVE
;
714 status
= smb2_lock(tree
, &lck
);
715 torture_assert_ntstatus_ok(torture
, status
, "lock");
717 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
719 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
720 torture_assert_ndr_success(torture
, ndr_ret
,
721 "ndr_push_srv_copychunk_copy");
723 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
725 * 2k12 & Samba return lock_conflict, Windows 7 & 2k8 return success...
727 * Edgar Olougouna @ MS wrote:
728 * Regarding the FSCTL_SRV_COPYCHUNK and STATUS_FILE_LOCK_CONFLICT
729 * discrepancy observed between Windows versions, we confirm that the
730 * behavior change is expected.
732 * CopyChunk in Windows Server 2012 use regular Readfile/Writefile APIs
733 * to move the chunks from the source to the destination.
734 * These ReadFile/WriteFile APIs go through the byte-range lock checks,
735 * and this explains the observed STATUS_FILE_LOCK_CONFLICT error.
737 * Prior to Windows Server 2012, CopyChunk used mapped sections to move
738 * the data. And byte range locks are not enforced on mapped I/O, and
739 * this explains the STATUS_SUCCESS observed on Windows Server 2008 R2.
741 torture_assert_ntstatus_equal(torture
, status
,
742 NT_STATUS_FILE_LOCK_CONFLICT
,
743 "FSCTL_SRV_COPYCHUNK locked");
745 /* should get cc response data with the lock conflict status */
746 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
748 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
749 torture_assert_ndr_success(torture
, ndr_ret
,
750 "ndr_pull_srv_copychunk_rsp");
751 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
752 0, /* chunks written */
753 0, /* chunk bytes unsuccessfully written */
754 0); /* total bytes written */
756 lck
.in
.lock_count
= 0x0001;
757 lck
.in
.lock_sequence
= 0x00000001;
758 lck
.in
.file
.handle
= src_h2
;
760 el
[0].offset
= cc_copy
.chunks
[0].source_off
;
761 el
[0].length
= cc_copy
.chunks
[0].length
;
763 el
[0].flags
= SMB2_LOCK_FLAG_UNLOCK
;
764 status
= smb2_lock(tree
, &lck
);
765 torture_assert_ntstatus_ok(torture
, status
, "unlock");
767 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
768 torture_assert_ntstatus_ok(torture
, status
,
769 "FSCTL_SRV_COPYCHUNK unlocked");
771 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
773 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
774 torture_assert_ndr_success(torture
, ndr_ret
,
775 "ndr_pull_srv_copychunk_rsp");
777 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
778 1, /* chunks written */
779 0, /* chunk bytes unsuccessfully written */
780 4096); /* total bytes written */
782 torture_fail(torture
, "bad copy chunk response data");
785 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 0, 4096, 0);
787 torture_fail(torture
, "inconsistent file data");
790 smb2_util_close(tree
, src_h2
);
791 smb2_util_close(tree
, src_h
);
792 smb2_util_close(tree
, dest_h
);
793 talloc_free(tmp_ctx
);
797 static bool test_ioctl_copy_chunk_dest_lck(struct torture_context
*torture
,
798 struct smb2_tree
*tree
)
800 struct smb2_handle src_h
;
801 struct smb2_handle dest_h
;
802 struct smb2_handle dest_h2
;
804 union smb_ioctl ioctl
;
805 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
806 struct srv_copychunk_copy cc_copy
;
807 struct srv_copychunk_rsp cc_rsp
;
808 enum ndr_err_code ndr_ret
;
810 struct smb2_lock lck
;
811 struct smb2_lock_element el
[1];
813 ok
= test_setup_copy_chunk(torture
, tree
, tmp_ctx
,
815 &src_h
, 4096, /* src file */
817 &dest_h
, 4096, /* dest file */
822 torture_fail(torture
, "setup copy chunk error");
825 cc_copy
.chunks
[0].source_off
= 0;
826 cc_copy
.chunks
[0].target_off
= 0;
827 cc_copy
.chunks
[0].length
= 4096;
829 /* open and lock the copychunk dest file */
830 status
= torture_smb2_testfile(tree
, FNAME2
, &dest_h2
);
831 torture_assert_ntstatus_ok(torture
, status
, "2nd src open");
833 lck
.in
.lock_count
= 0x0001;
834 lck
.in
.lock_sequence
= 0x00000000;
835 lck
.in
.file
.handle
= dest_h2
;
837 el
[0].offset
= cc_copy
.chunks
[0].target_off
;
838 el
[0].length
= cc_copy
.chunks
[0].length
;
840 el
[0].flags
= SMB2_LOCK_FLAG_EXCLUSIVE
;
842 status
= smb2_lock(tree
, &lck
);
843 torture_assert_ntstatus_ok(torture
, status
, "lock");
845 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
847 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
848 torture_assert_ndr_success(torture
, ndr_ret
,
849 "ndr_push_srv_copychunk_copy");
851 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
852 torture_assert_ntstatus_equal(torture
, status
,
853 NT_STATUS_FILE_LOCK_CONFLICT
,
854 "FSCTL_SRV_COPYCHUNK locked");
856 lck
.in
.lock_count
= 0x0001;
857 lck
.in
.lock_sequence
= 0x00000001;
858 lck
.in
.file
.handle
= dest_h2
;
860 el
[0].offset
= cc_copy
.chunks
[0].target_off
;
861 el
[0].length
= cc_copy
.chunks
[0].length
;
863 el
[0].flags
= SMB2_LOCK_FLAG_UNLOCK
;
864 status
= smb2_lock(tree
, &lck
);
865 torture_assert_ntstatus_ok(torture
, status
, "unlock");
867 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
868 torture_assert_ntstatus_ok(torture
, status
,
869 "FSCTL_SRV_COPYCHUNK unlocked");
871 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
873 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
874 torture_assert_ndr_success(torture
, ndr_ret
,
875 "ndr_pull_srv_copychunk_rsp");
877 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
878 1, /* chunks written */
879 0, /* chunk bytes unsuccessfully written */
880 4096); /* total bytes written */
882 torture_fail(torture
, "bad copy chunk response data");
885 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 0, 4096, 0);
887 torture_fail(torture
, "inconsistent file data");
890 smb2_util_close(tree
, dest_h2
);
891 smb2_util_close(tree
, src_h
);
892 smb2_util_close(tree
, dest_h
);
893 talloc_free(tmp_ctx
);
897 static bool test_ioctl_copy_chunk_bad_key(struct torture_context
*torture
,
898 struct smb2_tree
*tree
)
900 struct smb2_handle src_h
;
901 struct smb2_handle dest_h
;
903 union smb_ioctl ioctl
;
904 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
905 struct srv_copychunk_copy cc_copy
;
906 enum ndr_err_code ndr_ret
;
909 ok
= test_setup_copy_chunk(torture
, tree
, tmp_ctx
,
918 torture_fail(torture
, "setup copy chunk error");
921 /* overwrite the resume key with a bogus value */
922 memcpy(cc_copy
.source_key
, "deadbeefdeadbeefdeadbeef", 24);
924 cc_copy
.chunks
[0].source_off
= 0;
925 cc_copy
.chunks
[0].target_off
= 0;
926 cc_copy
.chunks
[0].length
= 4096;
928 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
930 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
931 torture_assert_ndr_success(torture
, ndr_ret
,
932 "ndr_push_srv_copychunk_copy");
934 /* Server 2k12 returns NT_STATUS_OBJECT_NAME_NOT_FOUND */
935 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
936 torture_assert_ntstatus_equal(torture
, status
,
937 NT_STATUS_OBJECT_NAME_NOT_FOUND
,
938 "FSCTL_SRV_COPYCHUNK");
940 smb2_util_close(tree
, src_h
);
941 smb2_util_close(tree
, dest_h
);
942 talloc_free(tmp_ctx
);
946 static bool test_ioctl_copy_chunk_src_is_dest(struct torture_context
*torture
,
947 struct smb2_tree
*tree
)
949 struct smb2_handle src_h
;
950 struct smb2_handle dest_h
;
952 union smb_ioctl ioctl
;
953 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
954 struct srv_copychunk_copy cc_copy
;
955 struct srv_copychunk_rsp cc_rsp
;
956 enum ndr_err_code ndr_ret
;
959 ok
= test_setup_copy_chunk(torture
, tree
, tmp_ctx
,
968 torture_fail(torture
, "setup copy chunk error");
971 /* the source is also the destination */
972 ioctl
.smb2
.in
.file
.handle
= src_h
;
974 /* non-overlapping */
975 cc_copy
.chunks
[0].source_off
= 0;
976 cc_copy
.chunks
[0].target_off
= 4096;
977 cc_copy
.chunks
[0].length
= 4096;
979 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
981 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
982 torture_assert_ndr_success(torture
, ndr_ret
,
983 "ndr_push_srv_copychunk_copy");
985 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
986 torture_assert_ntstatus_ok(torture
, status
,
987 "FSCTL_SRV_COPYCHUNK");
989 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
991 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
992 torture_assert_ndr_success(torture
, ndr_ret
,
993 "ndr_pull_srv_copychunk_rsp");
995 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
996 1, /* chunks written */
997 0, /* chunk bytes unsuccessfully written */
998 4096); /* total bytes written */
1000 torture_fail(torture
, "bad copy chunk response data");
1003 ok
= check_pattern(torture
, tree
, tmp_ctx
, src_h
, 0, 4096, 0);
1005 torture_fail(torture
, "inconsistent file data");
1007 ok
= check_pattern(torture
, tree
, tmp_ctx
, src_h
, 4096, 4096, 0);
1009 torture_fail(torture
, "inconsistent file data");
1012 smb2_util_close(tree
, src_h
);
1013 smb2_util_close(tree
, dest_h
);
1014 talloc_free(tmp_ctx
);
1019 * Test a single-chunk copychunk request, where the source and target ranges
1020 * overlap, and the SourceKey refers to the same target file. E.g:
1024 * File: src_and_dest
1025 * Offset: 0123456789
1030 * FSCTL_SRV_COPYCHUNK(src_and_dest)
1031 * SourceKey = SRV_REQUEST_RESUME_KEY(src_and_dest)
1033 * Chunks[0].SourceOffset = 0
1034 * Chunks[0].TargetOffset = 4
1035 * Chunks[0].Length = 6
1039 * File: src_and_dest
1040 * Offset: 0123456789
1043 * The resultant contents of src_and_dest is dependent on the server's
1044 * copy algorithm. In the above example, the server uses an IO buffer
1045 * large enough to hold the entire six-byte source data before writing
1046 * to TargetOffset. If the server were to use a four-byte IO buffer and
1047 * started reads/writes from the lowest offset, then the two overlapping
1048 * bytes in the above example would be overwritten before being read. The
1049 * resultant file contents would be abcdabcdab.
1051 * Windows 2008r2 appears to use a 2048 byte copy buffer, overlapping bytes
1052 * after this offset are written before being read. Windows 2012 on the
1053 * other hand appears to use a buffer large enough to hold its maximum
1054 * supported chunk size (1M). Samba currently uses a 64k copy buffer by
1055 * default (vfs_cc_state.buf).
1057 * This test uses an 8-byte overlap at 2040-2048, so that it passes against
1058 * Windows 2008, 2012 and Samba servers.
1061 test_ioctl_copy_chunk_src_is_dest_overlap(struct torture_context
*torture
,
1062 struct smb2_tree
*tree
)
1064 struct smb2_handle src_h
;
1065 struct smb2_handle dest_h
;
1067 union smb_ioctl ioctl
;
1068 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
1069 struct srv_copychunk_copy cc_copy
;
1070 struct srv_copychunk_rsp cc_rsp
;
1071 enum ndr_err_code ndr_ret
;
1074 /* exceed the vfs_default copy buffer */
1075 ok
= test_setup_copy_chunk(torture
, tree
, tmp_ctx
,
1078 SEC_RIGHTS_FILE_ALL
,
1080 SEC_RIGHTS_FILE_ALL
,
1084 torture_fail(torture
, "setup copy chunk error");
1087 /* the source is also the destination */
1088 ioctl
.smb2
.in
.file
.handle
= src_h
;
1090 /* 8 bytes overlap between source and target ranges */
1091 cc_copy
.chunks
[0].source_off
= 0;
1092 cc_copy
.chunks
[0].target_off
= 2048 - 8;
1093 cc_copy
.chunks
[0].length
= 2048;
1095 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1097 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1098 torture_assert_ndr_success(torture
, ndr_ret
,
1099 "ndr_push_srv_copychunk_copy");
1101 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1102 torture_assert_ntstatus_ok(torture
, status
,
1103 "FSCTL_SRV_COPYCHUNK");
1105 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
1107 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
1108 torture_assert_ndr_success(torture
, ndr_ret
,
1109 "ndr_pull_srv_copychunk_rsp");
1111 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
1112 1, /* chunks written */
1113 0, /* chunk bytes unsuccessfully written */
1114 2048); /* total bytes written */
1116 torture_fail(torture
, "bad copy chunk response data");
1119 ok
= check_pattern(torture
, tree
, tmp_ctx
, src_h
, 0, 2048 - 8, 0);
1121 torture_fail(torture
, "inconsistent file data");
1123 ok
= check_pattern(torture
, tree
, tmp_ctx
, src_h
, 2048 - 8, 2048, 0);
1125 torture_fail(torture
, "inconsistent file data");
1128 smb2_util_close(tree
, src_h
);
1129 smb2_util_close(tree
, dest_h
);
1130 talloc_free(tmp_ctx
);
1134 static bool test_ioctl_copy_chunk_bad_access(struct torture_context
*torture
,
1135 struct smb2_tree
*tree
)
1137 struct smb2_handle src_h
;
1138 struct smb2_handle dest_h
;
1140 union smb_ioctl ioctl
;
1141 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
1142 struct srv_copychunk_copy cc_copy
;
1143 enum ndr_err_code ndr_ret
;
1146 /* no read permission on src */
1147 ok
= test_setup_copy_chunk(torture
, tree
, tmp_ctx
,
1149 &src_h
, 4096, /* fill 4096 byte src file */
1150 SEC_RIGHTS_FILE_WRITE
,
1151 &dest_h
, 0, /* 0 byte dest file */
1152 SEC_RIGHTS_FILE_ALL
,
1156 torture_fail(torture
, "setup copy chunk error");
1159 cc_copy
.chunks
[0].source_off
= 0;
1160 cc_copy
.chunks
[0].target_off
= 0;
1161 cc_copy
.chunks
[0].length
= 4096;
1163 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1165 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1166 torture_assert_ndr_success(torture
, ndr_ret
,
1167 "ndr_push_srv_copychunk_copy");
1169 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1170 torture_assert_ntstatus_equal(torture
, status
,
1171 NT_STATUS_ACCESS_DENIED
,
1172 "FSCTL_SRV_COPYCHUNK");
1174 smb2_util_close(tree
, src_h
);
1175 smb2_util_close(tree
, dest_h
);
1177 /* no write permission on dest */
1178 ok
= test_setup_copy_chunk(torture
, tree
, tmp_ctx
,
1180 &src_h
, 4096, /* fill 4096 byte src file */
1181 SEC_RIGHTS_FILE_ALL
,
1182 &dest_h
, 0, /* 0 byte dest file */
1183 (SEC_RIGHTS_FILE_READ
1184 | SEC_RIGHTS_FILE_EXECUTE
),
1188 torture_fail(torture
, "setup copy chunk error");
1191 cc_copy
.chunks
[0].source_off
= 0;
1192 cc_copy
.chunks
[0].target_off
= 0;
1193 cc_copy
.chunks
[0].length
= 4096;
1195 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1197 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1198 torture_assert_ndr_success(torture
, ndr_ret
,
1199 "ndr_push_srv_copychunk_copy");
1201 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1202 torture_assert_ntstatus_equal(torture
, status
,
1203 NT_STATUS_ACCESS_DENIED
,
1204 "FSCTL_SRV_COPYCHUNK");
1206 smb2_util_close(tree
, src_h
);
1207 smb2_util_close(tree
, dest_h
);
1209 /* no read permission on dest */
1210 ok
= test_setup_copy_chunk(torture
, tree
, tmp_ctx
,
1212 &src_h
, 4096, /* fill 4096 byte src file */
1213 SEC_RIGHTS_FILE_ALL
,
1214 &dest_h
, 0, /* 0 byte dest file */
1215 (SEC_RIGHTS_FILE_WRITE
1216 | SEC_RIGHTS_FILE_EXECUTE
),
1220 torture_fail(torture
, "setup copy chunk error");
1223 cc_copy
.chunks
[0].source_off
= 0;
1224 cc_copy
.chunks
[0].target_off
= 0;
1225 cc_copy
.chunks
[0].length
= 4096;
1227 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1229 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1230 torture_assert_ndr_success(torture
, ndr_ret
,
1231 "ndr_push_srv_copychunk_copy");
1234 * FSCTL_SRV_COPYCHUNK requires read permission on dest,
1235 * FSCTL_SRV_COPYCHUNK_WRITE (not supported by Samba) on the other hand
1238 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1239 torture_assert_ntstatus_equal(torture
, status
,
1240 NT_STATUS_ACCESS_DENIED
,
1241 "FSCTL_SRV_COPYCHUNK");
1243 smb2_util_close(tree
, src_h
);
1244 smb2_util_close(tree
, dest_h
);
1245 talloc_free(tmp_ctx
);
1250 static bool test_ioctl_copy_chunk_src_exceed(struct torture_context
*torture
,
1251 struct smb2_tree
*tree
)
1253 struct smb2_handle src_h
;
1254 struct smb2_handle dest_h
;
1256 union smb_ioctl ioctl
;
1257 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
1258 struct srv_copychunk_copy cc_copy
;
1259 struct srv_copychunk_rsp cc_rsp
;
1260 enum ndr_err_code ndr_ret
;
1263 ok
= test_setup_copy_chunk(torture
, tree
, tmp_ctx
,
1265 &src_h
, 4096, /* fill 4096 byte src file */
1266 SEC_RIGHTS_FILE_ALL
,
1267 &dest_h
, 0, /* 0 byte dest file */
1268 SEC_RIGHTS_FILE_ALL
,
1272 torture_fail(torture
, "setup copy chunk error");
1275 /* Request copy where off + length exceeds size of src */
1276 cc_copy
.chunks
[0].source_off
= 1024;
1277 cc_copy
.chunks
[0].target_off
= 0;
1278 cc_copy
.chunks
[0].length
= 4096;
1280 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1282 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1283 torture_assert_ndr_success(torture
, ndr_ret
,
1284 "ndr_push_srv_copychunk_copy");
1286 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1287 torture_assert_ntstatus_equal(torture
, status
,
1288 NT_STATUS_INVALID_VIEW_SIZE
,
1289 "FSCTL_SRV_COPYCHUNK oversize");
1291 /* Request copy where length exceeds size of src */
1292 cc_copy
.chunks
[0].source_off
= 1024;
1293 cc_copy
.chunks
[0].target_off
= 0;
1294 cc_copy
.chunks
[0].length
= 3072;
1296 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1298 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1299 torture_assert_ndr_success(torture
, ndr_ret
,
1300 "ndr_push_srv_copychunk_copy");
1302 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1303 torture_assert_ntstatus_ok(torture
, status
,
1304 "FSCTL_SRV_COPYCHUNK just right");
1306 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
1308 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
1309 torture_assert_ndr_success(torture
, ndr_ret
,
1310 "ndr_pull_srv_copychunk_rsp");
1312 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
1313 1, /* chunks written */
1314 0, /* chunk bytes unsuccessfully written */
1315 3072); /* total bytes written */
1317 torture_fail(torture
, "bad copy chunk response data");
1320 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 0, 3072, 1024);
1322 torture_fail(torture
, "inconsistent file data");
1325 smb2_util_close(tree
, src_h
);
1326 smb2_util_close(tree
, dest_h
);
1327 talloc_free(tmp_ctx
);
1332 test_ioctl_copy_chunk_src_exceed_multi(struct torture_context
*torture
,
1333 struct smb2_tree
*tree
)
1335 struct smb2_handle src_h
;
1336 struct smb2_handle dest_h
;
1338 union smb_ioctl ioctl
;
1339 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
1340 struct srv_copychunk_copy cc_copy
;
1341 struct srv_copychunk_rsp cc_rsp
;
1342 enum ndr_err_code ndr_ret
;
1345 ok
= test_setup_copy_chunk(torture
, tree
, tmp_ctx
,
1347 &src_h
, 8192, /* fill 8192 byte src file */
1348 SEC_RIGHTS_FILE_ALL
,
1349 &dest_h
, 0, /* 0 byte dest file */
1350 SEC_RIGHTS_FILE_ALL
,
1354 torture_fail(torture
, "setup copy chunk error");
1357 /* Request copy where off + length exceeds size of src */
1358 cc_copy
.chunks
[0].source_off
= 0;
1359 cc_copy
.chunks
[0].target_off
= 0;
1360 cc_copy
.chunks
[0].length
= 4096;
1362 cc_copy
.chunks
[1].source_off
= 4096;
1363 cc_copy
.chunks
[1].target_off
= 4096;
1364 cc_copy
.chunks
[1].length
= 8192;
1366 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1368 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1369 torture_assert_ndr_success(torture
, ndr_ret
,
1370 "ndr_push_srv_copychunk_copy");
1372 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1373 torture_assert_ntstatus_equal(torture
, status
,
1374 NT_STATUS_INVALID_VIEW_SIZE
,
1375 "FSCTL_SRV_COPYCHUNK oversize");
1376 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
1378 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
1379 torture_assert_ndr_success(torture
, ndr_ret
, "unmarshalling response");
1381 /* first chunk should still be written */
1382 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
1383 1, /* chunks written */
1384 0, /* chunk bytes unsuccessfully written */
1385 4096); /* total bytes written */
1387 torture_fail(torture
, "bad copy chunk response data");
1389 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 0, 4096, 0);
1391 torture_fail(torture
, "inconsistent file data");
1394 smb2_util_close(tree
, src_h
);
1395 smb2_util_close(tree
, dest_h
);
1396 talloc_free(tmp_ctx
);
1400 static bool test_ioctl_copy_chunk_sparse_dest(struct torture_context
*torture
,
1401 struct smb2_tree
*tree
)
1403 struct smb2_handle src_h
;
1404 struct smb2_handle dest_h
;
1406 union smb_ioctl ioctl
;
1408 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
1409 struct srv_copychunk_copy cc_copy
;
1410 struct srv_copychunk_rsp cc_rsp
;
1411 enum ndr_err_code ndr_ret
;
1415 ok
= test_setup_copy_chunk(torture
, tree
, tmp_ctx
,
1417 &src_h
, 4096, /* fill 4096 byte src file */
1418 SEC_RIGHTS_FILE_ALL
,
1419 &dest_h
, 0, /* 0 byte dest file */
1420 SEC_RIGHTS_FILE_ALL
,
1424 torture_fail(torture
, "setup copy chunk error");
1427 /* copy all src file data (via a single chunk desc) */
1428 cc_copy
.chunks
[0].source_off
= 0;
1429 cc_copy
.chunks
[0].target_off
= 4096;
1430 cc_copy
.chunks
[0].length
= 4096;
1432 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1434 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1435 torture_assert_ndr_success(torture
, ndr_ret
,
1436 "ndr_push_srv_copychunk_copy");
1438 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1439 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_COPYCHUNK");
1441 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
1443 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
1444 torture_assert_ndr_success(torture
, ndr_ret
,
1445 "ndr_pull_srv_copychunk_rsp");
1447 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
1448 1, /* chunks written */
1449 0, /* chunk bytes unsuccessfully written */
1450 4096); /* total bytes written */
1452 torture_fail(torture
, "bad copy chunk response data");
1455 /* check for zeros in first 4k */
1457 r
.in
.file
.handle
= dest_h
;
1460 status
= smb2_read(tree
, tmp_ctx
, &r
);
1461 torture_assert_ntstatus_ok(torture
, status
, "read");
1463 torture_assert_u64_equal(torture
, r
.out
.data
.length
, 4096,
1464 "read data len mismatch");
1466 for (i
= 0; i
< 4096; i
++) {
1467 torture_assert(torture
, (r
.out
.data
.data
[i
] == 0),
1468 "sparse did not pass class");
1471 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 4096, 4096, 0);
1473 torture_fail(torture
, "inconsistent file data");
1476 smb2_util_close(tree
, src_h
);
1477 smb2_util_close(tree
, dest_h
);
1478 talloc_free(tmp_ctx
);
1483 * set the ioctl MaxOutputResponse size to less than
1484 * sizeof(struct srv_copychunk_rsp)
1486 static bool test_ioctl_copy_chunk_max_output_sz(struct torture_context
*torture
,
1487 struct smb2_tree
*tree
)
1489 struct smb2_handle src_h
;
1490 struct smb2_handle dest_h
;
1492 union smb_ioctl ioctl
;
1493 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
1494 struct srv_copychunk_copy cc_copy
;
1495 enum ndr_err_code ndr_ret
;
1498 ok
= test_setup_copy_chunk(torture
, tree
, tmp_ctx
,
1500 &src_h
, 4096, /* fill 4096 byte src file */
1501 SEC_RIGHTS_FILE_ALL
,
1502 &dest_h
, 0, /* 0 byte dest file */
1503 SEC_RIGHTS_FILE_ALL
,
1507 torture_fail(torture
, "setup copy chunk error");
1510 cc_copy
.chunks
[0].source_off
= 0;
1511 cc_copy
.chunks
[0].target_off
= 0;
1512 cc_copy
.chunks
[0].length
= 4096;
1513 /* req is valid, but use undersize max_response_size */
1514 ioctl
.smb2
.in
.max_response_size
= sizeof(struct srv_copychunk_rsp
) - 1;
1516 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1518 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1519 torture_assert_ndr_success(torture
, ndr_ret
,
1520 "ndr_push_srv_copychunk_copy");
1522 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1523 torture_assert_ntstatus_equal(torture
, status
,
1524 NT_STATUS_INVALID_PARAMETER
,
1525 "FSCTL_SRV_COPYCHUNK");
1527 smb2_util_close(tree
, src_h
);
1528 smb2_util_close(tree
, dest_h
);
1529 talloc_free(tmp_ctx
);
1534 basic testing of SMB2 ioctls
1536 struct torture_suite
*torture_smb2_ioctl_init(void)
1538 struct torture_suite
*suite
= torture_suite_create(talloc_autofree_context(), "ioctl");
1540 torture_suite_add_1smb2_test(suite
, "shadow_copy",
1541 test_ioctl_get_shadow_copy
);
1542 torture_suite_add_1smb2_test(suite
, "req_resume_key",
1543 test_ioctl_req_resume_key
);
1544 torture_suite_add_1smb2_test(suite
, "copy_chunk_simple",
1545 test_ioctl_copy_chunk_simple
);
1546 torture_suite_add_1smb2_test(suite
, "copy_chunk_multi",
1547 test_ioctl_copy_chunk_multi
);
1548 torture_suite_add_1smb2_test(suite
, "copy_chunk_tiny",
1549 test_ioctl_copy_chunk_tiny
);
1550 torture_suite_add_1smb2_test(suite
, "copy_chunk_overwrite",
1551 test_ioctl_copy_chunk_over
);
1552 torture_suite_add_1smb2_test(suite
, "copy_chunk_append",
1553 test_ioctl_copy_chunk_append
);
1554 torture_suite_add_1smb2_test(suite
, "copy_chunk_limits",
1555 test_ioctl_copy_chunk_limits
);
1556 torture_suite_add_1smb2_test(suite
, "copy_chunk_src_lock",
1557 test_ioctl_copy_chunk_src_lck
);
1558 torture_suite_add_1smb2_test(suite
, "copy_chunk_dest_lock",
1559 test_ioctl_copy_chunk_dest_lck
);
1560 torture_suite_add_1smb2_test(suite
, "copy_chunk_bad_key",
1561 test_ioctl_copy_chunk_bad_key
);
1562 torture_suite_add_1smb2_test(suite
, "copy_chunk_src_is_dest",
1563 test_ioctl_copy_chunk_src_is_dest
);
1564 torture_suite_add_1smb2_test(suite
, "copy_chunk_src_is_dest_overlap",
1565 test_ioctl_copy_chunk_src_is_dest_overlap
);
1566 torture_suite_add_1smb2_test(suite
, "copy_chunk_bad_access",
1567 test_ioctl_copy_chunk_bad_access
);
1568 torture_suite_add_1smb2_test(suite
, "copy_chunk_src_exceed",
1569 test_ioctl_copy_chunk_src_exceed
);
1570 torture_suite_add_1smb2_test(suite
, "copy_chunk_src_exceed_multi",
1571 test_ioctl_copy_chunk_src_exceed_multi
);
1572 torture_suite_add_1smb2_test(suite
, "copy_chunk_sparse_dest",
1573 test_ioctl_copy_chunk_sparse_dest
);
1574 torture_suite_add_1smb2_test(suite
, "copy_chunk_max_output_sz",
1575 test_ioctl_copy_chunk_max_output_sz
);
1577 suite
->description
= talloc_strdup(suite
, "SMB2-IOCTL tests");