ctdb-scripts: Support storing statd-callout state in cluster filesystem
[samba4-gss.git] / source4 / torture / smb2 / ioctl.c
blob7979e129ba7ddb6f4ee2a8939491867c0eb0eee9
1 /*
2 Unix SMB/CIFS implementation.
4 test suite for SMB2 ioctl operations
6 Copyright (C) David Disseldorp 2011-2024
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"
30 #include "lib/cmdline/cmdline.h"
31 #include "libcli/resolve/resolve.h"
32 #include "lib/param/param.h"
33 #include "lib/util/tevent_ntstatus.h"
35 #define FNAME "testfsctl.dat"
36 #define FNAME2 "testfsctl2.dat"
37 #define DNAME "testfsctl_dir"
40 basic testing of SMB2 shadow copy calls
42 static bool test_ioctl_get_shadow_copy(struct torture_context *torture,
43 struct smb2_tree *tree)
45 struct smb2_handle h;
46 uint8_t buf[100];
47 NTSTATUS status;
48 union smb_ioctl ioctl;
49 TALLOC_CTX *tmp_ctx = talloc_new(tree);
51 smb2_util_unlink(tree, FNAME);
53 status = torture_smb2_testfile(tree, FNAME, &h);
54 torture_assert_ntstatus_ok(torture, status, "create write");
56 ZERO_ARRAY(buf);
57 status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf));
58 torture_assert_ntstatus_ok(torture, status, "write");
60 ZERO_STRUCT(ioctl);
61 ioctl.smb2.level = RAW_IOCTL_SMB2;
62 ioctl.smb2.in.file.handle = h;
63 ioctl.smb2.in.function = FSCTL_SRV_ENUM_SNAPS;
64 ioctl.smb2.in.max_output_response = 16;
65 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
67 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
68 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)
69 || NT_STATUS_EQUAL(status, NT_STATUS_INVALID_DEVICE_REQUEST)) {
70 torture_skip(torture, "FSCTL_SRV_ENUM_SNAPS not supported\n");
72 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_ENUM_SNAPS");
74 return true;
78 basic testing of the SMB2 server side copy ioctls
80 static bool test_ioctl_req_resume_key(struct torture_context *torture,
81 struct smb2_tree *tree)
83 struct smb2_handle h;
84 uint8_t buf[100];
85 NTSTATUS status;
86 union smb_ioctl ioctl;
87 TALLOC_CTX *tmp_ctx = talloc_new(tree);
88 struct req_resume_key_rsp res_key;
89 enum ndr_err_code ndr_ret;
91 smb2_util_unlink(tree, FNAME);
93 status = torture_smb2_testfile(tree, FNAME, &h);
94 torture_assert_ntstatus_ok(torture, status, "create write");
96 ZERO_ARRAY(buf);
97 status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf));
98 torture_assert_ntstatus_ok(torture, status, "write");
100 ZERO_STRUCT(ioctl);
101 ioctl.smb2.level = RAW_IOCTL_SMB2;
102 ioctl.smb2.in.file.handle = h;
103 ioctl.smb2.in.function = FSCTL_SRV_REQUEST_RESUME_KEY;
104 ioctl.smb2.in.max_output_response = 32;
105 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
107 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
108 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_REQUEST_RESUME_KEY");
110 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, &res_key,
111 (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
112 torture_assert_ndr_success(torture, ndr_ret,
113 "ndr_pull_req_resume_key_rsp");
115 NDR_PRINT_DEBUG(req_resume_key_rsp, &res_key);
117 talloc_free(tmp_ctx);
118 return true;
122 testing fetching a resume key twice for one file handle
124 static bool test_ioctl_req_two_resume_keys(struct torture_context *torture,
125 struct smb2_tree *tree)
127 struct smb2_handle h;
128 uint8_t buf[100];
129 NTSTATUS status;
130 union smb_ioctl ioctl;
131 TALLOC_CTX *tmp_ctx = talloc_new(tree);
132 struct req_resume_key_rsp res_key;
133 enum ndr_err_code ndr_ret;
135 smb2_util_unlink(tree, FNAME);
137 status = torture_smb2_testfile(tree, FNAME, &h);
138 torture_assert_ntstatus_ok(torture, status, "create write");
140 ZERO_ARRAY(buf);
141 status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf));
142 torture_assert_ntstatus_ok(torture, status, "write");
144 ZERO_STRUCT(ioctl);
145 ioctl.smb2.level = RAW_IOCTL_SMB2;
146 ioctl.smb2.in.file.handle = h;
147 ioctl.smb2.in.function = FSCTL_SRV_REQUEST_RESUME_KEY;
148 ioctl.smb2.in.max_output_response = 32;
149 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
151 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
152 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_REQUEST_RESUME_KEY");
154 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, &res_key,
155 (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
156 torture_assert_ndr_success(torture, ndr_ret,
157 "ndr_pull_req_resume_key_rsp");
159 NDR_PRINT_DEBUG(req_resume_key_rsp, &res_key);
161 ZERO_STRUCT(ioctl);
162 ioctl.smb2.level = RAW_IOCTL_SMB2;
163 ioctl.smb2.in.file.handle = h;
164 ioctl.smb2.in.function = FSCTL_SRV_REQUEST_RESUME_KEY;
165 ioctl.smb2.in.max_output_response = 32;
166 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
168 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
169 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_REQUEST_RESUME_KEY");
171 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, &res_key,
172 (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
173 torture_assert_ndr_success(torture, ndr_ret,
174 "ndr_pull_req_resume_key_rsp");
176 NDR_PRINT_DEBUG(req_resume_key_rsp, &res_key);
178 talloc_free(tmp_ctx);
179 return true;
182 static uint64_t patt_hash(uint64_t off)
184 return off;
187 static bool write_pattern(struct torture_context *torture,
188 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
189 struct smb2_handle h, uint64_t off, uint64_t len,
190 uint64_t patt_off)
192 NTSTATUS status;
193 uint64_t i;
194 uint8_t *buf;
195 uint64_t io_sz = MIN(1024 * 64, len);
197 if (len == 0) {
198 return true;
201 torture_assert(torture, (len % 8) == 0, "invalid write len");
203 buf = talloc_zero_size(mem_ctx, io_sz);
204 torture_assert(torture, (buf != NULL), "no memory for file data buf");
206 while (len > 0) {
207 for (i = 0; i <= io_sz - 8; i += 8) {
208 SBVAL(buf, i, patt_hash(patt_off));
209 patt_off += 8;
212 status = smb2_util_write(tree, h,
213 buf, off, io_sz);
214 torture_assert_ntstatus_ok(torture, status, "file write");
216 len -= io_sz;
217 off += io_sz;
220 talloc_free(buf);
222 return true;
225 static bool check_pattern(struct torture_context *torture,
226 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
227 struct smb2_handle h, uint64_t off, uint64_t len,
228 uint64_t patt_off)
230 if (len == 0) {
231 return true;
234 torture_assert(torture, (len % 8) == 0, "invalid read len");
236 while (len > 0) {
237 uint64_t i;
238 struct smb2_read r;
239 NTSTATUS status;
240 uint64_t io_sz = MIN(1024 * 64, len);
242 ZERO_STRUCT(r);
243 r.in.file.handle = h;
244 r.in.length = io_sz;
245 r.in.offset = off;
246 status = smb2_read(tree, mem_ctx, &r);
247 torture_assert_ntstatus_ok(torture, status, "read");
249 torture_assert_u64_equal(torture, r.out.data.length, io_sz,
250 "read data len mismatch");
252 for (i = 0; i <= io_sz - 8; i += 8, patt_off += 8) {
253 uint64_t data = BVAL(r.out.data.data, i);
254 torture_assert_u64_equal(torture, data, patt_hash(patt_off),
255 talloc_asprintf(torture, "read data "
256 "pattern bad at %llu\n",
257 (unsigned long long)off + i));
259 talloc_free(r.out.data.data);
260 len -= io_sz;
261 off += io_sz;
264 return true;
267 static bool check_zero(struct torture_context *torture,
268 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
269 struct smb2_handle h, uint64_t off, uint64_t len)
271 uint64_t i;
272 struct smb2_read r;
273 NTSTATUS status;
275 if (len == 0) {
276 return true;
279 ZERO_STRUCT(r);
280 r.in.file.handle = h;
281 r.in.length = len;
282 r.in.offset = off;
283 status = smb2_read(tree, mem_ctx, &r);
284 torture_assert_ntstatus_ok(torture, status, "read");
286 torture_assert_u64_equal(torture, r.out.data.length, len,
287 "read data len mismatch");
289 for (i = 0; i <= len - 8; i += 8) {
290 uint64_t data = BVAL(r.out.data.data, i);
291 torture_assert_u64_equal(torture, data, 0,
292 talloc_asprintf(mem_ctx, "read zero "
293 "bad at %llu\n",
294 (unsigned long long)i));
297 talloc_free(r.out.data.data);
298 return true;
301 static bool test_setup_open(struct torture_context *torture,
302 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
303 const char *fname,
304 struct smb2_handle *fh,
305 uint32_t desired_access,
306 uint32_t file_attributes)
308 struct smb2_create io;
309 NTSTATUS status;
311 ZERO_STRUCT(io);
312 io.in.desired_access = desired_access;
313 io.in.file_attributes = file_attributes;
314 io.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
315 io.in.share_access =
316 NTCREATEX_SHARE_ACCESS_DELETE|
317 NTCREATEX_SHARE_ACCESS_READ|
318 NTCREATEX_SHARE_ACCESS_WRITE;
319 if (file_attributes & FILE_ATTRIBUTE_DIRECTORY) {
320 io.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
322 io.in.fname = fname;
324 status = smb2_create(tree, mem_ctx, &io);
325 torture_assert_ntstatus_ok(torture, status, "file create");
327 *fh = io.out.file.handle;
329 return true;
332 static bool test_setup_create_fill(struct torture_context *torture,
333 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
334 const char *fname,
335 struct smb2_handle *fh,
336 uint64_t size,
337 uint32_t desired_access,
338 uint32_t file_attributes)
340 bool ok;
341 uint32_t initial_access = desired_access;
343 if (size > 0) {
344 initial_access |= SEC_FILE_APPEND_DATA;
347 smb2_util_unlink(tree, fname);
349 ok = test_setup_open(torture, tree, mem_ctx,
350 fname,
352 initial_access,
353 file_attributes);
354 torture_assert(torture, ok, "file create");
356 if (size > 0) {
357 ok = write_pattern(torture, tree, mem_ctx, *fh, 0, size, 0);
358 torture_assert(torture, ok, "write pattern");
361 if (initial_access != desired_access) {
362 smb2_util_close(tree, *fh);
363 ok = test_setup_open(torture, tree, mem_ctx,
364 fname,
366 desired_access,
367 file_attributes);
368 torture_assert(torture, ok, "file open");
371 return true;
374 static bool test_setup_copy_chunk(struct torture_context *torture,
375 struct smb2_tree *src_tree,
376 struct smb2_tree *dst_tree,
377 TALLOC_CTX *mem_ctx,
378 uint32_t nchunks,
379 const char *src_name,
380 struct smb2_handle *src_h,
381 uint64_t src_size,
382 uint32_t src_desired_access,
383 const char *dst_name,
384 struct smb2_handle *dest_h,
385 uint64_t dest_size,
386 uint32_t dest_desired_access,
387 struct srv_copychunk_copy *cc_copy,
388 union smb_ioctl *ioctl)
390 struct req_resume_key_rsp res_key;
391 bool ok;
392 NTSTATUS status;
393 enum ndr_err_code ndr_ret;
395 ok = test_setup_create_fill(torture, src_tree, mem_ctx, src_name,
396 src_h, src_size, src_desired_access,
397 FILE_ATTRIBUTE_NORMAL);
398 torture_assert(torture, ok, "src file create fill");
400 ok = test_setup_create_fill(torture, dst_tree, mem_ctx, dst_name,
401 dest_h, dest_size, dest_desired_access,
402 FILE_ATTRIBUTE_NORMAL);
403 torture_assert(torture, ok, "dest file create fill");
405 ZERO_STRUCTPN(ioctl);
406 ioctl->smb2.level = RAW_IOCTL_SMB2;
407 ioctl->smb2.in.file.handle = *src_h;
408 ioctl->smb2.in.function = FSCTL_SRV_REQUEST_RESUME_KEY;
409 /* Allow for Key + ContextLength + Context */
410 ioctl->smb2.in.max_output_response = 32;
411 ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
413 status = smb2_ioctl(src_tree, mem_ctx, &ioctl->smb2);
414 torture_assert_ntstatus_ok(torture, status,
415 "FSCTL_SRV_REQUEST_RESUME_KEY");
417 ndr_ret = ndr_pull_struct_blob(&ioctl->smb2.out.out, mem_ctx, &res_key,
418 (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
420 torture_assert_ndr_success(torture, ndr_ret,
421 "ndr_pull_req_resume_key_rsp");
423 ZERO_STRUCTPN(ioctl);
424 ioctl->smb2.level = RAW_IOCTL_SMB2;
425 ioctl->smb2.in.file.handle = *dest_h;
426 ioctl->smb2.in.function = FSCTL_SRV_COPYCHUNK;
427 ioctl->smb2.in.max_output_response = sizeof(struct srv_copychunk_rsp);
428 ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
430 ZERO_STRUCTPN(cc_copy);
431 memcpy(cc_copy->source_key, res_key.resume_key, ARRAY_SIZE(cc_copy->source_key));
432 cc_copy->chunk_count = nchunks;
433 cc_copy->chunks = talloc_zero_array(mem_ctx, struct srv_copychunk, nchunks);
434 torture_assert(torture, (cc_copy->chunks != NULL), "no memory for chunks");
436 return true;
440 static bool check_copy_chunk_rsp(struct torture_context *torture,
441 struct srv_copychunk_rsp *cc_rsp,
442 uint32_t ex_chunks_written,
443 uint32_t ex_chunk_bytes_written,
444 uint32_t ex_total_bytes_written)
446 torture_assert_int_equal(torture, cc_rsp->chunks_written,
447 ex_chunks_written, "num chunks");
448 torture_assert_int_equal(torture, cc_rsp->chunk_bytes_written,
449 ex_chunk_bytes_written, "chunk bytes written");
450 torture_assert_int_equal(torture, cc_rsp->total_bytes_written,
451 ex_total_bytes_written, "chunk total bytes");
452 return true;
455 static bool test_ioctl_copy_chunk_simple(struct torture_context *torture,
456 struct smb2_tree *tree)
458 struct smb2_handle src_h;
459 struct smb2_handle dest_h;
460 NTSTATUS status;
461 union smb_ioctl ioctl;
462 TALLOC_CTX *tmp_ctx = talloc_new(tree);
463 struct srv_copychunk_copy cc_copy;
464 struct srv_copychunk_rsp cc_rsp;
465 enum ndr_err_code ndr_ret;
466 bool ok;
468 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
469 1, /* 1 chunk */
470 FNAME,
471 &src_h, 4096, /* fill 4096 byte src file */
472 SEC_RIGHTS_FILE_ALL,
473 FNAME2,
474 &dest_h, 0, /* 0 byte dest file */
475 SEC_RIGHTS_FILE_ALL,
476 &cc_copy,
477 &ioctl);
478 if (!ok) {
479 torture_fail(torture, "setup copy chunk error");
482 /* copy all src file data (via a single chunk desc) */
483 cc_copy.chunks[0].source_off = 0;
484 cc_copy.chunks[0].target_off = 0;
485 cc_copy.chunks[0].length = 4096;
487 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
488 &cc_copy,
489 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
490 torture_assert_ndr_success(torture, ndr_ret,
491 "ndr_push_srv_copychunk_copy");
493 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
494 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
496 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
497 &cc_rsp,
498 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
499 torture_assert_ndr_success(torture, ndr_ret,
500 "ndr_pull_srv_copychunk_rsp");
502 ok = check_copy_chunk_rsp(torture, &cc_rsp,
503 1, /* chunks written */
504 0, /* chunk bytes unsuccessfully written */
505 4096); /* total bytes written */
506 if (!ok) {
507 torture_fail(torture, "bad copy chunk response data");
510 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
511 if (!ok) {
512 torture_fail(torture, "inconsistent file data");
515 smb2_util_close(tree, src_h);
516 smb2_util_close(tree, dest_h);
517 talloc_free(tmp_ctx);
518 return true;
521 static bool test_ioctl_copy_chunk_multi(struct torture_context *torture,
522 struct smb2_tree *tree)
524 struct smb2_handle src_h;
525 struct smb2_handle dest_h;
526 NTSTATUS status;
527 union smb_ioctl ioctl;
528 TALLOC_CTX *tmp_ctx = talloc_new(tree);
529 struct srv_copychunk_copy cc_copy;
530 struct srv_copychunk_rsp cc_rsp;
531 enum ndr_err_code ndr_ret;
532 bool ok;
534 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
535 2, /* chunks */
536 FNAME,
537 &src_h, 8192, /* src file */
538 SEC_RIGHTS_FILE_ALL,
539 FNAME2,
540 &dest_h, 0, /* dest file */
541 SEC_RIGHTS_FILE_ALL,
542 &cc_copy,
543 &ioctl);
544 if (!ok) {
545 torture_fail(torture, "setup copy chunk error");
548 /* copy all src file data via two chunks */
549 cc_copy.chunks[0].source_off = 0;
550 cc_copy.chunks[0].target_off = 0;
551 cc_copy.chunks[0].length = 4096;
553 cc_copy.chunks[1].source_off = 4096;
554 cc_copy.chunks[1].target_off = 4096;
555 cc_copy.chunks[1].length = 4096;
557 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
558 &cc_copy,
559 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
560 torture_assert_ndr_success(torture, ndr_ret,
561 "ndr_push_srv_copychunk_copy");
563 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
564 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
566 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
567 &cc_rsp,
568 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
569 torture_assert_ndr_success(torture, ndr_ret,
570 "ndr_pull_srv_copychunk_rsp");
572 ok = check_copy_chunk_rsp(torture, &cc_rsp,
573 2, /* chunks written */
574 0, /* chunk bytes unsuccessfully written */
575 8192); /* total bytes written */
576 if (!ok) {
577 torture_fail(torture, "bad copy chunk response data");
580 smb2_util_close(tree, src_h);
581 smb2_util_close(tree, dest_h);
582 talloc_free(tmp_ctx);
583 return true;
586 static bool test_ioctl_copy_chunk_tiny(struct torture_context *torture,
587 struct smb2_tree *tree)
589 struct smb2_handle src_h;
590 struct smb2_handle dest_h;
591 NTSTATUS status;
592 union smb_ioctl ioctl;
593 TALLOC_CTX *tmp_ctx = talloc_new(tree);
594 struct srv_copychunk_copy cc_copy;
595 struct srv_copychunk_rsp cc_rsp;
596 enum ndr_err_code ndr_ret;
597 bool ok;
599 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
600 2, /* chunks */
601 FNAME,
602 &src_h, 96, /* src file */
603 SEC_RIGHTS_FILE_ALL,
604 FNAME2,
605 &dest_h, 0, /* dest file */
606 SEC_RIGHTS_FILE_ALL,
607 &cc_copy,
608 &ioctl);
609 if (!ok) {
610 torture_fail(torture, "setup copy chunk error");
613 /* copy all src file data via two chunks, sub block size chunks */
614 cc_copy.chunks[0].source_off = 0;
615 cc_copy.chunks[0].target_off = 0;
616 cc_copy.chunks[0].length = 48;
618 cc_copy.chunks[1].source_off = 48;
619 cc_copy.chunks[1].target_off = 48;
620 cc_copy.chunks[1].length = 48;
622 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
623 &cc_copy,
624 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
625 torture_assert_ndr_success(torture, ndr_ret,
626 "ndr_push_srv_copychunk_copy");
628 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
629 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
631 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
632 &cc_rsp,
633 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
634 torture_assert_ndr_success(torture, ndr_ret,
635 "ndr_pull_srv_copychunk_rsp");
637 ok = check_copy_chunk_rsp(torture, &cc_rsp,
638 2, /* chunks written */
639 0, /* chunk bytes unsuccessfully written */
640 96); /* total bytes written */
641 if (!ok) {
642 torture_fail(torture, "bad copy chunk response data");
645 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 96, 0);
646 if (!ok) {
647 torture_fail(torture, "inconsistent file data");
650 smb2_util_close(tree, src_h);
651 smb2_util_close(tree, dest_h);
652 talloc_free(tmp_ctx);
653 return true;
656 static bool test_ioctl_copy_chunk_over(struct torture_context *torture,
657 struct smb2_tree *tree)
659 struct smb2_handle src_h;
660 struct smb2_handle dest_h;
661 NTSTATUS status;
662 union smb_ioctl ioctl;
663 TALLOC_CTX *tmp_ctx = talloc_new(tree);
664 struct srv_copychunk_copy cc_copy;
665 struct srv_copychunk_rsp cc_rsp;
666 enum ndr_err_code ndr_ret;
667 bool ok;
669 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
670 2, /* chunks */
671 FNAME,
672 &src_h, 8192, /* src file */
673 SEC_RIGHTS_FILE_ALL,
674 FNAME2,
675 &dest_h, 4096, /* dest file */
676 SEC_RIGHTS_FILE_ALL,
677 &cc_copy,
678 &ioctl);
679 if (!ok) {
680 torture_fail(torture, "setup copy chunk error");
683 /* first chunk overwrites existing dest data */
684 cc_copy.chunks[0].source_off = 0;
685 cc_copy.chunks[0].target_off = 0;
686 cc_copy.chunks[0].length = 4096;
688 /* second chunk overwrites the first */
689 cc_copy.chunks[1].source_off = 4096;
690 cc_copy.chunks[1].target_off = 0;
691 cc_copy.chunks[1].length = 4096;
693 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
694 &cc_copy,
695 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
696 torture_assert_ndr_success(torture, ndr_ret,
697 "ndr_push_srv_copychunk_copy");
699 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
700 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
702 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
703 &cc_rsp,
704 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
705 torture_assert_ndr_success(torture, ndr_ret,
706 "ndr_pull_srv_copychunk_rsp");
708 ok = check_copy_chunk_rsp(torture, &cc_rsp,
709 2, /* chunks written */
710 0, /* chunk bytes unsuccessfully written */
711 8192); /* total bytes written */
712 if (!ok) {
713 torture_fail(torture, "bad copy chunk response data");
716 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 4096);
717 if (!ok) {
718 torture_fail(torture, "inconsistent file data");
721 smb2_util_close(tree, src_h);
722 smb2_util_close(tree, dest_h);
723 talloc_free(tmp_ctx);
724 return true;
727 static bool test_ioctl_copy_chunk_append(struct torture_context *torture,
728 struct smb2_tree *tree)
730 struct smb2_handle src_h;
731 struct smb2_handle dest_h;
732 NTSTATUS status;
733 union smb_ioctl ioctl;
734 TALLOC_CTX *tmp_ctx = talloc_new(tree);
735 struct srv_copychunk_copy cc_copy;
736 struct srv_copychunk_rsp cc_rsp;
737 enum ndr_err_code ndr_ret;
738 bool ok;
740 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
741 2, /* chunks */
742 FNAME,
743 &src_h, 4096, /* src file */
744 SEC_RIGHTS_FILE_ALL,
745 FNAME2,
746 &dest_h, 0, /* dest file */
747 SEC_RIGHTS_FILE_ALL,
748 &cc_copy,
749 &ioctl);
750 if (!ok) {
751 torture_fail(torture, "setup copy chunk error");
754 cc_copy.chunks[0].source_off = 0;
755 cc_copy.chunks[0].target_off = 0;
756 cc_copy.chunks[0].length = 4096;
758 /* second chunk appends the same data to the first */
759 cc_copy.chunks[1].source_off = 0;
760 cc_copy.chunks[1].target_off = 4096;
761 cc_copy.chunks[1].length = 4096;
763 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
764 &cc_copy,
765 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
766 torture_assert_ndr_success(torture, ndr_ret,
767 "ndr_push_srv_copychunk_copy");
769 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
770 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
772 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
773 &cc_rsp,
774 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
775 torture_assert_ndr_success(torture, ndr_ret,
776 "ndr_pull_srv_copychunk_rsp");
778 ok = check_copy_chunk_rsp(torture, &cc_rsp,
779 2, /* chunks written */
780 0, /* chunk bytes unsuccessfully written */
781 8192); /* total bytes written */
782 if (!ok) {
783 torture_fail(torture, "bad copy chunk response data");
786 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
787 if (!ok) {
788 torture_fail(torture, "inconsistent file data");
791 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 4096, 4096, 0);
792 if (!ok) {
793 torture_fail(torture, "inconsistent file data");
796 smb2_util_close(tree, src_h);
797 smb2_util_close(tree, dest_h);
798 talloc_free(tmp_ctx);
799 return true;
802 static bool test_ioctl_copy_chunk_limits(struct torture_context *torture,
803 struct smb2_tree *tree)
805 struct smb2_handle src_h;
806 struct smb2_handle dest_h;
807 NTSTATUS status;
808 union smb_ioctl ioctl;
809 TALLOC_CTX *tmp_ctx = talloc_new(tree);
810 struct srv_copychunk_copy cc_copy;
811 struct srv_copychunk_rsp cc_rsp;
812 enum ndr_err_code ndr_ret;
813 bool ok;
815 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
816 1, /* chunks */
817 FNAME,
818 &src_h, 4096, /* src file */
819 SEC_RIGHTS_FILE_ALL,
820 FNAME2,
821 &dest_h, 0, /* dest file */
822 SEC_RIGHTS_FILE_ALL,
823 &cc_copy,
824 &ioctl);
825 if (!ok) {
826 torture_fail(torture, "setup copy chunk error");
829 /* send huge chunk length request */
830 cc_copy.chunks[0].source_off = 0;
831 cc_copy.chunks[0].target_off = 0;
832 cc_copy.chunks[0].length = UINT_MAX;
834 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
835 &cc_copy,
836 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
837 torture_assert_ndr_success(torture, ndr_ret, "marshalling request");
839 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
840 torture_assert_ntstatus_equal(torture, status,
841 NT_STATUS_INVALID_PARAMETER,
842 "bad oversize chunk response");
844 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
845 &cc_rsp,
846 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
847 torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
849 torture_comment(torture, "limit max chunks, got %u\n",
850 cc_rsp.chunks_written);
851 torture_comment(torture, "limit max chunk len, got %u\n",
852 cc_rsp.chunk_bytes_written);
853 torture_comment(torture, "limit max total bytes, got %u\n",
854 cc_rsp.total_bytes_written);
856 smb2_util_close(tree, src_h);
857 smb2_util_close(tree, dest_h);
858 talloc_free(tmp_ctx);
859 return true;
862 static bool test_ioctl_copy_chunk_src_lck(struct torture_context *torture,
863 struct smb2_tree *tree)
865 struct smb2_handle src_h;
866 struct smb2_handle src_h2;
867 struct smb2_handle dest_h;
868 NTSTATUS status;
869 union smb_ioctl ioctl;
870 TALLOC_CTX *tmp_ctx = talloc_new(tree);
871 struct srv_copychunk_copy cc_copy;
872 struct srv_copychunk_rsp cc_rsp;
873 enum ndr_err_code ndr_ret;
874 bool ok;
875 struct smb2_lock lck;
876 struct smb2_lock_element el[1];
878 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
879 1, /* chunks */
880 FNAME,
881 &src_h, 4096, /* src file */
882 SEC_RIGHTS_FILE_ALL,
883 FNAME2,
884 &dest_h, 0, /* dest file */
885 SEC_RIGHTS_FILE_ALL,
886 &cc_copy,
887 &ioctl);
888 if (!ok) {
889 torture_fail(torture, "setup copy chunk error");
892 cc_copy.chunks[0].source_off = 0;
893 cc_copy.chunks[0].target_off = 0;
894 cc_copy.chunks[0].length = 4096;
896 /* open and lock the copychunk src file */
897 status = torture_smb2_testfile(tree, FNAME, &src_h2);
898 torture_assert_ntstatus_ok(torture, status, "2nd src open");
900 lck.in.lock_count = 0x0001;
901 lck.in.lock_sequence = 0x00000000;
902 lck.in.file.handle = src_h2;
903 lck.in.locks = el;
904 el[0].offset = cc_copy.chunks[0].source_off;
905 el[0].length = cc_copy.chunks[0].length;
906 el[0].reserved = 0;
907 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
909 status = smb2_lock(tree, &lck);
910 torture_assert_ntstatus_ok(torture, status, "lock");
912 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
913 &cc_copy,
914 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
915 torture_assert_ndr_success(torture, ndr_ret,
916 "ndr_push_srv_copychunk_copy");
918 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
920 * 2k12 & Samba return lock_conflict, Windows 7 & 2k8 return success...
922 * Edgar Olougouna @ MS wrote:
923 * Regarding the FSCTL_SRV_COPYCHUNK and STATUS_FILE_LOCK_CONFLICT
924 * discrepancy observed between Windows versions, we confirm that the
925 * behavior change is expected.
927 * CopyChunk in Windows Server 2012 use regular Readfile/Writefile APIs
928 * to move the chunks from the source to the destination.
929 * These ReadFile/WriteFile APIs go through the byte-range lock checks,
930 * and this explains the observed STATUS_FILE_LOCK_CONFLICT error.
932 * Prior to Windows Server 2012, CopyChunk used mapped sections to move
933 * the data. And byte range locks are not enforced on mapped I/O, and
934 * this explains the STATUS_SUCCESS observed on Windows Server 2008 R2.
936 torture_assert_ntstatus_equal(torture, status,
937 NT_STATUS_FILE_LOCK_CONFLICT,
938 "FSCTL_SRV_COPYCHUNK locked");
940 /* should get cc response data with the lock conflict status */
941 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
942 &cc_rsp,
943 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
944 torture_assert_ndr_success(torture, ndr_ret,
945 "ndr_pull_srv_copychunk_rsp");
946 ok = check_copy_chunk_rsp(torture, &cc_rsp,
947 0, /* chunks written */
948 0, /* chunk bytes unsuccessfully written */
949 0); /* total bytes written */
951 lck.in.lock_count = 0x0001;
952 lck.in.lock_sequence = 0x00000001;
953 lck.in.file.handle = src_h2;
954 lck.in.locks = el;
955 el[0].offset = cc_copy.chunks[0].source_off;
956 el[0].length = cc_copy.chunks[0].length;
957 el[0].reserved = 0;
958 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
959 status = smb2_lock(tree, &lck);
960 torture_assert_ntstatus_ok(torture, status, "unlock");
962 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
963 torture_assert_ntstatus_ok(torture, status,
964 "FSCTL_SRV_COPYCHUNK unlocked");
966 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
967 &cc_rsp,
968 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
969 torture_assert_ndr_success(torture, ndr_ret,
970 "ndr_pull_srv_copychunk_rsp");
972 ok = check_copy_chunk_rsp(torture, &cc_rsp,
973 1, /* chunks written */
974 0, /* chunk bytes unsuccessfully written */
975 4096); /* total bytes written */
976 if (!ok) {
977 torture_fail(torture, "bad copy chunk response data");
980 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
981 if (!ok) {
982 torture_fail(torture, "inconsistent file data");
985 smb2_util_close(tree, src_h2);
986 smb2_util_close(tree, src_h);
987 smb2_util_close(tree, dest_h);
988 talloc_free(tmp_ctx);
989 return true;
992 static bool test_ioctl_copy_chunk_dest_lck(struct torture_context *torture,
993 struct smb2_tree *tree)
995 struct smb2_handle src_h;
996 struct smb2_handle dest_h;
997 struct smb2_handle dest_h2;
998 NTSTATUS status;
999 union smb_ioctl ioctl;
1000 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1001 struct srv_copychunk_copy cc_copy;
1002 struct srv_copychunk_rsp cc_rsp;
1003 enum ndr_err_code ndr_ret;
1004 bool ok;
1005 struct smb2_lock lck;
1006 struct smb2_lock_element el[1];
1008 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
1009 1, /* chunks */
1010 FNAME,
1011 &src_h, 4096, /* src file */
1012 SEC_RIGHTS_FILE_ALL,
1013 FNAME2,
1014 &dest_h, 4096, /* dest file */
1015 SEC_RIGHTS_FILE_ALL,
1016 &cc_copy,
1017 &ioctl);
1018 if (!ok) {
1019 torture_fail(torture, "setup copy chunk error");
1022 cc_copy.chunks[0].source_off = 0;
1023 cc_copy.chunks[0].target_off = 0;
1024 cc_copy.chunks[0].length = 4096;
1026 /* open and lock the copychunk dest file */
1027 status = torture_smb2_testfile(tree, FNAME2, &dest_h2);
1028 torture_assert_ntstatus_ok(torture, status, "2nd src open");
1030 lck.in.lock_count = 0x0001;
1031 lck.in.lock_sequence = 0x00000000;
1032 lck.in.file.handle = dest_h2;
1033 lck.in.locks = el;
1034 el[0].offset = cc_copy.chunks[0].target_off;
1035 el[0].length = cc_copy.chunks[0].length;
1036 el[0].reserved = 0;
1037 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
1039 status = smb2_lock(tree, &lck);
1040 torture_assert_ntstatus_ok(torture, status, "lock");
1042 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1043 &cc_copy,
1044 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1045 torture_assert_ndr_success(torture, ndr_ret,
1046 "ndr_push_srv_copychunk_copy");
1048 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1049 torture_assert_ntstatus_equal(torture, status,
1050 NT_STATUS_FILE_LOCK_CONFLICT,
1051 "FSCTL_SRV_COPYCHUNK locked");
1053 lck.in.lock_count = 0x0001;
1054 lck.in.lock_sequence = 0x00000001;
1055 lck.in.file.handle = dest_h2;
1056 lck.in.locks = el;
1057 el[0].offset = cc_copy.chunks[0].target_off;
1058 el[0].length = cc_copy.chunks[0].length;
1059 el[0].reserved = 0;
1060 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
1061 status = smb2_lock(tree, &lck);
1062 torture_assert_ntstatus_ok(torture, status, "unlock");
1064 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1065 torture_assert_ntstatus_ok(torture, status,
1066 "FSCTL_SRV_COPYCHUNK unlocked");
1068 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1069 &cc_rsp,
1070 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1071 torture_assert_ndr_success(torture, ndr_ret,
1072 "ndr_pull_srv_copychunk_rsp");
1074 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1075 1, /* chunks written */
1076 0, /* chunk bytes unsuccessfully written */
1077 4096); /* total bytes written */
1078 if (!ok) {
1079 torture_fail(torture, "bad copy chunk response data");
1082 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
1083 if (!ok) {
1084 torture_fail(torture, "inconsistent file data");
1087 smb2_util_close(tree, dest_h2);
1088 smb2_util_close(tree, src_h);
1089 smb2_util_close(tree, dest_h);
1090 talloc_free(tmp_ctx);
1091 return true;
1094 static bool test_ioctl_copy_chunk_bad_key(struct torture_context *torture,
1095 struct smb2_tree *tree)
1097 struct smb2_handle src_h;
1098 struct smb2_handle dest_h;
1099 NTSTATUS status;
1100 union smb_ioctl ioctl;
1101 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1102 struct srv_copychunk_copy cc_copy;
1103 enum ndr_err_code ndr_ret;
1104 bool ok;
1106 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
1108 FNAME,
1109 &src_h, 4096,
1110 SEC_RIGHTS_FILE_ALL,
1111 FNAME2,
1112 &dest_h, 0,
1113 SEC_RIGHTS_FILE_ALL,
1114 &cc_copy,
1115 &ioctl);
1116 if (!ok) {
1117 torture_fail(torture, "setup copy chunk error");
1120 /* overwrite the resume key with a bogus value */
1121 memcpy(cc_copy.source_key, "deadbeefdeadbeefdeadbeef", 24);
1123 cc_copy.chunks[0].source_off = 0;
1124 cc_copy.chunks[0].target_off = 0;
1125 cc_copy.chunks[0].length = 4096;
1127 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1128 &cc_copy,
1129 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1130 torture_assert_ndr_success(torture, ndr_ret,
1131 "ndr_push_srv_copychunk_copy");
1133 /* Server 2k12 returns NT_STATUS_OBJECT_NAME_NOT_FOUND */
1134 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1135 torture_assert_ntstatus_equal(torture, status,
1136 NT_STATUS_OBJECT_NAME_NOT_FOUND,
1137 "FSCTL_SRV_COPYCHUNK");
1139 smb2_util_close(tree, src_h);
1140 smb2_util_close(tree, dest_h);
1141 talloc_free(tmp_ctx);
1142 return true;
1145 static bool test_ioctl_copy_chunk_src_is_dest(struct torture_context *torture,
1146 struct smb2_tree *tree)
1148 struct smb2_handle src_h;
1149 struct smb2_handle dest_h;
1150 NTSTATUS status;
1151 union smb_ioctl ioctl;
1152 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1153 struct srv_copychunk_copy cc_copy;
1154 struct srv_copychunk_rsp cc_rsp;
1155 enum ndr_err_code ndr_ret;
1156 bool ok;
1158 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
1160 FNAME,
1161 &src_h, 8192,
1162 SEC_RIGHTS_FILE_ALL,
1163 FNAME2,
1164 &dest_h, 0,
1165 SEC_RIGHTS_FILE_ALL,
1166 &cc_copy,
1167 &ioctl);
1168 if (!ok) {
1169 torture_fail(torture, "setup copy chunk error");
1172 /* the source is also the destination */
1173 ioctl.smb2.in.file.handle = src_h;
1175 /* non-overlapping */
1176 cc_copy.chunks[0].source_off = 0;
1177 cc_copy.chunks[0].target_off = 4096;
1178 cc_copy.chunks[0].length = 4096;
1180 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1181 &cc_copy,
1182 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1183 torture_assert_ndr_success(torture, ndr_ret,
1184 "ndr_push_srv_copychunk_copy");
1186 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1187 torture_assert_ntstatus_ok(torture, status,
1188 "FSCTL_SRV_COPYCHUNK");
1190 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1191 &cc_rsp,
1192 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1193 torture_assert_ndr_success(torture, ndr_ret,
1194 "ndr_pull_srv_copychunk_rsp");
1196 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1197 1, /* chunks written */
1198 0, /* chunk bytes unsuccessfully written */
1199 4096); /* total bytes written */
1200 if (!ok) {
1201 torture_fail(torture, "bad copy chunk response data");
1204 ok = check_pattern(torture, tree, tmp_ctx, src_h, 0, 4096, 0);
1205 if (!ok) {
1206 torture_fail(torture, "inconsistent file data");
1208 ok = check_pattern(torture, tree, tmp_ctx, src_h, 4096, 4096, 0);
1209 if (!ok) {
1210 torture_fail(torture, "inconsistent file data");
1213 smb2_util_close(tree, src_h);
1214 smb2_util_close(tree, dest_h);
1215 talloc_free(tmp_ctx);
1216 return true;
1220 * Test a single-chunk copychunk request, where the source and target ranges
1221 * overlap, and the SourceKey refers to the same target file. E.g:
1223 * Initial State
1224 * -------------
1225 * File: src_and_dest
1226 * Offset: 0123456789
1227 * Data: abcdefghij
1229 * Request
1230 * -------
1231 * FSCTL_SRV_COPYCHUNK(src_and_dest)
1232 * SourceKey = SRV_REQUEST_RESUME_KEY(src_and_dest)
1233 * ChunkCount = 1
1234 * Chunks[0].SourceOffset = 0
1235 * Chunks[0].TargetOffset = 4
1236 * Chunks[0].Length = 6
1238 * Resultant State
1239 * ---------------
1240 * File: src_and_dest
1241 * Offset: 0123456789
1242 * Data: abcdabcdef
1244 * The resultant contents of src_and_dest is dependent on the server's
1245 * copy algorithm. In the above example, the server uses an IO buffer
1246 * large enough to hold the entire six-byte source data before writing
1247 * to TargetOffset. If the server were to use a four-byte IO buffer and
1248 * started reads/writes from the lowest offset, then the two overlapping
1249 * bytes in the above example would be overwritten before being read. The
1250 * resultant file contents would be abcdabcdab.
1252 * Windows 2008r2 appears to use a 2048 byte copy buffer, overlapping bytes
1253 * after this offset are written before being read. Windows 2012 on the
1254 * other hand appears to use a buffer large enough to hold its maximum
1255 * supported chunk size (1M). Samba currently uses a 64k copy buffer by
1256 * default (vfs_cc_state.buf).
1258 * This test uses an 8-byte overlap at 2040-2048, so that it passes against
1259 * Windows 2008r2, 2012 and Samba servers. Note, 2008GM fails, as it appears
1260 * to use a different copy algorithm to 2008r2.
1262 static bool
1263 test_ioctl_copy_chunk_src_is_dest_overlap(struct torture_context *torture,
1264 struct smb2_tree *tree)
1266 struct smb2_handle src_h;
1267 struct smb2_handle dest_h;
1268 NTSTATUS status;
1269 union smb_ioctl ioctl;
1270 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1271 struct srv_copychunk_copy cc_copy;
1272 struct srv_copychunk_rsp cc_rsp;
1273 enum ndr_err_code ndr_ret;
1274 bool ok;
1276 /* exceed the vfs_default copy buffer */
1277 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
1279 FNAME,
1280 &src_h, 2048 * 2,
1281 SEC_RIGHTS_FILE_ALL,
1282 FNAME2,
1283 &dest_h, 0,
1284 SEC_RIGHTS_FILE_ALL,
1285 &cc_copy,
1286 &ioctl);
1287 if (!ok) {
1288 torture_fail(torture, "setup copy chunk error");
1291 /* the source is also the destination */
1292 ioctl.smb2.in.file.handle = src_h;
1294 /* 8 bytes overlap between source and target ranges */
1295 cc_copy.chunks[0].source_off = 0;
1296 cc_copy.chunks[0].target_off = 2048 - 8;
1297 cc_copy.chunks[0].length = 2048;
1299 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1300 &cc_copy,
1301 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1302 torture_assert_ndr_success(torture, ndr_ret,
1303 "ndr_push_srv_copychunk_copy");
1305 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1306 torture_assert_ntstatus_ok(torture, status,
1307 "FSCTL_SRV_COPYCHUNK");
1309 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1310 &cc_rsp,
1311 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1312 torture_assert_ndr_success(torture, ndr_ret,
1313 "ndr_pull_srv_copychunk_rsp");
1315 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1316 1, /* chunks written */
1317 0, /* chunk bytes unsuccessfully written */
1318 2048); /* total bytes written */
1319 if (!ok) {
1320 torture_fail(torture, "bad copy chunk response data");
1323 ok = check_pattern(torture, tree, tmp_ctx, src_h, 0, 2048 - 8, 0);
1324 if (!ok) {
1325 torture_fail(torture, "inconsistent file data");
1327 ok = check_pattern(torture, tree, tmp_ctx, src_h, 2048 - 8, 2048, 0);
1328 if (!ok) {
1329 torture_fail(torture, "inconsistent file data");
1332 smb2_util_close(tree, src_h);
1333 smb2_util_close(tree, dest_h);
1334 talloc_free(tmp_ctx);
1335 return true;
1338 static bool test_ioctl_copy_chunk_bad_access(struct torture_context *torture,
1339 struct smb2_tree *tree)
1341 struct smb2_handle src_h;
1342 struct smb2_handle dest_h;
1343 NTSTATUS status;
1344 union smb_ioctl ioctl;
1345 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1346 struct srv_copychunk_copy cc_copy;
1347 enum ndr_err_code ndr_ret;
1348 bool ok;
1349 /* read permission on src */
1350 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx, 1, /* 1 chunk */
1351 FNAME, &src_h, 4096, /* fill 4096 byte src file */
1352 SEC_FILE_READ_DATA | SEC_FILE_READ_ATTRIBUTE,
1353 FNAME2, &dest_h, 0, /* 0 byte dest file */
1354 SEC_RIGHTS_FILE_ALL, &cc_copy, &ioctl);
1355 if (!ok) {
1356 torture_fail(torture, "setup copy chunk error");
1359 cc_copy.chunks[0].source_off = 0;
1360 cc_copy.chunks[0].target_off = 0;
1361 cc_copy.chunks[0].length = 4096;
1363 ndr_ret = ndr_push_struct_blob(
1364 &ioctl.smb2.in.out, tmp_ctx, &cc_copy,
1365 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1366 torture_assert_ndr_success(torture, ndr_ret,
1367 "ndr_push_srv_copychunk_copy");
1369 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1370 torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK,
1371 "FSCTL_SRV_COPYCHUNK");
1373 smb2_util_close(tree, src_h);
1374 smb2_util_close(tree, dest_h);
1376 /* execute permission on src */
1377 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx, 1, /* 1 chunk */
1378 FNAME, &src_h, 4096, /* fill 4096 byte src file */
1379 SEC_FILE_EXECUTE | SEC_FILE_READ_ATTRIBUTE,
1380 FNAME2, &dest_h, 0, /* 0 byte dest file */
1381 SEC_RIGHTS_FILE_ALL, &cc_copy, &ioctl);
1382 if (!ok) {
1383 torture_fail(torture, "setup copy chunk error");
1386 cc_copy.chunks[0].source_off = 0;
1387 cc_copy.chunks[0].target_off = 0;
1388 cc_copy.chunks[0].length = 4096;
1390 ndr_ret = ndr_push_struct_blob(
1391 &ioctl.smb2.in.out, tmp_ctx, &cc_copy,
1392 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1393 torture_assert_ndr_success(torture, ndr_ret,
1394 "ndr_push_srv_copychunk_copy");
1396 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1397 torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK,
1398 "FSCTL_SRV_COPYCHUNK");
1400 smb2_util_close(tree, src_h);
1401 smb2_util_close(tree, dest_h);
1403 /* neither read nor execute permission on src */
1404 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx, 1, /* 1 chunk */
1405 FNAME, &src_h, 4096, /* fill 4096 byte src file */
1406 SEC_FILE_READ_ATTRIBUTE, FNAME2, &dest_h,
1407 0, /* 0 byte dest file */
1408 SEC_RIGHTS_FILE_ALL, &cc_copy, &ioctl);
1409 if (!ok) {
1410 torture_fail(torture, "setup copy chunk error");
1413 cc_copy.chunks[0].source_off = 0;
1414 cc_copy.chunks[0].target_off = 0;
1415 cc_copy.chunks[0].length = 4096;
1417 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1418 &cc_copy,
1419 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1420 torture_assert_ndr_success(torture, ndr_ret,
1421 "ndr_push_srv_copychunk_copy");
1423 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1424 torture_assert_ntstatus_equal(torture, status,
1425 NT_STATUS_ACCESS_DENIED,
1426 "FSCTL_SRV_COPYCHUNK");
1428 smb2_util_close(tree, src_h);
1429 smb2_util_close(tree, dest_h);
1431 /* no write permission on dest */
1432 ok = test_setup_copy_chunk(
1433 torture, tree, tree, tmp_ctx, 1, /* 1 chunk */
1434 FNAME, &src_h, 4096, /* fill 4096 byte src file */
1435 SEC_FILE_READ_DATA | SEC_FILE_READ_ATTRIBUTE, FNAME2, &dest_h,
1436 0, /* 0 byte dest file */
1437 (SEC_RIGHTS_FILE_ALL &
1438 ~(SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)),
1439 &cc_copy, &ioctl);
1440 if (!ok) {
1441 torture_fail(torture, "setup copy chunk error");
1444 cc_copy.chunks[0].source_off = 0;
1445 cc_copy.chunks[0].target_off = 0;
1446 cc_copy.chunks[0].length = 4096;
1448 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1449 &cc_copy,
1450 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1451 torture_assert_ndr_success(torture, ndr_ret,
1452 "ndr_push_srv_copychunk_copy");
1454 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1455 torture_assert_ntstatus_equal(torture, status,
1456 NT_STATUS_ACCESS_DENIED,
1457 "FSCTL_SRV_COPYCHUNK");
1459 smb2_util_close(tree, src_h);
1460 smb2_util_close(tree, dest_h);
1462 /* no read permission on dest */
1463 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx, 1, /* 1 chunk */
1464 FNAME, &src_h, 4096, /* fill 4096 byte src file */
1465 SEC_FILE_READ_DATA | SEC_FILE_READ_ATTRIBUTE,
1466 FNAME2, &dest_h, 0, /* 0 byte dest file */
1467 (SEC_RIGHTS_FILE_ALL & ~SEC_FILE_READ_DATA),
1468 &cc_copy, &ioctl);
1469 if (!ok) {
1470 torture_fail(torture, "setup copy chunk error");
1473 cc_copy.chunks[0].source_off = 0;
1474 cc_copy.chunks[0].target_off = 0;
1475 cc_copy.chunks[0].length = 4096;
1477 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1478 &cc_copy,
1479 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1480 torture_assert_ndr_success(torture, ndr_ret,
1481 "ndr_push_srv_copychunk_copy");
1484 * FSCTL_SRV_COPYCHUNK requires read permission on dest,
1485 * FSCTL_SRV_COPYCHUNK_WRITE on the other hand does not.
1487 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1488 torture_assert_ntstatus_equal(torture, status,
1489 NT_STATUS_ACCESS_DENIED,
1490 "FSCTL_SRV_COPYCHUNK");
1492 smb2_util_close(tree, src_h);
1493 smb2_util_close(tree, dest_h);
1494 talloc_free(tmp_ctx);
1496 return true;
1499 static bool test_ioctl_copy_chunk_write_access(struct torture_context *torture,
1500 struct smb2_tree *tree)
1502 struct smb2_handle src_h;
1503 struct smb2_handle dest_h;
1504 NTSTATUS status;
1505 union smb_ioctl ioctl;
1506 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1507 struct srv_copychunk_copy cc_copy;
1508 enum ndr_err_code ndr_ret;
1509 bool ok;
1511 /* no read permission on dest with FSCTL_SRV_COPYCHUNK_WRITE */
1512 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
1513 1, /* 1 chunk */
1514 FNAME,
1515 &src_h, 4096, /* fill 4096 byte src file */
1516 SEC_RIGHTS_FILE_ALL,
1517 FNAME2,
1518 &dest_h, 0, /* 0 byte dest file */
1519 (SEC_RIGHTS_FILE_WRITE
1520 | SEC_RIGHTS_FILE_EXECUTE),
1521 &cc_copy,
1522 &ioctl);
1523 if (!ok) {
1524 torture_fail(torture, "setup copy chunk error");
1527 ioctl.smb2.in.function = FSCTL_SRV_COPYCHUNK_WRITE;
1528 cc_copy.chunks[0].source_off = 0;
1529 cc_copy.chunks[0].target_off = 0;
1530 cc_copy.chunks[0].length = 4096;
1532 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1533 &cc_copy,
1534 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1535 torture_assert_ndr_success(torture, ndr_ret,
1536 "ndr_push_srv_copychunk_copy");
1538 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1539 torture_assert_ntstatus_ok(torture, status,
1540 "FSCTL_SRV_COPYCHUNK_WRITE");
1542 smb2_util_close(tree, src_h);
1543 smb2_util_close(tree, dest_h);
1544 talloc_free(tmp_ctx);
1546 return true;
1549 static bool test_ioctl_copy_chunk_src_exceed(struct torture_context *torture,
1550 struct smb2_tree *tree)
1552 struct smb2_handle src_h;
1553 struct smb2_handle dest_h;
1554 NTSTATUS status;
1555 union smb_ioctl ioctl;
1556 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1557 struct srv_copychunk_copy cc_copy;
1558 struct srv_copychunk_rsp cc_rsp;
1559 enum ndr_err_code ndr_ret;
1560 bool ok;
1562 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
1563 1, /* 1 chunk */
1564 FNAME,
1565 &src_h, 4096, /* fill 4096 byte src file */
1566 SEC_RIGHTS_FILE_ALL,
1567 FNAME2,
1568 &dest_h, 0, /* 0 byte dest file */
1569 SEC_RIGHTS_FILE_ALL,
1570 &cc_copy,
1571 &ioctl);
1572 if (!ok) {
1573 torture_fail(torture, "setup copy chunk error");
1576 /* Request copy where off + length exceeds size of src */
1577 cc_copy.chunks[0].source_off = 1024;
1578 cc_copy.chunks[0].target_off = 0;
1579 cc_copy.chunks[0].length = 4096;
1581 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1582 &cc_copy,
1583 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1584 torture_assert_ndr_success(torture, ndr_ret,
1585 "ndr_push_srv_copychunk_copy");
1587 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1588 torture_assert_ntstatus_equal(torture, status,
1589 NT_STATUS_INVALID_VIEW_SIZE,
1590 "FSCTL_SRV_COPYCHUNK oversize");
1592 /* Request copy where length exceeds size of src */
1593 cc_copy.chunks[0].source_off = 1024;
1594 cc_copy.chunks[0].target_off = 0;
1595 cc_copy.chunks[0].length = 3072;
1597 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1598 &cc_copy,
1599 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1600 torture_assert_ndr_success(torture, ndr_ret,
1601 "ndr_push_srv_copychunk_copy");
1603 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1604 torture_assert_ntstatus_ok(torture, status,
1605 "FSCTL_SRV_COPYCHUNK just right");
1607 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1608 &cc_rsp,
1609 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1610 torture_assert_ndr_success(torture, ndr_ret,
1611 "ndr_pull_srv_copychunk_rsp");
1613 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1614 1, /* chunks written */
1615 0, /* chunk bytes unsuccessfully written */
1616 3072); /* total bytes written */
1617 if (!ok) {
1618 torture_fail(torture, "bad copy chunk response data");
1621 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 3072, 1024);
1622 if (!ok) {
1623 torture_fail(torture, "inconsistent file data");
1626 smb2_util_close(tree, src_h);
1627 smb2_util_close(tree, dest_h);
1628 talloc_free(tmp_ctx);
1629 return true;
1632 static bool
1633 test_ioctl_copy_chunk_src_exceed_multi(struct torture_context *torture,
1634 struct smb2_tree *tree)
1636 struct smb2_handle src_h;
1637 struct smb2_handle dest_h;
1638 NTSTATUS status;
1639 union smb_ioctl ioctl;
1640 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1641 struct srv_copychunk_copy cc_copy;
1642 struct srv_copychunk_rsp cc_rsp;
1643 enum ndr_err_code ndr_ret;
1644 bool ok;
1646 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
1647 2, /* 2 chunks */
1648 FNAME,
1649 &src_h, 8192, /* fill 8192 byte src file */
1650 SEC_RIGHTS_FILE_ALL,
1651 FNAME2,
1652 &dest_h, 0, /* 0 byte dest file */
1653 SEC_RIGHTS_FILE_ALL,
1654 &cc_copy,
1655 &ioctl);
1656 if (!ok) {
1657 torture_fail(torture, "setup copy chunk error");
1660 /* Request copy where off + length exceeds size of src */
1661 cc_copy.chunks[0].source_off = 0;
1662 cc_copy.chunks[0].target_off = 0;
1663 cc_copy.chunks[0].length = 4096;
1665 cc_copy.chunks[1].source_off = 4096;
1666 cc_copy.chunks[1].target_off = 4096;
1667 cc_copy.chunks[1].length = 8192;
1669 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1670 &cc_copy,
1671 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1672 torture_assert_ndr_success(torture, ndr_ret,
1673 "ndr_push_srv_copychunk_copy");
1675 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1676 torture_assert_ntstatus_equal(torture, status,
1677 NT_STATUS_INVALID_VIEW_SIZE,
1678 "FSCTL_SRV_COPYCHUNK oversize");
1679 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1680 &cc_rsp,
1681 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1682 torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
1684 /* first chunk should still be written */
1685 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1686 1, /* chunks written */
1687 0, /* chunk bytes unsuccessfully written */
1688 4096); /* total bytes written */
1689 if (!ok) {
1690 torture_fail(torture, "bad copy chunk response data");
1692 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
1693 if (!ok) {
1694 torture_fail(torture, "inconsistent file data");
1697 smb2_util_close(tree, src_h);
1698 smb2_util_close(tree, dest_h);
1699 talloc_free(tmp_ctx);
1700 return true;
1703 static bool test_ioctl_copy_chunk_sparse_dest(struct torture_context *torture,
1704 struct smb2_tree *tree)
1706 struct smb2_handle src_h;
1707 struct smb2_handle dest_h;
1708 NTSTATUS status;
1709 union smb_ioctl ioctl;
1710 struct smb2_read r;
1711 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1712 struct srv_copychunk_copy cc_copy;
1713 struct srv_copychunk_rsp cc_rsp;
1714 enum ndr_err_code ndr_ret;
1715 bool ok;
1716 int i;
1718 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
1719 1, /* 1 chunk */
1720 FNAME,
1721 &src_h, 4096, /* fill 4096 byte src file */
1722 SEC_RIGHTS_FILE_ALL,
1723 FNAME2,
1724 &dest_h, 0, /* 0 byte dest file */
1725 SEC_RIGHTS_FILE_ALL,
1726 &cc_copy,
1727 &ioctl);
1728 if (!ok) {
1729 torture_fail(torture, "setup copy chunk error");
1732 /* copy all src file data (via a single chunk desc) */
1733 cc_copy.chunks[0].source_off = 0;
1734 cc_copy.chunks[0].target_off = 4096;
1735 cc_copy.chunks[0].length = 4096;
1737 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1738 &cc_copy,
1739 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1740 torture_assert_ndr_success(torture, ndr_ret,
1741 "ndr_push_srv_copychunk_copy");
1743 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1744 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
1746 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1747 &cc_rsp,
1748 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1749 torture_assert_ndr_success(torture, ndr_ret,
1750 "ndr_pull_srv_copychunk_rsp");
1752 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1753 1, /* chunks written */
1754 0, /* chunk bytes unsuccessfully written */
1755 4096); /* total bytes written */
1756 if (!ok) {
1757 torture_fail(torture, "bad copy chunk response data");
1760 /* check for zeros in first 4k */
1761 ZERO_STRUCT(r);
1762 r.in.file.handle = dest_h;
1763 r.in.length = 4096;
1764 r.in.offset = 0;
1765 status = smb2_read(tree, tmp_ctx, &r);
1766 torture_assert_ntstatus_ok(torture, status, "read");
1768 torture_assert_u64_equal(torture, r.out.data.length, 4096,
1769 "read data len mismatch");
1771 for (i = 0; i < 4096; i++) {
1772 torture_assert(torture, (r.out.data.data[i] == 0),
1773 "sparse did not pass class");
1776 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 4096, 4096, 0);
1777 if (!ok) {
1778 torture_fail(torture, "inconsistent file data");
1781 smb2_util_close(tree, src_h);
1782 smb2_util_close(tree, dest_h);
1783 talloc_free(tmp_ctx);
1784 return true;
1788 * set the ioctl MaxOutputResponse size to less than
1789 * sizeof(struct srv_copychunk_rsp)
1791 static bool test_ioctl_copy_chunk_max_output_sz(struct torture_context *torture,
1792 struct smb2_tree *tree)
1794 struct smb2_handle src_h;
1795 struct smb2_handle dest_h;
1796 NTSTATUS status;
1797 union smb_ioctl ioctl;
1798 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1799 struct srv_copychunk_copy cc_copy;
1800 enum ndr_err_code ndr_ret;
1801 bool ok;
1803 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
1804 1, /* 1 chunk */
1805 FNAME,
1806 &src_h, 4096, /* fill 4096 byte src file */
1807 SEC_RIGHTS_FILE_ALL,
1808 FNAME2,
1809 &dest_h, 0, /* 0 byte dest file */
1810 SEC_RIGHTS_FILE_ALL,
1811 &cc_copy,
1812 &ioctl);
1813 if (!ok) {
1814 torture_fail(torture, "setup copy chunk error");
1817 cc_copy.chunks[0].source_off = 0;
1818 cc_copy.chunks[0].target_off = 0;
1819 cc_copy.chunks[0].length = 4096;
1820 /* req is valid, but use undersize max_output_response */
1821 ioctl.smb2.in.max_output_response = sizeof(struct srv_copychunk_rsp) - 1;
1823 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1824 &cc_copy,
1825 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1826 torture_assert_ndr_success(torture, ndr_ret,
1827 "ndr_push_srv_copychunk_copy");
1829 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1830 torture_assert_ntstatus_equal(torture, status,
1831 NT_STATUS_INVALID_PARAMETER,
1832 "FSCTL_SRV_COPYCHUNK");
1834 smb2_util_close(tree, src_h);
1835 smb2_util_close(tree, dest_h);
1836 talloc_free(tmp_ctx);
1837 return true;
1840 static bool test_ioctl_copy_chunk_zero_length(struct torture_context *torture,
1841 struct smb2_tree *tree)
1843 struct smb2_handle src_h;
1844 struct smb2_handle dest_h;
1845 NTSTATUS status;
1846 union smb_ioctl ioctl;
1847 union smb_fileinfo q;
1848 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1849 struct srv_copychunk_copy cc_copy;
1850 struct srv_copychunk_rsp cc_rsp;
1851 enum ndr_err_code ndr_ret;
1852 bool ok;
1854 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
1855 1, /* 1 chunk */
1856 FNAME,
1857 &src_h, 4096, /* fill 4096 byte src file */
1858 SEC_RIGHTS_FILE_ALL,
1859 FNAME2,
1860 &dest_h, 0, /* 0 byte dest file */
1861 SEC_RIGHTS_FILE_ALL,
1862 &cc_copy,
1863 &ioctl);
1864 if (!ok) {
1865 torture_fail(torture, "setup copy chunk error");
1868 /* zero length server-side copy (via a single chunk desc) */
1869 cc_copy.chunks[0].source_off = 0;
1870 cc_copy.chunks[0].target_off = 0;
1871 cc_copy.chunks[0].length = 0;
1873 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1874 &cc_copy,
1875 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1876 torture_assert_ndr_success(torture, ndr_ret,
1877 "ndr_push_srv_copychunk_copy");
1879 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1880 torture_assert_ntstatus_equal(torture, status,
1881 NT_STATUS_INVALID_PARAMETER,
1882 "bad zero-length chunk response");
1884 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1885 &cc_rsp,
1886 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1887 torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
1889 ZERO_STRUCT(q);
1890 q.all_info2.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
1891 q.all_info2.in.file.handle = dest_h;
1892 status = smb2_getinfo_file(tree, torture, &q);
1893 torture_assert_ntstatus_ok(torture, status, "getinfo");
1895 torture_assert_int_equal(torture, q.all_info2.out.size, 0,
1896 "size after zero len clone");
1898 smb2_util_close(tree, src_h);
1899 smb2_util_close(tree, dest_h);
1900 talloc_free(tmp_ctx);
1901 return true;
1904 static bool copy_one_stream(struct torture_context *torture,
1905 struct smb2_tree *tree,
1906 TALLOC_CTX *tmp_ctx,
1907 const char *src_sname,
1908 const char *dst_sname)
1910 struct smb2_handle src_h = {{0}};
1911 struct smb2_handle dest_h = {{0}};
1912 NTSTATUS status;
1913 union smb_ioctl io;
1914 struct srv_copychunk_copy cc_copy;
1915 struct srv_copychunk_rsp cc_rsp;
1916 enum ndr_err_code ndr_ret;
1917 bool ok = false;
1919 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
1920 1, /* 1 chunk */
1921 src_sname,
1922 &src_h, 256, /* fill 256 byte src file */
1923 SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA,
1924 dst_sname,
1925 &dest_h, 0, /* 0 byte dest file */
1926 SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA,
1927 &cc_copy,
1928 &io);
1929 torture_assert_goto(torture, ok == true, ok, done,
1930 "setup copy chunk error\n");
1932 /* copy all src file data (via a single chunk desc) */
1933 cc_copy.chunks[0].source_off = 0;
1934 cc_copy.chunks[0].target_off = 0;
1935 cc_copy.chunks[0].length = 256;
1937 ndr_ret = ndr_push_struct_blob(
1938 &io.smb2.in.out, tmp_ctx, &cc_copy,
1939 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1941 torture_assert_ndr_success_goto(torture, ndr_ret, ok, done,
1942 "ndr_push_srv_copychunk_copy\n");
1944 status = smb2_ioctl(tree, tmp_ctx, &io.smb2);
1945 torture_assert_ntstatus_ok_goto(torture, status, ok, done,
1946 "FSCTL_SRV_COPYCHUNK\n");
1948 ndr_ret = ndr_pull_struct_blob(
1949 &io.smb2.out.out, tmp_ctx, &cc_rsp,
1950 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1952 torture_assert_ndr_success_goto(torture, ndr_ret, ok, done,
1953 "ndr_pull_srv_copychunk_rsp\n");
1955 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1956 1, /* chunks written */
1957 0, /* chunk bytes unsuccessfully written */
1958 256); /* total bytes written */
1959 torture_assert_goto(torture, ok == true, ok, done,
1960 "bad copy chunk response data\n");
1962 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 256, 0);
1963 if (!ok) {
1964 torture_fail(torture, "inconsistent file data\n");
1967 done:
1968 if (!smb2_util_handle_empty(src_h)) {
1969 smb2_util_close(tree, src_h);
1971 if (!smb2_util_handle_empty(dest_h)) {
1972 smb2_util_close(tree, dest_h);
1975 return ok;
1979 * Create a file
1981 static bool torture_setup_file(TALLOC_CTX *mem_ctx,
1982 struct smb2_tree *tree,
1983 const char *name)
1985 struct smb2_create io;
1986 NTSTATUS status;
1988 smb2_util_unlink(tree, name);
1989 ZERO_STRUCT(io);
1990 io.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED;
1991 io.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1992 io.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF;
1993 io.in.share_access =
1994 NTCREATEX_SHARE_ACCESS_DELETE|
1995 NTCREATEX_SHARE_ACCESS_READ|
1996 NTCREATEX_SHARE_ACCESS_WRITE;
1997 io.in.create_options = 0;
1998 io.in.fname = name;
2000 status = smb2_create(tree, mem_ctx, &io);
2001 if (!NT_STATUS_IS_OK(status)) {
2002 return false;
2005 status = smb2_util_close(tree, io.out.file.handle);
2006 if (!NT_STATUS_IS_OK(status)) {
2007 return false;
2010 return true;
2013 static bool test_copy_chunk_streams(struct torture_context *torture,
2014 struct smb2_tree *tree)
2016 const char *src_name = "src";
2017 const char *dst_name = "dst";
2018 struct names {
2019 const char *src_sname;
2020 const char *dst_sname;
2021 } names[] = {
2022 { "src:foo", "dst:foo" }
2024 int i;
2025 TALLOC_CTX *tmp_ctx = NULL;
2026 bool ok = false;
2028 tmp_ctx = talloc_new(tree);
2029 torture_assert_not_null_goto(torture, tmp_ctx, ok, done,
2030 "torture_setup_file\n");
2032 ok = torture_setup_file(torture, tree, src_name);
2033 torture_assert_goto(torture, ok == true, ok, done, "torture_setup_file\n");
2034 ok = torture_setup_file(torture, tree, dst_name);
2035 torture_assert_goto(torture, ok == true, ok, done, "torture_setup_file\n");
2037 for (i = 0; i < ARRAY_SIZE(names); i++) {
2038 ok = copy_one_stream(torture, tree, tmp_ctx,
2039 names[i].src_sname,
2040 names[i].dst_sname);
2041 torture_assert_goto(torture, ok == true, ok, done,
2042 "copy_one_stream failed\n");
2045 done:
2046 smb2_util_unlink(tree, src_name);
2047 smb2_util_unlink(tree, dst_name);
2048 talloc_free(tmp_ctx);
2049 return ok;
2052 static bool test_copy_chunk_across_shares(struct torture_context *tctx,
2053 struct smb2_tree *tree)
2055 TALLOC_CTX *mem_ctx = NULL;
2056 struct smb2_tree *tree2 = NULL;
2057 struct smb2_handle src_h = {{0}};
2058 struct smb2_handle dest_h = {{0}};
2059 union smb_ioctl ioctl;
2060 struct srv_copychunk_copy cc_copy;
2061 struct srv_copychunk_rsp cc_rsp;
2062 enum ndr_err_code ndr_ret;
2063 NTSTATUS status;
2064 bool ok = false;
2066 mem_ctx = talloc_new(tctx);
2067 torture_assert_not_null_goto(tctx, mem_ctx, ok, done,
2068 "talloc_new\n");
2070 ok = torture_smb2_tree_connect(tctx, tree->session, tctx, &tree2);
2071 torture_assert_goto(tctx, ok == true, ok, done,
2072 "torture_smb2_tree_connect failed\n");
2074 ok = test_setup_copy_chunk(tctx, tree, tree2, mem_ctx,
2075 1, /* 1 chunk */
2076 FNAME,
2077 &src_h, 4096, /* fill 4096 byte src file */
2078 SEC_RIGHTS_FILE_ALL,
2079 FNAME2,
2080 &dest_h, 0, /* 0 byte dest file */
2081 SEC_RIGHTS_FILE_ALL,
2082 &cc_copy,
2083 &ioctl);
2084 torture_assert_goto(tctx, ok == true, ok, done,
2085 "test_setup_copy_chunk failed\n");
2087 cc_copy.chunks[0].source_off = 0;
2088 cc_copy.chunks[0].target_off = 0;
2089 cc_copy.chunks[0].length = 4096;
2091 ndr_ret = ndr_push_struct_blob(
2092 &ioctl.smb2.in.out, mem_ctx, &cc_copy,
2093 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
2094 torture_assert_ndr_success_goto(tctx, ndr_ret, ok, done,
2095 "ndr_push_srv_copychunk_copy\n");
2097 status = smb2_ioctl(tree2, mem_ctx, &ioctl.smb2);
2098 torture_assert_ntstatus_ok_goto(tctx, status, ok, done,
2099 "FSCTL_SRV_COPYCHUNK\n");
2101 ndr_ret = ndr_pull_struct_blob(
2102 &ioctl.smb2.out.out, mem_ctx, &cc_rsp,
2103 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
2105 torture_assert_ndr_success_goto(tctx, ndr_ret, ok, done,
2106 "ndr_pull_srv_copychunk_rsp\n");
2108 ok = check_copy_chunk_rsp(tctx, &cc_rsp,
2109 1, /* chunks written */
2110 0, /* chunk bytes unsuccessfully written */
2111 4096); /* total bytes written */
2112 torture_assert_goto(tctx, ok == true, ok, done,
2113 "bad copy chunk response data\n");
2115 ok = check_pattern(tctx, tree2, mem_ctx, dest_h, 0, 4096, 0);
2116 torture_assert_goto(tctx, ok == true, ok, done,
2117 "inconsistent file data\n");
2119 done:
2120 TALLOC_FREE(mem_ctx);
2121 if (!smb2_util_handle_empty(src_h)) {
2122 smb2_util_close(tree, src_h);
2124 if (!smb2_util_handle_empty(dest_h)) {
2125 smb2_util_close(tree2, dest_h);
2127 smb2_util_unlink(tree, FNAME);
2128 smb2_util_unlink(tree2, FNAME2);
2129 if (tree2 != NULL) {
2130 smb2_tdis(tree2);
2132 return ok;
2135 /* Test closing the src handle */
2136 static bool test_copy_chunk_across_shares2(struct torture_context *tctx,
2137 struct smb2_tree *tree)
2139 TALLOC_CTX *mem_ctx = NULL;
2140 struct smb2_tree *tree2 = NULL;
2141 struct smb2_handle src_h = {{0}};
2142 struct smb2_handle dest_h = {{0}};
2143 union smb_ioctl ioctl;
2144 struct srv_copychunk_copy cc_copy;
2145 enum ndr_err_code ndr_ret;
2146 NTSTATUS status;
2147 bool ok = false;
2149 mem_ctx = talloc_new(tctx);
2150 torture_assert_not_null_goto(tctx, mem_ctx, ok, done,
2151 "talloc_new\n");
2153 ok = torture_smb2_tree_connect(tctx, tree->session, tctx, &tree2);
2154 torture_assert_goto(tctx, ok == true, ok, done,
2155 "torture_smb2_tree_connect failed\n");
2157 ok = test_setup_copy_chunk(tctx, tree, tree2, mem_ctx,
2158 1, /* 1 chunk */
2159 FNAME,
2160 &src_h, 4096, /* fill 4096 byte src file */
2161 SEC_RIGHTS_FILE_ALL,
2162 FNAME2,
2163 &dest_h, 0, /* 0 byte dest file */
2164 SEC_RIGHTS_FILE_ALL,
2165 &cc_copy,
2166 &ioctl);
2167 torture_assert_goto(tctx, ok == true, ok, done,
2168 "test_setup_copy_chunk failed\n");
2170 status = smb2_util_close(tree, src_h);
2171 torture_assert_ntstatus_ok_goto(tctx, status, ok, done,
2172 "smb2_util_close failed\n");
2173 ZERO_STRUCT(src_h);
2175 cc_copy.chunks[0].source_off = 0;
2176 cc_copy.chunks[0].target_off = 0;
2177 cc_copy.chunks[0].length = 4096;
2179 ndr_ret = ndr_push_struct_blob(
2180 &ioctl.smb2.in.out, mem_ctx, &cc_copy,
2181 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
2182 torture_assert_ndr_success_goto(tctx, ndr_ret, ok, done,
2183 "ndr_push_srv_copychunk_copy\n");
2185 status = smb2_ioctl(tree2, mem_ctx, &ioctl.smb2);
2186 torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND,
2187 ok, done, "smb2_ioctl failed\n");
2189 done:
2190 TALLOC_FREE(mem_ctx);
2191 if (!smb2_util_handle_empty(src_h)) {
2192 smb2_util_close(tree, src_h);
2194 if (!smb2_util_handle_empty(dest_h)) {
2195 smb2_util_close(tree2, dest_h);
2197 smb2_util_unlink(tree, FNAME);
2198 smb2_util_unlink(tree2, FNAME2);
2199 if (tree2 != NULL) {
2200 smb2_tdis(tree2);
2202 return ok;
2205 /* Test offset works */
2206 static bool test_copy_chunk_across_shares3(struct torture_context *tctx,
2207 struct smb2_tree *tree)
2209 TALLOC_CTX *mem_ctx = NULL;
2210 struct smb2_tree *tree2 = NULL;
2211 struct smb2_handle src_h = {{0}};
2212 struct smb2_handle dest_h = {{0}};
2213 union smb_ioctl ioctl;
2214 struct srv_copychunk_copy cc_copy;
2215 struct srv_copychunk_rsp cc_rsp;
2216 enum ndr_err_code ndr_ret;
2217 NTSTATUS status;
2218 bool ok = false;
2220 mem_ctx = talloc_new(tctx);
2221 torture_assert_not_null_goto(tctx, mem_ctx, ok, done,
2222 "talloc_new\n");
2224 ok = torture_smb2_tree_connect(tctx, tree->session, tctx, &tree2);
2225 torture_assert_goto(tctx, ok == true, ok, done,
2226 "torture_smb2_tree_connect failed\n");
2228 ok = test_setup_copy_chunk(tctx, tree, tree2, mem_ctx,
2229 2, /* 2 chunks */
2230 FNAME,
2231 &src_h, 4096, /* fill 4096 byte src file */
2232 SEC_RIGHTS_FILE_ALL,
2233 FNAME2,
2234 &dest_h, 0, /* 0 byte dest file */
2235 SEC_RIGHTS_FILE_ALL,
2236 &cc_copy,
2237 &ioctl);
2238 torture_assert_goto(tctx, ok == true, ok, done,
2239 "test_setup_copy_chunk failed\n");
2241 cc_copy.chunks[0].source_off = 0;
2242 cc_copy.chunks[0].target_off = 0;
2243 cc_copy.chunks[0].length = 4096;
2245 /* second chunk appends the same data to the first */
2246 cc_copy.chunks[1].source_off = 0;
2247 cc_copy.chunks[1].target_off = 4096;
2248 cc_copy.chunks[1].length = 4096;
2250 ndr_ret = ndr_push_struct_blob(
2251 &ioctl.smb2.in.out, mem_ctx, &cc_copy,
2252 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
2253 torture_assert_ndr_success_goto(tctx, ndr_ret, ok, done,
2254 "ndr_push_srv_copychunk_copy\n");
2256 status = smb2_ioctl(tree2, mem_ctx, &ioctl.smb2);
2257 torture_assert_ntstatus_ok_goto(tctx, status, ok, done, "smb2_ioctl failed\n");
2259 ndr_ret = ndr_pull_struct_blob(
2260 &ioctl.smb2.out.out, mem_ctx, &cc_rsp,
2261 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
2263 torture_assert_ndr_success_goto(tctx, ndr_ret, ok, done,
2264 "ndr_pull_srv_copychunk_rsp\n");
2266 ok = check_copy_chunk_rsp(tctx, &cc_rsp,
2267 2, /* chunks written */
2268 0, /* chunk bytes unsuccessfully written */
2269 8192); /* total bytes written */
2270 torture_assert_goto(tctx, ok == true, ok, done,
2271 "check_copy_chunk_rsp failed\n");
2273 ok = check_pattern(tctx, tree2, mem_ctx, dest_h, 0, 4096, 0);
2274 torture_assert_goto(tctx, ok == true, ok, done,
2275 "check_pattern failed\n");
2277 ok = check_pattern(tctx, tree2, mem_ctx, dest_h, 4096, 4096, 0);
2278 torture_assert_goto(tctx, ok == true, ok, done,
2279 "check_pattern failed\n");
2281 done:
2282 TALLOC_FREE(mem_ctx);
2283 if (!smb2_util_handle_empty(src_h)) {
2284 smb2_util_close(tree, src_h);
2286 if (!smb2_util_handle_empty(dest_h)) {
2287 smb2_util_close(tree2, dest_h);
2289 smb2_util_unlink(tree, FNAME);
2290 smb2_util_unlink(tree2, FNAME2);
2291 if (tree2 != NULL) {
2292 smb2_tdis(tree2);
2294 return ok;
2297 static NTSTATUS test_ioctl_compress_fs_supported(struct torture_context *torture,
2298 struct smb2_tree *tree,
2299 TALLOC_CTX *mem_ctx,
2300 struct smb2_handle *fh,
2301 bool *compress_support)
2303 NTSTATUS status;
2304 union smb_fsinfo info;
2306 ZERO_STRUCT(info);
2307 info.generic.level = RAW_QFS_ATTRIBUTE_INFORMATION;
2308 info.generic.handle = *fh;
2309 status = smb2_getinfo_fs(tree, tree, &info);
2310 if (!NT_STATUS_IS_OK(status)) {
2311 return status;
2314 if (info.attribute_info.out.fs_attr & FILE_FILE_COMPRESSION) {
2315 *compress_support = true;
2316 } else {
2317 *compress_support = false;
2319 return NT_STATUS_OK;
2322 static NTSTATUS test_ioctl_compress_get(struct torture_context *torture,
2323 TALLOC_CTX *mem_ctx,
2324 struct smb2_tree *tree,
2325 struct smb2_handle fh,
2326 uint16_t *_compression_fmt)
2328 union smb_ioctl ioctl;
2329 struct compression_state cmpr_state;
2330 enum ndr_err_code ndr_ret;
2331 NTSTATUS status;
2333 ZERO_STRUCT(ioctl);
2334 ioctl.smb2.level = RAW_IOCTL_SMB2;
2335 ioctl.smb2.in.file.handle = fh;
2336 ioctl.smb2.in.function = FSCTL_GET_COMPRESSION;
2337 ioctl.smb2.in.max_output_response = sizeof(struct compression_state);
2338 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2340 status = smb2_ioctl(tree, mem_ctx, &ioctl.smb2);
2341 if (!NT_STATUS_IS_OK(status)) {
2342 return status;
2345 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, mem_ctx,
2346 &cmpr_state,
2347 (ndr_pull_flags_fn_t)ndr_pull_compression_state);
2349 if (ndr_ret != NDR_ERR_SUCCESS) {
2350 return NT_STATUS_INTERNAL_ERROR;
2353 *_compression_fmt = cmpr_state.format;
2354 return NT_STATUS_OK;
2357 static NTSTATUS test_ioctl_compress_set(struct torture_context *torture,
2358 TALLOC_CTX *mem_ctx,
2359 struct smb2_tree *tree,
2360 struct smb2_handle fh,
2361 uint16_t compression_fmt)
2363 union smb_ioctl ioctl;
2364 struct compression_state cmpr_state;
2365 enum ndr_err_code ndr_ret;
2366 NTSTATUS status;
2368 ZERO_STRUCT(ioctl);
2369 ioctl.smb2.level = RAW_IOCTL_SMB2;
2370 ioctl.smb2.in.file.handle = fh;
2371 ioctl.smb2.in.function = FSCTL_SET_COMPRESSION;
2372 ioctl.smb2.in.max_output_response = 0;
2373 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2375 cmpr_state.format = compression_fmt;
2376 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, mem_ctx,
2377 &cmpr_state,
2378 (ndr_push_flags_fn_t)ndr_push_compression_state);
2379 if (ndr_ret != NDR_ERR_SUCCESS) {
2380 return NT_STATUS_INTERNAL_ERROR;
2383 status = smb2_ioctl(tree, mem_ctx, &ioctl.smb2);
2384 return status;
2387 static bool test_ioctl_compress_file_flag(struct torture_context *torture,
2388 struct smb2_tree *tree)
2390 struct smb2_handle fh;
2391 NTSTATUS status;
2392 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2393 bool ok;
2394 uint16_t compression_fmt;
2396 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2397 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2398 FILE_ATTRIBUTE_NORMAL);
2399 torture_assert(torture, ok, "setup compression file");
2401 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
2402 &ok);
2403 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2404 if (!ok) {
2405 smb2_util_close(tree, fh);
2406 torture_skip(torture, "FS compression not supported\n");
2409 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2410 &compression_fmt);
2411 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2413 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2414 "initial compression state not NONE");
2416 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
2417 COMPRESSION_FORMAT_DEFAULT);
2418 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
2420 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2421 &compression_fmt);
2422 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2424 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
2425 "invalid compression state after set");
2427 smb2_util_close(tree, fh);
2428 talloc_free(tmp_ctx);
2429 return true;
2432 static bool test_ioctl_compress_dir_inherit(struct torture_context *torture,
2433 struct smb2_tree *tree)
2435 struct smb2_handle dirh;
2436 struct smb2_handle fh;
2437 NTSTATUS status;
2438 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2439 uint16_t compression_fmt;
2440 bool ok;
2441 char path_buf[PATH_MAX];
2443 smb2_deltree(tree, DNAME);
2444 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2445 DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL,
2446 FILE_ATTRIBUTE_DIRECTORY);
2447 torture_assert(torture, ok, "setup compression directory");
2449 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &dirh,
2450 &ok);
2451 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2452 if (!ok) {
2453 smb2_util_close(tree, dirh);
2454 smb2_deltree(tree, DNAME);
2455 torture_skip(torture, "FS compression not supported\n");
2458 /* set compression on parent dir, then check for inheritance */
2459 status = test_ioctl_compress_set(torture, tmp_ctx, tree, dirh,
2460 COMPRESSION_FORMAT_LZNT1);
2461 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
2463 status = test_ioctl_compress_get(torture, tmp_ctx, tree, dirh,
2464 &compression_fmt);
2465 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2467 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
2468 "invalid compression state after set");
2470 snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME);
2471 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2472 path_buf, &fh, 4096, SEC_RIGHTS_FILE_ALL,
2473 FILE_ATTRIBUTE_NORMAL);
2474 torture_assert(torture, ok, "setup compression file");
2476 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2477 &compression_fmt);
2478 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2480 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
2481 "compression attr not inherited by new file");
2483 /* check compressed data is consistent */
2484 ok = check_pattern(torture, tree, tmp_ctx, fh, 0, 4096, 0);
2486 /* disable dir compression attr, file should remain compressed */
2487 status = test_ioctl_compress_set(torture, tmp_ctx, tree, dirh,
2488 COMPRESSION_FORMAT_NONE);
2489 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
2491 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2492 &compression_fmt);
2493 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2495 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
2496 "file compression attr removed after dir change");
2497 smb2_util_close(tree, fh);
2499 /* new files should no longer inherit compression attr */
2500 snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME2);
2501 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2502 path_buf, &fh, 0, SEC_RIGHTS_FILE_ALL,
2503 FILE_ATTRIBUTE_NORMAL);
2504 torture_assert(torture, ok, "setup file");
2506 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2507 &compression_fmt);
2508 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2510 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2511 "compression attr present on new file");
2513 smb2_util_close(tree, fh);
2514 smb2_util_close(tree, dirh);
2515 smb2_deltree(tree, DNAME);
2516 talloc_free(tmp_ctx);
2517 return true;
2520 static bool test_ioctl_compress_invalid_format(struct torture_context *torture,
2521 struct smb2_tree *tree)
2523 struct smb2_handle fh;
2524 NTSTATUS status;
2525 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2526 bool ok;
2527 uint16_t compression_fmt;
2529 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2530 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2531 FILE_ATTRIBUTE_NORMAL);
2532 torture_assert(torture, ok, "setup compression file");
2534 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
2535 &ok);
2536 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2537 if (!ok) {
2538 smb2_util_close(tree, fh);
2539 torture_skip(torture, "FS compression not supported\n");
2542 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
2543 0x0042); /* bogus */
2544 torture_assert_ntstatus_equal(torture, status,
2545 NT_STATUS_INVALID_PARAMETER,
2546 "invalid FSCTL_SET_COMPRESSION");
2548 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2549 &compression_fmt);
2550 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2552 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2553 "initial compression state not NONE");
2555 smb2_util_close(tree, fh);
2556 talloc_free(tmp_ctx);
2557 return true;
2560 static bool test_ioctl_compress_invalid_buf(struct torture_context *torture,
2561 struct smb2_tree *tree)
2563 struct smb2_handle fh;
2564 NTSTATUS status;
2565 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2566 bool ok;
2567 union smb_ioctl ioctl;
2569 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2570 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2571 FILE_ATTRIBUTE_NORMAL);
2572 torture_assert(torture, ok, "setup compression file");
2574 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
2575 &ok);
2576 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2577 if (!ok) {
2578 smb2_util_close(tree, fh);
2579 torture_skip(torture, "FS compression not supported\n");
2582 ZERO_STRUCT(ioctl);
2583 ioctl.smb2.level = RAW_IOCTL_SMB2;
2584 ioctl.smb2.in.file.handle = fh;
2585 ioctl.smb2.in.function = FSCTL_GET_COMPRESSION;
2586 ioctl.smb2.in.max_output_response = 0; /* no room for rsp data */
2587 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2589 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2590 if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_USER_BUFFER)
2591 && !NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
2592 /* neither Server 2k12 nor 2k8r2 response status */
2593 torture_assert(torture, true,
2594 "invalid FSCTL_SET_COMPRESSION");
2597 smb2_util_close(tree, fh);
2598 talloc_free(tmp_ctx);
2599 return true;
2602 static bool test_ioctl_compress_query_file_attr(struct torture_context *torture,
2603 struct smb2_tree *tree)
2605 struct smb2_handle fh;
2606 union smb_fileinfo io;
2607 NTSTATUS status;
2608 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2609 bool ok;
2611 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2612 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2613 FILE_ATTRIBUTE_NORMAL);
2614 torture_assert(torture, ok, "setup compression file");
2616 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
2617 &ok);
2618 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2619 if (!ok) {
2620 smb2_util_close(tree, fh);
2621 torture_skip(torture, "FS compression not supported\n");
2624 ZERO_STRUCT(io);
2625 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
2626 io.generic.in.file.handle = fh;
2627 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2628 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2630 torture_assert(torture,
2631 ((io.all_info2.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2632 "compression attr before set");
2634 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
2635 COMPRESSION_FORMAT_DEFAULT);
2636 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
2638 ZERO_STRUCT(io);
2639 io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2640 io.generic.in.file.handle = fh;
2641 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2642 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2644 torture_assert(torture,
2645 (io.basic_info.out.attrib & FILE_ATTRIBUTE_COMPRESSED),
2646 "no compression attr after set");
2648 smb2_util_close(tree, fh);
2649 talloc_free(tmp_ctx);
2650 return true;
2654 * Specify FILE_ATTRIBUTE_COMPRESSED on creation, Windows does not retain this
2655 * attribute.
2657 static bool test_ioctl_compress_create_with_attr(struct torture_context *torture,
2658 struct smb2_tree *tree)
2660 struct smb2_handle fh2;
2661 union smb_fileinfo io;
2662 NTSTATUS status;
2663 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2664 uint16_t compression_fmt;
2665 bool ok;
2667 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2668 FNAME2, &fh2, 0, SEC_RIGHTS_FILE_ALL,
2669 (FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_COMPRESSED));
2670 torture_assert(torture, ok, "setup compression file");
2672 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh2,
2673 &ok);
2674 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2675 if (!ok) {
2676 smb2_util_close(tree, fh2);
2677 torture_skip(torture, "FS compression not supported\n");
2680 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh2,
2681 &compression_fmt);
2682 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2684 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2685 "initial compression state not NONE");
2687 ZERO_STRUCT(io);
2688 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
2689 io.generic.in.file.handle = fh2;
2690 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2691 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2693 torture_assert(torture,
2694 ((io.all_info2.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2695 "incorrect compression attr");
2697 smb2_util_close(tree, fh2);
2698 talloc_free(tmp_ctx);
2699 return true;
2702 static bool test_ioctl_compress_inherit_disable(struct torture_context *torture,
2703 struct smb2_tree *tree)
2705 struct smb2_handle fh;
2706 struct smb2_handle dirh;
2707 char path_buf[PATH_MAX];
2708 NTSTATUS status;
2709 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2710 bool ok;
2711 uint16_t compression_fmt;
2713 struct smb2_create io;
2715 smb2_deltree(tree, DNAME);
2716 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2717 DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL,
2718 FILE_ATTRIBUTE_DIRECTORY);
2719 torture_assert(torture, ok, "setup compression directory");
2721 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &dirh,
2722 &ok);
2723 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2724 if (!ok) {
2725 smb2_util_close(tree, dirh);
2726 smb2_deltree(tree, DNAME);
2727 torture_skip(torture, "FS compression not supported\n");
2730 /* set compression on parent dir, then check for inheritance */
2731 status = test_ioctl_compress_set(torture, tmp_ctx, tree, dirh,
2732 COMPRESSION_FORMAT_LZNT1);
2733 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
2735 status = test_ioctl_compress_get(torture, tmp_ctx, tree, dirh,
2736 &compression_fmt);
2737 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2739 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
2740 "invalid compression state after set");
2741 smb2_util_close(tree, dirh);
2743 snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME);
2744 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2745 path_buf, &fh, 0, SEC_RIGHTS_FILE_ALL,
2746 FILE_ATTRIBUTE_NORMAL);
2747 torture_assert(torture, ok, "setup compression file");
2749 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2750 &compression_fmt);
2751 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2753 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
2754 "compression attr not inherited by new file");
2755 smb2_util_close(tree, fh);
2757 snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME2);
2759 /* NO_COMPRESSION option should block inheritance */
2760 ZERO_STRUCT(io);
2761 io.in.desired_access = SEC_RIGHTS_FILE_ALL;
2762 io.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
2763 io.in.create_disposition = NTCREATEX_DISP_CREATE;
2764 io.in.create_options = NTCREATEX_OPTIONS_NO_COMPRESSION;
2765 io.in.share_access =
2766 NTCREATEX_SHARE_ACCESS_DELETE|
2767 NTCREATEX_SHARE_ACCESS_READ|
2768 NTCREATEX_SHARE_ACCESS_WRITE;
2769 io.in.fname = path_buf;
2771 status = smb2_create(tree, tmp_ctx, &io);
2772 torture_assert_ntstatus_ok(torture, status, "file create");
2774 fh = io.out.file.handle;
2776 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2777 &compression_fmt);
2778 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2780 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2781 "compression attr inherited by NO_COMPRESSION file");
2782 smb2_util_close(tree, fh);
2785 snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, DNAME);
2786 ZERO_STRUCT(io);
2787 io.in.desired_access = SEC_RIGHTS_FILE_ALL;
2788 io.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
2789 io.in.create_disposition = NTCREATEX_DISP_CREATE;
2790 io.in.create_options = (NTCREATEX_OPTIONS_NO_COMPRESSION
2791 | NTCREATEX_OPTIONS_DIRECTORY);
2792 io.in.share_access =
2793 NTCREATEX_SHARE_ACCESS_DELETE|
2794 NTCREATEX_SHARE_ACCESS_READ|
2795 NTCREATEX_SHARE_ACCESS_WRITE;
2796 io.in.fname = path_buf;
2798 status = smb2_create(tree, tmp_ctx, &io);
2799 torture_assert_ntstatus_ok(torture, status, "dir create");
2801 dirh = io.out.file.handle;
2803 status = test_ioctl_compress_get(torture, tmp_ctx, tree, dirh,
2804 &compression_fmt);
2805 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2807 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2808 "compression attr inherited by NO_COMPRESSION dir");
2809 smb2_util_close(tree, dirh);
2810 smb2_deltree(tree, DNAME);
2812 talloc_free(tmp_ctx);
2813 return true;
2816 /* attempting to set compression via SetInfo should not stick */
2817 static bool test_ioctl_compress_set_file_attr(struct torture_context *torture,
2818 struct smb2_tree *tree)
2820 struct smb2_handle fh;
2821 struct smb2_handle dirh;
2822 union smb_fileinfo io;
2823 union smb_setfileinfo set_io;
2824 uint16_t compression_fmt;
2825 NTSTATUS status;
2826 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2827 bool ok;
2829 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2830 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2831 FILE_ATTRIBUTE_NORMAL);
2832 torture_assert(torture, ok, "setup compression file");
2834 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
2835 &ok);
2836 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2837 if (!ok) {
2838 smb2_util_close(tree, fh);
2839 torture_skip(torture, "FS compression not supported\n");
2842 ZERO_STRUCT(io);
2843 io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2844 io.generic.in.file.handle = fh;
2845 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2846 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2848 torture_assert(torture,
2849 ((io.basic_info.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2850 "compression attr before set");
2852 ZERO_STRUCT(set_io);
2853 set_io.generic.level = RAW_SFILEINFO_BASIC_INFORMATION;
2854 set_io.basic_info.in.file.handle = fh;
2855 set_io.basic_info.in.create_time = io.basic_info.out.create_time;
2856 set_io.basic_info.in.access_time = io.basic_info.out.access_time;
2857 set_io.basic_info.in.write_time = io.basic_info.out.write_time;
2858 set_io.basic_info.in.change_time = io.basic_info.out.change_time;
2859 set_io.basic_info.in.attrib = (io.basic_info.out.attrib
2860 | FILE_ATTRIBUTE_COMPRESSED);
2861 status = smb2_setinfo_file(tree, &set_io);
2862 torture_assert_ntstatus_ok(torture, status, "SMB2_SETINFO_FILE");
2864 ZERO_STRUCT(io);
2865 io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2866 io.generic.in.file.handle = fh;
2867 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2868 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2870 torture_assert(torture,
2871 ((io.basic_info.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2872 "compression attr after set");
2874 smb2_util_close(tree, fh);
2875 smb2_deltree(tree, DNAME);
2876 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2877 DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL,
2878 FILE_ATTRIBUTE_DIRECTORY);
2879 torture_assert(torture, ok, "setup compression directory");
2881 ZERO_STRUCT(io);
2882 io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2883 io.generic.in.file.handle = dirh;
2884 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2885 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2887 torture_assert(torture,
2888 ((io.basic_info.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2889 "compression attr before set");
2891 ZERO_STRUCT(set_io);
2892 set_io.generic.level = RAW_SFILEINFO_BASIC_INFORMATION;
2893 set_io.basic_info.in.file.handle = dirh;
2894 set_io.basic_info.in.create_time = io.basic_info.out.create_time;
2895 set_io.basic_info.in.access_time = io.basic_info.out.access_time;
2896 set_io.basic_info.in.write_time = io.basic_info.out.write_time;
2897 set_io.basic_info.in.change_time = io.basic_info.out.change_time;
2898 set_io.basic_info.in.attrib = (io.basic_info.out.attrib
2899 | FILE_ATTRIBUTE_COMPRESSED);
2900 status = smb2_setinfo_file(tree, &set_io);
2901 torture_assert_ntstatus_ok(torture, status, "SMB2_SETINFO_FILE");
2903 status = test_ioctl_compress_get(torture, tmp_ctx, tree, dirh,
2904 &compression_fmt);
2905 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2907 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2908 "dir compression set after SetInfo");
2910 smb2_util_close(tree, dirh);
2911 talloc_free(tmp_ctx);
2912 return true;
2915 static bool test_ioctl_compress_perms(struct torture_context *torture,
2916 struct smb2_tree *tree)
2918 struct smb2_handle fh;
2919 uint16_t compression_fmt;
2920 union smb_fileinfo io;
2921 NTSTATUS status;
2922 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2923 bool ok;
2925 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2926 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2927 FILE_ATTRIBUTE_NORMAL);
2928 torture_assert(torture, ok, "setup compression file");
2930 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
2931 &ok);
2932 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2933 smb2_util_close(tree, fh);
2934 if (!ok) {
2935 torture_skip(torture, "FS compression not supported\n");
2938 /* attempt get compression without READ_ATTR permission */
2939 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2940 FNAME, &fh, 0,
2941 (SEC_RIGHTS_FILE_READ & ~(SEC_FILE_READ_ATTRIBUTE
2942 | SEC_STD_READ_CONTROL
2943 | SEC_FILE_READ_EA)),
2944 FILE_ATTRIBUTE_NORMAL);
2945 torture_assert(torture, ok, "setup compression file");
2947 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2948 &compression_fmt);
2949 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2950 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2951 "compression set after create");
2952 smb2_util_close(tree, fh);
2954 /* set compression without WRITE_ATTR permission should succeed */
2955 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2956 FNAME, &fh, 0,
2957 (SEC_RIGHTS_FILE_WRITE & ~(SEC_FILE_WRITE_ATTRIBUTE
2958 | SEC_STD_WRITE_DAC
2959 | SEC_FILE_WRITE_EA)),
2960 FILE_ATTRIBUTE_NORMAL);
2961 torture_assert(torture, ok, "setup compression file");
2963 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
2964 COMPRESSION_FORMAT_DEFAULT);
2965 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
2966 smb2_util_close(tree, fh);
2968 ok = test_setup_open(torture, tree, tmp_ctx,
2969 FNAME, &fh, SEC_RIGHTS_FILE_ALL,
2970 FILE_ATTRIBUTE_NORMAL);
2971 torture_assert(torture, ok, "setup compression file");
2972 ZERO_STRUCT(io);
2973 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
2974 io.generic.in.file.handle = fh;
2975 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2976 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2978 torture_assert(torture,
2979 (io.all_info2.out.attrib & FILE_ATTRIBUTE_COMPRESSED),
2980 "incorrect compression attr");
2981 smb2_util_close(tree, fh);
2983 /* attempt get compression without READ_DATA permission */
2984 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2985 FNAME, &fh, 0,
2986 (SEC_RIGHTS_FILE_READ & ~SEC_FILE_READ_DATA),
2987 FILE_ATTRIBUTE_NORMAL);
2988 torture_assert(torture, ok, "setup compression file");
2990 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2991 &compression_fmt);
2992 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2993 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2994 "compression enabled after set");
2995 smb2_util_close(tree, fh);
2997 /* attempt get compression with only SYNCHRONIZE permission */
2998 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2999 FNAME, &fh, 0,
3000 SEC_STD_SYNCHRONIZE,
3001 FILE_ATTRIBUTE_NORMAL);
3002 torture_assert(torture, ok, "setup compression file");
3004 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
3005 &compression_fmt);
3006 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
3007 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
3008 "compression not enabled after set");
3009 smb2_util_close(tree, fh);
3011 /* attempt to set compression without WRITE_DATA permission */
3012 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3013 FNAME, &fh, 0,
3014 (SEC_RIGHTS_FILE_WRITE & (~SEC_FILE_WRITE_DATA)),
3015 FILE_ATTRIBUTE_NORMAL);
3016 torture_assert(torture, ok, "setup compression file");
3018 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
3019 COMPRESSION_FORMAT_DEFAULT);
3020 torture_assert_ntstatus_equal(torture, status,
3021 NT_STATUS_ACCESS_DENIED,
3022 "FSCTL_SET_COMPRESSION permission");
3023 smb2_util_close(tree, fh);
3025 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3026 FNAME, &fh, 0,
3027 (SEC_RIGHTS_FILE_WRITE & (~SEC_FILE_WRITE_DATA)),
3028 FILE_ATTRIBUTE_NORMAL);
3029 torture_assert(torture, ok, "setup compression file");
3031 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
3032 COMPRESSION_FORMAT_NONE);
3033 torture_assert_ntstatus_equal(torture, status,
3034 NT_STATUS_ACCESS_DENIED,
3035 "FSCTL_SET_COMPRESSION permission");
3036 smb2_util_close(tree, fh);
3038 talloc_free(tmp_ctx);
3039 return true;
3042 static bool test_ioctl_compress_notsup_get(struct torture_context *torture,
3043 struct smb2_tree *tree)
3045 struct smb2_handle fh;
3046 NTSTATUS status;
3047 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3048 bool ok;
3049 uint16_t compression_fmt;
3051 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3052 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3053 FILE_ATTRIBUTE_NORMAL);
3054 torture_assert(torture, ok, "setup compression file");
3056 /* skip if the server DOES support compression */
3057 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
3058 &ok);
3059 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3060 if (ok) {
3061 smb2_util_close(tree, fh);
3062 torture_skip(torture, "FS compression supported\n");
3066 * Despite not supporting compression, we should get a successful
3067 * response indicating that the file is uncompressed - like WS2016.
3069 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
3070 &compression_fmt);
3071 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
3073 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
3074 "initial compression state not NONE");
3076 smb2_util_close(tree, fh);
3077 talloc_free(tmp_ctx);
3078 return true;
3081 static bool test_ioctl_compress_notsup_set(struct torture_context *torture,
3082 struct smb2_tree *tree)
3084 struct smb2_handle fh;
3085 NTSTATUS status;
3086 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3087 bool ok;
3089 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3090 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3091 FILE_ATTRIBUTE_NORMAL);
3092 torture_assert(torture, ok, "setup compression file");
3094 /* skip if the server DOES support compression */
3095 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
3096 &ok);
3097 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3098 if (ok) {
3099 smb2_util_close(tree, fh);
3100 torture_skip(torture, "FS compression supported\n");
3103 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
3104 COMPRESSION_FORMAT_DEFAULT);
3105 torture_assert_ntstatus_equal(torture, status,
3106 NT_STATUS_NOT_SUPPORTED,
3107 "FSCTL_SET_COMPRESSION default");
3110 * Despite not supporting compression, we should get a successful
3111 * response for set(COMPRESSION_FORMAT_NONE) - like WS2016 ReFS.
3113 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
3114 COMPRESSION_FORMAT_NONE);
3115 torture_assert_ntstatus_ok(torture, status,
3116 "FSCTL_SET_COMPRESSION none");
3118 smb2_util_close(tree, fh);
3119 talloc_free(tmp_ctx);
3120 return true;
3124 basic testing of the SMB2 FSCTL_QUERY_NETWORK_INTERFACE_INFO ioctl
3126 static bool test_ioctl_network_interface_info(struct torture_context *torture,
3127 struct smb2_tree *tree)
3129 union smb_ioctl ioctl;
3130 struct smb2_handle fh;
3131 NTSTATUS status;
3132 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3133 struct fsctl_net_iface_info net_iface;
3134 enum ndr_err_code ndr_ret;
3135 uint32_t caps;
3137 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
3138 if (!(caps & SMB2_CAP_MULTI_CHANNEL)) {
3139 torture_skip(torture, "server doesn't support SMB2_CAP_MULTI_CHANNEL\n");
3142 ZERO_STRUCT(ioctl);
3143 ioctl.smb2.level = RAW_IOCTL_SMB2;
3144 fh.data[0] = UINT64_MAX;
3145 fh.data[1] = UINT64_MAX;
3146 ioctl.smb2.in.file.handle = fh;
3147 ioctl.smb2.in.function = FSCTL_QUERY_NETWORK_INTERFACE_INFO;
3148 ioctl.smb2.in.max_output_response = 0x10000; /* Windows client sets this to 64KiB */
3149 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3151 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3152 torture_assert_ntstatus_ok(torture, status, "FSCTL_QUERY_NETWORK_INTERFACE_INFO");
3154 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, &net_iface,
3155 (ndr_pull_flags_fn_t)ndr_pull_fsctl_net_iface_info);
3156 torture_assert_ndr_success(torture, ndr_ret,
3157 "ndr_pull_fsctl_net_iface_info");
3159 NDR_PRINT_DEBUG(fsctl_net_iface_info, &net_iface);
3161 talloc_free(tmp_ctx);
3162 return true;
3166 * Check whether all @fs_support_flags are set in the server's
3167 * RAW_QFS_ATTRIBUTE_INFORMATION FileSystemAttributes response.
3169 static NTSTATUS test_ioctl_fs_supported(struct torture_context *torture,
3170 struct smb2_tree *tree,
3171 TALLOC_CTX *mem_ctx,
3172 struct smb2_handle *fh,
3173 uint64_t fs_support_flags,
3174 bool *supported)
3176 NTSTATUS status;
3177 union smb_fsinfo info;
3179 ZERO_STRUCT(info);
3180 info.generic.level = RAW_QFS_ATTRIBUTE_INFORMATION;
3181 info.generic.handle = *fh;
3182 status = smb2_getinfo_fs(tree, tree, &info);
3183 if (!NT_STATUS_IS_OK(status)) {
3184 return status;
3187 if ((info.attribute_info.out.fs_attr & fs_support_flags)
3188 == fs_support_flags) {
3189 *supported = true;
3190 } else {
3191 *supported = false;
3193 return NT_STATUS_OK;
3196 static NTSTATUS test_ioctl_sparse_req(struct torture_context *torture,
3197 TALLOC_CTX *mem_ctx,
3198 struct smb2_tree *tree,
3199 struct smb2_handle fh,
3200 bool set)
3202 union smb_ioctl ioctl;
3203 NTSTATUS status;
3204 uint8_t set_sparse;
3206 ZERO_STRUCT(ioctl);
3207 ioctl.smb2.level = RAW_IOCTL_SMB2;
3208 ioctl.smb2.in.file.handle = fh;
3209 ioctl.smb2.in.function = FSCTL_SET_SPARSE;
3210 ioctl.smb2.in.max_output_response = 0;
3211 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3212 set_sparse = (set ? 0xFF : 0x0);
3213 ioctl.smb2.in.out.data = &set_sparse;
3214 ioctl.smb2.in.out.length = sizeof(set_sparse);
3216 status = smb2_ioctl(tree, mem_ctx, &ioctl.smb2);
3217 return status;
3220 static NTSTATUS test_sparse_get(struct torture_context *torture,
3221 TALLOC_CTX *mem_ctx,
3222 struct smb2_tree *tree,
3223 struct smb2_handle fh,
3224 bool *_is_sparse)
3226 union smb_fileinfo io;
3227 NTSTATUS status;
3229 ZERO_STRUCT(io);
3230 io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
3231 io.generic.in.file.handle = fh;
3232 status = smb2_getinfo_file(tree, mem_ctx, &io);
3233 if (!NT_STATUS_IS_OK(status)) {
3234 return status;
3236 *_is_sparse = !!(io.basic_info.out.attrib & FILE_ATTRIBUTE_SPARSE);
3238 return status;
3242 * Manually test setting and clearing sparse flag. Intended for file system
3243 * specific tests to toggle the flag through SMB and check the status in the
3244 * file system.
3246 bool test_ioctl_set_sparse(struct torture_context *tctx)
3248 bool set, ret = true;
3249 const char *filename = NULL;
3250 struct smb2_create create = { };
3251 struct smb2_tree *tree = NULL;
3252 NTSTATUS status;
3254 set = torture_setting_bool(tctx, "set_sparse", true);
3255 filename = torture_setting_string(tctx, "filename", NULL);
3257 if (filename == NULL) {
3258 torture_fail(tctx, "Need to provide filename through "
3259 "--option=torture:filename=testfile\n");
3260 return false;
3263 if (!torture_smb2_connection(tctx, &tree)) {
3264 torture_comment(tctx, "Initializing smb2 connection failed.\n");
3265 return false;
3268 create.in.desired_access = SEC_RIGHTS_DIR_ALL;
3269 create.in.create_options = 0;
3270 create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
3271 create.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
3272 NTCREATEX_SHARE_ACCESS_WRITE |
3273 NTCREATEX_SHARE_ACCESS_DELETE;
3274 create.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
3275 create.in.fname = filename;
3277 status = smb2_create(tree, tctx, &create);
3278 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
3279 "CREATE failed.\n");
3281 status = test_ioctl_sparse_req(tctx, tctx, tree,
3282 create.out.file.handle, set);
3283 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
3284 "FSCTL_SET_SPARSE failed.\n");
3285 done:
3287 return ret;
3290 static bool test_ioctl_sparse_file_flag(struct torture_context *torture,
3291 struct smb2_tree *tree)
3293 struct smb2_handle fh;
3294 union smb_fileinfo io;
3295 NTSTATUS status;
3296 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3297 bool ok;
3298 bool is_sparse;
3300 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3301 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3302 FILE_ATTRIBUTE_NORMAL);
3303 torture_assert(torture, ok, "setup file");
3305 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3306 FILE_SUPPORTS_SPARSE_FILES, &ok);
3307 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3308 if (!ok) {
3309 smb2_util_close(tree, fh);
3310 torture_skip(torture, "Sparse files not supported\n");
3313 ZERO_STRUCT(io);
3314 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
3315 io.generic.in.file.handle = fh;
3316 status = smb2_getinfo_file(tree, tmp_ctx, &io);
3317 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
3319 torture_assert(torture,
3320 ((io.all_info2.out.attrib & FILE_ATTRIBUTE_SPARSE) == 0),
3321 "sparse attr before set");
3323 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
3324 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3326 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3327 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3328 torture_assert(torture, is_sparse, "no sparse attr after set");
3330 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false);
3331 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3333 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3334 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3335 torture_assert(torture, !is_sparse, "sparse attr after unset");
3337 smb2_util_close(tree, fh);
3338 talloc_free(tmp_ctx);
3339 return true;
3342 static bool test_ioctl_sparse_file_attr(struct torture_context *torture,
3343 struct smb2_tree *tree)
3345 struct smb2_handle fh;
3346 NTSTATUS status;
3347 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3348 bool ok;
3349 bool is_sparse;
3351 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3352 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3353 (FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_SPARSE));
3354 torture_assert(torture, ok, "setup file");
3356 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3357 FILE_SUPPORTS_SPARSE_FILES, &ok);
3358 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3359 if (!ok) {
3360 smb2_util_close(tree, fh);
3361 torture_skip(torture, "Sparse files not supported\n");
3364 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3365 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3366 torture_assert(torture, !is_sparse, "sparse attr on open");
3368 smb2_util_close(tree, fh);
3369 talloc_free(tmp_ctx);
3370 return true;
3373 static bool test_ioctl_sparse_dir_flag(struct torture_context *torture,
3374 struct smb2_tree *tree)
3376 struct smb2_handle dirh;
3377 NTSTATUS status;
3378 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3379 bool ok;
3381 smb2_deltree(tree, DNAME);
3382 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3383 DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL,
3384 FILE_ATTRIBUTE_DIRECTORY);
3385 torture_assert(torture, ok, "setup sparse directory");
3387 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &dirh,
3388 FILE_SUPPORTS_SPARSE_FILES, &ok);
3389 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3390 if (!ok) {
3391 smb2_util_close(tree, dirh);
3392 smb2_deltree(tree, DNAME);
3393 torture_skip(torture, "Sparse files not supported\n");
3396 /* set sparse dir should fail, check for 2k12 & 2k8 response */
3397 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, dirh, true);
3398 torture_assert_ntstatus_equal(torture, status,
3399 NT_STATUS_INVALID_PARAMETER,
3400 "dir FSCTL_SET_SPARSE status");
3402 smb2_util_close(tree, dirh);
3403 smb2_deltree(tree, DNAME);
3404 talloc_free(tmp_ctx);
3405 return true;
3409 * FSCTL_SET_SPARSE can be sent with (already tested) or without a SetSparse
3410 * buffer to indicate whether the flag should be set or cleared. When sent
3411 * without a buffer, it must be handled as if SetSparse=TRUE.
3413 static bool test_ioctl_sparse_set_nobuf(struct torture_context *torture,
3414 struct smb2_tree *tree)
3416 struct smb2_handle fh;
3417 union smb_ioctl ioctl;
3418 NTSTATUS status;
3419 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3420 bool ok;
3421 bool is_sparse;
3423 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3424 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3425 FILE_ATTRIBUTE_NORMAL);
3426 torture_assert(torture, ok, "setup file");
3428 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3429 FILE_SUPPORTS_SPARSE_FILES, &ok);
3430 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3431 if (!ok) {
3432 smb2_util_close(tree, fh);
3433 torture_skip(torture, "Sparse files not supported\n");
3436 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3437 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3438 torture_assert(torture, !is_sparse, "sparse attr before set");
3440 ZERO_STRUCT(ioctl);
3441 ioctl.smb2.level = RAW_IOCTL_SMB2;
3442 ioctl.smb2.in.file.handle = fh;
3443 ioctl.smb2.in.function = FSCTL_SET_SPARSE;
3444 ioctl.smb2.in.max_output_response = 0;
3445 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3446 /* ioctl.smb2.in.out is zeroed, no SetSparse buffer */
3448 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3449 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3451 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3452 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3453 torture_assert(torture, is_sparse, "no sparse attr after set");
3455 /* second non-SetSparse request shouldn't toggle sparse */
3456 ZERO_STRUCT(ioctl);
3457 ioctl.smb2.level = RAW_IOCTL_SMB2;
3458 ioctl.smb2.in.file.handle = fh;
3459 ioctl.smb2.in.function = FSCTL_SET_SPARSE;
3460 ioctl.smb2.in.max_output_response = 0;
3461 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3463 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3464 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3466 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3467 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3468 torture_assert(torture, is_sparse, "no sparse attr after 2nd set");
3470 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false);
3471 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3473 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3474 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3475 torture_assert(torture, !is_sparse, "sparse attr after unset");
3477 smb2_util_close(tree, fh);
3478 talloc_free(tmp_ctx);
3479 return true;
3482 static bool test_ioctl_sparse_set_oversize(struct torture_context *torture,
3483 struct smb2_tree *tree)
3485 struct smb2_handle fh;
3486 union smb_ioctl ioctl;
3487 NTSTATUS status;
3488 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3489 bool ok;
3490 bool is_sparse;
3491 uint8_t buf[100];
3493 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3494 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3495 FILE_ATTRIBUTE_NORMAL);
3496 torture_assert(torture, ok, "setup file");
3498 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3499 FILE_SUPPORTS_SPARSE_FILES, &ok);
3500 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3501 if (!ok) {
3502 smb2_util_close(tree, fh);
3503 torture_skip(torture, "Sparse files not supported\n");
3506 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3507 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3508 torture_assert(torture, !is_sparse, "sparse attr before set");
3510 ZERO_STRUCT(ioctl);
3511 ioctl.smb2.level = RAW_IOCTL_SMB2;
3512 ioctl.smb2.in.file.handle = fh;
3513 ioctl.smb2.in.function = FSCTL_SET_SPARSE;
3514 ioctl.smb2.in.max_output_response = 0;
3515 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3518 * Attach a request buffer larger than FILE_SET_SPARSE_BUFFER
3519 * Windows still successfully processes the request.
3521 ZERO_ARRAY(buf);
3522 buf[0] = 0xFF; /* attempt to set sparse */
3523 ioctl.smb2.in.out.data = buf;
3524 ioctl.smb2.in.out.length = ARRAY_SIZE(buf);
3526 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3527 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3529 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3530 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3531 torture_assert(torture, is_sparse, "no sparse attr after set");
3533 ZERO_STRUCT(ioctl);
3534 ioctl.smb2.level = RAW_IOCTL_SMB2;
3535 ioctl.smb2.in.file.handle = fh;
3536 ioctl.smb2.in.function = FSCTL_SET_SPARSE;
3537 ioctl.smb2.in.max_output_response = 0;
3538 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3540 ZERO_ARRAY(buf); /* clear sparse */
3541 ioctl.smb2.in.out.data = buf;
3542 ioctl.smb2.in.out.length = ARRAY_SIZE(buf);
3544 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3545 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3547 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3548 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3549 torture_assert(torture, !is_sparse, "sparse attr after clear");
3551 smb2_util_close(tree, fh);
3552 talloc_free(tmp_ctx);
3553 return true;
3556 static NTSTATUS test_ioctl_qar_req(struct torture_context *torture,
3557 TALLOC_CTX *mem_ctx,
3558 struct smb2_tree *tree,
3559 struct smb2_handle fh,
3560 int64_t req_off,
3561 int64_t req_len,
3562 struct file_alloced_range_buf **_rsp,
3563 uint64_t *_rsp_count)
3565 union smb_ioctl ioctl;
3566 NTSTATUS status;
3567 enum ndr_err_code ndr_ret;
3568 struct file_alloced_range_buf far_buf;
3569 struct file_alloced_range_buf *far_rsp = NULL;
3570 uint64_t far_count = 0;
3571 int i;
3572 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
3573 if (tmp_ctx == NULL) {
3574 return NT_STATUS_NO_MEMORY;
3577 ZERO_STRUCT(ioctl);
3578 ioctl.smb2.level = RAW_IOCTL_SMB2;
3579 ioctl.smb2.in.file.handle = fh;
3580 ioctl.smb2.in.function = FSCTL_QUERY_ALLOCATED_RANGES;
3581 ioctl.smb2.in.max_output_response = 1024;
3582 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3584 far_buf.file_off = req_off;
3585 far_buf.len = req_len;
3587 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
3588 &far_buf,
3589 (ndr_push_flags_fn_t)ndr_push_file_alloced_range_buf);
3590 if (ndr_ret != NDR_ERR_SUCCESS) {
3591 status = NT_STATUS_UNSUCCESSFUL;
3592 goto err_out;
3595 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3596 if (!NT_STATUS_IS_OK(status)) {
3597 goto err_out;
3600 if (ioctl.smb2.out.out.length == 0) {
3601 goto done;
3604 if ((ioctl.smb2.out.out.length % sizeof(far_buf)) != 0) {
3605 torture_comment(torture, "invalid qry_alloced rsp len: %zd:",
3606 ioctl.smb2.out.out.length);
3607 status = NT_STATUS_INVALID_VIEW_SIZE;
3608 goto err_out;
3611 far_count = (ioctl.smb2.out.out.length / sizeof(far_buf));
3612 far_rsp = talloc_array(mem_ctx, struct file_alloced_range_buf,
3613 far_count);
3614 if (far_rsp == NULL) {
3615 status = NT_STATUS_NO_MEMORY;
3616 goto err_out;
3619 for (i = 0; i < far_count; i++) {
3620 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
3621 &far_rsp[i],
3622 (ndr_pull_flags_fn_t)ndr_pull_file_alloced_range_buf);
3623 if (ndr_ret != NDR_ERR_SUCCESS) {
3624 status = NT_STATUS_UNSUCCESSFUL;
3625 goto err_out;
3627 /* move to next buffer */
3628 ioctl.smb2.out.out.data += sizeof(far_buf);
3629 ioctl.smb2.out.out.length -= sizeof(far_buf);
3632 done:
3633 *_rsp = far_rsp;
3634 *_rsp_count = far_count;
3635 status = NT_STATUS_OK;
3636 err_out:
3637 talloc_free(tmp_ctx);
3638 return status;
3641 static bool test_ioctl_sparse_qar(struct torture_context *torture,
3642 struct smb2_tree *tree)
3644 struct smb2_handle fh;
3645 NTSTATUS status;
3646 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3647 bool ok;
3648 bool is_sparse;
3649 struct file_alloced_range_buf *far_rsp = NULL;
3650 uint64_t far_count = 0;
3652 /* zero length file, shouldn't have any ranges */
3653 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3654 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3655 FILE_ATTRIBUTE_NORMAL);
3656 torture_assert(torture, ok, "setup file");
3658 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3659 FILE_SUPPORTS_SPARSE_FILES, &ok);
3660 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3661 if (!ok) {
3662 smb2_util_close(tree, fh);
3663 torture_skip(torture, "Sparse files not supported\n");
3666 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3667 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3668 torture_assert(torture, !is_sparse, "sparse attr before set");
3670 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3671 0, /* off */
3672 0, /* len */
3673 &far_rsp,
3674 &far_count);
3675 torture_assert_ntstatus_ok(torture, status,
3676 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3677 torture_assert_u64_equal(torture, far_count, 0,
3678 "unexpected response len");
3680 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3681 0, /* off */
3682 1024, /* len */
3683 &far_rsp,
3684 &far_count);
3685 torture_assert_ntstatus_ok(torture, status,
3686 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3687 torture_assert_u64_equal(torture, far_count, 0,
3688 "unexpected response len");
3690 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
3691 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3693 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3694 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3695 torture_assert(torture, is_sparse, "no sparse attr after set");
3697 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3698 0, /* off */
3699 1024, /* len */
3700 &far_rsp,
3701 &far_count);
3702 torture_assert_ntstatus_ok(torture, status,
3703 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3704 torture_assert_u64_equal(torture, far_count, 0,
3705 "unexpected response len");
3707 /* write into the (now) sparse file at 4k offset */
3708 ok = write_pattern(torture, tree, tmp_ctx, fh,
3709 4096, /* off */
3710 1024, /* len */
3711 4096); /* pattern offset */
3712 torture_assert(torture, ok, "write pattern");
3715 * Query range before write off. Whether it's allocated or not is FS
3716 * dependent. NTFS deallocates chunks in 64K increments, but others
3717 * (e.g. XFS, Btrfs, etc.) may deallocate 4K chunks.
3719 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3720 0, /* off */
3721 4096, /* len */
3722 &far_rsp,
3723 &far_count);
3724 torture_assert_ntstatus_ok(torture, status,
3725 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3726 if (far_count == 0) {
3727 torture_comment(torture, "FS deallocated 4K chunk\n");
3728 } else {
3729 /* expect fully allocated */
3730 torture_assert_u64_equal(torture, far_count, 1,
3731 "unexpected response len");
3732 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0, "far offset");
3733 torture_assert_u64_equal(torture, far_rsp[0].len, 4096, "far len");
3737 * Query range before and past write, it should be allocated up to the
3738 * end of the write.
3740 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3741 0, /* off */
3742 8192, /* len */
3743 &far_rsp,
3744 &far_count);
3745 torture_assert_ntstatus_ok(torture, status,
3746 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3747 torture_assert_u64_equal(torture, far_count, 1,
3748 "unexpected response len");
3749 /* FS dependent */
3750 if (far_rsp[0].file_off == 4096) {
3751 /* 4K chunk unallocated */
3752 torture_assert_u64_equal(torture, far_rsp[0].file_off, 4096, "far offset");
3753 torture_assert_u64_equal(torture, far_rsp[0].len, 1024, "far len");
3754 } else {
3755 /* expect fully allocated */
3756 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0, "far offset");
3757 torture_assert_u64_equal(torture, far_rsp[0].len, 5120, "far len");
3760 smb2_util_close(tree, fh);
3761 talloc_free(tmp_ctx);
3762 return true;
3765 static bool test_ioctl_sparse_qar_malformed(struct torture_context *torture,
3766 struct smb2_tree *tree)
3768 struct smb2_handle fh;
3769 union smb_ioctl ioctl;
3770 struct file_alloced_range_buf far_buf;
3771 NTSTATUS status;
3772 enum ndr_err_code ndr_ret;
3773 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3774 bool ok;
3775 size_t old_len;
3777 /* zero length file, shouldn't have any ranges */
3778 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3779 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3780 FILE_ATTRIBUTE_NORMAL);
3781 torture_assert(torture, ok, "setup file");
3783 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3784 FILE_SUPPORTS_SPARSE_FILES, &ok);
3785 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3786 if (!ok) {
3787 smb2_util_close(tree, fh);
3788 torture_skip(torture, "Sparse files not supported\n");
3791 /* no allocated ranges, no space for range response, should pass */
3792 ZERO_STRUCT(ioctl);
3793 ioctl.smb2.level = RAW_IOCTL_SMB2;
3794 ioctl.smb2.in.file.handle = fh;
3795 ioctl.smb2.in.function = FSCTL_QUERY_ALLOCATED_RANGES;
3796 ioctl.smb2.in.max_output_response = 0;
3797 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3799 far_buf.file_off = 0;
3800 far_buf.len = 1024;
3801 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
3802 &far_buf,
3803 (ndr_push_flags_fn_t)ndr_push_file_alloced_range_buf);
3804 torture_assert_ndr_success(torture, ndr_ret, "push far ndr buf");
3806 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3807 torture_assert_ntstatus_ok(torture, status, "FSCTL_QUERY_ALLOCATED_RANGES");
3809 /* write into the file at 4k offset */
3810 ok = write_pattern(torture, tree, tmp_ctx, fh,
3811 0, /* off */
3812 1024, /* len */
3813 0); /* pattern offset */
3814 torture_assert(torture, ok, "write pattern");
3816 /* allocated range, no space for range response, should fail */
3817 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3818 torture_assert_ntstatus_equal(torture, status,
3819 NT_STATUS_BUFFER_TOO_SMALL, "qar no space");
3821 /* oversize (2x) file_alloced_range_buf in request, should pass */
3822 ioctl.smb2.in.max_output_response = 1024;
3823 old_len = ioctl.smb2.in.out.length;
3824 ok = data_blob_realloc(tmp_ctx, &ioctl.smb2.in.out,
3825 (ioctl.smb2.in.out.length * 2));
3826 torture_assert(torture, ok, "2x data buffer");
3827 memcpy(ioctl.smb2.in.out.data + old_len, ioctl.smb2.in.out.data,
3828 old_len);
3829 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3830 torture_assert_ntstatus_ok(torture, status, "qar too big");
3832 /* no file_alloced_range_buf in request, should fail */
3833 data_blob_free(&ioctl.smb2.in.out);
3834 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3835 torture_assert_ntstatus_equal(torture, status,
3836 NT_STATUS_INVALID_PARAMETER, "qar empty");
3838 return true;
3841 static bool test_ioctl_sparse_qar_truncated(struct torture_context *torture,
3842 struct smb2_tree *tree)
3844 struct smb2_handle fh;
3845 union smb_ioctl ioctl;
3846 struct file_alloced_range_buf far_buf;
3847 NTSTATUS status;
3848 enum ndr_err_code ndr_ret;
3849 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3850 bool ok;
3851 struct file_alloced_range_buf far_rsp;
3853 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3854 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3855 FILE_ATTRIBUTE_NORMAL);
3856 torture_assert(torture, ok, "setup file");
3858 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3859 FILE_SUPPORTS_SPARSE_FILES, &ok);
3860 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3861 if (!ok) {
3862 smb2_util_close(tree, fh);
3863 torture_skip(torture, "Sparse files not supported\n");
3866 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
3867 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3870 * Write 0 and 1M offsets as (hopefully) two separate extents.
3871 * XXX this test assumes that these ranges will be recorded as separate
3872 * FSCTL_QUERY_ALLOCATED_RANGES extents, which isn't strictly required:
3873 * the spec basically says the FS can do what it wants as long as
3874 * non-zeroed data ranges aren't reported as sparse holes.
3876 ok = write_pattern(torture, tree, tmp_ctx, fh,
3877 0, /* off */
3878 1024, /* len */
3879 0); /* pattern offset */
3880 torture_assert(torture, ok, "write pattern");
3881 ok = write_pattern(torture, tree, tmp_ctx, fh,
3882 1024 * 1024, /* off */
3883 1024, /* len */
3884 0); /* pattern offset */
3885 torture_assert(torture, ok, "write pattern");
3887 /* qar max output enough to carry one range, should be truncated */
3888 ZERO_STRUCT(ioctl);
3889 ioctl.smb2.level = RAW_IOCTL_SMB2;
3890 ioctl.smb2.in.file.handle = fh;
3891 ioctl.smb2.in.function = FSCTL_QUERY_ALLOCATED_RANGES;
3892 ioctl.smb2.in.max_output_response = sizeof(struct file_alloced_range_buf);
3893 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3895 far_buf.file_off = 0;
3896 far_buf.len = 2048 * 1024;
3897 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
3898 &far_buf,
3899 (ndr_push_flags_fn_t)ndr_push_file_alloced_range_buf);
3900 torture_assert_ndr_success(torture, ndr_ret, "push far ndr buf");
3902 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3903 torture_assert_ntstatus_equal(torture, status,
3904 STATUS_BUFFER_OVERFLOW, "qar truncated");
3905 torture_assert_size_equal(torture,
3906 ioctl.smb2.out.out.length, sizeof(far_buf),
3907 "qar outlen");
3908 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
3909 &far_rsp,
3910 (ndr_pull_flags_fn_t)ndr_pull_file_alloced_range_buf);
3911 torture_assert_ndr_success(torture, ndr_ret, "pull far range");
3912 torture_assert_u64_equal(torture, far_rsp.file_off, 0, "far offset");
3913 /* length depends on allocation behaviour of FS, so allow range */
3914 torture_assert(torture, far_rsp.len >= 1024, "far len");
3916 /* qar max output for just under 2 ranges, should be truncated */
3917 ZERO_STRUCT(ioctl);
3918 ioctl.smb2.level = RAW_IOCTL_SMB2;
3919 ioctl.smb2.in.file.handle = fh;
3920 ioctl.smb2.in.function = FSCTL_QUERY_ALLOCATED_RANGES;
3921 ioctl.smb2.in.max_output_response = 31;
3922 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3924 far_buf.file_off = 0;
3925 far_buf.len = 2048 * 1024;
3926 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
3927 &far_buf,
3928 (ndr_push_flags_fn_t)ndr_push_file_alloced_range_buf);
3929 torture_assert_ndr_success(torture, ndr_ret, "push far ndr buf");
3931 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3932 torture_assert_ntstatus_equal(torture, status,
3933 STATUS_BUFFER_OVERFLOW, "qar truncated");
3934 torture_assert_size_equal(torture,
3935 ioctl.smb2.out.out.length, sizeof(far_buf),
3936 "qar outlen");
3937 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
3938 &far_rsp,
3939 (ndr_pull_flags_fn_t)ndr_pull_file_alloced_range_buf);
3940 torture_assert_ndr_success(torture, ndr_ret, "pull far range");
3941 torture_assert_u64_equal(torture, far_rsp.file_off, 0, "far offset");
3942 torture_assert(torture, far_rsp.len >= 1024, "far len");
3944 /* qar max output for 2 ranges, should pass */
3945 ZERO_STRUCT(ioctl);
3946 ioctl.smb2.level = RAW_IOCTL_SMB2;
3947 ioctl.smb2.in.file.handle = fh;
3948 ioctl.smb2.in.function = FSCTL_QUERY_ALLOCATED_RANGES;
3949 ioctl.smb2.in.max_output_response = 32;
3950 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3952 far_buf.file_off = 0;
3953 far_buf.len = 2048 * 1024;
3954 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
3955 &far_buf,
3956 (ndr_push_flags_fn_t)ndr_push_file_alloced_range_buf);
3957 torture_assert_ndr_success(torture, ndr_ret, "push far ndr buf");
3959 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3960 torture_assert_ntstatus_ok(torture, status, "qar non-truncated");
3961 torture_assert_size_equal(torture,
3962 ioctl.smb2.out.out.length,
3963 2 * sizeof(far_buf), "qar outlen");
3964 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
3965 &far_rsp,
3966 (ndr_pull_flags_fn_t)ndr_pull_file_alloced_range_buf);
3967 torture_assert_ndr_success(torture, ndr_ret, "pull far range");
3968 torture_assert_u64_equal(torture, far_rsp.file_off, 0, "far offset");
3969 torture_assert(torture, far_rsp.len >= 1024, "far len");
3970 /* move to next buffer */
3971 ioctl.smb2.out.out.data += sizeof(far_buf);
3972 ioctl.smb2.out.out.length -= sizeof(far_buf);
3973 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
3974 &far_rsp,
3975 (ndr_pull_flags_fn_t)ndr_pull_file_alloced_range_buf);
3976 torture_assert_ndr_success(torture, ndr_ret, "pull far range");
3977 torture_assert_u64_equal(torture, far_rsp.file_off, 1024 * 1024,
3978 "far offset");
3979 torture_assert(torture, far_rsp.len >= 1024, "far len");
3981 smb2_util_close(tree, fh);
3982 talloc_free(tmp_ctx);
3983 return true;
3986 bool test_ioctl_alternate_data_stream(struct torture_context *tctx)
3988 bool ret = false;
3989 const char *fname = DNAME "\\test_stream_ioctl_dir";
3990 const char *sname = DNAME "\\test_stream_ioctl_dir:stream";
3991 NTSTATUS status;
3992 struct smb2_create create = {};
3993 struct smb2_tree *tree = NULL;
3994 struct smb2_handle h1 = {{0}};
3995 union smb_ioctl ioctl;
3997 if (!torture_smb2_connection(tctx, &tree)) {
3998 torture_comment(tctx, "Initializing smb2 connection failed.\n");
3999 return false;
4002 smb2_deltree(tree, DNAME);
4004 status = torture_smb2_testdir(tree, DNAME, &h1);
4005 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
4006 "torture_smb2_testdir failed\n");
4008 status = smb2_util_close(tree, h1);
4009 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
4010 "smb2_util_close failed\n");
4011 create = (struct smb2_create) {
4012 .in.desired_access = SEC_FILE_ALL,
4013 .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
4014 .in.file_attributes = FILE_ATTRIBUTE_HIDDEN,
4015 .in.create_disposition = NTCREATEX_DISP_CREATE,
4016 .in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION,
4017 .in.fname = fname,
4020 status = smb2_create(tree, tctx, &create);
4021 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
4022 "smb2_create failed\n");
4024 h1 = create.out.file.handle;
4025 status = smb2_util_close(tree, h1);
4026 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
4027 "smb2_util_close failed\n");
4029 create = (struct smb2_create) {
4030 .in.desired_access = SEC_FILE_ALL,
4031 .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
4032 .in.file_attributes = FILE_ATTRIBUTE_NORMAL,
4033 .in.create_disposition = NTCREATEX_DISP_CREATE,
4034 .in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION,
4035 .in.fname = sname,
4037 status = smb2_create(tree, tctx, &create);
4038 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
4039 "smb2_create failed\n");
4040 h1 = create.out.file.handle;
4042 ZERO_STRUCT(ioctl);
4043 ioctl.smb2.level = RAW_IOCTL_SMB2;
4044 ioctl.smb2.in.file.handle = h1;
4045 ioctl.smb2.in.function = FSCTL_CREATE_OR_GET_OBJECT_ID,
4046 ioctl.smb2.in.max_output_response = 64;
4047 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
4048 status = smb2_ioctl(tree, tctx, &ioctl.smb2);
4049 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
4050 "smb2_ioctl failed\n");
4051 ret = true;
4053 done:
4055 smb2_util_close(tree, h1);
4056 smb2_deltree(tree, DNAME);
4057 return ret;
4061 * 2.3.57 FSCTL_SET_ZERO_DATA Request
4063 * How an implementation zeros data within a file is implementation-dependent.
4064 * A file system MAY choose to deallocate regions of disk space that have been
4065 * zeroed.<50>
4066 * <50>
4067 * ... NTFS might deallocate disk space in the file if the file is stored on an
4068 * NTFS volume, and the file is sparse or compressed. It will free any allocated
4069 * space in chunks of 64 kilobytes that begin at an offset that is a multiple of
4070 * 64 kilobytes. Other bytes in the file (prior to the first freed 64-kilobyte
4071 * chunk and after the last freed 64-kilobyte chunk) will be zeroed but not
4072 * deallocated.
4074 static NTSTATUS test_ioctl_zdata_req(struct torture_context *torture,
4075 TALLOC_CTX *mem_ctx,
4076 struct smb2_tree *tree,
4077 struct smb2_handle fh,
4078 int64_t off,
4079 int64_t beyond_final_zero)
4081 union smb_ioctl ioctl;
4082 NTSTATUS status;
4083 enum ndr_err_code ndr_ret;
4084 struct file_zero_data_info zdata_info;
4085 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
4086 if (tmp_ctx == NULL) {
4087 return NT_STATUS_NO_MEMORY;
4090 ZERO_STRUCT(ioctl);
4091 ioctl.smb2.level = RAW_IOCTL_SMB2;
4092 ioctl.smb2.in.file.handle = fh;
4093 ioctl.smb2.in.function = FSCTL_SET_ZERO_DATA;
4094 ioctl.smb2.in.max_output_response = 0;
4095 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
4097 zdata_info.file_off = off;
4098 zdata_info.beyond_final_zero = beyond_final_zero;
4100 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
4101 &zdata_info,
4102 (ndr_push_flags_fn_t)ndr_push_file_zero_data_info);
4103 if (ndr_ret != NDR_ERR_SUCCESS) {
4104 status = NT_STATUS_UNSUCCESSFUL;
4105 goto err_out;
4108 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
4109 if (!NT_STATUS_IS_OK(status)) {
4110 goto err_out;
4113 status = NT_STATUS_OK;
4114 err_out:
4115 talloc_free(tmp_ctx);
4116 return status;
4119 bool test_ioctl_zero_data(struct torture_context *tctx)
4121 bool ret = true;
4122 int offset, beyond_final_zero;
4123 const char *filename;
4124 NTSTATUS status;
4125 struct smb2_create create = { };
4126 struct smb2_tree *tree = NULL;
4128 offset = torture_setting_int(tctx, "offset", -1);
4130 if (offset < 0) {
4131 torture_fail(tctx, "Need to provide non-negative offset "
4132 "through --option=torture:offset=NNN\n");
4133 return false;
4136 beyond_final_zero = torture_setting_int(tctx, "beyond_final_zero",
4137 -1);
4138 if (beyond_final_zero < 0) {
4139 torture_fail(tctx, "Need to provide non-negative "
4140 "'beyond final zero' through "
4141 "--option=torture:beyond_final_zero=NNN\n");
4142 return false;
4144 filename = torture_setting_string(tctx, "filename", NULL);
4145 if (filename == NULL) {
4146 torture_fail(tctx, "Need to provide filename through "
4147 "--option=torture:filename=testfile\n");
4148 return false;
4151 if (!torture_smb2_connection(tctx, &tree)) {
4152 torture_comment(tctx, "Initializing smb2 connection failed.\n");
4153 return false;
4156 create.in.desired_access = SEC_RIGHTS_DIR_ALL;
4157 create.in.create_options = 0;
4158 create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
4159 create.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
4160 NTCREATEX_SHARE_ACCESS_WRITE |
4161 NTCREATEX_SHARE_ACCESS_DELETE;
4162 create.in.create_disposition = NTCREATEX_DISP_OPEN;
4163 create.in.fname = filename;
4165 status = smb2_create(tree, tctx, &create);
4166 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
4167 "CREATE failed.\n");
4169 status = test_ioctl_zdata_req(tctx, tctx, tree,
4170 create.out.file.handle,
4171 offset,
4172 beyond_final_zero);
4173 torture_assert_ntstatus_ok_goto(tctx,
4174 status,
4175 ret,
4176 done,
4177 "FSCTL_SET_ZERO_DATA failed.\n");
4179 done:
4180 return ret;
4183 static bool test_ioctl_sparse_punch(struct torture_context *torture,
4184 struct smb2_tree *tree)
4186 struct smb2_handle fh;
4187 NTSTATUS status;
4188 TALLOC_CTX *tmp_ctx = talloc_new(tree);
4189 bool ok;
4190 bool is_sparse;
4191 struct file_alloced_range_buf *far_rsp = NULL;
4192 uint64_t far_count = 0;
4194 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4195 FNAME, &fh, 4096, SEC_RIGHTS_FILE_ALL,
4196 FILE_ATTRIBUTE_NORMAL);
4197 torture_assert(torture, ok, "setup file");
4199 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
4200 FILE_SUPPORTS_SPARSE_FILES, &ok);
4201 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4202 if (!ok) {
4203 smb2_util_close(tree, fh);
4204 torture_skip(torture, "Sparse files not supported\n");
4207 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4208 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4209 torture_assert(torture, !is_sparse, "sparse attr before set");
4211 /* zero (hole-punch) the data, without sparse flag */
4212 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4213 0, /* off */
4214 4096); /* beyond_final_zero */
4215 torture_assert_ntstatus_ok(torture, status, "zero_data");
4217 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4218 0, /* off */
4219 4096, /* len */
4220 &far_rsp,
4221 &far_count);
4222 torture_assert_ntstatus_ok(torture, status,
4223 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4224 torture_assert_u64_equal(torture, far_count, 1,
4225 "unexpected response len");
4227 /* expect fully allocated */
4228 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4229 "unexpected far off");
4230 torture_assert_u64_equal(torture, far_rsp[0].len, 4096,
4231 "unexpected far len");
4232 /* check that the data is now zeroed */
4233 ok = check_zero(torture, tree, tmp_ctx, fh, 0, 4096);
4234 torture_assert(torture, ok, "non-sparse zeroed range");
4236 /* set sparse */
4237 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4238 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4240 /* still fully allocated on NTFS, see note below for Samba */
4241 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4242 0, /* off */
4243 4096, /* len */
4244 &far_rsp,
4245 &far_count);
4246 torture_assert_ntstatus_ok(torture, status,
4247 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4249 * FS specific: Samba uses PUNCH_HOLE to zero the range, and
4250 * subsequently uses fallocate() to allocate the punched range if the
4251 * file is marked non-sparse and "strict allocate" is enabled. In both
4252 * cases, the zeroed range will not be detected by SEEK_DATA, so the
4253 * range won't be present in QAR responses until the file is marked
4254 * non-sparse again.
4256 if (far_count == 0) {
4257 torture_comment(torture, "non-sparse zeroed range disappeared "
4258 "after marking sparse\n");
4259 } else {
4260 /* NTFS: range remains fully allocated */
4261 torture_assert_u64_equal(torture, far_count, 1,
4262 "unexpected response len");
4263 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4264 "unexpected far off");
4265 torture_assert_u64_equal(torture, far_rsp[0].len, 4096,
4266 "unexpected far len");
4269 /* zero (hole-punch) the data, _with_ sparse flag */
4270 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4271 0, /* off */
4272 4096); /* beyond_final_zero */
4273 torture_assert_ntstatus_ok(torture, status, "zero_data");
4275 /* the range should no longer be alloced */
4276 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4277 0, /* off */
4278 4096, /* len */
4279 &far_rsp,
4280 &far_count);
4281 torture_assert_ntstatus_ok(torture, status,
4282 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4283 torture_assert_u64_equal(torture, far_count, 0,
4284 "unexpected response len");
4286 ok = check_zero(torture, tree, tmp_ctx, fh, 0, 4096);
4287 torture_assert(torture, ok, "sparse zeroed range");
4289 /* remove sparse flag, this should "unsparse" the zeroed range */
4290 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false);
4291 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4293 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4294 0, /* off */
4295 4096, /* len */
4296 &far_rsp,
4297 &far_count);
4298 torture_assert_ntstatus_ok(torture, status,
4299 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4300 torture_assert_u64_equal(torture, far_count, 1,
4301 "unexpected response len");
4302 /* expect fully allocated */
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, 4096,
4306 "unexpected far len");
4308 ok = check_zero(torture, tree, tmp_ctx, fh, 0, 4096);
4309 torture_assert(torture, ok, "sparse zeroed range");
4311 smb2_util_close(tree, fh);
4312 talloc_free(tmp_ctx);
4313 return true;
4317 * Find the point at which a zeroed range in a sparse file is deallocated by the
4318 * underlying filesystem. NTFS on Windows Server 2012 deallocates chunks in 64k
4319 * increments. Also check whether zeroed neighbours are merged for deallocation.
4321 static bool test_ioctl_sparse_hole_dealloc(struct torture_context *torture,
4322 struct smb2_tree *tree)
4324 struct smb2_handle fh;
4325 NTSTATUS status;
4326 TALLOC_CTX *tmp_ctx = talloc_new(tree);
4327 bool ok;
4328 uint64_t file_size;
4329 uint64_t hlen;
4330 uint64_t dealloc_chunk_len = 0;
4331 struct file_alloced_range_buf *far_rsp = NULL;
4332 uint64_t far_count = 0;
4334 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4335 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
4336 FILE_ATTRIBUTE_NORMAL);
4337 torture_assert(torture, ok, "setup file 1");
4339 /* check for FS sparse file */
4340 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
4341 FILE_SUPPORTS_SPARSE_FILES, &ok);
4342 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4343 if (!ok) {
4344 smb2_util_close(tree, fh);
4345 torture_skip(torture, "Sparse files not supported\n");
4348 /* set sparse */
4349 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4350 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4352 file_size = 1024 * 1024;
4354 ok = write_pattern(torture, tree, tmp_ctx, fh,
4355 0, /* off */
4356 file_size, /* len */
4357 0); /* pattern offset */
4358 torture_assert(torture, ok, "write pattern");
4360 /* check allocated ranges, should be fully allocated */
4361 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4362 0, /* off */
4363 file_size, /* len */
4364 &far_rsp,
4365 &far_count);
4366 torture_assert_ntstatus_ok(torture, status,
4367 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4368 torture_assert_u64_equal(torture, far_count, 1,
4369 "unexpected response len");
4370 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4371 "unexpected far off");
4372 torture_assert_u64_equal(torture, far_rsp[0].len, file_size,
4373 "unexpected far len");
4375 /* punch holes in sizes of 1k increments */
4376 for (hlen = 0; hlen <= file_size; hlen += 4096) {
4378 /* punch a hole from zero to the current increment */
4379 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4380 0, /* off */
4381 hlen); /* beyond_final_zero */
4382 torture_assert_ntstatus_ok(torture, status, "zero_data");
4384 /* ensure hole is zeroed, and pattern is consistent */
4385 ok = check_zero(torture, tree, tmp_ctx, fh, 0, hlen);
4386 torture_assert(torture, ok, "sparse zeroed range");
4388 ok = check_pattern(torture, tree, tmp_ctx, fh, hlen,
4389 file_size - hlen, hlen);
4390 torture_assert(torture, ok, "allocated pattern range");
4392 /* Check allocated ranges, hole might have been deallocated */
4393 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4394 0, /* off */
4395 file_size, /* len */
4396 &far_rsp,
4397 &far_count);
4398 torture_assert_ntstatus_ok(torture, status,
4399 "FSCTL_QUERY_ALLOCATED_RANGES");
4400 if ((hlen == file_size) && (far_count == 0)) {
4401 /* hole covered entire file, deallocation occurred */
4402 dealloc_chunk_len = file_size;
4403 break;
4406 torture_assert_u64_equal(torture, far_count, 1,
4407 "unexpected response len");
4408 if (far_rsp[0].file_off != 0) {
4410 * We now know the hole punch length needed to trigger a
4411 * deallocation on this FS...
4413 dealloc_chunk_len = hlen;
4414 torture_comment(torture, "hole punch %ju@0 resulted in "
4415 "deallocation of %ju@0\n",
4416 (uintmax_t)hlen,
4417 (uintmax_t)far_rsp[0].file_off);
4418 torture_assert_u64_equal(torture,
4419 file_size - far_rsp[0].len,
4420 far_rsp[0].file_off,
4421 "invalid alloced range");
4422 break;
4426 if (dealloc_chunk_len == 0) {
4427 torture_comment(torture, "strange, this FS never deallocates"
4428 "zeroed ranges in sparse files\n");
4429 return true; /* FS specific, not a failure */
4433 * Check whether deallocation occurs when the (now known)
4434 * deallocation chunk size is punched via two ZERO_DATA requests.
4435 * I.e. Does the FS merge the two ranges and deallocate the chunk?
4436 * NTFS on Windows Server 2012 does not.
4438 ok = write_pattern(torture, tree, tmp_ctx, fh,
4439 0, /* off */
4440 file_size, /* len */
4441 0); /* pattern offset */
4442 torture_assert(torture, ok, "write pattern");
4444 /* divide dealloc chunk size by two, to use as punch length */
4445 hlen = dealloc_chunk_len >> 1;
4448 * /half of dealloc chunk size 1M\
4449 * | |
4450 * /offset 0 | /dealloc chunk size |
4451 * |------------------ |-------------------|-------------------|
4452 * | zeroed, 1st punch | zeroed, 2nd punch | existing pattern |
4454 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4455 0, /* off */
4456 hlen); /* beyond final zero */
4457 torture_assert_ntstatus_ok(torture, status, "zero_data");
4459 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4460 hlen, /* off */
4461 dealloc_chunk_len); /* beyond final */
4462 torture_assert_ntstatus_ok(torture, status, "zero_data");
4464 /* ensure holes are zeroed, and pattern is consistent */
4465 ok = check_zero(torture, tree, tmp_ctx, fh, 0, dealloc_chunk_len);
4466 torture_assert(torture, ok, "sparse zeroed range");
4468 ok = check_pattern(torture, tree, tmp_ctx, fh, dealloc_chunk_len,
4469 file_size - dealloc_chunk_len, dealloc_chunk_len);
4470 torture_assert(torture, ok, "allocated pattern range");
4472 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4473 0, /* off */
4474 file_size, /* len */
4475 &far_rsp,
4476 &far_count);
4477 torture_assert_ntstatus_ok(torture, status,
4478 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4480 if ((far_count == 0) && (dealloc_chunk_len == file_size)) {
4481 torture_comment(torture, "holes merged for deallocation of "
4482 "full file\n");
4483 return true;
4485 torture_assert_u64_equal(torture, far_count, 1,
4486 "unexpected response len");
4487 if (far_rsp[0].file_off == dealloc_chunk_len) {
4488 torture_comment(torture, "holes merged for deallocation of "
4489 "%ju chunk\n", (uintmax_t)dealloc_chunk_len);
4490 torture_assert_u64_equal(torture,
4491 file_size - far_rsp[0].len,
4492 far_rsp[0].file_off,
4493 "invalid alloced range");
4494 } else {
4495 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4496 "unexpected deallocation");
4497 torture_comment(torture, "holes not merged for deallocation\n");
4500 smb2_util_close(tree, fh);
4503 * Check whether an unwritten range is allocated when a sparse file is
4504 * written to at an offset past the dealloc chunk size:
4506 * /dealloc chunk size
4507 * /offset 0 |
4508 * |------------------ |-------------------|
4509 * | unwritten | pattern |
4511 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4512 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
4513 FILE_ATTRIBUTE_NORMAL);
4514 torture_assert(torture, ok, "setup file 1");
4516 /* set sparse */
4517 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4518 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4520 ok = write_pattern(torture, tree, tmp_ctx, fh,
4521 dealloc_chunk_len, /* off */
4522 1024, /* len */
4523 dealloc_chunk_len); /* pattern offset */
4524 torture_assert(torture, ok, "write pattern");
4526 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4527 0, /* off */
4528 dealloc_chunk_len + 1024, /* len */
4529 &far_rsp,
4530 &far_count);
4531 torture_assert_ntstatus_ok(torture, status,
4532 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4533 torture_assert_u64_equal(torture, far_count, 1,
4534 "unexpected response len");
4535 if (far_rsp[0].file_off == 0) {
4536 torture_assert_u64_equal(torture, far_rsp[0].len,
4537 dealloc_chunk_len + 1024,
4538 "unexpected far len");
4539 torture_comment(torture, "unwritten range fully allocated\n");
4540 } else {
4541 torture_assert_u64_equal(torture, far_rsp[0].file_off, dealloc_chunk_len,
4542 "unexpected deallocation");
4543 torture_assert_u64_equal(torture, far_rsp[0].len, 1024,
4544 "unexpected far len");
4545 torture_comment(torture, "unwritten range not allocated\n");
4548 ok = check_zero(torture, tree, tmp_ctx, fh, 0, dealloc_chunk_len);
4549 torture_assert(torture, ok, "sparse zeroed range");
4551 ok = check_pattern(torture, tree, tmp_ctx, fh, dealloc_chunk_len,
4552 1024, dealloc_chunk_len);
4553 torture_assert(torture, ok, "allocated pattern range");
4555 /* unsparse, should now be fully allocated */
4556 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false);
4557 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4559 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4560 0, /* off */
4561 dealloc_chunk_len + 1024, /* len */
4562 &far_rsp,
4563 &far_count);
4564 torture_assert_ntstatus_ok(torture, status,
4565 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4566 torture_assert_u64_equal(torture, far_count, 1,
4567 "unexpected response len");
4568 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4569 "unexpected deallocation");
4570 torture_assert_u64_equal(torture, far_rsp[0].len,
4571 dealloc_chunk_len + 1024,
4572 "unexpected far len");
4574 smb2_util_close(tree, fh);
4575 talloc_free(tmp_ctx);
4576 return true;
4579 /* check whether a file with compression and sparse attrs can be deallocated */
4580 static bool test_ioctl_sparse_compressed(struct torture_context *torture,
4581 struct smb2_tree *tree)
4583 struct smb2_handle fh;
4584 NTSTATUS status;
4585 TALLOC_CTX *tmp_ctx = talloc_new(tree);
4586 bool ok;
4587 uint64_t file_size = 1024 * 1024;
4588 struct file_alloced_range_buf *far_rsp = NULL;
4589 uint64_t far_count = 0;
4591 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4592 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
4593 FILE_ATTRIBUTE_NORMAL);
4594 torture_assert(torture, ok, "setup file 1");
4596 /* check for FS sparse file and compression support */
4597 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
4598 FILE_SUPPORTS_SPARSE_FILES, &ok);
4599 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4600 if (!ok) {
4601 smb2_util_close(tree, fh);
4602 torture_skip(torture, "Sparse files not supported\n");
4605 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
4606 &ok);
4607 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4608 if (!ok) {
4609 smb2_util_close(tree, fh);
4610 torture_skip(torture, "FS compression not supported\n");
4613 /* set compression and write some data */
4614 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
4615 COMPRESSION_FORMAT_DEFAULT);
4616 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
4618 ok = write_pattern(torture, tree, tmp_ctx, fh,
4619 0, /* off */
4620 file_size, /* len */
4621 0); /* pattern offset */
4622 torture_assert(torture, ok, "write pattern");
4624 /* set sparse - now sparse and compressed */
4625 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4626 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4628 /* check allocated ranges, should be fully alloced */
4629 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4630 0, /* off */
4631 file_size, /* len */
4632 &far_rsp,
4633 &far_count);
4634 torture_assert_ntstatus_ok(torture, status,
4635 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4636 torture_assert_u64_equal(torture, far_count, 1,
4637 "unexpected response len");
4638 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4639 "unexpected far off");
4640 torture_assert_u64_equal(torture, far_rsp[0].len, file_size,
4641 "unexpected far len");
4643 /* zero (hole-punch) all data, with sparse and compressed attrs */
4644 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4645 0, /* off */
4646 file_size); /* beyond_final_zero */
4647 torture_assert_ntstatus_ok(torture, status, "zero_data");
4650 * Windows Server 2012 still deallocates a zeroed range when a sparse
4651 * file carries the compression attribute.
4653 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4654 0, /* off */
4655 file_size, /* len */
4656 &far_rsp,
4657 &far_count);
4658 torture_assert_ntstatus_ok(torture, status,
4659 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4660 if (far_count == 0) {
4661 torture_comment(torture, "sparse & compressed file "
4662 "deallocated after hole-punch\n");
4663 } else {
4664 torture_assert_u64_equal(torture, far_count, 1,
4665 "unexpected response len");
4666 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4667 "unexpected far off");
4668 torture_assert_u64_equal(torture, far_rsp[0].len, file_size,
4669 "unexpected far len");
4670 torture_comment(torture, "sparse & compressed file fully "
4671 "allocated after hole-punch\n");
4674 smb2_util_close(tree, fh);
4675 talloc_free(tmp_ctx);
4676 return true;
4680 * Create a sparse file, then attempt to copy unallocated and allocated ranges
4681 * into a target file using FSCTL_SRV_COPYCHUNK.
4683 static bool test_ioctl_sparse_copy_chunk(struct torture_context *torture,
4684 struct smb2_tree *tree)
4686 struct smb2_handle src_h;
4687 struct smb2_handle dest_h;
4688 NTSTATUS status;
4689 TALLOC_CTX *tmp_ctx = talloc_new(tree);
4690 bool ok;
4691 uint64_t dealloc_chunk_len = 64 * 1024; /* Windows 2012 */
4692 struct file_alloced_range_buf *far_rsp = NULL;
4693 uint64_t far_count = 0;
4694 union smb_ioctl ioctl;
4695 struct srv_copychunk_copy cc_copy;
4696 struct srv_copychunk_rsp cc_rsp;
4697 enum ndr_err_code ndr_ret;
4699 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4700 FNAME, &src_h, 0, SEC_RIGHTS_FILE_ALL,
4701 FILE_ATTRIBUTE_NORMAL);
4702 torture_assert(torture, ok, "setup file");
4704 /* check for FS sparse file support */
4705 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &src_h,
4706 FILE_SUPPORTS_SPARSE_FILES, &ok);
4707 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4708 smb2_util_close(tree, src_h);
4709 if (!ok) {
4710 torture_skip(torture, "Sparse files not supported\n");
4713 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
4714 1, /* chunks */
4715 FNAME,
4716 &src_h, 0, /* src file */
4717 SEC_RIGHTS_FILE_ALL,
4718 FNAME2,
4719 &dest_h, 0, /* dest file */
4720 SEC_RIGHTS_FILE_ALL,
4721 &cc_copy,
4722 &ioctl);
4723 torture_assert(torture, ok, "setup copy chunk error");
4725 /* set sparse */
4726 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, src_h, true);
4727 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4729 /* start after dealloc_chunk_len, to create an unwritten sparse range */
4730 ok = write_pattern(torture, tree, tmp_ctx, src_h,
4731 dealloc_chunk_len, /* off */
4732 1024, /* len */
4733 dealloc_chunk_len); /* pattern offset */
4734 torture_assert(torture, ok, "write pattern");
4736 /* Skip test if 64k chunk is allocated - FS specific */
4737 status = test_ioctl_qar_req(torture, tmp_ctx, tree, src_h,
4738 0, /* off */
4739 dealloc_chunk_len + 1024, /* len */
4740 &far_rsp,
4741 &far_count);
4742 torture_assert_ntstatus_ok(torture, status,
4743 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4744 torture_assert_u64_equal(torture, far_count, 1,
4745 "unexpected response len");
4746 if (far_rsp[0].file_off == 0) {
4747 torture_skip(torture, "unwritten range fully allocated\n");
4750 torture_assert_u64_equal(torture, far_rsp[0].file_off, dealloc_chunk_len,
4751 "unexpected allocation");
4752 torture_assert_u64_equal(torture, far_rsp[0].len, 1024,
4753 "unexpected far len");
4755 /* copy-chunk unallocated + written ranges into non-sparse dest */
4757 cc_copy.chunks[0].source_off = 0;
4758 cc_copy.chunks[0].target_off = 0;
4759 cc_copy.chunks[0].length = dealloc_chunk_len + 1024;
4761 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
4762 &cc_copy,
4763 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
4764 torture_assert_ndr_success(torture, ndr_ret,
4765 "ndr_push_srv_copychunk_copy");
4767 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
4768 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
4770 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
4771 &cc_rsp,
4772 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
4773 torture_assert_ndr_success(torture, ndr_ret,
4774 "ndr_pull_srv_copychunk_rsp");
4776 ok = check_copy_chunk_rsp(torture, &cc_rsp,
4777 1, /* chunks written */
4778 0, /* chunk bytes unsuccessfully written */
4779 dealloc_chunk_len + 1024); /* bytes written */
4780 torture_assert(torture, ok, "bad copy chunk response data");
4782 ok = check_zero(torture, tree, tmp_ctx, dest_h, 0, dealloc_chunk_len);
4783 torture_assert(torture, ok, "sparse zeroed range");
4785 ok = check_pattern(torture, tree, tmp_ctx, dest_h, dealloc_chunk_len,
4786 1024, dealloc_chunk_len);
4787 torture_assert(torture, ok, "copychunked range");
4789 /* copied range should be allocated in non-sparse dest */
4790 status = test_ioctl_qar_req(torture, tmp_ctx, tree, dest_h,
4791 0, /* off */
4792 dealloc_chunk_len + 1024, /* len */
4793 &far_rsp,
4794 &far_count);
4795 torture_assert_ntstatus_ok(torture, status,
4796 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4797 torture_assert_u64_equal(torture, far_count, 1,
4798 "unexpected response len");
4799 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4800 "unexpected allocation");
4801 torture_assert_u64_equal(torture, far_rsp[0].len,
4802 dealloc_chunk_len + 1024,
4803 "unexpected far len");
4805 /* set dest as sparse */
4806 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, dest_h, true);
4807 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4809 /* zero (hole-punch) all data */
4810 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, dest_h,
4811 0, /* off */
4812 dealloc_chunk_len + 1024);
4813 torture_assert_ntstatus_ok(torture, status, "zero_data");
4815 /* zeroed range might be deallocated */
4816 status = test_ioctl_qar_req(torture, tmp_ctx, tree, dest_h,
4817 0, /* off */
4818 dealloc_chunk_len + 1024, /* len */
4819 &far_rsp,
4820 &far_count);
4821 torture_assert_ntstatus_ok(torture, status,
4822 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4823 if (far_count == 0) {
4824 /* FS specific (e.g. NTFS) */
4825 torture_comment(torture, "FS deallocates file on full-range "
4826 "punch\n");
4827 } else {
4828 /* FS specific (e.g. EXT4) */
4829 torture_comment(torture, "FS doesn't deallocate file on "
4830 "full-range punch\n");
4832 ok = check_zero(torture, tree, tmp_ctx, dest_h, 0,
4833 dealloc_chunk_len + 1024);
4834 torture_assert(torture, ok, "punched zeroed range");
4836 /* copy-chunk again, this time with sparse dest */
4837 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
4838 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
4840 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
4841 &cc_rsp,
4842 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
4843 torture_assert_ndr_success(torture, ndr_ret,
4844 "ndr_pull_srv_copychunk_rsp");
4846 ok = check_copy_chunk_rsp(torture, &cc_rsp,
4847 1, /* chunks written */
4848 0, /* chunk bytes unsuccessfully written */
4849 dealloc_chunk_len + 1024); /* bytes written */
4850 torture_assert(torture, ok, "bad copy chunk response data");
4852 ok = check_zero(torture, tree, tmp_ctx, dest_h, 0, dealloc_chunk_len);
4853 torture_assert(torture, ok, "sparse zeroed range");
4855 ok = check_pattern(torture, tree, tmp_ctx, dest_h, dealloc_chunk_len,
4856 1024, dealloc_chunk_len);
4857 torture_assert(torture, ok, "copychunked range");
4859 /* copied range may be allocated in sparse dest */
4860 status = test_ioctl_qar_req(torture, tmp_ctx, tree, dest_h,
4861 0, /* off */
4862 dealloc_chunk_len + 1024, /* len */
4863 &far_rsp,
4864 &far_count);
4865 torture_assert_ntstatus_ok(torture, status,
4866 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4867 torture_assert_u64_equal(torture, far_count, 1,
4868 "unexpected response len");
4870 * FS specific: sparse region may be unallocated in dest if copy-chunk
4871 * is handled in a sparse preserving way - E.g. vfs_btrfs
4872 * with BTRFS_IOC_CLONE_RANGE.
4874 if (far_rsp[0].file_off == dealloc_chunk_len) {
4875 torture_comment(torture, "copy-chunk sparse range preserved\n");
4876 torture_assert_u64_equal(torture, far_rsp[0].len, 1024,
4877 "unexpected far len");
4878 } else {
4879 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4880 "unexpected allocation");
4881 torture_assert_u64_equal(torture, far_rsp[0].len,
4882 dealloc_chunk_len + 1024,
4883 "unexpected far len");
4886 smb2_util_close(tree, src_h);
4887 smb2_util_close(tree, dest_h);
4888 talloc_free(tmp_ctx);
4889 return true;
4892 static bool test_ioctl_sparse_punch_invalid(struct torture_context *torture,
4893 struct smb2_tree *tree)
4895 struct smb2_handle fh;
4896 NTSTATUS status;
4897 TALLOC_CTX *tmp_ctx = talloc_new(tree);
4898 bool ok;
4899 bool is_sparse;
4900 int i;
4902 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4903 FNAME, &fh, 4096, SEC_RIGHTS_FILE_ALL,
4904 FILE_ATTRIBUTE_NORMAL);
4905 torture_assert(torture, ok, "setup file");
4907 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
4908 FILE_SUPPORTS_SPARSE_FILES, &ok);
4909 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4910 if (!ok) {
4911 smb2_util_close(tree, fh);
4912 torture_skip(torture, "Sparse files not supported\n");
4915 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4916 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4917 torture_assert(torture, !is_sparse, "sparse attr before set");
4919 /* loop twice, without and with sparse attrib */
4920 for (i = 0; i <= 1; i++) {
4921 union smb_fileinfo io;
4922 struct file_alloced_range_buf *far_rsp = NULL;
4923 uint64_t far_count = 0;
4925 /* get size before & after. zero data should never change it */
4926 ZERO_STRUCT(io);
4927 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
4928 io.generic.in.file.handle = fh;
4929 status = smb2_getinfo_file(tree, tmp_ctx, &io);
4930 torture_assert_ntstatus_ok(torture, status, "getinfo");
4931 torture_assert_int_equal(torture, (int)io.all_info2.out.size,
4932 4096, "size after IO");
4934 /* valid 8 byte zero data, but after EOF */
4935 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4936 4096, /* off */
4937 4104); /* beyond_final_zero */
4938 torture_assert_ntstatus_ok(torture, status, "zero_data");
4940 /* valid 8 byte zero data, but after EOF */
4941 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4942 8192, /* off */
4943 8200); /* beyond_final_zero */
4944 torture_assert_ntstatus_ok(torture, status, "zero_data");
4946 ZERO_STRUCT(io);
4947 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
4948 io.generic.in.file.handle = fh;
4949 status = smb2_getinfo_file(tree, tmp_ctx, &io);
4950 torture_assert_ntstatus_ok(torture, status, "getinfo");
4951 torture_assert_int_equal(torture, (int)io.all_info2.out.size,
4952 4096, "size after IO");
4954 /* valid 0 byte zero data, without sparse flag */
4955 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4956 4095, /* off */
4957 4095); /* beyond_final_zero */
4958 torture_assert_ntstatus_ok(torture, status, "zero_data");
4960 /* INVALID off is past beyond_final_zero */
4961 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4962 4096, /* off */
4963 4095); /* beyond_final_zero */
4964 torture_assert_ntstatus_equal(torture, status,
4965 NT_STATUS_INVALID_PARAMETER,
4966 "invalid zero_data");
4968 /* zero length QAR - valid */
4969 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4970 0, /* off */
4971 0, /* len */
4972 &far_rsp, &far_count);
4973 torture_assert_ntstatus_ok(torture, status,
4974 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4975 torture_assert_u64_equal(torture, far_count, 0,
4976 "unexpected response len");
4978 /* QAR after EOF - valid */
4979 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4980 4096, /* off */
4981 1024, /* len */
4982 &far_rsp, &far_count);
4983 torture_assert_ntstatus_ok(torture, status,
4984 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4985 torture_assert_u64_equal(torture, far_count, 0,
4986 "unexpected response len");
4988 /* set sparse */
4989 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh,
4990 true);
4991 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4994 smb2_util_close(tree, fh);
4995 talloc_free(tmp_ctx);
4996 return true;
4999 static bool test_ioctl_sparse_perms(struct torture_context *torture,
5000 struct smb2_tree *tree)
5002 struct smb2_handle fh;
5003 NTSTATUS status;
5004 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5005 bool ok;
5006 bool is_sparse;
5007 struct file_alloced_range_buf *far_rsp = NULL;
5008 uint64_t far_count = 0;
5010 ok = test_setup_create_fill(torture, tree, tmp_ctx,
5011 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
5012 FILE_ATTRIBUTE_NORMAL);
5013 torture_assert(torture, ok, "setup file");
5015 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
5016 FILE_SUPPORTS_SPARSE_FILES, &ok);
5017 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
5018 smb2_util_close(tree, fh);
5019 if (!ok) {
5020 torture_skip(torture, "Sparse files not supported\n");
5023 /* set sparse without WRITE_ATTR permission should succeed */
5024 ok = test_setup_create_fill(torture, tree, tmp_ctx,
5025 FNAME, &fh, 0,
5026 (SEC_RIGHTS_FILE_WRITE & ~(SEC_FILE_WRITE_ATTRIBUTE
5027 | SEC_STD_WRITE_DAC
5028 | SEC_FILE_WRITE_EA)),
5029 FILE_ATTRIBUTE_NORMAL);
5030 torture_assert(torture, ok, "setup file");
5032 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
5033 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
5034 smb2_util_close(tree, fh);
5036 ok = test_setup_open(torture, tree, tmp_ctx,
5037 FNAME, &fh, SEC_RIGHTS_FILE_ALL,
5038 FILE_ATTRIBUTE_NORMAL);
5039 torture_assert(torture, ok, "setup file");
5040 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
5041 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
5042 torture_assert(torture, is_sparse, "sparse after set");
5043 smb2_util_close(tree, fh);
5045 /* attempt get sparse without READ_DATA permission */
5046 ok = test_setup_create_fill(torture, tree, tmp_ctx,
5047 FNAME, &fh, 0,
5048 (SEC_RIGHTS_FILE_READ & ~SEC_FILE_READ_DATA),
5049 FILE_ATTRIBUTE_NORMAL);
5050 torture_assert(torture, ok, "setup file");
5052 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
5053 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
5054 torture_assert(torture, !is_sparse, "sparse set");
5055 smb2_util_close(tree, fh);
5057 /* attempt to set sparse with only WRITE_ATTR permission */
5058 ok = test_setup_create_fill(torture, tree, tmp_ctx,
5059 FNAME, &fh, 0,
5060 SEC_FILE_WRITE_ATTRIBUTE,
5061 FILE_ATTRIBUTE_NORMAL);
5062 torture_assert(torture, ok, "setup file");
5064 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
5065 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
5066 smb2_util_close(tree, fh);
5068 /* attempt to set sparse with only WRITE_DATA permission */
5069 ok = test_setup_create_fill(torture, tree, tmp_ctx,
5070 FNAME, &fh, 0,
5071 SEC_FILE_WRITE_DATA,
5072 FILE_ATTRIBUTE_NORMAL);
5073 torture_assert(torture, ok, "setup file");
5075 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
5076 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
5077 smb2_util_close(tree, fh);
5079 ok = test_setup_open(torture, tree, tmp_ctx,
5080 FNAME, &fh, SEC_RIGHTS_FILE_ALL,
5081 FILE_ATTRIBUTE_NORMAL);
5082 torture_assert(torture, ok, "setup file");
5083 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
5084 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
5085 torture_assert(torture, is_sparse, "sparse after set");
5086 smb2_util_close(tree, fh);
5088 /* attempt to set sparse with only APPEND_DATA permission */
5089 ok = test_setup_create_fill(torture, tree, tmp_ctx,
5090 FNAME, &fh, 0,
5091 SEC_FILE_APPEND_DATA,
5092 FILE_ATTRIBUTE_NORMAL);
5093 torture_assert(torture, ok, "setup file");
5095 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
5096 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
5097 smb2_util_close(tree, fh);
5099 ok = test_setup_open(torture, tree, tmp_ctx,
5100 FNAME, &fh, SEC_RIGHTS_FILE_ALL,
5101 FILE_ATTRIBUTE_NORMAL);
5102 torture_assert(torture, ok, "setup file");
5103 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
5104 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
5105 torture_assert(torture, is_sparse, "sparse after set");
5106 smb2_util_close(tree, fh);
5108 /* attempt to set sparse with only WRITE_EA permission - should fail */
5109 ok = test_setup_create_fill(torture, tree, tmp_ctx,
5110 FNAME, &fh, 0,
5111 SEC_FILE_WRITE_EA,
5112 FILE_ATTRIBUTE_NORMAL);
5113 torture_assert(torture, ok, "setup file");
5115 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
5116 torture_assert_ntstatus_equal(torture, status,
5117 NT_STATUS_ACCESS_DENIED,
5118 "FSCTL_SET_SPARSE permission");
5119 smb2_util_close(tree, fh);
5121 ok = test_setup_open(torture, tree, tmp_ctx,
5122 FNAME, &fh, SEC_RIGHTS_FILE_ALL,
5123 FILE_ATTRIBUTE_NORMAL);
5124 torture_assert(torture, ok, "setup file");
5125 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
5126 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
5127 torture_assert(torture, !is_sparse, "sparse after set");
5128 smb2_util_close(tree, fh);
5130 /* attempt QAR with only READ_ATTR permission - should fail */
5131 ok = test_setup_open(torture, tree, tmp_ctx,
5132 FNAME, &fh, SEC_FILE_READ_ATTRIBUTE,
5133 FILE_ATTRIBUTE_NORMAL);
5134 torture_assert(torture, ok, "setup file");
5135 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
5136 4096, /* off */
5137 1024, /* len */
5138 &far_rsp, &far_count);
5139 torture_assert_ntstatus_equal(torture, status,
5140 NT_STATUS_ACCESS_DENIED,
5141 "FSCTL_QUERY_ALLOCATED_RANGES req passed");
5142 smb2_util_close(tree, fh);
5144 /* attempt QAR with only READ_DATA permission */
5145 ok = test_setup_open(torture, tree, tmp_ctx,
5146 FNAME, &fh, SEC_FILE_READ_DATA,
5147 FILE_ATTRIBUTE_NORMAL);
5148 torture_assert(torture, ok, "setup file");
5149 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
5150 0, /* off */
5151 1024, /* len */
5152 &far_rsp, &far_count);
5153 torture_assert_ntstatus_ok(torture, status,
5154 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5155 torture_assert_u64_equal(torture, far_count, 0,
5156 "unexpected response len");
5157 smb2_util_close(tree, fh);
5159 /* attempt QAR with only READ_EA permission - should fail */
5160 ok = test_setup_open(torture, tree, tmp_ctx,
5161 FNAME, &fh, SEC_FILE_READ_EA,
5162 FILE_ATTRIBUTE_NORMAL);
5163 torture_assert(torture, ok, "setup file");
5164 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
5165 4096, /* off */
5166 1024, /* len */
5167 &far_rsp, &far_count);
5168 torture_assert_ntstatus_equal(torture, status,
5169 NT_STATUS_ACCESS_DENIED,
5170 "FSCTL_QUERY_ALLOCATED_RANGES req passed");
5171 smb2_util_close(tree, fh);
5173 /* setup file for ZERO_DATA permissions tests */
5174 ok = test_setup_create_fill(torture, tree, tmp_ctx,
5175 FNAME, &fh, 8192,
5176 SEC_RIGHTS_FILE_ALL,
5177 FILE_ATTRIBUTE_NORMAL);
5178 torture_assert(torture, ok, "setup file");
5180 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
5181 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
5182 smb2_util_close(tree, fh);
5184 /* attempt ZERO_DATA with only WRITE_ATTR permission - should fail */
5185 ok = test_setup_open(torture, tree, tmp_ctx,
5186 FNAME, &fh, SEC_FILE_WRITE_ATTRIBUTE,
5187 FILE_ATTRIBUTE_NORMAL);
5188 torture_assert(torture, ok, "setup file");
5189 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
5190 0, /* off */
5191 4096); /* beyond_final_zero */
5192 torture_assert_ntstatus_equal(torture, status,
5193 NT_STATUS_ACCESS_DENIED,
5194 "zero_data permission");
5195 smb2_util_close(tree, fh);
5197 /* attempt ZERO_DATA with only WRITE_DATA permission */
5198 ok = test_setup_open(torture, tree, tmp_ctx,
5199 FNAME, &fh, SEC_FILE_WRITE_DATA,
5200 FILE_ATTRIBUTE_NORMAL);
5201 torture_assert(torture, ok, "setup file");
5202 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
5203 0, /* off */
5204 4096); /* beyond_final_zero */
5205 torture_assert_ntstatus_ok(torture, status, "zero_data");
5206 smb2_util_close(tree, fh);
5208 /* attempt ZERO_DATA with only APPEND_DATA permission - should fail */
5209 ok = test_setup_open(torture, tree, tmp_ctx,
5210 FNAME, &fh, SEC_FILE_APPEND_DATA,
5211 FILE_ATTRIBUTE_NORMAL);
5212 torture_assert(torture, ok, "setup file");
5213 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
5214 0, /* off */
5215 4096); /* beyond_final_zero */
5216 torture_assert_ntstatus_equal(torture, status,
5217 NT_STATUS_ACCESS_DENIED,
5218 "zero_data permission");
5219 smb2_util_close(tree, fh);
5221 /* attempt ZERO_DATA with only WRITE_EA permission - should fail */
5222 ok = test_setup_open(torture, tree, tmp_ctx,
5223 FNAME, &fh, SEC_FILE_WRITE_EA,
5224 FILE_ATTRIBUTE_NORMAL);
5225 torture_assert(torture, ok, "setup file");
5226 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
5227 0, /* off */
5228 4096); /* beyond_final_zero */
5229 torture_assert_ntstatus_equal(torture, status,
5230 NT_STATUS_ACCESS_DENIED,
5231 "zero_data permission");
5232 smb2_util_close(tree, fh);
5234 talloc_free(tmp_ctx);
5235 return true;
5238 static bool test_ioctl_sparse_lck(struct torture_context *torture,
5239 struct smb2_tree *tree)
5241 struct smb2_handle fh;
5242 struct smb2_handle fh2;
5243 NTSTATUS status;
5244 uint64_t dealloc_chunk_len = 64 * 1024; /* Windows 2012 */
5245 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5246 bool ok;
5247 bool is_sparse;
5248 struct smb2_lock lck;
5249 struct smb2_lock_element el[1];
5250 struct file_alloced_range_buf *far_rsp = NULL;
5251 uint64_t far_count = 0;
5253 ok = test_setup_create_fill(torture, tree, tmp_ctx, FNAME, &fh,
5254 dealloc_chunk_len, SEC_RIGHTS_FILE_ALL,
5255 FILE_ATTRIBUTE_NORMAL);
5256 torture_assert(torture, ok, "setup file");
5258 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
5259 FILE_SUPPORTS_SPARSE_FILES, &ok);
5260 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
5261 if (!ok) {
5262 torture_skip(torture, "Sparse files not supported\n");
5263 smb2_util_close(tree, fh);
5266 /* open and lock via separate fh2 */
5267 status = torture_smb2_testfile(tree, FNAME, &fh2);
5268 torture_assert_ntstatus_ok(torture, status, "2nd src open");
5270 lck.in.lock_count = 0x0001;
5271 lck.in.lock_sequence = 0x00000000;
5272 lck.in.file.handle = fh2;
5273 lck.in.locks = el;
5274 el[0].offset = 0;
5275 el[0].length = dealloc_chunk_len;
5276 el[0].reserved = 0;
5277 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
5279 status = smb2_lock(tree, &lck);
5280 torture_assert_ntstatus_ok(torture, status, "lock");
5282 /* set sparse while locked */
5283 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
5284 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
5286 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
5287 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
5288 torture_assert(torture, is_sparse, "sparse attr after set");
5290 /* zero data over locked range should fail */
5291 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
5292 0, /* off */
5293 4096); /* beyond_final_zero */
5294 torture_assert_ntstatus_equal(torture, status,
5295 NT_STATUS_FILE_LOCK_CONFLICT,
5296 "zero_data locked");
5298 /* QAR over locked range should pass */
5299 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
5300 0, /* off */
5301 4096, /* len */
5302 &far_rsp, &far_count);
5303 torture_assert_ntstatus_ok(torture, status,
5304 "FSCTL_QUERY_ALLOCATED_RANGES locked");
5305 torture_assert_u64_equal(torture, far_count, 1,
5306 "unexpected response len");
5307 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
5308 "unexpected allocation");
5309 torture_assert_u64_equal(torture, far_rsp[0].len,
5310 4096,
5311 "unexpected far len");
5313 /* zero data over range past EOF should pass */
5314 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
5315 dealloc_chunk_len, /* off */
5316 dealloc_chunk_len + 4096);
5317 torture_assert_ntstatus_ok(torture, status,
5318 "zero_data past EOF locked");
5320 /* QAR over range past EOF should pass */
5321 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
5322 dealloc_chunk_len, /* off */
5323 4096, /* len */
5324 &far_rsp, &far_count);
5325 torture_assert_ntstatus_ok(torture, status,
5326 "FSCTL_QUERY_ALLOCATED_RANGES past EOF locked");
5327 torture_assert_u64_equal(torture, far_count, 0,
5328 "unexpected response len");
5330 lck.in.lock_count = 0x0001;
5331 lck.in.lock_sequence = 0x00000001;
5332 lck.in.file.handle = fh2;
5333 lck.in.locks = el;
5334 el[0].offset = 0;
5335 el[0].length = dealloc_chunk_len;
5336 el[0].reserved = 0;
5337 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
5338 status = smb2_lock(tree, &lck);
5339 torture_assert_ntstatus_ok(torture, status, "unlock");
5341 smb2_util_close(tree, fh2);
5342 smb2_util_close(tree, fh);
5343 talloc_free(tmp_ctx);
5344 return true;
5347 /* alleviate QAR off-by-one bug paranoia - help me ob1 */
5348 static bool test_ioctl_sparse_qar_ob1(struct torture_context *torture,
5349 struct smb2_tree *tree)
5351 struct smb2_handle fh;
5352 NTSTATUS status;
5353 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5354 bool ok;
5355 uint64_t dealloc_chunk_len = 64 * 1024; /* Windows 2012 */
5356 struct file_alloced_range_buf *far_rsp = NULL;
5357 uint64_t far_count = 0;
5359 ok = test_setup_create_fill(torture, tree, tmp_ctx,
5360 FNAME, &fh, dealloc_chunk_len * 2,
5361 SEC_RIGHTS_FILE_ALL,
5362 FILE_ATTRIBUTE_NORMAL);
5363 torture_assert(torture, ok, "setup file");
5365 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
5366 FILE_SUPPORTS_SPARSE_FILES, &ok);
5367 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
5368 if (!ok) {
5369 torture_skip(torture, "Sparse files not supported\n");
5370 smb2_util_close(tree, fh);
5373 /* non-sparse QAR with range one before EOF */
5374 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
5375 0, /* off */
5376 dealloc_chunk_len * 2 - 1, /* len */
5377 &far_rsp, &far_count);
5378 torture_assert_ntstatus_ok(torture, status,
5379 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5380 torture_assert_u64_equal(torture, far_count, 1,
5381 "unexpected response len");
5382 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
5383 "unexpected allocation");
5384 torture_assert_u64_equal(torture, far_rsp[0].len,
5385 dealloc_chunk_len * 2 - 1,
5386 "unexpected far len");
5388 /* non-sparse QAR with range one after EOF */
5389 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
5390 0, /* off */
5391 dealloc_chunk_len * 2 + 1, /* len */
5392 &far_rsp, &far_count);
5393 torture_assert_ntstatus_ok(torture, status,
5394 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5395 torture_assert_u64_equal(torture, far_count, 1,
5396 "unexpected response len");
5397 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
5398 "unexpected allocation");
5399 torture_assert_u64_equal(torture, far_rsp[0].len,
5400 dealloc_chunk_len * 2,
5401 "unexpected far len");
5403 /* non-sparse QAR with range one after EOF from off=1 */
5404 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
5405 1, /* off */
5406 dealloc_chunk_len * 2, /* len */
5407 &far_rsp, &far_count);
5408 torture_assert_ntstatus_ok(torture, status,
5409 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5410 torture_assert_u64_equal(torture, far_count, 1,
5411 "unexpected response len");
5412 torture_assert_u64_equal(torture, far_rsp[0].file_off, 1,
5413 "unexpected allocation");
5414 torture_assert_u64_equal(torture, far_rsp[0].len,
5415 dealloc_chunk_len * 2 - 1,
5416 "unexpected far len");
5418 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
5419 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
5421 /* punch out second chunk */
5422 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
5423 dealloc_chunk_len, /* off */
5424 dealloc_chunk_len * 2);
5425 torture_assert_ntstatus_ok(torture, status, "zero_data");
5427 /* sparse QAR with range one before hole */
5428 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
5429 0, /* off */
5430 dealloc_chunk_len - 1, /* len */
5431 &far_rsp, &far_count);
5432 torture_assert_ntstatus_ok(torture, status,
5433 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5434 torture_assert_u64_equal(torture, far_count, 1,
5435 "unexpected response len");
5436 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
5437 "unexpected allocation");
5438 torture_assert_u64_equal(torture, far_rsp[0].len,
5439 dealloc_chunk_len - 1,
5440 "unexpected far len");
5442 /* sparse QAR with range one after hole */
5443 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
5444 0, /* off */
5445 dealloc_chunk_len + 1, /* len */
5446 &far_rsp, &far_count);
5447 torture_assert_ntstatus_ok(torture, status,
5448 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5449 torture_assert_u64_equal(torture, far_count, 1,
5450 "unexpected response len");
5451 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
5452 "unexpected allocation");
5453 torture_assert_u64_equal(torture, far_rsp[0].len,
5454 dealloc_chunk_len,
5455 "unexpected far len");
5457 /* sparse QAR with range one after hole from off=1 */
5458 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
5459 1, /* off */
5460 dealloc_chunk_len, /* len */
5461 &far_rsp, &far_count);
5462 torture_assert_ntstatus_ok(torture, status,
5463 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5464 torture_assert_u64_equal(torture, far_count, 1,
5465 "unexpected response len");
5466 torture_assert_u64_equal(torture, far_rsp[0].file_off, 1,
5467 "unexpected allocation");
5468 torture_assert_u64_equal(torture, far_rsp[0].len,
5469 dealloc_chunk_len - 1,
5470 "unexpected far len");
5472 /* sparse QAR with range one before EOF from off=chunk_len-1 */
5473 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
5474 dealloc_chunk_len - 1, /* off */
5475 dealloc_chunk_len, /* len */
5476 &far_rsp, &far_count);
5477 torture_assert_ntstatus_ok(torture, status,
5478 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5479 torture_assert_u64_equal(torture, far_count, 1,
5480 "unexpected response len");
5481 torture_assert_u64_equal(torture, far_rsp[0].file_off,
5482 dealloc_chunk_len - 1,
5483 "unexpected allocation");
5484 torture_assert_u64_equal(torture, far_rsp[0].len,
5485 1, "unexpected far len");
5487 /* sparse QAR with range one after EOF from off=chunk_len+1 */
5488 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
5489 dealloc_chunk_len + 1, /* off */
5490 dealloc_chunk_len, /* len */
5491 &far_rsp, &far_count);
5492 torture_assert_ntstatus_ok(torture, status,
5493 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5494 torture_assert_u64_equal(torture, far_count, 0,
5495 "unexpected response len");
5496 smb2_util_close(tree, fh);
5497 talloc_free(tmp_ctx);
5498 return true;
5501 /* test QAR with multi-range responses */
5502 static bool test_ioctl_sparse_qar_multi(struct torture_context *torture,
5503 struct smb2_tree *tree)
5505 struct smb2_handle fh;
5506 NTSTATUS status;
5507 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5508 bool ok;
5509 uint64_t dealloc_chunk_len = 64 * 1024; /* Windows 2012 */
5510 uint64_t this_off;
5511 int i;
5512 struct file_alloced_range_buf *far_rsp = NULL;
5513 uint64_t far_count = 0;
5515 ok = test_setup_create_fill(torture, tree, tmp_ctx,
5516 FNAME, &fh, dealloc_chunk_len * 2,
5517 SEC_RIGHTS_FILE_ALL,
5518 FILE_ATTRIBUTE_NORMAL);
5519 torture_assert(torture, ok, "setup file");
5521 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
5522 FILE_SUPPORTS_SPARSE_FILES, &ok);
5523 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
5524 if (!ok) {
5525 torture_skip(torture, "Sparse files not supported\n");
5526 smb2_util_close(tree, fh);
5529 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
5530 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
5532 /* each loop, write out two chunks and punch the first out */
5533 for (i = 0; i < 10; i++) {
5534 this_off = i * dealloc_chunk_len * 2;
5536 ok = write_pattern(torture, tree, tmp_ctx, fh,
5537 this_off, /* off */
5538 dealloc_chunk_len * 2, /* len */
5539 this_off); /* pattern offset */
5540 torture_assert(torture, ok, "write pattern");
5542 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
5543 this_off, /* off */
5544 this_off + dealloc_chunk_len);
5545 torture_assert_ntstatus_ok(torture, status, "zero_data");
5548 /* should now have one separate region for each iteration */
5549 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
5551 10 * dealloc_chunk_len * 2,
5552 &far_rsp, &far_count);
5553 torture_assert_ntstatus_ok(torture, status,
5554 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5555 if (far_count == 1) {
5556 torture_comment(torture, "this FS doesn't deallocate 64K"
5557 "zeroed ranges in sparse files\n");
5558 return true; /* FS specific, not a failure */
5560 torture_assert_u64_equal(torture, far_count, 10,
5561 "unexpected response len");
5562 for (i = 0; i < 10; i++) {
5563 this_off = i * dealloc_chunk_len * 2;
5565 torture_assert_u64_equal(torture, far_rsp[i].file_off,
5566 this_off + dealloc_chunk_len,
5567 "unexpected allocation");
5568 torture_assert_u64_equal(torture, far_rsp[i].len,
5569 dealloc_chunk_len,
5570 "unexpected far len");
5573 smb2_util_close(tree, fh);
5574 talloc_free(tmp_ctx);
5575 return true;
5578 static bool test_ioctl_sparse_qar_overflow(struct torture_context *torture,
5579 struct smb2_tree *tree)
5581 struct smb2_handle fh;
5582 union smb_ioctl ioctl;
5583 struct file_alloced_range_buf far_buf;
5584 NTSTATUS status;
5585 enum ndr_err_code ndr_ret;
5586 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5587 bool ok;
5589 ok = test_setup_create_fill(torture, tree, tmp_ctx,
5590 FNAME, &fh, 1024, SEC_RIGHTS_FILE_ALL,
5591 FILE_ATTRIBUTE_NORMAL);
5592 torture_assert(torture, ok, "setup file");
5594 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
5595 FILE_SUPPORTS_SPARSE_FILES, &ok);
5596 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
5597 if (!ok) {
5598 smb2_util_close(tree, fh);
5599 torture_skip(torture, "Sparse files not supported\n");
5602 /* no allocated ranges, no space for range response, should pass */
5603 ZERO_STRUCT(ioctl);
5604 ioctl.smb2.level = RAW_IOCTL_SMB2;
5605 ioctl.smb2.in.file.handle = fh;
5606 ioctl.smb2.in.function = FSCTL_QUERY_ALLOCATED_RANGES;
5607 ioctl.smb2.in.max_output_response = 1024;
5608 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
5610 /* off + length wraps around to 511 */
5611 far_buf.file_off = 512;
5612 far_buf.len = 0xffffffffffffffffLL;
5613 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5614 &far_buf,
5615 (ndr_push_flags_fn_t)ndr_push_file_alloced_range_buf);
5616 torture_assert_ndr_success(torture, ndr_ret, "push far ndr buf");
5618 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5619 torture_assert_ntstatus_equal(torture, status,
5620 NT_STATUS_INVALID_PARAMETER,
5621 "FSCTL_QUERY_ALLOCATED_RANGES overflow");
5623 return true;
5626 static NTSTATUS test_ioctl_trim_supported(struct torture_context *torture,
5627 struct smb2_tree *tree,
5628 TALLOC_CTX *mem_ctx,
5629 struct smb2_handle *fh,
5630 bool *trim_support)
5632 NTSTATUS status;
5633 union smb_fsinfo info;
5635 ZERO_STRUCT(info);
5636 info.generic.level = RAW_QFS_SECTOR_SIZE_INFORMATION;
5637 info.generic.handle = *fh;
5638 status = smb2_getinfo_fs(tree, tree, &info);
5639 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_INFO_CLASS)) {
5641 * Windows < Server 2012, 8 etc. don't support this info level
5642 * or the trim ioctl. Ignore the error and let the caller skip.
5644 *trim_support = false;
5645 return NT_STATUS_OK;
5646 } else if (!NT_STATUS_IS_OK(status)) {
5647 return status;
5650 torture_comment(torture, "sector size info: lb/s=%u, pb/sA=%u, "
5651 "pb/sP=%u, fse/sA=%u, flags=0x%x, bosa=%u, bopa=%u\n",
5652 (unsigned)info.sector_size_info.out.logical_bytes_per_sector,
5653 (unsigned)info.sector_size_info.out.phys_bytes_per_sector_atomic,
5654 (unsigned)info.sector_size_info.out.phys_bytes_per_sector_perf,
5655 (unsigned)info.sector_size_info.out.fs_effective_phys_bytes_per_sector_atomic,
5656 (unsigned)info.sector_size_info.out.flags,
5657 (unsigned)info.sector_size_info.out.byte_off_sector_align,
5658 (unsigned)info.sector_size_info.out.byte_off_partition_align);
5660 if (info.sector_size_info.out.flags & QFS_SSINFO_FLAGS_TRIM_ENABLED) {
5661 *trim_support = true;
5662 } else {
5663 *trim_support = false;
5665 return NT_STATUS_OK;
5668 static bool test_setup_trim(struct torture_context *torture,
5669 struct smb2_tree *tree,
5670 TALLOC_CTX *mem_ctx,
5671 uint32_t num_ranges,
5672 struct smb2_handle *fh,
5673 uint64_t file_size,
5674 uint32_t desired_access,
5675 struct fsctl_file_level_trim_req *trim_req,
5676 union smb_ioctl *ioctl)
5678 bool ok;
5680 ok = test_setup_create_fill(torture, tree, mem_ctx, FNAME,
5681 fh, file_size, desired_access,
5682 FILE_ATTRIBUTE_NORMAL);
5683 torture_assert(torture, ok, "src file create fill");
5685 ZERO_STRUCTPN(ioctl);
5686 ioctl->smb2.level = RAW_IOCTL_SMB2;
5687 ioctl->smb2.in.file.handle = *fh;
5688 ioctl->smb2.in.function = FSCTL_FILE_LEVEL_TRIM;
5689 ioctl->smb2.in.max_output_response
5690 = sizeof(struct fsctl_file_level_trim_rsp);
5691 ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
5693 ZERO_STRUCTPN(trim_req);
5694 /* leave key as zero for now. TODO test locking with differing keys */
5695 trim_req->num_ranges = num_ranges;
5696 trim_req->ranges = talloc_zero_array(mem_ctx,
5697 struct file_level_trim_range,
5698 num_ranges);
5699 torture_assert(torture, (trim_req->ranges != NULL), "no memory for ranges");
5701 return true;
5704 static bool test_ioctl_trim_simple(struct torture_context *torture,
5705 struct smb2_tree *tree)
5707 struct smb2_handle fh;
5708 NTSTATUS status;
5709 union smb_ioctl ioctl;
5710 bool trim_supported;
5711 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5712 struct fsctl_file_level_trim_req trim_req;
5713 struct fsctl_file_level_trim_rsp trim_rsp;
5714 uint64_t trim_chunk_len = 64 * 1024; /* trim 64K chunks */
5715 enum ndr_err_code ndr_ret;
5716 bool ok;
5718 ok = test_setup_trim(torture, tree, tmp_ctx,
5719 1, /* 1 range */
5720 &fh, 2 * trim_chunk_len, /* fill 128K file */
5721 SEC_RIGHTS_FILE_ALL,
5722 &trim_req,
5723 &ioctl);
5724 if (!ok) {
5725 torture_fail(torture, "setup trim error");
5728 status = test_ioctl_trim_supported(torture, tree, tmp_ctx, &fh,
5729 &trim_supported);
5730 torture_assert_ntstatus_ok(torture, status, "fsinfo");
5731 if (!trim_supported) {
5732 smb2_util_close(tree, fh);
5733 talloc_free(tmp_ctx);
5734 torture_skip(torture, "trim not supported\n");
5737 /* trim first chunk, leave second */
5738 trim_req.ranges[0].off = 0;
5739 trim_req.ranges[0].len = trim_chunk_len;
5741 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, &trim_req,
5742 (ndr_push_flags_fn_t)ndr_push_fsctl_file_level_trim_req);
5743 torture_assert_ndr_success(torture, ndr_ret,
5744 "ndr_push_fsctl_file_level_trim_req");
5746 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5747 torture_assert_ntstatus_ok(torture, status, "FILE_LEVEL_TRIM_RANGE");
5749 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
5750 &trim_rsp,
5751 (ndr_pull_flags_fn_t)ndr_pull_fsctl_file_level_trim_rsp);
5752 torture_assert_ndr_success(torture, ndr_ret,
5753 "ndr_pull_fsctl_file_level_trim_rsp");
5755 torture_assert_int_equal(torture, trim_rsp.num_ranges_processed, 1, "");
5757 /* second half of the file should remain consistent */
5758 ok = check_pattern(torture, tree, tmp_ctx, fh, trim_chunk_len,
5759 trim_chunk_len, trim_chunk_len);
5760 torture_assert(torture, ok, "non-trimmed range inconsistent");
5762 return true;
5765 static bool test_setup_dup_extents(struct torture_context *tctx,
5766 struct smb2_tree *tree,
5767 TALLOC_CTX *mem_ctx,
5768 struct smb2_handle *src_h,
5769 uint64_t src_size,
5770 uint32_t src_desired_access,
5771 struct smb2_handle *dest_h,
5772 uint64_t dest_size,
5773 uint32_t dest_desired_access,
5774 struct fsctl_dup_extents_to_file *dup_ext_buf,
5775 union smb_ioctl *ioctl)
5777 bool ok;
5779 ok = test_setup_create_fill(tctx, tree, mem_ctx, FNAME,
5780 src_h, src_size, src_desired_access,
5781 FILE_ATTRIBUTE_NORMAL);
5782 torture_assert(tctx, ok, "src file create fill");
5784 ok = test_setup_create_fill(tctx, tree, mem_ctx, FNAME2,
5785 dest_h, dest_size, dest_desired_access,
5786 FILE_ATTRIBUTE_NORMAL);
5787 torture_assert(tctx, ok, "dest file create fill");
5789 ZERO_STRUCTPN(ioctl);
5790 ioctl->smb2.level = RAW_IOCTL_SMB2;
5791 ioctl->smb2.in.file.handle = *dest_h;
5792 ioctl->smb2.in.function = FSCTL_DUP_EXTENTS_TO_FILE;
5793 ioctl->smb2.in.max_output_response = 0;
5794 ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
5796 ZERO_STRUCTPN(dup_ext_buf);
5797 smb2_push_handle(dup_ext_buf->source_fid, src_h);
5799 return true;
5802 static bool test_ioctl_dup_extents_simple(struct torture_context *tctx,
5803 struct smb2_tree *tree)
5805 struct smb2_handle src_h;
5806 struct smb2_handle dest_h;
5807 NTSTATUS status;
5808 union smb_ioctl ioctl;
5809 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5810 struct fsctl_dup_extents_to_file dup_ext_buf;
5811 enum ndr_err_code ndr_ret;
5812 union smb_fileinfo io;
5813 union smb_setfileinfo sinfo;
5814 bool ok;
5816 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5817 &src_h, 4096, /* fill 4096 byte src file */
5818 SEC_RIGHTS_FILE_ALL,
5819 &dest_h, 0, /* 0 byte dest file */
5820 SEC_RIGHTS_FILE_ALL,
5821 &dup_ext_buf,
5822 &ioctl);
5823 if (!ok) {
5824 torture_fail(tctx, "setup dup extents error");
5827 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5828 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
5829 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5830 if (!ok) {
5831 smb2_util_close(tree, src_h);
5832 smb2_util_close(tree, dest_h);
5833 talloc_free(tmp_ctx);
5834 torture_skip(tctx, "block refcounting not supported\n");
5837 /* extend dest to match src len */
5838 ZERO_STRUCT(sinfo);
5839 sinfo.end_of_file_info.level =
5840 RAW_SFILEINFO_END_OF_FILE_INFORMATION;
5841 sinfo.end_of_file_info.in.file.handle = dest_h;
5842 sinfo.end_of_file_info.in.size = 4096;
5843 status = smb2_setinfo_file(tree, &sinfo);
5844 torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
5846 /* copy all src file data */
5847 dup_ext_buf.source_off = 0;
5848 dup_ext_buf.target_off = 0;
5849 dup_ext_buf.byte_count = 4096;
5851 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5852 &dup_ext_buf,
5853 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5854 torture_assert_ndr_success(tctx, ndr_ret,
5855 "ndr_push_fsctl_dup_extents_to_file");
5857 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5858 torture_assert_ntstatus_ok(tctx, status,
5859 "FSCTL_DUP_EXTENTS_TO_FILE");
5861 /* the file size shouldn't have been changed by this operation! */
5862 ZERO_STRUCT(io);
5863 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5864 io.generic.in.file.handle = dest_h;
5865 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5866 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5867 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5868 4096, "size after IO");
5870 smb2_util_close(tree, src_h);
5871 smb2_util_close(tree, dest_h);
5873 /* reopen for pattern check */
5874 ok = test_setup_open(tctx, tree, tmp_ctx, FNAME, &src_h,
5875 SEC_RIGHTS_FILE_ALL, FILE_ATTRIBUTE_NORMAL);
5876 torture_assert_ntstatus_ok(tctx, status, "src open after dup");
5877 ok = test_setup_open(tctx, tree, tmp_ctx, FNAME2, &dest_h,
5878 SEC_RIGHTS_FILE_ALL, FILE_ATTRIBUTE_NORMAL);
5879 torture_assert_ntstatus_ok(tctx, status, "dest open after dup");
5881 ok = check_pattern(tctx, tree, tmp_ctx, src_h, 0, 4096, 0);
5882 if (!ok) {
5883 torture_fail(tctx, "inconsistent src file data");
5886 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 4096, 0);
5887 if (!ok) {
5888 torture_fail(tctx, "inconsistent dest file data");
5891 smb2_util_close(tree, src_h);
5892 smb2_util_close(tree, dest_h);
5893 talloc_free(tmp_ctx);
5894 return true;
5897 static bool test_ioctl_dup_extents_len_beyond_dest(struct torture_context *tctx,
5898 struct smb2_tree *tree)
5900 struct smb2_handle src_h;
5901 struct smb2_handle dest_h;
5902 NTSTATUS status;
5903 union smb_ioctl ioctl;
5904 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5905 struct fsctl_dup_extents_to_file dup_ext_buf;
5906 enum ndr_err_code ndr_ret;
5907 union smb_fileinfo io;
5908 union smb_setfileinfo sinfo;
5909 bool ok;
5911 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5912 &src_h, 32768, /* fill 32768 byte src file */
5913 SEC_RIGHTS_FILE_ALL,
5914 &dest_h, 0, /* 0 byte dest file */
5915 SEC_RIGHTS_FILE_ALL,
5916 &dup_ext_buf,
5917 &ioctl);
5918 if (!ok) {
5919 torture_fail(tctx, "setup dup extents error");
5922 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5923 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
5924 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5925 if (!ok) {
5926 smb2_util_close(tree, src_h);
5927 smb2_util_close(tree, dest_h);
5928 talloc_free(tmp_ctx);
5929 torture_skip(tctx, "block refcounting not supported\n");
5932 ZERO_STRUCT(io);
5933 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5934 io.generic.in.file.handle = dest_h;
5935 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5936 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5937 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5938 0, "size after IO");
5940 /* copy all src file data */
5941 dup_ext_buf.source_off = 0;
5942 dup_ext_buf.target_off = 0;
5943 dup_ext_buf.byte_count = 32768;
5945 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5946 &dup_ext_buf,
5947 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5948 torture_assert_ndr_success(tctx, ndr_ret,
5949 "ndr_push_fsctl_dup_extents_to_file");
5951 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5952 #if 0
5954 * 2.3.8 FSCTL_DUPLICATE_EXTENTS_TO_FILE Reply - this should fail, but
5955 * passes against WS2016 RTM!
5957 torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_SUPPORTED,
5958 "FSCTL_DUP_EXTENTS_TO_FILE");
5959 #endif
5961 /* the file sizes shouldn't have been changed */
5962 ZERO_STRUCT(io);
5963 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5964 io.generic.in.file.handle = src_h;
5965 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5966 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5967 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5968 32768, "size after IO");
5970 ZERO_STRUCT(io);
5971 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5972 io.generic.in.file.handle = dest_h;
5973 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5974 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5975 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5976 0, "size after IO");
5978 /* extend dest */
5979 ZERO_STRUCT(sinfo);
5980 sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
5981 sinfo.end_of_file_info.in.file.handle = dest_h;
5982 sinfo.end_of_file_info.in.size = 32768;
5983 status = smb2_setinfo_file(tree, &sinfo);
5984 torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
5986 ok = check_zero(tctx, tree, tmp_ctx, dest_h, 0, 32768);
5987 if (!ok) {
5988 torture_fail(tctx, "inconsistent file data");
5991 /* reissue ioctl, now with enough space */
5992 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5993 torture_assert_ntstatus_ok(tctx, status,
5994 "FSCTL_DUP_EXTENTS_TO_FILE");
5996 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
5997 if (!ok) {
5998 torture_fail(tctx, "inconsistent file data");
6001 smb2_util_close(tree, src_h);
6002 smb2_util_close(tree, dest_h);
6003 talloc_free(tmp_ctx);
6004 return true;
6007 static bool test_ioctl_dup_extents_len_beyond_src(struct torture_context *tctx,
6008 struct smb2_tree *tree)
6010 struct smb2_handle src_h;
6011 struct smb2_handle dest_h;
6012 NTSTATUS status;
6013 union smb_ioctl ioctl;
6014 TALLOC_CTX *tmp_ctx = talloc_new(tree);
6015 struct fsctl_dup_extents_to_file dup_ext_buf;
6016 enum ndr_err_code ndr_ret;
6017 union smb_fileinfo io;
6018 bool ok;
6020 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
6021 &src_h, 32768, /* fill 32768 byte src file */
6022 SEC_RIGHTS_FILE_ALL,
6023 &dest_h, 0, /* 0 byte dest file */
6024 SEC_RIGHTS_FILE_ALL,
6025 &dup_ext_buf,
6026 &ioctl);
6027 if (!ok) {
6028 torture_fail(tctx, "setup dup extents error");
6031 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
6032 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
6033 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
6034 if (!ok) {
6035 smb2_util_close(tree, src_h);
6036 smb2_util_close(tree, dest_h);
6037 talloc_free(tmp_ctx);
6038 torture_skip(tctx, "block refcounting not supported\n");
6041 ZERO_STRUCT(io);
6042 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
6043 io.generic.in.file.handle = dest_h;
6044 status = smb2_getinfo_file(tree, tmp_ctx, &io);
6045 torture_assert_ntstatus_ok(tctx, status, "getinfo");
6046 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
6047 0, "size after IO");
6049 /* exceed src file len */
6050 dup_ext_buf.source_off = 0;
6051 dup_ext_buf.target_off = 0;
6052 dup_ext_buf.byte_count = 32768 * 2;
6054 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6055 &dup_ext_buf,
6056 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6057 torture_assert_ndr_success(tctx, ndr_ret,
6058 "ndr_push_fsctl_dup_extents_to_file");
6060 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6061 torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_SUPPORTED,
6062 "FSCTL_DUP_EXTENTS_TO_FILE");
6064 /* the file sizes shouldn't have been changed */
6065 ZERO_STRUCT(io);
6066 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
6067 io.generic.in.file.handle = src_h;
6068 status = smb2_getinfo_file(tree, tmp_ctx, &io);
6069 torture_assert_ntstatus_ok(tctx, status, "getinfo");
6070 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
6071 32768, "size after IO");
6073 ZERO_STRUCT(io);
6074 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
6075 io.generic.in.file.handle = dest_h;
6076 status = smb2_getinfo_file(tree, tmp_ctx, &io);
6077 torture_assert_ntstatus_ok(tctx, status, "getinfo");
6078 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
6079 0, "size after IO");
6081 smb2_util_close(tree, src_h);
6082 smb2_util_close(tree, dest_h);
6083 talloc_free(tmp_ctx);
6084 return true;
6087 static bool test_ioctl_dup_extents_len_zero(struct torture_context *tctx,
6088 struct smb2_tree *tree)
6090 struct smb2_handle src_h;
6091 struct smb2_handle dest_h;
6092 NTSTATUS status;
6093 union smb_ioctl ioctl;
6094 TALLOC_CTX *tmp_ctx = talloc_new(tree);
6095 struct fsctl_dup_extents_to_file dup_ext_buf;
6096 enum ndr_err_code ndr_ret;
6097 union smb_fileinfo io;
6098 bool ok;
6100 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
6101 &src_h, 32768, /* fill 32768 byte src file */
6102 SEC_RIGHTS_FILE_ALL,
6103 &dest_h, 0, /* 0 byte dest file */
6104 SEC_RIGHTS_FILE_ALL,
6105 &dup_ext_buf,
6106 &ioctl);
6107 if (!ok) {
6108 torture_fail(tctx, "setup dup extents error");
6111 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
6112 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
6113 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
6114 if (!ok) {
6115 smb2_util_close(tree, src_h);
6116 smb2_util_close(tree, dest_h);
6117 talloc_free(tmp_ctx);
6118 torture_skip(tctx, "block refcounting not supported\n");
6121 ZERO_STRUCT(io);
6122 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
6123 io.generic.in.file.handle = dest_h;
6124 status = smb2_getinfo_file(tree, tmp_ctx, &io);
6125 torture_assert_ntstatus_ok(tctx, status, "getinfo");
6126 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
6127 0, "size after IO");
6129 dup_ext_buf.source_off = 0;
6130 dup_ext_buf.target_off = 0;
6131 dup_ext_buf.byte_count = 0;
6133 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6134 &dup_ext_buf,
6135 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6136 torture_assert_ndr_success(tctx, ndr_ret,
6137 "ndr_push_fsctl_dup_extents_to_file");
6139 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6140 torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
6142 /* the file sizes shouldn't have been changed */
6143 ZERO_STRUCT(io);
6144 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
6145 io.generic.in.file.handle = src_h;
6146 status = smb2_getinfo_file(tree, tmp_ctx, &io);
6147 torture_assert_ntstatus_ok(tctx, status, "getinfo");
6148 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
6149 32768, "size after IO");
6151 ZERO_STRUCT(io);
6152 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
6153 io.generic.in.file.handle = dest_h;
6154 status = smb2_getinfo_file(tree, tmp_ctx, &io);
6155 torture_assert_ntstatus_ok(tctx, status, "getinfo");
6156 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
6157 0, "size after IO");
6159 smb2_util_close(tree, src_h);
6160 smb2_util_close(tree, dest_h);
6161 talloc_free(tmp_ctx);
6162 return true;
6165 static bool test_ioctl_dup_extents_sparse_src(struct torture_context *tctx,
6166 struct smb2_tree *tree)
6168 struct smb2_handle src_h;
6169 struct smb2_handle dest_h;
6170 NTSTATUS status;
6171 union smb_ioctl ioctl;
6172 TALLOC_CTX *tmp_ctx = talloc_new(tree);
6173 struct fsctl_dup_extents_to_file dup_ext_buf;
6174 enum ndr_err_code ndr_ret;
6175 union smb_setfileinfo sinfo;
6176 bool ok;
6178 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
6179 &src_h, 0, /* filled after sparse flag */
6180 SEC_RIGHTS_FILE_ALL,
6181 &dest_h, 0, /* 0 byte dest file */
6182 SEC_RIGHTS_FILE_ALL,
6183 &dup_ext_buf,
6184 &ioctl);
6185 if (!ok) {
6186 torture_fail(tctx, "setup dup extents error");
6189 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
6190 FILE_SUPPORTS_BLOCK_REFCOUNTING
6191 | FILE_SUPPORTS_SPARSE_FILES, &ok);
6192 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
6193 if (!ok) {
6194 smb2_util_close(tree, src_h);
6195 smb2_util_close(tree, dest_h);
6196 talloc_free(tmp_ctx);
6197 torture_skip(tctx,
6198 "block refcounting and sparse files not supported\n");
6201 /* set sparse flag on src */
6202 status = test_ioctl_sparse_req(tctx, tmp_ctx, tree, src_h, true);
6203 torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_SPARSE");
6205 ok = write_pattern(tctx, tree, tmp_ctx, src_h, 0, 4096, 0);
6206 torture_assert(tctx, ok, "write pattern");
6208 /* extend dest */
6209 ZERO_STRUCT(sinfo);
6210 sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
6211 sinfo.end_of_file_info.in.file.handle = dest_h;
6212 sinfo.end_of_file_info.in.size = 4096;
6213 status = smb2_setinfo_file(tree, &sinfo);
6214 torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
6216 /* copy all src file data */
6217 dup_ext_buf.source_off = 0;
6218 dup_ext_buf.target_off = 0;
6219 dup_ext_buf.byte_count = 4096;
6221 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6222 &dup_ext_buf,
6223 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6224 torture_assert_ndr_success(tctx, ndr_ret,
6225 "ndr_push_fsctl_dup_extents_to_file");
6228 * src is sparse, but spec says: 2.3.8 FSCTL_DUPLICATE_EXTENTS_TO_FILE
6229 * Reply... STATUS_NOT_SUPPORTED: Target file is sparse, while source
6230 * is a non-sparse file.
6232 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6233 torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_SUPPORTED,
6234 "FSCTL_DUP_EXTENTS_TO_FILE");
6236 smb2_util_close(tree, src_h);
6237 smb2_util_close(tree, dest_h);
6238 talloc_free(tmp_ctx);
6239 return true;
6242 static bool test_ioctl_dup_extents_sparse_dest(struct torture_context *tctx,
6243 struct smb2_tree *tree)
6245 struct smb2_handle src_h;
6246 struct smb2_handle dest_h;
6247 NTSTATUS status;
6248 union smb_ioctl ioctl;
6249 TALLOC_CTX *tmp_ctx = talloc_new(tree);
6250 struct fsctl_dup_extents_to_file dup_ext_buf;
6251 enum ndr_err_code ndr_ret;
6252 union smb_setfileinfo sinfo;
6253 bool ok;
6255 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
6256 &src_h, 4096, /* fill 4096 byte src file */
6257 SEC_RIGHTS_FILE_ALL,
6258 &dest_h, 0, /* 0 byte dest file */
6259 SEC_RIGHTS_FILE_ALL,
6260 &dup_ext_buf,
6261 &ioctl);
6262 if (!ok) {
6263 torture_fail(tctx, "setup dup extents error");
6266 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
6267 FILE_SUPPORTS_BLOCK_REFCOUNTING
6268 | FILE_SUPPORTS_SPARSE_FILES, &ok);
6269 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
6270 if (!ok) {
6271 smb2_util_close(tree, src_h);
6272 smb2_util_close(tree, dest_h);
6273 talloc_free(tmp_ctx);
6274 torture_skip(tctx,
6275 "block refcounting and sparse files not supported\n");
6278 /* set sparse flag on dest */
6279 status = test_ioctl_sparse_req(tctx, tmp_ctx, tree, dest_h, true);
6280 torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_SPARSE");
6282 /* extend dest */
6283 ZERO_STRUCT(sinfo);
6284 sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
6285 sinfo.end_of_file_info.in.file.handle = dest_h;
6286 sinfo.end_of_file_info.in.size = dup_ext_buf.byte_count;
6287 status = smb2_setinfo_file(tree, &sinfo);
6288 torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
6290 /* copy all src file data */
6291 dup_ext_buf.source_off = 0;
6292 dup_ext_buf.target_off = 0;
6293 dup_ext_buf.byte_count = 4096;
6295 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6296 &dup_ext_buf,
6297 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6298 torture_assert_ndr_success(tctx, ndr_ret,
6299 "ndr_push_fsctl_dup_extents_to_file");
6302 * dest is sparse, but spec says: 2.3.8 FSCTL_DUPLICATE_EXTENTS_TO_FILE
6303 * Reply... STATUS_NOT_SUPPORTED: Target file is sparse, while source
6304 * is a non-sparse file.
6306 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6307 torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
6309 smb2_util_close(tree, src_h);
6310 smb2_util_close(tree, dest_h);
6311 talloc_free(tmp_ctx);
6312 return true;
6315 static bool test_ioctl_dup_extents_sparse_both(struct torture_context *tctx,
6316 struct smb2_tree *tree)
6318 struct smb2_handle src_h;
6319 struct smb2_handle dest_h;
6320 NTSTATUS status;
6321 union smb_ioctl ioctl;
6322 TALLOC_CTX *tmp_ctx = talloc_new(tree);
6323 struct fsctl_dup_extents_to_file dup_ext_buf;
6324 enum ndr_err_code ndr_ret;
6325 union smb_setfileinfo sinfo;
6326 bool ok;
6328 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
6329 &src_h, 0, /* fill 4096 byte src file */
6330 SEC_RIGHTS_FILE_ALL,
6331 &dest_h, 0, /* 0 byte dest file */
6332 SEC_RIGHTS_FILE_ALL,
6333 &dup_ext_buf,
6334 &ioctl);
6335 if (!ok) {
6336 torture_fail(tctx, "setup dup extents error");
6339 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
6340 FILE_SUPPORTS_BLOCK_REFCOUNTING
6341 | FILE_SUPPORTS_SPARSE_FILES, &ok);
6342 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
6343 if (!ok) {
6344 smb2_util_close(tree, src_h);
6345 smb2_util_close(tree, dest_h);
6346 talloc_free(tmp_ctx);
6347 torture_skip(tctx,
6348 "block refcounting and sparse files not supported\n");
6351 /* set sparse flag on src and dest */
6352 status = test_ioctl_sparse_req(tctx, tmp_ctx, tree, src_h, true);
6353 torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_SPARSE");
6354 status = test_ioctl_sparse_req(tctx, tmp_ctx, tree, dest_h, true);
6355 torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_SPARSE");
6357 ok = write_pattern(tctx, tree, tmp_ctx, src_h, 0, 4096, 0);
6358 torture_assert(tctx, ok, "write pattern");
6360 /* extend dest */
6361 ZERO_STRUCT(sinfo);
6362 sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
6363 sinfo.end_of_file_info.in.file.handle = dest_h;
6364 sinfo.end_of_file_info.in.size = 4096;
6365 status = smb2_setinfo_file(tree, &sinfo);
6366 torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
6368 /* copy all src file data */
6369 dup_ext_buf.source_off = 0;
6370 dup_ext_buf.target_off = 0;
6371 dup_ext_buf.byte_count = 4096;
6373 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6374 &dup_ext_buf,
6375 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6376 torture_assert_ndr_success(tctx, ndr_ret,
6377 "ndr_push_fsctl_dup_extents_to_file");
6379 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6380 torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
6382 smb2_util_close(tree, src_h);
6383 smb2_util_close(tree, dest_h);
6385 /* reopen for pattern check */
6386 ok = test_setup_open(tctx, tree, tmp_ctx, FNAME2, &dest_h,
6387 SEC_RIGHTS_FILE_ALL, FILE_ATTRIBUTE_NORMAL);
6388 torture_assert_ntstatus_ok(tctx, status, "dest open ater dup");
6390 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 4096, 0);
6391 if (!ok) {
6392 torture_fail(tctx, "inconsistent file data");
6395 smb2_util_close(tree, dest_h);
6396 talloc_free(tmp_ctx);
6397 return true;
6400 static bool test_ioctl_dup_extents_src_is_dest(struct torture_context *tctx,
6401 struct smb2_tree *tree)
6403 struct smb2_handle src_h;
6404 struct smb2_handle dest_h;
6405 NTSTATUS status;
6406 union smb_ioctl ioctl;
6407 TALLOC_CTX *tmp_ctx = talloc_new(tree);
6408 struct fsctl_dup_extents_to_file dup_ext_buf;
6409 enum ndr_err_code ndr_ret;
6410 union smb_fileinfo io;
6411 bool ok;
6413 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
6414 &src_h, 32768, /* fill 32768 byte src file */
6415 SEC_RIGHTS_FILE_ALL,
6416 &dest_h, 0,
6417 SEC_RIGHTS_FILE_ALL,
6418 &dup_ext_buf,
6419 &ioctl);
6420 if (!ok) {
6421 torture_fail(tctx, "setup dup extents error");
6423 /* dest_h not needed for this test */
6424 smb2_util_close(tree, dest_h);
6426 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
6427 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
6428 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
6429 if (!ok) {
6430 smb2_util_close(tree, src_h);
6431 talloc_free(tmp_ctx);
6432 torture_skip(tctx, "block refcounting not supported\n");
6435 /* src and dest are the same file handle */
6436 ioctl.smb2.in.file.handle = src_h;
6438 /* no overlap between src and tgt */
6439 dup_ext_buf.source_off = 0;
6440 dup_ext_buf.target_off = 16384;
6441 dup_ext_buf.byte_count = 16384;
6443 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6444 &dup_ext_buf,
6445 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6446 torture_assert_ndr_success(tctx, ndr_ret,
6447 "ndr_push_fsctl_dup_extents_to_file");
6449 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6450 torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
6452 /* the file size shouldn't have been changed */
6453 ZERO_STRUCT(io);
6454 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
6455 io.generic.in.file.handle = src_h;
6456 status = smb2_getinfo_file(tree, tmp_ctx, &io);
6457 torture_assert_ntstatus_ok(tctx, status, "getinfo");
6458 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
6459 32768, "size after IO");
6461 ok = check_pattern(tctx, tree, tmp_ctx, src_h, 0, 16384, 0);
6462 if (!ok) {
6463 torture_fail(tctx, "inconsistent file data");
6465 ok = check_pattern(tctx, tree, tmp_ctx, src_h, 16384, 16384, 0);
6466 if (!ok) {
6467 torture_fail(tctx, "inconsistent file data");
6470 smb2_util_close(tree, src_h);
6471 talloc_free(tmp_ctx);
6472 return true;
6476 * unlike copy-chunk, dup extents doesn't support overlapping ranges between
6477 * source and target. This makes it a *lot* cleaner to implement on the server.
6479 static bool
6480 test_ioctl_dup_extents_src_is_dest_overlap(struct torture_context *tctx,
6481 struct smb2_tree *tree)
6483 struct smb2_handle src_h;
6484 struct smb2_handle dest_h;
6485 NTSTATUS status;
6486 union smb_ioctl ioctl;
6487 TALLOC_CTX *tmp_ctx = talloc_new(tree);
6488 struct fsctl_dup_extents_to_file dup_ext_buf;
6489 enum ndr_err_code ndr_ret;
6490 union smb_fileinfo io;
6491 bool ok;
6493 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
6494 &src_h, 32768, /* fill 32768 byte src file */
6495 SEC_RIGHTS_FILE_ALL,
6496 &dest_h, 0,
6497 SEC_RIGHTS_FILE_ALL,
6498 &dup_ext_buf,
6499 &ioctl);
6500 if (!ok) {
6501 torture_fail(tctx, "setup dup extents error");
6503 /* dest_h not needed for this test */
6504 smb2_util_close(tree, dest_h);
6506 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
6507 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
6508 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
6509 if (!ok) {
6510 smb2_util_close(tree, src_h);
6511 talloc_free(tmp_ctx);
6512 torture_skip(tctx, "block refcounting not supported\n");
6515 /* src and dest are the same file handle */
6516 ioctl.smb2.in.file.handle = src_h;
6518 /* 8K overlap between src and tgt */
6519 dup_ext_buf.source_off = 0;
6520 dup_ext_buf.target_off = 8192;
6521 dup_ext_buf.byte_count = 16384;
6523 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6524 &dup_ext_buf,
6525 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6526 torture_assert_ndr_success(tctx, ndr_ret,
6527 "ndr_push_fsctl_dup_extents_to_file");
6529 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6530 torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_SUPPORTED,
6531 "FSCTL_DUP_EXTENTS_TO_FILE");
6533 /* the file size and data should match beforehand */
6534 ZERO_STRUCT(io);
6535 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
6536 io.generic.in.file.handle = src_h;
6537 status = smb2_getinfo_file(tree, tmp_ctx, &io);
6538 torture_assert_ntstatus_ok(tctx, status, "getinfo");
6539 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
6540 32768, "size after IO");
6542 ok = check_pattern(tctx, tree, tmp_ctx, src_h, 0, 32768, 0);
6543 if (!ok) {
6544 torture_fail(tctx, "inconsistent file data");
6547 smb2_util_close(tree, src_h);
6548 talloc_free(tmp_ctx);
6549 return true;
6553 * The compression tests won't run against Windows servers yet - ReFS doesn't
6554 * (yet) offer support for compression.
6556 static bool test_ioctl_dup_extents_compressed_src(struct torture_context *tctx,
6557 struct smb2_tree *tree)
6559 struct smb2_handle src_h;
6560 struct smb2_handle dest_h;
6561 NTSTATUS status;
6562 union smb_ioctl ioctl;
6563 TALLOC_CTX *tmp_ctx = talloc_new(tree);
6564 struct fsctl_dup_extents_to_file dup_ext_buf;
6565 enum ndr_err_code ndr_ret;
6566 union smb_setfileinfo sinfo;
6567 bool ok;
6569 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
6570 &src_h, 0, /* filled after compressed flag */
6571 SEC_RIGHTS_FILE_ALL,
6572 &dest_h, 0,
6573 SEC_RIGHTS_FILE_ALL,
6574 &dup_ext_buf,
6575 &ioctl);
6576 if (!ok) {
6577 torture_fail(tctx, "setup dup extents error");
6580 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
6581 FILE_SUPPORTS_BLOCK_REFCOUNTING
6582 | FILE_FILE_COMPRESSION, &ok);
6583 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
6584 if (!ok) {
6585 smb2_util_close(tree, src_h);
6586 smb2_util_close(tree, dest_h);
6587 talloc_free(tmp_ctx);
6588 torture_skip(tctx,
6589 "block refcounting and compressed files not supported\n");
6592 /* set compressed flag on src */
6593 status = test_ioctl_compress_set(tctx, tmp_ctx, tree, src_h,
6594 COMPRESSION_FORMAT_DEFAULT);
6595 torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_COMPRESSION");
6597 ok = write_pattern(tctx, tree, tmp_ctx, src_h, 0, 4096, 0);
6598 torture_assert(tctx, ok, "write pattern");
6600 /* extend dest */
6601 ZERO_STRUCT(sinfo);
6602 sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
6603 sinfo.end_of_file_info.in.file.handle = dest_h;
6604 sinfo.end_of_file_info.in.size = 4096;
6605 status = smb2_setinfo_file(tree, &sinfo);
6606 torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
6608 /* copy all src file data */
6609 dup_ext_buf.source_off = 0;
6610 dup_ext_buf.target_off = 0;
6611 dup_ext_buf.byte_count = 4096;
6613 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6614 &dup_ext_buf,
6615 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6616 torture_assert_ndr_success(tctx, ndr_ret,
6617 "ndr_push_fsctl_dup_extents_to_file");
6619 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6620 torture_assert_ntstatus_ok(tctx, status,
6621 "FSCTL_DUP_EXTENTS_TO_FILE");
6623 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 4096, 0);
6624 if (!ok) {
6625 torture_fail(tctx, "inconsistent file data");
6628 smb2_util_close(tree, src_h);
6629 smb2_util_close(tree, dest_h);
6630 talloc_free(tmp_ctx);
6631 return true;
6634 static bool test_ioctl_dup_extents_compressed_dest(struct torture_context *tctx,
6635 struct smb2_tree *tree)
6637 struct smb2_handle src_h;
6638 struct smb2_handle dest_h;
6639 NTSTATUS status;
6640 union smb_ioctl ioctl;
6641 TALLOC_CTX *tmp_ctx = talloc_new(tree);
6642 struct fsctl_dup_extents_to_file dup_ext_buf;
6643 enum ndr_err_code ndr_ret;
6644 union smb_setfileinfo sinfo;
6645 bool ok;
6647 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
6648 &src_h, 4096,
6649 SEC_RIGHTS_FILE_ALL,
6650 &dest_h, 0,
6651 SEC_RIGHTS_FILE_ALL,
6652 &dup_ext_buf,
6653 &ioctl);
6654 if (!ok) {
6655 torture_fail(tctx, "setup dup extents error");
6658 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
6659 FILE_SUPPORTS_BLOCK_REFCOUNTING
6660 | FILE_FILE_COMPRESSION, &ok);
6661 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
6662 if (!ok) {
6663 smb2_util_close(tree, src_h);
6664 smb2_util_close(tree, dest_h);
6665 talloc_free(tmp_ctx);
6666 torture_skip(tctx,
6667 "block refcounting and compressed files not supported\n");
6670 /* set compressed flag on dest */
6671 status = test_ioctl_compress_set(tctx, tmp_ctx, tree, dest_h,
6672 COMPRESSION_FORMAT_DEFAULT);
6673 torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_COMPRESSION");
6675 /* extend dest */
6676 ZERO_STRUCT(sinfo);
6677 sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
6678 sinfo.end_of_file_info.in.file.handle = dest_h;
6679 sinfo.end_of_file_info.in.size = 4096;
6680 status = smb2_setinfo_file(tree, &sinfo);
6681 torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
6683 /* copy all src file data */
6684 dup_ext_buf.source_off = 0;
6685 dup_ext_buf.target_off = 0;
6686 dup_ext_buf.byte_count = 4096;
6688 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6689 &dup_ext_buf,
6690 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6691 torture_assert_ndr_success(tctx, ndr_ret,
6692 "ndr_push_fsctl_dup_extents_to_file");
6694 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6695 torture_assert_ntstatus_ok(tctx, status,
6696 "FSCTL_DUP_EXTENTS_TO_FILE");
6698 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 4096, 0);
6699 if (!ok) {
6700 torture_fail(tctx, "inconsistent file data");
6703 smb2_util_close(tree, src_h);
6704 smb2_util_close(tree, dest_h);
6705 talloc_free(tmp_ctx);
6706 return true;
6709 static bool test_ioctl_dup_extents_bad_handle(struct torture_context *tctx,
6710 struct smb2_tree *tree)
6712 struct smb2_handle src_h;
6713 struct smb2_handle dest_h;
6714 struct smb2_handle bogus_h;
6715 NTSTATUS status;
6716 union smb_ioctl ioctl;
6717 TALLOC_CTX *tmp_ctx = talloc_new(tree);
6718 struct fsctl_dup_extents_to_file dup_ext_buf;
6719 enum ndr_err_code ndr_ret;
6720 bool ok;
6722 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
6723 &src_h, 32768, /* fill 32768 byte src file */
6724 SEC_RIGHTS_FILE_ALL,
6725 &dest_h, 32768,
6726 SEC_RIGHTS_FILE_ALL,
6727 &dup_ext_buf,
6728 &ioctl);
6729 if (!ok) {
6730 torture_fail(tctx, "setup dup extents error");
6733 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
6734 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
6735 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
6736 if (!ok) {
6737 smb2_util_close(tree, src_h);
6738 smb2_util_close(tree, dest_h);
6739 talloc_free(tmp_ctx);
6740 torture_skip(tctx, "block refcounting not supported\n");
6743 /* open and close a file, keeping the handle as now a "bogus" handle */
6744 ok = test_setup_create_fill(tctx, tree, tmp_ctx, "bogus_file",
6745 &bogus_h, 0, SEC_RIGHTS_FILE_ALL,
6746 FILE_ATTRIBUTE_NORMAL);
6747 torture_assert(tctx, ok, "bogus file create fill");
6748 smb2_util_close(tree, bogus_h);
6750 /* bogus dest file handle */
6751 ioctl.smb2.in.file.handle = bogus_h;
6753 dup_ext_buf.source_off = 0;
6754 dup_ext_buf.target_off = 0;
6755 dup_ext_buf.byte_count = 32768;
6757 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6758 &dup_ext_buf,
6759 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6760 torture_assert_ndr_success(tctx, ndr_ret,
6761 "ndr_push_fsctl_dup_extents_to_file");
6763 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6764 torture_assert_ntstatus_equal(tctx, status, NT_STATUS_FILE_CLOSED,
6765 "FSCTL_DUP_EXTENTS_TO_FILE");
6767 ok = check_pattern(tctx, tree, tmp_ctx, src_h, 0, 32768, 0);
6768 if (!ok) {
6769 torture_fail(tctx, "inconsistent file data");
6771 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
6772 if (!ok) {
6773 torture_fail(tctx, "inconsistent file data");
6776 /* reinstate dest, add bogus src file handle */
6777 ioctl.smb2.in.file.handle = dest_h;
6778 smb2_push_handle(dup_ext_buf.source_fid, &bogus_h);
6780 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6781 &dup_ext_buf,
6782 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6783 torture_assert_ndr_success(tctx, ndr_ret,
6784 "ndr_push_fsctl_dup_extents_to_file");
6786 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6787 torture_assert_ntstatus_equal(tctx, status, NT_STATUS_INVALID_HANDLE,
6788 "FSCTL_DUP_EXTENTS_TO_FILE");
6790 ok = check_pattern(tctx, tree, tmp_ctx, src_h, 0, 32768, 0);
6791 if (!ok) {
6792 torture_fail(tctx, "inconsistent file data");
6794 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
6795 if (!ok) {
6796 torture_fail(tctx, "inconsistent file data");
6799 smb2_util_close(tree, src_h);
6800 smb2_util_close(tree, dest_h);
6801 talloc_free(tmp_ctx);
6802 return true;
6805 static bool test_ioctl_dup_extents_src_lck(struct torture_context *tctx,
6806 struct smb2_tree *tree)
6808 struct smb2_handle src_h;
6809 struct smb2_handle src_h2;
6810 struct smb2_handle dest_h;
6811 NTSTATUS status;
6812 union smb_ioctl ioctl;
6813 TALLOC_CTX *tmp_ctx = talloc_new(tree);
6814 struct fsctl_dup_extents_to_file dup_ext_buf;
6815 enum ndr_err_code ndr_ret;
6816 bool ok;
6817 struct smb2_lock lck;
6818 struct smb2_lock_element el[1];
6820 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
6821 &src_h, 32768, /* fill 32768 byte src file */
6822 SEC_RIGHTS_FILE_ALL,
6823 &dest_h, 0,
6824 SEC_RIGHTS_FILE_ALL,
6825 &dup_ext_buf,
6826 &ioctl);
6827 if (!ok) {
6828 torture_fail(tctx, "setup dup extents error");
6831 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
6832 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
6833 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
6834 if (!ok) {
6835 smb2_util_close(tree, src_h);
6836 smb2_util_close(tree, dest_h);
6837 talloc_free(tmp_ctx);
6838 torture_skip(tctx, "block refcounting not supported\n");
6841 /* dest pattern is different to src */
6842 ok = write_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 32768);
6843 torture_assert(tctx, ok, "write pattern");
6845 /* setup dup ext req, values used for locking */
6846 dup_ext_buf.source_off = 0;
6847 dup_ext_buf.target_off = 0;
6848 dup_ext_buf.byte_count = 32768;
6850 /* open and lock the dup extents src file */
6851 status = torture_smb2_testfile(tree, FNAME, &src_h2);
6852 torture_assert_ntstatus_ok(tctx, status, "2nd src open");
6854 lck.in.lock_count = 0x0001;
6855 lck.in.lock_sequence = 0x00000000;
6856 lck.in.file.handle = src_h2;
6857 lck.in.locks = el;
6858 el[0].offset = dup_ext_buf.source_off;
6859 el[0].length = dup_ext_buf.byte_count;
6860 el[0].reserved = 0;
6861 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
6863 status = smb2_lock(tree, &lck);
6864 torture_assert_ntstatus_ok(tctx, status, "lock");
6866 status = smb2_util_write(tree, src_h,
6867 "conflicted", 0, sizeof("conflicted"));
6868 torture_assert_ntstatus_equal(tctx, status,
6869 NT_STATUS_FILE_LOCK_CONFLICT, "file write");
6871 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6872 &dup_ext_buf,
6873 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6874 torture_assert_ndr_success(tctx, ndr_ret,
6875 "ndr_push_fsctl_dup_extents_to_file");
6878 * In contrast to copy-chunk, dup extents doesn't cause a lock conflict
6879 * here.
6881 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6882 torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
6884 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
6885 if (!ok) {
6886 torture_fail(tctx, "inconsistent file data");
6889 lck.in.lock_count = 0x0001;
6890 lck.in.lock_sequence = 0x00000001;
6891 lck.in.file.handle = src_h2;
6892 lck.in.locks = el;
6893 el[0].offset = dup_ext_buf.source_off;
6894 el[0].length = dup_ext_buf.byte_count;
6895 el[0].reserved = 0;
6896 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
6897 status = smb2_lock(tree, &lck);
6898 torture_assert_ntstatus_ok(tctx, status, "unlock");
6900 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6901 torture_assert_ntstatus_ok(tctx, status,
6902 "FSCTL_DUP_EXTENTS_TO_FILE unlocked");
6904 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
6905 if (!ok) {
6906 torture_fail(tctx, "inconsistent file data");
6909 smb2_util_close(tree, src_h2);
6910 smb2_util_close(tree, src_h);
6911 smb2_util_close(tree, dest_h);
6912 talloc_free(tmp_ctx);
6913 return true;
6916 static bool test_ioctl_dup_extents_dest_lck(struct torture_context *tctx,
6917 struct smb2_tree *tree)
6919 struct smb2_handle src_h;
6920 struct smb2_handle dest_h;
6921 struct smb2_handle dest_h2;
6922 NTSTATUS status;
6923 union smb_ioctl ioctl;
6924 TALLOC_CTX *tmp_ctx = talloc_new(tree);
6925 struct fsctl_dup_extents_to_file dup_ext_buf;
6926 enum ndr_err_code ndr_ret;
6927 bool ok;
6928 struct smb2_lock lck;
6929 struct smb2_lock_element el[1];
6931 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
6932 &src_h, 32768, /* fill 32768 byte src file */
6933 SEC_RIGHTS_FILE_ALL,
6934 &dest_h, 0,
6935 SEC_RIGHTS_FILE_ALL,
6936 &dup_ext_buf,
6937 &ioctl);
6938 if (!ok) {
6939 torture_fail(tctx, "setup dup extents error");
6942 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
6943 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
6944 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
6945 if (!ok) {
6946 smb2_util_close(tree, src_h);
6947 smb2_util_close(tree, dest_h);
6948 talloc_free(tmp_ctx);
6949 torture_skip(tctx, "block refcounting not supported\n");
6952 /* dest pattern is different to src */
6953 ok = write_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 32768);
6954 torture_assert(tctx, ok, "write pattern");
6956 /* setup dup ext req, values used for locking */
6957 dup_ext_buf.source_off = 0;
6958 dup_ext_buf.target_off = 0;
6959 dup_ext_buf.byte_count = 32768;
6961 /* open and lock the dup extents dest file */
6962 status = torture_smb2_testfile(tree, FNAME2, &dest_h2);
6963 torture_assert_ntstatus_ok(tctx, status, "2nd src open");
6965 lck.in.lock_count = 0x0001;
6966 lck.in.lock_sequence = 0x00000000;
6967 lck.in.file.handle = dest_h2;
6968 lck.in.locks = el;
6969 el[0].offset = dup_ext_buf.source_off;
6970 el[0].length = dup_ext_buf.byte_count;
6971 el[0].reserved = 0;
6972 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
6974 status = smb2_lock(tree, &lck);
6975 torture_assert_ntstatus_ok(tctx, status, "lock");
6977 status = smb2_util_write(tree, dest_h,
6978 "conflicted", 0, sizeof("conflicted"));
6979 torture_assert_ntstatus_equal(tctx, status,
6980 NT_STATUS_FILE_LOCK_CONFLICT, "file write");
6982 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6983 &dup_ext_buf,
6984 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6985 torture_assert_ndr_success(tctx, ndr_ret,
6986 "ndr_push_fsctl_dup_extents_to_file");
6989 * In contrast to copy-chunk, dup extents doesn't cause a lock conflict
6990 * here.
6992 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6993 torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
6995 lck.in.lock_count = 0x0001;
6996 lck.in.lock_sequence = 0x00000001;
6997 lck.in.file.handle = dest_h2;
6998 lck.in.locks = el;
6999 el[0].offset = dup_ext_buf.source_off;
7000 el[0].length = dup_ext_buf.byte_count;
7001 el[0].reserved = 0;
7002 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
7003 status = smb2_lock(tree, &lck);
7004 torture_assert_ntstatus_ok(tctx, status, "unlock");
7006 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
7007 torture_assert_ntstatus_ok(tctx, status,
7008 "FSCTL_DUP_EXTENTS_TO_FILE unlocked");
7010 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
7011 if (!ok) {
7012 torture_fail(tctx, "inconsistent file data");
7015 smb2_util_close(tree, src_h);
7016 smb2_util_close(tree, dest_h);
7017 smb2_util_close(tree, dest_h2);
7018 talloc_free(tmp_ctx);
7019 return true;
7023 basic regression test for BUG 14607
7024 https://bugzilla.samba.org/show_bug.cgi?id=14607
7026 static bool test_ioctl_bug14607(struct torture_context *torture,
7027 struct smb2_tree *tree)
7029 TALLOC_CTX *tmp_ctx = talloc_new(tree);
7030 uint32_t timeout_msec;
7031 NTSTATUS status;
7032 DATA_BLOB out_input_buffer = data_blob_null;
7033 DATA_BLOB out_output_buffer = data_blob_null;
7035 timeout_msec = tree->session->transport->options.request_timeout * 1000;
7037 status = smb2cli_ioctl(tree->session->transport->conn,
7038 timeout_msec,
7039 tree->session->smbXcli,
7040 tree->smbXcli,
7041 UINT64_MAX, /* in_fid_persistent */
7042 UINT64_MAX, /* in_fid_volatile */
7043 FSCTL_SMBTORTURE_IOCTL_RESPONSE_BODY_PADDING8,
7044 0, /* in_max_input_length */
7045 NULL, /* in_input_buffer */
7046 1, /* in_max_output_length */
7047 NULL, /* in_output_buffer */
7048 SMB2_IOCTL_FLAG_IS_FSCTL,
7049 tmp_ctx,
7050 &out_input_buffer,
7051 &out_output_buffer);
7052 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED) ||
7053 NT_STATUS_EQUAL(status, NT_STATUS_FILE_CLOSED) ||
7054 NT_STATUS_EQUAL(status, NT_STATUS_FS_DRIVER_REQUIRED) ||
7055 NT_STATUS_EQUAL(status, NT_STATUS_INVALID_DEVICE_REQUEST))
7057 torture_comment(torture,
7058 "FSCTL_SMBTORTURE_IOCTL_RESPONSE_BODY_PADDING8: %s\n",
7059 nt_errstr(status));
7060 torture_skip(torture, "server doesn't support FSCTL_SMBTORTURE_IOCTL_RESPONSE_BODY_PADDING8\n");
7062 torture_assert_ntstatus_ok(torture, status, "FSCTL_SMBTORTURE_IOCTL_RESPONSE_BODY_PADDING8");
7064 torture_assert_int_equal(torture, out_output_buffer.length, 1,
7065 "output length");
7066 torture_assert_int_equal(torture, out_output_buffer.data[0], 8,
7067 "output buffer byte should be 8");
7069 talloc_free(tmp_ctx);
7070 return true;
7074 basic regression test for BUG 14769
7075 https://bugzilla.samba.org/show_bug.cgi?id=14769
7077 static bool test_ioctl_bug14769(struct torture_context *torture,
7078 struct smb2_tree *tree)
7080 NTSTATUS status;
7081 const char *fname = "bug14769";
7082 bool ret = false;
7083 struct smb2_handle h;
7084 struct smb2_ioctl ioctl;
7085 struct smb2_close cl;
7086 struct smb2_request *smb2arr[2] = { 0 };
7087 uint8_t tosend_msec = 200;
7088 DATA_BLOB send_buf = { &tosend_msec, 1 };
7090 /* Create a test file. */
7091 smb2_util_unlink(tree, fname);
7092 status = torture_smb2_testfile(tree, fname, &h);
7093 torture_assert_ntstatus_ok(torture, status, "create bug14769");
7096 * Send (not receive) the FSCTL.
7097 * This should go async with a wait time of 200 msec.
7099 ZERO_STRUCT(ioctl);
7100 ioctl.in.file.handle = h;
7101 ioctl.in.function = FSCTL_SMBTORTURE_FSP_ASYNC_SLEEP;
7102 ioctl.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
7103 ioctl.in.out = send_buf;
7105 smb2arr[0] = smb2_ioctl_send(tree, &ioctl);
7106 torture_assert_goto(torture,
7107 smb2arr[0] != NULL,
7108 ret,
7109 done,
7110 "smb2_ioctl_send failed\n");
7111 /* Immediately send the close. */
7112 ZERO_STRUCT(cl);
7113 cl.in.file.handle = h;
7114 cl.in.flags = 0;
7115 smb2arr[1] = smb2_close_send(tree, &cl);
7116 torture_assert_goto(torture,
7117 smb2arr[1] != NULL,
7118 ret,
7119 done,
7120 "smb2_close_send failed\n");
7122 /* Now get the FSCTL reply. */
7124 * If we suffer from bug #14769 this will fail as
7125 * the ioctl will return with NT_STATUS_FILE_CLOSED,
7126 * as the close will have closed the handle without
7127 * waiting for the ioctl to complete. The server shouldn't
7128 * complete the close until the ioctl finishes.
7130 status = smb2_ioctl_recv(smb2arr[0], tree, &ioctl);
7131 torture_assert_ntstatus_ok_goto(torture,
7132 status,
7133 ret,
7134 done,
7135 "smb2_ioctl_recv failed\n");
7137 /* Followed by the close reply. */
7138 status = smb2_close_recv(smb2arr[1], &cl);
7139 torture_assert_ntstatus_ok_goto(torture,
7140 status,
7141 ret,
7142 done,
7143 "smb2_ioctl_close failed\n");
7144 ret = true;
7146 done:
7147 smb2_util_unlink(tree, fname);
7148 return ret;
7152 basic regression test for BUG 14788,
7153 with FSCTL_VALIDATE_NEGOTIATE_INFO
7154 https://bugzilla.samba.org/show_bug.cgi?id=14788
7156 static bool test_ioctl_bug14788_VALIDATE_NEGOTIATE(struct torture_context *torture,
7157 struct smb2_tree *tree0)
7159 const char *host = torture_setting_string(torture, "host", NULL);
7160 const char *share = torture_setting_string(torture, "share", NULL);
7161 const char *noperm_share = torture_setting_string(torture, "noperm_share", "noperm");
7162 struct smb2_transport *transport0 = tree0->session->transport;
7163 struct smbcli_options options;
7164 struct smb2_transport *transport = NULL;
7165 struct smb2_tree *tree = NULL;
7166 struct smb2_session *session = NULL;
7167 uint16_t noperm_flags = 0;
7168 const char *noperm_unc = NULL;
7169 struct smb2_tree *noperm_tree = NULL;
7170 uint32_t timeout_msec;
7171 struct tevent_req *subreq = NULL;
7172 struct cli_credentials *credentials = samba_cmdline_get_creds();
7173 NTSTATUS status;
7175 if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_00) {
7176 torture_skip(torture, "Can't test without SMB 3 support");
7179 options = transport0->options;
7180 options.client_guid = GUID_random();
7181 options.min_protocol = PROTOCOL_SMB3_00;
7182 options.max_protocol = PROTOCOL_SMB3_02;
7184 status = smb2_connect(torture,
7185 host,
7186 lpcfg_smb_ports(torture->lp_ctx),
7187 share,
7188 lpcfg_resolve_context(torture->lp_ctx),
7189 credentials,
7190 &tree,
7191 torture->ev,
7192 &options,
7193 lpcfg_socket_options(torture->lp_ctx),
7194 lpcfg_gensec_settings(torture, torture->lp_ctx)
7196 torture_assert_ntstatus_ok(torture, status, "smb2_connect options failed");
7197 session = tree->session;
7198 transport = session->transport;
7200 timeout_msec = tree->session->transport->options.request_timeout * 1000;
7202 subreq = smb2cli_validate_negotiate_info_send(torture,
7203 torture->ev,
7204 transport->conn,
7205 timeout_msec,
7206 session->smbXcli,
7207 tree->smbXcli);
7208 torture_assert(torture,
7209 tevent_req_poll_ntstatus(subreq, torture->ev, &status),
7210 "tevent_req_poll_ntstatus");
7211 status = smb2cli_validate_negotiate_info_recv(subreq);
7212 torture_assert_ntstatus_ok(torture, status, "smb2cli_validate_negotiate_info");
7214 noperm_unc = talloc_asprintf(torture, "\\\\%s\\%s", host, noperm_share);
7215 torture_assert(torture, noperm_unc != NULL, "talloc_asprintf");
7217 noperm_tree = smb2_tree_init(session, torture, false);
7218 torture_assert(torture, noperm_tree != NULL, "smb2_tree_init");
7220 status = smb2cli_raw_tcon(transport->conn,
7221 SMB2_HDR_FLAG_SIGNED,
7222 0, /* clear_flags */
7223 timeout_msec,
7224 session->smbXcli,
7225 noperm_tree->smbXcli,
7226 noperm_flags,
7227 noperm_unc);
7228 if (NT_STATUS_EQUAL(status, NT_STATUS_BAD_NETWORK_NAME)) {
7229 torture_skip(torture, talloc_asprintf(torture,
7230 "noperm_unc[%s] %s",
7231 noperm_unc, nt_errstr(status)));
7233 torture_assert_ntstatus_ok(torture, status,
7234 talloc_asprintf(torture,
7235 "smb2cli_tcon(%s)",
7236 noperm_unc));
7238 subreq = smb2cli_validate_negotiate_info_send(torture,
7239 torture->ev,
7240 transport->conn,
7241 timeout_msec,
7242 session->smbXcli,
7243 noperm_tree->smbXcli);
7244 torture_assert(torture,
7245 tevent_req_poll_ntstatus(subreq, torture->ev, &status),
7246 "tevent_req_poll_ntstatus");
7247 status = smb2cli_validate_negotiate_info_recv(subreq);
7248 torture_assert_ntstatus_ok(torture, status, "smb2cli_validate_negotiate_info noperm");
7250 return true;
7254 basic regression test for BUG 14788,
7255 with FSCTL_QUERY_NETWORK_INTERFACE_INFO
7256 https://bugzilla.samba.org/show_bug.cgi?id=14788
7258 static bool test_ioctl_bug14788_NETWORK_INTERFACE(struct torture_context *torture,
7259 struct smb2_tree *tree0)
7261 const char *host = torture_setting_string(torture, "host", NULL);
7262 const char *share = torture_setting_string(torture, "share", NULL);
7263 const char *noperm_share = torture_setting_string(torture, "noperm_share", "noperm");
7264 struct smb2_transport *transport0 = tree0->session->transport;
7265 struct smbcli_options options;
7266 struct smb2_transport *transport = NULL;
7267 struct smb2_tree *tree = NULL;
7268 struct smb2_session *session = NULL;
7269 uint16_t noperm_flags = 0;
7270 const char *noperm_unc = NULL;
7271 struct smb2_tree *noperm_tree = NULL;
7272 uint32_t timeout_msec;
7273 DATA_BLOB out_input_buffer = data_blob_null;
7274 DATA_BLOB out_output_buffer = data_blob_null;
7275 struct cli_credentials *credentials = samba_cmdline_get_creds();
7276 NTSTATUS status;
7278 if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_00) {
7279 torture_skip(torture, "Can't test without SMB 3 support");
7282 options = transport0->options;
7283 options.client_guid = GUID_random();
7284 options.min_protocol = PROTOCOL_SMB3_00;
7285 options.max_protocol = PROTOCOL_SMB3_02;
7287 status = smb2_connect(torture,
7288 host,
7289 lpcfg_smb_ports(torture->lp_ctx),
7290 share,
7291 lpcfg_resolve_context(torture->lp_ctx),
7292 credentials,
7293 &tree,
7294 torture->ev,
7295 &options,
7296 lpcfg_socket_options(torture->lp_ctx),
7297 lpcfg_gensec_settings(torture, torture->lp_ctx)
7299 torture_assert_ntstatus_ok(torture, status, "smb2_connect options failed");
7300 session = tree->session;
7301 transport = session->transport;
7303 timeout_msec = tree->session->transport->options.request_timeout * 1000;
7306 * A valid FSCTL_QUERY_NETWORK_INTERFACE_INFO
7308 status = smb2cli_ioctl(transport->conn,
7309 timeout_msec,
7310 session->smbXcli,
7311 tree->smbXcli,
7312 UINT64_MAX, /* in_fid_persistent */
7313 UINT64_MAX, /* in_fid_volatile */
7314 FSCTL_QUERY_NETWORK_INTERFACE_INFO,
7315 0, /* in_max_input_length */
7316 NULL, /* in_input_buffer */
7317 UINT16_MAX, /* in_max_output_length */
7318 NULL, /* in_output_buffer */
7319 SMB2_IOCTL_FLAG_IS_FSCTL,
7320 torture,
7321 &out_input_buffer,
7322 &out_output_buffer);
7323 torture_assert_ntstatus_ok(torture, status,
7324 "FSCTL_QUERY_NETWORK_INTERFACE_INFO");
7327 * An invalid FSCTL_QUERY_NETWORK_INTERFACE_INFO,
7328 * with file_id_* is being UINT64_MAX and
7329 * in_max_output_length = 1.
7331 * This demonstrates NT_STATUS_BUFFER_TOO_SMALL
7332 * if the server is not able to return the
7333 * whole response buffer to the client.
7335 status = smb2cli_ioctl(transport->conn,
7336 timeout_msec,
7337 session->smbXcli,
7338 tree->smbXcli,
7339 UINT64_MAX, /* in_fid_persistent */
7340 UINT64_MAX, /* in_fid_volatile */
7341 FSCTL_QUERY_NETWORK_INTERFACE_INFO,
7342 0, /* in_max_input_length */
7343 NULL, /* in_input_buffer */
7344 1, /* in_max_output_length */
7345 NULL, /* in_output_buffer */
7346 SMB2_IOCTL_FLAG_IS_FSCTL,
7347 torture,
7348 &out_input_buffer,
7349 &out_output_buffer);
7350 torture_assert_ntstatus_equal(torture, status,
7351 NT_STATUS_BUFFER_TOO_SMALL,
7352 "FSCTL_QUERY_NETWORK_INTERFACE_INFO");
7355 * An invalid FSCTL_QUERY_NETWORK_INTERFACE_INFO,
7356 * with file_id_* not being UINT64_MAX.
7358 * This gives INVALID_PARAMETER instead
7359 * of FILE_CLOSED.
7361 status = smb2cli_ioctl(transport->conn,
7362 timeout_msec,
7363 session->smbXcli,
7364 tree->smbXcli,
7365 INT64_MAX, /* in_fid_persistent */
7366 INT64_MAX, /* in_fid_volatile */
7367 FSCTL_QUERY_NETWORK_INTERFACE_INFO,
7368 0, /* in_max_input_length */
7369 NULL, /* in_input_buffer */
7370 UINT16_MAX, /* in_max_output_length */
7371 NULL, /* in_output_buffer */
7372 SMB2_IOCTL_FLAG_IS_FSCTL,
7373 torture,
7374 &out_input_buffer,
7375 &out_output_buffer);
7376 torture_assert_ntstatus_equal(torture, status,
7377 NT_STATUS_INVALID_PARAMETER,
7378 "FSCTL_QUERY_NETWORK_INTERFACE_INFO");
7381 * An invalid FSCTL_QUERY_NETWORK_INTERFACE_INFO,
7382 * with file_id_* not being UINT64_MAX and
7383 * in_max_output_length = 1.
7385 * This proves INVALID_PARAMETER instead
7386 * of BUFFER_TOO_SMALL.
7388 status = smb2cli_ioctl(transport->conn,
7389 timeout_msec,
7390 session->smbXcli,
7391 tree->smbXcli,
7392 INT64_MAX, /* in_fid_persistent */
7393 INT64_MAX, /* in_fid_volatile */
7394 FSCTL_QUERY_NETWORK_INTERFACE_INFO,
7395 0, /* in_max_input_length */
7396 NULL, /* in_input_buffer */
7397 1, /* in_max_output_length */
7398 NULL, /* in_output_buffer */
7399 SMB2_IOCTL_FLAG_IS_FSCTL,
7400 torture,
7401 &out_input_buffer,
7402 &out_output_buffer);
7403 torture_assert_ntstatus_equal(torture, status,
7404 NT_STATUS_INVALID_PARAMETER,
7405 "FSCTL_QUERY_NETWORK_INTERFACE_INFO");
7407 noperm_unc = talloc_asprintf(torture, "\\\\%s\\%s", host, noperm_share);
7408 torture_assert(torture, noperm_unc != NULL, "talloc_asprintf");
7410 noperm_tree = smb2_tree_init(session, torture, false);
7411 torture_assert(torture, noperm_tree != NULL, "smb2_tree_init");
7413 status = smb2cli_raw_tcon(transport->conn,
7414 SMB2_HDR_FLAG_SIGNED,
7415 0, /* clear_flags */
7416 timeout_msec,
7417 session->smbXcli,
7418 noperm_tree->smbXcli,
7419 noperm_flags,
7420 noperm_unc);
7421 if (NT_STATUS_EQUAL(status, NT_STATUS_BAD_NETWORK_NAME)) {
7422 torture_skip(torture, talloc_asprintf(torture,
7423 "noperm_unc[%s] %s",
7424 noperm_unc, nt_errstr(status)));
7426 torture_assert_ntstatus_ok(torture, status,
7427 talloc_asprintf(torture,
7428 "smb2cli_tcon(%s)",
7429 noperm_unc));
7432 * A valid FSCTL_QUERY_NETWORK_INTERFACE_INFO
7434 status = smb2cli_ioctl(transport->conn,
7435 timeout_msec,
7436 session->smbXcli,
7437 noperm_tree->smbXcli,
7438 UINT64_MAX, /* in_fid_persistent */
7439 UINT64_MAX, /* in_fid_volatile */
7440 FSCTL_QUERY_NETWORK_INTERFACE_INFO,
7441 0, /* in_max_input_length */
7442 NULL, /* in_input_buffer */
7443 UINT16_MAX, /* in_max_output_length */
7444 NULL, /* in_output_buffer */
7445 SMB2_IOCTL_FLAG_IS_FSCTL,
7446 torture,
7447 &out_input_buffer,
7448 &out_output_buffer);
7449 torture_assert_ntstatus_ok(torture, status,
7450 "FSCTL_QUERY_NETWORK_INTERFACE_INFO");
7453 * An invalid FSCTL_QUERY_NETWORK_INTERFACE_INFO,
7454 * with file_id_* is being UINT64_MAX and
7455 * in_max_output_length = 1.
7457 * This demonstrates NT_STATUS_BUFFER_TOO_SMALL
7458 * if the server is not able to return the
7459 * whole response buffer to the client.
7461 status = smb2cli_ioctl(transport->conn,
7462 timeout_msec,
7463 session->smbXcli,
7464 noperm_tree->smbXcli,
7465 UINT64_MAX, /* in_fid_persistent */
7466 UINT64_MAX, /* in_fid_volatile */
7467 FSCTL_QUERY_NETWORK_INTERFACE_INFO,
7468 0, /* in_max_input_length */
7469 NULL, /* in_input_buffer */
7470 1, /* in_max_output_length */
7471 NULL, /* in_output_buffer */
7472 SMB2_IOCTL_FLAG_IS_FSCTL,
7473 torture,
7474 &out_input_buffer,
7475 &out_output_buffer);
7476 torture_assert_ntstatus_equal(torture, status,
7477 NT_STATUS_BUFFER_TOO_SMALL,
7478 "FSCTL_QUERY_NETWORK_INTERFACE_INFO");
7481 * An invalid FSCTL_QUERY_NETWORK_INTERFACE_INFO,
7482 * with file_id_* not being UINT64_MAX.
7484 * This gives INVALID_PARAMETER instead
7485 * of FILE_CLOSED.
7487 status = smb2cli_ioctl(transport->conn,
7488 timeout_msec,
7489 session->smbXcli,
7490 noperm_tree->smbXcli,
7491 INT64_MAX, /* in_fid_persistent */
7492 INT64_MAX, /* in_fid_volatile */
7493 FSCTL_QUERY_NETWORK_INTERFACE_INFO,
7494 0, /* in_max_input_length */
7495 NULL, /* in_input_buffer */
7496 UINT16_MAX, /* in_max_output_length */
7497 NULL, /* in_output_buffer */
7498 SMB2_IOCTL_FLAG_IS_FSCTL,
7499 torture,
7500 &out_input_buffer,
7501 &out_output_buffer);
7502 torture_assert_ntstatus_equal(torture, status,
7503 NT_STATUS_INVALID_PARAMETER,
7504 "FSCTL_QUERY_NETWORK_INTERFACE_INFO");
7507 * An invalid FSCTL_QUERY_NETWORK_INTERFACE_INFO,
7508 * with file_id_* not being UINT64_MAX and
7509 * in_max_output_length = 1.
7511 * This proves INVALID_PARAMETER instead
7512 * of BUFFER_TOO_SMALL.
7514 status = smb2cli_ioctl(transport->conn,
7515 timeout_msec,
7516 session->smbXcli,
7517 noperm_tree->smbXcli,
7518 INT64_MAX, /* in_fid_persistent */
7519 INT64_MAX, /* in_fid_volatile */
7520 FSCTL_QUERY_NETWORK_INTERFACE_INFO,
7521 0, /* in_max_input_length */
7522 NULL, /* in_input_buffer */
7523 1, /* in_max_output_length */
7524 NULL, /* in_output_buffer */
7525 SMB2_IOCTL_FLAG_IS_FSCTL,
7526 torture,
7527 &out_input_buffer,
7528 &out_output_buffer);
7529 torture_assert_ntstatus_equal(torture, status,
7530 NT_STATUS_INVALID_PARAMETER,
7531 "FSCTL_QUERY_NETWORK_INTERFACE_INFO");
7533 return true;
7537 * basic regression test for BUG 15664
7538 * https://bugzilla.samba.org/show_bug.cgi?id=15664
7540 static bool test_ioctl_copy_chunk_bug15644(struct torture_context *torture,
7541 struct smb2_tree *tree)
7543 struct smb2_handle dest_h;
7544 NTSTATUS status;
7545 union smb_ioctl ioctl;
7546 TALLOC_CTX *tmp_ctx = talloc_new(tree);
7547 struct srv_copychunk chunk;
7548 struct srv_copychunk_copy cc_copy;
7549 enum ndr_err_code ndr_ret;
7550 bool ok;
7552 ok = test_setup_create_fill(torture,
7553 tree,
7554 tmp_ctx,
7555 FNAME2,
7556 &dest_h,
7558 SEC_RIGHTS_FILE_ALL,
7559 FILE_ATTRIBUTE_NORMAL);
7560 torture_assert(torture, ok, "dest file create fill");
7562 ZERO_STRUCT(ioctl);
7563 ioctl.smb2.level = RAW_IOCTL_SMB2;
7564 ioctl.smb2.in.file.handle = dest_h;
7565 ioctl.smb2.in.function = FSCTL_SRV_COPYCHUNK;
7566 ioctl.smb2.in.max_output_response = sizeof(struct srv_copychunk_rsp);
7567 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
7569 ZERO_STRUCT(chunk);
7570 ZERO_STRUCT(cc_copy);
7571 /* overwrite the resume key with a bogus value */
7572 memcpy(cc_copy.source_key, "deadbeefdeadbeefdeadbeef", 24);
7573 cc_copy.chunk_count = 1;
7574 cc_copy.chunks = &chunk;
7575 cc_copy.chunks[0].source_off = 0;
7576 cc_copy.chunks[0].target_off = 0;
7577 cc_copy.chunks[0].length = 4096;
7579 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
7580 &cc_copy,
7581 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
7582 torture_assert_ndr_success(torture, ndr_ret,
7583 "ndr_push_srv_copychunk_copy");
7585 /* Server 2k12 returns NT_STATUS_OBJECT_NAME_NOT_FOUND */
7586 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
7587 torture_assert_ntstatus_equal(torture, status,
7588 NT_STATUS_OBJECT_NAME_NOT_FOUND,
7589 "FSCTL_SRV_COPYCHUNK");
7591 status = smb2_util_close(tree, dest_h);
7592 torture_assert_ntstatus_ok(torture, status, "close");
7594 talloc_free(tmp_ctx);
7595 return true;
7599 * testing of SMB2 ioctls
7601 struct torture_suite *torture_smb2_ioctl_init(TALLOC_CTX *ctx)
7603 struct torture_suite *suite = torture_suite_create(ctx, "ioctl");
7604 struct torture_suite *bug14788 = torture_suite_create(ctx, "bug14788");
7606 torture_suite_add_1smb2_test(suite, "shadow_copy",
7607 test_ioctl_get_shadow_copy);
7608 torture_suite_add_1smb2_test(suite, "req_resume_key",
7609 test_ioctl_req_resume_key);
7610 torture_suite_add_1smb2_test(suite, "req_two_resume_keys",
7611 test_ioctl_req_two_resume_keys);
7612 torture_suite_add_1smb2_test(suite, "copy_chunk_simple",
7613 test_ioctl_copy_chunk_simple);
7614 torture_suite_add_1smb2_test(suite, "copy_chunk_multi",
7615 test_ioctl_copy_chunk_multi);
7616 torture_suite_add_1smb2_test(suite, "copy_chunk_tiny",
7617 test_ioctl_copy_chunk_tiny);
7618 torture_suite_add_1smb2_test(suite, "copy_chunk_overwrite",
7619 test_ioctl_copy_chunk_over);
7620 torture_suite_add_1smb2_test(suite, "copy_chunk_append",
7621 test_ioctl_copy_chunk_append);
7622 torture_suite_add_1smb2_test(suite, "copy_chunk_limits",
7623 test_ioctl_copy_chunk_limits);
7624 torture_suite_add_1smb2_test(suite, "copy_chunk_src_lock",
7625 test_ioctl_copy_chunk_src_lck);
7626 torture_suite_add_1smb2_test(suite, "copy_chunk_dest_lock",
7627 test_ioctl_copy_chunk_dest_lck);
7628 torture_suite_add_1smb2_test(suite, "copy_chunk_bad_key",
7629 test_ioctl_copy_chunk_bad_key);
7630 torture_suite_add_1smb2_test(suite, "copy_chunk_bug15644",
7631 test_ioctl_copy_chunk_bug15644);
7632 torture_suite_add_1smb2_test(suite, "copy_chunk_src_is_dest",
7633 test_ioctl_copy_chunk_src_is_dest);
7634 torture_suite_add_1smb2_test(suite, "copy_chunk_src_is_dest_overlap",
7635 test_ioctl_copy_chunk_src_is_dest_overlap);
7636 torture_suite_add_1smb2_test(suite, "copy_chunk_bad_access",
7637 test_ioctl_copy_chunk_bad_access);
7638 torture_suite_add_1smb2_test(suite, "copy_chunk_write_access",
7639 test_ioctl_copy_chunk_write_access);
7640 torture_suite_add_1smb2_test(suite, "copy_chunk_src_exceed",
7641 test_ioctl_copy_chunk_src_exceed);
7642 torture_suite_add_1smb2_test(suite, "copy_chunk_src_exceed_multi",
7643 test_ioctl_copy_chunk_src_exceed_multi);
7644 torture_suite_add_1smb2_test(suite, "copy_chunk_sparse_dest",
7645 test_ioctl_copy_chunk_sparse_dest);
7646 torture_suite_add_1smb2_test(suite, "copy_chunk_max_output_sz",
7647 test_ioctl_copy_chunk_max_output_sz);
7648 torture_suite_add_1smb2_test(suite, "copy_chunk_zero_length",
7649 test_ioctl_copy_chunk_zero_length);
7650 torture_suite_add_1smb2_test(suite, "copy-chunk streams",
7651 test_copy_chunk_streams);
7652 torture_suite_add_1smb2_test(suite, "copy_chunk_across_shares",
7653 test_copy_chunk_across_shares);
7654 torture_suite_add_1smb2_test(suite, "copy_chunk_across_shares2",
7655 test_copy_chunk_across_shares2);
7656 torture_suite_add_1smb2_test(suite, "copy_chunk_across_shares3",
7657 test_copy_chunk_across_shares3);
7658 torture_suite_add_1smb2_test(suite, "compress_file_flag",
7659 test_ioctl_compress_file_flag);
7660 torture_suite_add_1smb2_test(suite, "compress_dir_inherit",
7661 test_ioctl_compress_dir_inherit);
7662 torture_suite_add_1smb2_test(suite, "compress_invalid_format",
7663 test_ioctl_compress_invalid_format);
7664 torture_suite_add_1smb2_test(suite, "compress_invalid_buf",
7665 test_ioctl_compress_invalid_buf);
7666 torture_suite_add_1smb2_test(suite, "compress_query_file_attr",
7667 test_ioctl_compress_query_file_attr);
7668 torture_suite_add_1smb2_test(suite, "compress_create_with_attr",
7669 test_ioctl_compress_create_with_attr);
7670 torture_suite_add_1smb2_test(suite, "compress_inherit_disable",
7671 test_ioctl_compress_inherit_disable);
7672 torture_suite_add_1smb2_test(suite, "compress_set_file_attr",
7673 test_ioctl_compress_set_file_attr);
7674 torture_suite_add_1smb2_test(suite, "compress_perms",
7675 test_ioctl_compress_perms);
7676 torture_suite_add_1smb2_test(suite, "compress_notsup_get",
7677 test_ioctl_compress_notsup_get);
7678 torture_suite_add_1smb2_test(suite, "compress_notsup_set",
7679 test_ioctl_compress_notsup_set);
7680 torture_suite_add_1smb2_test(suite, "network_interface_info",
7681 test_ioctl_network_interface_info);
7682 torture_suite_add_1smb2_test(suite, "sparse_file_flag",
7683 test_ioctl_sparse_file_flag);
7684 torture_suite_add_1smb2_test(suite, "sparse_file_attr",
7685 test_ioctl_sparse_file_attr);
7686 torture_suite_add_1smb2_test(suite, "sparse_dir_flag",
7687 test_ioctl_sparse_dir_flag);
7688 torture_suite_add_1smb2_test(suite, "sparse_set_nobuf",
7689 test_ioctl_sparse_set_nobuf);
7690 torture_suite_add_1smb2_test(suite, "sparse_set_oversize",
7691 test_ioctl_sparse_set_oversize);
7692 torture_suite_add_1smb2_test(suite, "sparse_qar",
7693 test_ioctl_sparse_qar);
7694 torture_suite_add_1smb2_test(suite, "sparse_qar_malformed",
7695 test_ioctl_sparse_qar_malformed);
7696 torture_suite_add_1smb2_test(suite, "sparse_qar_truncated",
7697 test_ioctl_sparse_qar_truncated);
7698 torture_suite_add_1smb2_test(suite, "sparse_punch",
7699 test_ioctl_sparse_punch);
7700 torture_suite_add_1smb2_test(suite, "sparse_hole_dealloc",
7701 test_ioctl_sparse_hole_dealloc);
7702 torture_suite_add_1smb2_test(suite, "sparse_compressed",
7703 test_ioctl_sparse_compressed);
7704 torture_suite_add_1smb2_test(suite, "sparse_copy_chunk",
7705 test_ioctl_sparse_copy_chunk);
7706 torture_suite_add_1smb2_test(suite, "sparse_punch_invalid",
7707 test_ioctl_sparse_punch_invalid);
7708 torture_suite_add_1smb2_test(suite, "sparse_perms",
7709 test_ioctl_sparse_perms);
7710 torture_suite_add_1smb2_test(suite, "sparse_lock",
7711 test_ioctl_sparse_lck);
7712 torture_suite_add_1smb2_test(suite, "sparse_qar_ob1",
7713 test_ioctl_sparse_qar_ob1);
7714 torture_suite_add_1smb2_test(suite, "sparse_qar_multi",
7715 test_ioctl_sparse_qar_multi);
7716 torture_suite_add_1smb2_test(suite, "sparse_qar_overflow",
7717 test_ioctl_sparse_qar_overflow);
7718 torture_suite_add_1smb2_test(suite, "trim_simple",
7719 test_ioctl_trim_simple);
7720 torture_suite_add_1smb2_test(suite, "dup_extents_simple",
7721 test_ioctl_dup_extents_simple);
7722 torture_suite_add_1smb2_test(suite, "dup_extents_len_beyond_dest",
7723 test_ioctl_dup_extents_len_beyond_dest);
7724 torture_suite_add_1smb2_test(suite, "dup_extents_len_beyond_src",
7725 test_ioctl_dup_extents_len_beyond_src);
7726 torture_suite_add_1smb2_test(suite, "dup_extents_len_zero",
7727 test_ioctl_dup_extents_len_zero);
7728 torture_suite_add_1smb2_test(suite, "dup_extents_sparse_src",
7729 test_ioctl_dup_extents_sparse_src);
7730 torture_suite_add_1smb2_test(suite, "dup_extents_sparse_dest",
7731 test_ioctl_dup_extents_sparse_dest);
7732 torture_suite_add_1smb2_test(suite, "dup_extents_sparse_both",
7733 test_ioctl_dup_extents_sparse_both);
7734 torture_suite_add_1smb2_test(suite, "dup_extents_src_is_dest",
7735 test_ioctl_dup_extents_src_is_dest);
7736 torture_suite_add_1smb2_test(suite, "dup_extents_src_is_dest_overlap",
7737 test_ioctl_dup_extents_src_is_dest_overlap);
7738 torture_suite_add_1smb2_test(suite, "dup_extents_compressed_src",
7739 test_ioctl_dup_extents_compressed_src);
7740 torture_suite_add_1smb2_test(suite, "dup_extents_compressed_dest",
7741 test_ioctl_dup_extents_compressed_dest);
7742 torture_suite_add_1smb2_test(suite, "dup_extents_bad_handle",
7743 test_ioctl_dup_extents_bad_handle);
7744 torture_suite_add_1smb2_test(suite, "dup_extents_src_lock",
7745 test_ioctl_dup_extents_src_lck);
7746 torture_suite_add_1smb2_test(suite, "dup_extents_dest_lock",
7747 test_ioctl_dup_extents_dest_lck);
7748 torture_suite_add_1smb2_test(suite, "bug14607",
7749 test_ioctl_bug14607);
7750 torture_suite_add_1smb2_test(suite, "bug14769",
7751 test_ioctl_bug14769);
7753 torture_suite_add_1smb2_test(bug14788, "VALIDATE_NEGOTIATE",
7754 test_ioctl_bug14788_VALIDATE_NEGOTIATE);
7755 torture_suite_add_1smb2_test(bug14788, "NETWORK_INTERFACE",
7756 test_ioctl_bug14788_NETWORK_INTERFACE);
7757 torture_suite_add_suite(suite, bug14788);
7759 suite->description = talloc_strdup(suite, "SMB2-IOCTL tests");
7761 return suite;