lib: tevent: Use system <tevent.h>, not internal header path (except in self-test).
[Samba.git] / source4 / torture / smb2 / ioctl.c
blob90171e98ac2bb4db98e920be6e1f05a79769ced4
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;
118 testing fetching a resume key twice for one file handle
120 static bool test_ioctl_req_two_resume_keys(struct torture_context *torture,
121 struct smb2_tree *tree)
123 struct smb2_handle h;
124 uint8_t buf[100];
125 NTSTATUS status;
126 union smb_ioctl ioctl;
127 TALLOC_CTX *tmp_ctx = talloc_new(tree);
128 struct req_resume_key_rsp res_key;
129 enum ndr_err_code ndr_ret;
131 smb2_util_unlink(tree, FNAME);
133 status = torture_smb2_testfile(tree, FNAME, &h);
134 torture_assert_ntstatus_ok(torture, status, "create write");
136 ZERO_ARRAY(buf);
137 status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf));
138 torture_assert_ntstatus_ok(torture, status, "write");
140 ZERO_STRUCT(ioctl);
141 ioctl.smb2.level = RAW_IOCTL_SMB2;
142 ioctl.smb2.in.file.handle = h;
143 ioctl.smb2.in.function = FSCTL_SRV_REQUEST_RESUME_KEY;
144 ioctl.smb2.in.max_response_size = 32;
145 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
147 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
148 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_REQUEST_RESUME_KEY");
150 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, &res_key,
151 (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
152 torture_assert_ndr_success(torture, ndr_ret,
153 "ndr_pull_req_resume_key_rsp");
155 ndr_print_debug((ndr_print_fn_t)ndr_print_req_resume_key_rsp, "yo", &res_key);
157 ZERO_STRUCT(ioctl);
158 ioctl.smb2.level = RAW_IOCTL_SMB2;
159 ioctl.smb2.in.file.handle = h;
160 ioctl.smb2.in.function = FSCTL_SRV_REQUEST_RESUME_KEY;
161 ioctl.smb2.in.max_response_size = 32;
162 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
164 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
165 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_REQUEST_RESUME_KEY");
167 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, &res_key,
168 (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
169 torture_assert_ndr_success(torture, ndr_ret,
170 "ndr_pull_req_resume_key_rsp");
172 ndr_print_debug((ndr_print_fn_t)ndr_print_req_resume_key_rsp, "yo", &res_key);
174 talloc_free(tmp_ctx);
175 return true;
178 static uint64_t patt_hash(uint64_t off)
180 return off;
183 static bool write_pattern(struct torture_context *torture,
184 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
185 struct smb2_handle h, uint64_t off, uint64_t len,
186 uint64_t patt_off)
188 NTSTATUS status;
189 uint64_t i;
190 uint8_t *buf;
191 uint64_t io_sz = MIN(1024 * 64, len);
193 if (len == 0) {
194 return true;
197 torture_assert(torture, (len % 8) == 0, "invalid write len");
199 buf = talloc_zero_size(mem_ctx, io_sz);
200 torture_assert(torture, (buf != NULL), "no memory for file data buf");
202 while (len > 0) {
203 for (i = 0; i <= io_sz - 8; i += 8) {
204 SBVAL(buf, i, patt_hash(patt_off));
205 patt_off += 8;
208 status = smb2_util_write(tree, h,
209 buf, off, io_sz);
210 torture_assert_ntstatus_ok(torture, status, "file write");
212 len -= io_sz;
213 off += io_sz;
216 talloc_free(buf);
218 return true;
221 static bool check_pattern(struct torture_context *torture,
222 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
223 struct smb2_handle h, uint64_t off, uint64_t len,
224 uint64_t patt_off)
226 if (len == 0) {
227 return true;
230 torture_assert(torture, (len % 8) == 0, "invalid read len");
232 while (len > 0) {
233 uint64_t i;
234 struct smb2_read r;
235 NTSTATUS status;
236 uint64_t io_sz = MIN(1024 * 64, len);
238 ZERO_STRUCT(r);
239 r.in.file.handle = h;
240 r.in.length = io_sz;
241 r.in.offset = off;
242 status = smb2_read(tree, mem_ctx, &r);
243 torture_assert_ntstatus_ok(torture, status, "read");
245 torture_assert_u64_equal(torture, r.out.data.length, io_sz,
246 "read data len mismatch");
248 for (i = 0; i <= io_sz - 8; i += 8, patt_off += 8) {
249 uint64_t data = BVAL(r.out.data.data, i);
250 torture_assert_u64_equal(torture, data, patt_hash(patt_off),
251 talloc_asprintf(torture, "read data "
252 "pattern bad at %llu\n",
253 (unsigned long long)off + i));
255 talloc_free(r.out.data.data);
256 len -= io_sz;
257 off += io_sz;
260 return true;
263 static bool check_zero(struct torture_context *torture,
264 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
265 struct smb2_handle h, uint64_t off, uint64_t len)
267 uint64_t i;
268 struct smb2_read r;
269 NTSTATUS status;
271 if (len == 0) {
272 return true;
275 ZERO_STRUCT(r);
276 r.in.file.handle = h;
277 r.in.length = len;
278 r.in.offset = off;
279 status = smb2_read(tree, mem_ctx, &r);
280 torture_assert_ntstatus_ok(torture, status, "read");
282 torture_assert_u64_equal(torture, r.out.data.length, len,
283 "read data len mismatch");
285 for (i = 0; i <= len - 8; i += 8) {
286 uint64_t data = BVAL(r.out.data.data, i);
287 torture_assert_u64_equal(torture, data, 0,
288 talloc_asprintf(mem_ctx, "read zero "
289 "bad at %llu\n",
290 (unsigned long long)i));
293 talloc_free(r.out.data.data);
294 return true;
297 static bool test_setup_open(struct torture_context *torture,
298 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
299 const char *fname,
300 struct smb2_handle *fh,
301 uint32_t desired_access,
302 uint32_t file_attributes)
304 struct smb2_create io;
305 NTSTATUS status;
307 ZERO_STRUCT(io);
308 io.in.desired_access = desired_access;
309 io.in.file_attributes = file_attributes;
310 io.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
311 io.in.share_access =
312 NTCREATEX_SHARE_ACCESS_DELETE|
313 NTCREATEX_SHARE_ACCESS_READ|
314 NTCREATEX_SHARE_ACCESS_WRITE;
315 if (file_attributes & FILE_ATTRIBUTE_DIRECTORY) {
316 io.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
318 io.in.fname = fname;
320 status = smb2_create(tree, mem_ctx, &io);
321 torture_assert_ntstatus_ok(torture, status, "file create");
323 *fh = io.out.file.handle;
325 return true;
328 static bool test_setup_create_fill(struct torture_context *torture,
329 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
330 const char *fname,
331 struct smb2_handle *fh,
332 uint64_t size,
333 uint32_t desired_access,
334 uint32_t file_attributes)
336 bool ok;
337 uint32_t initial_access = desired_access;
339 if (size > 0) {
340 initial_access |= SEC_FILE_APPEND_DATA;
343 smb2_util_unlink(tree, fname);
345 ok = test_setup_open(torture, tree, mem_ctx,
346 fname,
348 initial_access,
349 file_attributes);
350 torture_assert(torture, ok, "file create");
352 if (size > 0) {
353 ok = write_pattern(torture, tree, mem_ctx, *fh, 0, size, 0);
354 torture_assert(torture, ok, "write pattern");
357 if (initial_access != desired_access) {
358 smb2_util_close(tree, *fh);
359 ok = test_setup_open(torture, tree, mem_ctx,
360 fname,
362 desired_access,
363 file_attributes);
364 torture_assert(torture, ok, "file open");
367 return true;
370 static bool test_setup_copy_chunk(struct torture_context *torture,
371 struct smb2_tree *src_tree,
372 struct smb2_tree *dst_tree,
373 TALLOC_CTX *mem_ctx,
374 uint32_t nchunks,
375 const char *src_name,
376 struct smb2_handle *src_h,
377 uint64_t src_size,
378 uint32_t src_desired_access,
379 const char *dst_name,
380 struct smb2_handle *dest_h,
381 uint64_t dest_size,
382 uint32_t dest_desired_access,
383 struct srv_copychunk_copy *cc_copy,
384 union smb_ioctl *ioctl)
386 struct req_resume_key_rsp res_key;
387 bool ok;
388 NTSTATUS status;
389 enum ndr_err_code ndr_ret;
391 ok = test_setup_create_fill(torture, src_tree, mem_ctx, src_name,
392 src_h, src_size, src_desired_access,
393 FILE_ATTRIBUTE_NORMAL);
394 torture_assert(torture, ok, "src file create fill");
396 ok = test_setup_create_fill(torture, dst_tree, mem_ctx, dst_name,
397 dest_h, dest_size, dest_desired_access,
398 FILE_ATTRIBUTE_NORMAL);
399 torture_assert(torture, ok, "dest file create fill");
401 ZERO_STRUCTPN(ioctl);
402 ioctl->smb2.level = RAW_IOCTL_SMB2;
403 ioctl->smb2.in.file.handle = *src_h;
404 ioctl->smb2.in.function = FSCTL_SRV_REQUEST_RESUME_KEY;
405 /* Allow for Key + ContextLength + Context */
406 ioctl->smb2.in.max_response_size = 32;
407 ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
409 status = smb2_ioctl(src_tree, mem_ctx, &ioctl->smb2);
410 torture_assert_ntstatus_ok(torture, status,
411 "FSCTL_SRV_REQUEST_RESUME_KEY");
413 ndr_ret = ndr_pull_struct_blob(&ioctl->smb2.out.out, mem_ctx, &res_key,
414 (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
416 torture_assert_ndr_success(torture, ndr_ret,
417 "ndr_pull_req_resume_key_rsp");
419 ZERO_STRUCTPN(ioctl);
420 ioctl->smb2.level = RAW_IOCTL_SMB2;
421 ioctl->smb2.in.file.handle = *dest_h;
422 ioctl->smb2.in.function = FSCTL_SRV_COPYCHUNK;
423 ioctl->smb2.in.max_response_size = sizeof(struct srv_copychunk_rsp);
424 ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
426 ZERO_STRUCTPN(cc_copy);
427 memcpy(cc_copy->source_key, res_key.resume_key, ARRAY_SIZE(cc_copy->source_key));
428 cc_copy->chunk_count = nchunks;
429 cc_copy->chunks = talloc_zero_array(mem_ctx, struct srv_copychunk, nchunks);
430 torture_assert(torture, (cc_copy->chunks != NULL), "no memory for chunks");
432 return true;
436 static bool check_copy_chunk_rsp(struct torture_context *torture,
437 struct srv_copychunk_rsp *cc_rsp,
438 uint32_t ex_chunks_written,
439 uint32_t ex_chunk_bytes_written,
440 uint32_t ex_total_bytes_written)
442 torture_assert_int_equal(torture, cc_rsp->chunks_written,
443 ex_chunks_written, "num chunks");
444 torture_assert_int_equal(torture, cc_rsp->chunk_bytes_written,
445 ex_chunk_bytes_written, "chunk bytes written");
446 torture_assert_int_equal(torture, cc_rsp->total_bytes_written,
447 ex_total_bytes_written, "chunk total bytes");
448 return true;
451 static bool test_ioctl_copy_chunk_simple(struct torture_context *torture,
452 struct smb2_tree *tree)
454 struct smb2_handle src_h;
455 struct smb2_handle dest_h;
456 NTSTATUS status;
457 union smb_ioctl ioctl;
458 TALLOC_CTX *tmp_ctx = talloc_new(tree);
459 struct srv_copychunk_copy cc_copy;
460 struct srv_copychunk_rsp cc_rsp;
461 enum ndr_err_code ndr_ret;
462 bool ok;
464 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
465 1, /* 1 chunk */
466 FNAME,
467 &src_h, 4096, /* fill 4096 byte src file */
468 SEC_RIGHTS_FILE_ALL,
469 FNAME2,
470 &dest_h, 0, /* 0 byte dest file */
471 SEC_RIGHTS_FILE_ALL,
472 &cc_copy,
473 &ioctl);
474 if (!ok) {
475 torture_fail(torture, "setup copy chunk error");
478 /* copy all src file data (via a single chunk desc) */
479 cc_copy.chunks[0].source_off = 0;
480 cc_copy.chunks[0].target_off = 0;
481 cc_copy.chunks[0].length = 4096;
483 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
484 &cc_copy,
485 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
486 torture_assert_ndr_success(torture, ndr_ret,
487 "ndr_push_srv_copychunk_copy");
489 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
490 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
492 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
493 &cc_rsp,
494 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
495 torture_assert_ndr_success(torture, ndr_ret,
496 "ndr_pull_srv_copychunk_rsp");
498 ok = check_copy_chunk_rsp(torture, &cc_rsp,
499 1, /* chunks written */
500 0, /* chunk bytes unsuccessfully written */
501 4096); /* total bytes written */
502 if (!ok) {
503 torture_fail(torture, "bad copy chunk response data");
506 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
507 if (!ok) {
508 torture_fail(torture, "inconsistent file data");
511 smb2_util_close(tree, src_h);
512 smb2_util_close(tree, dest_h);
513 talloc_free(tmp_ctx);
514 return true;
517 static bool test_ioctl_copy_chunk_multi(struct torture_context *torture,
518 struct smb2_tree *tree)
520 struct smb2_handle src_h;
521 struct smb2_handle dest_h;
522 NTSTATUS status;
523 union smb_ioctl ioctl;
524 TALLOC_CTX *tmp_ctx = talloc_new(tree);
525 struct srv_copychunk_copy cc_copy;
526 struct srv_copychunk_rsp cc_rsp;
527 enum ndr_err_code ndr_ret;
528 bool ok;
530 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
531 2, /* chunks */
532 FNAME,
533 &src_h, 8192, /* src file */
534 SEC_RIGHTS_FILE_ALL,
535 FNAME2,
536 &dest_h, 0, /* dest file */
537 SEC_RIGHTS_FILE_ALL,
538 &cc_copy,
539 &ioctl);
540 if (!ok) {
541 torture_fail(torture, "setup copy chunk error");
544 /* copy all src file data via two chunks */
545 cc_copy.chunks[0].source_off = 0;
546 cc_copy.chunks[0].target_off = 0;
547 cc_copy.chunks[0].length = 4096;
549 cc_copy.chunks[1].source_off = 4096;
550 cc_copy.chunks[1].target_off = 4096;
551 cc_copy.chunks[1].length = 4096;
553 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
554 &cc_copy,
555 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
556 torture_assert_ndr_success(torture, ndr_ret,
557 "ndr_push_srv_copychunk_copy");
559 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
560 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
562 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
563 &cc_rsp,
564 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
565 torture_assert_ndr_success(torture, ndr_ret,
566 "ndr_pull_srv_copychunk_rsp");
568 ok = check_copy_chunk_rsp(torture, &cc_rsp,
569 2, /* chunks written */
570 0, /* chunk bytes unsuccessfully written */
571 8192); /* total bytes written */
572 if (!ok) {
573 torture_fail(torture, "bad copy chunk response data");
576 smb2_util_close(tree, src_h);
577 smb2_util_close(tree, dest_h);
578 talloc_free(tmp_ctx);
579 return true;
582 static bool test_ioctl_copy_chunk_tiny(struct torture_context *torture,
583 struct smb2_tree *tree)
585 struct smb2_handle src_h;
586 struct smb2_handle dest_h;
587 NTSTATUS status;
588 union smb_ioctl ioctl;
589 TALLOC_CTX *tmp_ctx = talloc_new(tree);
590 struct srv_copychunk_copy cc_copy;
591 struct srv_copychunk_rsp cc_rsp;
592 enum ndr_err_code ndr_ret;
593 bool ok;
595 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
596 2, /* chunks */
597 FNAME,
598 &src_h, 96, /* src file */
599 SEC_RIGHTS_FILE_ALL,
600 FNAME2,
601 &dest_h, 0, /* dest file */
602 SEC_RIGHTS_FILE_ALL,
603 &cc_copy,
604 &ioctl);
605 if (!ok) {
606 torture_fail(torture, "setup copy chunk error");
609 /* copy all src file data via two chunks, sub block size chunks */
610 cc_copy.chunks[0].source_off = 0;
611 cc_copy.chunks[0].target_off = 0;
612 cc_copy.chunks[0].length = 48;
614 cc_copy.chunks[1].source_off = 48;
615 cc_copy.chunks[1].target_off = 48;
616 cc_copy.chunks[1].length = 48;
618 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
619 &cc_copy,
620 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
621 torture_assert_ndr_success(torture, ndr_ret,
622 "ndr_push_srv_copychunk_copy");
624 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
625 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
627 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
628 &cc_rsp,
629 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
630 torture_assert_ndr_success(torture, ndr_ret,
631 "ndr_pull_srv_copychunk_rsp");
633 ok = check_copy_chunk_rsp(torture, &cc_rsp,
634 2, /* chunks written */
635 0, /* chunk bytes unsuccessfully written */
636 96); /* total bytes written */
637 if (!ok) {
638 torture_fail(torture, "bad copy chunk response data");
641 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 96, 0);
642 if (!ok) {
643 torture_fail(torture, "inconsistent file data");
646 smb2_util_close(tree, src_h);
647 smb2_util_close(tree, dest_h);
648 talloc_free(tmp_ctx);
649 return true;
652 static bool test_ioctl_copy_chunk_over(struct torture_context *torture,
653 struct smb2_tree *tree)
655 struct smb2_handle src_h;
656 struct smb2_handle dest_h;
657 NTSTATUS status;
658 union smb_ioctl ioctl;
659 TALLOC_CTX *tmp_ctx = talloc_new(tree);
660 struct srv_copychunk_copy cc_copy;
661 struct srv_copychunk_rsp cc_rsp;
662 enum ndr_err_code ndr_ret;
663 bool ok;
665 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
666 2, /* chunks */
667 FNAME,
668 &src_h, 8192, /* src file */
669 SEC_RIGHTS_FILE_ALL,
670 FNAME2,
671 &dest_h, 4096, /* dest file */
672 SEC_RIGHTS_FILE_ALL,
673 &cc_copy,
674 &ioctl);
675 if (!ok) {
676 torture_fail(torture, "setup copy chunk error");
679 /* first chunk overwrites existing dest data */
680 cc_copy.chunks[0].source_off = 0;
681 cc_copy.chunks[0].target_off = 0;
682 cc_copy.chunks[0].length = 4096;
684 /* second chunk overwrites the first */
685 cc_copy.chunks[1].source_off = 4096;
686 cc_copy.chunks[1].target_off = 0;
687 cc_copy.chunks[1].length = 4096;
689 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
690 &cc_copy,
691 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
692 torture_assert_ndr_success(torture, ndr_ret,
693 "ndr_push_srv_copychunk_copy");
695 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
696 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
698 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
699 &cc_rsp,
700 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
701 torture_assert_ndr_success(torture, ndr_ret,
702 "ndr_pull_srv_copychunk_rsp");
704 ok = check_copy_chunk_rsp(torture, &cc_rsp,
705 2, /* chunks written */
706 0, /* chunk bytes unsuccessfully written */
707 8192); /* total bytes written */
708 if (!ok) {
709 torture_fail(torture, "bad copy chunk response data");
712 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 4096);
713 if (!ok) {
714 torture_fail(torture, "inconsistent file data");
717 smb2_util_close(tree, src_h);
718 smb2_util_close(tree, dest_h);
719 talloc_free(tmp_ctx);
720 return true;
723 static bool test_ioctl_copy_chunk_append(struct torture_context *torture,
724 struct smb2_tree *tree)
726 struct smb2_handle src_h;
727 struct smb2_handle dest_h;
728 NTSTATUS status;
729 union smb_ioctl ioctl;
730 TALLOC_CTX *tmp_ctx = talloc_new(tree);
731 struct srv_copychunk_copy cc_copy;
732 struct srv_copychunk_rsp cc_rsp;
733 enum ndr_err_code ndr_ret;
734 bool ok;
736 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
737 2, /* chunks */
738 FNAME,
739 &src_h, 4096, /* src file */
740 SEC_RIGHTS_FILE_ALL,
741 FNAME2,
742 &dest_h, 0, /* dest file */
743 SEC_RIGHTS_FILE_ALL,
744 &cc_copy,
745 &ioctl);
746 if (!ok) {
747 torture_fail(torture, "setup copy chunk error");
750 cc_copy.chunks[0].source_off = 0;
751 cc_copy.chunks[0].target_off = 0;
752 cc_copy.chunks[0].length = 4096;
754 /* second chunk appends the same data to the first */
755 cc_copy.chunks[1].source_off = 0;
756 cc_copy.chunks[1].target_off = 4096;
757 cc_copy.chunks[1].length = 4096;
759 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
760 &cc_copy,
761 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
762 torture_assert_ndr_success(torture, ndr_ret,
763 "ndr_push_srv_copychunk_copy");
765 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
766 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
768 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
769 &cc_rsp,
770 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
771 torture_assert_ndr_success(torture, ndr_ret,
772 "ndr_pull_srv_copychunk_rsp");
774 ok = check_copy_chunk_rsp(torture, &cc_rsp,
775 2, /* chunks written */
776 0, /* chunk bytes unsuccessfully written */
777 8192); /* total bytes written */
778 if (!ok) {
779 torture_fail(torture, "bad copy chunk response data");
782 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
783 if (!ok) {
784 torture_fail(torture, "inconsistent file data");
787 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 4096, 4096, 0);
788 if (!ok) {
789 torture_fail(torture, "inconsistent file data");
792 smb2_util_close(tree, src_h);
793 smb2_util_close(tree, dest_h);
794 talloc_free(tmp_ctx);
795 return true;
798 static bool test_ioctl_copy_chunk_limits(struct torture_context *torture,
799 struct smb2_tree *tree)
801 struct smb2_handle src_h;
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;
811 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
812 1, /* chunks */
813 FNAME,
814 &src_h, 4096, /* src file */
815 SEC_RIGHTS_FILE_ALL,
816 FNAME2,
817 &dest_h, 0, /* dest file */
818 SEC_RIGHTS_FILE_ALL,
819 &cc_copy,
820 &ioctl);
821 if (!ok) {
822 torture_fail(torture, "setup copy chunk error");
825 /* send huge chunk length request */
826 cc_copy.chunks[0].source_off = 0;
827 cc_copy.chunks[0].target_off = 0;
828 cc_copy.chunks[0].length = UINT_MAX;
830 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
831 &cc_copy,
832 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
833 torture_assert_ndr_success(torture, ndr_ret, "marshalling request");
835 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
836 torture_assert_ntstatus_equal(torture, status,
837 NT_STATUS_INVALID_PARAMETER,
838 "bad oversize chunk response");
840 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
841 &cc_rsp,
842 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
843 torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
845 torture_comment(torture, "limit max chunks, got %u\n",
846 cc_rsp.chunks_written);
847 torture_comment(torture, "limit max chunk len, got %u\n",
848 cc_rsp.chunk_bytes_written);
849 torture_comment(torture, "limit max total bytes, got %u\n",
850 cc_rsp.total_bytes_written);
852 smb2_util_close(tree, src_h);
853 smb2_util_close(tree, dest_h);
854 talloc_free(tmp_ctx);
855 return true;
858 static bool test_ioctl_copy_chunk_src_lck(struct torture_context *torture,
859 struct smb2_tree *tree)
861 struct smb2_handle src_h;
862 struct smb2_handle src_h2;
863 struct smb2_handle dest_h;
864 NTSTATUS status;
865 union smb_ioctl ioctl;
866 TALLOC_CTX *tmp_ctx = talloc_new(tree);
867 struct srv_copychunk_copy cc_copy;
868 struct srv_copychunk_rsp cc_rsp;
869 enum ndr_err_code ndr_ret;
870 bool ok;
871 struct smb2_lock lck;
872 struct smb2_lock_element el[1];
874 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
875 1, /* chunks */
876 FNAME,
877 &src_h, 4096, /* src file */
878 SEC_RIGHTS_FILE_ALL,
879 FNAME2,
880 &dest_h, 0, /* dest file */
881 SEC_RIGHTS_FILE_ALL,
882 &cc_copy,
883 &ioctl);
884 if (!ok) {
885 torture_fail(torture, "setup copy chunk error");
888 cc_copy.chunks[0].source_off = 0;
889 cc_copy.chunks[0].target_off = 0;
890 cc_copy.chunks[0].length = 4096;
892 /* open and lock the copychunk src file */
893 status = torture_smb2_testfile(tree, FNAME, &src_h2);
894 torture_assert_ntstatus_ok(torture, status, "2nd src open");
896 lck.in.lock_count = 0x0001;
897 lck.in.lock_sequence = 0x00000000;
898 lck.in.file.handle = src_h2;
899 lck.in.locks = el;
900 el[0].offset = cc_copy.chunks[0].source_off;
901 el[0].length = cc_copy.chunks[0].length;
902 el[0].reserved = 0;
903 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
905 status = smb2_lock(tree, &lck);
906 torture_assert_ntstatus_ok(torture, status, "lock");
908 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
909 &cc_copy,
910 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
911 torture_assert_ndr_success(torture, ndr_ret,
912 "ndr_push_srv_copychunk_copy");
914 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
916 * 2k12 & Samba return lock_conflict, Windows 7 & 2k8 return success...
918 * Edgar Olougouna @ MS wrote:
919 * Regarding the FSCTL_SRV_COPYCHUNK and STATUS_FILE_LOCK_CONFLICT
920 * discrepancy observed between Windows versions, we confirm that the
921 * behavior change is expected.
923 * CopyChunk in Windows Server 2012 use regular Readfile/Writefile APIs
924 * to move the chunks from the source to the destination.
925 * These ReadFile/WriteFile APIs go through the byte-range lock checks,
926 * and this explains the observed STATUS_FILE_LOCK_CONFLICT error.
928 * Prior to Windows Server 2012, CopyChunk used mapped sections to move
929 * the data. And byte range locks are not enforced on mapped I/O, and
930 * this explains the STATUS_SUCCESS observed on Windows Server 2008 R2.
932 torture_assert_ntstatus_equal(torture, status,
933 NT_STATUS_FILE_LOCK_CONFLICT,
934 "FSCTL_SRV_COPYCHUNK locked");
936 /* should get cc response data with the lock conflict status */
937 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
938 &cc_rsp,
939 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
940 torture_assert_ndr_success(torture, ndr_ret,
941 "ndr_pull_srv_copychunk_rsp");
942 ok = check_copy_chunk_rsp(torture, &cc_rsp,
943 0, /* chunks written */
944 0, /* chunk bytes unsuccessfully written */
945 0); /* total bytes written */
947 lck.in.lock_count = 0x0001;
948 lck.in.lock_sequence = 0x00000001;
949 lck.in.file.handle = src_h2;
950 lck.in.locks = el;
951 el[0].offset = cc_copy.chunks[0].source_off;
952 el[0].length = cc_copy.chunks[0].length;
953 el[0].reserved = 0;
954 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
955 status = smb2_lock(tree, &lck);
956 torture_assert_ntstatus_ok(torture, status, "unlock");
958 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
959 torture_assert_ntstatus_ok(torture, status,
960 "FSCTL_SRV_COPYCHUNK unlocked");
962 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
963 &cc_rsp,
964 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
965 torture_assert_ndr_success(torture, ndr_ret,
966 "ndr_pull_srv_copychunk_rsp");
968 ok = check_copy_chunk_rsp(torture, &cc_rsp,
969 1, /* chunks written */
970 0, /* chunk bytes unsuccessfully written */
971 4096); /* total bytes written */
972 if (!ok) {
973 torture_fail(torture, "bad copy chunk response data");
976 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
977 if (!ok) {
978 torture_fail(torture, "inconsistent file data");
981 smb2_util_close(tree, src_h2);
982 smb2_util_close(tree, src_h);
983 smb2_util_close(tree, dest_h);
984 talloc_free(tmp_ctx);
985 return true;
988 static bool test_ioctl_copy_chunk_dest_lck(struct torture_context *torture,
989 struct smb2_tree *tree)
991 struct smb2_handle src_h;
992 struct smb2_handle dest_h;
993 struct smb2_handle dest_h2;
994 NTSTATUS status;
995 union smb_ioctl ioctl;
996 TALLOC_CTX *tmp_ctx = talloc_new(tree);
997 struct srv_copychunk_copy cc_copy;
998 struct srv_copychunk_rsp cc_rsp;
999 enum ndr_err_code ndr_ret;
1000 bool ok;
1001 struct smb2_lock lck;
1002 struct smb2_lock_element el[1];
1004 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
1005 1, /* chunks */
1006 FNAME,
1007 &src_h, 4096, /* src file */
1008 SEC_RIGHTS_FILE_ALL,
1009 FNAME2,
1010 &dest_h, 4096, /* dest file */
1011 SEC_RIGHTS_FILE_ALL,
1012 &cc_copy,
1013 &ioctl);
1014 if (!ok) {
1015 torture_fail(torture, "setup copy chunk error");
1018 cc_copy.chunks[0].source_off = 0;
1019 cc_copy.chunks[0].target_off = 0;
1020 cc_copy.chunks[0].length = 4096;
1022 /* open and lock the copychunk dest file */
1023 status = torture_smb2_testfile(tree, FNAME2, &dest_h2);
1024 torture_assert_ntstatus_ok(torture, status, "2nd src open");
1026 lck.in.lock_count = 0x0001;
1027 lck.in.lock_sequence = 0x00000000;
1028 lck.in.file.handle = dest_h2;
1029 lck.in.locks = el;
1030 el[0].offset = cc_copy.chunks[0].target_off;
1031 el[0].length = cc_copy.chunks[0].length;
1032 el[0].reserved = 0;
1033 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
1035 status = smb2_lock(tree, &lck);
1036 torture_assert_ntstatus_ok(torture, status, "lock");
1038 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1039 &cc_copy,
1040 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1041 torture_assert_ndr_success(torture, ndr_ret,
1042 "ndr_push_srv_copychunk_copy");
1044 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1045 torture_assert_ntstatus_equal(torture, status,
1046 NT_STATUS_FILE_LOCK_CONFLICT,
1047 "FSCTL_SRV_COPYCHUNK locked");
1049 lck.in.lock_count = 0x0001;
1050 lck.in.lock_sequence = 0x00000001;
1051 lck.in.file.handle = dest_h2;
1052 lck.in.locks = el;
1053 el[0].offset = cc_copy.chunks[0].target_off;
1054 el[0].length = cc_copy.chunks[0].length;
1055 el[0].reserved = 0;
1056 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
1057 status = smb2_lock(tree, &lck);
1058 torture_assert_ntstatus_ok(torture, status, "unlock");
1060 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1061 torture_assert_ntstatus_ok(torture, status,
1062 "FSCTL_SRV_COPYCHUNK unlocked");
1064 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1065 &cc_rsp,
1066 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1067 torture_assert_ndr_success(torture, ndr_ret,
1068 "ndr_pull_srv_copychunk_rsp");
1070 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1071 1, /* chunks written */
1072 0, /* chunk bytes unsuccessfully written */
1073 4096); /* total bytes written */
1074 if (!ok) {
1075 torture_fail(torture, "bad copy chunk response data");
1078 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
1079 if (!ok) {
1080 torture_fail(torture, "inconsistent file data");
1083 smb2_util_close(tree, dest_h2);
1084 smb2_util_close(tree, src_h);
1085 smb2_util_close(tree, dest_h);
1086 talloc_free(tmp_ctx);
1087 return true;
1090 static bool test_ioctl_copy_chunk_bad_key(struct torture_context *torture,
1091 struct smb2_tree *tree)
1093 struct smb2_handle src_h;
1094 struct smb2_handle dest_h;
1095 NTSTATUS status;
1096 union smb_ioctl ioctl;
1097 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1098 struct srv_copychunk_copy cc_copy;
1099 enum ndr_err_code ndr_ret;
1100 bool ok;
1102 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
1104 FNAME,
1105 &src_h, 4096,
1106 SEC_RIGHTS_FILE_ALL,
1107 FNAME2,
1108 &dest_h, 0,
1109 SEC_RIGHTS_FILE_ALL,
1110 &cc_copy,
1111 &ioctl);
1112 if (!ok) {
1113 torture_fail(torture, "setup copy chunk error");
1116 /* overwrite the resume key with a bogus value */
1117 memcpy(cc_copy.source_key, "deadbeefdeadbeefdeadbeef", 24);
1119 cc_copy.chunks[0].source_off = 0;
1120 cc_copy.chunks[0].target_off = 0;
1121 cc_copy.chunks[0].length = 4096;
1123 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1124 &cc_copy,
1125 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1126 torture_assert_ndr_success(torture, ndr_ret,
1127 "ndr_push_srv_copychunk_copy");
1129 /* Server 2k12 returns NT_STATUS_OBJECT_NAME_NOT_FOUND */
1130 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1131 torture_assert_ntstatus_equal(torture, status,
1132 NT_STATUS_OBJECT_NAME_NOT_FOUND,
1133 "FSCTL_SRV_COPYCHUNK");
1135 smb2_util_close(tree, src_h);
1136 smb2_util_close(tree, dest_h);
1137 talloc_free(tmp_ctx);
1138 return true;
1141 static bool test_ioctl_copy_chunk_src_is_dest(struct torture_context *torture,
1142 struct smb2_tree *tree)
1144 struct smb2_handle src_h;
1145 struct smb2_handle dest_h;
1146 NTSTATUS status;
1147 union smb_ioctl ioctl;
1148 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1149 struct srv_copychunk_copy cc_copy;
1150 struct srv_copychunk_rsp cc_rsp;
1151 enum ndr_err_code ndr_ret;
1152 bool ok;
1154 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
1156 FNAME,
1157 &src_h, 8192,
1158 SEC_RIGHTS_FILE_ALL,
1159 FNAME2,
1160 &dest_h, 0,
1161 SEC_RIGHTS_FILE_ALL,
1162 &cc_copy,
1163 &ioctl);
1164 if (!ok) {
1165 torture_fail(torture, "setup copy chunk error");
1168 /* the source is also the destination */
1169 ioctl.smb2.in.file.handle = src_h;
1171 /* non-overlapping */
1172 cc_copy.chunks[0].source_off = 0;
1173 cc_copy.chunks[0].target_off = 4096;
1174 cc_copy.chunks[0].length = 4096;
1176 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1177 &cc_copy,
1178 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1179 torture_assert_ndr_success(torture, ndr_ret,
1180 "ndr_push_srv_copychunk_copy");
1182 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1183 torture_assert_ntstatus_ok(torture, status,
1184 "FSCTL_SRV_COPYCHUNK");
1186 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1187 &cc_rsp,
1188 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1189 torture_assert_ndr_success(torture, ndr_ret,
1190 "ndr_pull_srv_copychunk_rsp");
1192 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1193 1, /* chunks written */
1194 0, /* chunk bytes unsuccessfully written */
1195 4096); /* total bytes written */
1196 if (!ok) {
1197 torture_fail(torture, "bad copy chunk response data");
1200 ok = check_pattern(torture, tree, tmp_ctx, src_h, 0, 4096, 0);
1201 if (!ok) {
1202 torture_fail(torture, "inconsistent file data");
1204 ok = check_pattern(torture, tree, tmp_ctx, src_h, 4096, 4096, 0);
1205 if (!ok) {
1206 torture_fail(torture, "inconsistent file data");
1209 smb2_util_close(tree, src_h);
1210 smb2_util_close(tree, dest_h);
1211 talloc_free(tmp_ctx);
1212 return true;
1216 * Test a single-chunk copychunk request, where the source and target ranges
1217 * overlap, and the SourceKey refers to the same target file. E.g:
1219 * Initial State
1220 * -------------
1221 * File: src_and_dest
1222 * Offset: 0123456789
1223 * Data: abcdefghij
1225 * Request
1226 * -------
1227 * FSCTL_SRV_COPYCHUNK(src_and_dest)
1228 * SourceKey = SRV_REQUEST_RESUME_KEY(src_and_dest)
1229 * ChunkCount = 1
1230 * Chunks[0].SourceOffset = 0
1231 * Chunks[0].TargetOffset = 4
1232 * Chunks[0].Length = 6
1234 * Resultant State
1235 * ---------------
1236 * File: src_and_dest
1237 * Offset: 0123456789
1238 * Data: abcdabcdef
1240 * The resultant contents of src_and_dest is dependent on the server's
1241 * copy algorithm. In the above example, the server uses an IO buffer
1242 * large enough to hold the entire six-byte source data before writing
1243 * to TargetOffset. If the server were to use a four-byte IO buffer and
1244 * started reads/writes from the lowest offset, then the two overlapping
1245 * bytes in the above example would be overwritten before being read. The
1246 * resultant file contents would be abcdabcdab.
1248 * Windows 2008r2 appears to use a 2048 byte copy buffer, overlapping bytes
1249 * after this offset are written before being read. Windows 2012 on the
1250 * other hand appears to use a buffer large enough to hold its maximum
1251 * supported chunk size (1M). Samba currently uses a 64k copy buffer by
1252 * default (vfs_cc_state.buf).
1254 * This test uses an 8-byte overlap at 2040-2048, so that it passes against
1255 * Windows 2008r2, 2012 and Samba servers. Note, 2008GM fails, as it appears
1256 * to use a different copy algorithm to 2008r2.
1258 static bool
1259 test_ioctl_copy_chunk_src_is_dest_overlap(struct torture_context *torture,
1260 struct smb2_tree *tree)
1262 struct smb2_handle src_h;
1263 struct smb2_handle dest_h;
1264 NTSTATUS status;
1265 union smb_ioctl ioctl;
1266 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1267 struct srv_copychunk_copy cc_copy;
1268 struct srv_copychunk_rsp cc_rsp;
1269 enum ndr_err_code ndr_ret;
1270 bool ok;
1272 /* exceed the vfs_default copy buffer */
1273 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
1275 FNAME,
1276 &src_h, 2048 * 2,
1277 SEC_RIGHTS_FILE_ALL,
1278 FNAME2,
1279 &dest_h, 0,
1280 SEC_RIGHTS_FILE_ALL,
1281 &cc_copy,
1282 &ioctl);
1283 if (!ok) {
1284 torture_fail(torture, "setup copy chunk error");
1287 /* the source is also the destination */
1288 ioctl.smb2.in.file.handle = src_h;
1290 /* 8 bytes overlap between source and target ranges */
1291 cc_copy.chunks[0].source_off = 0;
1292 cc_copy.chunks[0].target_off = 2048 - 8;
1293 cc_copy.chunks[0].length = 2048;
1295 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1296 &cc_copy,
1297 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1298 torture_assert_ndr_success(torture, ndr_ret,
1299 "ndr_push_srv_copychunk_copy");
1301 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1302 torture_assert_ntstatus_ok(torture, status,
1303 "FSCTL_SRV_COPYCHUNK");
1305 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1306 &cc_rsp,
1307 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1308 torture_assert_ndr_success(torture, ndr_ret,
1309 "ndr_pull_srv_copychunk_rsp");
1311 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1312 1, /* chunks written */
1313 0, /* chunk bytes unsuccessfully written */
1314 2048); /* total bytes written */
1315 if (!ok) {
1316 torture_fail(torture, "bad copy chunk response data");
1319 ok = check_pattern(torture, tree, tmp_ctx, src_h, 0, 2048 - 8, 0);
1320 if (!ok) {
1321 torture_fail(torture, "inconsistent file data");
1323 ok = check_pattern(torture, tree, tmp_ctx, src_h, 2048 - 8, 2048, 0);
1324 if (!ok) {
1325 torture_fail(torture, "inconsistent file data");
1328 smb2_util_close(tree, src_h);
1329 smb2_util_close(tree, dest_h);
1330 talloc_free(tmp_ctx);
1331 return true;
1334 static bool test_ioctl_copy_chunk_bad_access(struct torture_context *torture,
1335 struct smb2_tree *tree)
1337 struct smb2_handle src_h;
1338 struct smb2_handle dest_h;
1339 NTSTATUS status;
1340 union smb_ioctl ioctl;
1341 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1342 struct srv_copychunk_copy cc_copy;
1343 enum ndr_err_code ndr_ret;
1344 bool ok;
1345 /* read permission on src */
1346 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx, 1, /* 1 chunk */
1347 FNAME, &src_h, 4096, /* fill 4096 byte src file */
1348 SEC_FILE_READ_DATA | SEC_FILE_READ_ATTRIBUTE,
1349 FNAME2, &dest_h, 0, /* 0 byte dest file */
1350 SEC_RIGHTS_FILE_ALL, &cc_copy, &ioctl);
1351 if (!ok) {
1352 torture_fail(torture, "setup copy chunk error");
1355 cc_copy.chunks[0].source_off = 0;
1356 cc_copy.chunks[0].target_off = 0;
1357 cc_copy.chunks[0].length = 4096;
1359 ndr_ret = ndr_push_struct_blob(
1360 &ioctl.smb2.in.out, tmp_ctx, &cc_copy,
1361 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1362 torture_assert_ndr_success(torture, ndr_ret,
1363 "ndr_push_srv_copychunk_copy");
1365 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1366 torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK,
1367 "FSCTL_SRV_COPYCHUNK");
1369 smb2_util_close(tree, src_h);
1370 smb2_util_close(tree, dest_h);
1372 /* execute permission on src */
1373 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx, 1, /* 1 chunk */
1374 FNAME, &src_h, 4096, /* fill 4096 byte src file */
1375 SEC_FILE_EXECUTE | SEC_FILE_READ_ATTRIBUTE,
1376 FNAME2, &dest_h, 0, /* 0 byte dest file */
1377 SEC_RIGHTS_FILE_ALL, &cc_copy, &ioctl);
1378 if (!ok) {
1379 torture_fail(torture, "setup copy chunk error");
1382 cc_copy.chunks[0].source_off = 0;
1383 cc_copy.chunks[0].target_off = 0;
1384 cc_copy.chunks[0].length = 4096;
1386 ndr_ret = ndr_push_struct_blob(
1387 &ioctl.smb2.in.out, tmp_ctx, &cc_copy,
1388 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1389 torture_assert_ndr_success(torture, ndr_ret,
1390 "ndr_push_srv_copychunk_copy");
1392 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1393 torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK,
1394 "FSCTL_SRV_COPYCHUNK");
1396 smb2_util_close(tree, src_h);
1397 smb2_util_close(tree, dest_h);
1399 /* neither read nor execute permission on src */
1400 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx, 1, /* 1 chunk */
1401 FNAME, &src_h, 4096, /* fill 4096 byte src file */
1402 SEC_FILE_READ_ATTRIBUTE, FNAME2, &dest_h,
1403 0, /* 0 byte dest file */
1404 SEC_RIGHTS_FILE_ALL, &cc_copy, &ioctl);
1405 if (!ok) {
1406 torture_fail(torture, "setup copy chunk error");
1409 cc_copy.chunks[0].source_off = 0;
1410 cc_copy.chunks[0].target_off = 0;
1411 cc_copy.chunks[0].length = 4096;
1413 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1414 &cc_copy,
1415 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1416 torture_assert_ndr_success(torture, ndr_ret,
1417 "ndr_push_srv_copychunk_copy");
1419 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1420 torture_assert_ntstatus_equal(torture, status,
1421 NT_STATUS_ACCESS_DENIED,
1422 "FSCTL_SRV_COPYCHUNK");
1424 smb2_util_close(tree, src_h);
1425 smb2_util_close(tree, dest_h);
1427 /* no write permission on dest */
1428 ok = test_setup_copy_chunk(
1429 torture, tree, tree, tmp_ctx, 1, /* 1 chunk */
1430 FNAME, &src_h, 4096, /* fill 4096 byte src file */
1431 SEC_FILE_READ_DATA | SEC_FILE_READ_ATTRIBUTE, FNAME2, &dest_h,
1432 0, /* 0 byte dest file */
1433 (SEC_RIGHTS_FILE_ALL &
1434 ~(SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)),
1435 &cc_copy, &ioctl);
1436 if (!ok) {
1437 torture_fail(torture, "setup copy chunk error");
1440 cc_copy.chunks[0].source_off = 0;
1441 cc_copy.chunks[0].target_off = 0;
1442 cc_copy.chunks[0].length = 4096;
1444 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1445 &cc_copy,
1446 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1447 torture_assert_ndr_success(torture, ndr_ret,
1448 "ndr_push_srv_copychunk_copy");
1450 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1451 torture_assert_ntstatus_equal(torture, status,
1452 NT_STATUS_ACCESS_DENIED,
1453 "FSCTL_SRV_COPYCHUNK");
1455 smb2_util_close(tree, src_h);
1456 smb2_util_close(tree, dest_h);
1458 /* no read permission on dest */
1459 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx, 1, /* 1 chunk */
1460 FNAME, &src_h, 4096, /* fill 4096 byte src file */
1461 SEC_FILE_READ_DATA | SEC_FILE_READ_ATTRIBUTE,
1462 FNAME2, &dest_h, 0, /* 0 byte dest file */
1463 (SEC_RIGHTS_FILE_ALL & ~SEC_FILE_READ_DATA),
1464 &cc_copy, &ioctl);
1465 if (!ok) {
1466 torture_fail(torture, "setup copy chunk error");
1469 cc_copy.chunks[0].source_off = 0;
1470 cc_copy.chunks[0].target_off = 0;
1471 cc_copy.chunks[0].length = 4096;
1473 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1474 &cc_copy,
1475 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1476 torture_assert_ndr_success(torture, ndr_ret,
1477 "ndr_push_srv_copychunk_copy");
1480 * FSCTL_SRV_COPYCHUNK requires read permission on dest,
1481 * FSCTL_SRV_COPYCHUNK_WRITE on the other hand does not.
1483 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1484 torture_assert_ntstatus_equal(torture, status,
1485 NT_STATUS_ACCESS_DENIED,
1486 "FSCTL_SRV_COPYCHUNK");
1488 smb2_util_close(tree, src_h);
1489 smb2_util_close(tree, dest_h);
1490 talloc_free(tmp_ctx);
1492 return true;
1495 static bool test_ioctl_copy_chunk_write_access(struct torture_context *torture,
1496 struct smb2_tree *tree)
1498 struct smb2_handle src_h;
1499 struct smb2_handle dest_h;
1500 NTSTATUS status;
1501 union smb_ioctl ioctl;
1502 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1503 struct srv_copychunk_copy cc_copy;
1504 enum ndr_err_code ndr_ret;
1505 bool ok;
1507 /* no read permission on dest with FSCTL_SRV_COPYCHUNK_WRITE */
1508 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
1509 1, /* 1 chunk */
1510 FNAME,
1511 &src_h, 4096, /* fill 4096 byte src file */
1512 SEC_RIGHTS_FILE_ALL,
1513 FNAME2,
1514 &dest_h, 0, /* 0 byte dest file */
1515 (SEC_RIGHTS_FILE_WRITE
1516 | SEC_RIGHTS_FILE_EXECUTE),
1517 &cc_copy,
1518 &ioctl);
1519 if (!ok) {
1520 torture_fail(torture, "setup copy chunk error");
1523 ioctl.smb2.in.function = FSCTL_SRV_COPYCHUNK_WRITE;
1524 cc_copy.chunks[0].source_off = 0;
1525 cc_copy.chunks[0].target_off = 0;
1526 cc_copy.chunks[0].length = 4096;
1528 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1529 &cc_copy,
1530 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1531 torture_assert_ndr_success(torture, ndr_ret,
1532 "ndr_push_srv_copychunk_copy");
1534 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1535 torture_assert_ntstatus_ok(torture, status,
1536 "FSCTL_SRV_COPYCHUNK_WRITE");
1538 smb2_util_close(tree, src_h);
1539 smb2_util_close(tree, dest_h);
1540 talloc_free(tmp_ctx);
1542 return true;
1545 static bool test_ioctl_copy_chunk_src_exceed(struct torture_context *torture,
1546 struct smb2_tree *tree)
1548 struct smb2_handle src_h;
1549 struct smb2_handle dest_h;
1550 NTSTATUS status;
1551 union smb_ioctl ioctl;
1552 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1553 struct srv_copychunk_copy cc_copy;
1554 struct srv_copychunk_rsp cc_rsp;
1555 enum ndr_err_code ndr_ret;
1556 bool ok;
1558 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
1559 1, /* 1 chunk */
1560 FNAME,
1561 &src_h, 4096, /* fill 4096 byte src file */
1562 SEC_RIGHTS_FILE_ALL,
1563 FNAME2,
1564 &dest_h, 0, /* 0 byte dest file */
1565 SEC_RIGHTS_FILE_ALL,
1566 &cc_copy,
1567 &ioctl);
1568 if (!ok) {
1569 torture_fail(torture, "setup copy chunk error");
1572 /* Request copy where off + length exceeds size of src */
1573 cc_copy.chunks[0].source_off = 1024;
1574 cc_copy.chunks[0].target_off = 0;
1575 cc_copy.chunks[0].length = 4096;
1577 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1578 &cc_copy,
1579 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1580 torture_assert_ndr_success(torture, ndr_ret,
1581 "ndr_push_srv_copychunk_copy");
1583 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1584 torture_assert_ntstatus_equal(torture, status,
1585 NT_STATUS_INVALID_VIEW_SIZE,
1586 "FSCTL_SRV_COPYCHUNK oversize");
1588 /* Request copy where length exceeds size of src */
1589 cc_copy.chunks[0].source_off = 1024;
1590 cc_copy.chunks[0].target_off = 0;
1591 cc_copy.chunks[0].length = 3072;
1593 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1594 &cc_copy,
1595 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1596 torture_assert_ndr_success(torture, ndr_ret,
1597 "ndr_push_srv_copychunk_copy");
1599 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1600 torture_assert_ntstatus_ok(torture, status,
1601 "FSCTL_SRV_COPYCHUNK just right");
1603 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1604 &cc_rsp,
1605 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1606 torture_assert_ndr_success(torture, ndr_ret,
1607 "ndr_pull_srv_copychunk_rsp");
1609 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1610 1, /* chunks written */
1611 0, /* chunk bytes unsuccessfully written */
1612 3072); /* total bytes written */
1613 if (!ok) {
1614 torture_fail(torture, "bad copy chunk response data");
1617 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 3072, 1024);
1618 if (!ok) {
1619 torture_fail(torture, "inconsistent file data");
1622 smb2_util_close(tree, src_h);
1623 smb2_util_close(tree, dest_h);
1624 talloc_free(tmp_ctx);
1625 return true;
1628 static bool
1629 test_ioctl_copy_chunk_src_exceed_multi(struct torture_context *torture,
1630 struct smb2_tree *tree)
1632 struct smb2_handle src_h;
1633 struct smb2_handle dest_h;
1634 NTSTATUS status;
1635 union smb_ioctl ioctl;
1636 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1637 struct srv_copychunk_copy cc_copy;
1638 struct srv_copychunk_rsp cc_rsp;
1639 enum ndr_err_code ndr_ret;
1640 bool ok;
1642 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
1643 2, /* 2 chunks */
1644 FNAME,
1645 &src_h, 8192, /* fill 8192 byte src file */
1646 SEC_RIGHTS_FILE_ALL,
1647 FNAME2,
1648 &dest_h, 0, /* 0 byte dest file */
1649 SEC_RIGHTS_FILE_ALL,
1650 &cc_copy,
1651 &ioctl);
1652 if (!ok) {
1653 torture_fail(torture, "setup copy chunk error");
1656 /* Request copy where off + length exceeds size of src */
1657 cc_copy.chunks[0].source_off = 0;
1658 cc_copy.chunks[0].target_off = 0;
1659 cc_copy.chunks[0].length = 4096;
1661 cc_copy.chunks[1].source_off = 4096;
1662 cc_copy.chunks[1].target_off = 4096;
1663 cc_copy.chunks[1].length = 8192;
1665 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1666 &cc_copy,
1667 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1668 torture_assert_ndr_success(torture, ndr_ret,
1669 "ndr_push_srv_copychunk_copy");
1671 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1672 torture_assert_ntstatus_equal(torture, status,
1673 NT_STATUS_INVALID_VIEW_SIZE,
1674 "FSCTL_SRV_COPYCHUNK oversize");
1675 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1676 &cc_rsp,
1677 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1678 torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
1680 /* first chunk should still be written */
1681 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1682 1, /* chunks written */
1683 0, /* chunk bytes unsuccessfully written */
1684 4096); /* total bytes written */
1685 if (!ok) {
1686 torture_fail(torture, "bad copy chunk response data");
1688 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
1689 if (!ok) {
1690 torture_fail(torture, "inconsistent file data");
1693 smb2_util_close(tree, src_h);
1694 smb2_util_close(tree, dest_h);
1695 talloc_free(tmp_ctx);
1696 return true;
1699 static bool test_ioctl_copy_chunk_sparse_dest(struct torture_context *torture,
1700 struct smb2_tree *tree)
1702 struct smb2_handle src_h;
1703 struct smb2_handle dest_h;
1704 NTSTATUS status;
1705 union smb_ioctl ioctl;
1706 struct smb2_read r;
1707 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1708 struct srv_copychunk_copy cc_copy;
1709 struct srv_copychunk_rsp cc_rsp;
1710 enum ndr_err_code ndr_ret;
1711 bool ok;
1712 int i;
1714 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
1715 1, /* 1 chunk */
1716 FNAME,
1717 &src_h, 4096, /* fill 4096 byte src file */
1718 SEC_RIGHTS_FILE_ALL,
1719 FNAME2,
1720 &dest_h, 0, /* 0 byte dest file */
1721 SEC_RIGHTS_FILE_ALL,
1722 &cc_copy,
1723 &ioctl);
1724 if (!ok) {
1725 torture_fail(torture, "setup copy chunk error");
1728 /* copy all src file data (via a single chunk desc) */
1729 cc_copy.chunks[0].source_off = 0;
1730 cc_copy.chunks[0].target_off = 4096;
1731 cc_copy.chunks[0].length = 4096;
1733 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1734 &cc_copy,
1735 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1736 torture_assert_ndr_success(torture, ndr_ret,
1737 "ndr_push_srv_copychunk_copy");
1739 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1740 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
1742 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1743 &cc_rsp,
1744 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1745 torture_assert_ndr_success(torture, ndr_ret,
1746 "ndr_pull_srv_copychunk_rsp");
1748 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1749 1, /* chunks written */
1750 0, /* chunk bytes unsuccessfully written */
1751 4096); /* total bytes written */
1752 if (!ok) {
1753 torture_fail(torture, "bad copy chunk response data");
1756 /* check for zeros in first 4k */
1757 ZERO_STRUCT(r);
1758 r.in.file.handle = dest_h;
1759 r.in.length = 4096;
1760 r.in.offset = 0;
1761 status = smb2_read(tree, tmp_ctx, &r);
1762 torture_assert_ntstatus_ok(torture, status, "read");
1764 torture_assert_u64_equal(torture, r.out.data.length, 4096,
1765 "read data len mismatch");
1767 for (i = 0; i < 4096; i++) {
1768 torture_assert(torture, (r.out.data.data[i] == 0),
1769 "sparse did not pass class");
1772 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 4096, 4096, 0);
1773 if (!ok) {
1774 torture_fail(torture, "inconsistent file data");
1777 smb2_util_close(tree, src_h);
1778 smb2_util_close(tree, dest_h);
1779 talloc_free(tmp_ctx);
1780 return true;
1784 * set the ioctl MaxOutputResponse size to less than
1785 * sizeof(struct srv_copychunk_rsp)
1787 static bool test_ioctl_copy_chunk_max_output_sz(struct torture_context *torture,
1788 struct smb2_tree *tree)
1790 struct smb2_handle src_h;
1791 struct smb2_handle dest_h;
1792 NTSTATUS status;
1793 union smb_ioctl ioctl;
1794 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1795 struct srv_copychunk_copy cc_copy;
1796 enum ndr_err_code ndr_ret;
1797 bool ok;
1799 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
1800 1, /* 1 chunk */
1801 FNAME,
1802 &src_h, 4096, /* fill 4096 byte src file */
1803 SEC_RIGHTS_FILE_ALL,
1804 FNAME2,
1805 &dest_h, 0, /* 0 byte dest file */
1806 SEC_RIGHTS_FILE_ALL,
1807 &cc_copy,
1808 &ioctl);
1809 if (!ok) {
1810 torture_fail(torture, "setup copy chunk error");
1813 cc_copy.chunks[0].source_off = 0;
1814 cc_copy.chunks[0].target_off = 0;
1815 cc_copy.chunks[0].length = 4096;
1816 /* req is valid, but use undersize max_response_size */
1817 ioctl.smb2.in.max_response_size = sizeof(struct srv_copychunk_rsp) - 1;
1819 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1820 &cc_copy,
1821 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1822 torture_assert_ndr_success(torture, ndr_ret,
1823 "ndr_push_srv_copychunk_copy");
1825 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1826 torture_assert_ntstatus_equal(torture, status,
1827 NT_STATUS_INVALID_PARAMETER,
1828 "FSCTL_SRV_COPYCHUNK");
1830 smb2_util_close(tree, src_h);
1831 smb2_util_close(tree, dest_h);
1832 talloc_free(tmp_ctx);
1833 return true;
1836 static bool test_ioctl_copy_chunk_zero_length(struct torture_context *torture,
1837 struct smb2_tree *tree)
1839 struct smb2_handle src_h;
1840 struct smb2_handle dest_h;
1841 NTSTATUS status;
1842 union smb_ioctl ioctl;
1843 union smb_fileinfo q;
1844 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1845 struct srv_copychunk_copy cc_copy;
1846 struct srv_copychunk_rsp cc_rsp;
1847 enum ndr_err_code ndr_ret;
1848 bool ok;
1850 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
1851 1, /* 1 chunk */
1852 FNAME,
1853 &src_h, 4096, /* fill 4096 byte src file */
1854 SEC_RIGHTS_FILE_ALL,
1855 FNAME2,
1856 &dest_h, 0, /* 0 byte dest file */
1857 SEC_RIGHTS_FILE_ALL,
1858 &cc_copy,
1859 &ioctl);
1860 if (!ok) {
1861 torture_fail(torture, "setup copy chunk error");
1864 /* zero length server-side copy (via a single chunk desc) */
1865 cc_copy.chunks[0].source_off = 0;
1866 cc_copy.chunks[0].target_off = 0;
1867 cc_copy.chunks[0].length = 0;
1869 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1870 &cc_copy,
1871 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1872 torture_assert_ndr_success(torture, ndr_ret,
1873 "ndr_push_srv_copychunk_copy");
1875 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1876 torture_assert_ntstatus_equal(torture, status,
1877 NT_STATUS_INVALID_PARAMETER,
1878 "bad zero-length chunk response");
1880 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1881 &cc_rsp,
1882 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1883 torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
1885 ZERO_STRUCT(q);
1886 q.all_info2.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
1887 q.all_info2.in.file.handle = dest_h;
1888 status = smb2_getinfo_file(tree, torture, &q);
1889 torture_assert_ntstatus_ok(torture, status, "getinfo");
1891 torture_assert_int_equal(torture, q.all_info2.out.size, 0,
1892 "size after zero len clone");
1894 smb2_util_close(tree, src_h);
1895 smb2_util_close(tree, dest_h);
1896 talloc_free(tmp_ctx);
1897 return true;
1900 static bool copy_one_stream(struct torture_context *torture,
1901 struct smb2_tree *tree,
1902 TALLOC_CTX *tmp_ctx,
1903 const char *src_sname,
1904 const char *dst_sname)
1906 struct smb2_handle src_h = {{0}};
1907 struct smb2_handle dest_h = {{0}};
1908 NTSTATUS status;
1909 union smb_ioctl io;
1910 struct srv_copychunk_copy cc_copy;
1911 struct srv_copychunk_rsp cc_rsp;
1912 enum ndr_err_code ndr_ret;
1913 bool ok = false;
1915 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
1916 1, /* 1 chunk */
1917 src_sname,
1918 &src_h, 256, /* fill 256 byte src file */
1919 SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA,
1920 dst_sname,
1921 &dest_h, 0, /* 0 byte dest file */
1922 SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA,
1923 &cc_copy,
1924 &io);
1925 torture_assert_goto(torture, ok == true, ok, done,
1926 "setup copy chunk error\n");
1928 /* copy all src file data (via a single chunk desc) */
1929 cc_copy.chunks[0].source_off = 0;
1930 cc_copy.chunks[0].target_off = 0;
1931 cc_copy.chunks[0].length = 256;
1933 ndr_ret = ndr_push_struct_blob(
1934 &io.smb2.in.out, tmp_ctx, &cc_copy,
1935 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1937 torture_assert_ndr_success_goto(torture, ndr_ret, ok, done,
1938 "ndr_push_srv_copychunk_copy\n");
1940 status = smb2_ioctl(tree, tmp_ctx, &io.smb2);
1941 torture_assert_ntstatus_ok_goto(torture, status, ok, done,
1942 "FSCTL_SRV_COPYCHUNK\n");
1944 ndr_ret = ndr_pull_struct_blob(
1945 &io.smb2.out.out, tmp_ctx, &cc_rsp,
1946 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1948 torture_assert_ndr_success_goto(torture, ndr_ret, ok, done,
1949 "ndr_pull_srv_copychunk_rsp\n");
1951 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1952 1, /* chunks written */
1953 0, /* chunk bytes unsuccessfully written */
1954 256); /* total bytes written */
1955 torture_assert_goto(torture, ok == true, ok, done,
1956 "bad copy chunk response data\n");
1958 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 256, 0);
1959 if (!ok) {
1960 torture_fail(torture, "inconsistent file data\n");
1963 done:
1964 if (!smb2_util_handle_empty(src_h)) {
1965 smb2_util_close(tree, src_h);
1967 if (!smb2_util_handle_empty(dest_h)) {
1968 smb2_util_close(tree, dest_h);
1971 return ok;
1975 * Create a file
1977 static bool torture_setup_file(TALLOC_CTX *mem_ctx,
1978 struct smb2_tree *tree,
1979 const char *name)
1981 struct smb2_create io;
1982 NTSTATUS status;
1984 smb2_util_unlink(tree, name);
1985 ZERO_STRUCT(io);
1986 io.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED;
1987 io.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1988 io.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF;
1989 io.in.share_access =
1990 NTCREATEX_SHARE_ACCESS_DELETE|
1991 NTCREATEX_SHARE_ACCESS_READ|
1992 NTCREATEX_SHARE_ACCESS_WRITE;
1993 io.in.create_options = 0;
1994 io.in.fname = name;
1996 status = smb2_create(tree, mem_ctx, &io);
1997 if (!NT_STATUS_IS_OK(status)) {
1998 return false;
2001 status = smb2_util_close(tree, io.out.file.handle);
2002 if (!NT_STATUS_IS_OK(status)) {
2003 return false;
2006 return true;
2009 static bool test_copy_chunk_streams(struct torture_context *torture,
2010 struct smb2_tree *tree)
2012 const char *src_name = "src";
2013 const char *dst_name = "dst";
2014 struct names {
2015 const char *src_sname;
2016 const char *dst_sname;
2017 } names[] = {
2018 { "src:foo", "dst:foo" }
2020 int i;
2021 TALLOC_CTX *tmp_ctx = NULL;
2022 bool ok = false;
2024 tmp_ctx = talloc_new(tree);
2025 torture_assert_not_null_goto(torture, tmp_ctx, ok, done,
2026 "torture_setup_file\n");
2028 ok = torture_setup_file(torture, tree, src_name);
2029 torture_assert_goto(torture, ok == true, ok, done, "torture_setup_file\n");
2030 ok = torture_setup_file(torture, tree, dst_name);
2031 torture_assert_goto(torture, ok == true, ok, done, "torture_setup_file\n");
2033 for (i = 0; i < ARRAY_SIZE(names); i++) {
2034 ok = copy_one_stream(torture, tree, tmp_ctx,
2035 names[i].src_sname,
2036 names[i].dst_sname);
2037 torture_assert_goto(torture, ok == true, ok, done,
2038 "copy_one_stream failed\n");
2041 done:
2042 smb2_util_unlink(tree, src_name);
2043 smb2_util_unlink(tree, dst_name);
2044 talloc_free(tmp_ctx);
2045 return ok;
2048 static bool test_copy_chunk_across_shares(struct torture_context *tctx,
2049 struct smb2_tree *tree)
2051 TALLOC_CTX *mem_ctx = NULL;
2052 struct smb2_tree *tree2 = NULL;
2053 struct smb2_handle src_h = {{0}};
2054 struct smb2_handle dest_h = {{0}};
2055 union smb_ioctl ioctl;
2056 struct srv_copychunk_copy cc_copy;
2057 struct srv_copychunk_rsp cc_rsp;
2058 enum ndr_err_code ndr_ret;
2059 NTSTATUS status;
2060 bool ok = false;
2062 mem_ctx = talloc_new(tctx);
2063 torture_assert_not_null_goto(tctx, mem_ctx, ok, done,
2064 "talloc_new\n");
2066 ok = torture_smb2_tree_connect(tctx, tree->session, tctx, &tree2);
2067 torture_assert_goto(tctx, ok == true, ok, done,
2068 "torture_smb2_tree_connect failed\n");
2070 ok = test_setup_copy_chunk(tctx, tree, tree2, mem_ctx,
2071 1, /* 1 chunk */
2072 FNAME,
2073 &src_h, 4096, /* fill 4096 byte src file */
2074 SEC_RIGHTS_FILE_ALL,
2075 FNAME2,
2076 &dest_h, 0, /* 0 byte dest file */
2077 SEC_RIGHTS_FILE_ALL,
2078 &cc_copy,
2079 &ioctl);
2080 torture_assert_goto(tctx, ok == true, ok, done,
2081 "test_setup_copy_chunk failed\n");
2083 cc_copy.chunks[0].source_off = 0;
2084 cc_copy.chunks[0].target_off = 0;
2085 cc_copy.chunks[0].length = 4096;
2087 ndr_ret = ndr_push_struct_blob(
2088 &ioctl.smb2.in.out, mem_ctx, &cc_copy,
2089 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
2090 torture_assert_ndr_success_goto(tctx, ndr_ret, ok, done,
2091 "ndr_push_srv_copychunk_copy\n");
2093 status = smb2_ioctl(tree2, mem_ctx, &ioctl.smb2);
2094 torture_assert_ntstatus_ok_goto(tctx, status, ok, done,
2095 "FSCTL_SRV_COPYCHUNK\n");
2097 ndr_ret = ndr_pull_struct_blob(
2098 &ioctl.smb2.out.out, mem_ctx, &cc_rsp,
2099 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
2101 torture_assert_ndr_success_goto(tctx, ndr_ret, ok, done,
2102 "ndr_pull_srv_copychunk_rsp\n");
2104 ok = check_copy_chunk_rsp(tctx, &cc_rsp,
2105 1, /* chunks written */
2106 0, /* chunk bytes unsuccessfully written */
2107 4096); /* total bytes written */
2108 torture_assert_goto(tctx, ok == true, ok, done,
2109 "bad copy chunk response data\n");
2111 ok = check_pattern(tctx, tree2, mem_ctx, dest_h, 0, 4096, 0);
2112 torture_assert_goto(tctx, ok == true, ok, done,
2113 "inconsistent file data\n");
2115 done:
2116 TALLOC_FREE(mem_ctx);
2117 if (!smb2_util_handle_empty(src_h)) {
2118 smb2_util_close(tree, src_h);
2120 if (!smb2_util_handle_empty(dest_h)) {
2121 smb2_util_close(tree2, dest_h);
2123 smb2_util_unlink(tree, FNAME);
2124 smb2_util_unlink(tree2, FNAME2);
2125 if (tree2 != NULL) {
2126 smb2_tdis(tree2);
2128 return ok;
2131 /* Test closing the src handle */
2132 static bool test_copy_chunk_across_shares2(struct torture_context *tctx,
2133 struct smb2_tree *tree)
2135 TALLOC_CTX *mem_ctx = NULL;
2136 struct smb2_tree *tree2 = NULL;
2137 struct smb2_handle src_h = {{0}};
2138 struct smb2_handle dest_h = {{0}};
2139 union smb_ioctl ioctl;
2140 struct srv_copychunk_copy cc_copy;
2141 enum ndr_err_code ndr_ret;
2142 NTSTATUS status;
2143 bool ok = false;
2145 mem_ctx = talloc_new(tctx);
2146 torture_assert_not_null_goto(tctx, mem_ctx, ok, done,
2147 "talloc_new\n");
2149 ok = torture_smb2_tree_connect(tctx, tree->session, tctx, &tree2);
2150 torture_assert_goto(tctx, ok == true, ok, done,
2151 "torture_smb2_tree_connect failed\n");
2153 ok = test_setup_copy_chunk(tctx, tree, tree2, mem_ctx,
2154 1, /* 1 chunk */
2155 FNAME,
2156 &src_h, 4096, /* fill 4096 byte src file */
2157 SEC_RIGHTS_FILE_ALL,
2158 FNAME2,
2159 &dest_h, 0, /* 0 byte dest file */
2160 SEC_RIGHTS_FILE_ALL,
2161 &cc_copy,
2162 &ioctl);
2163 torture_assert_goto(tctx, ok == true, ok, done,
2164 "test_setup_copy_chunk failed\n");
2166 status = smb2_util_close(tree, src_h);
2167 torture_assert_ntstatus_ok_goto(tctx, status, ok, done,
2168 "smb2_util_close failed\n");
2169 ZERO_STRUCT(src_h);
2171 cc_copy.chunks[0].source_off = 0;
2172 cc_copy.chunks[0].target_off = 0;
2173 cc_copy.chunks[0].length = 4096;
2175 ndr_ret = ndr_push_struct_blob(
2176 &ioctl.smb2.in.out, mem_ctx, &cc_copy,
2177 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
2178 torture_assert_ndr_success_goto(tctx, ndr_ret, ok, done,
2179 "ndr_push_srv_copychunk_copy\n");
2181 status = smb2_ioctl(tree2, mem_ctx, &ioctl.smb2);
2182 torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND,
2183 ok, done, "smb2_ioctl failed\n");
2185 done:
2186 TALLOC_FREE(mem_ctx);
2187 if (!smb2_util_handle_empty(src_h)) {
2188 smb2_util_close(tree, src_h);
2190 if (!smb2_util_handle_empty(dest_h)) {
2191 smb2_util_close(tree2, dest_h);
2193 smb2_util_unlink(tree, FNAME);
2194 smb2_util_unlink(tree2, FNAME2);
2195 if (tree2 != NULL) {
2196 smb2_tdis(tree2);
2198 return ok;
2201 /* Test offset works */
2202 static bool test_copy_chunk_across_shares3(struct torture_context *tctx,
2203 struct smb2_tree *tree)
2205 TALLOC_CTX *mem_ctx = NULL;
2206 struct smb2_tree *tree2 = NULL;
2207 struct smb2_handle src_h = {{0}};
2208 struct smb2_handle dest_h = {{0}};
2209 union smb_ioctl ioctl;
2210 struct srv_copychunk_copy cc_copy;
2211 struct srv_copychunk_rsp cc_rsp;
2212 enum ndr_err_code ndr_ret;
2213 NTSTATUS status;
2214 bool ok = false;
2216 mem_ctx = talloc_new(tctx);
2217 torture_assert_not_null_goto(tctx, mem_ctx, ok, done,
2218 "talloc_new\n");
2220 ok = torture_smb2_tree_connect(tctx, tree->session, tctx, &tree2);
2221 torture_assert_goto(tctx, ok == true, ok, done,
2222 "torture_smb2_tree_connect failed\n");
2224 ok = test_setup_copy_chunk(tctx, tree, tree2, mem_ctx,
2225 2, /* 2 chunks */
2226 FNAME,
2227 &src_h, 4096, /* fill 4096 byte src file */
2228 SEC_RIGHTS_FILE_ALL,
2229 FNAME2,
2230 &dest_h, 0, /* 0 byte dest file */
2231 SEC_RIGHTS_FILE_ALL,
2232 &cc_copy,
2233 &ioctl);
2234 torture_assert_goto(tctx, ok == true, ok, done,
2235 "test_setup_copy_chunk failed\n");
2237 cc_copy.chunks[0].source_off = 0;
2238 cc_copy.chunks[0].target_off = 0;
2239 cc_copy.chunks[0].length = 4096;
2241 /* second chunk appends the same data to the first */
2242 cc_copy.chunks[1].source_off = 0;
2243 cc_copy.chunks[1].target_off = 4096;
2244 cc_copy.chunks[1].length = 4096;
2246 ndr_ret = ndr_push_struct_blob(
2247 &ioctl.smb2.in.out, mem_ctx, &cc_copy,
2248 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
2249 torture_assert_ndr_success_goto(tctx, ndr_ret, ok, done,
2250 "ndr_push_srv_copychunk_copy\n");
2252 status = smb2_ioctl(tree2, mem_ctx, &ioctl.smb2);
2253 torture_assert_ntstatus_ok_goto(tctx, status, ok, done, "smb2_ioctl failed\n");
2255 ndr_ret = ndr_pull_struct_blob(
2256 &ioctl.smb2.out.out, mem_ctx, &cc_rsp,
2257 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
2259 torture_assert_ndr_success_goto(tctx, ndr_ret, ok, done,
2260 "ndr_pull_srv_copychunk_rsp\n");
2262 ok = check_copy_chunk_rsp(tctx, &cc_rsp,
2263 2, /* chunks written */
2264 0, /* chunk bytes unsuccessfully written */
2265 8192); /* total bytes written */
2266 torture_assert_goto(tctx, ok == true, ok, done,
2267 "check_copy_chunk_rsp failed\n");
2269 ok = check_pattern(tctx, tree2, mem_ctx, dest_h, 0, 4096, 0);
2270 torture_assert_goto(tctx, ok == true, ok, done,
2271 "check_pattern failed\n");
2273 ok = check_pattern(tctx, tree2, mem_ctx, dest_h, 4096, 4096, 0);
2274 torture_assert_goto(tctx, ok == true, ok, done,
2275 "check_pattern failed\n");
2277 done:
2278 TALLOC_FREE(mem_ctx);
2279 if (!smb2_util_handle_empty(src_h)) {
2280 smb2_util_close(tree, src_h);
2282 if (!smb2_util_handle_empty(dest_h)) {
2283 smb2_util_close(tree2, dest_h);
2285 smb2_util_unlink(tree, FNAME);
2286 smb2_util_unlink(tree2, FNAME2);
2287 if (tree2 != NULL) {
2288 smb2_tdis(tree2);
2290 return ok;
2293 static NTSTATUS test_ioctl_compress_fs_supported(struct torture_context *torture,
2294 struct smb2_tree *tree,
2295 TALLOC_CTX *mem_ctx,
2296 struct smb2_handle *fh,
2297 bool *compress_support)
2299 NTSTATUS status;
2300 union smb_fsinfo info;
2302 ZERO_STRUCT(info);
2303 info.generic.level = RAW_QFS_ATTRIBUTE_INFORMATION;
2304 info.generic.handle = *fh;
2305 status = smb2_getinfo_fs(tree, tree, &info);
2306 if (!NT_STATUS_IS_OK(status)) {
2307 return status;
2310 if (info.attribute_info.out.fs_attr & FILE_FILE_COMPRESSION) {
2311 *compress_support = true;
2312 } else {
2313 *compress_support = false;
2315 return NT_STATUS_OK;
2318 static NTSTATUS test_ioctl_compress_get(struct torture_context *torture,
2319 TALLOC_CTX *mem_ctx,
2320 struct smb2_tree *tree,
2321 struct smb2_handle fh,
2322 uint16_t *_compression_fmt)
2324 union smb_ioctl ioctl;
2325 struct compression_state cmpr_state;
2326 enum ndr_err_code ndr_ret;
2327 NTSTATUS status;
2329 ZERO_STRUCT(ioctl);
2330 ioctl.smb2.level = RAW_IOCTL_SMB2;
2331 ioctl.smb2.in.file.handle = fh;
2332 ioctl.smb2.in.function = FSCTL_GET_COMPRESSION;
2333 ioctl.smb2.in.max_response_size = sizeof(struct compression_state);
2334 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2336 status = smb2_ioctl(tree, mem_ctx, &ioctl.smb2);
2337 if (!NT_STATUS_IS_OK(status)) {
2338 return status;
2341 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, mem_ctx,
2342 &cmpr_state,
2343 (ndr_pull_flags_fn_t)ndr_pull_compression_state);
2345 if (ndr_ret != NDR_ERR_SUCCESS) {
2346 return NT_STATUS_INTERNAL_ERROR;
2349 *_compression_fmt = cmpr_state.format;
2350 return NT_STATUS_OK;
2353 static NTSTATUS test_ioctl_compress_set(struct torture_context *torture,
2354 TALLOC_CTX *mem_ctx,
2355 struct smb2_tree *tree,
2356 struct smb2_handle fh,
2357 uint16_t compression_fmt)
2359 union smb_ioctl ioctl;
2360 struct compression_state cmpr_state;
2361 enum ndr_err_code ndr_ret;
2362 NTSTATUS status;
2364 ZERO_STRUCT(ioctl);
2365 ioctl.smb2.level = RAW_IOCTL_SMB2;
2366 ioctl.smb2.in.file.handle = fh;
2367 ioctl.smb2.in.function = FSCTL_SET_COMPRESSION;
2368 ioctl.smb2.in.max_response_size = 0;
2369 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2371 cmpr_state.format = compression_fmt;
2372 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, mem_ctx,
2373 &cmpr_state,
2374 (ndr_push_flags_fn_t)ndr_push_compression_state);
2375 if (ndr_ret != NDR_ERR_SUCCESS) {
2376 return NT_STATUS_INTERNAL_ERROR;
2379 status = smb2_ioctl(tree, mem_ctx, &ioctl.smb2);
2380 return status;
2383 static bool test_ioctl_compress_file_flag(struct torture_context *torture,
2384 struct smb2_tree *tree)
2386 struct smb2_handle fh;
2387 NTSTATUS status;
2388 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2389 bool ok;
2390 uint16_t compression_fmt;
2392 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2393 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2394 FILE_ATTRIBUTE_NORMAL);
2395 torture_assert(torture, ok, "setup compression file");
2397 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
2398 &ok);
2399 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2400 if (!ok) {
2401 smb2_util_close(tree, fh);
2402 torture_skip(torture, "FS compression not supported\n");
2405 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2406 &compression_fmt);
2407 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2409 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2410 "initial compression state not NONE");
2412 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
2413 COMPRESSION_FORMAT_DEFAULT);
2414 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
2416 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2417 &compression_fmt);
2418 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2420 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
2421 "invalid compression state after set");
2423 smb2_util_close(tree, fh);
2424 talloc_free(tmp_ctx);
2425 return true;
2428 static bool test_ioctl_compress_dir_inherit(struct torture_context *torture,
2429 struct smb2_tree *tree)
2431 struct smb2_handle dirh;
2432 struct smb2_handle fh;
2433 NTSTATUS status;
2434 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2435 uint16_t compression_fmt;
2436 bool ok;
2437 char path_buf[PATH_MAX];
2439 smb2_deltree(tree, DNAME);
2440 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2441 DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL,
2442 FILE_ATTRIBUTE_DIRECTORY);
2443 torture_assert(torture, ok, "setup compression directory");
2445 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &dirh,
2446 &ok);
2447 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2448 if (!ok) {
2449 smb2_util_close(tree, dirh);
2450 smb2_deltree(tree, DNAME);
2451 torture_skip(torture, "FS compression not supported\n");
2454 /* set compression on parent dir, then check for inheritance */
2455 status = test_ioctl_compress_set(torture, tmp_ctx, tree, dirh,
2456 COMPRESSION_FORMAT_LZNT1);
2457 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
2459 status = test_ioctl_compress_get(torture, tmp_ctx, tree, dirh,
2460 &compression_fmt);
2461 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2463 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
2464 "invalid compression state after set");
2466 snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME);
2467 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2468 path_buf, &fh, 4096, SEC_RIGHTS_FILE_ALL,
2469 FILE_ATTRIBUTE_NORMAL);
2470 torture_assert(torture, ok, "setup compression file");
2472 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2473 &compression_fmt);
2474 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2476 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
2477 "compression attr not inherited by new file");
2479 /* check compressed data is consistent */
2480 ok = check_pattern(torture, tree, tmp_ctx, fh, 0, 4096, 0);
2482 /* disable dir compression attr, file should remain compressed */
2483 status = test_ioctl_compress_set(torture, tmp_ctx, tree, dirh,
2484 COMPRESSION_FORMAT_NONE);
2485 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
2487 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2488 &compression_fmt);
2489 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2491 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
2492 "file compression attr removed after dir change");
2493 smb2_util_close(tree, fh);
2495 /* new files should no longer inherit compression attr */
2496 snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME2);
2497 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2498 path_buf, &fh, 0, SEC_RIGHTS_FILE_ALL,
2499 FILE_ATTRIBUTE_NORMAL);
2500 torture_assert(torture, ok, "setup file");
2502 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2503 &compression_fmt);
2504 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2506 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2507 "compression attr present on new file");
2509 smb2_util_close(tree, fh);
2510 smb2_util_close(tree, dirh);
2511 smb2_deltree(tree, DNAME);
2512 talloc_free(tmp_ctx);
2513 return true;
2516 static bool test_ioctl_compress_invalid_format(struct torture_context *torture,
2517 struct smb2_tree *tree)
2519 struct smb2_handle fh;
2520 NTSTATUS status;
2521 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2522 bool ok;
2523 uint16_t compression_fmt;
2525 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2526 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2527 FILE_ATTRIBUTE_NORMAL);
2528 torture_assert(torture, ok, "setup compression file");
2530 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
2531 &ok);
2532 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2533 if (!ok) {
2534 smb2_util_close(tree, fh);
2535 torture_skip(torture, "FS compression not supported\n");
2538 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
2539 0x0042); /* bogus */
2540 torture_assert_ntstatus_equal(torture, status,
2541 NT_STATUS_INVALID_PARAMETER,
2542 "invalid FSCTL_SET_COMPRESSION");
2544 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2545 &compression_fmt);
2546 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2548 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2549 "initial compression state not NONE");
2551 smb2_util_close(tree, fh);
2552 talloc_free(tmp_ctx);
2553 return true;
2556 static bool test_ioctl_compress_invalid_buf(struct torture_context *torture,
2557 struct smb2_tree *tree)
2559 struct smb2_handle fh;
2560 NTSTATUS status;
2561 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2562 bool ok;
2563 union smb_ioctl ioctl;
2565 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2566 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2567 FILE_ATTRIBUTE_NORMAL);
2568 torture_assert(torture, ok, "setup compression file");
2570 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
2571 &ok);
2572 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2573 if (!ok) {
2574 smb2_util_close(tree, fh);
2575 torture_skip(torture, "FS compression not supported\n");
2578 ZERO_STRUCT(ioctl);
2579 ioctl.smb2.level = RAW_IOCTL_SMB2;
2580 ioctl.smb2.in.file.handle = fh;
2581 ioctl.smb2.in.function = FSCTL_GET_COMPRESSION;
2582 ioctl.smb2.in.max_response_size = 0; /* no room for rsp data */
2583 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2585 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2586 if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_USER_BUFFER)
2587 && !NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
2588 /* neither Server 2k12 nor 2k8r2 response status */
2589 torture_assert(torture, true,
2590 "invalid FSCTL_SET_COMPRESSION");
2593 smb2_util_close(tree, fh);
2594 talloc_free(tmp_ctx);
2595 return true;
2598 static bool test_ioctl_compress_query_file_attr(struct torture_context *torture,
2599 struct smb2_tree *tree)
2601 struct smb2_handle fh;
2602 union smb_fileinfo io;
2603 NTSTATUS status;
2604 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2605 bool ok;
2607 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2608 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2609 FILE_ATTRIBUTE_NORMAL);
2610 torture_assert(torture, ok, "setup compression file");
2612 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
2613 &ok);
2614 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2615 if (!ok) {
2616 smb2_util_close(tree, fh);
2617 torture_skip(torture, "FS compression not supported\n");
2620 ZERO_STRUCT(io);
2621 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
2622 io.generic.in.file.handle = fh;
2623 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2624 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2626 torture_assert(torture,
2627 ((io.all_info2.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2628 "compression attr before set");
2630 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
2631 COMPRESSION_FORMAT_DEFAULT);
2632 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
2634 ZERO_STRUCT(io);
2635 io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2636 io.generic.in.file.handle = fh;
2637 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2638 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2640 torture_assert(torture,
2641 (io.basic_info.out.attrib & FILE_ATTRIBUTE_COMPRESSED),
2642 "no compression attr after set");
2644 smb2_util_close(tree, fh);
2645 talloc_free(tmp_ctx);
2646 return true;
2650 * Specify FILE_ATTRIBUTE_COMPRESSED on creation, Windows does not retain this
2651 * attribute.
2653 static bool test_ioctl_compress_create_with_attr(struct torture_context *torture,
2654 struct smb2_tree *tree)
2656 struct smb2_handle fh2;
2657 union smb_fileinfo io;
2658 NTSTATUS status;
2659 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2660 uint16_t compression_fmt;
2661 bool ok;
2663 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2664 FNAME2, &fh2, 0, SEC_RIGHTS_FILE_ALL,
2665 (FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_COMPRESSED));
2666 torture_assert(torture, ok, "setup compression file");
2668 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh2,
2669 &ok);
2670 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2671 if (!ok) {
2672 smb2_util_close(tree, fh2);
2673 torture_skip(torture, "FS compression not supported\n");
2676 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh2,
2677 &compression_fmt);
2678 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2680 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2681 "initial compression state not NONE");
2683 ZERO_STRUCT(io);
2684 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
2685 io.generic.in.file.handle = fh2;
2686 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2687 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2689 torture_assert(torture,
2690 ((io.all_info2.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2691 "incorrect compression attr");
2693 smb2_util_close(tree, fh2);
2694 talloc_free(tmp_ctx);
2695 return true;
2698 static bool test_ioctl_compress_inherit_disable(struct torture_context *torture,
2699 struct smb2_tree *tree)
2701 struct smb2_handle fh;
2702 struct smb2_handle dirh;
2703 char path_buf[PATH_MAX];
2704 NTSTATUS status;
2705 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2706 bool ok;
2707 uint16_t compression_fmt;
2709 struct smb2_create io;
2711 smb2_deltree(tree, DNAME);
2712 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2713 DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL,
2714 FILE_ATTRIBUTE_DIRECTORY);
2715 torture_assert(torture, ok, "setup compression directory");
2717 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &dirh,
2718 &ok);
2719 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2720 if (!ok) {
2721 smb2_util_close(tree, dirh);
2722 smb2_deltree(tree, DNAME);
2723 torture_skip(torture, "FS compression not supported\n");
2726 /* set compression on parent dir, then check for inheritance */
2727 status = test_ioctl_compress_set(torture, tmp_ctx, tree, dirh,
2728 COMPRESSION_FORMAT_LZNT1);
2729 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
2731 status = test_ioctl_compress_get(torture, tmp_ctx, tree, dirh,
2732 &compression_fmt);
2733 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2735 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
2736 "invalid compression state after set");
2737 smb2_util_close(tree, dirh);
2739 snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME);
2740 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2741 path_buf, &fh, 0, SEC_RIGHTS_FILE_ALL,
2742 FILE_ATTRIBUTE_NORMAL);
2743 torture_assert(torture, ok, "setup compression file");
2745 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2746 &compression_fmt);
2747 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2749 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
2750 "compression attr not inherited by new file");
2751 smb2_util_close(tree, fh);
2753 snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME2);
2755 /* NO_COMPRESSION option should block inheritance */
2756 ZERO_STRUCT(io);
2757 io.in.desired_access = SEC_RIGHTS_FILE_ALL;
2758 io.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
2759 io.in.create_disposition = NTCREATEX_DISP_CREATE;
2760 io.in.create_options = NTCREATEX_OPTIONS_NO_COMPRESSION;
2761 io.in.share_access =
2762 NTCREATEX_SHARE_ACCESS_DELETE|
2763 NTCREATEX_SHARE_ACCESS_READ|
2764 NTCREATEX_SHARE_ACCESS_WRITE;
2765 io.in.fname = path_buf;
2767 status = smb2_create(tree, tmp_ctx, &io);
2768 torture_assert_ntstatus_ok(torture, status, "file create");
2770 fh = io.out.file.handle;
2772 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2773 &compression_fmt);
2774 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2776 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2777 "compression attr inherited by NO_COMPRESSION file");
2778 smb2_util_close(tree, fh);
2781 snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, DNAME);
2782 ZERO_STRUCT(io);
2783 io.in.desired_access = SEC_RIGHTS_FILE_ALL;
2784 io.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
2785 io.in.create_disposition = NTCREATEX_DISP_CREATE;
2786 io.in.create_options = (NTCREATEX_OPTIONS_NO_COMPRESSION
2787 | NTCREATEX_OPTIONS_DIRECTORY);
2788 io.in.share_access =
2789 NTCREATEX_SHARE_ACCESS_DELETE|
2790 NTCREATEX_SHARE_ACCESS_READ|
2791 NTCREATEX_SHARE_ACCESS_WRITE;
2792 io.in.fname = path_buf;
2794 status = smb2_create(tree, tmp_ctx, &io);
2795 torture_assert_ntstatus_ok(torture, status, "dir create");
2797 dirh = io.out.file.handle;
2799 status = test_ioctl_compress_get(torture, tmp_ctx, tree, dirh,
2800 &compression_fmt);
2801 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2803 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2804 "compression attr inherited by NO_COMPRESSION dir");
2805 smb2_util_close(tree, dirh);
2806 smb2_deltree(tree, DNAME);
2808 talloc_free(tmp_ctx);
2809 return true;
2812 /* attempting to set compression via SetInfo should not stick */
2813 static bool test_ioctl_compress_set_file_attr(struct torture_context *torture,
2814 struct smb2_tree *tree)
2816 struct smb2_handle fh;
2817 struct smb2_handle dirh;
2818 union smb_fileinfo io;
2819 union smb_setfileinfo set_io;
2820 uint16_t compression_fmt;
2821 NTSTATUS status;
2822 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2823 bool ok;
2825 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2826 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2827 FILE_ATTRIBUTE_NORMAL);
2828 torture_assert(torture, ok, "setup compression file");
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 not supported\n");
2838 ZERO_STRUCT(io);
2839 io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2840 io.generic.in.file.handle = fh;
2841 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2842 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2844 torture_assert(torture,
2845 ((io.basic_info.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2846 "compression attr before set");
2848 ZERO_STRUCT(set_io);
2849 set_io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2850 set_io.basic_info.in.file.handle = fh;
2851 set_io.basic_info.in.create_time = io.basic_info.out.create_time;
2852 set_io.basic_info.in.access_time = io.basic_info.out.access_time;
2853 set_io.basic_info.in.write_time = io.basic_info.out.write_time;
2854 set_io.basic_info.in.change_time = io.basic_info.out.change_time;
2855 set_io.basic_info.in.attrib = (io.basic_info.out.attrib
2856 | FILE_ATTRIBUTE_COMPRESSED);
2857 status = smb2_setinfo_file(tree, &set_io);
2858 torture_assert_ntstatus_ok(torture, status, "SMB2_SETINFO_FILE");
2860 ZERO_STRUCT(io);
2861 io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2862 io.generic.in.file.handle = fh;
2863 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2864 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2866 torture_assert(torture,
2867 ((io.basic_info.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2868 "compression attr after set");
2870 smb2_util_close(tree, fh);
2871 smb2_deltree(tree, DNAME);
2872 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2873 DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL,
2874 FILE_ATTRIBUTE_DIRECTORY);
2875 torture_assert(torture, ok, "setup compression directory");
2877 ZERO_STRUCT(io);
2878 io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2879 io.generic.in.file.handle = dirh;
2880 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2881 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2883 torture_assert(torture,
2884 ((io.basic_info.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2885 "compression attr before set");
2887 ZERO_STRUCT(set_io);
2888 set_io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2889 set_io.basic_info.in.file.handle = dirh;
2890 set_io.basic_info.in.create_time = io.basic_info.out.create_time;
2891 set_io.basic_info.in.access_time = io.basic_info.out.access_time;
2892 set_io.basic_info.in.write_time = io.basic_info.out.write_time;
2893 set_io.basic_info.in.change_time = io.basic_info.out.change_time;
2894 set_io.basic_info.in.attrib = (io.basic_info.out.attrib
2895 | FILE_ATTRIBUTE_COMPRESSED);
2896 status = smb2_setinfo_file(tree, &set_io);
2897 torture_assert_ntstatus_ok(torture, status, "SMB2_SETINFO_FILE");
2899 status = test_ioctl_compress_get(torture, tmp_ctx, tree, dirh,
2900 &compression_fmt);
2901 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2903 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2904 "dir compression set after SetInfo");
2906 smb2_util_close(tree, dirh);
2907 talloc_free(tmp_ctx);
2908 return true;
2911 static bool test_ioctl_compress_perms(struct torture_context *torture,
2912 struct smb2_tree *tree)
2914 struct smb2_handle fh;
2915 uint16_t compression_fmt;
2916 union smb_fileinfo io;
2917 NTSTATUS status;
2918 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2919 bool ok;
2921 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2922 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2923 FILE_ATTRIBUTE_NORMAL);
2924 torture_assert(torture, ok, "setup compression file");
2926 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
2927 &ok);
2928 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2929 smb2_util_close(tree, fh);
2930 if (!ok) {
2931 torture_skip(torture, "FS compression not supported\n");
2934 /* attempt get compression without READ_ATTR permission */
2935 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2936 FNAME, &fh, 0,
2937 (SEC_RIGHTS_FILE_READ & ~(SEC_FILE_READ_ATTRIBUTE
2938 | SEC_STD_READ_CONTROL
2939 | SEC_FILE_READ_EA)),
2940 FILE_ATTRIBUTE_NORMAL);
2941 torture_assert(torture, ok, "setup compression file");
2943 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2944 &compression_fmt);
2945 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2946 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2947 "compression set after create");
2948 smb2_util_close(tree, fh);
2950 /* set compression without WRITE_ATTR permission should succeed */
2951 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2952 FNAME, &fh, 0,
2953 (SEC_RIGHTS_FILE_WRITE & ~(SEC_FILE_WRITE_ATTRIBUTE
2954 | SEC_STD_WRITE_DAC
2955 | SEC_FILE_WRITE_EA)),
2956 FILE_ATTRIBUTE_NORMAL);
2957 torture_assert(torture, ok, "setup compression file");
2959 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
2960 COMPRESSION_FORMAT_DEFAULT);
2961 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
2962 smb2_util_close(tree, fh);
2964 ok = test_setup_open(torture, tree, tmp_ctx,
2965 FNAME, &fh, SEC_RIGHTS_FILE_ALL,
2966 FILE_ATTRIBUTE_NORMAL);
2967 torture_assert(torture, ok, "setup compression file");
2968 ZERO_STRUCT(io);
2969 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
2970 io.generic.in.file.handle = fh;
2971 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2972 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2974 torture_assert(torture,
2975 (io.all_info2.out.attrib & FILE_ATTRIBUTE_COMPRESSED),
2976 "incorrect compression attr");
2977 smb2_util_close(tree, fh);
2979 /* attempt get compression without READ_DATA permission */
2980 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2981 FNAME, &fh, 0,
2982 (SEC_RIGHTS_FILE_READ & ~SEC_FILE_READ_DATA),
2983 FILE_ATTRIBUTE_NORMAL);
2984 torture_assert(torture, ok, "setup compression file");
2986 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2987 &compression_fmt);
2988 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2989 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2990 "compression enabled after set");
2991 smb2_util_close(tree, fh);
2993 /* attempt get compression with only SYNCHRONIZE permission */
2994 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2995 FNAME, &fh, 0,
2996 SEC_STD_SYNCHRONIZE,
2997 FILE_ATTRIBUTE_NORMAL);
2998 torture_assert(torture, ok, "setup compression file");
3000 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
3001 &compression_fmt);
3002 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
3003 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
3004 "compression not enabled after set");
3005 smb2_util_close(tree, fh);
3007 /* attempt to set compression without WRITE_DATA permission */
3008 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3009 FNAME, &fh, 0,
3010 (SEC_RIGHTS_FILE_WRITE & (~SEC_FILE_WRITE_DATA)),
3011 FILE_ATTRIBUTE_NORMAL);
3012 torture_assert(torture, ok, "setup compression file");
3014 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
3015 COMPRESSION_FORMAT_DEFAULT);
3016 torture_assert_ntstatus_equal(torture, status,
3017 NT_STATUS_ACCESS_DENIED,
3018 "FSCTL_SET_COMPRESSION permission");
3019 smb2_util_close(tree, fh);
3021 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3022 FNAME, &fh, 0,
3023 (SEC_RIGHTS_FILE_WRITE & (~SEC_FILE_WRITE_DATA)),
3024 FILE_ATTRIBUTE_NORMAL);
3025 torture_assert(torture, ok, "setup compression file");
3027 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
3028 COMPRESSION_FORMAT_NONE);
3029 torture_assert_ntstatus_equal(torture, status,
3030 NT_STATUS_ACCESS_DENIED,
3031 "FSCTL_SET_COMPRESSION permission");
3032 smb2_util_close(tree, fh);
3034 talloc_free(tmp_ctx);
3035 return true;
3038 static bool test_ioctl_compress_notsup_get(struct torture_context *torture,
3039 struct smb2_tree *tree)
3041 struct smb2_handle fh;
3042 NTSTATUS status;
3043 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3044 bool ok;
3045 uint16_t compression_fmt;
3047 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3048 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3049 FILE_ATTRIBUTE_NORMAL);
3050 torture_assert(torture, ok, "setup compression file");
3052 /* skip if the server DOES support compression */
3053 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
3054 &ok);
3055 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3056 if (ok) {
3057 smb2_util_close(tree, fh);
3058 torture_skip(torture, "FS compression supported\n");
3062 * Despite not supporting compression, we should get a successful
3063 * response indicating that the file is uncompressed - like WS2016.
3065 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
3066 &compression_fmt);
3067 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
3069 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
3070 "initial compression state not NONE");
3072 smb2_util_close(tree, fh);
3073 talloc_free(tmp_ctx);
3074 return true;
3077 static bool test_ioctl_compress_notsup_set(struct torture_context *torture,
3078 struct smb2_tree *tree)
3080 struct smb2_handle fh;
3081 NTSTATUS status;
3082 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3083 bool ok;
3085 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3086 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3087 FILE_ATTRIBUTE_NORMAL);
3088 torture_assert(torture, ok, "setup compression file");
3090 /* skip if the server DOES support compression */
3091 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
3092 &ok);
3093 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3094 if (ok) {
3095 smb2_util_close(tree, fh);
3096 torture_skip(torture, "FS compression supported\n");
3099 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
3100 COMPRESSION_FORMAT_DEFAULT);
3101 torture_assert_ntstatus_equal(torture, status,
3102 NT_STATUS_NOT_SUPPORTED,
3103 "FSCTL_SET_COMPRESSION default");
3106 * Despite not supporting compression, we should get a successful
3107 * response for set(COMPRESSION_FORMAT_NONE) - like WS2016 ReFS.
3109 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
3110 COMPRESSION_FORMAT_NONE);
3111 torture_assert_ntstatus_ok(torture, status,
3112 "FSCTL_SET_COMPRESSION none");
3114 smb2_util_close(tree, fh);
3115 talloc_free(tmp_ctx);
3116 return true;
3120 basic testing of the SMB2 FSCTL_QUERY_NETWORK_INTERFACE_INFO ioctl
3122 static bool test_ioctl_network_interface_info(struct torture_context *torture,
3123 struct smb2_tree *tree)
3125 union smb_ioctl ioctl;
3126 struct smb2_handle fh;
3127 NTSTATUS status;
3128 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3129 struct fsctl_net_iface_info net_iface;
3130 enum ndr_err_code ndr_ret;
3131 uint32_t caps;
3133 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
3134 if (!(caps & SMB2_CAP_MULTI_CHANNEL)) {
3135 torture_skip(torture, "server doesn't support SMB2_CAP_MULTI_CHANNEL\n");
3138 ZERO_STRUCT(ioctl);
3139 ioctl.smb2.level = RAW_IOCTL_SMB2;
3140 fh.data[0] = UINT64_MAX;
3141 fh.data[1] = UINT64_MAX;
3142 ioctl.smb2.in.file.handle = fh;
3143 ioctl.smb2.in.function = FSCTL_QUERY_NETWORK_INTERFACE_INFO;
3144 ioctl.smb2.in.max_response_size = 0x10000; /* Windows client sets this to 64KiB */
3145 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3147 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3148 torture_assert_ntstatus_ok(torture, status, "FSCTL_QUERY_NETWORK_INTERFACE_INFO");
3150 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, &net_iface,
3151 (ndr_pull_flags_fn_t)ndr_pull_fsctl_net_iface_info);
3152 torture_assert_ndr_success(torture, ndr_ret,
3153 "ndr_pull_fsctl_net_iface_info");
3155 ndr_print_debug((ndr_print_fn_t)ndr_print_fsctl_net_iface_info,
3156 "Network Interface Info", &net_iface);
3158 talloc_free(tmp_ctx);
3159 return true;
3163 * Check whether all @fs_support_flags are set in the server's
3164 * RAW_QFS_ATTRIBUTE_INFORMATION FileSystemAttributes response.
3166 static NTSTATUS test_ioctl_fs_supported(struct torture_context *torture,
3167 struct smb2_tree *tree,
3168 TALLOC_CTX *mem_ctx,
3169 struct smb2_handle *fh,
3170 uint64_t fs_support_flags,
3171 bool *supported)
3173 NTSTATUS status;
3174 union smb_fsinfo info;
3176 ZERO_STRUCT(info);
3177 info.generic.level = RAW_QFS_ATTRIBUTE_INFORMATION;
3178 info.generic.handle = *fh;
3179 status = smb2_getinfo_fs(tree, tree, &info);
3180 if (!NT_STATUS_IS_OK(status)) {
3181 return status;
3184 if ((info.attribute_info.out.fs_attr & fs_support_flags)
3185 == fs_support_flags) {
3186 *supported = true;
3187 } else {
3188 *supported = false;
3190 return NT_STATUS_OK;
3193 static NTSTATUS test_ioctl_sparse_req(struct torture_context *torture,
3194 TALLOC_CTX *mem_ctx,
3195 struct smb2_tree *tree,
3196 struct smb2_handle fh,
3197 bool set)
3199 union smb_ioctl ioctl;
3200 NTSTATUS status;
3201 uint8_t set_sparse;
3203 ZERO_STRUCT(ioctl);
3204 ioctl.smb2.level = RAW_IOCTL_SMB2;
3205 ioctl.smb2.in.file.handle = fh;
3206 ioctl.smb2.in.function = FSCTL_SET_SPARSE;
3207 ioctl.smb2.in.max_response_size = 0;
3208 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3209 set_sparse = (set ? 0xFF : 0x0);
3210 ioctl.smb2.in.out.data = &set_sparse;
3211 ioctl.smb2.in.out.length = sizeof(set_sparse);
3213 status = smb2_ioctl(tree, mem_ctx, &ioctl.smb2);
3214 return status;
3217 static NTSTATUS test_sparse_get(struct torture_context *torture,
3218 TALLOC_CTX *mem_ctx,
3219 struct smb2_tree *tree,
3220 struct smb2_handle fh,
3221 bool *_is_sparse)
3223 union smb_fileinfo io;
3224 NTSTATUS status;
3226 ZERO_STRUCT(io);
3227 io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
3228 io.generic.in.file.handle = fh;
3229 status = smb2_getinfo_file(tree, mem_ctx, &io);
3230 if (!NT_STATUS_IS_OK(status)) {
3231 return status;
3233 *_is_sparse = !!(io.basic_info.out.attrib & FILE_ATTRIBUTE_SPARSE);
3235 return status;
3238 static bool test_ioctl_sparse_file_flag(struct torture_context *torture,
3239 struct smb2_tree *tree)
3241 struct smb2_handle fh;
3242 union smb_fileinfo io;
3243 NTSTATUS status;
3244 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3245 bool ok;
3246 bool is_sparse;
3248 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3249 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3250 FILE_ATTRIBUTE_NORMAL);
3251 torture_assert(torture, ok, "setup file");
3253 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3254 FILE_SUPPORTS_SPARSE_FILES, &ok);
3255 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3256 if (!ok) {
3257 smb2_util_close(tree, fh);
3258 torture_skip(torture, "Sparse files not supported\n");
3261 ZERO_STRUCT(io);
3262 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
3263 io.generic.in.file.handle = fh;
3264 status = smb2_getinfo_file(tree, tmp_ctx, &io);
3265 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
3267 torture_assert(torture,
3268 ((io.all_info2.out.attrib & FILE_ATTRIBUTE_SPARSE) == 0),
3269 "sparse attr before set");
3271 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
3272 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3274 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3275 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3276 torture_assert(torture, is_sparse, "no sparse attr after set");
3278 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false);
3279 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3281 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3282 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3283 torture_assert(torture, !is_sparse, "sparse attr after unset");
3285 smb2_util_close(tree, fh);
3286 talloc_free(tmp_ctx);
3287 return true;
3290 static bool test_ioctl_sparse_file_attr(struct torture_context *torture,
3291 struct smb2_tree *tree)
3293 struct smb2_handle fh;
3294 NTSTATUS status;
3295 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3296 bool ok;
3297 bool is_sparse;
3299 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3300 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3301 (FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_SPARSE));
3302 torture_assert(torture, ok, "setup file");
3304 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3305 FILE_SUPPORTS_SPARSE_FILES, &ok);
3306 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3307 if (!ok) {
3308 smb2_util_close(tree, fh);
3309 torture_skip(torture, "Sparse files not supported\n");
3312 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3313 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3314 torture_assert(torture, !is_sparse, "sparse attr on open");
3316 smb2_util_close(tree, fh);
3317 talloc_free(tmp_ctx);
3318 return true;
3321 static bool test_ioctl_sparse_dir_flag(struct torture_context *torture,
3322 struct smb2_tree *tree)
3324 struct smb2_handle dirh;
3325 NTSTATUS status;
3326 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3327 bool ok;
3329 smb2_deltree(tree, DNAME);
3330 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3331 DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL,
3332 FILE_ATTRIBUTE_DIRECTORY);
3333 torture_assert(torture, ok, "setup sparse directory");
3335 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &dirh,
3336 FILE_SUPPORTS_SPARSE_FILES, &ok);
3337 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3338 if (!ok) {
3339 smb2_util_close(tree, dirh);
3340 smb2_deltree(tree, DNAME);
3341 torture_skip(torture, "Sparse files not supported\n");
3344 /* set sparse dir should fail, check for 2k12 & 2k8 response */
3345 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, dirh, true);
3346 torture_assert_ntstatus_equal(torture, status,
3347 NT_STATUS_INVALID_PARAMETER,
3348 "dir FSCTL_SET_SPARSE status");
3350 smb2_util_close(tree, dirh);
3351 smb2_deltree(tree, DNAME);
3352 talloc_free(tmp_ctx);
3353 return true;
3357 * FSCTL_SET_SPARSE can be sent with (already tested) or without a SetSparse
3358 * buffer to indicate whether the flag should be set or cleared. When sent
3359 * without a buffer, it must be handled as if SetSparse=TRUE.
3361 static bool test_ioctl_sparse_set_nobuf(struct torture_context *torture,
3362 struct smb2_tree *tree)
3364 struct smb2_handle fh;
3365 union smb_ioctl ioctl;
3366 NTSTATUS status;
3367 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3368 bool ok;
3369 bool is_sparse;
3371 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3372 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3373 FILE_ATTRIBUTE_NORMAL);
3374 torture_assert(torture, ok, "setup file");
3376 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3377 FILE_SUPPORTS_SPARSE_FILES, &ok);
3378 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3379 if (!ok) {
3380 smb2_util_close(tree, fh);
3381 torture_skip(torture, "Sparse files not supported\n");
3384 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3385 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3386 torture_assert(torture, !is_sparse, "sparse attr before set");
3388 ZERO_STRUCT(ioctl);
3389 ioctl.smb2.level = RAW_IOCTL_SMB2;
3390 ioctl.smb2.in.file.handle = fh;
3391 ioctl.smb2.in.function = FSCTL_SET_SPARSE;
3392 ioctl.smb2.in.max_response_size = 0;
3393 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3394 /* ioctl.smb2.in.out is zeroed, no SetSparse buffer */
3396 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3397 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3399 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3400 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3401 torture_assert(torture, is_sparse, "no sparse attr after set");
3403 /* second non-SetSparse request shouldn't toggle sparse */
3404 ZERO_STRUCT(ioctl);
3405 ioctl.smb2.level = RAW_IOCTL_SMB2;
3406 ioctl.smb2.in.file.handle = fh;
3407 ioctl.smb2.in.function = FSCTL_SET_SPARSE;
3408 ioctl.smb2.in.max_response_size = 0;
3409 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3411 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3412 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3414 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3415 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3416 torture_assert(torture, is_sparse, "no sparse attr after 2nd set");
3418 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false);
3419 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3421 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3422 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3423 torture_assert(torture, !is_sparse, "sparse attr after unset");
3425 smb2_util_close(tree, fh);
3426 talloc_free(tmp_ctx);
3427 return true;
3430 static bool test_ioctl_sparse_set_oversize(struct torture_context *torture,
3431 struct smb2_tree *tree)
3433 struct smb2_handle fh;
3434 union smb_ioctl ioctl;
3435 NTSTATUS status;
3436 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3437 bool ok;
3438 bool is_sparse;
3439 uint8_t buf[100];
3441 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3442 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3443 FILE_ATTRIBUTE_NORMAL);
3444 torture_assert(torture, ok, "setup file");
3446 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3447 FILE_SUPPORTS_SPARSE_FILES, &ok);
3448 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3449 if (!ok) {
3450 smb2_util_close(tree, fh);
3451 torture_skip(torture, "Sparse files not supported\n");
3454 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3455 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3456 torture_assert(torture, !is_sparse, "sparse attr before set");
3458 ZERO_STRUCT(ioctl);
3459 ioctl.smb2.level = RAW_IOCTL_SMB2;
3460 ioctl.smb2.in.file.handle = fh;
3461 ioctl.smb2.in.function = FSCTL_SET_SPARSE;
3462 ioctl.smb2.in.max_response_size = 0;
3463 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3466 * Attach a request buffer larger than FILE_SET_SPARSE_BUFFER
3467 * Windows still successfully processes the request.
3469 ZERO_ARRAY(buf);
3470 buf[0] = 0xFF; /* attempt to set sparse */
3471 ioctl.smb2.in.out.data = buf;
3472 ioctl.smb2.in.out.length = ARRAY_SIZE(buf);
3474 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3475 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3477 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3478 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3479 torture_assert(torture, is_sparse, "no sparse attr after set");
3481 ZERO_STRUCT(ioctl);
3482 ioctl.smb2.level = RAW_IOCTL_SMB2;
3483 ioctl.smb2.in.file.handle = fh;
3484 ioctl.smb2.in.function = FSCTL_SET_SPARSE;
3485 ioctl.smb2.in.max_response_size = 0;
3486 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3488 ZERO_ARRAY(buf); /* clear sparse */
3489 ioctl.smb2.in.out.data = buf;
3490 ioctl.smb2.in.out.length = ARRAY_SIZE(buf);
3492 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3493 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3495 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3496 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3497 torture_assert(torture, !is_sparse, "sparse attr after clear");
3499 smb2_util_close(tree, fh);
3500 talloc_free(tmp_ctx);
3501 return true;
3504 static NTSTATUS test_ioctl_qar_req(struct torture_context *torture,
3505 TALLOC_CTX *mem_ctx,
3506 struct smb2_tree *tree,
3507 struct smb2_handle fh,
3508 int64_t req_off,
3509 int64_t req_len,
3510 struct file_alloced_range_buf **_rsp,
3511 uint64_t *_rsp_count)
3513 union smb_ioctl ioctl;
3514 NTSTATUS status;
3515 enum ndr_err_code ndr_ret;
3516 struct file_alloced_range_buf far_buf;
3517 struct file_alloced_range_buf *far_rsp = NULL;
3518 uint64_t far_count = 0;
3519 int i;
3520 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
3521 if (tmp_ctx == NULL) {
3522 return NT_STATUS_NO_MEMORY;
3525 ZERO_STRUCT(ioctl);
3526 ioctl.smb2.level = RAW_IOCTL_SMB2;
3527 ioctl.smb2.in.file.handle = fh;
3528 ioctl.smb2.in.function = FSCTL_QUERY_ALLOCATED_RANGES;
3529 ioctl.smb2.in.max_response_size = 1024;
3530 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3532 far_buf.file_off = req_off;
3533 far_buf.len = req_len;
3535 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
3536 &far_buf,
3537 (ndr_push_flags_fn_t)ndr_push_file_alloced_range_buf);
3538 if (ndr_ret != NDR_ERR_SUCCESS) {
3539 status = NT_STATUS_UNSUCCESSFUL;
3540 goto err_out;
3543 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3544 if (!NT_STATUS_IS_OK(status)) {
3545 goto err_out;
3548 if (ioctl.smb2.out.out.length == 0) {
3549 goto done;
3552 if ((ioctl.smb2.out.out.length % sizeof(far_buf)) != 0) {
3553 torture_comment(torture, "invalid qry_alloced rsp len: %zd:",
3554 ioctl.smb2.out.out.length);
3555 status = NT_STATUS_INVALID_VIEW_SIZE;
3556 goto err_out;
3559 far_count = (ioctl.smb2.out.out.length / sizeof(far_buf));
3560 far_rsp = talloc_array(mem_ctx, struct file_alloced_range_buf,
3561 far_count);
3562 if (far_rsp == NULL) {
3563 status = NT_STATUS_NO_MEMORY;
3564 goto err_out;
3567 for (i = 0; i < far_count; i++) {
3568 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
3569 &far_rsp[i],
3570 (ndr_pull_flags_fn_t)ndr_pull_file_alloced_range_buf);
3571 if (ndr_ret != NDR_ERR_SUCCESS) {
3572 status = NT_STATUS_UNSUCCESSFUL;
3573 goto err_out;
3575 /* move to next buffer */
3576 ioctl.smb2.out.out.data += sizeof(far_buf);
3577 ioctl.smb2.out.out.length -= sizeof(far_buf);
3580 done:
3581 *_rsp = far_rsp;
3582 *_rsp_count = far_count;
3583 status = NT_STATUS_OK;
3584 err_out:
3585 talloc_free(tmp_ctx);
3586 return status;
3589 static bool test_ioctl_sparse_qar(struct torture_context *torture,
3590 struct smb2_tree *tree)
3592 struct smb2_handle fh;
3593 NTSTATUS status;
3594 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3595 bool ok;
3596 bool is_sparse;
3597 struct file_alloced_range_buf *far_rsp = NULL;
3598 uint64_t far_count = 0;
3600 /* zero length file, shouldn't have any ranges */
3601 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3602 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3603 FILE_ATTRIBUTE_NORMAL);
3604 torture_assert(torture, ok, "setup file");
3606 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3607 FILE_SUPPORTS_SPARSE_FILES, &ok);
3608 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3609 if (!ok) {
3610 smb2_util_close(tree, fh);
3611 torture_skip(torture, "Sparse files not supported\n");
3614 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3615 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3616 torture_assert(torture, !is_sparse, "sparse attr before set");
3618 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3619 0, /* off */
3620 0, /* len */
3621 &far_rsp,
3622 &far_count);
3623 torture_assert_ntstatus_ok(torture, status,
3624 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3625 torture_assert_u64_equal(torture, far_count, 0,
3626 "unexpected response len");
3628 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3629 0, /* off */
3630 1024, /* len */
3631 &far_rsp,
3632 &far_count);
3633 torture_assert_ntstatus_ok(torture, status,
3634 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3635 torture_assert_u64_equal(torture, far_count, 0,
3636 "unexpected response len");
3638 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
3639 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3641 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3642 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3643 torture_assert(torture, is_sparse, "no sparse attr after set");
3645 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3646 0, /* off */
3647 1024, /* len */
3648 &far_rsp,
3649 &far_count);
3650 torture_assert_ntstatus_ok(torture, status,
3651 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3652 torture_assert_u64_equal(torture, far_count, 0,
3653 "unexpected response len");
3655 /* write into the (now) sparse file at 4k offset */
3656 ok = write_pattern(torture, tree, tmp_ctx, fh,
3657 4096, /* off */
3658 1024, /* len */
3659 4096); /* pattern offset */
3660 torture_assert(torture, ok, "write pattern");
3663 * Query range before write off. Whether it's allocated or not is FS
3664 * dependent. NTFS deallocates chunks in 64K increments, but others
3665 * (e.g. XFS, Btrfs, etc.) may deallocate 4K chunks.
3667 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3668 0, /* off */
3669 4096, /* len */
3670 &far_rsp,
3671 &far_count);
3672 torture_assert_ntstatus_ok(torture, status,
3673 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3674 if (far_count == 0) {
3675 torture_comment(torture, "FS deallocated 4K chunk\n");
3676 } else {
3677 /* expect fully allocated */
3678 torture_assert_u64_equal(torture, far_count, 1,
3679 "unexpected response len");
3680 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0, "far offset");
3681 torture_assert_u64_equal(torture, far_rsp[0].len, 4096, "far len");
3685 * Query range before and past write, it should be allocated up to the
3686 * end of the write.
3688 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3689 0, /* off */
3690 8192, /* len */
3691 &far_rsp,
3692 &far_count);
3693 torture_assert_ntstatus_ok(torture, status,
3694 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3695 torture_assert_u64_equal(torture, far_count, 1,
3696 "unexpected response len");
3697 /* FS dependent */
3698 if (far_rsp[0].file_off == 4096) {
3699 /* 4K chunk unallocated */
3700 torture_assert_u64_equal(torture, far_rsp[0].file_off, 4096, "far offset");
3701 torture_assert_u64_equal(torture, far_rsp[0].len, 1024, "far len");
3702 } else {
3703 /* expect fully allocated */
3704 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0, "far offset");
3705 torture_assert_u64_equal(torture, far_rsp[0].len, 5120, "far len");
3708 smb2_util_close(tree, fh);
3709 talloc_free(tmp_ctx);
3710 return true;
3713 static bool test_ioctl_sparse_qar_malformed(struct torture_context *torture,
3714 struct smb2_tree *tree)
3716 struct smb2_handle fh;
3717 union smb_ioctl ioctl;
3718 struct file_alloced_range_buf far_buf;
3719 NTSTATUS status;
3720 enum ndr_err_code ndr_ret;
3721 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3722 bool ok;
3723 size_t old_len;
3725 /* zero length file, shouldn't have any ranges */
3726 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3727 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3728 FILE_ATTRIBUTE_NORMAL);
3729 torture_assert(torture, ok, "setup file");
3731 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3732 FILE_SUPPORTS_SPARSE_FILES, &ok);
3733 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3734 if (!ok) {
3735 smb2_util_close(tree, fh);
3736 torture_skip(torture, "Sparse files not supported\n");
3739 /* no allocated ranges, no space for range response, should pass */
3740 ZERO_STRUCT(ioctl);
3741 ioctl.smb2.level = RAW_IOCTL_SMB2;
3742 ioctl.smb2.in.file.handle = fh;
3743 ioctl.smb2.in.function = FSCTL_QUERY_ALLOCATED_RANGES;
3744 ioctl.smb2.in.max_response_size = 0;
3745 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3747 far_buf.file_off = 0;
3748 far_buf.len = 1024;
3749 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
3750 &far_buf,
3751 (ndr_push_flags_fn_t)ndr_push_file_alloced_range_buf);
3752 torture_assert_ndr_success(torture, ndr_ret, "push far ndr buf");
3754 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3755 torture_assert_ntstatus_ok(torture, status, "FSCTL_QUERY_ALLOCATED_RANGES");
3757 /* write into the file at 4k offset */
3758 ok = write_pattern(torture, tree, tmp_ctx, fh,
3759 0, /* off */
3760 1024, /* len */
3761 0); /* pattern offset */
3762 torture_assert(torture, ok, "write pattern");
3764 /* allocated range, no space for range response, should fail */
3765 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3766 torture_assert_ntstatus_equal(torture, status,
3767 NT_STATUS_BUFFER_TOO_SMALL, "qar no space");
3769 /* oversize (2x) file_alloced_range_buf in request, should pass */
3770 ioctl.smb2.in.max_response_size = 1024;
3771 old_len = ioctl.smb2.in.out.length;
3772 ok = data_blob_realloc(tmp_ctx, &ioctl.smb2.in.out,
3773 (ioctl.smb2.in.out.length * 2));
3774 torture_assert(torture, ok, "2x data buffer");
3775 memcpy(ioctl.smb2.in.out.data + old_len, ioctl.smb2.in.out.data,
3776 old_len);
3777 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3778 torture_assert_ntstatus_ok(torture, status, "qar too big");
3780 /* no file_alloced_range_buf in request, should fail */
3781 data_blob_free(&ioctl.smb2.in.out);
3782 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3783 torture_assert_ntstatus_equal(torture, status,
3784 NT_STATUS_INVALID_PARAMETER, "qar empty");
3786 return true;
3790 * 2.3.57 FSCTL_SET_ZERO_DATA Request
3792 * How an implementation zeros data within a file is implementation-dependent.
3793 * A file system MAY choose to deallocate regions of disk space that have been
3794 * zeroed.<50>
3795 * <50>
3796 * ... NTFS might deallocate disk space in the file if the file is stored on an
3797 * NTFS volume, and the file is sparse or compressed. It will free any allocated
3798 * space in chunks of 64 kilobytes that begin at an offset that is a multiple of
3799 * 64 kilobytes. Other bytes in the file (prior to the first freed 64-kilobyte
3800 * chunk and after the last freed 64-kilobyte chunk) will be zeroed but not
3801 * deallocated.
3803 static NTSTATUS test_ioctl_zdata_req(struct torture_context *torture,
3804 TALLOC_CTX *mem_ctx,
3805 struct smb2_tree *tree,
3806 struct smb2_handle fh,
3807 int64_t off,
3808 int64_t beyond_final_zero)
3810 union smb_ioctl ioctl;
3811 NTSTATUS status;
3812 enum ndr_err_code ndr_ret;
3813 struct file_zero_data_info zdata_info;
3814 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
3815 if (tmp_ctx == NULL) {
3816 return NT_STATUS_NO_MEMORY;
3819 ZERO_STRUCT(ioctl);
3820 ioctl.smb2.level = RAW_IOCTL_SMB2;
3821 ioctl.smb2.in.file.handle = fh;
3822 ioctl.smb2.in.function = FSCTL_SET_ZERO_DATA;
3823 ioctl.smb2.in.max_response_size = 0;
3824 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3826 zdata_info.file_off = off;
3827 zdata_info.beyond_final_zero = beyond_final_zero;
3829 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
3830 &zdata_info,
3831 (ndr_push_flags_fn_t)ndr_push_file_zero_data_info);
3832 if (ndr_ret != NDR_ERR_SUCCESS) {
3833 status = NT_STATUS_UNSUCCESSFUL;
3834 goto err_out;
3837 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3838 if (!NT_STATUS_IS_OK(status)) {
3839 goto err_out;
3842 status = NT_STATUS_OK;
3843 err_out:
3844 talloc_free(tmp_ctx);
3845 return status;
3848 static bool test_ioctl_sparse_punch(struct torture_context *torture,
3849 struct smb2_tree *tree)
3851 struct smb2_handle fh;
3852 NTSTATUS status;
3853 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3854 bool ok;
3855 bool is_sparse;
3856 struct file_alloced_range_buf *far_rsp = NULL;
3857 uint64_t far_count = 0;
3859 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3860 FNAME, &fh, 4096, SEC_RIGHTS_FILE_ALL,
3861 FILE_ATTRIBUTE_NORMAL);
3862 torture_assert(torture, ok, "setup file");
3864 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3865 FILE_SUPPORTS_SPARSE_FILES, &ok);
3866 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3867 if (!ok) {
3868 smb2_util_close(tree, fh);
3869 torture_skip(torture, "Sparse files not supported\n");
3872 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3873 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3874 torture_assert(torture, !is_sparse, "sparse attr before set");
3876 /* zero (hole-punch) the data, without sparse flag */
3877 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3878 0, /* off */
3879 4096); /* beyond_final_zero */
3880 torture_assert_ntstatus_ok(torture, status, "zero_data");
3882 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3883 0, /* off */
3884 4096, /* len */
3885 &far_rsp,
3886 &far_count);
3887 torture_assert_ntstatus_ok(torture, status,
3888 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3889 torture_assert_u64_equal(torture, far_count, 1,
3890 "unexpected response len");
3892 /* expect fully allocated */
3893 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3894 "unexpected far off");
3895 torture_assert_u64_equal(torture, far_rsp[0].len, 4096,
3896 "unexpected far len");
3897 /* check that the data is now zeroed */
3898 ok = check_zero(torture, tree, tmp_ctx, fh, 0, 4096);
3899 torture_assert(torture, ok, "non-sparse zeroed range");
3901 /* set sparse */
3902 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
3903 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3905 /* still fully allocated on NTFS, see note below for Samba */
3906 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3907 0, /* off */
3908 4096, /* len */
3909 &far_rsp,
3910 &far_count);
3911 torture_assert_ntstatus_ok(torture, status,
3912 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3914 * FS specific: Samba uses PUNCH_HOLE to zero the range, and
3915 * subsequently uses fallocate() to allocate the punched range if the
3916 * file is marked non-sparse and "strict allocate" is enabled. In both
3917 * cases, the zeroed range will not be detected by SEEK_DATA, so the
3918 * range won't be present in QAR responses until the file is marked
3919 * non-sparse again.
3921 if (far_count == 0) {
3922 torture_comment(torture, "non-sparse zeroed range disappeared "
3923 "after marking sparse\n");
3924 } else {
3925 /* NTFS: range remains fully allocated */
3926 torture_assert_u64_equal(torture, far_count, 1,
3927 "unexpected response len");
3928 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3929 "unexpected far off");
3930 torture_assert_u64_equal(torture, far_rsp[0].len, 4096,
3931 "unexpected far len");
3934 /* zero (hole-punch) the data, _with_ sparse flag */
3935 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3936 0, /* off */
3937 4096); /* beyond_final_zero */
3938 torture_assert_ntstatus_ok(torture, status, "zero_data");
3940 /* the range should no longer be alloced */
3941 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3942 0, /* off */
3943 4096, /* len */
3944 &far_rsp,
3945 &far_count);
3946 torture_assert_ntstatus_ok(torture, status,
3947 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3948 torture_assert_u64_equal(torture, far_count, 0,
3949 "unexpected response len");
3951 ok = check_zero(torture, tree, tmp_ctx, fh, 0, 4096);
3952 torture_assert(torture, ok, "sparse zeroed range");
3954 /* remove sparse flag, this should "unsparse" the zeroed range */
3955 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false);
3956 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3958 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3959 0, /* off */
3960 4096, /* len */
3961 &far_rsp,
3962 &far_count);
3963 torture_assert_ntstatus_ok(torture, status,
3964 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3965 torture_assert_u64_equal(torture, far_count, 1,
3966 "unexpected response len");
3967 /* expect fully allocated */
3968 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3969 "unexpected far off");
3970 torture_assert_u64_equal(torture, far_rsp[0].len, 4096,
3971 "unexpected far len");
3973 ok = check_zero(torture, tree, tmp_ctx, fh, 0, 4096);
3974 torture_assert(torture, ok, "sparse zeroed range");
3976 smb2_util_close(tree, fh);
3977 talloc_free(tmp_ctx);
3978 return true;
3982 * Find the point at which a zeroed range in a sparse file is deallocated by the
3983 * underlying filesystem. NTFS on Windows Server 2012 deallocates chunks in 64k
3984 * increments. Also check whether zeroed neighbours are merged for deallocation.
3986 static bool test_ioctl_sparse_hole_dealloc(struct torture_context *torture,
3987 struct smb2_tree *tree)
3989 struct smb2_handle fh;
3990 NTSTATUS status;
3991 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3992 bool ok;
3993 uint64_t file_size;
3994 uint64_t hlen;
3995 uint64_t dealloc_chunk_len = 0;
3996 struct file_alloced_range_buf *far_rsp = NULL;
3997 uint64_t far_count = 0;
3999 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4000 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
4001 FILE_ATTRIBUTE_NORMAL);
4002 torture_assert(torture, ok, "setup file 1");
4004 /* check for FS sparse file */
4005 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
4006 FILE_SUPPORTS_SPARSE_FILES, &ok);
4007 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4008 if (!ok) {
4009 smb2_util_close(tree, fh);
4010 torture_skip(torture, "Sparse files not supported\n");
4013 /* set sparse */
4014 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4015 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4017 file_size = 1024 * 1024;
4019 ok = write_pattern(torture, tree, tmp_ctx, fh,
4020 0, /* off */
4021 file_size, /* len */
4022 0); /* pattern offset */
4023 torture_assert(torture, ok, "write pattern");
4025 /* check allocated ranges, should be fully allocated */
4026 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4027 0, /* off */
4028 file_size, /* len */
4029 &far_rsp,
4030 &far_count);
4031 torture_assert_ntstatus_ok(torture, status,
4032 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4033 torture_assert_u64_equal(torture, far_count, 1,
4034 "unexpected response len");
4035 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4036 "unexpected far off");
4037 torture_assert_u64_equal(torture, far_rsp[0].len, file_size,
4038 "unexpected far len");
4040 /* punch holes in sizes of 1k increments */
4041 for (hlen = 0; hlen <= file_size; hlen += 4096) {
4043 /* punch a hole from zero to the current increment */
4044 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4045 0, /* off */
4046 hlen); /* beyond_final_zero */
4047 torture_assert_ntstatus_ok(torture, status, "zero_data");
4049 /* ensure hole is zeroed, and pattern is consistent */
4050 ok = check_zero(torture, tree, tmp_ctx, fh, 0, hlen);
4051 torture_assert(torture, ok, "sparse zeroed range");
4053 ok = check_pattern(torture, tree, tmp_ctx, fh, hlen,
4054 file_size - hlen, hlen);
4055 torture_assert(torture, ok, "allocated pattern range");
4057 /* Check allocated ranges, hole might have been deallocated */
4058 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4059 0, /* off */
4060 file_size, /* len */
4061 &far_rsp,
4062 &far_count);
4063 torture_assert_ntstatus_ok(torture, status,
4064 "FSCTL_QUERY_ALLOCATED_RANGES");
4065 if ((hlen == file_size) && (far_count == 0)) {
4066 /* hole covered entire file, deallocation occurred */
4067 dealloc_chunk_len = file_size;
4068 break;
4071 torture_assert_u64_equal(torture, far_count, 1,
4072 "unexpected response len");
4073 if (far_rsp[0].file_off != 0) {
4075 * We now know the hole punch length needed to trigger a
4076 * deallocation on this FS...
4078 dealloc_chunk_len = hlen;
4079 torture_comment(torture, "hole punch %ju@0 resulted in "
4080 "deallocation of %ju@0\n",
4081 (uintmax_t)hlen,
4082 (uintmax_t)far_rsp[0].file_off);
4083 torture_assert_u64_equal(torture,
4084 file_size - far_rsp[0].len,
4085 far_rsp[0].file_off,
4086 "invalid alloced range");
4087 break;
4091 if (dealloc_chunk_len == 0) {
4092 torture_comment(torture, "strange, this FS never deallocates"
4093 "zeroed ranges in sparse files\n");
4094 return true; /* FS specific, not a failure */
4098 * Check whether deallocation occurs when the (now known)
4099 * deallocation chunk size is punched via two ZERO_DATA requests.
4100 * I.e. Does the FS merge the two ranges and deallocate the chunk?
4101 * NTFS on Windows Server 2012 does not.
4103 ok = write_pattern(torture, tree, tmp_ctx, fh,
4104 0, /* off */
4105 file_size, /* len */
4106 0); /* pattern offset */
4107 torture_assert(torture, ok, "write pattern");
4109 /* divide dealloc chunk size by two, to use as punch length */
4110 hlen = dealloc_chunk_len >> 1;
4113 * /half of dealloc chunk size 1M\
4114 * | |
4115 * /offset 0 | /dealloc chunk size |
4116 * |------------------ |-------------------|-------------------|
4117 * | zeroed, 1st punch | zeroed, 2nd punch | existing pattern |
4119 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4120 0, /* off */
4121 hlen); /* beyond final zero */
4122 torture_assert_ntstatus_ok(torture, status, "zero_data");
4124 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4125 hlen, /* off */
4126 dealloc_chunk_len); /* beyond final */
4127 torture_assert_ntstatus_ok(torture, status, "zero_data");
4129 /* ensure holes are zeroed, and pattern is consistent */
4130 ok = check_zero(torture, tree, tmp_ctx, fh, 0, dealloc_chunk_len);
4131 torture_assert(torture, ok, "sparse zeroed range");
4133 ok = check_pattern(torture, tree, tmp_ctx, fh, dealloc_chunk_len,
4134 file_size - dealloc_chunk_len, dealloc_chunk_len);
4135 torture_assert(torture, ok, "allocated pattern range");
4137 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4138 0, /* off */
4139 file_size, /* len */
4140 &far_rsp,
4141 &far_count);
4142 torture_assert_ntstatus_ok(torture, status,
4143 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4145 if ((far_count == 0) && (dealloc_chunk_len == file_size)) {
4146 torture_comment(torture, "holes merged for deallocation of "
4147 "full file\n");
4148 return true;
4150 torture_assert_u64_equal(torture, far_count, 1,
4151 "unexpected response len");
4152 if (far_rsp[0].file_off == dealloc_chunk_len) {
4153 torture_comment(torture, "holes merged for deallocation of "
4154 "%ju chunk\n", (uintmax_t)dealloc_chunk_len);
4155 torture_assert_u64_equal(torture,
4156 file_size - far_rsp[0].len,
4157 far_rsp[0].file_off,
4158 "invalid alloced range");
4159 } else {
4160 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4161 "unexpected deallocation");
4162 torture_comment(torture, "holes not merged for deallocation\n");
4165 smb2_util_close(tree, fh);
4168 * Check whether an unwritten range is allocated when a sparse file is
4169 * written to at an offset past the dealloc chunk size:
4171 * /dealloc chunk size
4172 * /offset 0 |
4173 * |------------------ |-------------------|
4174 * | unwritten | pattern |
4176 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4177 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
4178 FILE_ATTRIBUTE_NORMAL);
4179 torture_assert(torture, ok, "setup file 1");
4181 /* set sparse */
4182 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4183 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4185 ok = write_pattern(torture, tree, tmp_ctx, fh,
4186 dealloc_chunk_len, /* off */
4187 1024, /* len */
4188 dealloc_chunk_len); /* pattern offset */
4189 torture_assert(torture, ok, "write pattern");
4191 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4192 0, /* off */
4193 dealloc_chunk_len + 1024, /* len */
4194 &far_rsp,
4195 &far_count);
4196 torture_assert_ntstatus_ok(torture, status,
4197 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4198 torture_assert_u64_equal(torture, far_count, 1,
4199 "unexpected response len");
4200 if (far_rsp[0].file_off == 0) {
4201 torture_assert_u64_equal(torture, far_rsp[0].len,
4202 dealloc_chunk_len + 1024,
4203 "unexpected far len");
4204 torture_comment(torture, "unwritten range fully allocated\n");
4205 } else {
4206 torture_assert_u64_equal(torture, far_rsp[0].file_off, dealloc_chunk_len,
4207 "unexpected deallocation");
4208 torture_assert_u64_equal(torture, far_rsp[0].len, 1024,
4209 "unexpected far len");
4210 torture_comment(torture, "unwritten range not allocated\n");
4213 ok = check_zero(torture, tree, tmp_ctx, fh, 0, dealloc_chunk_len);
4214 torture_assert(torture, ok, "sparse zeroed range");
4216 ok = check_pattern(torture, tree, tmp_ctx, fh, dealloc_chunk_len,
4217 1024, dealloc_chunk_len);
4218 torture_assert(torture, ok, "allocated pattern range");
4220 /* unsparse, should now be fully allocated */
4221 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false);
4222 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4224 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4225 0, /* off */
4226 dealloc_chunk_len + 1024, /* len */
4227 &far_rsp,
4228 &far_count);
4229 torture_assert_ntstatus_ok(torture, status,
4230 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4231 torture_assert_u64_equal(torture, far_count, 1,
4232 "unexpected response len");
4233 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4234 "unexpected deallocation");
4235 torture_assert_u64_equal(torture, far_rsp[0].len,
4236 dealloc_chunk_len + 1024,
4237 "unexpected far len");
4239 smb2_util_close(tree, fh);
4240 talloc_free(tmp_ctx);
4241 return true;
4244 /* check whether a file with compression and sparse attrs can be deallocated */
4245 static bool test_ioctl_sparse_compressed(struct torture_context *torture,
4246 struct smb2_tree *tree)
4248 struct smb2_handle fh;
4249 NTSTATUS status;
4250 TALLOC_CTX *tmp_ctx = talloc_new(tree);
4251 bool ok;
4252 uint64_t file_size = 1024 * 1024;
4253 struct file_alloced_range_buf *far_rsp = NULL;
4254 uint64_t far_count = 0;
4256 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4257 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
4258 FILE_ATTRIBUTE_NORMAL);
4259 torture_assert(torture, ok, "setup file 1");
4261 /* check for FS sparse file and compression support */
4262 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
4263 FILE_SUPPORTS_SPARSE_FILES, &ok);
4264 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4265 if (!ok) {
4266 smb2_util_close(tree, fh);
4267 torture_skip(torture, "Sparse files not supported\n");
4270 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
4271 &ok);
4272 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4273 if (!ok) {
4274 smb2_util_close(tree, fh);
4275 torture_skip(torture, "FS compression not supported\n");
4278 /* set compression and write some data */
4279 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
4280 COMPRESSION_FORMAT_DEFAULT);
4281 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
4283 ok = write_pattern(torture, tree, tmp_ctx, fh,
4284 0, /* off */
4285 file_size, /* len */
4286 0); /* pattern offset */
4287 torture_assert(torture, ok, "write pattern");
4289 /* set sparse - now sparse and compressed */
4290 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4291 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4293 /* check allocated ranges, should be fully alloced */
4294 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4295 0, /* off */
4296 file_size, /* len */
4297 &far_rsp,
4298 &far_count);
4299 torture_assert_ntstatus_ok(torture, status,
4300 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4301 torture_assert_u64_equal(torture, far_count, 1,
4302 "unexpected response len");
4303 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4304 "unexpected far off");
4305 torture_assert_u64_equal(torture, far_rsp[0].len, file_size,
4306 "unexpected far len");
4308 /* zero (hole-punch) all data, with sparse and compressed attrs */
4309 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4310 0, /* off */
4311 file_size); /* beyond_final_zero */
4312 torture_assert_ntstatus_ok(torture, status, "zero_data");
4315 * Windows Server 2012 still deallocates a zeroed range when a sparse
4316 * file carries the compression attribute.
4318 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4319 0, /* off */
4320 file_size, /* len */
4321 &far_rsp,
4322 &far_count);
4323 torture_assert_ntstatus_ok(torture, status,
4324 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4325 if (far_count == 0) {
4326 torture_comment(torture, "sparse & compressed file "
4327 "deallocated after hole-punch\n");
4328 } else {
4329 torture_assert_u64_equal(torture, far_count, 1,
4330 "unexpected response len");
4331 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4332 "unexpected far off");
4333 torture_assert_u64_equal(torture, far_rsp[0].len, file_size,
4334 "unexpected far len");
4335 torture_comment(torture, "sparse & compressed file fully "
4336 "allocated after hole-punch\n");
4339 smb2_util_close(tree, fh);
4340 talloc_free(tmp_ctx);
4341 return true;
4345 * Create a sparse file, then attempt to copy unallocated and allocated ranges
4346 * into a target file using FSCTL_SRV_COPYCHUNK.
4348 static bool test_ioctl_sparse_copy_chunk(struct torture_context *torture,
4349 struct smb2_tree *tree)
4351 struct smb2_handle src_h;
4352 struct smb2_handle dest_h;
4353 NTSTATUS status;
4354 TALLOC_CTX *tmp_ctx = talloc_new(tree);
4355 bool ok;
4356 uint64_t dealloc_chunk_len = 64 * 1024; /* Windows 2012 */
4357 struct file_alloced_range_buf *far_rsp = NULL;
4358 uint64_t far_count = 0;
4359 union smb_ioctl ioctl;
4360 struct srv_copychunk_copy cc_copy;
4361 struct srv_copychunk_rsp cc_rsp;
4362 enum ndr_err_code ndr_ret;
4364 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4365 FNAME, &src_h, 0, SEC_RIGHTS_FILE_ALL,
4366 FILE_ATTRIBUTE_NORMAL);
4367 torture_assert(torture, ok, "setup file");
4369 /* check for FS sparse file support */
4370 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &src_h,
4371 FILE_SUPPORTS_SPARSE_FILES, &ok);
4372 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4373 smb2_util_close(tree, src_h);
4374 if (!ok) {
4375 torture_skip(torture, "Sparse files not supported\n");
4378 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
4379 1, /* chunks */
4380 FNAME,
4381 &src_h, 0, /* src file */
4382 SEC_RIGHTS_FILE_ALL,
4383 FNAME2,
4384 &dest_h, 0, /* dest file */
4385 SEC_RIGHTS_FILE_ALL,
4386 &cc_copy,
4387 &ioctl);
4388 torture_assert(torture, ok, "setup copy chunk error");
4390 /* set sparse */
4391 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, src_h, true);
4392 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4394 /* start after dealloc_chunk_len, to create an unwritten sparse range */
4395 ok = write_pattern(torture, tree, tmp_ctx, src_h,
4396 dealloc_chunk_len, /* off */
4397 1024, /* len */
4398 dealloc_chunk_len); /* pattern offset */
4399 torture_assert(torture, ok, "write pattern");
4401 /* Skip test if 64k chunk is allocated - FS specific */
4402 status = test_ioctl_qar_req(torture, tmp_ctx, tree, src_h,
4403 0, /* off */
4404 dealloc_chunk_len + 1024, /* len */
4405 &far_rsp,
4406 &far_count);
4407 torture_assert_ntstatus_ok(torture, status,
4408 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4409 torture_assert_u64_equal(torture, far_count, 1,
4410 "unexpected response len");
4411 if (far_rsp[0].file_off == 0) {
4412 torture_skip(torture, "unwritten range fully allocated\n");
4415 torture_assert_u64_equal(torture, far_rsp[0].file_off, dealloc_chunk_len,
4416 "unexpected allocation");
4417 torture_assert_u64_equal(torture, far_rsp[0].len, 1024,
4418 "unexpected far len");
4420 /* copy-chunk unallocated + written ranges into non-sparse dest */
4422 cc_copy.chunks[0].source_off = 0;
4423 cc_copy.chunks[0].target_off = 0;
4424 cc_copy.chunks[0].length = dealloc_chunk_len + 1024;
4426 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
4427 &cc_copy,
4428 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
4429 torture_assert_ndr_success(torture, ndr_ret,
4430 "ndr_push_srv_copychunk_copy");
4432 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
4433 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
4435 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
4436 &cc_rsp,
4437 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
4438 torture_assert_ndr_success(torture, ndr_ret,
4439 "ndr_pull_srv_copychunk_rsp");
4441 ok = check_copy_chunk_rsp(torture, &cc_rsp,
4442 1, /* chunks written */
4443 0, /* chunk bytes unsuccessfully written */
4444 dealloc_chunk_len + 1024); /* bytes written */
4445 torture_assert(torture, ok, "bad copy chunk response data");
4447 ok = check_zero(torture, tree, tmp_ctx, dest_h, 0, dealloc_chunk_len);
4448 torture_assert(torture, ok, "sparse zeroed range");
4450 ok = check_pattern(torture, tree, tmp_ctx, dest_h, dealloc_chunk_len,
4451 1024, dealloc_chunk_len);
4452 torture_assert(torture, ok, "copychunked range");
4454 /* copied range should be allocated in non-sparse dest */
4455 status = test_ioctl_qar_req(torture, tmp_ctx, tree, dest_h,
4456 0, /* off */
4457 dealloc_chunk_len + 1024, /* len */
4458 &far_rsp,
4459 &far_count);
4460 torture_assert_ntstatus_ok(torture, status,
4461 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4462 torture_assert_u64_equal(torture, far_count, 1,
4463 "unexpected response len");
4464 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4465 "unexpected allocation");
4466 torture_assert_u64_equal(torture, far_rsp[0].len,
4467 dealloc_chunk_len + 1024,
4468 "unexpected far len");
4470 /* set dest as sparse */
4471 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, dest_h, true);
4472 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4474 /* zero (hole-punch) all data */
4475 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, dest_h,
4476 0, /* off */
4477 dealloc_chunk_len + 1024);
4478 torture_assert_ntstatus_ok(torture, status, "zero_data");
4480 /* zeroed range might be deallocated */
4481 status = test_ioctl_qar_req(torture, tmp_ctx, tree, dest_h,
4482 0, /* off */
4483 dealloc_chunk_len + 1024, /* len */
4484 &far_rsp,
4485 &far_count);
4486 torture_assert_ntstatus_ok(torture, status,
4487 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4488 if (far_count == 0) {
4489 /* FS specific (e.g. NTFS) */
4490 torture_comment(torture, "FS deallocates file on full-range "
4491 "punch\n");
4492 } else {
4493 /* FS specific (e.g. EXT4) */
4494 torture_comment(torture, "FS doesn't deallocate file on "
4495 "full-range punch\n");
4497 ok = check_zero(torture, tree, tmp_ctx, dest_h, 0,
4498 dealloc_chunk_len + 1024);
4499 torture_assert(torture, ok, "punched zeroed range");
4501 /* copy-chunk again, this time with sparse dest */
4502 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
4503 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
4505 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
4506 &cc_rsp,
4507 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
4508 torture_assert_ndr_success(torture, ndr_ret,
4509 "ndr_pull_srv_copychunk_rsp");
4511 ok = check_copy_chunk_rsp(torture, &cc_rsp,
4512 1, /* chunks written */
4513 0, /* chunk bytes unsuccessfully written */
4514 dealloc_chunk_len + 1024); /* bytes written */
4515 torture_assert(torture, ok, "bad copy chunk response data");
4517 ok = check_zero(torture, tree, tmp_ctx, dest_h, 0, dealloc_chunk_len);
4518 torture_assert(torture, ok, "sparse zeroed range");
4520 ok = check_pattern(torture, tree, tmp_ctx, dest_h, dealloc_chunk_len,
4521 1024, dealloc_chunk_len);
4522 torture_assert(torture, ok, "copychunked range");
4524 /* copied range may be allocated in sparse dest */
4525 status = test_ioctl_qar_req(torture, tmp_ctx, tree, dest_h,
4526 0, /* off */
4527 dealloc_chunk_len + 1024, /* len */
4528 &far_rsp,
4529 &far_count);
4530 torture_assert_ntstatus_ok(torture, status,
4531 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4532 torture_assert_u64_equal(torture, far_count, 1,
4533 "unexpected response len");
4535 * FS specific: sparse region may be unallocated in dest if copy-chunk
4536 * is handled in a sparse preserving way - E.g. vfs_btrfs
4537 * with BTRFS_IOC_CLONE_RANGE.
4539 if (far_rsp[0].file_off == dealloc_chunk_len) {
4540 torture_comment(torture, "copy-chunk sparse range preserved\n");
4541 torture_assert_u64_equal(torture, far_rsp[0].len, 1024,
4542 "unexpected far len");
4543 } else {
4544 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4545 "unexpected allocation");
4546 torture_assert_u64_equal(torture, far_rsp[0].len,
4547 dealloc_chunk_len + 1024,
4548 "unexpected far len");
4551 smb2_util_close(tree, src_h);
4552 smb2_util_close(tree, dest_h);
4553 talloc_free(tmp_ctx);
4554 return true;
4557 static bool test_ioctl_sparse_punch_invalid(struct torture_context *torture,
4558 struct smb2_tree *tree)
4560 struct smb2_handle fh;
4561 NTSTATUS status;
4562 TALLOC_CTX *tmp_ctx = talloc_new(tree);
4563 bool ok;
4564 bool is_sparse;
4565 int i;
4567 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4568 FNAME, &fh, 4096, SEC_RIGHTS_FILE_ALL,
4569 FILE_ATTRIBUTE_NORMAL);
4570 torture_assert(torture, ok, "setup file");
4572 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
4573 FILE_SUPPORTS_SPARSE_FILES, &ok);
4574 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4575 if (!ok) {
4576 smb2_util_close(tree, fh);
4577 torture_skip(torture, "Sparse files not supported\n");
4580 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4581 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4582 torture_assert(torture, !is_sparse, "sparse attr before set");
4584 /* loop twice, without and with sparse attrib */
4585 for (i = 0; i <= 1; i++) {
4586 union smb_fileinfo io;
4587 struct file_alloced_range_buf *far_rsp = NULL;
4588 uint64_t far_count = 0;
4590 /* get size before & after. zero data should never change it */
4591 ZERO_STRUCT(io);
4592 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
4593 io.generic.in.file.handle = fh;
4594 status = smb2_getinfo_file(tree, tmp_ctx, &io);
4595 torture_assert_ntstatus_ok(torture, status, "getinfo");
4596 torture_assert_int_equal(torture, (int)io.all_info2.out.size,
4597 4096, "size after IO");
4599 /* valid 8 byte zero data, but after EOF */
4600 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4601 4096, /* off */
4602 4104); /* beyond_final_zero */
4603 torture_assert_ntstatus_ok(torture, status, "zero_data");
4605 /* valid 8 byte zero data, but after EOF */
4606 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4607 8192, /* off */
4608 8200); /* beyond_final_zero */
4609 torture_assert_ntstatus_ok(torture, status, "zero_data");
4611 ZERO_STRUCT(io);
4612 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
4613 io.generic.in.file.handle = fh;
4614 status = smb2_getinfo_file(tree, tmp_ctx, &io);
4615 torture_assert_ntstatus_ok(torture, status, "getinfo");
4616 torture_assert_int_equal(torture, (int)io.all_info2.out.size,
4617 4096, "size after IO");
4619 /* valid 0 byte zero data, without sparse flag */
4620 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4621 4095, /* off */
4622 4095); /* beyond_final_zero */
4623 torture_assert_ntstatus_ok(torture, status, "zero_data");
4625 /* INVALID off is past beyond_final_zero */
4626 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4627 4096, /* off */
4628 4095); /* beyond_final_zero */
4629 torture_assert_ntstatus_equal(torture, status,
4630 NT_STATUS_INVALID_PARAMETER,
4631 "invalid zero_data");
4633 /* zero length QAR - valid */
4634 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4635 0, /* off */
4636 0, /* len */
4637 &far_rsp, &far_count);
4638 torture_assert_ntstatus_ok(torture, status,
4639 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4640 torture_assert_u64_equal(torture, far_count, 0,
4641 "unexpected response len");
4643 /* QAR after EOF - valid */
4644 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4645 4096, /* off */
4646 1024, /* len */
4647 &far_rsp, &far_count);
4648 torture_assert_ntstatus_ok(torture, status,
4649 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4650 torture_assert_u64_equal(torture, far_count, 0,
4651 "unexpected response len");
4653 /* set sparse */
4654 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh,
4655 true);
4656 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4659 smb2_util_close(tree, fh);
4660 talloc_free(tmp_ctx);
4661 return true;
4664 static bool test_ioctl_sparse_perms(struct torture_context *torture,
4665 struct smb2_tree *tree)
4667 struct smb2_handle fh;
4668 NTSTATUS status;
4669 TALLOC_CTX *tmp_ctx = talloc_new(tree);
4670 bool ok;
4671 bool is_sparse;
4672 struct file_alloced_range_buf *far_rsp = NULL;
4673 uint64_t far_count = 0;
4675 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4676 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
4677 FILE_ATTRIBUTE_NORMAL);
4678 torture_assert(torture, ok, "setup file");
4680 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
4681 FILE_SUPPORTS_SPARSE_FILES, &ok);
4682 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4683 smb2_util_close(tree, fh);
4684 if (!ok) {
4685 torture_skip(torture, "Sparse files not supported\n");
4688 /* set sparse without WRITE_ATTR permission should succeed */
4689 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4690 FNAME, &fh, 0,
4691 (SEC_RIGHTS_FILE_WRITE & ~(SEC_FILE_WRITE_ATTRIBUTE
4692 | SEC_STD_WRITE_DAC
4693 | SEC_FILE_WRITE_EA)),
4694 FILE_ATTRIBUTE_NORMAL);
4695 torture_assert(torture, ok, "setup file");
4697 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4698 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4699 smb2_util_close(tree, fh);
4701 ok = test_setup_open(torture, tree, tmp_ctx,
4702 FNAME, &fh, SEC_RIGHTS_FILE_ALL,
4703 FILE_ATTRIBUTE_NORMAL);
4704 torture_assert(torture, ok, "setup file");
4705 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4706 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4707 torture_assert(torture, is_sparse, "sparse after set");
4708 smb2_util_close(tree, fh);
4710 /* attempt get sparse without READ_DATA permission */
4711 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4712 FNAME, &fh, 0,
4713 (SEC_RIGHTS_FILE_READ & ~SEC_FILE_READ_DATA),
4714 FILE_ATTRIBUTE_NORMAL);
4715 torture_assert(torture, ok, "setup file");
4717 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4718 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4719 torture_assert(torture, !is_sparse, "sparse set");
4720 smb2_util_close(tree, fh);
4722 /* attempt to set sparse with only WRITE_ATTR permission */
4723 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4724 FNAME, &fh, 0,
4725 SEC_FILE_WRITE_ATTRIBUTE,
4726 FILE_ATTRIBUTE_NORMAL);
4727 torture_assert(torture, ok, "setup file");
4729 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4730 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4731 smb2_util_close(tree, fh);
4733 /* attempt to set sparse with only WRITE_DATA permission */
4734 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4735 FNAME, &fh, 0,
4736 SEC_FILE_WRITE_DATA,
4737 FILE_ATTRIBUTE_NORMAL);
4738 torture_assert(torture, ok, "setup file");
4740 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4741 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4742 smb2_util_close(tree, fh);
4744 ok = test_setup_open(torture, tree, tmp_ctx,
4745 FNAME, &fh, SEC_RIGHTS_FILE_ALL,
4746 FILE_ATTRIBUTE_NORMAL);
4747 torture_assert(torture, ok, "setup file");
4748 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4749 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4750 torture_assert(torture, is_sparse, "sparse after set");
4751 smb2_util_close(tree, fh);
4753 /* attempt to set sparse with only APPEND_DATA permission */
4754 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4755 FNAME, &fh, 0,
4756 SEC_FILE_APPEND_DATA,
4757 FILE_ATTRIBUTE_NORMAL);
4758 torture_assert(torture, ok, "setup file");
4760 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4761 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4762 smb2_util_close(tree, fh);
4764 ok = test_setup_open(torture, tree, tmp_ctx,
4765 FNAME, &fh, SEC_RIGHTS_FILE_ALL,
4766 FILE_ATTRIBUTE_NORMAL);
4767 torture_assert(torture, ok, "setup file");
4768 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4769 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4770 torture_assert(torture, is_sparse, "sparse after set");
4771 smb2_util_close(tree, fh);
4773 /* attempt to set sparse with only WRITE_EA permission - should fail */
4774 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4775 FNAME, &fh, 0,
4776 SEC_FILE_WRITE_EA,
4777 FILE_ATTRIBUTE_NORMAL);
4778 torture_assert(torture, ok, "setup file");
4780 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4781 torture_assert_ntstatus_equal(torture, status,
4782 NT_STATUS_ACCESS_DENIED,
4783 "FSCTL_SET_SPARSE permission");
4784 smb2_util_close(tree, fh);
4786 ok = test_setup_open(torture, tree, tmp_ctx,
4787 FNAME, &fh, SEC_RIGHTS_FILE_ALL,
4788 FILE_ATTRIBUTE_NORMAL);
4789 torture_assert(torture, ok, "setup file");
4790 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4791 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4792 torture_assert(torture, !is_sparse, "sparse after set");
4793 smb2_util_close(tree, fh);
4795 /* attempt QAR with only READ_ATTR permission - should fail */
4796 ok = test_setup_open(torture, tree, tmp_ctx,
4797 FNAME, &fh, SEC_FILE_READ_ATTRIBUTE,
4798 FILE_ATTRIBUTE_NORMAL);
4799 torture_assert(torture, ok, "setup file");
4800 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4801 4096, /* off */
4802 1024, /* len */
4803 &far_rsp, &far_count);
4804 torture_assert_ntstatus_equal(torture, status,
4805 NT_STATUS_ACCESS_DENIED,
4806 "FSCTL_QUERY_ALLOCATED_RANGES req passed");
4807 smb2_util_close(tree, fh);
4809 /* attempt QAR with only READ_DATA permission */
4810 ok = test_setup_open(torture, tree, tmp_ctx,
4811 FNAME, &fh, SEC_FILE_READ_DATA,
4812 FILE_ATTRIBUTE_NORMAL);
4813 torture_assert(torture, ok, "setup file");
4814 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4815 0, /* off */
4816 1024, /* len */
4817 &far_rsp, &far_count);
4818 torture_assert_ntstatus_ok(torture, status,
4819 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4820 torture_assert_u64_equal(torture, far_count, 0,
4821 "unexpected response len");
4822 smb2_util_close(tree, fh);
4824 /* attempt QAR with only READ_EA permission - should fail */
4825 ok = test_setup_open(torture, tree, tmp_ctx,
4826 FNAME, &fh, SEC_FILE_READ_EA,
4827 FILE_ATTRIBUTE_NORMAL);
4828 torture_assert(torture, ok, "setup file");
4829 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4830 4096, /* off */
4831 1024, /* len */
4832 &far_rsp, &far_count);
4833 torture_assert_ntstatus_equal(torture, status,
4834 NT_STATUS_ACCESS_DENIED,
4835 "FSCTL_QUERY_ALLOCATED_RANGES req passed");
4836 smb2_util_close(tree, fh);
4838 /* setup file for ZERO_DATA permissions tests */
4839 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4840 FNAME, &fh, 8192,
4841 SEC_RIGHTS_FILE_ALL,
4842 FILE_ATTRIBUTE_NORMAL);
4843 torture_assert(torture, ok, "setup file");
4845 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4846 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4847 smb2_util_close(tree, fh);
4849 /* attempt ZERO_DATA with only WRITE_ATTR permission - should fail */
4850 ok = test_setup_open(torture, tree, tmp_ctx,
4851 FNAME, &fh, SEC_FILE_WRITE_ATTRIBUTE,
4852 FILE_ATTRIBUTE_NORMAL);
4853 torture_assert(torture, ok, "setup file");
4854 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4855 0, /* off */
4856 4096); /* beyond_final_zero */
4857 torture_assert_ntstatus_equal(torture, status,
4858 NT_STATUS_ACCESS_DENIED,
4859 "zero_data permission");
4860 smb2_util_close(tree, fh);
4862 /* attempt ZERO_DATA with only WRITE_DATA permission */
4863 ok = test_setup_open(torture, tree, tmp_ctx,
4864 FNAME, &fh, SEC_FILE_WRITE_DATA,
4865 FILE_ATTRIBUTE_NORMAL);
4866 torture_assert(torture, ok, "setup file");
4867 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4868 0, /* off */
4869 4096); /* beyond_final_zero */
4870 torture_assert_ntstatus_ok(torture, status, "zero_data");
4871 smb2_util_close(tree, fh);
4873 /* attempt ZERO_DATA with only APPEND_DATA permission - should fail */
4874 ok = test_setup_open(torture, tree, tmp_ctx,
4875 FNAME, &fh, SEC_FILE_APPEND_DATA,
4876 FILE_ATTRIBUTE_NORMAL);
4877 torture_assert(torture, ok, "setup file");
4878 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4879 0, /* off */
4880 4096); /* beyond_final_zero */
4881 torture_assert_ntstatus_equal(torture, status,
4882 NT_STATUS_ACCESS_DENIED,
4883 "zero_data permission");
4884 smb2_util_close(tree, fh);
4886 /* attempt ZERO_DATA with only WRITE_EA permission - should fail */
4887 ok = test_setup_open(torture, tree, tmp_ctx,
4888 FNAME, &fh, SEC_FILE_WRITE_EA,
4889 FILE_ATTRIBUTE_NORMAL);
4890 torture_assert(torture, ok, "setup file");
4891 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4892 0, /* off */
4893 4096); /* beyond_final_zero */
4894 torture_assert_ntstatus_equal(torture, status,
4895 NT_STATUS_ACCESS_DENIED,
4896 "zero_data permission");
4897 smb2_util_close(tree, fh);
4899 talloc_free(tmp_ctx);
4900 return true;
4903 static bool test_ioctl_sparse_lck(struct torture_context *torture,
4904 struct smb2_tree *tree)
4906 struct smb2_handle fh;
4907 struct smb2_handle fh2;
4908 NTSTATUS status;
4909 uint64_t dealloc_chunk_len = 64 * 1024; /* Windows 2012 */
4910 TALLOC_CTX *tmp_ctx = talloc_new(tree);
4911 bool ok;
4912 bool is_sparse;
4913 struct smb2_lock lck;
4914 struct smb2_lock_element el[1];
4915 struct file_alloced_range_buf *far_rsp = NULL;
4916 uint64_t far_count = 0;
4918 ok = test_setup_create_fill(torture, tree, tmp_ctx, FNAME, &fh,
4919 dealloc_chunk_len, SEC_RIGHTS_FILE_ALL,
4920 FILE_ATTRIBUTE_NORMAL);
4921 torture_assert(torture, ok, "setup file");
4923 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
4924 FILE_SUPPORTS_SPARSE_FILES, &ok);
4925 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4926 if (!ok) {
4927 torture_skip(torture, "Sparse files not supported\n");
4928 smb2_util_close(tree, fh);
4931 /* open and lock via separate fh2 */
4932 status = torture_smb2_testfile(tree, FNAME, &fh2);
4933 torture_assert_ntstatus_ok(torture, status, "2nd src open");
4935 lck.in.lock_count = 0x0001;
4936 lck.in.lock_sequence = 0x00000000;
4937 lck.in.file.handle = fh2;
4938 lck.in.locks = el;
4939 el[0].offset = 0;
4940 el[0].length = dealloc_chunk_len;
4941 el[0].reserved = 0;
4942 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
4944 status = smb2_lock(tree, &lck);
4945 torture_assert_ntstatus_ok(torture, status, "lock");
4947 /* set sparse while locked */
4948 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4949 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4951 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4952 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4953 torture_assert(torture, is_sparse, "sparse attr after set");
4955 /* zero data over locked range should fail */
4956 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4957 0, /* off */
4958 4096); /* beyond_final_zero */
4959 torture_assert_ntstatus_equal(torture, status,
4960 NT_STATUS_FILE_LOCK_CONFLICT,
4961 "zero_data locked");
4963 /* QAR over locked range should pass */
4964 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4965 0, /* off */
4966 4096, /* len */
4967 &far_rsp, &far_count);
4968 torture_assert_ntstatus_ok(torture, status,
4969 "FSCTL_QUERY_ALLOCATED_RANGES locked");
4970 torture_assert_u64_equal(torture, far_count, 1,
4971 "unexpected response len");
4972 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4973 "unexpected allocation");
4974 torture_assert_u64_equal(torture, far_rsp[0].len,
4975 4096,
4976 "unexpected far len");
4978 /* zero data over range past EOF should pass */
4979 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4980 dealloc_chunk_len, /* off */
4981 dealloc_chunk_len + 4096);
4982 torture_assert_ntstatus_ok(torture, status,
4983 "zero_data past EOF locked");
4985 /* QAR over range past EOF should pass */
4986 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4987 dealloc_chunk_len, /* off */
4988 4096, /* len */
4989 &far_rsp, &far_count);
4990 torture_assert_ntstatus_ok(torture, status,
4991 "FSCTL_QUERY_ALLOCATED_RANGES past EOF locked");
4992 torture_assert_u64_equal(torture, far_count, 0,
4993 "unexpected response len");
4995 lck.in.lock_count = 0x0001;
4996 lck.in.lock_sequence = 0x00000001;
4997 lck.in.file.handle = fh2;
4998 lck.in.locks = el;
4999 el[0].offset = 0;
5000 el[0].length = dealloc_chunk_len;
5001 el[0].reserved = 0;
5002 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
5003 status = smb2_lock(tree, &lck);
5004 torture_assert_ntstatus_ok(torture, status, "unlock");
5006 smb2_util_close(tree, fh2);
5007 smb2_util_close(tree, fh);
5008 talloc_free(tmp_ctx);
5009 return true;
5012 /* alleviate QAR off-by-one bug paranoia - help me ob1 */
5013 static bool test_ioctl_sparse_qar_ob1(struct torture_context *torture,
5014 struct smb2_tree *tree)
5016 struct smb2_handle fh;
5017 NTSTATUS status;
5018 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5019 bool ok;
5020 uint64_t dealloc_chunk_len = 64 * 1024; /* Windows 2012 */
5021 struct file_alloced_range_buf *far_rsp = NULL;
5022 uint64_t far_count = 0;
5024 ok = test_setup_create_fill(torture, tree, tmp_ctx,
5025 FNAME, &fh, dealloc_chunk_len * 2,
5026 SEC_RIGHTS_FILE_ALL,
5027 FILE_ATTRIBUTE_NORMAL);
5028 torture_assert(torture, ok, "setup file");
5030 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
5031 FILE_SUPPORTS_SPARSE_FILES, &ok);
5032 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
5033 if (!ok) {
5034 torture_skip(torture, "Sparse files not supported\n");
5035 smb2_util_close(tree, fh);
5038 /* non-sparse QAR with range one before EOF */
5039 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
5040 0, /* off */
5041 dealloc_chunk_len * 2 - 1, /* len */
5042 &far_rsp, &far_count);
5043 torture_assert_ntstatus_ok(torture, status,
5044 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5045 torture_assert_u64_equal(torture, far_count, 1,
5046 "unexpected response len");
5047 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
5048 "unexpected allocation");
5049 torture_assert_u64_equal(torture, far_rsp[0].len,
5050 dealloc_chunk_len * 2 - 1,
5051 "unexpected far len");
5053 /* non-sparse QAR with range one after EOF */
5054 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
5055 0, /* off */
5056 dealloc_chunk_len * 2 + 1, /* len */
5057 &far_rsp, &far_count);
5058 torture_assert_ntstatus_ok(torture, status,
5059 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5060 torture_assert_u64_equal(torture, far_count, 1,
5061 "unexpected response len");
5062 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
5063 "unexpected allocation");
5064 torture_assert_u64_equal(torture, far_rsp[0].len,
5065 dealloc_chunk_len * 2,
5066 "unexpected far len");
5068 /* non-sparse QAR with range one after EOF from off=1 */
5069 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
5070 1, /* off */
5071 dealloc_chunk_len * 2, /* len */
5072 &far_rsp, &far_count);
5073 torture_assert_ntstatus_ok(torture, status,
5074 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5075 torture_assert_u64_equal(torture, far_count, 1,
5076 "unexpected response len");
5077 torture_assert_u64_equal(torture, far_rsp[0].file_off, 1,
5078 "unexpected allocation");
5079 torture_assert_u64_equal(torture, far_rsp[0].len,
5080 dealloc_chunk_len * 2 - 1,
5081 "unexpected far len");
5083 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
5084 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
5086 /* punch out second chunk */
5087 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
5088 dealloc_chunk_len, /* off */
5089 dealloc_chunk_len * 2);
5090 torture_assert_ntstatus_ok(torture, status, "zero_data");
5092 /* sparse QAR with range one before hole */
5093 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
5094 0, /* off */
5095 dealloc_chunk_len - 1, /* len */
5096 &far_rsp, &far_count);
5097 torture_assert_ntstatus_ok(torture, status,
5098 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5099 torture_assert_u64_equal(torture, far_count, 1,
5100 "unexpected response len");
5101 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
5102 "unexpected allocation");
5103 torture_assert_u64_equal(torture, far_rsp[0].len,
5104 dealloc_chunk_len - 1,
5105 "unexpected far len");
5107 /* sparse QAR with range one after hole */
5108 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
5109 0, /* off */
5110 dealloc_chunk_len + 1, /* len */
5111 &far_rsp, &far_count);
5112 torture_assert_ntstatus_ok(torture, status,
5113 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5114 torture_assert_u64_equal(torture, far_count, 1,
5115 "unexpected response len");
5116 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
5117 "unexpected allocation");
5118 torture_assert_u64_equal(torture, far_rsp[0].len,
5119 dealloc_chunk_len,
5120 "unexpected far len");
5122 /* sparse QAR with range one after hole from off=1 */
5123 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
5124 1, /* off */
5125 dealloc_chunk_len, /* len */
5126 &far_rsp, &far_count);
5127 torture_assert_ntstatus_ok(torture, status,
5128 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5129 torture_assert_u64_equal(torture, far_count, 1,
5130 "unexpected response len");
5131 torture_assert_u64_equal(torture, far_rsp[0].file_off, 1,
5132 "unexpected allocation");
5133 torture_assert_u64_equal(torture, far_rsp[0].len,
5134 dealloc_chunk_len - 1,
5135 "unexpected far len");
5137 /* sparse QAR with range one before EOF from off=chunk_len-1 */
5138 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
5139 dealloc_chunk_len - 1, /* off */
5140 dealloc_chunk_len, /* len */
5141 &far_rsp, &far_count);
5142 torture_assert_ntstatus_ok(torture, status,
5143 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5144 torture_assert_u64_equal(torture, far_count, 1,
5145 "unexpected response len");
5146 torture_assert_u64_equal(torture, far_rsp[0].file_off,
5147 dealloc_chunk_len - 1,
5148 "unexpected allocation");
5149 torture_assert_u64_equal(torture, far_rsp[0].len,
5150 1, "unexpected far len");
5152 /* sparse QAR with range one after EOF from off=chunk_len+1 */
5153 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
5154 dealloc_chunk_len + 1, /* off */
5155 dealloc_chunk_len, /* len */
5156 &far_rsp, &far_count);
5157 torture_assert_ntstatus_ok(torture, status,
5158 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5159 torture_assert_u64_equal(torture, far_count, 0,
5160 "unexpected response len");
5161 smb2_util_close(tree, fh);
5162 talloc_free(tmp_ctx);
5163 return true;
5166 /* test QAR with multi-range responses */
5167 static bool test_ioctl_sparse_qar_multi(struct torture_context *torture,
5168 struct smb2_tree *tree)
5170 struct smb2_handle fh;
5171 NTSTATUS status;
5172 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5173 bool ok;
5174 uint64_t dealloc_chunk_len = 64 * 1024; /* Windows 2012 */
5175 uint64_t this_off;
5176 int i;
5177 struct file_alloced_range_buf *far_rsp = NULL;
5178 uint64_t far_count = 0;
5180 ok = test_setup_create_fill(torture, tree, tmp_ctx,
5181 FNAME, &fh, dealloc_chunk_len * 2,
5182 SEC_RIGHTS_FILE_ALL,
5183 FILE_ATTRIBUTE_NORMAL);
5184 torture_assert(torture, ok, "setup file");
5186 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
5187 FILE_SUPPORTS_SPARSE_FILES, &ok);
5188 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
5189 if (!ok) {
5190 torture_skip(torture, "Sparse files not supported\n");
5191 smb2_util_close(tree, fh);
5194 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
5195 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
5197 /* each loop, write out two chunks and punch the first out */
5198 for (i = 0; i < 10; i++) {
5199 this_off = i * dealloc_chunk_len * 2;
5201 ok = write_pattern(torture, tree, tmp_ctx, fh,
5202 this_off, /* off */
5203 dealloc_chunk_len * 2, /* len */
5204 this_off); /* pattern offset */
5205 torture_assert(torture, ok, "write pattern");
5207 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
5208 this_off, /* off */
5209 this_off + dealloc_chunk_len);
5210 torture_assert_ntstatus_ok(torture, status, "zero_data");
5213 /* should now have one separate region for each iteration */
5214 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
5216 10 * dealloc_chunk_len * 2,
5217 &far_rsp, &far_count);
5218 torture_assert_ntstatus_ok(torture, status,
5219 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5220 if (far_count == 1) {
5221 torture_comment(torture, "this FS doesn't deallocate 64K"
5222 "zeroed ranges in sparse files\n");
5223 return true; /* FS specific, not a failure */
5225 torture_assert_u64_equal(torture, far_count, 10,
5226 "unexpected response len");
5227 for (i = 0; i < 10; i++) {
5228 this_off = i * dealloc_chunk_len * 2;
5230 torture_assert_u64_equal(torture, far_rsp[i].file_off,
5231 this_off + dealloc_chunk_len,
5232 "unexpected allocation");
5233 torture_assert_u64_equal(torture, far_rsp[i].len,
5234 dealloc_chunk_len,
5235 "unexpected far len");
5238 smb2_util_close(tree, fh);
5239 talloc_free(tmp_ctx);
5240 return true;
5243 static bool test_ioctl_sparse_qar_overflow(struct torture_context *torture,
5244 struct smb2_tree *tree)
5246 struct smb2_handle fh;
5247 union smb_ioctl ioctl;
5248 struct file_alloced_range_buf far_buf;
5249 NTSTATUS status;
5250 enum ndr_err_code ndr_ret;
5251 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5252 bool ok;
5254 ok = test_setup_create_fill(torture, tree, tmp_ctx,
5255 FNAME, &fh, 1024, SEC_RIGHTS_FILE_ALL,
5256 FILE_ATTRIBUTE_NORMAL);
5257 torture_assert(torture, ok, "setup file");
5259 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
5260 FILE_SUPPORTS_SPARSE_FILES, &ok);
5261 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
5262 if (!ok) {
5263 smb2_util_close(tree, fh);
5264 torture_skip(torture, "Sparse files not supported\n");
5267 /* no allocated ranges, no space for range response, should pass */
5268 ZERO_STRUCT(ioctl);
5269 ioctl.smb2.level = RAW_IOCTL_SMB2;
5270 ioctl.smb2.in.file.handle = fh;
5271 ioctl.smb2.in.function = FSCTL_QUERY_ALLOCATED_RANGES;
5272 ioctl.smb2.in.max_response_size = 1024;
5273 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
5275 /* off + length wraps around to 511 */
5276 far_buf.file_off = 512;
5277 far_buf.len = 0xffffffffffffffffLL;
5278 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5279 &far_buf,
5280 (ndr_push_flags_fn_t)ndr_push_file_alloced_range_buf);
5281 torture_assert_ndr_success(torture, ndr_ret, "push far ndr buf");
5283 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5284 torture_assert_ntstatus_equal(torture, status,
5285 NT_STATUS_INVALID_PARAMETER,
5286 "FSCTL_QUERY_ALLOCATED_RANGES overflow");
5288 return true;
5291 static NTSTATUS test_ioctl_trim_supported(struct torture_context *torture,
5292 struct smb2_tree *tree,
5293 TALLOC_CTX *mem_ctx,
5294 struct smb2_handle *fh,
5295 bool *trim_support)
5297 NTSTATUS status;
5298 union smb_fsinfo info;
5300 ZERO_STRUCT(info);
5301 info.generic.level = RAW_QFS_SECTOR_SIZE_INFORMATION;
5302 info.generic.handle = *fh;
5303 status = smb2_getinfo_fs(tree, tree, &info);
5304 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_INFO_CLASS)) {
5306 * Windows < Server 2012, 8 etc. don't support this info level
5307 * or the trim ioctl. Ignore the error and let the caller skip.
5309 *trim_support = false;
5310 return NT_STATUS_OK;
5311 } else if (!NT_STATUS_IS_OK(status)) {
5312 return status;
5315 torture_comment(torture, "sector size info: lb/s=%u, pb/sA=%u, "
5316 "pb/sP=%u, fse/sA=%u, flags=0x%x, bosa=%u, bopa=%u\n",
5317 (unsigned)info.sector_size_info.out.logical_bytes_per_sector,
5318 (unsigned)info.sector_size_info.out.phys_bytes_per_sector_atomic,
5319 (unsigned)info.sector_size_info.out.phys_bytes_per_sector_perf,
5320 (unsigned)info.sector_size_info.out.fs_effective_phys_bytes_per_sector_atomic,
5321 (unsigned)info.sector_size_info.out.flags,
5322 (unsigned)info.sector_size_info.out.byte_off_sector_align,
5323 (unsigned)info.sector_size_info.out.byte_off_partition_align);
5325 if (info.sector_size_info.out.flags & QFS_SSINFO_FLAGS_TRIM_ENABLED) {
5326 *trim_support = true;
5327 } else {
5328 *trim_support = false;
5330 return NT_STATUS_OK;
5333 static bool test_setup_trim(struct torture_context *torture,
5334 struct smb2_tree *tree,
5335 TALLOC_CTX *mem_ctx,
5336 uint32_t num_ranges,
5337 struct smb2_handle *fh,
5338 uint64_t file_size,
5339 uint32_t desired_access,
5340 struct fsctl_file_level_trim_req *trim_req,
5341 union smb_ioctl *ioctl)
5343 bool ok;
5345 ok = test_setup_create_fill(torture, tree, mem_ctx, FNAME,
5346 fh, file_size, desired_access,
5347 FILE_ATTRIBUTE_NORMAL);
5348 torture_assert(torture, ok, "src file create fill");
5350 ZERO_STRUCTPN(ioctl);
5351 ioctl->smb2.level = RAW_IOCTL_SMB2;
5352 ioctl->smb2.in.file.handle = *fh;
5353 ioctl->smb2.in.function = FSCTL_FILE_LEVEL_TRIM;
5354 ioctl->smb2.in.max_response_size
5355 = sizeof(struct fsctl_file_level_trim_rsp);
5356 ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
5358 ZERO_STRUCTPN(trim_req);
5359 /* leave key as zero for now. TODO test locking with differing keys */
5360 trim_req->num_ranges = num_ranges;
5361 trim_req->ranges = talloc_zero_array(mem_ctx,
5362 struct file_level_trim_range,
5363 num_ranges);
5364 torture_assert(torture, (trim_req->ranges != NULL), "no memory for ranges");
5366 return true;
5369 static bool test_ioctl_trim_simple(struct torture_context *torture,
5370 struct smb2_tree *tree)
5372 struct smb2_handle fh;
5373 NTSTATUS status;
5374 union smb_ioctl ioctl;
5375 bool trim_supported;
5376 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5377 struct fsctl_file_level_trim_req trim_req;
5378 struct fsctl_file_level_trim_rsp trim_rsp;
5379 uint64_t trim_chunk_len = 64 * 1024; /* trim 64K chunks */
5380 enum ndr_err_code ndr_ret;
5381 bool ok;
5383 ok = test_setup_trim(torture, tree, tmp_ctx,
5384 1, /* 1 range */
5385 &fh, 2 * trim_chunk_len, /* fill 128K file */
5386 SEC_RIGHTS_FILE_ALL,
5387 &trim_req,
5388 &ioctl);
5389 if (!ok) {
5390 torture_fail(torture, "setup trim error");
5393 status = test_ioctl_trim_supported(torture, tree, tmp_ctx, &fh,
5394 &trim_supported);
5395 torture_assert_ntstatus_ok(torture, status, "fsinfo");
5396 if (!trim_supported) {
5397 smb2_util_close(tree, fh);
5398 talloc_free(tmp_ctx);
5399 torture_skip(torture, "trim not supported\n");
5402 /* trim first chunk, leave second */
5403 trim_req.ranges[0].off = 0;
5404 trim_req.ranges[0].len = trim_chunk_len;
5406 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, &trim_req,
5407 (ndr_push_flags_fn_t)ndr_push_fsctl_file_level_trim_req);
5408 torture_assert_ndr_success(torture, ndr_ret,
5409 "ndr_push_fsctl_file_level_trim_req");
5411 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5412 torture_assert_ntstatus_ok(torture, status, "FILE_LEVEL_TRIM_RANGE");
5414 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
5415 &trim_rsp,
5416 (ndr_pull_flags_fn_t)ndr_pull_fsctl_file_level_trim_rsp);
5417 torture_assert_ndr_success(torture, ndr_ret,
5418 "ndr_pull_fsctl_file_level_trim_rsp");
5420 torture_assert_int_equal(torture, trim_rsp.num_ranges_processed, 1, "");
5422 /* second half of the file should remain consitent */
5423 ok = check_pattern(torture, tree, tmp_ctx, fh, trim_chunk_len,
5424 trim_chunk_len, trim_chunk_len);
5425 torture_assert(torture, ok, "non-trimmed range inconsistent");
5427 return true;
5430 static bool test_setup_dup_extents(struct torture_context *tctx,
5431 struct smb2_tree *tree,
5432 TALLOC_CTX *mem_ctx,
5433 struct smb2_handle *src_h,
5434 uint64_t src_size,
5435 uint32_t src_desired_access,
5436 struct smb2_handle *dest_h,
5437 uint64_t dest_size,
5438 uint32_t dest_desired_access,
5439 struct fsctl_dup_extents_to_file *dup_ext_buf,
5440 union smb_ioctl *ioctl)
5442 bool ok;
5444 ok = test_setup_create_fill(tctx, tree, mem_ctx, FNAME,
5445 src_h, src_size, src_desired_access,
5446 FILE_ATTRIBUTE_NORMAL);
5447 torture_assert(tctx, ok, "src file create fill");
5449 ok = test_setup_create_fill(tctx, tree, mem_ctx, FNAME2,
5450 dest_h, dest_size, dest_desired_access,
5451 FILE_ATTRIBUTE_NORMAL);
5452 torture_assert(tctx, ok, "dest file create fill");
5454 ZERO_STRUCTPN(ioctl);
5455 ioctl->smb2.level = RAW_IOCTL_SMB2;
5456 ioctl->smb2.in.file.handle = *dest_h;
5457 ioctl->smb2.in.function = FSCTL_DUP_EXTENTS_TO_FILE;
5458 ioctl->smb2.in.max_response_size = 0;
5459 ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
5461 ZERO_STRUCTPN(dup_ext_buf);
5462 smb2_push_handle(dup_ext_buf->source_fid, src_h);
5464 return true;
5467 static bool test_ioctl_dup_extents_simple(struct torture_context *tctx,
5468 struct smb2_tree *tree)
5470 struct smb2_handle src_h;
5471 struct smb2_handle dest_h;
5472 NTSTATUS status;
5473 union smb_ioctl ioctl;
5474 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5475 struct fsctl_dup_extents_to_file dup_ext_buf;
5476 enum ndr_err_code ndr_ret;
5477 union smb_fileinfo io;
5478 union smb_setfileinfo sinfo;
5479 bool ok;
5481 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5482 &src_h, 4096, /* fill 4096 byte src file */
5483 SEC_RIGHTS_FILE_ALL,
5484 &dest_h, 0, /* 0 byte dest file */
5485 SEC_RIGHTS_FILE_ALL,
5486 &dup_ext_buf,
5487 &ioctl);
5488 if (!ok) {
5489 torture_fail(tctx, "setup dup extents error");
5492 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5493 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
5494 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5495 if (!ok) {
5496 smb2_util_close(tree, src_h);
5497 smb2_util_close(tree, dest_h);
5498 talloc_free(tmp_ctx);
5499 torture_skip(tctx, "block refcounting not supported\n");
5502 /* extend dest to match src len */
5503 ZERO_STRUCT(sinfo);
5504 sinfo.end_of_file_info.level =
5505 RAW_SFILEINFO_END_OF_FILE_INFORMATION;
5506 sinfo.end_of_file_info.in.file.handle = dest_h;
5507 sinfo.end_of_file_info.in.size = 4096;
5508 status = smb2_setinfo_file(tree, &sinfo);
5509 torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
5511 /* copy all src file data */
5512 dup_ext_buf.source_off = 0;
5513 dup_ext_buf.target_off = 0;
5514 dup_ext_buf.byte_count = 4096;
5516 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5517 &dup_ext_buf,
5518 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5519 torture_assert_ndr_success(tctx, ndr_ret,
5520 "ndr_push_fsctl_dup_extents_to_file");
5522 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5523 torture_assert_ntstatus_ok(tctx, status,
5524 "FSCTL_DUP_EXTENTS_TO_FILE");
5526 /* the file size shouldn't have been changed by this operation! */
5527 ZERO_STRUCT(io);
5528 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5529 io.generic.in.file.handle = dest_h;
5530 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5531 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5532 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5533 4096, "size after IO");
5535 smb2_util_close(tree, src_h);
5536 smb2_util_close(tree, dest_h);
5538 /* reopen for pattern check */
5539 ok = test_setup_open(tctx, tree, tmp_ctx, FNAME, &src_h,
5540 SEC_RIGHTS_FILE_ALL, FILE_ATTRIBUTE_NORMAL);
5541 torture_assert_ntstatus_ok(tctx, status, "src open after dup");
5542 ok = test_setup_open(tctx, tree, tmp_ctx, FNAME2, &dest_h,
5543 SEC_RIGHTS_FILE_ALL, FILE_ATTRIBUTE_NORMAL);
5544 torture_assert_ntstatus_ok(tctx, status, "dest open after dup");
5546 ok = check_pattern(tctx, tree, tmp_ctx, src_h, 0, 4096, 0);
5547 if (!ok) {
5548 torture_fail(tctx, "inconsistent src file data");
5551 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 4096, 0);
5552 if (!ok) {
5553 torture_fail(tctx, "inconsistent dest file data");
5556 smb2_util_close(tree, src_h);
5557 smb2_util_close(tree, dest_h);
5558 talloc_free(tmp_ctx);
5559 return true;
5562 static bool test_ioctl_dup_extents_len_beyond_dest(struct torture_context *tctx,
5563 struct smb2_tree *tree)
5565 struct smb2_handle src_h;
5566 struct smb2_handle dest_h;
5567 NTSTATUS status;
5568 union smb_ioctl ioctl;
5569 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5570 struct fsctl_dup_extents_to_file dup_ext_buf;
5571 enum ndr_err_code ndr_ret;
5572 union smb_fileinfo io;
5573 union smb_setfileinfo sinfo;
5574 bool ok;
5576 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5577 &src_h, 32768, /* fill 32768 byte src file */
5578 SEC_RIGHTS_FILE_ALL,
5579 &dest_h, 0, /* 0 byte dest file */
5580 SEC_RIGHTS_FILE_ALL,
5581 &dup_ext_buf,
5582 &ioctl);
5583 if (!ok) {
5584 torture_fail(tctx, "setup dup extents error");
5587 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5588 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
5589 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5590 if (!ok) {
5591 smb2_util_close(tree, src_h);
5592 smb2_util_close(tree, dest_h);
5593 talloc_free(tmp_ctx);
5594 torture_skip(tctx, "block refcounting not supported\n");
5597 ZERO_STRUCT(io);
5598 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5599 io.generic.in.file.handle = dest_h;
5600 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5601 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5602 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5603 0, "size after IO");
5605 /* copy all src file data */
5606 dup_ext_buf.source_off = 0;
5607 dup_ext_buf.target_off = 0;
5608 dup_ext_buf.byte_count = 32768;
5610 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5611 &dup_ext_buf,
5612 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5613 torture_assert_ndr_success(tctx, ndr_ret,
5614 "ndr_push_fsctl_dup_extents_to_file");
5616 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5617 #if 0
5619 * 2.3.8 FSCTL_DUPLICATE_EXTENTS_TO_FILE Reply - this should fail, but
5620 * passes against WS2016 RTM!
5622 torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_SUPPORTED,
5623 "FSCTL_DUP_EXTENTS_TO_FILE");
5624 #endif
5626 /* the file sizes shouldn't have been changed */
5627 ZERO_STRUCT(io);
5628 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5629 io.generic.in.file.handle = src_h;
5630 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5631 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5632 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5633 32768, "size after IO");
5635 ZERO_STRUCT(io);
5636 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5637 io.generic.in.file.handle = dest_h;
5638 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5639 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5640 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5641 0, "size after IO");
5643 /* extend dest */
5644 ZERO_STRUCT(sinfo);
5645 sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
5646 sinfo.end_of_file_info.in.file.handle = dest_h;
5647 sinfo.end_of_file_info.in.size = 32768;
5648 status = smb2_setinfo_file(tree, &sinfo);
5649 torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
5651 ok = check_zero(tctx, tree, tmp_ctx, dest_h, 0, 32768);
5652 if (!ok) {
5653 torture_fail(tctx, "inconsistent file data");
5656 /* reissue ioctl, now with enough space */
5657 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5658 torture_assert_ntstatus_ok(tctx, status,
5659 "FSCTL_DUP_EXTENTS_TO_FILE");
5661 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
5662 if (!ok) {
5663 torture_fail(tctx, "inconsistent file data");
5666 smb2_util_close(tree, src_h);
5667 smb2_util_close(tree, dest_h);
5668 talloc_free(tmp_ctx);
5669 return true;
5672 static bool test_ioctl_dup_extents_len_beyond_src(struct torture_context *tctx,
5673 struct smb2_tree *tree)
5675 struct smb2_handle src_h;
5676 struct smb2_handle dest_h;
5677 NTSTATUS status;
5678 union smb_ioctl ioctl;
5679 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5680 struct fsctl_dup_extents_to_file dup_ext_buf;
5681 enum ndr_err_code ndr_ret;
5682 union smb_fileinfo io;
5683 bool ok;
5685 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5686 &src_h, 32768, /* fill 32768 byte src file */
5687 SEC_RIGHTS_FILE_ALL,
5688 &dest_h, 0, /* 0 byte dest file */
5689 SEC_RIGHTS_FILE_ALL,
5690 &dup_ext_buf,
5691 &ioctl);
5692 if (!ok) {
5693 torture_fail(tctx, "setup dup extents error");
5696 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5697 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
5698 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5699 if (!ok) {
5700 smb2_util_close(tree, src_h);
5701 smb2_util_close(tree, dest_h);
5702 talloc_free(tmp_ctx);
5703 torture_skip(tctx, "block refcounting not supported\n");
5706 ZERO_STRUCT(io);
5707 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5708 io.generic.in.file.handle = dest_h;
5709 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5710 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5711 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5712 0, "size after IO");
5714 /* exceed src file len */
5715 dup_ext_buf.source_off = 0;
5716 dup_ext_buf.target_off = 0;
5717 dup_ext_buf.byte_count = 32768 * 2;
5719 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5720 &dup_ext_buf,
5721 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5722 torture_assert_ndr_success(tctx, ndr_ret,
5723 "ndr_push_fsctl_dup_extents_to_file");
5725 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5726 torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_SUPPORTED,
5727 "FSCTL_DUP_EXTENTS_TO_FILE");
5729 /* the file sizes shouldn't have been changed */
5730 ZERO_STRUCT(io);
5731 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5732 io.generic.in.file.handle = src_h;
5733 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5734 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5735 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5736 32768, "size after IO");
5738 ZERO_STRUCT(io);
5739 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5740 io.generic.in.file.handle = dest_h;
5741 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5742 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5743 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5744 0, "size after IO");
5746 smb2_util_close(tree, src_h);
5747 smb2_util_close(tree, dest_h);
5748 talloc_free(tmp_ctx);
5749 return true;
5752 static bool test_ioctl_dup_extents_len_zero(struct torture_context *tctx,
5753 struct smb2_tree *tree)
5755 struct smb2_handle src_h;
5756 struct smb2_handle dest_h;
5757 NTSTATUS status;
5758 union smb_ioctl ioctl;
5759 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5760 struct fsctl_dup_extents_to_file dup_ext_buf;
5761 enum ndr_err_code ndr_ret;
5762 union smb_fileinfo io;
5763 bool ok;
5765 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5766 &src_h, 32768, /* fill 32768 byte src file */
5767 SEC_RIGHTS_FILE_ALL,
5768 &dest_h, 0, /* 0 byte dest file */
5769 SEC_RIGHTS_FILE_ALL,
5770 &dup_ext_buf,
5771 &ioctl);
5772 if (!ok) {
5773 torture_fail(tctx, "setup dup extents error");
5776 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5777 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
5778 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5779 if (!ok) {
5780 smb2_util_close(tree, src_h);
5781 smb2_util_close(tree, dest_h);
5782 talloc_free(tmp_ctx);
5783 torture_skip(tctx, "block refcounting not supported\n");
5786 ZERO_STRUCT(io);
5787 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5788 io.generic.in.file.handle = dest_h;
5789 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5790 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5791 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5792 0, "size after IO");
5794 dup_ext_buf.source_off = 0;
5795 dup_ext_buf.target_off = 0;
5796 dup_ext_buf.byte_count = 0;
5798 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5799 &dup_ext_buf,
5800 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5801 torture_assert_ndr_success(tctx, ndr_ret,
5802 "ndr_push_fsctl_dup_extents_to_file");
5804 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5805 torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
5807 /* the file sizes shouldn't have been changed */
5808 ZERO_STRUCT(io);
5809 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5810 io.generic.in.file.handle = src_h;
5811 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5812 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5813 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5814 32768, "size after IO");
5816 ZERO_STRUCT(io);
5817 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5818 io.generic.in.file.handle = dest_h;
5819 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5820 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5821 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5822 0, "size after IO");
5824 smb2_util_close(tree, src_h);
5825 smb2_util_close(tree, dest_h);
5826 talloc_free(tmp_ctx);
5827 return true;
5830 static bool test_ioctl_dup_extents_sparse_src(struct torture_context *tctx,
5831 struct smb2_tree *tree)
5833 struct smb2_handle src_h;
5834 struct smb2_handle dest_h;
5835 NTSTATUS status;
5836 union smb_ioctl ioctl;
5837 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5838 struct fsctl_dup_extents_to_file dup_ext_buf;
5839 enum ndr_err_code ndr_ret;
5840 union smb_setfileinfo sinfo;
5841 bool ok;
5843 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5844 &src_h, 0, /* filled after sparse flag */
5845 SEC_RIGHTS_FILE_ALL,
5846 &dest_h, 0, /* 0 byte dest file */
5847 SEC_RIGHTS_FILE_ALL,
5848 &dup_ext_buf,
5849 &ioctl);
5850 if (!ok) {
5851 torture_fail(tctx, "setup dup extents error");
5854 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5855 FILE_SUPPORTS_BLOCK_REFCOUNTING
5856 | FILE_SUPPORTS_SPARSE_FILES, &ok);
5857 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5858 if (!ok) {
5859 smb2_util_close(tree, src_h);
5860 smb2_util_close(tree, dest_h);
5861 talloc_free(tmp_ctx);
5862 torture_skip(tctx,
5863 "block refcounting and sparse files not supported\n");
5866 /* set sparse flag on src */
5867 status = test_ioctl_sparse_req(tctx, tmp_ctx, tree, src_h, true);
5868 torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_SPARSE");
5870 ok = write_pattern(tctx, tree, tmp_ctx, src_h, 0, 4096, 0);
5871 torture_assert(tctx, ok, "write pattern");
5873 /* extend dest */
5874 ZERO_STRUCT(sinfo);
5875 sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
5876 sinfo.end_of_file_info.in.file.handle = dest_h;
5877 sinfo.end_of_file_info.in.size = 4096;
5878 status = smb2_setinfo_file(tree, &sinfo);
5879 torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
5881 /* copy all src file data */
5882 dup_ext_buf.source_off = 0;
5883 dup_ext_buf.target_off = 0;
5884 dup_ext_buf.byte_count = 4096;
5886 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5887 &dup_ext_buf,
5888 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5889 torture_assert_ndr_success(tctx, ndr_ret,
5890 "ndr_push_fsctl_dup_extents_to_file");
5893 * src is sparse, but spec says: 2.3.8 FSCTL_DUPLICATE_EXTENTS_TO_FILE
5894 * Reply... STATUS_NOT_SUPPORTED: Target file is sparse, while source
5895 * is a non-sparse file.
5897 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5898 torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_SUPPORTED,
5899 "FSCTL_DUP_EXTENTS_TO_FILE");
5901 smb2_util_close(tree, src_h);
5902 smb2_util_close(tree, dest_h);
5903 talloc_free(tmp_ctx);
5904 return true;
5907 static bool test_ioctl_dup_extents_sparse_dest(struct torture_context *tctx,
5908 struct smb2_tree *tree)
5910 struct smb2_handle src_h;
5911 struct smb2_handle dest_h;
5912 NTSTATUS status;
5913 union smb_ioctl ioctl;
5914 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5915 struct fsctl_dup_extents_to_file dup_ext_buf;
5916 enum ndr_err_code ndr_ret;
5917 union smb_setfileinfo sinfo;
5918 bool ok;
5920 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5921 &src_h, 4096, /* fill 4096 byte src file */
5922 SEC_RIGHTS_FILE_ALL,
5923 &dest_h, 0, /* 0 byte dest file */
5924 SEC_RIGHTS_FILE_ALL,
5925 &dup_ext_buf,
5926 &ioctl);
5927 if (!ok) {
5928 torture_fail(tctx, "setup dup extents error");
5931 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5932 FILE_SUPPORTS_BLOCK_REFCOUNTING
5933 | FILE_SUPPORTS_SPARSE_FILES, &ok);
5934 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5935 if (!ok) {
5936 smb2_util_close(tree, src_h);
5937 smb2_util_close(tree, dest_h);
5938 talloc_free(tmp_ctx);
5939 torture_skip(tctx,
5940 "block refcounting and sparse files not supported\n");
5943 /* set sparse flag on dest */
5944 status = test_ioctl_sparse_req(tctx, tmp_ctx, tree, dest_h, true);
5945 torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_SPARSE");
5947 /* extend dest */
5948 ZERO_STRUCT(sinfo);
5949 sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
5950 sinfo.end_of_file_info.in.file.handle = dest_h;
5951 sinfo.end_of_file_info.in.size = dup_ext_buf.byte_count;
5952 status = smb2_setinfo_file(tree, &sinfo);
5953 torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
5955 /* copy all src file data */
5956 dup_ext_buf.source_off = 0;
5957 dup_ext_buf.target_off = 0;
5958 dup_ext_buf.byte_count = 4096;
5960 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5961 &dup_ext_buf,
5962 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5963 torture_assert_ndr_success(tctx, ndr_ret,
5964 "ndr_push_fsctl_dup_extents_to_file");
5967 * dest is sparse, but spec says: 2.3.8 FSCTL_DUPLICATE_EXTENTS_TO_FILE
5968 * Reply... STATUS_NOT_SUPPORTED: Target file is sparse, while source
5969 * is a non-sparse file.
5971 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5972 torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
5974 smb2_util_close(tree, src_h);
5975 smb2_util_close(tree, dest_h);
5976 talloc_free(tmp_ctx);
5977 return true;
5980 static bool test_ioctl_dup_extents_sparse_both(struct torture_context *tctx,
5981 struct smb2_tree *tree)
5983 struct smb2_handle src_h;
5984 struct smb2_handle dest_h;
5985 NTSTATUS status;
5986 union smb_ioctl ioctl;
5987 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5988 struct fsctl_dup_extents_to_file dup_ext_buf;
5989 enum ndr_err_code ndr_ret;
5990 union smb_setfileinfo sinfo;
5991 bool ok;
5993 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5994 &src_h, 0, /* fill 4096 byte src file */
5995 SEC_RIGHTS_FILE_ALL,
5996 &dest_h, 0, /* 0 byte dest file */
5997 SEC_RIGHTS_FILE_ALL,
5998 &dup_ext_buf,
5999 &ioctl);
6000 if (!ok) {
6001 torture_fail(tctx, "setup dup extents error");
6004 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
6005 FILE_SUPPORTS_BLOCK_REFCOUNTING
6006 | FILE_SUPPORTS_SPARSE_FILES, &ok);
6007 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
6008 if (!ok) {
6009 smb2_util_close(tree, src_h);
6010 smb2_util_close(tree, dest_h);
6011 talloc_free(tmp_ctx);
6012 torture_skip(tctx,
6013 "block refcounting and sparse files not supported\n");
6016 /* set sparse flag on src and dest */
6017 status = test_ioctl_sparse_req(tctx, tmp_ctx, tree, src_h, true);
6018 torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_SPARSE");
6019 status = test_ioctl_sparse_req(tctx, tmp_ctx, tree, dest_h, true);
6020 torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_SPARSE");
6022 ok = write_pattern(tctx, tree, tmp_ctx, src_h, 0, 4096, 0);
6023 torture_assert(tctx, ok, "write pattern");
6025 /* extend dest */
6026 ZERO_STRUCT(sinfo);
6027 sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
6028 sinfo.end_of_file_info.in.file.handle = dest_h;
6029 sinfo.end_of_file_info.in.size = 4096;
6030 status = smb2_setinfo_file(tree, &sinfo);
6031 torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
6033 /* copy all src file data */
6034 dup_ext_buf.source_off = 0;
6035 dup_ext_buf.target_off = 0;
6036 dup_ext_buf.byte_count = 4096;
6038 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6039 &dup_ext_buf,
6040 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6041 torture_assert_ndr_success(tctx, ndr_ret,
6042 "ndr_push_fsctl_dup_extents_to_file");
6044 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6045 torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
6047 smb2_util_close(tree, src_h);
6048 smb2_util_close(tree, dest_h);
6050 /* reopen for pattern check */
6051 ok = test_setup_open(tctx, tree, tmp_ctx, FNAME2, &dest_h,
6052 SEC_RIGHTS_FILE_ALL, FILE_ATTRIBUTE_NORMAL);
6053 torture_assert_ntstatus_ok(tctx, status, "dest open ater dup");
6055 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 4096, 0);
6056 if (!ok) {
6057 torture_fail(tctx, "inconsistent file data");
6060 smb2_util_close(tree, dest_h);
6061 talloc_free(tmp_ctx);
6062 return true;
6065 static bool test_ioctl_dup_extents_src_is_dest(struct torture_context *tctx,
6066 struct smb2_tree *tree)
6068 struct smb2_handle src_h;
6069 struct smb2_handle dest_h;
6070 NTSTATUS status;
6071 union smb_ioctl ioctl;
6072 TALLOC_CTX *tmp_ctx = talloc_new(tree);
6073 struct fsctl_dup_extents_to_file dup_ext_buf;
6074 enum ndr_err_code ndr_ret;
6075 union smb_fileinfo io;
6076 bool ok;
6078 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
6079 &src_h, 32768, /* fill 32768 byte src file */
6080 SEC_RIGHTS_FILE_ALL,
6081 &dest_h, 0,
6082 SEC_RIGHTS_FILE_ALL,
6083 &dup_ext_buf,
6084 &ioctl);
6085 if (!ok) {
6086 torture_fail(tctx, "setup dup extents error");
6088 /* dest_h not needed for this test */
6089 smb2_util_close(tree, dest_h);
6091 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
6092 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
6093 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
6094 if (!ok) {
6095 smb2_util_close(tree, src_h);
6096 talloc_free(tmp_ctx);
6097 torture_skip(tctx, "block refcounting not supported\n");
6100 /* src and dest are the same file handle */
6101 ioctl.smb2.in.file.handle = src_h;
6103 /* no overlap between src and tgt */
6104 dup_ext_buf.source_off = 0;
6105 dup_ext_buf.target_off = 16384;
6106 dup_ext_buf.byte_count = 16384;
6108 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6109 &dup_ext_buf,
6110 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6111 torture_assert_ndr_success(tctx, ndr_ret,
6112 "ndr_push_fsctl_dup_extents_to_file");
6114 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6115 torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
6117 /* the file size shouldn't have been changed */
6118 ZERO_STRUCT(io);
6119 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
6120 io.generic.in.file.handle = src_h;
6121 status = smb2_getinfo_file(tree, tmp_ctx, &io);
6122 torture_assert_ntstatus_ok(tctx, status, "getinfo");
6123 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
6124 32768, "size after IO");
6126 ok = check_pattern(tctx, tree, tmp_ctx, src_h, 0, 16384, 0);
6127 if (!ok) {
6128 torture_fail(tctx, "inconsistent file data");
6130 ok = check_pattern(tctx, tree, tmp_ctx, src_h, 16384, 16384, 0);
6131 if (!ok) {
6132 torture_fail(tctx, "inconsistent file data");
6135 smb2_util_close(tree, src_h);
6136 talloc_free(tmp_ctx);
6137 return true;
6141 * unlike copy-chunk, dup extents doesn't support overlapping ranges between
6142 * source and target. This makes it a *lot* cleaner to implement on the server.
6144 static bool
6145 test_ioctl_dup_extents_src_is_dest_overlap(struct torture_context *tctx,
6146 struct smb2_tree *tree)
6148 struct smb2_handle src_h;
6149 struct smb2_handle dest_h;
6150 NTSTATUS status;
6151 union smb_ioctl ioctl;
6152 TALLOC_CTX *tmp_ctx = talloc_new(tree);
6153 struct fsctl_dup_extents_to_file dup_ext_buf;
6154 enum ndr_err_code ndr_ret;
6155 union smb_fileinfo io;
6156 bool ok;
6158 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
6159 &src_h, 32768, /* fill 32768 byte src file */
6160 SEC_RIGHTS_FILE_ALL,
6161 &dest_h, 0,
6162 SEC_RIGHTS_FILE_ALL,
6163 &dup_ext_buf,
6164 &ioctl);
6165 if (!ok) {
6166 torture_fail(tctx, "setup dup extents error");
6168 /* dest_h not needed for this test */
6169 smb2_util_close(tree, dest_h);
6171 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
6172 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
6173 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
6174 if (!ok) {
6175 smb2_util_close(tree, src_h);
6176 talloc_free(tmp_ctx);
6177 torture_skip(tctx, "block refcounting not supported\n");
6180 /* src and dest are the same file handle */
6181 ioctl.smb2.in.file.handle = src_h;
6183 /* 8K overlap between src and tgt */
6184 dup_ext_buf.source_off = 0;
6185 dup_ext_buf.target_off = 8192;
6186 dup_ext_buf.byte_count = 16384;
6188 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6189 &dup_ext_buf,
6190 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6191 torture_assert_ndr_success(tctx, ndr_ret,
6192 "ndr_push_fsctl_dup_extents_to_file");
6194 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6195 torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_SUPPORTED,
6196 "FSCTL_DUP_EXTENTS_TO_FILE");
6198 /* the file size and data should match beforehand */
6199 ZERO_STRUCT(io);
6200 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
6201 io.generic.in.file.handle = src_h;
6202 status = smb2_getinfo_file(tree, tmp_ctx, &io);
6203 torture_assert_ntstatus_ok(tctx, status, "getinfo");
6204 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
6205 32768, "size after IO");
6207 ok = check_pattern(tctx, tree, tmp_ctx, src_h, 0, 32768, 0);
6208 if (!ok) {
6209 torture_fail(tctx, "inconsistent file data");
6212 smb2_util_close(tree, src_h);
6213 talloc_free(tmp_ctx);
6214 return true;
6218 * The compression tests won't run against Windows servers yet - ReFS doesn't
6219 * (yet) offer support for compression.
6221 static bool test_ioctl_dup_extents_compressed_src(struct torture_context *tctx,
6222 struct smb2_tree *tree)
6224 struct smb2_handle src_h;
6225 struct smb2_handle dest_h;
6226 NTSTATUS status;
6227 union smb_ioctl ioctl;
6228 TALLOC_CTX *tmp_ctx = talloc_new(tree);
6229 struct fsctl_dup_extents_to_file dup_ext_buf;
6230 enum ndr_err_code ndr_ret;
6231 union smb_setfileinfo sinfo;
6232 bool ok;
6234 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
6235 &src_h, 0, /* filled after compressed flag */
6236 SEC_RIGHTS_FILE_ALL,
6237 &dest_h, 0,
6238 SEC_RIGHTS_FILE_ALL,
6239 &dup_ext_buf,
6240 &ioctl);
6241 if (!ok) {
6242 torture_fail(tctx, "setup dup extents error");
6245 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
6246 FILE_SUPPORTS_BLOCK_REFCOUNTING
6247 | FILE_FILE_COMPRESSION, &ok);
6248 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
6249 if (!ok) {
6250 smb2_util_close(tree, src_h);
6251 smb2_util_close(tree, dest_h);
6252 talloc_free(tmp_ctx);
6253 torture_skip(tctx,
6254 "block refcounting and compressed files not supported\n");
6257 /* set compressed flag on src */
6258 status = test_ioctl_compress_set(tctx, tmp_ctx, tree, src_h,
6259 COMPRESSION_FORMAT_DEFAULT);
6260 torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_COMPRESSION");
6262 ok = write_pattern(tctx, tree, tmp_ctx, src_h, 0, 4096, 0);
6263 torture_assert(tctx, ok, "write pattern");
6265 /* extend dest */
6266 ZERO_STRUCT(sinfo);
6267 sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
6268 sinfo.end_of_file_info.in.file.handle = dest_h;
6269 sinfo.end_of_file_info.in.size = 4096;
6270 status = smb2_setinfo_file(tree, &sinfo);
6271 torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
6273 /* copy all src file data */
6274 dup_ext_buf.source_off = 0;
6275 dup_ext_buf.target_off = 0;
6276 dup_ext_buf.byte_count = 4096;
6278 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6279 &dup_ext_buf,
6280 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6281 torture_assert_ndr_success(tctx, ndr_ret,
6282 "ndr_push_fsctl_dup_extents_to_file");
6284 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6285 torture_assert_ntstatus_ok(tctx, status,
6286 "FSCTL_DUP_EXTENTS_TO_FILE");
6288 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 4096, 0);
6289 if (!ok) {
6290 torture_fail(tctx, "inconsistent file data");
6293 smb2_util_close(tree, src_h);
6294 smb2_util_close(tree, dest_h);
6295 talloc_free(tmp_ctx);
6296 return true;
6299 static bool test_ioctl_dup_extents_compressed_dest(struct torture_context *tctx,
6300 struct smb2_tree *tree)
6302 struct smb2_handle src_h;
6303 struct smb2_handle dest_h;
6304 NTSTATUS status;
6305 union smb_ioctl ioctl;
6306 TALLOC_CTX *tmp_ctx = talloc_new(tree);
6307 struct fsctl_dup_extents_to_file dup_ext_buf;
6308 enum ndr_err_code ndr_ret;
6309 union smb_setfileinfo sinfo;
6310 bool ok;
6312 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
6313 &src_h, 4096,
6314 SEC_RIGHTS_FILE_ALL,
6315 &dest_h, 0,
6316 SEC_RIGHTS_FILE_ALL,
6317 &dup_ext_buf,
6318 &ioctl);
6319 if (!ok) {
6320 torture_fail(tctx, "setup dup extents error");
6323 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
6324 FILE_SUPPORTS_BLOCK_REFCOUNTING
6325 | FILE_FILE_COMPRESSION, &ok);
6326 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
6327 if (!ok) {
6328 smb2_util_close(tree, src_h);
6329 smb2_util_close(tree, dest_h);
6330 talloc_free(tmp_ctx);
6331 torture_skip(tctx,
6332 "block refcounting and compressed files not supported\n");
6335 /* set compressed flag on dest */
6336 status = test_ioctl_compress_set(tctx, tmp_ctx, tree, dest_h,
6337 COMPRESSION_FORMAT_DEFAULT);
6338 torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_COMPRESSION");
6340 /* extend dest */
6341 ZERO_STRUCT(sinfo);
6342 sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
6343 sinfo.end_of_file_info.in.file.handle = dest_h;
6344 sinfo.end_of_file_info.in.size = 4096;
6345 status = smb2_setinfo_file(tree, &sinfo);
6346 torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
6348 /* copy all src file data */
6349 dup_ext_buf.source_off = 0;
6350 dup_ext_buf.target_off = 0;
6351 dup_ext_buf.byte_count = 4096;
6353 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6354 &dup_ext_buf,
6355 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6356 torture_assert_ndr_success(tctx, ndr_ret,
6357 "ndr_push_fsctl_dup_extents_to_file");
6359 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6360 torture_assert_ntstatus_ok(tctx, status,
6361 "FSCTL_DUP_EXTENTS_TO_FILE");
6363 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 4096, 0);
6364 if (!ok) {
6365 torture_fail(tctx, "inconsistent file data");
6368 smb2_util_close(tree, src_h);
6369 smb2_util_close(tree, dest_h);
6370 talloc_free(tmp_ctx);
6371 return true;
6374 static bool test_ioctl_dup_extents_bad_handle(struct torture_context *tctx,
6375 struct smb2_tree *tree)
6377 struct smb2_handle src_h;
6378 struct smb2_handle dest_h;
6379 struct smb2_handle bogus_h;
6380 NTSTATUS status;
6381 union smb_ioctl ioctl;
6382 TALLOC_CTX *tmp_ctx = talloc_new(tree);
6383 struct fsctl_dup_extents_to_file dup_ext_buf;
6384 enum ndr_err_code ndr_ret;
6385 bool ok;
6387 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
6388 &src_h, 32768, /* fill 32768 byte src file */
6389 SEC_RIGHTS_FILE_ALL,
6390 &dest_h, 32768,
6391 SEC_RIGHTS_FILE_ALL,
6392 &dup_ext_buf,
6393 &ioctl);
6394 if (!ok) {
6395 torture_fail(tctx, "setup dup extents error");
6398 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
6399 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
6400 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
6401 if (!ok) {
6402 smb2_util_close(tree, src_h);
6403 smb2_util_close(tree, dest_h);
6404 talloc_free(tmp_ctx);
6405 torture_skip(tctx, "block refcounting not supported\n");
6408 /* open and close a file, keeping the handle as now a "bogus" handle */
6409 ok = test_setup_create_fill(tctx, tree, tmp_ctx, "bogus_file",
6410 &bogus_h, 0, SEC_RIGHTS_FILE_ALL,
6411 FILE_ATTRIBUTE_NORMAL);
6412 torture_assert(tctx, ok, "bogus file create fill");
6413 smb2_util_close(tree, bogus_h);
6415 /* bogus dest file handle */
6416 ioctl.smb2.in.file.handle = bogus_h;
6418 dup_ext_buf.source_off = 0;
6419 dup_ext_buf.target_off = 0;
6420 dup_ext_buf.byte_count = 32768;
6422 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6423 &dup_ext_buf,
6424 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6425 torture_assert_ndr_success(tctx, ndr_ret,
6426 "ndr_push_fsctl_dup_extents_to_file");
6428 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6429 torture_assert_ntstatus_equal(tctx, status, NT_STATUS_FILE_CLOSED,
6430 "FSCTL_DUP_EXTENTS_TO_FILE");
6432 ok = check_pattern(tctx, tree, tmp_ctx, src_h, 0, 32768, 0);
6433 if (!ok) {
6434 torture_fail(tctx, "inconsistent file data");
6436 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
6437 if (!ok) {
6438 torture_fail(tctx, "inconsistent file data");
6441 /* reinstate dest, add bogus src file handle */
6442 ioctl.smb2.in.file.handle = dest_h;
6443 smb2_push_handle(dup_ext_buf.source_fid, &bogus_h);
6445 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6446 &dup_ext_buf,
6447 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6448 torture_assert_ndr_success(tctx, ndr_ret,
6449 "ndr_push_fsctl_dup_extents_to_file");
6451 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6452 torture_assert_ntstatus_equal(tctx, status, NT_STATUS_INVALID_HANDLE,
6453 "FSCTL_DUP_EXTENTS_TO_FILE");
6455 ok = check_pattern(tctx, tree, tmp_ctx, src_h, 0, 32768, 0);
6456 if (!ok) {
6457 torture_fail(tctx, "inconsistent file data");
6459 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
6460 if (!ok) {
6461 torture_fail(tctx, "inconsistent file data");
6464 smb2_util_close(tree, src_h);
6465 smb2_util_close(tree, dest_h);
6466 talloc_free(tmp_ctx);
6467 return true;
6470 static bool test_ioctl_dup_extents_src_lck(struct torture_context *tctx,
6471 struct smb2_tree *tree)
6473 struct smb2_handle src_h;
6474 struct smb2_handle src_h2;
6475 struct smb2_handle dest_h;
6476 NTSTATUS status;
6477 union smb_ioctl ioctl;
6478 TALLOC_CTX *tmp_ctx = talloc_new(tree);
6479 struct fsctl_dup_extents_to_file dup_ext_buf;
6480 enum ndr_err_code ndr_ret;
6481 bool ok;
6482 struct smb2_lock lck;
6483 struct smb2_lock_element el[1];
6485 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
6486 &src_h, 32768, /* fill 32768 byte src file */
6487 SEC_RIGHTS_FILE_ALL,
6488 &dest_h, 0,
6489 SEC_RIGHTS_FILE_ALL,
6490 &dup_ext_buf,
6491 &ioctl);
6492 if (!ok) {
6493 torture_fail(tctx, "setup dup extents error");
6496 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
6497 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
6498 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
6499 if (!ok) {
6500 smb2_util_close(tree, src_h);
6501 smb2_util_close(tree, dest_h);
6502 talloc_free(tmp_ctx);
6503 torture_skip(tctx, "block refcounting not supported\n");
6506 /* dest pattern is different to src */
6507 ok = write_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 32768);
6508 torture_assert(tctx, ok, "write pattern");
6510 /* setup dup ext req, values used for locking */
6511 dup_ext_buf.source_off = 0;
6512 dup_ext_buf.target_off = 0;
6513 dup_ext_buf.byte_count = 32768;
6515 /* open and lock the dup extents src file */
6516 status = torture_smb2_testfile(tree, FNAME, &src_h2);
6517 torture_assert_ntstatus_ok(tctx, status, "2nd src open");
6519 lck.in.lock_count = 0x0001;
6520 lck.in.lock_sequence = 0x00000000;
6521 lck.in.file.handle = src_h2;
6522 lck.in.locks = el;
6523 el[0].offset = dup_ext_buf.source_off;
6524 el[0].length = dup_ext_buf.byte_count;
6525 el[0].reserved = 0;
6526 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
6528 status = smb2_lock(tree, &lck);
6529 torture_assert_ntstatus_ok(tctx, status, "lock");
6531 status = smb2_util_write(tree, src_h,
6532 "conflicted", 0, sizeof("conflicted"));
6533 torture_assert_ntstatus_equal(tctx, status,
6534 NT_STATUS_FILE_LOCK_CONFLICT, "file write");
6536 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6537 &dup_ext_buf,
6538 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6539 torture_assert_ndr_success(tctx, ndr_ret,
6540 "ndr_push_fsctl_dup_extents_to_file");
6543 * In contrast to copy-chunk, dup extents doesn't cause a lock conflict
6544 * here.
6546 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6547 torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
6549 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
6550 if (!ok) {
6551 torture_fail(tctx, "inconsistent file data");
6554 lck.in.lock_count = 0x0001;
6555 lck.in.lock_sequence = 0x00000001;
6556 lck.in.file.handle = src_h2;
6557 lck.in.locks = el;
6558 el[0].offset = dup_ext_buf.source_off;
6559 el[0].length = dup_ext_buf.byte_count;
6560 el[0].reserved = 0;
6561 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
6562 status = smb2_lock(tree, &lck);
6563 torture_assert_ntstatus_ok(tctx, status, "unlock");
6565 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6566 torture_assert_ntstatus_ok(tctx, status,
6567 "FSCTL_DUP_EXTENTS_TO_FILE unlocked");
6569 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
6570 if (!ok) {
6571 torture_fail(tctx, "inconsistent file data");
6574 smb2_util_close(tree, src_h2);
6575 smb2_util_close(tree, src_h);
6576 smb2_util_close(tree, dest_h);
6577 talloc_free(tmp_ctx);
6578 return true;
6581 static bool test_ioctl_dup_extents_dest_lck(struct torture_context *tctx,
6582 struct smb2_tree *tree)
6584 struct smb2_handle src_h;
6585 struct smb2_handle dest_h;
6586 struct smb2_handle dest_h2;
6587 NTSTATUS status;
6588 union smb_ioctl ioctl;
6589 TALLOC_CTX *tmp_ctx = talloc_new(tree);
6590 struct fsctl_dup_extents_to_file dup_ext_buf;
6591 enum ndr_err_code ndr_ret;
6592 bool ok;
6593 struct smb2_lock lck;
6594 struct smb2_lock_element el[1];
6596 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
6597 &src_h, 32768, /* fill 32768 byte src file */
6598 SEC_RIGHTS_FILE_ALL,
6599 &dest_h, 0,
6600 SEC_RIGHTS_FILE_ALL,
6601 &dup_ext_buf,
6602 &ioctl);
6603 if (!ok) {
6604 torture_fail(tctx, "setup dup extents error");
6607 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
6608 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
6609 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
6610 if (!ok) {
6611 smb2_util_close(tree, src_h);
6612 smb2_util_close(tree, dest_h);
6613 talloc_free(tmp_ctx);
6614 torture_skip(tctx, "block refcounting not supported\n");
6617 /* dest pattern is different to src */
6618 ok = write_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 32768);
6619 torture_assert(tctx, ok, "write pattern");
6621 /* setup dup ext req, values used for locking */
6622 dup_ext_buf.source_off = 0;
6623 dup_ext_buf.target_off = 0;
6624 dup_ext_buf.byte_count = 32768;
6626 /* open and lock the dup extents dest file */
6627 status = torture_smb2_testfile(tree, FNAME2, &dest_h2);
6628 torture_assert_ntstatus_ok(tctx, status, "2nd src open");
6630 lck.in.lock_count = 0x0001;
6631 lck.in.lock_sequence = 0x00000000;
6632 lck.in.file.handle = dest_h2;
6633 lck.in.locks = el;
6634 el[0].offset = dup_ext_buf.source_off;
6635 el[0].length = dup_ext_buf.byte_count;
6636 el[0].reserved = 0;
6637 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
6639 status = smb2_lock(tree, &lck);
6640 torture_assert_ntstatus_ok(tctx, status, "lock");
6642 status = smb2_util_write(tree, dest_h,
6643 "conflicted", 0, sizeof("conflicted"));
6644 torture_assert_ntstatus_equal(tctx, status,
6645 NT_STATUS_FILE_LOCK_CONFLICT, "file write");
6647 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6648 &dup_ext_buf,
6649 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6650 torture_assert_ndr_success(tctx, ndr_ret,
6651 "ndr_push_fsctl_dup_extents_to_file");
6654 * In contrast to copy-chunk, dup extents doesn't cause a lock conflict
6655 * here.
6657 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6658 torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
6660 lck.in.lock_count = 0x0001;
6661 lck.in.lock_sequence = 0x00000001;
6662 lck.in.file.handle = dest_h2;
6663 lck.in.locks = el;
6664 el[0].offset = dup_ext_buf.source_off;
6665 el[0].length = dup_ext_buf.byte_count;
6666 el[0].reserved = 0;
6667 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
6668 status = smb2_lock(tree, &lck);
6669 torture_assert_ntstatus_ok(tctx, status, "unlock");
6671 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6672 torture_assert_ntstatus_ok(tctx, status,
6673 "FSCTL_DUP_EXTENTS_TO_FILE unlocked");
6675 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
6676 if (!ok) {
6677 torture_fail(tctx, "inconsistent file data");
6680 smb2_util_close(tree, src_h);
6681 smb2_util_close(tree, dest_h);
6682 smb2_util_close(tree, dest_h2);
6683 talloc_free(tmp_ctx);
6684 return true;
6688 * testing of SMB2 ioctls
6690 struct torture_suite *torture_smb2_ioctl_init(TALLOC_CTX *ctx)
6692 struct torture_suite *suite = torture_suite_create(ctx, "ioctl");
6694 torture_suite_add_1smb2_test(suite, "shadow_copy",
6695 test_ioctl_get_shadow_copy);
6696 torture_suite_add_1smb2_test(suite, "req_resume_key",
6697 test_ioctl_req_resume_key);
6698 torture_suite_add_1smb2_test(suite, "req_two_resume_keys",
6699 test_ioctl_req_two_resume_keys);
6700 torture_suite_add_1smb2_test(suite, "copy_chunk_simple",
6701 test_ioctl_copy_chunk_simple);
6702 torture_suite_add_1smb2_test(suite, "copy_chunk_multi",
6703 test_ioctl_copy_chunk_multi);
6704 torture_suite_add_1smb2_test(suite, "copy_chunk_tiny",
6705 test_ioctl_copy_chunk_tiny);
6706 torture_suite_add_1smb2_test(suite, "copy_chunk_overwrite",
6707 test_ioctl_copy_chunk_over);
6708 torture_suite_add_1smb2_test(suite, "copy_chunk_append",
6709 test_ioctl_copy_chunk_append);
6710 torture_suite_add_1smb2_test(suite, "copy_chunk_limits",
6711 test_ioctl_copy_chunk_limits);
6712 torture_suite_add_1smb2_test(suite, "copy_chunk_src_lock",
6713 test_ioctl_copy_chunk_src_lck);
6714 torture_suite_add_1smb2_test(suite, "copy_chunk_dest_lock",
6715 test_ioctl_copy_chunk_dest_lck);
6716 torture_suite_add_1smb2_test(suite, "copy_chunk_bad_key",
6717 test_ioctl_copy_chunk_bad_key);
6718 torture_suite_add_1smb2_test(suite, "copy_chunk_src_is_dest",
6719 test_ioctl_copy_chunk_src_is_dest);
6720 torture_suite_add_1smb2_test(suite, "copy_chunk_src_is_dest_overlap",
6721 test_ioctl_copy_chunk_src_is_dest_overlap);
6722 torture_suite_add_1smb2_test(suite, "copy_chunk_bad_access",
6723 test_ioctl_copy_chunk_bad_access);
6724 torture_suite_add_1smb2_test(suite, "copy_chunk_write_access",
6725 test_ioctl_copy_chunk_write_access);
6726 torture_suite_add_1smb2_test(suite, "copy_chunk_src_exceed",
6727 test_ioctl_copy_chunk_src_exceed);
6728 torture_suite_add_1smb2_test(suite, "copy_chunk_src_exceed_multi",
6729 test_ioctl_copy_chunk_src_exceed_multi);
6730 torture_suite_add_1smb2_test(suite, "copy_chunk_sparse_dest",
6731 test_ioctl_copy_chunk_sparse_dest);
6732 torture_suite_add_1smb2_test(suite, "copy_chunk_max_output_sz",
6733 test_ioctl_copy_chunk_max_output_sz);
6734 torture_suite_add_1smb2_test(suite, "copy_chunk_zero_length",
6735 test_ioctl_copy_chunk_zero_length);
6736 torture_suite_add_1smb2_test(suite, "copy-chunk streams",
6737 test_copy_chunk_streams);
6738 torture_suite_add_1smb2_test(suite, "copy_chunk_across_shares",
6739 test_copy_chunk_across_shares);
6740 torture_suite_add_1smb2_test(suite, "copy_chunk_across_shares2",
6741 test_copy_chunk_across_shares2);
6742 torture_suite_add_1smb2_test(suite, "copy_chunk_across_shares3",
6743 test_copy_chunk_across_shares3);
6744 torture_suite_add_1smb2_test(suite, "compress_file_flag",
6745 test_ioctl_compress_file_flag);
6746 torture_suite_add_1smb2_test(suite, "compress_dir_inherit",
6747 test_ioctl_compress_dir_inherit);
6748 torture_suite_add_1smb2_test(suite, "compress_invalid_format",
6749 test_ioctl_compress_invalid_format);
6750 torture_suite_add_1smb2_test(suite, "compress_invalid_buf",
6751 test_ioctl_compress_invalid_buf);
6752 torture_suite_add_1smb2_test(suite, "compress_query_file_attr",
6753 test_ioctl_compress_query_file_attr);
6754 torture_suite_add_1smb2_test(suite, "compress_create_with_attr",
6755 test_ioctl_compress_create_with_attr);
6756 torture_suite_add_1smb2_test(suite, "compress_inherit_disable",
6757 test_ioctl_compress_inherit_disable);
6758 torture_suite_add_1smb2_test(suite, "compress_set_file_attr",
6759 test_ioctl_compress_set_file_attr);
6760 torture_suite_add_1smb2_test(suite, "compress_perms",
6761 test_ioctl_compress_perms);
6762 torture_suite_add_1smb2_test(suite, "compress_notsup_get",
6763 test_ioctl_compress_notsup_get);
6764 torture_suite_add_1smb2_test(suite, "compress_notsup_set",
6765 test_ioctl_compress_notsup_set);
6766 torture_suite_add_1smb2_test(suite, "network_interface_info",
6767 test_ioctl_network_interface_info);
6768 torture_suite_add_1smb2_test(suite, "sparse_file_flag",
6769 test_ioctl_sparse_file_flag);
6770 torture_suite_add_1smb2_test(suite, "sparse_file_attr",
6771 test_ioctl_sparse_file_attr);
6772 torture_suite_add_1smb2_test(suite, "sparse_dir_flag",
6773 test_ioctl_sparse_dir_flag);
6774 torture_suite_add_1smb2_test(suite, "sparse_set_nobuf",
6775 test_ioctl_sparse_set_nobuf);
6776 torture_suite_add_1smb2_test(suite, "sparse_set_oversize",
6777 test_ioctl_sparse_set_oversize);
6778 torture_suite_add_1smb2_test(suite, "sparse_qar",
6779 test_ioctl_sparse_qar);
6780 torture_suite_add_1smb2_test(suite, "sparse_qar_malformed",
6781 test_ioctl_sparse_qar_malformed);
6782 torture_suite_add_1smb2_test(suite, "sparse_punch",
6783 test_ioctl_sparse_punch);
6784 torture_suite_add_1smb2_test(suite, "sparse_hole_dealloc",
6785 test_ioctl_sparse_hole_dealloc);
6786 torture_suite_add_1smb2_test(suite, "sparse_compressed",
6787 test_ioctl_sparse_compressed);
6788 torture_suite_add_1smb2_test(suite, "sparse_copy_chunk",
6789 test_ioctl_sparse_copy_chunk);
6790 torture_suite_add_1smb2_test(suite, "sparse_punch_invalid",
6791 test_ioctl_sparse_punch_invalid);
6792 torture_suite_add_1smb2_test(suite, "sparse_perms",
6793 test_ioctl_sparse_perms);
6794 torture_suite_add_1smb2_test(suite, "sparse_lock",
6795 test_ioctl_sparse_lck);
6796 torture_suite_add_1smb2_test(suite, "sparse_qar_ob1",
6797 test_ioctl_sparse_qar_ob1);
6798 torture_suite_add_1smb2_test(suite, "sparse_qar_multi",
6799 test_ioctl_sparse_qar_multi);
6800 torture_suite_add_1smb2_test(suite, "sparse_qar_overflow",
6801 test_ioctl_sparse_qar_overflow);
6802 torture_suite_add_1smb2_test(suite, "trim_simple",
6803 test_ioctl_trim_simple);
6804 torture_suite_add_1smb2_test(suite, "dup_extents_simple",
6805 test_ioctl_dup_extents_simple);
6806 torture_suite_add_1smb2_test(suite, "dup_extents_len_beyond_dest",
6807 test_ioctl_dup_extents_len_beyond_dest);
6808 torture_suite_add_1smb2_test(suite, "dup_extents_len_beyond_src",
6809 test_ioctl_dup_extents_len_beyond_src);
6810 torture_suite_add_1smb2_test(suite, "dup_extents_len_zero",
6811 test_ioctl_dup_extents_len_zero);
6812 torture_suite_add_1smb2_test(suite, "dup_extents_sparse_src",
6813 test_ioctl_dup_extents_sparse_src);
6814 torture_suite_add_1smb2_test(suite, "dup_extents_sparse_dest",
6815 test_ioctl_dup_extents_sparse_dest);
6816 torture_suite_add_1smb2_test(suite, "dup_extents_sparse_both",
6817 test_ioctl_dup_extents_sparse_both);
6818 torture_suite_add_1smb2_test(suite, "dup_extents_src_is_dest",
6819 test_ioctl_dup_extents_src_is_dest);
6820 torture_suite_add_1smb2_test(suite, "dup_extents_src_is_dest_overlap",
6821 test_ioctl_dup_extents_src_is_dest_overlap);
6822 torture_suite_add_1smb2_test(suite, "dup_extents_compressed_src",
6823 test_ioctl_dup_extents_compressed_src);
6824 torture_suite_add_1smb2_test(suite, "dup_extents_compressed_dest",
6825 test_ioctl_dup_extents_compressed_dest);
6826 torture_suite_add_1smb2_test(suite, "dup_extents_bad_handle",
6827 test_ioctl_dup_extents_bad_handle);
6828 torture_suite_add_1smb2_test(suite, "dup_extents_src_lock",
6829 test_ioctl_dup_extents_src_lck);
6830 torture_suite_add_1smb2_test(suite, "dup_extents_dest_lock",
6831 test_ioctl_dup_extents_dest_lck);
6833 suite->description = talloc_strdup(suite, "SMB2-IOCTL tests");
6835 return suite;