torture: extend FSCTL_[GET/SET]_COMPRESSION tests
[Samba.git] / source4 / torture / smb2 / ioctl.c
blobd3ac73f7608b82f1be5dec76e026774b981fe7af
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"
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)
40 struct smb2_handle h;
41 uint8_t buf[100];
42 NTSTATUS status;
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");
51 ZERO_ARRAY(buf);
52 status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf));
53 torture_assert_ntstatus_ok(torture, status, "write");
55 ZERO_STRUCT(ioctl);
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");
69 return true;
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)
78 struct smb2_handle h;
79 uint8_t buf[100];
80 NTSTATUS status;
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");
91 ZERO_ARRAY(buf);
92 status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf));
93 torture_assert_ntstatus_ok(torture, status, "write");
95 ZERO_STRUCT(ioctl);
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);
113 return true;
116 static uint64_t patt_hash(uint64_t off)
118 return 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,
124 uint64_t patt_off)
126 uint64_t i;
127 struct smb2_read r;
128 NTSTATUS status;
130 ZERO_STRUCT(r);
131 r.in.file.handle = h;
132 r.in.length = len;
133 r.in.offset = off;
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);
149 return true;
152 static bool test_setup_create_fill(struct torture_context *torture,
153 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
154 const char *fname,
155 struct smb2_handle *fh,
156 uint64_t size,
157 uint32_t desired_access)
159 struct smb2_create io;
160 NTSTATUS status;
161 uint64_t i;
162 uint8_t *buf = talloc_zero_size(mem_ctx, size);
163 torture_assert(torture, (buf != NULL), "no memory for file data buf");
165 smb2_util_unlink(tree, fname);
167 ZERO_STRUCT(io);
168 io.in.desired_access = desired_access;
169 io.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
170 io.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
171 io.in.share_access =
172 NTCREATEX_SHARE_ACCESS_DELETE|
173 NTCREATEX_SHARE_ACCESS_READ|
174 NTCREATEX_SHARE_ACCESS_WRITE;
175 io.in.fname = fname;
177 status = smb2_create(tree, mem_ctx, &io);
178 torture_assert_ntstatus_ok(torture, status, "file create");
180 *fh = io.out.file.handle;
182 if (size > 0) {
183 uint64_t cur_off = 0;
184 for (i = 0; i <= size - 8; i += 8) {
185 SBVAL(buf, i, patt_hash(i));
187 while (size > 0) {
188 uint64_t io_sz = MIN(1024 * 1024, size);
189 status = smb2_util_write(tree, *fh,
190 buf + cur_off, cur_off, io_sz);
191 torture_assert_ntstatus_ok(torture, status, "file write");
193 size -= io_sz;
194 cur_off += io_sz;
197 return true;
200 static bool test_setup_copy_chunk(struct torture_context *torture,
201 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
202 uint32_t nchunks,
203 struct smb2_handle *src_h,
204 uint64_t src_size,
205 uint32_t src_desired_access,
206 struct smb2_handle *dest_h,
207 uint64_t dest_size,
208 uint32_t dest_desired_access,
209 struct srv_copychunk_copy *cc_copy,
210 union smb_ioctl *ioctl)
212 struct req_resume_key_rsp res_key;
213 bool ok;
214 NTSTATUS status;
215 enum ndr_err_code ndr_ret;
217 ok = test_setup_create_fill(torture, tree, mem_ctx, FNAME,
218 src_h, src_size, src_desired_access);
219 torture_assert(torture, ok, "src file create fill");
221 ok = test_setup_create_fill(torture, tree, mem_ctx, FNAME2,
222 dest_h, dest_size, dest_desired_access);
223 torture_assert(torture, ok, "dest file create fill");
225 ZERO_STRUCTPN(ioctl);
226 ioctl->smb2.level = RAW_IOCTL_SMB2;
227 ioctl->smb2.in.file.handle = *src_h;
228 ioctl->smb2.in.function = FSCTL_SRV_REQUEST_RESUME_KEY;
229 /* Allow for Key + ContextLength + Context */
230 ioctl->smb2.in.max_response_size = 32;
231 ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
233 status = smb2_ioctl(tree, mem_ctx, &ioctl->smb2);
234 torture_assert_ntstatus_ok(torture, status,
235 "FSCTL_SRV_REQUEST_RESUME_KEY");
237 ndr_ret = ndr_pull_struct_blob(&ioctl->smb2.out.out, mem_ctx, &res_key,
238 (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
240 torture_assert_ndr_success(torture, ndr_ret,
241 "ndr_pull_req_resume_key_rsp");
243 ZERO_STRUCTPN(ioctl);
244 ioctl->smb2.level = RAW_IOCTL_SMB2;
245 ioctl->smb2.in.file.handle = *dest_h;
246 ioctl->smb2.in.function = FSCTL_SRV_COPYCHUNK;
247 ioctl->smb2.in.max_response_size = sizeof(struct srv_copychunk_rsp);
248 ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
250 ZERO_STRUCTPN(cc_copy);
251 memcpy(cc_copy->source_key, res_key.resume_key, ARRAY_SIZE(cc_copy->source_key));
252 cc_copy->chunk_count = nchunks;
253 cc_copy->chunks = talloc_zero_array(mem_ctx, struct srv_copychunk, nchunks);
254 torture_assert(torture, (cc_copy->chunks != NULL), "no memory for chunks");
256 return true;
260 static bool check_copy_chunk_rsp(struct torture_context *torture,
261 struct srv_copychunk_rsp *cc_rsp,
262 uint32_t ex_chunks_written,
263 uint32_t ex_chunk_bytes_written,
264 uint32_t ex_total_bytes_written)
266 torture_assert_int_equal(torture, cc_rsp->chunks_written,
267 ex_chunks_written, "num chunks");
268 torture_assert_int_equal(torture, cc_rsp->chunk_bytes_written,
269 ex_chunk_bytes_written, "chunk bytes written");
270 torture_assert_int_equal(torture, cc_rsp->total_bytes_written,
271 ex_total_bytes_written, "chunk total bytes");
272 return true;
275 static bool test_ioctl_copy_chunk_simple(struct torture_context *torture,
276 struct smb2_tree *tree)
278 struct smb2_handle src_h;
279 struct smb2_handle dest_h;
280 NTSTATUS status;
281 union smb_ioctl ioctl;
282 TALLOC_CTX *tmp_ctx = talloc_new(tree);
283 struct srv_copychunk_copy cc_copy;
284 struct srv_copychunk_rsp cc_rsp;
285 enum ndr_err_code ndr_ret;
286 bool ok;
288 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
289 1, /* 1 chunk */
290 &src_h, 4096, /* fill 4096 byte src file */
291 SEC_RIGHTS_FILE_ALL,
292 &dest_h, 0, /* 0 byte dest file */
293 SEC_RIGHTS_FILE_ALL,
294 &cc_copy,
295 &ioctl);
296 if (!ok) {
297 torture_fail(torture, "setup copy chunk error");
300 /* copy all src file data (via a single chunk desc) */
301 cc_copy.chunks[0].source_off = 0;
302 cc_copy.chunks[0].target_off = 0;
303 cc_copy.chunks[0].length = 4096;
305 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
306 &cc_copy,
307 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
308 torture_assert_ndr_success(torture, ndr_ret,
309 "ndr_push_srv_copychunk_copy");
311 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
312 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
314 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
315 &cc_rsp,
316 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
317 torture_assert_ndr_success(torture, ndr_ret,
318 "ndr_pull_srv_copychunk_rsp");
320 ok = check_copy_chunk_rsp(torture, &cc_rsp,
321 1, /* chunks written */
322 0, /* chunk bytes unsuccessfully written */
323 4096); /* total bytes written */
324 if (!ok) {
325 torture_fail(torture, "bad copy chunk response data");
328 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
329 if (!ok) {
330 torture_fail(torture, "inconsistent file data");
333 smb2_util_close(tree, src_h);
334 smb2_util_close(tree, dest_h);
335 talloc_free(tmp_ctx);
336 return true;
339 static bool test_ioctl_copy_chunk_multi(struct torture_context *torture,
340 struct smb2_tree *tree)
342 struct smb2_handle src_h;
343 struct smb2_handle dest_h;
344 NTSTATUS status;
345 union smb_ioctl ioctl;
346 TALLOC_CTX *tmp_ctx = talloc_new(tree);
347 struct srv_copychunk_copy cc_copy;
348 struct srv_copychunk_rsp cc_rsp;
349 enum ndr_err_code ndr_ret;
350 bool ok;
352 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
353 2, /* chunks */
354 &src_h, 8192, /* src file */
355 SEC_RIGHTS_FILE_ALL,
356 &dest_h, 0, /* dest file */
357 SEC_RIGHTS_FILE_ALL,
358 &cc_copy,
359 &ioctl);
360 if (!ok) {
361 torture_fail(torture, "setup copy chunk error");
364 /* copy all src file data via two chunks */
365 cc_copy.chunks[0].source_off = 0;
366 cc_copy.chunks[0].target_off = 0;
367 cc_copy.chunks[0].length = 4096;
369 cc_copy.chunks[1].source_off = 4096;
370 cc_copy.chunks[1].target_off = 4096;
371 cc_copy.chunks[1].length = 4096;
373 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
374 &cc_copy,
375 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
376 torture_assert_ndr_success(torture, ndr_ret,
377 "ndr_push_srv_copychunk_copy");
379 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
380 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
382 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
383 &cc_rsp,
384 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
385 torture_assert_ndr_success(torture, ndr_ret,
386 "ndr_pull_srv_copychunk_rsp");
388 ok = check_copy_chunk_rsp(torture, &cc_rsp,
389 2, /* chunks written */
390 0, /* chunk bytes unsuccessfully written */
391 8192); /* total bytes written */
392 if (!ok) {
393 torture_fail(torture, "bad copy chunk response data");
396 smb2_util_close(tree, src_h);
397 smb2_util_close(tree, dest_h);
398 talloc_free(tmp_ctx);
399 return true;
402 static bool test_ioctl_copy_chunk_tiny(struct torture_context *torture,
403 struct smb2_tree *tree)
405 struct smb2_handle src_h;
406 struct smb2_handle dest_h;
407 NTSTATUS status;
408 union smb_ioctl ioctl;
409 TALLOC_CTX *tmp_ctx = talloc_new(tree);
410 struct srv_copychunk_copy cc_copy;
411 struct srv_copychunk_rsp cc_rsp;
412 enum ndr_err_code ndr_ret;
413 bool ok;
415 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
416 2, /* chunks */
417 &src_h, 100, /* src file */
418 SEC_RIGHTS_FILE_ALL,
419 &dest_h, 0, /* dest file */
420 SEC_RIGHTS_FILE_ALL,
421 &cc_copy,
422 &ioctl);
423 if (!ok) {
424 torture_fail(torture, "setup copy chunk error");
427 /* copy all src file data via two chunks, sub block size chunks */
428 cc_copy.chunks[0].source_off = 0;
429 cc_copy.chunks[0].target_off = 0;
430 cc_copy.chunks[0].length = 50;
432 cc_copy.chunks[1].source_off = 50;
433 cc_copy.chunks[1].target_off = 50;
434 cc_copy.chunks[1].length = 50;
436 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
437 &cc_copy,
438 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
439 torture_assert_ndr_success(torture, ndr_ret,
440 "ndr_push_srv_copychunk_copy");
442 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
443 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
445 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
446 &cc_rsp,
447 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
448 torture_assert_ndr_success(torture, ndr_ret,
449 "ndr_pull_srv_copychunk_rsp");
451 ok = check_copy_chunk_rsp(torture, &cc_rsp,
452 2, /* chunks written */
453 0, /* chunk bytes unsuccessfully written */
454 100); /* total bytes written */
455 if (!ok) {
456 torture_fail(torture, "bad copy chunk response data");
459 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 100, 0);
460 if (!ok) {
461 torture_fail(torture, "inconsistent file data");
464 smb2_util_close(tree, src_h);
465 smb2_util_close(tree, dest_h);
466 talloc_free(tmp_ctx);
467 return true;
470 static bool test_ioctl_copy_chunk_over(struct torture_context *torture,
471 struct smb2_tree *tree)
473 struct smb2_handle src_h;
474 struct smb2_handle dest_h;
475 NTSTATUS status;
476 union smb_ioctl ioctl;
477 TALLOC_CTX *tmp_ctx = talloc_new(tree);
478 struct srv_copychunk_copy cc_copy;
479 struct srv_copychunk_rsp cc_rsp;
480 enum ndr_err_code ndr_ret;
481 bool ok;
483 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
484 2, /* chunks */
485 &src_h, 8192, /* src file */
486 SEC_RIGHTS_FILE_ALL,
487 &dest_h, 4096, /* dest file */
488 SEC_RIGHTS_FILE_ALL,
489 &cc_copy,
490 &ioctl);
491 if (!ok) {
492 torture_fail(torture, "setup copy chunk error");
495 /* first chunk overwrites existing dest data */
496 cc_copy.chunks[0].source_off = 0;
497 cc_copy.chunks[0].target_off = 0;
498 cc_copy.chunks[0].length = 4096;
500 /* second chunk overwrites the first */
501 cc_copy.chunks[1].source_off = 4096;
502 cc_copy.chunks[1].target_off = 0;
503 cc_copy.chunks[1].length = 4096;
505 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
506 &cc_copy,
507 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
508 torture_assert_ndr_success(torture, ndr_ret,
509 "ndr_push_srv_copychunk_copy");
511 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
512 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
514 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
515 &cc_rsp,
516 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
517 torture_assert_ndr_success(torture, ndr_ret,
518 "ndr_pull_srv_copychunk_rsp");
520 ok = check_copy_chunk_rsp(torture, &cc_rsp,
521 2, /* chunks written */
522 0, /* chunk bytes unsuccessfully written */
523 8192); /* total bytes written */
524 if (!ok) {
525 torture_fail(torture, "bad copy chunk response data");
528 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 4096);
529 if (!ok) {
530 torture_fail(torture, "inconsistent file data");
533 smb2_util_close(tree, src_h);
534 smb2_util_close(tree, dest_h);
535 talloc_free(tmp_ctx);
536 return true;
539 static bool test_ioctl_copy_chunk_append(struct torture_context *torture,
540 struct smb2_tree *tree)
542 struct smb2_handle src_h;
543 struct smb2_handle dest_h;
544 NTSTATUS status;
545 union smb_ioctl ioctl;
546 TALLOC_CTX *tmp_ctx = talloc_new(tree);
547 struct srv_copychunk_copy cc_copy;
548 struct srv_copychunk_rsp cc_rsp;
549 enum ndr_err_code ndr_ret;
550 bool ok;
552 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
553 2, /* chunks */
554 &src_h, 4096, /* src file */
555 SEC_RIGHTS_FILE_ALL,
556 &dest_h, 0, /* dest file */
557 SEC_RIGHTS_FILE_ALL,
558 &cc_copy,
559 &ioctl);
560 if (!ok) {
561 torture_fail(torture, "setup copy chunk error");
564 cc_copy.chunks[0].source_off = 0;
565 cc_copy.chunks[0].target_off = 0;
566 cc_copy.chunks[0].length = 4096;
568 /* second chunk appends the same data to the first */
569 cc_copy.chunks[1].source_off = 0;
570 cc_copy.chunks[1].target_off = 4096;
571 cc_copy.chunks[1].length = 4096;
573 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
574 &cc_copy,
575 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
576 torture_assert_ndr_success(torture, ndr_ret,
577 "ndr_push_srv_copychunk_copy");
579 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
580 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
582 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
583 &cc_rsp,
584 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
585 torture_assert_ndr_success(torture, ndr_ret,
586 "ndr_pull_srv_copychunk_rsp");
588 ok = check_copy_chunk_rsp(torture, &cc_rsp,
589 2, /* chunks written */
590 0, /* chunk bytes unsuccessfully written */
591 8192); /* total bytes written */
592 if (!ok) {
593 torture_fail(torture, "bad copy chunk response data");
596 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
597 if (!ok) {
598 torture_fail(torture, "inconsistent file data");
601 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 4096, 4096, 0);
602 if (!ok) {
603 torture_fail(torture, "inconsistent file data");
606 smb2_util_close(tree, src_h);
607 smb2_util_close(tree, dest_h);
608 talloc_free(tmp_ctx);
609 return true;
612 static bool test_ioctl_copy_chunk_limits(struct torture_context *torture,
613 struct smb2_tree *tree)
615 struct smb2_handle src_h;
616 struct smb2_handle dest_h;
617 NTSTATUS status;
618 union smb_ioctl ioctl;
619 TALLOC_CTX *tmp_ctx = talloc_new(tree);
620 struct srv_copychunk_copy cc_copy;
621 struct srv_copychunk_rsp cc_rsp;
622 enum ndr_err_code ndr_ret;
623 bool ok;
625 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
626 1, /* chunks */
627 &src_h, 4096, /* src file */
628 SEC_RIGHTS_FILE_ALL,
629 &dest_h, 0, /* dest file */
630 SEC_RIGHTS_FILE_ALL,
631 &cc_copy,
632 &ioctl);
633 if (!ok) {
634 torture_fail(torture, "setup copy chunk error");
637 /* send huge chunk length request */
638 cc_copy.chunks[0].source_off = 0;
639 cc_copy.chunks[0].target_off = 0;
640 cc_copy.chunks[0].length = UINT_MAX;
642 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
643 &cc_copy,
644 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
645 torture_assert_ndr_success(torture, ndr_ret, "marshalling request");
647 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
648 torture_assert_ntstatus_equal(torture, status,
649 NT_STATUS_INVALID_PARAMETER,
650 "bad oversize chunk response");
652 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
653 &cc_rsp,
654 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
655 torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
657 torture_comment(torture, "limit max chunks, got %u\n",
658 cc_rsp.chunks_written);
659 torture_comment(torture, "limit max chunk len, got %u\n",
660 cc_rsp.chunk_bytes_written);
661 torture_comment(torture, "limit max total bytes, got %u\n",
662 cc_rsp.total_bytes_written);
664 smb2_util_close(tree, src_h);
665 smb2_util_close(tree, dest_h);
666 talloc_free(tmp_ctx);
667 return true;
670 static bool test_ioctl_copy_chunk_src_lck(struct torture_context *torture,
671 struct smb2_tree *tree)
673 struct smb2_handle src_h;
674 struct smb2_handle src_h2;
675 struct smb2_handle dest_h;
676 NTSTATUS status;
677 union smb_ioctl ioctl;
678 TALLOC_CTX *tmp_ctx = talloc_new(tree);
679 struct srv_copychunk_copy cc_copy;
680 struct srv_copychunk_rsp cc_rsp;
681 enum ndr_err_code ndr_ret;
682 bool ok;
683 struct smb2_lock lck;
684 struct smb2_lock_element el[1];
686 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
687 1, /* chunks */
688 &src_h, 4096, /* src file */
689 SEC_RIGHTS_FILE_ALL,
690 &dest_h, 0, /* dest file */
691 SEC_RIGHTS_FILE_ALL,
692 &cc_copy,
693 &ioctl);
694 if (!ok) {
695 torture_fail(torture, "setup copy chunk error");
698 cc_copy.chunks[0].source_off = 0;
699 cc_copy.chunks[0].target_off = 0;
700 cc_copy.chunks[0].length = 4096;
702 /* open and lock the copychunk src file */
703 status = torture_smb2_testfile(tree, FNAME, &src_h2);
704 torture_assert_ntstatus_ok(torture, status, "2nd src open");
706 lck.in.lock_count = 0x0001;
707 lck.in.lock_sequence = 0x00000000;
708 lck.in.file.handle = src_h2;
709 lck.in.locks = el;
710 el[0].offset = cc_copy.chunks[0].source_off;
711 el[0].length = cc_copy.chunks[0].length;
712 el[0].reserved = 0;
713 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
715 status = smb2_lock(tree, &lck);
716 torture_assert_ntstatus_ok(torture, status, "lock");
718 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
719 &cc_copy,
720 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
721 torture_assert_ndr_success(torture, ndr_ret,
722 "ndr_push_srv_copychunk_copy");
724 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
726 * 2k12 & Samba return lock_conflict, Windows 7 & 2k8 return success...
728 * Edgar Olougouna @ MS wrote:
729 * Regarding the FSCTL_SRV_COPYCHUNK and STATUS_FILE_LOCK_CONFLICT
730 * discrepancy observed between Windows versions, we confirm that the
731 * behavior change is expected.
733 * CopyChunk in Windows Server 2012 use regular Readfile/Writefile APIs
734 * to move the chunks from the source to the destination.
735 * These ReadFile/WriteFile APIs go through the byte-range lock checks,
736 * and this explains the observed STATUS_FILE_LOCK_CONFLICT error.
738 * Prior to Windows Server 2012, CopyChunk used mapped sections to move
739 * the data. And byte range locks are not enforced on mapped I/O, and
740 * this explains the STATUS_SUCCESS observed on Windows Server 2008 R2.
742 torture_assert_ntstatus_equal(torture, status,
743 NT_STATUS_FILE_LOCK_CONFLICT,
744 "FSCTL_SRV_COPYCHUNK locked");
746 /* should get cc response data with the lock conflict status */
747 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
748 &cc_rsp,
749 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
750 torture_assert_ndr_success(torture, ndr_ret,
751 "ndr_pull_srv_copychunk_rsp");
752 ok = check_copy_chunk_rsp(torture, &cc_rsp,
753 0, /* chunks written */
754 0, /* chunk bytes unsuccessfully written */
755 0); /* total bytes written */
757 lck.in.lock_count = 0x0001;
758 lck.in.lock_sequence = 0x00000001;
759 lck.in.file.handle = src_h2;
760 lck.in.locks = el;
761 el[0].offset = cc_copy.chunks[0].source_off;
762 el[0].length = cc_copy.chunks[0].length;
763 el[0].reserved = 0;
764 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
765 status = smb2_lock(tree, &lck);
766 torture_assert_ntstatus_ok(torture, status, "unlock");
768 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
769 torture_assert_ntstatus_ok(torture, status,
770 "FSCTL_SRV_COPYCHUNK unlocked");
772 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
773 &cc_rsp,
774 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
775 torture_assert_ndr_success(torture, ndr_ret,
776 "ndr_pull_srv_copychunk_rsp");
778 ok = check_copy_chunk_rsp(torture, &cc_rsp,
779 1, /* chunks written */
780 0, /* chunk bytes unsuccessfully written */
781 4096); /* total bytes written */
782 if (!ok) {
783 torture_fail(torture, "bad copy chunk response data");
786 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
787 if (!ok) {
788 torture_fail(torture, "inconsistent file data");
791 smb2_util_close(tree, src_h2);
792 smb2_util_close(tree, src_h);
793 smb2_util_close(tree, dest_h);
794 talloc_free(tmp_ctx);
795 return true;
798 static bool test_ioctl_copy_chunk_dest_lck(struct torture_context *torture,
799 struct smb2_tree *tree)
801 struct smb2_handle src_h;
802 struct smb2_handle dest_h;
803 struct smb2_handle dest_h2;
804 NTSTATUS status;
805 union smb_ioctl ioctl;
806 TALLOC_CTX *tmp_ctx = talloc_new(tree);
807 struct srv_copychunk_copy cc_copy;
808 struct srv_copychunk_rsp cc_rsp;
809 enum ndr_err_code ndr_ret;
810 bool ok;
811 struct smb2_lock lck;
812 struct smb2_lock_element el[1];
814 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
815 1, /* chunks */
816 &src_h, 4096, /* src file */
817 SEC_RIGHTS_FILE_ALL,
818 &dest_h, 4096, /* dest file */
819 SEC_RIGHTS_FILE_ALL,
820 &cc_copy,
821 &ioctl);
822 if (!ok) {
823 torture_fail(torture, "setup copy chunk error");
826 cc_copy.chunks[0].source_off = 0;
827 cc_copy.chunks[0].target_off = 0;
828 cc_copy.chunks[0].length = 4096;
830 /* open and lock the copychunk dest file */
831 status = torture_smb2_testfile(tree, FNAME2, &dest_h2);
832 torture_assert_ntstatus_ok(torture, status, "2nd src open");
834 lck.in.lock_count = 0x0001;
835 lck.in.lock_sequence = 0x00000000;
836 lck.in.file.handle = dest_h2;
837 lck.in.locks = el;
838 el[0].offset = cc_copy.chunks[0].target_off;
839 el[0].length = cc_copy.chunks[0].length;
840 el[0].reserved = 0;
841 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
843 status = smb2_lock(tree, &lck);
844 torture_assert_ntstatus_ok(torture, status, "lock");
846 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
847 &cc_copy,
848 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
849 torture_assert_ndr_success(torture, ndr_ret,
850 "ndr_push_srv_copychunk_copy");
852 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
853 torture_assert_ntstatus_equal(torture, status,
854 NT_STATUS_FILE_LOCK_CONFLICT,
855 "FSCTL_SRV_COPYCHUNK locked");
857 lck.in.lock_count = 0x0001;
858 lck.in.lock_sequence = 0x00000001;
859 lck.in.file.handle = dest_h2;
860 lck.in.locks = el;
861 el[0].offset = cc_copy.chunks[0].target_off;
862 el[0].length = cc_copy.chunks[0].length;
863 el[0].reserved = 0;
864 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
865 status = smb2_lock(tree, &lck);
866 torture_assert_ntstatus_ok(torture, status, "unlock");
868 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
869 torture_assert_ntstatus_ok(torture, status,
870 "FSCTL_SRV_COPYCHUNK unlocked");
872 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
873 &cc_rsp,
874 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
875 torture_assert_ndr_success(torture, ndr_ret,
876 "ndr_pull_srv_copychunk_rsp");
878 ok = check_copy_chunk_rsp(torture, &cc_rsp,
879 1, /* chunks written */
880 0, /* chunk bytes unsuccessfully written */
881 4096); /* total bytes written */
882 if (!ok) {
883 torture_fail(torture, "bad copy chunk response data");
886 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
887 if (!ok) {
888 torture_fail(torture, "inconsistent file data");
891 smb2_util_close(tree, dest_h2);
892 smb2_util_close(tree, src_h);
893 smb2_util_close(tree, dest_h);
894 talloc_free(tmp_ctx);
895 return true;
898 static bool test_ioctl_copy_chunk_bad_key(struct torture_context *torture,
899 struct smb2_tree *tree)
901 struct smb2_handle src_h;
902 struct smb2_handle dest_h;
903 NTSTATUS status;
904 union smb_ioctl ioctl;
905 TALLOC_CTX *tmp_ctx = talloc_new(tree);
906 struct srv_copychunk_copy cc_copy;
907 enum ndr_err_code ndr_ret;
908 bool ok;
910 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
912 &src_h, 4096,
913 SEC_RIGHTS_FILE_ALL,
914 &dest_h, 0,
915 SEC_RIGHTS_FILE_ALL,
916 &cc_copy,
917 &ioctl);
918 if (!ok) {
919 torture_fail(torture, "setup copy chunk error");
922 /* overwrite the resume key with a bogus value */
923 memcpy(cc_copy.source_key, "deadbeefdeadbeefdeadbeef", 24);
925 cc_copy.chunks[0].source_off = 0;
926 cc_copy.chunks[0].target_off = 0;
927 cc_copy.chunks[0].length = 4096;
929 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
930 &cc_copy,
931 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
932 torture_assert_ndr_success(torture, ndr_ret,
933 "ndr_push_srv_copychunk_copy");
935 /* Server 2k12 returns NT_STATUS_OBJECT_NAME_NOT_FOUND */
936 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
937 torture_assert_ntstatus_equal(torture, status,
938 NT_STATUS_OBJECT_NAME_NOT_FOUND,
939 "FSCTL_SRV_COPYCHUNK");
941 smb2_util_close(tree, src_h);
942 smb2_util_close(tree, dest_h);
943 talloc_free(tmp_ctx);
944 return true;
947 static bool test_ioctl_copy_chunk_src_is_dest(struct torture_context *torture,
948 struct smb2_tree *tree)
950 struct smb2_handle src_h;
951 struct smb2_handle dest_h;
952 NTSTATUS status;
953 union smb_ioctl ioctl;
954 TALLOC_CTX *tmp_ctx = talloc_new(tree);
955 struct srv_copychunk_copy cc_copy;
956 struct srv_copychunk_rsp cc_rsp;
957 enum ndr_err_code ndr_ret;
958 bool ok;
960 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
962 &src_h, 8192,
963 SEC_RIGHTS_FILE_ALL,
964 &dest_h, 0,
965 SEC_RIGHTS_FILE_ALL,
966 &cc_copy,
967 &ioctl);
968 if (!ok) {
969 torture_fail(torture, "setup copy chunk error");
972 /* the source is also the destination */
973 ioctl.smb2.in.file.handle = src_h;
975 /* non-overlapping */
976 cc_copy.chunks[0].source_off = 0;
977 cc_copy.chunks[0].target_off = 4096;
978 cc_copy.chunks[0].length = 4096;
980 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
981 &cc_copy,
982 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
983 torture_assert_ndr_success(torture, ndr_ret,
984 "ndr_push_srv_copychunk_copy");
986 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
987 torture_assert_ntstatus_ok(torture, status,
988 "FSCTL_SRV_COPYCHUNK");
990 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
991 &cc_rsp,
992 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
993 torture_assert_ndr_success(torture, ndr_ret,
994 "ndr_pull_srv_copychunk_rsp");
996 ok = check_copy_chunk_rsp(torture, &cc_rsp,
997 1, /* chunks written */
998 0, /* chunk bytes unsuccessfully written */
999 4096); /* total bytes written */
1000 if (!ok) {
1001 torture_fail(torture, "bad copy chunk response data");
1004 ok = check_pattern(torture, tree, tmp_ctx, src_h, 0, 4096, 0);
1005 if (!ok) {
1006 torture_fail(torture, "inconsistent file data");
1008 ok = check_pattern(torture, tree, tmp_ctx, src_h, 4096, 4096, 0);
1009 if (!ok) {
1010 torture_fail(torture, "inconsistent file data");
1013 smb2_util_close(tree, src_h);
1014 smb2_util_close(tree, dest_h);
1015 talloc_free(tmp_ctx);
1016 return true;
1020 * Test a single-chunk copychunk request, where the source and target ranges
1021 * overlap, and the SourceKey refers to the same target file. E.g:
1023 * Initial State
1024 * -------------
1025 * File: src_and_dest
1026 * Offset: 0123456789
1027 * Data: abcdefghij
1029 * Request
1030 * -------
1031 * FSCTL_SRV_COPYCHUNK(src_and_dest)
1032 * SourceKey = SRV_REQUEST_RESUME_KEY(src_and_dest)
1033 * ChunkCount = 1
1034 * Chunks[0].SourceOffset = 0
1035 * Chunks[0].TargetOffset = 4
1036 * Chunks[0].Length = 6
1038 * Resultant State
1039 * ---------------
1040 * File: src_and_dest
1041 * Offset: 0123456789
1042 * Data: abcdabcdef
1044 * The resultant contents of src_and_dest is dependent on the server's
1045 * copy algorithm. In the above example, the server uses an IO buffer
1046 * large enough to hold the entire six-byte source data before writing
1047 * to TargetOffset. If the server were to use a four-byte IO buffer and
1048 * started reads/writes from the lowest offset, then the two overlapping
1049 * bytes in the above example would be overwritten before being read. The
1050 * resultant file contents would be abcdabcdab.
1052 * Windows 2008r2 appears to use a 2048 byte copy buffer, overlapping bytes
1053 * after this offset are written before being read. Windows 2012 on the
1054 * other hand appears to use a buffer large enough to hold its maximum
1055 * supported chunk size (1M). Samba currently uses a 64k copy buffer by
1056 * default (vfs_cc_state.buf).
1058 * This test uses an 8-byte overlap at 2040-2048, so that it passes against
1059 * Windows 2008, 2012 and Samba servers.
1061 static bool
1062 test_ioctl_copy_chunk_src_is_dest_overlap(struct torture_context *torture,
1063 struct smb2_tree *tree)
1065 struct smb2_handle src_h;
1066 struct smb2_handle dest_h;
1067 NTSTATUS status;
1068 union smb_ioctl ioctl;
1069 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1070 struct srv_copychunk_copy cc_copy;
1071 struct srv_copychunk_rsp cc_rsp;
1072 enum ndr_err_code ndr_ret;
1073 bool ok;
1075 /* exceed the vfs_default copy buffer */
1076 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1078 &src_h, 2048 * 2,
1079 SEC_RIGHTS_FILE_ALL,
1080 &dest_h, 0,
1081 SEC_RIGHTS_FILE_ALL,
1082 &cc_copy,
1083 &ioctl);
1084 if (!ok) {
1085 torture_fail(torture, "setup copy chunk error");
1088 /* the source is also the destination */
1089 ioctl.smb2.in.file.handle = src_h;
1091 /* 8 bytes overlap between source and target ranges */
1092 cc_copy.chunks[0].source_off = 0;
1093 cc_copy.chunks[0].target_off = 2048 - 8;
1094 cc_copy.chunks[0].length = 2048;
1096 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1097 &cc_copy,
1098 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1099 torture_assert_ndr_success(torture, ndr_ret,
1100 "ndr_push_srv_copychunk_copy");
1102 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1103 torture_assert_ntstatus_ok(torture, status,
1104 "FSCTL_SRV_COPYCHUNK");
1106 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1107 &cc_rsp,
1108 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1109 torture_assert_ndr_success(torture, ndr_ret,
1110 "ndr_pull_srv_copychunk_rsp");
1112 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1113 1, /* chunks written */
1114 0, /* chunk bytes unsuccessfully written */
1115 2048); /* total bytes written */
1116 if (!ok) {
1117 torture_fail(torture, "bad copy chunk response data");
1120 ok = check_pattern(torture, tree, tmp_ctx, src_h, 0, 2048 - 8, 0);
1121 if (!ok) {
1122 torture_fail(torture, "inconsistent file data");
1124 ok = check_pattern(torture, tree, tmp_ctx, src_h, 2048 - 8, 2048, 0);
1125 if (!ok) {
1126 torture_fail(torture, "inconsistent file data");
1129 smb2_util_close(tree, src_h);
1130 smb2_util_close(tree, dest_h);
1131 talloc_free(tmp_ctx);
1132 return true;
1135 static bool test_ioctl_copy_chunk_bad_access(struct torture_context *torture,
1136 struct smb2_tree *tree)
1138 struct smb2_handle src_h;
1139 struct smb2_handle dest_h;
1140 NTSTATUS status;
1141 union smb_ioctl ioctl;
1142 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1143 struct srv_copychunk_copy cc_copy;
1144 enum ndr_err_code ndr_ret;
1145 bool ok;
1147 /* no read permission on src */
1148 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1149 1, /* 1 chunk */
1150 &src_h, 4096, /* fill 4096 byte src file */
1151 SEC_RIGHTS_FILE_WRITE,
1152 &dest_h, 0, /* 0 byte dest file */
1153 SEC_RIGHTS_FILE_ALL,
1154 &cc_copy,
1155 &ioctl);
1156 if (!ok) {
1157 torture_fail(torture, "setup copy chunk error");
1160 cc_copy.chunks[0].source_off = 0;
1161 cc_copy.chunks[0].target_off = 0;
1162 cc_copy.chunks[0].length = 4096;
1164 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1165 &cc_copy,
1166 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1167 torture_assert_ndr_success(torture, ndr_ret,
1168 "ndr_push_srv_copychunk_copy");
1170 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1171 torture_assert_ntstatus_equal(torture, status,
1172 NT_STATUS_ACCESS_DENIED,
1173 "FSCTL_SRV_COPYCHUNK");
1175 smb2_util_close(tree, src_h);
1176 smb2_util_close(tree, dest_h);
1178 /* no write permission on dest */
1179 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1180 1, /* 1 chunk */
1181 &src_h, 4096, /* fill 4096 byte src file */
1182 SEC_RIGHTS_FILE_ALL,
1183 &dest_h, 0, /* 0 byte dest file */
1184 (SEC_RIGHTS_FILE_READ
1185 | SEC_RIGHTS_FILE_EXECUTE),
1186 &cc_copy,
1187 &ioctl);
1188 if (!ok) {
1189 torture_fail(torture, "setup copy chunk error");
1192 cc_copy.chunks[0].source_off = 0;
1193 cc_copy.chunks[0].target_off = 0;
1194 cc_copy.chunks[0].length = 4096;
1196 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1197 &cc_copy,
1198 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1199 torture_assert_ndr_success(torture, ndr_ret,
1200 "ndr_push_srv_copychunk_copy");
1202 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1203 torture_assert_ntstatus_equal(torture, status,
1204 NT_STATUS_ACCESS_DENIED,
1205 "FSCTL_SRV_COPYCHUNK");
1207 smb2_util_close(tree, src_h);
1208 smb2_util_close(tree, dest_h);
1210 /* no read permission on dest */
1211 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1212 1, /* 1 chunk */
1213 &src_h, 4096, /* fill 4096 byte src file */
1214 SEC_RIGHTS_FILE_ALL,
1215 &dest_h, 0, /* 0 byte dest file */
1216 (SEC_RIGHTS_FILE_WRITE
1217 | SEC_RIGHTS_FILE_EXECUTE),
1218 &cc_copy,
1219 &ioctl);
1220 if (!ok) {
1221 torture_fail(torture, "setup copy chunk error");
1224 cc_copy.chunks[0].source_off = 0;
1225 cc_copy.chunks[0].target_off = 0;
1226 cc_copy.chunks[0].length = 4096;
1228 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1229 &cc_copy,
1230 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1231 torture_assert_ndr_success(torture, ndr_ret,
1232 "ndr_push_srv_copychunk_copy");
1235 * FSCTL_SRV_COPYCHUNK requires read permission on dest,
1236 * FSCTL_SRV_COPYCHUNK_WRITE (not supported by Samba) on the other hand
1237 * does not.
1239 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1240 torture_assert_ntstatus_equal(torture, status,
1241 NT_STATUS_ACCESS_DENIED,
1242 "FSCTL_SRV_COPYCHUNK");
1244 smb2_util_close(tree, src_h);
1245 smb2_util_close(tree, dest_h);
1246 talloc_free(tmp_ctx);
1248 return true;
1251 static bool test_ioctl_copy_chunk_src_exceed(struct torture_context *torture,
1252 struct smb2_tree *tree)
1254 struct smb2_handle src_h;
1255 struct smb2_handle dest_h;
1256 NTSTATUS status;
1257 union smb_ioctl ioctl;
1258 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1259 struct srv_copychunk_copy cc_copy;
1260 struct srv_copychunk_rsp cc_rsp;
1261 enum ndr_err_code ndr_ret;
1262 bool ok;
1264 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1265 1, /* 1 chunk */
1266 &src_h, 4096, /* fill 4096 byte src file */
1267 SEC_RIGHTS_FILE_ALL,
1268 &dest_h, 0, /* 0 byte dest file */
1269 SEC_RIGHTS_FILE_ALL,
1270 &cc_copy,
1271 &ioctl);
1272 if (!ok) {
1273 torture_fail(torture, "setup copy chunk error");
1276 /* Request copy where off + length exceeds size of src */
1277 cc_copy.chunks[0].source_off = 1024;
1278 cc_copy.chunks[0].target_off = 0;
1279 cc_copy.chunks[0].length = 4096;
1281 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1282 &cc_copy,
1283 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1284 torture_assert_ndr_success(torture, ndr_ret,
1285 "ndr_push_srv_copychunk_copy");
1287 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1288 torture_assert_ntstatus_equal(torture, status,
1289 NT_STATUS_INVALID_VIEW_SIZE,
1290 "FSCTL_SRV_COPYCHUNK oversize");
1292 /* Request copy where length exceeds size of src */
1293 cc_copy.chunks[0].source_off = 1024;
1294 cc_copy.chunks[0].target_off = 0;
1295 cc_copy.chunks[0].length = 3072;
1297 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1298 &cc_copy,
1299 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1300 torture_assert_ndr_success(torture, ndr_ret,
1301 "ndr_push_srv_copychunk_copy");
1303 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1304 torture_assert_ntstatus_ok(torture, status,
1305 "FSCTL_SRV_COPYCHUNK just right");
1307 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1308 &cc_rsp,
1309 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1310 torture_assert_ndr_success(torture, ndr_ret,
1311 "ndr_pull_srv_copychunk_rsp");
1313 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1314 1, /* chunks written */
1315 0, /* chunk bytes unsuccessfully written */
1316 3072); /* total bytes written */
1317 if (!ok) {
1318 torture_fail(torture, "bad copy chunk response data");
1321 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 3072, 1024);
1322 if (!ok) {
1323 torture_fail(torture, "inconsistent file data");
1326 smb2_util_close(tree, src_h);
1327 smb2_util_close(tree, dest_h);
1328 talloc_free(tmp_ctx);
1329 return true;
1332 static bool
1333 test_ioctl_copy_chunk_src_exceed_multi(struct torture_context *torture,
1334 struct smb2_tree *tree)
1336 struct smb2_handle src_h;
1337 struct smb2_handle dest_h;
1338 NTSTATUS status;
1339 union smb_ioctl ioctl;
1340 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1341 struct srv_copychunk_copy cc_copy;
1342 struct srv_copychunk_rsp cc_rsp;
1343 enum ndr_err_code ndr_ret;
1344 bool ok;
1346 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1347 2, /* 2 chunks */
1348 &src_h, 8192, /* fill 8192 byte src file */
1349 SEC_RIGHTS_FILE_ALL,
1350 &dest_h, 0, /* 0 byte dest file */
1351 SEC_RIGHTS_FILE_ALL,
1352 &cc_copy,
1353 &ioctl);
1354 if (!ok) {
1355 torture_fail(torture, "setup copy chunk error");
1358 /* Request copy where off + length exceeds size of src */
1359 cc_copy.chunks[0].source_off = 0;
1360 cc_copy.chunks[0].target_off = 0;
1361 cc_copy.chunks[0].length = 4096;
1363 cc_copy.chunks[1].source_off = 4096;
1364 cc_copy.chunks[1].target_off = 4096;
1365 cc_copy.chunks[1].length = 8192;
1367 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1368 &cc_copy,
1369 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1370 torture_assert_ndr_success(torture, ndr_ret,
1371 "ndr_push_srv_copychunk_copy");
1373 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1374 torture_assert_ntstatus_equal(torture, status,
1375 NT_STATUS_INVALID_VIEW_SIZE,
1376 "FSCTL_SRV_COPYCHUNK oversize");
1377 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1378 &cc_rsp,
1379 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1380 torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
1382 /* first chunk should still be written */
1383 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1384 1, /* chunks written */
1385 0, /* chunk bytes unsuccessfully written */
1386 4096); /* total bytes written */
1387 if (!ok) {
1388 torture_fail(torture, "bad copy chunk response data");
1390 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
1391 if (!ok) {
1392 torture_fail(torture, "inconsistent file data");
1395 smb2_util_close(tree, src_h);
1396 smb2_util_close(tree, dest_h);
1397 talloc_free(tmp_ctx);
1398 return true;
1401 static bool test_ioctl_copy_chunk_sparse_dest(struct torture_context *torture,
1402 struct smb2_tree *tree)
1404 struct smb2_handle src_h;
1405 struct smb2_handle dest_h;
1406 NTSTATUS status;
1407 union smb_ioctl ioctl;
1408 struct smb2_read r;
1409 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1410 struct srv_copychunk_copy cc_copy;
1411 struct srv_copychunk_rsp cc_rsp;
1412 enum ndr_err_code ndr_ret;
1413 bool ok;
1414 int i;
1416 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1417 1, /* 1 chunk */
1418 &src_h, 4096, /* fill 4096 byte src file */
1419 SEC_RIGHTS_FILE_ALL,
1420 &dest_h, 0, /* 0 byte dest file */
1421 SEC_RIGHTS_FILE_ALL,
1422 &cc_copy,
1423 &ioctl);
1424 if (!ok) {
1425 torture_fail(torture, "setup copy chunk error");
1428 /* copy all src file data (via a single chunk desc) */
1429 cc_copy.chunks[0].source_off = 0;
1430 cc_copy.chunks[0].target_off = 4096;
1431 cc_copy.chunks[0].length = 4096;
1433 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1434 &cc_copy,
1435 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1436 torture_assert_ndr_success(torture, ndr_ret,
1437 "ndr_push_srv_copychunk_copy");
1439 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1440 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
1442 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1443 &cc_rsp,
1444 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1445 torture_assert_ndr_success(torture, ndr_ret,
1446 "ndr_pull_srv_copychunk_rsp");
1448 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1449 1, /* chunks written */
1450 0, /* chunk bytes unsuccessfully written */
1451 4096); /* total bytes written */
1452 if (!ok) {
1453 torture_fail(torture, "bad copy chunk response data");
1456 /* check for zeros in first 4k */
1457 ZERO_STRUCT(r);
1458 r.in.file.handle = dest_h;
1459 r.in.length = 4096;
1460 r.in.offset = 0;
1461 status = smb2_read(tree, tmp_ctx, &r);
1462 torture_assert_ntstatus_ok(torture, status, "read");
1464 torture_assert_u64_equal(torture, r.out.data.length, 4096,
1465 "read data len mismatch");
1467 for (i = 0; i < 4096; i++) {
1468 torture_assert(torture, (r.out.data.data[i] == 0),
1469 "sparse did not pass class");
1472 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 4096, 4096, 0);
1473 if (!ok) {
1474 torture_fail(torture, "inconsistent file data");
1477 smb2_util_close(tree, src_h);
1478 smb2_util_close(tree, dest_h);
1479 talloc_free(tmp_ctx);
1480 return true;
1484 * set the ioctl MaxOutputResponse size to less than
1485 * sizeof(struct srv_copychunk_rsp)
1487 static bool test_ioctl_copy_chunk_max_output_sz(struct torture_context *torture,
1488 struct smb2_tree *tree)
1490 struct smb2_handle src_h;
1491 struct smb2_handle dest_h;
1492 NTSTATUS status;
1493 union smb_ioctl ioctl;
1494 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1495 struct srv_copychunk_copy cc_copy;
1496 enum ndr_err_code ndr_ret;
1497 bool ok;
1499 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1500 1, /* 1 chunk */
1501 &src_h, 4096, /* fill 4096 byte src file */
1502 SEC_RIGHTS_FILE_ALL,
1503 &dest_h, 0, /* 0 byte dest file */
1504 SEC_RIGHTS_FILE_ALL,
1505 &cc_copy,
1506 &ioctl);
1507 if (!ok) {
1508 torture_fail(torture, "setup copy chunk error");
1511 cc_copy.chunks[0].source_off = 0;
1512 cc_copy.chunks[0].target_off = 0;
1513 cc_copy.chunks[0].length = 4096;
1514 /* req is valid, but use undersize max_response_size */
1515 ioctl.smb2.in.max_response_size = sizeof(struct srv_copychunk_rsp) - 1;
1517 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1518 &cc_copy,
1519 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1520 torture_assert_ndr_success(torture, ndr_ret,
1521 "ndr_push_srv_copychunk_copy");
1523 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1524 torture_assert_ntstatus_equal(torture, status,
1525 NT_STATUS_INVALID_PARAMETER,
1526 "FSCTL_SRV_COPYCHUNK");
1528 smb2_util_close(tree, src_h);
1529 smb2_util_close(tree, dest_h);
1530 talloc_free(tmp_ctx);
1531 return true;
1534 static NTSTATUS test_ioctl_compress_get(struct torture_context *torture,
1535 TALLOC_CTX *mem_ctx,
1536 struct smb2_tree *tree,
1537 struct smb2_handle fh,
1538 uint16_t *_compression_fmt)
1540 union smb_ioctl ioctl;
1541 struct compression_state cmpr_state;
1542 enum ndr_err_code ndr_ret;
1543 NTSTATUS status;
1545 ZERO_STRUCT(ioctl);
1546 ioctl.smb2.level = RAW_IOCTL_SMB2;
1547 ioctl.smb2.in.file.handle = fh;
1548 ioctl.smb2.in.function = FSCTL_GET_COMPRESSION;
1549 ioctl.smb2.in.max_response_size = sizeof(struct compression_state);
1550 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
1552 status = smb2_ioctl(tree, mem_ctx, &ioctl.smb2);
1553 if (!NT_STATUS_IS_OK(status)) {
1554 return status;
1557 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, mem_ctx,
1558 &cmpr_state,
1559 (ndr_pull_flags_fn_t)ndr_pull_compression_state);
1561 if (ndr_ret != NDR_ERR_SUCCESS) {
1562 return NT_STATUS_INTERNAL_ERROR;
1565 *_compression_fmt = cmpr_state.format;
1566 return NT_STATUS_OK;
1569 static NTSTATUS test_ioctl_compress_set(struct torture_context *torture,
1570 TALLOC_CTX *mem_ctx,
1571 struct smb2_tree *tree,
1572 struct smb2_handle fh,
1573 uint16_t compression_fmt)
1575 union smb_ioctl ioctl;
1576 struct compression_state cmpr_state;
1577 enum ndr_err_code ndr_ret;
1578 NTSTATUS status;
1580 ZERO_STRUCT(ioctl);
1581 ioctl.smb2.level = RAW_IOCTL_SMB2;
1582 ioctl.smb2.in.file.handle = fh;
1583 ioctl.smb2.in.function = FSCTL_SET_COMPRESSION;
1584 ioctl.smb2.in.max_response_size = 0;
1585 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
1587 cmpr_state.format = compression_fmt;
1588 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, mem_ctx,
1589 &cmpr_state,
1590 (ndr_push_flags_fn_t)ndr_push_compression_state);
1591 if (ndr_ret != NDR_ERR_SUCCESS) {
1592 return NT_STATUS_INTERNAL_ERROR;
1595 status = smb2_ioctl(tree, mem_ctx, &ioctl.smb2);
1596 return status;
1599 static bool test_ioctl_compress_file_flag(struct torture_context *torture,
1600 struct smb2_tree *tree)
1602 struct smb2_handle fh;
1603 NTSTATUS status;
1604 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1605 bool ok;
1606 uint16_t compression_fmt;
1608 ok = test_setup_create_fill(torture, tree, tmp_ctx,
1609 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL);
1610 torture_assert(torture, ok, "setup compression file");
1612 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1613 &compression_fmt);
1614 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_DEVICE_REQUEST)) {
1615 smb2_util_close(tree, fh);
1616 torture_skip(torture, "FSCTL_GET_COMPRESSION not supported\n");
1618 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1620 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
1621 "initial compression state not NONE");
1623 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
1624 COMPRESSION_FORMAT_DEFAULT);
1625 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
1627 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1628 &compression_fmt);
1629 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1631 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
1632 "invalid compression state after set");
1634 smb2_util_close(tree, fh);
1635 talloc_free(tmp_ctx);
1636 return true;
1639 static bool test_ioctl_compress_dir_inherit(struct torture_context *torture,
1640 struct smb2_tree *tree)
1642 struct smb2_handle dirh;
1643 struct smb2_handle fh;
1644 NTSTATUS status;
1645 struct smb2_create io;
1646 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1647 uint16_t compression_fmt;
1648 bool ok;
1649 char path_buf[PATH_MAX];
1651 smb2_deltree(tree, DNAME);
1652 status = smb2_util_mkdir(tree, DNAME);
1653 torture_assert_ntstatus_ok(torture, status, "mkdir");
1655 ZERO_STRUCT(io);
1656 io.in.desired_access = SEC_RIGHTS_FILE_ALL;
1657 io.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
1658 io.in.create_disposition = NTCREATEX_DISP_OPEN;
1659 io.in.share_access =
1660 NTCREATEX_SHARE_ACCESS_DELETE|
1661 NTCREATEX_SHARE_ACCESS_READ|
1662 NTCREATEX_SHARE_ACCESS_WRITE;
1663 io.in.fname = DNAME;
1665 status = smb2_create(tree, tmp_ctx, &io);
1666 torture_assert_ntstatus_ok(torture, status, "dir create");
1667 dirh = io.out.file.handle;
1669 /* set compression on base share, then check for file inheritance */
1670 status = test_ioctl_compress_set(torture, tmp_ctx, tree, dirh,
1671 COMPRESSION_FORMAT_LZNT1);
1672 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_DEVICE_REQUEST)) {
1673 smb2_util_close(tree, dirh);
1674 smb2_deltree(tree, DNAME);
1675 torture_skip(torture, "FSCTL_SET_COMPRESSION not supported\n");
1677 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
1679 status = test_ioctl_compress_get(torture, tmp_ctx, tree, dirh,
1680 &compression_fmt);
1681 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1683 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
1684 "invalid compression state after set");
1686 snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME);
1687 ok = test_setup_create_fill(torture, tree, tmp_ctx,
1688 path_buf, &fh, 4096, SEC_RIGHTS_FILE_ALL);
1689 torture_assert(torture, ok, "setup compression file");
1691 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1692 &compression_fmt);
1693 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1695 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
1696 "compression attr not inherited by new file");
1698 /* check compressed data is consistent */
1699 ok = check_pattern(torture, tree, tmp_ctx, fh, 0, 4096, 0);
1701 /* disable dir compression attr, file should remain compressed */
1702 status = test_ioctl_compress_set(torture, tmp_ctx, tree, dirh,
1703 COMPRESSION_FORMAT_NONE);
1704 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
1706 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1707 &compression_fmt);
1708 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1710 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
1711 "file compression attr removed after dir change");
1712 smb2_util_close(tree, fh);
1714 /* new files should no longer inherit compression attr */
1715 snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME2);
1716 ok = test_setup_create_fill(torture, tree, tmp_ctx,
1717 path_buf, &fh, 0, SEC_RIGHTS_FILE_ALL);
1718 torture_assert(torture, ok, "setup file");
1720 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1721 &compression_fmt);
1722 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1724 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
1725 "compression attr present on new file");
1727 smb2_util_close(tree, fh);
1728 smb2_util_close(tree, dirh);
1729 smb2_deltree(tree, DNAME);
1730 talloc_free(tmp_ctx);
1731 return true;
1734 static bool test_ioctl_compress_invalid_format(struct torture_context *torture,
1735 struct smb2_tree *tree)
1737 struct smb2_handle fh;
1738 NTSTATUS status;
1739 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1740 bool ok;
1741 uint16_t compression_fmt;
1743 ok = test_setup_create_fill(torture, tree, tmp_ctx,
1744 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL);
1745 torture_assert(torture, ok, "setup compression file");
1747 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
1748 0x0042); /* bogus */
1749 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_DEVICE_REQUEST)) {
1750 smb2_util_close(tree, fh);
1751 torture_skip(torture, "FSCTL_SET_COMPRESSION not supported\n");
1753 torture_assert_ntstatus_equal(torture, status,
1754 NT_STATUS_INVALID_PARAMETER,
1755 "invalid FSCTL_SET_COMPRESSION");
1757 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1758 &compression_fmt);
1759 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1761 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
1762 "initial compression state not NONE");
1764 smb2_util_close(tree, fh);
1765 talloc_free(tmp_ctx);
1766 return true;
1769 static bool test_ioctl_compress_invalid_buf(struct torture_context *torture,
1770 struct smb2_tree *tree)
1772 struct smb2_handle fh;
1773 NTSTATUS status;
1774 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1775 bool ok;
1776 union smb_ioctl ioctl;
1778 ok = test_setup_create_fill(torture, tree, tmp_ctx,
1779 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL);
1780 torture_assert(torture, ok, "setup compression file");
1782 ZERO_STRUCT(ioctl);
1783 ioctl.smb2.level = RAW_IOCTL_SMB2;
1784 ioctl.smb2.in.file.handle = fh;
1785 ioctl.smb2.in.function = FSCTL_GET_COMPRESSION;
1786 ioctl.smb2.in.max_response_size = 0; /* no room for rsp data */
1787 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
1789 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1790 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_DEVICE_REQUEST)) {
1791 smb2_util_close(tree, fh);
1792 torture_skip(torture, "FSCTL_GET_COMPRESSION not supported\n");
1794 /* expect Server 2k12 response status */
1795 torture_assert_ntstatus_equal(torture, status,
1796 NT_STATUS_INVALID_USER_BUFFER,
1797 "invalid FSCTL_SET_COMPRESSION");
1799 smb2_util_close(tree, fh);
1800 talloc_free(tmp_ctx);
1801 return true;
1805 basic testing of SMB2 ioctls
1807 struct torture_suite *torture_smb2_ioctl_init(void)
1809 struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "ioctl");
1811 torture_suite_add_1smb2_test(suite, "shadow_copy",
1812 test_ioctl_get_shadow_copy);
1813 torture_suite_add_1smb2_test(suite, "req_resume_key",
1814 test_ioctl_req_resume_key);
1815 torture_suite_add_1smb2_test(suite, "copy_chunk_simple",
1816 test_ioctl_copy_chunk_simple);
1817 torture_suite_add_1smb2_test(suite, "copy_chunk_multi",
1818 test_ioctl_copy_chunk_multi);
1819 torture_suite_add_1smb2_test(suite, "copy_chunk_tiny",
1820 test_ioctl_copy_chunk_tiny);
1821 torture_suite_add_1smb2_test(suite, "copy_chunk_overwrite",
1822 test_ioctl_copy_chunk_over);
1823 torture_suite_add_1smb2_test(suite, "copy_chunk_append",
1824 test_ioctl_copy_chunk_append);
1825 torture_suite_add_1smb2_test(suite, "copy_chunk_limits",
1826 test_ioctl_copy_chunk_limits);
1827 torture_suite_add_1smb2_test(suite, "copy_chunk_src_lock",
1828 test_ioctl_copy_chunk_src_lck);
1829 torture_suite_add_1smb2_test(suite, "copy_chunk_dest_lock",
1830 test_ioctl_copy_chunk_dest_lck);
1831 torture_suite_add_1smb2_test(suite, "copy_chunk_bad_key",
1832 test_ioctl_copy_chunk_bad_key);
1833 torture_suite_add_1smb2_test(suite, "copy_chunk_src_is_dest",
1834 test_ioctl_copy_chunk_src_is_dest);
1835 torture_suite_add_1smb2_test(suite, "copy_chunk_src_is_dest_overlap",
1836 test_ioctl_copy_chunk_src_is_dest_overlap);
1837 torture_suite_add_1smb2_test(suite, "copy_chunk_bad_access",
1838 test_ioctl_copy_chunk_bad_access);
1839 torture_suite_add_1smb2_test(suite, "copy_chunk_src_exceed",
1840 test_ioctl_copy_chunk_src_exceed);
1841 torture_suite_add_1smb2_test(suite, "copy_chunk_src_exceed_multi",
1842 test_ioctl_copy_chunk_src_exceed_multi);
1843 torture_suite_add_1smb2_test(suite, "copy_chunk_sparse_dest",
1844 test_ioctl_copy_chunk_sparse_dest);
1845 torture_suite_add_1smb2_test(suite, "copy_chunk_max_output_sz",
1846 test_ioctl_copy_chunk_max_output_sz);
1847 torture_suite_add_1smb2_test(suite, "compress_file_flag",
1848 test_ioctl_compress_file_flag);
1849 torture_suite_add_1smb2_test(suite, "compress_dir_inherit",
1850 test_ioctl_compress_dir_inherit);
1851 torture_suite_add_1smb2_test(suite, "compress_invalid_format",
1852 test_ioctl_compress_invalid_format);
1853 torture_suite_add_1smb2_test(suite, "compress_invalid_buf",
1854 test_ioctl_compress_invalid_buf);
1856 suite->description = talloc_strdup(suite, "SMB2-IOCTL tests");
1858 return suite;