2 Unix SMB/CIFS implementation.
4 test suite for SMB2 ioctl operations
6 Copyright (C) David Disseldorp 2011-2013
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"
32 #define DNAME "testfsctl_dir"
35 basic testing of SMB2 shadow copy calls
37 static bool test_ioctl_get_shadow_copy(struct torture_context *torture,
38 struct smb2_tree *tree)
43 union smb_ioctl ioctl;
44 TALLOC_CTX *tmp_ctx = talloc_new(tree);
46 smb2_util_unlink(tree, FNAME);
48 status = torture_smb2_testfile(tree, FNAME, &h);
49 torture_assert_ntstatus_ok(torture, status, "create write");
52 status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf));
53 torture_assert_ntstatus_ok(torture, status, "write");
56 ioctl.smb2.level = RAW_IOCTL_SMB2;
57 ioctl.smb2.in.file.handle = h;
58 ioctl.smb2.in.function = FSCTL_SRV_ENUM_SNAPS;
59 ioctl.smb2.in.max_response_size = 16;
60 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
62 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
63 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)
64 || NT_STATUS_EQUAL(status, NT_STATUS_INVALID_DEVICE_REQUEST)) {
65 torture_skip(torture, "FSCTL_SRV_ENUM_SNAPS not supported\n");
67 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_ENUM_SNAPS");
73 basic testing of the SMB2 server side copy ioctls
75 static bool test_ioctl_req_resume_key(struct torture_context *torture,
76 struct smb2_tree *tree)
81 union smb_ioctl ioctl;
82 TALLOC_CTX *tmp_ctx = talloc_new(tree);
83 struct req_resume_key_rsp res_key;
84 enum ndr_err_code ndr_ret;
86 smb2_util_unlink(tree, FNAME);
88 status = torture_smb2_testfile(tree, FNAME, &h);
89 torture_assert_ntstatus_ok(torture, status, "create write");
92 status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf));
93 torture_assert_ntstatus_ok(torture, status, "write");
96 ioctl.smb2.level = RAW_IOCTL_SMB2;
97 ioctl.smb2.in.file.handle = h;
98 ioctl.smb2.in.function = FSCTL_SRV_REQUEST_RESUME_KEY;
99 ioctl.smb2.in.max_response_size = 32;
100 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
102 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
103 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_REQUEST_RESUME_KEY");
105 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, &res_key,
106 (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
107 torture_assert_ndr_success(torture, ndr_ret,
108 "ndr_pull_req_resume_key_rsp");
110 ndr_print_debug((ndr_print_fn_t)ndr_print_req_resume_key_rsp, "yo", &res_key);
112 talloc_free(tmp_ctx);
116 static uint64_t patt_hash(uint64_t off)
121 static bool check_pattern(struct torture_context *torture,
122 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
123 struct smb2_handle h, uint64_t off, uint64_t len,
131 r.in.file.handle = h;
134 status = smb2_read(tree, mem_ctx, &r);
135 torture_assert_ntstatus_ok(torture, status, "read");
137 torture_assert_u64_equal(torture, r.out.data.length, len,
138 "read data len mismatch");
140 for (i = 0; i <= len - 8; i += 8, patt_off += 8) {
141 uint64_t data = BVAL(r.out.data.data, i);
142 torture_assert_u64_equal(torture, data, patt_hash(patt_off),
143 talloc_asprintf(torture, "read data "
144 "pattern bad at %llu\n",
145 (unsigned long long)i));
148 talloc_free(r.out.data.data);
152 static bool test_setup_create_fill(struct torture_context *torture,
153 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
155 struct smb2_handle *fh,
157 uint32_t desired_access,
158 uint32_t file_attributes)
160 struct smb2_create io;
163 uint8_t *buf = talloc_zero_size(mem_ctx, size);
164 torture_assert(torture, (buf != NULL), "no memory for file data buf");
166 smb2_util_unlink(tree, fname);
169 io.in.desired_access = desired_access;
170 io.in.file_attributes = file_attributes;
171 io.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
173 NTCREATEX_SHARE_ACCESS_DELETE|
174 NTCREATEX_SHARE_ACCESS_READ|
175 NTCREATEX_SHARE_ACCESS_WRITE;
178 status = smb2_create(tree, mem_ctx, &io);
179 torture_assert_ntstatus_ok(torture, status, "file create");
181 *fh = io.out.file.handle;
184 uint64_t cur_off = 0;
185 for (i = 0; i <= size - 8; i += 8) {
186 SBVAL(buf, i, patt_hash(i));
189 uint64_t io_sz = MIN(1024 * 1024, size);
190 status = smb2_util_write(tree, *fh,
191 buf + cur_off, cur_off, io_sz);
192 torture_assert_ntstatus_ok(torture, status, "file write");
201 static bool test_setup_copy_chunk(struct torture_context *torture,
202 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
204 struct smb2_handle *src_h,
206 uint32_t src_desired_access,
207 struct smb2_handle *dest_h,
209 uint32_t dest_desired_access,
210 struct srv_copychunk_copy *cc_copy,
211 union smb_ioctl *ioctl)
213 struct req_resume_key_rsp res_key;
216 enum ndr_err_code ndr_ret;
218 ok = test_setup_create_fill(torture, tree, mem_ctx, FNAME,
219 src_h, src_size, src_desired_access,
220 FILE_ATTRIBUTE_NORMAL);
221 torture_assert(torture, ok, "src file create fill");
223 ok = test_setup_create_fill(torture, tree, mem_ctx, FNAME2,
224 dest_h, dest_size, dest_desired_access,
225 FILE_ATTRIBUTE_NORMAL);
226 torture_assert(torture, ok, "dest file create fill");
228 ZERO_STRUCTPN(ioctl);
229 ioctl->smb2.level = RAW_IOCTL_SMB2;
230 ioctl->smb2.in.file.handle = *src_h;
231 ioctl->smb2.in.function = FSCTL_SRV_REQUEST_RESUME_KEY;
232 /* Allow for Key + ContextLength + Context */
233 ioctl->smb2.in.max_response_size = 32;
234 ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
236 status = smb2_ioctl(tree, mem_ctx, &ioctl->smb2);
237 torture_assert_ntstatus_ok(torture, status,
238 "FSCTL_SRV_REQUEST_RESUME_KEY");
240 ndr_ret = ndr_pull_struct_blob(&ioctl->smb2.out.out, mem_ctx, &res_key,
241 (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
243 torture_assert_ndr_success(torture, ndr_ret,
244 "ndr_pull_req_resume_key_rsp");
246 ZERO_STRUCTPN(ioctl);
247 ioctl->smb2.level = RAW_IOCTL_SMB2;
248 ioctl->smb2.in.file.handle = *dest_h;
249 ioctl->smb2.in.function = FSCTL_SRV_COPYCHUNK;
250 ioctl->smb2.in.max_response_size = sizeof(struct srv_copychunk_rsp);
251 ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
253 ZERO_STRUCTPN(cc_copy);
254 memcpy(cc_copy->source_key, res_key.resume_key, ARRAY_SIZE(cc_copy->source_key));
255 cc_copy->chunk_count = nchunks;
256 cc_copy->chunks = talloc_zero_array(mem_ctx, struct srv_copychunk, nchunks);
257 torture_assert(torture, (cc_copy->chunks != NULL), "no memory for chunks");
263 static bool check_copy_chunk_rsp(struct torture_context *torture,
264 struct srv_copychunk_rsp *cc_rsp,
265 uint32_t ex_chunks_written,
266 uint32_t ex_chunk_bytes_written,
267 uint32_t ex_total_bytes_written)
269 torture_assert_int_equal(torture, cc_rsp->chunks_written,
270 ex_chunks_written, "num chunks");
271 torture_assert_int_equal(torture, cc_rsp->chunk_bytes_written,
272 ex_chunk_bytes_written, "chunk bytes written");
273 torture_assert_int_equal(torture, cc_rsp->total_bytes_written,
274 ex_total_bytes_written, "chunk total bytes");
278 static bool test_ioctl_copy_chunk_simple(struct torture_context *torture,
279 struct smb2_tree *tree)
281 struct smb2_handle src_h;
282 struct smb2_handle dest_h;
284 union smb_ioctl ioctl;
285 TALLOC_CTX *tmp_ctx = talloc_new(tree);
286 struct srv_copychunk_copy cc_copy;
287 struct srv_copychunk_rsp cc_rsp;
288 enum ndr_err_code ndr_ret;
291 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
293 &src_h, 4096, /* fill 4096 byte src file */
295 &dest_h, 0, /* 0 byte dest file */
300 torture_fail(torture, "setup copy chunk error");
303 /* copy all src file data (via a single chunk desc) */
304 cc_copy.chunks[0].source_off = 0;
305 cc_copy.chunks[0].target_off = 0;
306 cc_copy.chunks[0].length = 4096;
308 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
310 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
311 torture_assert_ndr_success(torture, ndr_ret,
312 "ndr_push_srv_copychunk_copy");
314 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
315 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
317 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
319 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
320 torture_assert_ndr_success(torture, ndr_ret,
321 "ndr_pull_srv_copychunk_rsp");
323 ok = check_copy_chunk_rsp(torture, &cc_rsp,
324 1, /* chunks written */
325 0, /* chunk bytes unsuccessfully written */
326 4096); /* total bytes written */
328 torture_fail(torture, "bad copy chunk response data");
331 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
333 torture_fail(torture, "inconsistent file data");
336 smb2_util_close(tree, src_h);
337 smb2_util_close(tree, dest_h);
338 talloc_free(tmp_ctx);
342 static bool test_ioctl_copy_chunk_multi(struct torture_context *torture,
343 struct smb2_tree *tree)
345 struct smb2_handle src_h;
346 struct smb2_handle dest_h;
348 union smb_ioctl ioctl;
349 TALLOC_CTX *tmp_ctx = talloc_new(tree);
350 struct srv_copychunk_copy cc_copy;
351 struct srv_copychunk_rsp cc_rsp;
352 enum ndr_err_code ndr_ret;
355 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
357 &src_h, 8192, /* src file */
359 &dest_h, 0, /* dest file */
364 torture_fail(torture, "setup copy chunk error");
367 /* copy all src file data via two chunks */
368 cc_copy.chunks[0].source_off = 0;
369 cc_copy.chunks[0].target_off = 0;
370 cc_copy.chunks[0].length = 4096;
372 cc_copy.chunks[1].source_off = 4096;
373 cc_copy.chunks[1].target_off = 4096;
374 cc_copy.chunks[1].length = 4096;
376 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
378 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
379 torture_assert_ndr_success(torture, ndr_ret,
380 "ndr_push_srv_copychunk_copy");
382 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
383 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
385 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
387 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
388 torture_assert_ndr_success(torture, ndr_ret,
389 "ndr_pull_srv_copychunk_rsp");
391 ok = check_copy_chunk_rsp(torture, &cc_rsp,
392 2, /* chunks written */
393 0, /* chunk bytes unsuccessfully written */
394 8192); /* total bytes written */
396 torture_fail(torture, "bad copy chunk response data");
399 smb2_util_close(tree, src_h);
400 smb2_util_close(tree, dest_h);
401 talloc_free(tmp_ctx);
405 static bool test_ioctl_copy_chunk_tiny(struct torture_context *torture,
406 struct smb2_tree *tree)
408 struct smb2_handle src_h;
409 struct smb2_handle dest_h;
411 union smb_ioctl ioctl;
412 TALLOC_CTX *tmp_ctx = talloc_new(tree);
413 struct srv_copychunk_copy cc_copy;
414 struct srv_copychunk_rsp cc_rsp;
415 enum ndr_err_code ndr_ret;
418 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
420 &src_h, 100, /* src file */
422 &dest_h, 0, /* dest file */
427 torture_fail(torture, "setup copy chunk error");
430 /* copy all src file data via two chunks, sub block size chunks */
431 cc_copy.chunks[0].source_off = 0;
432 cc_copy.chunks[0].target_off = 0;
433 cc_copy.chunks[0].length = 50;
435 cc_copy.chunks[1].source_off = 50;
436 cc_copy.chunks[1].target_off = 50;
437 cc_copy.chunks[1].length = 50;
439 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
441 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
442 torture_assert_ndr_success(torture, ndr_ret,
443 "ndr_push_srv_copychunk_copy");
445 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
446 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
448 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
450 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
451 torture_assert_ndr_success(torture, ndr_ret,
452 "ndr_pull_srv_copychunk_rsp");
454 ok = check_copy_chunk_rsp(torture, &cc_rsp,
455 2, /* chunks written */
456 0, /* chunk bytes unsuccessfully written */
457 100); /* total bytes written */
459 torture_fail(torture, "bad copy chunk response data");
462 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 100, 0);
464 torture_fail(torture, "inconsistent file data");
467 smb2_util_close(tree, src_h);
468 smb2_util_close(tree, dest_h);
469 talloc_free(tmp_ctx);
473 static bool test_ioctl_copy_chunk_over(struct torture_context *torture,
474 struct smb2_tree *tree)
476 struct smb2_handle src_h;
477 struct smb2_handle dest_h;
479 union smb_ioctl ioctl;
480 TALLOC_CTX *tmp_ctx = talloc_new(tree);
481 struct srv_copychunk_copy cc_copy;
482 struct srv_copychunk_rsp cc_rsp;
483 enum ndr_err_code ndr_ret;
486 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
488 &src_h, 8192, /* src file */
490 &dest_h, 4096, /* dest file */
495 torture_fail(torture, "setup copy chunk error");
498 /* first chunk overwrites existing dest data */
499 cc_copy.chunks[0].source_off = 0;
500 cc_copy.chunks[0].target_off = 0;
501 cc_copy.chunks[0].length = 4096;
503 /* second chunk overwrites the first */
504 cc_copy.chunks[1].source_off = 4096;
505 cc_copy.chunks[1].target_off = 0;
506 cc_copy.chunks[1].length = 4096;
508 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
510 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
511 torture_assert_ndr_success(torture, ndr_ret,
512 "ndr_push_srv_copychunk_copy");
514 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
515 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
517 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
519 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
520 torture_assert_ndr_success(torture, ndr_ret,
521 "ndr_pull_srv_copychunk_rsp");
523 ok = check_copy_chunk_rsp(torture, &cc_rsp,
524 2, /* chunks written */
525 0, /* chunk bytes unsuccessfully written */
526 8192); /* total bytes written */
528 torture_fail(torture, "bad copy chunk response data");
531 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 4096);
533 torture_fail(torture, "inconsistent file data");
536 smb2_util_close(tree, src_h);
537 smb2_util_close(tree, dest_h);
538 talloc_free(tmp_ctx);
542 static bool test_ioctl_copy_chunk_append(struct torture_context *torture,
543 struct smb2_tree *tree)
545 struct smb2_handle src_h;
546 struct smb2_handle dest_h;
548 union smb_ioctl ioctl;
549 TALLOC_CTX *tmp_ctx = talloc_new(tree);
550 struct srv_copychunk_copy cc_copy;
551 struct srv_copychunk_rsp cc_rsp;
552 enum ndr_err_code ndr_ret;
555 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
557 &src_h, 4096, /* src file */
559 &dest_h, 0, /* dest file */
564 torture_fail(torture, "setup copy chunk error");
567 cc_copy.chunks[0].source_off = 0;
568 cc_copy.chunks[0].target_off = 0;
569 cc_copy.chunks[0].length = 4096;
571 /* second chunk appends the same data to the first */
572 cc_copy.chunks[1].source_off = 0;
573 cc_copy.chunks[1].target_off = 4096;
574 cc_copy.chunks[1].length = 4096;
576 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
578 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
579 torture_assert_ndr_success(torture, ndr_ret,
580 "ndr_push_srv_copychunk_copy");
582 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
583 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
585 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
587 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
588 torture_assert_ndr_success(torture, ndr_ret,
589 "ndr_pull_srv_copychunk_rsp");
591 ok = check_copy_chunk_rsp(torture, &cc_rsp,
592 2, /* chunks written */
593 0, /* chunk bytes unsuccessfully written */
594 8192); /* total bytes written */
596 torture_fail(torture, "bad copy chunk response data");
599 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
601 torture_fail(torture, "inconsistent file data");
604 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 4096, 4096, 0);
606 torture_fail(torture, "inconsistent file data");
609 smb2_util_close(tree, src_h);
610 smb2_util_close(tree, dest_h);
611 talloc_free(tmp_ctx);
615 static bool test_ioctl_copy_chunk_limits(struct torture_context *torture,
616 struct smb2_tree *tree)
618 struct smb2_handle src_h;
619 struct smb2_handle dest_h;
621 union smb_ioctl ioctl;
622 TALLOC_CTX *tmp_ctx = talloc_new(tree);
623 struct srv_copychunk_copy cc_copy;
624 struct srv_copychunk_rsp cc_rsp;
625 enum ndr_err_code ndr_ret;
628 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
630 &src_h, 4096, /* src file */
632 &dest_h, 0, /* dest file */
637 torture_fail(torture, "setup copy chunk error");
640 /* send huge chunk length request */
641 cc_copy.chunks[0].source_off = 0;
642 cc_copy.chunks[0].target_off = 0;
643 cc_copy.chunks[0].length = UINT_MAX;
645 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
647 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
648 torture_assert_ndr_success(torture, ndr_ret, "marshalling request");
650 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
651 torture_assert_ntstatus_equal(torture, status,
652 NT_STATUS_INVALID_PARAMETER,
653 "bad oversize chunk response");
655 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
657 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
658 torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
660 torture_comment(torture, "limit max chunks, got %u\n",
661 cc_rsp.chunks_written);
662 torture_comment(torture, "limit max chunk len, got %u\n",
663 cc_rsp.chunk_bytes_written);
664 torture_comment(torture, "limit max total bytes, got %u\n",
665 cc_rsp.total_bytes_written);
667 smb2_util_close(tree, src_h);
668 smb2_util_close(tree, dest_h);
669 talloc_free(tmp_ctx);
673 static bool test_ioctl_copy_chunk_src_lck(struct torture_context *torture,
674 struct smb2_tree *tree)
676 struct smb2_handle src_h;
677 struct smb2_handle src_h2;
678 struct smb2_handle dest_h;
680 union smb_ioctl ioctl;
681 TALLOC_CTX *tmp_ctx = talloc_new(tree);
682 struct srv_copychunk_copy cc_copy;
683 struct srv_copychunk_rsp cc_rsp;
684 enum ndr_err_code ndr_ret;
686 struct smb2_lock lck;
687 struct smb2_lock_element el[1];
689 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
691 &src_h, 4096, /* src file */
693 &dest_h, 0, /* dest file */
698 torture_fail(torture, "setup copy chunk error");
701 cc_copy.chunks[0].source_off = 0;
702 cc_copy.chunks[0].target_off = 0;
703 cc_copy.chunks[0].length = 4096;
705 /* open and lock the copychunk src file */
706 status = torture_smb2_testfile(tree, FNAME, &src_h2);
707 torture_assert_ntstatus_ok(torture, status, "2nd src open");
709 lck.in.lock_count = 0x0001;
710 lck.in.lock_sequence = 0x00000000;
711 lck.in.file.handle = src_h2;
713 el[0].offset = cc_copy.chunks[0].source_off;
714 el[0].length = cc_copy.chunks[0].length;
716 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
718 status = smb2_lock(tree, &lck);
719 torture_assert_ntstatus_ok(torture, status, "lock");
721 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
723 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
724 torture_assert_ndr_success(torture, ndr_ret,
725 "ndr_push_srv_copychunk_copy");
727 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
729 * 2k12 & Samba return lock_conflict, Windows 7 & 2k8 return success...
731 * Edgar Olougouna @ MS wrote:
732 * Regarding the FSCTL_SRV_COPYCHUNK and STATUS_FILE_LOCK_CONFLICT
733 * discrepancy observed between Windows versions, we confirm that the
734 * behavior change is expected.
736 * CopyChunk in Windows Server 2012 use regular Readfile/Writefile APIs
737 * to move the chunks from the source to the destination.
738 * These ReadFile/WriteFile APIs go through the byte-range lock checks,
739 * and this explains the observed STATUS_FILE_LOCK_CONFLICT error.
741 * Prior to Windows Server 2012, CopyChunk used mapped sections to move
742 * the data. And byte range locks are not enforced on mapped I/O, and
743 * this explains the STATUS_SUCCESS observed on Windows Server 2008 R2.
745 torture_assert_ntstatus_equal(torture, status,
746 NT_STATUS_FILE_LOCK_CONFLICT,
747 "FSCTL_SRV_COPYCHUNK locked");
749 /* should get cc response data with the lock conflict status */
750 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
752 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
753 torture_assert_ndr_success(torture, ndr_ret,
754 "ndr_pull_srv_copychunk_rsp");
755 ok = check_copy_chunk_rsp(torture, &cc_rsp,
756 0, /* chunks written */
757 0, /* chunk bytes unsuccessfully written */
758 0); /* total bytes written */
760 lck.in.lock_count = 0x0001;
761 lck.in.lock_sequence = 0x00000001;
762 lck.in.file.handle = src_h2;
764 el[0].offset = cc_copy.chunks[0].source_off;
765 el[0].length = cc_copy.chunks[0].length;
767 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
768 status = smb2_lock(tree, &lck);
769 torture_assert_ntstatus_ok(torture, status, "unlock");
771 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
772 torture_assert_ntstatus_ok(torture, status,
773 "FSCTL_SRV_COPYCHUNK unlocked");
775 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
777 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
778 torture_assert_ndr_success(torture, ndr_ret,
779 "ndr_pull_srv_copychunk_rsp");
781 ok = check_copy_chunk_rsp(torture, &cc_rsp,
782 1, /* chunks written */
783 0, /* chunk bytes unsuccessfully written */
784 4096); /* total bytes written */
786 torture_fail(torture, "bad copy chunk response data");
789 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
791 torture_fail(torture, "inconsistent file data");
794 smb2_util_close(tree, src_h2);
795 smb2_util_close(tree, src_h);
796 smb2_util_close(tree, dest_h);
797 talloc_free(tmp_ctx);
801 static bool test_ioctl_copy_chunk_dest_lck(struct torture_context *torture,
802 struct smb2_tree *tree)
804 struct smb2_handle src_h;
805 struct smb2_handle dest_h;
806 struct smb2_handle dest_h2;
808 union smb_ioctl ioctl;
809 TALLOC_CTX *tmp_ctx = talloc_new(tree);
810 struct srv_copychunk_copy cc_copy;
811 struct srv_copychunk_rsp cc_rsp;
812 enum ndr_err_code ndr_ret;
814 struct smb2_lock lck;
815 struct smb2_lock_element el[1];
817 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
819 &src_h, 4096, /* src file */
821 &dest_h, 4096, /* dest file */
826 torture_fail(torture, "setup copy chunk error");
829 cc_copy.chunks[0].source_off = 0;
830 cc_copy.chunks[0].target_off = 0;
831 cc_copy.chunks[0].length = 4096;
833 /* open and lock the copychunk dest file */
834 status = torture_smb2_testfile(tree, FNAME2, &dest_h2);
835 torture_assert_ntstatus_ok(torture, status, "2nd src open");
837 lck.in.lock_count = 0x0001;
838 lck.in.lock_sequence = 0x00000000;
839 lck.in.file.handle = dest_h2;
841 el[0].offset = cc_copy.chunks[0].target_off;
842 el[0].length = cc_copy.chunks[0].length;
844 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
846 status = smb2_lock(tree, &lck);
847 torture_assert_ntstatus_ok(torture, status, "lock");
849 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
851 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
852 torture_assert_ndr_success(torture, ndr_ret,
853 "ndr_push_srv_copychunk_copy");
855 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
856 torture_assert_ntstatus_equal(torture, status,
857 NT_STATUS_FILE_LOCK_CONFLICT,
858 "FSCTL_SRV_COPYCHUNK locked");
860 lck.in.lock_count = 0x0001;
861 lck.in.lock_sequence = 0x00000001;
862 lck.in.file.handle = dest_h2;
864 el[0].offset = cc_copy.chunks[0].target_off;
865 el[0].length = cc_copy.chunks[0].length;
867 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
868 status = smb2_lock(tree, &lck);
869 torture_assert_ntstatus_ok(torture, status, "unlock");
871 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
872 torture_assert_ntstatus_ok(torture, status,
873 "FSCTL_SRV_COPYCHUNK unlocked");
875 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
877 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
878 torture_assert_ndr_success(torture, ndr_ret,
879 "ndr_pull_srv_copychunk_rsp");
881 ok = check_copy_chunk_rsp(torture, &cc_rsp,
882 1, /* chunks written */
883 0, /* chunk bytes unsuccessfully written */
884 4096); /* total bytes written */
886 torture_fail(torture, "bad copy chunk response data");
889 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
891 torture_fail(torture, "inconsistent file data");
894 smb2_util_close(tree, dest_h2);
895 smb2_util_close(tree, src_h);
896 smb2_util_close(tree, dest_h);
897 talloc_free(tmp_ctx);
901 static bool test_ioctl_copy_chunk_bad_key(struct torture_context *torture,
902 struct smb2_tree *tree)
904 struct smb2_handle src_h;
905 struct smb2_handle dest_h;
907 union smb_ioctl ioctl;
908 TALLOC_CTX *tmp_ctx = talloc_new(tree);
909 struct srv_copychunk_copy cc_copy;
910 enum ndr_err_code ndr_ret;
913 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
922 torture_fail(torture, "setup copy chunk error");
925 /* overwrite the resume key with a bogus value */
926 memcpy(cc_copy.source_key, "deadbeefdeadbeefdeadbeef", 24);
928 cc_copy.chunks[0].source_off = 0;
929 cc_copy.chunks[0].target_off = 0;
930 cc_copy.chunks[0].length = 4096;
932 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
934 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
935 torture_assert_ndr_success(torture, ndr_ret,
936 "ndr_push_srv_copychunk_copy");
938 /* Server 2k12 returns NT_STATUS_OBJECT_NAME_NOT_FOUND */
939 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
940 torture_assert_ntstatus_equal(torture, status,
941 NT_STATUS_OBJECT_NAME_NOT_FOUND,
942 "FSCTL_SRV_COPYCHUNK");
944 smb2_util_close(tree, src_h);
945 smb2_util_close(tree, dest_h);
946 talloc_free(tmp_ctx);
950 static bool test_ioctl_copy_chunk_src_is_dest(struct torture_context *torture,
951 struct smb2_tree *tree)
953 struct smb2_handle src_h;
954 struct smb2_handle dest_h;
956 union smb_ioctl ioctl;
957 TALLOC_CTX *tmp_ctx = talloc_new(tree);
958 struct srv_copychunk_copy cc_copy;
959 struct srv_copychunk_rsp cc_rsp;
960 enum ndr_err_code ndr_ret;
963 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
972 torture_fail(torture, "setup copy chunk error");
975 /* the source is also the destination */
976 ioctl.smb2.in.file.handle = src_h;
978 /* non-overlapping */
979 cc_copy.chunks[0].source_off = 0;
980 cc_copy.chunks[0].target_off = 4096;
981 cc_copy.chunks[0].length = 4096;
983 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
985 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
986 torture_assert_ndr_success(torture, ndr_ret,
987 "ndr_push_srv_copychunk_copy");
989 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
990 torture_assert_ntstatus_ok(torture, status,
991 "FSCTL_SRV_COPYCHUNK");
993 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
995 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
996 torture_assert_ndr_success(torture, ndr_ret,
997 "ndr_pull_srv_copychunk_rsp");
999 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1000 1, /* chunks written */
1001 0, /* chunk bytes unsuccessfully written */
1002 4096); /* total bytes written */
1004 torture_fail(torture, "bad copy chunk response data");
1007 ok = check_pattern(torture, tree, tmp_ctx, src_h, 0, 4096, 0);
1009 torture_fail(torture, "inconsistent file data");
1011 ok = check_pattern(torture, tree, tmp_ctx, src_h, 4096, 4096, 0);
1013 torture_fail(torture, "inconsistent file data");
1016 smb2_util_close(tree, src_h);
1017 smb2_util_close(tree, dest_h);
1018 talloc_free(tmp_ctx);
1023 * Test a single-chunk copychunk request, where the source and target ranges
1024 * overlap, and the SourceKey refers to the same target file. E.g:
1028 * File: src_and_dest
1029 * Offset: 0123456789
1034 * FSCTL_SRV_COPYCHUNK(src_and_dest)
1035 * SourceKey = SRV_REQUEST_RESUME_KEY(src_and_dest)
1037 * Chunks[0].SourceOffset = 0
1038 * Chunks[0].TargetOffset = 4
1039 * Chunks[0].Length = 6
1043 * File: src_and_dest
1044 * Offset: 0123456789
1047 * The resultant contents of src_and_dest is dependent on the server's
1048 * copy algorithm. In the above example, the server uses an IO buffer
1049 * large enough to hold the entire six-byte source data before writing
1050 * to TargetOffset. If the server were to use a four-byte IO buffer and
1051 * started reads/writes from the lowest offset, then the two overlapping
1052 * bytes in the above example would be overwritten before being read. The
1053 * resultant file contents would be abcdabcdab.
1055 * Windows 2008r2 appears to use a 2048 byte copy buffer, overlapping bytes
1056 * after this offset are written before being read. Windows 2012 on the
1057 * other hand appears to use a buffer large enough to hold its maximum
1058 * supported chunk size (1M). Samba currently uses a 64k copy buffer by
1059 * default (vfs_cc_state.buf).
1061 * This test uses an 8-byte overlap at 2040-2048, so that it passes against
1062 * Windows 2008, 2012 and Samba servers.
1065 test_ioctl_copy_chunk_src_is_dest_overlap(struct torture_context *torture,
1066 struct smb2_tree *tree)
1068 struct smb2_handle src_h;
1069 struct smb2_handle dest_h;
1071 union smb_ioctl ioctl;
1072 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1073 struct srv_copychunk_copy cc_copy;
1074 struct srv_copychunk_rsp cc_rsp;
1075 enum ndr_err_code ndr_ret;
1078 /* exceed the vfs_default copy buffer */
1079 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1082 SEC_RIGHTS_FILE_ALL,
1084 SEC_RIGHTS_FILE_ALL,
1088 torture_fail(torture, "setup copy chunk error");
1091 /* the source is also the destination */
1092 ioctl.smb2.in.file.handle = src_h;
1094 /* 8 bytes overlap between source and target ranges */
1095 cc_copy.chunks[0].source_off = 0;
1096 cc_copy.chunks[0].target_off = 2048 - 8;
1097 cc_copy.chunks[0].length = 2048;
1099 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1101 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1102 torture_assert_ndr_success(torture, ndr_ret,
1103 "ndr_push_srv_copychunk_copy");
1105 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1106 torture_assert_ntstatus_ok(torture, status,
1107 "FSCTL_SRV_COPYCHUNK");
1109 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1111 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1112 torture_assert_ndr_success(torture, ndr_ret,
1113 "ndr_pull_srv_copychunk_rsp");
1115 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1116 1, /* chunks written */
1117 0, /* chunk bytes unsuccessfully written */
1118 2048); /* total bytes written */
1120 torture_fail(torture, "bad copy chunk response data");
1123 ok = check_pattern(torture, tree, tmp_ctx, src_h, 0, 2048 - 8, 0);
1125 torture_fail(torture, "inconsistent file data");
1127 ok = check_pattern(torture, tree, tmp_ctx, src_h, 2048 - 8, 2048, 0);
1129 torture_fail(torture, "inconsistent file data");
1132 smb2_util_close(tree, src_h);
1133 smb2_util_close(tree, dest_h);
1134 talloc_free(tmp_ctx);
1138 static bool test_ioctl_copy_chunk_bad_access(struct torture_context *torture,
1139 struct smb2_tree *tree)
1141 struct smb2_handle src_h;
1142 struct smb2_handle dest_h;
1144 union smb_ioctl ioctl;
1145 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1146 struct srv_copychunk_copy cc_copy;
1147 enum ndr_err_code ndr_ret;
1150 /* no read permission on src */
1151 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1153 &src_h, 4096, /* fill 4096 byte src file */
1154 SEC_RIGHTS_FILE_WRITE,
1155 &dest_h, 0, /* 0 byte dest file */
1156 SEC_RIGHTS_FILE_ALL,
1160 torture_fail(torture, "setup copy chunk error");
1163 cc_copy.chunks[0].source_off = 0;
1164 cc_copy.chunks[0].target_off = 0;
1165 cc_copy.chunks[0].length = 4096;
1167 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1169 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1170 torture_assert_ndr_success(torture, ndr_ret,
1171 "ndr_push_srv_copychunk_copy");
1173 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1174 torture_assert_ntstatus_equal(torture, status,
1175 NT_STATUS_ACCESS_DENIED,
1176 "FSCTL_SRV_COPYCHUNK");
1178 smb2_util_close(tree, src_h);
1179 smb2_util_close(tree, dest_h);
1181 /* no write permission on dest */
1182 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1184 &src_h, 4096, /* fill 4096 byte src file */
1185 SEC_RIGHTS_FILE_ALL,
1186 &dest_h, 0, /* 0 byte dest file */
1187 (SEC_RIGHTS_FILE_READ
1188 | SEC_RIGHTS_FILE_EXECUTE),
1192 torture_fail(torture, "setup copy chunk error");
1195 cc_copy.chunks[0].source_off = 0;
1196 cc_copy.chunks[0].target_off = 0;
1197 cc_copy.chunks[0].length = 4096;
1199 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1201 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1202 torture_assert_ndr_success(torture, ndr_ret,
1203 "ndr_push_srv_copychunk_copy");
1205 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1206 torture_assert_ntstatus_equal(torture, status,
1207 NT_STATUS_ACCESS_DENIED,
1208 "FSCTL_SRV_COPYCHUNK");
1210 smb2_util_close(tree, src_h);
1211 smb2_util_close(tree, dest_h);
1213 /* no read permission on dest */
1214 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1216 &src_h, 4096, /* fill 4096 byte src file */
1217 SEC_RIGHTS_FILE_ALL,
1218 &dest_h, 0, /* 0 byte dest file */
1219 (SEC_RIGHTS_FILE_WRITE
1220 | SEC_RIGHTS_FILE_EXECUTE),
1224 torture_fail(torture, "setup copy chunk error");
1227 cc_copy.chunks[0].source_off = 0;
1228 cc_copy.chunks[0].target_off = 0;
1229 cc_copy.chunks[0].length = 4096;
1231 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1233 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1234 torture_assert_ndr_success(torture, ndr_ret,
1235 "ndr_push_srv_copychunk_copy");
1238 * FSCTL_SRV_COPYCHUNK requires read permission on dest,
1239 * FSCTL_SRV_COPYCHUNK_WRITE (not supported by Samba) on the other hand
1242 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1243 torture_assert_ntstatus_equal(torture, status,
1244 NT_STATUS_ACCESS_DENIED,
1245 "FSCTL_SRV_COPYCHUNK");
1247 smb2_util_close(tree, src_h);
1248 smb2_util_close(tree, dest_h);
1249 talloc_free(tmp_ctx);
1254 static bool test_ioctl_copy_chunk_src_exceed(struct torture_context *torture,
1255 struct smb2_tree *tree)
1257 struct smb2_handle src_h;
1258 struct smb2_handle dest_h;
1260 union smb_ioctl ioctl;
1261 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1262 struct srv_copychunk_copy cc_copy;
1263 struct srv_copychunk_rsp cc_rsp;
1264 enum ndr_err_code ndr_ret;
1267 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1269 &src_h, 4096, /* fill 4096 byte src file */
1270 SEC_RIGHTS_FILE_ALL,
1271 &dest_h, 0, /* 0 byte dest file */
1272 SEC_RIGHTS_FILE_ALL,
1276 torture_fail(torture, "setup copy chunk error");
1279 /* Request copy where off + length exceeds size of src */
1280 cc_copy.chunks[0].source_off = 1024;
1281 cc_copy.chunks[0].target_off = 0;
1282 cc_copy.chunks[0].length = 4096;
1284 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1286 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1287 torture_assert_ndr_success(torture, ndr_ret,
1288 "ndr_push_srv_copychunk_copy");
1290 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1291 torture_assert_ntstatus_equal(torture, status,
1292 NT_STATUS_INVALID_VIEW_SIZE,
1293 "FSCTL_SRV_COPYCHUNK oversize");
1295 /* Request copy where length exceeds size of src */
1296 cc_copy.chunks[0].source_off = 1024;
1297 cc_copy.chunks[0].target_off = 0;
1298 cc_copy.chunks[0].length = 3072;
1300 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1302 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1303 torture_assert_ndr_success(torture, ndr_ret,
1304 "ndr_push_srv_copychunk_copy");
1306 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1307 torture_assert_ntstatus_ok(torture, status,
1308 "FSCTL_SRV_COPYCHUNK just right");
1310 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1312 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1313 torture_assert_ndr_success(torture, ndr_ret,
1314 "ndr_pull_srv_copychunk_rsp");
1316 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1317 1, /* chunks written */
1318 0, /* chunk bytes unsuccessfully written */
1319 3072); /* total bytes written */
1321 torture_fail(torture, "bad copy chunk response data");
1324 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 3072, 1024);
1326 torture_fail(torture, "inconsistent file data");
1329 smb2_util_close(tree, src_h);
1330 smb2_util_close(tree, dest_h);
1331 talloc_free(tmp_ctx);
1336 test_ioctl_copy_chunk_src_exceed_multi(struct torture_context *torture,
1337 struct smb2_tree *tree)
1339 struct smb2_handle src_h;
1340 struct smb2_handle dest_h;
1342 union smb_ioctl ioctl;
1343 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1344 struct srv_copychunk_copy cc_copy;
1345 struct srv_copychunk_rsp cc_rsp;
1346 enum ndr_err_code ndr_ret;
1349 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1351 &src_h, 8192, /* fill 8192 byte src file */
1352 SEC_RIGHTS_FILE_ALL,
1353 &dest_h, 0, /* 0 byte dest file */
1354 SEC_RIGHTS_FILE_ALL,
1358 torture_fail(torture, "setup copy chunk error");
1361 /* Request copy where off + length exceeds size of src */
1362 cc_copy.chunks[0].source_off = 0;
1363 cc_copy.chunks[0].target_off = 0;
1364 cc_copy.chunks[0].length = 4096;
1366 cc_copy.chunks[1].source_off = 4096;
1367 cc_copy.chunks[1].target_off = 4096;
1368 cc_copy.chunks[1].length = 8192;
1370 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1372 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1373 torture_assert_ndr_success(torture, ndr_ret,
1374 "ndr_push_srv_copychunk_copy");
1376 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1377 torture_assert_ntstatus_equal(torture, status,
1378 NT_STATUS_INVALID_VIEW_SIZE,
1379 "FSCTL_SRV_COPYCHUNK oversize");
1380 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1382 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1383 torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
1385 /* first chunk should still be written */
1386 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1387 1, /* chunks written */
1388 0, /* chunk bytes unsuccessfully written */
1389 4096); /* total bytes written */
1391 torture_fail(torture, "bad copy chunk response data");
1393 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
1395 torture_fail(torture, "inconsistent file data");
1398 smb2_util_close(tree, src_h);
1399 smb2_util_close(tree, dest_h);
1400 talloc_free(tmp_ctx);
1404 static bool test_ioctl_copy_chunk_sparse_dest(struct torture_context *torture,
1405 struct smb2_tree *tree)
1407 struct smb2_handle src_h;
1408 struct smb2_handle dest_h;
1410 union smb_ioctl ioctl;
1412 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1413 struct srv_copychunk_copy cc_copy;
1414 struct srv_copychunk_rsp cc_rsp;
1415 enum ndr_err_code ndr_ret;
1419 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1421 &src_h, 4096, /* fill 4096 byte src file */
1422 SEC_RIGHTS_FILE_ALL,
1423 &dest_h, 0, /* 0 byte dest file */
1424 SEC_RIGHTS_FILE_ALL,
1428 torture_fail(torture, "setup copy chunk error");
1431 /* copy all src file data (via a single chunk desc) */
1432 cc_copy.chunks[0].source_off = 0;
1433 cc_copy.chunks[0].target_off = 4096;
1434 cc_copy.chunks[0].length = 4096;
1436 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1438 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1439 torture_assert_ndr_success(torture, ndr_ret,
1440 "ndr_push_srv_copychunk_copy");
1442 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1443 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
1445 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1447 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1448 torture_assert_ndr_success(torture, ndr_ret,
1449 "ndr_pull_srv_copychunk_rsp");
1451 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1452 1, /* chunks written */
1453 0, /* chunk bytes unsuccessfully written */
1454 4096); /* total bytes written */
1456 torture_fail(torture, "bad copy chunk response data");
1459 /* check for zeros in first 4k */
1461 r.in.file.handle = dest_h;
1464 status = smb2_read(tree, tmp_ctx, &r);
1465 torture_assert_ntstatus_ok(torture, status, "read");
1467 torture_assert_u64_equal(torture, r.out.data.length, 4096,
1468 "read data len mismatch");
1470 for (i = 0; i < 4096; i++) {
1471 torture_assert(torture, (r.out.data.data[i] == 0),
1472 "sparse did not pass class");
1475 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 4096, 4096, 0);
1477 torture_fail(torture, "inconsistent file data");
1480 smb2_util_close(tree, src_h);
1481 smb2_util_close(tree, dest_h);
1482 talloc_free(tmp_ctx);
1487 * set the ioctl MaxOutputResponse size to less than
1488 * sizeof(struct srv_copychunk_rsp)
1490 static bool test_ioctl_copy_chunk_max_output_sz(struct torture_context *torture,
1491 struct smb2_tree *tree)
1493 struct smb2_handle src_h;
1494 struct smb2_handle dest_h;
1496 union smb_ioctl ioctl;
1497 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1498 struct srv_copychunk_copy cc_copy;
1499 enum ndr_err_code ndr_ret;
1502 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1504 &src_h, 4096, /* fill 4096 byte src file */
1505 SEC_RIGHTS_FILE_ALL,
1506 &dest_h, 0, /* 0 byte dest file */
1507 SEC_RIGHTS_FILE_ALL,
1511 torture_fail(torture, "setup copy chunk error");
1514 cc_copy.chunks[0].source_off = 0;
1515 cc_copy.chunks[0].target_off = 0;
1516 cc_copy.chunks[0].length = 4096;
1517 /* req is valid, but use undersize max_response_size */
1518 ioctl.smb2.in.max_response_size = sizeof(struct srv_copychunk_rsp) - 1;
1520 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1522 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1523 torture_assert_ndr_success(torture, ndr_ret,
1524 "ndr_push_srv_copychunk_copy");
1526 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1527 torture_assert_ntstatus_equal(torture, status,
1528 NT_STATUS_INVALID_PARAMETER,
1529 "FSCTL_SRV_COPYCHUNK");
1531 smb2_util_close(tree, src_h);
1532 smb2_util_close(tree, dest_h);
1533 talloc_free(tmp_ctx);
1537 static NTSTATUS test_ioctl_compress_fs_supported(struct torture_context *torture,
1538 struct smb2_tree *tree,
1539 TALLOC_CTX *mem_ctx,
1540 struct smb2_handle *fh,
1541 bool *compress_support)
1544 union smb_fsinfo info;
1547 info.generic.level = RAW_QFS_ATTRIBUTE_INFORMATION;
1548 info.generic.handle = *fh;
1549 status = smb2_getinfo_fs(tree, tree, &info);
1550 if (!NT_STATUS_IS_OK(status)) {
1554 if (info.attribute_info.out.fs_attr & FILE_FILE_COMPRESSION) {
1555 *compress_support = true;
1557 *compress_support = false;
1559 return NT_STATUS_OK;
1562 static NTSTATUS test_ioctl_compress_get(struct torture_context *torture,
1563 TALLOC_CTX *mem_ctx,
1564 struct smb2_tree *tree,
1565 struct smb2_handle fh,
1566 uint16_t *_compression_fmt)
1568 union smb_ioctl ioctl;
1569 struct compression_state cmpr_state;
1570 enum ndr_err_code ndr_ret;
1574 ioctl.smb2.level = RAW_IOCTL_SMB2;
1575 ioctl.smb2.in.file.handle = fh;
1576 ioctl.smb2.in.function = FSCTL_GET_COMPRESSION;
1577 ioctl.smb2.in.max_response_size = sizeof(struct compression_state);
1578 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
1580 status = smb2_ioctl(tree, mem_ctx, &ioctl.smb2);
1581 if (!NT_STATUS_IS_OK(status)) {
1585 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, mem_ctx,
1587 (ndr_pull_flags_fn_t)ndr_pull_compression_state);
1589 if (ndr_ret != NDR_ERR_SUCCESS) {
1590 return NT_STATUS_INTERNAL_ERROR;
1593 *_compression_fmt = cmpr_state.format;
1594 return NT_STATUS_OK;
1597 static NTSTATUS test_ioctl_compress_set(struct torture_context *torture,
1598 TALLOC_CTX *mem_ctx,
1599 struct smb2_tree *tree,
1600 struct smb2_handle fh,
1601 uint16_t compression_fmt)
1603 union smb_ioctl ioctl;
1604 struct compression_state cmpr_state;
1605 enum ndr_err_code ndr_ret;
1609 ioctl.smb2.level = RAW_IOCTL_SMB2;
1610 ioctl.smb2.in.file.handle = fh;
1611 ioctl.smb2.in.function = FSCTL_SET_COMPRESSION;
1612 ioctl.smb2.in.max_response_size = 0;
1613 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
1615 cmpr_state.format = compression_fmt;
1616 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, mem_ctx,
1618 (ndr_push_flags_fn_t)ndr_push_compression_state);
1619 if (ndr_ret != NDR_ERR_SUCCESS) {
1620 return NT_STATUS_INTERNAL_ERROR;
1623 status = smb2_ioctl(tree, mem_ctx, &ioctl.smb2);
1627 static bool test_ioctl_compress_file_flag(struct torture_context *torture,
1628 struct smb2_tree *tree)
1630 struct smb2_handle fh;
1632 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1634 uint16_t compression_fmt;
1636 ok = test_setup_create_fill(torture, tree, tmp_ctx,
1637 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
1638 FILE_ATTRIBUTE_NORMAL);
1639 torture_assert(torture, ok, "setup compression file");
1641 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
1643 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
1645 smb2_util_close(tree, fh);
1646 torture_skip(torture, "FS compression not supported\n");
1649 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1651 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1653 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
1654 "initial compression state not NONE");
1656 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
1657 COMPRESSION_FORMAT_DEFAULT);
1658 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
1660 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1662 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1664 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
1665 "invalid compression state after set");
1667 smb2_util_close(tree, fh);
1668 talloc_free(tmp_ctx);
1672 static bool test_ioctl_compress_dir_inherit(struct torture_context *torture,
1673 struct smb2_tree *tree)
1675 struct smb2_handle dirh;
1676 struct smb2_handle fh;
1678 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1679 uint16_t compression_fmt;
1681 char path_buf[PATH_MAX];
1683 smb2_deltree(tree, DNAME);
1684 status = smb2_util_mkdir(tree, DNAME);
1685 torture_assert_ntstatus_ok(torture, status, "mkdir");
1687 ok = test_setup_create_fill(torture, tree, tmp_ctx,
1688 DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL,
1689 FILE_ATTRIBUTE_DIRECTORY);
1690 torture_assert(torture, ok, "setup compression directory");
1692 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &dirh,
1694 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
1696 smb2_util_close(tree, dirh);
1697 smb2_deltree(tree, DNAME);
1698 torture_skip(torture, "FS compression not supported\n");
1701 /* set compression on base share, then check for file inheritance */
1702 status = test_ioctl_compress_set(torture, tmp_ctx, tree, dirh,
1703 COMPRESSION_FORMAT_LZNT1);
1704 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
1706 status = test_ioctl_compress_get(torture, tmp_ctx, tree, dirh,
1708 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1710 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
1711 "invalid compression state after set");
1713 snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME);
1714 ok = test_setup_create_fill(torture, tree, tmp_ctx,
1715 path_buf, &fh, 4096, SEC_RIGHTS_FILE_ALL,
1716 FILE_ATTRIBUTE_NORMAL);
1717 torture_assert(torture, ok, "setup compression file");
1719 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1721 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1723 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
1724 "compression attr not inherited by new file");
1726 /* check compressed data is consistent */
1727 ok = check_pattern(torture, tree, tmp_ctx, fh, 0, 4096, 0);
1729 /* disable dir compression attr, file should remain compressed */
1730 status = test_ioctl_compress_set(torture, tmp_ctx, tree, dirh,
1731 COMPRESSION_FORMAT_NONE);
1732 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
1734 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1736 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1738 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
1739 "file compression attr removed after dir change");
1740 smb2_util_close(tree, fh);
1742 /* new files should no longer inherit compression attr */
1743 snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME2);
1744 ok = test_setup_create_fill(torture, tree, tmp_ctx,
1745 path_buf, &fh, 0, SEC_RIGHTS_FILE_ALL,
1746 FILE_ATTRIBUTE_NORMAL);
1747 torture_assert(torture, ok, "setup file");
1749 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1751 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1753 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
1754 "compression attr present on new file");
1756 smb2_util_close(tree, fh);
1757 smb2_util_close(tree, dirh);
1758 smb2_deltree(tree, DNAME);
1759 talloc_free(tmp_ctx);
1763 static bool test_ioctl_compress_invalid_format(struct torture_context *torture,
1764 struct smb2_tree *tree)
1766 struct smb2_handle fh;
1768 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1770 uint16_t compression_fmt;
1772 ok = test_setup_create_fill(torture, tree, tmp_ctx,
1773 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
1774 FILE_ATTRIBUTE_NORMAL);
1775 torture_assert(torture, ok, "setup compression file");
1777 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
1779 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
1781 smb2_util_close(tree, fh);
1782 torture_skip(torture, "FS compression not supported\n");
1785 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
1786 0x0042); /* bogus */
1787 torture_assert_ntstatus_equal(torture, status,
1788 NT_STATUS_INVALID_PARAMETER,
1789 "invalid FSCTL_SET_COMPRESSION");
1791 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1793 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1795 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
1796 "initial compression state not NONE");
1798 smb2_util_close(tree, fh);
1799 talloc_free(tmp_ctx);
1803 static bool test_ioctl_compress_invalid_buf(struct torture_context *torture,
1804 struct smb2_tree *tree)
1806 struct smb2_handle fh;
1808 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1810 union smb_ioctl ioctl;
1812 ok = test_setup_create_fill(torture, tree, tmp_ctx,
1813 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
1814 FILE_ATTRIBUTE_NORMAL);
1815 torture_assert(torture, ok, "setup compression file");
1817 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
1819 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
1821 smb2_util_close(tree, fh);
1822 torture_skip(torture, "FS compression not supported\n");
1826 ioctl.smb2.level = RAW_IOCTL_SMB2;
1827 ioctl.smb2.in.file.handle = fh;
1828 ioctl.smb2.in.function = FSCTL_GET_COMPRESSION;
1829 ioctl.smb2.in.max_response_size = 0; /* no room for rsp data */
1830 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
1832 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1833 /* expect Server 2k12 response status */
1834 torture_assert_ntstatus_equal(torture, status,
1835 NT_STATUS_INVALID_USER_BUFFER,
1836 "invalid FSCTL_SET_COMPRESSION");
1838 smb2_util_close(tree, fh);
1839 talloc_free(tmp_ctx);
1843 static bool test_ioctl_compress_query_file_attr(struct torture_context *torture,
1844 struct smb2_tree *tree)
1846 struct smb2_handle fh;
1847 union smb_fileinfo io;
1849 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1852 ok = test_setup_create_fill(torture, tree, tmp_ctx,
1853 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
1854 FILE_ATTRIBUTE_NORMAL);
1855 torture_assert(torture, ok, "setup compression file");
1857 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
1859 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
1861 smb2_util_close(tree, fh);
1862 torture_skip(torture, "FS compression not supported\n");
1865 status = smb2_getinfo_file(tree, tmp_ctx, &io);
1867 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
1868 io.generic.in.file.handle = fh;
1869 status = smb2_getinfo_file(tree, tmp_ctx, &io);
1870 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
1872 torture_assert(torture,
1873 ((io.all_info2.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
1874 "compression attr before set");
1876 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
1877 COMPRESSION_FORMAT_DEFAULT);
1878 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
1881 io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
1882 io.generic.in.file.handle = fh;
1883 status = smb2_getinfo_file(tree, tmp_ctx, &io);
1884 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
1886 torture_assert(torture,
1887 (io.basic_info.out.attrib & FILE_ATTRIBUTE_COMPRESSED),
1888 "no compression attr after set");
1890 smb2_util_close(tree, fh);
1891 talloc_free(tmp_ctx);
1896 basic testing of SMB2 ioctls
1898 struct torture_suite *torture_smb2_ioctl_init(void)
1900 struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "ioctl");
1902 torture_suite_add_1smb2_test(suite, "shadow_copy",
1903 test_ioctl_get_shadow_copy);
1904 torture_suite_add_1smb2_test(suite, "req_resume_key",
1905 test_ioctl_req_resume_key);
1906 torture_suite_add_1smb2_test(suite, "copy_chunk_simple",
1907 test_ioctl_copy_chunk_simple);
1908 torture_suite_add_1smb2_test(suite, "copy_chunk_multi",
1909 test_ioctl_copy_chunk_multi);
1910 torture_suite_add_1smb2_test(suite, "copy_chunk_tiny",
1911 test_ioctl_copy_chunk_tiny);
1912 torture_suite_add_1smb2_test(suite, "copy_chunk_overwrite",
1913 test_ioctl_copy_chunk_over);
1914 torture_suite_add_1smb2_test(suite, "copy_chunk_append",
1915 test_ioctl_copy_chunk_append);
1916 torture_suite_add_1smb2_test(suite, "copy_chunk_limits",
1917 test_ioctl_copy_chunk_limits);
1918 torture_suite_add_1smb2_test(suite, "copy_chunk_src_lock",
1919 test_ioctl_copy_chunk_src_lck);
1920 torture_suite_add_1smb2_test(suite, "copy_chunk_dest_lock",
1921 test_ioctl_copy_chunk_dest_lck);
1922 torture_suite_add_1smb2_test(suite, "copy_chunk_bad_key",
1923 test_ioctl_copy_chunk_bad_key);
1924 torture_suite_add_1smb2_test(suite, "copy_chunk_src_is_dest",
1925 test_ioctl_copy_chunk_src_is_dest);
1926 torture_suite_add_1smb2_test(suite, "copy_chunk_src_is_dest_overlap",
1927 test_ioctl_copy_chunk_src_is_dest_overlap);
1928 torture_suite_add_1smb2_test(suite, "copy_chunk_bad_access",
1929 test_ioctl_copy_chunk_bad_access);
1930 torture_suite_add_1smb2_test(suite, "copy_chunk_src_exceed",
1931 test_ioctl_copy_chunk_src_exceed);
1932 torture_suite_add_1smb2_test(suite, "copy_chunk_src_exceed_multi",
1933 test_ioctl_copy_chunk_src_exceed_multi);
1934 torture_suite_add_1smb2_test(suite, "copy_chunk_sparse_dest",
1935 test_ioctl_copy_chunk_sparse_dest);
1936 torture_suite_add_1smb2_test(suite, "copy_chunk_max_output_sz",
1937 test_ioctl_copy_chunk_max_output_sz);
1938 torture_suite_add_1smb2_test(suite, "compress_file_flag",
1939 test_ioctl_compress_file_flag);
1940 torture_suite_add_1smb2_test(suite, "compress_dir_inherit",
1941 test_ioctl_compress_dir_inherit);
1942 torture_suite_add_1smb2_test(suite, "compress_invalid_format",
1943 test_ioctl_compress_invalid_format);
1944 torture_suite_add_1smb2_test(suite, "compress_invalid_buf",
1945 test_ioctl_compress_invalid_buf);
1946 torture_suite_add_1smb2_test(suite, "compress_query_file_attr",
1947 test_ioctl_compress_query_file_attr);
1949 suite->description = talloc_strdup(suite, "SMB2-IOCTL tests");