s4/torture: add a test for copy-chunk across shares
[Samba.git] / source4 / torture / smb2 / ioctl.c
blob3ccf7be2d8cf79a79f5f9958124272c6e92d0526
1 /*
2 Unix SMB/CIFS implementation.
4 test suite for SMB2 ioctl operations
6 Copyright (C) David Disseldorp 2011-2016
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 "../libcli/smb/smbXcli_base.h"
29 #include "librpc/gen_ndr/ndr_ioctl.h"
31 #define FNAME "testfsctl.dat"
32 #define FNAME2 "testfsctl2.dat"
33 #define DNAME "testfsctl_dir"
36 basic testing of SMB2 shadow copy calls
38 static bool test_ioctl_get_shadow_copy(struct torture_context *torture,
39 struct smb2_tree *tree)
41 struct smb2_handle h;
42 uint8_t buf[100];
43 NTSTATUS status;
44 union smb_ioctl ioctl;
45 TALLOC_CTX *tmp_ctx = talloc_new(tree);
47 smb2_util_unlink(tree, FNAME);
49 status = torture_smb2_testfile(tree, FNAME, &h);
50 torture_assert_ntstatus_ok(torture, status, "create write");
52 ZERO_ARRAY(buf);
53 status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf));
54 torture_assert_ntstatus_ok(torture, status, "write");
56 ZERO_STRUCT(ioctl);
57 ioctl.smb2.level = RAW_IOCTL_SMB2;
58 ioctl.smb2.in.file.handle = h;
59 ioctl.smb2.in.function = FSCTL_SRV_ENUM_SNAPS;
60 ioctl.smb2.in.max_response_size = 16;
61 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
63 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
64 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)
65 || NT_STATUS_EQUAL(status, NT_STATUS_INVALID_DEVICE_REQUEST)) {
66 torture_skip(torture, "FSCTL_SRV_ENUM_SNAPS not supported\n");
68 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_ENUM_SNAPS");
70 return true;
74 basic testing of the SMB2 server side copy ioctls
76 static bool test_ioctl_req_resume_key(struct torture_context *torture,
77 struct smb2_tree *tree)
79 struct smb2_handle h;
80 uint8_t buf[100];
81 NTSTATUS status;
82 union smb_ioctl ioctl;
83 TALLOC_CTX *tmp_ctx = talloc_new(tree);
84 struct req_resume_key_rsp res_key;
85 enum ndr_err_code ndr_ret;
87 smb2_util_unlink(tree, FNAME);
89 status = torture_smb2_testfile(tree, FNAME, &h);
90 torture_assert_ntstatus_ok(torture, status, "create write");
92 ZERO_ARRAY(buf);
93 status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf));
94 torture_assert_ntstatus_ok(torture, status, "write");
96 ZERO_STRUCT(ioctl);
97 ioctl.smb2.level = RAW_IOCTL_SMB2;
98 ioctl.smb2.in.file.handle = h;
99 ioctl.smb2.in.function = FSCTL_SRV_REQUEST_RESUME_KEY;
100 ioctl.smb2.in.max_response_size = 32;
101 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
103 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
104 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_REQUEST_RESUME_KEY");
106 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, &res_key,
107 (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
108 torture_assert_ndr_success(torture, ndr_ret,
109 "ndr_pull_req_resume_key_rsp");
111 ndr_print_debug((ndr_print_fn_t)ndr_print_req_resume_key_rsp, "yo", &res_key);
113 talloc_free(tmp_ctx);
114 return true;
117 static uint64_t patt_hash(uint64_t off)
119 return off;
122 static bool write_pattern(struct torture_context *torture,
123 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
124 struct smb2_handle h, uint64_t off, uint64_t len,
125 uint64_t patt_off)
127 NTSTATUS status;
128 uint64_t i;
129 uint8_t *buf;
130 uint64_t io_sz = MIN(1024 * 64, len);
132 if (len == 0) {
133 return true;
136 torture_assert(torture, (len % 8) == 0, "invalid write len");
138 buf = talloc_zero_size(mem_ctx, io_sz);
139 torture_assert(torture, (buf != NULL), "no memory for file data buf");
141 while (len > 0) {
142 for (i = 0; i <= io_sz - 8; i += 8) {
143 SBVAL(buf, i, patt_hash(patt_off));
144 patt_off += 8;
147 status = smb2_util_write(tree, h,
148 buf, off, io_sz);
149 torture_assert_ntstatus_ok(torture, status, "file write");
151 len -= io_sz;
152 off += io_sz;
155 talloc_free(buf);
157 return true;
160 static bool check_pattern(struct torture_context *torture,
161 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
162 struct smb2_handle h, uint64_t off, uint64_t len,
163 uint64_t patt_off)
165 if (len == 0) {
166 return true;
169 torture_assert(torture, (len % 8) == 0, "invalid read len");
171 while (len > 0) {
172 uint64_t i;
173 struct smb2_read r;
174 NTSTATUS status;
175 uint64_t io_sz = MIN(1024 * 64, len);
177 ZERO_STRUCT(r);
178 r.in.file.handle = h;
179 r.in.length = io_sz;
180 r.in.offset = off;
181 status = smb2_read(tree, mem_ctx, &r);
182 torture_assert_ntstatus_ok(torture, status, "read");
184 torture_assert_u64_equal(torture, r.out.data.length, io_sz,
185 "read data len mismatch");
187 for (i = 0; i <= io_sz - 8; i += 8, patt_off += 8) {
188 uint64_t data = BVAL(r.out.data.data, i);
189 torture_assert_u64_equal(torture, data, patt_hash(patt_off),
190 talloc_asprintf(torture, "read data "
191 "pattern bad at %llu\n",
192 (unsigned long long)off + i));
194 talloc_free(r.out.data.data);
195 len -= io_sz;
196 off += io_sz;
199 return true;
202 static bool check_zero(struct torture_context *torture,
203 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
204 struct smb2_handle h, uint64_t off, uint64_t len)
206 uint64_t i;
207 struct smb2_read r;
208 NTSTATUS status;
210 if (len == 0) {
211 return true;
214 ZERO_STRUCT(r);
215 r.in.file.handle = h;
216 r.in.length = len;
217 r.in.offset = off;
218 status = smb2_read(tree, mem_ctx, &r);
219 torture_assert_ntstatus_ok(torture, status, "read");
221 torture_assert_u64_equal(torture, r.out.data.length, len,
222 "read data len mismatch");
224 for (i = 0; i <= len - 8; i += 8) {
225 uint64_t data = BVAL(r.out.data.data, i);
226 torture_assert_u64_equal(torture, data, 0,
227 talloc_asprintf(mem_ctx, "read zero "
228 "bad at %llu\n",
229 (unsigned long long)i));
232 talloc_free(r.out.data.data);
233 return true;
236 static bool test_setup_open(struct torture_context *torture,
237 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
238 const char *fname,
239 struct smb2_handle *fh,
240 uint32_t desired_access,
241 uint32_t file_attributes)
243 struct smb2_create io;
244 NTSTATUS status;
246 ZERO_STRUCT(io);
247 io.in.desired_access = desired_access;
248 io.in.file_attributes = file_attributes;
249 io.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
250 io.in.share_access =
251 NTCREATEX_SHARE_ACCESS_DELETE|
252 NTCREATEX_SHARE_ACCESS_READ|
253 NTCREATEX_SHARE_ACCESS_WRITE;
254 if (file_attributes & FILE_ATTRIBUTE_DIRECTORY) {
255 io.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
257 io.in.fname = fname;
259 status = smb2_create(tree, mem_ctx, &io);
260 torture_assert_ntstatus_ok(torture, status, "file create");
262 *fh = io.out.file.handle;
264 return true;
267 static bool test_setup_create_fill(struct torture_context *torture,
268 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
269 const char *fname,
270 struct smb2_handle *fh,
271 uint64_t size,
272 uint32_t desired_access,
273 uint32_t file_attributes)
275 bool ok;
276 uint32_t initial_access = desired_access;
278 if (size > 0) {
279 initial_access |= SEC_FILE_APPEND_DATA;
282 smb2_util_unlink(tree, fname);
284 ok = test_setup_open(torture, tree, mem_ctx,
285 fname,
287 initial_access,
288 file_attributes);
289 torture_assert(torture, ok, "file create");
291 if (size > 0) {
292 ok = write_pattern(torture, tree, mem_ctx, *fh, 0, size, 0);
293 torture_assert(torture, ok, "write pattern");
296 if (initial_access != desired_access) {
297 smb2_util_close(tree, *fh);
298 ok = test_setup_open(torture, tree, mem_ctx,
299 fname,
301 desired_access,
302 file_attributes);
303 torture_assert(torture, ok, "file open");
306 return true;
309 static bool test_setup_copy_chunk(struct torture_context *torture,
310 struct smb2_tree *src_tree,
311 struct smb2_tree *dst_tree,
312 TALLOC_CTX *mem_ctx,
313 uint32_t nchunks,
314 const char *src_name,
315 struct smb2_handle *src_h,
316 uint64_t src_size,
317 uint32_t src_desired_access,
318 const char *dst_name,
319 struct smb2_handle *dest_h,
320 uint64_t dest_size,
321 uint32_t dest_desired_access,
322 struct srv_copychunk_copy *cc_copy,
323 union smb_ioctl *ioctl)
325 struct req_resume_key_rsp res_key;
326 bool ok;
327 NTSTATUS status;
328 enum ndr_err_code ndr_ret;
330 ok = test_setup_create_fill(torture, src_tree, mem_ctx, src_name,
331 src_h, src_size, src_desired_access,
332 FILE_ATTRIBUTE_NORMAL);
333 torture_assert(torture, ok, "src file create fill");
335 ok = test_setup_create_fill(torture, dst_tree, mem_ctx, dst_name,
336 dest_h, dest_size, dest_desired_access,
337 FILE_ATTRIBUTE_NORMAL);
338 torture_assert(torture, ok, "dest file create fill");
340 ZERO_STRUCTPN(ioctl);
341 ioctl->smb2.level = RAW_IOCTL_SMB2;
342 ioctl->smb2.in.file.handle = *src_h;
343 ioctl->smb2.in.function = FSCTL_SRV_REQUEST_RESUME_KEY;
344 /* Allow for Key + ContextLength + Context */
345 ioctl->smb2.in.max_response_size = 32;
346 ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
348 status = smb2_ioctl(src_tree, mem_ctx, &ioctl->smb2);
349 torture_assert_ntstatus_ok(torture, status,
350 "FSCTL_SRV_REQUEST_RESUME_KEY");
352 ndr_ret = ndr_pull_struct_blob(&ioctl->smb2.out.out, mem_ctx, &res_key,
353 (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
355 torture_assert_ndr_success(torture, ndr_ret,
356 "ndr_pull_req_resume_key_rsp");
358 ZERO_STRUCTPN(ioctl);
359 ioctl->smb2.level = RAW_IOCTL_SMB2;
360 ioctl->smb2.in.file.handle = *dest_h;
361 ioctl->smb2.in.function = FSCTL_SRV_COPYCHUNK;
362 ioctl->smb2.in.max_response_size = sizeof(struct srv_copychunk_rsp);
363 ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
365 ZERO_STRUCTPN(cc_copy);
366 memcpy(cc_copy->source_key, res_key.resume_key, ARRAY_SIZE(cc_copy->source_key));
367 cc_copy->chunk_count = nchunks;
368 cc_copy->chunks = talloc_zero_array(mem_ctx, struct srv_copychunk, nchunks);
369 torture_assert(torture, (cc_copy->chunks != NULL), "no memory for chunks");
371 return true;
375 static bool check_copy_chunk_rsp(struct torture_context *torture,
376 struct srv_copychunk_rsp *cc_rsp,
377 uint32_t ex_chunks_written,
378 uint32_t ex_chunk_bytes_written,
379 uint32_t ex_total_bytes_written)
381 torture_assert_int_equal(torture, cc_rsp->chunks_written,
382 ex_chunks_written, "num chunks");
383 torture_assert_int_equal(torture, cc_rsp->chunk_bytes_written,
384 ex_chunk_bytes_written, "chunk bytes written");
385 torture_assert_int_equal(torture, cc_rsp->total_bytes_written,
386 ex_total_bytes_written, "chunk total bytes");
387 return true;
390 static bool test_ioctl_copy_chunk_simple(struct torture_context *torture,
391 struct smb2_tree *tree)
393 struct smb2_handle src_h;
394 struct smb2_handle dest_h;
395 NTSTATUS status;
396 union smb_ioctl ioctl;
397 TALLOC_CTX *tmp_ctx = talloc_new(tree);
398 struct srv_copychunk_copy cc_copy;
399 struct srv_copychunk_rsp cc_rsp;
400 enum ndr_err_code ndr_ret;
401 bool ok;
403 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
404 1, /* 1 chunk */
405 FNAME,
406 &src_h, 4096, /* fill 4096 byte src file */
407 SEC_RIGHTS_FILE_ALL,
408 FNAME2,
409 &dest_h, 0, /* 0 byte dest file */
410 SEC_RIGHTS_FILE_ALL,
411 &cc_copy,
412 &ioctl);
413 if (!ok) {
414 torture_fail(torture, "setup copy chunk error");
417 /* copy all src file data (via a single chunk desc) */
418 cc_copy.chunks[0].source_off = 0;
419 cc_copy.chunks[0].target_off = 0;
420 cc_copy.chunks[0].length = 4096;
422 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
423 &cc_copy,
424 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
425 torture_assert_ndr_success(torture, ndr_ret,
426 "ndr_push_srv_copychunk_copy");
428 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
429 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
431 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
432 &cc_rsp,
433 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
434 torture_assert_ndr_success(torture, ndr_ret,
435 "ndr_pull_srv_copychunk_rsp");
437 ok = check_copy_chunk_rsp(torture, &cc_rsp,
438 1, /* chunks written */
439 0, /* chunk bytes unsuccessfully written */
440 4096); /* total bytes written */
441 if (!ok) {
442 torture_fail(torture, "bad copy chunk response data");
445 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
446 if (!ok) {
447 torture_fail(torture, "inconsistent file data");
450 smb2_util_close(tree, src_h);
451 smb2_util_close(tree, dest_h);
452 talloc_free(tmp_ctx);
453 return true;
456 static bool test_ioctl_copy_chunk_multi(struct torture_context *torture,
457 struct smb2_tree *tree)
459 struct smb2_handle src_h;
460 struct smb2_handle dest_h;
461 NTSTATUS status;
462 union smb_ioctl ioctl;
463 TALLOC_CTX *tmp_ctx = talloc_new(tree);
464 struct srv_copychunk_copy cc_copy;
465 struct srv_copychunk_rsp cc_rsp;
466 enum ndr_err_code ndr_ret;
467 bool ok;
469 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
470 2, /* chunks */
471 FNAME,
472 &src_h, 8192, /* src file */
473 SEC_RIGHTS_FILE_ALL,
474 FNAME2,
475 &dest_h, 0, /* dest file */
476 SEC_RIGHTS_FILE_ALL,
477 &cc_copy,
478 &ioctl);
479 if (!ok) {
480 torture_fail(torture, "setup copy chunk error");
483 /* copy all src file data via two chunks */
484 cc_copy.chunks[0].source_off = 0;
485 cc_copy.chunks[0].target_off = 0;
486 cc_copy.chunks[0].length = 4096;
488 cc_copy.chunks[1].source_off = 4096;
489 cc_copy.chunks[1].target_off = 4096;
490 cc_copy.chunks[1].length = 4096;
492 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
493 &cc_copy,
494 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
495 torture_assert_ndr_success(torture, ndr_ret,
496 "ndr_push_srv_copychunk_copy");
498 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
499 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
501 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
502 &cc_rsp,
503 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
504 torture_assert_ndr_success(torture, ndr_ret,
505 "ndr_pull_srv_copychunk_rsp");
507 ok = check_copy_chunk_rsp(torture, &cc_rsp,
508 2, /* chunks written */
509 0, /* chunk bytes unsuccessfully written */
510 8192); /* total bytes written */
511 if (!ok) {
512 torture_fail(torture, "bad copy chunk response data");
515 smb2_util_close(tree, src_h);
516 smb2_util_close(tree, dest_h);
517 talloc_free(tmp_ctx);
518 return true;
521 static bool test_ioctl_copy_chunk_tiny(struct torture_context *torture,
522 struct smb2_tree *tree)
524 struct smb2_handle src_h;
525 struct smb2_handle dest_h;
526 NTSTATUS status;
527 union smb_ioctl ioctl;
528 TALLOC_CTX *tmp_ctx = talloc_new(tree);
529 struct srv_copychunk_copy cc_copy;
530 struct srv_copychunk_rsp cc_rsp;
531 enum ndr_err_code ndr_ret;
532 bool ok;
534 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
535 2, /* chunks */
536 FNAME,
537 &src_h, 96, /* src file */
538 SEC_RIGHTS_FILE_ALL,
539 FNAME2,
540 &dest_h, 0, /* dest file */
541 SEC_RIGHTS_FILE_ALL,
542 &cc_copy,
543 &ioctl);
544 if (!ok) {
545 torture_fail(torture, "setup copy chunk error");
548 /* copy all src file data via two chunks, sub block size chunks */
549 cc_copy.chunks[0].source_off = 0;
550 cc_copy.chunks[0].target_off = 0;
551 cc_copy.chunks[0].length = 48;
553 cc_copy.chunks[1].source_off = 48;
554 cc_copy.chunks[1].target_off = 48;
555 cc_copy.chunks[1].length = 48;
557 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
558 &cc_copy,
559 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
560 torture_assert_ndr_success(torture, ndr_ret,
561 "ndr_push_srv_copychunk_copy");
563 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
564 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
566 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
567 &cc_rsp,
568 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
569 torture_assert_ndr_success(torture, ndr_ret,
570 "ndr_pull_srv_copychunk_rsp");
572 ok = check_copy_chunk_rsp(torture, &cc_rsp,
573 2, /* chunks written */
574 0, /* chunk bytes unsuccessfully written */
575 96); /* total bytes written */
576 if (!ok) {
577 torture_fail(torture, "bad copy chunk response data");
580 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 96, 0);
581 if (!ok) {
582 torture_fail(torture, "inconsistent file data");
585 smb2_util_close(tree, src_h);
586 smb2_util_close(tree, dest_h);
587 talloc_free(tmp_ctx);
588 return true;
591 static bool test_ioctl_copy_chunk_over(struct torture_context *torture,
592 struct smb2_tree *tree)
594 struct smb2_handle src_h;
595 struct smb2_handle dest_h;
596 NTSTATUS status;
597 union smb_ioctl ioctl;
598 TALLOC_CTX *tmp_ctx = talloc_new(tree);
599 struct srv_copychunk_copy cc_copy;
600 struct srv_copychunk_rsp cc_rsp;
601 enum ndr_err_code ndr_ret;
602 bool ok;
604 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
605 2, /* chunks */
606 FNAME,
607 &src_h, 8192, /* src file */
608 SEC_RIGHTS_FILE_ALL,
609 FNAME2,
610 &dest_h, 4096, /* dest file */
611 SEC_RIGHTS_FILE_ALL,
612 &cc_copy,
613 &ioctl);
614 if (!ok) {
615 torture_fail(torture, "setup copy chunk error");
618 /* first chunk overwrites existing dest data */
619 cc_copy.chunks[0].source_off = 0;
620 cc_copy.chunks[0].target_off = 0;
621 cc_copy.chunks[0].length = 4096;
623 /* second chunk overwrites the first */
624 cc_copy.chunks[1].source_off = 4096;
625 cc_copy.chunks[1].target_off = 0;
626 cc_copy.chunks[1].length = 4096;
628 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
629 &cc_copy,
630 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
631 torture_assert_ndr_success(torture, ndr_ret,
632 "ndr_push_srv_copychunk_copy");
634 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
635 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
637 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
638 &cc_rsp,
639 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
640 torture_assert_ndr_success(torture, ndr_ret,
641 "ndr_pull_srv_copychunk_rsp");
643 ok = check_copy_chunk_rsp(torture, &cc_rsp,
644 2, /* chunks written */
645 0, /* chunk bytes unsuccessfully written */
646 8192); /* total bytes written */
647 if (!ok) {
648 torture_fail(torture, "bad copy chunk response data");
651 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 4096);
652 if (!ok) {
653 torture_fail(torture, "inconsistent file data");
656 smb2_util_close(tree, src_h);
657 smb2_util_close(tree, dest_h);
658 talloc_free(tmp_ctx);
659 return true;
662 static bool test_ioctl_copy_chunk_append(struct torture_context *torture,
663 struct smb2_tree *tree)
665 struct smb2_handle src_h;
666 struct smb2_handle dest_h;
667 NTSTATUS status;
668 union smb_ioctl ioctl;
669 TALLOC_CTX *tmp_ctx = talloc_new(tree);
670 struct srv_copychunk_copy cc_copy;
671 struct srv_copychunk_rsp cc_rsp;
672 enum ndr_err_code ndr_ret;
673 bool ok;
675 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
676 2, /* chunks */
677 FNAME,
678 &src_h, 4096, /* src file */
679 SEC_RIGHTS_FILE_ALL,
680 FNAME2,
681 &dest_h, 0, /* dest file */
682 SEC_RIGHTS_FILE_ALL,
683 &cc_copy,
684 &ioctl);
685 if (!ok) {
686 torture_fail(torture, "setup copy chunk error");
689 cc_copy.chunks[0].source_off = 0;
690 cc_copy.chunks[0].target_off = 0;
691 cc_copy.chunks[0].length = 4096;
693 /* second chunk appends the same data to the first */
694 cc_copy.chunks[1].source_off = 0;
695 cc_copy.chunks[1].target_off = 4096;
696 cc_copy.chunks[1].length = 4096;
698 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
699 &cc_copy,
700 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
701 torture_assert_ndr_success(torture, ndr_ret,
702 "ndr_push_srv_copychunk_copy");
704 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
705 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
707 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
708 &cc_rsp,
709 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
710 torture_assert_ndr_success(torture, ndr_ret,
711 "ndr_pull_srv_copychunk_rsp");
713 ok = check_copy_chunk_rsp(torture, &cc_rsp,
714 2, /* chunks written */
715 0, /* chunk bytes unsuccessfully written */
716 8192); /* total bytes written */
717 if (!ok) {
718 torture_fail(torture, "bad copy chunk response data");
721 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
722 if (!ok) {
723 torture_fail(torture, "inconsistent file data");
726 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 4096, 4096, 0);
727 if (!ok) {
728 torture_fail(torture, "inconsistent file data");
731 smb2_util_close(tree, src_h);
732 smb2_util_close(tree, dest_h);
733 talloc_free(tmp_ctx);
734 return true;
737 static bool test_ioctl_copy_chunk_limits(struct torture_context *torture,
738 struct smb2_tree *tree)
740 struct smb2_handle src_h;
741 struct smb2_handle dest_h;
742 NTSTATUS status;
743 union smb_ioctl ioctl;
744 TALLOC_CTX *tmp_ctx = talloc_new(tree);
745 struct srv_copychunk_copy cc_copy;
746 struct srv_copychunk_rsp cc_rsp;
747 enum ndr_err_code ndr_ret;
748 bool ok;
750 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
751 1, /* chunks */
752 FNAME,
753 &src_h, 4096, /* src file */
754 SEC_RIGHTS_FILE_ALL,
755 FNAME2,
756 &dest_h, 0, /* dest file */
757 SEC_RIGHTS_FILE_ALL,
758 &cc_copy,
759 &ioctl);
760 if (!ok) {
761 torture_fail(torture, "setup copy chunk error");
764 /* send huge chunk length request */
765 cc_copy.chunks[0].source_off = 0;
766 cc_copy.chunks[0].target_off = 0;
767 cc_copy.chunks[0].length = UINT_MAX;
769 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
770 &cc_copy,
771 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
772 torture_assert_ndr_success(torture, ndr_ret, "marshalling request");
774 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
775 torture_assert_ntstatus_equal(torture, status,
776 NT_STATUS_INVALID_PARAMETER,
777 "bad oversize chunk response");
779 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
780 &cc_rsp,
781 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
782 torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
784 torture_comment(torture, "limit max chunks, got %u\n",
785 cc_rsp.chunks_written);
786 torture_comment(torture, "limit max chunk len, got %u\n",
787 cc_rsp.chunk_bytes_written);
788 torture_comment(torture, "limit max total bytes, got %u\n",
789 cc_rsp.total_bytes_written);
791 smb2_util_close(tree, src_h);
792 smb2_util_close(tree, dest_h);
793 talloc_free(tmp_ctx);
794 return true;
797 static bool test_ioctl_copy_chunk_src_lck(struct torture_context *torture,
798 struct smb2_tree *tree)
800 struct smb2_handle src_h;
801 struct smb2_handle src_h2;
802 struct smb2_handle dest_h;
803 NTSTATUS status;
804 union smb_ioctl ioctl;
805 TALLOC_CTX *tmp_ctx = talloc_new(tree);
806 struct srv_copychunk_copy cc_copy;
807 struct srv_copychunk_rsp cc_rsp;
808 enum ndr_err_code ndr_ret;
809 bool ok;
810 struct smb2_lock lck;
811 struct smb2_lock_element el[1];
813 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
814 1, /* chunks */
815 FNAME,
816 &src_h, 4096, /* src file */
817 SEC_RIGHTS_FILE_ALL,
818 FNAME2,
819 &dest_h, 0, /* dest file */
820 SEC_RIGHTS_FILE_ALL,
821 &cc_copy,
822 &ioctl);
823 if (!ok) {
824 torture_fail(torture, "setup copy chunk error");
827 cc_copy.chunks[0].source_off = 0;
828 cc_copy.chunks[0].target_off = 0;
829 cc_copy.chunks[0].length = 4096;
831 /* open and lock the copychunk src file */
832 status = torture_smb2_testfile(tree, FNAME, &src_h2);
833 torture_assert_ntstatus_ok(torture, status, "2nd src open");
835 lck.in.lock_count = 0x0001;
836 lck.in.lock_sequence = 0x00000000;
837 lck.in.file.handle = src_h2;
838 lck.in.locks = el;
839 el[0].offset = cc_copy.chunks[0].source_off;
840 el[0].length = cc_copy.chunks[0].length;
841 el[0].reserved = 0;
842 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
844 status = smb2_lock(tree, &lck);
845 torture_assert_ntstatus_ok(torture, status, "lock");
847 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
848 &cc_copy,
849 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
850 torture_assert_ndr_success(torture, ndr_ret,
851 "ndr_push_srv_copychunk_copy");
853 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
855 * 2k12 & Samba return lock_conflict, Windows 7 & 2k8 return success...
857 * Edgar Olougouna @ MS wrote:
858 * Regarding the FSCTL_SRV_COPYCHUNK and STATUS_FILE_LOCK_CONFLICT
859 * discrepancy observed between Windows versions, we confirm that the
860 * behavior change is expected.
862 * CopyChunk in Windows Server 2012 use regular Readfile/Writefile APIs
863 * to move the chunks from the source to the destination.
864 * These ReadFile/WriteFile APIs go through the byte-range lock checks,
865 * and this explains the observed STATUS_FILE_LOCK_CONFLICT error.
867 * Prior to Windows Server 2012, CopyChunk used mapped sections to move
868 * the data. And byte range locks are not enforced on mapped I/O, and
869 * this explains the STATUS_SUCCESS observed on Windows Server 2008 R2.
871 torture_assert_ntstatus_equal(torture, status,
872 NT_STATUS_FILE_LOCK_CONFLICT,
873 "FSCTL_SRV_COPYCHUNK locked");
875 /* should get cc response data with the lock conflict status */
876 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
877 &cc_rsp,
878 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
879 torture_assert_ndr_success(torture, ndr_ret,
880 "ndr_pull_srv_copychunk_rsp");
881 ok = check_copy_chunk_rsp(torture, &cc_rsp,
882 0, /* chunks written */
883 0, /* chunk bytes unsuccessfully written */
884 0); /* total bytes written */
886 lck.in.lock_count = 0x0001;
887 lck.in.lock_sequence = 0x00000001;
888 lck.in.file.handle = src_h2;
889 lck.in.locks = el;
890 el[0].offset = cc_copy.chunks[0].source_off;
891 el[0].length = cc_copy.chunks[0].length;
892 el[0].reserved = 0;
893 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
894 status = smb2_lock(tree, &lck);
895 torture_assert_ntstatus_ok(torture, status, "unlock");
897 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
898 torture_assert_ntstatus_ok(torture, status,
899 "FSCTL_SRV_COPYCHUNK unlocked");
901 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
902 &cc_rsp,
903 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
904 torture_assert_ndr_success(torture, ndr_ret,
905 "ndr_pull_srv_copychunk_rsp");
907 ok = check_copy_chunk_rsp(torture, &cc_rsp,
908 1, /* chunks written */
909 0, /* chunk bytes unsuccessfully written */
910 4096); /* total bytes written */
911 if (!ok) {
912 torture_fail(torture, "bad copy chunk response data");
915 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
916 if (!ok) {
917 torture_fail(torture, "inconsistent file data");
920 smb2_util_close(tree, src_h2);
921 smb2_util_close(tree, src_h);
922 smb2_util_close(tree, dest_h);
923 talloc_free(tmp_ctx);
924 return true;
927 static bool test_ioctl_copy_chunk_dest_lck(struct torture_context *torture,
928 struct smb2_tree *tree)
930 struct smb2_handle src_h;
931 struct smb2_handle dest_h;
932 struct smb2_handle dest_h2;
933 NTSTATUS status;
934 union smb_ioctl ioctl;
935 TALLOC_CTX *tmp_ctx = talloc_new(tree);
936 struct srv_copychunk_copy cc_copy;
937 struct srv_copychunk_rsp cc_rsp;
938 enum ndr_err_code ndr_ret;
939 bool ok;
940 struct smb2_lock lck;
941 struct smb2_lock_element el[1];
943 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
944 1, /* chunks */
945 FNAME,
946 &src_h, 4096, /* src file */
947 SEC_RIGHTS_FILE_ALL,
948 FNAME2,
949 &dest_h, 4096, /* dest file */
950 SEC_RIGHTS_FILE_ALL,
951 &cc_copy,
952 &ioctl);
953 if (!ok) {
954 torture_fail(torture, "setup copy chunk error");
957 cc_copy.chunks[0].source_off = 0;
958 cc_copy.chunks[0].target_off = 0;
959 cc_copy.chunks[0].length = 4096;
961 /* open and lock the copychunk dest file */
962 status = torture_smb2_testfile(tree, FNAME2, &dest_h2);
963 torture_assert_ntstatus_ok(torture, status, "2nd src open");
965 lck.in.lock_count = 0x0001;
966 lck.in.lock_sequence = 0x00000000;
967 lck.in.file.handle = dest_h2;
968 lck.in.locks = el;
969 el[0].offset = cc_copy.chunks[0].target_off;
970 el[0].length = cc_copy.chunks[0].length;
971 el[0].reserved = 0;
972 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
974 status = smb2_lock(tree, &lck);
975 torture_assert_ntstatus_ok(torture, status, "lock");
977 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
978 &cc_copy,
979 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
980 torture_assert_ndr_success(torture, ndr_ret,
981 "ndr_push_srv_copychunk_copy");
983 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
984 torture_assert_ntstatus_equal(torture, status,
985 NT_STATUS_FILE_LOCK_CONFLICT,
986 "FSCTL_SRV_COPYCHUNK locked");
988 lck.in.lock_count = 0x0001;
989 lck.in.lock_sequence = 0x00000001;
990 lck.in.file.handle = dest_h2;
991 lck.in.locks = el;
992 el[0].offset = cc_copy.chunks[0].target_off;
993 el[0].length = cc_copy.chunks[0].length;
994 el[0].reserved = 0;
995 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
996 status = smb2_lock(tree, &lck);
997 torture_assert_ntstatus_ok(torture, status, "unlock");
999 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1000 torture_assert_ntstatus_ok(torture, status,
1001 "FSCTL_SRV_COPYCHUNK unlocked");
1003 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1004 &cc_rsp,
1005 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1006 torture_assert_ndr_success(torture, ndr_ret,
1007 "ndr_pull_srv_copychunk_rsp");
1009 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1010 1, /* chunks written */
1011 0, /* chunk bytes unsuccessfully written */
1012 4096); /* total bytes written */
1013 if (!ok) {
1014 torture_fail(torture, "bad copy chunk response data");
1017 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
1018 if (!ok) {
1019 torture_fail(torture, "inconsistent file data");
1022 smb2_util_close(tree, dest_h2);
1023 smb2_util_close(tree, src_h);
1024 smb2_util_close(tree, dest_h);
1025 talloc_free(tmp_ctx);
1026 return true;
1029 static bool test_ioctl_copy_chunk_bad_key(struct torture_context *torture,
1030 struct smb2_tree *tree)
1032 struct smb2_handle src_h;
1033 struct smb2_handle dest_h;
1034 NTSTATUS status;
1035 union smb_ioctl ioctl;
1036 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1037 struct srv_copychunk_copy cc_copy;
1038 enum ndr_err_code ndr_ret;
1039 bool ok;
1041 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
1043 FNAME,
1044 &src_h, 4096,
1045 SEC_RIGHTS_FILE_ALL,
1046 FNAME2,
1047 &dest_h, 0,
1048 SEC_RIGHTS_FILE_ALL,
1049 &cc_copy,
1050 &ioctl);
1051 if (!ok) {
1052 torture_fail(torture, "setup copy chunk error");
1055 /* overwrite the resume key with a bogus value */
1056 memcpy(cc_copy.source_key, "deadbeefdeadbeefdeadbeef", 24);
1058 cc_copy.chunks[0].source_off = 0;
1059 cc_copy.chunks[0].target_off = 0;
1060 cc_copy.chunks[0].length = 4096;
1062 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1063 &cc_copy,
1064 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1065 torture_assert_ndr_success(torture, ndr_ret,
1066 "ndr_push_srv_copychunk_copy");
1068 /* Server 2k12 returns NT_STATUS_OBJECT_NAME_NOT_FOUND */
1069 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1070 torture_assert_ntstatus_equal(torture, status,
1071 NT_STATUS_OBJECT_NAME_NOT_FOUND,
1072 "FSCTL_SRV_COPYCHUNK");
1074 smb2_util_close(tree, src_h);
1075 smb2_util_close(tree, dest_h);
1076 talloc_free(tmp_ctx);
1077 return true;
1080 static bool test_ioctl_copy_chunk_src_is_dest(struct torture_context *torture,
1081 struct smb2_tree *tree)
1083 struct smb2_handle src_h;
1084 struct smb2_handle dest_h;
1085 NTSTATUS status;
1086 union smb_ioctl ioctl;
1087 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1088 struct srv_copychunk_copy cc_copy;
1089 struct srv_copychunk_rsp cc_rsp;
1090 enum ndr_err_code ndr_ret;
1091 bool ok;
1093 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
1095 FNAME,
1096 &src_h, 8192,
1097 SEC_RIGHTS_FILE_ALL,
1098 FNAME2,
1099 &dest_h, 0,
1100 SEC_RIGHTS_FILE_ALL,
1101 &cc_copy,
1102 &ioctl);
1103 if (!ok) {
1104 torture_fail(torture, "setup copy chunk error");
1107 /* the source is also the destination */
1108 ioctl.smb2.in.file.handle = src_h;
1110 /* non-overlapping */
1111 cc_copy.chunks[0].source_off = 0;
1112 cc_copy.chunks[0].target_off = 4096;
1113 cc_copy.chunks[0].length = 4096;
1115 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1116 &cc_copy,
1117 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1118 torture_assert_ndr_success(torture, ndr_ret,
1119 "ndr_push_srv_copychunk_copy");
1121 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1122 torture_assert_ntstatus_ok(torture, status,
1123 "FSCTL_SRV_COPYCHUNK");
1125 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1126 &cc_rsp,
1127 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1128 torture_assert_ndr_success(torture, ndr_ret,
1129 "ndr_pull_srv_copychunk_rsp");
1131 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1132 1, /* chunks written */
1133 0, /* chunk bytes unsuccessfully written */
1134 4096); /* total bytes written */
1135 if (!ok) {
1136 torture_fail(torture, "bad copy chunk response data");
1139 ok = check_pattern(torture, tree, tmp_ctx, src_h, 0, 4096, 0);
1140 if (!ok) {
1141 torture_fail(torture, "inconsistent file data");
1143 ok = check_pattern(torture, tree, tmp_ctx, src_h, 4096, 4096, 0);
1144 if (!ok) {
1145 torture_fail(torture, "inconsistent file data");
1148 smb2_util_close(tree, src_h);
1149 smb2_util_close(tree, dest_h);
1150 talloc_free(tmp_ctx);
1151 return true;
1155 * Test a single-chunk copychunk request, where the source and target ranges
1156 * overlap, and the SourceKey refers to the same target file. E.g:
1158 * Initial State
1159 * -------------
1160 * File: src_and_dest
1161 * Offset: 0123456789
1162 * Data: abcdefghij
1164 * Request
1165 * -------
1166 * FSCTL_SRV_COPYCHUNK(src_and_dest)
1167 * SourceKey = SRV_REQUEST_RESUME_KEY(src_and_dest)
1168 * ChunkCount = 1
1169 * Chunks[0].SourceOffset = 0
1170 * Chunks[0].TargetOffset = 4
1171 * Chunks[0].Length = 6
1173 * Resultant State
1174 * ---------------
1175 * File: src_and_dest
1176 * Offset: 0123456789
1177 * Data: abcdabcdef
1179 * The resultant contents of src_and_dest is dependent on the server's
1180 * copy algorithm. In the above example, the server uses an IO buffer
1181 * large enough to hold the entire six-byte source data before writing
1182 * to TargetOffset. If the server were to use a four-byte IO buffer and
1183 * started reads/writes from the lowest offset, then the two overlapping
1184 * bytes in the above example would be overwritten before being read. The
1185 * resultant file contents would be abcdabcdab.
1187 * Windows 2008r2 appears to use a 2048 byte copy buffer, overlapping bytes
1188 * after this offset are written before being read. Windows 2012 on the
1189 * other hand appears to use a buffer large enough to hold its maximum
1190 * supported chunk size (1M). Samba currently uses a 64k copy buffer by
1191 * default (vfs_cc_state.buf).
1193 * This test uses an 8-byte overlap at 2040-2048, so that it passes against
1194 * Windows 2008r2, 2012 and Samba servers. Note, 2008GM fails, as it appears
1195 * to use a different copy algorithm to 2008r2.
1197 static bool
1198 test_ioctl_copy_chunk_src_is_dest_overlap(struct torture_context *torture,
1199 struct smb2_tree *tree)
1201 struct smb2_handle src_h;
1202 struct smb2_handle dest_h;
1203 NTSTATUS status;
1204 union smb_ioctl ioctl;
1205 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1206 struct srv_copychunk_copy cc_copy;
1207 struct srv_copychunk_rsp cc_rsp;
1208 enum ndr_err_code ndr_ret;
1209 bool ok;
1211 /* exceed the vfs_default copy buffer */
1212 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
1214 FNAME,
1215 &src_h, 2048 * 2,
1216 SEC_RIGHTS_FILE_ALL,
1217 FNAME2,
1218 &dest_h, 0,
1219 SEC_RIGHTS_FILE_ALL,
1220 &cc_copy,
1221 &ioctl);
1222 if (!ok) {
1223 torture_fail(torture, "setup copy chunk error");
1226 /* the source is also the destination */
1227 ioctl.smb2.in.file.handle = src_h;
1229 /* 8 bytes overlap between source and target ranges */
1230 cc_copy.chunks[0].source_off = 0;
1231 cc_copy.chunks[0].target_off = 2048 - 8;
1232 cc_copy.chunks[0].length = 2048;
1234 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1235 &cc_copy,
1236 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1237 torture_assert_ndr_success(torture, ndr_ret,
1238 "ndr_push_srv_copychunk_copy");
1240 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1241 torture_assert_ntstatus_ok(torture, status,
1242 "FSCTL_SRV_COPYCHUNK");
1244 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1245 &cc_rsp,
1246 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1247 torture_assert_ndr_success(torture, ndr_ret,
1248 "ndr_pull_srv_copychunk_rsp");
1250 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1251 1, /* chunks written */
1252 0, /* chunk bytes unsuccessfully written */
1253 2048); /* total bytes written */
1254 if (!ok) {
1255 torture_fail(torture, "bad copy chunk response data");
1258 ok = check_pattern(torture, tree, tmp_ctx, src_h, 0, 2048 - 8, 0);
1259 if (!ok) {
1260 torture_fail(torture, "inconsistent file data");
1262 ok = check_pattern(torture, tree, tmp_ctx, src_h, 2048 - 8, 2048, 0);
1263 if (!ok) {
1264 torture_fail(torture, "inconsistent file data");
1267 smb2_util_close(tree, src_h);
1268 smb2_util_close(tree, dest_h);
1269 talloc_free(tmp_ctx);
1270 return true;
1273 static bool test_ioctl_copy_chunk_bad_access(struct torture_context *torture,
1274 struct smb2_tree *tree)
1276 struct smb2_handle src_h;
1277 struct smb2_handle dest_h;
1278 NTSTATUS status;
1279 union smb_ioctl ioctl;
1280 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1281 struct srv_copychunk_copy cc_copy;
1282 enum ndr_err_code ndr_ret;
1283 bool ok;
1284 /* read permission on src */
1285 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx, 1, /* 1 chunk */
1286 FNAME, &src_h, 4096, /* fill 4096 byte src file */
1287 SEC_FILE_READ_DATA | SEC_FILE_READ_ATTRIBUTE,
1288 FNAME2, &dest_h, 0, /* 0 byte dest file */
1289 SEC_RIGHTS_FILE_ALL, &cc_copy, &ioctl);
1290 if (!ok) {
1291 torture_fail(torture, "setup copy chunk error");
1294 cc_copy.chunks[0].source_off = 0;
1295 cc_copy.chunks[0].target_off = 0;
1296 cc_copy.chunks[0].length = 4096;
1298 ndr_ret = ndr_push_struct_blob(
1299 &ioctl.smb2.in.out, tmp_ctx, &cc_copy,
1300 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1301 torture_assert_ndr_success(torture, ndr_ret,
1302 "ndr_push_srv_copychunk_copy");
1304 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1305 torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK,
1306 "FSCTL_SRV_COPYCHUNK");
1308 smb2_util_close(tree, src_h);
1309 smb2_util_close(tree, dest_h);
1311 /* execute permission on src */
1312 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx, 1, /* 1 chunk */
1313 FNAME, &src_h, 4096, /* fill 4096 byte src file */
1314 SEC_FILE_EXECUTE | SEC_FILE_READ_ATTRIBUTE,
1315 FNAME2, &dest_h, 0, /* 0 byte dest file */
1316 SEC_RIGHTS_FILE_ALL, &cc_copy, &ioctl);
1317 if (!ok) {
1318 torture_fail(torture, "setup copy chunk error");
1321 cc_copy.chunks[0].source_off = 0;
1322 cc_copy.chunks[0].target_off = 0;
1323 cc_copy.chunks[0].length = 4096;
1325 ndr_ret = ndr_push_struct_blob(
1326 &ioctl.smb2.in.out, tmp_ctx, &cc_copy,
1327 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1328 torture_assert_ndr_success(torture, ndr_ret,
1329 "ndr_push_srv_copychunk_copy");
1331 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1332 torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK,
1333 "FSCTL_SRV_COPYCHUNK");
1335 smb2_util_close(tree, src_h);
1336 smb2_util_close(tree, dest_h);
1338 /* neither read nor execute permission on src */
1339 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx, 1, /* 1 chunk */
1340 FNAME, &src_h, 4096, /* fill 4096 byte src file */
1341 SEC_FILE_READ_ATTRIBUTE, FNAME2, &dest_h,
1342 0, /* 0 byte dest file */
1343 SEC_RIGHTS_FILE_ALL, &cc_copy, &ioctl);
1344 if (!ok) {
1345 torture_fail(torture, "setup copy chunk error");
1348 cc_copy.chunks[0].source_off = 0;
1349 cc_copy.chunks[0].target_off = 0;
1350 cc_copy.chunks[0].length = 4096;
1352 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1353 &cc_copy,
1354 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1355 torture_assert_ndr_success(torture, ndr_ret,
1356 "ndr_push_srv_copychunk_copy");
1358 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1359 torture_assert_ntstatus_equal(torture, status,
1360 NT_STATUS_ACCESS_DENIED,
1361 "FSCTL_SRV_COPYCHUNK");
1363 smb2_util_close(tree, src_h);
1364 smb2_util_close(tree, dest_h);
1366 /* no write permission on dest */
1367 ok = test_setup_copy_chunk(
1368 torture, tree, tree, tmp_ctx, 1, /* 1 chunk */
1369 FNAME, &src_h, 4096, /* fill 4096 byte src file */
1370 SEC_FILE_READ_DATA | SEC_FILE_READ_ATTRIBUTE, FNAME2, &dest_h,
1371 0, /* 0 byte dest file */
1372 (SEC_RIGHTS_FILE_ALL &
1373 ~(SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)),
1374 &cc_copy, &ioctl);
1375 if (!ok) {
1376 torture_fail(torture, "setup copy chunk error");
1379 cc_copy.chunks[0].source_off = 0;
1380 cc_copy.chunks[0].target_off = 0;
1381 cc_copy.chunks[0].length = 4096;
1383 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1384 &cc_copy,
1385 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1386 torture_assert_ndr_success(torture, ndr_ret,
1387 "ndr_push_srv_copychunk_copy");
1389 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1390 torture_assert_ntstatus_equal(torture, status,
1391 NT_STATUS_ACCESS_DENIED,
1392 "FSCTL_SRV_COPYCHUNK");
1394 smb2_util_close(tree, src_h);
1395 smb2_util_close(tree, dest_h);
1397 /* no read permission on dest */
1398 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx, 1, /* 1 chunk */
1399 FNAME, &src_h, 4096, /* fill 4096 byte src file */
1400 SEC_FILE_READ_DATA | SEC_FILE_READ_ATTRIBUTE,
1401 FNAME2, &dest_h, 0, /* 0 byte dest file */
1402 (SEC_RIGHTS_FILE_ALL & ~SEC_FILE_READ_DATA),
1403 &cc_copy, &ioctl);
1404 if (!ok) {
1405 torture_fail(torture, "setup copy chunk error");
1408 cc_copy.chunks[0].source_off = 0;
1409 cc_copy.chunks[0].target_off = 0;
1410 cc_copy.chunks[0].length = 4096;
1412 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1413 &cc_copy,
1414 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1415 torture_assert_ndr_success(torture, ndr_ret,
1416 "ndr_push_srv_copychunk_copy");
1419 * FSCTL_SRV_COPYCHUNK requires read permission on dest,
1420 * FSCTL_SRV_COPYCHUNK_WRITE on the other hand does not.
1422 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1423 torture_assert_ntstatus_equal(torture, status,
1424 NT_STATUS_ACCESS_DENIED,
1425 "FSCTL_SRV_COPYCHUNK");
1427 smb2_util_close(tree, src_h);
1428 smb2_util_close(tree, dest_h);
1429 talloc_free(tmp_ctx);
1431 return true;
1434 static bool test_ioctl_copy_chunk_write_access(struct torture_context *torture,
1435 struct smb2_tree *tree)
1437 struct smb2_handle src_h;
1438 struct smb2_handle dest_h;
1439 NTSTATUS status;
1440 union smb_ioctl ioctl;
1441 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1442 struct srv_copychunk_copy cc_copy;
1443 enum ndr_err_code ndr_ret;
1444 bool ok;
1446 /* no read permission on dest with FSCTL_SRV_COPYCHUNK_WRITE */
1447 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
1448 1, /* 1 chunk */
1449 FNAME,
1450 &src_h, 4096, /* fill 4096 byte src file */
1451 SEC_RIGHTS_FILE_ALL,
1452 FNAME2,
1453 &dest_h, 0, /* 0 byte dest file */
1454 (SEC_RIGHTS_FILE_WRITE
1455 | SEC_RIGHTS_FILE_EXECUTE),
1456 &cc_copy,
1457 &ioctl);
1458 if (!ok) {
1459 torture_fail(torture, "setup copy chunk error");
1462 ioctl.smb2.in.function = FSCTL_SRV_COPYCHUNK_WRITE;
1463 cc_copy.chunks[0].source_off = 0;
1464 cc_copy.chunks[0].target_off = 0;
1465 cc_copy.chunks[0].length = 4096;
1467 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1468 &cc_copy,
1469 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1470 torture_assert_ndr_success(torture, ndr_ret,
1471 "ndr_push_srv_copychunk_copy");
1473 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1474 torture_assert_ntstatus_ok(torture, status,
1475 "FSCTL_SRV_COPYCHUNK_WRITE");
1477 smb2_util_close(tree, src_h);
1478 smb2_util_close(tree, dest_h);
1479 talloc_free(tmp_ctx);
1481 return true;
1484 static bool test_ioctl_copy_chunk_src_exceed(struct torture_context *torture,
1485 struct smb2_tree *tree)
1487 struct smb2_handle src_h;
1488 struct smb2_handle dest_h;
1489 NTSTATUS status;
1490 union smb_ioctl ioctl;
1491 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1492 struct srv_copychunk_copy cc_copy;
1493 struct srv_copychunk_rsp cc_rsp;
1494 enum ndr_err_code ndr_ret;
1495 bool ok;
1497 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
1498 1, /* 1 chunk */
1499 FNAME,
1500 &src_h, 4096, /* fill 4096 byte src file */
1501 SEC_RIGHTS_FILE_ALL,
1502 FNAME2,
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 /* Request copy where off + length exceeds size of src */
1512 cc_copy.chunks[0].source_off = 1024;
1513 cc_copy.chunks[0].target_off = 0;
1514 cc_copy.chunks[0].length = 4096;
1516 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1517 &cc_copy,
1518 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1519 torture_assert_ndr_success(torture, ndr_ret,
1520 "ndr_push_srv_copychunk_copy");
1522 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1523 torture_assert_ntstatus_equal(torture, status,
1524 NT_STATUS_INVALID_VIEW_SIZE,
1525 "FSCTL_SRV_COPYCHUNK oversize");
1527 /* Request copy where length exceeds size of src */
1528 cc_copy.chunks[0].source_off = 1024;
1529 cc_copy.chunks[0].target_off = 0;
1530 cc_copy.chunks[0].length = 3072;
1532 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1533 &cc_copy,
1534 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1535 torture_assert_ndr_success(torture, ndr_ret,
1536 "ndr_push_srv_copychunk_copy");
1538 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1539 torture_assert_ntstatus_ok(torture, status,
1540 "FSCTL_SRV_COPYCHUNK just right");
1542 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1543 &cc_rsp,
1544 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1545 torture_assert_ndr_success(torture, ndr_ret,
1546 "ndr_pull_srv_copychunk_rsp");
1548 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1549 1, /* chunks written */
1550 0, /* chunk bytes unsuccessfully written */
1551 3072); /* total bytes written */
1552 if (!ok) {
1553 torture_fail(torture, "bad copy chunk response data");
1556 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 3072, 1024);
1557 if (!ok) {
1558 torture_fail(torture, "inconsistent file data");
1561 smb2_util_close(tree, src_h);
1562 smb2_util_close(tree, dest_h);
1563 talloc_free(tmp_ctx);
1564 return true;
1567 static bool
1568 test_ioctl_copy_chunk_src_exceed_multi(struct torture_context *torture,
1569 struct smb2_tree *tree)
1571 struct smb2_handle src_h;
1572 struct smb2_handle dest_h;
1573 NTSTATUS status;
1574 union smb_ioctl ioctl;
1575 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1576 struct srv_copychunk_copy cc_copy;
1577 struct srv_copychunk_rsp cc_rsp;
1578 enum ndr_err_code ndr_ret;
1579 bool ok;
1581 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
1582 2, /* 2 chunks */
1583 FNAME,
1584 &src_h, 8192, /* fill 8192 byte src file */
1585 SEC_RIGHTS_FILE_ALL,
1586 FNAME2,
1587 &dest_h, 0, /* 0 byte dest file */
1588 SEC_RIGHTS_FILE_ALL,
1589 &cc_copy,
1590 &ioctl);
1591 if (!ok) {
1592 torture_fail(torture, "setup copy chunk error");
1595 /* Request copy where off + length exceeds size of src */
1596 cc_copy.chunks[0].source_off = 0;
1597 cc_copy.chunks[0].target_off = 0;
1598 cc_copy.chunks[0].length = 4096;
1600 cc_copy.chunks[1].source_off = 4096;
1601 cc_copy.chunks[1].target_off = 4096;
1602 cc_copy.chunks[1].length = 8192;
1604 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1605 &cc_copy,
1606 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1607 torture_assert_ndr_success(torture, ndr_ret,
1608 "ndr_push_srv_copychunk_copy");
1610 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1611 torture_assert_ntstatus_equal(torture, status,
1612 NT_STATUS_INVALID_VIEW_SIZE,
1613 "FSCTL_SRV_COPYCHUNK oversize");
1614 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1615 &cc_rsp,
1616 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1617 torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
1619 /* first chunk should still be written */
1620 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1621 1, /* chunks written */
1622 0, /* chunk bytes unsuccessfully written */
1623 4096); /* total bytes written */
1624 if (!ok) {
1625 torture_fail(torture, "bad copy chunk response data");
1627 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
1628 if (!ok) {
1629 torture_fail(torture, "inconsistent file data");
1632 smb2_util_close(tree, src_h);
1633 smb2_util_close(tree, dest_h);
1634 talloc_free(tmp_ctx);
1635 return true;
1638 static bool test_ioctl_copy_chunk_sparse_dest(struct torture_context *torture,
1639 struct smb2_tree *tree)
1641 struct smb2_handle src_h;
1642 struct smb2_handle dest_h;
1643 NTSTATUS status;
1644 union smb_ioctl ioctl;
1645 struct smb2_read r;
1646 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1647 struct srv_copychunk_copy cc_copy;
1648 struct srv_copychunk_rsp cc_rsp;
1649 enum ndr_err_code ndr_ret;
1650 bool ok;
1651 int i;
1653 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
1654 1, /* 1 chunk */
1655 FNAME,
1656 &src_h, 4096, /* fill 4096 byte src file */
1657 SEC_RIGHTS_FILE_ALL,
1658 FNAME2,
1659 &dest_h, 0, /* 0 byte dest file */
1660 SEC_RIGHTS_FILE_ALL,
1661 &cc_copy,
1662 &ioctl);
1663 if (!ok) {
1664 torture_fail(torture, "setup copy chunk error");
1667 /* copy all src file data (via a single chunk desc) */
1668 cc_copy.chunks[0].source_off = 0;
1669 cc_copy.chunks[0].target_off = 4096;
1670 cc_copy.chunks[0].length = 4096;
1672 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1673 &cc_copy,
1674 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1675 torture_assert_ndr_success(torture, ndr_ret,
1676 "ndr_push_srv_copychunk_copy");
1678 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1679 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
1681 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1682 &cc_rsp,
1683 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1684 torture_assert_ndr_success(torture, ndr_ret,
1685 "ndr_pull_srv_copychunk_rsp");
1687 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1688 1, /* chunks written */
1689 0, /* chunk bytes unsuccessfully written */
1690 4096); /* total bytes written */
1691 if (!ok) {
1692 torture_fail(torture, "bad copy chunk response data");
1695 /* check for zeros in first 4k */
1696 ZERO_STRUCT(r);
1697 r.in.file.handle = dest_h;
1698 r.in.length = 4096;
1699 r.in.offset = 0;
1700 status = smb2_read(tree, tmp_ctx, &r);
1701 torture_assert_ntstatus_ok(torture, status, "read");
1703 torture_assert_u64_equal(torture, r.out.data.length, 4096,
1704 "read data len mismatch");
1706 for (i = 0; i < 4096; i++) {
1707 torture_assert(torture, (r.out.data.data[i] == 0),
1708 "sparse did not pass class");
1711 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 4096, 4096, 0);
1712 if (!ok) {
1713 torture_fail(torture, "inconsistent file data");
1716 smb2_util_close(tree, src_h);
1717 smb2_util_close(tree, dest_h);
1718 talloc_free(tmp_ctx);
1719 return true;
1723 * set the ioctl MaxOutputResponse size to less than
1724 * sizeof(struct srv_copychunk_rsp)
1726 static bool test_ioctl_copy_chunk_max_output_sz(struct torture_context *torture,
1727 struct smb2_tree *tree)
1729 struct smb2_handle src_h;
1730 struct smb2_handle dest_h;
1731 NTSTATUS status;
1732 union smb_ioctl ioctl;
1733 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1734 struct srv_copychunk_copy cc_copy;
1735 enum ndr_err_code ndr_ret;
1736 bool ok;
1738 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
1739 1, /* 1 chunk */
1740 FNAME,
1741 &src_h, 4096, /* fill 4096 byte src file */
1742 SEC_RIGHTS_FILE_ALL,
1743 FNAME2,
1744 &dest_h, 0, /* 0 byte dest file */
1745 SEC_RIGHTS_FILE_ALL,
1746 &cc_copy,
1747 &ioctl);
1748 if (!ok) {
1749 torture_fail(torture, "setup copy chunk error");
1752 cc_copy.chunks[0].source_off = 0;
1753 cc_copy.chunks[0].target_off = 0;
1754 cc_copy.chunks[0].length = 4096;
1755 /* req is valid, but use undersize max_response_size */
1756 ioctl.smb2.in.max_response_size = sizeof(struct srv_copychunk_rsp) - 1;
1758 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1759 &cc_copy,
1760 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1761 torture_assert_ndr_success(torture, ndr_ret,
1762 "ndr_push_srv_copychunk_copy");
1764 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1765 torture_assert_ntstatus_equal(torture, status,
1766 NT_STATUS_INVALID_PARAMETER,
1767 "FSCTL_SRV_COPYCHUNK");
1769 smb2_util_close(tree, src_h);
1770 smb2_util_close(tree, dest_h);
1771 talloc_free(tmp_ctx);
1772 return true;
1775 static bool test_ioctl_copy_chunk_zero_length(struct torture_context *torture,
1776 struct smb2_tree *tree)
1778 struct smb2_handle src_h;
1779 struct smb2_handle dest_h;
1780 NTSTATUS status;
1781 union smb_ioctl ioctl;
1782 union smb_fileinfo q;
1783 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1784 struct srv_copychunk_copy cc_copy;
1785 struct srv_copychunk_rsp cc_rsp;
1786 enum ndr_err_code ndr_ret;
1787 bool ok;
1789 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
1790 1, /* 1 chunk */
1791 FNAME,
1792 &src_h, 4096, /* fill 4096 byte src file */
1793 SEC_RIGHTS_FILE_ALL,
1794 FNAME2,
1795 &dest_h, 0, /* 0 byte dest file */
1796 SEC_RIGHTS_FILE_ALL,
1797 &cc_copy,
1798 &ioctl);
1799 if (!ok) {
1800 torture_fail(torture, "setup copy chunk error");
1803 /* zero length server-side copy (via a single chunk desc) */
1804 cc_copy.chunks[0].source_off = 0;
1805 cc_copy.chunks[0].target_off = 0;
1806 cc_copy.chunks[0].length = 0;
1808 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1809 &cc_copy,
1810 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1811 torture_assert_ndr_success(torture, ndr_ret,
1812 "ndr_push_srv_copychunk_copy");
1814 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1815 torture_assert_ntstatus_equal(torture, status,
1816 NT_STATUS_INVALID_PARAMETER,
1817 "bad zero-length chunk response");
1819 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1820 &cc_rsp,
1821 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1822 torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
1824 ZERO_STRUCT(q);
1825 q.all_info2.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
1826 q.all_info2.in.file.handle = dest_h;
1827 status = smb2_getinfo_file(tree, torture, &q);
1828 torture_assert_ntstatus_ok(torture, status, "getinfo");
1830 torture_assert_int_equal(torture, q.all_info2.out.size, 0,
1831 "size after zero len clone");
1833 smb2_util_close(tree, src_h);
1834 smb2_util_close(tree, dest_h);
1835 talloc_free(tmp_ctx);
1836 return true;
1839 static bool copy_one_stream(struct torture_context *torture,
1840 struct smb2_tree *tree,
1841 TALLOC_CTX *tmp_ctx,
1842 const char *src_sname,
1843 const char *dst_sname)
1845 struct smb2_handle src_h = {{0}};
1846 struct smb2_handle dest_h = {{0}};
1847 NTSTATUS status;
1848 union smb_ioctl io;
1849 struct srv_copychunk_copy cc_copy;
1850 struct srv_copychunk_rsp cc_rsp;
1851 enum ndr_err_code ndr_ret;
1852 bool ok = false;
1854 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
1855 1, /* 1 chunk */
1856 src_sname,
1857 &src_h, 256, /* fill 256 byte src file */
1858 SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA,
1859 dst_sname,
1860 &dest_h, 0, /* 0 byte dest file */
1861 SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA,
1862 &cc_copy,
1863 &io);
1864 torture_assert_goto(torture, ok == true, ok, done,
1865 "setup copy chunk error\n");
1867 /* copy all src file data (via a single chunk desc) */
1868 cc_copy.chunks[0].source_off = 0;
1869 cc_copy.chunks[0].target_off = 0;
1870 cc_copy.chunks[0].length = 256;
1872 ndr_ret = ndr_push_struct_blob(
1873 &io.smb2.in.out, tmp_ctx, &cc_copy,
1874 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1876 torture_assert_ndr_success_goto(torture, ndr_ret, ok, done,
1877 "ndr_push_srv_copychunk_copy\n");
1879 status = smb2_ioctl(tree, tmp_ctx, &io.smb2);
1880 torture_assert_ntstatus_ok_goto(torture, status, ok, done,
1881 "FSCTL_SRV_COPYCHUNK\n");
1883 ndr_ret = ndr_pull_struct_blob(
1884 &io.smb2.out.out, tmp_ctx, &cc_rsp,
1885 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1887 torture_assert_ndr_success_goto(torture, ndr_ret, ok, done,
1888 "ndr_pull_srv_copychunk_rsp\n");
1890 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1891 1, /* chunks written */
1892 0, /* chunk bytes unsuccessfully written */
1893 256); /* total bytes written */
1894 torture_assert_goto(torture, ok == true, ok, done,
1895 "bad copy chunk response data\n");
1897 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 256, 0);
1898 if (!ok) {
1899 torture_fail(torture, "inconsistent file data\n");
1902 done:
1903 if (!smb2_util_handle_empty(src_h)) {
1904 smb2_util_close(tree, src_h);
1906 if (!smb2_util_handle_empty(dest_h)) {
1907 smb2_util_close(tree, dest_h);
1910 return ok;
1914 * Create a file
1916 static bool torture_setup_file(TALLOC_CTX *mem_ctx,
1917 struct smb2_tree *tree,
1918 const char *name)
1920 struct smb2_create io;
1921 NTSTATUS status;
1923 smb2_util_unlink(tree, name);
1924 ZERO_STRUCT(io);
1925 io.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED;
1926 io.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1927 io.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF;
1928 io.in.share_access =
1929 NTCREATEX_SHARE_ACCESS_DELETE|
1930 NTCREATEX_SHARE_ACCESS_READ|
1931 NTCREATEX_SHARE_ACCESS_WRITE;
1932 io.in.create_options = 0;
1933 io.in.fname = name;
1935 status = smb2_create(tree, mem_ctx, &io);
1936 if (!NT_STATUS_IS_OK(status)) {
1937 return false;
1940 status = smb2_util_close(tree, io.out.file.handle);
1941 if (!NT_STATUS_IS_OK(status)) {
1942 return false;
1945 return true;
1948 static bool test_copy_chunk_streams(struct torture_context *torture,
1949 struct smb2_tree *tree)
1951 const char *src_name = "src";
1952 const char *dst_name = "dst";
1953 struct names {
1954 const char *src_sname;
1955 const char *dst_sname;
1956 } names[] = {
1957 { "src:foo", "dst:foo" }
1959 int i;
1960 TALLOC_CTX *tmp_ctx = NULL;
1961 bool ok = false;
1963 tmp_ctx = talloc_new(tree);
1964 torture_assert_not_null_goto(torture, tmp_ctx, ok, done,
1965 "torture_setup_file\n");
1967 ok = torture_setup_file(torture, tree, src_name);
1968 torture_assert_goto(torture, ok == true, ok, done, "torture_setup_file\n");
1969 ok = torture_setup_file(torture, tree, dst_name);
1970 torture_assert_goto(torture, ok == true, ok, done, "torture_setup_file\n");
1972 for (i = 0; i < ARRAY_SIZE(names); i++) {
1973 ok = copy_one_stream(torture, tree, tmp_ctx,
1974 names[i].src_sname,
1975 names[i].dst_sname);
1976 torture_assert_goto(torture, ok == true, ok, done,
1977 "copy_one_stream failed\n");
1980 done:
1981 smb2_util_unlink(tree, src_name);
1982 smb2_util_unlink(tree, dst_name);
1983 talloc_free(tmp_ctx);
1984 return ok;
1987 static bool test_copy_chunk_across_shares(struct torture_context *tctx,
1988 struct smb2_tree *tree)
1990 TALLOC_CTX *mem_ctx = NULL;
1991 struct smb2_tree *tree2 = NULL;
1992 struct smb2_handle src_h = {{0}};
1993 struct smb2_handle dest_h = {{0}};
1994 union smb_ioctl ioctl;
1995 struct srv_copychunk_copy cc_copy;
1996 struct srv_copychunk_rsp cc_rsp;
1997 enum ndr_err_code ndr_ret;
1998 NTSTATUS status;
1999 bool ok = false;
2001 mem_ctx = talloc_new(tctx);
2002 torture_assert_not_null_goto(tctx, mem_ctx, ok, done,
2003 "talloc_new\n");
2005 ok = torture_smb2_tree_connect(tctx, tree->session, tctx, &tree2);
2006 torture_assert_goto(tctx, ok == true, ok, done,
2007 "torture_smb2_tree_connect failed\n");
2009 ok = test_setup_copy_chunk(tctx, tree, tree2, mem_ctx,
2010 1, /* 1 chunk */
2011 FNAME,
2012 &src_h, 4096, /* fill 4096 byte src file */
2013 SEC_RIGHTS_FILE_ALL,
2014 FNAME2,
2015 &dest_h, 0, /* 0 byte dest file */
2016 SEC_RIGHTS_FILE_ALL,
2017 &cc_copy,
2018 &ioctl);
2019 torture_assert_goto(tctx, ok == true, ok, done,
2020 "test_setup_copy_chunk failed\n");
2022 cc_copy.chunks[0].source_off = 0;
2023 cc_copy.chunks[0].target_off = 0;
2024 cc_copy.chunks[0].length = 4096;
2026 ndr_ret = ndr_push_struct_blob(
2027 &ioctl.smb2.in.out, mem_ctx, &cc_copy,
2028 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
2029 torture_assert_ndr_success_goto(tctx, ndr_ret, ok, done,
2030 "ndr_push_srv_copychunk_copy\n");
2032 status = smb2_ioctl(tree2, mem_ctx, &ioctl.smb2);
2033 torture_assert_ntstatus_ok_goto(tctx, status, ok, done,
2034 "FSCTL_SRV_COPYCHUNK\n");
2036 ndr_ret = ndr_pull_struct_blob(
2037 &ioctl.smb2.out.out, mem_ctx, &cc_rsp,
2038 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
2040 torture_assert_ndr_success_goto(tctx, ndr_ret, ok, done,
2041 "ndr_pull_srv_copychunk_rsp\n");
2043 ok = check_copy_chunk_rsp(tctx, &cc_rsp,
2044 1, /* chunks written */
2045 0, /* chunk bytes unsuccessfully written */
2046 4096); /* total bytes written */
2047 torture_assert_goto(tctx, ok == true, ok, done,
2048 "bad copy chunk response data\n");
2050 ok = check_pattern(tctx, tree2, mem_ctx, dest_h, 0, 4096, 0);
2051 torture_assert_goto(tctx, ok == true, ok, done,
2052 "inconsistent file data\n");
2054 done:
2055 TALLOC_FREE(mem_ctx);
2056 if (!smb2_util_handle_empty(src_h)) {
2057 smb2_util_close(tree, src_h);
2059 if (!smb2_util_handle_empty(dest_h)) {
2060 smb2_util_close(tree2, dest_h);
2062 smb2_util_unlink(tree, FNAME);
2063 smb2_util_unlink(tree2, FNAME2);
2064 if (tree2 != NULL) {
2065 smb2_tdis(tree2);
2067 return ok;
2070 static NTSTATUS test_ioctl_compress_fs_supported(struct torture_context *torture,
2071 struct smb2_tree *tree,
2072 TALLOC_CTX *mem_ctx,
2073 struct smb2_handle *fh,
2074 bool *compress_support)
2076 NTSTATUS status;
2077 union smb_fsinfo info;
2079 ZERO_STRUCT(info);
2080 info.generic.level = RAW_QFS_ATTRIBUTE_INFORMATION;
2081 info.generic.handle = *fh;
2082 status = smb2_getinfo_fs(tree, tree, &info);
2083 if (!NT_STATUS_IS_OK(status)) {
2084 return status;
2087 if (info.attribute_info.out.fs_attr & FILE_FILE_COMPRESSION) {
2088 *compress_support = true;
2089 } else {
2090 *compress_support = false;
2092 return NT_STATUS_OK;
2095 static NTSTATUS test_ioctl_compress_get(struct torture_context *torture,
2096 TALLOC_CTX *mem_ctx,
2097 struct smb2_tree *tree,
2098 struct smb2_handle fh,
2099 uint16_t *_compression_fmt)
2101 union smb_ioctl ioctl;
2102 struct compression_state cmpr_state;
2103 enum ndr_err_code ndr_ret;
2104 NTSTATUS status;
2106 ZERO_STRUCT(ioctl);
2107 ioctl.smb2.level = RAW_IOCTL_SMB2;
2108 ioctl.smb2.in.file.handle = fh;
2109 ioctl.smb2.in.function = FSCTL_GET_COMPRESSION;
2110 ioctl.smb2.in.max_response_size = sizeof(struct compression_state);
2111 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2113 status = smb2_ioctl(tree, mem_ctx, &ioctl.smb2);
2114 if (!NT_STATUS_IS_OK(status)) {
2115 return status;
2118 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, mem_ctx,
2119 &cmpr_state,
2120 (ndr_pull_flags_fn_t)ndr_pull_compression_state);
2122 if (ndr_ret != NDR_ERR_SUCCESS) {
2123 return NT_STATUS_INTERNAL_ERROR;
2126 *_compression_fmt = cmpr_state.format;
2127 return NT_STATUS_OK;
2130 static NTSTATUS test_ioctl_compress_set(struct torture_context *torture,
2131 TALLOC_CTX *mem_ctx,
2132 struct smb2_tree *tree,
2133 struct smb2_handle fh,
2134 uint16_t compression_fmt)
2136 union smb_ioctl ioctl;
2137 struct compression_state cmpr_state;
2138 enum ndr_err_code ndr_ret;
2139 NTSTATUS status;
2141 ZERO_STRUCT(ioctl);
2142 ioctl.smb2.level = RAW_IOCTL_SMB2;
2143 ioctl.smb2.in.file.handle = fh;
2144 ioctl.smb2.in.function = FSCTL_SET_COMPRESSION;
2145 ioctl.smb2.in.max_response_size = 0;
2146 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2148 cmpr_state.format = compression_fmt;
2149 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, mem_ctx,
2150 &cmpr_state,
2151 (ndr_push_flags_fn_t)ndr_push_compression_state);
2152 if (ndr_ret != NDR_ERR_SUCCESS) {
2153 return NT_STATUS_INTERNAL_ERROR;
2156 status = smb2_ioctl(tree, mem_ctx, &ioctl.smb2);
2157 return status;
2160 static bool test_ioctl_compress_file_flag(struct torture_context *torture,
2161 struct smb2_tree *tree)
2163 struct smb2_handle fh;
2164 NTSTATUS status;
2165 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2166 bool ok;
2167 uint16_t compression_fmt;
2169 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2170 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2171 FILE_ATTRIBUTE_NORMAL);
2172 torture_assert(torture, ok, "setup compression file");
2174 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
2175 &ok);
2176 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2177 if (!ok) {
2178 smb2_util_close(tree, fh);
2179 torture_skip(torture, "FS compression not supported\n");
2182 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2183 &compression_fmt);
2184 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2186 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2187 "initial compression state not NONE");
2189 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
2190 COMPRESSION_FORMAT_DEFAULT);
2191 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
2193 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2194 &compression_fmt);
2195 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2197 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
2198 "invalid compression state after set");
2200 smb2_util_close(tree, fh);
2201 talloc_free(tmp_ctx);
2202 return true;
2205 static bool test_ioctl_compress_dir_inherit(struct torture_context *torture,
2206 struct smb2_tree *tree)
2208 struct smb2_handle dirh;
2209 struct smb2_handle fh;
2210 NTSTATUS status;
2211 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2212 uint16_t compression_fmt;
2213 bool ok;
2214 char path_buf[PATH_MAX];
2216 smb2_deltree(tree, DNAME);
2217 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2218 DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL,
2219 FILE_ATTRIBUTE_DIRECTORY);
2220 torture_assert(torture, ok, "setup compression directory");
2222 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &dirh,
2223 &ok);
2224 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2225 if (!ok) {
2226 smb2_util_close(tree, dirh);
2227 smb2_deltree(tree, DNAME);
2228 torture_skip(torture, "FS compression not supported\n");
2231 /* set compression on parent dir, then check for inheritance */
2232 status = test_ioctl_compress_set(torture, tmp_ctx, tree, dirh,
2233 COMPRESSION_FORMAT_LZNT1);
2234 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
2236 status = test_ioctl_compress_get(torture, tmp_ctx, tree, dirh,
2237 &compression_fmt);
2238 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2240 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
2241 "invalid compression state after set");
2243 snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME);
2244 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2245 path_buf, &fh, 4096, SEC_RIGHTS_FILE_ALL,
2246 FILE_ATTRIBUTE_NORMAL);
2247 torture_assert(torture, ok, "setup compression file");
2249 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2250 &compression_fmt);
2251 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2253 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
2254 "compression attr not inherited by new file");
2256 /* check compressed data is consistent */
2257 ok = check_pattern(torture, tree, tmp_ctx, fh, 0, 4096, 0);
2259 /* disable dir compression attr, file should remain compressed */
2260 status = test_ioctl_compress_set(torture, tmp_ctx, tree, dirh,
2261 COMPRESSION_FORMAT_NONE);
2262 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
2264 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2265 &compression_fmt);
2266 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2268 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
2269 "file compression attr removed after dir change");
2270 smb2_util_close(tree, fh);
2272 /* new files should no longer inherit compression attr */
2273 snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME2);
2274 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2275 path_buf, &fh, 0, SEC_RIGHTS_FILE_ALL,
2276 FILE_ATTRIBUTE_NORMAL);
2277 torture_assert(torture, ok, "setup file");
2279 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2280 &compression_fmt);
2281 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2283 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2284 "compression attr present on new file");
2286 smb2_util_close(tree, fh);
2287 smb2_util_close(tree, dirh);
2288 smb2_deltree(tree, DNAME);
2289 talloc_free(tmp_ctx);
2290 return true;
2293 static bool test_ioctl_compress_invalid_format(struct torture_context *torture,
2294 struct smb2_tree *tree)
2296 struct smb2_handle fh;
2297 NTSTATUS status;
2298 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2299 bool ok;
2300 uint16_t compression_fmt;
2302 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2303 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2304 FILE_ATTRIBUTE_NORMAL);
2305 torture_assert(torture, ok, "setup compression file");
2307 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
2308 &ok);
2309 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2310 if (!ok) {
2311 smb2_util_close(tree, fh);
2312 torture_skip(torture, "FS compression not supported\n");
2315 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
2316 0x0042); /* bogus */
2317 torture_assert_ntstatus_equal(torture, status,
2318 NT_STATUS_INVALID_PARAMETER,
2319 "invalid FSCTL_SET_COMPRESSION");
2321 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2322 &compression_fmt);
2323 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2325 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2326 "initial compression state not NONE");
2328 smb2_util_close(tree, fh);
2329 talloc_free(tmp_ctx);
2330 return true;
2333 static bool test_ioctl_compress_invalid_buf(struct torture_context *torture,
2334 struct smb2_tree *tree)
2336 struct smb2_handle fh;
2337 NTSTATUS status;
2338 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2339 bool ok;
2340 union smb_ioctl ioctl;
2342 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2343 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2344 FILE_ATTRIBUTE_NORMAL);
2345 torture_assert(torture, ok, "setup compression file");
2347 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
2348 &ok);
2349 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2350 if (!ok) {
2351 smb2_util_close(tree, fh);
2352 torture_skip(torture, "FS compression not supported\n");
2355 ZERO_STRUCT(ioctl);
2356 ioctl.smb2.level = RAW_IOCTL_SMB2;
2357 ioctl.smb2.in.file.handle = fh;
2358 ioctl.smb2.in.function = FSCTL_GET_COMPRESSION;
2359 ioctl.smb2.in.max_response_size = 0; /* no room for rsp data */
2360 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2362 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2363 if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_USER_BUFFER)
2364 && !NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
2365 /* neither Server 2k12 nor 2k8r2 response status */
2366 torture_assert(torture, true,
2367 "invalid FSCTL_SET_COMPRESSION");
2370 smb2_util_close(tree, fh);
2371 talloc_free(tmp_ctx);
2372 return true;
2375 static bool test_ioctl_compress_query_file_attr(struct torture_context *torture,
2376 struct smb2_tree *tree)
2378 struct smb2_handle fh;
2379 union smb_fileinfo io;
2380 NTSTATUS status;
2381 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2382 bool ok;
2384 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2385 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2386 FILE_ATTRIBUTE_NORMAL);
2387 torture_assert(torture, ok, "setup compression file");
2389 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
2390 &ok);
2391 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2392 if (!ok) {
2393 smb2_util_close(tree, fh);
2394 torture_skip(torture, "FS compression not supported\n");
2397 ZERO_STRUCT(io);
2398 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
2399 io.generic.in.file.handle = fh;
2400 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2401 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2403 torture_assert(torture,
2404 ((io.all_info2.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2405 "compression attr before set");
2407 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
2408 COMPRESSION_FORMAT_DEFAULT);
2409 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
2411 ZERO_STRUCT(io);
2412 io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2413 io.generic.in.file.handle = fh;
2414 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2415 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2417 torture_assert(torture,
2418 (io.basic_info.out.attrib & FILE_ATTRIBUTE_COMPRESSED),
2419 "no compression attr after set");
2421 smb2_util_close(tree, fh);
2422 talloc_free(tmp_ctx);
2423 return true;
2427 * Specify FILE_ATTRIBUTE_COMPRESSED on creation, Windows does not retain this
2428 * attribute.
2430 static bool test_ioctl_compress_create_with_attr(struct torture_context *torture,
2431 struct smb2_tree *tree)
2433 struct smb2_handle fh2;
2434 union smb_fileinfo io;
2435 NTSTATUS status;
2436 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2437 uint16_t compression_fmt;
2438 bool ok;
2440 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2441 FNAME2, &fh2, 0, SEC_RIGHTS_FILE_ALL,
2442 (FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_COMPRESSED));
2443 torture_assert(torture, ok, "setup compression file");
2445 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh2,
2446 &ok);
2447 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2448 if (!ok) {
2449 smb2_util_close(tree, fh2);
2450 torture_skip(torture, "FS compression not supported\n");
2453 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh2,
2454 &compression_fmt);
2455 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2457 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2458 "initial compression state not NONE");
2460 ZERO_STRUCT(io);
2461 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
2462 io.generic.in.file.handle = fh2;
2463 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2464 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2466 torture_assert(torture,
2467 ((io.all_info2.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2468 "incorrect compression attr");
2470 smb2_util_close(tree, fh2);
2471 talloc_free(tmp_ctx);
2472 return true;
2475 static bool test_ioctl_compress_inherit_disable(struct torture_context *torture,
2476 struct smb2_tree *tree)
2478 struct smb2_handle fh;
2479 struct smb2_handle dirh;
2480 char path_buf[PATH_MAX];
2481 NTSTATUS status;
2482 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2483 bool ok;
2484 uint16_t compression_fmt;
2486 struct smb2_create io;
2488 smb2_deltree(tree, DNAME);
2489 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2490 DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL,
2491 FILE_ATTRIBUTE_DIRECTORY);
2492 torture_assert(torture, ok, "setup compression directory");
2494 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &dirh,
2495 &ok);
2496 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2497 if (!ok) {
2498 smb2_util_close(tree, dirh);
2499 smb2_deltree(tree, DNAME);
2500 torture_skip(torture, "FS compression not supported\n");
2503 /* set compression on parent dir, then check for inheritance */
2504 status = test_ioctl_compress_set(torture, tmp_ctx, tree, dirh,
2505 COMPRESSION_FORMAT_LZNT1);
2506 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
2508 status = test_ioctl_compress_get(torture, tmp_ctx, tree, dirh,
2509 &compression_fmt);
2510 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2512 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
2513 "invalid compression state after set");
2514 smb2_util_close(tree, dirh);
2516 snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME);
2517 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2518 path_buf, &fh, 0, SEC_RIGHTS_FILE_ALL,
2519 FILE_ATTRIBUTE_NORMAL);
2520 torture_assert(torture, ok, "setup compression file");
2522 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2523 &compression_fmt);
2524 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2526 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
2527 "compression attr not inherited by new file");
2528 smb2_util_close(tree, fh);
2530 snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME2);
2532 /* NO_COMPRESSION option should block inheritance */
2533 ZERO_STRUCT(io);
2534 io.in.desired_access = SEC_RIGHTS_FILE_ALL;
2535 io.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
2536 io.in.create_disposition = NTCREATEX_DISP_CREATE;
2537 io.in.create_options = NTCREATEX_OPTIONS_NO_COMPRESSION;
2538 io.in.share_access =
2539 NTCREATEX_SHARE_ACCESS_DELETE|
2540 NTCREATEX_SHARE_ACCESS_READ|
2541 NTCREATEX_SHARE_ACCESS_WRITE;
2542 io.in.fname = path_buf;
2544 status = smb2_create(tree, tmp_ctx, &io);
2545 torture_assert_ntstatus_ok(torture, status, "file create");
2547 fh = io.out.file.handle;
2549 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2550 &compression_fmt);
2551 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2553 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2554 "compression attr inherited by NO_COMPRESSION file");
2555 smb2_util_close(tree, fh);
2558 snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, DNAME);
2559 ZERO_STRUCT(io);
2560 io.in.desired_access = SEC_RIGHTS_FILE_ALL;
2561 io.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
2562 io.in.create_disposition = NTCREATEX_DISP_CREATE;
2563 io.in.create_options = (NTCREATEX_OPTIONS_NO_COMPRESSION
2564 | NTCREATEX_OPTIONS_DIRECTORY);
2565 io.in.share_access =
2566 NTCREATEX_SHARE_ACCESS_DELETE|
2567 NTCREATEX_SHARE_ACCESS_READ|
2568 NTCREATEX_SHARE_ACCESS_WRITE;
2569 io.in.fname = path_buf;
2571 status = smb2_create(tree, tmp_ctx, &io);
2572 torture_assert_ntstatus_ok(torture, status, "dir create");
2574 dirh = io.out.file.handle;
2576 status = test_ioctl_compress_get(torture, tmp_ctx, tree, dirh,
2577 &compression_fmt);
2578 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2580 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2581 "compression attr inherited by NO_COMPRESSION dir");
2582 smb2_util_close(tree, dirh);
2583 smb2_deltree(tree, DNAME);
2585 talloc_free(tmp_ctx);
2586 return true;
2589 /* attempting to set compression via SetInfo should not stick */
2590 static bool test_ioctl_compress_set_file_attr(struct torture_context *torture,
2591 struct smb2_tree *tree)
2593 struct smb2_handle fh;
2594 struct smb2_handle dirh;
2595 union smb_fileinfo io;
2596 union smb_setfileinfo set_io;
2597 uint16_t compression_fmt;
2598 NTSTATUS status;
2599 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2600 bool ok;
2602 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2603 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2604 FILE_ATTRIBUTE_NORMAL);
2605 torture_assert(torture, ok, "setup compression file");
2607 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
2608 &ok);
2609 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2610 if (!ok) {
2611 smb2_util_close(tree, fh);
2612 torture_skip(torture, "FS compression not supported\n");
2615 ZERO_STRUCT(io);
2616 io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2617 io.generic.in.file.handle = fh;
2618 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2619 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2621 torture_assert(torture,
2622 ((io.basic_info.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2623 "compression attr before set");
2625 ZERO_STRUCT(set_io);
2626 set_io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2627 set_io.basic_info.in.file.handle = fh;
2628 set_io.basic_info.in.create_time = io.basic_info.out.create_time;
2629 set_io.basic_info.in.access_time = io.basic_info.out.access_time;
2630 set_io.basic_info.in.write_time = io.basic_info.out.write_time;
2631 set_io.basic_info.in.change_time = io.basic_info.out.change_time;
2632 set_io.basic_info.in.attrib = (io.basic_info.out.attrib
2633 | FILE_ATTRIBUTE_COMPRESSED);
2634 status = smb2_setinfo_file(tree, &set_io);
2635 torture_assert_ntstatus_ok(torture, status, "SMB2_SETINFO_FILE");
2637 ZERO_STRUCT(io);
2638 io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2639 io.generic.in.file.handle = fh;
2640 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2641 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2643 torture_assert(torture,
2644 ((io.basic_info.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2645 "compression attr after set");
2647 smb2_util_close(tree, fh);
2648 smb2_deltree(tree, DNAME);
2649 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2650 DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL,
2651 FILE_ATTRIBUTE_DIRECTORY);
2652 torture_assert(torture, ok, "setup compression directory");
2654 ZERO_STRUCT(io);
2655 io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2656 io.generic.in.file.handle = dirh;
2657 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2658 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2660 torture_assert(torture,
2661 ((io.basic_info.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2662 "compression attr before set");
2664 ZERO_STRUCT(set_io);
2665 set_io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2666 set_io.basic_info.in.file.handle = dirh;
2667 set_io.basic_info.in.create_time = io.basic_info.out.create_time;
2668 set_io.basic_info.in.access_time = io.basic_info.out.access_time;
2669 set_io.basic_info.in.write_time = io.basic_info.out.write_time;
2670 set_io.basic_info.in.change_time = io.basic_info.out.change_time;
2671 set_io.basic_info.in.attrib = (io.basic_info.out.attrib
2672 | FILE_ATTRIBUTE_COMPRESSED);
2673 status = smb2_setinfo_file(tree, &set_io);
2674 torture_assert_ntstatus_ok(torture, status, "SMB2_SETINFO_FILE");
2676 status = test_ioctl_compress_get(torture, tmp_ctx, tree, dirh,
2677 &compression_fmt);
2678 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2680 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2681 "dir compression set after SetInfo");
2683 smb2_util_close(tree, dirh);
2684 talloc_free(tmp_ctx);
2685 return true;
2688 static bool test_ioctl_compress_perms(struct torture_context *torture,
2689 struct smb2_tree *tree)
2691 struct smb2_handle fh;
2692 uint16_t compression_fmt;
2693 union smb_fileinfo io;
2694 NTSTATUS status;
2695 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2696 bool ok;
2698 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2699 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2700 FILE_ATTRIBUTE_NORMAL);
2701 torture_assert(torture, ok, "setup compression file");
2703 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
2704 &ok);
2705 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2706 smb2_util_close(tree, fh);
2707 if (!ok) {
2708 torture_skip(torture, "FS compression not supported\n");
2711 /* attempt get compression without READ_ATTR permission */
2712 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2713 FNAME, &fh, 0,
2714 (SEC_RIGHTS_FILE_READ & ~(SEC_FILE_READ_ATTRIBUTE
2715 | SEC_STD_READ_CONTROL
2716 | SEC_FILE_READ_EA)),
2717 FILE_ATTRIBUTE_NORMAL);
2718 torture_assert(torture, ok, "setup compression file");
2720 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2721 &compression_fmt);
2722 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2723 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2724 "compression set after create");
2725 smb2_util_close(tree, fh);
2727 /* set compression without WRITE_ATTR permission should succeed */
2728 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2729 FNAME, &fh, 0,
2730 (SEC_RIGHTS_FILE_WRITE & ~(SEC_FILE_WRITE_ATTRIBUTE
2731 | SEC_STD_WRITE_DAC
2732 | SEC_FILE_WRITE_EA)),
2733 FILE_ATTRIBUTE_NORMAL);
2734 torture_assert(torture, ok, "setup compression file");
2736 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
2737 COMPRESSION_FORMAT_DEFAULT);
2738 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
2739 smb2_util_close(tree, fh);
2741 ok = test_setup_open(torture, tree, tmp_ctx,
2742 FNAME, &fh, SEC_RIGHTS_FILE_ALL,
2743 FILE_ATTRIBUTE_NORMAL);
2744 torture_assert(torture, ok, "setup compression file");
2745 ZERO_STRUCT(io);
2746 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
2747 io.generic.in.file.handle = fh;
2748 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2749 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2751 torture_assert(torture,
2752 (io.all_info2.out.attrib & FILE_ATTRIBUTE_COMPRESSED),
2753 "incorrect compression attr");
2754 smb2_util_close(tree, fh);
2756 /* attempt get compression without READ_DATA permission */
2757 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2758 FNAME, &fh, 0,
2759 (SEC_RIGHTS_FILE_READ & ~SEC_FILE_READ_DATA),
2760 FILE_ATTRIBUTE_NORMAL);
2761 torture_assert(torture, ok, "setup compression file");
2763 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2764 &compression_fmt);
2765 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2766 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2767 "compression enabled after set");
2768 smb2_util_close(tree, fh);
2770 /* attempt get compression with only SYNCHRONIZE permission */
2771 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2772 FNAME, &fh, 0,
2773 SEC_STD_SYNCHRONIZE,
2774 FILE_ATTRIBUTE_NORMAL);
2775 torture_assert(torture, ok, "setup compression file");
2777 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2778 &compression_fmt);
2779 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2780 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2781 "compression not enabled after set");
2782 smb2_util_close(tree, fh);
2784 /* attempt to set compression without WRITE_DATA permission */
2785 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2786 FNAME, &fh, 0,
2787 (SEC_RIGHTS_FILE_WRITE & (~SEC_FILE_WRITE_DATA)),
2788 FILE_ATTRIBUTE_NORMAL);
2789 torture_assert(torture, ok, "setup compression file");
2791 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
2792 COMPRESSION_FORMAT_DEFAULT);
2793 torture_assert_ntstatus_equal(torture, status,
2794 NT_STATUS_ACCESS_DENIED,
2795 "FSCTL_SET_COMPRESSION permission");
2796 smb2_util_close(tree, fh);
2798 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2799 FNAME, &fh, 0,
2800 (SEC_RIGHTS_FILE_WRITE & (~SEC_FILE_WRITE_DATA)),
2801 FILE_ATTRIBUTE_NORMAL);
2802 torture_assert(torture, ok, "setup compression file");
2804 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
2805 COMPRESSION_FORMAT_NONE);
2806 torture_assert_ntstatus_equal(torture, status,
2807 NT_STATUS_ACCESS_DENIED,
2808 "FSCTL_SET_COMPRESSION permission");
2809 smb2_util_close(tree, fh);
2811 talloc_free(tmp_ctx);
2812 return true;
2815 static bool test_ioctl_compress_notsup_get(struct torture_context *torture,
2816 struct smb2_tree *tree)
2818 struct smb2_handle fh;
2819 NTSTATUS status;
2820 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2821 bool ok;
2822 uint16_t compression_fmt;
2824 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2825 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2826 FILE_ATTRIBUTE_NORMAL);
2827 torture_assert(torture, ok, "setup compression file");
2829 /* skip if the server DOES support compression */
2830 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
2831 &ok);
2832 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2833 if (ok) {
2834 smb2_util_close(tree, fh);
2835 torture_skip(torture, "FS compression supported\n");
2839 * Despite not supporting compression, we should get a successful
2840 * response indicating that the file is uncompressed - like WS2016.
2842 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2843 &compression_fmt);
2844 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2846 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2847 "initial compression state not NONE");
2849 smb2_util_close(tree, fh);
2850 talloc_free(tmp_ctx);
2851 return true;
2854 static bool test_ioctl_compress_notsup_set(struct torture_context *torture,
2855 struct smb2_tree *tree)
2857 struct smb2_handle fh;
2858 NTSTATUS status;
2859 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2860 bool ok;
2862 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2863 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2864 FILE_ATTRIBUTE_NORMAL);
2865 torture_assert(torture, ok, "setup compression file");
2867 /* skip if the server DOES support compression */
2868 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
2869 &ok);
2870 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2871 if (ok) {
2872 smb2_util_close(tree, fh);
2873 torture_skip(torture, "FS compression supported\n");
2876 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
2877 COMPRESSION_FORMAT_DEFAULT);
2878 torture_assert_ntstatus_equal(torture, status,
2879 NT_STATUS_NOT_SUPPORTED,
2880 "FSCTL_SET_COMPRESSION default");
2883 * Despite not supporting compression, we should get a successful
2884 * response for set(COMPRESSION_FORMAT_NONE) - like WS2016 ReFS.
2886 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
2887 COMPRESSION_FORMAT_NONE);
2888 torture_assert_ntstatus_ok(torture, status,
2889 "FSCTL_SET_COMPRESSION none");
2891 smb2_util_close(tree, fh);
2892 talloc_free(tmp_ctx);
2893 return true;
2897 basic testing of the SMB2 FSCTL_QUERY_NETWORK_INTERFACE_INFO ioctl
2899 static bool test_ioctl_network_interface_info(struct torture_context *torture,
2900 struct smb2_tree *tree)
2902 union smb_ioctl ioctl;
2903 struct smb2_handle fh;
2904 NTSTATUS status;
2905 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2906 struct fsctl_net_iface_info net_iface;
2907 enum ndr_err_code ndr_ret;
2908 uint32_t caps;
2910 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
2911 if (!(caps & SMB2_CAP_MULTI_CHANNEL)) {
2912 torture_skip(torture, "server doesn't support SMB2_CAP_MULTI_CHANNEL\n");
2915 ZERO_STRUCT(ioctl);
2916 ioctl.smb2.level = RAW_IOCTL_SMB2;
2917 fh.data[0] = UINT64_MAX;
2918 fh.data[1] = UINT64_MAX;
2919 ioctl.smb2.in.file.handle = fh;
2920 ioctl.smb2.in.function = FSCTL_QUERY_NETWORK_INTERFACE_INFO;
2921 ioctl.smb2.in.max_response_size = 0x10000; /* Windows client sets this to 64KiB */
2922 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2924 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2925 torture_assert_ntstatus_ok(torture, status, "FSCTL_QUERY_NETWORK_INTERFACE_INFO");
2927 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, &net_iface,
2928 (ndr_pull_flags_fn_t)ndr_pull_fsctl_net_iface_info);
2929 torture_assert_ndr_success(torture, ndr_ret,
2930 "ndr_pull_fsctl_net_iface_info");
2932 ndr_print_debug((ndr_print_fn_t)ndr_print_fsctl_net_iface_info,
2933 "Network Interface Info", &net_iface);
2935 talloc_free(tmp_ctx);
2936 return true;
2940 * Check whether all @fs_support_flags are set in the server's
2941 * RAW_QFS_ATTRIBUTE_INFORMATION FileSystemAttributes response.
2943 static NTSTATUS test_ioctl_fs_supported(struct torture_context *torture,
2944 struct smb2_tree *tree,
2945 TALLOC_CTX *mem_ctx,
2946 struct smb2_handle *fh,
2947 uint64_t fs_support_flags,
2948 bool *supported)
2950 NTSTATUS status;
2951 union smb_fsinfo info;
2953 ZERO_STRUCT(info);
2954 info.generic.level = RAW_QFS_ATTRIBUTE_INFORMATION;
2955 info.generic.handle = *fh;
2956 status = smb2_getinfo_fs(tree, tree, &info);
2957 if (!NT_STATUS_IS_OK(status)) {
2958 return status;
2961 if ((info.attribute_info.out.fs_attr & fs_support_flags)
2962 == fs_support_flags) {
2963 *supported = true;
2964 } else {
2965 *supported = false;
2967 return NT_STATUS_OK;
2970 static NTSTATUS test_ioctl_sparse_req(struct torture_context *torture,
2971 TALLOC_CTX *mem_ctx,
2972 struct smb2_tree *tree,
2973 struct smb2_handle fh,
2974 bool set)
2976 union smb_ioctl ioctl;
2977 NTSTATUS status;
2978 uint8_t set_sparse;
2980 ZERO_STRUCT(ioctl);
2981 ioctl.smb2.level = RAW_IOCTL_SMB2;
2982 ioctl.smb2.in.file.handle = fh;
2983 ioctl.smb2.in.function = FSCTL_SET_SPARSE;
2984 ioctl.smb2.in.max_response_size = 0;
2985 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2986 set_sparse = (set ? 0xFF : 0x0);
2987 ioctl.smb2.in.out.data = &set_sparse;
2988 ioctl.smb2.in.out.length = sizeof(set_sparse);
2990 status = smb2_ioctl(tree, mem_ctx, &ioctl.smb2);
2991 return status;
2994 static NTSTATUS test_sparse_get(struct torture_context *torture,
2995 TALLOC_CTX *mem_ctx,
2996 struct smb2_tree *tree,
2997 struct smb2_handle fh,
2998 bool *_is_sparse)
3000 union smb_fileinfo io;
3001 NTSTATUS status;
3003 ZERO_STRUCT(io);
3004 io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
3005 io.generic.in.file.handle = fh;
3006 status = smb2_getinfo_file(tree, mem_ctx, &io);
3007 if (!NT_STATUS_IS_OK(status)) {
3008 return status;
3010 *_is_sparse = !!(io.basic_info.out.attrib & FILE_ATTRIBUTE_SPARSE);
3012 return status;
3015 static bool test_ioctl_sparse_file_flag(struct torture_context *torture,
3016 struct smb2_tree *tree)
3018 struct smb2_handle fh;
3019 union smb_fileinfo io;
3020 NTSTATUS status;
3021 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3022 bool ok;
3023 bool is_sparse;
3025 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3026 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3027 FILE_ATTRIBUTE_NORMAL);
3028 torture_assert(torture, ok, "setup file");
3030 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3031 FILE_SUPPORTS_SPARSE_FILES, &ok);
3032 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3033 if (!ok) {
3034 smb2_util_close(tree, fh);
3035 torture_skip(torture, "Sparse files not supported\n");
3038 ZERO_STRUCT(io);
3039 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
3040 io.generic.in.file.handle = fh;
3041 status = smb2_getinfo_file(tree, tmp_ctx, &io);
3042 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
3044 torture_assert(torture,
3045 ((io.all_info2.out.attrib & FILE_ATTRIBUTE_SPARSE) == 0),
3046 "sparse attr before set");
3048 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
3049 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3051 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3052 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3053 torture_assert(torture, is_sparse, "no sparse attr after set");
3055 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false);
3056 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3058 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3059 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3060 torture_assert(torture, !is_sparse, "sparse attr after unset");
3062 smb2_util_close(tree, fh);
3063 talloc_free(tmp_ctx);
3064 return true;
3067 static bool test_ioctl_sparse_file_attr(struct torture_context *torture,
3068 struct smb2_tree *tree)
3070 struct smb2_handle fh;
3071 NTSTATUS status;
3072 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3073 bool ok;
3074 bool is_sparse;
3076 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3077 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3078 (FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_SPARSE));
3079 torture_assert(torture, ok, "setup file");
3081 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3082 FILE_SUPPORTS_SPARSE_FILES, &ok);
3083 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3084 if (!ok) {
3085 smb2_util_close(tree, fh);
3086 torture_skip(torture, "Sparse files not supported\n");
3089 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3090 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3091 torture_assert(torture, !is_sparse, "sparse attr on open");
3093 smb2_util_close(tree, fh);
3094 talloc_free(tmp_ctx);
3095 return true;
3098 static bool test_ioctl_sparse_dir_flag(struct torture_context *torture,
3099 struct smb2_tree *tree)
3101 struct smb2_handle dirh;
3102 NTSTATUS status;
3103 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3104 bool ok;
3106 smb2_deltree(tree, DNAME);
3107 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3108 DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL,
3109 FILE_ATTRIBUTE_DIRECTORY);
3110 torture_assert(torture, ok, "setup sparse directory");
3112 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &dirh,
3113 FILE_SUPPORTS_SPARSE_FILES, &ok);
3114 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3115 if (!ok) {
3116 smb2_util_close(tree, dirh);
3117 smb2_deltree(tree, DNAME);
3118 torture_skip(torture, "Sparse files not supported\n");
3121 /* set sparse dir should fail, check for 2k12 & 2k8 response */
3122 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, dirh, true);
3123 torture_assert_ntstatus_equal(torture, status,
3124 NT_STATUS_INVALID_PARAMETER,
3125 "dir FSCTL_SET_SPARSE status");
3127 smb2_util_close(tree, dirh);
3128 smb2_deltree(tree, DNAME);
3129 talloc_free(tmp_ctx);
3130 return true;
3134 * FSCTL_SET_SPARSE can be sent with (already tested) or without a SetSparse
3135 * buffer to indicate whether the flag should be set or cleared. When sent
3136 * without a buffer, it must be handled as if SetSparse=TRUE.
3138 static bool test_ioctl_sparse_set_nobuf(struct torture_context *torture,
3139 struct smb2_tree *tree)
3141 struct smb2_handle fh;
3142 union smb_ioctl ioctl;
3143 NTSTATUS status;
3144 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3145 bool ok;
3146 bool is_sparse;
3148 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3149 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3150 FILE_ATTRIBUTE_NORMAL);
3151 torture_assert(torture, ok, "setup file");
3153 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3154 FILE_SUPPORTS_SPARSE_FILES, &ok);
3155 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3156 if (!ok) {
3157 smb2_util_close(tree, fh);
3158 torture_skip(torture, "Sparse files not supported\n");
3161 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3162 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3163 torture_assert(torture, !is_sparse, "sparse attr before set");
3165 ZERO_STRUCT(ioctl);
3166 ioctl.smb2.level = RAW_IOCTL_SMB2;
3167 ioctl.smb2.in.file.handle = fh;
3168 ioctl.smb2.in.function = FSCTL_SET_SPARSE;
3169 ioctl.smb2.in.max_response_size = 0;
3170 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3171 /* ioctl.smb2.in.out is zeroed, no SetSparse buffer */
3173 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3174 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3176 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3177 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3178 torture_assert(torture, is_sparse, "no sparse attr after set");
3180 /* second non-SetSparse request shouldn't toggle sparse */
3181 ZERO_STRUCT(ioctl);
3182 ioctl.smb2.level = RAW_IOCTL_SMB2;
3183 ioctl.smb2.in.file.handle = fh;
3184 ioctl.smb2.in.function = FSCTL_SET_SPARSE;
3185 ioctl.smb2.in.max_response_size = 0;
3186 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3188 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3189 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3191 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3192 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3193 torture_assert(torture, is_sparse, "no sparse attr after 2nd set");
3195 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false);
3196 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3198 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3199 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3200 torture_assert(torture, !is_sparse, "sparse attr after unset");
3202 smb2_util_close(tree, fh);
3203 talloc_free(tmp_ctx);
3204 return true;
3207 static bool test_ioctl_sparse_set_oversize(struct torture_context *torture,
3208 struct smb2_tree *tree)
3210 struct smb2_handle fh;
3211 union smb_ioctl ioctl;
3212 NTSTATUS status;
3213 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3214 bool ok;
3215 bool is_sparse;
3216 uint8_t buf[100];
3218 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3219 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3220 FILE_ATTRIBUTE_NORMAL);
3221 torture_assert(torture, ok, "setup file");
3223 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3224 FILE_SUPPORTS_SPARSE_FILES, &ok);
3225 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3226 if (!ok) {
3227 smb2_util_close(tree, fh);
3228 torture_skip(torture, "Sparse files not supported\n");
3231 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3232 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3233 torture_assert(torture, !is_sparse, "sparse attr before set");
3235 ZERO_STRUCT(ioctl);
3236 ioctl.smb2.level = RAW_IOCTL_SMB2;
3237 ioctl.smb2.in.file.handle = fh;
3238 ioctl.smb2.in.function = FSCTL_SET_SPARSE;
3239 ioctl.smb2.in.max_response_size = 0;
3240 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3243 * Attach a request buffer larger than FILE_SET_SPARSE_BUFFER
3244 * Windows still successfully processes the request.
3246 ZERO_ARRAY(buf);
3247 buf[0] = 0xFF; /* attempt to set sparse */
3248 ioctl.smb2.in.out.data = buf;
3249 ioctl.smb2.in.out.length = ARRAY_SIZE(buf);
3251 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3252 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3254 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3255 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3256 torture_assert(torture, is_sparse, "no sparse attr after set");
3258 ZERO_STRUCT(ioctl);
3259 ioctl.smb2.level = RAW_IOCTL_SMB2;
3260 ioctl.smb2.in.file.handle = fh;
3261 ioctl.smb2.in.function = FSCTL_SET_SPARSE;
3262 ioctl.smb2.in.max_response_size = 0;
3263 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3265 ZERO_ARRAY(buf); /* clear sparse */
3266 ioctl.smb2.in.out.data = buf;
3267 ioctl.smb2.in.out.length = ARRAY_SIZE(buf);
3269 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3270 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3272 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3273 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3274 torture_assert(torture, !is_sparse, "sparse attr after clear");
3276 smb2_util_close(tree, fh);
3277 talloc_free(tmp_ctx);
3278 return true;
3281 static NTSTATUS test_ioctl_qar_req(struct torture_context *torture,
3282 TALLOC_CTX *mem_ctx,
3283 struct smb2_tree *tree,
3284 struct smb2_handle fh,
3285 int64_t req_off,
3286 int64_t req_len,
3287 struct file_alloced_range_buf **_rsp,
3288 uint64_t *_rsp_count)
3290 union smb_ioctl ioctl;
3291 NTSTATUS status;
3292 enum ndr_err_code ndr_ret;
3293 struct file_alloced_range_buf far_buf;
3294 struct file_alloced_range_buf *far_rsp = NULL;
3295 uint64_t far_count = 0;
3296 int i;
3297 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
3298 if (tmp_ctx == NULL) {
3299 return NT_STATUS_NO_MEMORY;
3302 ZERO_STRUCT(ioctl);
3303 ioctl.smb2.level = RAW_IOCTL_SMB2;
3304 ioctl.smb2.in.file.handle = fh;
3305 ioctl.smb2.in.function = FSCTL_QUERY_ALLOCATED_RANGES;
3306 ioctl.smb2.in.max_response_size = 1024;
3307 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3309 far_buf.file_off = req_off;
3310 far_buf.len = req_len;
3312 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
3313 &far_buf,
3314 (ndr_push_flags_fn_t)ndr_push_file_alloced_range_buf);
3315 if (ndr_ret != NDR_ERR_SUCCESS) {
3316 status = NT_STATUS_UNSUCCESSFUL;
3317 goto err_out;
3320 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3321 if (!NT_STATUS_IS_OK(status)) {
3322 goto err_out;
3325 if (ioctl.smb2.out.out.length == 0) {
3326 goto done;
3329 if ((ioctl.smb2.out.out.length % sizeof(far_buf)) != 0) {
3330 torture_comment(torture, "invalid qry_alloced rsp len: %zd:",
3331 ioctl.smb2.out.out.length);
3332 status = NT_STATUS_INVALID_VIEW_SIZE;
3333 goto err_out;
3336 far_count = (ioctl.smb2.out.out.length / sizeof(far_buf));
3337 far_rsp = talloc_array(mem_ctx, struct file_alloced_range_buf,
3338 far_count);
3339 if (far_rsp == NULL) {
3340 status = NT_STATUS_NO_MEMORY;
3341 goto err_out;
3344 for (i = 0; i < far_count; i++) {
3345 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
3346 &far_rsp[i],
3347 (ndr_pull_flags_fn_t)ndr_pull_file_alloced_range_buf);
3348 if (ndr_ret != NDR_ERR_SUCCESS) {
3349 status = NT_STATUS_UNSUCCESSFUL;
3350 goto err_out;
3352 /* move to next buffer */
3353 ioctl.smb2.out.out.data += sizeof(far_buf);
3354 ioctl.smb2.out.out.length -= sizeof(far_buf);
3357 done:
3358 *_rsp = far_rsp;
3359 *_rsp_count = far_count;
3360 status = NT_STATUS_OK;
3361 err_out:
3362 talloc_free(tmp_ctx);
3363 return status;
3366 static bool test_ioctl_sparse_qar(struct torture_context *torture,
3367 struct smb2_tree *tree)
3369 struct smb2_handle fh;
3370 NTSTATUS status;
3371 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3372 bool ok;
3373 bool is_sparse;
3374 struct file_alloced_range_buf *far_rsp = NULL;
3375 uint64_t far_count = 0;
3377 /* zero length file, shouldn't have any ranges */
3378 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3379 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3380 FILE_ATTRIBUTE_NORMAL);
3381 torture_assert(torture, ok, "setup file");
3383 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3384 FILE_SUPPORTS_SPARSE_FILES, &ok);
3385 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3386 if (!ok) {
3387 smb2_util_close(tree, fh);
3388 torture_skip(torture, "Sparse files not supported\n");
3391 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3392 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3393 torture_assert(torture, !is_sparse, "sparse attr before set");
3395 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3396 0, /* off */
3397 0, /* len */
3398 &far_rsp,
3399 &far_count);
3400 torture_assert_ntstatus_ok(torture, status,
3401 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3402 torture_assert_u64_equal(torture, far_count, 0,
3403 "unexpected response len");
3405 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3406 0, /* off */
3407 1024, /* len */
3408 &far_rsp,
3409 &far_count);
3410 torture_assert_ntstatus_ok(torture, status,
3411 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3412 torture_assert_u64_equal(torture, far_count, 0,
3413 "unexpected response len");
3415 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
3416 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3418 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3419 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3420 torture_assert(torture, is_sparse, "no sparse attr after set");
3422 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3423 0, /* off */
3424 1024, /* len */
3425 &far_rsp,
3426 &far_count);
3427 torture_assert_ntstatus_ok(torture, status,
3428 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3429 torture_assert_u64_equal(torture, far_count, 0,
3430 "unexpected response len");
3432 /* write into the (now) sparse file at 4k offset */
3433 ok = write_pattern(torture, tree, tmp_ctx, fh,
3434 4096, /* off */
3435 1024, /* len */
3436 4096); /* pattern offset */
3437 torture_assert(torture, ok, "write pattern");
3440 * Query range before write off. Whether it's allocated or not is FS
3441 * dependent. NTFS deallocates chunks in 64K increments, but others
3442 * (e.g. XFS, Btrfs, etc.) may deallocate 4K chunks.
3444 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3445 0, /* off */
3446 4096, /* len */
3447 &far_rsp,
3448 &far_count);
3449 torture_assert_ntstatus_ok(torture, status,
3450 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3451 if (far_count == 0) {
3452 torture_comment(torture, "FS deallocated 4K chunk\n");
3453 } else {
3454 /* expect fully allocated */
3455 torture_assert_u64_equal(torture, far_count, 1,
3456 "unexpected response len");
3457 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0, "far offset");
3458 torture_assert_u64_equal(torture, far_rsp[0].len, 4096, "far len");
3462 * Query range before and past write, it should be allocated up to the
3463 * end of the write.
3465 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3466 0, /* off */
3467 8192, /* len */
3468 &far_rsp,
3469 &far_count);
3470 torture_assert_ntstatus_ok(torture, status,
3471 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3472 torture_assert_u64_equal(torture, far_count, 1,
3473 "unexpected response len");
3474 /* FS dependent */
3475 if (far_rsp[0].file_off == 4096) {
3476 /* 4K chunk unallocated */
3477 torture_assert_u64_equal(torture, far_rsp[0].file_off, 4096, "far offset");
3478 torture_assert_u64_equal(torture, far_rsp[0].len, 1024, "far len");
3479 } else {
3480 /* expect fully allocated */
3481 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0, "far offset");
3482 torture_assert_u64_equal(torture, far_rsp[0].len, 5120, "far len");
3485 smb2_util_close(tree, fh);
3486 talloc_free(tmp_ctx);
3487 return true;
3490 static bool test_ioctl_sparse_qar_malformed(struct torture_context *torture,
3491 struct smb2_tree *tree)
3493 struct smb2_handle fh;
3494 union smb_ioctl ioctl;
3495 struct file_alloced_range_buf far_buf;
3496 NTSTATUS status;
3497 enum ndr_err_code ndr_ret;
3498 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3499 bool ok;
3500 size_t old_len;
3502 /* zero length file, shouldn't have any ranges */
3503 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3504 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3505 FILE_ATTRIBUTE_NORMAL);
3506 torture_assert(torture, ok, "setup file");
3508 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3509 FILE_SUPPORTS_SPARSE_FILES, &ok);
3510 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3511 if (!ok) {
3512 smb2_util_close(tree, fh);
3513 torture_skip(torture, "Sparse files not supported\n");
3516 /* no allocated ranges, no space for range response, should pass */
3517 ZERO_STRUCT(ioctl);
3518 ioctl.smb2.level = RAW_IOCTL_SMB2;
3519 ioctl.smb2.in.file.handle = fh;
3520 ioctl.smb2.in.function = FSCTL_QUERY_ALLOCATED_RANGES;
3521 ioctl.smb2.in.max_response_size = 0;
3522 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3524 far_buf.file_off = 0;
3525 far_buf.len = 1024;
3526 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
3527 &far_buf,
3528 (ndr_push_flags_fn_t)ndr_push_file_alloced_range_buf);
3529 torture_assert_ndr_success(torture, ndr_ret, "push far ndr buf");
3531 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3532 torture_assert_ntstatus_ok(torture, status, "FSCTL_QUERY_ALLOCATED_RANGES");
3534 /* write into the file at 4k offset */
3535 ok = write_pattern(torture, tree, tmp_ctx, fh,
3536 0, /* off */
3537 1024, /* len */
3538 0); /* pattern offset */
3539 torture_assert(torture, ok, "write pattern");
3541 /* allocated range, no space for range response, should fail */
3542 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3543 torture_assert_ntstatus_equal(torture, status,
3544 NT_STATUS_BUFFER_TOO_SMALL, "qar no space");
3546 /* oversize (2x) file_alloced_range_buf in request, should pass */
3547 ioctl.smb2.in.max_response_size = 1024;
3548 old_len = ioctl.smb2.in.out.length;
3549 ok = data_blob_realloc(tmp_ctx, &ioctl.smb2.in.out,
3550 (ioctl.smb2.in.out.length * 2));
3551 torture_assert(torture, ok, "2x data buffer");
3552 memcpy(ioctl.smb2.in.out.data + old_len, ioctl.smb2.in.out.data,
3553 old_len);
3554 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3555 torture_assert_ntstatus_ok(torture, status, "qar too big");
3557 /* no file_alloced_range_buf in request, should fail */
3558 data_blob_free(&ioctl.smb2.in.out);
3559 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3560 torture_assert_ntstatus_equal(torture, status,
3561 NT_STATUS_INVALID_PARAMETER, "qar empty");
3563 return true;
3567 * 2.3.57 FSCTL_SET_ZERO_DATA Request
3569 * How an implementation zeros data within a file is implementation-dependent.
3570 * A file system MAY choose to deallocate regions of disk space that have been
3571 * zeroed.<50>
3572 * <50>
3573 * ... NTFS might deallocate disk space in the file if the file is stored on an
3574 * NTFS volume, and the file is sparse or compressed. It will free any allocated
3575 * space in chunks of 64 kilobytes that begin at an offset that is a multiple of
3576 * 64 kilobytes. Other bytes in the file (prior to the first freed 64-kilobyte
3577 * chunk and after the last freed 64-kilobyte chunk) will be zeroed but not
3578 * deallocated.
3580 static NTSTATUS test_ioctl_zdata_req(struct torture_context *torture,
3581 TALLOC_CTX *mem_ctx,
3582 struct smb2_tree *tree,
3583 struct smb2_handle fh,
3584 int64_t off,
3585 int64_t beyond_final_zero)
3587 union smb_ioctl ioctl;
3588 NTSTATUS status;
3589 enum ndr_err_code ndr_ret;
3590 struct file_zero_data_info zdata_info;
3591 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
3592 if (tmp_ctx == NULL) {
3593 return NT_STATUS_NO_MEMORY;
3596 ZERO_STRUCT(ioctl);
3597 ioctl.smb2.level = RAW_IOCTL_SMB2;
3598 ioctl.smb2.in.file.handle = fh;
3599 ioctl.smb2.in.function = FSCTL_SET_ZERO_DATA;
3600 ioctl.smb2.in.max_response_size = 0;
3601 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3603 zdata_info.file_off = off;
3604 zdata_info.beyond_final_zero = beyond_final_zero;
3606 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
3607 &zdata_info,
3608 (ndr_push_flags_fn_t)ndr_push_file_zero_data_info);
3609 if (ndr_ret != NDR_ERR_SUCCESS) {
3610 status = NT_STATUS_UNSUCCESSFUL;
3611 goto err_out;
3614 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3615 if (!NT_STATUS_IS_OK(status)) {
3616 goto err_out;
3619 status = NT_STATUS_OK;
3620 err_out:
3621 talloc_free(tmp_ctx);
3622 return status;
3625 static bool test_ioctl_sparse_punch(struct torture_context *torture,
3626 struct smb2_tree *tree)
3628 struct smb2_handle fh;
3629 NTSTATUS status;
3630 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3631 bool ok;
3632 bool is_sparse;
3633 struct file_alloced_range_buf *far_rsp = NULL;
3634 uint64_t far_count = 0;
3636 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3637 FNAME, &fh, 4096, SEC_RIGHTS_FILE_ALL,
3638 FILE_ATTRIBUTE_NORMAL);
3639 torture_assert(torture, ok, "setup file");
3641 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3642 FILE_SUPPORTS_SPARSE_FILES, &ok);
3643 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3644 if (!ok) {
3645 smb2_util_close(tree, fh);
3646 torture_skip(torture, "Sparse files not supported\n");
3649 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3650 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3651 torture_assert(torture, !is_sparse, "sparse attr before set");
3653 /* zero (hole-punch) the data, without sparse flag */
3654 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3655 0, /* off */
3656 4096); /* beyond_final_zero */
3657 torture_assert_ntstatus_ok(torture, status, "zero_data");
3659 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3660 0, /* off */
3661 4096, /* len */
3662 &far_rsp,
3663 &far_count);
3664 torture_assert_ntstatus_ok(torture, status,
3665 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3666 torture_assert_u64_equal(torture, far_count, 1,
3667 "unexpected response len");
3669 /* expect fully allocated */
3670 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3671 "unexpected far off");
3672 torture_assert_u64_equal(torture, far_rsp[0].len, 4096,
3673 "unexpected far len");
3674 /* check that the data is now zeroed */
3675 ok = check_zero(torture, tree, tmp_ctx, fh, 0, 4096);
3676 torture_assert(torture, ok, "non-sparse zeroed range");
3678 /* set sparse */
3679 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
3680 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3682 /* still fully allocated on NTFS, see note below for Samba */
3683 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3684 0, /* off */
3685 4096, /* len */
3686 &far_rsp,
3687 &far_count);
3688 torture_assert_ntstatus_ok(torture, status,
3689 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3691 * FS specific: Samba uses PUNCH_HOLE to zero the range, and
3692 * subsequently uses fallocate() to allocate the punched range if the
3693 * file is marked non-sparse and "strict allocate" is enabled. In both
3694 * cases, the zeroed range will not be detected by SEEK_DATA, so the
3695 * range won't be present in QAR responses until the file is marked
3696 * non-sparse again.
3698 if (far_count == 0) {
3699 torture_comment(torture, "non-sparse zeroed range disappeared "
3700 "after marking sparse\n");
3701 } else {
3702 /* NTFS: range remains fully allocated */
3703 torture_assert_u64_equal(torture, far_count, 1,
3704 "unexpected response len");
3705 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3706 "unexpected far off");
3707 torture_assert_u64_equal(torture, far_rsp[0].len, 4096,
3708 "unexpected far len");
3711 /* zero (hole-punch) the data, _with_ sparse flag */
3712 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3713 0, /* off */
3714 4096); /* beyond_final_zero */
3715 torture_assert_ntstatus_ok(torture, status, "zero_data");
3717 /* the range should no longer be alloced */
3718 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3719 0, /* off */
3720 4096, /* len */
3721 &far_rsp,
3722 &far_count);
3723 torture_assert_ntstatus_ok(torture, status,
3724 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3725 torture_assert_u64_equal(torture, far_count, 0,
3726 "unexpected response len");
3728 ok = check_zero(torture, tree, tmp_ctx, fh, 0, 4096);
3729 torture_assert(torture, ok, "sparse zeroed range");
3731 /* remove sparse flag, this should "unsparse" the zeroed range */
3732 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false);
3733 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3735 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3736 0, /* off */
3737 4096, /* len */
3738 &far_rsp,
3739 &far_count);
3740 torture_assert_ntstatus_ok(torture, status,
3741 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3742 torture_assert_u64_equal(torture, far_count, 1,
3743 "unexpected response len");
3744 /* expect fully allocated */
3745 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3746 "unexpected far off");
3747 torture_assert_u64_equal(torture, far_rsp[0].len, 4096,
3748 "unexpected far len");
3750 ok = check_zero(torture, tree, tmp_ctx, fh, 0, 4096);
3751 torture_assert(torture, ok, "sparse zeroed range");
3753 smb2_util_close(tree, fh);
3754 talloc_free(tmp_ctx);
3755 return true;
3759 * Find the point at which a zeroed range in a sparse file is deallocated by the
3760 * underlying filesystem. NTFS on Windows Server 2012 deallocates chunks in 64k
3761 * increments. Also check whether zeroed neighbours are merged for deallocation.
3763 static bool test_ioctl_sparse_hole_dealloc(struct torture_context *torture,
3764 struct smb2_tree *tree)
3766 struct smb2_handle fh;
3767 NTSTATUS status;
3768 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3769 bool ok;
3770 uint64_t file_size;
3771 uint64_t hlen;
3772 uint64_t dealloc_chunk_len = 0;
3773 struct file_alloced_range_buf *far_rsp = NULL;
3774 uint64_t far_count = 0;
3776 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3777 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3778 FILE_ATTRIBUTE_NORMAL);
3779 torture_assert(torture, ok, "setup file 1");
3781 /* check for FS sparse file */
3782 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3783 FILE_SUPPORTS_SPARSE_FILES, &ok);
3784 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3785 if (!ok) {
3786 smb2_util_close(tree, fh);
3787 torture_skip(torture, "Sparse files not supported\n");
3790 /* set sparse */
3791 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
3792 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3794 file_size = 1024 * 1024;
3796 ok = write_pattern(torture, tree, tmp_ctx, fh,
3797 0, /* off */
3798 file_size, /* len */
3799 0); /* pattern offset */
3800 torture_assert(torture, ok, "write pattern");
3802 /* check allocated ranges, should be fully allocated */
3803 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3804 0, /* off */
3805 file_size, /* len */
3806 &far_rsp,
3807 &far_count);
3808 torture_assert_ntstatus_ok(torture, status,
3809 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3810 torture_assert_u64_equal(torture, far_count, 1,
3811 "unexpected response len");
3812 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3813 "unexpected far off");
3814 torture_assert_u64_equal(torture, far_rsp[0].len, file_size,
3815 "unexpected far len");
3817 /* punch holes in sizes of 1k increments */
3818 for (hlen = 0; hlen <= file_size; hlen += 4096) {
3820 /* punch a hole from zero to the current increment */
3821 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3822 0, /* off */
3823 hlen); /* beyond_final_zero */
3824 torture_assert_ntstatus_ok(torture, status, "zero_data");
3826 /* ensure hole is zeroed, and pattern is consistent */
3827 ok = check_zero(torture, tree, tmp_ctx, fh, 0, hlen);
3828 torture_assert(torture, ok, "sparse zeroed range");
3830 ok = check_pattern(torture, tree, tmp_ctx, fh, hlen,
3831 file_size - hlen, hlen);
3832 torture_assert(torture, ok, "allocated pattern range");
3834 /* Check allocated ranges, hole might have been deallocated */
3835 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3836 0, /* off */
3837 file_size, /* len */
3838 &far_rsp,
3839 &far_count);
3840 torture_assert_ntstatus_ok(torture, status,
3841 "FSCTL_QUERY_ALLOCATED_RANGES");
3842 if ((hlen == file_size) && (far_count == 0)) {
3843 /* hole covered entire file, deallocation occurred */
3844 dealloc_chunk_len = file_size;
3845 break;
3848 torture_assert_u64_equal(torture, far_count, 1,
3849 "unexpected response len");
3850 if (far_rsp[0].file_off != 0) {
3852 * We now know the hole punch length needed to trigger a
3853 * deallocation on this FS...
3855 dealloc_chunk_len = hlen;
3856 torture_comment(torture, "hole punch %ju@0 resulted in "
3857 "deallocation of %ju@0\n",
3858 (uintmax_t)hlen,
3859 (uintmax_t)far_rsp[0].file_off);
3860 torture_assert_u64_equal(torture,
3861 file_size - far_rsp[0].len,
3862 far_rsp[0].file_off,
3863 "invalid alloced range");
3864 break;
3868 if (dealloc_chunk_len == 0) {
3869 torture_comment(torture, "strange, this FS never deallocates"
3870 "zeroed ranges in sparse files\n");
3871 return true; /* FS specific, not a failure */
3875 * Check whether deallocation occurs when the (now known)
3876 * deallocation chunk size is punched via two ZERO_DATA requests.
3877 * I.e. Does the FS merge the two ranges and deallocate the chunk?
3878 * NTFS on Windows Server 2012 does not.
3880 ok = write_pattern(torture, tree, tmp_ctx, fh,
3881 0, /* off */
3882 file_size, /* len */
3883 0); /* pattern offset */
3884 torture_assert(torture, ok, "write pattern");
3886 /* divide dealloc chunk size by two, to use as punch length */
3887 hlen = dealloc_chunk_len >> 1;
3890 * /half of dealloc chunk size 1M\
3891 * | |
3892 * /offset 0 | /dealloc chunk size |
3893 * |------------------ |-------------------|-------------------|
3894 * | zeroed, 1st punch | zeroed, 2nd punch | existing pattern |
3896 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3897 0, /* off */
3898 hlen); /* beyond final zero */
3899 torture_assert_ntstatus_ok(torture, status, "zero_data");
3901 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3902 hlen, /* off */
3903 dealloc_chunk_len); /* beyond final */
3904 torture_assert_ntstatus_ok(torture, status, "zero_data");
3906 /* ensure holes are zeroed, and pattern is consistent */
3907 ok = check_zero(torture, tree, tmp_ctx, fh, 0, dealloc_chunk_len);
3908 torture_assert(torture, ok, "sparse zeroed range");
3910 ok = check_pattern(torture, tree, tmp_ctx, fh, dealloc_chunk_len,
3911 file_size - dealloc_chunk_len, dealloc_chunk_len);
3912 torture_assert(torture, ok, "allocated pattern range");
3914 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3915 0, /* off */
3916 file_size, /* len */
3917 &far_rsp,
3918 &far_count);
3919 torture_assert_ntstatus_ok(torture, status,
3920 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3922 if ((far_count == 0) && (dealloc_chunk_len == file_size)) {
3923 torture_comment(torture, "holes merged for deallocation of "
3924 "full file\n");
3925 return true;
3927 torture_assert_u64_equal(torture, far_count, 1,
3928 "unexpected response len");
3929 if (far_rsp[0].file_off == dealloc_chunk_len) {
3930 torture_comment(torture, "holes merged for deallocation of "
3931 "%ju chunk\n", (uintmax_t)dealloc_chunk_len);
3932 torture_assert_u64_equal(torture,
3933 file_size - far_rsp[0].len,
3934 far_rsp[0].file_off,
3935 "invalid alloced range");
3936 } else {
3937 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3938 "unexpected deallocation");
3939 torture_comment(torture, "holes not merged for deallocation\n");
3942 smb2_util_close(tree, fh);
3945 * Check whether an unwritten range is allocated when a sparse file is
3946 * written to at an offset past the dealloc chunk size:
3948 * /dealloc chunk size
3949 * /offset 0 |
3950 * |------------------ |-------------------|
3951 * | unwritten | pattern |
3953 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3954 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3955 FILE_ATTRIBUTE_NORMAL);
3956 torture_assert(torture, ok, "setup file 1");
3958 /* set sparse */
3959 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
3960 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3962 ok = write_pattern(torture, tree, tmp_ctx, fh,
3963 dealloc_chunk_len, /* off */
3964 1024, /* len */
3965 dealloc_chunk_len); /* pattern offset */
3966 torture_assert(torture, ok, "write pattern");
3968 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3969 0, /* off */
3970 dealloc_chunk_len + 1024, /* len */
3971 &far_rsp,
3972 &far_count);
3973 torture_assert_ntstatus_ok(torture, status,
3974 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3975 torture_assert_u64_equal(torture, far_count, 1,
3976 "unexpected response len");
3977 if (far_rsp[0].file_off == 0) {
3978 torture_assert_u64_equal(torture, far_rsp[0].len,
3979 dealloc_chunk_len + 1024,
3980 "unexpected far len");
3981 torture_comment(torture, "unwritten range fully allocated\n");
3982 } else {
3983 torture_assert_u64_equal(torture, far_rsp[0].file_off, dealloc_chunk_len,
3984 "unexpected deallocation");
3985 torture_assert_u64_equal(torture, far_rsp[0].len, 1024,
3986 "unexpected far len");
3987 torture_comment(torture, "unwritten range not allocated\n");
3990 ok = check_zero(torture, tree, tmp_ctx, fh, 0, dealloc_chunk_len);
3991 torture_assert(torture, ok, "sparse zeroed range");
3993 ok = check_pattern(torture, tree, tmp_ctx, fh, dealloc_chunk_len,
3994 1024, dealloc_chunk_len);
3995 torture_assert(torture, ok, "allocated pattern range");
3997 /* unsparse, should now be fully allocated */
3998 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false);
3999 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4001 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4002 0, /* off */
4003 dealloc_chunk_len + 1024, /* len */
4004 &far_rsp,
4005 &far_count);
4006 torture_assert_ntstatus_ok(torture, status,
4007 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4008 torture_assert_u64_equal(torture, far_count, 1,
4009 "unexpected response len");
4010 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4011 "unexpected deallocation");
4012 torture_assert_u64_equal(torture, far_rsp[0].len,
4013 dealloc_chunk_len + 1024,
4014 "unexpected far len");
4016 smb2_util_close(tree, fh);
4017 talloc_free(tmp_ctx);
4018 return true;
4021 /* check whether a file with compression and sparse attrs can be deallocated */
4022 static bool test_ioctl_sparse_compressed(struct torture_context *torture,
4023 struct smb2_tree *tree)
4025 struct smb2_handle fh;
4026 NTSTATUS status;
4027 TALLOC_CTX *tmp_ctx = talloc_new(tree);
4028 bool ok;
4029 uint64_t file_size = 1024 * 1024;
4030 struct file_alloced_range_buf *far_rsp = NULL;
4031 uint64_t far_count = 0;
4033 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4034 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
4035 FILE_ATTRIBUTE_NORMAL);
4036 torture_assert(torture, ok, "setup file 1");
4038 /* check for FS sparse file and compression support */
4039 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
4040 FILE_SUPPORTS_SPARSE_FILES, &ok);
4041 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4042 if (!ok) {
4043 smb2_util_close(tree, fh);
4044 torture_skip(torture, "Sparse files not supported\n");
4047 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
4048 &ok);
4049 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4050 if (!ok) {
4051 smb2_util_close(tree, fh);
4052 torture_skip(torture, "FS compression not supported\n");
4055 /* set compression and write some data */
4056 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
4057 COMPRESSION_FORMAT_DEFAULT);
4058 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
4060 ok = write_pattern(torture, tree, tmp_ctx, fh,
4061 0, /* off */
4062 file_size, /* len */
4063 0); /* pattern offset */
4064 torture_assert(torture, ok, "write pattern");
4066 /* set sparse - now sparse and compressed */
4067 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4068 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4070 /* check allocated ranges, should be fully alloced */
4071 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4072 0, /* off */
4073 file_size, /* len */
4074 &far_rsp,
4075 &far_count);
4076 torture_assert_ntstatus_ok(torture, status,
4077 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4078 torture_assert_u64_equal(torture, far_count, 1,
4079 "unexpected response len");
4080 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4081 "unexpected far off");
4082 torture_assert_u64_equal(torture, far_rsp[0].len, file_size,
4083 "unexpected far len");
4085 /* zero (hole-punch) all data, with sparse and compressed attrs */
4086 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4087 0, /* off */
4088 file_size); /* beyond_final_zero */
4089 torture_assert_ntstatus_ok(torture, status, "zero_data");
4092 * Windows Server 2012 still deallocates a zeroed range when a sparse
4093 * file carries the compression attribute.
4095 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4096 0, /* off */
4097 file_size, /* len */
4098 &far_rsp,
4099 &far_count);
4100 torture_assert_ntstatus_ok(torture, status,
4101 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4102 if (far_count == 0) {
4103 torture_comment(torture, "sparse & compressed file "
4104 "deallocated after hole-punch\n");
4105 } else {
4106 torture_assert_u64_equal(torture, far_count, 1,
4107 "unexpected response len");
4108 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4109 "unexpected far off");
4110 torture_assert_u64_equal(torture, far_rsp[0].len, file_size,
4111 "unexpected far len");
4112 torture_comment(torture, "sparse & compressed file fully "
4113 "allocated after hole-punch\n");
4116 smb2_util_close(tree, fh);
4117 talloc_free(tmp_ctx);
4118 return true;
4122 * Create a sparse file, then attempt to copy unallocated and allocated ranges
4123 * into a target file using FSCTL_SRV_COPYCHUNK.
4125 static bool test_ioctl_sparse_copy_chunk(struct torture_context *torture,
4126 struct smb2_tree *tree)
4128 struct smb2_handle src_h;
4129 struct smb2_handle dest_h;
4130 NTSTATUS status;
4131 TALLOC_CTX *tmp_ctx = talloc_new(tree);
4132 bool ok;
4133 uint64_t dealloc_chunk_len = 64 * 1024; /* Windows 2012 */
4134 struct file_alloced_range_buf *far_rsp = NULL;
4135 uint64_t far_count = 0;
4136 union smb_ioctl ioctl;
4137 struct srv_copychunk_copy cc_copy;
4138 struct srv_copychunk_rsp cc_rsp;
4139 enum ndr_err_code ndr_ret;
4141 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4142 FNAME, &src_h, 0, SEC_RIGHTS_FILE_ALL,
4143 FILE_ATTRIBUTE_NORMAL);
4144 torture_assert(torture, ok, "setup file");
4146 /* check for FS sparse file support */
4147 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &src_h,
4148 FILE_SUPPORTS_SPARSE_FILES, &ok);
4149 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4150 smb2_util_close(tree, src_h);
4151 if (!ok) {
4152 torture_skip(torture, "Sparse files not supported\n");
4155 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
4156 1, /* chunks */
4157 FNAME,
4158 &src_h, 0, /* src file */
4159 SEC_RIGHTS_FILE_ALL,
4160 FNAME2,
4161 &dest_h, 0, /* dest file */
4162 SEC_RIGHTS_FILE_ALL,
4163 &cc_copy,
4164 &ioctl);
4165 torture_assert(torture, ok, "setup copy chunk error");
4167 /* set sparse */
4168 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, src_h, true);
4169 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4171 /* start after dealloc_chunk_len, to create an unwritten sparse range */
4172 ok = write_pattern(torture, tree, tmp_ctx, src_h,
4173 dealloc_chunk_len, /* off */
4174 1024, /* len */
4175 dealloc_chunk_len); /* pattern offset */
4176 torture_assert(torture, ok, "write pattern");
4178 /* Skip test if 64k chunk is allocated - FS specific */
4179 status = test_ioctl_qar_req(torture, tmp_ctx, tree, src_h,
4180 0, /* off */
4181 dealloc_chunk_len + 1024, /* len */
4182 &far_rsp,
4183 &far_count);
4184 torture_assert_ntstatus_ok(torture, status,
4185 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4186 torture_assert_u64_equal(torture, far_count, 1,
4187 "unexpected response len");
4188 if (far_rsp[0].file_off == 0) {
4189 torture_skip(torture, "unwritten range fully allocated\n");
4192 torture_assert_u64_equal(torture, far_rsp[0].file_off, dealloc_chunk_len,
4193 "unexpected allocation");
4194 torture_assert_u64_equal(torture, far_rsp[0].len, 1024,
4195 "unexpected far len");
4197 /* copy-chunk unallocated + written ranges into non-sparse dest */
4199 cc_copy.chunks[0].source_off = 0;
4200 cc_copy.chunks[0].target_off = 0;
4201 cc_copy.chunks[0].length = dealloc_chunk_len + 1024;
4203 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
4204 &cc_copy,
4205 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
4206 torture_assert_ndr_success(torture, ndr_ret,
4207 "ndr_push_srv_copychunk_copy");
4209 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
4210 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
4212 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
4213 &cc_rsp,
4214 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
4215 torture_assert_ndr_success(torture, ndr_ret,
4216 "ndr_pull_srv_copychunk_rsp");
4218 ok = check_copy_chunk_rsp(torture, &cc_rsp,
4219 1, /* chunks written */
4220 0, /* chunk bytes unsuccessfully written */
4221 dealloc_chunk_len + 1024); /* bytes written */
4222 torture_assert(torture, ok, "bad copy chunk response data");
4224 ok = check_zero(torture, tree, tmp_ctx, dest_h, 0, dealloc_chunk_len);
4225 torture_assert(torture, ok, "sparse zeroed range");
4227 ok = check_pattern(torture, tree, tmp_ctx, dest_h, dealloc_chunk_len,
4228 1024, dealloc_chunk_len);
4229 torture_assert(torture, ok, "copychunked range");
4231 /* copied range should be allocated in non-sparse dest */
4232 status = test_ioctl_qar_req(torture, tmp_ctx, tree, dest_h,
4233 0, /* off */
4234 dealloc_chunk_len + 1024, /* len */
4235 &far_rsp,
4236 &far_count);
4237 torture_assert_ntstatus_ok(torture, status,
4238 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4239 torture_assert_u64_equal(torture, far_count, 1,
4240 "unexpected response len");
4241 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4242 "unexpected allocation");
4243 torture_assert_u64_equal(torture, far_rsp[0].len,
4244 dealloc_chunk_len + 1024,
4245 "unexpected far len");
4247 /* set dest as sparse */
4248 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, dest_h, true);
4249 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4251 /* zero (hole-punch) all data */
4252 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, dest_h,
4253 0, /* off */
4254 dealloc_chunk_len + 1024);
4255 torture_assert_ntstatus_ok(torture, status, "zero_data");
4257 /* zeroed range might be deallocated */
4258 status = test_ioctl_qar_req(torture, tmp_ctx, tree, dest_h,
4259 0, /* off */
4260 dealloc_chunk_len + 1024, /* len */
4261 &far_rsp,
4262 &far_count);
4263 torture_assert_ntstatus_ok(torture, status,
4264 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4265 if (far_count == 0) {
4266 /* FS specific (e.g. NTFS) */
4267 torture_comment(torture, "FS deallocates file on full-range "
4268 "punch\n");
4269 } else {
4270 /* FS specific (e.g. EXT4) */
4271 torture_comment(torture, "FS doesn't deallocate file on "
4272 "full-range punch\n");
4274 ok = check_zero(torture, tree, tmp_ctx, dest_h, 0,
4275 dealloc_chunk_len + 1024);
4276 torture_assert(torture, ok, "punched zeroed range");
4278 /* copy-chunk again, this time with sparse dest */
4279 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
4280 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
4282 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
4283 &cc_rsp,
4284 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
4285 torture_assert_ndr_success(torture, ndr_ret,
4286 "ndr_pull_srv_copychunk_rsp");
4288 ok = check_copy_chunk_rsp(torture, &cc_rsp,
4289 1, /* chunks written */
4290 0, /* chunk bytes unsuccessfully written */
4291 dealloc_chunk_len + 1024); /* bytes written */
4292 torture_assert(torture, ok, "bad copy chunk response data");
4294 ok = check_zero(torture, tree, tmp_ctx, dest_h, 0, dealloc_chunk_len);
4295 torture_assert(torture, ok, "sparse zeroed range");
4297 ok = check_pattern(torture, tree, tmp_ctx, dest_h, dealloc_chunk_len,
4298 1024, dealloc_chunk_len);
4299 torture_assert(torture, ok, "copychunked range");
4301 /* copied range may be allocated in sparse dest */
4302 status = test_ioctl_qar_req(torture, tmp_ctx, tree, dest_h,
4303 0, /* off */
4304 dealloc_chunk_len + 1024, /* len */
4305 &far_rsp,
4306 &far_count);
4307 torture_assert_ntstatus_ok(torture, status,
4308 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4309 torture_assert_u64_equal(torture, far_count, 1,
4310 "unexpected response len");
4312 * FS specific: sparse region may be unallocated in dest if copy-chunk
4313 * is handled in a sparse preserving way - E.g. vfs_btrfs
4314 * with BTRFS_IOC_CLONE_RANGE.
4316 if (far_rsp[0].file_off == dealloc_chunk_len) {
4317 torture_comment(torture, "copy-chunk sparse range preserved\n");
4318 torture_assert_u64_equal(torture, far_rsp[0].len, 1024,
4319 "unexpected far len");
4320 } else {
4321 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4322 "unexpected allocation");
4323 torture_assert_u64_equal(torture, far_rsp[0].len,
4324 dealloc_chunk_len + 1024,
4325 "unexpected far len");
4328 smb2_util_close(tree, src_h);
4329 smb2_util_close(tree, dest_h);
4330 talloc_free(tmp_ctx);
4331 return true;
4334 static bool test_ioctl_sparse_punch_invalid(struct torture_context *torture,
4335 struct smb2_tree *tree)
4337 struct smb2_handle fh;
4338 NTSTATUS status;
4339 TALLOC_CTX *tmp_ctx = talloc_new(tree);
4340 bool ok;
4341 bool is_sparse;
4342 int i;
4344 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4345 FNAME, &fh, 4096, SEC_RIGHTS_FILE_ALL,
4346 FILE_ATTRIBUTE_NORMAL);
4347 torture_assert(torture, ok, "setup file");
4349 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
4350 FILE_SUPPORTS_SPARSE_FILES, &ok);
4351 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4352 if (!ok) {
4353 smb2_util_close(tree, fh);
4354 torture_skip(torture, "Sparse files not supported\n");
4357 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4358 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4359 torture_assert(torture, !is_sparse, "sparse attr before set");
4361 /* loop twice, without and with sparse attrib */
4362 for (i = 0; i <= 1; i++) {
4363 union smb_fileinfo io;
4364 struct file_alloced_range_buf *far_rsp = NULL;
4365 uint64_t far_count = 0;
4367 /* get size before & after. zero data should never change it */
4368 ZERO_STRUCT(io);
4369 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
4370 io.generic.in.file.handle = fh;
4371 status = smb2_getinfo_file(tree, tmp_ctx, &io);
4372 torture_assert_ntstatus_ok(torture, status, "getinfo");
4373 torture_assert_int_equal(torture, (int)io.all_info2.out.size,
4374 4096, "size after IO");
4376 /* valid 8 byte zero data, but after EOF */
4377 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4378 4096, /* off */
4379 4104); /* beyond_final_zero */
4380 torture_assert_ntstatus_ok(torture, status, "zero_data");
4382 /* valid 8 byte zero data, but after EOF */
4383 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4384 8192, /* off */
4385 8200); /* beyond_final_zero */
4386 torture_assert_ntstatus_ok(torture, status, "zero_data");
4388 ZERO_STRUCT(io);
4389 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
4390 io.generic.in.file.handle = fh;
4391 status = smb2_getinfo_file(tree, tmp_ctx, &io);
4392 torture_assert_ntstatus_ok(torture, status, "getinfo");
4393 torture_assert_int_equal(torture, (int)io.all_info2.out.size,
4394 4096, "size after IO");
4396 /* valid 0 byte zero data, without sparse flag */
4397 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4398 4095, /* off */
4399 4095); /* beyond_final_zero */
4400 torture_assert_ntstatus_ok(torture, status, "zero_data");
4402 /* INVALID off is past beyond_final_zero */
4403 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4404 4096, /* off */
4405 4095); /* beyond_final_zero */
4406 torture_assert_ntstatus_equal(torture, status,
4407 NT_STATUS_INVALID_PARAMETER,
4408 "invalid zero_data");
4410 /* zero length QAR - valid */
4411 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4412 0, /* off */
4413 0, /* len */
4414 &far_rsp, &far_count);
4415 torture_assert_ntstatus_ok(torture, status,
4416 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4417 torture_assert_u64_equal(torture, far_count, 0,
4418 "unexpected response len");
4420 /* QAR after EOF - valid */
4421 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4422 4096, /* off */
4423 1024, /* len */
4424 &far_rsp, &far_count);
4425 torture_assert_ntstatus_ok(torture, status,
4426 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4427 torture_assert_u64_equal(torture, far_count, 0,
4428 "unexpected response len");
4430 /* set sparse */
4431 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh,
4432 true);
4433 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4436 smb2_util_close(tree, fh);
4437 talloc_free(tmp_ctx);
4438 return true;
4441 static bool test_ioctl_sparse_perms(struct torture_context *torture,
4442 struct smb2_tree *tree)
4444 struct smb2_handle fh;
4445 NTSTATUS status;
4446 TALLOC_CTX *tmp_ctx = talloc_new(tree);
4447 bool ok;
4448 bool is_sparse;
4449 struct file_alloced_range_buf *far_rsp = NULL;
4450 uint64_t far_count = 0;
4452 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4453 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
4454 FILE_ATTRIBUTE_NORMAL);
4455 torture_assert(torture, ok, "setup file");
4457 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
4458 FILE_SUPPORTS_SPARSE_FILES, &ok);
4459 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4460 smb2_util_close(tree, fh);
4461 if (!ok) {
4462 torture_skip(torture, "Sparse files not supported\n");
4465 /* set sparse without WRITE_ATTR permission should succeed */
4466 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4467 FNAME, &fh, 0,
4468 (SEC_RIGHTS_FILE_WRITE & ~(SEC_FILE_WRITE_ATTRIBUTE
4469 | SEC_STD_WRITE_DAC
4470 | SEC_FILE_WRITE_EA)),
4471 FILE_ATTRIBUTE_NORMAL);
4472 torture_assert(torture, ok, "setup file");
4474 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4475 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4476 smb2_util_close(tree, fh);
4478 ok = test_setup_open(torture, tree, tmp_ctx,
4479 FNAME, &fh, SEC_RIGHTS_FILE_ALL,
4480 FILE_ATTRIBUTE_NORMAL);
4481 torture_assert(torture, ok, "setup file");
4482 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4483 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4484 torture_assert(torture, is_sparse, "sparse after set");
4485 smb2_util_close(tree, fh);
4487 /* attempt get sparse without READ_DATA permission */
4488 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4489 FNAME, &fh, 0,
4490 (SEC_RIGHTS_FILE_READ & ~SEC_FILE_READ_DATA),
4491 FILE_ATTRIBUTE_NORMAL);
4492 torture_assert(torture, ok, "setup file");
4494 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4495 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4496 torture_assert(torture, !is_sparse, "sparse set");
4497 smb2_util_close(tree, fh);
4499 /* attempt to set sparse with only WRITE_ATTR permission */
4500 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4501 FNAME, &fh, 0,
4502 SEC_FILE_WRITE_ATTRIBUTE,
4503 FILE_ATTRIBUTE_NORMAL);
4504 torture_assert(torture, ok, "setup file");
4506 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4507 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4508 smb2_util_close(tree, fh);
4510 /* attempt to set sparse with only WRITE_DATA permission */
4511 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4512 FNAME, &fh, 0,
4513 SEC_FILE_WRITE_DATA,
4514 FILE_ATTRIBUTE_NORMAL);
4515 torture_assert(torture, ok, "setup file");
4517 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4518 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4519 smb2_util_close(tree, fh);
4521 ok = test_setup_open(torture, tree, tmp_ctx,
4522 FNAME, &fh, SEC_RIGHTS_FILE_ALL,
4523 FILE_ATTRIBUTE_NORMAL);
4524 torture_assert(torture, ok, "setup file");
4525 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4526 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4527 torture_assert(torture, is_sparse, "sparse after set");
4528 smb2_util_close(tree, fh);
4530 /* attempt to set sparse with only APPEND_DATA permission */
4531 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4532 FNAME, &fh, 0,
4533 SEC_FILE_APPEND_DATA,
4534 FILE_ATTRIBUTE_NORMAL);
4535 torture_assert(torture, ok, "setup file");
4537 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4538 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4539 smb2_util_close(tree, fh);
4541 ok = test_setup_open(torture, tree, tmp_ctx,
4542 FNAME, &fh, SEC_RIGHTS_FILE_ALL,
4543 FILE_ATTRIBUTE_NORMAL);
4544 torture_assert(torture, ok, "setup file");
4545 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4546 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4547 torture_assert(torture, is_sparse, "sparse after set");
4548 smb2_util_close(tree, fh);
4550 /* attempt to set sparse with only WRITE_EA permission - should fail */
4551 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4552 FNAME, &fh, 0,
4553 SEC_FILE_WRITE_EA,
4554 FILE_ATTRIBUTE_NORMAL);
4555 torture_assert(torture, ok, "setup file");
4557 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4558 torture_assert_ntstatus_equal(torture, status,
4559 NT_STATUS_ACCESS_DENIED,
4560 "FSCTL_SET_SPARSE permission");
4561 smb2_util_close(tree, fh);
4563 ok = test_setup_open(torture, tree, tmp_ctx,
4564 FNAME, &fh, SEC_RIGHTS_FILE_ALL,
4565 FILE_ATTRIBUTE_NORMAL);
4566 torture_assert(torture, ok, "setup file");
4567 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4568 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4569 torture_assert(torture, !is_sparse, "sparse after set");
4570 smb2_util_close(tree, fh);
4572 /* attempt QAR with only READ_ATTR permission - should fail */
4573 ok = test_setup_open(torture, tree, tmp_ctx,
4574 FNAME, &fh, SEC_FILE_READ_ATTRIBUTE,
4575 FILE_ATTRIBUTE_NORMAL);
4576 torture_assert(torture, ok, "setup file");
4577 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4578 4096, /* off */
4579 1024, /* len */
4580 &far_rsp, &far_count);
4581 torture_assert_ntstatus_equal(torture, status,
4582 NT_STATUS_ACCESS_DENIED,
4583 "FSCTL_QUERY_ALLOCATED_RANGES req passed");
4584 smb2_util_close(tree, fh);
4586 /* attempt QAR with only READ_DATA permission */
4587 ok = test_setup_open(torture, tree, tmp_ctx,
4588 FNAME, &fh, SEC_FILE_READ_DATA,
4589 FILE_ATTRIBUTE_NORMAL);
4590 torture_assert(torture, ok, "setup file");
4591 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4592 0, /* off */
4593 1024, /* len */
4594 &far_rsp, &far_count);
4595 torture_assert_ntstatus_ok(torture, status,
4596 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4597 torture_assert_u64_equal(torture, far_count, 0,
4598 "unexpected response len");
4599 smb2_util_close(tree, fh);
4601 /* attempt QAR with only READ_EA permission - should fail */
4602 ok = test_setup_open(torture, tree, tmp_ctx,
4603 FNAME, &fh, SEC_FILE_READ_EA,
4604 FILE_ATTRIBUTE_NORMAL);
4605 torture_assert(torture, ok, "setup file");
4606 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4607 4096, /* off */
4608 1024, /* len */
4609 &far_rsp, &far_count);
4610 torture_assert_ntstatus_equal(torture, status,
4611 NT_STATUS_ACCESS_DENIED,
4612 "FSCTL_QUERY_ALLOCATED_RANGES req passed");
4613 smb2_util_close(tree, fh);
4615 /* setup file for ZERO_DATA permissions tests */
4616 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4617 FNAME, &fh, 8192,
4618 SEC_RIGHTS_FILE_ALL,
4619 FILE_ATTRIBUTE_NORMAL);
4620 torture_assert(torture, ok, "setup file");
4622 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4623 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4624 smb2_util_close(tree, fh);
4626 /* attempt ZERO_DATA with only WRITE_ATTR permission - should fail */
4627 ok = test_setup_open(torture, tree, tmp_ctx,
4628 FNAME, &fh, SEC_FILE_WRITE_ATTRIBUTE,
4629 FILE_ATTRIBUTE_NORMAL);
4630 torture_assert(torture, ok, "setup file");
4631 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4632 0, /* off */
4633 4096); /* beyond_final_zero */
4634 torture_assert_ntstatus_equal(torture, status,
4635 NT_STATUS_ACCESS_DENIED,
4636 "zero_data permission");
4637 smb2_util_close(tree, fh);
4639 /* attempt ZERO_DATA with only WRITE_DATA permission */
4640 ok = test_setup_open(torture, tree, tmp_ctx,
4641 FNAME, &fh, SEC_FILE_WRITE_DATA,
4642 FILE_ATTRIBUTE_NORMAL);
4643 torture_assert(torture, ok, "setup file");
4644 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4645 0, /* off */
4646 4096); /* beyond_final_zero */
4647 torture_assert_ntstatus_ok(torture, status, "zero_data");
4648 smb2_util_close(tree, fh);
4650 /* attempt ZERO_DATA with only APPEND_DATA permission - should fail */
4651 ok = test_setup_open(torture, tree, tmp_ctx,
4652 FNAME, &fh, SEC_FILE_APPEND_DATA,
4653 FILE_ATTRIBUTE_NORMAL);
4654 torture_assert(torture, ok, "setup file");
4655 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4656 0, /* off */
4657 4096); /* beyond_final_zero */
4658 torture_assert_ntstatus_equal(torture, status,
4659 NT_STATUS_ACCESS_DENIED,
4660 "zero_data permission");
4661 smb2_util_close(tree, fh);
4663 /* attempt ZERO_DATA with only WRITE_EA permission - should fail */
4664 ok = test_setup_open(torture, tree, tmp_ctx,
4665 FNAME, &fh, SEC_FILE_WRITE_EA,
4666 FILE_ATTRIBUTE_NORMAL);
4667 torture_assert(torture, ok, "setup file");
4668 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4669 0, /* off */
4670 4096); /* beyond_final_zero */
4671 torture_assert_ntstatus_equal(torture, status,
4672 NT_STATUS_ACCESS_DENIED,
4673 "zero_data permission");
4674 smb2_util_close(tree, fh);
4676 talloc_free(tmp_ctx);
4677 return true;
4680 static bool test_ioctl_sparse_lck(struct torture_context *torture,
4681 struct smb2_tree *tree)
4683 struct smb2_handle fh;
4684 struct smb2_handle fh2;
4685 NTSTATUS status;
4686 uint64_t dealloc_chunk_len = 64 * 1024; /* Windows 2012 */
4687 TALLOC_CTX *tmp_ctx = talloc_new(tree);
4688 bool ok;
4689 bool is_sparse;
4690 struct smb2_lock lck;
4691 struct smb2_lock_element el[1];
4692 struct file_alloced_range_buf *far_rsp = NULL;
4693 uint64_t far_count = 0;
4695 ok = test_setup_create_fill(torture, tree, tmp_ctx, FNAME, &fh,
4696 dealloc_chunk_len, SEC_RIGHTS_FILE_ALL,
4697 FILE_ATTRIBUTE_NORMAL);
4698 torture_assert(torture, ok, "setup file");
4700 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
4701 FILE_SUPPORTS_SPARSE_FILES, &ok);
4702 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4703 if (!ok) {
4704 torture_skip(torture, "Sparse files not supported\n");
4705 smb2_util_close(tree, fh);
4708 /* open and lock via separate fh2 */
4709 status = torture_smb2_testfile(tree, FNAME, &fh2);
4710 torture_assert_ntstatus_ok(torture, status, "2nd src open");
4712 lck.in.lock_count = 0x0001;
4713 lck.in.lock_sequence = 0x00000000;
4714 lck.in.file.handle = fh2;
4715 lck.in.locks = el;
4716 el[0].offset = 0;
4717 el[0].length = dealloc_chunk_len;
4718 el[0].reserved = 0;
4719 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
4721 status = smb2_lock(tree, &lck);
4722 torture_assert_ntstatus_ok(torture, status, "lock");
4724 /* set sparse while locked */
4725 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4726 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4728 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4729 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4730 torture_assert(torture, is_sparse, "sparse attr after set");
4732 /* zero data over locked range should fail */
4733 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4734 0, /* off */
4735 4096); /* beyond_final_zero */
4736 torture_assert_ntstatus_equal(torture, status,
4737 NT_STATUS_FILE_LOCK_CONFLICT,
4738 "zero_data locked");
4740 /* QAR over locked range should pass */
4741 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4742 0, /* off */
4743 4096, /* len */
4744 &far_rsp, &far_count);
4745 torture_assert_ntstatus_ok(torture, status,
4746 "FSCTL_QUERY_ALLOCATED_RANGES locked");
4747 torture_assert_u64_equal(torture, far_count, 1,
4748 "unexpected response len");
4749 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4750 "unexpected allocation");
4751 torture_assert_u64_equal(torture, far_rsp[0].len,
4752 4096,
4753 "unexpected far len");
4755 /* zero data over range past EOF should pass */
4756 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4757 dealloc_chunk_len, /* off */
4758 dealloc_chunk_len + 4096);
4759 torture_assert_ntstatus_ok(torture, status,
4760 "zero_data past EOF locked");
4762 /* QAR over range past EOF should pass */
4763 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4764 dealloc_chunk_len, /* off */
4765 4096, /* len */
4766 &far_rsp, &far_count);
4767 torture_assert_ntstatus_ok(torture, status,
4768 "FSCTL_QUERY_ALLOCATED_RANGES past EOF locked");
4769 torture_assert_u64_equal(torture, far_count, 0,
4770 "unexpected response len");
4772 lck.in.lock_count = 0x0001;
4773 lck.in.lock_sequence = 0x00000001;
4774 lck.in.file.handle = fh2;
4775 lck.in.locks = el;
4776 el[0].offset = 0;
4777 el[0].length = dealloc_chunk_len;
4778 el[0].reserved = 0;
4779 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
4780 status = smb2_lock(tree, &lck);
4781 torture_assert_ntstatus_ok(torture, status, "unlock");
4783 smb2_util_close(tree, fh2);
4784 smb2_util_close(tree, fh);
4785 talloc_free(tmp_ctx);
4786 return true;
4789 /* alleviate QAR off-by-one bug paranoia - help me ob1 */
4790 static bool test_ioctl_sparse_qar_ob1(struct torture_context *torture,
4791 struct smb2_tree *tree)
4793 struct smb2_handle fh;
4794 NTSTATUS status;
4795 TALLOC_CTX *tmp_ctx = talloc_new(tree);
4796 bool ok;
4797 uint64_t dealloc_chunk_len = 64 * 1024; /* Windows 2012 */
4798 struct file_alloced_range_buf *far_rsp = NULL;
4799 uint64_t far_count = 0;
4801 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4802 FNAME, &fh, dealloc_chunk_len * 2,
4803 SEC_RIGHTS_FILE_ALL,
4804 FILE_ATTRIBUTE_NORMAL);
4805 torture_assert(torture, ok, "setup file");
4807 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
4808 FILE_SUPPORTS_SPARSE_FILES, &ok);
4809 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4810 if (!ok) {
4811 torture_skip(torture, "Sparse files not supported\n");
4812 smb2_util_close(tree, fh);
4815 /* non-sparse QAR with range one before EOF */
4816 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4817 0, /* off */
4818 dealloc_chunk_len * 2 - 1, /* len */
4819 &far_rsp, &far_count);
4820 torture_assert_ntstatus_ok(torture, status,
4821 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4822 torture_assert_u64_equal(torture, far_count, 1,
4823 "unexpected response len");
4824 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4825 "unexpected allocation");
4826 torture_assert_u64_equal(torture, far_rsp[0].len,
4827 dealloc_chunk_len * 2 - 1,
4828 "unexpected far len");
4830 /* non-sparse QAR with range one after EOF */
4831 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4832 0, /* off */
4833 dealloc_chunk_len * 2 + 1, /* len */
4834 &far_rsp, &far_count);
4835 torture_assert_ntstatus_ok(torture, status,
4836 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4837 torture_assert_u64_equal(torture, far_count, 1,
4838 "unexpected response len");
4839 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4840 "unexpected allocation");
4841 torture_assert_u64_equal(torture, far_rsp[0].len,
4842 dealloc_chunk_len * 2,
4843 "unexpected far len");
4845 /* non-sparse QAR with range one after EOF from off=1 */
4846 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4847 1, /* off */
4848 dealloc_chunk_len * 2, /* len */
4849 &far_rsp, &far_count);
4850 torture_assert_ntstatus_ok(torture, status,
4851 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4852 torture_assert_u64_equal(torture, far_count, 1,
4853 "unexpected response len");
4854 torture_assert_u64_equal(torture, far_rsp[0].file_off, 1,
4855 "unexpected allocation");
4856 torture_assert_u64_equal(torture, far_rsp[0].len,
4857 dealloc_chunk_len * 2 - 1,
4858 "unexpected far len");
4860 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4861 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4863 /* punch out second chunk */
4864 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4865 dealloc_chunk_len, /* off */
4866 dealloc_chunk_len * 2);
4867 torture_assert_ntstatus_ok(torture, status, "zero_data");
4869 /* sparse QAR with range one before hole */
4870 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4871 0, /* off */
4872 dealloc_chunk_len - 1, /* len */
4873 &far_rsp, &far_count);
4874 torture_assert_ntstatus_ok(torture, status,
4875 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4876 torture_assert_u64_equal(torture, far_count, 1,
4877 "unexpected response len");
4878 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4879 "unexpected allocation");
4880 torture_assert_u64_equal(torture, far_rsp[0].len,
4881 dealloc_chunk_len - 1,
4882 "unexpected far len");
4884 /* sparse QAR with range one after hole */
4885 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4886 0, /* off */
4887 dealloc_chunk_len + 1, /* len */
4888 &far_rsp, &far_count);
4889 torture_assert_ntstatus_ok(torture, status,
4890 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4891 torture_assert_u64_equal(torture, far_count, 1,
4892 "unexpected response len");
4893 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4894 "unexpected allocation");
4895 torture_assert_u64_equal(torture, far_rsp[0].len,
4896 dealloc_chunk_len,
4897 "unexpected far len");
4899 /* sparse QAR with range one after hole from off=1 */
4900 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4901 1, /* off */
4902 dealloc_chunk_len, /* len */
4903 &far_rsp, &far_count);
4904 torture_assert_ntstatus_ok(torture, status,
4905 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4906 torture_assert_u64_equal(torture, far_count, 1,
4907 "unexpected response len");
4908 torture_assert_u64_equal(torture, far_rsp[0].file_off, 1,
4909 "unexpected allocation");
4910 torture_assert_u64_equal(torture, far_rsp[0].len,
4911 dealloc_chunk_len - 1,
4912 "unexpected far len");
4914 /* sparse QAR with range one before EOF from off=chunk_len-1 */
4915 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4916 dealloc_chunk_len - 1, /* off */
4917 dealloc_chunk_len, /* len */
4918 &far_rsp, &far_count);
4919 torture_assert_ntstatus_ok(torture, status,
4920 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4921 torture_assert_u64_equal(torture, far_count, 1,
4922 "unexpected response len");
4923 torture_assert_u64_equal(torture, far_rsp[0].file_off,
4924 dealloc_chunk_len - 1,
4925 "unexpected allocation");
4926 torture_assert_u64_equal(torture, far_rsp[0].len,
4927 1, "unexpected far len");
4929 /* sparse QAR with range one after EOF from off=chunk_len+1 */
4930 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4931 dealloc_chunk_len + 1, /* off */
4932 dealloc_chunk_len, /* len */
4933 &far_rsp, &far_count);
4934 torture_assert_ntstatus_ok(torture, status,
4935 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4936 torture_assert_u64_equal(torture, far_count, 0,
4937 "unexpected response len");
4938 smb2_util_close(tree, fh);
4939 talloc_free(tmp_ctx);
4940 return true;
4943 /* test QAR with multi-range responses */
4944 static bool test_ioctl_sparse_qar_multi(struct torture_context *torture,
4945 struct smb2_tree *tree)
4947 struct smb2_handle fh;
4948 NTSTATUS status;
4949 TALLOC_CTX *tmp_ctx = talloc_new(tree);
4950 bool ok;
4951 uint64_t dealloc_chunk_len = 64 * 1024; /* Windows 2012 */
4952 uint64_t this_off;
4953 int i;
4954 struct file_alloced_range_buf *far_rsp = NULL;
4955 uint64_t far_count = 0;
4957 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4958 FNAME, &fh, dealloc_chunk_len * 2,
4959 SEC_RIGHTS_FILE_ALL,
4960 FILE_ATTRIBUTE_NORMAL);
4961 torture_assert(torture, ok, "setup file");
4963 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
4964 FILE_SUPPORTS_SPARSE_FILES, &ok);
4965 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4966 if (!ok) {
4967 torture_skip(torture, "Sparse files not supported\n");
4968 smb2_util_close(tree, fh);
4971 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4972 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4974 /* each loop, write out two chunks and punch the first out */
4975 for (i = 0; i < 10; i++) {
4976 this_off = i * dealloc_chunk_len * 2;
4978 ok = write_pattern(torture, tree, tmp_ctx, fh,
4979 this_off, /* off */
4980 dealloc_chunk_len * 2, /* len */
4981 this_off); /* pattern offset */
4982 torture_assert(torture, ok, "write pattern");
4984 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4985 this_off, /* off */
4986 this_off + dealloc_chunk_len);
4987 torture_assert_ntstatus_ok(torture, status, "zero_data");
4990 /* should now have one separate region for each iteration */
4991 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4993 10 * dealloc_chunk_len * 2,
4994 &far_rsp, &far_count);
4995 torture_assert_ntstatus_ok(torture, status,
4996 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4997 if (far_count == 1) {
4998 torture_comment(torture, "this FS doesn't deallocate 64K"
4999 "zeroed ranges in sparse files\n");
5000 return true; /* FS specific, not a failure */
5002 torture_assert_u64_equal(torture, far_count, 10,
5003 "unexpected response len");
5004 for (i = 0; i < 10; i++) {
5005 this_off = i * dealloc_chunk_len * 2;
5007 torture_assert_u64_equal(torture, far_rsp[i].file_off,
5008 this_off + dealloc_chunk_len,
5009 "unexpected allocation");
5010 torture_assert_u64_equal(torture, far_rsp[i].len,
5011 dealloc_chunk_len,
5012 "unexpected far len");
5015 smb2_util_close(tree, fh);
5016 talloc_free(tmp_ctx);
5017 return true;
5020 static bool test_ioctl_sparse_qar_overflow(struct torture_context *torture,
5021 struct smb2_tree *tree)
5023 struct smb2_handle fh;
5024 union smb_ioctl ioctl;
5025 struct file_alloced_range_buf far_buf;
5026 NTSTATUS status;
5027 enum ndr_err_code ndr_ret;
5028 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5029 bool ok;
5031 ok = test_setup_create_fill(torture, tree, tmp_ctx,
5032 FNAME, &fh, 1024, SEC_RIGHTS_FILE_ALL,
5033 FILE_ATTRIBUTE_NORMAL);
5034 torture_assert(torture, ok, "setup file");
5036 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
5037 FILE_SUPPORTS_SPARSE_FILES, &ok);
5038 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
5039 if (!ok) {
5040 smb2_util_close(tree, fh);
5041 torture_skip(torture, "Sparse files not supported\n");
5044 /* no allocated ranges, no space for range response, should pass */
5045 ZERO_STRUCT(ioctl);
5046 ioctl.smb2.level = RAW_IOCTL_SMB2;
5047 ioctl.smb2.in.file.handle = fh;
5048 ioctl.smb2.in.function = FSCTL_QUERY_ALLOCATED_RANGES;
5049 ioctl.smb2.in.max_response_size = 1024;
5050 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
5052 /* off + length wraps around to 511 */
5053 far_buf.file_off = 512;
5054 far_buf.len = 0xffffffffffffffffLL;
5055 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5056 &far_buf,
5057 (ndr_push_flags_fn_t)ndr_push_file_alloced_range_buf);
5058 torture_assert_ndr_success(torture, ndr_ret, "push far ndr buf");
5060 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5061 torture_assert_ntstatus_equal(torture, status,
5062 NT_STATUS_INVALID_PARAMETER,
5063 "FSCTL_QUERY_ALLOCATED_RANGES overflow");
5065 return true;
5068 static NTSTATUS test_ioctl_trim_supported(struct torture_context *torture,
5069 struct smb2_tree *tree,
5070 TALLOC_CTX *mem_ctx,
5071 struct smb2_handle *fh,
5072 bool *trim_support)
5074 NTSTATUS status;
5075 union smb_fsinfo info;
5077 ZERO_STRUCT(info);
5078 info.generic.level = RAW_QFS_SECTOR_SIZE_INFORMATION;
5079 info.generic.handle = *fh;
5080 status = smb2_getinfo_fs(tree, tree, &info);
5081 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_INFO_CLASS)) {
5083 * Windows < Server 2012, 8 etc. don't support this info level
5084 * or the trim ioctl. Ignore the error and let the caller skip.
5086 *trim_support = false;
5087 return NT_STATUS_OK;
5088 } else if (!NT_STATUS_IS_OK(status)) {
5089 return status;
5092 torture_comment(torture, "sector size info: lb/s=%u, pb/sA=%u, "
5093 "pb/sP=%u, fse/sA=%u, flags=0x%x, bosa=%u, bopa=%u\n",
5094 (unsigned)info.sector_size_info.out.logical_bytes_per_sector,
5095 (unsigned)info.sector_size_info.out.phys_bytes_per_sector_atomic,
5096 (unsigned)info.sector_size_info.out.phys_bytes_per_sector_perf,
5097 (unsigned)info.sector_size_info.out.fs_effective_phys_bytes_per_sector_atomic,
5098 (unsigned)info.sector_size_info.out.flags,
5099 (unsigned)info.sector_size_info.out.byte_off_sector_align,
5100 (unsigned)info.sector_size_info.out.byte_off_partition_align);
5102 if (info.sector_size_info.out.flags & QFS_SSINFO_FLAGS_TRIM_ENABLED) {
5103 *trim_support = true;
5104 } else {
5105 *trim_support = false;
5107 return NT_STATUS_OK;
5110 static bool test_setup_trim(struct torture_context *torture,
5111 struct smb2_tree *tree,
5112 TALLOC_CTX *mem_ctx,
5113 uint32_t num_ranges,
5114 struct smb2_handle *fh,
5115 uint64_t file_size,
5116 uint32_t desired_access,
5117 struct fsctl_file_level_trim_req *trim_req,
5118 union smb_ioctl *ioctl)
5120 bool ok;
5122 ok = test_setup_create_fill(torture, tree, mem_ctx, FNAME,
5123 fh, file_size, desired_access,
5124 FILE_ATTRIBUTE_NORMAL);
5125 torture_assert(torture, ok, "src file create fill");
5127 ZERO_STRUCTPN(ioctl);
5128 ioctl->smb2.level = RAW_IOCTL_SMB2;
5129 ioctl->smb2.in.file.handle = *fh;
5130 ioctl->smb2.in.function = FSCTL_FILE_LEVEL_TRIM;
5131 ioctl->smb2.in.max_response_size
5132 = sizeof(struct fsctl_file_level_trim_rsp);
5133 ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
5135 ZERO_STRUCTPN(trim_req);
5136 /* leave key as zero for now. TODO test locking with differing keys */
5137 trim_req->num_ranges = num_ranges;
5138 trim_req->ranges = talloc_zero_array(mem_ctx,
5139 struct file_level_trim_range,
5140 num_ranges);
5141 torture_assert(torture, (trim_req->ranges != NULL), "no memory for ranges");
5143 return true;
5146 static bool test_ioctl_trim_simple(struct torture_context *torture,
5147 struct smb2_tree *tree)
5149 struct smb2_handle fh;
5150 NTSTATUS status;
5151 union smb_ioctl ioctl;
5152 bool trim_supported;
5153 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5154 struct fsctl_file_level_trim_req trim_req;
5155 struct fsctl_file_level_trim_rsp trim_rsp;
5156 uint64_t trim_chunk_len = 64 * 1024; /* trim 64K chunks */
5157 enum ndr_err_code ndr_ret;
5158 bool ok;
5160 ok = test_setup_trim(torture, tree, tmp_ctx,
5161 1, /* 1 range */
5162 &fh, 2 * trim_chunk_len, /* fill 128K file */
5163 SEC_RIGHTS_FILE_ALL,
5164 &trim_req,
5165 &ioctl);
5166 if (!ok) {
5167 torture_fail(torture, "setup trim error");
5170 status = test_ioctl_trim_supported(torture, tree, tmp_ctx, &fh,
5171 &trim_supported);
5172 torture_assert_ntstatus_ok(torture, status, "fsinfo");
5173 if (!trim_supported) {
5174 smb2_util_close(tree, fh);
5175 talloc_free(tmp_ctx);
5176 torture_skip(torture, "trim not supported\n");
5179 /* trim first chunk, leave second */
5180 trim_req.ranges[0].off = 0;
5181 trim_req.ranges[0].len = trim_chunk_len;
5183 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, &trim_req,
5184 (ndr_push_flags_fn_t)ndr_push_fsctl_file_level_trim_req);
5185 torture_assert_ndr_success(torture, ndr_ret,
5186 "ndr_push_fsctl_file_level_trim_req");
5188 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5189 torture_assert_ntstatus_ok(torture, status, "FILE_LEVEL_TRIM_RANGE");
5191 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
5192 &trim_rsp,
5193 (ndr_pull_flags_fn_t)ndr_pull_fsctl_file_level_trim_rsp);
5194 torture_assert_ndr_success(torture, ndr_ret,
5195 "ndr_pull_fsctl_file_level_trim_rsp");
5197 torture_assert_int_equal(torture, trim_rsp.num_ranges_processed, 1, "");
5199 /* second half of the file should remain consitent */
5200 ok = check_pattern(torture, tree, tmp_ctx, fh, trim_chunk_len,
5201 trim_chunk_len, trim_chunk_len);
5202 torture_assert(torture, ok, "non-trimmed range inconsistent");
5204 return true;
5207 static bool test_setup_dup_extents(struct torture_context *tctx,
5208 struct smb2_tree *tree,
5209 TALLOC_CTX *mem_ctx,
5210 struct smb2_handle *src_h,
5211 uint64_t src_size,
5212 uint32_t src_desired_access,
5213 struct smb2_handle *dest_h,
5214 uint64_t dest_size,
5215 uint32_t dest_desired_access,
5216 struct fsctl_dup_extents_to_file *dup_ext_buf,
5217 union smb_ioctl *ioctl)
5219 bool ok;
5221 ok = test_setup_create_fill(tctx, tree, mem_ctx, FNAME,
5222 src_h, src_size, src_desired_access,
5223 FILE_ATTRIBUTE_NORMAL);
5224 torture_assert(tctx, ok, "src file create fill");
5226 ok = test_setup_create_fill(tctx, tree, mem_ctx, FNAME2,
5227 dest_h, dest_size, dest_desired_access,
5228 FILE_ATTRIBUTE_NORMAL);
5229 torture_assert(tctx, ok, "dest file create fill");
5231 ZERO_STRUCTPN(ioctl);
5232 ioctl->smb2.level = RAW_IOCTL_SMB2;
5233 ioctl->smb2.in.file.handle = *dest_h;
5234 ioctl->smb2.in.function = FSCTL_DUP_EXTENTS_TO_FILE;
5235 ioctl->smb2.in.max_response_size = 0;
5236 ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
5238 ZERO_STRUCTPN(dup_ext_buf);
5239 smb2_push_handle(dup_ext_buf->source_fid, src_h);
5241 return true;
5244 static bool test_ioctl_dup_extents_simple(struct torture_context *tctx,
5245 struct smb2_tree *tree)
5247 struct smb2_handle src_h;
5248 struct smb2_handle dest_h;
5249 NTSTATUS status;
5250 union smb_ioctl ioctl;
5251 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5252 struct fsctl_dup_extents_to_file dup_ext_buf;
5253 enum ndr_err_code ndr_ret;
5254 union smb_fileinfo io;
5255 union smb_setfileinfo sinfo;
5256 bool ok;
5258 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5259 &src_h, 4096, /* fill 4096 byte src file */
5260 SEC_RIGHTS_FILE_ALL,
5261 &dest_h, 0, /* 0 byte dest file */
5262 SEC_RIGHTS_FILE_ALL,
5263 &dup_ext_buf,
5264 &ioctl);
5265 if (!ok) {
5266 torture_fail(tctx, "setup dup extents error");
5269 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5270 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
5271 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5272 if (!ok) {
5273 smb2_util_close(tree, src_h);
5274 smb2_util_close(tree, dest_h);
5275 talloc_free(tmp_ctx);
5276 torture_skip(tctx, "block refcounting not supported\n");
5279 /* extend dest to match src len */
5280 ZERO_STRUCT(sinfo);
5281 sinfo.end_of_file_info.level =
5282 RAW_SFILEINFO_END_OF_FILE_INFORMATION;
5283 sinfo.end_of_file_info.in.file.handle = dest_h;
5284 sinfo.end_of_file_info.in.size = 4096;
5285 status = smb2_setinfo_file(tree, &sinfo);
5286 torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
5288 /* copy all src file data */
5289 dup_ext_buf.source_off = 0;
5290 dup_ext_buf.target_off = 0;
5291 dup_ext_buf.byte_count = 4096;
5293 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5294 &dup_ext_buf,
5295 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5296 torture_assert_ndr_success(tctx, ndr_ret,
5297 "ndr_push_fsctl_dup_extents_to_file");
5299 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5300 torture_assert_ntstatus_ok(tctx, status,
5301 "FSCTL_DUP_EXTENTS_TO_FILE");
5303 /* the file size shouldn't have been changed by this operation! */
5304 ZERO_STRUCT(io);
5305 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5306 io.generic.in.file.handle = dest_h;
5307 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5308 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5309 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5310 4096, "size after IO");
5312 smb2_util_close(tree, src_h);
5313 smb2_util_close(tree, dest_h);
5315 /* reopen for pattern check */
5316 ok = test_setup_open(tctx, tree, tmp_ctx, FNAME, &src_h,
5317 SEC_RIGHTS_FILE_ALL, FILE_ATTRIBUTE_NORMAL);
5318 torture_assert_ntstatus_ok(tctx, status, "src open after dup");
5319 ok = test_setup_open(tctx, tree, tmp_ctx, FNAME2, &dest_h,
5320 SEC_RIGHTS_FILE_ALL, FILE_ATTRIBUTE_NORMAL);
5321 torture_assert_ntstatus_ok(tctx, status, "dest open after dup");
5323 ok = check_pattern(tctx, tree, tmp_ctx, src_h, 0, 4096, 0);
5324 if (!ok) {
5325 torture_fail(tctx, "inconsistent src file data");
5328 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 4096, 0);
5329 if (!ok) {
5330 torture_fail(tctx, "inconsistent dest file data");
5333 smb2_util_close(tree, src_h);
5334 smb2_util_close(tree, dest_h);
5335 talloc_free(tmp_ctx);
5336 return true;
5339 static bool test_ioctl_dup_extents_len_beyond_dest(struct torture_context *tctx,
5340 struct smb2_tree *tree)
5342 struct smb2_handle src_h;
5343 struct smb2_handle dest_h;
5344 NTSTATUS status;
5345 union smb_ioctl ioctl;
5346 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5347 struct fsctl_dup_extents_to_file dup_ext_buf;
5348 enum ndr_err_code ndr_ret;
5349 union smb_fileinfo io;
5350 union smb_setfileinfo sinfo;
5351 bool ok;
5353 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5354 &src_h, 32768, /* fill 32768 byte src file */
5355 SEC_RIGHTS_FILE_ALL,
5356 &dest_h, 0, /* 0 byte dest file */
5357 SEC_RIGHTS_FILE_ALL,
5358 &dup_ext_buf,
5359 &ioctl);
5360 if (!ok) {
5361 torture_fail(tctx, "setup dup extents error");
5364 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5365 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
5366 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5367 if (!ok) {
5368 smb2_util_close(tree, src_h);
5369 smb2_util_close(tree, dest_h);
5370 talloc_free(tmp_ctx);
5371 torture_skip(tctx, "block refcounting not supported\n");
5374 ZERO_STRUCT(io);
5375 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5376 io.generic.in.file.handle = dest_h;
5377 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5378 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5379 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5380 0, "size after IO");
5382 /* copy all src file data */
5383 dup_ext_buf.source_off = 0;
5384 dup_ext_buf.target_off = 0;
5385 dup_ext_buf.byte_count = 32768;
5387 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5388 &dup_ext_buf,
5389 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5390 torture_assert_ndr_success(tctx, ndr_ret,
5391 "ndr_push_fsctl_dup_extents_to_file");
5393 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5394 #if 0
5396 * 2.3.8 FSCTL_DUPLICATE_EXTENTS_TO_FILE Reply - this should fail, but
5397 * passes against WS2016 RTM!
5399 torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_SUPPORTED,
5400 "FSCTL_DUP_EXTENTS_TO_FILE");
5401 #endif
5403 /* the file sizes shouldn't have been changed */
5404 ZERO_STRUCT(io);
5405 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5406 io.generic.in.file.handle = src_h;
5407 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5408 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5409 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5410 32768, "size after IO");
5412 ZERO_STRUCT(io);
5413 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5414 io.generic.in.file.handle = dest_h;
5415 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5416 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5417 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5418 0, "size after IO");
5420 /* extend dest */
5421 ZERO_STRUCT(sinfo);
5422 sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
5423 sinfo.end_of_file_info.in.file.handle = dest_h;
5424 sinfo.end_of_file_info.in.size = 32768;
5425 status = smb2_setinfo_file(tree, &sinfo);
5426 torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
5428 ok = check_zero(tctx, tree, tmp_ctx, dest_h, 0, 32768);
5429 if (!ok) {
5430 torture_fail(tctx, "inconsistent file data");
5433 /* reissue ioctl, now with enough space */
5434 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5435 torture_assert_ntstatus_ok(tctx, status,
5436 "FSCTL_DUP_EXTENTS_TO_FILE");
5438 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
5439 if (!ok) {
5440 torture_fail(tctx, "inconsistent file data");
5443 smb2_util_close(tree, src_h);
5444 smb2_util_close(tree, dest_h);
5445 talloc_free(tmp_ctx);
5446 return true;
5449 static bool test_ioctl_dup_extents_len_beyond_src(struct torture_context *tctx,
5450 struct smb2_tree *tree)
5452 struct smb2_handle src_h;
5453 struct smb2_handle dest_h;
5454 NTSTATUS status;
5455 union smb_ioctl ioctl;
5456 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5457 struct fsctl_dup_extents_to_file dup_ext_buf;
5458 enum ndr_err_code ndr_ret;
5459 union smb_fileinfo io;
5460 bool ok;
5462 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5463 &src_h, 32768, /* fill 32768 byte src file */
5464 SEC_RIGHTS_FILE_ALL,
5465 &dest_h, 0, /* 0 byte dest file */
5466 SEC_RIGHTS_FILE_ALL,
5467 &dup_ext_buf,
5468 &ioctl);
5469 if (!ok) {
5470 torture_fail(tctx, "setup dup extents error");
5473 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5474 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
5475 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5476 if (!ok) {
5477 smb2_util_close(tree, src_h);
5478 smb2_util_close(tree, dest_h);
5479 talloc_free(tmp_ctx);
5480 torture_skip(tctx, "block refcounting not supported\n");
5483 ZERO_STRUCT(io);
5484 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5485 io.generic.in.file.handle = dest_h;
5486 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5487 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5488 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5489 0, "size after IO");
5491 /* exceed src file len */
5492 dup_ext_buf.source_off = 0;
5493 dup_ext_buf.target_off = 0;
5494 dup_ext_buf.byte_count = 32768 * 2;
5496 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5497 &dup_ext_buf,
5498 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5499 torture_assert_ndr_success(tctx, ndr_ret,
5500 "ndr_push_fsctl_dup_extents_to_file");
5502 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5503 torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_SUPPORTED,
5504 "FSCTL_DUP_EXTENTS_TO_FILE");
5506 /* the file sizes shouldn't have been changed */
5507 ZERO_STRUCT(io);
5508 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5509 io.generic.in.file.handle = src_h;
5510 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5511 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5512 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5513 32768, "size after IO");
5515 ZERO_STRUCT(io);
5516 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5517 io.generic.in.file.handle = dest_h;
5518 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5519 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5520 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5521 0, "size after IO");
5523 smb2_util_close(tree, src_h);
5524 smb2_util_close(tree, dest_h);
5525 talloc_free(tmp_ctx);
5526 return true;
5529 static bool test_ioctl_dup_extents_len_zero(struct torture_context *tctx,
5530 struct smb2_tree *tree)
5532 struct smb2_handle src_h;
5533 struct smb2_handle dest_h;
5534 NTSTATUS status;
5535 union smb_ioctl ioctl;
5536 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5537 struct fsctl_dup_extents_to_file dup_ext_buf;
5538 enum ndr_err_code ndr_ret;
5539 union smb_fileinfo io;
5540 bool ok;
5542 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5543 &src_h, 32768, /* fill 32768 byte src file */
5544 SEC_RIGHTS_FILE_ALL,
5545 &dest_h, 0, /* 0 byte dest file */
5546 SEC_RIGHTS_FILE_ALL,
5547 &dup_ext_buf,
5548 &ioctl);
5549 if (!ok) {
5550 torture_fail(tctx, "setup dup extents error");
5553 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5554 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
5555 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5556 if (!ok) {
5557 smb2_util_close(tree, src_h);
5558 smb2_util_close(tree, dest_h);
5559 talloc_free(tmp_ctx);
5560 torture_skip(tctx, "block refcounting not supported\n");
5563 ZERO_STRUCT(io);
5564 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5565 io.generic.in.file.handle = dest_h;
5566 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5567 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5568 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5569 0, "size after IO");
5571 dup_ext_buf.source_off = 0;
5572 dup_ext_buf.target_off = 0;
5573 dup_ext_buf.byte_count = 0;
5575 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5576 &dup_ext_buf,
5577 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5578 torture_assert_ndr_success(tctx, ndr_ret,
5579 "ndr_push_fsctl_dup_extents_to_file");
5581 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5582 torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
5584 /* the file sizes shouldn't have been changed */
5585 ZERO_STRUCT(io);
5586 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5587 io.generic.in.file.handle = src_h;
5588 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5589 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5590 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5591 32768, "size after IO");
5593 ZERO_STRUCT(io);
5594 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5595 io.generic.in.file.handle = dest_h;
5596 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5597 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5598 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5599 0, "size after IO");
5601 smb2_util_close(tree, src_h);
5602 smb2_util_close(tree, dest_h);
5603 talloc_free(tmp_ctx);
5604 return true;
5607 static bool test_ioctl_dup_extents_sparse_src(struct torture_context *tctx,
5608 struct smb2_tree *tree)
5610 struct smb2_handle src_h;
5611 struct smb2_handle dest_h;
5612 NTSTATUS status;
5613 union smb_ioctl ioctl;
5614 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5615 struct fsctl_dup_extents_to_file dup_ext_buf;
5616 enum ndr_err_code ndr_ret;
5617 union smb_setfileinfo sinfo;
5618 bool ok;
5620 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5621 &src_h, 0, /* filled after sparse flag */
5622 SEC_RIGHTS_FILE_ALL,
5623 &dest_h, 0, /* 0 byte dest file */
5624 SEC_RIGHTS_FILE_ALL,
5625 &dup_ext_buf,
5626 &ioctl);
5627 if (!ok) {
5628 torture_fail(tctx, "setup dup extents error");
5631 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5632 FILE_SUPPORTS_BLOCK_REFCOUNTING
5633 | FILE_SUPPORTS_SPARSE_FILES, &ok);
5634 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5635 if (!ok) {
5636 smb2_util_close(tree, src_h);
5637 smb2_util_close(tree, dest_h);
5638 talloc_free(tmp_ctx);
5639 torture_skip(tctx,
5640 "block refcounting and sparse files not supported\n");
5643 /* set sparse flag on src */
5644 status = test_ioctl_sparse_req(tctx, tmp_ctx, tree, src_h, true);
5645 torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_SPARSE");
5647 ok = write_pattern(tctx, tree, tmp_ctx, src_h, 0, 4096, 0);
5648 torture_assert(tctx, ok, "write pattern");
5650 /* extend dest */
5651 ZERO_STRUCT(sinfo);
5652 sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
5653 sinfo.end_of_file_info.in.file.handle = dest_h;
5654 sinfo.end_of_file_info.in.size = 4096;
5655 status = smb2_setinfo_file(tree, &sinfo);
5656 torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
5658 /* copy all src file data */
5659 dup_ext_buf.source_off = 0;
5660 dup_ext_buf.target_off = 0;
5661 dup_ext_buf.byte_count = 4096;
5663 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5664 &dup_ext_buf,
5665 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5666 torture_assert_ndr_success(tctx, ndr_ret,
5667 "ndr_push_fsctl_dup_extents_to_file");
5670 * src is sparse, but spec says: 2.3.8 FSCTL_DUPLICATE_EXTENTS_TO_FILE
5671 * Reply... STATUS_NOT_SUPPORTED: Target file is sparse, while source
5672 * is a non-sparse file.
5674 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5675 torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_SUPPORTED,
5676 "FSCTL_DUP_EXTENTS_TO_FILE");
5678 smb2_util_close(tree, src_h);
5679 smb2_util_close(tree, dest_h);
5680 talloc_free(tmp_ctx);
5681 return true;
5684 static bool test_ioctl_dup_extents_sparse_dest(struct torture_context *tctx,
5685 struct smb2_tree *tree)
5687 struct smb2_handle src_h;
5688 struct smb2_handle dest_h;
5689 NTSTATUS status;
5690 union smb_ioctl ioctl;
5691 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5692 struct fsctl_dup_extents_to_file dup_ext_buf;
5693 enum ndr_err_code ndr_ret;
5694 union smb_setfileinfo sinfo;
5695 bool ok;
5697 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5698 &src_h, 4096, /* fill 4096 byte src file */
5699 SEC_RIGHTS_FILE_ALL,
5700 &dest_h, 0, /* 0 byte dest file */
5701 SEC_RIGHTS_FILE_ALL,
5702 &dup_ext_buf,
5703 &ioctl);
5704 if (!ok) {
5705 torture_fail(tctx, "setup dup extents error");
5708 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5709 FILE_SUPPORTS_BLOCK_REFCOUNTING
5710 | FILE_SUPPORTS_SPARSE_FILES, &ok);
5711 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5712 if (!ok) {
5713 smb2_util_close(tree, src_h);
5714 smb2_util_close(tree, dest_h);
5715 talloc_free(tmp_ctx);
5716 torture_skip(tctx,
5717 "block refcounting and sparse files not supported\n");
5720 /* set sparse flag on dest */
5721 status = test_ioctl_sparse_req(tctx, tmp_ctx, tree, dest_h, true);
5722 torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_SPARSE");
5724 /* extend dest */
5725 ZERO_STRUCT(sinfo);
5726 sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
5727 sinfo.end_of_file_info.in.file.handle = dest_h;
5728 sinfo.end_of_file_info.in.size = dup_ext_buf.byte_count;
5729 status = smb2_setinfo_file(tree, &sinfo);
5730 torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
5732 /* copy all src file data */
5733 dup_ext_buf.source_off = 0;
5734 dup_ext_buf.target_off = 0;
5735 dup_ext_buf.byte_count = 4096;
5737 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5738 &dup_ext_buf,
5739 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5740 torture_assert_ndr_success(tctx, ndr_ret,
5741 "ndr_push_fsctl_dup_extents_to_file");
5744 * dest is sparse, but spec says: 2.3.8 FSCTL_DUPLICATE_EXTENTS_TO_FILE
5745 * Reply... STATUS_NOT_SUPPORTED: Target file is sparse, while source
5746 * is a non-sparse file.
5748 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5749 torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
5751 smb2_util_close(tree, src_h);
5752 smb2_util_close(tree, dest_h);
5753 talloc_free(tmp_ctx);
5754 return true;
5757 static bool test_ioctl_dup_extents_sparse_both(struct torture_context *tctx,
5758 struct smb2_tree *tree)
5760 struct smb2_handle src_h;
5761 struct smb2_handle dest_h;
5762 NTSTATUS status;
5763 union smb_ioctl ioctl;
5764 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5765 struct fsctl_dup_extents_to_file dup_ext_buf;
5766 enum ndr_err_code ndr_ret;
5767 union smb_setfileinfo sinfo;
5768 bool ok;
5770 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5771 &src_h, 0, /* fill 4096 byte src file */
5772 SEC_RIGHTS_FILE_ALL,
5773 &dest_h, 0, /* 0 byte dest file */
5774 SEC_RIGHTS_FILE_ALL,
5775 &dup_ext_buf,
5776 &ioctl);
5777 if (!ok) {
5778 torture_fail(tctx, "setup dup extents error");
5781 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5782 FILE_SUPPORTS_BLOCK_REFCOUNTING
5783 | FILE_SUPPORTS_SPARSE_FILES, &ok);
5784 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5785 if (!ok) {
5786 smb2_util_close(tree, src_h);
5787 smb2_util_close(tree, dest_h);
5788 talloc_free(tmp_ctx);
5789 torture_skip(tctx,
5790 "block refcounting and sparse files not supported\n");
5793 /* set sparse flag on src and dest */
5794 status = test_ioctl_sparse_req(tctx, tmp_ctx, tree, src_h, true);
5795 torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_SPARSE");
5796 status = test_ioctl_sparse_req(tctx, tmp_ctx, tree, dest_h, true);
5797 torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_SPARSE");
5799 ok = write_pattern(tctx, tree, tmp_ctx, src_h, 0, 4096, 0);
5800 torture_assert(tctx, ok, "write pattern");
5802 /* extend dest */
5803 ZERO_STRUCT(sinfo);
5804 sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
5805 sinfo.end_of_file_info.in.file.handle = dest_h;
5806 sinfo.end_of_file_info.in.size = 4096;
5807 status = smb2_setinfo_file(tree, &sinfo);
5808 torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
5810 /* copy all src file data */
5811 dup_ext_buf.source_off = 0;
5812 dup_ext_buf.target_off = 0;
5813 dup_ext_buf.byte_count = 4096;
5815 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5816 &dup_ext_buf,
5817 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5818 torture_assert_ndr_success(tctx, ndr_ret,
5819 "ndr_push_fsctl_dup_extents_to_file");
5821 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5822 torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
5824 smb2_util_close(tree, src_h);
5825 smb2_util_close(tree, dest_h);
5827 /* reopen for pattern check */
5828 ok = test_setup_open(tctx, tree, tmp_ctx, FNAME2, &dest_h,
5829 SEC_RIGHTS_FILE_ALL, FILE_ATTRIBUTE_NORMAL);
5830 torture_assert_ntstatus_ok(tctx, status, "dest open ater dup");
5832 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 4096, 0);
5833 if (!ok) {
5834 torture_fail(tctx, "inconsistent file data");
5837 smb2_util_close(tree, dest_h);
5838 talloc_free(tmp_ctx);
5839 return true;
5842 static bool test_ioctl_dup_extents_src_is_dest(struct torture_context *tctx,
5843 struct smb2_tree *tree)
5845 struct smb2_handle src_h;
5846 struct smb2_handle dest_h;
5847 NTSTATUS status;
5848 union smb_ioctl ioctl;
5849 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5850 struct fsctl_dup_extents_to_file dup_ext_buf;
5851 enum ndr_err_code ndr_ret;
5852 union smb_fileinfo io;
5853 bool ok;
5855 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5856 &src_h, 32768, /* fill 32768 byte src file */
5857 SEC_RIGHTS_FILE_ALL,
5858 &dest_h, 0,
5859 SEC_RIGHTS_FILE_ALL,
5860 &dup_ext_buf,
5861 &ioctl);
5862 if (!ok) {
5863 torture_fail(tctx, "setup dup extents error");
5865 /* dest_h not needed for this test */
5866 smb2_util_close(tree, dest_h);
5868 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5869 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
5870 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5871 if (!ok) {
5872 smb2_util_close(tree, src_h);
5873 talloc_free(tmp_ctx);
5874 torture_skip(tctx, "block refcounting not supported\n");
5877 /* src and dest are the same file handle */
5878 ioctl.smb2.in.file.handle = src_h;
5880 /* no overlap between src and tgt */
5881 dup_ext_buf.source_off = 0;
5882 dup_ext_buf.target_off = 16384;
5883 dup_ext_buf.byte_count = 16384;
5885 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5886 &dup_ext_buf,
5887 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5888 torture_assert_ndr_success(tctx, ndr_ret,
5889 "ndr_push_fsctl_dup_extents_to_file");
5891 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5892 torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
5894 /* the file size shouldn't have been changed */
5895 ZERO_STRUCT(io);
5896 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5897 io.generic.in.file.handle = src_h;
5898 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5899 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5900 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5901 32768, "size after IO");
5903 ok = check_pattern(tctx, tree, tmp_ctx, src_h, 0, 16384, 0);
5904 if (!ok) {
5905 torture_fail(tctx, "inconsistent file data");
5907 ok = check_pattern(tctx, tree, tmp_ctx, src_h, 16384, 16384, 0);
5908 if (!ok) {
5909 torture_fail(tctx, "inconsistent file data");
5912 smb2_util_close(tree, src_h);
5913 talloc_free(tmp_ctx);
5914 return true;
5918 * unlike copy-chunk, dup extents doesn't support overlapping ranges between
5919 * source and target. This makes it a *lot* cleaner to implement on the server.
5921 static bool
5922 test_ioctl_dup_extents_src_is_dest_overlap(struct torture_context *tctx,
5923 struct smb2_tree *tree)
5925 struct smb2_handle src_h;
5926 struct smb2_handle dest_h;
5927 NTSTATUS status;
5928 union smb_ioctl ioctl;
5929 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5930 struct fsctl_dup_extents_to_file dup_ext_buf;
5931 enum ndr_err_code ndr_ret;
5932 union smb_fileinfo io;
5933 bool ok;
5935 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5936 &src_h, 32768, /* fill 32768 byte src file */
5937 SEC_RIGHTS_FILE_ALL,
5938 &dest_h, 0,
5939 SEC_RIGHTS_FILE_ALL,
5940 &dup_ext_buf,
5941 &ioctl);
5942 if (!ok) {
5943 torture_fail(tctx, "setup dup extents error");
5945 /* dest_h not needed for this test */
5946 smb2_util_close(tree, dest_h);
5948 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5949 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
5950 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5951 if (!ok) {
5952 smb2_util_close(tree, src_h);
5953 talloc_free(tmp_ctx);
5954 torture_skip(tctx, "block refcounting not supported\n");
5957 /* src and dest are the same file handle */
5958 ioctl.smb2.in.file.handle = src_h;
5960 /* 8K overlap between src and tgt */
5961 dup_ext_buf.source_off = 0;
5962 dup_ext_buf.target_off = 8192;
5963 dup_ext_buf.byte_count = 16384;
5965 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5966 &dup_ext_buf,
5967 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5968 torture_assert_ndr_success(tctx, ndr_ret,
5969 "ndr_push_fsctl_dup_extents_to_file");
5971 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5972 torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_SUPPORTED,
5973 "FSCTL_DUP_EXTENTS_TO_FILE");
5975 /* the file size and data should match beforehand */
5976 ZERO_STRUCT(io);
5977 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5978 io.generic.in.file.handle = src_h;
5979 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5980 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5981 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5982 32768, "size after IO");
5984 ok = check_pattern(tctx, tree, tmp_ctx, src_h, 0, 32768, 0);
5985 if (!ok) {
5986 torture_fail(tctx, "inconsistent file data");
5989 smb2_util_close(tree, src_h);
5990 talloc_free(tmp_ctx);
5991 return true;
5995 * The compression tests won't run against Windows servers yet - ReFS doesn't
5996 * (yet) offer support for compression.
5998 static bool test_ioctl_dup_extents_compressed_src(struct torture_context *tctx,
5999 struct smb2_tree *tree)
6001 struct smb2_handle src_h;
6002 struct smb2_handle dest_h;
6003 NTSTATUS status;
6004 union smb_ioctl ioctl;
6005 TALLOC_CTX *tmp_ctx = talloc_new(tree);
6006 struct fsctl_dup_extents_to_file dup_ext_buf;
6007 enum ndr_err_code ndr_ret;
6008 union smb_setfileinfo sinfo;
6009 bool ok;
6011 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
6012 &src_h, 0, /* filled after compressed flag */
6013 SEC_RIGHTS_FILE_ALL,
6014 &dest_h, 0,
6015 SEC_RIGHTS_FILE_ALL,
6016 &dup_ext_buf,
6017 &ioctl);
6018 if (!ok) {
6019 torture_fail(tctx, "setup dup extents error");
6022 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
6023 FILE_SUPPORTS_BLOCK_REFCOUNTING
6024 | FILE_FILE_COMPRESSION, &ok);
6025 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
6026 if (!ok) {
6027 smb2_util_close(tree, src_h);
6028 smb2_util_close(tree, dest_h);
6029 talloc_free(tmp_ctx);
6030 torture_skip(tctx,
6031 "block refcounting and compressed files not supported\n");
6034 /* set compressed flag on src */
6035 status = test_ioctl_compress_set(tctx, tmp_ctx, tree, src_h,
6036 COMPRESSION_FORMAT_DEFAULT);
6037 torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_COMPRESSION");
6039 ok = write_pattern(tctx, tree, tmp_ctx, src_h, 0, 4096, 0);
6040 torture_assert(tctx, ok, "write pattern");
6042 /* extend dest */
6043 ZERO_STRUCT(sinfo);
6044 sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
6045 sinfo.end_of_file_info.in.file.handle = dest_h;
6046 sinfo.end_of_file_info.in.size = 4096;
6047 status = smb2_setinfo_file(tree, &sinfo);
6048 torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
6050 /* copy all src file data */
6051 dup_ext_buf.source_off = 0;
6052 dup_ext_buf.target_off = 0;
6053 dup_ext_buf.byte_count = 4096;
6055 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6056 &dup_ext_buf,
6057 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6058 torture_assert_ndr_success(tctx, ndr_ret,
6059 "ndr_push_fsctl_dup_extents_to_file");
6061 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6062 torture_assert_ntstatus_ok(tctx, status,
6063 "FSCTL_DUP_EXTENTS_TO_FILE");
6065 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 4096, 0);
6066 if (!ok) {
6067 torture_fail(tctx, "inconsistent file data");
6070 smb2_util_close(tree, src_h);
6071 smb2_util_close(tree, dest_h);
6072 talloc_free(tmp_ctx);
6073 return true;
6076 static bool test_ioctl_dup_extents_compressed_dest(struct torture_context *tctx,
6077 struct smb2_tree *tree)
6079 struct smb2_handle src_h;
6080 struct smb2_handle dest_h;
6081 NTSTATUS status;
6082 union smb_ioctl ioctl;
6083 TALLOC_CTX *tmp_ctx = talloc_new(tree);
6084 struct fsctl_dup_extents_to_file dup_ext_buf;
6085 enum ndr_err_code ndr_ret;
6086 union smb_setfileinfo sinfo;
6087 bool ok;
6089 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
6090 &src_h, 4096,
6091 SEC_RIGHTS_FILE_ALL,
6092 &dest_h, 0,
6093 SEC_RIGHTS_FILE_ALL,
6094 &dup_ext_buf,
6095 &ioctl);
6096 if (!ok) {
6097 torture_fail(tctx, "setup dup extents error");
6100 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
6101 FILE_SUPPORTS_BLOCK_REFCOUNTING
6102 | FILE_FILE_COMPRESSION, &ok);
6103 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
6104 if (!ok) {
6105 smb2_util_close(tree, src_h);
6106 smb2_util_close(tree, dest_h);
6107 talloc_free(tmp_ctx);
6108 torture_skip(tctx,
6109 "block refcounting and compressed files not supported\n");
6112 /* set compressed flag on dest */
6113 status = test_ioctl_compress_set(tctx, tmp_ctx, tree, dest_h,
6114 COMPRESSION_FORMAT_DEFAULT);
6115 torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_COMPRESSION");
6117 /* extend dest */
6118 ZERO_STRUCT(sinfo);
6119 sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
6120 sinfo.end_of_file_info.in.file.handle = dest_h;
6121 sinfo.end_of_file_info.in.size = 4096;
6122 status = smb2_setinfo_file(tree, &sinfo);
6123 torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
6125 /* copy all src file data */
6126 dup_ext_buf.source_off = 0;
6127 dup_ext_buf.target_off = 0;
6128 dup_ext_buf.byte_count = 4096;
6130 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6131 &dup_ext_buf,
6132 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6133 torture_assert_ndr_success(tctx, ndr_ret,
6134 "ndr_push_fsctl_dup_extents_to_file");
6136 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6137 torture_assert_ntstatus_ok(tctx, status,
6138 "FSCTL_DUP_EXTENTS_TO_FILE");
6140 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 4096, 0);
6141 if (!ok) {
6142 torture_fail(tctx, "inconsistent file data");
6145 smb2_util_close(tree, src_h);
6146 smb2_util_close(tree, dest_h);
6147 talloc_free(tmp_ctx);
6148 return true;
6151 static bool test_ioctl_dup_extents_bad_handle(struct torture_context *tctx,
6152 struct smb2_tree *tree)
6154 struct smb2_handle src_h;
6155 struct smb2_handle dest_h;
6156 struct smb2_handle bogus_h;
6157 NTSTATUS status;
6158 union smb_ioctl ioctl;
6159 TALLOC_CTX *tmp_ctx = talloc_new(tree);
6160 struct fsctl_dup_extents_to_file dup_ext_buf;
6161 enum ndr_err_code ndr_ret;
6162 bool ok;
6164 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
6165 &src_h, 32768, /* fill 32768 byte src file */
6166 SEC_RIGHTS_FILE_ALL,
6167 &dest_h, 32768,
6168 SEC_RIGHTS_FILE_ALL,
6169 &dup_ext_buf,
6170 &ioctl);
6171 if (!ok) {
6172 torture_fail(tctx, "setup dup extents error");
6175 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
6176 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
6177 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
6178 if (!ok) {
6179 smb2_util_close(tree, src_h);
6180 smb2_util_close(tree, dest_h);
6181 talloc_free(tmp_ctx);
6182 torture_skip(tctx, "block refcounting not supported\n");
6185 /* open and close a file, keeping the handle as now a "bogus" handle */
6186 ok = test_setup_create_fill(tctx, tree, tmp_ctx, "bogus_file",
6187 &bogus_h, 0, SEC_RIGHTS_FILE_ALL,
6188 FILE_ATTRIBUTE_NORMAL);
6189 torture_assert(tctx, ok, "bogus file create fill");
6190 smb2_util_close(tree, bogus_h);
6192 /* bogus dest file handle */
6193 ioctl.smb2.in.file.handle = bogus_h;
6195 dup_ext_buf.source_off = 0;
6196 dup_ext_buf.target_off = 0;
6197 dup_ext_buf.byte_count = 32768;
6199 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6200 &dup_ext_buf,
6201 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6202 torture_assert_ndr_success(tctx, ndr_ret,
6203 "ndr_push_fsctl_dup_extents_to_file");
6205 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6206 torture_assert_ntstatus_equal(tctx, status, NT_STATUS_FILE_CLOSED,
6207 "FSCTL_DUP_EXTENTS_TO_FILE");
6209 ok = check_pattern(tctx, tree, tmp_ctx, src_h, 0, 32768, 0);
6210 if (!ok) {
6211 torture_fail(tctx, "inconsistent file data");
6213 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
6214 if (!ok) {
6215 torture_fail(tctx, "inconsistent file data");
6218 /* reinstate dest, add bogus src file handle */
6219 ioctl.smb2.in.file.handle = dest_h;
6220 smb2_push_handle(dup_ext_buf.source_fid, &bogus_h);
6222 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6223 &dup_ext_buf,
6224 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6225 torture_assert_ndr_success(tctx, ndr_ret,
6226 "ndr_push_fsctl_dup_extents_to_file");
6228 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6229 torture_assert_ntstatus_equal(tctx, status, NT_STATUS_INVALID_HANDLE,
6230 "FSCTL_DUP_EXTENTS_TO_FILE");
6232 ok = check_pattern(tctx, tree, tmp_ctx, src_h, 0, 32768, 0);
6233 if (!ok) {
6234 torture_fail(tctx, "inconsistent file data");
6236 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
6237 if (!ok) {
6238 torture_fail(tctx, "inconsistent file data");
6241 smb2_util_close(tree, src_h);
6242 smb2_util_close(tree, dest_h);
6243 talloc_free(tmp_ctx);
6244 return true;
6247 static bool test_ioctl_dup_extents_src_lck(struct torture_context *tctx,
6248 struct smb2_tree *tree)
6250 struct smb2_handle src_h;
6251 struct smb2_handle src_h2;
6252 struct smb2_handle dest_h;
6253 NTSTATUS status;
6254 union smb_ioctl ioctl;
6255 TALLOC_CTX *tmp_ctx = talloc_new(tree);
6256 struct fsctl_dup_extents_to_file dup_ext_buf;
6257 enum ndr_err_code ndr_ret;
6258 bool ok;
6259 struct smb2_lock lck;
6260 struct smb2_lock_element el[1];
6262 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
6263 &src_h, 32768, /* fill 32768 byte src file */
6264 SEC_RIGHTS_FILE_ALL,
6265 &dest_h, 0,
6266 SEC_RIGHTS_FILE_ALL,
6267 &dup_ext_buf,
6268 &ioctl);
6269 if (!ok) {
6270 torture_fail(tctx, "setup dup extents error");
6273 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
6274 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
6275 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
6276 if (!ok) {
6277 smb2_util_close(tree, src_h);
6278 smb2_util_close(tree, dest_h);
6279 talloc_free(tmp_ctx);
6280 torture_skip(tctx, "block refcounting not supported\n");
6283 /* dest pattern is different to src */
6284 ok = write_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 32768);
6285 torture_assert(tctx, ok, "write pattern");
6287 /* setup dup ext req, values used for locking */
6288 dup_ext_buf.source_off = 0;
6289 dup_ext_buf.target_off = 0;
6290 dup_ext_buf.byte_count = 32768;
6292 /* open and lock the dup extents src file */
6293 status = torture_smb2_testfile(tree, FNAME, &src_h2);
6294 torture_assert_ntstatus_ok(tctx, status, "2nd src open");
6296 lck.in.lock_count = 0x0001;
6297 lck.in.lock_sequence = 0x00000000;
6298 lck.in.file.handle = src_h2;
6299 lck.in.locks = el;
6300 el[0].offset = dup_ext_buf.source_off;
6301 el[0].length = dup_ext_buf.byte_count;
6302 el[0].reserved = 0;
6303 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
6305 status = smb2_lock(tree, &lck);
6306 torture_assert_ntstatus_ok(tctx, status, "lock");
6308 status = smb2_util_write(tree, src_h,
6309 "conflicted", 0, sizeof("conflicted"));
6310 torture_assert_ntstatus_equal(tctx, status,
6311 NT_STATUS_FILE_LOCK_CONFLICT, "file write");
6313 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6314 &dup_ext_buf,
6315 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6316 torture_assert_ndr_success(tctx, ndr_ret,
6317 "ndr_push_fsctl_dup_extents_to_file");
6320 * In contrast to copy-chunk, dup extents doesn't cause a lock conflict
6321 * here.
6323 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6324 torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
6326 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
6327 if (!ok) {
6328 torture_fail(tctx, "inconsistent file data");
6331 lck.in.lock_count = 0x0001;
6332 lck.in.lock_sequence = 0x00000001;
6333 lck.in.file.handle = src_h2;
6334 lck.in.locks = el;
6335 el[0].offset = dup_ext_buf.source_off;
6336 el[0].length = dup_ext_buf.byte_count;
6337 el[0].reserved = 0;
6338 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
6339 status = smb2_lock(tree, &lck);
6340 torture_assert_ntstatus_ok(tctx, status, "unlock");
6342 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6343 torture_assert_ntstatus_ok(tctx, status,
6344 "FSCTL_DUP_EXTENTS_TO_FILE unlocked");
6346 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
6347 if (!ok) {
6348 torture_fail(tctx, "inconsistent file data");
6351 smb2_util_close(tree, src_h2);
6352 smb2_util_close(tree, src_h);
6353 smb2_util_close(tree, dest_h);
6354 talloc_free(tmp_ctx);
6355 return true;
6358 static bool test_ioctl_dup_extents_dest_lck(struct torture_context *tctx,
6359 struct smb2_tree *tree)
6361 struct smb2_handle src_h;
6362 struct smb2_handle dest_h;
6363 struct smb2_handle dest_h2;
6364 NTSTATUS status;
6365 union smb_ioctl ioctl;
6366 TALLOC_CTX *tmp_ctx = talloc_new(tree);
6367 struct fsctl_dup_extents_to_file dup_ext_buf;
6368 enum ndr_err_code ndr_ret;
6369 bool ok;
6370 struct smb2_lock lck;
6371 struct smb2_lock_element el[1];
6373 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
6374 &src_h, 32768, /* fill 32768 byte src file */
6375 SEC_RIGHTS_FILE_ALL,
6376 &dest_h, 0,
6377 SEC_RIGHTS_FILE_ALL,
6378 &dup_ext_buf,
6379 &ioctl);
6380 if (!ok) {
6381 torture_fail(tctx, "setup dup extents error");
6384 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
6385 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
6386 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
6387 if (!ok) {
6388 smb2_util_close(tree, src_h);
6389 smb2_util_close(tree, dest_h);
6390 talloc_free(tmp_ctx);
6391 torture_skip(tctx, "block refcounting not supported\n");
6394 /* dest pattern is different to src */
6395 ok = write_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 32768);
6396 torture_assert(tctx, ok, "write pattern");
6398 /* setup dup ext req, values used for locking */
6399 dup_ext_buf.source_off = 0;
6400 dup_ext_buf.target_off = 0;
6401 dup_ext_buf.byte_count = 32768;
6403 /* open and lock the dup extents dest file */
6404 status = torture_smb2_testfile(tree, FNAME2, &dest_h2);
6405 torture_assert_ntstatus_ok(tctx, status, "2nd src open");
6407 lck.in.lock_count = 0x0001;
6408 lck.in.lock_sequence = 0x00000000;
6409 lck.in.file.handle = dest_h2;
6410 lck.in.locks = el;
6411 el[0].offset = dup_ext_buf.source_off;
6412 el[0].length = dup_ext_buf.byte_count;
6413 el[0].reserved = 0;
6414 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
6416 status = smb2_lock(tree, &lck);
6417 torture_assert_ntstatus_ok(tctx, status, "lock");
6419 status = smb2_util_write(tree, dest_h,
6420 "conflicted", 0, sizeof("conflicted"));
6421 torture_assert_ntstatus_equal(tctx, status,
6422 NT_STATUS_FILE_LOCK_CONFLICT, "file write");
6424 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6425 &dup_ext_buf,
6426 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6427 torture_assert_ndr_success(tctx, ndr_ret,
6428 "ndr_push_fsctl_dup_extents_to_file");
6431 * In contrast to copy-chunk, dup extents doesn't cause a lock conflict
6432 * here.
6434 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6435 torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
6437 lck.in.lock_count = 0x0001;
6438 lck.in.lock_sequence = 0x00000001;
6439 lck.in.file.handle = dest_h2;
6440 lck.in.locks = el;
6441 el[0].offset = dup_ext_buf.source_off;
6442 el[0].length = dup_ext_buf.byte_count;
6443 el[0].reserved = 0;
6444 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
6445 status = smb2_lock(tree, &lck);
6446 torture_assert_ntstatus_ok(tctx, status, "unlock");
6448 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6449 torture_assert_ntstatus_ok(tctx, status,
6450 "FSCTL_DUP_EXTENTS_TO_FILE unlocked");
6452 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
6453 if (!ok) {
6454 torture_fail(tctx, "inconsistent file data");
6457 smb2_util_close(tree, src_h);
6458 smb2_util_close(tree, dest_h);
6459 smb2_util_close(tree, dest_h2);
6460 talloc_free(tmp_ctx);
6461 return true;
6465 * testing of SMB2 ioctls
6467 struct torture_suite *torture_smb2_ioctl_init(TALLOC_CTX *ctx)
6469 struct torture_suite *suite = torture_suite_create(ctx, "ioctl");
6471 torture_suite_add_1smb2_test(suite, "shadow_copy",
6472 test_ioctl_get_shadow_copy);
6473 torture_suite_add_1smb2_test(suite, "req_resume_key",
6474 test_ioctl_req_resume_key);
6475 torture_suite_add_1smb2_test(suite, "copy_chunk_simple",
6476 test_ioctl_copy_chunk_simple);
6477 torture_suite_add_1smb2_test(suite, "copy_chunk_multi",
6478 test_ioctl_copy_chunk_multi);
6479 torture_suite_add_1smb2_test(suite, "copy_chunk_tiny",
6480 test_ioctl_copy_chunk_tiny);
6481 torture_suite_add_1smb2_test(suite, "copy_chunk_overwrite",
6482 test_ioctl_copy_chunk_over);
6483 torture_suite_add_1smb2_test(suite, "copy_chunk_append",
6484 test_ioctl_copy_chunk_append);
6485 torture_suite_add_1smb2_test(suite, "copy_chunk_limits",
6486 test_ioctl_copy_chunk_limits);
6487 torture_suite_add_1smb2_test(suite, "copy_chunk_src_lock",
6488 test_ioctl_copy_chunk_src_lck);
6489 torture_suite_add_1smb2_test(suite, "copy_chunk_dest_lock",
6490 test_ioctl_copy_chunk_dest_lck);
6491 torture_suite_add_1smb2_test(suite, "copy_chunk_bad_key",
6492 test_ioctl_copy_chunk_bad_key);
6493 torture_suite_add_1smb2_test(suite, "copy_chunk_src_is_dest",
6494 test_ioctl_copy_chunk_src_is_dest);
6495 torture_suite_add_1smb2_test(suite, "copy_chunk_src_is_dest_overlap",
6496 test_ioctl_copy_chunk_src_is_dest_overlap);
6497 torture_suite_add_1smb2_test(suite, "copy_chunk_bad_access",
6498 test_ioctl_copy_chunk_bad_access);
6499 torture_suite_add_1smb2_test(suite, "copy_chunk_write_access",
6500 test_ioctl_copy_chunk_write_access);
6501 torture_suite_add_1smb2_test(suite, "copy_chunk_src_exceed",
6502 test_ioctl_copy_chunk_src_exceed);
6503 torture_suite_add_1smb2_test(suite, "copy_chunk_src_exceed_multi",
6504 test_ioctl_copy_chunk_src_exceed_multi);
6505 torture_suite_add_1smb2_test(suite, "copy_chunk_sparse_dest",
6506 test_ioctl_copy_chunk_sparse_dest);
6507 torture_suite_add_1smb2_test(suite, "copy_chunk_max_output_sz",
6508 test_ioctl_copy_chunk_max_output_sz);
6509 torture_suite_add_1smb2_test(suite, "copy_chunk_zero_length",
6510 test_ioctl_copy_chunk_zero_length);
6511 torture_suite_add_1smb2_test(suite, "copy-chunk streams",
6512 test_copy_chunk_streams);
6513 torture_suite_add_1smb2_test(suite, "copy_chunk_across_shares",
6514 test_copy_chunk_across_shares);
6515 torture_suite_add_1smb2_test(suite, "compress_file_flag",
6516 test_ioctl_compress_file_flag);
6517 torture_suite_add_1smb2_test(suite, "compress_dir_inherit",
6518 test_ioctl_compress_dir_inherit);
6519 torture_suite_add_1smb2_test(suite, "compress_invalid_format",
6520 test_ioctl_compress_invalid_format);
6521 torture_suite_add_1smb2_test(suite, "compress_invalid_buf",
6522 test_ioctl_compress_invalid_buf);
6523 torture_suite_add_1smb2_test(suite, "compress_query_file_attr",
6524 test_ioctl_compress_query_file_attr);
6525 torture_suite_add_1smb2_test(suite, "compress_create_with_attr",
6526 test_ioctl_compress_create_with_attr);
6527 torture_suite_add_1smb2_test(suite, "compress_inherit_disable",
6528 test_ioctl_compress_inherit_disable);
6529 torture_suite_add_1smb2_test(suite, "compress_set_file_attr",
6530 test_ioctl_compress_set_file_attr);
6531 torture_suite_add_1smb2_test(suite, "compress_perms",
6532 test_ioctl_compress_perms);
6533 torture_suite_add_1smb2_test(suite, "compress_notsup_get",
6534 test_ioctl_compress_notsup_get);
6535 torture_suite_add_1smb2_test(suite, "compress_notsup_set",
6536 test_ioctl_compress_notsup_set);
6537 torture_suite_add_1smb2_test(suite, "network_interface_info",
6538 test_ioctl_network_interface_info);
6539 torture_suite_add_1smb2_test(suite, "sparse_file_flag",
6540 test_ioctl_sparse_file_flag);
6541 torture_suite_add_1smb2_test(suite, "sparse_file_attr",
6542 test_ioctl_sparse_file_attr);
6543 torture_suite_add_1smb2_test(suite, "sparse_dir_flag",
6544 test_ioctl_sparse_dir_flag);
6545 torture_suite_add_1smb2_test(suite, "sparse_set_nobuf",
6546 test_ioctl_sparse_set_nobuf);
6547 torture_suite_add_1smb2_test(suite, "sparse_set_oversize",
6548 test_ioctl_sparse_set_oversize);
6549 torture_suite_add_1smb2_test(suite, "sparse_qar",
6550 test_ioctl_sparse_qar);
6551 torture_suite_add_1smb2_test(suite, "sparse_qar_malformed",
6552 test_ioctl_sparse_qar_malformed);
6553 torture_suite_add_1smb2_test(suite, "sparse_punch",
6554 test_ioctl_sparse_punch);
6555 torture_suite_add_1smb2_test(suite, "sparse_hole_dealloc",
6556 test_ioctl_sparse_hole_dealloc);
6557 torture_suite_add_1smb2_test(suite, "sparse_compressed",
6558 test_ioctl_sparse_compressed);
6559 torture_suite_add_1smb2_test(suite, "sparse_copy_chunk",
6560 test_ioctl_sparse_copy_chunk);
6561 torture_suite_add_1smb2_test(suite, "sparse_punch_invalid",
6562 test_ioctl_sparse_punch_invalid);
6563 torture_suite_add_1smb2_test(suite, "sparse_perms",
6564 test_ioctl_sparse_perms);
6565 torture_suite_add_1smb2_test(suite, "sparse_lock",
6566 test_ioctl_sparse_lck);
6567 torture_suite_add_1smb2_test(suite, "sparse_qar_ob1",
6568 test_ioctl_sparse_qar_ob1);
6569 torture_suite_add_1smb2_test(suite, "sparse_qar_multi",
6570 test_ioctl_sparse_qar_multi);
6571 torture_suite_add_1smb2_test(suite, "sparse_qar_overflow",
6572 test_ioctl_sparse_qar_overflow);
6573 torture_suite_add_1smb2_test(suite, "trim_simple",
6574 test_ioctl_trim_simple);
6575 torture_suite_add_1smb2_test(suite, "dup_extents_simple",
6576 test_ioctl_dup_extents_simple);
6577 torture_suite_add_1smb2_test(suite, "dup_extents_len_beyond_dest",
6578 test_ioctl_dup_extents_len_beyond_dest);
6579 torture_suite_add_1smb2_test(suite, "dup_extents_len_beyond_src",
6580 test_ioctl_dup_extents_len_beyond_src);
6581 torture_suite_add_1smb2_test(suite, "dup_extents_len_zero",
6582 test_ioctl_dup_extents_len_zero);
6583 torture_suite_add_1smb2_test(suite, "dup_extents_sparse_src",
6584 test_ioctl_dup_extents_sparse_src);
6585 torture_suite_add_1smb2_test(suite, "dup_extents_sparse_dest",
6586 test_ioctl_dup_extents_sparse_dest);
6587 torture_suite_add_1smb2_test(suite, "dup_extents_sparse_both",
6588 test_ioctl_dup_extents_sparse_both);
6589 torture_suite_add_1smb2_test(suite, "dup_extents_src_is_dest",
6590 test_ioctl_dup_extents_src_is_dest);
6591 torture_suite_add_1smb2_test(suite, "dup_extents_src_is_dest_overlap",
6592 test_ioctl_dup_extents_src_is_dest_overlap);
6593 torture_suite_add_1smb2_test(suite, "dup_extents_compressed_src",
6594 test_ioctl_dup_extents_compressed_src);
6595 torture_suite_add_1smb2_test(suite, "dup_extents_compressed_dest",
6596 test_ioctl_dup_extents_compressed_dest);
6597 torture_suite_add_1smb2_test(suite, "dup_extents_bad_handle",
6598 test_ioctl_dup_extents_bad_handle);
6599 torture_suite_add_1smb2_test(suite, "dup_extents_src_lock",
6600 test_ioctl_dup_extents_src_lck);
6601 torture_suite_add_1smb2_test(suite, "dup_extents_dest_lock",
6602 test_ioctl_dup_extents_dest_lck);
6604 suite->description = talloc_strdup(suite, "SMB2-IOCTL tests");
6606 return suite;