s3: smbd: Add test to show smbd crashes when doing an FSCTL on a named stream handle.
[Samba.git] / source4 / torture / smb2 / ioctl.c
blob6ceaccfc7ca4f8d5b9fbe41338f32fc9b8146e82
1 /*
2 Unix SMB/CIFS implementation.
4 test suite for SMB2 ioctl operations
6 Copyright (C) David Disseldorp 2011-2016
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "includes.h"
23 #include "librpc/gen_ndr/security.h"
24 #include "libcli/smb2/smb2.h"
25 #include "libcli/smb2/smb2_calls.h"
26 #include "torture/torture.h"
27 #include "torture/smb2/proto.h"
28 #include "../libcli/smb/smbXcli_base.h"
29 #include "librpc/gen_ndr/ndr_ioctl.h"
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 * specifc 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 bool test_ioctl_alternate_data_stream(struct torture_context *tctx)
3843 bool ret = false;
3844 const char *fname = DNAME "\\test_stream_ioctl_dir";
3845 const char *sname = DNAME "\\test_stream_ioctl_dir:stream";
3846 NTSTATUS status;
3847 struct smb2_create create = {};
3848 struct smb2_tree *tree = NULL;
3849 struct smb2_handle h1 = {{0}};
3850 union smb_ioctl ioctl;
3852 if (!torture_smb2_connection(tctx, &tree)) {
3853 torture_comment(tctx, "Initializing smb2 connection failed.\n");
3854 return false;
3857 smb2_deltree(tree, DNAME);
3859 status = torture_smb2_testdir(tree, DNAME, &h1);
3860 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
3861 "torture_smb2_testdir failed\n");
3863 status = smb2_util_close(tree, h1);
3864 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
3865 "smb2_util_close failed\n");
3866 create = (struct smb2_create) {
3867 .in.desired_access = SEC_FILE_ALL,
3868 .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
3869 .in.file_attributes = FILE_ATTRIBUTE_HIDDEN,
3870 .in.create_disposition = NTCREATEX_DISP_CREATE,
3871 .in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION,
3872 .in.fname = fname,
3875 status = smb2_create(tree, tctx, &create);
3876 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
3877 "smb2_create failed\n");
3879 h1 = create.out.file.handle;
3880 status = smb2_util_close(tree, h1);
3881 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
3882 "smb2_util_close failed\n");
3884 create = (struct smb2_create) {
3885 .in.desired_access = SEC_FILE_ALL,
3886 .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
3887 .in.file_attributes = FILE_ATTRIBUTE_NORMAL,
3888 .in.create_disposition = NTCREATEX_DISP_CREATE,
3889 .in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION,
3890 .in.fname = sname,
3892 status = smb2_create(tree, tctx, &create);
3893 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
3894 "smb2_create failed\n");
3895 h1 = create.out.file.handle;
3897 ZERO_STRUCT(ioctl);
3898 ioctl.smb2.level = RAW_IOCTL_SMB2;
3899 ioctl.smb2.in.file.handle = h1;
3900 ioctl.smb2.in.function = FSCTL_CREATE_OR_GET_OBJECT_ID,
3901 ioctl.smb2.in.max_output_response = 64;
3902 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3903 status = smb2_ioctl(tree, tctx, &ioctl.smb2);
3904 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
3905 "smb2_ioctl failed\n");
3906 ret = true;
3908 done:
3910 smb2_util_close(tree, h1);
3911 smb2_deltree(tree, DNAME);
3912 return ret;
3916 * 2.3.57 FSCTL_SET_ZERO_DATA Request
3918 * How an implementation zeros data within a file is implementation-dependent.
3919 * A file system MAY choose to deallocate regions of disk space that have been
3920 * zeroed.<50>
3921 * <50>
3922 * ... NTFS might deallocate disk space in the file if the file is stored on an
3923 * NTFS volume, and the file is sparse or compressed. It will free any allocated
3924 * space in chunks of 64 kilobytes that begin at an offset that is a multiple of
3925 * 64 kilobytes. Other bytes in the file (prior to the first freed 64-kilobyte
3926 * chunk and after the last freed 64-kilobyte chunk) will be zeroed but not
3927 * deallocated.
3929 static NTSTATUS test_ioctl_zdata_req(struct torture_context *torture,
3930 TALLOC_CTX *mem_ctx,
3931 struct smb2_tree *tree,
3932 struct smb2_handle fh,
3933 int64_t off,
3934 int64_t beyond_final_zero)
3936 union smb_ioctl ioctl;
3937 NTSTATUS status;
3938 enum ndr_err_code ndr_ret;
3939 struct file_zero_data_info zdata_info;
3940 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
3941 if (tmp_ctx == NULL) {
3942 return NT_STATUS_NO_MEMORY;
3945 ZERO_STRUCT(ioctl);
3946 ioctl.smb2.level = RAW_IOCTL_SMB2;
3947 ioctl.smb2.in.file.handle = fh;
3948 ioctl.smb2.in.function = FSCTL_SET_ZERO_DATA;
3949 ioctl.smb2.in.max_output_response = 0;
3950 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3952 zdata_info.file_off = off;
3953 zdata_info.beyond_final_zero = beyond_final_zero;
3955 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
3956 &zdata_info,
3957 (ndr_push_flags_fn_t)ndr_push_file_zero_data_info);
3958 if (ndr_ret != NDR_ERR_SUCCESS) {
3959 status = NT_STATUS_UNSUCCESSFUL;
3960 goto err_out;
3963 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3964 if (!NT_STATUS_IS_OK(status)) {
3965 goto err_out;
3968 status = NT_STATUS_OK;
3969 err_out:
3970 talloc_free(tmp_ctx);
3971 return status;
3974 bool test_ioctl_zero_data(struct torture_context *tctx)
3976 bool ret = true;
3977 int offset, beyond_final_zero;
3978 const char *filename;
3979 NTSTATUS status;
3980 struct smb2_create create = { };
3981 struct smb2_tree *tree = NULL;
3983 offset = torture_setting_int(tctx, "offset", -1);
3985 if (offset < 0) {
3986 torture_fail(tctx, "Need to provide non-negative offset "
3987 "through --option=torture:offset=NNN\n");
3988 return false;
3991 beyond_final_zero = torture_setting_int(tctx, "beyond_final_zero",
3992 -1);
3993 if (beyond_final_zero < 0) {
3994 torture_fail(tctx, "Need to provide non-negative "
3995 "'beyond final zero' through "
3996 "--option=torture:beyond_final_zero=NNN\n");
3997 return false;
3999 filename = torture_setting_string(tctx, "filename", NULL);
4000 if (filename == NULL) {
4001 torture_fail(tctx, "Need to provide filename through "
4002 "--option=torture:filename=testfile\n");
4003 return false;
4006 if (!torture_smb2_connection(tctx, &tree)) {
4007 torture_comment(tctx, "Initializing smb2 connection failed.\n");
4008 return false;
4011 create.in.desired_access = SEC_RIGHTS_DIR_ALL;
4012 create.in.create_options = 0;
4013 create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
4014 create.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
4015 NTCREATEX_SHARE_ACCESS_WRITE |
4016 NTCREATEX_SHARE_ACCESS_DELETE;
4017 create.in.create_disposition = NTCREATEX_DISP_OPEN;
4018 create.in.fname = filename;
4020 status = smb2_create(tree, tctx, &create);
4021 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
4022 "CREATE failed.\n");
4024 status = test_ioctl_zdata_req(tctx, tctx, tree,
4025 create.out.file.handle,
4026 offset,
4027 beyond_final_zero);
4028 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
4029 "FSCTL_ZERO_DATA failed.\n");
4031 done:
4032 return ret;
4035 static bool test_ioctl_sparse_punch(struct torture_context *torture,
4036 struct smb2_tree *tree)
4038 struct smb2_handle fh;
4039 NTSTATUS status;
4040 TALLOC_CTX *tmp_ctx = talloc_new(tree);
4041 bool ok;
4042 bool is_sparse;
4043 struct file_alloced_range_buf *far_rsp = NULL;
4044 uint64_t far_count = 0;
4046 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4047 FNAME, &fh, 4096, SEC_RIGHTS_FILE_ALL,
4048 FILE_ATTRIBUTE_NORMAL);
4049 torture_assert(torture, ok, "setup file");
4051 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
4052 FILE_SUPPORTS_SPARSE_FILES, &ok);
4053 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4054 if (!ok) {
4055 smb2_util_close(tree, fh);
4056 torture_skip(torture, "Sparse files not supported\n");
4059 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4060 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4061 torture_assert(torture, !is_sparse, "sparse attr before set");
4063 /* zero (hole-punch) the data, without sparse flag */
4064 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4065 0, /* off */
4066 4096); /* beyond_final_zero */
4067 torture_assert_ntstatus_ok(torture, status, "zero_data");
4069 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4070 0, /* off */
4071 4096, /* len */
4072 &far_rsp,
4073 &far_count);
4074 torture_assert_ntstatus_ok(torture, status,
4075 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4076 torture_assert_u64_equal(torture, far_count, 1,
4077 "unexpected response len");
4079 /* expect fully allocated */
4080 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4081 "unexpected far off");
4082 torture_assert_u64_equal(torture, far_rsp[0].len, 4096,
4083 "unexpected far len");
4084 /* check that the data is now zeroed */
4085 ok = check_zero(torture, tree, tmp_ctx, fh, 0, 4096);
4086 torture_assert(torture, ok, "non-sparse zeroed range");
4088 /* set sparse */
4089 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4090 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4092 /* still fully allocated on NTFS, see note below for Samba */
4093 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4094 0, /* off */
4095 4096, /* len */
4096 &far_rsp,
4097 &far_count);
4098 torture_assert_ntstatus_ok(torture, status,
4099 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4101 * FS specific: Samba uses PUNCH_HOLE to zero the range, and
4102 * subsequently uses fallocate() to allocate the punched range if the
4103 * file is marked non-sparse and "strict allocate" is enabled. In both
4104 * cases, the zeroed range will not be detected by SEEK_DATA, so the
4105 * range won't be present in QAR responses until the file is marked
4106 * non-sparse again.
4108 if (far_count == 0) {
4109 torture_comment(torture, "non-sparse zeroed range disappeared "
4110 "after marking sparse\n");
4111 } else {
4112 /* NTFS: range remains fully allocated */
4113 torture_assert_u64_equal(torture, far_count, 1,
4114 "unexpected response len");
4115 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4116 "unexpected far off");
4117 torture_assert_u64_equal(torture, far_rsp[0].len, 4096,
4118 "unexpected far len");
4121 /* zero (hole-punch) the data, _with_ sparse flag */
4122 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4123 0, /* off */
4124 4096); /* beyond_final_zero */
4125 torture_assert_ntstatus_ok(torture, status, "zero_data");
4127 /* the range should no longer be alloced */
4128 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4129 0, /* off */
4130 4096, /* len */
4131 &far_rsp,
4132 &far_count);
4133 torture_assert_ntstatus_ok(torture, status,
4134 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4135 torture_assert_u64_equal(torture, far_count, 0,
4136 "unexpected response len");
4138 ok = check_zero(torture, tree, tmp_ctx, fh, 0, 4096);
4139 torture_assert(torture, ok, "sparse zeroed range");
4141 /* remove sparse flag, this should "unsparse" the zeroed range */
4142 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false);
4143 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4145 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4146 0, /* off */
4147 4096, /* len */
4148 &far_rsp,
4149 &far_count);
4150 torture_assert_ntstatus_ok(torture, status,
4151 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4152 torture_assert_u64_equal(torture, far_count, 1,
4153 "unexpected response len");
4154 /* expect fully allocated */
4155 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4156 "unexpected far off");
4157 torture_assert_u64_equal(torture, far_rsp[0].len, 4096,
4158 "unexpected far len");
4160 ok = check_zero(torture, tree, tmp_ctx, fh, 0, 4096);
4161 torture_assert(torture, ok, "sparse zeroed range");
4163 smb2_util_close(tree, fh);
4164 talloc_free(tmp_ctx);
4165 return true;
4169 * Find the point at which a zeroed range in a sparse file is deallocated by the
4170 * underlying filesystem. NTFS on Windows Server 2012 deallocates chunks in 64k
4171 * increments. Also check whether zeroed neighbours are merged for deallocation.
4173 static bool test_ioctl_sparse_hole_dealloc(struct torture_context *torture,
4174 struct smb2_tree *tree)
4176 struct smb2_handle fh;
4177 NTSTATUS status;
4178 TALLOC_CTX *tmp_ctx = talloc_new(tree);
4179 bool ok;
4180 uint64_t file_size;
4181 uint64_t hlen;
4182 uint64_t dealloc_chunk_len = 0;
4183 struct file_alloced_range_buf *far_rsp = NULL;
4184 uint64_t far_count = 0;
4186 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4187 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
4188 FILE_ATTRIBUTE_NORMAL);
4189 torture_assert(torture, ok, "setup file 1");
4191 /* check for FS sparse file */
4192 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
4193 FILE_SUPPORTS_SPARSE_FILES, &ok);
4194 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4195 if (!ok) {
4196 smb2_util_close(tree, fh);
4197 torture_skip(torture, "Sparse files not supported\n");
4200 /* set sparse */
4201 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4202 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4204 file_size = 1024 * 1024;
4206 ok = write_pattern(torture, tree, tmp_ctx, fh,
4207 0, /* off */
4208 file_size, /* len */
4209 0); /* pattern offset */
4210 torture_assert(torture, ok, "write pattern");
4212 /* check allocated ranges, should be fully allocated */
4213 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4214 0, /* off */
4215 file_size, /* len */
4216 &far_rsp,
4217 &far_count);
4218 torture_assert_ntstatus_ok(torture, status,
4219 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4220 torture_assert_u64_equal(torture, far_count, 1,
4221 "unexpected response len");
4222 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4223 "unexpected far off");
4224 torture_assert_u64_equal(torture, far_rsp[0].len, file_size,
4225 "unexpected far len");
4227 /* punch holes in sizes of 1k increments */
4228 for (hlen = 0; hlen <= file_size; hlen += 4096) {
4230 /* punch a hole from zero to the current increment */
4231 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4232 0, /* off */
4233 hlen); /* beyond_final_zero */
4234 torture_assert_ntstatus_ok(torture, status, "zero_data");
4236 /* ensure hole is zeroed, and pattern is consistent */
4237 ok = check_zero(torture, tree, tmp_ctx, fh, 0, hlen);
4238 torture_assert(torture, ok, "sparse zeroed range");
4240 ok = check_pattern(torture, tree, tmp_ctx, fh, hlen,
4241 file_size - hlen, hlen);
4242 torture_assert(torture, ok, "allocated pattern range");
4244 /* Check allocated ranges, hole might have been deallocated */
4245 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4246 0, /* off */
4247 file_size, /* len */
4248 &far_rsp,
4249 &far_count);
4250 torture_assert_ntstatus_ok(torture, status,
4251 "FSCTL_QUERY_ALLOCATED_RANGES");
4252 if ((hlen == file_size) && (far_count == 0)) {
4253 /* hole covered entire file, deallocation occurred */
4254 dealloc_chunk_len = file_size;
4255 break;
4258 torture_assert_u64_equal(torture, far_count, 1,
4259 "unexpected response len");
4260 if (far_rsp[0].file_off != 0) {
4262 * We now know the hole punch length needed to trigger a
4263 * deallocation on this FS...
4265 dealloc_chunk_len = hlen;
4266 torture_comment(torture, "hole punch %ju@0 resulted in "
4267 "deallocation of %ju@0\n",
4268 (uintmax_t)hlen,
4269 (uintmax_t)far_rsp[0].file_off);
4270 torture_assert_u64_equal(torture,
4271 file_size - far_rsp[0].len,
4272 far_rsp[0].file_off,
4273 "invalid alloced range");
4274 break;
4278 if (dealloc_chunk_len == 0) {
4279 torture_comment(torture, "strange, this FS never deallocates"
4280 "zeroed ranges in sparse files\n");
4281 return true; /* FS specific, not a failure */
4285 * Check whether deallocation occurs when the (now known)
4286 * deallocation chunk size is punched via two ZERO_DATA requests.
4287 * I.e. Does the FS merge the two ranges and deallocate the chunk?
4288 * NTFS on Windows Server 2012 does not.
4290 ok = write_pattern(torture, tree, tmp_ctx, fh,
4291 0, /* off */
4292 file_size, /* len */
4293 0); /* pattern offset */
4294 torture_assert(torture, ok, "write pattern");
4296 /* divide dealloc chunk size by two, to use as punch length */
4297 hlen = dealloc_chunk_len >> 1;
4300 * /half of dealloc chunk size 1M\
4301 * | |
4302 * /offset 0 | /dealloc chunk size |
4303 * |------------------ |-------------------|-------------------|
4304 * | zeroed, 1st punch | zeroed, 2nd punch | existing pattern |
4306 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4307 0, /* off */
4308 hlen); /* beyond final zero */
4309 torture_assert_ntstatus_ok(torture, status, "zero_data");
4311 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4312 hlen, /* off */
4313 dealloc_chunk_len); /* beyond final */
4314 torture_assert_ntstatus_ok(torture, status, "zero_data");
4316 /* ensure holes are zeroed, and pattern is consistent */
4317 ok = check_zero(torture, tree, tmp_ctx, fh, 0, dealloc_chunk_len);
4318 torture_assert(torture, ok, "sparse zeroed range");
4320 ok = check_pattern(torture, tree, tmp_ctx, fh, dealloc_chunk_len,
4321 file_size - dealloc_chunk_len, dealloc_chunk_len);
4322 torture_assert(torture, ok, "allocated pattern range");
4324 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4325 0, /* off */
4326 file_size, /* len */
4327 &far_rsp,
4328 &far_count);
4329 torture_assert_ntstatus_ok(torture, status,
4330 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4332 if ((far_count == 0) && (dealloc_chunk_len == file_size)) {
4333 torture_comment(torture, "holes merged for deallocation of "
4334 "full file\n");
4335 return true;
4337 torture_assert_u64_equal(torture, far_count, 1,
4338 "unexpected response len");
4339 if (far_rsp[0].file_off == dealloc_chunk_len) {
4340 torture_comment(torture, "holes merged for deallocation of "
4341 "%ju chunk\n", (uintmax_t)dealloc_chunk_len);
4342 torture_assert_u64_equal(torture,
4343 file_size - far_rsp[0].len,
4344 far_rsp[0].file_off,
4345 "invalid alloced range");
4346 } else {
4347 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4348 "unexpected deallocation");
4349 torture_comment(torture, "holes not merged for deallocation\n");
4352 smb2_util_close(tree, fh);
4355 * Check whether an unwritten range is allocated when a sparse file is
4356 * written to at an offset past the dealloc chunk size:
4358 * /dealloc chunk size
4359 * /offset 0 |
4360 * |------------------ |-------------------|
4361 * | unwritten | pattern |
4363 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4364 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
4365 FILE_ATTRIBUTE_NORMAL);
4366 torture_assert(torture, ok, "setup file 1");
4368 /* set sparse */
4369 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4370 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4372 ok = write_pattern(torture, tree, tmp_ctx, fh,
4373 dealloc_chunk_len, /* off */
4374 1024, /* len */
4375 dealloc_chunk_len); /* pattern offset */
4376 torture_assert(torture, ok, "write pattern");
4378 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4379 0, /* off */
4380 dealloc_chunk_len + 1024, /* len */
4381 &far_rsp,
4382 &far_count);
4383 torture_assert_ntstatus_ok(torture, status,
4384 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4385 torture_assert_u64_equal(torture, far_count, 1,
4386 "unexpected response len");
4387 if (far_rsp[0].file_off == 0) {
4388 torture_assert_u64_equal(torture, far_rsp[0].len,
4389 dealloc_chunk_len + 1024,
4390 "unexpected far len");
4391 torture_comment(torture, "unwritten range fully allocated\n");
4392 } else {
4393 torture_assert_u64_equal(torture, far_rsp[0].file_off, dealloc_chunk_len,
4394 "unexpected deallocation");
4395 torture_assert_u64_equal(torture, far_rsp[0].len, 1024,
4396 "unexpected far len");
4397 torture_comment(torture, "unwritten range not allocated\n");
4400 ok = check_zero(torture, tree, tmp_ctx, fh, 0, dealloc_chunk_len);
4401 torture_assert(torture, ok, "sparse zeroed range");
4403 ok = check_pattern(torture, tree, tmp_ctx, fh, dealloc_chunk_len,
4404 1024, dealloc_chunk_len);
4405 torture_assert(torture, ok, "allocated pattern range");
4407 /* unsparse, should now be fully allocated */
4408 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false);
4409 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4411 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4412 0, /* off */
4413 dealloc_chunk_len + 1024, /* len */
4414 &far_rsp,
4415 &far_count);
4416 torture_assert_ntstatus_ok(torture, status,
4417 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4418 torture_assert_u64_equal(torture, far_count, 1,
4419 "unexpected response len");
4420 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4421 "unexpected deallocation");
4422 torture_assert_u64_equal(torture, far_rsp[0].len,
4423 dealloc_chunk_len + 1024,
4424 "unexpected far len");
4426 smb2_util_close(tree, fh);
4427 talloc_free(tmp_ctx);
4428 return true;
4431 /* check whether a file with compression and sparse attrs can be deallocated */
4432 static bool test_ioctl_sparse_compressed(struct torture_context *torture,
4433 struct smb2_tree *tree)
4435 struct smb2_handle fh;
4436 NTSTATUS status;
4437 TALLOC_CTX *tmp_ctx = talloc_new(tree);
4438 bool ok;
4439 uint64_t file_size = 1024 * 1024;
4440 struct file_alloced_range_buf *far_rsp = NULL;
4441 uint64_t far_count = 0;
4443 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4444 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
4445 FILE_ATTRIBUTE_NORMAL);
4446 torture_assert(torture, ok, "setup file 1");
4448 /* check for FS sparse file and compression support */
4449 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
4450 FILE_SUPPORTS_SPARSE_FILES, &ok);
4451 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4452 if (!ok) {
4453 smb2_util_close(tree, fh);
4454 torture_skip(torture, "Sparse files not supported\n");
4457 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
4458 &ok);
4459 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4460 if (!ok) {
4461 smb2_util_close(tree, fh);
4462 torture_skip(torture, "FS compression not supported\n");
4465 /* set compression and write some data */
4466 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
4467 COMPRESSION_FORMAT_DEFAULT);
4468 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
4470 ok = write_pattern(torture, tree, tmp_ctx, fh,
4471 0, /* off */
4472 file_size, /* len */
4473 0); /* pattern offset */
4474 torture_assert(torture, ok, "write pattern");
4476 /* set sparse - now sparse and compressed */
4477 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4478 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4480 /* check allocated ranges, should be fully alloced */
4481 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4482 0, /* off */
4483 file_size, /* len */
4484 &far_rsp,
4485 &far_count);
4486 torture_assert_ntstatus_ok(torture, status,
4487 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4488 torture_assert_u64_equal(torture, far_count, 1,
4489 "unexpected response len");
4490 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4491 "unexpected far off");
4492 torture_assert_u64_equal(torture, far_rsp[0].len, file_size,
4493 "unexpected far len");
4495 /* zero (hole-punch) all data, with sparse and compressed attrs */
4496 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4497 0, /* off */
4498 file_size); /* beyond_final_zero */
4499 torture_assert_ntstatus_ok(torture, status, "zero_data");
4502 * Windows Server 2012 still deallocates a zeroed range when a sparse
4503 * file carries the compression attribute.
4505 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4506 0, /* off */
4507 file_size, /* len */
4508 &far_rsp,
4509 &far_count);
4510 torture_assert_ntstatus_ok(torture, status,
4511 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4512 if (far_count == 0) {
4513 torture_comment(torture, "sparse & compressed file "
4514 "deallocated after hole-punch\n");
4515 } else {
4516 torture_assert_u64_equal(torture, far_count, 1,
4517 "unexpected response len");
4518 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4519 "unexpected far off");
4520 torture_assert_u64_equal(torture, far_rsp[0].len, file_size,
4521 "unexpected far len");
4522 torture_comment(torture, "sparse & compressed file fully "
4523 "allocated after hole-punch\n");
4526 smb2_util_close(tree, fh);
4527 talloc_free(tmp_ctx);
4528 return true;
4532 * Create a sparse file, then attempt to copy unallocated and allocated ranges
4533 * into a target file using FSCTL_SRV_COPYCHUNK.
4535 static bool test_ioctl_sparse_copy_chunk(struct torture_context *torture,
4536 struct smb2_tree *tree)
4538 struct smb2_handle src_h;
4539 struct smb2_handle dest_h;
4540 NTSTATUS status;
4541 TALLOC_CTX *tmp_ctx = talloc_new(tree);
4542 bool ok;
4543 uint64_t dealloc_chunk_len = 64 * 1024; /* Windows 2012 */
4544 struct file_alloced_range_buf *far_rsp = NULL;
4545 uint64_t far_count = 0;
4546 union smb_ioctl ioctl;
4547 struct srv_copychunk_copy cc_copy;
4548 struct srv_copychunk_rsp cc_rsp;
4549 enum ndr_err_code ndr_ret;
4551 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4552 FNAME, &src_h, 0, SEC_RIGHTS_FILE_ALL,
4553 FILE_ATTRIBUTE_NORMAL);
4554 torture_assert(torture, ok, "setup file");
4556 /* check for FS sparse file support */
4557 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &src_h,
4558 FILE_SUPPORTS_SPARSE_FILES, &ok);
4559 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4560 smb2_util_close(tree, src_h);
4561 if (!ok) {
4562 torture_skip(torture, "Sparse files not supported\n");
4565 ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
4566 1, /* chunks */
4567 FNAME,
4568 &src_h, 0, /* src file */
4569 SEC_RIGHTS_FILE_ALL,
4570 FNAME2,
4571 &dest_h, 0, /* dest file */
4572 SEC_RIGHTS_FILE_ALL,
4573 &cc_copy,
4574 &ioctl);
4575 torture_assert(torture, ok, "setup copy chunk error");
4577 /* set sparse */
4578 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, src_h, true);
4579 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4581 /* start after dealloc_chunk_len, to create an unwritten sparse range */
4582 ok = write_pattern(torture, tree, tmp_ctx, src_h,
4583 dealloc_chunk_len, /* off */
4584 1024, /* len */
4585 dealloc_chunk_len); /* pattern offset */
4586 torture_assert(torture, ok, "write pattern");
4588 /* Skip test if 64k chunk is allocated - FS specific */
4589 status = test_ioctl_qar_req(torture, tmp_ctx, tree, src_h,
4590 0, /* off */
4591 dealloc_chunk_len + 1024, /* len */
4592 &far_rsp,
4593 &far_count);
4594 torture_assert_ntstatus_ok(torture, status,
4595 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4596 torture_assert_u64_equal(torture, far_count, 1,
4597 "unexpected response len");
4598 if (far_rsp[0].file_off == 0) {
4599 torture_skip(torture, "unwritten range fully allocated\n");
4602 torture_assert_u64_equal(torture, far_rsp[0].file_off, dealloc_chunk_len,
4603 "unexpected allocation");
4604 torture_assert_u64_equal(torture, far_rsp[0].len, 1024,
4605 "unexpected far len");
4607 /* copy-chunk unallocated + written ranges into non-sparse dest */
4609 cc_copy.chunks[0].source_off = 0;
4610 cc_copy.chunks[0].target_off = 0;
4611 cc_copy.chunks[0].length = dealloc_chunk_len + 1024;
4613 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
4614 &cc_copy,
4615 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
4616 torture_assert_ndr_success(torture, ndr_ret,
4617 "ndr_push_srv_copychunk_copy");
4619 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
4620 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
4622 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
4623 &cc_rsp,
4624 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
4625 torture_assert_ndr_success(torture, ndr_ret,
4626 "ndr_pull_srv_copychunk_rsp");
4628 ok = check_copy_chunk_rsp(torture, &cc_rsp,
4629 1, /* chunks written */
4630 0, /* chunk bytes unsuccessfully written */
4631 dealloc_chunk_len + 1024); /* bytes written */
4632 torture_assert(torture, ok, "bad copy chunk response data");
4634 ok = check_zero(torture, tree, tmp_ctx, dest_h, 0, dealloc_chunk_len);
4635 torture_assert(torture, ok, "sparse zeroed range");
4637 ok = check_pattern(torture, tree, tmp_ctx, dest_h, dealloc_chunk_len,
4638 1024, dealloc_chunk_len);
4639 torture_assert(torture, ok, "copychunked range");
4641 /* copied range should be allocated in non-sparse dest */
4642 status = test_ioctl_qar_req(torture, tmp_ctx, tree, dest_h,
4643 0, /* off */
4644 dealloc_chunk_len + 1024, /* len */
4645 &far_rsp,
4646 &far_count);
4647 torture_assert_ntstatus_ok(torture, status,
4648 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4649 torture_assert_u64_equal(torture, far_count, 1,
4650 "unexpected response len");
4651 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4652 "unexpected allocation");
4653 torture_assert_u64_equal(torture, far_rsp[0].len,
4654 dealloc_chunk_len + 1024,
4655 "unexpected far len");
4657 /* set dest as sparse */
4658 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, dest_h, true);
4659 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4661 /* zero (hole-punch) all data */
4662 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, dest_h,
4663 0, /* off */
4664 dealloc_chunk_len + 1024);
4665 torture_assert_ntstatus_ok(torture, status, "zero_data");
4667 /* zeroed range might be deallocated */
4668 status = test_ioctl_qar_req(torture, tmp_ctx, tree, dest_h,
4669 0, /* off */
4670 dealloc_chunk_len + 1024, /* len */
4671 &far_rsp,
4672 &far_count);
4673 torture_assert_ntstatus_ok(torture, status,
4674 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4675 if (far_count == 0) {
4676 /* FS specific (e.g. NTFS) */
4677 torture_comment(torture, "FS deallocates file on full-range "
4678 "punch\n");
4679 } else {
4680 /* FS specific (e.g. EXT4) */
4681 torture_comment(torture, "FS doesn't deallocate file on "
4682 "full-range punch\n");
4684 ok = check_zero(torture, tree, tmp_ctx, dest_h, 0,
4685 dealloc_chunk_len + 1024);
4686 torture_assert(torture, ok, "punched zeroed range");
4688 /* copy-chunk again, this time with sparse dest */
4689 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
4690 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
4692 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
4693 &cc_rsp,
4694 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
4695 torture_assert_ndr_success(torture, ndr_ret,
4696 "ndr_pull_srv_copychunk_rsp");
4698 ok = check_copy_chunk_rsp(torture, &cc_rsp,
4699 1, /* chunks written */
4700 0, /* chunk bytes unsuccessfully written */
4701 dealloc_chunk_len + 1024); /* bytes written */
4702 torture_assert(torture, ok, "bad copy chunk response data");
4704 ok = check_zero(torture, tree, tmp_ctx, dest_h, 0, dealloc_chunk_len);
4705 torture_assert(torture, ok, "sparse zeroed range");
4707 ok = check_pattern(torture, tree, tmp_ctx, dest_h, dealloc_chunk_len,
4708 1024, dealloc_chunk_len);
4709 torture_assert(torture, ok, "copychunked range");
4711 /* copied range may be allocated in sparse dest */
4712 status = test_ioctl_qar_req(torture, tmp_ctx, tree, dest_h,
4713 0, /* off */
4714 dealloc_chunk_len + 1024, /* len */
4715 &far_rsp,
4716 &far_count);
4717 torture_assert_ntstatus_ok(torture, status,
4718 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4719 torture_assert_u64_equal(torture, far_count, 1,
4720 "unexpected response len");
4722 * FS specific: sparse region may be unallocated in dest if copy-chunk
4723 * is handled in a sparse preserving way - E.g. vfs_btrfs
4724 * with BTRFS_IOC_CLONE_RANGE.
4726 if (far_rsp[0].file_off == dealloc_chunk_len) {
4727 torture_comment(torture, "copy-chunk sparse range preserved\n");
4728 torture_assert_u64_equal(torture, far_rsp[0].len, 1024,
4729 "unexpected far len");
4730 } else {
4731 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4732 "unexpected allocation");
4733 torture_assert_u64_equal(torture, far_rsp[0].len,
4734 dealloc_chunk_len + 1024,
4735 "unexpected far len");
4738 smb2_util_close(tree, src_h);
4739 smb2_util_close(tree, dest_h);
4740 talloc_free(tmp_ctx);
4741 return true;
4744 static bool test_ioctl_sparse_punch_invalid(struct torture_context *torture,
4745 struct smb2_tree *tree)
4747 struct smb2_handle fh;
4748 NTSTATUS status;
4749 TALLOC_CTX *tmp_ctx = talloc_new(tree);
4750 bool ok;
4751 bool is_sparse;
4752 int i;
4754 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4755 FNAME, &fh, 4096, SEC_RIGHTS_FILE_ALL,
4756 FILE_ATTRIBUTE_NORMAL);
4757 torture_assert(torture, ok, "setup file");
4759 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
4760 FILE_SUPPORTS_SPARSE_FILES, &ok);
4761 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4762 if (!ok) {
4763 smb2_util_close(tree, fh);
4764 torture_skip(torture, "Sparse files not supported\n");
4767 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4768 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4769 torture_assert(torture, !is_sparse, "sparse attr before set");
4771 /* loop twice, without and with sparse attrib */
4772 for (i = 0; i <= 1; i++) {
4773 union smb_fileinfo io;
4774 struct file_alloced_range_buf *far_rsp = NULL;
4775 uint64_t far_count = 0;
4777 /* get size before & after. zero data should never change it */
4778 ZERO_STRUCT(io);
4779 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
4780 io.generic.in.file.handle = fh;
4781 status = smb2_getinfo_file(tree, tmp_ctx, &io);
4782 torture_assert_ntstatus_ok(torture, status, "getinfo");
4783 torture_assert_int_equal(torture, (int)io.all_info2.out.size,
4784 4096, "size after IO");
4786 /* valid 8 byte zero data, but after EOF */
4787 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4788 4096, /* off */
4789 4104); /* beyond_final_zero */
4790 torture_assert_ntstatus_ok(torture, status, "zero_data");
4792 /* valid 8 byte zero data, but after EOF */
4793 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4794 8192, /* off */
4795 8200); /* beyond_final_zero */
4796 torture_assert_ntstatus_ok(torture, status, "zero_data");
4798 ZERO_STRUCT(io);
4799 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
4800 io.generic.in.file.handle = fh;
4801 status = smb2_getinfo_file(tree, tmp_ctx, &io);
4802 torture_assert_ntstatus_ok(torture, status, "getinfo");
4803 torture_assert_int_equal(torture, (int)io.all_info2.out.size,
4804 4096, "size after IO");
4806 /* valid 0 byte zero data, without sparse flag */
4807 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4808 4095, /* off */
4809 4095); /* beyond_final_zero */
4810 torture_assert_ntstatus_ok(torture, status, "zero_data");
4812 /* INVALID off is past beyond_final_zero */
4813 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4814 4096, /* off */
4815 4095); /* beyond_final_zero */
4816 torture_assert_ntstatus_equal(torture, status,
4817 NT_STATUS_INVALID_PARAMETER,
4818 "invalid zero_data");
4820 /* zero length QAR - valid */
4821 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4822 0, /* off */
4823 0, /* len */
4824 &far_rsp, &far_count);
4825 torture_assert_ntstatus_ok(torture, status,
4826 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4827 torture_assert_u64_equal(torture, far_count, 0,
4828 "unexpected response len");
4830 /* QAR after EOF - valid */
4831 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4832 4096, /* off */
4833 1024, /* len */
4834 &far_rsp, &far_count);
4835 torture_assert_ntstatus_ok(torture, status,
4836 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4837 torture_assert_u64_equal(torture, far_count, 0,
4838 "unexpected response len");
4840 /* set sparse */
4841 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh,
4842 true);
4843 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4846 smb2_util_close(tree, fh);
4847 talloc_free(tmp_ctx);
4848 return true;
4851 static bool test_ioctl_sparse_perms(struct torture_context *torture,
4852 struct smb2_tree *tree)
4854 struct smb2_handle fh;
4855 NTSTATUS status;
4856 TALLOC_CTX *tmp_ctx = talloc_new(tree);
4857 bool ok;
4858 bool is_sparse;
4859 struct file_alloced_range_buf *far_rsp = NULL;
4860 uint64_t far_count = 0;
4862 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4863 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
4864 FILE_ATTRIBUTE_NORMAL);
4865 torture_assert(torture, ok, "setup file");
4867 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
4868 FILE_SUPPORTS_SPARSE_FILES, &ok);
4869 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4870 smb2_util_close(tree, fh);
4871 if (!ok) {
4872 torture_skip(torture, "Sparse files not supported\n");
4875 /* set sparse without WRITE_ATTR permission should succeed */
4876 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4877 FNAME, &fh, 0,
4878 (SEC_RIGHTS_FILE_WRITE & ~(SEC_FILE_WRITE_ATTRIBUTE
4879 | SEC_STD_WRITE_DAC
4880 | SEC_FILE_WRITE_EA)),
4881 FILE_ATTRIBUTE_NORMAL);
4882 torture_assert(torture, ok, "setup file");
4884 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4885 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4886 smb2_util_close(tree, fh);
4888 ok = test_setup_open(torture, tree, tmp_ctx,
4889 FNAME, &fh, SEC_RIGHTS_FILE_ALL,
4890 FILE_ATTRIBUTE_NORMAL);
4891 torture_assert(torture, ok, "setup file");
4892 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4893 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4894 torture_assert(torture, is_sparse, "sparse after set");
4895 smb2_util_close(tree, fh);
4897 /* attempt get sparse without READ_DATA permission */
4898 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4899 FNAME, &fh, 0,
4900 (SEC_RIGHTS_FILE_READ & ~SEC_FILE_READ_DATA),
4901 FILE_ATTRIBUTE_NORMAL);
4902 torture_assert(torture, ok, "setup file");
4904 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4905 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4906 torture_assert(torture, !is_sparse, "sparse set");
4907 smb2_util_close(tree, fh);
4909 /* attempt to set sparse with only WRITE_ATTR permission */
4910 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4911 FNAME, &fh, 0,
4912 SEC_FILE_WRITE_ATTRIBUTE,
4913 FILE_ATTRIBUTE_NORMAL);
4914 torture_assert(torture, ok, "setup file");
4916 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4917 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4918 smb2_util_close(tree, fh);
4920 /* attempt to set sparse with only WRITE_DATA permission */
4921 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4922 FNAME, &fh, 0,
4923 SEC_FILE_WRITE_DATA,
4924 FILE_ATTRIBUTE_NORMAL);
4925 torture_assert(torture, ok, "setup file");
4927 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4928 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4929 smb2_util_close(tree, fh);
4931 ok = test_setup_open(torture, tree, tmp_ctx,
4932 FNAME, &fh, SEC_RIGHTS_FILE_ALL,
4933 FILE_ATTRIBUTE_NORMAL);
4934 torture_assert(torture, ok, "setup file");
4935 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4936 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4937 torture_assert(torture, is_sparse, "sparse after set");
4938 smb2_util_close(tree, fh);
4940 /* attempt to set sparse with only APPEND_DATA permission */
4941 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4942 FNAME, &fh, 0,
4943 SEC_FILE_APPEND_DATA,
4944 FILE_ATTRIBUTE_NORMAL);
4945 torture_assert(torture, ok, "setup file");
4947 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4948 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4949 smb2_util_close(tree, fh);
4951 ok = test_setup_open(torture, tree, tmp_ctx,
4952 FNAME, &fh, SEC_RIGHTS_FILE_ALL,
4953 FILE_ATTRIBUTE_NORMAL);
4954 torture_assert(torture, ok, "setup file");
4955 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4956 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4957 torture_assert(torture, is_sparse, "sparse after set");
4958 smb2_util_close(tree, fh);
4960 /* attempt to set sparse with only WRITE_EA permission - should fail */
4961 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4962 FNAME, &fh, 0,
4963 SEC_FILE_WRITE_EA,
4964 FILE_ATTRIBUTE_NORMAL);
4965 torture_assert(torture, ok, "setup file");
4967 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4968 torture_assert_ntstatus_equal(torture, status,
4969 NT_STATUS_ACCESS_DENIED,
4970 "FSCTL_SET_SPARSE permission");
4971 smb2_util_close(tree, fh);
4973 ok = test_setup_open(torture, tree, tmp_ctx,
4974 FNAME, &fh, SEC_RIGHTS_FILE_ALL,
4975 FILE_ATTRIBUTE_NORMAL);
4976 torture_assert(torture, ok, "setup file");
4977 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4978 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4979 torture_assert(torture, !is_sparse, "sparse after set");
4980 smb2_util_close(tree, fh);
4982 /* attempt QAR with only READ_ATTR permission - should fail */
4983 ok = test_setup_open(torture, tree, tmp_ctx,
4984 FNAME, &fh, SEC_FILE_READ_ATTRIBUTE,
4985 FILE_ATTRIBUTE_NORMAL);
4986 torture_assert(torture, ok, "setup file");
4987 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4988 4096, /* off */
4989 1024, /* len */
4990 &far_rsp, &far_count);
4991 torture_assert_ntstatus_equal(torture, status,
4992 NT_STATUS_ACCESS_DENIED,
4993 "FSCTL_QUERY_ALLOCATED_RANGES req passed");
4994 smb2_util_close(tree, fh);
4996 /* attempt QAR with only READ_DATA permission */
4997 ok = test_setup_open(torture, tree, tmp_ctx,
4998 FNAME, &fh, SEC_FILE_READ_DATA,
4999 FILE_ATTRIBUTE_NORMAL);
5000 torture_assert(torture, ok, "setup file");
5001 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
5002 0, /* off */
5003 1024, /* len */
5004 &far_rsp, &far_count);
5005 torture_assert_ntstatus_ok(torture, status,
5006 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5007 torture_assert_u64_equal(torture, far_count, 0,
5008 "unexpected response len");
5009 smb2_util_close(tree, fh);
5011 /* attempt QAR with only READ_EA permission - should fail */
5012 ok = test_setup_open(torture, tree, tmp_ctx,
5013 FNAME, &fh, SEC_FILE_READ_EA,
5014 FILE_ATTRIBUTE_NORMAL);
5015 torture_assert(torture, ok, "setup file");
5016 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
5017 4096, /* off */
5018 1024, /* len */
5019 &far_rsp, &far_count);
5020 torture_assert_ntstatus_equal(torture, status,
5021 NT_STATUS_ACCESS_DENIED,
5022 "FSCTL_QUERY_ALLOCATED_RANGES req passed");
5023 smb2_util_close(tree, fh);
5025 /* setup file for ZERO_DATA permissions tests */
5026 ok = test_setup_create_fill(torture, tree, tmp_ctx,
5027 FNAME, &fh, 8192,
5028 SEC_RIGHTS_FILE_ALL,
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 /* attempt ZERO_DATA with only WRITE_ATTR permission - should fail */
5037 ok = test_setup_open(torture, tree, tmp_ctx,
5038 FNAME, &fh, SEC_FILE_WRITE_ATTRIBUTE,
5039 FILE_ATTRIBUTE_NORMAL);
5040 torture_assert(torture, ok, "setup file");
5041 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
5042 0, /* off */
5043 4096); /* beyond_final_zero */
5044 torture_assert_ntstatus_equal(torture, status,
5045 NT_STATUS_ACCESS_DENIED,
5046 "zero_data permission");
5047 smb2_util_close(tree, fh);
5049 /* attempt ZERO_DATA with only WRITE_DATA permission */
5050 ok = test_setup_open(torture, tree, tmp_ctx,
5051 FNAME, &fh, SEC_FILE_WRITE_DATA,
5052 FILE_ATTRIBUTE_NORMAL);
5053 torture_assert(torture, ok, "setup file");
5054 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
5055 0, /* off */
5056 4096); /* beyond_final_zero */
5057 torture_assert_ntstatus_ok(torture, status, "zero_data");
5058 smb2_util_close(tree, fh);
5060 /* attempt ZERO_DATA with only APPEND_DATA permission - should fail */
5061 ok = test_setup_open(torture, tree, tmp_ctx,
5062 FNAME, &fh, SEC_FILE_APPEND_DATA,
5063 FILE_ATTRIBUTE_NORMAL);
5064 torture_assert(torture, ok, "setup file");
5065 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
5066 0, /* off */
5067 4096); /* beyond_final_zero */
5068 torture_assert_ntstatus_equal(torture, status,
5069 NT_STATUS_ACCESS_DENIED,
5070 "zero_data permission");
5071 smb2_util_close(tree, fh);
5073 /* attempt ZERO_DATA with only WRITE_EA permission - should fail */
5074 ok = test_setup_open(torture, tree, tmp_ctx,
5075 FNAME, &fh, SEC_FILE_WRITE_EA,
5076 FILE_ATTRIBUTE_NORMAL);
5077 torture_assert(torture, ok, "setup file");
5078 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
5079 0, /* off */
5080 4096); /* beyond_final_zero */
5081 torture_assert_ntstatus_equal(torture, status,
5082 NT_STATUS_ACCESS_DENIED,
5083 "zero_data permission");
5084 smb2_util_close(tree, fh);
5086 talloc_free(tmp_ctx);
5087 return true;
5090 static bool test_ioctl_sparse_lck(struct torture_context *torture,
5091 struct smb2_tree *tree)
5093 struct smb2_handle fh;
5094 struct smb2_handle fh2;
5095 NTSTATUS status;
5096 uint64_t dealloc_chunk_len = 64 * 1024; /* Windows 2012 */
5097 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5098 bool ok;
5099 bool is_sparse;
5100 struct smb2_lock lck;
5101 struct smb2_lock_element el[1];
5102 struct file_alloced_range_buf *far_rsp = NULL;
5103 uint64_t far_count = 0;
5105 ok = test_setup_create_fill(torture, tree, tmp_ctx, FNAME, &fh,
5106 dealloc_chunk_len, SEC_RIGHTS_FILE_ALL,
5107 FILE_ATTRIBUTE_NORMAL);
5108 torture_assert(torture, ok, "setup file");
5110 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
5111 FILE_SUPPORTS_SPARSE_FILES, &ok);
5112 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
5113 if (!ok) {
5114 torture_skip(torture, "Sparse files not supported\n");
5115 smb2_util_close(tree, fh);
5118 /* open and lock via separate fh2 */
5119 status = torture_smb2_testfile(tree, FNAME, &fh2);
5120 torture_assert_ntstatus_ok(torture, status, "2nd src open");
5122 lck.in.lock_count = 0x0001;
5123 lck.in.lock_sequence = 0x00000000;
5124 lck.in.file.handle = fh2;
5125 lck.in.locks = el;
5126 el[0].offset = 0;
5127 el[0].length = dealloc_chunk_len;
5128 el[0].reserved = 0;
5129 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
5131 status = smb2_lock(tree, &lck);
5132 torture_assert_ntstatus_ok(torture, status, "lock");
5134 /* set sparse while locked */
5135 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
5136 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
5138 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
5139 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
5140 torture_assert(torture, is_sparse, "sparse attr after set");
5142 /* zero data over locked range should fail */
5143 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
5144 0, /* off */
5145 4096); /* beyond_final_zero */
5146 torture_assert_ntstatus_equal(torture, status,
5147 NT_STATUS_FILE_LOCK_CONFLICT,
5148 "zero_data locked");
5150 /* QAR over locked range should pass */
5151 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
5152 0, /* off */
5153 4096, /* len */
5154 &far_rsp, &far_count);
5155 torture_assert_ntstatus_ok(torture, status,
5156 "FSCTL_QUERY_ALLOCATED_RANGES locked");
5157 torture_assert_u64_equal(torture, far_count, 1,
5158 "unexpected response len");
5159 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
5160 "unexpected allocation");
5161 torture_assert_u64_equal(torture, far_rsp[0].len,
5162 4096,
5163 "unexpected far len");
5165 /* zero data over range past EOF should pass */
5166 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
5167 dealloc_chunk_len, /* off */
5168 dealloc_chunk_len + 4096);
5169 torture_assert_ntstatus_ok(torture, status,
5170 "zero_data past EOF locked");
5172 /* QAR over range past EOF should pass */
5173 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
5174 dealloc_chunk_len, /* off */
5175 4096, /* len */
5176 &far_rsp, &far_count);
5177 torture_assert_ntstatus_ok(torture, status,
5178 "FSCTL_QUERY_ALLOCATED_RANGES past EOF locked");
5179 torture_assert_u64_equal(torture, far_count, 0,
5180 "unexpected response len");
5182 lck.in.lock_count = 0x0001;
5183 lck.in.lock_sequence = 0x00000001;
5184 lck.in.file.handle = fh2;
5185 lck.in.locks = el;
5186 el[0].offset = 0;
5187 el[0].length = dealloc_chunk_len;
5188 el[0].reserved = 0;
5189 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
5190 status = smb2_lock(tree, &lck);
5191 torture_assert_ntstatus_ok(torture, status, "unlock");
5193 smb2_util_close(tree, fh2);
5194 smb2_util_close(tree, fh);
5195 talloc_free(tmp_ctx);
5196 return true;
5199 /* alleviate QAR off-by-one bug paranoia - help me ob1 */
5200 static bool test_ioctl_sparse_qar_ob1(struct torture_context *torture,
5201 struct smb2_tree *tree)
5203 struct smb2_handle fh;
5204 NTSTATUS status;
5205 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5206 bool ok;
5207 uint64_t dealloc_chunk_len = 64 * 1024; /* Windows 2012 */
5208 struct file_alloced_range_buf *far_rsp = NULL;
5209 uint64_t far_count = 0;
5211 ok = test_setup_create_fill(torture, tree, tmp_ctx,
5212 FNAME, &fh, dealloc_chunk_len * 2,
5213 SEC_RIGHTS_FILE_ALL,
5214 FILE_ATTRIBUTE_NORMAL);
5215 torture_assert(torture, ok, "setup file");
5217 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
5218 FILE_SUPPORTS_SPARSE_FILES, &ok);
5219 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
5220 if (!ok) {
5221 torture_skip(torture, "Sparse files not supported\n");
5222 smb2_util_close(tree, fh);
5225 /* non-sparse QAR with range one before EOF */
5226 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
5227 0, /* off */
5228 dealloc_chunk_len * 2 - 1, /* len */
5229 &far_rsp, &far_count);
5230 torture_assert_ntstatus_ok(torture, status,
5231 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5232 torture_assert_u64_equal(torture, far_count, 1,
5233 "unexpected response len");
5234 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
5235 "unexpected allocation");
5236 torture_assert_u64_equal(torture, far_rsp[0].len,
5237 dealloc_chunk_len * 2 - 1,
5238 "unexpected far len");
5240 /* non-sparse QAR with range one after EOF */
5241 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
5242 0, /* off */
5243 dealloc_chunk_len * 2 + 1, /* len */
5244 &far_rsp, &far_count);
5245 torture_assert_ntstatus_ok(torture, status,
5246 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5247 torture_assert_u64_equal(torture, far_count, 1,
5248 "unexpected response len");
5249 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
5250 "unexpected allocation");
5251 torture_assert_u64_equal(torture, far_rsp[0].len,
5252 dealloc_chunk_len * 2,
5253 "unexpected far len");
5255 /* non-sparse QAR with range one after EOF from off=1 */
5256 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
5257 1, /* off */
5258 dealloc_chunk_len * 2, /* len */
5259 &far_rsp, &far_count);
5260 torture_assert_ntstatus_ok(torture, status,
5261 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5262 torture_assert_u64_equal(torture, far_count, 1,
5263 "unexpected response len");
5264 torture_assert_u64_equal(torture, far_rsp[0].file_off, 1,
5265 "unexpected allocation");
5266 torture_assert_u64_equal(torture, far_rsp[0].len,
5267 dealloc_chunk_len * 2 - 1,
5268 "unexpected far len");
5270 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
5271 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
5273 /* punch out second chunk */
5274 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
5275 dealloc_chunk_len, /* off */
5276 dealloc_chunk_len * 2);
5277 torture_assert_ntstatus_ok(torture, status, "zero_data");
5279 /* sparse QAR with range one before hole */
5280 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
5281 0, /* off */
5282 dealloc_chunk_len - 1, /* len */
5283 &far_rsp, &far_count);
5284 torture_assert_ntstatus_ok(torture, status,
5285 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5286 torture_assert_u64_equal(torture, far_count, 1,
5287 "unexpected response len");
5288 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
5289 "unexpected allocation");
5290 torture_assert_u64_equal(torture, far_rsp[0].len,
5291 dealloc_chunk_len - 1,
5292 "unexpected far len");
5294 /* sparse QAR with range one after hole */
5295 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
5296 0, /* off */
5297 dealloc_chunk_len + 1, /* len */
5298 &far_rsp, &far_count);
5299 torture_assert_ntstatus_ok(torture, status,
5300 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5301 torture_assert_u64_equal(torture, far_count, 1,
5302 "unexpected response len");
5303 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
5304 "unexpected allocation");
5305 torture_assert_u64_equal(torture, far_rsp[0].len,
5306 dealloc_chunk_len,
5307 "unexpected far len");
5309 /* sparse QAR with range one after hole from off=1 */
5310 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
5311 1, /* off */
5312 dealloc_chunk_len, /* len */
5313 &far_rsp, &far_count);
5314 torture_assert_ntstatus_ok(torture, status,
5315 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5316 torture_assert_u64_equal(torture, far_count, 1,
5317 "unexpected response len");
5318 torture_assert_u64_equal(torture, far_rsp[0].file_off, 1,
5319 "unexpected allocation");
5320 torture_assert_u64_equal(torture, far_rsp[0].len,
5321 dealloc_chunk_len - 1,
5322 "unexpected far len");
5324 /* sparse QAR with range one before EOF from off=chunk_len-1 */
5325 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
5326 dealloc_chunk_len - 1, /* off */
5327 dealloc_chunk_len, /* len */
5328 &far_rsp, &far_count);
5329 torture_assert_ntstatus_ok(torture, status,
5330 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5331 torture_assert_u64_equal(torture, far_count, 1,
5332 "unexpected response len");
5333 torture_assert_u64_equal(torture, far_rsp[0].file_off,
5334 dealloc_chunk_len - 1,
5335 "unexpected allocation");
5336 torture_assert_u64_equal(torture, far_rsp[0].len,
5337 1, "unexpected far len");
5339 /* sparse QAR with range one after EOF from off=chunk_len+1 */
5340 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
5341 dealloc_chunk_len + 1, /* off */
5342 dealloc_chunk_len, /* len */
5343 &far_rsp, &far_count);
5344 torture_assert_ntstatus_ok(torture, status,
5345 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5346 torture_assert_u64_equal(torture, far_count, 0,
5347 "unexpected response len");
5348 smb2_util_close(tree, fh);
5349 talloc_free(tmp_ctx);
5350 return true;
5353 /* test QAR with multi-range responses */
5354 static bool test_ioctl_sparse_qar_multi(struct torture_context *torture,
5355 struct smb2_tree *tree)
5357 struct smb2_handle fh;
5358 NTSTATUS status;
5359 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5360 bool ok;
5361 uint64_t dealloc_chunk_len = 64 * 1024; /* Windows 2012 */
5362 uint64_t this_off;
5363 int i;
5364 struct file_alloced_range_buf *far_rsp = NULL;
5365 uint64_t far_count = 0;
5367 ok = test_setup_create_fill(torture, tree, tmp_ctx,
5368 FNAME, &fh, dealloc_chunk_len * 2,
5369 SEC_RIGHTS_FILE_ALL,
5370 FILE_ATTRIBUTE_NORMAL);
5371 torture_assert(torture, ok, "setup file");
5373 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
5374 FILE_SUPPORTS_SPARSE_FILES, &ok);
5375 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
5376 if (!ok) {
5377 torture_skip(torture, "Sparse files not supported\n");
5378 smb2_util_close(tree, fh);
5381 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
5382 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
5384 /* each loop, write out two chunks and punch the first out */
5385 for (i = 0; i < 10; i++) {
5386 this_off = i * dealloc_chunk_len * 2;
5388 ok = write_pattern(torture, tree, tmp_ctx, fh,
5389 this_off, /* off */
5390 dealloc_chunk_len * 2, /* len */
5391 this_off); /* pattern offset */
5392 torture_assert(torture, ok, "write pattern");
5394 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
5395 this_off, /* off */
5396 this_off + dealloc_chunk_len);
5397 torture_assert_ntstatus_ok(torture, status, "zero_data");
5400 /* should now have one separate region for each iteration */
5401 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
5403 10 * dealloc_chunk_len * 2,
5404 &far_rsp, &far_count);
5405 torture_assert_ntstatus_ok(torture, status,
5406 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5407 if (far_count == 1) {
5408 torture_comment(torture, "this FS doesn't deallocate 64K"
5409 "zeroed ranges in sparse files\n");
5410 return true; /* FS specific, not a failure */
5412 torture_assert_u64_equal(torture, far_count, 10,
5413 "unexpected response len");
5414 for (i = 0; i < 10; i++) {
5415 this_off = i * dealloc_chunk_len * 2;
5417 torture_assert_u64_equal(torture, far_rsp[i].file_off,
5418 this_off + dealloc_chunk_len,
5419 "unexpected allocation");
5420 torture_assert_u64_equal(torture, far_rsp[i].len,
5421 dealloc_chunk_len,
5422 "unexpected far len");
5425 smb2_util_close(tree, fh);
5426 talloc_free(tmp_ctx);
5427 return true;
5430 static bool test_ioctl_sparse_qar_overflow(struct torture_context *torture,
5431 struct smb2_tree *tree)
5433 struct smb2_handle fh;
5434 union smb_ioctl ioctl;
5435 struct file_alloced_range_buf far_buf;
5436 NTSTATUS status;
5437 enum ndr_err_code ndr_ret;
5438 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5439 bool ok;
5441 ok = test_setup_create_fill(torture, tree, tmp_ctx,
5442 FNAME, &fh, 1024, SEC_RIGHTS_FILE_ALL,
5443 FILE_ATTRIBUTE_NORMAL);
5444 torture_assert(torture, ok, "setup file");
5446 status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
5447 FILE_SUPPORTS_SPARSE_FILES, &ok);
5448 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
5449 if (!ok) {
5450 smb2_util_close(tree, fh);
5451 torture_skip(torture, "Sparse files not supported\n");
5454 /* no allocated ranges, no space for range response, should pass */
5455 ZERO_STRUCT(ioctl);
5456 ioctl.smb2.level = RAW_IOCTL_SMB2;
5457 ioctl.smb2.in.file.handle = fh;
5458 ioctl.smb2.in.function = FSCTL_QUERY_ALLOCATED_RANGES;
5459 ioctl.smb2.in.max_output_response = 1024;
5460 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
5462 /* off + length wraps around to 511 */
5463 far_buf.file_off = 512;
5464 far_buf.len = 0xffffffffffffffffLL;
5465 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5466 &far_buf,
5467 (ndr_push_flags_fn_t)ndr_push_file_alloced_range_buf);
5468 torture_assert_ndr_success(torture, ndr_ret, "push far ndr buf");
5470 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5471 torture_assert_ntstatus_equal(torture, status,
5472 NT_STATUS_INVALID_PARAMETER,
5473 "FSCTL_QUERY_ALLOCATED_RANGES overflow");
5475 return true;
5478 static NTSTATUS test_ioctl_trim_supported(struct torture_context *torture,
5479 struct smb2_tree *tree,
5480 TALLOC_CTX *mem_ctx,
5481 struct smb2_handle *fh,
5482 bool *trim_support)
5484 NTSTATUS status;
5485 union smb_fsinfo info;
5487 ZERO_STRUCT(info);
5488 info.generic.level = RAW_QFS_SECTOR_SIZE_INFORMATION;
5489 info.generic.handle = *fh;
5490 status = smb2_getinfo_fs(tree, tree, &info);
5491 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_INFO_CLASS)) {
5493 * Windows < Server 2012, 8 etc. don't support this info level
5494 * or the trim ioctl. Ignore the error and let the caller skip.
5496 *trim_support = false;
5497 return NT_STATUS_OK;
5498 } else if (!NT_STATUS_IS_OK(status)) {
5499 return status;
5502 torture_comment(torture, "sector size info: lb/s=%u, pb/sA=%u, "
5503 "pb/sP=%u, fse/sA=%u, flags=0x%x, bosa=%u, bopa=%u\n",
5504 (unsigned)info.sector_size_info.out.logical_bytes_per_sector,
5505 (unsigned)info.sector_size_info.out.phys_bytes_per_sector_atomic,
5506 (unsigned)info.sector_size_info.out.phys_bytes_per_sector_perf,
5507 (unsigned)info.sector_size_info.out.fs_effective_phys_bytes_per_sector_atomic,
5508 (unsigned)info.sector_size_info.out.flags,
5509 (unsigned)info.sector_size_info.out.byte_off_sector_align,
5510 (unsigned)info.sector_size_info.out.byte_off_partition_align);
5512 if (info.sector_size_info.out.flags & QFS_SSINFO_FLAGS_TRIM_ENABLED) {
5513 *trim_support = true;
5514 } else {
5515 *trim_support = false;
5517 return NT_STATUS_OK;
5520 static bool test_setup_trim(struct torture_context *torture,
5521 struct smb2_tree *tree,
5522 TALLOC_CTX *mem_ctx,
5523 uint32_t num_ranges,
5524 struct smb2_handle *fh,
5525 uint64_t file_size,
5526 uint32_t desired_access,
5527 struct fsctl_file_level_trim_req *trim_req,
5528 union smb_ioctl *ioctl)
5530 bool ok;
5532 ok = test_setup_create_fill(torture, tree, mem_ctx, FNAME,
5533 fh, file_size, desired_access,
5534 FILE_ATTRIBUTE_NORMAL);
5535 torture_assert(torture, ok, "src file create fill");
5537 ZERO_STRUCTPN(ioctl);
5538 ioctl->smb2.level = RAW_IOCTL_SMB2;
5539 ioctl->smb2.in.file.handle = *fh;
5540 ioctl->smb2.in.function = FSCTL_FILE_LEVEL_TRIM;
5541 ioctl->smb2.in.max_output_response
5542 = sizeof(struct fsctl_file_level_trim_rsp);
5543 ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
5545 ZERO_STRUCTPN(trim_req);
5546 /* leave key as zero for now. TODO test locking with differing keys */
5547 trim_req->num_ranges = num_ranges;
5548 trim_req->ranges = talloc_zero_array(mem_ctx,
5549 struct file_level_trim_range,
5550 num_ranges);
5551 torture_assert(torture, (trim_req->ranges != NULL), "no memory for ranges");
5553 return true;
5556 static bool test_ioctl_trim_simple(struct torture_context *torture,
5557 struct smb2_tree *tree)
5559 struct smb2_handle fh;
5560 NTSTATUS status;
5561 union smb_ioctl ioctl;
5562 bool trim_supported;
5563 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5564 struct fsctl_file_level_trim_req trim_req;
5565 struct fsctl_file_level_trim_rsp trim_rsp;
5566 uint64_t trim_chunk_len = 64 * 1024; /* trim 64K chunks */
5567 enum ndr_err_code ndr_ret;
5568 bool ok;
5570 ok = test_setup_trim(torture, tree, tmp_ctx,
5571 1, /* 1 range */
5572 &fh, 2 * trim_chunk_len, /* fill 128K file */
5573 SEC_RIGHTS_FILE_ALL,
5574 &trim_req,
5575 &ioctl);
5576 if (!ok) {
5577 torture_fail(torture, "setup trim error");
5580 status = test_ioctl_trim_supported(torture, tree, tmp_ctx, &fh,
5581 &trim_supported);
5582 torture_assert_ntstatus_ok(torture, status, "fsinfo");
5583 if (!trim_supported) {
5584 smb2_util_close(tree, fh);
5585 talloc_free(tmp_ctx);
5586 torture_skip(torture, "trim not supported\n");
5589 /* trim first chunk, leave second */
5590 trim_req.ranges[0].off = 0;
5591 trim_req.ranges[0].len = trim_chunk_len;
5593 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, &trim_req,
5594 (ndr_push_flags_fn_t)ndr_push_fsctl_file_level_trim_req);
5595 torture_assert_ndr_success(torture, ndr_ret,
5596 "ndr_push_fsctl_file_level_trim_req");
5598 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5599 torture_assert_ntstatus_ok(torture, status, "FILE_LEVEL_TRIM_RANGE");
5601 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
5602 &trim_rsp,
5603 (ndr_pull_flags_fn_t)ndr_pull_fsctl_file_level_trim_rsp);
5604 torture_assert_ndr_success(torture, ndr_ret,
5605 "ndr_pull_fsctl_file_level_trim_rsp");
5607 torture_assert_int_equal(torture, trim_rsp.num_ranges_processed, 1, "");
5609 /* second half of the file should remain consitent */
5610 ok = check_pattern(torture, tree, tmp_ctx, fh, trim_chunk_len,
5611 trim_chunk_len, trim_chunk_len);
5612 torture_assert(torture, ok, "non-trimmed range inconsistent");
5614 return true;
5617 static bool test_setup_dup_extents(struct torture_context *tctx,
5618 struct smb2_tree *tree,
5619 TALLOC_CTX *mem_ctx,
5620 struct smb2_handle *src_h,
5621 uint64_t src_size,
5622 uint32_t src_desired_access,
5623 struct smb2_handle *dest_h,
5624 uint64_t dest_size,
5625 uint32_t dest_desired_access,
5626 struct fsctl_dup_extents_to_file *dup_ext_buf,
5627 union smb_ioctl *ioctl)
5629 bool ok;
5631 ok = test_setup_create_fill(tctx, tree, mem_ctx, FNAME,
5632 src_h, src_size, src_desired_access,
5633 FILE_ATTRIBUTE_NORMAL);
5634 torture_assert(tctx, ok, "src file create fill");
5636 ok = test_setup_create_fill(tctx, tree, mem_ctx, FNAME2,
5637 dest_h, dest_size, dest_desired_access,
5638 FILE_ATTRIBUTE_NORMAL);
5639 torture_assert(tctx, ok, "dest file create fill");
5641 ZERO_STRUCTPN(ioctl);
5642 ioctl->smb2.level = RAW_IOCTL_SMB2;
5643 ioctl->smb2.in.file.handle = *dest_h;
5644 ioctl->smb2.in.function = FSCTL_DUP_EXTENTS_TO_FILE;
5645 ioctl->smb2.in.max_output_response = 0;
5646 ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
5648 ZERO_STRUCTPN(dup_ext_buf);
5649 smb2_push_handle(dup_ext_buf->source_fid, src_h);
5651 return true;
5654 static bool test_ioctl_dup_extents_simple(struct torture_context *tctx,
5655 struct smb2_tree *tree)
5657 struct smb2_handle src_h;
5658 struct smb2_handle dest_h;
5659 NTSTATUS status;
5660 union smb_ioctl ioctl;
5661 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5662 struct fsctl_dup_extents_to_file dup_ext_buf;
5663 enum ndr_err_code ndr_ret;
5664 union smb_fileinfo io;
5665 union smb_setfileinfo sinfo;
5666 bool ok;
5668 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5669 &src_h, 4096, /* fill 4096 byte src file */
5670 SEC_RIGHTS_FILE_ALL,
5671 &dest_h, 0, /* 0 byte dest file */
5672 SEC_RIGHTS_FILE_ALL,
5673 &dup_ext_buf,
5674 &ioctl);
5675 if (!ok) {
5676 torture_fail(tctx, "setup dup extents error");
5679 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5680 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
5681 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5682 if (!ok) {
5683 smb2_util_close(tree, src_h);
5684 smb2_util_close(tree, dest_h);
5685 talloc_free(tmp_ctx);
5686 torture_skip(tctx, "block refcounting not supported\n");
5689 /* extend dest to match src len */
5690 ZERO_STRUCT(sinfo);
5691 sinfo.end_of_file_info.level =
5692 RAW_SFILEINFO_END_OF_FILE_INFORMATION;
5693 sinfo.end_of_file_info.in.file.handle = dest_h;
5694 sinfo.end_of_file_info.in.size = 4096;
5695 status = smb2_setinfo_file(tree, &sinfo);
5696 torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
5698 /* copy all src file data */
5699 dup_ext_buf.source_off = 0;
5700 dup_ext_buf.target_off = 0;
5701 dup_ext_buf.byte_count = 4096;
5703 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5704 &dup_ext_buf,
5705 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5706 torture_assert_ndr_success(tctx, ndr_ret,
5707 "ndr_push_fsctl_dup_extents_to_file");
5709 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5710 torture_assert_ntstatus_ok(tctx, status,
5711 "FSCTL_DUP_EXTENTS_TO_FILE");
5713 /* the file size shouldn't have been changed by this operation! */
5714 ZERO_STRUCT(io);
5715 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5716 io.generic.in.file.handle = dest_h;
5717 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5718 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5719 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5720 4096, "size after IO");
5722 smb2_util_close(tree, src_h);
5723 smb2_util_close(tree, dest_h);
5725 /* reopen for pattern check */
5726 ok = test_setup_open(tctx, tree, tmp_ctx, FNAME, &src_h,
5727 SEC_RIGHTS_FILE_ALL, FILE_ATTRIBUTE_NORMAL);
5728 torture_assert_ntstatus_ok(tctx, status, "src open after dup");
5729 ok = test_setup_open(tctx, tree, tmp_ctx, FNAME2, &dest_h,
5730 SEC_RIGHTS_FILE_ALL, FILE_ATTRIBUTE_NORMAL);
5731 torture_assert_ntstatus_ok(tctx, status, "dest open after dup");
5733 ok = check_pattern(tctx, tree, tmp_ctx, src_h, 0, 4096, 0);
5734 if (!ok) {
5735 torture_fail(tctx, "inconsistent src file data");
5738 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 4096, 0);
5739 if (!ok) {
5740 torture_fail(tctx, "inconsistent dest file data");
5743 smb2_util_close(tree, src_h);
5744 smb2_util_close(tree, dest_h);
5745 talloc_free(tmp_ctx);
5746 return true;
5749 static bool test_ioctl_dup_extents_len_beyond_dest(struct torture_context *tctx,
5750 struct smb2_tree *tree)
5752 struct smb2_handle src_h;
5753 struct smb2_handle dest_h;
5754 NTSTATUS status;
5755 union smb_ioctl ioctl;
5756 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5757 struct fsctl_dup_extents_to_file dup_ext_buf;
5758 enum ndr_err_code ndr_ret;
5759 union smb_fileinfo io;
5760 union smb_setfileinfo sinfo;
5761 bool ok;
5763 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5764 &src_h, 32768, /* fill 32768 byte src file */
5765 SEC_RIGHTS_FILE_ALL,
5766 &dest_h, 0, /* 0 byte dest file */
5767 SEC_RIGHTS_FILE_ALL,
5768 &dup_ext_buf,
5769 &ioctl);
5770 if (!ok) {
5771 torture_fail(tctx, "setup dup extents error");
5774 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5775 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
5776 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5777 if (!ok) {
5778 smb2_util_close(tree, src_h);
5779 smb2_util_close(tree, dest_h);
5780 talloc_free(tmp_ctx);
5781 torture_skip(tctx, "block refcounting not supported\n");
5784 ZERO_STRUCT(io);
5785 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5786 io.generic.in.file.handle = dest_h;
5787 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5788 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5789 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5790 0, "size after IO");
5792 /* copy all src file data */
5793 dup_ext_buf.source_off = 0;
5794 dup_ext_buf.target_off = 0;
5795 dup_ext_buf.byte_count = 32768;
5797 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5798 &dup_ext_buf,
5799 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5800 torture_assert_ndr_success(tctx, ndr_ret,
5801 "ndr_push_fsctl_dup_extents_to_file");
5803 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5804 #if 0
5806 * 2.3.8 FSCTL_DUPLICATE_EXTENTS_TO_FILE Reply - this should fail, but
5807 * passes against WS2016 RTM!
5809 torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_SUPPORTED,
5810 "FSCTL_DUP_EXTENTS_TO_FILE");
5811 #endif
5813 /* the file sizes shouldn't have been changed */
5814 ZERO_STRUCT(io);
5815 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5816 io.generic.in.file.handle = src_h;
5817 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5818 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5819 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5820 32768, "size after IO");
5822 ZERO_STRUCT(io);
5823 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5824 io.generic.in.file.handle = dest_h;
5825 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5826 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5827 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5828 0, "size after IO");
5830 /* extend dest */
5831 ZERO_STRUCT(sinfo);
5832 sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
5833 sinfo.end_of_file_info.in.file.handle = dest_h;
5834 sinfo.end_of_file_info.in.size = 32768;
5835 status = smb2_setinfo_file(tree, &sinfo);
5836 torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
5838 ok = check_zero(tctx, tree, tmp_ctx, dest_h, 0, 32768);
5839 if (!ok) {
5840 torture_fail(tctx, "inconsistent file data");
5843 /* reissue ioctl, now with enough space */
5844 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5845 torture_assert_ntstatus_ok(tctx, status,
5846 "FSCTL_DUP_EXTENTS_TO_FILE");
5848 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
5849 if (!ok) {
5850 torture_fail(tctx, "inconsistent file data");
5853 smb2_util_close(tree, src_h);
5854 smb2_util_close(tree, dest_h);
5855 talloc_free(tmp_ctx);
5856 return true;
5859 static bool test_ioctl_dup_extents_len_beyond_src(struct torture_context *tctx,
5860 struct smb2_tree *tree)
5862 struct smb2_handle src_h;
5863 struct smb2_handle dest_h;
5864 NTSTATUS status;
5865 union smb_ioctl ioctl;
5866 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5867 struct fsctl_dup_extents_to_file dup_ext_buf;
5868 enum ndr_err_code ndr_ret;
5869 union smb_fileinfo io;
5870 bool ok;
5872 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5873 &src_h, 32768, /* fill 32768 byte src file */
5874 SEC_RIGHTS_FILE_ALL,
5875 &dest_h, 0, /* 0 byte dest file */
5876 SEC_RIGHTS_FILE_ALL,
5877 &dup_ext_buf,
5878 &ioctl);
5879 if (!ok) {
5880 torture_fail(tctx, "setup dup extents error");
5883 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5884 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
5885 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5886 if (!ok) {
5887 smb2_util_close(tree, src_h);
5888 smb2_util_close(tree, dest_h);
5889 talloc_free(tmp_ctx);
5890 torture_skip(tctx, "block refcounting not supported\n");
5893 ZERO_STRUCT(io);
5894 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5895 io.generic.in.file.handle = dest_h;
5896 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5897 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5898 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5899 0, "size after IO");
5901 /* exceed src file len */
5902 dup_ext_buf.source_off = 0;
5903 dup_ext_buf.target_off = 0;
5904 dup_ext_buf.byte_count = 32768 * 2;
5906 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5907 &dup_ext_buf,
5908 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5909 torture_assert_ndr_success(tctx, ndr_ret,
5910 "ndr_push_fsctl_dup_extents_to_file");
5912 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5913 torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_SUPPORTED,
5914 "FSCTL_DUP_EXTENTS_TO_FILE");
5916 /* the file sizes shouldn't have been changed */
5917 ZERO_STRUCT(io);
5918 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5919 io.generic.in.file.handle = src_h;
5920 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5921 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5922 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5923 32768, "size after IO");
5925 ZERO_STRUCT(io);
5926 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5927 io.generic.in.file.handle = dest_h;
5928 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5929 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5930 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5931 0, "size after IO");
5933 smb2_util_close(tree, src_h);
5934 smb2_util_close(tree, dest_h);
5935 talloc_free(tmp_ctx);
5936 return true;
5939 static bool test_ioctl_dup_extents_len_zero(struct torture_context *tctx,
5940 struct smb2_tree *tree)
5942 struct smb2_handle src_h;
5943 struct smb2_handle dest_h;
5944 NTSTATUS status;
5945 union smb_ioctl ioctl;
5946 TALLOC_CTX *tmp_ctx = talloc_new(tree);
5947 struct fsctl_dup_extents_to_file dup_ext_buf;
5948 enum ndr_err_code ndr_ret;
5949 union smb_fileinfo io;
5950 bool ok;
5952 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5953 &src_h, 32768, /* fill 32768 byte src file */
5954 SEC_RIGHTS_FILE_ALL,
5955 &dest_h, 0, /* 0 byte dest file */
5956 SEC_RIGHTS_FILE_ALL,
5957 &dup_ext_buf,
5958 &ioctl);
5959 if (!ok) {
5960 torture_fail(tctx, "setup dup extents error");
5963 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5964 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
5965 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5966 if (!ok) {
5967 smb2_util_close(tree, src_h);
5968 smb2_util_close(tree, dest_h);
5969 talloc_free(tmp_ctx);
5970 torture_skip(tctx, "block refcounting not supported\n");
5973 ZERO_STRUCT(io);
5974 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5975 io.generic.in.file.handle = dest_h;
5976 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5977 torture_assert_ntstatus_ok(tctx, status, "getinfo");
5978 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5979 0, "size after IO");
5981 dup_ext_buf.source_off = 0;
5982 dup_ext_buf.target_off = 0;
5983 dup_ext_buf.byte_count = 0;
5985 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5986 &dup_ext_buf,
5987 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5988 torture_assert_ndr_success(tctx, ndr_ret,
5989 "ndr_push_fsctl_dup_extents_to_file");
5991 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5992 torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
5994 /* the file sizes shouldn't have been changed */
5995 ZERO_STRUCT(io);
5996 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5997 io.generic.in.file.handle = src_h;
5998 status = smb2_getinfo_file(tree, tmp_ctx, &io);
5999 torture_assert_ntstatus_ok(tctx, status, "getinfo");
6000 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
6001 32768, "size after IO");
6003 ZERO_STRUCT(io);
6004 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
6005 io.generic.in.file.handle = dest_h;
6006 status = smb2_getinfo_file(tree, tmp_ctx, &io);
6007 torture_assert_ntstatus_ok(tctx, status, "getinfo");
6008 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
6009 0, "size after IO");
6011 smb2_util_close(tree, src_h);
6012 smb2_util_close(tree, dest_h);
6013 talloc_free(tmp_ctx);
6014 return true;
6017 static bool test_ioctl_dup_extents_sparse_src(struct torture_context *tctx,
6018 struct smb2_tree *tree)
6020 struct smb2_handle src_h;
6021 struct smb2_handle dest_h;
6022 NTSTATUS status;
6023 union smb_ioctl ioctl;
6024 TALLOC_CTX *tmp_ctx = talloc_new(tree);
6025 struct fsctl_dup_extents_to_file dup_ext_buf;
6026 enum ndr_err_code ndr_ret;
6027 union smb_setfileinfo sinfo;
6028 bool ok;
6030 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
6031 &src_h, 0, /* filled after sparse flag */
6032 SEC_RIGHTS_FILE_ALL,
6033 &dest_h, 0, /* 0 byte dest file */
6034 SEC_RIGHTS_FILE_ALL,
6035 &dup_ext_buf,
6036 &ioctl);
6037 if (!ok) {
6038 torture_fail(tctx, "setup dup extents error");
6041 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
6042 FILE_SUPPORTS_BLOCK_REFCOUNTING
6043 | FILE_SUPPORTS_SPARSE_FILES, &ok);
6044 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
6045 if (!ok) {
6046 smb2_util_close(tree, src_h);
6047 smb2_util_close(tree, dest_h);
6048 talloc_free(tmp_ctx);
6049 torture_skip(tctx,
6050 "block refcounting and sparse files not supported\n");
6053 /* set sparse flag on src */
6054 status = test_ioctl_sparse_req(tctx, tmp_ctx, tree, src_h, true);
6055 torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_SPARSE");
6057 ok = write_pattern(tctx, tree, tmp_ctx, src_h, 0, 4096, 0);
6058 torture_assert(tctx, ok, "write pattern");
6060 /* extend dest */
6061 ZERO_STRUCT(sinfo);
6062 sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
6063 sinfo.end_of_file_info.in.file.handle = dest_h;
6064 sinfo.end_of_file_info.in.size = 4096;
6065 status = smb2_setinfo_file(tree, &sinfo);
6066 torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
6068 /* copy all src file data */
6069 dup_ext_buf.source_off = 0;
6070 dup_ext_buf.target_off = 0;
6071 dup_ext_buf.byte_count = 4096;
6073 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6074 &dup_ext_buf,
6075 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6076 torture_assert_ndr_success(tctx, ndr_ret,
6077 "ndr_push_fsctl_dup_extents_to_file");
6080 * src is sparse, but spec says: 2.3.8 FSCTL_DUPLICATE_EXTENTS_TO_FILE
6081 * Reply... STATUS_NOT_SUPPORTED: Target file is sparse, while source
6082 * is a non-sparse file.
6084 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6085 torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_SUPPORTED,
6086 "FSCTL_DUP_EXTENTS_TO_FILE");
6088 smb2_util_close(tree, src_h);
6089 smb2_util_close(tree, dest_h);
6090 talloc_free(tmp_ctx);
6091 return true;
6094 static bool test_ioctl_dup_extents_sparse_dest(struct torture_context *tctx,
6095 struct smb2_tree *tree)
6097 struct smb2_handle src_h;
6098 struct smb2_handle dest_h;
6099 NTSTATUS status;
6100 union smb_ioctl ioctl;
6101 TALLOC_CTX *tmp_ctx = talloc_new(tree);
6102 struct fsctl_dup_extents_to_file dup_ext_buf;
6103 enum ndr_err_code ndr_ret;
6104 union smb_setfileinfo sinfo;
6105 bool ok;
6107 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
6108 &src_h, 4096, /* fill 4096 byte src file */
6109 SEC_RIGHTS_FILE_ALL,
6110 &dest_h, 0, /* 0 byte dest file */
6111 SEC_RIGHTS_FILE_ALL,
6112 &dup_ext_buf,
6113 &ioctl);
6114 if (!ok) {
6115 torture_fail(tctx, "setup dup extents error");
6118 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
6119 FILE_SUPPORTS_BLOCK_REFCOUNTING
6120 | FILE_SUPPORTS_SPARSE_FILES, &ok);
6121 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
6122 if (!ok) {
6123 smb2_util_close(tree, src_h);
6124 smb2_util_close(tree, dest_h);
6125 talloc_free(tmp_ctx);
6126 torture_skip(tctx,
6127 "block refcounting and sparse files not supported\n");
6130 /* set sparse flag on dest */
6131 status = test_ioctl_sparse_req(tctx, tmp_ctx, tree, dest_h, true);
6132 torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_SPARSE");
6134 /* extend dest */
6135 ZERO_STRUCT(sinfo);
6136 sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
6137 sinfo.end_of_file_info.in.file.handle = dest_h;
6138 sinfo.end_of_file_info.in.size = dup_ext_buf.byte_count;
6139 status = smb2_setinfo_file(tree, &sinfo);
6140 torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
6142 /* copy all src file data */
6143 dup_ext_buf.source_off = 0;
6144 dup_ext_buf.target_off = 0;
6145 dup_ext_buf.byte_count = 4096;
6147 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6148 &dup_ext_buf,
6149 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6150 torture_assert_ndr_success(tctx, ndr_ret,
6151 "ndr_push_fsctl_dup_extents_to_file");
6154 * dest is sparse, but spec says: 2.3.8 FSCTL_DUPLICATE_EXTENTS_TO_FILE
6155 * Reply... STATUS_NOT_SUPPORTED: Target file is sparse, while source
6156 * is a non-sparse file.
6158 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6159 torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
6161 smb2_util_close(tree, src_h);
6162 smb2_util_close(tree, dest_h);
6163 talloc_free(tmp_ctx);
6164 return true;
6167 static bool test_ioctl_dup_extents_sparse_both(struct torture_context *tctx,
6168 struct smb2_tree *tree)
6170 struct smb2_handle src_h;
6171 struct smb2_handle dest_h;
6172 NTSTATUS status;
6173 union smb_ioctl ioctl;
6174 TALLOC_CTX *tmp_ctx = talloc_new(tree);
6175 struct fsctl_dup_extents_to_file dup_ext_buf;
6176 enum ndr_err_code ndr_ret;
6177 union smb_setfileinfo sinfo;
6178 bool ok;
6180 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
6181 &src_h, 0, /* fill 4096 byte src file */
6182 SEC_RIGHTS_FILE_ALL,
6183 &dest_h, 0, /* 0 byte dest file */
6184 SEC_RIGHTS_FILE_ALL,
6185 &dup_ext_buf,
6186 &ioctl);
6187 if (!ok) {
6188 torture_fail(tctx, "setup dup extents error");
6191 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
6192 FILE_SUPPORTS_BLOCK_REFCOUNTING
6193 | FILE_SUPPORTS_SPARSE_FILES, &ok);
6194 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
6195 if (!ok) {
6196 smb2_util_close(tree, src_h);
6197 smb2_util_close(tree, dest_h);
6198 talloc_free(tmp_ctx);
6199 torture_skip(tctx,
6200 "block refcounting and sparse files not supported\n");
6203 /* set sparse flag on src and dest */
6204 status = test_ioctl_sparse_req(tctx, tmp_ctx, tree, src_h, true);
6205 torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_SPARSE");
6206 status = test_ioctl_sparse_req(tctx, tmp_ctx, tree, dest_h, true);
6207 torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_SPARSE");
6209 ok = write_pattern(tctx, tree, tmp_ctx, src_h, 0, 4096, 0);
6210 torture_assert(tctx, ok, "write pattern");
6212 /* extend dest */
6213 ZERO_STRUCT(sinfo);
6214 sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
6215 sinfo.end_of_file_info.in.file.handle = dest_h;
6216 sinfo.end_of_file_info.in.size = 4096;
6217 status = smb2_setinfo_file(tree, &sinfo);
6218 torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
6220 /* copy all src file data */
6221 dup_ext_buf.source_off = 0;
6222 dup_ext_buf.target_off = 0;
6223 dup_ext_buf.byte_count = 4096;
6225 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6226 &dup_ext_buf,
6227 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6228 torture_assert_ndr_success(tctx, ndr_ret,
6229 "ndr_push_fsctl_dup_extents_to_file");
6231 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6232 torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
6234 smb2_util_close(tree, src_h);
6235 smb2_util_close(tree, dest_h);
6237 /* reopen for pattern check */
6238 ok = test_setup_open(tctx, tree, tmp_ctx, FNAME2, &dest_h,
6239 SEC_RIGHTS_FILE_ALL, FILE_ATTRIBUTE_NORMAL);
6240 torture_assert_ntstatus_ok(tctx, status, "dest open ater dup");
6242 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 4096, 0);
6243 if (!ok) {
6244 torture_fail(tctx, "inconsistent file data");
6247 smb2_util_close(tree, dest_h);
6248 talloc_free(tmp_ctx);
6249 return true;
6252 static bool test_ioctl_dup_extents_src_is_dest(struct torture_context *tctx,
6253 struct smb2_tree *tree)
6255 struct smb2_handle src_h;
6256 struct smb2_handle dest_h;
6257 NTSTATUS status;
6258 union smb_ioctl ioctl;
6259 TALLOC_CTX *tmp_ctx = talloc_new(tree);
6260 struct fsctl_dup_extents_to_file dup_ext_buf;
6261 enum ndr_err_code ndr_ret;
6262 union smb_fileinfo io;
6263 bool ok;
6265 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
6266 &src_h, 32768, /* fill 32768 byte src file */
6267 SEC_RIGHTS_FILE_ALL,
6268 &dest_h, 0,
6269 SEC_RIGHTS_FILE_ALL,
6270 &dup_ext_buf,
6271 &ioctl);
6272 if (!ok) {
6273 torture_fail(tctx, "setup dup extents error");
6275 /* dest_h not needed for this test */
6276 smb2_util_close(tree, dest_h);
6278 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
6279 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
6280 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
6281 if (!ok) {
6282 smb2_util_close(tree, src_h);
6283 talloc_free(tmp_ctx);
6284 torture_skip(tctx, "block refcounting not supported\n");
6287 /* src and dest are the same file handle */
6288 ioctl.smb2.in.file.handle = src_h;
6290 /* no overlap between src and tgt */
6291 dup_ext_buf.source_off = 0;
6292 dup_ext_buf.target_off = 16384;
6293 dup_ext_buf.byte_count = 16384;
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");
6301 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6302 torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
6304 /* the file size shouldn't have been changed */
6305 ZERO_STRUCT(io);
6306 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
6307 io.generic.in.file.handle = src_h;
6308 status = smb2_getinfo_file(tree, tmp_ctx, &io);
6309 torture_assert_ntstatus_ok(tctx, status, "getinfo");
6310 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
6311 32768, "size after IO");
6313 ok = check_pattern(tctx, tree, tmp_ctx, src_h, 0, 16384, 0);
6314 if (!ok) {
6315 torture_fail(tctx, "inconsistent file data");
6317 ok = check_pattern(tctx, tree, tmp_ctx, src_h, 16384, 16384, 0);
6318 if (!ok) {
6319 torture_fail(tctx, "inconsistent file data");
6322 smb2_util_close(tree, src_h);
6323 talloc_free(tmp_ctx);
6324 return true;
6328 * unlike copy-chunk, dup extents doesn't support overlapping ranges between
6329 * source and target. This makes it a *lot* cleaner to implement on the server.
6331 static bool
6332 test_ioctl_dup_extents_src_is_dest_overlap(struct torture_context *tctx,
6333 struct smb2_tree *tree)
6335 struct smb2_handle src_h;
6336 struct smb2_handle dest_h;
6337 NTSTATUS status;
6338 union smb_ioctl ioctl;
6339 TALLOC_CTX *tmp_ctx = talloc_new(tree);
6340 struct fsctl_dup_extents_to_file dup_ext_buf;
6341 enum ndr_err_code ndr_ret;
6342 union smb_fileinfo io;
6343 bool ok;
6345 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
6346 &src_h, 32768, /* fill 32768 byte src file */
6347 SEC_RIGHTS_FILE_ALL,
6348 &dest_h, 0,
6349 SEC_RIGHTS_FILE_ALL,
6350 &dup_ext_buf,
6351 &ioctl);
6352 if (!ok) {
6353 torture_fail(tctx, "setup dup extents error");
6355 /* dest_h not needed for this test */
6356 smb2_util_close(tree, dest_h);
6358 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
6359 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
6360 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
6361 if (!ok) {
6362 smb2_util_close(tree, src_h);
6363 talloc_free(tmp_ctx);
6364 torture_skip(tctx, "block refcounting not supported\n");
6367 /* src and dest are the same file handle */
6368 ioctl.smb2.in.file.handle = src_h;
6370 /* 8K overlap between src and tgt */
6371 dup_ext_buf.source_off = 0;
6372 dup_ext_buf.target_off = 8192;
6373 dup_ext_buf.byte_count = 16384;
6375 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6376 &dup_ext_buf,
6377 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6378 torture_assert_ndr_success(tctx, ndr_ret,
6379 "ndr_push_fsctl_dup_extents_to_file");
6381 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6382 torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_SUPPORTED,
6383 "FSCTL_DUP_EXTENTS_TO_FILE");
6385 /* the file size and data should match beforehand */
6386 ZERO_STRUCT(io);
6387 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
6388 io.generic.in.file.handle = src_h;
6389 status = smb2_getinfo_file(tree, tmp_ctx, &io);
6390 torture_assert_ntstatus_ok(tctx, status, "getinfo");
6391 torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
6392 32768, "size after IO");
6394 ok = check_pattern(tctx, tree, tmp_ctx, src_h, 0, 32768, 0);
6395 if (!ok) {
6396 torture_fail(tctx, "inconsistent file data");
6399 smb2_util_close(tree, src_h);
6400 talloc_free(tmp_ctx);
6401 return true;
6405 * The compression tests won't run against Windows servers yet - ReFS doesn't
6406 * (yet) offer support for compression.
6408 static bool test_ioctl_dup_extents_compressed_src(struct torture_context *tctx,
6409 struct smb2_tree *tree)
6411 struct smb2_handle src_h;
6412 struct smb2_handle dest_h;
6413 NTSTATUS status;
6414 union smb_ioctl ioctl;
6415 TALLOC_CTX *tmp_ctx = talloc_new(tree);
6416 struct fsctl_dup_extents_to_file dup_ext_buf;
6417 enum ndr_err_code ndr_ret;
6418 union smb_setfileinfo sinfo;
6419 bool ok;
6421 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
6422 &src_h, 0, /* filled after compressed flag */
6423 SEC_RIGHTS_FILE_ALL,
6424 &dest_h, 0,
6425 SEC_RIGHTS_FILE_ALL,
6426 &dup_ext_buf,
6427 &ioctl);
6428 if (!ok) {
6429 torture_fail(tctx, "setup dup extents error");
6432 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
6433 FILE_SUPPORTS_BLOCK_REFCOUNTING
6434 | FILE_FILE_COMPRESSION, &ok);
6435 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
6436 if (!ok) {
6437 smb2_util_close(tree, src_h);
6438 smb2_util_close(tree, dest_h);
6439 talloc_free(tmp_ctx);
6440 torture_skip(tctx,
6441 "block refcounting and compressed files not supported\n");
6444 /* set compressed flag on src */
6445 status = test_ioctl_compress_set(tctx, tmp_ctx, tree, src_h,
6446 COMPRESSION_FORMAT_DEFAULT);
6447 torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_COMPRESSION");
6449 ok = write_pattern(tctx, tree, tmp_ctx, src_h, 0, 4096, 0);
6450 torture_assert(tctx, ok, "write pattern");
6452 /* extend dest */
6453 ZERO_STRUCT(sinfo);
6454 sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
6455 sinfo.end_of_file_info.in.file.handle = dest_h;
6456 sinfo.end_of_file_info.in.size = 4096;
6457 status = smb2_setinfo_file(tree, &sinfo);
6458 torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
6460 /* copy all src file data */
6461 dup_ext_buf.source_off = 0;
6462 dup_ext_buf.target_off = 0;
6463 dup_ext_buf.byte_count = 4096;
6465 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6466 &dup_ext_buf,
6467 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6468 torture_assert_ndr_success(tctx, ndr_ret,
6469 "ndr_push_fsctl_dup_extents_to_file");
6471 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6472 torture_assert_ntstatus_ok(tctx, status,
6473 "FSCTL_DUP_EXTENTS_TO_FILE");
6475 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 4096, 0);
6476 if (!ok) {
6477 torture_fail(tctx, "inconsistent file data");
6480 smb2_util_close(tree, src_h);
6481 smb2_util_close(tree, dest_h);
6482 talloc_free(tmp_ctx);
6483 return true;
6486 static bool test_ioctl_dup_extents_compressed_dest(struct torture_context *tctx,
6487 struct smb2_tree *tree)
6489 struct smb2_handle src_h;
6490 struct smb2_handle dest_h;
6491 NTSTATUS status;
6492 union smb_ioctl ioctl;
6493 TALLOC_CTX *tmp_ctx = talloc_new(tree);
6494 struct fsctl_dup_extents_to_file dup_ext_buf;
6495 enum ndr_err_code ndr_ret;
6496 union smb_setfileinfo sinfo;
6497 bool ok;
6499 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
6500 &src_h, 4096,
6501 SEC_RIGHTS_FILE_ALL,
6502 &dest_h, 0,
6503 SEC_RIGHTS_FILE_ALL,
6504 &dup_ext_buf,
6505 &ioctl);
6506 if (!ok) {
6507 torture_fail(tctx, "setup dup extents error");
6510 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
6511 FILE_SUPPORTS_BLOCK_REFCOUNTING
6512 | FILE_FILE_COMPRESSION, &ok);
6513 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
6514 if (!ok) {
6515 smb2_util_close(tree, src_h);
6516 smb2_util_close(tree, dest_h);
6517 talloc_free(tmp_ctx);
6518 torture_skip(tctx,
6519 "block refcounting and compressed files not supported\n");
6522 /* set compressed flag on dest */
6523 status = test_ioctl_compress_set(tctx, tmp_ctx, tree, dest_h,
6524 COMPRESSION_FORMAT_DEFAULT);
6525 torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_COMPRESSION");
6527 /* extend dest */
6528 ZERO_STRUCT(sinfo);
6529 sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
6530 sinfo.end_of_file_info.in.file.handle = dest_h;
6531 sinfo.end_of_file_info.in.size = 4096;
6532 status = smb2_setinfo_file(tree, &sinfo);
6533 torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
6535 /* copy all src file data */
6536 dup_ext_buf.source_off = 0;
6537 dup_ext_buf.target_off = 0;
6538 dup_ext_buf.byte_count = 4096;
6540 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6541 &dup_ext_buf,
6542 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6543 torture_assert_ndr_success(tctx, ndr_ret,
6544 "ndr_push_fsctl_dup_extents_to_file");
6546 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6547 torture_assert_ntstatus_ok(tctx, status,
6548 "FSCTL_DUP_EXTENTS_TO_FILE");
6550 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 4096, 0);
6551 if (!ok) {
6552 torture_fail(tctx, "inconsistent file data");
6555 smb2_util_close(tree, src_h);
6556 smb2_util_close(tree, dest_h);
6557 talloc_free(tmp_ctx);
6558 return true;
6561 static bool test_ioctl_dup_extents_bad_handle(struct torture_context *tctx,
6562 struct smb2_tree *tree)
6564 struct smb2_handle src_h;
6565 struct smb2_handle dest_h;
6566 struct smb2_handle bogus_h;
6567 NTSTATUS status;
6568 union smb_ioctl ioctl;
6569 TALLOC_CTX *tmp_ctx = talloc_new(tree);
6570 struct fsctl_dup_extents_to_file dup_ext_buf;
6571 enum ndr_err_code ndr_ret;
6572 bool ok;
6574 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
6575 &src_h, 32768, /* fill 32768 byte src file */
6576 SEC_RIGHTS_FILE_ALL,
6577 &dest_h, 32768,
6578 SEC_RIGHTS_FILE_ALL,
6579 &dup_ext_buf,
6580 &ioctl);
6581 if (!ok) {
6582 torture_fail(tctx, "setup dup extents error");
6585 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
6586 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
6587 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
6588 if (!ok) {
6589 smb2_util_close(tree, src_h);
6590 smb2_util_close(tree, dest_h);
6591 talloc_free(tmp_ctx);
6592 torture_skip(tctx, "block refcounting not supported\n");
6595 /* open and close a file, keeping the handle as now a "bogus" handle */
6596 ok = test_setup_create_fill(tctx, tree, tmp_ctx, "bogus_file",
6597 &bogus_h, 0, SEC_RIGHTS_FILE_ALL,
6598 FILE_ATTRIBUTE_NORMAL);
6599 torture_assert(tctx, ok, "bogus file create fill");
6600 smb2_util_close(tree, bogus_h);
6602 /* bogus dest file handle */
6603 ioctl.smb2.in.file.handle = bogus_h;
6605 dup_ext_buf.source_off = 0;
6606 dup_ext_buf.target_off = 0;
6607 dup_ext_buf.byte_count = 32768;
6609 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6610 &dup_ext_buf,
6611 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6612 torture_assert_ndr_success(tctx, ndr_ret,
6613 "ndr_push_fsctl_dup_extents_to_file");
6615 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6616 torture_assert_ntstatus_equal(tctx, status, NT_STATUS_FILE_CLOSED,
6617 "FSCTL_DUP_EXTENTS_TO_FILE");
6619 ok = check_pattern(tctx, tree, tmp_ctx, src_h, 0, 32768, 0);
6620 if (!ok) {
6621 torture_fail(tctx, "inconsistent file data");
6623 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
6624 if (!ok) {
6625 torture_fail(tctx, "inconsistent file data");
6628 /* reinstate dest, add bogus src file handle */
6629 ioctl.smb2.in.file.handle = dest_h;
6630 smb2_push_handle(dup_ext_buf.source_fid, &bogus_h);
6632 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6633 &dup_ext_buf,
6634 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6635 torture_assert_ndr_success(tctx, ndr_ret,
6636 "ndr_push_fsctl_dup_extents_to_file");
6638 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6639 torture_assert_ntstatus_equal(tctx, status, NT_STATUS_INVALID_HANDLE,
6640 "FSCTL_DUP_EXTENTS_TO_FILE");
6642 ok = check_pattern(tctx, tree, tmp_ctx, src_h, 0, 32768, 0);
6643 if (!ok) {
6644 torture_fail(tctx, "inconsistent file data");
6646 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
6647 if (!ok) {
6648 torture_fail(tctx, "inconsistent file data");
6651 smb2_util_close(tree, src_h);
6652 smb2_util_close(tree, dest_h);
6653 talloc_free(tmp_ctx);
6654 return true;
6657 static bool test_ioctl_dup_extents_src_lck(struct torture_context *tctx,
6658 struct smb2_tree *tree)
6660 struct smb2_handle src_h;
6661 struct smb2_handle src_h2;
6662 struct smb2_handle dest_h;
6663 NTSTATUS status;
6664 union smb_ioctl ioctl;
6665 TALLOC_CTX *tmp_ctx = talloc_new(tree);
6666 struct fsctl_dup_extents_to_file dup_ext_buf;
6667 enum ndr_err_code ndr_ret;
6668 bool ok;
6669 struct smb2_lock lck;
6670 struct smb2_lock_element el[1];
6672 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
6673 &src_h, 32768, /* fill 32768 byte src file */
6674 SEC_RIGHTS_FILE_ALL,
6675 &dest_h, 0,
6676 SEC_RIGHTS_FILE_ALL,
6677 &dup_ext_buf,
6678 &ioctl);
6679 if (!ok) {
6680 torture_fail(tctx, "setup dup extents error");
6683 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
6684 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
6685 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
6686 if (!ok) {
6687 smb2_util_close(tree, src_h);
6688 smb2_util_close(tree, dest_h);
6689 talloc_free(tmp_ctx);
6690 torture_skip(tctx, "block refcounting not supported\n");
6693 /* dest pattern is different to src */
6694 ok = write_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 32768);
6695 torture_assert(tctx, ok, "write pattern");
6697 /* setup dup ext req, values used for locking */
6698 dup_ext_buf.source_off = 0;
6699 dup_ext_buf.target_off = 0;
6700 dup_ext_buf.byte_count = 32768;
6702 /* open and lock the dup extents src file */
6703 status = torture_smb2_testfile(tree, FNAME, &src_h2);
6704 torture_assert_ntstatus_ok(tctx, status, "2nd src open");
6706 lck.in.lock_count = 0x0001;
6707 lck.in.lock_sequence = 0x00000000;
6708 lck.in.file.handle = src_h2;
6709 lck.in.locks = el;
6710 el[0].offset = dup_ext_buf.source_off;
6711 el[0].length = dup_ext_buf.byte_count;
6712 el[0].reserved = 0;
6713 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
6715 status = smb2_lock(tree, &lck);
6716 torture_assert_ntstatus_ok(tctx, status, "lock");
6718 status = smb2_util_write(tree, src_h,
6719 "conflicted", 0, sizeof("conflicted"));
6720 torture_assert_ntstatus_equal(tctx, status,
6721 NT_STATUS_FILE_LOCK_CONFLICT, "file write");
6723 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6724 &dup_ext_buf,
6725 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6726 torture_assert_ndr_success(tctx, ndr_ret,
6727 "ndr_push_fsctl_dup_extents_to_file");
6730 * In contrast to copy-chunk, dup extents doesn't cause a lock conflict
6731 * here.
6733 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6734 torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
6736 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
6737 if (!ok) {
6738 torture_fail(tctx, "inconsistent file data");
6741 lck.in.lock_count = 0x0001;
6742 lck.in.lock_sequence = 0x00000001;
6743 lck.in.file.handle = src_h2;
6744 lck.in.locks = el;
6745 el[0].offset = dup_ext_buf.source_off;
6746 el[0].length = dup_ext_buf.byte_count;
6747 el[0].reserved = 0;
6748 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
6749 status = smb2_lock(tree, &lck);
6750 torture_assert_ntstatus_ok(tctx, status, "unlock");
6752 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6753 torture_assert_ntstatus_ok(tctx, status,
6754 "FSCTL_DUP_EXTENTS_TO_FILE unlocked");
6756 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
6757 if (!ok) {
6758 torture_fail(tctx, "inconsistent file data");
6761 smb2_util_close(tree, src_h2);
6762 smb2_util_close(tree, src_h);
6763 smb2_util_close(tree, dest_h);
6764 talloc_free(tmp_ctx);
6765 return true;
6768 static bool test_ioctl_dup_extents_dest_lck(struct torture_context *tctx,
6769 struct smb2_tree *tree)
6771 struct smb2_handle src_h;
6772 struct smb2_handle dest_h;
6773 struct smb2_handle dest_h2;
6774 NTSTATUS status;
6775 union smb_ioctl ioctl;
6776 TALLOC_CTX *tmp_ctx = talloc_new(tree);
6777 struct fsctl_dup_extents_to_file dup_ext_buf;
6778 enum ndr_err_code ndr_ret;
6779 bool ok;
6780 struct smb2_lock lck;
6781 struct smb2_lock_element el[1];
6783 ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
6784 &src_h, 32768, /* fill 32768 byte src file */
6785 SEC_RIGHTS_FILE_ALL,
6786 &dest_h, 0,
6787 SEC_RIGHTS_FILE_ALL,
6788 &dup_ext_buf,
6789 &ioctl);
6790 if (!ok) {
6791 torture_fail(tctx, "setup dup extents error");
6794 status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
6795 FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
6796 torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
6797 if (!ok) {
6798 smb2_util_close(tree, src_h);
6799 smb2_util_close(tree, dest_h);
6800 talloc_free(tmp_ctx);
6801 torture_skip(tctx, "block refcounting not supported\n");
6804 /* dest pattern is different to src */
6805 ok = write_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 32768);
6806 torture_assert(tctx, ok, "write pattern");
6808 /* setup dup ext req, values used for locking */
6809 dup_ext_buf.source_off = 0;
6810 dup_ext_buf.target_off = 0;
6811 dup_ext_buf.byte_count = 32768;
6813 /* open and lock the dup extents dest file */
6814 status = torture_smb2_testfile(tree, FNAME2, &dest_h2);
6815 torture_assert_ntstatus_ok(tctx, status, "2nd src open");
6817 lck.in.lock_count = 0x0001;
6818 lck.in.lock_sequence = 0x00000000;
6819 lck.in.file.handle = dest_h2;
6820 lck.in.locks = el;
6821 el[0].offset = dup_ext_buf.source_off;
6822 el[0].length = dup_ext_buf.byte_count;
6823 el[0].reserved = 0;
6824 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
6826 status = smb2_lock(tree, &lck);
6827 torture_assert_ntstatus_ok(tctx, status, "lock");
6829 status = smb2_util_write(tree, dest_h,
6830 "conflicted", 0, sizeof("conflicted"));
6831 torture_assert_ntstatus_equal(tctx, status,
6832 NT_STATUS_FILE_LOCK_CONFLICT, "file write");
6834 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6835 &dup_ext_buf,
6836 (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6837 torture_assert_ndr_success(tctx, ndr_ret,
6838 "ndr_push_fsctl_dup_extents_to_file");
6841 * In contrast to copy-chunk, dup extents doesn't cause a lock conflict
6842 * here.
6844 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6845 torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
6847 lck.in.lock_count = 0x0001;
6848 lck.in.lock_sequence = 0x00000001;
6849 lck.in.file.handle = dest_h2;
6850 lck.in.locks = el;
6851 el[0].offset = dup_ext_buf.source_off;
6852 el[0].length = dup_ext_buf.byte_count;
6853 el[0].reserved = 0;
6854 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
6855 status = smb2_lock(tree, &lck);
6856 torture_assert_ntstatus_ok(tctx, status, "unlock");
6858 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6859 torture_assert_ntstatus_ok(tctx, status,
6860 "FSCTL_DUP_EXTENTS_TO_FILE unlocked");
6862 ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
6863 if (!ok) {
6864 torture_fail(tctx, "inconsistent file data");
6867 smb2_util_close(tree, src_h);
6868 smb2_util_close(tree, dest_h);
6869 smb2_util_close(tree, dest_h2);
6870 talloc_free(tmp_ctx);
6871 return true;
6875 basic regression test for BUG 14607
6876 https://bugzilla.samba.org/show_bug.cgi?id=14607
6878 static bool test_ioctl_bug14607(struct torture_context *torture,
6879 struct smb2_tree *tree)
6881 TALLOC_CTX *tmp_ctx = talloc_new(tree);
6882 uint32_t timeout_msec;
6883 NTSTATUS status;
6884 DATA_BLOB out_input_buffer = data_blob_null;
6885 DATA_BLOB out_output_buffer = data_blob_null;
6887 timeout_msec = tree->session->transport->options.request_timeout * 1000;
6889 status = smb2cli_ioctl(tree->session->transport->conn,
6890 timeout_msec,
6891 tree->session->smbXcli,
6892 tree->smbXcli,
6893 UINT64_MAX, /* in_fid_persistent */
6894 UINT64_MAX, /* in_fid_volatile */
6895 FSCTL_SMBTORTURE_IOCTL_RESPONSE_BODY_PADDING8,
6896 0, /* in_max_input_length */
6897 NULL, /* in_input_buffer */
6898 1, /* in_max_output_length */
6899 NULL, /* in_output_buffer */
6900 SMB2_IOCTL_FLAG_IS_FSCTL,
6901 tmp_ctx,
6902 &out_input_buffer,
6903 &out_output_buffer);
6904 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED) ||
6905 NT_STATUS_EQUAL(status, NT_STATUS_FILE_CLOSED) ||
6906 NT_STATUS_EQUAL(status, NT_STATUS_FS_DRIVER_REQUIRED) ||
6907 NT_STATUS_EQUAL(status, NT_STATUS_INVALID_DEVICE_REQUEST))
6909 torture_comment(torture,
6910 "FSCTL_SMBTORTURE_IOCTL_RESPONSE_BODY_PADDING8: %s\n",
6911 nt_errstr(status));
6912 torture_skip(torture, "server doesn't support FSCTL_SMBTORTURE_IOCTL_RESPONSE_BODY_PADDING8\n");
6914 torture_assert_ntstatus_ok(torture, status, "FSCTL_SMBTORTURE_IOCTL_RESPONSE_BODY_PADDING8");
6916 torture_assert_int_equal(torture, out_output_buffer.length, 1,
6917 "output length");
6918 torture_assert_int_equal(torture, out_output_buffer.data[0], 8,
6919 "output buffer byte should be 8");
6921 talloc_free(tmp_ctx);
6922 return true;
6926 basic regression test for BUG 14769
6927 https://bugzilla.samba.org/show_bug.cgi?id=14769
6929 static bool test_ioctl_bug14769(struct torture_context *torture,
6930 struct smb2_tree *tree)
6932 NTSTATUS status;
6933 const char *fname = "bug14769";
6934 bool ret = false;
6935 struct smb2_handle h;
6936 struct smb2_ioctl ioctl;
6937 struct smb2_close cl;
6938 struct smb2_request *smb2arr[2] = { 0 };
6939 uint8_t tosend_msec = 200;
6940 DATA_BLOB send_buf = { &tosend_msec, 1 };
6942 /* Create a test file. */
6943 smb2_util_unlink(tree, fname);
6944 status = torture_smb2_testfile(tree, fname, &h);
6945 torture_assert_ntstatus_ok(torture, status, "create bug14769");
6948 * Send (not receive) the FSCTL.
6949 * This should go async with a wait time of 200 msec.
6951 ZERO_STRUCT(ioctl);
6952 ioctl.in.file.handle = h;
6953 ioctl.in.function = FSCTL_SMBTORTURE_FSP_ASYNC_SLEEP;
6954 ioctl.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
6955 ioctl.in.out = send_buf;
6957 smb2arr[0] = smb2_ioctl_send(tree, &ioctl);
6958 torture_assert_goto(torture,
6959 smb2arr[0] != NULL,
6960 ret,
6961 done,
6962 "smb2_ioctl_send failed\n");
6963 /* Immediately send the close. */
6964 ZERO_STRUCT(cl);
6965 cl.in.file.handle = h;
6966 cl.in.flags = 0;
6967 smb2arr[1] = smb2_close_send(tree, &cl);
6968 torture_assert_goto(torture,
6969 smb2arr[1] != NULL,
6970 ret,
6971 done,
6972 "smb2_close_send failed\n");
6974 /* Now get the FSCTL reply. */
6976 * If we suffer from bug #14769 this will fail as
6977 * the ioctl will return with NT_STATUS_FILE_CLOSED,
6978 * as the close will have closed the handle without
6979 * waiting for the ioctl to complete. The server shouldn't
6980 * complete the close until the ioctl finishes.
6982 status = smb2_ioctl_recv(smb2arr[0], tree, &ioctl);
6983 torture_assert_ntstatus_ok_goto(torture,
6984 status,
6985 ret,
6986 done,
6987 "smb2_ioctl_recv failed\n");
6989 /* Followed by the close reply. */
6990 status = smb2_close_recv(smb2arr[1], &cl);
6991 torture_assert_ntstatus_ok_goto(torture,
6992 status,
6993 ret,
6994 done,
6995 "smb2_ioctl_close failed\n");
6996 ret = true;
6998 done:
6999 smb2_util_unlink(tree, fname);
7000 return ret;
7004 basic regression test for BUG 14788,
7005 with FSCTL_VALIDATE_NEGOTIATE_INFO
7006 https://bugzilla.samba.org/show_bug.cgi?id=14788
7008 static bool test_ioctl_bug14788_VALIDATE_NEGOTIATE(struct torture_context *torture,
7009 struct smb2_tree *tree0)
7011 const char *host = torture_setting_string(torture, "host", NULL);
7012 const char *share = torture_setting_string(torture, "share", NULL);
7013 const char *noperm_share = torture_setting_string(torture, "noperm_share", "noperm");
7014 struct smb2_transport *transport0 = tree0->session->transport;
7015 struct smbcli_options options;
7016 struct smb2_transport *transport = NULL;
7017 struct smb2_tree *tree = NULL;
7018 struct smb2_session *session = NULL;
7019 uint16_t noperm_flags = 0;
7020 const char *noperm_unc = NULL;
7021 struct smb2_tree *noperm_tree = NULL;
7022 uint32_t timeout_msec;
7023 struct tevent_req *subreq = NULL;
7024 struct cli_credentials *credentials = samba_cmdline_get_creds();
7025 NTSTATUS status;
7027 if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_00) {
7028 torture_skip(torture, "Can't test without SMB 3 support");
7031 options = transport0->options;
7032 options.client_guid = GUID_random();
7033 options.min_protocol = PROTOCOL_SMB3_00;
7034 options.max_protocol = PROTOCOL_SMB3_02;
7036 status = smb2_connect(torture,
7037 host,
7038 lpcfg_smb_ports(torture->lp_ctx),
7039 share,
7040 lpcfg_resolve_context(torture->lp_ctx),
7041 credentials,
7042 &tree,
7043 torture->ev,
7044 &options,
7045 lpcfg_socket_options(torture->lp_ctx),
7046 lpcfg_gensec_settings(torture, torture->lp_ctx)
7048 torture_assert_ntstatus_ok(torture, status, "smb2_connect options failed");
7049 session = tree->session;
7050 transport = session->transport;
7052 timeout_msec = tree->session->transport->options.request_timeout * 1000;
7054 subreq = smb2cli_validate_negotiate_info_send(torture,
7055 torture->ev,
7056 transport->conn,
7057 timeout_msec,
7058 session->smbXcli,
7059 tree->smbXcli);
7060 torture_assert(torture,
7061 tevent_req_poll_ntstatus(subreq, torture->ev, &status),
7062 "tevent_req_poll_ntstatus");
7063 status = smb2cli_validate_negotiate_info_recv(subreq);
7064 torture_assert_ntstatus_ok(torture, status, "smb2cli_validate_negotiate_info");
7066 noperm_unc = talloc_asprintf(torture, "\\\\%s\\%s", host, noperm_share);
7067 torture_assert(torture, noperm_unc != NULL, "talloc_asprintf");
7069 noperm_tree = smb2_tree_init(session, torture, false);
7070 torture_assert(torture, noperm_tree != NULL, "smb2_tree_init");
7072 status = smb2cli_raw_tcon(transport->conn,
7073 SMB2_HDR_FLAG_SIGNED,
7074 0, /* clear_flags */
7075 timeout_msec,
7076 session->smbXcli,
7077 noperm_tree->smbXcli,
7078 noperm_flags,
7079 noperm_unc);
7080 if (NT_STATUS_EQUAL(status, NT_STATUS_BAD_NETWORK_NAME)) {
7081 torture_skip(torture, talloc_asprintf(torture,
7082 "noperm_unc[%s] %s",
7083 noperm_unc, nt_errstr(status)));
7085 torture_assert_ntstatus_ok(torture, status,
7086 talloc_asprintf(torture,
7087 "smb2cli_tcon(%s)",
7088 noperm_unc));
7090 subreq = smb2cli_validate_negotiate_info_send(torture,
7091 torture->ev,
7092 transport->conn,
7093 timeout_msec,
7094 session->smbXcli,
7095 noperm_tree->smbXcli);
7096 torture_assert(torture,
7097 tevent_req_poll_ntstatus(subreq, torture->ev, &status),
7098 "tevent_req_poll_ntstatus");
7099 status = smb2cli_validate_negotiate_info_recv(subreq);
7100 torture_assert_ntstatus_ok(torture, status, "smb2cli_validate_negotiate_info noperm");
7102 return true;
7106 basic regression test for BUG 14788,
7107 with FSCTL_QUERY_NETWORK_INTERFACE_INFO
7108 https://bugzilla.samba.org/show_bug.cgi?id=14788
7110 static bool test_ioctl_bug14788_NETWORK_INTERFACE(struct torture_context *torture,
7111 struct smb2_tree *tree0)
7113 const char *host = torture_setting_string(torture, "host", NULL);
7114 const char *share = torture_setting_string(torture, "share", NULL);
7115 const char *noperm_share = torture_setting_string(torture, "noperm_share", "noperm");
7116 struct smb2_transport *transport0 = tree0->session->transport;
7117 struct smbcli_options options;
7118 struct smb2_transport *transport = NULL;
7119 struct smb2_tree *tree = NULL;
7120 struct smb2_session *session = NULL;
7121 uint16_t noperm_flags = 0;
7122 const char *noperm_unc = NULL;
7123 struct smb2_tree *noperm_tree = NULL;
7124 uint32_t timeout_msec;
7125 DATA_BLOB out_input_buffer = data_blob_null;
7126 DATA_BLOB out_output_buffer = data_blob_null;
7127 struct cli_credentials *credentials = samba_cmdline_get_creds();
7128 NTSTATUS status;
7130 if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_00) {
7131 torture_skip(torture, "Can't test without SMB 3 support");
7134 options = transport0->options;
7135 options.client_guid = GUID_random();
7136 options.min_protocol = PROTOCOL_SMB3_00;
7137 options.max_protocol = PROTOCOL_SMB3_02;
7139 status = smb2_connect(torture,
7140 host,
7141 lpcfg_smb_ports(torture->lp_ctx),
7142 share,
7143 lpcfg_resolve_context(torture->lp_ctx),
7144 credentials,
7145 &tree,
7146 torture->ev,
7147 &options,
7148 lpcfg_socket_options(torture->lp_ctx),
7149 lpcfg_gensec_settings(torture, torture->lp_ctx)
7151 torture_assert_ntstatus_ok(torture, status, "smb2_connect options failed");
7152 session = tree->session;
7153 transport = session->transport;
7155 timeout_msec = tree->session->transport->options.request_timeout * 1000;
7158 * A valid FSCTL_QUERY_NETWORK_INTERFACE_INFO
7160 status = smb2cli_ioctl(transport->conn,
7161 timeout_msec,
7162 session->smbXcli,
7163 tree->smbXcli,
7164 UINT64_MAX, /* in_fid_persistent */
7165 UINT64_MAX, /* in_fid_volatile */
7166 FSCTL_QUERY_NETWORK_INTERFACE_INFO,
7167 0, /* in_max_input_length */
7168 NULL, /* in_input_buffer */
7169 UINT16_MAX, /* in_max_output_length */
7170 NULL, /* in_output_buffer */
7171 SMB2_IOCTL_FLAG_IS_FSCTL,
7172 torture,
7173 &out_input_buffer,
7174 &out_output_buffer);
7175 torture_assert_ntstatus_ok(torture, status,
7176 "FSCTL_QUERY_NETWORK_INTERFACE_INFO");
7179 * An invalid FSCTL_QUERY_NETWORK_INTERFACE_INFO,
7180 * with file_id_* is being UINT64_MAX and
7181 * in_max_output_length = 1.
7183 * This demonstrates NT_STATUS_BUFFER_TOO_SMALL
7184 * if the server is not able to return the
7185 * whole response buffer to the client.
7187 status = smb2cli_ioctl(transport->conn,
7188 timeout_msec,
7189 session->smbXcli,
7190 tree->smbXcli,
7191 UINT64_MAX, /* in_fid_persistent */
7192 UINT64_MAX, /* in_fid_volatile */
7193 FSCTL_QUERY_NETWORK_INTERFACE_INFO,
7194 0, /* in_max_input_length */
7195 NULL, /* in_input_buffer */
7196 1, /* in_max_output_length */
7197 NULL, /* in_output_buffer */
7198 SMB2_IOCTL_FLAG_IS_FSCTL,
7199 torture,
7200 &out_input_buffer,
7201 &out_output_buffer);
7202 torture_assert_ntstatus_equal(torture, status,
7203 NT_STATUS_BUFFER_TOO_SMALL,
7204 "FSCTL_QUERY_NETWORK_INTERFACE_INFO");
7207 * An invalid FSCTL_QUERY_NETWORK_INTERFACE_INFO,
7208 * with file_id_* not being UINT64_MAX.
7210 * This gives INVALID_PARAMETER instead
7211 * of FILE_CLOSED.
7213 status = smb2cli_ioctl(transport->conn,
7214 timeout_msec,
7215 session->smbXcli,
7216 tree->smbXcli,
7217 INT64_MAX, /* in_fid_persistent */
7218 INT64_MAX, /* in_fid_volatile */
7219 FSCTL_QUERY_NETWORK_INTERFACE_INFO,
7220 0, /* in_max_input_length */
7221 NULL, /* in_input_buffer */
7222 UINT16_MAX, /* in_max_output_length */
7223 NULL, /* in_output_buffer */
7224 SMB2_IOCTL_FLAG_IS_FSCTL,
7225 torture,
7226 &out_input_buffer,
7227 &out_output_buffer);
7228 torture_assert_ntstatus_equal(torture, status,
7229 NT_STATUS_INVALID_PARAMETER,
7230 "FSCTL_QUERY_NETWORK_INTERFACE_INFO");
7233 * An invalid FSCTL_QUERY_NETWORK_INTERFACE_INFO,
7234 * with file_id_* not being UINT64_MAX and
7235 * in_max_output_length = 1.
7237 * This proves INVALID_PARAMETER instead
7238 * of BUFFER_TOO_SMALL.
7240 status = smb2cli_ioctl(transport->conn,
7241 timeout_msec,
7242 session->smbXcli,
7243 tree->smbXcli,
7244 INT64_MAX, /* in_fid_persistent */
7245 INT64_MAX, /* in_fid_volatile */
7246 FSCTL_QUERY_NETWORK_INTERFACE_INFO,
7247 0, /* in_max_input_length */
7248 NULL, /* in_input_buffer */
7249 1, /* in_max_output_length */
7250 NULL, /* in_output_buffer */
7251 SMB2_IOCTL_FLAG_IS_FSCTL,
7252 torture,
7253 &out_input_buffer,
7254 &out_output_buffer);
7255 torture_assert_ntstatus_equal(torture, status,
7256 NT_STATUS_INVALID_PARAMETER,
7257 "FSCTL_QUERY_NETWORK_INTERFACE_INFO");
7259 noperm_unc = talloc_asprintf(torture, "\\\\%s\\%s", host, noperm_share);
7260 torture_assert(torture, noperm_unc != NULL, "talloc_asprintf");
7262 noperm_tree = smb2_tree_init(session, torture, false);
7263 torture_assert(torture, noperm_tree != NULL, "smb2_tree_init");
7265 status = smb2cli_raw_tcon(transport->conn,
7266 SMB2_HDR_FLAG_SIGNED,
7267 0, /* clear_flags */
7268 timeout_msec,
7269 session->smbXcli,
7270 noperm_tree->smbXcli,
7271 noperm_flags,
7272 noperm_unc);
7273 if (NT_STATUS_EQUAL(status, NT_STATUS_BAD_NETWORK_NAME)) {
7274 torture_skip(torture, talloc_asprintf(torture,
7275 "noperm_unc[%s] %s",
7276 noperm_unc, nt_errstr(status)));
7278 torture_assert_ntstatus_ok(torture, status,
7279 talloc_asprintf(torture,
7280 "smb2cli_tcon(%s)",
7281 noperm_unc));
7284 * A valid FSCTL_QUERY_NETWORK_INTERFACE_INFO
7286 status = smb2cli_ioctl(transport->conn,
7287 timeout_msec,
7288 session->smbXcli,
7289 noperm_tree->smbXcli,
7290 UINT64_MAX, /* in_fid_persistent */
7291 UINT64_MAX, /* in_fid_volatile */
7292 FSCTL_QUERY_NETWORK_INTERFACE_INFO,
7293 0, /* in_max_input_length */
7294 NULL, /* in_input_buffer */
7295 UINT16_MAX, /* in_max_output_length */
7296 NULL, /* in_output_buffer */
7297 SMB2_IOCTL_FLAG_IS_FSCTL,
7298 torture,
7299 &out_input_buffer,
7300 &out_output_buffer);
7301 torture_assert_ntstatus_ok(torture, status,
7302 "FSCTL_QUERY_NETWORK_INTERFACE_INFO");
7305 * An invalid FSCTL_QUERY_NETWORK_INTERFACE_INFO,
7306 * with file_id_* is being UINT64_MAX and
7307 * in_max_output_length = 1.
7309 * This demonstrates NT_STATUS_BUFFER_TOO_SMALL
7310 * if the server is not able to return the
7311 * whole response buffer to the client.
7313 status = smb2cli_ioctl(transport->conn,
7314 timeout_msec,
7315 session->smbXcli,
7316 noperm_tree->smbXcli,
7317 UINT64_MAX, /* in_fid_persistent */
7318 UINT64_MAX, /* in_fid_volatile */
7319 FSCTL_QUERY_NETWORK_INTERFACE_INFO,
7320 0, /* in_max_input_length */
7321 NULL, /* in_input_buffer */
7322 1, /* in_max_output_length */
7323 NULL, /* in_output_buffer */
7324 SMB2_IOCTL_FLAG_IS_FSCTL,
7325 torture,
7326 &out_input_buffer,
7327 &out_output_buffer);
7328 torture_assert_ntstatus_equal(torture, status,
7329 NT_STATUS_BUFFER_TOO_SMALL,
7330 "FSCTL_QUERY_NETWORK_INTERFACE_INFO");
7333 * An invalid FSCTL_QUERY_NETWORK_INTERFACE_INFO,
7334 * with file_id_* not being UINT64_MAX.
7336 * This gives INVALID_PARAMETER instead
7337 * of FILE_CLOSED.
7339 status = smb2cli_ioctl(transport->conn,
7340 timeout_msec,
7341 session->smbXcli,
7342 noperm_tree->smbXcli,
7343 INT64_MAX, /* in_fid_persistent */
7344 INT64_MAX, /* in_fid_volatile */
7345 FSCTL_QUERY_NETWORK_INTERFACE_INFO,
7346 0, /* in_max_input_length */
7347 NULL, /* in_input_buffer */
7348 UINT16_MAX, /* in_max_output_length */
7349 NULL, /* in_output_buffer */
7350 SMB2_IOCTL_FLAG_IS_FSCTL,
7351 torture,
7352 &out_input_buffer,
7353 &out_output_buffer);
7354 torture_assert_ntstatus_equal(torture, status,
7355 NT_STATUS_INVALID_PARAMETER,
7356 "FSCTL_QUERY_NETWORK_INTERFACE_INFO");
7359 * An invalid FSCTL_QUERY_NETWORK_INTERFACE_INFO,
7360 * with file_id_* not being UINT64_MAX and
7361 * in_max_output_length = 1.
7363 * This proves INVALID_PARAMETER instead
7364 * of BUFFER_TOO_SMALL.
7366 status = smb2cli_ioctl(transport->conn,
7367 timeout_msec,
7368 session->smbXcli,
7369 noperm_tree->smbXcli,
7370 INT64_MAX, /* in_fid_persistent */
7371 INT64_MAX, /* in_fid_volatile */
7372 FSCTL_QUERY_NETWORK_INTERFACE_INFO,
7373 0, /* in_max_input_length */
7374 NULL, /* in_input_buffer */
7375 1, /* in_max_output_length */
7376 NULL, /* in_output_buffer */
7377 SMB2_IOCTL_FLAG_IS_FSCTL,
7378 torture,
7379 &out_input_buffer,
7380 &out_output_buffer);
7381 torture_assert_ntstatus_equal(torture, status,
7382 NT_STATUS_INVALID_PARAMETER,
7383 "FSCTL_QUERY_NETWORK_INTERFACE_INFO");
7385 return true;
7389 * testing of SMB2 ioctls
7391 struct torture_suite *torture_smb2_ioctl_init(TALLOC_CTX *ctx)
7393 struct torture_suite *suite = torture_suite_create(ctx, "ioctl");
7394 struct torture_suite *bug14788 = torture_suite_create(ctx, "bug14788");
7396 torture_suite_add_1smb2_test(suite, "shadow_copy",
7397 test_ioctl_get_shadow_copy);
7398 torture_suite_add_1smb2_test(suite, "req_resume_key",
7399 test_ioctl_req_resume_key);
7400 torture_suite_add_1smb2_test(suite, "req_two_resume_keys",
7401 test_ioctl_req_two_resume_keys);
7402 torture_suite_add_1smb2_test(suite, "copy_chunk_simple",
7403 test_ioctl_copy_chunk_simple);
7404 torture_suite_add_1smb2_test(suite, "copy_chunk_multi",
7405 test_ioctl_copy_chunk_multi);
7406 torture_suite_add_1smb2_test(suite, "copy_chunk_tiny",
7407 test_ioctl_copy_chunk_tiny);
7408 torture_suite_add_1smb2_test(suite, "copy_chunk_overwrite",
7409 test_ioctl_copy_chunk_over);
7410 torture_suite_add_1smb2_test(suite, "copy_chunk_append",
7411 test_ioctl_copy_chunk_append);
7412 torture_suite_add_1smb2_test(suite, "copy_chunk_limits",
7413 test_ioctl_copy_chunk_limits);
7414 torture_suite_add_1smb2_test(suite, "copy_chunk_src_lock",
7415 test_ioctl_copy_chunk_src_lck);
7416 torture_suite_add_1smb2_test(suite, "copy_chunk_dest_lock",
7417 test_ioctl_copy_chunk_dest_lck);
7418 torture_suite_add_1smb2_test(suite, "copy_chunk_bad_key",
7419 test_ioctl_copy_chunk_bad_key);
7420 torture_suite_add_1smb2_test(suite, "copy_chunk_src_is_dest",
7421 test_ioctl_copy_chunk_src_is_dest);
7422 torture_suite_add_1smb2_test(suite, "copy_chunk_src_is_dest_overlap",
7423 test_ioctl_copy_chunk_src_is_dest_overlap);
7424 torture_suite_add_1smb2_test(suite, "copy_chunk_bad_access",
7425 test_ioctl_copy_chunk_bad_access);
7426 torture_suite_add_1smb2_test(suite, "copy_chunk_write_access",
7427 test_ioctl_copy_chunk_write_access);
7428 torture_suite_add_1smb2_test(suite, "copy_chunk_src_exceed",
7429 test_ioctl_copy_chunk_src_exceed);
7430 torture_suite_add_1smb2_test(suite, "copy_chunk_src_exceed_multi",
7431 test_ioctl_copy_chunk_src_exceed_multi);
7432 torture_suite_add_1smb2_test(suite, "copy_chunk_sparse_dest",
7433 test_ioctl_copy_chunk_sparse_dest);
7434 torture_suite_add_1smb2_test(suite, "copy_chunk_max_output_sz",
7435 test_ioctl_copy_chunk_max_output_sz);
7436 torture_suite_add_1smb2_test(suite, "copy_chunk_zero_length",
7437 test_ioctl_copy_chunk_zero_length);
7438 torture_suite_add_1smb2_test(suite, "copy-chunk streams",
7439 test_copy_chunk_streams);
7440 torture_suite_add_1smb2_test(suite, "copy_chunk_across_shares",
7441 test_copy_chunk_across_shares);
7442 torture_suite_add_1smb2_test(suite, "copy_chunk_across_shares2",
7443 test_copy_chunk_across_shares2);
7444 torture_suite_add_1smb2_test(suite, "copy_chunk_across_shares3",
7445 test_copy_chunk_across_shares3);
7446 torture_suite_add_1smb2_test(suite, "compress_file_flag",
7447 test_ioctl_compress_file_flag);
7448 torture_suite_add_1smb2_test(suite, "compress_dir_inherit",
7449 test_ioctl_compress_dir_inherit);
7450 torture_suite_add_1smb2_test(suite, "compress_invalid_format",
7451 test_ioctl_compress_invalid_format);
7452 torture_suite_add_1smb2_test(suite, "compress_invalid_buf",
7453 test_ioctl_compress_invalid_buf);
7454 torture_suite_add_1smb2_test(suite, "compress_query_file_attr",
7455 test_ioctl_compress_query_file_attr);
7456 torture_suite_add_1smb2_test(suite, "compress_create_with_attr",
7457 test_ioctl_compress_create_with_attr);
7458 torture_suite_add_1smb2_test(suite, "compress_inherit_disable",
7459 test_ioctl_compress_inherit_disable);
7460 torture_suite_add_1smb2_test(suite, "compress_set_file_attr",
7461 test_ioctl_compress_set_file_attr);
7462 torture_suite_add_1smb2_test(suite, "compress_perms",
7463 test_ioctl_compress_perms);
7464 torture_suite_add_1smb2_test(suite, "compress_notsup_get",
7465 test_ioctl_compress_notsup_get);
7466 torture_suite_add_1smb2_test(suite, "compress_notsup_set",
7467 test_ioctl_compress_notsup_set);
7468 torture_suite_add_1smb2_test(suite, "network_interface_info",
7469 test_ioctl_network_interface_info);
7470 torture_suite_add_1smb2_test(suite, "sparse_file_flag",
7471 test_ioctl_sparse_file_flag);
7472 torture_suite_add_1smb2_test(suite, "sparse_file_attr",
7473 test_ioctl_sparse_file_attr);
7474 torture_suite_add_1smb2_test(suite, "sparse_dir_flag",
7475 test_ioctl_sparse_dir_flag);
7476 torture_suite_add_1smb2_test(suite, "sparse_set_nobuf",
7477 test_ioctl_sparse_set_nobuf);
7478 torture_suite_add_1smb2_test(suite, "sparse_set_oversize",
7479 test_ioctl_sparse_set_oversize);
7480 torture_suite_add_1smb2_test(suite, "sparse_qar",
7481 test_ioctl_sparse_qar);
7482 torture_suite_add_1smb2_test(suite, "sparse_qar_malformed",
7483 test_ioctl_sparse_qar_malformed);
7484 torture_suite_add_1smb2_test(suite, "sparse_punch",
7485 test_ioctl_sparse_punch);
7486 torture_suite_add_1smb2_test(suite, "sparse_hole_dealloc",
7487 test_ioctl_sparse_hole_dealloc);
7488 torture_suite_add_1smb2_test(suite, "sparse_compressed",
7489 test_ioctl_sparse_compressed);
7490 torture_suite_add_1smb2_test(suite, "sparse_copy_chunk",
7491 test_ioctl_sparse_copy_chunk);
7492 torture_suite_add_1smb2_test(suite, "sparse_punch_invalid",
7493 test_ioctl_sparse_punch_invalid);
7494 torture_suite_add_1smb2_test(suite, "sparse_perms",
7495 test_ioctl_sparse_perms);
7496 torture_suite_add_1smb2_test(suite, "sparse_lock",
7497 test_ioctl_sparse_lck);
7498 torture_suite_add_1smb2_test(suite, "sparse_qar_ob1",
7499 test_ioctl_sparse_qar_ob1);
7500 torture_suite_add_1smb2_test(suite, "sparse_qar_multi",
7501 test_ioctl_sparse_qar_multi);
7502 torture_suite_add_1smb2_test(suite, "sparse_qar_overflow",
7503 test_ioctl_sparse_qar_overflow);
7504 torture_suite_add_1smb2_test(suite, "trim_simple",
7505 test_ioctl_trim_simple);
7506 torture_suite_add_1smb2_test(suite, "dup_extents_simple",
7507 test_ioctl_dup_extents_simple);
7508 torture_suite_add_1smb2_test(suite, "dup_extents_len_beyond_dest",
7509 test_ioctl_dup_extents_len_beyond_dest);
7510 torture_suite_add_1smb2_test(suite, "dup_extents_len_beyond_src",
7511 test_ioctl_dup_extents_len_beyond_src);
7512 torture_suite_add_1smb2_test(suite, "dup_extents_len_zero",
7513 test_ioctl_dup_extents_len_zero);
7514 torture_suite_add_1smb2_test(suite, "dup_extents_sparse_src",
7515 test_ioctl_dup_extents_sparse_src);
7516 torture_suite_add_1smb2_test(suite, "dup_extents_sparse_dest",
7517 test_ioctl_dup_extents_sparse_dest);
7518 torture_suite_add_1smb2_test(suite, "dup_extents_sparse_both",
7519 test_ioctl_dup_extents_sparse_both);
7520 torture_suite_add_1smb2_test(suite, "dup_extents_src_is_dest",
7521 test_ioctl_dup_extents_src_is_dest);
7522 torture_suite_add_1smb2_test(suite, "dup_extents_src_is_dest_overlap",
7523 test_ioctl_dup_extents_src_is_dest_overlap);
7524 torture_suite_add_1smb2_test(suite, "dup_extents_compressed_src",
7525 test_ioctl_dup_extents_compressed_src);
7526 torture_suite_add_1smb2_test(suite, "dup_extents_compressed_dest",
7527 test_ioctl_dup_extents_compressed_dest);
7528 torture_suite_add_1smb2_test(suite, "dup_extents_bad_handle",
7529 test_ioctl_dup_extents_bad_handle);
7530 torture_suite_add_1smb2_test(suite, "dup_extents_src_lock",
7531 test_ioctl_dup_extents_src_lck);
7532 torture_suite_add_1smb2_test(suite, "dup_extents_dest_lock",
7533 test_ioctl_dup_extents_dest_lck);
7534 torture_suite_add_1smb2_test(suite, "bug14607",
7535 test_ioctl_bug14607);
7536 torture_suite_add_1smb2_test(suite, "bug14769",
7537 test_ioctl_bug14769);
7539 torture_suite_add_1smb2_test(bug14788, "VALIDATE_NEGOTIATE",
7540 test_ioctl_bug14788_VALIDATE_NEGOTIATE);
7541 torture_suite_add_1smb2_test(bug14788, "NETWORK_INTERFACE",
7542 test_ioctl_bug14788_NETWORK_INTERFACE);
7543 torture_suite_add_suite(suite, bug14788);
7545 suite->description = talloc_strdup(suite, "SMB2-IOCTL tests");
7547 return suite;