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 uint64_t patt_hash(uint64_t off)
135 static bool check_pattern(struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
136 struct smb2_handle h, uint64_t off, uint64_t len,
144 r.in.file.handle = h;
147 status = smb2_read(tree, mem_ctx, &r);
148 if (!NT_STATUS_IS_OK(status)) {
149 printf("read failed - %s\n", nt_errstr(status));
151 } else if (len != r.out.data.length) {
152 printf("read data len mismatch got %zu, expected %llu\n",
153 r.out.data.length, (unsigned long long)len);
157 for (i = 0; i <= len - 8; i += 8, patt_off += 8) {
158 if (BVAL(r.out.data.data, i) != patt_hash(patt_off)) {
159 printf("pattern bad at %llu, got %llx, expected %llx\n",
160 (unsigned long long)i,
161 (unsigned long long)BVAL(r.out.data.data, i),
162 (unsigned long long)patt_hash(patt_off));
167 talloc_free(r.out.data.data);
171 static bool test_setup_copy_chunk(struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
173 struct smb2_handle *src_h,
175 struct smb2_handle *dest_h,
177 struct srv_copychunk_copy *cc_copy,
178 union smb_ioctl *ioctl)
180 struct req_resume_key_rsp res_key;
182 enum ndr_err_code ndr_ret;
184 uint8_t *buf = talloc_zero_size(mem_ctx, MAX(src_size, dest_size));
186 printf("no mem for file data buffer\n");
190 smb2_util_unlink(tree, FNAME);
191 smb2_util_unlink(tree, FNAME2);
193 status = torture_smb2_testfile(tree, FNAME, src_h);
194 if (!NT_STATUS_IS_OK(status)) {
195 printf("create write\n");
200 for (i = 0; i <= src_size - 8; i += 8) {
201 SBVAL(buf, i, patt_hash(i));
203 status = smb2_util_write(tree, *src_h, buf, 0, src_size);
204 if (!NT_STATUS_IS_OK(status)) {
205 printf("failed src write\n");
210 status = torture_smb2_testfile(tree, FNAME2, dest_h);
211 if (!NT_STATUS_IS_OK(status)) {
212 printf("create write\n");
217 for (i = 0; i <= src_size - 8; i += 8) {
218 SBVAL(buf, i, patt_hash(i));
220 status = smb2_util_write(tree, *dest_h, buf, 0, dest_size);
221 if (!NT_STATUS_IS_OK(status)) {
222 printf("failed dest write\n");
227 ZERO_STRUCTPN(ioctl);
228 ioctl->smb2.level = RAW_IOCTL_SMB2;
229 ioctl->smb2.in.file.handle = *src_h;
230 ioctl->smb2.in.function = FSCTL_SRV_REQUEST_RESUME_KEY;
231 /* Allow for Key + ContextLength + Context */
232 ioctl->smb2.in.max_response_size = 32;
233 ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
235 status = smb2_ioctl(tree, mem_ctx, &ioctl->smb2);
236 if (!NT_STATUS_IS_OK(status)) {
237 printf("FSCTL_SRV_REQUEST_RESUME_KEY failed\n");
241 ndr_ret = ndr_pull_struct_blob(&ioctl->smb2.out.out, mem_ctx, &res_key,
242 (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
243 if (ndr_ret != NDR_ERR_SUCCESS) {
247 ZERO_STRUCTPN(ioctl);
248 ioctl->smb2.level = RAW_IOCTL_SMB2;
249 ioctl->smb2.in.file.handle = *dest_h;
250 ioctl->smb2.in.function = FSCTL_SRV_COPYCHUNK;
251 ioctl->smb2.in.max_response_size = sizeof(struct srv_copychunk_rsp);
252 ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
254 ZERO_STRUCTPN(cc_copy);
255 memcpy(cc_copy->source_key, res_key.resume_key, ARRAY_SIZE(cc_copy->source_key));
256 cc_copy->chunk_count = nchunks;
257 cc_copy->chunks = talloc_zero_array(mem_ctx, struct srv_copychunk, nchunks);
258 if (cc_copy->chunks == NULL) {
259 printf("not enough memory to allocate %u chunks\n", nchunks);
267 static bool check_copy_chunk_rsp(struct srv_copychunk_rsp *cc_rsp,
268 uint32_t ex_chunks_written,
269 uint32_t ex_chunk_bytes_written,
270 uint32_t ex_total_bytes_written)
272 if (cc_rsp->chunks_written != ex_chunks_written) {
273 printf("expected %u chunks, got %u\n",
274 ex_chunks_written, cc_rsp->chunks_written);
277 if (cc_rsp->chunk_bytes_written != ex_chunk_bytes_written) {
278 printf("expected %u chunk bytes remaining, got %u\n",
279 ex_chunk_bytes_written, cc_rsp->chunk_bytes_written);
282 if (cc_rsp->total_bytes_written != ex_total_bytes_written) {
283 printf("expected %u total bytes, got %u\n",
284 ex_total_bytes_written, cc_rsp->total_bytes_written);
290 static bool test_ioctl_copy_chunk_simple(struct torture_context *torture,
291 struct smb2_tree *tree)
293 struct smb2_handle src_h;
294 struct smb2_handle dest_h;
296 union smb_ioctl ioctl;
297 TALLOC_CTX *tmp_ctx = talloc_new(tree);
298 struct srv_copychunk_copy cc_copy;
299 struct srv_copychunk_rsp cc_rsp;
300 enum ndr_err_code ndr_ret;
303 ok = test_setup_copy_chunk(tree, tmp_ctx,
305 &src_h, 4096, /* fill 4096 byte src file */
306 &dest_h, 0, /* 0 byte dest file */
313 /* copy all src file data (via a single chunk desc) */
314 cc_copy.chunks[0].source_off = 0;
315 cc_copy.chunks[0].target_off = 0;
316 cc_copy.chunks[0].length = 4096;
318 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
320 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
321 if (ndr_ret != NDR_ERR_SUCCESS) {
325 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
326 if (!NT_STATUS_IS_OK(status)) {
327 printf("FSCTL_SRV_COPYCHUNK failed\n");
331 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
333 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
334 if (ndr_ret != NDR_ERR_SUCCESS) {
338 ok = check_copy_chunk_rsp(&cc_rsp,
339 1, /* chunks written */
340 0, /* chunk bytes unsuccessfully written */
341 4096); /* total bytes written */
346 ok = check_pattern(tree, tmp_ctx, dest_h, 0, 4096, 0);
351 smb2_util_close(tree, src_h);
352 smb2_util_close(tree, dest_h);
353 talloc_free(tmp_ctx);
357 static bool test_ioctl_copy_chunk_multi(struct torture_context *torture,
358 struct smb2_tree *tree)
360 struct smb2_handle src_h;
361 struct smb2_handle dest_h;
363 union smb_ioctl ioctl;
364 TALLOC_CTX *tmp_ctx = talloc_new(tree);
365 struct srv_copychunk_copy cc_copy;
366 struct srv_copychunk_rsp cc_rsp;
367 enum ndr_err_code ndr_ret;
370 ok = test_setup_copy_chunk(tree, tmp_ctx,
372 &src_h, 8192, /* src file */
373 &dest_h, 0, /* dest file */
380 /* copy all src file data via two chunks */
381 cc_copy.chunks[0].source_off = 0;
382 cc_copy.chunks[0].target_off = 0;
383 cc_copy.chunks[0].length = 4096;
385 cc_copy.chunks[1].source_off = 4096;
386 cc_copy.chunks[1].target_off = 4096;
387 cc_copy.chunks[1].length = 4096;
389 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
391 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
392 if (ndr_ret != NDR_ERR_SUCCESS) {
396 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
397 if (!NT_STATUS_IS_OK(status)) {
398 printf("FSCTL_SRV_COPYCHUNK failed\n");
402 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
404 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
405 if (ndr_ret != NDR_ERR_SUCCESS) {
409 ok = check_copy_chunk_rsp(&cc_rsp,
410 2, /* chunks written */
411 0, /* chunk bytes unsuccessfully written */
412 8192); /* total bytes written */
417 smb2_util_close(tree, src_h);
418 smb2_util_close(tree, dest_h);
419 talloc_free(tmp_ctx);
423 static bool test_ioctl_copy_chunk_tiny(struct torture_context *torture,
424 struct smb2_tree *tree)
426 struct smb2_handle src_h;
427 struct smb2_handle dest_h;
429 union smb_ioctl ioctl;
430 TALLOC_CTX *tmp_ctx = talloc_new(tree);
431 struct srv_copychunk_copy cc_copy;
432 struct srv_copychunk_rsp cc_rsp;
433 enum ndr_err_code ndr_ret;
436 ok = test_setup_copy_chunk(tree, tmp_ctx,
438 &src_h, 100, /* src file */
439 &dest_h, 0, /* dest file */
446 /* copy all src file data via two chunks, sub block size chunks */
447 cc_copy.chunks[0].source_off = 0;
448 cc_copy.chunks[0].target_off = 0;
449 cc_copy.chunks[0].length = 50;
451 cc_copy.chunks[1].source_off = 50;
452 cc_copy.chunks[1].target_off = 50;
453 cc_copy.chunks[1].length = 50;
455 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
457 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
458 if (ndr_ret != NDR_ERR_SUCCESS) {
462 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
463 if (!NT_STATUS_IS_OK(status)) {
464 printf("FSCTL_SRV_COPYCHUNK failed\n");
468 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
470 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
471 if (ndr_ret != NDR_ERR_SUCCESS) {
475 ok = check_copy_chunk_rsp(&cc_rsp,
476 2, /* chunks written */
477 0, /* chunk bytes unsuccessfully written */
478 100); /* total bytes written */
483 ok = check_pattern(tree, tmp_ctx, dest_h, 0, 100, 0);
488 smb2_util_close(tree, src_h);
489 smb2_util_close(tree, dest_h);
490 talloc_free(tmp_ctx);
494 static bool test_ioctl_copy_chunk_over(struct torture_context *torture,
495 struct smb2_tree *tree)
497 struct smb2_handle src_h;
498 struct smb2_handle dest_h;
500 union smb_ioctl ioctl;
501 TALLOC_CTX *tmp_ctx = talloc_new(tree);
502 struct srv_copychunk_copy cc_copy;
503 struct srv_copychunk_rsp cc_rsp;
504 enum ndr_err_code ndr_ret;
507 ok = test_setup_copy_chunk(tree, tmp_ctx,
509 &src_h, 8192, /* src file */
510 &dest_h, 4096, /* dest file */
517 /* first chunk overwrites existing dest data */
518 cc_copy.chunks[0].source_off = 0;
519 cc_copy.chunks[0].target_off = 0;
520 cc_copy.chunks[0].length = 4096;
522 /* second chunk overwrites the first */
523 cc_copy.chunks[1].source_off = 4096;
524 cc_copy.chunks[1].target_off = 0;
525 cc_copy.chunks[1].length = 4096;
527 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
529 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
530 if (ndr_ret != NDR_ERR_SUCCESS) {
534 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
535 if (!NT_STATUS_IS_OK(status)) {
536 printf("FSCTL_SRV_COPYCHUNK failed\n");
540 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
542 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
543 if (ndr_ret != NDR_ERR_SUCCESS) {
547 ok = check_copy_chunk_rsp(&cc_rsp,
548 2, /* chunks written */
549 0, /* chunk bytes unsuccessfully written */
550 8192); /* total bytes written */
555 ok = check_pattern(tree, tmp_ctx, dest_h, 0, 4096, 4096);
560 smb2_util_close(tree, src_h);
561 smb2_util_close(tree, dest_h);
562 talloc_free(tmp_ctx);
566 static bool test_ioctl_copy_chunk_append(struct torture_context *torture,
567 struct smb2_tree *tree)
569 struct smb2_handle src_h;
570 struct smb2_handle dest_h;
572 union smb_ioctl ioctl;
573 TALLOC_CTX *tmp_ctx = talloc_new(tree);
574 struct srv_copychunk_copy cc_copy;
575 struct srv_copychunk_rsp cc_rsp;
576 enum ndr_err_code ndr_ret;
579 ok = test_setup_copy_chunk(tree, tmp_ctx,
581 &src_h, 4096, /* src file */
582 &dest_h, 0, /* dest file */
589 cc_copy.chunks[0].source_off = 0;
590 cc_copy.chunks[0].target_off = 0;
591 cc_copy.chunks[0].length = 4096;
593 /* second chunk appends the same data to the first */
594 cc_copy.chunks[1].source_off = 0;
595 cc_copy.chunks[1].target_off = 4096;
596 cc_copy.chunks[1].length = 4096;
598 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
600 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
601 if (ndr_ret != NDR_ERR_SUCCESS) {
605 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
606 if (!NT_STATUS_IS_OK(status)) {
607 printf("FSCTL_SRV_COPYCHUNK failed\n");
611 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
613 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
614 if (ndr_ret != NDR_ERR_SUCCESS) {
618 ok = check_copy_chunk_rsp(&cc_rsp,
619 2, /* chunks written */
620 0, /* chunk bytes unsuccessfully written */
621 8192); /* total bytes written */
626 ok = check_pattern(tree, tmp_ctx, dest_h, 0, 4096, 0);
631 ok = check_pattern(tree, tmp_ctx, dest_h, 4096, 4096, 0);
636 smb2_util_close(tree, src_h);
637 smb2_util_close(tree, dest_h);
638 talloc_free(tmp_ctx);
643 basic testing of SMB2 ioctls
645 struct torture_suite *torture_smb2_ioctl_init(void)
647 struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "ioctl");
649 torture_suite_add_1smb2_test(suite, "shadow_copy",
650 test_ioctl_get_shadow_copy);
651 torture_suite_add_1smb2_test(suite, "req_resume_key",
652 test_ioctl_req_resume_key);
653 torture_suite_add_1smb2_test(suite, "copy_chunk_simple",
654 test_ioctl_copy_chunk_simple);
655 torture_suite_add_1smb2_test(suite, "copy_chunk_multi",
656 test_ioctl_copy_chunk_multi);
657 torture_suite_add_1smb2_test(suite, "copy_chunk_tiny",
658 test_ioctl_copy_chunk_tiny);
659 torture_suite_add_1smb2_test(suite, "copy_chunk_overwrite",
660 test_ioctl_copy_chunk_over);
661 torture_suite_add_1smb2_test(suite, "copy_chunk_append",
662 test_ioctl_copy_chunk_append);
664 suite->description = talloc_strdup(suite, "SMB2-IOCTL tests");