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_copy_chunk(struct torture_context
*torture
,
152 struct smb2_tree
*tree
, TALLOC_CTX
*mem_ctx
,
154 struct smb2_handle
*src_h
,
156 uint32_t src_desired_access
,
157 struct smb2_handle
*dest_h
,
159 uint32_t dest_desired_access
,
160 struct srv_copychunk_copy
*cc_copy
,
161 union smb_ioctl
*ioctl
)
163 struct req_resume_key_rsp res_key
;
164 struct smb2_create io
;
166 enum ndr_err_code ndr_ret
;
168 uint8_t *buf
= talloc_zero_size(mem_ctx
, MAX(src_size
, dest_size
));
169 torture_assert(torture
, (buf
!= NULL
), "no memory for file data buf");
171 smb2_util_unlink(tree
, FNAME
);
172 smb2_util_unlink(tree
, FNAME2
);
175 io
.in
.desired_access
= src_desired_access
;
176 io
.in
.file_attributes
= FILE_ATTRIBUTE_NORMAL
;
177 io
.in
.create_disposition
= NTCREATEX_DISP_OPEN_IF
;
179 NTCREATEX_SHARE_ACCESS_DELETE
|
180 NTCREATEX_SHARE_ACCESS_READ
|
181 NTCREATEX_SHARE_ACCESS_WRITE
;
184 status
= smb2_create(tree
, mem_ctx
, &io
);
185 torture_assert_ntstatus_ok(torture
, status
, "src create");
187 *src_h
= io
.out
.file
.handle
;
190 uint64_t cur_off
= 0;
191 for (i
= 0; i
<= src_size
- 8; i
+= 8) {
192 SBVAL(buf
, i
, patt_hash(i
));
194 while (src_size
> 0) {
195 uint64_t io_sz
= MIN(1024 * 1024, src_size
);
196 status
= smb2_util_write(tree
, *src_h
,
197 buf
+ cur_off
, cur_off
, io_sz
);
198 torture_assert_ntstatus_ok(torture
, status
, "src write");
206 io
.in
.desired_access
= dest_desired_access
;
207 io
.in
.file_attributes
= FILE_ATTRIBUTE_NORMAL
;
208 io
.in
.create_disposition
= NTCREATEX_DISP_OPEN_IF
;
210 NTCREATEX_SHARE_ACCESS_DELETE
|
211 NTCREATEX_SHARE_ACCESS_READ
|
212 NTCREATEX_SHARE_ACCESS_WRITE
;
213 io
.in
.fname
= FNAME2
;
215 status
= smb2_create(tree
, mem_ctx
, &io
);
216 torture_assert_ntstatus_ok(torture
, status
, "dest create");
218 *dest_h
= io
.out
.file
.handle
;
221 uint64_t cur_off
= 0;
222 for (i
= 0; i
<= dest_size
- 8; i
+= 8) {
223 SBVAL(buf
, i
, patt_hash(i
));
225 while (dest_size
> 0) {
226 uint64_t io_sz
= MIN(1024 * 1024, dest_size
);
227 status
= smb2_util_write(tree
, *dest_h
,
228 buf
+ cur_off
, cur_off
, io_sz
);
229 torture_assert_ntstatus_ok(torture
, status
, "dest write");
236 ZERO_STRUCTPN(ioctl
);
237 ioctl
->smb2
.level
= RAW_IOCTL_SMB2
;
238 ioctl
->smb2
.in
.file
.handle
= *src_h
;
239 ioctl
->smb2
.in
.function
= FSCTL_SRV_REQUEST_RESUME_KEY
;
240 /* Allow for Key + ContextLength + Context */
241 ioctl
->smb2
.in
.max_response_size
= 32;
242 ioctl
->smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
244 status
= smb2_ioctl(tree
, mem_ctx
, &ioctl
->smb2
);
245 torture_assert_ntstatus_ok(torture
, status
,
246 "FSCTL_SRV_REQUEST_RESUME_KEY");
249 ndr_ret
= ndr_pull_struct_blob(&ioctl
->smb2
.out
.out
, mem_ctx
, &res_key
,
250 (ndr_pull_flags_fn_t
)ndr_pull_req_resume_key_rsp
);
252 torture_assert_ndr_success(torture
, ndr_ret
,
253 "ndr_pull_req_resume_key_rsp");
255 ZERO_STRUCTPN(ioctl
);
256 ioctl
->smb2
.level
= RAW_IOCTL_SMB2
;
257 ioctl
->smb2
.in
.file
.handle
= *dest_h
;
258 ioctl
->smb2
.in
.function
= FSCTL_SRV_COPYCHUNK
;
259 ioctl
->smb2
.in
.max_response_size
= sizeof(struct srv_copychunk_rsp
);
260 ioctl
->smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
262 ZERO_STRUCTPN(cc_copy
);
263 memcpy(cc_copy
->source_key
, res_key
.resume_key
, ARRAY_SIZE(cc_copy
->source_key
));
264 cc_copy
->chunk_count
= nchunks
;
265 cc_copy
->chunks
= talloc_zero_array(mem_ctx
, struct srv_copychunk
, nchunks
);
266 torture_assert(torture
, (cc_copy
->chunks
!= NULL
), "no memory for chunks");
272 static bool check_copy_chunk_rsp(struct torture_context
*torture
,
273 struct srv_copychunk_rsp
*cc_rsp
,
274 uint32_t ex_chunks_written
,
275 uint32_t ex_chunk_bytes_written
,
276 uint32_t ex_total_bytes_written
)
278 torture_assert_int_equal(torture
, cc_rsp
->chunks_written
,
279 ex_chunks_written
, "num chunks");
280 torture_assert_int_equal(torture
, cc_rsp
->chunk_bytes_written
,
281 ex_chunk_bytes_written
, "chunk bytes written");
282 torture_assert_int_equal(torture
, cc_rsp
->total_bytes_written
,
283 ex_total_bytes_written
, "chunk total bytes");
287 static bool test_ioctl_copy_chunk_simple(struct torture_context
*torture
,
288 struct smb2_tree
*tree
)
290 struct smb2_handle src_h
;
291 struct smb2_handle dest_h
;
293 union smb_ioctl ioctl
;
294 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
295 struct srv_copychunk_copy cc_copy
;
296 struct srv_copychunk_rsp cc_rsp
;
297 enum ndr_err_code ndr_ret
;
300 ok
= test_setup_copy_chunk(torture
, tree
, tmp_ctx
,
302 &src_h
, 4096, /* fill 4096 byte src file */
304 &dest_h
, 0, /* 0 byte dest file */
309 torture_fail(torture
, "setup copy chunk error");
312 /* copy all src file data (via a single chunk desc) */
313 cc_copy
.chunks
[0].source_off
= 0;
314 cc_copy
.chunks
[0].target_off
= 0;
315 cc_copy
.chunks
[0].length
= 4096;
317 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
319 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
320 torture_assert_ndr_success(torture
, ndr_ret
,
321 "ndr_push_srv_copychunk_copy");
323 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
324 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_COPYCHUNK");
326 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
328 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
329 torture_assert_ndr_success(torture
, ndr_ret
,
330 "ndr_pull_srv_copychunk_rsp");
332 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
333 1, /* chunks written */
334 0, /* chunk bytes unsuccessfully written */
335 4096); /* total bytes written */
337 torture_fail(torture
, "bad copy chunk response data");
340 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 0, 4096, 0);
342 torture_fail(torture
, "inconsistent file data");
345 smb2_util_close(tree
, src_h
);
346 smb2_util_close(tree
, dest_h
);
347 talloc_free(tmp_ctx
);
351 static bool test_ioctl_copy_chunk_multi(struct torture_context
*torture
,
352 struct smb2_tree
*tree
)
354 struct smb2_handle src_h
;
355 struct smb2_handle dest_h
;
357 union smb_ioctl ioctl
;
358 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
359 struct srv_copychunk_copy cc_copy
;
360 struct srv_copychunk_rsp cc_rsp
;
361 enum ndr_err_code ndr_ret
;
364 ok
= test_setup_copy_chunk(torture
, tree
, tmp_ctx
,
366 &src_h
, 8192, /* src file */
368 &dest_h
, 0, /* dest file */
373 torture_fail(torture
, "setup copy chunk error");
376 /* copy all src file data via two chunks */
377 cc_copy
.chunks
[0].source_off
= 0;
378 cc_copy
.chunks
[0].target_off
= 0;
379 cc_copy
.chunks
[0].length
= 4096;
381 cc_copy
.chunks
[1].source_off
= 4096;
382 cc_copy
.chunks
[1].target_off
= 4096;
383 cc_copy
.chunks
[1].length
= 4096;
385 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
387 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
388 torture_assert_ndr_success(torture
, ndr_ret
,
389 "ndr_push_srv_copychunk_copy");
391 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
392 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_COPYCHUNK");
394 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
396 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
397 torture_assert_ndr_success(torture
, ndr_ret
,
398 "ndr_pull_srv_copychunk_rsp");
400 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
401 2, /* chunks written */
402 0, /* chunk bytes unsuccessfully written */
403 8192); /* total bytes written */
405 torture_fail(torture
, "bad copy chunk response data");
408 smb2_util_close(tree
, src_h
);
409 smb2_util_close(tree
, dest_h
);
410 talloc_free(tmp_ctx
);
414 static bool test_ioctl_copy_chunk_tiny(struct torture_context
*torture
,
415 struct smb2_tree
*tree
)
417 struct smb2_handle src_h
;
418 struct smb2_handle dest_h
;
420 union smb_ioctl ioctl
;
421 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
422 struct srv_copychunk_copy cc_copy
;
423 struct srv_copychunk_rsp cc_rsp
;
424 enum ndr_err_code ndr_ret
;
427 ok
= test_setup_copy_chunk(torture
, tree
, tmp_ctx
,
429 &src_h
, 100, /* src file */
431 &dest_h
, 0, /* dest file */
436 torture_fail(torture
, "setup copy chunk error");
439 /* copy all src file data via two chunks, sub block size chunks */
440 cc_copy
.chunks
[0].source_off
= 0;
441 cc_copy
.chunks
[0].target_off
= 0;
442 cc_copy
.chunks
[0].length
= 50;
444 cc_copy
.chunks
[1].source_off
= 50;
445 cc_copy
.chunks
[1].target_off
= 50;
446 cc_copy
.chunks
[1].length
= 50;
448 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
450 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
451 torture_assert_ndr_success(torture
, ndr_ret
,
452 "ndr_push_srv_copychunk_copy");
454 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
455 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_COPYCHUNK");
457 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
459 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
460 torture_assert_ndr_success(torture
, ndr_ret
,
461 "ndr_pull_srv_copychunk_rsp");
463 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
464 2, /* chunks written */
465 0, /* chunk bytes unsuccessfully written */
466 100); /* total bytes written */
468 torture_fail(torture
, "bad copy chunk response data");
471 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 0, 100, 0);
473 torture_fail(torture
, "inconsistent file data");
476 smb2_util_close(tree
, src_h
);
477 smb2_util_close(tree
, dest_h
);
478 talloc_free(tmp_ctx
);
482 static bool test_ioctl_copy_chunk_over(struct torture_context
*torture
,
483 struct smb2_tree
*tree
)
485 struct smb2_handle src_h
;
486 struct smb2_handle dest_h
;
488 union smb_ioctl ioctl
;
489 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
490 struct srv_copychunk_copy cc_copy
;
491 struct srv_copychunk_rsp cc_rsp
;
492 enum ndr_err_code ndr_ret
;
495 ok
= test_setup_copy_chunk(torture
, tree
, tmp_ctx
,
497 &src_h
, 8192, /* src file */
499 &dest_h
, 4096, /* dest file */
504 torture_fail(torture
, "setup copy chunk error");
507 /* first chunk overwrites existing dest data */
508 cc_copy
.chunks
[0].source_off
= 0;
509 cc_copy
.chunks
[0].target_off
= 0;
510 cc_copy
.chunks
[0].length
= 4096;
512 /* second chunk overwrites the first */
513 cc_copy
.chunks
[1].source_off
= 4096;
514 cc_copy
.chunks
[1].target_off
= 0;
515 cc_copy
.chunks
[1].length
= 4096;
517 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
519 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
520 torture_assert_ndr_success(torture
, ndr_ret
,
521 "ndr_push_srv_copychunk_copy");
523 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
524 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_COPYCHUNK");
526 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
528 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
529 torture_assert_ndr_success(torture
, ndr_ret
,
530 "ndr_pull_srv_copychunk_rsp");
532 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
533 2, /* chunks written */
534 0, /* chunk bytes unsuccessfully written */
535 8192); /* total bytes written */
537 torture_fail(torture
, "bad copy chunk response data");
540 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 0, 4096, 4096);
542 torture_fail(torture
, "inconsistent file data");
545 smb2_util_close(tree
, src_h
);
546 smb2_util_close(tree
, dest_h
);
547 talloc_free(tmp_ctx
);
551 static bool test_ioctl_copy_chunk_append(struct torture_context
*torture
,
552 struct smb2_tree
*tree
)
554 struct smb2_handle src_h
;
555 struct smb2_handle dest_h
;
557 union smb_ioctl ioctl
;
558 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
559 struct srv_copychunk_copy cc_copy
;
560 struct srv_copychunk_rsp cc_rsp
;
561 enum ndr_err_code ndr_ret
;
564 ok
= test_setup_copy_chunk(torture
, tree
, tmp_ctx
,
566 &src_h
, 4096, /* src file */
568 &dest_h
, 0, /* dest file */
573 torture_fail(torture
, "setup copy chunk error");
576 cc_copy
.chunks
[0].source_off
= 0;
577 cc_copy
.chunks
[0].target_off
= 0;
578 cc_copy
.chunks
[0].length
= 4096;
580 /* second chunk appends the same data to the first */
581 cc_copy
.chunks
[1].source_off
= 0;
582 cc_copy
.chunks
[1].target_off
= 4096;
583 cc_copy
.chunks
[1].length
= 4096;
585 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
587 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
588 torture_assert_ndr_success(torture
, ndr_ret
,
589 "ndr_push_srv_copychunk_copy");
591 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
592 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_COPYCHUNK");
594 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
596 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
597 torture_assert_ndr_success(torture
, ndr_ret
,
598 "ndr_pull_srv_copychunk_rsp");
600 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
601 2, /* chunks written */
602 0, /* chunk bytes unsuccessfully written */
603 8192); /* total bytes written */
605 torture_fail(torture
, "bad copy chunk response data");
608 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 0, 4096, 0);
610 torture_fail(torture
, "inconsistent file data");
613 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 4096, 4096, 0);
615 torture_fail(torture
, "inconsistent file data");
618 smb2_util_close(tree
, src_h
);
619 smb2_util_close(tree
, dest_h
);
620 talloc_free(tmp_ctx
);
624 static bool test_ioctl_copy_chunk_limits(struct torture_context
*torture
,
625 struct smb2_tree
*tree
)
627 struct smb2_handle src_h
;
628 struct smb2_handle dest_h
;
630 union smb_ioctl ioctl
;
631 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
632 struct srv_copychunk_copy cc_copy
;
633 struct srv_copychunk_rsp cc_rsp
;
634 enum ndr_err_code ndr_ret
;
637 ok
= test_setup_copy_chunk(torture
, tree
, tmp_ctx
,
639 &src_h
, 4096, /* src file */
641 &dest_h
, 0, /* dest file */
646 torture_fail(torture
, "setup copy chunk error");
649 /* send huge chunk length request */
650 cc_copy
.chunks
[0].source_off
= 0;
651 cc_copy
.chunks
[0].target_off
= 0;
652 cc_copy
.chunks
[0].length
= UINT_MAX
;
654 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
656 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
657 torture_assert_ndr_success(torture
, ndr_ret
, "marshalling request");
659 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
660 torture_assert_ntstatus_equal(torture
, status
,
661 NT_STATUS_INVALID_PARAMETER
,
662 "bad oversize chunk response");
664 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
666 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
667 torture_assert_ndr_success(torture
, ndr_ret
, "unmarshalling response");
669 torture_comment(torture
, "limit max chunks, got %u\n",
670 cc_rsp
.chunks_written
);
671 torture_comment(torture
, "limit max chunk len, got %u\n",
672 cc_rsp
.chunk_bytes_written
);
673 torture_comment(torture
, "limit max total bytes, got %u\n",
674 cc_rsp
.total_bytes_written
);
676 smb2_util_close(tree
, src_h
);
677 smb2_util_close(tree
, dest_h
);
678 talloc_free(tmp_ctx
);
682 static bool test_ioctl_copy_chunk_src_lck(struct torture_context
*torture
,
683 struct smb2_tree
*tree
)
685 struct smb2_handle src_h
;
686 struct smb2_handle src_h2
;
687 struct smb2_handle dest_h
;
689 union smb_ioctl ioctl
;
690 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
691 struct srv_copychunk_copy cc_copy
;
692 struct srv_copychunk_rsp cc_rsp
;
693 enum ndr_err_code ndr_ret
;
695 struct smb2_lock lck
;
696 struct smb2_lock_element el
[1];
698 ok
= test_setup_copy_chunk(torture
, tree
, tmp_ctx
,
700 &src_h
, 4096, /* src file */
702 &dest_h
, 0, /* dest file */
707 torture_fail(torture
, "setup copy chunk error");
710 cc_copy
.chunks
[0].source_off
= 0;
711 cc_copy
.chunks
[0].target_off
= 0;
712 cc_copy
.chunks
[0].length
= 4096;
714 /* open and lock the copychunk src file */
715 status
= torture_smb2_testfile(tree
, FNAME
, &src_h2
);
716 torture_assert_ntstatus_ok(torture
, status
, "2nd src open");
718 lck
.in
.lock_count
= 0x0001;
719 lck
.in
.lock_sequence
= 0x00000000;
720 lck
.in
.file
.handle
= src_h2
;
722 el
[0].offset
= cc_copy
.chunks
[0].source_off
;
723 el
[0].length
= cc_copy
.chunks
[0].length
;
725 el
[0].flags
= SMB2_LOCK_FLAG_EXCLUSIVE
;
727 status
= smb2_lock(tree
, &lck
);
728 torture_assert_ntstatus_ok(torture
, status
, "lock");
730 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
732 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
733 torture_assert_ndr_success(torture
, ndr_ret
,
734 "ndr_push_srv_copychunk_copy");
736 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
738 * 2k12 & Samba return lock_conflict, Windows 7 & 2k8 return success...
740 * Edgar Olougouna @ MS wrote:
741 * Regarding the FSCTL_SRV_COPYCHUNK and STATUS_FILE_LOCK_CONFLICT
742 * discrepancy observed between Windows versions, we confirm that the
743 * behavior change is expected.
745 * CopyChunk in Windows Server 2012 use regular Readfile/Writefile APIs
746 * to move the chunks from the source to the destination.
747 * These ReadFile/WriteFile APIs go through the byte-range lock checks,
748 * and this explains the observed STATUS_FILE_LOCK_CONFLICT error.
750 * Prior to Windows Server 2012, CopyChunk used mapped sections to move
751 * the data. And byte range locks are not enforced on mapped I/O, and
752 * this explains the STATUS_SUCCESS observed on Windows Server 2008 R2.
754 torture_assert_ntstatus_equal(torture
, status
,
755 NT_STATUS_FILE_LOCK_CONFLICT
,
756 "FSCTL_SRV_COPYCHUNK locked");
758 /* should get cc response data with the lock conflict status */
759 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
761 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
762 torture_assert_ndr_success(torture
, ndr_ret
,
763 "ndr_pull_srv_copychunk_rsp");
764 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
765 0, /* chunks written */
766 0, /* chunk bytes unsuccessfully written */
767 0); /* total bytes written */
769 lck
.in
.lock_count
= 0x0001;
770 lck
.in
.lock_sequence
= 0x00000001;
771 lck
.in
.file
.handle
= src_h2
;
773 el
[0].offset
= cc_copy
.chunks
[0].source_off
;
774 el
[0].length
= cc_copy
.chunks
[0].length
;
776 el
[0].flags
= SMB2_LOCK_FLAG_UNLOCK
;
777 status
= smb2_lock(tree
, &lck
);
778 torture_assert_ntstatus_ok(torture
, status
, "unlock");
780 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
781 torture_assert_ntstatus_ok(torture
, status
,
782 "FSCTL_SRV_COPYCHUNK unlocked");
784 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
786 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
787 torture_assert_ndr_success(torture
, ndr_ret
,
788 "ndr_pull_srv_copychunk_rsp");
790 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
791 1, /* chunks written */
792 0, /* chunk bytes unsuccessfully written */
793 4096); /* total bytes written */
795 torture_fail(torture
, "bad copy chunk response data");
798 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 0, 4096, 0);
800 torture_fail(torture
, "inconsistent file data");
803 smb2_util_close(tree
, src_h2
);
804 smb2_util_close(tree
, src_h
);
805 smb2_util_close(tree
, dest_h
);
806 talloc_free(tmp_ctx
);
810 static bool test_ioctl_copy_chunk_dest_lck(struct torture_context
*torture
,
811 struct smb2_tree
*tree
)
813 struct smb2_handle src_h
;
814 struct smb2_handle dest_h
;
815 struct smb2_handle dest_h2
;
817 union smb_ioctl ioctl
;
818 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
819 struct srv_copychunk_copy cc_copy
;
820 struct srv_copychunk_rsp cc_rsp
;
821 enum ndr_err_code ndr_ret
;
823 struct smb2_lock lck
;
824 struct smb2_lock_element el
[1];
826 ok
= test_setup_copy_chunk(torture
, tree
, tmp_ctx
,
828 &src_h
, 4096, /* src file */
830 &dest_h
, 4096, /* dest file */
835 torture_fail(torture
, "setup copy chunk error");
838 cc_copy
.chunks
[0].source_off
= 0;
839 cc_copy
.chunks
[0].target_off
= 0;
840 cc_copy
.chunks
[0].length
= 4096;
842 /* open and lock the copychunk dest file */
843 status
= torture_smb2_testfile(tree
, FNAME2
, &dest_h2
);
844 torture_assert_ntstatus_ok(torture
, status
, "2nd src open");
846 lck
.in
.lock_count
= 0x0001;
847 lck
.in
.lock_sequence
= 0x00000000;
848 lck
.in
.file
.handle
= dest_h2
;
850 el
[0].offset
= cc_copy
.chunks
[0].target_off
;
851 el
[0].length
= cc_copy
.chunks
[0].length
;
853 el
[0].flags
= SMB2_LOCK_FLAG_EXCLUSIVE
;
855 status
= smb2_lock(tree
, &lck
);
856 torture_assert_ntstatus_ok(torture
, status
, "lock");
858 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
860 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
861 torture_assert_ndr_success(torture
, ndr_ret
,
862 "ndr_push_srv_copychunk_copy");
864 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
865 torture_assert_ntstatus_equal(torture
, status
,
866 NT_STATUS_FILE_LOCK_CONFLICT
,
867 "FSCTL_SRV_COPYCHUNK locked");
869 lck
.in
.lock_count
= 0x0001;
870 lck
.in
.lock_sequence
= 0x00000001;
871 lck
.in
.file
.handle
= dest_h2
;
873 el
[0].offset
= cc_copy
.chunks
[0].target_off
;
874 el
[0].length
= cc_copy
.chunks
[0].length
;
876 el
[0].flags
= SMB2_LOCK_FLAG_UNLOCK
;
877 status
= smb2_lock(tree
, &lck
);
878 torture_assert_ntstatus_ok(torture
, status
, "unlock");
880 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
881 torture_assert_ntstatus_ok(torture
, status
,
882 "FSCTL_SRV_COPYCHUNK unlocked");
884 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
886 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
887 torture_assert_ndr_success(torture
, ndr_ret
,
888 "ndr_pull_srv_copychunk_rsp");
890 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
891 1, /* chunks written */
892 0, /* chunk bytes unsuccessfully written */
893 4096); /* total bytes written */
895 torture_fail(torture
, "bad copy chunk response data");
898 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 0, 4096, 0);
900 torture_fail(torture
, "inconsistent file data");
903 smb2_util_close(tree
, dest_h2
);
904 smb2_util_close(tree
, src_h
);
905 smb2_util_close(tree
, dest_h
);
906 talloc_free(tmp_ctx
);
910 static bool test_ioctl_copy_chunk_bad_key(struct torture_context
*torture
,
911 struct smb2_tree
*tree
)
913 struct smb2_handle src_h
;
914 struct smb2_handle dest_h
;
916 union smb_ioctl ioctl
;
917 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
918 struct srv_copychunk_copy cc_copy
;
919 enum ndr_err_code ndr_ret
;
922 ok
= test_setup_copy_chunk(torture
, tree
, tmp_ctx
,
931 torture_fail(torture
, "setup copy chunk error");
934 /* overwrite the resume key with a bogus value */
935 memcpy(cc_copy
.source_key
, "deadbeefdeadbeefdeadbeef", 24);
937 cc_copy
.chunks
[0].source_off
= 0;
938 cc_copy
.chunks
[0].target_off
= 0;
939 cc_copy
.chunks
[0].length
= 4096;
941 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
943 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
944 torture_assert_ndr_success(torture
, ndr_ret
,
945 "ndr_push_srv_copychunk_copy");
947 /* Server 2k12 returns NT_STATUS_OBJECT_NAME_NOT_FOUND */
948 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
949 torture_assert_ntstatus_equal(torture
, status
,
950 NT_STATUS_OBJECT_NAME_NOT_FOUND
,
951 "FSCTL_SRV_COPYCHUNK");
953 smb2_util_close(tree
, src_h
);
954 smb2_util_close(tree
, dest_h
);
955 talloc_free(tmp_ctx
);
959 static bool test_ioctl_copy_chunk_src_is_dest(struct torture_context
*torture
,
960 struct smb2_tree
*tree
)
962 struct smb2_handle src_h
;
963 struct smb2_handle dest_h
;
965 union smb_ioctl ioctl
;
966 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
967 struct srv_copychunk_copy cc_copy
;
968 struct srv_copychunk_rsp cc_rsp
;
969 enum ndr_err_code ndr_ret
;
972 ok
= test_setup_copy_chunk(torture
, tree
, tmp_ctx
,
981 torture_fail(torture
, "setup copy chunk error");
984 /* the source is also the destination */
985 ioctl
.smb2
.in
.file
.handle
= src_h
;
987 /* non-overlapping */
988 cc_copy
.chunks
[0].source_off
= 0;
989 cc_copy
.chunks
[0].target_off
= 4096;
990 cc_copy
.chunks
[0].length
= 4096;
992 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
994 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
995 torture_assert_ndr_success(torture
, ndr_ret
,
996 "ndr_push_srv_copychunk_copy");
998 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
999 torture_assert_ntstatus_ok(torture
, status
,
1000 "FSCTL_SRV_COPYCHUNK");
1002 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
1004 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
1005 torture_assert_ndr_success(torture
, ndr_ret
,
1006 "ndr_pull_srv_copychunk_rsp");
1008 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
1009 1, /* chunks written */
1010 0, /* chunk bytes unsuccessfully written */
1011 4096); /* total bytes written */
1013 torture_fail(torture
, "bad copy chunk response data");
1016 ok
= check_pattern(torture
, tree
, tmp_ctx
, src_h
, 0, 4096, 0);
1018 torture_fail(torture
, "inconsistent file data");
1020 ok
= check_pattern(torture
, tree
, tmp_ctx
, src_h
, 4096, 4096, 0);
1022 torture_fail(torture
, "inconsistent file data");
1025 smb2_util_close(tree
, src_h
);
1026 smb2_util_close(tree
, dest_h
);
1027 talloc_free(tmp_ctx
);
1032 * Test a single-chunk copychunk request, where the source and target ranges
1033 * overlap, and the SourceKey refers to the same target file. E.g:
1037 * File: src_and_dest
1038 * Offset: 0123456789
1043 * FSCTL_SRV_COPYCHUNK(src_and_dest)
1044 * SourceKey = SRV_REQUEST_RESUME_KEY(src_and_dest)
1046 * Chunks[0].SourceOffset = 0
1047 * Chunks[0].TargetOffset = 4
1048 * Chunks[0].Length = 6
1052 * File: src_and_dest
1053 * Offset: 0123456789
1056 * The resultant contents of src_and_dest is dependent on the server's
1057 * copy algorithm. In the above example, the server uses an IO buffer
1058 * large enough to hold the entire six-byte source data before writing
1059 * to TargetOffset. If the server were to use a four-byte IO buffer and
1060 * started reads/writes from the lowest offset, then the two overlapping
1061 * bytes in the above example would be overwritten before being read. The
1062 * resultant file contents would be abcdabcdab.
1064 * Windows 2008r2 appears to use a 2048 byte copy buffer, overlapping bytes
1065 * after this offset are written before being read. Windows 2012 on the
1066 * other hand appears to use a buffer large enough to hold its maximum
1067 * supported chunk size (1M). Samba currently uses a 64k copy buffer by
1068 * default (vfs_cc_state.buf).
1070 * This test uses an 8-byte overlap at 2040-2048, so that it passes against
1071 * Windows 2008, 2012 and Samba servers.
1074 test_ioctl_copy_chunk_src_is_dest_overlap(struct torture_context
*torture
,
1075 struct smb2_tree
*tree
)
1077 struct smb2_handle src_h
;
1078 struct smb2_handle dest_h
;
1080 union smb_ioctl ioctl
;
1081 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
1082 struct srv_copychunk_copy cc_copy
;
1083 struct srv_copychunk_rsp cc_rsp
;
1084 enum ndr_err_code ndr_ret
;
1087 /* exceed the vfs_default copy buffer */
1088 ok
= test_setup_copy_chunk(torture
, tree
, tmp_ctx
,
1091 SEC_RIGHTS_FILE_ALL
,
1093 SEC_RIGHTS_FILE_ALL
,
1097 torture_fail(torture
, "setup copy chunk error");
1100 /* the source is also the destination */
1101 ioctl
.smb2
.in
.file
.handle
= src_h
;
1103 /* 8 bytes overlap between source and target ranges */
1104 cc_copy
.chunks
[0].source_off
= 0;
1105 cc_copy
.chunks
[0].target_off
= 2048 - 8;
1106 cc_copy
.chunks
[0].length
= 2048;
1108 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1110 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1111 torture_assert_ndr_success(torture
, ndr_ret
,
1112 "ndr_push_srv_copychunk_copy");
1114 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1115 torture_assert_ntstatus_ok(torture
, status
,
1116 "FSCTL_SRV_COPYCHUNK");
1118 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
1120 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
1121 torture_assert_ndr_success(torture
, ndr_ret
,
1122 "ndr_pull_srv_copychunk_rsp");
1124 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
1125 1, /* chunks written */
1126 0, /* chunk bytes unsuccessfully written */
1127 2048); /* total bytes written */
1129 torture_fail(torture
, "bad copy chunk response data");
1132 ok
= check_pattern(torture
, tree
, tmp_ctx
, src_h
, 0, 2048 - 8, 0);
1134 torture_fail(torture
, "inconsistent file data");
1136 ok
= check_pattern(torture
, tree
, tmp_ctx
, src_h
, 2048 - 8, 2048, 0);
1138 torture_fail(torture
, "inconsistent file data");
1141 smb2_util_close(tree
, src_h
);
1142 smb2_util_close(tree
, dest_h
);
1143 talloc_free(tmp_ctx
);
1147 static bool test_ioctl_copy_chunk_bad_access(struct torture_context
*torture
,
1148 struct smb2_tree
*tree
)
1150 struct smb2_handle src_h
;
1151 struct smb2_handle dest_h
;
1153 union smb_ioctl ioctl
;
1154 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
1155 struct srv_copychunk_copy cc_copy
;
1156 enum ndr_err_code ndr_ret
;
1159 /* no read permission on src */
1160 ok
= test_setup_copy_chunk(torture
, tree
, tmp_ctx
,
1162 &src_h
, 4096, /* fill 4096 byte src file */
1163 SEC_RIGHTS_FILE_WRITE
,
1164 &dest_h
, 0, /* 0 byte dest file */
1165 SEC_RIGHTS_FILE_ALL
,
1169 torture_fail(torture
, "setup copy chunk error");
1172 cc_copy
.chunks
[0].source_off
= 0;
1173 cc_copy
.chunks
[0].target_off
= 0;
1174 cc_copy
.chunks
[0].length
= 4096;
1176 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1178 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1179 torture_assert_ndr_success(torture
, ndr_ret
,
1180 "ndr_push_srv_copychunk_copy");
1182 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1183 torture_assert_ntstatus_equal(torture
, status
,
1184 NT_STATUS_ACCESS_DENIED
,
1185 "FSCTL_SRV_COPYCHUNK");
1187 smb2_util_close(tree
, src_h
);
1188 smb2_util_close(tree
, dest_h
);
1190 /* no write permission on dest */
1191 ok
= test_setup_copy_chunk(torture
, tree
, tmp_ctx
,
1193 &src_h
, 4096, /* fill 4096 byte src file */
1194 SEC_RIGHTS_FILE_ALL
,
1195 &dest_h
, 0, /* 0 byte dest file */
1196 (SEC_RIGHTS_FILE_READ
1197 | SEC_RIGHTS_FILE_EXECUTE
),
1201 torture_fail(torture
, "setup copy chunk error");
1204 cc_copy
.chunks
[0].source_off
= 0;
1205 cc_copy
.chunks
[0].target_off
= 0;
1206 cc_copy
.chunks
[0].length
= 4096;
1208 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1210 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1211 torture_assert_ndr_success(torture
, ndr_ret
,
1212 "ndr_push_srv_copychunk_copy");
1214 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1215 torture_assert_ntstatus_equal(torture
, status
,
1216 NT_STATUS_ACCESS_DENIED
,
1217 "FSCTL_SRV_COPYCHUNK");
1219 smb2_util_close(tree
, src_h
);
1220 smb2_util_close(tree
, dest_h
);
1222 /* no read permission on dest */
1223 ok
= test_setup_copy_chunk(torture
, tree
, tmp_ctx
,
1225 &src_h
, 4096, /* fill 4096 byte src file */
1226 SEC_RIGHTS_FILE_ALL
,
1227 &dest_h
, 0, /* 0 byte dest file */
1228 (SEC_RIGHTS_FILE_WRITE
1229 | SEC_RIGHTS_FILE_EXECUTE
),
1233 torture_fail(torture
, "setup copy chunk error");
1236 cc_copy
.chunks
[0].source_off
= 0;
1237 cc_copy
.chunks
[0].target_off
= 0;
1238 cc_copy
.chunks
[0].length
= 4096;
1240 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1242 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1243 torture_assert_ndr_success(torture
, ndr_ret
,
1244 "ndr_push_srv_copychunk_copy");
1247 * FSCTL_SRV_COPYCHUNK requires read permission on dest,
1248 * FSCTL_SRV_COPYCHUNK_WRITE (not supported by Samba) on the other hand
1251 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1252 torture_assert_ntstatus_equal(torture
, status
,
1253 NT_STATUS_ACCESS_DENIED
,
1254 "FSCTL_SRV_COPYCHUNK");
1256 smb2_util_close(tree
, src_h
);
1257 smb2_util_close(tree
, dest_h
);
1258 talloc_free(tmp_ctx
);
1263 static bool test_ioctl_copy_chunk_src_exceed(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 ok
= test_setup_copy_chunk(torture
, tree
, tmp_ctx
,
1278 &src_h
, 4096, /* fill 4096 byte src file */
1279 SEC_RIGHTS_FILE_ALL
,
1280 &dest_h
, 0, /* 0 byte dest file */
1281 SEC_RIGHTS_FILE_ALL
,
1285 torture_fail(torture
, "setup copy chunk error");
1288 /* Request copy where off + length exceeds size of src */
1289 cc_copy
.chunks
[0].source_off
= 1024;
1290 cc_copy
.chunks
[0].target_off
= 0;
1291 cc_copy
.chunks
[0].length
= 4096;
1293 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1295 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1296 torture_assert_ndr_success(torture
, ndr_ret
,
1297 "ndr_push_srv_copychunk_copy");
1299 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1300 torture_assert_ntstatus_equal(torture
, status
,
1301 NT_STATUS_INVALID_VIEW_SIZE
,
1302 "FSCTL_SRV_COPYCHUNK oversize");
1304 /* Request copy where length exceeds size of src */
1305 cc_copy
.chunks
[0].source_off
= 1024;
1306 cc_copy
.chunks
[0].target_off
= 0;
1307 cc_copy
.chunks
[0].length
= 3072;
1309 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1311 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1312 torture_assert_ndr_success(torture
, ndr_ret
,
1313 "ndr_push_srv_copychunk_copy");
1315 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1316 torture_assert_ntstatus_ok(torture
, status
,
1317 "FSCTL_SRV_COPYCHUNK just right");
1319 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
1321 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
1322 torture_assert_ndr_success(torture
, ndr_ret
,
1323 "ndr_pull_srv_copychunk_rsp");
1325 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
1326 1, /* chunks written */
1327 0, /* chunk bytes unsuccessfully written */
1328 3072); /* total bytes written */
1330 torture_fail(torture
, "bad copy chunk response data");
1333 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 0, 3072, 1024);
1335 torture_fail(torture
, "inconsistent file data");
1338 smb2_util_close(tree
, src_h
);
1339 smb2_util_close(tree
, dest_h
);
1340 talloc_free(tmp_ctx
);
1345 test_ioctl_copy_chunk_src_exceed_multi(struct torture_context
*torture
,
1346 struct smb2_tree
*tree
)
1348 struct smb2_handle src_h
;
1349 struct smb2_handle dest_h
;
1351 union smb_ioctl ioctl
;
1352 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
1353 struct srv_copychunk_copy cc_copy
;
1354 struct srv_copychunk_rsp cc_rsp
;
1355 enum ndr_err_code ndr_ret
;
1358 ok
= test_setup_copy_chunk(torture
, tree
, tmp_ctx
,
1360 &src_h
, 8192, /* fill 8192 byte src file */
1361 SEC_RIGHTS_FILE_ALL
,
1362 &dest_h
, 0, /* 0 byte dest file */
1363 SEC_RIGHTS_FILE_ALL
,
1367 torture_fail(torture
, "setup copy chunk error");
1370 /* Request copy where off + length exceeds size of src */
1371 cc_copy
.chunks
[0].source_off
= 0;
1372 cc_copy
.chunks
[0].target_off
= 0;
1373 cc_copy
.chunks
[0].length
= 4096;
1375 cc_copy
.chunks
[1].source_off
= 4096;
1376 cc_copy
.chunks
[1].target_off
= 4096;
1377 cc_copy
.chunks
[1].length
= 8192;
1379 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1381 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1382 torture_assert_ndr_success(torture
, ndr_ret
,
1383 "ndr_push_srv_copychunk_copy");
1385 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1386 torture_assert_ntstatus_equal(torture
, status
,
1387 NT_STATUS_INVALID_VIEW_SIZE
,
1388 "FSCTL_SRV_COPYCHUNK oversize");
1389 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
1391 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
1392 torture_assert_ndr_success(torture
, ndr_ret
, "unmarshalling response");
1394 /* first chunk should still be written */
1395 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
1396 1, /* chunks written */
1397 0, /* chunk bytes unsuccessfully written */
1398 4096); /* total bytes written */
1400 torture_fail(torture
, "bad copy chunk response data");
1402 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 0, 4096, 0);
1404 torture_fail(torture
, "inconsistent file data");
1407 smb2_util_close(tree
, src_h
);
1408 smb2_util_close(tree
, dest_h
);
1409 talloc_free(tmp_ctx
);
1413 static bool test_ioctl_copy_chunk_sparse_dest(struct torture_context
*torture
,
1414 struct smb2_tree
*tree
)
1416 struct smb2_handle src_h
;
1417 struct smb2_handle dest_h
;
1419 union smb_ioctl ioctl
;
1421 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
1422 struct srv_copychunk_copy cc_copy
;
1423 struct srv_copychunk_rsp cc_rsp
;
1424 enum ndr_err_code ndr_ret
;
1428 ok
= test_setup_copy_chunk(torture
, tree
, tmp_ctx
,
1430 &src_h
, 4096, /* fill 4096 byte src file */
1431 SEC_RIGHTS_FILE_ALL
,
1432 &dest_h
, 0, /* 0 byte dest file */
1433 SEC_RIGHTS_FILE_ALL
,
1437 torture_fail(torture
, "setup copy chunk error");
1440 /* copy all src file data (via a single chunk desc) */
1441 cc_copy
.chunks
[0].source_off
= 0;
1442 cc_copy
.chunks
[0].target_off
= 4096;
1443 cc_copy
.chunks
[0].length
= 4096;
1445 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1447 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1448 torture_assert_ndr_success(torture
, ndr_ret
,
1449 "ndr_push_srv_copychunk_copy");
1451 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1452 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_COPYCHUNK");
1454 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
1456 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
1457 torture_assert_ndr_success(torture
, ndr_ret
,
1458 "ndr_pull_srv_copychunk_rsp");
1460 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
1461 1, /* chunks written */
1462 0, /* chunk bytes unsuccessfully written */
1463 4096); /* total bytes written */
1465 torture_fail(torture
, "bad copy chunk response data");
1468 /* check for zeros in first 4k */
1470 r
.in
.file
.handle
= dest_h
;
1473 status
= smb2_read(tree
, tmp_ctx
, &r
);
1474 torture_assert_ntstatus_ok(torture
, status
, "read");
1476 torture_assert_u64_equal(torture
, r
.out
.data
.length
, 4096,
1477 "read data len mismatch");
1479 for (i
= 0; i
< 4096; i
++) {
1480 torture_assert(torture
, (r
.out
.data
.data
[i
] == 0),
1481 "sparse did not pass class");
1484 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 4096, 4096, 0);
1486 torture_fail(torture
, "inconsistent file data");
1489 smb2_util_close(tree
, src_h
);
1490 smb2_util_close(tree
, dest_h
);
1491 talloc_free(tmp_ctx
);
1496 * set the ioctl MaxOutputResponse size to less than
1497 * sizeof(struct srv_copychunk_rsp)
1499 static bool test_ioctl_copy_chunk_max_output_sz(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 ok
= test_setup_copy_chunk(torture
, tree
, tmp_ctx
,
1513 &src_h
, 4096, /* fill 4096 byte src file */
1514 SEC_RIGHTS_FILE_ALL
,
1515 &dest_h
, 0, /* 0 byte dest file */
1516 SEC_RIGHTS_FILE_ALL
,
1520 torture_fail(torture
, "setup copy chunk error");
1523 cc_copy
.chunks
[0].source_off
= 0;
1524 cc_copy
.chunks
[0].target_off
= 0;
1525 cc_copy
.chunks
[0].length
= 4096;
1526 /* req is valid, but use undersize max_response_size */
1527 ioctl
.smb2
.in
.max_response_size
= sizeof(struct srv_copychunk_rsp
) - 1;
1529 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
1531 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
1532 torture_assert_ndr_success(torture
, ndr_ret
,
1533 "ndr_push_srv_copychunk_copy");
1535 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
1536 torture_assert_ntstatus_equal(torture
, status
,
1537 NT_STATUS_INVALID_PARAMETER
,
1538 "FSCTL_SRV_COPYCHUNK");
1540 smb2_util_close(tree
, src_h
);
1541 smb2_util_close(tree
, dest_h
);
1542 talloc_free(tmp_ctx
);
1547 basic testing of SMB2 ioctls
1549 struct torture_suite
*torture_smb2_ioctl_init(void)
1551 struct torture_suite
*suite
= torture_suite_create(talloc_autofree_context(), "ioctl");
1553 torture_suite_add_1smb2_test(suite
, "shadow_copy",
1554 test_ioctl_get_shadow_copy
);
1555 torture_suite_add_1smb2_test(suite
, "req_resume_key",
1556 test_ioctl_req_resume_key
);
1557 torture_suite_add_1smb2_test(suite
, "copy_chunk_simple",
1558 test_ioctl_copy_chunk_simple
);
1559 torture_suite_add_1smb2_test(suite
, "copy_chunk_multi",
1560 test_ioctl_copy_chunk_multi
);
1561 torture_suite_add_1smb2_test(suite
, "copy_chunk_tiny",
1562 test_ioctl_copy_chunk_tiny
);
1563 torture_suite_add_1smb2_test(suite
, "copy_chunk_overwrite",
1564 test_ioctl_copy_chunk_over
);
1565 torture_suite_add_1smb2_test(suite
, "copy_chunk_append",
1566 test_ioctl_copy_chunk_append
);
1567 torture_suite_add_1smb2_test(suite
, "copy_chunk_limits",
1568 test_ioctl_copy_chunk_limits
);
1569 torture_suite_add_1smb2_test(suite
, "copy_chunk_src_lock",
1570 test_ioctl_copy_chunk_src_lck
);
1571 torture_suite_add_1smb2_test(suite
, "copy_chunk_dest_lock",
1572 test_ioctl_copy_chunk_dest_lck
);
1573 torture_suite_add_1smb2_test(suite
, "copy_chunk_bad_key",
1574 test_ioctl_copy_chunk_bad_key
);
1575 torture_suite_add_1smb2_test(suite
, "copy_chunk_src_is_dest",
1576 test_ioctl_copy_chunk_src_is_dest
);
1577 torture_suite_add_1smb2_test(suite
, "copy_chunk_src_is_dest_overlap",
1578 test_ioctl_copy_chunk_src_is_dest_overlap
);
1579 torture_suite_add_1smb2_test(suite
, "copy_chunk_bad_access",
1580 test_ioctl_copy_chunk_bad_access
);
1581 torture_suite_add_1smb2_test(suite
, "copy_chunk_src_exceed",
1582 test_ioctl_copy_chunk_src_exceed
);
1583 torture_suite_add_1smb2_test(suite
, "copy_chunk_src_exceed_multi",
1584 test_ioctl_copy_chunk_src_exceed_multi
);
1585 torture_suite_add_1smb2_test(suite
, "copy_chunk_sparse_dest",
1586 test_ioctl_copy_chunk_sparse_dest
);
1587 torture_suite_add_1smb2_test(suite
, "copy_chunk_max_output_sz",
1588 test_ioctl_copy_chunk_max_output_sz
);
1590 suite
->description
= talloc_strdup(suite
, "SMB2-IOCTL tests");