torture: basic FSCTL_QUERY_ALLOCATED_RANGES test
[Samba.git] / source4 / torture / smb2 / ioctl.c
blobb568bed9cb70e600399e75a229193a3c1b546155
1 /*
2 Unix SMB/CIFS implementation.
4 test suite for SMB2 ioctl operations
6 Copyright (C) David Disseldorp 2011-2013
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "includes.h"
23 #include "librpc/gen_ndr/security.h"
24 #include "libcli/smb2/smb2.h"
25 #include "libcli/smb2/smb2_calls.h"
26 #include "torture/torture.h"
27 #include "torture/smb2/proto.h"
28 #include "../libcli/smb/smbXcli_base.h"
29 #include "librpc/gen_ndr/ndr_ioctl.h"
31 #define FNAME "testfsctl.dat"
32 #define FNAME2 "testfsctl2.dat"
33 #define DNAME "testfsctl_dir"
36 basic testing of SMB2 shadow copy calls
38 static bool test_ioctl_get_shadow_copy(struct torture_context *torture,
39 struct smb2_tree *tree)
41 struct smb2_handle h;
42 uint8_t buf[100];
43 NTSTATUS status;
44 union smb_ioctl ioctl;
45 TALLOC_CTX *tmp_ctx = talloc_new(tree);
47 smb2_util_unlink(tree, FNAME);
49 status = torture_smb2_testfile(tree, FNAME, &h);
50 torture_assert_ntstatus_ok(torture, status, "create write");
52 ZERO_ARRAY(buf);
53 status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf));
54 torture_assert_ntstatus_ok(torture, status, "write");
56 ZERO_STRUCT(ioctl);
57 ioctl.smb2.level = RAW_IOCTL_SMB2;
58 ioctl.smb2.in.file.handle = h;
59 ioctl.smb2.in.function = FSCTL_SRV_ENUM_SNAPS;
60 ioctl.smb2.in.max_response_size = 16;
61 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
63 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
64 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)
65 || NT_STATUS_EQUAL(status, NT_STATUS_INVALID_DEVICE_REQUEST)) {
66 torture_skip(torture, "FSCTL_SRV_ENUM_SNAPS not supported\n");
68 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_ENUM_SNAPS");
70 return true;
74 basic testing of the SMB2 server side copy ioctls
76 static bool test_ioctl_req_resume_key(struct torture_context *torture,
77 struct smb2_tree *tree)
79 struct smb2_handle h;
80 uint8_t buf[100];
81 NTSTATUS status;
82 union smb_ioctl ioctl;
83 TALLOC_CTX *tmp_ctx = talloc_new(tree);
84 struct req_resume_key_rsp res_key;
85 enum ndr_err_code ndr_ret;
87 smb2_util_unlink(tree, FNAME);
89 status = torture_smb2_testfile(tree, FNAME, &h);
90 torture_assert_ntstatus_ok(torture, status, "create write");
92 ZERO_ARRAY(buf);
93 status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf));
94 torture_assert_ntstatus_ok(torture, status, "write");
96 ZERO_STRUCT(ioctl);
97 ioctl.smb2.level = RAW_IOCTL_SMB2;
98 ioctl.smb2.in.file.handle = h;
99 ioctl.smb2.in.function = FSCTL_SRV_REQUEST_RESUME_KEY;
100 ioctl.smb2.in.max_response_size = 32;
101 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
103 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
104 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_REQUEST_RESUME_KEY");
106 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, &res_key,
107 (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
108 torture_assert_ndr_success(torture, ndr_ret,
109 "ndr_pull_req_resume_key_rsp");
111 ndr_print_debug((ndr_print_fn_t)ndr_print_req_resume_key_rsp, "yo", &res_key);
113 talloc_free(tmp_ctx);
114 return true;
117 static uint64_t patt_hash(uint64_t off)
119 return off;
122 static bool write_pattern(struct torture_context *torture,
123 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
124 struct smb2_handle h, uint64_t off, uint64_t len,
125 uint64_t patt_off)
127 NTSTATUS status;
128 uint64_t i;
129 uint8_t *buf;
130 uint64_t buf_off = 0;
132 if (len == 0) {
133 return true;
136 buf = talloc_zero_size(mem_ctx, len);
137 torture_assert(torture, (buf != NULL), "no memory for file data buf");
139 for (i = 0; i <= len - 8; i += 8) {
140 SBVAL(buf, i, patt_hash(patt_off));
141 patt_off += 8;
144 while (len > 0) {
145 uint64_t io_sz = MIN(1024 * 1024, len);
146 status = smb2_util_write(tree, h,
147 buf + buf_off, off, io_sz);
148 torture_assert_ntstatus_ok(torture, status, "file write");
150 len -= io_sz;
151 buf_off += io_sz;
152 off += io_sz;
155 return true;
158 static bool check_pattern(struct torture_context *torture,
159 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
160 struct smb2_handle h, uint64_t off, uint64_t len,
161 uint64_t patt_off)
163 uint64_t i;
164 struct smb2_read r;
165 NTSTATUS status;
167 ZERO_STRUCT(r);
168 r.in.file.handle = h;
169 r.in.length = len;
170 r.in.offset = off;
171 status = smb2_read(tree, mem_ctx, &r);
172 torture_assert_ntstatus_ok(torture, status, "read");
174 torture_assert_u64_equal(torture, r.out.data.length, len,
175 "read data len mismatch");
177 for (i = 0; i <= len - 8; i += 8, patt_off += 8) {
178 uint64_t data = BVAL(r.out.data.data, i);
179 torture_assert_u64_equal(torture, data, patt_hash(patt_off),
180 talloc_asprintf(torture, "read data "
181 "pattern bad at %llu\n",
182 (unsigned long long)i));
185 talloc_free(r.out.data.data);
186 return true;
189 static bool test_setup_open(struct torture_context *torture,
190 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
191 const char *fname,
192 struct smb2_handle *fh,
193 uint32_t desired_access,
194 uint32_t file_attributes)
196 struct smb2_create io;
197 NTSTATUS status;
199 ZERO_STRUCT(io);
200 io.in.desired_access = desired_access;
201 io.in.file_attributes = file_attributes;
202 io.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
203 io.in.share_access =
204 NTCREATEX_SHARE_ACCESS_DELETE|
205 NTCREATEX_SHARE_ACCESS_READ|
206 NTCREATEX_SHARE_ACCESS_WRITE;
207 if (file_attributes & FILE_ATTRIBUTE_DIRECTORY) {
208 io.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
210 io.in.fname = fname;
212 status = smb2_create(tree, mem_ctx, &io);
213 torture_assert_ntstatus_ok(torture, status, "file create");
215 *fh = io.out.file.handle;
217 return true;
220 static bool test_setup_create_fill(struct torture_context *torture,
221 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
222 const char *fname,
223 struct smb2_handle *fh,
224 uint64_t size,
225 uint32_t desired_access,
226 uint32_t file_attributes)
228 bool ok;
230 smb2_util_unlink(tree, fname);
232 ok = test_setup_open(torture, tree, mem_ctx,
233 fname,
235 desired_access,
236 file_attributes);
237 torture_assert(torture, ok, "file open");
239 if (size > 0) {
240 ok = write_pattern(torture, tree, mem_ctx, *fh, 0, size, 0);
241 torture_assert(torture, ok, "write pattern");
243 return true;
246 static bool test_setup_copy_chunk(struct torture_context *torture,
247 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
248 uint32_t nchunks,
249 struct smb2_handle *src_h,
250 uint64_t src_size,
251 uint32_t src_desired_access,
252 struct smb2_handle *dest_h,
253 uint64_t dest_size,
254 uint32_t dest_desired_access,
255 struct srv_copychunk_copy *cc_copy,
256 union smb_ioctl *ioctl)
258 struct req_resume_key_rsp res_key;
259 bool ok;
260 NTSTATUS status;
261 enum ndr_err_code ndr_ret;
263 ok = test_setup_create_fill(torture, tree, mem_ctx, FNAME,
264 src_h, src_size, src_desired_access,
265 FILE_ATTRIBUTE_NORMAL);
266 torture_assert(torture, ok, "src file create fill");
268 ok = test_setup_create_fill(torture, tree, mem_ctx, FNAME2,
269 dest_h, dest_size, dest_desired_access,
270 FILE_ATTRIBUTE_NORMAL);
271 torture_assert(torture, ok, "dest file create fill");
273 ZERO_STRUCTPN(ioctl);
274 ioctl->smb2.level = RAW_IOCTL_SMB2;
275 ioctl->smb2.in.file.handle = *src_h;
276 ioctl->smb2.in.function = FSCTL_SRV_REQUEST_RESUME_KEY;
277 /* Allow for Key + ContextLength + Context */
278 ioctl->smb2.in.max_response_size = 32;
279 ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
281 status = smb2_ioctl(tree, mem_ctx, &ioctl->smb2);
282 torture_assert_ntstatus_ok(torture, status,
283 "FSCTL_SRV_REQUEST_RESUME_KEY");
285 ndr_ret = ndr_pull_struct_blob(&ioctl->smb2.out.out, mem_ctx, &res_key,
286 (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
288 torture_assert_ndr_success(torture, ndr_ret,
289 "ndr_pull_req_resume_key_rsp");
291 ZERO_STRUCTPN(ioctl);
292 ioctl->smb2.level = RAW_IOCTL_SMB2;
293 ioctl->smb2.in.file.handle = *dest_h;
294 ioctl->smb2.in.function = FSCTL_SRV_COPYCHUNK;
295 ioctl->smb2.in.max_response_size = sizeof(struct srv_copychunk_rsp);
296 ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
298 ZERO_STRUCTPN(cc_copy);
299 memcpy(cc_copy->source_key, res_key.resume_key, ARRAY_SIZE(cc_copy->source_key));
300 cc_copy->chunk_count = nchunks;
301 cc_copy->chunks = talloc_zero_array(mem_ctx, struct srv_copychunk, nchunks);
302 torture_assert(torture, (cc_copy->chunks != NULL), "no memory for chunks");
304 return true;
308 static bool check_copy_chunk_rsp(struct torture_context *torture,
309 struct srv_copychunk_rsp *cc_rsp,
310 uint32_t ex_chunks_written,
311 uint32_t ex_chunk_bytes_written,
312 uint32_t ex_total_bytes_written)
314 torture_assert_int_equal(torture, cc_rsp->chunks_written,
315 ex_chunks_written, "num chunks");
316 torture_assert_int_equal(torture, cc_rsp->chunk_bytes_written,
317 ex_chunk_bytes_written, "chunk bytes written");
318 torture_assert_int_equal(torture, cc_rsp->total_bytes_written,
319 ex_total_bytes_written, "chunk total bytes");
320 return true;
323 static bool test_ioctl_copy_chunk_simple(struct torture_context *torture,
324 struct smb2_tree *tree)
326 struct smb2_handle src_h;
327 struct smb2_handle dest_h;
328 NTSTATUS status;
329 union smb_ioctl ioctl;
330 TALLOC_CTX *tmp_ctx = talloc_new(tree);
331 struct srv_copychunk_copy cc_copy;
332 struct srv_copychunk_rsp cc_rsp;
333 enum ndr_err_code ndr_ret;
334 bool ok;
336 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
337 1, /* 1 chunk */
338 &src_h, 4096, /* fill 4096 byte src file */
339 SEC_RIGHTS_FILE_ALL,
340 &dest_h, 0, /* 0 byte dest file */
341 SEC_RIGHTS_FILE_ALL,
342 &cc_copy,
343 &ioctl);
344 if (!ok) {
345 torture_fail(torture, "setup copy chunk error");
348 /* copy all src file data (via a single chunk desc) */
349 cc_copy.chunks[0].source_off = 0;
350 cc_copy.chunks[0].target_off = 0;
351 cc_copy.chunks[0].length = 4096;
353 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
354 &cc_copy,
355 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
356 torture_assert_ndr_success(torture, ndr_ret,
357 "ndr_push_srv_copychunk_copy");
359 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
360 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
362 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
363 &cc_rsp,
364 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
365 torture_assert_ndr_success(torture, ndr_ret,
366 "ndr_pull_srv_copychunk_rsp");
368 ok = check_copy_chunk_rsp(torture, &cc_rsp,
369 1, /* chunks written */
370 0, /* chunk bytes unsuccessfully written */
371 4096); /* total bytes written */
372 if (!ok) {
373 torture_fail(torture, "bad copy chunk response data");
376 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
377 if (!ok) {
378 torture_fail(torture, "inconsistent file data");
381 smb2_util_close(tree, src_h);
382 smb2_util_close(tree, dest_h);
383 talloc_free(tmp_ctx);
384 return true;
387 static bool test_ioctl_copy_chunk_multi(struct torture_context *torture,
388 struct smb2_tree *tree)
390 struct smb2_handle src_h;
391 struct smb2_handle dest_h;
392 NTSTATUS status;
393 union smb_ioctl ioctl;
394 TALLOC_CTX *tmp_ctx = talloc_new(tree);
395 struct srv_copychunk_copy cc_copy;
396 struct srv_copychunk_rsp cc_rsp;
397 enum ndr_err_code ndr_ret;
398 bool ok;
400 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
401 2, /* chunks */
402 &src_h, 8192, /* src file */
403 SEC_RIGHTS_FILE_ALL,
404 &dest_h, 0, /* dest file */
405 SEC_RIGHTS_FILE_ALL,
406 &cc_copy,
407 &ioctl);
408 if (!ok) {
409 torture_fail(torture, "setup copy chunk error");
412 /* copy all src file data via two chunks */
413 cc_copy.chunks[0].source_off = 0;
414 cc_copy.chunks[0].target_off = 0;
415 cc_copy.chunks[0].length = 4096;
417 cc_copy.chunks[1].source_off = 4096;
418 cc_copy.chunks[1].target_off = 4096;
419 cc_copy.chunks[1].length = 4096;
421 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
422 &cc_copy,
423 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
424 torture_assert_ndr_success(torture, ndr_ret,
425 "ndr_push_srv_copychunk_copy");
427 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
428 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
430 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
431 &cc_rsp,
432 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
433 torture_assert_ndr_success(torture, ndr_ret,
434 "ndr_pull_srv_copychunk_rsp");
436 ok = check_copy_chunk_rsp(torture, &cc_rsp,
437 2, /* chunks written */
438 0, /* chunk bytes unsuccessfully written */
439 8192); /* total bytes written */
440 if (!ok) {
441 torture_fail(torture, "bad copy chunk response data");
444 smb2_util_close(tree, src_h);
445 smb2_util_close(tree, dest_h);
446 talloc_free(tmp_ctx);
447 return true;
450 static bool test_ioctl_copy_chunk_tiny(struct torture_context *torture,
451 struct smb2_tree *tree)
453 struct smb2_handle src_h;
454 struct smb2_handle dest_h;
455 NTSTATUS status;
456 union smb_ioctl ioctl;
457 TALLOC_CTX *tmp_ctx = talloc_new(tree);
458 struct srv_copychunk_copy cc_copy;
459 struct srv_copychunk_rsp cc_rsp;
460 enum ndr_err_code ndr_ret;
461 bool ok;
463 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
464 2, /* chunks */
465 &src_h, 100, /* src file */
466 SEC_RIGHTS_FILE_ALL,
467 &dest_h, 0, /* dest file */
468 SEC_RIGHTS_FILE_ALL,
469 &cc_copy,
470 &ioctl);
471 if (!ok) {
472 torture_fail(torture, "setup copy chunk error");
475 /* copy all src file data via two chunks, sub block size chunks */
476 cc_copy.chunks[0].source_off = 0;
477 cc_copy.chunks[0].target_off = 0;
478 cc_copy.chunks[0].length = 50;
480 cc_copy.chunks[1].source_off = 50;
481 cc_copy.chunks[1].target_off = 50;
482 cc_copy.chunks[1].length = 50;
484 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
485 &cc_copy,
486 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
487 torture_assert_ndr_success(torture, ndr_ret,
488 "ndr_push_srv_copychunk_copy");
490 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
491 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
493 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
494 &cc_rsp,
495 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
496 torture_assert_ndr_success(torture, ndr_ret,
497 "ndr_pull_srv_copychunk_rsp");
499 ok = check_copy_chunk_rsp(torture, &cc_rsp,
500 2, /* chunks written */
501 0, /* chunk bytes unsuccessfully written */
502 100); /* total bytes written */
503 if (!ok) {
504 torture_fail(torture, "bad copy chunk response data");
507 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 100, 0);
508 if (!ok) {
509 torture_fail(torture, "inconsistent file data");
512 smb2_util_close(tree, src_h);
513 smb2_util_close(tree, dest_h);
514 talloc_free(tmp_ctx);
515 return true;
518 static bool test_ioctl_copy_chunk_over(struct torture_context *torture,
519 struct smb2_tree *tree)
521 struct smb2_handle src_h;
522 struct smb2_handle dest_h;
523 NTSTATUS status;
524 union smb_ioctl ioctl;
525 TALLOC_CTX *tmp_ctx = talloc_new(tree);
526 struct srv_copychunk_copy cc_copy;
527 struct srv_copychunk_rsp cc_rsp;
528 enum ndr_err_code ndr_ret;
529 bool ok;
531 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
532 2, /* chunks */
533 &src_h, 8192, /* src file */
534 SEC_RIGHTS_FILE_ALL,
535 &dest_h, 4096, /* dest file */
536 SEC_RIGHTS_FILE_ALL,
537 &cc_copy,
538 &ioctl);
539 if (!ok) {
540 torture_fail(torture, "setup copy chunk error");
543 /* first chunk overwrites existing dest data */
544 cc_copy.chunks[0].source_off = 0;
545 cc_copy.chunks[0].target_off = 0;
546 cc_copy.chunks[0].length = 4096;
548 /* second chunk overwrites the first */
549 cc_copy.chunks[1].source_off = 4096;
550 cc_copy.chunks[1].target_off = 0;
551 cc_copy.chunks[1].length = 4096;
553 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
554 &cc_copy,
555 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
556 torture_assert_ndr_success(torture, ndr_ret,
557 "ndr_push_srv_copychunk_copy");
559 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
560 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
562 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
563 &cc_rsp,
564 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
565 torture_assert_ndr_success(torture, ndr_ret,
566 "ndr_pull_srv_copychunk_rsp");
568 ok = check_copy_chunk_rsp(torture, &cc_rsp,
569 2, /* chunks written */
570 0, /* chunk bytes unsuccessfully written */
571 8192); /* total bytes written */
572 if (!ok) {
573 torture_fail(torture, "bad copy chunk response data");
576 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 4096);
577 if (!ok) {
578 torture_fail(torture, "inconsistent file data");
581 smb2_util_close(tree, src_h);
582 smb2_util_close(tree, dest_h);
583 talloc_free(tmp_ctx);
584 return true;
587 static bool test_ioctl_copy_chunk_append(struct torture_context *torture,
588 struct smb2_tree *tree)
590 struct smb2_handle src_h;
591 struct smb2_handle dest_h;
592 NTSTATUS status;
593 union smb_ioctl ioctl;
594 TALLOC_CTX *tmp_ctx = talloc_new(tree);
595 struct srv_copychunk_copy cc_copy;
596 struct srv_copychunk_rsp cc_rsp;
597 enum ndr_err_code ndr_ret;
598 bool ok;
600 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
601 2, /* chunks */
602 &src_h, 4096, /* src file */
603 SEC_RIGHTS_FILE_ALL,
604 &dest_h, 0, /* dest file */
605 SEC_RIGHTS_FILE_ALL,
606 &cc_copy,
607 &ioctl);
608 if (!ok) {
609 torture_fail(torture, "setup copy chunk error");
612 cc_copy.chunks[0].source_off = 0;
613 cc_copy.chunks[0].target_off = 0;
614 cc_copy.chunks[0].length = 4096;
616 /* second chunk appends the same data to the first */
617 cc_copy.chunks[1].source_off = 0;
618 cc_copy.chunks[1].target_off = 4096;
619 cc_copy.chunks[1].length = 4096;
621 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
622 &cc_copy,
623 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
624 torture_assert_ndr_success(torture, ndr_ret,
625 "ndr_push_srv_copychunk_copy");
627 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
628 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
630 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
631 &cc_rsp,
632 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
633 torture_assert_ndr_success(torture, ndr_ret,
634 "ndr_pull_srv_copychunk_rsp");
636 ok = check_copy_chunk_rsp(torture, &cc_rsp,
637 2, /* chunks written */
638 0, /* chunk bytes unsuccessfully written */
639 8192); /* total bytes written */
640 if (!ok) {
641 torture_fail(torture, "bad copy chunk response data");
644 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
645 if (!ok) {
646 torture_fail(torture, "inconsistent file data");
649 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 4096, 4096, 0);
650 if (!ok) {
651 torture_fail(torture, "inconsistent file data");
654 smb2_util_close(tree, src_h);
655 smb2_util_close(tree, dest_h);
656 talloc_free(tmp_ctx);
657 return true;
660 static bool test_ioctl_copy_chunk_limits(struct torture_context *torture,
661 struct smb2_tree *tree)
663 struct smb2_handle src_h;
664 struct smb2_handle dest_h;
665 NTSTATUS status;
666 union smb_ioctl ioctl;
667 TALLOC_CTX *tmp_ctx = talloc_new(tree);
668 struct srv_copychunk_copy cc_copy;
669 struct srv_copychunk_rsp cc_rsp;
670 enum ndr_err_code ndr_ret;
671 bool ok;
673 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
674 1, /* chunks */
675 &src_h, 4096, /* src file */
676 SEC_RIGHTS_FILE_ALL,
677 &dest_h, 0, /* dest file */
678 SEC_RIGHTS_FILE_ALL,
679 &cc_copy,
680 &ioctl);
681 if (!ok) {
682 torture_fail(torture, "setup copy chunk error");
685 /* send huge chunk length request */
686 cc_copy.chunks[0].source_off = 0;
687 cc_copy.chunks[0].target_off = 0;
688 cc_copy.chunks[0].length = UINT_MAX;
690 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
691 &cc_copy,
692 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
693 torture_assert_ndr_success(torture, ndr_ret, "marshalling request");
695 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
696 torture_assert_ntstatus_equal(torture, status,
697 NT_STATUS_INVALID_PARAMETER,
698 "bad oversize chunk response");
700 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
701 &cc_rsp,
702 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
703 torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
705 torture_comment(torture, "limit max chunks, got %u\n",
706 cc_rsp.chunks_written);
707 torture_comment(torture, "limit max chunk len, got %u\n",
708 cc_rsp.chunk_bytes_written);
709 torture_comment(torture, "limit max total bytes, got %u\n",
710 cc_rsp.total_bytes_written);
712 smb2_util_close(tree, src_h);
713 smb2_util_close(tree, dest_h);
714 talloc_free(tmp_ctx);
715 return true;
718 static bool test_ioctl_copy_chunk_src_lck(struct torture_context *torture,
719 struct smb2_tree *tree)
721 struct smb2_handle src_h;
722 struct smb2_handle src_h2;
723 struct smb2_handle dest_h;
724 NTSTATUS status;
725 union smb_ioctl ioctl;
726 TALLOC_CTX *tmp_ctx = talloc_new(tree);
727 struct srv_copychunk_copy cc_copy;
728 struct srv_copychunk_rsp cc_rsp;
729 enum ndr_err_code ndr_ret;
730 bool ok;
731 struct smb2_lock lck;
732 struct smb2_lock_element el[1];
734 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
735 1, /* chunks */
736 &src_h, 4096, /* src file */
737 SEC_RIGHTS_FILE_ALL,
738 &dest_h, 0, /* dest file */
739 SEC_RIGHTS_FILE_ALL,
740 &cc_copy,
741 &ioctl);
742 if (!ok) {
743 torture_fail(torture, "setup copy chunk error");
746 cc_copy.chunks[0].source_off = 0;
747 cc_copy.chunks[0].target_off = 0;
748 cc_copy.chunks[0].length = 4096;
750 /* open and lock the copychunk src file */
751 status = torture_smb2_testfile(tree, FNAME, &src_h2);
752 torture_assert_ntstatus_ok(torture, status, "2nd src open");
754 lck.in.lock_count = 0x0001;
755 lck.in.lock_sequence = 0x00000000;
756 lck.in.file.handle = src_h2;
757 lck.in.locks = el;
758 el[0].offset = cc_copy.chunks[0].source_off;
759 el[0].length = cc_copy.chunks[0].length;
760 el[0].reserved = 0;
761 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
763 status = smb2_lock(tree, &lck);
764 torture_assert_ntstatus_ok(torture, status, "lock");
766 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
767 &cc_copy,
768 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
769 torture_assert_ndr_success(torture, ndr_ret,
770 "ndr_push_srv_copychunk_copy");
772 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
774 * 2k12 & Samba return lock_conflict, Windows 7 & 2k8 return success...
776 * Edgar Olougouna @ MS wrote:
777 * Regarding the FSCTL_SRV_COPYCHUNK and STATUS_FILE_LOCK_CONFLICT
778 * discrepancy observed between Windows versions, we confirm that the
779 * behavior change is expected.
781 * CopyChunk in Windows Server 2012 use regular Readfile/Writefile APIs
782 * to move the chunks from the source to the destination.
783 * These ReadFile/WriteFile APIs go through the byte-range lock checks,
784 * and this explains the observed STATUS_FILE_LOCK_CONFLICT error.
786 * Prior to Windows Server 2012, CopyChunk used mapped sections to move
787 * the data. And byte range locks are not enforced on mapped I/O, and
788 * this explains the STATUS_SUCCESS observed on Windows Server 2008 R2.
790 torture_assert_ntstatus_equal(torture, status,
791 NT_STATUS_FILE_LOCK_CONFLICT,
792 "FSCTL_SRV_COPYCHUNK locked");
794 /* should get cc response data with the lock conflict status */
795 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
796 &cc_rsp,
797 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
798 torture_assert_ndr_success(torture, ndr_ret,
799 "ndr_pull_srv_copychunk_rsp");
800 ok = check_copy_chunk_rsp(torture, &cc_rsp,
801 0, /* chunks written */
802 0, /* chunk bytes unsuccessfully written */
803 0); /* total bytes written */
805 lck.in.lock_count = 0x0001;
806 lck.in.lock_sequence = 0x00000001;
807 lck.in.file.handle = src_h2;
808 lck.in.locks = el;
809 el[0].offset = cc_copy.chunks[0].source_off;
810 el[0].length = cc_copy.chunks[0].length;
811 el[0].reserved = 0;
812 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
813 status = smb2_lock(tree, &lck);
814 torture_assert_ntstatus_ok(torture, status, "unlock");
816 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
817 torture_assert_ntstatus_ok(torture, status,
818 "FSCTL_SRV_COPYCHUNK unlocked");
820 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
821 &cc_rsp,
822 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
823 torture_assert_ndr_success(torture, ndr_ret,
824 "ndr_pull_srv_copychunk_rsp");
826 ok = check_copy_chunk_rsp(torture, &cc_rsp,
827 1, /* chunks written */
828 0, /* chunk bytes unsuccessfully written */
829 4096); /* total bytes written */
830 if (!ok) {
831 torture_fail(torture, "bad copy chunk response data");
834 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
835 if (!ok) {
836 torture_fail(torture, "inconsistent file data");
839 smb2_util_close(tree, src_h2);
840 smb2_util_close(tree, src_h);
841 smb2_util_close(tree, dest_h);
842 talloc_free(tmp_ctx);
843 return true;
846 static bool test_ioctl_copy_chunk_dest_lck(struct torture_context *torture,
847 struct smb2_tree *tree)
849 struct smb2_handle src_h;
850 struct smb2_handle dest_h;
851 struct smb2_handle dest_h2;
852 NTSTATUS status;
853 union smb_ioctl ioctl;
854 TALLOC_CTX *tmp_ctx = talloc_new(tree);
855 struct srv_copychunk_copy cc_copy;
856 struct srv_copychunk_rsp cc_rsp;
857 enum ndr_err_code ndr_ret;
858 bool ok;
859 struct smb2_lock lck;
860 struct smb2_lock_element el[1];
862 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
863 1, /* chunks */
864 &src_h, 4096, /* src file */
865 SEC_RIGHTS_FILE_ALL,
866 &dest_h, 4096, /* dest file */
867 SEC_RIGHTS_FILE_ALL,
868 &cc_copy,
869 &ioctl);
870 if (!ok) {
871 torture_fail(torture, "setup copy chunk error");
874 cc_copy.chunks[0].source_off = 0;
875 cc_copy.chunks[0].target_off = 0;
876 cc_copy.chunks[0].length = 4096;
878 /* open and lock the copychunk dest file */
879 status = torture_smb2_testfile(tree, FNAME2, &dest_h2);
880 torture_assert_ntstatus_ok(torture, status, "2nd src open");
882 lck.in.lock_count = 0x0001;
883 lck.in.lock_sequence = 0x00000000;
884 lck.in.file.handle = dest_h2;
885 lck.in.locks = el;
886 el[0].offset = cc_copy.chunks[0].target_off;
887 el[0].length = cc_copy.chunks[0].length;
888 el[0].reserved = 0;
889 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
891 status = smb2_lock(tree, &lck);
892 torture_assert_ntstatus_ok(torture, status, "lock");
894 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
895 &cc_copy,
896 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
897 torture_assert_ndr_success(torture, ndr_ret,
898 "ndr_push_srv_copychunk_copy");
900 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
901 torture_assert_ntstatus_equal(torture, status,
902 NT_STATUS_FILE_LOCK_CONFLICT,
903 "FSCTL_SRV_COPYCHUNK locked");
905 lck.in.lock_count = 0x0001;
906 lck.in.lock_sequence = 0x00000001;
907 lck.in.file.handle = dest_h2;
908 lck.in.locks = el;
909 el[0].offset = cc_copy.chunks[0].target_off;
910 el[0].length = cc_copy.chunks[0].length;
911 el[0].reserved = 0;
912 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
913 status = smb2_lock(tree, &lck);
914 torture_assert_ntstatus_ok(torture, status, "unlock");
916 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
917 torture_assert_ntstatus_ok(torture, status,
918 "FSCTL_SRV_COPYCHUNK unlocked");
920 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
921 &cc_rsp,
922 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
923 torture_assert_ndr_success(torture, ndr_ret,
924 "ndr_pull_srv_copychunk_rsp");
926 ok = check_copy_chunk_rsp(torture, &cc_rsp,
927 1, /* chunks written */
928 0, /* chunk bytes unsuccessfully written */
929 4096); /* total bytes written */
930 if (!ok) {
931 torture_fail(torture, "bad copy chunk response data");
934 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
935 if (!ok) {
936 torture_fail(torture, "inconsistent file data");
939 smb2_util_close(tree, dest_h2);
940 smb2_util_close(tree, src_h);
941 smb2_util_close(tree, dest_h);
942 talloc_free(tmp_ctx);
943 return true;
946 static bool test_ioctl_copy_chunk_bad_key(struct torture_context *torture,
947 struct smb2_tree *tree)
949 struct smb2_handle src_h;
950 struct smb2_handle dest_h;
951 NTSTATUS status;
952 union smb_ioctl ioctl;
953 TALLOC_CTX *tmp_ctx = talloc_new(tree);
954 struct srv_copychunk_copy cc_copy;
955 enum ndr_err_code ndr_ret;
956 bool ok;
958 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
960 &src_h, 4096,
961 SEC_RIGHTS_FILE_ALL,
962 &dest_h, 0,
963 SEC_RIGHTS_FILE_ALL,
964 &cc_copy,
965 &ioctl);
966 if (!ok) {
967 torture_fail(torture, "setup copy chunk error");
970 /* overwrite the resume key with a bogus value */
971 memcpy(cc_copy.source_key, "deadbeefdeadbeefdeadbeef", 24);
973 cc_copy.chunks[0].source_off = 0;
974 cc_copy.chunks[0].target_off = 0;
975 cc_copy.chunks[0].length = 4096;
977 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
978 &cc_copy,
979 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
980 torture_assert_ndr_success(torture, ndr_ret,
981 "ndr_push_srv_copychunk_copy");
983 /* Server 2k12 returns NT_STATUS_OBJECT_NAME_NOT_FOUND */
984 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
985 torture_assert_ntstatus_equal(torture, status,
986 NT_STATUS_OBJECT_NAME_NOT_FOUND,
987 "FSCTL_SRV_COPYCHUNK");
989 smb2_util_close(tree, src_h);
990 smb2_util_close(tree, dest_h);
991 talloc_free(tmp_ctx);
992 return true;
995 static bool test_ioctl_copy_chunk_src_is_dest(struct torture_context *torture,
996 struct smb2_tree *tree)
998 struct smb2_handle src_h;
999 struct smb2_handle dest_h;
1000 NTSTATUS status;
1001 union smb_ioctl ioctl;
1002 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1003 struct srv_copychunk_copy cc_copy;
1004 struct srv_copychunk_rsp cc_rsp;
1005 enum ndr_err_code ndr_ret;
1006 bool ok;
1008 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1010 &src_h, 8192,
1011 SEC_RIGHTS_FILE_ALL,
1012 &dest_h, 0,
1013 SEC_RIGHTS_FILE_ALL,
1014 &cc_copy,
1015 &ioctl);
1016 if (!ok) {
1017 torture_fail(torture, "setup copy chunk error");
1020 /* the source is also the destination */
1021 ioctl.smb2.in.file.handle = src_h;
1023 /* non-overlapping */
1024 cc_copy.chunks[0].source_off = 0;
1025 cc_copy.chunks[0].target_off = 4096;
1026 cc_copy.chunks[0].length = 4096;
1028 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1029 &cc_copy,
1030 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1031 torture_assert_ndr_success(torture, ndr_ret,
1032 "ndr_push_srv_copychunk_copy");
1034 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1035 torture_assert_ntstatus_ok(torture, status,
1036 "FSCTL_SRV_COPYCHUNK");
1038 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1039 &cc_rsp,
1040 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1041 torture_assert_ndr_success(torture, ndr_ret,
1042 "ndr_pull_srv_copychunk_rsp");
1044 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1045 1, /* chunks written */
1046 0, /* chunk bytes unsuccessfully written */
1047 4096); /* total bytes written */
1048 if (!ok) {
1049 torture_fail(torture, "bad copy chunk response data");
1052 ok = check_pattern(torture, tree, tmp_ctx, src_h, 0, 4096, 0);
1053 if (!ok) {
1054 torture_fail(torture, "inconsistent file data");
1056 ok = check_pattern(torture, tree, tmp_ctx, src_h, 4096, 4096, 0);
1057 if (!ok) {
1058 torture_fail(torture, "inconsistent file data");
1061 smb2_util_close(tree, src_h);
1062 smb2_util_close(tree, dest_h);
1063 talloc_free(tmp_ctx);
1064 return true;
1068 * Test a single-chunk copychunk request, where the source and target ranges
1069 * overlap, and the SourceKey refers to the same target file. E.g:
1071 * Initial State
1072 * -------------
1073 * File: src_and_dest
1074 * Offset: 0123456789
1075 * Data: abcdefghij
1077 * Request
1078 * -------
1079 * FSCTL_SRV_COPYCHUNK(src_and_dest)
1080 * SourceKey = SRV_REQUEST_RESUME_KEY(src_and_dest)
1081 * ChunkCount = 1
1082 * Chunks[0].SourceOffset = 0
1083 * Chunks[0].TargetOffset = 4
1084 * Chunks[0].Length = 6
1086 * Resultant State
1087 * ---------------
1088 * File: src_and_dest
1089 * Offset: 0123456789
1090 * Data: abcdabcdef
1092 * The resultant contents of src_and_dest is dependent on the server's
1093 * copy algorithm. In the above example, the server uses an IO buffer
1094 * large enough to hold the entire six-byte source data before writing
1095 * to TargetOffset. If the server were to use a four-byte IO buffer and
1096 * started reads/writes from the lowest offset, then the two overlapping
1097 * bytes in the above example would be overwritten before being read. The
1098 * resultant file contents would be abcdabcdab.
1100 * Windows 2008r2 appears to use a 2048 byte copy buffer, overlapping bytes
1101 * after this offset are written before being read. Windows 2012 on the
1102 * other hand appears to use a buffer large enough to hold its maximum
1103 * supported chunk size (1M). Samba currently uses a 64k copy buffer by
1104 * default (vfs_cc_state.buf).
1106 * This test uses an 8-byte overlap at 2040-2048, so that it passes against
1107 * Windows 2008r2, 2012 and Samba servers. Note, 2008GM fails, as it appears
1108 * to use a different copy algorithm to 2008r2.
1110 static bool
1111 test_ioctl_copy_chunk_src_is_dest_overlap(struct torture_context *torture,
1112 struct smb2_tree *tree)
1114 struct smb2_handle src_h;
1115 struct smb2_handle dest_h;
1116 NTSTATUS status;
1117 union smb_ioctl ioctl;
1118 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1119 struct srv_copychunk_copy cc_copy;
1120 struct srv_copychunk_rsp cc_rsp;
1121 enum ndr_err_code ndr_ret;
1122 bool ok;
1124 /* exceed the vfs_default copy buffer */
1125 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1127 &src_h, 2048 * 2,
1128 SEC_RIGHTS_FILE_ALL,
1129 &dest_h, 0,
1130 SEC_RIGHTS_FILE_ALL,
1131 &cc_copy,
1132 &ioctl);
1133 if (!ok) {
1134 torture_fail(torture, "setup copy chunk error");
1137 /* the source is also the destination */
1138 ioctl.smb2.in.file.handle = src_h;
1140 /* 8 bytes overlap between source and target ranges */
1141 cc_copy.chunks[0].source_off = 0;
1142 cc_copy.chunks[0].target_off = 2048 - 8;
1143 cc_copy.chunks[0].length = 2048;
1145 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1146 &cc_copy,
1147 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1148 torture_assert_ndr_success(torture, ndr_ret,
1149 "ndr_push_srv_copychunk_copy");
1151 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1152 torture_assert_ntstatus_ok(torture, status,
1153 "FSCTL_SRV_COPYCHUNK");
1155 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1156 &cc_rsp,
1157 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1158 torture_assert_ndr_success(torture, ndr_ret,
1159 "ndr_pull_srv_copychunk_rsp");
1161 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1162 1, /* chunks written */
1163 0, /* chunk bytes unsuccessfully written */
1164 2048); /* total bytes written */
1165 if (!ok) {
1166 torture_fail(torture, "bad copy chunk response data");
1169 ok = check_pattern(torture, tree, tmp_ctx, src_h, 0, 2048 - 8, 0);
1170 if (!ok) {
1171 torture_fail(torture, "inconsistent file data");
1173 ok = check_pattern(torture, tree, tmp_ctx, src_h, 2048 - 8, 2048, 0);
1174 if (!ok) {
1175 torture_fail(torture, "inconsistent file data");
1178 smb2_util_close(tree, src_h);
1179 smb2_util_close(tree, dest_h);
1180 talloc_free(tmp_ctx);
1181 return true;
1184 static bool test_ioctl_copy_chunk_bad_access(struct torture_context *torture,
1185 struct smb2_tree *tree)
1187 struct smb2_handle src_h;
1188 struct smb2_handle dest_h;
1189 NTSTATUS status;
1190 union smb_ioctl ioctl;
1191 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1192 struct srv_copychunk_copy cc_copy;
1193 enum ndr_err_code ndr_ret;
1194 bool ok;
1196 /* no read permission on src */
1197 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1198 1, /* 1 chunk */
1199 &src_h, 4096, /* fill 4096 byte src file */
1200 SEC_RIGHTS_FILE_WRITE,
1201 &dest_h, 0, /* 0 byte dest file */
1202 SEC_RIGHTS_FILE_ALL,
1203 &cc_copy,
1204 &ioctl);
1205 if (!ok) {
1206 torture_fail(torture, "setup copy chunk error");
1209 cc_copy.chunks[0].source_off = 0;
1210 cc_copy.chunks[0].target_off = 0;
1211 cc_copy.chunks[0].length = 4096;
1213 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1214 &cc_copy,
1215 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1216 torture_assert_ndr_success(torture, ndr_ret,
1217 "ndr_push_srv_copychunk_copy");
1219 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1220 torture_assert_ntstatus_equal(torture, status,
1221 NT_STATUS_ACCESS_DENIED,
1222 "FSCTL_SRV_COPYCHUNK");
1224 smb2_util_close(tree, src_h);
1225 smb2_util_close(tree, dest_h);
1227 /* no write permission on dest */
1228 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1229 1, /* 1 chunk */
1230 &src_h, 4096, /* fill 4096 byte src file */
1231 SEC_RIGHTS_FILE_ALL,
1232 &dest_h, 0, /* 0 byte dest file */
1233 (SEC_RIGHTS_FILE_READ
1234 | SEC_RIGHTS_FILE_EXECUTE),
1235 &cc_copy,
1236 &ioctl);
1237 if (!ok) {
1238 torture_fail(torture, "setup copy chunk error");
1241 cc_copy.chunks[0].source_off = 0;
1242 cc_copy.chunks[0].target_off = 0;
1243 cc_copy.chunks[0].length = 4096;
1245 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1246 &cc_copy,
1247 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1248 torture_assert_ndr_success(torture, ndr_ret,
1249 "ndr_push_srv_copychunk_copy");
1251 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1252 torture_assert_ntstatus_equal(torture, status,
1253 NT_STATUS_ACCESS_DENIED,
1254 "FSCTL_SRV_COPYCHUNK");
1256 smb2_util_close(tree, src_h);
1257 smb2_util_close(tree, dest_h);
1259 /* no read permission on dest */
1260 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1261 1, /* 1 chunk */
1262 &src_h, 4096, /* fill 4096 byte src file */
1263 SEC_RIGHTS_FILE_ALL,
1264 &dest_h, 0, /* 0 byte dest file */
1265 (SEC_RIGHTS_FILE_WRITE
1266 | SEC_RIGHTS_FILE_EXECUTE),
1267 &cc_copy,
1268 &ioctl);
1269 if (!ok) {
1270 torture_fail(torture, "setup copy chunk error");
1273 cc_copy.chunks[0].source_off = 0;
1274 cc_copy.chunks[0].target_off = 0;
1275 cc_copy.chunks[0].length = 4096;
1277 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1278 &cc_copy,
1279 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1280 torture_assert_ndr_success(torture, ndr_ret,
1281 "ndr_push_srv_copychunk_copy");
1284 * FSCTL_SRV_COPYCHUNK requires read permission on dest,
1285 * FSCTL_SRV_COPYCHUNK_WRITE on the other hand does not.
1287 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1288 torture_assert_ntstatus_equal(torture, status,
1289 NT_STATUS_ACCESS_DENIED,
1290 "FSCTL_SRV_COPYCHUNK");
1292 smb2_util_close(tree, src_h);
1293 smb2_util_close(tree, dest_h);
1294 talloc_free(tmp_ctx);
1296 return true;
1299 static bool test_ioctl_copy_chunk_write_access(struct torture_context *torture,
1300 struct smb2_tree *tree)
1302 struct smb2_handle src_h;
1303 struct smb2_handle dest_h;
1304 NTSTATUS status;
1305 union smb_ioctl ioctl;
1306 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1307 struct srv_copychunk_copy cc_copy;
1308 enum ndr_err_code ndr_ret;
1309 bool ok;
1311 /* no read permission on dest with FSCTL_SRV_COPYCHUNK_WRITE */
1312 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1313 1, /* 1 chunk */
1314 &src_h, 4096, /* fill 4096 byte src file */
1315 SEC_RIGHTS_FILE_ALL,
1316 &dest_h, 0, /* 0 byte dest file */
1317 (SEC_RIGHTS_FILE_WRITE
1318 | SEC_RIGHTS_FILE_EXECUTE),
1319 &cc_copy,
1320 &ioctl);
1321 if (!ok) {
1322 torture_fail(torture, "setup copy chunk error");
1325 ioctl.smb2.in.function = FSCTL_SRV_COPYCHUNK_WRITE;
1326 cc_copy.chunks[0].source_off = 0;
1327 cc_copy.chunks[0].target_off = 0;
1328 cc_copy.chunks[0].length = 4096;
1330 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1331 &cc_copy,
1332 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1333 torture_assert_ndr_success(torture, ndr_ret,
1334 "ndr_push_srv_copychunk_copy");
1336 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1337 torture_assert_ntstatus_ok(torture, status,
1338 "FSCTL_SRV_COPYCHUNK_WRITE");
1340 smb2_util_close(tree, src_h);
1341 smb2_util_close(tree, dest_h);
1342 talloc_free(tmp_ctx);
1344 return true;
1347 static bool test_ioctl_copy_chunk_src_exceed(struct torture_context *torture,
1348 struct smb2_tree *tree)
1350 struct smb2_handle src_h;
1351 struct smb2_handle dest_h;
1352 NTSTATUS status;
1353 union smb_ioctl ioctl;
1354 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1355 struct srv_copychunk_copy cc_copy;
1356 struct srv_copychunk_rsp cc_rsp;
1357 enum ndr_err_code ndr_ret;
1358 bool ok;
1360 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1361 1, /* 1 chunk */
1362 &src_h, 4096, /* fill 4096 byte src file */
1363 SEC_RIGHTS_FILE_ALL,
1364 &dest_h, 0, /* 0 byte dest file */
1365 SEC_RIGHTS_FILE_ALL,
1366 &cc_copy,
1367 &ioctl);
1368 if (!ok) {
1369 torture_fail(torture, "setup copy chunk error");
1372 /* Request copy where off + length exceeds size of src */
1373 cc_copy.chunks[0].source_off = 1024;
1374 cc_copy.chunks[0].target_off = 0;
1375 cc_copy.chunks[0].length = 4096;
1377 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1378 &cc_copy,
1379 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1380 torture_assert_ndr_success(torture, ndr_ret,
1381 "ndr_push_srv_copychunk_copy");
1383 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1384 torture_assert_ntstatus_equal(torture, status,
1385 NT_STATUS_INVALID_VIEW_SIZE,
1386 "FSCTL_SRV_COPYCHUNK oversize");
1388 /* Request copy where length exceeds size of src */
1389 cc_copy.chunks[0].source_off = 1024;
1390 cc_copy.chunks[0].target_off = 0;
1391 cc_copy.chunks[0].length = 3072;
1393 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1394 &cc_copy,
1395 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1396 torture_assert_ndr_success(torture, ndr_ret,
1397 "ndr_push_srv_copychunk_copy");
1399 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1400 torture_assert_ntstatus_ok(torture, status,
1401 "FSCTL_SRV_COPYCHUNK just right");
1403 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1404 &cc_rsp,
1405 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1406 torture_assert_ndr_success(torture, ndr_ret,
1407 "ndr_pull_srv_copychunk_rsp");
1409 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1410 1, /* chunks written */
1411 0, /* chunk bytes unsuccessfully written */
1412 3072); /* total bytes written */
1413 if (!ok) {
1414 torture_fail(torture, "bad copy chunk response data");
1417 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 3072, 1024);
1418 if (!ok) {
1419 torture_fail(torture, "inconsistent file data");
1422 smb2_util_close(tree, src_h);
1423 smb2_util_close(tree, dest_h);
1424 talloc_free(tmp_ctx);
1425 return true;
1428 static bool
1429 test_ioctl_copy_chunk_src_exceed_multi(struct torture_context *torture,
1430 struct smb2_tree *tree)
1432 struct smb2_handle src_h;
1433 struct smb2_handle dest_h;
1434 NTSTATUS status;
1435 union smb_ioctl ioctl;
1436 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1437 struct srv_copychunk_copy cc_copy;
1438 struct srv_copychunk_rsp cc_rsp;
1439 enum ndr_err_code ndr_ret;
1440 bool ok;
1442 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1443 2, /* 2 chunks */
1444 &src_h, 8192, /* fill 8192 byte src file */
1445 SEC_RIGHTS_FILE_ALL,
1446 &dest_h, 0, /* 0 byte dest file */
1447 SEC_RIGHTS_FILE_ALL,
1448 &cc_copy,
1449 &ioctl);
1450 if (!ok) {
1451 torture_fail(torture, "setup copy chunk error");
1454 /* Request copy where off + length exceeds size of src */
1455 cc_copy.chunks[0].source_off = 0;
1456 cc_copy.chunks[0].target_off = 0;
1457 cc_copy.chunks[0].length = 4096;
1459 cc_copy.chunks[1].source_off = 4096;
1460 cc_copy.chunks[1].target_off = 4096;
1461 cc_copy.chunks[1].length = 8192;
1463 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1464 &cc_copy,
1465 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1466 torture_assert_ndr_success(torture, ndr_ret,
1467 "ndr_push_srv_copychunk_copy");
1469 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1470 torture_assert_ntstatus_equal(torture, status,
1471 NT_STATUS_INVALID_VIEW_SIZE,
1472 "FSCTL_SRV_COPYCHUNK oversize");
1473 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1474 &cc_rsp,
1475 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1476 torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
1478 /* first chunk should still be written */
1479 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1480 1, /* chunks written */
1481 0, /* chunk bytes unsuccessfully written */
1482 4096); /* total bytes written */
1483 if (!ok) {
1484 torture_fail(torture, "bad copy chunk response data");
1486 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
1487 if (!ok) {
1488 torture_fail(torture, "inconsistent file data");
1491 smb2_util_close(tree, src_h);
1492 smb2_util_close(tree, dest_h);
1493 talloc_free(tmp_ctx);
1494 return true;
1497 static bool test_ioctl_copy_chunk_sparse_dest(struct torture_context *torture,
1498 struct smb2_tree *tree)
1500 struct smb2_handle src_h;
1501 struct smb2_handle dest_h;
1502 NTSTATUS status;
1503 union smb_ioctl ioctl;
1504 struct smb2_read r;
1505 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1506 struct srv_copychunk_copy cc_copy;
1507 struct srv_copychunk_rsp cc_rsp;
1508 enum ndr_err_code ndr_ret;
1509 bool ok;
1510 int i;
1512 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1513 1, /* 1 chunk */
1514 &src_h, 4096, /* fill 4096 byte src file */
1515 SEC_RIGHTS_FILE_ALL,
1516 &dest_h, 0, /* 0 byte dest file */
1517 SEC_RIGHTS_FILE_ALL,
1518 &cc_copy,
1519 &ioctl);
1520 if (!ok) {
1521 torture_fail(torture, "setup copy chunk error");
1524 /* copy all src file data (via a single chunk desc) */
1525 cc_copy.chunks[0].source_off = 0;
1526 cc_copy.chunks[0].target_off = 4096;
1527 cc_copy.chunks[0].length = 4096;
1529 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1530 &cc_copy,
1531 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1532 torture_assert_ndr_success(torture, ndr_ret,
1533 "ndr_push_srv_copychunk_copy");
1535 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1536 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
1538 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1539 &cc_rsp,
1540 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1541 torture_assert_ndr_success(torture, ndr_ret,
1542 "ndr_pull_srv_copychunk_rsp");
1544 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1545 1, /* chunks written */
1546 0, /* chunk bytes unsuccessfully written */
1547 4096); /* total bytes written */
1548 if (!ok) {
1549 torture_fail(torture, "bad copy chunk response data");
1552 /* check for zeros in first 4k */
1553 ZERO_STRUCT(r);
1554 r.in.file.handle = dest_h;
1555 r.in.length = 4096;
1556 r.in.offset = 0;
1557 status = smb2_read(tree, tmp_ctx, &r);
1558 torture_assert_ntstatus_ok(torture, status, "read");
1560 torture_assert_u64_equal(torture, r.out.data.length, 4096,
1561 "read data len mismatch");
1563 for (i = 0; i < 4096; i++) {
1564 torture_assert(torture, (r.out.data.data[i] == 0),
1565 "sparse did not pass class");
1568 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 4096, 4096, 0);
1569 if (!ok) {
1570 torture_fail(torture, "inconsistent file data");
1573 smb2_util_close(tree, src_h);
1574 smb2_util_close(tree, dest_h);
1575 talloc_free(tmp_ctx);
1576 return true;
1580 * set the ioctl MaxOutputResponse size to less than
1581 * sizeof(struct srv_copychunk_rsp)
1583 static bool test_ioctl_copy_chunk_max_output_sz(struct torture_context *torture,
1584 struct smb2_tree *tree)
1586 struct smb2_handle src_h;
1587 struct smb2_handle dest_h;
1588 NTSTATUS status;
1589 union smb_ioctl ioctl;
1590 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1591 struct srv_copychunk_copy cc_copy;
1592 enum ndr_err_code ndr_ret;
1593 bool ok;
1595 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1596 1, /* 1 chunk */
1597 &src_h, 4096, /* fill 4096 byte src file */
1598 SEC_RIGHTS_FILE_ALL,
1599 &dest_h, 0, /* 0 byte dest file */
1600 SEC_RIGHTS_FILE_ALL,
1601 &cc_copy,
1602 &ioctl);
1603 if (!ok) {
1604 torture_fail(torture, "setup copy chunk error");
1607 cc_copy.chunks[0].source_off = 0;
1608 cc_copy.chunks[0].target_off = 0;
1609 cc_copy.chunks[0].length = 4096;
1610 /* req is valid, but use undersize max_response_size */
1611 ioctl.smb2.in.max_response_size = sizeof(struct srv_copychunk_rsp) - 1;
1613 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1614 &cc_copy,
1615 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1616 torture_assert_ndr_success(torture, ndr_ret,
1617 "ndr_push_srv_copychunk_copy");
1619 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1620 torture_assert_ntstatus_equal(torture, status,
1621 NT_STATUS_INVALID_PARAMETER,
1622 "FSCTL_SRV_COPYCHUNK");
1624 smb2_util_close(tree, src_h);
1625 smb2_util_close(tree, dest_h);
1626 talloc_free(tmp_ctx);
1627 return true;
1630 static bool test_ioctl_copy_chunk_zero_length(struct torture_context *torture,
1631 struct smb2_tree *tree)
1633 struct smb2_handle src_h;
1634 struct smb2_handle dest_h;
1635 NTSTATUS status;
1636 union smb_ioctl ioctl;
1637 union smb_fileinfo q;
1638 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1639 struct srv_copychunk_copy cc_copy;
1640 struct srv_copychunk_rsp cc_rsp;
1641 enum ndr_err_code ndr_ret;
1642 bool ok;
1644 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1645 1, /* 1 chunk */
1646 &src_h, 4096, /* fill 4096 byte src file */
1647 SEC_RIGHTS_FILE_ALL,
1648 &dest_h, 0, /* 0 byte dest file */
1649 SEC_RIGHTS_FILE_ALL,
1650 &cc_copy,
1651 &ioctl);
1652 if (!ok) {
1653 torture_fail(torture, "setup copy chunk error");
1656 /* zero length server-side copy (via a single chunk desc) */
1657 cc_copy.chunks[0].source_off = 0;
1658 cc_copy.chunks[0].target_off = 0;
1659 cc_copy.chunks[0].length = 0;
1661 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1662 &cc_copy,
1663 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1664 torture_assert_ndr_success(torture, ndr_ret,
1665 "ndr_push_srv_copychunk_copy");
1667 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1668 torture_assert_ntstatus_equal(torture, status,
1669 NT_STATUS_INVALID_PARAMETER,
1670 "bad zero-length chunk response");
1672 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1673 &cc_rsp,
1674 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1675 torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
1677 ZERO_STRUCT(q);
1678 q.all_info2.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
1679 q.all_info2.in.file.handle = dest_h;
1680 status = smb2_getinfo_file(tree, torture, &q);
1681 torture_assert_ntstatus_ok(torture, status, "getinfo");
1683 torture_assert_int_equal(torture, q.all_info2.out.size, 0,
1684 "size after zero len clone");
1686 smb2_util_close(tree, src_h);
1687 smb2_util_close(tree, dest_h);
1688 talloc_free(tmp_ctx);
1689 return true;
1692 static NTSTATUS test_ioctl_compress_fs_supported(struct torture_context *torture,
1693 struct smb2_tree *tree,
1694 TALLOC_CTX *mem_ctx,
1695 struct smb2_handle *fh,
1696 bool *compress_support)
1698 NTSTATUS status;
1699 union smb_fsinfo info;
1701 ZERO_STRUCT(info);
1702 info.generic.level = RAW_QFS_ATTRIBUTE_INFORMATION;
1703 info.generic.handle = *fh;
1704 status = smb2_getinfo_fs(tree, tree, &info);
1705 if (!NT_STATUS_IS_OK(status)) {
1706 return status;
1709 if (info.attribute_info.out.fs_attr & FILE_FILE_COMPRESSION) {
1710 *compress_support = true;
1711 } else {
1712 *compress_support = false;
1714 return NT_STATUS_OK;
1717 static NTSTATUS test_ioctl_compress_get(struct torture_context *torture,
1718 TALLOC_CTX *mem_ctx,
1719 struct smb2_tree *tree,
1720 struct smb2_handle fh,
1721 uint16_t *_compression_fmt)
1723 union smb_ioctl ioctl;
1724 struct compression_state cmpr_state;
1725 enum ndr_err_code ndr_ret;
1726 NTSTATUS status;
1728 ZERO_STRUCT(ioctl);
1729 ioctl.smb2.level = RAW_IOCTL_SMB2;
1730 ioctl.smb2.in.file.handle = fh;
1731 ioctl.smb2.in.function = FSCTL_GET_COMPRESSION;
1732 ioctl.smb2.in.max_response_size = sizeof(struct compression_state);
1733 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
1735 status = smb2_ioctl(tree, mem_ctx, &ioctl.smb2);
1736 if (!NT_STATUS_IS_OK(status)) {
1737 return status;
1740 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, mem_ctx,
1741 &cmpr_state,
1742 (ndr_pull_flags_fn_t)ndr_pull_compression_state);
1744 if (ndr_ret != NDR_ERR_SUCCESS) {
1745 return NT_STATUS_INTERNAL_ERROR;
1748 *_compression_fmt = cmpr_state.format;
1749 return NT_STATUS_OK;
1752 static NTSTATUS test_ioctl_compress_set(struct torture_context *torture,
1753 TALLOC_CTX *mem_ctx,
1754 struct smb2_tree *tree,
1755 struct smb2_handle fh,
1756 uint16_t compression_fmt)
1758 union smb_ioctl ioctl;
1759 struct compression_state cmpr_state;
1760 enum ndr_err_code ndr_ret;
1761 NTSTATUS status;
1763 ZERO_STRUCT(ioctl);
1764 ioctl.smb2.level = RAW_IOCTL_SMB2;
1765 ioctl.smb2.in.file.handle = fh;
1766 ioctl.smb2.in.function = FSCTL_SET_COMPRESSION;
1767 ioctl.smb2.in.max_response_size = 0;
1768 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
1770 cmpr_state.format = compression_fmt;
1771 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, mem_ctx,
1772 &cmpr_state,
1773 (ndr_push_flags_fn_t)ndr_push_compression_state);
1774 if (ndr_ret != NDR_ERR_SUCCESS) {
1775 return NT_STATUS_INTERNAL_ERROR;
1778 status = smb2_ioctl(tree, mem_ctx, &ioctl.smb2);
1779 return status;
1782 static bool test_ioctl_compress_file_flag(struct torture_context *torture,
1783 struct smb2_tree *tree)
1785 struct smb2_handle fh;
1786 NTSTATUS status;
1787 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1788 bool ok;
1789 uint16_t compression_fmt;
1791 ok = test_setup_create_fill(torture, tree, tmp_ctx,
1792 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
1793 FILE_ATTRIBUTE_NORMAL);
1794 torture_assert(torture, ok, "setup compression file");
1796 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
1797 &ok);
1798 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
1799 if (!ok) {
1800 smb2_util_close(tree, fh);
1801 torture_skip(torture, "FS compression not supported\n");
1804 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1805 &compression_fmt);
1806 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1808 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
1809 "initial compression state not NONE");
1811 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
1812 COMPRESSION_FORMAT_DEFAULT);
1813 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
1815 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1816 &compression_fmt);
1817 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1819 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
1820 "invalid compression state after set");
1822 smb2_util_close(tree, fh);
1823 talloc_free(tmp_ctx);
1824 return true;
1827 static bool test_ioctl_compress_dir_inherit(struct torture_context *torture,
1828 struct smb2_tree *tree)
1830 struct smb2_handle dirh;
1831 struct smb2_handle fh;
1832 NTSTATUS status;
1833 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1834 uint16_t compression_fmt;
1835 bool ok;
1836 char path_buf[PATH_MAX];
1838 smb2_deltree(tree, DNAME);
1839 ok = test_setup_create_fill(torture, tree, tmp_ctx,
1840 DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL,
1841 FILE_ATTRIBUTE_DIRECTORY);
1842 torture_assert(torture, ok, "setup compression directory");
1844 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &dirh,
1845 &ok);
1846 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
1847 if (!ok) {
1848 smb2_util_close(tree, dirh);
1849 smb2_deltree(tree, DNAME);
1850 torture_skip(torture, "FS compression not supported\n");
1853 /* set compression on parent dir, then check for inheritance */
1854 status = test_ioctl_compress_set(torture, tmp_ctx, tree, dirh,
1855 COMPRESSION_FORMAT_LZNT1);
1856 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
1858 status = test_ioctl_compress_get(torture, tmp_ctx, tree, dirh,
1859 &compression_fmt);
1860 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1862 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
1863 "invalid compression state after set");
1865 snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME);
1866 ok = test_setup_create_fill(torture, tree, tmp_ctx,
1867 path_buf, &fh, 4096, SEC_RIGHTS_FILE_ALL,
1868 FILE_ATTRIBUTE_NORMAL);
1869 torture_assert(torture, ok, "setup compression file");
1871 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1872 &compression_fmt);
1873 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1875 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
1876 "compression attr not inherited by new file");
1878 /* check compressed data is consistent */
1879 ok = check_pattern(torture, tree, tmp_ctx, fh, 0, 4096, 0);
1881 /* disable dir compression attr, file should remain compressed */
1882 status = test_ioctl_compress_set(torture, tmp_ctx, tree, dirh,
1883 COMPRESSION_FORMAT_NONE);
1884 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
1886 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1887 &compression_fmt);
1888 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1890 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
1891 "file compression attr removed after dir change");
1892 smb2_util_close(tree, fh);
1894 /* new files should no longer inherit compression attr */
1895 snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME2);
1896 ok = test_setup_create_fill(torture, tree, tmp_ctx,
1897 path_buf, &fh, 0, SEC_RIGHTS_FILE_ALL,
1898 FILE_ATTRIBUTE_NORMAL);
1899 torture_assert(torture, ok, "setup file");
1901 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1902 &compression_fmt);
1903 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1905 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
1906 "compression attr present on new file");
1908 smb2_util_close(tree, fh);
1909 smb2_util_close(tree, dirh);
1910 smb2_deltree(tree, DNAME);
1911 talloc_free(tmp_ctx);
1912 return true;
1915 static bool test_ioctl_compress_invalid_format(struct torture_context *torture,
1916 struct smb2_tree *tree)
1918 struct smb2_handle fh;
1919 NTSTATUS status;
1920 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1921 bool ok;
1922 uint16_t compression_fmt;
1924 ok = test_setup_create_fill(torture, tree, tmp_ctx,
1925 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
1926 FILE_ATTRIBUTE_NORMAL);
1927 torture_assert(torture, ok, "setup compression file");
1929 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
1930 &ok);
1931 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
1932 if (!ok) {
1933 smb2_util_close(tree, fh);
1934 torture_skip(torture, "FS compression not supported\n");
1937 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
1938 0x0042); /* bogus */
1939 torture_assert_ntstatus_equal(torture, status,
1940 NT_STATUS_INVALID_PARAMETER,
1941 "invalid FSCTL_SET_COMPRESSION");
1943 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1944 &compression_fmt);
1945 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1947 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
1948 "initial compression state not NONE");
1950 smb2_util_close(tree, fh);
1951 talloc_free(tmp_ctx);
1952 return true;
1955 static bool test_ioctl_compress_invalid_buf(struct torture_context *torture,
1956 struct smb2_tree *tree)
1958 struct smb2_handle fh;
1959 NTSTATUS status;
1960 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1961 bool ok;
1962 union smb_ioctl ioctl;
1964 ok = test_setup_create_fill(torture, tree, tmp_ctx,
1965 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
1966 FILE_ATTRIBUTE_NORMAL);
1967 torture_assert(torture, ok, "setup compression file");
1969 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
1970 &ok);
1971 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
1972 if (!ok) {
1973 smb2_util_close(tree, fh);
1974 torture_skip(torture, "FS compression not supported\n");
1977 ZERO_STRUCT(ioctl);
1978 ioctl.smb2.level = RAW_IOCTL_SMB2;
1979 ioctl.smb2.in.file.handle = fh;
1980 ioctl.smb2.in.function = FSCTL_GET_COMPRESSION;
1981 ioctl.smb2.in.max_response_size = 0; /* no room for rsp data */
1982 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
1984 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1985 if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_USER_BUFFER)
1986 && !NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
1987 /* neither Server 2k12 nor 2k8r2 response status */
1988 torture_assert(torture, true,
1989 "invalid FSCTL_SET_COMPRESSION");
1992 smb2_util_close(tree, fh);
1993 talloc_free(tmp_ctx);
1994 return true;
1997 static bool test_ioctl_compress_query_file_attr(struct torture_context *torture,
1998 struct smb2_tree *tree)
2000 struct smb2_handle fh;
2001 union smb_fileinfo io;
2002 NTSTATUS status;
2003 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2004 bool ok;
2006 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2007 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2008 FILE_ATTRIBUTE_NORMAL);
2009 torture_assert(torture, ok, "setup compression file");
2011 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
2012 &ok);
2013 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2014 if (!ok) {
2015 smb2_util_close(tree, fh);
2016 torture_skip(torture, "FS compression not supported\n");
2019 ZERO_STRUCT(io);
2020 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
2021 io.generic.in.file.handle = fh;
2022 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2023 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2025 torture_assert(torture,
2026 ((io.all_info2.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2027 "compression attr before set");
2029 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
2030 COMPRESSION_FORMAT_DEFAULT);
2031 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
2033 ZERO_STRUCT(io);
2034 io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2035 io.generic.in.file.handle = fh;
2036 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2037 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2039 torture_assert(torture,
2040 (io.basic_info.out.attrib & FILE_ATTRIBUTE_COMPRESSED),
2041 "no compression attr after set");
2043 smb2_util_close(tree, fh);
2044 talloc_free(tmp_ctx);
2045 return true;
2049 * Specify FILE_ATTRIBUTE_COMPRESSED on creation, Windows does not retain this
2050 * attribute.
2052 static bool test_ioctl_compress_create_with_attr(struct torture_context *torture,
2053 struct smb2_tree *tree)
2055 struct smb2_handle fh2;
2056 union smb_fileinfo io;
2057 NTSTATUS status;
2058 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2059 uint16_t compression_fmt;
2060 bool ok;
2062 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2063 FNAME2, &fh2, 0, SEC_RIGHTS_FILE_ALL,
2064 (FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_COMPRESSED));
2065 torture_assert(torture, ok, "setup compression file");
2067 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh2,
2068 &ok);
2069 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2070 if (!ok) {
2071 smb2_util_close(tree, fh2);
2072 torture_skip(torture, "FS compression not supported\n");
2075 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh2,
2076 &compression_fmt);
2077 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2079 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2080 "initial compression state not NONE");
2082 ZERO_STRUCT(io);
2083 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
2084 io.generic.in.file.handle = fh2;
2085 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2086 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2088 torture_assert(torture,
2089 ((io.all_info2.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2090 "incorrect compression attr");
2092 smb2_util_close(tree, fh2);
2093 talloc_free(tmp_ctx);
2094 return true;
2097 static bool test_ioctl_compress_inherit_disable(struct torture_context *torture,
2098 struct smb2_tree *tree)
2100 struct smb2_handle fh;
2101 struct smb2_handle dirh;
2102 char path_buf[PATH_MAX];
2103 NTSTATUS status;
2104 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2105 bool ok;
2106 uint16_t compression_fmt;
2108 struct smb2_create io;
2110 smb2_deltree(tree, DNAME);
2111 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2112 DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL,
2113 FILE_ATTRIBUTE_DIRECTORY);
2114 torture_assert(torture, ok, "setup compression directory");
2116 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &dirh,
2117 &ok);
2118 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2119 if (!ok) {
2120 smb2_util_close(tree, dirh);
2121 smb2_deltree(tree, DNAME);
2122 torture_skip(torture, "FS compression not supported\n");
2125 /* set compression on parent dir, then check for inheritance */
2126 status = test_ioctl_compress_set(torture, tmp_ctx, tree, dirh,
2127 COMPRESSION_FORMAT_LZNT1);
2128 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
2130 status = test_ioctl_compress_get(torture, tmp_ctx, tree, dirh,
2131 &compression_fmt);
2132 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2134 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
2135 "invalid compression state after set");
2136 smb2_util_close(tree, dirh);
2138 snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME);
2139 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2140 path_buf, &fh, 0, SEC_RIGHTS_FILE_ALL,
2141 FILE_ATTRIBUTE_NORMAL);
2142 torture_assert(torture, ok, "setup compression file");
2144 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2145 &compression_fmt);
2146 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2148 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
2149 "compression attr not inherited by new file");
2150 smb2_util_close(tree, fh);
2152 snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME2);
2154 /* NO_COMPRESSION option should block inheritance */
2155 ZERO_STRUCT(io);
2156 io.in.desired_access = SEC_RIGHTS_FILE_ALL;
2157 io.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
2158 io.in.create_disposition = NTCREATEX_DISP_CREATE;
2159 io.in.create_options = NTCREATEX_OPTIONS_NO_COMPRESSION;
2160 io.in.share_access =
2161 NTCREATEX_SHARE_ACCESS_DELETE|
2162 NTCREATEX_SHARE_ACCESS_READ|
2163 NTCREATEX_SHARE_ACCESS_WRITE;
2164 io.in.fname = path_buf;
2166 status = smb2_create(tree, tmp_ctx, &io);
2167 torture_assert_ntstatus_ok(torture, status, "file create");
2169 fh = io.out.file.handle;
2171 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2172 &compression_fmt);
2173 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2175 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2176 "compression attr inherited by NO_COMPRESSION file");
2177 smb2_util_close(tree, fh);
2180 snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, DNAME);
2181 ZERO_STRUCT(io);
2182 io.in.desired_access = SEC_RIGHTS_FILE_ALL;
2183 io.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
2184 io.in.create_disposition = NTCREATEX_DISP_CREATE;
2185 io.in.create_options = (NTCREATEX_OPTIONS_NO_COMPRESSION
2186 | NTCREATEX_OPTIONS_DIRECTORY);
2187 io.in.share_access =
2188 NTCREATEX_SHARE_ACCESS_DELETE|
2189 NTCREATEX_SHARE_ACCESS_READ|
2190 NTCREATEX_SHARE_ACCESS_WRITE;
2191 io.in.fname = path_buf;
2193 status = smb2_create(tree, tmp_ctx, &io);
2194 torture_assert_ntstatus_ok(torture, status, "dir create");
2196 dirh = io.out.file.handle;
2198 status = test_ioctl_compress_get(torture, tmp_ctx, tree, dirh,
2199 &compression_fmt);
2200 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2202 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2203 "compression attr inherited by NO_COMPRESSION dir");
2204 smb2_util_close(tree, dirh);
2205 smb2_deltree(tree, DNAME);
2207 talloc_free(tmp_ctx);
2208 return true;
2211 /* attempting to set compression via SetInfo should not stick */
2212 static bool test_ioctl_compress_set_file_attr(struct torture_context *torture,
2213 struct smb2_tree *tree)
2215 struct smb2_handle fh;
2216 struct smb2_handle dirh;
2217 union smb_fileinfo io;
2218 union smb_setfileinfo set_io;
2219 uint16_t compression_fmt;
2220 NTSTATUS status;
2221 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2222 bool ok;
2224 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2225 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2226 FILE_ATTRIBUTE_NORMAL);
2227 torture_assert(torture, ok, "setup compression file");
2229 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
2230 &ok);
2231 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2232 if (!ok) {
2233 smb2_util_close(tree, fh);
2234 torture_skip(torture, "FS compression not supported\n");
2237 ZERO_STRUCT(io);
2238 io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2239 io.generic.in.file.handle = fh;
2240 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2241 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2243 torture_assert(torture,
2244 ((io.basic_info.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2245 "compression attr before set");
2247 ZERO_STRUCT(set_io);
2248 set_io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2249 set_io.basic_info.in.file.handle = fh;
2250 set_io.basic_info.in.create_time = io.basic_info.out.create_time;
2251 set_io.basic_info.in.access_time = io.basic_info.out.access_time;
2252 set_io.basic_info.in.write_time = io.basic_info.out.write_time;
2253 set_io.basic_info.in.change_time = io.basic_info.out.change_time;
2254 set_io.basic_info.in.attrib = (io.basic_info.out.attrib
2255 | FILE_ATTRIBUTE_COMPRESSED);
2256 status = smb2_setinfo_file(tree, &set_io);
2257 torture_assert_ntstatus_ok(torture, status, "SMB2_SETINFO_FILE");
2259 ZERO_STRUCT(io);
2260 io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2261 io.generic.in.file.handle = fh;
2262 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2263 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2265 torture_assert(torture,
2266 ((io.basic_info.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2267 "compression attr after set");
2269 smb2_util_close(tree, fh);
2270 smb2_deltree(tree, DNAME);
2271 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2272 DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL,
2273 FILE_ATTRIBUTE_DIRECTORY);
2274 torture_assert(torture, ok, "setup compression directory");
2276 ZERO_STRUCT(io);
2277 io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2278 io.generic.in.file.handle = dirh;
2279 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2280 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2282 torture_assert(torture,
2283 ((io.basic_info.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2284 "compression attr before set");
2286 ZERO_STRUCT(set_io);
2287 set_io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2288 set_io.basic_info.in.file.handle = dirh;
2289 set_io.basic_info.in.create_time = io.basic_info.out.create_time;
2290 set_io.basic_info.in.access_time = io.basic_info.out.access_time;
2291 set_io.basic_info.in.write_time = io.basic_info.out.write_time;
2292 set_io.basic_info.in.change_time = io.basic_info.out.change_time;
2293 set_io.basic_info.in.attrib = (io.basic_info.out.attrib
2294 | FILE_ATTRIBUTE_COMPRESSED);
2295 status = smb2_setinfo_file(tree, &set_io);
2296 torture_assert_ntstatus_ok(torture, status, "SMB2_SETINFO_FILE");
2298 status = test_ioctl_compress_get(torture, tmp_ctx, tree, dirh,
2299 &compression_fmt);
2300 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2302 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2303 "dir compression set after SetInfo");
2305 smb2_util_close(tree, dirh);
2306 talloc_free(tmp_ctx);
2307 return true;
2310 static bool test_ioctl_compress_perms(struct torture_context *torture,
2311 struct smb2_tree *tree)
2313 struct smb2_handle fh;
2314 uint16_t compression_fmt;
2315 union smb_fileinfo io;
2316 NTSTATUS status;
2317 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2318 bool ok;
2320 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2321 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2322 FILE_ATTRIBUTE_NORMAL);
2323 torture_assert(torture, ok, "setup compression file");
2325 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
2326 &ok);
2327 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2328 smb2_util_close(tree, fh);
2329 if (!ok) {
2330 torture_skip(torture, "FS compression not supported\n");
2333 /* attempt get compression without READ_ATTR permission */
2334 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2335 FNAME, &fh, 0,
2336 (SEC_RIGHTS_FILE_READ & ~(SEC_FILE_READ_ATTRIBUTE
2337 | SEC_STD_READ_CONTROL
2338 | SEC_FILE_READ_EA)),
2339 FILE_ATTRIBUTE_NORMAL);
2340 torture_assert(torture, ok, "setup compression file");
2342 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2343 &compression_fmt);
2344 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2345 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2346 "compression set after create");
2347 smb2_util_close(tree, fh);
2349 /* set compression without WRITE_ATTR permission should succeed */
2350 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2351 FNAME, &fh, 0,
2352 (SEC_RIGHTS_FILE_WRITE & ~(SEC_FILE_WRITE_ATTRIBUTE
2353 | SEC_STD_WRITE_DAC
2354 | SEC_FILE_WRITE_EA)),
2355 FILE_ATTRIBUTE_NORMAL);
2356 torture_assert(torture, ok, "setup compression file");
2358 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
2359 COMPRESSION_FORMAT_DEFAULT);
2360 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
2361 smb2_util_close(tree, fh);
2363 ok = test_setup_open(torture, tree, tmp_ctx,
2364 FNAME, &fh, SEC_RIGHTS_FILE_ALL,
2365 FILE_ATTRIBUTE_NORMAL);
2366 torture_assert(torture, ok, "setup compression file");
2367 ZERO_STRUCT(io);
2368 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
2369 io.generic.in.file.handle = fh;
2370 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2371 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2373 torture_assert(torture,
2374 (io.all_info2.out.attrib & FILE_ATTRIBUTE_COMPRESSED),
2375 "incorrect compression attr");
2376 smb2_util_close(tree, fh);
2378 /* attempt get compression without READ_DATA permission */
2379 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2380 FNAME, &fh, 0,
2381 (SEC_RIGHTS_FILE_READ & ~SEC_FILE_READ_DATA),
2382 FILE_ATTRIBUTE_NORMAL);
2383 torture_assert(torture, ok, "setup compression file");
2385 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2386 &compression_fmt);
2387 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2388 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2389 "compression enabled after set");
2390 smb2_util_close(tree, fh);
2392 /* attempt get compression with only SYNCHRONIZE permission */
2393 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2394 FNAME, &fh, 0,
2395 SEC_STD_SYNCHRONIZE,
2396 FILE_ATTRIBUTE_NORMAL);
2397 torture_assert(torture, ok, "setup compression file");
2399 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2400 &compression_fmt);
2401 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2402 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2403 "compression not enabled after set");
2404 smb2_util_close(tree, fh);
2406 /* attempt to set compression without WRITE_DATA permission */
2407 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2408 FNAME, &fh, 0,
2409 (SEC_RIGHTS_FILE_WRITE & (~SEC_FILE_WRITE_DATA)),
2410 FILE_ATTRIBUTE_NORMAL);
2411 torture_assert(torture, ok, "setup compression file");
2413 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
2414 COMPRESSION_FORMAT_DEFAULT);
2415 torture_assert_ntstatus_equal(torture, status,
2416 NT_STATUS_ACCESS_DENIED,
2417 "FSCTL_SET_COMPRESSION permission");
2418 smb2_util_close(tree, fh);
2420 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2421 FNAME, &fh, 0,
2422 (SEC_RIGHTS_FILE_WRITE & (~SEC_FILE_WRITE_DATA)),
2423 FILE_ATTRIBUTE_NORMAL);
2424 torture_assert(torture, ok, "setup compression file");
2426 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
2427 COMPRESSION_FORMAT_NONE);
2428 torture_assert_ntstatus_equal(torture, status,
2429 NT_STATUS_ACCESS_DENIED,
2430 "FSCTL_SET_COMPRESSION permission");
2431 smb2_util_close(tree, fh);
2433 talloc_free(tmp_ctx);
2434 return true;
2438 basic testing of the SMB2 FSCTL_QUERY_NETWORK_INTERFACE_INFO ioctl
2440 static bool test_ioctl_network_interface_info(struct torture_context *torture,
2441 struct smb2_tree *tree)
2443 union smb_ioctl ioctl;
2444 struct smb2_handle fh;
2445 NTSTATUS status;
2446 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2447 struct fsctl_net_iface_info net_iface;
2448 enum ndr_err_code ndr_ret;
2449 uint32_t caps;
2451 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
2452 if (!(caps & SMB2_CAP_MULTI_CHANNEL)) {
2453 torture_skip(torture, "server doesn't support SMB2_CAP_MULTI_CHANNEL\n");
2456 ZERO_STRUCT(ioctl);
2457 ioctl.smb2.level = RAW_IOCTL_SMB2;
2458 fh.data[0] = UINT64_MAX;
2459 fh.data[1] = UINT64_MAX;
2460 ioctl.smb2.in.file.handle = fh;
2461 ioctl.smb2.in.function = FSCTL_QUERY_NETWORK_INTERFACE_INFO;
2462 ioctl.smb2.in.max_response_size = 0x10000; /* Windows client sets this to 64KiB */
2463 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2465 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2466 torture_assert_ntstatus_ok(torture, status, "FSCTL_QUERY_NETWORK_INTERFACE_INFO");
2468 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, &net_iface,
2469 (ndr_pull_flags_fn_t)ndr_pull_fsctl_net_iface_info);
2470 torture_assert_ndr_success(torture, ndr_ret,
2471 "ndr_pull_fsctl_net_iface_info");
2473 ndr_print_debug((ndr_print_fn_t)ndr_print_fsctl_net_iface_info,
2474 "Network Interface Info", &net_iface);
2476 talloc_free(tmp_ctx);
2477 return true;
2480 static NTSTATUS test_ioctl_sparse_fs_supported(struct torture_context *torture,
2481 struct smb2_tree *tree,
2482 TALLOC_CTX *mem_ctx,
2483 struct smb2_handle *fh,
2484 bool *sparse_support)
2486 NTSTATUS status;
2487 union smb_fsinfo info;
2489 ZERO_STRUCT(info);
2490 info.generic.level = RAW_QFS_ATTRIBUTE_INFORMATION;
2491 info.generic.handle = *fh;
2492 status = smb2_getinfo_fs(tree, tree, &info);
2493 if (!NT_STATUS_IS_OK(status)) {
2494 return status;
2497 if (info.attribute_info.out.fs_attr & FILE_SUPPORTS_SPARSE_FILES) {
2498 *sparse_support = true;
2499 } else {
2500 *sparse_support = false;
2502 return NT_STATUS_OK;
2505 static NTSTATUS test_ioctl_sparse_req(struct torture_context *torture,
2506 TALLOC_CTX *mem_ctx,
2507 struct smb2_tree *tree,
2508 struct smb2_handle fh,
2509 bool set)
2511 union smb_ioctl ioctl;
2512 NTSTATUS status;
2513 uint8_t set_sparse;
2515 ZERO_STRUCT(ioctl);
2516 ioctl.smb2.level = RAW_IOCTL_SMB2;
2517 ioctl.smb2.in.file.handle = fh;
2518 ioctl.smb2.in.function = FSCTL_SET_SPARSE;
2519 ioctl.smb2.in.max_response_size = 0;
2520 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2521 set_sparse = (set ? 0xFF : 0x0);
2522 ioctl.smb2.in.out.data = &set_sparse;
2523 ioctl.smb2.in.out.length = sizeof(set_sparse);
2525 status = smb2_ioctl(tree, mem_ctx, &ioctl.smb2);
2526 return status;
2529 static NTSTATUS test_sparse_get(struct torture_context *torture,
2530 TALLOC_CTX *mem_ctx,
2531 struct smb2_tree *tree,
2532 struct smb2_handle fh,
2533 bool *_is_sparse)
2535 union smb_fileinfo io;
2536 NTSTATUS status;
2538 ZERO_STRUCT(io);
2539 io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2540 io.generic.in.file.handle = fh;
2541 status = smb2_getinfo_file(tree, mem_ctx, &io);
2542 if (!NT_STATUS_IS_OK(status)) {
2543 return status;
2545 *_is_sparse = !!(io.basic_info.out.attrib & FILE_ATTRIBUTE_SPARSE);
2547 return status;
2550 static bool test_ioctl_sparse_file_flag(struct torture_context *torture,
2551 struct smb2_tree *tree)
2553 struct smb2_handle fh;
2554 union smb_fileinfo io;
2555 NTSTATUS status;
2556 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2557 bool ok;
2558 bool is_sparse;
2560 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2561 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2562 FILE_ATTRIBUTE_NORMAL);
2563 torture_assert(torture, ok, "setup file");
2565 status = test_ioctl_sparse_fs_supported(torture, tree, tmp_ctx, &fh,
2566 &ok);
2567 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2568 if (!ok) {
2569 smb2_util_close(tree, fh);
2570 torture_skip(torture, "Sparse files not supported\n");
2573 ZERO_STRUCT(io);
2574 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
2575 io.generic.in.file.handle = fh;
2576 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2577 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2579 torture_assert(torture,
2580 ((io.all_info2.out.attrib & FILE_ATTRIBUTE_SPARSE) == 0),
2581 "sparse attr before set");
2583 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
2584 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2586 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2587 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2588 torture_assert(torture, is_sparse, "no sparse attr after set");
2590 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false);
2591 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2593 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2594 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2595 torture_assert(torture, !is_sparse, "sparse attr after unset");
2597 smb2_util_close(tree, fh);
2598 talloc_free(tmp_ctx);
2599 return true;
2602 static bool test_ioctl_sparse_file_attr(struct torture_context *torture,
2603 struct smb2_tree *tree)
2605 struct smb2_handle fh;
2606 NTSTATUS status;
2607 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2608 bool ok;
2609 bool is_sparse;
2611 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2612 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2613 (FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_SPARSE));
2614 torture_assert(torture, ok, "setup file");
2616 status = test_ioctl_sparse_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, "Sparse files not supported\n");
2624 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2625 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2626 torture_assert(torture, !is_sparse, "sparse attr on open");
2628 smb2_util_close(tree, fh);
2629 talloc_free(tmp_ctx);
2630 return true;
2633 static bool test_ioctl_sparse_dir_flag(struct torture_context *torture,
2634 struct smb2_tree *tree)
2636 struct smb2_handle dirh;
2637 NTSTATUS status;
2638 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2639 bool ok;
2641 smb2_deltree(tree, DNAME);
2642 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2643 DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL,
2644 FILE_ATTRIBUTE_DIRECTORY);
2645 torture_assert(torture, ok, "setup sparse directory");
2647 status = test_ioctl_sparse_fs_supported(torture, tree, tmp_ctx, &dirh,
2648 &ok);
2649 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2650 if (!ok) {
2651 smb2_util_close(tree, dirh);
2652 smb2_deltree(tree, DNAME);
2653 torture_skip(torture, "Sparse files not supported\n");
2656 /* set sparse dir should fail, check for 2k12 & 2k8 response */
2657 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, dirh, true);
2658 torture_assert_ntstatus_equal(torture, status,
2659 NT_STATUS_INVALID_PARAMETER,
2660 "dir FSCTL_SET_SPARSE status");
2662 smb2_util_close(tree, dirh);
2663 smb2_deltree(tree, DNAME);
2664 talloc_free(tmp_ctx);
2665 return true;
2669 * FSCTL_SET_SPARSE can be sent with (already tested) or without a SetSparse
2670 * buffer to indicate whether the flag should be set or cleared. When sent
2671 * without a buffer, it must be handled as if SetSparse=TRUE.
2673 static bool test_ioctl_sparse_set_nobuf(struct torture_context *torture,
2674 struct smb2_tree *tree)
2676 struct smb2_handle fh;
2677 union smb_ioctl ioctl;
2678 NTSTATUS status;
2679 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2680 bool ok;
2681 bool is_sparse;
2683 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2684 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2685 FILE_ATTRIBUTE_NORMAL);
2686 torture_assert(torture, ok, "setup file");
2688 status = test_ioctl_sparse_fs_supported(torture, tree, tmp_ctx, &fh,
2689 &ok);
2690 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2691 if (!ok) {
2692 smb2_util_close(tree, fh);
2693 torture_skip(torture, "Sparse files not supported\n");
2696 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2697 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2698 torture_assert(torture, !is_sparse, "sparse attr before set");
2700 ZERO_STRUCT(ioctl);
2701 ioctl.smb2.level = RAW_IOCTL_SMB2;
2702 ioctl.smb2.in.file.handle = fh;
2703 ioctl.smb2.in.function = FSCTL_SET_SPARSE;
2704 ioctl.smb2.in.max_response_size = 0;
2705 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2706 /* ioctl.smb2.in.out is zeroed, no SetSparse buffer */
2708 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2709 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2711 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2712 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2713 torture_assert(torture, is_sparse, "no sparse attr after set");
2715 /* second non-SetSparse request shouldn't toggle sparse */
2716 ZERO_STRUCT(ioctl);
2717 ioctl.smb2.level = RAW_IOCTL_SMB2;
2718 ioctl.smb2.in.file.handle = fh;
2719 ioctl.smb2.in.function = FSCTL_SET_SPARSE;
2720 ioctl.smb2.in.max_response_size = 0;
2721 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2723 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2724 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2726 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2727 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2728 torture_assert(torture, is_sparse, "no sparse attr after 2nd set");
2730 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false);
2731 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2733 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2734 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2735 torture_assert(torture, !is_sparse, "sparse attr after unset");
2737 smb2_util_close(tree, fh);
2738 talloc_free(tmp_ctx);
2739 return true;
2742 static bool test_ioctl_sparse_set_oversize(struct torture_context *torture,
2743 struct smb2_tree *tree)
2745 struct smb2_handle fh;
2746 union smb_ioctl ioctl;
2747 NTSTATUS status;
2748 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2749 bool ok;
2750 bool is_sparse;
2751 uint8_t buf[100];
2753 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2754 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2755 FILE_ATTRIBUTE_NORMAL);
2756 torture_assert(torture, ok, "setup file");
2758 status = test_ioctl_sparse_fs_supported(torture, tree, tmp_ctx, &fh,
2759 &ok);
2760 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2761 if (!ok) {
2762 smb2_util_close(tree, fh);
2763 torture_skip(torture, "Sparse files not supported\n");
2766 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2767 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2768 torture_assert(torture, !is_sparse, "sparse attr before set");
2770 ZERO_STRUCT(ioctl);
2771 ioctl.smb2.level = RAW_IOCTL_SMB2;
2772 ioctl.smb2.in.file.handle = fh;
2773 ioctl.smb2.in.function = FSCTL_SET_SPARSE;
2774 ioctl.smb2.in.max_response_size = 0;
2775 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2778 * Attach a request buffer larger than FILE_SET_SPARSE_BUFFER
2779 * Windows still successfully processes the request.
2781 ZERO_ARRAY(buf);
2782 buf[0] = 0xFF; /* attempt to set sparse */
2783 ioctl.smb2.in.out.data = buf;
2784 ioctl.smb2.in.out.length = ARRAY_SIZE(buf);
2786 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2787 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2789 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2790 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2791 torture_assert(torture, is_sparse, "no sparse attr after set");
2793 ZERO_STRUCT(ioctl);
2794 ioctl.smb2.level = RAW_IOCTL_SMB2;
2795 ioctl.smb2.in.file.handle = fh;
2796 ioctl.smb2.in.function = FSCTL_SET_SPARSE;
2797 ioctl.smb2.in.max_response_size = 0;
2798 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2800 ZERO_ARRAY(buf); /* clear sparse */
2801 ioctl.smb2.in.out.data = buf;
2802 ioctl.smb2.in.out.length = ARRAY_SIZE(buf);
2804 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2805 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2807 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2808 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2809 torture_assert(torture, !is_sparse, "sparse attr after clear");
2811 smb2_util_close(tree, fh);
2812 talloc_free(tmp_ctx);
2813 return true;
2816 static NTSTATUS test_ioctl_qar_req(struct torture_context *torture,
2817 TALLOC_CTX *mem_ctx,
2818 struct smb2_tree *tree,
2819 struct smb2_handle fh,
2820 int64_t req_off,
2821 int64_t req_len,
2822 struct file_alloced_range_buf **_rsp,
2823 uint64_t *_rsp_count)
2825 union smb_ioctl ioctl;
2826 NTSTATUS status;
2827 enum ndr_err_code ndr_ret;
2828 struct file_alloced_range_buf far_buf;
2829 struct file_alloced_range_buf *far_rsp = NULL;
2830 uint64_t far_count = 0;
2831 int i;
2832 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
2833 if (tmp_ctx == NULL) {
2834 return NT_STATUS_NO_MEMORY;
2837 ZERO_STRUCT(ioctl);
2838 ioctl.smb2.level = RAW_IOCTL_SMB2;
2839 ioctl.smb2.in.file.handle = fh;
2840 ioctl.smb2.in.function = FSCTL_QUERY_ALLOCATED_RANGES;
2841 ioctl.smb2.in.max_response_size = 1024;
2842 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2844 far_buf.file_off = req_off;
2845 far_buf.len = req_len;
2847 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
2848 &far_buf,
2849 (ndr_push_flags_fn_t)ndr_push_file_alloced_range_buf);
2850 if (ndr_ret != NDR_ERR_SUCCESS) {
2851 status = NT_STATUS_UNSUCCESSFUL;
2852 goto err_out;
2855 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2856 if (!NT_STATUS_IS_OK(status)) {
2857 goto err_out;
2860 if (ioctl.smb2.out.out.length == 0) {
2861 goto done;
2864 if ((ioctl.smb2.out.out.length % sizeof(far_buf)) != 0) {
2865 torture_comment(torture, "invalid qry_alloced rsp len: %zd:",
2866 ioctl.smb2.out.out.length);
2867 status = NT_STATUS_INVALID_VIEW_SIZE;
2868 goto err_out;
2871 far_count = (ioctl.smb2.out.out.length / sizeof(far_buf));
2872 far_rsp = talloc_array(mem_ctx, struct file_alloced_range_buf,
2873 far_count);
2874 if (far_rsp == NULL) {
2875 status = NT_STATUS_NO_MEMORY;
2876 goto err_out;
2879 for (i = 0; i < far_count; i++) {
2880 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
2881 &far_rsp[i],
2882 (ndr_pull_flags_fn_t)ndr_pull_file_alloced_range_buf);
2883 if (ndr_ret != NDR_ERR_SUCCESS) {
2884 status = NT_STATUS_UNSUCCESSFUL;
2885 goto err_out;
2889 done:
2890 *_rsp = far_rsp;
2891 *_rsp_count = far_count;
2892 status = NT_STATUS_OK;
2893 err_out:
2894 talloc_free(tmp_ctx);
2895 return status;
2898 static bool test_ioctl_sparse_qar(struct torture_context *torture,
2899 struct smb2_tree *tree)
2901 struct smb2_handle fh;
2902 NTSTATUS status;
2903 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2904 bool ok;
2905 bool is_sparse;
2906 struct file_alloced_range_buf *far_rsp = NULL;
2907 uint64_t far_count = 0;
2909 /* zero length file, shouldn't have any ranges */
2910 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2911 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2912 FILE_ATTRIBUTE_NORMAL);
2913 torture_assert(torture, ok, "setup file");
2915 status = test_ioctl_sparse_fs_supported(torture, tree, tmp_ctx, &fh,
2916 &ok);
2917 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2918 if (!ok) {
2919 smb2_util_close(tree, fh);
2920 torture_skip(torture, "Sparse files not supported\n");
2923 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2924 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2925 torture_assert(torture, !is_sparse, "sparse attr before set");
2927 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
2928 0, /* off */
2929 0, /* len */
2930 &far_rsp,
2931 &far_count);
2932 torture_assert_ntstatus_ok(torture, status,
2933 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
2934 torture_assert_u64_equal(torture, far_count, 0,
2935 "unexpected response len");
2937 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
2938 0, /* off */
2939 1024, /* len */
2940 &far_rsp,
2941 &far_count);
2942 torture_assert_ntstatus_ok(torture, status,
2943 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
2944 torture_assert_u64_equal(torture, far_count, 0,
2945 "unexpected response len");
2947 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
2948 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2950 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2951 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2952 torture_assert(torture, is_sparse, "no sparse attr after set");
2954 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
2955 0, /* off */
2956 1024, /* len */
2957 &far_rsp,
2958 &far_count);
2959 torture_assert_ntstatus_ok(torture, status,
2960 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
2961 torture_assert_u64_equal(torture, far_count, 0,
2962 "unexpected response len");
2964 /* write into the (now) sparse file at 4k offset */
2965 ok = write_pattern(torture, tree, tmp_ctx, fh,
2966 4096, /* off */
2967 1024, /* len */
2968 4096); /* pattern offset */
2969 torture_assert(torture, ok, "write pattern");
2971 /* query range before write off, it should be alloced */
2972 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
2973 0, /* off */
2974 4096, /* len */
2975 &far_rsp,
2976 &far_count);
2977 torture_assert_ntstatus_ok(torture, status,
2978 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
2979 torture_assert_u64_equal(torture, far_count, 1,
2980 "unexpected response len");
2981 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0, "far offset");
2982 torture_assert_u64_equal(torture, far_rsp[0].len, 4096, "far len");
2985 * Query range before and past write, it should be allocated up to the
2986 * end of the write.
2988 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
2989 0, /* off */
2990 8192, /* len */
2991 &far_rsp,
2992 &far_count);
2993 torture_assert_ntstatus_ok(torture, status,
2994 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
2995 torture_assert_u64_equal(torture, far_count, 1,
2996 "unexpected response len");
2997 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0, "far offset");
2998 torture_assert_u64_equal(torture, far_rsp[0].len, 5120, "far len");
3000 smb2_util_close(tree, fh);
3001 talloc_free(tmp_ctx);
3002 return true;
3006 * basic testing of SMB2 ioctls
3008 struct torture_suite *torture_smb2_ioctl_init(void)
3010 struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "ioctl");
3012 torture_suite_add_1smb2_test(suite, "shadow_copy",
3013 test_ioctl_get_shadow_copy);
3014 torture_suite_add_1smb2_test(suite, "req_resume_key",
3015 test_ioctl_req_resume_key);
3016 torture_suite_add_1smb2_test(suite, "copy_chunk_simple",
3017 test_ioctl_copy_chunk_simple);
3018 torture_suite_add_1smb2_test(suite, "copy_chunk_multi",
3019 test_ioctl_copy_chunk_multi);
3020 torture_suite_add_1smb2_test(suite, "copy_chunk_tiny",
3021 test_ioctl_copy_chunk_tiny);
3022 torture_suite_add_1smb2_test(suite, "copy_chunk_overwrite",
3023 test_ioctl_copy_chunk_over);
3024 torture_suite_add_1smb2_test(suite, "copy_chunk_append",
3025 test_ioctl_copy_chunk_append);
3026 torture_suite_add_1smb2_test(suite, "copy_chunk_limits",
3027 test_ioctl_copy_chunk_limits);
3028 torture_suite_add_1smb2_test(suite, "copy_chunk_src_lock",
3029 test_ioctl_copy_chunk_src_lck);
3030 torture_suite_add_1smb2_test(suite, "copy_chunk_dest_lock",
3031 test_ioctl_copy_chunk_dest_lck);
3032 torture_suite_add_1smb2_test(suite, "copy_chunk_bad_key",
3033 test_ioctl_copy_chunk_bad_key);
3034 torture_suite_add_1smb2_test(suite, "copy_chunk_src_is_dest",
3035 test_ioctl_copy_chunk_src_is_dest);
3036 torture_suite_add_1smb2_test(suite, "copy_chunk_src_is_dest_overlap",
3037 test_ioctl_copy_chunk_src_is_dest_overlap);
3038 torture_suite_add_1smb2_test(suite, "copy_chunk_bad_access",
3039 test_ioctl_copy_chunk_bad_access);
3040 torture_suite_add_1smb2_test(suite, "copy_chunk_write_access",
3041 test_ioctl_copy_chunk_write_access);
3042 torture_suite_add_1smb2_test(suite, "copy_chunk_src_exceed",
3043 test_ioctl_copy_chunk_src_exceed);
3044 torture_suite_add_1smb2_test(suite, "copy_chunk_src_exceed_multi",
3045 test_ioctl_copy_chunk_src_exceed_multi);
3046 torture_suite_add_1smb2_test(suite, "copy_chunk_sparse_dest",
3047 test_ioctl_copy_chunk_sparse_dest);
3048 torture_suite_add_1smb2_test(suite, "copy_chunk_max_output_sz",
3049 test_ioctl_copy_chunk_max_output_sz);
3050 torture_suite_add_1smb2_test(suite, "copy_chunk_zero_length",
3051 test_ioctl_copy_chunk_zero_length);
3052 torture_suite_add_1smb2_test(suite, "compress_file_flag",
3053 test_ioctl_compress_file_flag);
3054 torture_suite_add_1smb2_test(suite, "compress_dir_inherit",
3055 test_ioctl_compress_dir_inherit);
3056 torture_suite_add_1smb2_test(suite, "compress_invalid_format",
3057 test_ioctl_compress_invalid_format);
3058 torture_suite_add_1smb2_test(suite, "compress_invalid_buf",
3059 test_ioctl_compress_invalid_buf);
3060 torture_suite_add_1smb2_test(suite, "compress_query_file_attr",
3061 test_ioctl_compress_query_file_attr);
3062 torture_suite_add_1smb2_test(suite, "compress_create_with_attr",
3063 test_ioctl_compress_create_with_attr);
3064 torture_suite_add_1smb2_test(suite, "compress_inherit_disable",
3065 test_ioctl_compress_inherit_disable);
3066 torture_suite_add_1smb2_test(suite, "compress_set_file_attr",
3067 test_ioctl_compress_set_file_attr);
3068 torture_suite_add_1smb2_test(suite, "compress_perms",
3069 test_ioctl_compress_perms);
3070 torture_suite_add_1smb2_test(suite, "network_interface_info",
3071 test_ioctl_network_interface_info);
3072 torture_suite_add_1smb2_test(suite, "sparse_file_flag",
3073 test_ioctl_sparse_file_flag);
3074 torture_suite_add_1smb2_test(suite, "sparse_file_attr",
3075 test_ioctl_sparse_file_attr);
3076 torture_suite_add_1smb2_test(suite, "sparse_dir_flag",
3077 test_ioctl_sparse_dir_flag);
3078 torture_suite_add_1smb2_test(suite, "sparse_set_nobuf",
3079 test_ioctl_sparse_set_nobuf);
3080 torture_suite_add_1smb2_test(suite, "sparse_set_oversize",
3081 test_ioctl_sparse_set_oversize);
3082 torture_suite_add_1smb2_test(suite, "sparse_qar",
3083 test_ioctl_sparse_qar);
3085 suite->description = talloc_strdup(suite, "SMB2-IOCTL tests");
3087 return suite;