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 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_ENUM_SNAPS");
68 basic testing of the SMB2 server side copy ioctls
70 static bool test_ioctl_req_resume_key(struct torture_context
*torture
,
71 struct smb2_tree
*tree
)
76 union smb_ioctl ioctl
;
77 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
78 struct req_resume_key_rsp res_key
;
79 enum ndr_err_code ndr_ret
;
81 smb2_util_unlink(tree
, FNAME
);
83 status
= torture_smb2_testfile(tree
, FNAME
, &h
);
84 torture_assert_ntstatus_ok(torture
, status
, "create write");
87 status
= smb2_util_write(tree
, h
, buf
, 0, ARRAY_SIZE(buf
));
88 torture_assert_ntstatus_ok(torture
, status
, "write");
91 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
92 ioctl
.smb2
.in
.file
.handle
= h
;
93 ioctl
.smb2
.in
.function
= FSCTL_SRV_REQUEST_RESUME_KEY
;
94 ioctl
.smb2
.in
.max_response_size
= 32;
95 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
97 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
98 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_REQUEST_RESUME_KEY");
100 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
, &res_key
,
101 (ndr_pull_flags_fn_t
)ndr_pull_req_resume_key_rsp
);
102 torture_assert_ndr_success(torture
, ndr_ret
,
103 "ndr_pull_req_resume_key_rsp");
105 ndr_print_debug((ndr_print_fn_t
)ndr_print_req_resume_key_rsp
, "yo", &res_key
);
107 talloc_free(tmp_ctx
);
111 static uint64_t patt_hash(uint64_t off
)
116 static bool check_pattern(struct torture_context
*torture
,
117 struct smb2_tree
*tree
, TALLOC_CTX
*mem_ctx
,
118 struct smb2_handle h
, uint64_t off
, uint64_t len
,
126 r
.in
.file
.handle
= h
;
129 status
= smb2_read(tree
, mem_ctx
, &r
);
130 torture_assert_ntstatus_ok(torture
, status
, "read");
132 torture_assert_u64_equal(torture
, r
.out
.data
.length
, len
,
133 "read data len mismatch");
135 for (i
= 0; i
<= len
- 8; i
+= 8, patt_off
+= 8) {
136 uint64_t data
= BVAL(r
.out
.data
.data
, i
);
137 torture_assert_u64_equal(torture
, data
, patt_hash(patt_off
),
138 talloc_asprintf(torture
, "read data "
139 "pattern bad at %llu\n",
140 (unsigned long long)i
));
143 talloc_free(r
.out
.data
.data
);
147 static bool test_setup_copy_chunk(struct torture_context
*torture
,
148 struct smb2_tree
*tree
, TALLOC_CTX
*mem_ctx
,
150 struct smb2_handle
*src_h
,
152 struct smb2_handle
*dest_h
,
154 struct srv_copychunk_copy
*cc_copy
,
155 union smb_ioctl
*ioctl
)
157 struct req_resume_key_rsp res_key
;
159 enum ndr_err_code ndr_ret
;
161 uint8_t *buf
= talloc_zero_size(mem_ctx
, MAX(src_size
, dest_size
));
163 printf("no mem for file data buffer\n");
167 smb2_util_unlink(tree
, FNAME
);
168 smb2_util_unlink(tree
, FNAME2
);
170 status
= torture_smb2_testfile(tree
, FNAME
, src_h
);
171 torture_assert_ntstatus_ok(torture
, status
, "create write");
174 for (i
= 0; i
<= src_size
- 8; i
+= 8) {
175 SBVAL(buf
, i
, patt_hash(i
));
177 status
= smb2_util_write(tree
, *src_h
, buf
, 0, src_size
);
178 torture_assert_ntstatus_ok(torture
, status
, "src write");
181 status
= torture_smb2_testfile(tree
, FNAME2
, dest_h
);
182 torture_assert_ntstatus_ok(torture
, status
, "create write");
185 for (i
= 0; i
<= src_size
- 8; i
+= 8) {
186 SBVAL(buf
, i
, patt_hash(i
));
188 status
= smb2_util_write(tree
, *dest_h
, buf
, 0, dest_size
);
189 torture_assert_ntstatus_ok(torture
, status
, "dest write");
192 ZERO_STRUCTPN(ioctl
);
193 ioctl
->smb2
.level
= RAW_IOCTL_SMB2
;
194 ioctl
->smb2
.in
.file
.handle
= *src_h
;
195 ioctl
->smb2
.in
.function
= FSCTL_SRV_REQUEST_RESUME_KEY
;
196 /* Allow for Key + ContextLength + Context */
197 ioctl
->smb2
.in
.max_response_size
= 32;
198 ioctl
->smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
200 status
= smb2_ioctl(tree
, mem_ctx
, &ioctl
->smb2
);
201 torture_assert_ntstatus_ok(torture
, status
,
202 "FSCTL_SRV_REQUEST_RESUME_KEY");
205 ndr_ret
= ndr_pull_struct_blob(&ioctl
->smb2
.out
.out
, mem_ctx
, &res_key
,
206 (ndr_pull_flags_fn_t
)ndr_pull_req_resume_key_rsp
);
208 torture_assert_ndr_success(torture
, ndr_ret
,
209 "ndr_pull_req_resume_key_rsp");
211 ZERO_STRUCTPN(ioctl
);
212 ioctl
->smb2
.level
= RAW_IOCTL_SMB2
;
213 ioctl
->smb2
.in
.file
.handle
= *dest_h
;
214 ioctl
->smb2
.in
.function
= FSCTL_SRV_COPYCHUNK
;
215 ioctl
->smb2
.in
.max_response_size
= sizeof(struct srv_copychunk_rsp
);
216 ioctl
->smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
218 ZERO_STRUCTPN(cc_copy
);
219 memcpy(cc_copy
->source_key
, res_key
.resume_key
, ARRAY_SIZE(cc_copy
->source_key
));
220 cc_copy
->chunk_count
= nchunks
;
221 cc_copy
->chunks
= talloc_zero_array(mem_ctx
, struct srv_copychunk
, nchunks
);
222 if (cc_copy
->chunks
== NULL
) {
223 printf("not enough memory to allocate %u chunks\n", nchunks
);
231 static bool check_copy_chunk_rsp(struct torture_context
*torture
,
232 struct srv_copychunk_rsp
*cc_rsp
,
233 uint32_t ex_chunks_written
,
234 uint32_t ex_chunk_bytes_written
,
235 uint32_t ex_total_bytes_written
)
237 torture_assert_int_equal(torture
, cc_rsp
->chunks_written
,
238 ex_chunks_written
, "num chunks");
239 torture_assert_int_equal(torture
, cc_rsp
->chunk_bytes_written
,
240 ex_chunk_bytes_written
, "chunk bytes written");
241 torture_assert_int_equal(torture
, cc_rsp
->total_bytes_written
,
242 ex_total_bytes_written
, "chunk total bytes");
246 static bool test_ioctl_copy_chunk_simple(struct torture_context
*torture
,
247 struct smb2_tree
*tree
)
249 struct smb2_handle src_h
;
250 struct smb2_handle dest_h
;
252 union smb_ioctl ioctl
;
253 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
254 struct srv_copychunk_copy cc_copy
;
255 struct srv_copychunk_rsp cc_rsp
;
256 enum ndr_err_code ndr_ret
;
259 ok
= test_setup_copy_chunk(torture
, tree
, tmp_ctx
,
261 &src_h
, 4096, /* fill 4096 byte src file */
262 &dest_h
, 0, /* 0 byte dest file */
269 /* copy all src file data (via a single chunk desc) */
270 cc_copy
.chunks
[0].source_off
= 0;
271 cc_copy
.chunks
[0].target_off
= 0;
272 cc_copy
.chunks
[0].length
= 4096;
274 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
276 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
277 torture_assert_ndr_success(torture
, ndr_ret
,
278 "ndr_push_srv_copychunk_copy");
280 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
281 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_COPYCHUNK");
283 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
285 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
286 torture_assert_ndr_success(torture
, ndr_ret
,
287 "ndr_pull_srv_copychunk_rsp");
289 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
290 1, /* chunks written */
291 0, /* chunk bytes unsuccessfully written */
292 4096); /* total bytes written */
297 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 0, 4096, 0);
302 smb2_util_close(tree
, src_h
);
303 smb2_util_close(tree
, dest_h
);
304 talloc_free(tmp_ctx
);
308 static bool test_ioctl_copy_chunk_multi(struct torture_context
*torture
,
309 struct smb2_tree
*tree
)
311 struct smb2_handle src_h
;
312 struct smb2_handle dest_h
;
314 union smb_ioctl ioctl
;
315 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
316 struct srv_copychunk_copy cc_copy
;
317 struct srv_copychunk_rsp cc_rsp
;
318 enum ndr_err_code ndr_ret
;
321 ok
= test_setup_copy_chunk(torture
, tree
, tmp_ctx
,
323 &src_h
, 8192, /* src file */
324 &dest_h
, 0, /* dest file */
331 /* copy all src file data via two chunks */
332 cc_copy
.chunks
[0].source_off
= 0;
333 cc_copy
.chunks
[0].target_off
= 0;
334 cc_copy
.chunks
[0].length
= 4096;
336 cc_copy
.chunks
[1].source_off
= 4096;
337 cc_copy
.chunks
[1].target_off
= 4096;
338 cc_copy
.chunks
[1].length
= 4096;
340 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
342 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
343 torture_assert_ndr_success(torture
, ndr_ret
,
344 "ndr_push_srv_copychunk_copy");
346 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
347 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_COPYCHUNK");
349 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
351 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
352 torture_assert_ndr_success(torture
, ndr_ret
,
353 "ndr_pull_srv_copychunk_rsp");
355 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
356 2, /* chunks written */
357 0, /* chunk bytes unsuccessfully written */
358 8192); /* total bytes written */
363 smb2_util_close(tree
, src_h
);
364 smb2_util_close(tree
, dest_h
);
365 talloc_free(tmp_ctx
);
369 static bool test_ioctl_copy_chunk_tiny(struct torture_context
*torture
,
370 struct smb2_tree
*tree
)
372 struct smb2_handle src_h
;
373 struct smb2_handle dest_h
;
375 union smb_ioctl ioctl
;
376 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
377 struct srv_copychunk_copy cc_copy
;
378 struct srv_copychunk_rsp cc_rsp
;
379 enum ndr_err_code ndr_ret
;
382 ok
= test_setup_copy_chunk(torture
, tree
, tmp_ctx
,
384 &src_h
, 100, /* src file */
385 &dest_h
, 0, /* dest file */
392 /* copy all src file data via two chunks, sub block size chunks */
393 cc_copy
.chunks
[0].source_off
= 0;
394 cc_copy
.chunks
[0].target_off
= 0;
395 cc_copy
.chunks
[0].length
= 50;
397 cc_copy
.chunks
[1].source_off
= 50;
398 cc_copy
.chunks
[1].target_off
= 50;
399 cc_copy
.chunks
[1].length
= 50;
401 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
403 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
404 torture_assert_ndr_success(torture
, ndr_ret
,
405 "ndr_push_srv_copychunk_copy");
407 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
408 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_COPYCHUNK");
410 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
412 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
413 torture_assert_ndr_success(torture
, ndr_ret
,
414 "ndr_pull_srv_copychunk_rsp");
416 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
417 2, /* chunks written */
418 0, /* chunk bytes unsuccessfully written */
419 100); /* total bytes written */
424 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 0, 100, 0);
429 smb2_util_close(tree
, src_h
);
430 smb2_util_close(tree
, dest_h
);
431 talloc_free(tmp_ctx
);
435 static bool test_ioctl_copy_chunk_over(struct torture_context
*torture
,
436 struct smb2_tree
*tree
)
438 struct smb2_handle src_h
;
439 struct smb2_handle dest_h
;
441 union smb_ioctl ioctl
;
442 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
443 struct srv_copychunk_copy cc_copy
;
444 struct srv_copychunk_rsp cc_rsp
;
445 enum ndr_err_code ndr_ret
;
448 ok
= test_setup_copy_chunk(torture
, tree
, tmp_ctx
,
450 &src_h
, 8192, /* src file */
451 &dest_h
, 4096, /* dest file */
458 /* first chunk overwrites existing dest data */
459 cc_copy
.chunks
[0].source_off
= 0;
460 cc_copy
.chunks
[0].target_off
= 0;
461 cc_copy
.chunks
[0].length
= 4096;
463 /* second chunk overwrites the first */
464 cc_copy
.chunks
[1].source_off
= 4096;
465 cc_copy
.chunks
[1].target_off
= 0;
466 cc_copy
.chunks
[1].length
= 4096;
468 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
470 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
471 torture_assert_ndr_success(torture
, ndr_ret
,
472 "ndr_push_srv_copychunk_copy");
474 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
475 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_COPYCHUNK");
477 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
479 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
480 torture_assert_ndr_success(torture
, ndr_ret
,
481 "ndr_pull_srv_copychunk_rsp");
483 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
484 2, /* chunks written */
485 0, /* chunk bytes unsuccessfully written */
486 8192); /* total bytes written */
491 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 0, 4096, 4096);
496 smb2_util_close(tree
, src_h
);
497 smb2_util_close(tree
, dest_h
);
498 talloc_free(tmp_ctx
);
502 static bool test_ioctl_copy_chunk_append(struct torture_context
*torture
,
503 struct smb2_tree
*tree
)
505 struct smb2_handle src_h
;
506 struct smb2_handle dest_h
;
508 union smb_ioctl ioctl
;
509 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
510 struct srv_copychunk_copy cc_copy
;
511 struct srv_copychunk_rsp cc_rsp
;
512 enum ndr_err_code ndr_ret
;
515 ok
= test_setup_copy_chunk(torture
, tree
, tmp_ctx
,
517 &src_h
, 4096, /* src file */
518 &dest_h
, 0, /* dest file */
525 cc_copy
.chunks
[0].source_off
= 0;
526 cc_copy
.chunks
[0].target_off
= 0;
527 cc_copy
.chunks
[0].length
= 4096;
529 /* second chunk appends the same data to the first */
530 cc_copy
.chunks
[1].source_off
= 0;
531 cc_copy
.chunks
[1].target_off
= 4096;
532 cc_copy
.chunks
[1].length
= 4096;
534 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
536 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
537 torture_assert_ndr_success(torture
, ndr_ret
,
538 "ndr_push_srv_copychunk_copy");
540 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
541 torture_assert_ntstatus_ok(torture
, status
, "FSCTL_SRV_COPYCHUNK");
543 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
545 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
546 torture_assert_ndr_success(torture
, ndr_ret
,
547 "ndr_pull_srv_copychunk_rsp");
549 ok
= check_copy_chunk_rsp(torture
, &cc_rsp
,
550 2, /* chunks written */
551 0, /* chunk bytes unsuccessfully written */
552 8192); /* total bytes written */
557 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 0, 4096, 0);
562 ok
= check_pattern(torture
, tree
, tmp_ctx
, dest_h
, 4096, 4096, 0);
567 smb2_util_close(tree
, src_h
);
568 smb2_util_close(tree
, dest_h
);
569 talloc_free(tmp_ctx
);
574 basic testing of SMB2 ioctls
576 struct torture_suite
*torture_smb2_ioctl_init(void)
578 struct torture_suite
*suite
= torture_suite_create(talloc_autofree_context(), "ioctl");
580 torture_suite_add_1smb2_test(suite
, "shadow_copy",
581 test_ioctl_get_shadow_copy
);
582 torture_suite_add_1smb2_test(suite
, "req_resume_key",
583 test_ioctl_req_resume_key
);
584 torture_suite_add_1smb2_test(suite
, "copy_chunk_simple",
585 test_ioctl_copy_chunk_simple
);
586 torture_suite_add_1smb2_test(suite
, "copy_chunk_multi",
587 test_ioctl_copy_chunk_multi
);
588 torture_suite_add_1smb2_test(suite
, "copy_chunk_tiny",
589 test_ioctl_copy_chunk_tiny
);
590 torture_suite_add_1smb2_test(suite
, "copy_chunk_overwrite",
591 test_ioctl_copy_chunk_over
);
592 torture_suite_add_1smb2_test(suite
, "copy_chunk_append",
593 test_ioctl_copy_chunk_append
);
595 suite
->description
= talloc_strdup(suite
, "SMB2-IOCTL tests");