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 if (!NT_STATUS_IS_OK(status
)) {
49 printf("create write\n");
54 status
= smb2_util_write(tree
, h
, buf
, 0, ARRAY_SIZE(buf
));
55 if (!NT_STATUS_IS_OK(status
)) {
56 printf("failed write\n");
61 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
62 ioctl
.smb2
.in
.file
.handle
= h
;
63 ioctl
.smb2
.in
.function
= FSCTL_SRV_ENUM_SNAPS
;
64 ioctl
.smb2
.in
.max_response_size
= 16;
65 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
67 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
68 if (!NT_STATUS_IS_OK(status
)) {
69 printf("FSCTL_SRV_ENUM_SNAPS failed\n");
77 basic testing of the SMB2 server side copy ioctls
79 static bool test_ioctl_req_resume_key(struct torture_context
*torture
,
80 struct smb2_tree
*tree
)
85 union smb_ioctl ioctl
;
86 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
87 struct req_resume_key_rsp res_key
;
88 enum ndr_err_code ndr_ret
;
90 smb2_util_unlink(tree
, FNAME
);
92 status
= torture_smb2_testfile(tree
, FNAME
, &h
);
93 if (!NT_STATUS_IS_OK(status
)) {
94 printf("create write\n");
99 status
= smb2_util_write(tree
, h
, buf
, 0, ARRAY_SIZE(buf
));
100 if (!NT_STATUS_IS_OK(status
)) {
101 printf("failed write\n");
106 ioctl
.smb2
.level
= RAW_IOCTL_SMB2
;
107 ioctl
.smb2
.in
.file
.handle
= h
;
108 ioctl
.smb2
.in
.function
= FSCTL_SRV_REQUEST_RESUME_KEY
;
109 ioctl
.smb2
.in
.max_response_size
= 32;
110 ioctl
.smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
112 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
113 if (!NT_STATUS_IS_OK(status
)) {
114 printf("FSCTL_SRV_REQUEST_RESUME_KEY failed\n");
118 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
, &res_key
,
119 (ndr_pull_flags_fn_t
)ndr_pull_req_resume_key_rsp
);
120 if (ndr_ret
!= NDR_ERR_SUCCESS
) {
124 ndr_print_debug((ndr_print_fn_t
)ndr_print_req_resume_key_rsp
, "yo", &res_key
);
126 talloc_free(tmp_ctx
);
130 static bool test_setup_copy_chunk(struct smb2_tree
*tree
, TALLOC_CTX
*mem_ctx
,
132 struct smb2_handle
*src_h
,
134 struct smb2_handle
*dest_h
,
136 struct srv_copychunk_copy
*cc_copy
,
137 union smb_ioctl
*ioctl
)
139 struct req_resume_key_rsp res_key
;
141 enum ndr_err_code ndr_ret
;
142 uint8_t *buf
= talloc_zero_size(mem_ctx
, MAX(src_size
, dest_size
));
144 printf("no mem for file data buffer\n");
148 smb2_util_unlink(tree
, FNAME
);
149 smb2_util_unlink(tree
, FNAME2
);
151 status
= torture_smb2_testfile(tree
, FNAME
, src_h
);
152 if (!NT_STATUS_IS_OK(status
)) {
153 printf("create write\n");
158 status
= smb2_util_write(tree
, *src_h
, buf
, 0, src_size
);
159 if (!NT_STATUS_IS_OK(status
)) {
160 printf("failed src write\n");
165 status
= torture_smb2_testfile(tree
, FNAME2
, dest_h
);
166 if (!NT_STATUS_IS_OK(status
)) {
167 printf("create write\n");
172 status
= smb2_util_write(tree
, *dest_h
, buf
, 0, dest_size
);
173 if (!NT_STATUS_IS_OK(status
)) {
174 printf("failed dest write\n");
179 ZERO_STRUCTPN(ioctl
);
180 ioctl
->smb2
.level
= RAW_IOCTL_SMB2
;
181 ioctl
->smb2
.in
.file
.handle
= *src_h
;
182 ioctl
->smb2
.in
.function
= FSCTL_SRV_REQUEST_RESUME_KEY
;
183 /* Allow for Key + ContextLength + Context */
184 ioctl
->smb2
.in
.max_response_size
= 32;
185 ioctl
->smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
187 status
= smb2_ioctl(tree
, mem_ctx
, &ioctl
->smb2
);
188 if (!NT_STATUS_IS_OK(status
)) {
189 printf("FSCTL_SRV_REQUEST_RESUME_KEY failed\n");
193 ndr_ret
= ndr_pull_struct_blob(&ioctl
->smb2
.out
.out
, mem_ctx
, &res_key
,
194 (ndr_pull_flags_fn_t
)ndr_pull_req_resume_key_rsp
);
195 if (ndr_ret
!= NDR_ERR_SUCCESS
) {
199 ZERO_STRUCTPN(ioctl
);
200 ioctl
->smb2
.level
= RAW_IOCTL_SMB2
;
201 ioctl
->smb2
.in
.file
.handle
= *dest_h
;
202 ioctl
->smb2
.in
.function
= FSCTL_SRV_COPYCHUNK
;
203 ioctl
->smb2
.in
.max_response_size
= sizeof(struct srv_copychunk_rsp
);
204 ioctl
->smb2
.in
.flags
= SMB2_IOCTL_FLAG_IS_FSCTL
;
206 ZERO_STRUCTPN(cc_copy
);
207 memcpy(cc_copy
->source_key
, res_key
.resume_key
, ARRAY_SIZE(cc_copy
->source_key
));
208 cc_copy
->chunk_count
= nchunks
;
209 cc_copy
->chunks
= talloc_zero_array(mem_ctx
, struct srv_copychunk
, nchunks
);
210 if (cc_copy
->chunks
== NULL
) {
211 printf("not enough memory to allocate %u chunks\n", nchunks
);
219 static bool check_copy_chunk_rsp(struct srv_copychunk_rsp
*cc_rsp
,
220 uint32_t ex_chunks_written
,
221 uint32_t ex_chunk_bytes_written
,
222 uint32_t ex_total_bytes_written
)
224 if (cc_rsp
->chunks_written
!= ex_chunks_written
) {
225 printf("expected %u chunks, got %u\n",
226 ex_chunks_written
, cc_rsp
->chunks_written
);
229 if (cc_rsp
->chunk_bytes_written
!= ex_chunk_bytes_written
) {
230 printf("expected %u chunk bytes remaining, got %u\n",
231 ex_chunk_bytes_written
, cc_rsp
->chunk_bytes_written
);
234 if (cc_rsp
->total_bytes_written
!= ex_total_bytes_written
) {
235 printf("expected %u total bytes, got %u\n",
236 ex_total_bytes_written
, cc_rsp
->total_bytes_written
);
242 static bool test_ioctl_copy_chunk_simple(struct torture_context
*torture
,
243 struct smb2_tree
*tree
)
245 struct smb2_handle src_h
;
246 struct smb2_handle dest_h
;
248 union smb_ioctl ioctl
;
249 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
250 struct srv_copychunk_copy cc_copy
;
251 struct srv_copychunk_rsp cc_rsp
;
252 enum ndr_err_code ndr_ret
;
255 ok
= test_setup_copy_chunk(tree
, tmp_ctx
,
257 &src_h
, 4096, /* fill 4096 byte src file */
258 &dest_h
, 0, /* 0 byte dest file */
265 /* copy all src file data (via a single chunk desc) */
266 cc_copy
.chunks
[0].source_off
= 0;
267 cc_copy
.chunks
[0].target_off
= 0;
268 cc_copy
.chunks
[0].length
= 4096;
270 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
272 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
273 if (ndr_ret
!= NDR_ERR_SUCCESS
) {
277 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
278 if (!NT_STATUS_IS_OK(status
)) {
279 printf("FSCTL_SRV_COPYCHUNK failed\n");
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 if (ndr_ret
!= NDR_ERR_SUCCESS
) {
290 ok
= check_copy_chunk_rsp(&cc_rsp
,
291 1, /* chunks written */
292 0, /* chunk bytes unsuccessfully written */
293 4096); /* total bytes written */
298 smb2_util_close(tree
, src_h
);
299 smb2_util_close(tree
, dest_h
);
300 talloc_free(tmp_ctx
);
304 static bool test_ioctl_copy_chunk_multi(struct torture_context
*torture
,
305 struct smb2_tree
*tree
)
307 struct smb2_handle src_h
;
308 struct smb2_handle dest_h
;
310 union smb_ioctl ioctl
;
311 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
312 struct srv_copychunk_copy cc_copy
;
313 struct srv_copychunk_rsp cc_rsp
;
314 enum ndr_err_code ndr_ret
;
317 ok
= test_setup_copy_chunk(tree
, tmp_ctx
,
319 &src_h
, 8192, /* src file */
320 &dest_h
, 0, /* dest file */
327 /* copy all src file data via two chunks */
328 cc_copy
.chunks
[0].source_off
= 0;
329 cc_copy
.chunks
[0].target_off
= 0;
330 cc_copy
.chunks
[0].length
= 4096;
332 cc_copy
.chunks
[1].source_off
= 4096;
333 cc_copy
.chunks
[1].target_off
= 4096;
334 cc_copy
.chunks
[1].length
= 4096;
336 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
338 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
339 if (ndr_ret
!= NDR_ERR_SUCCESS
) {
343 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
344 if (!NT_STATUS_IS_OK(status
)) {
345 printf("FSCTL_SRV_COPYCHUNK failed\n");
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 if (ndr_ret
!= NDR_ERR_SUCCESS
) {
356 ok
= check_copy_chunk_rsp(&cc_rsp
,
357 2, /* chunks written */
358 0, /* chunk bytes unsuccessfully written */
359 8192); /* total bytes written */
364 smb2_util_close(tree
, src_h
);
365 smb2_util_close(tree
, dest_h
);
366 talloc_free(tmp_ctx
);
370 static bool test_ioctl_copy_chunk_tiny(struct torture_context
*torture
,
371 struct smb2_tree
*tree
)
373 struct smb2_handle src_h
;
374 struct smb2_handle dest_h
;
376 union smb_ioctl ioctl
;
377 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
378 struct srv_copychunk_copy cc_copy
;
379 struct srv_copychunk_rsp cc_rsp
;
380 enum ndr_err_code ndr_ret
;
383 ok
= test_setup_copy_chunk(tree
, tmp_ctx
,
385 &src_h
, 100, /* src file */
386 &dest_h
, 0, /* dest file */
393 /* copy all src file data via two chunks, sub block size chunks */
394 cc_copy
.chunks
[0].source_off
= 0;
395 cc_copy
.chunks
[0].target_off
= 0;
396 cc_copy
.chunks
[0].length
= 50;
398 cc_copy
.chunks
[1].source_off
= 50;
399 cc_copy
.chunks
[1].target_off
= 50;
400 cc_copy
.chunks
[1].length
= 50;
402 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
404 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
405 if (ndr_ret
!= NDR_ERR_SUCCESS
) {
409 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
410 if (!NT_STATUS_IS_OK(status
)) {
411 printf("FSCTL_SRV_COPYCHUNK failed\n");
415 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
417 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
418 if (ndr_ret
!= NDR_ERR_SUCCESS
) {
422 ok
= check_copy_chunk_rsp(&cc_rsp
,
423 2, /* chunks written */
424 0, /* chunk bytes unsuccessfully written */
425 100); /* total bytes written */
430 smb2_util_close(tree
, src_h
);
431 smb2_util_close(tree
, dest_h
);
432 talloc_free(tmp_ctx
);
436 static bool test_ioctl_copy_chunk_over(struct torture_context
*torture
,
437 struct smb2_tree
*tree
)
439 struct smb2_handle src_h
;
440 struct smb2_handle dest_h
;
442 union smb_ioctl ioctl
;
443 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
444 struct srv_copychunk_copy cc_copy
;
445 struct srv_copychunk_rsp cc_rsp
;
446 enum ndr_err_code ndr_ret
;
449 ok
= test_setup_copy_chunk(tree
, tmp_ctx
,
451 &src_h
, 8192, /* src file */
452 &dest_h
, 4096, /* dest file */
459 /* first chunk overwrites existing dest data */
460 cc_copy
.chunks
[0].source_off
= 0;
461 cc_copy
.chunks
[0].target_off
= 0;
462 cc_copy
.chunks
[0].length
= 4096;
464 /* second chunk overwrites the first */
465 cc_copy
.chunks
[1].source_off
= 4096;
466 cc_copy
.chunks
[1].target_off
= 0;
467 cc_copy
.chunks
[1].length
= 4096;
469 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
471 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
472 if (ndr_ret
!= NDR_ERR_SUCCESS
) {
476 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
477 if (!NT_STATUS_IS_OK(status
)) {
478 printf("FSCTL_SRV_COPYCHUNK failed\n");
482 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
484 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
485 if (ndr_ret
!= NDR_ERR_SUCCESS
) {
489 ok
= check_copy_chunk_rsp(&cc_rsp
,
490 2, /* chunks written */
491 0, /* chunk bytes unsuccessfully written */
492 8192); /* total bytes written */
497 smb2_util_close(tree
, src_h
);
498 smb2_util_close(tree
, dest_h
);
499 talloc_free(tmp_ctx
);
503 static bool test_ioctl_copy_chunk_append(struct torture_context
*torture
,
504 struct smb2_tree
*tree
)
506 struct smb2_handle src_h
;
507 struct smb2_handle dest_h
;
509 union smb_ioctl ioctl
;
510 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
511 struct srv_copychunk_copy cc_copy
;
512 struct srv_copychunk_rsp cc_rsp
;
513 enum ndr_err_code ndr_ret
;
516 ok
= test_setup_copy_chunk(tree
, tmp_ctx
,
518 &src_h
, 4096, /* src file */
519 &dest_h
, 0, /* dest file */
526 cc_copy
.chunks
[0].source_off
= 0;
527 cc_copy
.chunks
[0].target_off
= 0;
528 cc_copy
.chunks
[0].length
= 4096;
530 /* second chunk appends the same data to the first */
531 cc_copy
.chunks
[1].source_off
= 0;
532 cc_copy
.chunks
[1].target_off
= 4096;
533 cc_copy
.chunks
[1].length
= 4096;
535 ndr_ret
= ndr_push_struct_blob(&ioctl
.smb2
.in
.out
, tmp_ctx
,
537 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
538 if (ndr_ret
!= NDR_ERR_SUCCESS
) {
542 status
= smb2_ioctl(tree
, tmp_ctx
, &ioctl
.smb2
);
543 if (!NT_STATUS_IS_OK(status
)) {
544 printf("FSCTL_SRV_COPYCHUNK failed\n");
548 ndr_ret
= ndr_pull_struct_blob(&ioctl
.smb2
.out
.out
, tmp_ctx
,
550 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
551 if (ndr_ret
!= NDR_ERR_SUCCESS
) {
555 ok
= check_copy_chunk_rsp(&cc_rsp
,
556 2, /* chunks written */
557 0, /* chunk bytes unsuccessfully written */
558 8192); /* total bytes written */
563 smb2_util_close(tree
, src_h
);
564 smb2_util_close(tree
, dest_h
);
565 talloc_free(tmp_ctx
);
570 basic testing of SMB2 ioctls
572 struct torture_suite
*torture_smb2_ioctl_init(void)
574 struct torture_suite
*suite
= torture_suite_create(talloc_autofree_context(), "ioctl");
576 torture_suite_add_1smb2_test(suite
, "shadow_copy",
577 test_ioctl_get_shadow_copy
);
578 torture_suite_add_1smb2_test(suite
, "req_resume_key",
579 test_ioctl_req_resume_key
);
580 torture_suite_add_1smb2_test(suite
, "copy_chunk_simple",
581 test_ioctl_copy_chunk_simple
);
582 torture_suite_add_1smb2_test(suite
, "copy_chunk_multi",
583 test_ioctl_copy_chunk_multi
);
584 torture_suite_add_1smb2_test(suite
, "copy_chunk_tiny",
585 test_ioctl_copy_chunk_tiny
);
586 torture_suite_add_1smb2_test(suite
, "copy_chunk_overwrite",
587 test_ioctl_copy_chunk_over
);
588 torture_suite_add_1smb2_test(suite
, "copy_chunk_append",
589 test_ioctl_copy_chunk_append
);
591 suite
->description
= talloc_strdup(suite
, "SMB2-IOCTL tests");