s3:smbd: s/struct timed_event/struct tevent_timer
[Samba/gebeck_regimport.git] / source4 / torture / smb2 / ioctl.c
blob501b233d5b5b05ea554e61bf739d9ca1bd08ba75
1 /*
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/>.
22 #include "includes.h"
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)
39 struct smb2_handle h;
40 uint8_t buf[100];
41 NTSTATUS status;
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");
50 ZERO_ARRAY(buf);
51 status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf));
52 torture_assert_ntstatus_ok(torture, status, "write");
54 ZERO_STRUCT(ioctl);
55 ioctl.smb2.level = RAW_IOCTL_SMB2;
56 ioctl.smb2.in.file.handle = h;
57 ioctl.smb2.in.function = FSCTL_SRV_ENUM_SNAPS;
58 ioctl.smb2.in.max_response_size = 16;
59 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
61 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
62 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)
63 || NT_STATUS_EQUAL(status, NT_STATUS_INVALID_DEVICE_REQUEST)) {
64 torture_skip(torture, "FSCTL_SRV_ENUM_SNAPS not supported\n");
66 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_ENUM_SNAPS");
68 return true;
72 basic testing of the SMB2 server side copy ioctls
74 static bool test_ioctl_req_resume_key(struct torture_context *torture,
75 struct smb2_tree *tree)
77 struct smb2_handle h;
78 uint8_t buf[100];
79 NTSTATUS status;
80 union smb_ioctl ioctl;
81 TALLOC_CTX *tmp_ctx = talloc_new(tree);
82 struct req_resume_key_rsp res_key;
83 enum ndr_err_code ndr_ret;
85 smb2_util_unlink(tree, FNAME);
87 status = torture_smb2_testfile(tree, FNAME, &h);
88 torture_assert_ntstatus_ok(torture, status, "create write");
90 ZERO_ARRAY(buf);
91 status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf));
92 torture_assert_ntstatus_ok(torture, status, "write");
94 ZERO_STRUCT(ioctl);
95 ioctl.smb2.level = RAW_IOCTL_SMB2;
96 ioctl.smb2.in.file.handle = h;
97 ioctl.smb2.in.function = FSCTL_SRV_REQUEST_RESUME_KEY;
98 ioctl.smb2.in.max_response_size = 32;
99 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
101 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
102 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_REQUEST_RESUME_KEY");
104 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, &res_key,
105 (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
106 torture_assert_ndr_success(torture, ndr_ret,
107 "ndr_pull_req_resume_key_rsp");
109 ndr_print_debug((ndr_print_fn_t)ndr_print_req_resume_key_rsp, "yo", &res_key);
111 talloc_free(tmp_ctx);
112 return true;
115 static uint64_t patt_hash(uint64_t off)
117 return off;
120 static bool check_pattern(struct torture_context *torture,
121 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
122 struct smb2_handle h, uint64_t off, uint64_t len,
123 uint64_t patt_off)
125 uint64_t i;
126 struct smb2_read r;
127 NTSTATUS status;
129 ZERO_STRUCT(r);
130 r.in.file.handle = h;
131 r.in.length = len;
132 r.in.offset = off;
133 status = smb2_read(tree, mem_ctx, &r);
134 torture_assert_ntstatus_ok(torture, status, "read");
136 torture_assert_u64_equal(torture, r.out.data.length, len,
137 "read data len mismatch");
139 for (i = 0; i <= len - 8; i += 8, patt_off += 8) {
140 uint64_t data = BVAL(r.out.data.data, i);
141 torture_assert_u64_equal(torture, data, patt_hash(patt_off),
142 talloc_asprintf(torture, "read data "
143 "pattern bad at %llu\n",
144 (unsigned long long)i));
147 talloc_free(r.out.data.data);
148 return true;
151 static bool test_setup_copy_chunk(struct torture_context *torture,
152 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
153 uint32_t nchunks,
154 struct smb2_handle *src_h,
155 uint64_t src_size,
156 uint32_t src_desired_access,
157 struct smb2_handle *dest_h,
158 uint64_t dest_size,
159 uint32_t dest_desired_access,
160 struct srv_copychunk_copy *cc_copy,
161 union smb_ioctl *ioctl)
163 struct req_resume_key_rsp res_key;
164 struct smb2_create io;
165 NTSTATUS status;
166 enum ndr_err_code ndr_ret;
167 uint64_t i;
168 uint8_t *buf = talloc_zero_size(mem_ctx, MAX(src_size, dest_size));
169 torture_assert(torture, (buf != NULL), "no memory for file data buf");
171 smb2_util_unlink(tree, FNAME);
172 smb2_util_unlink(tree, FNAME2);
174 ZERO_STRUCT(io);
175 io.in.desired_access = src_desired_access;
176 io.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
177 io.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
178 io.in.share_access =
179 NTCREATEX_SHARE_ACCESS_DELETE|
180 NTCREATEX_SHARE_ACCESS_READ|
181 NTCREATEX_SHARE_ACCESS_WRITE;
182 io.in.fname = FNAME;
184 status = smb2_create(tree, mem_ctx, &io);
185 torture_assert_ntstatus_ok(torture, status, "src create");
187 *src_h = io.out.file.handle;
189 if (src_size > 0) {
190 uint64_t cur_off = 0;
191 for (i = 0; i <= src_size - 8; i += 8) {
192 SBVAL(buf, i, patt_hash(i));
194 while (src_size > 0) {
195 uint64_t io_sz = MIN(1024 * 1024, src_size);
196 status = smb2_util_write(tree, *src_h,
197 buf + cur_off, cur_off, io_sz);
198 torture_assert_ntstatus_ok(torture, status, "src write");
200 src_size -= io_sz;
201 cur_off += io_sz;
205 ZERO_STRUCT(io);
206 io.in.desired_access = dest_desired_access;
207 io.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
208 io.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
209 io.in.share_access =
210 NTCREATEX_SHARE_ACCESS_DELETE|
211 NTCREATEX_SHARE_ACCESS_READ|
212 NTCREATEX_SHARE_ACCESS_WRITE;
213 io.in.fname = FNAME2;
215 status = smb2_create(tree, mem_ctx, &io);
216 torture_assert_ntstatus_ok(torture, status, "dest create");
218 *dest_h = io.out.file.handle;
220 if (dest_size > 0) {
221 uint64_t cur_off = 0;
222 for (i = 0; i <= dest_size - 8; i += 8) {
223 SBVAL(buf, i, patt_hash(i));
225 while (dest_size > 0) {
226 uint64_t io_sz = MIN(1024 * 1024, dest_size);
227 status = smb2_util_write(tree, *dest_h,
228 buf + cur_off, cur_off, io_sz);
229 torture_assert_ntstatus_ok(torture, status, "dest write");
231 dest_size -= io_sz;
232 cur_off += io_sz;
236 ZERO_STRUCTPN(ioctl);
237 ioctl->smb2.level = RAW_IOCTL_SMB2;
238 ioctl->smb2.in.file.handle = *src_h;
239 ioctl->smb2.in.function = FSCTL_SRV_REQUEST_RESUME_KEY;
240 /* Allow for Key + ContextLength + Context */
241 ioctl->smb2.in.max_response_size = 32;
242 ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
244 status = smb2_ioctl(tree, mem_ctx, &ioctl->smb2);
245 torture_assert_ntstatus_ok(torture, status,
246 "FSCTL_SRV_REQUEST_RESUME_KEY");
249 ndr_ret = ndr_pull_struct_blob(&ioctl->smb2.out.out, mem_ctx, &res_key,
250 (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
252 torture_assert_ndr_success(torture, ndr_ret,
253 "ndr_pull_req_resume_key_rsp");
255 ZERO_STRUCTPN(ioctl);
256 ioctl->smb2.level = RAW_IOCTL_SMB2;
257 ioctl->smb2.in.file.handle = *dest_h;
258 ioctl->smb2.in.function = FSCTL_SRV_COPYCHUNK;
259 ioctl->smb2.in.max_response_size = sizeof(struct srv_copychunk_rsp);
260 ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
262 ZERO_STRUCTPN(cc_copy);
263 memcpy(cc_copy->source_key, res_key.resume_key, ARRAY_SIZE(cc_copy->source_key));
264 cc_copy->chunk_count = nchunks;
265 cc_copy->chunks = talloc_zero_array(mem_ctx, struct srv_copychunk, nchunks);
266 torture_assert(torture, (cc_copy->chunks != NULL), "no memory for chunks");
268 return true;
272 static bool check_copy_chunk_rsp(struct torture_context *torture,
273 struct srv_copychunk_rsp *cc_rsp,
274 uint32_t ex_chunks_written,
275 uint32_t ex_chunk_bytes_written,
276 uint32_t ex_total_bytes_written)
278 torture_assert_int_equal(torture, cc_rsp->chunks_written,
279 ex_chunks_written, "num chunks");
280 torture_assert_int_equal(torture, cc_rsp->chunk_bytes_written,
281 ex_chunk_bytes_written, "chunk bytes written");
282 torture_assert_int_equal(torture, cc_rsp->total_bytes_written,
283 ex_total_bytes_written, "chunk total bytes");
284 return true;
287 static bool test_ioctl_copy_chunk_simple(struct torture_context *torture,
288 struct smb2_tree *tree)
290 struct smb2_handle src_h;
291 struct smb2_handle dest_h;
292 NTSTATUS status;
293 union smb_ioctl ioctl;
294 TALLOC_CTX *tmp_ctx = talloc_new(tree);
295 struct srv_copychunk_copy cc_copy;
296 struct srv_copychunk_rsp cc_rsp;
297 enum ndr_err_code ndr_ret;
298 bool ok;
300 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
301 1, /* 1 chunk */
302 &src_h, 4096, /* fill 4096 byte src file */
303 SEC_RIGHTS_FILE_ALL,
304 &dest_h, 0, /* 0 byte dest file */
305 SEC_RIGHTS_FILE_ALL,
306 &cc_copy,
307 &ioctl);
308 if (!ok) {
309 torture_fail(torture, "setup copy chunk error");
312 /* copy all src file data (via a single chunk desc) */
313 cc_copy.chunks[0].source_off = 0;
314 cc_copy.chunks[0].target_off = 0;
315 cc_copy.chunks[0].length = 4096;
317 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
318 &cc_copy,
319 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
320 torture_assert_ndr_success(torture, ndr_ret,
321 "ndr_push_srv_copychunk_copy");
323 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
324 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
326 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
327 &cc_rsp,
328 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
329 torture_assert_ndr_success(torture, ndr_ret,
330 "ndr_pull_srv_copychunk_rsp");
332 ok = check_copy_chunk_rsp(torture, &cc_rsp,
333 1, /* chunks written */
334 0, /* chunk bytes unsuccessfully written */
335 4096); /* total bytes written */
336 if (!ok) {
337 torture_fail(torture, "bad copy chunk response data");
340 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
341 if (!ok) {
342 torture_fail(torture, "inconsistent file data");
345 smb2_util_close(tree, src_h);
346 smb2_util_close(tree, dest_h);
347 talloc_free(tmp_ctx);
348 return true;
351 static bool test_ioctl_copy_chunk_multi(struct torture_context *torture,
352 struct smb2_tree *tree)
354 struct smb2_handle src_h;
355 struct smb2_handle dest_h;
356 NTSTATUS status;
357 union smb_ioctl ioctl;
358 TALLOC_CTX *tmp_ctx = talloc_new(tree);
359 struct srv_copychunk_copy cc_copy;
360 struct srv_copychunk_rsp cc_rsp;
361 enum ndr_err_code ndr_ret;
362 bool ok;
364 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
365 2, /* chunks */
366 &src_h, 8192, /* src file */
367 SEC_RIGHTS_FILE_ALL,
368 &dest_h, 0, /* dest file */
369 SEC_RIGHTS_FILE_ALL,
370 &cc_copy,
371 &ioctl);
372 if (!ok) {
373 torture_fail(torture, "setup copy chunk error");
376 /* copy all src file data via two chunks */
377 cc_copy.chunks[0].source_off = 0;
378 cc_copy.chunks[0].target_off = 0;
379 cc_copy.chunks[0].length = 4096;
381 cc_copy.chunks[1].source_off = 4096;
382 cc_copy.chunks[1].target_off = 4096;
383 cc_copy.chunks[1].length = 4096;
385 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
386 &cc_copy,
387 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
388 torture_assert_ndr_success(torture, ndr_ret,
389 "ndr_push_srv_copychunk_copy");
391 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
392 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
394 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
395 &cc_rsp,
396 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
397 torture_assert_ndr_success(torture, ndr_ret,
398 "ndr_pull_srv_copychunk_rsp");
400 ok = check_copy_chunk_rsp(torture, &cc_rsp,
401 2, /* chunks written */
402 0, /* chunk bytes unsuccessfully written */
403 8192); /* total bytes written */
404 if (!ok) {
405 torture_fail(torture, "bad copy chunk response data");
408 smb2_util_close(tree, src_h);
409 smb2_util_close(tree, dest_h);
410 talloc_free(tmp_ctx);
411 return true;
414 static bool test_ioctl_copy_chunk_tiny(struct torture_context *torture,
415 struct smb2_tree *tree)
417 struct smb2_handle src_h;
418 struct smb2_handle dest_h;
419 NTSTATUS status;
420 union smb_ioctl ioctl;
421 TALLOC_CTX *tmp_ctx = talloc_new(tree);
422 struct srv_copychunk_copy cc_copy;
423 struct srv_copychunk_rsp cc_rsp;
424 enum ndr_err_code ndr_ret;
425 bool ok;
427 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
428 2, /* chunks */
429 &src_h, 100, /* src file */
430 SEC_RIGHTS_FILE_ALL,
431 &dest_h, 0, /* dest file */
432 SEC_RIGHTS_FILE_ALL,
433 &cc_copy,
434 &ioctl);
435 if (!ok) {
436 torture_fail(torture, "setup copy chunk error");
439 /* copy all src file data via two chunks, sub block size chunks */
440 cc_copy.chunks[0].source_off = 0;
441 cc_copy.chunks[0].target_off = 0;
442 cc_copy.chunks[0].length = 50;
444 cc_copy.chunks[1].source_off = 50;
445 cc_copy.chunks[1].target_off = 50;
446 cc_copy.chunks[1].length = 50;
448 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
449 &cc_copy,
450 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
451 torture_assert_ndr_success(torture, ndr_ret,
452 "ndr_push_srv_copychunk_copy");
454 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
455 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
457 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
458 &cc_rsp,
459 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
460 torture_assert_ndr_success(torture, ndr_ret,
461 "ndr_pull_srv_copychunk_rsp");
463 ok = check_copy_chunk_rsp(torture, &cc_rsp,
464 2, /* chunks written */
465 0, /* chunk bytes unsuccessfully written */
466 100); /* total bytes written */
467 if (!ok) {
468 torture_fail(torture, "bad copy chunk response data");
471 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 100, 0);
472 if (!ok) {
473 torture_fail(torture, "inconsistent file data");
476 smb2_util_close(tree, src_h);
477 smb2_util_close(tree, dest_h);
478 talloc_free(tmp_ctx);
479 return true;
482 static bool test_ioctl_copy_chunk_over(struct torture_context *torture,
483 struct smb2_tree *tree)
485 struct smb2_handle src_h;
486 struct smb2_handle dest_h;
487 NTSTATUS status;
488 union smb_ioctl ioctl;
489 TALLOC_CTX *tmp_ctx = talloc_new(tree);
490 struct srv_copychunk_copy cc_copy;
491 struct srv_copychunk_rsp cc_rsp;
492 enum ndr_err_code ndr_ret;
493 bool ok;
495 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
496 2, /* chunks */
497 &src_h, 8192, /* src file */
498 SEC_RIGHTS_FILE_ALL,
499 &dest_h, 4096, /* dest file */
500 SEC_RIGHTS_FILE_ALL,
501 &cc_copy,
502 &ioctl);
503 if (!ok) {
504 torture_fail(torture, "setup copy chunk error");
507 /* first chunk overwrites existing dest data */
508 cc_copy.chunks[0].source_off = 0;
509 cc_copy.chunks[0].target_off = 0;
510 cc_copy.chunks[0].length = 4096;
512 /* second chunk overwrites the first */
513 cc_copy.chunks[1].source_off = 4096;
514 cc_copy.chunks[1].target_off = 0;
515 cc_copy.chunks[1].length = 4096;
517 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
518 &cc_copy,
519 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
520 torture_assert_ndr_success(torture, ndr_ret,
521 "ndr_push_srv_copychunk_copy");
523 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
524 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
526 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
527 &cc_rsp,
528 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
529 torture_assert_ndr_success(torture, ndr_ret,
530 "ndr_pull_srv_copychunk_rsp");
532 ok = check_copy_chunk_rsp(torture, &cc_rsp,
533 2, /* chunks written */
534 0, /* chunk bytes unsuccessfully written */
535 8192); /* total bytes written */
536 if (!ok) {
537 torture_fail(torture, "bad copy chunk response data");
540 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 4096);
541 if (!ok) {
542 torture_fail(torture, "inconsistent file data");
545 smb2_util_close(tree, src_h);
546 smb2_util_close(tree, dest_h);
547 talloc_free(tmp_ctx);
548 return true;
551 static bool test_ioctl_copy_chunk_append(struct torture_context *torture,
552 struct smb2_tree *tree)
554 struct smb2_handle src_h;
555 struct smb2_handle dest_h;
556 NTSTATUS status;
557 union smb_ioctl ioctl;
558 TALLOC_CTX *tmp_ctx = talloc_new(tree);
559 struct srv_copychunk_copy cc_copy;
560 struct srv_copychunk_rsp cc_rsp;
561 enum ndr_err_code ndr_ret;
562 bool ok;
564 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
565 2, /* chunks */
566 &src_h, 4096, /* src file */
567 SEC_RIGHTS_FILE_ALL,
568 &dest_h, 0, /* dest file */
569 SEC_RIGHTS_FILE_ALL,
570 &cc_copy,
571 &ioctl);
572 if (!ok) {
573 torture_fail(torture, "setup copy chunk error");
576 cc_copy.chunks[0].source_off = 0;
577 cc_copy.chunks[0].target_off = 0;
578 cc_copy.chunks[0].length = 4096;
580 /* second chunk appends the same data to the first */
581 cc_copy.chunks[1].source_off = 0;
582 cc_copy.chunks[1].target_off = 4096;
583 cc_copy.chunks[1].length = 4096;
585 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
586 &cc_copy,
587 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
588 torture_assert_ndr_success(torture, ndr_ret,
589 "ndr_push_srv_copychunk_copy");
591 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
592 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
594 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
595 &cc_rsp,
596 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
597 torture_assert_ndr_success(torture, ndr_ret,
598 "ndr_pull_srv_copychunk_rsp");
600 ok = check_copy_chunk_rsp(torture, &cc_rsp,
601 2, /* chunks written */
602 0, /* chunk bytes unsuccessfully written */
603 8192); /* total bytes written */
604 if (!ok) {
605 torture_fail(torture, "bad copy chunk response data");
608 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
609 if (!ok) {
610 torture_fail(torture, "inconsistent file data");
613 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 4096, 4096, 0);
614 if (!ok) {
615 torture_fail(torture, "inconsistent file data");
618 smb2_util_close(tree, src_h);
619 smb2_util_close(tree, dest_h);
620 talloc_free(tmp_ctx);
621 return true;
624 static bool test_ioctl_copy_chunk_limits(struct torture_context *torture,
625 struct smb2_tree *tree)
627 struct smb2_handle src_h;
628 struct smb2_handle dest_h;
629 NTSTATUS status;
630 union smb_ioctl ioctl;
631 TALLOC_CTX *tmp_ctx = talloc_new(tree);
632 struct srv_copychunk_copy cc_copy;
633 struct srv_copychunk_rsp cc_rsp;
634 enum ndr_err_code ndr_ret;
635 bool ok;
637 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
638 1, /* chunks */
639 &src_h, 4096, /* src file */
640 SEC_RIGHTS_FILE_ALL,
641 &dest_h, 0, /* dest file */
642 SEC_RIGHTS_FILE_ALL,
643 &cc_copy,
644 &ioctl);
645 if (!ok) {
646 torture_fail(torture, "setup copy chunk error");
649 /* send huge chunk length request */
650 cc_copy.chunks[0].source_off = 0;
651 cc_copy.chunks[0].target_off = 0;
652 cc_copy.chunks[0].length = UINT_MAX;
654 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
655 &cc_copy,
656 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
657 torture_assert_ndr_success(torture, ndr_ret, "marshalling request");
659 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
660 torture_assert_ntstatus_equal(torture, status,
661 NT_STATUS_INVALID_PARAMETER,
662 "bad oversize chunk response");
664 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
665 &cc_rsp,
666 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
667 torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
669 torture_comment(torture, "limit max chunks, got %u\n",
670 cc_rsp.chunks_written);
671 torture_comment(torture, "limit max chunk len, got %u\n",
672 cc_rsp.chunk_bytes_written);
673 torture_comment(torture, "limit max total bytes, got %u\n",
674 cc_rsp.total_bytes_written);
676 smb2_util_close(tree, src_h);
677 smb2_util_close(tree, dest_h);
678 talloc_free(tmp_ctx);
679 return true;
682 static bool test_ioctl_copy_chunk_src_lck(struct torture_context *torture,
683 struct smb2_tree *tree)
685 struct smb2_handle src_h;
686 struct smb2_handle src_h2;
687 struct smb2_handle dest_h;
688 NTSTATUS status;
689 union smb_ioctl ioctl;
690 TALLOC_CTX *tmp_ctx = talloc_new(tree);
691 struct srv_copychunk_copy cc_copy;
692 struct srv_copychunk_rsp cc_rsp;
693 enum ndr_err_code ndr_ret;
694 bool ok;
695 struct smb2_lock lck;
696 struct smb2_lock_element el[1];
698 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
699 1, /* chunks */
700 &src_h, 4096, /* src file */
701 SEC_RIGHTS_FILE_ALL,
702 &dest_h, 0, /* dest file */
703 SEC_RIGHTS_FILE_ALL,
704 &cc_copy,
705 &ioctl);
706 if (!ok) {
707 torture_fail(torture, "setup copy chunk error");
710 cc_copy.chunks[0].source_off = 0;
711 cc_copy.chunks[0].target_off = 0;
712 cc_copy.chunks[0].length = 4096;
714 /* open and lock the copychunk src file */
715 status = torture_smb2_testfile(tree, FNAME, &src_h2);
716 torture_assert_ntstatus_ok(torture, status, "2nd src open");
718 lck.in.lock_count = 0x0001;
719 lck.in.lock_sequence = 0x00000000;
720 lck.in.file.handle = src_h2;
721 lck.in.locks = el;
722 el[0].offset = cc_copy.chunks[0].source_off;
723 el[0].length = cc_copy.chunks[0].length;
724 el[0].reserved = 0;
725 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
727 status = smb2_lock(tree, &lck);
728 torture_assert_ntstatus_ok(torture, status, "lock");
730 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
731 &cc_copy,
732 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
733 torture_assert_ndr_success(torture, ndr_ret,
734 "ndr_push_srv_copychunk_copy");
736 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
738 * 2k12 & Samba return lock_conflict, Windows 7 & 2k8 return success...
740 * Edgar Olougouna @ MS wrote:
741 * Regarding the FSCTL_SRV_COPYCHUNK and STATUS_FILE_LOCK_CONFLICT
742 * discrepancy observed between Windows versions, we confirm that the
743 * behavior change is expected.
745 * CopyChunk in Windows Server 2012 use regular Readfile/Writefile APIs
746 * to move the chunks from the source to the destination.
747 * These ReadFile/WriteFile APIs go through the byte-range lock checks,
748 * and this explains the observed STATUS_FILE_LOCK_CONFLICT error.
750 * Prior to Windows Server 2012, CopyChunk used mapped sections to move
751 * the data. And byte range locks are not enforced on mapped I/O, and
752 * this explains the STATUS_SUCCESS observed on Windows Server 2008 R2.
754 torture_assert_ntstatus_equal(torture, status,
755 NT_STATUS_FILE_LOCK_CONFLICT,
756 "FSCTL_SRV_COPYCHUNK locked");
758 /* should get cc response data with the lock conflict status */
759 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
760 &cc_rsp,
761 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
762 torture_assert_ndr_success(torture, ndr_ret,
763 "ndr_pull_srv_copychunk_rsp");
764 ok = check_copy_chunk_rsp(torture, &cc_rsp,
765 0, /* chunks written */
766 0, /* chunk bytes unsuccessfully written */
767 0); /* total bytes written */
769 lck.in.lock_count = 0x0001;
770 lck.in.lock_sequence = 0x00000001;
771 lck.in.file.handle = src_h2;
772 lck.in.locks = el;
773 el[0].offset = cc_copy.chunks[0].source_off;
774 el[0].length = cc_copy.chunks[0].length;
775 el[0].reserved = 0;
776 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
777 status = smb2_lock(tree, &lck);
778 torture_assert_ntstatus_ok(torture, status, "unlock");
780 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
781 torture_assert_ntstatus_ok(torture, status,
782 "FSCTL_SRV_COPYCHUNK unlocked");
784 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
785 &cc_rsp,
786 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
787 torture_assert_ndr_success(torture, ndr_ret,
788 "ndr_pull_srv_copychunk_rsp");
790 ok = check_copy_chunk_rsp(torture, &cc_rsp,
791 1, /* chunks written */
792 0, /* chunk bytes unsuccessfully written */
793 4096); /* total bytes written */
794 if (!ok) {
795 torture_fail(torture, "bad copy chunk response data");
798 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
799 if (!ok) {
800 torture_fail(torture, "inconsistent file data");
803 smb2_util_close(tree, src_h2);
804 smb2_util_close(tree, src_h);
805 smb2_util_close(tree, dest_h);
806 talloc_free(tmp_ctx);
807 return true;
810 static bool test_ioctl_copy_chunk_dest_lck(struct torture_context *torture,
811 struct smb2_tree *tree)
813 struct smb2_handle src_h;
814 struct smb2_handle dest_h;
815 struct smb2_handle dest_h2;
816 NTSTATUS status;
817 union smb_ioctl ioctl;
818 TALLOC_CTX *tmp_ctx = talloc_new(tree);
819 struct srv_copychunk_copy cc_copy;
820 struct srv_copychunk_rsp cc_rsp;
821 enum ndr_err_code ndr_ret;
822 bool ok;
823 struct smb2_lock lck;
824 struct smb2_lock_element el[1];
826 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
827 1, /* chunks */
828 &src_h, 4096, /* src file */
829 SEC_RIGHTS_FILE_ALL,
830 &dest_h, 4096, /* dest file */
831 SEC_RIGHTS_FILE_ALL,
832 &cc_copy,
833 &ioctl);
834 if (!ok) {
835 torture_fail(torture, "setup copy chunk error");
838 cc_copy.chunks[0].source_off = 0;
839 cc_copy.chunks[0].target_off = 0;
840 cc_copy.chunks[0].length = 4096;
842 /* open and lock the copychunk dest file */
843 status = torture_smb2_testfile(tree, FNAME2, &dest_h2);
844 torture_assert_ntstatus_ok(torture, status, "2nd src open");
846 lck.in.lock_count = 0x0001;
847 lck.in.lock_sequence = 0x00000000;
848 lck.in.file.handle = dest_h2;
849 lck.in.locks = el;
850 el[0].offset = cc_copy.chunks[0].target_off;
851 el[0].length = cc_copy.chunks[0].length;
852 el[0].reserved = 0;
853 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
855 status = smb2_lock(tree, &lck);
856 torture_assert_ntstatus_ok(torture, status, "lock");
858 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
859 &cc_copy,
860 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
861 torture_assert_ndr_success(torture, ndr_ret,
862 "ndr_push_srv_copychunk_copy");
864 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
865 torture_assert_ntstatus_equal(torture, status,
866 NT_STATUS_FILE_LOCK_CONFLICT,
867 "FSCTL_SRV_COPYCHUNK locked");
869 lck.in.lock_count = 0x0001;
870 lck.in.lock_sequence = 0x00000001;
871 lck.in.file.handle = dest_h2;
872 lck.in.locks = el;
873 el[0].offset = cc_copy.chunks[0].target_off;
874 el[0].length = cc_copy.chunks[0].length;
875 el[0].reserved = 0;
876 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
877 status = smb2_lock(tree, &lck);
878 torture_assert_ntstatus_ok(torture, status, "unlock");
880 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
881 torture_assert_ntstatus_ok(torture, status,
882 "FSCTL_SRV_COPYCHUNK unlocked");
884 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
885 &cc_rsp,
886 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
887 torture_assert_ndr_success(torture, ndr_ret,
888 "ndr_pull_srv_copychunk_rsp");
890 ok = check_copy_chunk_rsp(torture, &cc_rsp,
891 1, /* chunks written */
892 0, /* chunk bytes unsuccessfully written */
893 4096); /* total bytes written */
894 if (!ok) {
895 torture_fail(torture, "bad copy chunk response data");
898 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
899 if (!ok) {
900 torture_fail(torture, "inconsistent file data");
903 smb2_util_close(tree, dest_h2);
904 smb2_util_close(tree, src_h);
905 smb2_util_close(tree, dest_h);
906 talloc_free(tmp_ctx);
907 return true;
910 static bool test_ioctl_copy_chunk_bad_key(struct torture_context *torture,
911 struct smb2_tree *tree)
913 struct smb2_handle src_h;
914 struct smb2_handle dest_h;
915 NTSTATUS status;
916 union smb_ioctl ioctl;
917 TALLOC_CTX *tmp_ctx = talloc_new(tree);
918 struct srv_copychunk_copy cc_copy;
919 enum ndr_err_code ndr_ret;
920 bool ok;
922 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
924 &src_h, 4096,
925 SEC_RIGHTS_FILE_ALL,
926 &dest_h, 0,
927 SEC_RIGHTS_FILE_ALL,
928 &cc_copy,
929 &ioctl);
930 if (!ok) {
931 torture_fail(torture, "setup copy chunk error");
934 /* overwrite the resume key with a bogus value */
935 memcpy(cc_copy.source_key, "deadbeefdeadbeefdeadbeef", 24);
937 cc_copy.chunks[0].source_off = 0;
938 cc_copy.chunks[0].target_off = 0;
939 cc_copy.chunks[0].length = 4096;
941 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
942 &cc_copy,
943 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
944 torture_assert_ndr_success(torture, ndr_ret,
945 "ndr_push_srv_copychunk_copy");
947 /* Server 2k12 returns NT_STATUS_OBJECT_NAME_NOT_FOUND */
948 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
949 torture_assert_ntstatus_equal(torture, status,
950 NT_STATUS_OBJECT_NAME_NOT_FOUND,
951 "FSCTL_SRV_COPYCHUNK");
953 smb2_util_close(tree, src_h);
954 smb2_util_close(tree, dest_h);
955 talloc_free(tmp_ctx);
956 return true;
959 static bool test_ioctl_copy_chunk_src_is_dest(struct torture_context *torture,
960 struct smb2_tree *tree)
962 struct smb2_handle src_h;
963 struct smb2_handle dest_h;
964 NTSTATUS status;
965 union smb_ioctl ioctl;
966 TALLOC_CTX *tmp_ctx = talloc_new(tree);
967 struct srv_copychunk_copy cc_copy;
968 struct srv_copychunk_rsp cc_rsp;
969 enum ndr_err_code ndr_ret;
970 bool ok;
972 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
974 &src_h, 8192,
975 SEC_RIGHTS_FILE_ALL,
976 &dest_h, 0,
977 SEC_RIGHTS_FILE_ALL,
978 &cc_copy,
979 &ioctl);
980 if (!ok) {
981 torture_fail(torture, "setup copy chunk error");
984 /* the source is also the destination */
985 ioctl.smb2.in.file.handle = src_h;
987 /* non-overlapping */
988 cc_copy.chunks[0].source_off = 0;
989 cc_copy.chunks[0].target_off = 4096;
990 cc_copy.chunks[0].length = 4096;
992 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
993 &cc_copy,
994 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
995 torture_assert_ndr_success(torture, ndr_ret,
996 "ndr_push_srv_copychunk_copy");
998 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
999 torture_assert_ntstatus_ok(torture, status,
1000 "FSCTL_SRV_COPYCHUNK");
1002 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1003 &cc_rsp,
1004 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1005 torture_assert_ndr_success(torture, ndr_ret,
1006 "ndr_pull_srv_copychunk_rsp");
1008 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1009 1, /* chunks written */
1010 0, /* chunk bytes unsuccessfully written */
1011 4096); /* total bytes written */
1012 if (!ok) {
1013 torture_fail(torture, "bad copy chunk response data");
1016 ok = check_pattern(torture, tree, tmp_ctx, src_h, 0, 4096, 0);
1017 if (!ok) {
1018 torture_fail(torture, "inconsistent file data");
1020 ok = check_pattern(torture, tree, tmp_ctx, src_h, 4096, 4096, 0);
1021 if (!ok) {
1022 torture_fail(torture, "inconsistent file data");
1025 smb2_util_close(tree, src_h);
1026 smb2_util_close(tree, dest_h);
1027 talloc_free(tmp_ctx);
1028 return true;
1032 * Test a single-chunk copychunk request, where the source and target ranges
1033 * overlap, and the SourceKey refers to the same target file. E.g:
1035 * Initial State
1036 * -------------
1037 * File: src_and_dest
1038 * Offset: 0123456789
1039 * Data: abcdefghij
1041 * Request
1042 * -------
1043 * FSCTL_SRV_COPYCHUNK(src_and_dest)
1044 * SourceKey = SRV_REQUEST_RESUME_KEY(src_and_dest)
1045 * ChunkCount = 1
1046 * Chunks[0].SourceOffset = 0
1047 * Chunks[0].TargetOffset = 4
1048 * Chunks[0].Length = 6
1050 * Resultant State
1051 * ---------------
1052 * File: src_and_dest
1053 * Offset: 0123456789
1054 * Data: abcdabcdef
1056 * The resultant contents of src_and_dest is dependent on the server's
1057 * copy algorithm. In the above example, the server uses an IO buffer
1058 * large enough to hold the entire six-byte source data before writing
1059 * to TargetOffset. If the server were to use a four-byte IO buffer and
1060 * started reads/writes from the lowest offset, then the two overlapping
1061 * bytes in the above example would be overwritten before being read. The
1062 * resultant file contents would be abcdabcdab.
1064 * Windows 2008r2 appears to use a 2048 byte copy buffer, overlapping bytes
1065 * after this offset are written before being read. Windows 2012 on the
1066 * other hand appears to use a buffer large enough to hold its maximum
1067 * supported chunk size (1M). Samba currently uses a 64k copy buffer by
1068 * default (vfs_cc_state.buf).
1070 * This test uses an 8-byte overlap at 2040-2048, so that it passes against
1071 * Windows 2008, 2012 and Samba servers.
1073 static bool
1074 test_ioctl_copy_chunk_src_is_dest_overlap(struct torture_context *torture,
1075 struct smb2_tree *tree)
1077 struct smb2_handle src_h;
1078 struct smb2_handle dest_h;
1079 NTSTATUS status;
1080 union smb_ioctl ioctl;
1081 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1082 struct srv_copychunk_copy cc_copy;
1083 struct srv_copychunk_rsp cc_rsp;
1084 enum ndr_err_code ndr_ret;
1085 bool ok;
1087 /* exceed the vfs_default copy buffer */
1088 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1090 &src_h, 2048 * 2,
1091 SEC_RIGHTS_FILE_ALL,
1092 &dest_h, 0,
1093 SEC_RIGHTS_FILE_ALL,
1094 &cc_copy,
1095 &ioctl);
1096 if (!ok) {
1097 torture_fail(torture, "setup copy chunk error");
1100 /* the source is also the destination */
1101 ioctl.smb2.in.file.handle = src_h;
1103 /* 8 bytes overlap between source and target ranges */
1104 cc_copy.chunks[0].source_off = 0;
1105 cc_copy.chunks[0].target_off = 2048 - 8;
1106 cc_copy.chunks[0].length = 2048;
1108 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1109 &cc_copy,
1110 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1111 torture_assert_ndr_success(torture, ndr_ret,
1112 "ndr_push_srv_copychunk_copy");
1114 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1115 torture_assert_ntstatus_ok(torture, status,
1116 "FSCTL_SRV_COPYCHUNK");
1118 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1119 &cc_rsp,
1120 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1121 torture_assert_ndr_success(torture, ndr_ret,
1122 "ndr_pull_srv_copychunk_rsp");
1124 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1125 1, /* chunks written */
1126 0, /* chunk bytes unsuccessfully written */
1127 2048); /* total bytes written */
1128 if (!ok) {
1129 torture_fail(torture, "bad copy chunk response data");
1132 ok = check_pattern(torture, tree, tmp_ctx, src_h, 0, 2048 - 8, 0);
1133 if (!ok) {
1134 torture_fail(torture, "inconsistent file data");
1136 ok = check_pattern(torture, tree, tmp_ctx, src_h, 2048 - 8, 2048, 0);
1137 if (!ok) {
1138 torture_fail(torture, "inconsistent file data");
1141 smb2_util_close(tree, src_h);
1142 smb2_util_close(tree, dest_h);
1143 talloc_free(tmp_ctx);
1144 return true;
1147 static bool test_ioctl_copy_chunk_bad_access(struct torture_context *torture,
1148 struct smb2_tree *tree)
1150 struct smb2_handle src_h;
1151 struct smb2_handle dest_h;
1152 NTSTATUS status;
1153 union smb_ioctl ioctl;
1154 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1155 struct srv_copychunk_copy cc_copy;
1156 enum ndr_err_code ndr_ret;
1157 bool ok;
1159 /* no read permission on src */
1160 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1161 1, /* 1 chunk */
1162 &src_h, 4096, /* fill 4096 byte src file */
1163 SEC_RIGHTS_FILE_WRITE,
1164 &dest_h, 0, /* 0 byte dest file */
1165 SEC_RIGHTS_FILE_ALL,
1166 &cc_copy,
1167 &ioctl);
1168 if (!ok) {
1169 torture_fail(torture, "setup copy chunk error");
1172 cc_copy.chunks[0].source_off = 0;
1173 cc_copy.chunks[0].target_off = 0;
1174 cc_copy.chunks[0].length = 4096;
1176 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1177 &cc_copy,
1178 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1179 torture_assert_ndr_success(torture, ndr_ret,
1180 "ndr_push_srv_copychunk_copy");
1182 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1183 torture_assert_ntstatus_equal(torture, status,
1184 NT_STATUS_ACCESS_DENIED,
1185 "FSCTL_SRV_COPYCHUNK");
1187 smb2_util_close(tree, src_h);
1188 smb2_util_close(tree, dest_h);
1190 /* no write permission on dest */
1191 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1192 1, /* 1 chunk */
1193 &src_h, 4096, /* fill 4096 byte src file */
1194 SEC_RIGHTS_FILE_ALL,
1195 &dest_h, 0, /* 0 byte dest file */
1196 (SEC_RIGHTS_FILE_READ
1197 | SEC_RIGHTS_FILE_EXECUTE),
1198 &cc_copy,
1199 &ioctl);
1200 if (!ok) {
1201 torture_fail(torture, "setup copy chunk error");
1204 cc_copy.chunks[0].source_off = 0;
1205 cc_copy.chunks[0].target_off = 0;
1206 cc_copy.chunks[0].length = 4096;
1208 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1209 &cc_copy,
1210 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1211 torture_assert_ndr_success(torture, ndr_ret,
1212 "ndr_push_srv_copychunk_copy");
1214 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1215 torture_assert_ntstatus_equal(torture, status,
1216 NT_STATUS_ACCESS_DENIED,
1217 "FSCTL_SRV_COPYCHUNK");
1219 smb2_util_close(tree, src_h);
1220 smb2_util_close(tree, dest_h);
1222 /* no read permission on dest */
1223 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1224 1, /* 1 chunk */
1225 &src_h, 4096, /* fill 4096 byte src file */
1226 SEC_RIGHTS_FILE_ALL,
1227 &dest_h, 0, /* 0 byte dest file */
1228 (SEC_RIGHTS_FILE_WRITE
1229 | SEC_RIGHTS_FILE_EXECUTE),
1230 &cc_copy,
1231 &ioctl);
1232 if (!ok) {
1233 torture_fail(torture, "setup copy chunk error");
1236 cc_copy.chunks[0].source_off = 0;
1237 cc_copy.chunks[0].target_off = 0;
1238 cc_copy.chunks[0].length = 4096;
1240 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1241 &cc_copy,
1242 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1243 torture_assert_ndr_success(torture, ndr_ret,
1244 "ndr_push_srv_copychunk_copy");
1247 * FSCTL_SRV_COPYCHUNK requires read permission on dest,
1248 * FSCTL_SRV_COPYCHUNK_WRITE (not supported by Samba) on the other hand
1249 * does not.
1251 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1252 torture_assert_ntstatus_equal(torture, status,
1253 NT_STATUS_ACCESS_DENIED,
1254 "FSCTL_SRV_COPYCHUNK");
1256 smb2_util_close(tree, src_h);
1257 smb2_util_close(tree, dest_h);
1258 talloc_free(tmp_ctx);
1260 return true;
1263 static bool test_ioctl_copy_chunk_src_exceed(struct torture_context *torture,
1264 struct smb2_tree *tree)
1266 struct smb2_handle src_h;
1267 struct smb2_handle dest_h;
1268 NTSTATUS status;
1269 union smb_ioctl ioctl;
1270 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1271 struct srv_copychunk_copy cc_copy;
1272 struct srv_copychunk_rsp cc_rsp;
1273 enum ndr_err_code ndr_ret;
1274 bool ok;
1276 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1277 1, /* 1 chunk */
1278 &src_h, 4096, /* fill 4096 byte src file */
1279 SEC_RIGHTS_FILE_ALL,
1280 &dest_h, 0, /* 0 byte dest file */
1281 SEC_RIGHTS_FILE_ALL,
1282 &cc_copy,
1283 &ioctl);
1284 if (!ok) {
1285 torture_fail(torture, "setup copy chunk error");
1288 /* Request copy where off + length exceeds size of src */
1289 cc_copy.chunks[0].source_off = 1024;
1290 cc_copy.chunks[0].target_off = 0;
1291 cc_copy.chunks[0].length = 4096;
1293 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1294 &cc_copy,
1295 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1296 torture_assert_ndr_success(torture, ndr_ret,
1297 "ndr_push_srv_copychunk_copy");
1299 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1300 torture_assert_ntstatus_equal(torture, status,
1301 NT_STATUS_INVALID_VIEW_SIZE,
1302 "FSCTL_SRV_COPYCHUNK oversize");
1304 /* Request copy where length exceeds size of src */
1305 cc_copy.chunks[0].source_off = 1024;
1306 cc_copy.chunks[0].target_off = 0;
1307 cc_copy.chunks[0].length = 3072;
1309 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1310 &cc_copy,
1311 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1312 torture_assert_ndr_success(torture, ndr_ret,
1313 "ndr_push_srv_copychunk_copy");
1315 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1316 torture_assert_ntstatus_ok(torture, status,
1317 "FSCTL_SRV_COPYCHUNK just right");
1319 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1320 &cc_rsp,
1321 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1322 torture_assert_ndr_success(torture, ndr_ret,
1323 "ndr_pull_srv_copychunk_rsp");
1325 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1326 1, /* chunks written */
1327 0, /* chunk bytes unsuccessfully written */
1328 3072); /* total bytes written */
1329 if (!ok) {
1330 torture_fail(torture, "bad copy chunk response data");
1333 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 3072, 1024);
1334 if (!ok) {
1335 torture_fail(torture, "inconsistent file data");
1338 smb2_util_close(tree, src_h);
1339 smb2_util_close(tree, dest_h);
1340 talloc_free(tmp_ctx);
1341 return true;
1344 static bool
1345 test_ioctl_copy_chunk_src_exceed_multi(struct torture_context *torture,
1346 struct smb2_tree *tree)
1348 struct smb2_handle src_h;
1349 struct smb2_handle dest_h;
1350 NTSTATUS status;
1351 union smb_ioctl ioctl;
1352 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1353 struct srv_copychunk_copy cc_copy;
1354 struct srv_copychunk_rsp cc_rsp;
1355 enum ndr_err_code ndr_ret;
1356 bool ok;
1358 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1359 2, /* 2 chunks */
1360 &src_h, 8192, /* fill 8192 byte src file */
1361 SEC_RIGHTS_FILE_ALL,
1362 &dest_h, 0, /* 0 byte dest file */
1363 SEC_RIGHTS_FILE_ALL,
1364 &cc_copy,
1365 &ioctl);
1366 if (!ok) {
1367 torture_fail(torture, "setup copy chunk error");
1370 /* Request copy where off + length exceeds size of src */
1371 cc_copy.chunks[0].source_off = 0;
1372 cc_copy.chunks[0].target_off = 0;
1373 cc_copy.chunks[0].length = 4096;
1375 cc_copy.chunks[1].source_off = 4096;
1376 cc_copy.chunks[1].target_off = 4096;
1377 cc_copy.chunks[1].length = 8192;
1379 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1380 &cc_copy,
1381 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1382 torture_assert_ndr_success(torture, ndr_ret,
1383 "ndr_push_srv_copychunk_copy");
1385 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1386 torture_assert_ntstatus_equal(torture, status,
1387 NT_STATUS_INVALID_VIEW_SIZE,
1388 "FSCTL_SRV_COPYCHUNK oversize");
1389 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1390 &cc_rsp,
1391 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1392 torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
1394 /* first chunk should still be written */
1395 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1396 1, /* chunks written */
1397 0, /* chunk bytes unsuccessfully written */
1398 4096); /* total bytes written */
1399 if (!ok) {
1400 torture_fail(torture, "bad copy chunk response data");
1402 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
1403 if (!ok) {
1404 torture_fail(torture, "inconsistent file data");
1407 smb2_util_close(tree, src_h);
1408 smb2_util_close(tree, dest_h);
1409 talloc_free(tmp_ctx);
1410 return true;
1413 static bool test_ioctl_copy_chunk_sparse_dest(struct torture_context *torture,
1414 struct smb2_tree *tree)
1416 struct smb2_handle src_h;
1417 struct smb2_handle dest_h;
1418 NTSTATUS status;
1419 union smb_ioctl ioctl;
1420 struct smb2_read r;
1421 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1422 struct srv_copychunk_copy cc_copy;
1423 struct srv_copychunk_rsp cc_rsp;
1424 enum ndr_err_code ndr_ret;
1425 bool ok;
1426 int i;
1428 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1429 1, /* 1 chunk */
1430 &src_h, 4096, /* fill 4096 byte src file */
1431 SEC_RIGHTS_FILE_ALL,
1432 &dest_h, 0, /* 0 byte dest file */
1433 SEC_RIGHTS_FILE_ALL,
1434 &cc_copy,
1435 &ioctl);
1436 if (!ok) {
1437 torture_fail(torture, "setup copy chunk error");
1440 /* copy all src file data (via a single chunk desc) */
1441 cc_copy.chunks[0].source_off = 0;
1442 cc_copy.chunks[0].target_off = 4096;
1443 cc_copy.chunks[0].length = 4096;
1445 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1446 &cc_copy,
1447 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1448 torture_assert_ndr_success(torture, ndr_ret,
1449 "ndr_push_srv_copychunk_copy");
1451 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1452 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
1454 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1455 &cc_rsp,
1456 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1457 torture_assert_ndr_success(torture, ndr_ret,
1458 "ndr_pull_srv_copychunk_rsp");
1460 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1461 1, /* chunks written */
1462 0, /* chunk bytes unsuccessfully written */
1463 4096); /* total bytes written */
1464 if (!ok) {
1465 torture_fail(torture, "bad copy chunk response data");
1468 /* check for zeros in first 4k */
1469 ZERO_STRUCT(r);
1470 r.in.file.handle = dest_h;
1471 r.in.length = 4096;
1472 r.in.offset = 0;
1473 status = smb2_read(tree, tmp_ctx, &r);
1474 torture_assert_ntstatus_ok(torture, status, "read");
1476 torture_assert_u64_equal(torture, r.out.data.length, 4096,
1477 "read data len mismatch");
1479 for (i = 0; i < 4096; i++) {
1480 torture_assert(torture, (r.out.data.data[i] == 0),
1481 "sparse did not pass class");
1484 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 4096, 4096, 0);
1485 if (!ok) {
1486 torture_fail(torture, "inconsistent file data");
1489 smb2_util_close(tree, src_h);
1490 smb2_util_close(tree, dest_h);
1491 talloc_free(tmp_ctx);
1492 return true;
1496 * set the ioctl MaxOutputResponse size to less than
1497 * sizeof(struct srv_copychunk_rsp)
1499 static bool test_ioctl_copy_chunk_max_output_sz(struct torture_context *torture,
1500 struct smb2_tree *tree)
1502 struct smb2_handle src_h;
1503 struct smb2_handle dest_h;
1504 NTSTATUS status;
1505 union smb_ioctl ioctl;
1506 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1507 struct srv_copychunk_copy cc_copy;
1508 enum ndr_err_code ndr_ret;
1509 bool ok;
1511 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1512 1, /* 1 chunk */
1513 &src_h, 4096, /* fill 4096 byte src file */
1514 SEC_RIGHTS_FILE_ALL,
1515 &dest_h, 0, /* 0 byte dest file */
1516 SEC_RIGHTS_FILE_ALL,
1517 &cc_copy,
1518 &ioctl);
1519 if (!ok) {
1520 torture_fail(torture, "setup copy chunk error");
1523 cc_copy.chunks[0].source_off = 0;
1524 cc_copy.chunks[0].target_off = 0;
1525 cc_copy.chunks[0].length = 4096;
1526 /* req is valid, but use undersize max_response_size */
1527 ioctl.smb2.in.max_response_size = sizeof(struct srv_copychunk_rsp) - 1;
1529 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1530 &cc_copy,
1531 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1532 torture_assert_ndr_success(torture, ndr_ret,
1533 "ndr_push_srv_copychunk_copy");
1535 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1536 torture_assert_ntstatus_equal(torture, status,
1537 NT_STATUS_INVALID_PARAMETER,
1538 "FSCTL_SRV_COPYCHUNK");
1540 smb2_util_close(tree, src_h);
1541 smb2_util_close(tree, dest_h);
1542 talloc_free(tmp_ctx);
1543 return true;
1547 basic testing of SMB2 ioctls
1549 struct torture_suite *torture_smb2_ioctl_init(void)
1551 struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "ioctl");
1553 torture_suite_add_1smb2_test(suite, "shadow_copy",
1554 test_ioctl_get_shadow_copy);
1555 torture_suite_add_1smb2_test(suite, "req_resume_key",
1556 test_ioctl_req_resume_key);
1557 torture_suite_add_1smb2_test(suite, "copy_chunk_simple",
1558 test_ioctl_copy_chunk_simple);
1559 torture_suite_add_1smb2_test(suite, "copy_chunk_multi",
1560 test_ioctl_copy_chunk_multi);
1561 torture_suite_add_1smb2_test(suite, "copy_chunk_tiny",
1562 test_ioctl_copy_chunk_tiny);
1563 torture_suite_add_1smb2_test(suite, "copy_chunk_overwrite",
1564 test_ioctl_copy_chunk_over);
1565 torture_suite_add_1smb2_test(suite, "copy_chunk_append",
1566 test_ioctl_copy_chunk_append);
1567 torture_suite_add_1smb2_test(suite, "copy_chunk_limits",
1568 test_ioctl_copy_chunk_limits);
1569 torture_suite_add_1smb2_test(suite, "copy_chunk_src_lock",
1570 test_ioctl_copy_chunk_src_lck);
1571 torture_suite_add_1smb2_test(suite, "copy_chunk_dest_lock",
1572 test_ioctl_copy_chunk_dest_lck);
1573 torture_suite_add_1smb2_test(suite, "copy_chunk_bad_key",
1574 test_ioctl_copy_chunk_bad_key);
1575 torture_suite_add_1smb2_test(suite, "copy_chunk_src_is_dest",
1576 test_ioctl_copy_chunk_src_is_dest);
1577 torture_suite_add_1smb2_test(suite, "copy_chunk_src_is_dest_overlap",
1578 test_ioctl_copy_chunk_src_is_dest_overlap);
1579 torture_suite_add_1smb2_test(suite, "copy_chunk_bad_access",
1580 test_ioctl_copy_chunk_bad_access);
1581 torture_suite_add_1smb2_test(suite, "copy_chunk_src_exceed",
1582 test_ioctl_copy_chunk_src_exceed);
1583 torture_suite_add_1smb2_test(suite, "copy_chunk_src_exceed_multi",
1584 test_ioctl_copy_chunk_src_exceed_multi);
1585 torture_suite_add_1smb2_test(suite, "copy_chunk_sparse_dest",
1586 test_ioctl_copy_chunk_sparse_dest);
1587 torture_suite_add_1smb2_test(suite, "copy_chunk_max_output_sz",
1588 test_ioctl_copy_chunk_max_output_sz);
1590 suite->description = talloc_strdup(suite, "SMB2-IOCTL tests");
1592 return suite;