torture/ioctl: add QAR off-by-one bug paranoia test
[Samba.git] / source4 / torture / smb2 / ioctl.c
blob38b92a9590d672832c5deebd5e416e11dbd31d00
1 /*
2 Unix SMB/CIFS implementation.
4 test suite for SMB2 ioctl operations
6 Copyright (C) David Disseldorp 2011-2015
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "includes.h"
23 #include "librpc/gen_ndr/security.h"
24 #include "libcli/smb2/smb2.h"
25 #include "libcli/smb2/smb2_calls.h"
26 #include "torture/torture.h"
27 #include "torture/smb2/proto.h"
28 #include "../libcli/smb/smbXcli_base.h"
29 #include "librpc/gen_ndr/ndr_ioctl.h"
31 #define FNAME "testfsctl.dat"
32 #define FNAME2 "testfsctl2.dat"
33 #define DNAME "testfsctl_dir"
36 basic testing of SMB2 shadow copy calls
38 static bool test_ioctl_get_shadow_copy(struct torture_context *torture,
39 struct smb2_tree *tree)
41 struct smb2_handle h;
42 uint8_t buf[100];
43 NTSTATUS status;
44 union smb_ioctl ioctl;
45 TALLOC_CTX *tmp_ctx = talloc_new(tree);
47 smb2_util_unlink(tree, FNAME);
49 status = torture_smb2_testfile(tree, FNAME, &h);
50 torture_assert_ntstatus_ok(torture, status, "create write");
52 ZERO_ARRAY(buf);
53 status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf));
54 torture_assert_ntstatus_ok(torture, status, "write");
56 ZERO_STRUCT(ioctl);
57 ioctl.smb2.level = RAW_IOCTL_SMB2;
58 ioctl.smb2.in.file.handle = h;
59 ioctl.smb2.in.function = FSCTL_SRV_ENUM_SNAPS;
60 ioctl.smb2.in.max_response_size = 16;
61 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
63 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
64 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)
65 || NT_STATUS_EQUAL(status, NT_STATUS_INVALID_DEVICE_REQUEST)) {
66 torture_skip(torture, "FSCTL_SRV_ENUM_SNAPS not supported\n");
68 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_ENUM_SNAPS");
70 return true;
74 basic testing of the SMB2 server side copy ioctls
76 static bool test_ioctl_req_resume_key(struct torture_context *torture,
77 struct smb2_tree *tree)
79 struct smb2_handle h;
80 uint8_t buf[100];
81 NTSTATUS status;
82 union smb_ioctl ioctl;
83 TALLOC_CTX *tmp_ctx = talloc_new(tree);
84 struct req_resume_key_rsp res_key;
85 enum ndr_err_code ndr_ret;
87 smb2_util_unlink(tree, FNAME);
89 status = torture_smb2_testfile(tree, FNAME, &h);
90 torture_assert_ntstatus_ok(torture, status, "create write");
92 ZERO_ARRAY(buf);
93 status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf));
94 torture_assert_ntstatus_ok(torture, status, "write");
96 ZERO_STRUCT(ioctl);
97 ioctl.smb2.level = RAW_IOCTL_SMB2;
98 ioctl.smb2.in.file.handle = h;
99 ioctl.smb2.in.function = FSCTL_SRV_REQUEST_RESUME_KEY;
100 ioctl.smb2.in.max_response_size = 32;
101 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
103 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
104 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_REQUEST_RESUME_KEY");
106 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, &res_key,
107 (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
108 torture_assert_ndr_success(torture, ndr_ret,
109 "ndr_pull_req_resume_key_rsp");
111 ndr_print_debug((ndr_print_fn_t)ndr_print_req_resume_key_rsp, "yo", &res_key);
113 talloc_free(tmp_ctx);
114 return true;
117 static uint64_t patt_hash(uint64_t off)
119 return off;
122 static bool write_pattern(struct torture_context *torture,
123 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
124 struct smb2_handle h, uint64_t off, uint64_t len,
125 uint64_t patt_off)
127 NTSTATUS status;
128 uint64_t i;
129 uint8_t *buf;
130 uint64_t io_sz = MIN(1024 * 64, len);
132 if (len == 0) {
133 return true;
136 torture_assert(torture, (len % 8) == 0, "invalid write len");
138 buf = talloc_zero_size(mem_ctx, io_sz);
139 torture_assert(torture, (buf != NULL), "no memory for file data buf");
141 while (len > 0) {
142 for (i = 0; i <= io_sz - 8; i += 8) {
143 SBVAL(buf, i, patt_hash(patt_off));
144 patt_off += 8;
147 status = smb2_util_write(tree, h,
148 buf, off, io_sz);
149 torture_assert_ntstatus_ok(torture, status, "file write");
151 len -= io_sz;
152 off += io_sz;
155 talloc_free(buf);
157 return true;
160 static bool check_pattern(struct torture_context *torture,
161 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
162 struct smb2_handle h, uint64_t off, uint64_t len,
163 uint64_t patt_off)
165 if (len == 0) {
166 return true;
169 torture_assert(torture, (len % 8) == 0, "invalid read len");
171 while (len > 0) {
172 uint64_t i;
173 struct smb2_read r;
174 NTSTATUS status;
175 uint64_t io_sz = MIN(1024 * 64, len);
177 ZERO_STRUCT(r);
178 r.in.file.handle = h;
179 r.in.length = io_sz;
180 r.in.offset = off;
181 status = smb2_read(tree, mem_ctx, &r);
182 torture_assert_ntstatus_ok(torture, status, "read");
184 torture_assert_u64_equal(torture, r.out.data.length, io_sz,
185 "read data len mismatch");
187 for (i = 0; i <= io_sz - 8; i += 8, patt_off += 8) {
188 uint64_t data = BVAL(r.out.data.data, i);
189 torture_assert_u64_equal(torture, data, patt_hash(patt_off),
190 talloc_asprintf(torture, "read data "
191 "pattern bad at %llu\n",
192 (unsigned long long)off + i));
194 talloc_free(r.out.data.data);
195 len -= io_sz;
196 off += io_sz;
199 return true;
202 static bool check_zero(struct torture_context *torture,
203 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
204 struct smb2_handle h, uint64_t off, uint64_t len)
206 uint64_t i;
207 struct smb2_read r;
208 NTSTATUS status;
210 if (len == 0) {
211 return true;
214 ZERO_STRUCT(r);
215 r.in.file.handle = h;
216 r.in.length = len;
217 r.in.offset = off;
218 status = smb2_read(tree, mem_ctx, &r);
219 torture_assert_ntstatus_ok(torture, status, "read");
221 torture_assert_u64_equal(torture, r.out.data.length, len,
222 "read data len mismatch");
224 for (i = 0; i <= len - 8; i += 8) {
225 uint64_t data = BVAL(r.out.data.data, i);
226 torture_assert_u64_equal(torture, data, 0,
227 talloc_asprintf(mem_ctx, "read zero "
228 "bad at %llu\n",
229 (unsigned long long)i));
232 talloc_free(r.out.data.data);
233 return true;
236 static bool test_setup_open(struct torture_context *torture,
237 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
238 const char *fname,
239 struct smb2_handle *fh,
240 uint32_t desired_access,
241 uint32_t file_attributes)
243 struct smb2_create io;
244 NTSTATUS status;
246 ZERO_STRUCT(io);
247 io.in.desired_access = desired_access;
248 io.in.file_attributes = file_attributes;
249 io.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
250 io.in.share_access =
251 NTCREATEX_SHARE_ACCESS_DELETE|
252 NTCREATEX_SHARE_ACCESS_READ|
253 NTCREATEX_SHARE_ACCESS_WRITE;
254 if (file_attributes & FILE_ATTRIBUTE_DIRECTORY) {
255 io.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
257 io.in.fname = fname;
259 status = smb2_create(tree, mem_ctx, &io);
260 torture_assert_ntstatus_ok(torture, status, "file create");
262 *fh = io.out.file.handle;
264 return true;
267 static bool test_setup_create_fill(struct torture_context *torture,
268 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
269 const char *fname,
270 struct smb2_handle *fh,
271 uint64_t size,
272 uint32_t desired_access,
273 uint32_t file_attributes)
275 bool ok;
277 smb2_util_unlink(tree, fname);
279 ok = test_setup_open(torture, tree, mem_ctx,
280 fname,
282 desired_access,
283 file_attributes);
284 torture_assert(torture, ok, "file open");
286 if (size > 0) {
287 ok = write_pattern(torture, tree, mem_ctx, *fh, 0, size, 0);
288 torture_assert(torture, ok, "write pattern");
290 return true;
293 static bool test_setup_copy_chunk(struct torture_context *torture,
294 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
295 uint32_t nchunks,
296 struct smb2_handle *src_h,
297 uint64_t src_size,
298 uint32_t src_desired_access,
299 struct smb2_handle *dest_h,
300 uint64_t dest_size,
301 uint32_t dest_desired_access,
302 struct srv_copychunk_copy *cc_copy,
303 union smb_ioctl *ioctl)
305 struct req_resume_key_rsp res_key;
306 bool ok;
307 NTSTATUS status;
308 enum ndr_err_code ndr_ret;
310 ok = test_setup_create_fill(torture, tree, mem_ctx, FNAME,
311 src_h, src_size, src_desired_access,
312 FILE_ATTRIBUTE_NORMAL);
313 torture_assert(torture, ok, "src file create fill");
315 ok = test_setup_create_fill(torture, tree, mem_ctx, FNAME2,
316 dest_h, dest_size, dest_desired_access,
317 FILE_ATTRIBUTE_NORMAL);
318 torture_assert(torture, ok, "dest file create fill");
320 ZERO_STRUCTPN(ioctl);
321 ioctl->smb2.level = RAW_IOCTL_SMB2;
322 ioctl->smb2.in.file.handle = *src_h;
323 ioctl->smb2.in.function = FSCTL_SRV_REQUEST_RESUME_KEY;
324 /* Allow for Key + ContextLength + Context */
325 ioctl->smb2.in.max_response_size = 32;
326 ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
328 status = smb2_ioctl(tree, mem_ctx, &ioctl->smb2);
329 torture_assert_ntstatus_ok(torture, status,
330 "FSCTL_SRV_REQUEST_RESUME_KEY");
332 ndr_ret = ndr_pull_struct_blob(&ioctl->smb2.out.out, mem_ctx, &res_key,
333 (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
335 torture_assert_ndr_success(torture, ndr_ret,
336 "ndr_pull_req_resume_key_rsp");
338 ZERO_STRUCTPN(ioctl);
339 ioctl->smb2.level = RAW_IOCTL_SMB2;
340 ioctl->smb2.in.file.handle = *dest_h;
341 ioctl->smb2.in.function = FSCTL_SRV_COPYCHUNK;
342 ioctl->smb2.in.max_response_size = sizeof(struct srv_copychunk_rsp);
343 ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
345 ZERO_STRUCTPN(cc_copy);
346 memcpy(cc_copy->source_key, res_key.resume_key, ARRAY_SIZE(cc_copy->source_key));
347 cc_copy->chunk_count = nchunks;
348 cc_copy->chunks = talloc_zero_array(mem_ctx, struct srv_copychunk, nchunks);
349 torture_assert(torture, (cc_copy->chunks != NULL), "no memory for chunks");
351 return true;
355 static bool check_copy_chunk_rsp(struct torture_context *torture,
356 struct srv_copychunk_rsp *cc_rsp,
357 uint32_t ex_chunks_written,
358 uint32_t ex_chunk_bytes_written,
359 uint32_t ex_total_bytes_written)
361 torture_assert_int_equal(torture, cc_rsp->chunks_written,
362 ex_chunks_written, "num chunks");
363 torture_assert_int_equal(torture, cc_rsp->chunk_bytes_written,
364 ex_chunk_bytes_written, "chunk bytes written");
365 torture_assert_int_equal(torture, cc_rsp->total_bytes_written,
366 ex_total_bytes_written, "chunk total bytes");
367 return true;
370 static bool test_ioctl_copy_chunk_simple(struct torture_context *torture,
371 struct smb2_tree *tree)
373 struct smb2_handle src_h;
374 struct smb2_handle dest_h;
375 NTSTATUS status;
376 union smb_ioctl ioctl;
377 TALLOC_CTX *tmp_ctx = talloc_new(tree);
378 struct srv_copychunk_copy cc_copy;
379 struct srv_copychunk_rsp cc_rsp;
380 enum ndr_err_code ndr_ret;
381 bool ok;
383 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
384 1, /* 1 chunk */
385 &src_h, 4096, /* fill 4096 byte src file */
386 SEC_RIGHTS_FILE_ALL,
387 &dest_h, 0, /* 0 byte dest file */
388 SEC_RIGHTS_FILE_ALL,
389 &cc_copy,
390 &ioctl);
391 if (!ok) {
392 torture_fail(torture, "setup copy chunk error");
395 /* copy all src file data (via a single chunk desc) */
396 cc_copy.chunks[0].source_off = 0;
397 cc_copy.chunks[0].target_off = 0;
398 cc_copy.chunks[0].length = 4096;
400 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
401 &cc_copy,
402 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
403 torture_assert_ndr_success(torture, ndr_ret,
404 "ndr_push_srv_copychunk_copy");
406 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
407 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
409 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
410 &cc_rsp,
411 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
412 torture_assert_ndr_success(torture, ndr_ret,
413 "ndr_pull_srv_copychunk_rsp");
415 ok = check_copy_chunk_rsp(torture, &cc_rsp,
416 1, /* chunks written */
417 0, /* chunk bytes unsuccessfully written */
418 4096); /* total bytes written */
419 if (!ok) {
420 torture_fail(torture, "bad copy chunk response data");
423 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
424 if (!ok) {
425 torture_fail(torture, "inconsistent file data");
428 smb2_util_close(tree, src_h);
429 smb2_util_close(tree, dest_h);
430 talloc_free(tmp_ctx);
431 return true;
434 static bool test_ioctl_copy_chunk_multi(struct torture_context *torture,
435 struct smb2_tree *tree)
437 struct smb2_handle src_h;
438 struct smb2_handle dest_h;
439 NTSTATUS status;
440 union smb_ioctl ioctl;
441 TALLOC_CTX *tmp_ctx = talloc_new(tree);
442 struct srv_copychunk_copy cc_copy;
443 struct srv_copychunk_rsp cc_rsp;
444 enum ndr_err_code ndr_ret;
445 bool ok;
447 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
448 2, /* chunks */
449 &src_h, 8192, /* src file */
450 SEC_RIGHTS_FILE_ALL,
451 &dest_h, 0, /* dest file */
452 SEC_RIGHTS_FILE_ALL,
453 &cc_copy,
454 &ioctl);
455 if (!ok) {
456 torture_fail(torture, "setup copy chunk error");
459 /* copy all src file data via two chunks */
460 cc_copy.chunks[0].source_off = 0;
461 cc_copy.chunks[0].target_off = 0;
462 cc_copy.chunks[0].length = 4096;
464 cc_copy.chunks[1].source_off = 4096;
465 cc_copy.chunks[1].target_off = 4096;
466 cc_copy.chunks[1].length = 4096;
468 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
469 &cc_copy,
470 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
471 torture_assert_ndr_success(torture, ndr_ret,
472 "ndr_push_srv_copychunk_copy");
474 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
475 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
477 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
478 &cc_rsp,
479 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
480 torture_assert_ndr_success(torture, ndr_ret,
481 "ndr_pull_srv_copychunk_rsp");
483 ok = check_copy_chunk_rsp(torture, &cc_rsp,
484 2, /* chunks written */
485 0, /* chunk bytes unsuccessfully written */
486 8192); /* total bytes written */
487 if (!ok) {
488 torture_fail(torture, "bad copy chunk response data");
491 smb2_util_close(tree, src_h);
492 smb2_util_close(tree, dest_h);
493 talloc_free(tmp_ctx);
494 return true;
497 static bool test_ioctl_copy_chunk_tiny(struct torture_context *torture,
498 struct smb2_tree *tree)
500 struct smb2_handle src_h;
501 struct smb2_handle dest_h;
502 NTSTATUS status;
503 union smb_ioctl ioctl;
504 TALLOC_CTX *tmp_ctx = talloc_new(tree);
505 struct srv_copychunk_copy cc_copy;
506 struct srv_copychunk_rsp cc_rsp;
507 enum ndr_err_code ndr_ret;
508 bool ok;
510 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
511 2, /* chunks */
512 &src_h, 96, /* src file */
513 SEC_RIGHTS_FILE_ALL,
514 &dest_h, 0, /* dest file */
515 SEC_RIGHTS_FILE_ALL,
516 &cc_copy,
517 &ioctl);
518 if (!ok) {
519 torture_fail(torture, "setup copy chunk error");
522 /* copy all src file data via two chunks, sub block size chunks */
523 cc_copy.chunks[0].source_off = 0;
524 cc_copy.chunks[0].target_off = 0;
525 cc_copy.chunks[0].length = 48;
527 cc_copy.chunks[1].source_off = 48;
528 cc_copy.chunks[1].target_off = 48;
529 cc_copy.chunks[1].length = 48;
531 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
532 &cc_copy,
533 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
534 torture_assert_ndr_success(torture, ndr_ret,
535 "ndr_push_srv_copychunk_copy");
537 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
538 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
540 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
541 &cc_rsp,
542 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
543 torture_assert_ndr_success(torture, ndr_ret,
544 "ndr_pull_srv_copychunk_rsp");
546 ok = check_copy_chunk_rsp(torture, &cc_rsp,
547 2, /* chunks written */
548 0, /* chunk bytes unsuccessfully written */
549 96); /* total bytes written */
550 if (!ok) {
551 torture_fail(torture, "bad copy chunk response data");
554 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 96, 0);
555 if (!ok) {
556 torture_fail(torture, "inconsistent file data");
559 smb2_util_close(tree, src_h);
560 smb2_util_close(tree, dest_h);
561 talloc_free(tmp_ctx);
562 return true;
565 static bool test_ioctl_copy_chunk_over(struct torture_context *torture,
566 struct smb2_tree *tree)
568 struct smb2_handle src_h;
569 struct smb2_handle dest_h;
570 NTSTATUS status;
571 union smb_ioctl ioctl;
572 TALLOC_CTX *tmp_ctx = talloc_new(tree);
573 struct srv_copychunk_copy cc_copy;
574 struct srv_copychunk_rsp cc_rsp;
575 enum ndr_err_code ndr_ret;
576 bool ok;
578 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
579 2, /* chunks */
580 &src_h, 8192, /* src file */
581 SEC_RIGHTS_FILE_ALL,
582 &dest_h, 4096, /* dest file */
583 SEC_RIGHTS_FILE_ALL,
584 &cc_copy,
585 &ioctl);
586 if (!ok) {
587 torture_fail(torture, "setup copy chunk error");
590 /* first chunk overwrites existing dest data */
591 cc_copy.chunks[0].source_off = 0;
592 cc_copy.chunks[0].target_off = 0;
593 cc_copy.chunks[0].length = 4096;
595 /* second chunk overwrites the first */
596 cc_copy.chunks[1].source_off = 4096;
597 cc_copy.chunks[1].target_off = 0;
598 cc_copy.chunks[1].length = 4096;
600 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
601 &cc_copy,
602 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
603 torture_assert_ndr_success(torture, ndr_ret,
604 "ndr_push_srv_copychunk_copy");
606 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
607 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
609 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
610 &cc_rsp,
611 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
612 torture_assert_ndr_success(torture, ndr_ret,
613 "ndr_pull_srv_copychunk_rsp");
615 ok = check_copy_chunk_rsp(torture, &cc_rsp,
616 2, /* chunks written */
617 0, /* chunk bytes unsuccessfully written */
618 8192); /* total bytes written */
619 if (!ok) {
620 torture_fail(torture, "bad copy chunk response data");
623 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 4096);
624 if (!ok) {
625 torture_fail(torture, "inconsistent file data");
628 smb2_util_close(tree, src_h);
629 smb2_util_close(tree, dest_h);
630 talloc_free(tmp_ctx);
631 return true;
634 static bool test_ioctl_copy_chunk_append(struct torture_context *torture,
635 struct smb2_tree *tree)
637 struct smb2_handle src_h;
638 struct smb2_handle dest_h;
639 NTSTATUS status;
640 union smb_ioctl ioctl;
641 TALLOC_CTX *tmp_ctx = talloc_new(tree);
642 struct srv_copychunk_copy cc_copy;
643 struct srv_copychunk_rsp cc_rsp;
644 enum ndr_err_code ndr_ret;
645 bool ok;
647 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
648 2, /* chunks */
649 &src_h, 4096, /* src file */
650 SEC_RIGHTS_FILE_ALL,
651 &dest_h, 0, /* dest file */
652 SEC_RIGHTS_FILE_ALL,
653 &cc_copy,
654 &ioctl);
655 if (!ok) {
656 torture_fail(torture, "setup copy chunk error");
659 cc_copy.chunks[0].source_off = 0;
660 cc_copy.chunks[0].target_off = 0;
661 cc_copy.chunks[0].length = 4096;
663 /* second chunk appends the same data to the first */
664 cc_copy.chunks[1].source_off = 0;
665 cc_copy.chunks[1].target_off = 4096;
666 cc_copy.chunks[1].length = 4096;
668 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
669 &cc_copy,
670 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
671 torture_assert_ndr_success(torture, ndr_ret,
672 "ndr_push_srv_copychunk_copy");
674 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
675 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
677 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
678 &cc_rsp,
679 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
680 torture_assert_ndr_success(torture, ndr_ret,
681 "ndr_pull_srv_copychunk_rsp");
683 ok = check_copy_chunk_rsp(torture, &cc_rsp,
684 2, /* chunks written */
685 0, /* chunk bytes unsuccessfully written */
686 8192); /* total bytes written */
687 if (!ok) {
688 torture_fail(torture, "bad copy chunk response data");
691 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
692 if (!ok) {
693 torture_fail(torture, "inconsistent file data");
696 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 4096, 4096, 0);
697 if (!ok) {
698 torture_fail(torture, "inconsistent file data");
701 smb2_util_close(tree, src_h);
702 smb2_util_close(tree, dest_h);
703 talloc_free(tmp_ctx);
704 return true;
707 static bool test_ioctl_copy_chunk_limits(struct torture_context *torture,
708 struct smb2_tree *tree)
710 struct smb2_handle src_h;
711 struct smb2_handle dest_h;
712 NTSTATUS status;
713 union smb_ioctl ioctl;
714 TALLOC_CTX *tmp_ctx = talloc_new(tree);
715 struct srv_copychunk_copy cc_copy;
716 struct srv_copychunk_rsp cc_rsp;
717 enum ndr_err_code ndr_ret;
718 bool ok;
720 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
721 1, /* chunks */
722 &src_h, 4096, /* src file */
723 SEC_RIGHTS_FILE_ALL,
724 &dest_h, 0, /* dest file */
725 SEC_RIGHTS_FILE_ALL,
726 &cc_copy,
727 &ioctl);
728 if (!ok) {
729 torture_fail(torture, "setup copy chunk error");
732 /* send huge chunk length request */
733 cc_copy.chunks[0].source_off = 0;
734 cc_copy.chunks[0].target_off = 0;
735 cc_copy.chunks[0].length = UINT_MAX;
737 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
738 &cc_copy,
739 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
740 torture_assert_ndr_success(torture, ndr_ret, "marshalling request");
742 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
743 torture_assert_ntstatus_equal(torture, status,
744 NT_STATUS_INVALID_PARAMETER,
745 "bad oversize chunk response");
747 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
748 &cc_rsp,
749 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
750 torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
752 torture_comment(torture, "limit max chunks, got %u\n",
753 cc_rsp.chunks_written);
754 torture_comment(torture, "limit max chunk len, got %u\n",
755 cc_rsp.chunk_bytes_written);
756 torture_comment(torture, "limit max total bytes, got %u\n",
757 cc_rsp.total_bytes_written);
759 smb2_util_close(tree, src_h);
760 smb2_util_close(tree, dest_h);
761 talloc_free(tmp_ctx);
762 return true;
765 static bool test_ioctl_copy_chunk_src_lck(struct torture_context *torture,
766 struct smb2_tree *tree)
768 struct smb2_handle src_h;
769 struct smb2_handle src_h2;
770 struct smb2_handle dest_h;
771 NTSTATUS status;
772 union smb_ioctl ioctl;
773 TALLOC_CTX *tmp_ctx = talloc_new(tree);
774 struct srv_copychunk_copy cc_copy;
775 struct srv_copychunk_rsp cc_rsp;
776 enum ndr_err_code ndr_ret;
777 bool ok;
778 struct smb2_lock lck;
779 struct smb2_lock_element el[1];
781 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
782 1, /* chunks */
783 &src_h, 4096, /* src file */
784 SEC_RIGHTS_FILE_ALL,
785 &dest_h, 0, /* dest file */
786 SEC_RIGHTS_FILE_ALL,
787 &cc_copy,
788 &ioctl);
789 if (!ok) {
790 torture_fail(torture, "setup copy chunk error");
793 cc_copy.chunks[0].source_off = 0;
794 cc_copy.chunks[0].target_off = 0;
795 cc_copy.chunks[0].length = 4096;
797 /* open and lock the copychunk src file */
798 status = torture_smb2_testfile(tree, FNAME, &src_h2);
799 torture_assert_ntstatus_ok(torture, status, "2nd src open");
801 lck.in.lock_count = 0x0001;
802 lck.in.lock_sequence = 0x00000000;
803 lck.in.file.handle = src_h2;
804 lck.in.locks = el;
805 el[0].offset = cc_copy.chunks[0].source_off;
806 el[0].length = cc_copy.chunks[0].length;
807 el[0].reserved = 0;
808 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
810 status = smb2_lock(tree, &lck);
811 torture_assert_ntstatus_ok(torture, status, "lock");
813 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
814 &cc_copy,
815 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
816 torture_assert_ndr_success(torture, ndr_ret,
817 "ndr_push_srv_copychunk_copy");
819 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
821 * 2k12 & Samba return lock_conflict, Windows 7 & 2k8 return success...
823 * Edgar Olougouna @ MS wrote:
824 * Regarding the FSCTL_SRV_COPYCHUNK and STATUS_FILE_LOCK_CONFLICT
825 * discrepancy observed between Windows versions, we confirm that the
826 * behavior change is expected.
828 * CopyChunk in Windows Server 2012 use regular Readfile/Writefile APIs
829 * to move the chunks from the source to the destination.
830 * These ReadFile/WriteFile APIs go through the byte-range lock checks,
831 * and this explains the observed STATUS_FILE_LOCK_CONFLICT error.
833 * Prior to Windows Server 2012, CopyChunk used mapped sections to move
834 * the data. And byte range locks are not enforced on mapped I/O, and
835 * this explains the STATUS_SUCCESS observed on Windows Server 2008 R2.
837 torture_assert_ntstatus_equal(torture, status,
838 NT_STATUS_FILE_LOCK_CONFLICT,
839 "FSCTL_SRV_COPYCHUNK locked");
841 /* should get cc response data with the lock conflict status */
842 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
843 &cc_rsp,
844 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
845 torture_assert_ndr_success(torture, ndr_ret,
846 "ndr_pull_srv_copychunk_rsp");
847 ok = check_copy_chunk_rsp(torture, &cc_rsp,
848 0, /* chunks written */
849 0, /* chunk bytes unsuccessfully written */
850 0); /* total bytes written */
852 lck.in.lock_count = 0x0001;
853 lck.in.lock_sequence = 0x00000001;
854 lck.in.file.handle = src_h2;
855 lck.in.locks = el;
856 el[0].offset = cc_copy.chunks[0].source_off;
857 el[0].length = cc_copy.chunks[0].length;
858 el[0].reserved = 0;
859 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
860 status = smb2_lock(tree, &lck);
861 torture_assert_ntstatus_ok(torture, status, "unlock");
863 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
864 torture_assert_ntstatus_ok(torture, status,
865 "FSCTL_SRV_COPYCHUNK unlocked");
867 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
868 &cc_rsp,
869 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
870 torture_assert_ndr_success(torture, ndr_ret,
871 "ndr_pull_srv_copychunk_rsp");
873 ok = check_copy_chunk_rsp(torture, &cc_rsp,
874 1, /* chunks written */
875 0, /* chunk bytes unsuccessfully written */
876 4096); /* total bytes written */
877 if (!ok) {
878 torture_fail(torture, "bad copy chunk response data");
881 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
882 if (!ok) {
883 torture_fail(torture, "inconsistent file data");
886 smb2_util_close(tree, src_h2);
887 smb2_util_close(tree, src_h);
888 smb2_util_close(tree, dest_h);
889 talloc_free(tmp_ctx);
890 return true;
893 static bool test_ioctl_copy_chunk_dest_lck(struct torture_context *torture,
894 struct smb2_tree *tree)
896 struct smb2_handle src_h;
897 struct smb2_handle dest_h;
898 struct smb2_handle dest_h2;
899 NTSTATUS status;
900 union smb_ioctl ioctl;
901 TALLOC_CTX *tmp_ctx = talloc_new(tree);
902 struct srv_copychunk_copy cc_copy;
903 struct srv_copychunk_rsp cc_rsp;
904 enum ndr_err_code ndr_ret;
905 bool ok;
906 struct smb2_lock lck;
907 struct smb2_lock_element el[1];
909 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
910 1, /* chunks */
911 &src_h, 4096, /* src file */
912 SEC_RIGHTS_FILE_ALL,
913 &dest_h, 4096, /* dest file */
914 SEC_RIGHTS_FILE_ALL,
915 &cc_copy,
916 &ioctl);
917 if (!ok) {
918 torture_fail(torture, "setup copy chunk error");
921 cc_copy.chunks[0].source_off = 0;
922 cc_copy.chunks[0].target_off = 0;
923 cc_copy.chunks[0].length = 4096;
925 /* open and lock the copychunk dest file */
926 status = torture_smb2_testfile(tree, FNAME2, &dest_h2);
927 torture_assert_ntstatus_ok(torture, status, "2nd src open");
929 lck.in.lock_count = 0x0001;
930 lck.in.lock_sequence = 0x00000000;
931 lck.in.file.handle = dest_h2;
932 lck.in.locks = el;
933 el[0].offset = cc_copy.chunks[0].target_off;
934 el[0].length = cc_copy.chunks[0].length;
935 el[0].reserved = 0;
936 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
938 status = smb2_lock(tree, &lck);
939 torture_assert_ntstatus_ok(torture, status, "lock");
941 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
942 &cc_copy,
943 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
944 torture_assert_ndr_success(torture, ndr_ret,
945 "ndr_push_srv_copychunk_copy");
947 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
948 torture_assert_ntstatus_equal(torture, status,
949 NT_STATUS_FILE_LOCK_CONFLICT,
950 "FSCTL_SRV_COPYCHUNK locked");
952 lck.in.lock_count = 0x0001;
953 lck.in.lock_sequence = 0x00000001;
954 lck.in.file.handle = dest_h2;
955 lck.in.locks = el;
956 el[0].offset = cc_copy.chunks[0].target_off;
957 el[0].length = cc_copy.chunks[0].length;
958 el[0].reserved = 0;
959 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
960 status = smb2_lock(tree, &lck);
961 torture_assert_ntstatus_ok(torture, status, "unlock");
963 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
964 torture_assert_ntstatus_ok(torture, status,
965 "FSCTL_SRV_COPYCHUNK unlocked");
967 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
968 &cc_rsp,
969 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
970 torture_assert_ndr_success(torture, ndr_ret,
971 "ndr_pull_srv_copychunk_rsp");
973 ok = check_copy_chunk_rsp(torture, &cc_rsp,
974 1, /* chunks written */
975 0, /* chunk bytes unsuccessfully written */
976 4096); /* total bytes written */
977 if (!ok) {
978 torture_fail(torture, "bad copy chunk response data");
981 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
982 if (!ok) {
983 torture_fail(torture, "inconsistent file data");
986 smb2_util_close(tree, dest_h2);
987 smb2_util_close(tree, src_h);
988 smb2_util_close(tree, dest_h);
989 talloc_free(tmp_ctx);
990 return true;
993 static bool test_ioctl_copy_chunk_bad_key(struct torture_context *torture,
994 struct smb2_tree *tree)
996 struct smb2_handle src_h;
997 struct smb2_handle dest_h;
998 NTSTATUS status;
999 union smb_ioctl ioctl;
1000 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1001 struct srv_copychunk_copy cc_copy;
1002 enum ndr_err_code ndr_ret;
1003 bool ok;
1005 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1007 &src_h, 4096,
1008 SEC_RIGHTS_FILE_ALL,
1009 &dest_h, 0,
1010 SEC_RIGHTS_FILE_ALL,
1011 &cc_copy,
1012 &ioctl);
1013 if (!ok) {
1014 torture_fail(torture, "setup copy chunk error");
1017 /* overwrite the resume key with a bogus value */
1018 memcpy(cc_copy.source_key, "deadbeefdeadbeefdeadbeef", 24);
1020 cc_copy.chunks[0].source_off = 0;
1021 cc_copy.chunks[0].target_off = 0;
1022 cc_copy.chunks[0].length = 4096;
1024 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1025 &cc_copy,
1026 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1027 torture_assert_ndr_success(torture, ndr_ret,
1028 "ndr_push_srv_copychunk_copy");
1030 /* Server 2k12 returns NT_STATUS_OBJECT_NAME_NOT_FOUND */
1031 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1032 torture_assert_ntstatus_equal(torture, status,
1033 NT_STATUS_OBJECT_NAME_NOT_FOUND,
1034 "FSCTL_SRV_COPYCHUNK");
1036 smb2_util_close(tree, src_h);
1037 smb2_util_close(tree, dest_h);
1038 talloc_free(tmp_ctx);
1039 return true;
1042 static bool test_ioctl_copy_chunk_src_is_dest(struct torture_context *torture,
1043 struct smb2_tree *tree)
1045 struct smb2_handle src_h;
1046 struct smb2_handle dest_h;
1047 NTSTATUS status;
1048 union smb_ioctl ioctl;
1049 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1050 struct srv_copychunk_copy cc_copy;
1051 struct srv_copychunk_rsp cc_rsp;
1052 enum ndr_err_code ndr_ret;
1053 bool ok;
1055 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1057 &src_h, 8192,
1058 SEC_RIGHTS_FILE_ALL,
1059 &dest_h, 0,
1060 SEC_RIGHTS_FILE_ALL,
1061 &cc_copy,
1062 &ioctl);
1063 if (!ok) {
1064 torture_fail(torture, "setup copy chunk error");
1067 /* the source is also the destination */
1068 ioctl.smb2.in.file.handle = src_h;
1070 /* non-overlapping */
1071 cc_copy.chunks[0].source_off = 0;
1072 cc_copy.chunks[0].target_off = 4096;
1073 cc_copy.chunks[0].length = 4096;
1075 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1076 &cc_copy,
1077 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1078 torture_assert_ndr_success(torture, ndr_ret,
1079 "ndr_push_srv_copychunk_copy");
1081 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1082 torture_assert_ntstatus_ok(torture, status,
1083 "FSCTL_SRV_COPYCHUNK");
1085 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1086 &cc_rsp,
1087 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1088 torture_assert_ndr_success(torture, ndr_ret,
1089 "ndr_pull_srv_copychunk_rsp");
1091 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1092 1, /* chunks written */
1093 0, /* chunk bytes unsuccessfully written */
1094 4096); /* total bytes written */
1095 if (!ok) {
1096 torture_fail(torture, "bad copy chunk response data");
1099 ok = check_pattern(torture, tree, tmp_ctx, src_h, 0, 4096, 0);
1100 if (!ok) {
1101 torture_fail(torture, "inconsistent file data");
1103 ok = check_pattern(torture, tree, tmp_ctx, src_h, 4096, 4096, 0);
1104 if (!ok) {
1105 torture_fail(torture, "inconsistent file data");
1108 smb2_util_close(tree, src_h);
1109 smb2_util_close(tree, dest_h);
1110 talloc_free(tmp_ctx);
1111 return true;
1115 * Test a single-chunk copychunk request, where the source and target ranges
1116 * overlap, and the SourceKey refers to the same target file. E.g:
1118 * Initial State
1119 * -------------
1120 * File: src_and_dest
1121 * Offset: 0123456789
1122 * Data: abcdefghij
1124 * Request
1125 * -------
1126 * FSCTL_SRV_COPYCHUNK(src_and_dest)
1127 * SourceKey = SRV_REQUEST_RESUME_KEY(src_and_dest)
1128 * ChunkCount = 1
1129 * Chunks[0].SourceOffset = 0
1130 * Chunks[0].TargetOffset = 4
1131 * Chunks[0].Length = 6
1133 * Resultant State
1134 * ---------------
1135 * File: src_and_dest
1136 * Offset: 0123456789
1137 * Data: abcdabcdef
1139 * The resultant contents of src_and_dest is dependent on the server's
1140 * copy algorithm. In the above example, the server uses an IO buffer
1141 * large enough to hold the entire six-byte source data before writing
1142 * to TargetOffset. If the server were to use a four-byte IO buffer and
1143 * started reads/writes from the lowest offset, then the two overlapping
1144 * bytes in the above example would be overwritten before being read. The
1145 * resultant file contents would be abcdabcdab.
1147 * Windows 2008r2 appears to use a 2048 byte copy buffer, overlapping bytes
1148 * after this offset are written before being read. Windows 2012 on the
1149 * other hand appears to use a buffer large enough to hold its maximum
1150 * supported chunk size (1M). Samba currently uses a 64k copy buffer by
1151 * default (vfs_cc_state.buf).
1153 * This test uses an 8-byte overlap at 2040-2048, so that it passes against
1154 * Windows 2008r2, 2012 and Samba servers. Note, 2008GM fails, as it appears
1155 * to use a different copy algorithm to 2008r2.
1157 static bool
1158 test_ioctl_copy_chunk_src_is_dest_overlap(struct torture_context *torture,
1159 struct smb2_tree *tree)
1161 struct smb2_handle src_h;
1162 struct smb2_handle dest_h;
1163 NTSTATUS status;
1164 union smb_ioctl ioctl;
1165 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1166 struct srv_copychunk_copy cc_copy;
1167 struct srv_copychunk_rsp cc_rsp;
1168 enum ndr_err_code ndr_ret;
1169 bool ok;
1171 /* exceed the vfs_default copy buffer */
1172 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1174 &src_h, 2048 * 2,
1175 SEC_RIGHTS_FILE_ALL,
1176 &dest_h, 0,
1177 SEC_RIGHTS_FILE_ALL,
1178 &cc_copy,
1179 &ioctl);
1180 if (!ok) {
1181 torture_fail(torture, "setup copy chunk error");
1184 /* the source is also the destination */
1185 ioctl.smb2.in.file.handle = src_h;
1187 /* 8 bytes overlap between source and target ranges */
1188 cc_copy.chunks[0].source_off = 0;
1189 cc_copy.chunks[0].target_off = 2048 - 8;
1190 cc_copy.chunks[0].length = 2048;
1192 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1193 &cc_copy,
1194 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1195 torture_assert_ndr_success(torture, ndr_ret,
1196 "ndr_push_srv_copychunk_copy");
1198 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1199 torture_assert_ntstatus_ok(torture, status,
1200 "FSCTL_SRV_COPYCHUNK");
1202 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1203 &cc_rsp,
1204 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1205 torture_assert_ndr_success(torture, ndr_ret,
1206 "ndr_pull_srv_copychunk_rsp");
1208 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1209 1, /* chunks written */
1210 0, /* chunk bytes unsuccessfully written */
1211 2048); /* total bytes written */
1212 if (!ok) {
1213 torture_fail(torture, "bad copy chunk response data");
1216 ok = check_pattern(torture, tree, tmp_ctx, src_h, 0, 2048 - 8, 0);
1217 if (!ok) {
1218 torture_fail(torture, "inconsistent file data");
1220 ok = check_pattern(torture, tree, tmp_ctx, src_h, 2048 - 8, 2048, 0);
1221 if (!ok) {
1222 torture_fail(torture, "inconsistent file data");
1225 smb2_util_close(tree, src_h);
1226 smb2_util_close(tree, dest_h);
1227 talloc_free(tmp_ctx);
1228 return true;
1231 static bool test_ioctl_copy_chunk_bad_access(struct torture_context *torture,
1232 struct smb2_tree *tree)
1234 struct smb2_handle src_h;
1235 struct smb2_handle dest_h;
1236 NTSTATUS status;
1237 union smb_ioctl ioctl;
1238 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1239 struct srv_copychunk_copy cc_copy;
1240 enum ndr_err_code ndr_ret;
1241 bool ok;
1243 /* no read permission on src */
1244 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1245 1, /* 1 chunk */
1246 &src_h, 4096, /* fill 4096 byte src file */
1247 SEC_RIGHTS_FILE_WRITE,
1248 &dest_h, 0, /* 0 byte dest file */
1249 SEC_RIGHTS_FILE_ALL,
1250 &cc_copy,
1251 &ioctl);
1252 if (!ok) {
1253 torture_fail(torture, "setup copy chunk error");
1256 cc_copy.chunks[0].source_off = 0;
1257 cc_copy.chunks[0].target_off = 0;
1258 cc_copy.chunks[0].length = 4096;
1260 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1261 &cc_copy,
1262 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1263 torture_assert_ndr_success(torture, ndr_ret,
1264 "ndr_push_srv_copychunk_copy");
1266 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1267 torture_assert_ntstatus_equal(torture, status,
1268 NT_STATUS_ACCESS_DENIED,
1269 "FSCTL_SRV_COPYCHUNK");
1271 smb2_util_close(tree, src_h);
1272 smb2_util_close(tree, dest_h);
1274 /* no write permission on dest */
1275 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1276 1, /* 1 chunk */
1277 &src_h, 4096, /* fill 4096 byte src file */
1278 SEC_RIGHTS_FILE_ALL,
1279 &dest_h, 0, /* 0 byte dest file */
1280 (SEC_RIGHTS_FILE_READ
1281 | SEC_RIGHTS_FILE_EXECUTE),
1282 &cc_copy,
1283 &ioctl);
1284 if (!ok) {
1285 torture_fail(torture, "setup copy chunk error");
1288 cc_copy.chunks[0].source_off = 0;
1289 cc_copy.chunks[0].target_off = 0;
1290 cc_copy.chunks[0].length = 4096;
1292 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1293 &cc_copy,
1294 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1295 torture_assert_ndr_success(torture, ndr_ret,
1296 "ndr_push_srv_copychunk_copy");
1298 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1299 torture_assert_ntstatus_equal(torture, status,
1300 NT_STATUS_ACCESS_DENIED,
1301 "FSCTL_SRV_COPYCHUNK");
1303 smb2_util_close(tree, src_h);
1304 smb2_util_close(tree, dest_h);
1306 /* no read permission on dest */
1307 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1308 1, /* 1 chunk */
1309 &src_h, 4096, /* fill 4096 byte src file */
1310 SEC_RIGHTS_FILE_ALL,
1311 &dest_h, 0, /* 0 byte dest file */
1312 (SEC_RIGHTS_FILE_WRITE
1313 | SEC_RIGHTS_FILE_EXECUTE),
1314 &cc_copy,
1315 &ioctl);
1316 if (!ok) {
1317 torture_fail(torture, "setup copy chunk error");
1320 cc_copy.chunks[0].source_off = 0;
1321 cc_copy.chunks[0].target_off = 0;
1322 cc_copy.chunks[0].length = 4096;
1324 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1325 &cc_copy,
1326 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1327 torture_assert_ndr_success(torture, ndr_ret,
1328 "ndr_push_srv_copychunk_copy");
1331 * FSCTL_SRV_COPYCHUNK requires read permission on dest,
1332 * FSCTL_SRV_COPYCHUNK_WRITE on the other hand does not.
1334 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1335 torture_assert_ntstatus_equal(torture, status,
1336 NT_STATUS_ACCESS_DENIED,
1337 "FSCTL_SRV_COPYCHUNK");
1339 smb2_util_close(tree, src_h);
1340 smb2_util_close(tree, dest_h);
1341 talloc_free(tmp_ctx);
1343 return true;
1346 static bool test_ioctl_copy_chunk_write_access(struct torture_context *torture,
1347 struct smb2_tree *tree)
1349 struct smb2_handle src_h;
1350 struct smb2_handle dest_h;
1351 NTSTATUS status;
1352 union smb_ioctl ioctl;
1353 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1354 struct srv_copychunk_copy cc_copy;
1355 enum ndr_err_code ndr_ret;
1356 bool ok;
1358 /* no read permission on dest with FSCTL_SRV_COPYCHUNK_WRITE */
1359 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1360 1, /* 1 chunk */
1361 &src_h, 4096, /* fill 4096 byte src file */
1362 SEC_RIGHTS_FILE_ALL,
1363 &dest_h, 0, /* 0 byte dest file */
1364 (SEC_RIGHTS_FILE_WRITE
1365 | SEC_RIGHTS_FILE_EXECUTE),
1366 &cc_copy,
1367 &ioctl);
1368 if (!ok) {
1369 torture_fail(torture, "setup copy chunk error");
1372 ioctl.smb2.in.function = FSCTL_SRV_COPYCHUNK_WRITE;
1373 cc_copy.chunks[0].source_off = 0;
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_ok(torture, status,
1385 "FSCTL_SRV_COPYCHUNK_WRITE");
1387 smb2_util_close(tree, src_h);
1388 smb2_util_close(tree, dest_h);
1389 talloc_free(tmp_ctx);
1391 return true;
1394 static bool test_ioctl_copy_chunk_src_exceed(struct torture_context *torture,
1395 struct smb2_tree *tree)
1397 struct smb2_handle src_h;
1398 struct smb2_handle dest_h;
1399 NTSTATUS status;
1400 union smb_ioctl ioctl;
1401 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1402 struct srv_copychunk_copy cc_copy;
1403 struct srv_copychunk_rsp cc_rsp;
1404 enum ndr_err_code ndr_ret;
1405 bool ok;
1407 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1408 1, /* 1 chunk */
1409 &src_h, 4096, /* fill 4096 byte src file */
1410 SEC_RIGHTS_FILE_ALL,
1411 &dest_h, 0, /* 0 byte dest file */
1412 SEC_RIGHTS_FILE_ALL,
1413 &cc_copy,
1414 &ioctl);
1415 if (!ok) {
1416 torture_fail(torture, "setup copy chunk error");
1419 /* Request copy where off + length exceeds size of src */
1420 cc_copy.chunks[0].source_off = 1024;
1421 cc_copy.chunks[0].target_off = 0;
1422 cc_copy.chunks[0].length = 4096;
1424 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1425 &cc_copy,
1426 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1427 torture_assert_ndr_success(torture, ndr_ret,
1428 "ndr_push_srv_copychunk_copy");
1430 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1431 torture_assert_ntstatus_equal(torture, status,
1432 NT_STATUS_INVALID_VIEW_SIZE,
1433 "FSCTL_SRV_COPYCHUNK oversize");
1435 /* Request copy where length exceeds size of src */
1436 cc_copy.chunks[0].source_off = 1024;
1437 cc_copy.chunks[0].target_off = 0;
1438 cc_copy.chunks[0].length = 3072;
1440 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1441 &cc_copy,
1442 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1443 torture_assert_ndr_success(torture, ndr_ret,
1444 "ndr_push_srv_copychunk_copy");
1446 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1447 torture_assert_ntstatus_ok(torture, status,
1448 "FSCTL_SRV_COPYCHUNK just right");
1450 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1451 &cc_rsp,
1452 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1453 torture_assert_ndr_success(torture, ndr_ret,
1454 "ndr_pull_srv_copychunk_rsp");
1456 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1457 1, /* chunks written */
1458 0, /* chunk bytes unsuccessfully written */
1459 3072); /* total bytes written */
1460 if (!ok) {
1461 torture_fail(torture, "bad copy chunk response data");
1464 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 3072, 1024);
1465 if (!ok) {
1466 torture_fail(torture, "inconsistent file data");
1469 smb2_util_close(tree, src_h);
1470 smb2_util_close(tree, dest_h);
1471 talloc_free(tmp_ctx);
1472 return true;
1475 static bool
1476 test_ioctl_copy_chunk_src_exceed_multi(struct torture_context *torture,
1477 struct smb2_tree *tree)
1479 struct smb2_handle src_h;
1480 struct smb2_handle dest_h;
1481 NTSTATUS status;
1482 union smb_ioctl ioctl;
1483 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1484 struct srv_copychunk_copy cc_copy;
1485 struct srv_copychunk_rsp cc_rsp;
1486 enum ndr_err_code ndr_ret;
1487 bool ok;
1489 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1490 2, /* 2 chunks */
1491 &src_h, 8192, /* fill 8192 byte src file */
1492 SEC_RIGHTS_FILE_ALL,
1493 &dest_h, 0, /* 0 byte dest file */
1494 SEC_RIGHTS_FILE_ALL,
1495 &cc_copy,
1496 &ioctl);
1497 if (!ok) {
1498 torture_fail(torture, "setup copy chunk error");
1501 /* Request copy where off + length exceeds size of src */
1502 cc_copy.chunks[0].source_off = 0;
1503 cc_copy.chunks[0].target_off = 0;
1504 cc_copy.chunks[0].length = 4096;
1506 cc_copy.chunks[1].source_off = 4096;
1507 cc_copy.chunks[1].target_off = 4096;
1508 cc_copy.chunks[1].length = 8192;
1510 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1511 &cc_copy,
1512 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1513 torture_assert_ndr_success(torture, ndr_ret,
1514 "ndr_push_srv_copychunk_copy");
1516 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1517 torture_assert_ntstatus_equal(torture, status,
1518 NT_STATUS_INVALID_VIEW_SIZE,
1519 "FSCTL_SRV_COPYCHUNK oversize");
1520 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1521 &cc_rsp,
1522 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1523 torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
1525 /* first chunk should still be written */
1526 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1527 1, /* chunks written */
1528 0, /* chunk bytes unsuccessfully written */
1529 4096); /* total bytes written */
1530 if (!ok) {
1531 torture_fail(torture, "bad copy chunk response data");
1533 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
1534 if (!ok) {
1535 torture_fail(torture, "inconsistent file data");
1538 smb2_util_close(tree, src_h);
1539 smb2_util_close(tree, dest_h);
1540 talloc_free(tmp_ctx);
1541 return true;
1544 static bool test_ioctl_copy_chunk_sparse_dest(struct torture_context *torture,
1545 struct smb2_tree *tree)
1547 struct smb2_handle src_h;
1548 struct smb2_handle dest_h;
1549 NTSTATUS status;
1550 union smb_ioctl ioctl;
1551 struct smb2_read r;
1552 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1553 struct srv_copychunk_copy cc_copy;
1554 struct srv_copychunk_rsp cc_rsp;
1555 enum ndr_err_code ndr_ret;
1556 bool ok;
1557 int i;
1559 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1560 1, /* 1 chunk */
1561 &src_h, 4096, /* fill 4096 byte src file */
1562 SEC_RIGHTS_FILE_ALL,
1563 &dest_h, 0, /* 0 byte dest file */
1564 SEC_RIGHTS_FILE_ALL,
1565 &cc_copy,
1566 &ioctl);
1567 if (!ok) {
1568 torture_fail(torture, "setup copy chunk error");
1571 /* copy all src file data (via a single chunk desc) */
1572 cc_copy.chunks[0].source_off = 0;
1573 cc_copy.chunks[0].target_off = 4096;
1574 cc_copy.chunks[0].length = 4096;
1576 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1577 &cc_copy,
1578 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1579 torture_assert_ndr_success(torture, ndr_ret,
1580 "ndr_push_srv_copychunk_copy");
1582 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1583 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
1585 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1586 &cc_rsp,
1587 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1588 torture_assert_ndr_success(torture, ndr_ret,
1589 "ndr_pull_srv_copychunk_rsp");
1591 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1592 1, /* chunks written */
1593 0, /* chunk bytes unsuccessfully written */
1594 4096); /* total bytes written */
1595 if (!ok) {
1596 torture_fail(torture, "bad copy chunk response data");
1599 /* check for zeros in first 4k */
1600 ZERO_STRUCT(r);
1601 r.in.file.handle = dest_h;
1602 r.in.length = 4096;
1603 r.in.offset = 0;
1604 status = smb2_read(tree, tmp_ctx, &r);
1605 torture_assert_ntstatus_ok(torture, status, "read");
1607 torture_assert_u64_equal(torture, r.out.data.length, 4096,
1608 "read data len mismatch");
1610 for (i = 0; i < 4096; i++) {
1611 torture_assert(torture, (r.out.data.data[i] == 0),
1612 "sparse did not pass class");
1615 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 4096, 4096, 0);
1616 if (!ok) {
1617 torture_fail(torture, "inconsistent file data");
1620 smb2_util_close(tree, src_h);
1621 smb2_util_close(tree, dest_h);
1622 talloc_free(tmp_ctx);
1623 return true;
1627 * set the ioctl MaxOutputResponse size to less than
1628 * sizeof(struct srv_copychunk_rsp)
1630 static bool test_ioctl_copy_chunk_max_output_sz(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 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1638 struct srv_copychunk_copy cc_copy;
1639 enum ndr_err_code ndr_ret;
1640 bool ok;
1642 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1643 1, /* 1 chunk */
1644 &src_h, 4096, /* fill 4096 byte src file */
1645 SEC_RIGHTS_FILE_ALL,
1646 &dest_h, 0, /* 0 byte dest file */
1647 SEC_RIGHTS_FILE_ALL,
1648 &cc_copy,
1649 &ioctl);
1650 if (!ok) {
1651 torture_fail(torture, "setup copy chunk error");
1654 cc_copy.chunks[0].source_off = 0;
1655 cc_copy.chunks[0].target_off = 0;
1656 cc_copy.chunks[0].length = 4096;
1657 /* req is valid, but use undersize max_response_size */
1658 ioctl.smb2.in.max_response_size = sizeof(struct srv_copychunk_rsp) - 1;
1660 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1661 &cc_copy,
1662 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1663 torture_assert_ndr_success(torture, ndr_ret,
1664 "ndr_push_srv_copychunk_copy");
1666 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1667 torture_assert_ntstatus_equal(torture, status,
1668 NT_STATUS_INVALID_PARAMETER,
1669 "FSCTL_SRV_COPYCHUNK");
1671 smb2_util_close(tree, src_h);
1672 smb2_util_close(tree, dest_h);
1673 talloc_free(tmp_ctx);
1674 return true;
1677 static bool test_ioctl_copy_chunk_zero_length(struct torture_context *torture,
1678 struct smb2_tree *tree)
1680 struct smb2_handle src_h;
1681 struct smb2_handle dest_h;
1682 NTSTATUS status;
1683 union smb_ioctl ioctl;
1684 union smb_fileinfo q;
1685 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1686 struct srv_copychunk_copy cc_copy;
1687 struct srv_copychunk_rsp cc_rsp;
1688 enum ndr_err_code ndr_ret;
1689 bool ok;
1691 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1692 1, /* 1 chunk */
1693 &src_h, 4096, /* fill 4096 byte src file */
1694 SEC_RIGHTS_FILE_ALL,
1695 &dest_h, 0, /* 0 byte dest file */
1696 SEC_RIGHTS_FILE_ALL,
1697 &cc_copy,
1698 &ioctl);
1699 if (!ok) {
1700 torture_fail(torture, "setup copy chunk error");
1703 /* zero length server-side copy (via a single chunk desc) */
1704 cc_copy.chunks[0].source_off = 0;
1705 cc_copy.chunks[0].target_off = 0;
1706 cc_copy.chunks[0].length = 0;
1708 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1709 &cc_copy,
1710 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1711 torture_assert_ndr_success(torture, ndr_ret,
1712 "ndr_push_srv_copychunk_copy");
1714 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1715 torture_assert_ntstatus_equal(torture, status,
1716 NT_STATUS_INVALID_PARAMETER,
1717 "bad zero-length chunk response");
1719 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1720 &cc_rsp,
1721 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1722 torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
1724 ZERO_STRUCT(q);
1725 q.all_info2.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
1726 q.all_info2.in.file.handle = dest_h;
1727 status = smb2_getinfo_file(tree, torture, &q);
1728 torture_assert_ntstatus_ok(torture, status, "getinfo");
1730 torture_assert_int_equal(torture, q.all_info2.out.size, 0,
1731 "size after zero len clone");
1733 smb2_util_close(tree, src_h);
1734 smb2_util_close(tree, dest_h);
1735 talloc_free(tmp_ctx);
1736 return true;
1739 static NTSTATUS test_ioctl_compress_fs_supported(struct torture_context *torture,
1740 struct smb2_tree *tree,
1741 TALLOC_CTX *mem_ctx,
1742 struct smb2_handle *fh,
1743 bool *compress_support)
1745 NTSTATUS status;
1746 union smb_fsinfo info;
1748 ZERO_STRUCT(info);
1749 info.generic.level = RAW_QFS_ATTRIBUTE_INFORMATION;
1750 info.generic.handle = *fh;
1751 status = smb2_getinfo_fs(tree, tree, &info);
1752 if (!NT_STATUS_IS_OK(status)) {
1753 return status;
1756 if (info.attribute_info.out.fs_attr & FILE_FILE_COMPRESSION) {
1757 *compress_support = true;
1758 } else {
1759 *compress_support = false;
1761 return NT_STATUS_OK;
1764 static NTSTATUS test_ioctl_compress_get(struct torture_context *torture,
1765 TALLOC_CTX *mem_ctx,
1766 struct smb2_tree *tree,
1767 struct smb2_handle fh,
1768 uint16_t *_compression_fmt)
1770 union smb_ioctl ioctl;
1771 struct compression_state cmpr_state;
1772 enum ndr_err_code ndr_ret;
1773 NTSTATUS status;
1775 ZERO_STRUCT(ioctl);
1776 ioctl.smb2.level = RAW_IOCTL_SMB2;
1777 ioctl.smb2.in.file.handle = fh;
1778 ioctl.smb2.in.function = FSCTL_GET_COMPRESSION;
1779 ioctl.smb2.in.max_response_size = sizeof(struct compression_state);
1780 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
1782 status = smb2_ioctl(tree, mem_ctx, &ioctl.smb2);
1783 if (!NT_STATUS_IS_OK(status)) {
1784 return status;
1787 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, mem_ctx,
1788 &cmpr_state,
1789 (ndr_pull_flags_fn_t)ndr_pull_compression_state);
1791 if (ndr_ret != NDR_ERR_SUCCESS) {
1792 return NT_STATUS_INTERNAL_ERROR;
1795 *_compression_fmt = cmpr_state.format;
1796 return NT_STATUS_OK;
1799 static NTSTATUS test_ioctl_compress_set(struct torture_context *torture,
1800 TALLOC_CTX *mem_ctx,
1801 struct smb2_tree *tree,
1802 struct smb2_handle fh,
1803 uint16_t compression_fmt)
1805 union smb_ioctl ioctl;
1806 struct compression_state cmpr_state;
1807 enum ndr_err_code ndr_ret;
1808 NTSTATUS status;
1810 ZERO_STRUCT(ioctl);
1811 ioctl.smb2.level = RAW_IOCTL_SMB2;
1812 ioctl.smb2.in.file.handle = fh;
1813 ioctl.smb2.in.function = FSCTL_SET_COMPRESSION;
1814 ioctl.smb2.in.max_response_size = 0;
1815 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
1817 cmpr_state.format = compression_fmt;
1818 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, mem_ctx,
1819 &cmpr_state,
1820 (ndr_push_flags_fn_t)ndr_push_compression_state);
1821 if (ndr_ret != NDR_ERR_SUCCESS) {
1822 return NT_STATUS_INTERNAL_ERROR;
1825 status = smb2_ioctl(tree, mem_ctx, &ioctl.smb2);
1826 return status;
1829 static bool test_ioctl_compress_file_flag(struct torture_context *torture,
1830 struct smb2_tree *tree)
1832 struct smb2_handle fh;
1833 NTSTATUS status;
1834 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1835 bool ok;
1836 uint16_t compression_fmt;
1838 ok = test_setup_create_fill(torture, tree, tmp_ctx,
1839 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
1840 FILE_ATTRIBUTE_NORMAL);
1841 torture_assert(torture, ok, "setup compression file");
1843 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
1844 &ok);
1845 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
1846 if (!ok) {
1847 smb2_util_close(tree, fh);
1848 torture_skip(torture, "FS compression not supported\n");
1851 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1852 &compression_fmt);
1853 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1855 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
1856 "initial compression state not NONE");
1858 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
1859 COMPRESSION_FORMAT_DEFAULT);
1860 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
1862 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1863 &compression_fmt);
1864 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1866 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
1867 "invalid compression state after set");
1869 smb2_util_close(tree, fh);
1870 talloc_free(tmp_ctx);
1871 return true;
1874 static bool test_ioctl_compress_dir_inherit(struct torture_context *torture,
1875 struct smb2_tree *tree)
1877 struct smb2_handle dirh;
1878 struct smb2_handle fh;
1879 NTSTATUS status;
1880 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1881 uint16_t compression_fmt;
1882 bool ok;
1883 char path_buf[PATH_MAX];
1885 smb2_deltree(tree, DNAME);
1886 ok = test_setup_create_fill(torture, tree, tmp_ctx,
1887 DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL,
1888 FILE_ATTRIBUTE_DIRECTORY);
1889 torture_assert(torture, ok, "setup compression directory");
1891 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &dirh,
1892 &ok);
1893 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
1894 if (!ok) {
1895 smb2_util_close(tree, dirh);
1896 smb2_deltree(tree, DNAME);
1897 torture_skip(torture, "FS compression not supported\n");
1900 /* set compression on parent dir, then check for inheritance */
1901 status = test_ioctl_compress_set(torture, tmp_ctx, tree, dirh,
1902 COMPRESSION_FORMAT_LZNT1);
1903 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
1905 status = test_ioctl_compress_get(torture, tmp_ctx, tree, dirh,
1906 &compression_fmt);
1907 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1909 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
1910 "invalid compression state after set");
1912 snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME);
1913 ok = test_setup_create_fill(torture, tree, tmp_ctx,
1914 path_buf, &fh, 4096, SEC_RIGHTS_FILE_ALL,
1915 FILE_ATTRIBUTE_NORMAL);
1916 torture_assert(torture, ok, "setup compression file");
1918 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1919 &compression_fmt);
1920 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1922 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
1923 "compression attr not inherited by new file");
1925 /* check compressed data is consistent */
1926 ok = check_pattern(torture, tree, tmp_ctx, fh, 0, 4096, 0);
1928 /* disable dir compression attr, file should remain compressed */
1929 status = test_ioctl_compress_set(torture, tmp_ctx, tree, dirh,
1930 COMPRESSION_FORMAT_NONE);
1931 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
1933 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1934 &compression_fmt);
1935 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1937 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
1938 "file compression attr removed after dir change");
1939 smb2_util_close(tree, fh);
1941 /* new files should no longer inherit compression attr */
1942 snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME2);
1943 ok = test_setup_create_fill(torture, tree, tmp_ctx,
1944 path_buf, &fh, 0, SEC_RIGHTS_FILE_ALL,
1945 FILE_ATTRIBUTE_NORMAL);
1946 torture_assert(torture, ok, "setup file");
1948 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1949 &compression_fmt);
1950 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1952 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
1953 "compression attr present on new file");
1955 smb2_util_close(tree, fh);
1956 smb2_util_close(tree, dirh);
1957 smb2_deltree(tree, DNAME);
1958 talloc_free(tmp_ctx);
1959 return true;
1962 static bool test_ioctl_compress_invalid_format(struct torture_context *torture,
1963 struct smb2_tree *tree)
1965 struct smb2_handle fh;
1966 NTSTATUS status;
1967 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1968 bool ok;
1969 uint16_t compression_fmt;
1971 ok = test_setup_create_fill(torture, tree, tmp_ctx,
1972 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
1973 FILE_ATTRIBUTE_NORMAL);
1974 torture_assert(torture, ok, "setup compression file");
1976 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
1977 &ok);
1978 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
1979 if (!ok) {
1980 smb2_util_close(tree, fh);
1981 torture_skip(torture, "FS compression not supported\n");
1984 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
1985 0x0042); /* bogus */
1986 torture_assert_ntstatus_equal(torture, status,
1987 NT_STATUS_INVALID_PARAMETER,
1988 "invalid FSCTL_SET_COMPRESSION");
1990 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1991 &compression_fmt);
1992 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1994 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
1995 "initial compression state not NONE");
1997 smb2_util_close(tree, fh);
1998 talloc_free(tmp_ctx);
1999 return true;
2002 static bool test_ioctl_compress_invalid_buf(struct torture_context *torture,
2003 struct smb2_tree *tree)
2005 struct smb2_handle fh;
2006 NTSTATUS status;
2007 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2008 bool ok;
2009 union smb_ioctl ioctl;
2011 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2012 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2013 FILE_ATTRIBUTE_NORMAL);
2014 torture_assert(torture, ok, "setup compression file");
2016 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
2017 &ok);
2018 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2019 if (!ok) {
2020 smb2_util_close(tree, fh);
2021 torture_skip(torture, "FS compression not supported\n");
2024 ZERO_STRUCT(ioctl);
2025 ioctl.smb2.level = RAW_IOCTL_SMB2;
2026 ioctl.smb2.in.file.handle = fh;
2027 ioctl.smb2.in.function = FSCTL_GET_COMPRESSION;
2028 ioctl.smb2.in.max_response_size = 0; /* no room for rsp data */
2029 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2031 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2032 if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_USER_BUFFER)
2033 && !NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
2034 /* neither Server 2k12 nor 2k8r2 response status */
2035 torture_assert(torture, true,
2036 "invalid FSCTL_SET_COMPRESSION");
2039 smb2_util_close(tree, fh);
2040 talloc_free(tmp_ctx);
2041 return true;
2044 static bool test_ioctl_compress_query_file_attr(struct torture_context *torture,
2045 struct smb2_tree *tree)
2047 struct smb2_handle fh;
2048 union smb_fileinfo io;
2049 NTSTATUS status;
2050 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2051 bool ok;
2053 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2054 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2055 FILE_ATTRIBUTE_NORMAL);
2056 torture_assert(torture, ok, "setup compression file");
2058 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
2059 &ok);
2060 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2061 if (!ok) {
2062 smb2_util_close(tree, fh);
2063 torture_skip(torture, "FS compression not supported\n");
2066 ZERO_STRUCT(io);
2067 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
2068 io.generic.in.file.handle = fh;
2069 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2070 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2072 torture_assert(torture,
2073 ((io.all_info2.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2074 "compression attr before set");
2076 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
2077 COMPRESSION_FORMAT_DEFAULT);
2078 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
2080 ZERO_STRUCT(io);
2081 io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2082 io.generic.in.file.handle = fh;
2083 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2084 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2086 torture_assert(torture,
2087 (io.basic_info.out.attrib & FILE_ATTRIBUTE_COMPRESSED),
2088 "no compression attr after set");
2090 smb2_util_close(tree, fh);
2091 talloc_free(tmp_ctx);
2092 return true;
2096 * Specify FILE_ATTRIBUTE_COMPRESSED on creation, Windows does not retain this
2097 * attribute.
2099 static bool test_ioctl_compress_create_with_attr(struct torture_context *torture,
2100 struct smb2_tree *tree)
2102 struct smb2_handle fh2;
2103 union smb_fileinfo io;
2104 NTSTATUS status;
2105 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2106 uint16_t compression_fmt;
2107 bool ok;
2109 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2110 FNAME2, &fh2, 0, SEC_RIGHTS_FILE_ALL,
2111 (FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_COMPRESSED));
2112 torture_assert(torture, ok, "setup compression file");
2114 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh2,
2115 &ok);
2116 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2117 if (!ok) {
2118 smb2_util_close(tree, fh2);
2119 torture_skip(torture, "FS compression not supported\n");
2122 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh2,
2123 &compression_fmt);
2124 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2126 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2127 "initial compression state not NONE");
2129 ZERO_STRUCT(io);
2130 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
2131 io.generic.in.file.handle = fh2;
2132 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2133 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2135 torture_assert(torture,
2136 ((io.all_info2.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2137 "incorrect compression attr");
2139 smb2_util_close(tree, fh2);
2140 talloc_free(tmp_ctx);
2141 return true;
2144 static bool test_ioctl_compress_inherit_disable(struct torture_context *torture,
2145 struct smb2_tree *tree)
2147 struct smb2_handle fh;
2148 struct smb2_handle dirh;
2149 char path_buf[PATH_MAX];
2150 NTSTATUS status;
2151 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2152 bool ok;
2153 uint16_t compression_fmt;
2155 struct smb2_create io;
2157 smb2_deltree(tree, DNAME);
2158 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2159 DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL,
2160 FILE_ATTRIBUTE_DIRECTORY);
2161 torture_assert(torture, ok, "setup compression directory");
2163 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &dirh,
2164 &ok);
2165 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2166 if (!ok) {
2167 smb2_util_close(tree, dirh);
2168 smb2_deltree(tree, DNAME);
2169 torture_skip(torture, "FS compression not supported\n");
2172 /* set compression on parent dir, then check for inheritance */
2173 status = test_ioctl_compress_set(torture, tmp_ctx, tree, dirh,
2174 COMPRESSION_FORMAT_LZNT1);
2175 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
2177 status = test_ioctl_compress_get(torture, tmp_ctx, tree, dirh,
2178 &compression_fmt);
2179 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2181 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
2182 "invalid compression state after set");
2183 smb2_util_close(tree, dirh);
2185 snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME);
2186 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2187 path_buf, &fh, 0, SEC_RIGHTS_FILE_ALL,
2188 FILE_ATTRIBUTE_NORMAL);
2189 torture_assert(torture, ok, "setup compression file");
2191 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2192 &compression_fmt);
2193 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2195 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
2196 "compression attr not inherited by new file");
2197 smb2_util_close(tree, fh);
2199 snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME2);
2201 /* NO_COMPRESSION option should block inheritance */
2202 ZERO_STRUCT(io);
2203 io.in.desired_access = SEC_RIGHTS_FILE_ALL;
2204 io.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
2205 io.in.create_disposition = NTCREATEX_DISP_CREATE;
2206 io.in.create_options = NTCREATEX_OPTIONS_NO_COMPRESSION;
2207 io.in.share_access =
2208 NTCREATEX_SHARE_ACCESS_DELETE|
2209 NTCREATEX_SHARE_ACCESS_READ|
2210 NTCREATEX_SHARE_ACCESS_WRITE;
2211 io.in.fname = path_buf;
2213 status = smb2_create(tree, tmp_ctx, &io);
2214 torture_assert_ntstatus_ok(torture, status, "file create");
2216 fh = io.out.file.handle;
2218 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2219 &compression_fmt);
2220 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2222 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2223 "compression attr inherited by NO_COMPRESSION file");
2224 smb2_util_close(tree, fh);
2227 snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, DNAME);
2228 ZERO_STRUCT(io);
2229 io.in.desired_access = SEC_RIGHTS_FILE_ALL;
2230 io.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
2231 io.in.create_disposition = NTCREATEX_DISP_CREATE;
2232 io.in.create_options = (NTCREATEX_OPTIONS_NO_COMPRESSION
2233 | NTCREATEX_OPTIONS_DIRECTORY);
2234 io.in.share_access =
2235 NTCREATEX_SHARE_ACCESS_DELETE|
2236 NTCREATEX_SHARE_ACCESS_READ|
2237 NTCREATEX_SHARE_ACCESS_WRITE;
2238 io.in.fname = path_buf;
2240 status = smb2_create(tree, tmp_ctx, &io);
2241 torture_assert_ntstatus_ok(torture, status, "dir create");
2243 dirh = io.out.file.handle;
2245 status = test_ioctl_compress_get(torture, tmp_ctx, tree, dirh,
2246 &compression_fmt);
2247 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2249 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2250 "compression attr inherited by NO_COMPRESSION dir");
2251 smb2_util_close(tree, dirh);
2252 smb2_deltree(tree, DNAME);
2254 talloc_free(tmp_ctx);
2255 return true;
2258 /* attempting to set compression via SetInfo should not stick */
2259 static bool test_ioctl_compress_set_file_attr(struct torture_context *torture,
2260 struct smb2_tree *tree)
2262 struct smb2_handle fh;
2263 struct smb2_handle dirh;
2264 union smb_fileinfo io;
2265 union smb_setfileinfo set_io;
2266 uint16_t compression_fmt;
2267 NTSTATUS status;
2268 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2269 bool ok;
2271 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2272 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2273 FILE_ATTRIBUTE_NORMAL);
2274 torture_assert(torture, ok, "setup compression file");
2276 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
2277 &ok);
2278 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2279 if (!ok) {
2280 smb2_util_close(tree, fh);
2281 torture_skip(torture, "FS compression not supported\n");
2284 ZERO_STRUCT(io);
2285 io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2286 io.generic.in.file.handle = fh;
2287 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2288 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2290 torture_assert(torture,
2291 ((io.basic_info.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2292 "compression attr before set");
2294 ZERO_STRUCT(set_io);
2295 set_io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2296 set_io.basic_info.in.file.handle = fh;
2297 set_io.basic_info.in.create_time = io.basic_info.out.create_time;
2298 set_io.basic_info.in.access_time = io.basic_info.out.access_time;
2299 set_io.basic_info.in.write_time = io.basic_info.out.write_time;
2300 set_io.basic_info.in.change_time = io.basic_info.out.change_time;
2301 set_io.basic_info.in.attrib = (io.basic_info.out.attrib
2302 | FILE_ATTRIBUTE_COMPRESSED);
2303 status = smb2_setinfo_file(tree, &set_io);
2304 torture_assert_ntstatus_ok(torture, status, "SMB2_SETINFO_FILE");
2306 ZERO_STRUCT(io);
2307 io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2308 io.generic.in.file.handle = fh;
2309 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2310 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2312 torture_assert(torture,
2313 ((io.basic_info.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2314 "compression attr after set");
2316 smb2_util_close(tree, fh);
2317 smb2_deltree(tree, DNAME);
2318 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2319 DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL,
2320 FILE_ATTRIBUTE_DIRECTORY);
2321 torture_assert(torture, ok, "setup compression directory");
2323 ZERO_STRUCT(io);
2324 io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2325 io.generic.in.file.handle = dirh;
2326 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2327 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2329 torture_assert(torture,
2330 ((io.basic_info.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2331 "compression attr before set");
2333 ZERO_STRUCT(set_io);
2334 set_io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2335 set_io.basic_info.in.file.handle = dirh;
2336 set_io.basic_info.in.create_time = io.basic_info.out.create_time;
2337 set_io.basic_info.in.access_time = io.basic_info.out.access_time;
2338 set_io.basic_info.in.write_time = io.basic_info.out.write_time;
2339 set_io.basic_info.in.change_time = io.basic_info.out.change_time;
2340 set_io.basic_info.in.attrib = (io.basic_info.out.attrib
2341 | FILE_ATTRIBUTE_COMPRESSED);
2342 status = smb2_setinfo_file(tree, &set_io);
2343 torture_assert_ntstatus_ok(torture, status, "SMB2_SETINFO_FILE");
2345 status = test_ioctl_compress_get(torture, tmp_ctx, tree, dirh,
2346 &compression_fmt);
2347 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2349 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2350 "dir compression set after SetInfo");
2352 smb2_util_close(tree, dirh);
2353 talloc_free(tmp_ctx);
2354 return true;
2357 static bool test_ioctl_compress_perms(struct torture_context *torture,
2358 struct smb2_tree *tree)
2360 struct smb2_handle fh;
2361 uint16_t compression_fmt;
2362 union smb_fileinfo io;
2363 NTSTATUS status;
2364 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2365 bool ok;
2367 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2368 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2369 FILE_ATTRIBUTE_NORMAL);
2370 torture_assert(torture, ok, "setup compression file");
2372 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
2373 &ok);
2374 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2375 smb2_util_close(tree, fh);
2376 if (!ok) {
2377 torture_skip(torture, "FS compression not supported\n");
2380 /* attempt get compression without READ_ATTR permission */
2381 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2382 FNAME, &fh, 0,
2383 (SEC_RIGHTS_FILE_READ & ~(SEC_FILE_READ_ATTRIBUTE
2384 | SEC_STD_READ_CONTROL
2385 | SEC_FILE_READ_EA)),
2386 FILE_ATTRIBUTE_NORMAL);
2387 torture_assert(torture, ok, "setup compression file");
2389 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2390 &compression_fmt);
2391 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2392 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2393 "compression set after create");
2394 smb2_util_close(tree, fh);
2396 /* set compression without WRITE_ATTR permission should succeed */
2397 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2398 FNAME, &fh, 0,
2399 (SEC_RIGHTS_FILE_WRITE & ~(SEC_FILE_WRITE_ATTRIBUTE
2400 | SEC_STD_WRITE_DAC
2401 | SEC_FILE_WRITE_EA)),
2402 FILE_ATTRIBUTE_NORMAL);
2403 torture_assert(torture, ok, "setup compression file");
2405 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
2406 COMPRESSION_FORMAT_DEFAULT);
2407 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
2408 smb2_util_close(tree, fh);
2410 ok = test_setup_open(torture, tree, tmp_ctx,
2411 FNAME, &fh, SEC_RIGHTS_FILE_ALL,
2412 FILE_ATTRIBUTE_NORMAL);
2413 torture_assert(torture, ok, "setup compression file");
2414 ZERO_STRUCT(io);
2415 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
2416 io.generic.in.file.handle = fh;
2417 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2418 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2420 torture_assert(torture,
2421 (io.all_info2.out.attrib & FILE_ATTRIBUTE_COMPRESSED),
2422 "incorrect compression attr");
2423 smb2_util_close(tree, fh);
2425 /* attempt get compression without READ_DATA permission */
2426 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2427 FNAME, &fh, 0,
2428 (SEC_RIGHTS_FILE_READ & ~SEC_FILE_READ_DATA),
2429 FILE_ATTRIBUTE_NORMAL);
2430 torture_assert(torture, ok, "setup compression file");
2432 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2433 &compression_fmt);
2434 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2435 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2436 "compression enabled after set");
2437 smb2_util_close(tree, fh);
2439 /* attempt get compression with only SYNCHRONIZE permission */
2440 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2441 FNAME, &fh, 0,
2442 SEC_STD_SYNCHRONIZE,
2443 FILE_ATTRIBUTE_NORMAL);
2444 torture_assert(torture, ok, "setup compression file");
2446 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2447 &compression_fmt);
2448 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2449 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2450 "compression not enabled after set");
2451 smb2_util_close(tree, fh);
2453 /* attempt to set compression without WRITE_DATA permission */
2454 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2455 FNAME, &fh, 0,
2456 (SEC_RIGHTS_FILE_WRITE & (~SEC_FILE_WRITE_DATA)),
2457 FILE_ATTRIBUTE_NORMAL);
2458 torture_assert(torture, ok, "setup compression file");
2460 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
2461 COMPRESSION_FORMAT_DEFAULT);
2462 torture_assert_ntstatus_equal(torture, status,
2463 NT_STATUS_ACCESS_DENIED,
2464 "FSCTL_SET_COMPRESSION permission");
2465 smb2_util_close(tree, fh);
2467 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2468 FNAME, &fh, 0,
2469 (SEC_RIGHTS_FILE_WRITE & (~SEC_FILE_WRITE_DATA)),
2470 FILE_ATTRIBUTE_NORMAL);
2471 torture_assert(torture, ok, "setup compression file");
2473 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
2474 COMPRESSION_FORMAT_NONE);
2475 torture_assert_ntstatus_equal(torture, status,
2476 NT_STATUS_ACCESS_DENIED,
2477 "FSCTL_SET_COMPRESSION permission");
2478 smb2_util_close(tree, fh);
2480 talloc_free(tmp_ctx);
2481 return true;
2485 basic testing of the SMB2 FSCTL_QUERY_NETWORK_INTERFACE_INFO ioctl
2487 static bool test_ioctl_network_interface_info(struct torture_context *torture,
2488 struct smb2_tree *tree)
2490 union smb_ioctl ioctl;
2491 struct smb2_handle fh;
2492 NTSTATUS status;
2493 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2494 struct fsctl_net_iface_info net_iface;
2495 enum ndr_err_code ndr_ret;
2496 uint32_t caps;
2498 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
2499 if (!(caps & SMB2_CAP_MULTI_CHANNEL)) {
2500 torture_skip(torture, "server doesn't support SMB2_CAP_MULTI_CHANNEL\n");
2503 ZERO_STRUCT(ioctl);
2504 ioctl.smb2.level = RAW_IOCTL_SMB2;
2505 fh.data[0] = UINT64_MAX;
2506 fh.data[1] = UINT64_MAX;
2507 ioctl.smb2.in.file.handle = fh;
2508 ioctl.smb2.in.function = FSCTL_QUERY_NETWORK_INTERFACE_INFO;
2509 ioctl.smb2.in.max_response_size = 0x10000; /* Windows client sets this to 64KiB */
2510 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2512 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2513 torture_assert_ntstatus_ok(torture, status, "FSCTL_QUERY_NETWORK_INTERFACE_INFO");
2515 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, &net_iface,
2516 (ndr_pull_flags_fn_t)ndr_pull_fsctl_net_iface_info);
2517 torture_assert_ndr_success(torture, ndr_ret,
2518 "ndr_pull_fsctl_net_iface_info");
2520 ndr_print_debug((ndr_print_fn_t)ndr_print_fsctl_net_iface_info,
2521 "Network Interface Info", &net_iface);
2523 talloc_free(tmp_ctx);
2524 return true;
2527 static NTSTATUS test_ioctl_sparse_fs_supported(struct torture_context *torture,
2528 struct smb2_tree *tree,
2529 TALLOC_CTX *mem_ctx,
2530 struct smb2_handle *fh,
2531 bool *sparse_support)
2533 NTSTATUS status;
2534 union smb_fsinfo info;
2536 ZERO_STRUCT(info);
2537 info.generic.level = RAW_QFS_ATTRIBUTE_INFORMATION;
2538 info.generic.handle = *fh;
2539 status = smb2_getinfo_fs(tree, tree, &info);
2540 if (!NT_STATUS_IS_OK(status)) {
2541 return status;
2544 if (info.attribute_info.out.fs_attr & FILE_SUPPORTS_SPARSE_FILES) {
2545 *sparse_support = true;
2546 } else {
2547 *sparse_support = false;
2549 return NT_STATUS_OK;
2552 static NTSTATUS test_ioctl_sparse_req(struct torture_context *torture,
2553 TALLOC_CTX *mem_ctx,
2554 struct smb2_tree *tree,
2555 struct smb2_handle fh,
2556 bool set)
2558 union smb_ioctl ioctl;
2559 NTSTATUS status;
2560 uint8_t set_sparse;
2562 ZERO_STRUCT(ioctl);
2563 ioctl.smb2.level = RAW_IOCTL_SMB2;
2564 ioctl.smb2.in.file.handle = fh;
2565 ioctl.smb2.in.function = FSCTL_SET_SPARSE;
2566 ioctl.smb2.in.max_response_size = 0;
2567 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2568 set_sparse = (set ? 0xFF : 0x0);
2569 ioctl.smb2.in.out.data = &set_sparse;
2570 ioctl.smb2.in.out.length = sizeof(set_sparse);
2572 status = smb2_ioctl(tree, mem_ctx, &ioctl.smb2);
2573 return status;
2576 static NTSTATUS test_sparse_get(struct torture_context *torture,
2577 TALLOC_CTX *mem_ctx,
2578 struct smb2_tree *tree,
2579 struct smb2_handle fh,
2580 bool *_is_sparse)
2582 union smb_fileinfo io;
2583 NTSTATUS status;
2585 ZERO_STRUCT(io);
2586 io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2587 io.generic.in.file.handle = fh;
2588 status = smb2_getinfo_file(tree, mem_ctx, &io);
2589 if (!NT_STATUS_IS_OK(status)) {
2590 return status;
2592 *_is_sparse = !!(io.basic_info.out.attrib & FILE_ATTRIBUTE_SPARSE);
2594 return status;
2597 static bool test_ioctl_sparse_file_flag(struct torture_context *torture,
2598 struct smb2_tree *tree)
2600 struct smb2_handle fh;
2601 union smb_fileinfo io;
2602 NTSTATUS status;
2603 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2604 bool ok;
2605 bool is_sparse;
2607 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2608 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2609 FILE_ATTRIBUTE_NORMAL);
2610 torture_assert(torture, ok, "setup file");
2612 status = test_ioctl_sparse_fs_supported(torture, tree, tmp_ctx, &fh,
2613 &ok);
2614 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2615 if (!ok) {
2616 smb2_util_close(tree, fh);
2617 torture_skip(torture, "Sparse files not supported\n");
2620 ZERO_STRUCT(io);
2621 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
2622 io.generic.in.file.handle = fh;
2623 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2624 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2626 torture_assert(torture,
2627 ((io.all_info2.out.attrib & FILE_ATTRIBUTE_SPARSE) == 0),
2628 "sparse attr before set");
2630 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
2631 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2633 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2634 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2635 torture_assert(torture, is_sparse, "no sparse attr after set");
2637 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false);
2638 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2640 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2641 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2642 torture_assert(torture, !is_sparse, "sparse attr after unset");
2644 smb2_util_close(tree, fh);
2645 talloc_free(tmp_ctx);
2646 return true;
2649 static bool test_ioctl_sparse_file_attr(struct torture_context *torture,
2650 struct smb2_tree *tree)
2652 struct smb2_handle fh;
2653 NTSTATUS status;
2654 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2655 bool ok;
2656 bool is_sparse;
2658 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2659 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2660 (FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_SPARSE));
2661 torture_assert(torture, ok, "setup file");
2663 status = test_ioctl_sparse_fs_supported(torture, tree, tmp_ctx, &fh,
2664 &ok);
2665 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2666 if (!ok) {
2667 smb2_util_close(tree, fh);
2668 torture_skip(torture, "Sparse files not supported\n");
2671 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2672 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2673 torture_assert(torture, !is_sparse, "sparse attr on open");
2675 smb2_util_close(tree, fh);
2676 talloc_free(tmp_ctx);
2677 return true;
2680 static bool test_ioctl_sparse_dir_flag(struct torture_context *torture,
2681 struct smb2_tree *tree)
2683 struct smb2_handle dirh;
2684 NTSTATUS status;
2685 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2686 bool ok;
2688 smb2_deltree(tree, DNAME);
2689 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2690 DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL,
2691 FILE_ATTRIBUTE_DIRECTORY);
2692 torture_assert(torture, ok, "setup sparse directory");
2694 status = test_ioctl_sparse_fs_supported(torture, tree, tmp_ctx, &dirh,
2695 &ok);
2696 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2697 if (!ok) {
2698 smb2_util_close(tree, dirh);
2699 smb2_deltree(tree, DNAME);
2700 torture_skip(torture, "Sparse files not supported\n");
2703 /* set sparse dir should fail, check for 2k12 & 2k8 response */
2704 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, dirh, true);
2705 torture_assert_ntstatus_equal(torture, status,
2706 NT_STATUS_INVALID_PARAMETER,
2707 "dir FSCTL_SET_SPARSE status");
2709 smb2_util_close(tree, dirh);
2710 smb2_deltree(tree, DNAME);
2711 talloc_free(tmp_ctx);
2712 return true;
2716 * FSCTL_SET_SPARSE can be sent with (already tested) or without a SetSparse
2717 * buffer to indicate whether the flag should be set or cleared. When sent
2718 * without a buffer, it must be handled as if SetSparse=TRUE.
2720 static bool test_ioctl_sparse_set_nobuf(struct torture_context *torture,
2721 struct smb2_tree *tree)
2723 struct smb2_handle fh;
2724 union smb_ioctl ioctl;
2725 NTSTATUS status;
2726 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2727 bool ok;
2728 bool is_sparse;
2730 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2731 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2732 FILE_ATTRIBUTE_NORMAL);
2733 torture_assert(torture, ok, "setup file");
2735 status = test_ioctl_sparse_fs_supported(torture, tree, tmp_ctx, &fh,
2736 &ok);
2737 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2738 if (!ok) {
2739 smb2_util_close(tree, fh);
2740 torture_skip(torture, "Sparse files not supported\n");
2743 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2744 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2745 torture_assert(torture, !is_sparse, "sparse attr before set");
2747 ZERO_STRUCT(ioctl);
2748 ioctl.smb2.level = RAW_IOCTL_SMB2;
2749 ioctl.smb2.in.file.handle = fh;
2750 ioctl.smb2.in.function = FSCTL_SET_SPARSE;
2751 ioctl.smb2.in.max_response_size = 0;
2752 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2753 /* ioctl.smb2.in.out is zeroed, no SetSparse buffer */
2755 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2756 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2758 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2759 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2760 torture_assert(torture, is_sparse, "no sparse attr after set");
2762 /* second non-SetSparse request shouldn't toggle sparse */
2763 ZERO_STRUCT(ioctl);
2764 ioctl.smb2.level = RAW_IOCTL_SMB2;
2765 ioctl.smb2.in.file.handle = fh;
2766 ioctl.smb2.in.function = FSCTL_SET_SPARSE;
2767 ioctl.smb2.in.max_response_size = 0;
2768 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2770 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2771 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2773 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2774 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2775 torture_assert(torture, is_sparse, "no sparse attr after 2nd set");
2777 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false);
2778 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2780 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2781 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2782 torture_assert(torture, !is_sparse, "sparse attr after unset");
2784 smb2_util_close(tree, fh);
2785 talloc_free(tmp_ctx);
2786 return true;
2789 static bool test_ioctl_sparse_set_oversize(struct torture_context *torture,
2790 struct smb2_tree *tree)
2792 struct smb2_handle fh;
2793 union smb_ioctl ioctl;
2794 NTSTATUS status;
2795 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2796 bool ok;
2797 bool is_sparse;
2798 uint8_t buf[100];
2800 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2801 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2802 FILE_ATTRIBUTE_NORMAL);
2803 torture_assert(torture, ok, "setup file");
2805 status = test_ioctl_sparse_fs_supported(torture, tree, tmp_ctx, &fh,
2806 &ok);
2807 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2808 if (!ok) {
2809 smb2_util_close(tree, fh);
2810 torture_skip(torture, "Sparse files not supported\n");
2813 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2814 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2815 torture_assert(torture, !is_sparse, "sparse attr before set");
2817 ZERO_STRUCT(ioctl);
2818 ioctl.smb2.level = RAW_IOCTL_SMB2;
2819 ioctl.smb2.in.file.handle = fh;
2820 ioctl.smb2.in.function = FSCTL_SET_SPARSE;
2821 ioctl.smb2.in.max_response_size = 0;
2822 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2825 * Attach a request buffer larger than FILE_SET_SPARSE_BUFFER
2826 * Windows still successfully processes the request.
2828 ZERO_ARRAY(buf);
2829 buf[0] = 0xFF; /* attempt to set sparse */
2830 ioctl.smb2.in.out.data = buf;
2831 ioctl.smb2.in.out.length = ARRAY_SIZE(buf);
2833 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2834 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2836 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2837 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2838 torture_assert(torture, is_sparse, "no sparse attr after set");
2840 ZERO_STRUCT(ioctl);
2841 ioctl.smb2.level = RAW_IOCTL_SMB2;
2842 ioctl.smb2.in.file.handle = fh;
2843 ioctl.smb2.in.function = FSCTL_SET_SPARSE;
2844 ioctl.smb2.in.max_response_size = 0;
2845 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2847 ZERO_ARRAY(buf); /* clear sparse */
2848 ioctl.smb2.in.out.data = buf;
2849 ioctl.smb2.in.out.length = ARRAY_SIZE(buf);
2851 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2852 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2854 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2855 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2856 torture_assert(torture, !is_sparse, "sparse attr after clear");
2858 smb2_util_close(tree, fh);
2859 talloc_free(tmp_ctx);
2860 return true;
2863 static NTSTATUS test_ioctl_qar_req(struct torture_context *torture,
2864 TALLOC_CTX *mem_ctx,
2865 struct smb2_tree *tree,
2866 struct smb2_handle fh,
2867 int64_t req_off,
2868 int64_t req_len,
2869 struct file_alloced_range_buf **_rsp,
2870 uint64_t *_rsp_count)
2872 union smb_ioctl ioctl;
2873 NTSTATUS status;
2874 enum ndr_err_code ndr_ret;
2875 struct file_alloced_range_buf far_buf;
2876 struct file_alloced_range_buf *far_rsp = NULL;
2877 uint64_t far_count = 0;
2878 int i;
2879 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
2880 if (tmp_ctx == NULL) {
2881 return NT_STATUS_NO_MEMORY;
2884 ZERO_STRUCT(ioctl);
2885 ioctl.smb2.level = RAW_IOCTL_SMB2;
2886 ioctl.smb2.in.file.handle = fh;
2887 ioctl.smb2.in.function = FSCTL_QUERY_ALLOCATED_RANGES;
2888 ioctl.smb2.in.max_response_size = 1024;
2889 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2891 far_buf.file_off = req_off;
2892 far_buf.len = req_len;
2894 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
2895 &far_buf,
2896 (ndr_push_flags_fn_t)ndr_push_file_alloced_range_buf);
2897 if (ndr_ret != NDR_ERR_SUCCESS) {
2898 status = NT_STATUS_UNSUCCESSFUL;
2899 goto err_out;
2902 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2903 if (!NT_STATUS_IS_OK(status)) {
2904 goto err_out;
2907 if (ioctl.smb2.out.out.length == 0) {
2908 goto done;
2911 if ((ioctl.smb2.out.out.length % sizeof(far_buf)) != 0) {
2912 torture_comment(torture, "invalid qry_alloced rsp len: %zd:",
2913 ioctl.smb2.out.out.length);
2914 status = NT_STATUS_INVALID_VIEW_SIZE;
2915 goto err_out;
2918 far_count = (ioctl.smb2.out.out.length / sizeof(far_buf));
2919 far_rsp = talloc_array(mem_ctx, struct file_alloced_range_buf,
2920 far_count);
2921 if (far_rsp == NULL) {
2922 status = NT_STATUS_NO_MEMORY;
2923 goto err_out;
2926 for (i = 0; i < far_count; i++) {
2927 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
2928 &far_rsp[i],
2929 (ndr_pull_flags_fn_t)ndr_pull_file_alloced_range_buf);
2930 if (ndr_ret != NDR_ERR_SUCCESS) {
2931 status = NT_STATUS_UNSUCCESSFUL;
2932 goto err_out;
2934 /* move to next buffer */
2935 ioctl.smb2.out.out.data += sizeof(far_buf);
2936 ioctl.smb2.out.out.length -= sizeof(far_buf);
2939 done:
2940 *_rsp = far_rsp;
2941 *_rsp_count = far_count;
2942 status = NT_STATUS_OK;
2943 err_out:
2944 talloc_free(tmp_ctx);
2945 return status;
2948 static bool test_ioctl_sparse_qar(struct torture_context *torture,
2949 struct smb2_tree *tree)
2951 struct smb2_handle fh;
2952 NTSTATUS status;
2953 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2954 bool ok;
2955 bool is_sparse;
2956 struct file_alloced_range_buf *far_rsp = NULL;
2957 uint64_t far_count = 0;
2959 /* zero length file, shouldn't have any ranges */
2960 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2961 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2962 FILE_ATTRIBUTE_NORMAL);
2963 torture_assert(torture, ok, "setup file");
2965 status = test_ioctl_sparse_fs_supported(torture, tree, tmp_ctx, &fh,
2966 &ok);
2967 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2968 if (!ok) {
2969 smb2_util_close(tree, fh);
2970 torture_skip(torture, "Sparse files not supported\n");
2973 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2974 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2975 torture_assert(torture, !is_sparse, "sparse attr before set");
2977 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
2978 0, /* off */
2979 0, /* len */
2980 &far_rsp,
2981 &far_count);
2982 torture_assert_ntstatus_ok(torture, status,
2983 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
2984 torture_assert_u64_equal(torture, far_count, 0,
2985 "unexpected response len");
2987 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
2988 0, /* off */
2989 1024, /* len */
2990 &far_rsp,
2991 &far_count);
2992 torture_assert_ntstatus_ok(torture, status,
2993 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
2994 torture_assert_u64_equal(torture, far_count, 0,
2995 "unexpected response len");
2997 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
2998 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3000 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3001 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3002 torture_assert(torture, is_sparse, "no sparse attr after set");
3004 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3005 0, /* off */
3006 1024, /* len */
3007 &far_rsp,
3008 &far_count);
3009 torture_assert_ntstatus_ok(torture, status,
3010 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3011 torture_assert_u64_equal(torture, far_count, 0,
3012 "unexpected response len");
3014 /* write into the (now) sparse file at 4k offset */
3015 ok = write_pattern(torture, tree, tmp_ctx, fh,
3016 4096, /* off */
3017 1024, /* len */
3018 4096); /* pattern offset */
3019 torture_assert(torture, ok, "write pattern");
3022 * Query range before write off. Whether it's allocated or not is FS
3023 * dependent. NTFS deallocates chunks in 64K increments, but others
3024 * (e.g. XFS, Btrfs, etc.) may deallocate 4K chunks.
3026 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3027 0, /* off */
3028 4096, /* len */
3029 &far_rsp,
3030 &far_count);
3031 torture_assert_ntstatus_ok(torture, status,
3032 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3033 if (far_count == 0) {
3034 torture_comment(torture, "FS deallocated 4K chunk\n");
3035 } else {
3036 /* expect fully allocated */
3037 torture_assert_u64_equal(torture, far_count, 1,
3038 "unexpected response len");
3039 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0, "far offset");
3040 torture_assert_u64_equal(torture, far_rsp[0].len, 4096, "far len");
3044 * Query range before and past write, it should be allocated up to the
3045 * end of the write.
3047 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3048 0, /* off */
3049 8192, /* len */
3050 &far_rsp,
3051 &far_count);
3052 torture_assert_ntstatus_ok(torture, status,
3053 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3054 torture_assert_u64_equal(torture, far_count, 1,
3055 "unexpected response len");
3056 /* FS dependent */
3057 if (far_rsp[0].file_off == 4096) {
3058 /* 4K chunk unallocated */
3059 torture_assert_u64_equal(torture, far_rsp[0].file_off, 4096, "far offset");
3060 torture_assert_u64_equal(torture, far_rsp[0].len, 1024, "far len");
3061 } else {
3062 /* expect fully allocated */
3063 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0, "far offset");
3064 torture_assert_u64_equal(torture, far_rsp[0].len, 5120, "far len");
3067 smb2_util_close(tree, fh);
3068 talloc_free(tmp_ctx);
3069 return true;
3072 static bool test_ioctl_sparse_qar_malformed(struct torture_context *torture,
3073 struct smb2_tree *tree)
3075 struct smb2_handle fh;
3076 union smb_ioctl ioctl;
3077 struct file_alloced_range_buf far_buf;
3078 NTSTATUS status;
3079 enum ndr_err_code ndr_ret;
3080 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3081 bool ok;
3082 size_t old_len;
3084 /* zero length file, shouldn't have any ranges */
3085 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3086 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3087 FILE_ATTRIBUTE_NORMAL);
3088 torture_assert(torture, ok, "setup file");
3090 status = test_ioctl_sparse_fs_supported(torture, tree, tmp_ctx, &fh,
3091 &ok);
3092 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3093 if (!ok) {
3094 smb2_util_close(tree, fh);
3095 torture_skip(torture, "Sparse files not supported\n");
3098 /* no allocated ranges, no space for range response, should pass */
3099 ZERO_STRUCT(ioctl);
3100 ioctl.smb2.level = RAW_IOCTL_SMB2;
3101 ioctl.smb2.in.file.handle = fh;
3102 ioctl.smb2.in.function = FSCTL_QUERY_ALLOCATED_RANGES;
3103 ioctl.smb2.in.max_response_size = 0;
3104 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3106 far_buf.file_off = 0;
3107 far_buf.len = 1024;
3108 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
3109 &far_buf,
3110 (ndr_push_flags_fn_t)ndr_push_file_alloced_range_buf);
3111 torture_assert_ndr_success(torture, ndr_ret, "push far ndr buf");
3113 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3114 torture_assert_ntstatus_ok(torture, status, "FSCTL_QUERY_ALLOCATED_RANGES");
3116 /* write into the file at 4k offset */
3117 ok = write_pattern(torture, tree, tmp_ctx, fh,
3118 0, /* off */
3119 1024, /* len */
3120 0); /* pattern offset */
3121 torture_assert(torture, ok, "write pattern");
3123 /* allocated range, no space for range response, should fail */
3124 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3125 torture_assert_ntstatus_equal(torture, status,
3126 NT_STATUS_BUFFER_TOO_SMALL, "qar no space");
3128 /* oversize (2x) file_alloced_range_buf in request, should pass */
3129 ioctl.smb2.in.max_response_size = 1024;
3130 old_len = ioctl.smb2.in.out.length;
3131 ok = data_blob_realloc(tmp_ctx, &ioctl.smb2.in.out,
3132 (ioctl.smb2.in.out.length * 2));
3133 torture_assert(torture, ok, "2x data buffer");
3134 memcpy(ioctl.smb2.in.out.data + old_len, ioctl.smb2.in.out.data,
3135 old_len);
3136 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3137 torture_assert_ntstatus_ok(torture, status, "qar too big");
3139 /* no file_alloced_range_buf in request, should fail */
3140 data_blob_free(&ioctl.smb2.in.out);
3141 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3142 torture_assert_ntstatus_equal(torture, status,
3143 NT_STATUS_INVALID_PARAMETER, "qar empty");
3145 return true;
3149 * 2.3.57 FSCTL_SET_ZERO_DATA Request
3151 * How an implementation zeros data within a file is implementation-dependent.
3152 * A file system MAY choose to deallocate regions of disk space that have been
3153 * zeroed.<50>
3154 * <50>
3155 * ... NTFS might deallocate disk space in the file if the file is stored on an
3156 * NTFS volume, and the file is sparse or compressed. It will free any allocated
3157 * space in chunks of 64 kilobytes that begin at an offset that is a multiple of
3158 * 64 kilobytes. Other bytes in the file (prior to the first freed 64-kilobyte
3159 * chunk and after the last freed 64-kilobyte chunk) will be zeroed but not
3160 * deallocated.
3162 static NTSTATUS test_ioctl_zdata_req(struct torture_context *torture,
3163 TALLOC_CTX *mem_ctx,
3164 struct smb2_tree *tree,
3165 struct smb2_handle fh,
3166 int64_t off,
3167 int64_t beyond_final_zero)
3169 union smb_ioctl ioctl;
3170 NTSTATUS status;
3171 enum ndr_err_code ndr_ret;
3172 struct file_zero_data_info zdata_info;
3173 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
3174 if (tmp_ctx == NULL) {
3175 return NT_STATUS_NO_MEMORY;
3178 ZERO_STRUCT(ioctl);
3179 ioctl.smb2.level = RAW_IOCTL_SMB2;
3180 ioctl.smb2.in.file.handle = fh;
3181 ioctl.smb2.in.function = FSCTL_SET_ZERO_DATA;
3182 ioctl.smb2.in.max_response_size = 0;
3183 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3185 zdata_info.file_off = off;
3186 zdata_info.beyond_final_zero = beyond_final_zero;
3188 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
3189 &zdata_info,
3190 (ndr_push_flags_fn_t)ndr_push_file_zero_data_info);
3191 if (ndr_ret != NDR_ERR_SUCCESS) {
3192 status = NT_STATUS_UNSUCCESSFUL;
3193 goto err_out;
3196 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3197 if (!NT_STATUS_IS_OK(status)) {
3198 goto err_out;
3201 status = NT_STATUS_OK;
3202 err_out:
3203 talloc_free(tmp_ctx);
3204 return status;
3207 static bool test_ioctl_sparse_punch(struct torture_context *torture,
3208 struct smb2_tree *tree)
3210 struct smb2_handle fh;
3211 NTSTATUS status;
3212 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3213 bool ok;
3214 bool is_sparse;
3215 struct file_alloced_range_buf *far_rsp = NULL;
3216 uint64_t far_count = 0;
3218 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3219 FNAME, &fh, 4096, SEC_RIGHTS_FILE_ALL,
3220 FILE_ATTRIBUTE_NORMAL);
3221 torture_assert(torture, ok, "setup file");
3223 status = test_ioctl_sparse_fs_supported(torture, tree, tmp_ctx, &fh,
3224 &ok);
3225 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3226 if (!ok) {
3227 smb2_util_close(tree, fh);
3228 torture_skip(torture, "Sparse files not supported\n");
3231 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3232 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3233 torture_assert(torture, !is_sparse, "sparse attr before set");
3235 /* zero (hole-punch) the data, without sparse flag */
3236 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3237 0, /* off */
3238 4096); /* beyond_final_zero */
3239 torture_assert_ntstatus_ok(torture, status, "zero_data");
3241 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3242 0, /* off */
3243 4096, /* len */
3244 &far_rsp,
3245 &far_count);
3246 torture_assert_ntstatus_ok(torture, status,
3247 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3248 torture_assert_u64_equal(torture, far_count, 1,
3249 "unexpected response len");
3251 /* expect fully allocated */
3252 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3253 "unexpected far off");
3254 torture_assert_u64_equal(torture, far_rsp[0].len, 4096,
3255 "unexpected far len");
3256 /* check that the data is now zeroed */
3257 ok = check_zero(torture, tree, tmp_ctx, fh, 0, 4096);
3258 torture_assert(torture, ok, "non-sparse zeroed range");
3260 /* set sparse */
3261 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
3262 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3264 /* still fully allocated on NTFS, see note below for Samba */
3265 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3266 0, /* off */
3267 4096, /* len */
3268 &far_rsp,
3269 &far_count);
3270 torture_assert_ntstatus_ok(torture, status,
3271 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3273 * FS specific: Samba uses PUNCH_HOLE to zero the range, and
3274 * subsequently uses fallocate() to allocate the punched range if the
3275 * file is marked non-sparse and "strict allocate" is enabled. In both
3276 * cases, the zeroed range will not be detected by SEEK_DATA, so the
3277 * range won't be present in QAR responses until the file is marked
3278 * non-sparse again.
3280 if (far_count == 0) {
3281 torture_comment(torture, "non-sparse zeroed range disappeared "
3282 "after marking sparse\n");
3283 } else {
3284 /* NTFS: range remains fully allocated */
3285 torture_assert_u64_equal(torture, far_count, 1,
3286 "unexpected response len");
3287 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3288 "unexpected far off");
3289 torture_assert_u64_equal(torture, far_rsp[0].len, 4096,
3290 "unexpected far len");
3293 /* zero (hole-punch) the data, _with_ sparse flag */
3294 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3295 0, /* off */
3296 4096); /* beyond_final_zero */
3297 torture_assert_ntstatus_ok(torture, status, "zero_data");
3299 /* the range should no longer be alloced */
3300 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3301 0, /* off */
3302 4096, /* len */
3303 &far_rsp,
3304 &far_count);
3305 torture_assert_ntstatus_ok(torture, status,
3306 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3307 torture_assert_u64_equal(torture, far_count, 0,
3308 "unexpected response len");
3310 ok = check_zero(torture, tree, tmp_ctx, fh, 0, 4096);
3311 torture_assert(torture, ok, "sparse zeroed range");
3313 /* remove sparse flag, this should "unsparse" the zeroed range */
3314 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false);
3315 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3317 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3318 0, /* off */
3319 4096, /* len */
3320 &far_rsp,
3321 &far_count);
3322 torture_assert_ntstatus_ok(torture, status,
3323 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3324 torture_assert_u64_equal(torture, far_count, 1,
3325 "unexpected response len");
3326 /* expect fully allocated */
3327 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3328 "unexpected far off");
3329 torture_assert_u64_equal(torture, far_rsp[0].len, 4096,
3330 "unexpected far len");
3332 ok = check_zero(torture, tree, tmp_ctx, fh, 0, 4096);
3333 torture_assert(torture, ok, "sparse zeroed range");
3335 smb2_util_close(tree, fh);
3336 talloc_free(tmp_ctx);
3337 return true;
3341 * Find the point at which a zeroed range in a sparse file is deallocated by the
3342 * underlying filesystem. NTFS on Windows Server 2012 deallocates chunks in 64k
3343 * increments. Also check whether zeroed neighbours are merged for deallocation.
3345 static bool test_ioctl_sparse_hole_dealloc(struct torture_context *torture,
3346 struct smb2_tree *tree)
3348 struct smb2_handle fh;
3349 NTSTATUS status;
3350 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3351 bool ok;
3352 uint64_t file_size;
3353 uint64_t hlen;
3354 uint64_t dealloc_chunk_len = 0;
3355 struct file_alloced_range_buf *far_rsp = NULL;
3356 uint64_t far_count = 0;
3358 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3359 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3360 FILE_ATTRIBUTE_NORMAL);
3361 torture_assert(torture, ok, "setup file 1");
3363 /* check for FS sparse file */
3364 status = test_ioctl_sparse_fs_supported(torture, tree, tmp_ctx, &fh,
3365 &ok);
3366 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3367 if (!ok) {
3368 smb2_util_close(tree, fh);
3369 torture_skip(torture, "Sparse files not supported\n");
3372 /* set sparse */
3373 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
3374 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3376 file_size = 1024 * 1024;
3378 ok = write_pattern(torture, tree, tmp_ctx, fh,
3379 0, /* off */
3380 file_size, /* len */
3381 0); /* pattern offset */
3382 torture_assert(torture, ok, "write pattern");
3384 /* check allocated ranges, should be fully allocated */
3385 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3386 0, /* off */
3387 file_size, /* len */
3388 &far_rsp,
3389 &far_count);
3390 torture_assert_ntstatus_ok(torture, status,
3391 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3392 torture_assert_u64_equal(torture, far_count, 1,
3393 "unexpected response len");
3394 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3395 "unexpected far off");
3396 torture_assert_u64_equal(torture, far_rsp[0].len, file_size,
3397 "unexpected far len");
3399 /* punch holes in sizes of 1k increments */
3400 for (hlen = 0; hlen <= file_size; hlen += 4096) {
3402 /* punch a hole from zero to the current increment */
3403 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3404 0, /* off */
3405 hlen); /* beyond_final_zero */
3406 torture_assert_ntstatus_ok(torture, status, "zero_data");
3408 /* ensure hole is zeroed, and pattern is consistent */
3409 ok = check_zero(torture, tree, tmp_ctx, fh, 0, hlen);
3410 torture_assert(torture, ok, "sparse zeroed range");
3412 ok = check_pattern(torture, tree, tmp_ctx, fh, hlen,
3413 file_size - hlen, hlen);
3414 torture_assert(torture, ok, "allocated pattern range");
3416 /* Check allocated ranges, hole might have been deallocated */
3417 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3418 0, /* off */
3419 file_size, /* len */
3420 &far_rsp,
3421 &far_count);
3422 torture_assert_ntstatus_ok(torture, status,
3423 "FSCTL_QUERY_ALLOCATED_RANGES");
3424 if ((hlen == file_size) && (far_count == 0)) {
3425 /* hole covered entire file, deallocation occurred */
3426 dealloc_chunk_len = file_size;
3427 break;
3430 torture_assert_u64_equal(torture, far_count, 1,
3431 "unexpected response len");
3432 if (far_rsp[0].file_off != 0) {
3434 * We now know the hole punch length needed to trigger a
3435 * deallocation on this FS...
3437 dealloc_chunk_len = hlen;
3438 torture_comment(torture, "hole punch %lu@0 resulted in "
3439 "deallocation of %lu@0\n", hlen,
3440 far_rsp[0].file_off);
3441 torture_assert_u64_equal(torture,
3442 file_size - far_rsp[0].len,
3443 far_rsp[0].file_off,
3444 "invalid alloced range");
3445 break;
3449 if (dealloc_chunk_len == 0) {
3450 torture_comment(torture, "strange, this FS never deallocates"
3451 "zeroed ranges in sparse files\n");
3452 return true; /* FS specific, not a failure */
3456 * Check whether deallocation occurs when the (now known)
3457 * deallocation chunk size is punched via two ZERO_DATA requests.
3458 * I.e. Does the FS merge the two ranges and deallocate the chunk?
3459 * NTFS on Windows Server 2012 does not.
3461 ok = write_pattern(torture, tree, tmp_ctx, fh,
3462 0, /* off */
3463 file_size, /* len */
3464 0); /* pattern offset */
3465 torture_assert(torture, ok, "write pattern");
3467 /* divide dealloc chunk size by two, to use as punch length */
3468 hlen = dealloc_chunk_len >> 1;
3471 * /half of dealloc chunk size 1M\
3472 * | |
3473 * /offset 0 | /dealloc chunk size |
3474 * |------------------ |-------------------|-------------------|
3475 * | zeroed, 1st punch | zeroed, 2nd punch | existing pattern |
3477 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3478 0, /* off */
3479 hlen); /* beyond final zero */
3480 torture_assert_ntstatus_ok(torture, status, "zero_data");
3482 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3483 hlen, /* off */
3484 dealloc_chunk_len); /* beyond final */
3485 torture_assert_ntstatus_ok(torture, status, "zero_data");
3487 /* ensure holes are zeroed, and pattern is consistent */
3488 ok = check_zero(torture, tree, tmp_ctx, fh, 0, dealloc_chunk_len);
3489 torture_assert(torture, ok, "sparse zeroed range");
3491 ok = check_pattern(torture, tree, tmp_ctx, fh, dealloc_chunk_len,
3492 file_size - dealloc_chunk_len, dealloc_chunk_len);
3493 torture_assert(torture, ok, "allocated pattern range");
3495 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3496 0, /* off */
3497 file_size, /* len */
3498 &far_rsp,
3499 &far_count);
3500 torture_assert_ntstatus_ok(torture, status,
3501 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3503 if ((far_count == 0) && (dealloc_chunk_len == file_size)) {
3504 torture_comment(torture, "holes merged for deallocation of "
3505 "full file\n");
3506 return true;
3508 torture_assert_u64_equal(torture, far_count, 1,
3509 "unexpected response len");
3510 if (far_rsp[0].file_off == dealloc_chunk_len) {
3511 torture_comment(torture, "holes merged for deallocation of "
3512 "%lu chunk\n", dealloc_chunk_len);
3513 torture_assert_u64_equal(torture,
3514 file_size - far_rsp[0].len,
3515 far_rsp[0].file_off,
3516 "invalid alloced range");
3517 } else {
3518 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3519 "unexpected deallocation");
3520 torture_comment(torture, "holes not merged for deallocation\n");
3523 smb2_util_close(tree, fh);
3526 * Check whether an unwritten range is allocated when a sparse file is
3527 * written to at an offset past the dealloc chunk size:
3529 * /dealloc chunk size
3530 * /offset 0 |
3531 * |------------------ |-------------------|
3532 * | unwritten | pattern |
3534 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3535 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3536 FILE_ATTRIBUTE_NORMAL);
3537 torture_assert(torture, ok, "setup file 1");
3539 /* set sparse */
3540 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
3541 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3543 ok = write_pattern(torture, tree, tmp_ctx, fh,
3544 dealloc_chunk_len, /* off */
3545 1024, /* len */
3546 dealloc_chunk_len); /* pattern offset */
3547 torture_assert(torture, ok, "write pattern");
3549 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3550 0, /* off */
3551 dealloc_chunk_len + 1024, /* len */
3552 &far_rsp,
3553 &far_count);
3554 torture_assert_ntstatus_ok(torture, status,
3555 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3556 torture_assert_u64_equal(torture, far_count, 1,
3557 "unexpected response len");
3558 if (far_rsp[0].file_off == 0) {
3559 torture_assert_u64_equal(torture, far_rsp[0].len,
3560 dealloc_chunk_len + 1024,
3561 "unexpected far len");
3562 torture_comment(torture, "unwritten range fully allocated\n");
3563 } else {
3564 torture_assert_u64_equal(torture, far_rsp[0].file_off, dealloc_chunk_len,
3565 "unexpected deallocation");
3566 torture_assert_u64_equal(torture, far_rsp[0].len, 1024,
3567 "unexpected far len");
3568 torture_comment(torture, "unwritten range not allocated\n");
3571 ok = check_zero(torture, tree, tmp_ctx, fh, 0, dealloc_chunk_len);
3572 torture_assert(torture, ok, "sparse zeroed range");
3574 ok = check_pattern(torture, tree, tmp_ctx, fh, dealloc_chunk_len,
3575 1024, dealloc_chunk_len);
3576 torture_assert(torture, ok, "allocated pattern range");
3578 /* unsparse, should now be fully allocated */
3579 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false);
3580 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3582 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3583 0, /* off */
3584 dealloc_chunk_len + 1024, /* len */
3585 &far_rsp,
3586 &far_count);
3587 torture_assert_ntstatus_ok(torture, status,
3588 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3589 torture_assert_u64_equal(torture, far_count, 1,
3590 "unexpected response len");
3591 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3592 "unexpected deallocation");
3593 torture_assert_u64_equal(torture, far_rsp[0].len,
3594 dealloc_chunk_len + 1024,
3595 "unexpected far len");
3597 smb2_util_close(tree, fh);
3598 talloc_free(tmp_ctx);
3599 return true;
3602 /* check whether a file with compression and sparse attrs can be deallocated */
3603 static bool test_ioctl_sparse_compressed(struct torture_context *torture,
3604 struct smb2_tree *tree)
3606 struct smb2_handle fh;
3607 NTSTATUS status;
3608 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3609 bool ok;
3610 uint64_t file_size = 1024 * 1024;
3611 struct file_alloced_range_buf *far_rsp = NULL;
3612 uint64_t far_count = 0;
3614 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3615 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3616 FILE_ATTRIBUTE_NORMAL);
3617 torture_assert(torture, ok, "setup file 1");
3619 /* check for FS sparse file and compression support */
3620 status = test_ioctl_sparse_fs_supported(torture, tree, tmp_ctx, &fh,
3621 &ok);
3622 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3623 if (!ok) {
3624 smb2_util_close(tree, fh);
3625 torture_skip(torture, "Sparse files not supported\n");
3628 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
3629 &ok);
3630 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3631 if (!ok) {
3632 smb2_util_close(tree, fh);
3633 torture_skip(torture, "FS compression not supported\n");
3636 /* set compression and write some data */
3637 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
3638 COMPRESSION_FORMAT_DEFAULT);
3639 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
3641 ok = write_pattern(torture, tree, tmp_ctx, fh,
3642 0, /* off */
3643 file_size, /* len */
3644 0); /* pattern offset */
3645 torture_assert(torture, ok, "write pattern");
3647 /* set sparse - now sparse and compressed */
3648 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
3649 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3651 /* check allocated ranges, should be fully alloced */
3652 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3653 0, /* off */
3654 file_size, /* len */
3655 &far_rsp,
3656 &far_count);
3657 torture_assert_ntstatus_ok(torture, status,
3658 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3659 torture_assert_u64_equal(torture, far_count, 1,
3660 "unexpected response len");
3661 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3662 "unexpected far off");
3663 torture_assert_u64_equal(torture, far_rsp[0].len, file_size,
3664 "unexpected far len");
3666 /* zero (hole-punch) all data, with sparse and compressed attrs */
3667 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3668 0, /* off */
3669 file_size); /* beyond_final_zero */
3670 torture_assert_ntstatus_ok(torture, status, "zero_data");
3673 * Windows Server 2012 still deallocates a zeroed range when a sparse
3674 * file carries the compression attribute.
3676 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3677 0, /* off */
3678 file_size, /* len */
3679 &far_rsp,
3680 &far_count);
3681 torture_assert_ntstatus_ok(torture, status,
3682 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3683 if (far_count == 0) {
3684 torture_comment(torture, "sparse & compressed file "
3685 "deallocated after hole-punch\n");
3686 } else {
3687 torture_assert_u64_equal(torture, far_count, 1,
3688 "unexpected response len");
3689 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3690 "unexpected far off");
3691 torture_assert_u64_equal(torture, far_rsp[0].len, file_size,
3692 "unexpected far len");
3693 torture_comment(torture, "sparse & compressed file fully "
3694 "allocated after hole-punch\n");
3697 smb2_util_close(tree, fh);
3698 talloc_free(tmp_ctx);
3699 return true;
3703 * Create a sparse file, then attempt to copy unallocated and allocated ranges
3704 * into a target file using FSCTL_SRV_COPYCHUNK.
3706 static bool test_ioctl_sparse_copy_chunk(struct torture_context *torture,
3707 struct smb2_tree *tree)
3709 struct smb2_handle src_h;
3710 struct smb2_handle dest_h;
3711 NTSTATUS status;
3712 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3713 bool ok;
3714 uint64_t dealloc_chunk_len = 64 * 1024; /* Windows 2012 */
3715 struct file_alloced_range_buf *far_rsp = NULL;
3716 uint64_t far_count = 0;
3717 union smb_ioctl ioctl;
3718 struct srv_copychunk_copy cc_copy;
3719 struct srv_copychunk_rsp cc_rsp;
3720 enum ndr_err_code ndr_ret;
3722 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3723 FNAME, &src_h, 0, SEC_RIGHTS_FILE_ALL,
3724 FILE_ATTRIBUTE_NORMAL);
3725 torture_assert(torture, ok, "setup file");
3727 /* check for FS sparse file support */
3728 status = test_ioctl_sparse_fs_supported(torture, tree, tmp_ctx, &src_h,
3729 &ok);
3730 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3731 smb2_util_close(tree, src_h);
3732 if (!ok) {
3733 torture_skip(torture, "Sparse files not supported\n");
3736 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
3737 1, /* chunks */
3738 &src_h, 0, /* src file */
3739 SEC_RIGHTS_FILE_ALL,
3740 &dest_h, 0, /* dest file */
3741 SEC_RIGHTS_FILE_ALL,
3742 &cc_copy,
3743 &ioctl);
3744 torture_assert(torture, ok, "setup copy chunk error");
3746 /* set sparse */
3747 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, src_h, true);
3748 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3750 /* start after dealloc_chunk_len, to create an unwritten sparse range */
3751 ok = write_pattern(torture, tree, tmp_ctx, src_h,
3752 dealloc_chunk_len, /* off */
3753 1024, /* len */
3754 dealloc_chunk_len); /* pattern offset */
3755 torture_assert(torture, ok, "write pattern");
3757 /* Skip test if 64k chunk is allocated - FS specific */
3758 status = test_ioctl_qar_req(torture, tmp_ctx, tree, src_h,
3759 0, /* off */
3760 dealloc_chunk_len + 1024, /* len */
3761 &far_rsp,
3762 &far_count);
3763 torture_assert_ntstatus_ok(torture, status,
3764 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3765 torture_assert_u64_equal(torture, far_count, 1,
3766 "unexpected response len");
3767 if (far_rsp[0].file_off == 0) {
3768 torture_skip(torture, "unwritten range fully allocated\n");
3771 torture_assert_u64_equal(torture, far_rsp[0].file_off, dealloc_chunk_len,
3772 "unexpected allocation");
3773 torture_assert_u64_equal(torture, far_rsp[0].len, 1024,
3774 "unexpected far len");
3776 /* copy-chunk unallocated + written ranges into non-sparse dest */
3778 cc_copy.chunks[0].source_off = 0;
3779 cc_copy.chunks[0].target_off = 0;
3780 cc_copy.chunks[0].length = dealloc_chunk_len + 1024;
3782 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
3783 &cc_copy,
3784 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
3785 torture_assert_ndr_success(torture, ndr_ret,
3786 "ndr_push_srv_copychunk_copy");
3788 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3789 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
3791 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
3792 &cc_rsp,
3793 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
3794 torture_assert_ndr_success(torture, ndr_ret,
3795 "ndr_pull_srv_copychunk_rsp");
3797 ok = check_copy_chunk_rsp(torture, &cc_rsp,
3798 1, /* chunks written */
3799 0, /* chunk bytes unsuccessfully written */
3800 dealloc_chunk_len + 1024); /* bytes written */
3801 torture_assert(torture, ok, "bad copy chunk response data");
3803 ok = check_zero(torture, tree, tmp_ctx, dest_h, 0, dealloc_chunk_len);
3804 torture_assert(torture, ok, "sparse zeroed range");
3806 ok = check_pattern(torture, tree, tmp_ctx, dest_h, dealloc_chunk_len,
3807 1024, dealloc_chunk_len);
3808 torture_assert(torture, ok, "copychunked range");
3810 /* copied range should be allocated in non-sparse dest */
3811 status = test_ioctl_qar_req(torture, tmp_ctx, tree, dest_h,
3812 0, /* off */
3813 dealloc_chunk_len + 1024, /* len */
3814 &far_rsp,
3815 &far_count);
3816 torture_assert_ntstatus_ok(torture, status,
3817 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3818 torture_assert_u64_equal(torture, far_count, 1,
3819 "unexpected response len");
3820 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3821 "unexpected allocation");
3822 torture_assert_u64_equal(torture, far_rsp[0].len,
3823 dealloc_chunk_len + 1024,
3824 "unexpected far len");
3826 /* set dest as sparse */
3827 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, dest_h, true);
3828 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3830 /* zero (hole-punch) all data */
3831 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, dest_h,
3832 0, /* off */
3833 dealloc_chunk_len + 1024);
3834 torture_assert_ntstatus_ok(torture, status, "zero_data");
3836 /* zeroed range might be deallocated */
3837 status = test_ioctl_qar_req(torture, tmp_ctx, tree, dest_h,
3838 0, /* off */
3839 dealloc_chunk_len + 1024, /* len */
3840 &far_rsp,
3841 &far_count);
3842 torture_assert_ntstatus_ok(torture, status,
3843 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3844 if (far_count == 0) {
3845 /* FS specific (e.g. NTFS) */
3846 torture_comment(torture, "FS deallocates file on full-range "
3847 "punch\n");
3848 } else {
3849 /* FS specific (e.g. EXT4) */
3850 torture_comment(torture, "FS doesn't deallocate file on "
3851 "full-range punch\n");
3853 ok = check_zero(torture, tree, tmp_ctx, dest_h, 0,
3854 dealloc_chunk_len + 1024);
3855 torture_assert(torture, ok, "punched zeroed range");
3857 /* copy-chunk again, this time with sparse dest */
3858 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3859 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
3861 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
3862 &cc_rsp,
3863 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
3864 torture_assert_ndr_success(torture, ndr_ret,
3865 "ndr_pull_srv_copychunk_rsp");
3867 ok = check_copy_chunk_rsp(torture, &cc_rsp,
3868 1, /* chunks written */
3869 0, /* chunk bytes unsuccessfully written */
3870 dealloc_chunk_len + 1024); /* bytes written */
3871 torture_assert(torture, ok, "bad copy chunk response data");
3873 ok = check_zero(torture, tree, tmp_ctx, dest_h, 0, dealloc_chunk_len);
3874 torture_assert(torture, ok, "sparse zeroed range");
3876 ok = check_pattern(torture, tree, tmp_ctx, dest_h, dealloc_chunk_len,
3877 1024, dealloc_chunk_len);
3878 torture_assert(torture, ok, "copychunked range");
3880 /* copied range may be allocated in sparse dest */
3881 status = test_ioctl_qar_req(torture, tmp_ctx, tree, dest_h,
3882 0, /* off */
3883 dealloc_chunk_len + 1024, /* len */
3884 &far_rsp,
3885 &far_count);
3886 torture_assert_ntstatus_ok(torture, status,
3887 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3888 torture_assert_u64_equal(torture, far_count, 1,
3889 "unexpected response len");
3891 * FS specific: sparse region may be unallocated in dest if copy-chunk
3892 * is handled in a sparse preserving way - E.g. vfs_btrfs
3893 * with BTRFS_IOC_CLONE_RANGE.
3895 if (far_rsp[0].file_off == dealloc_chunk_len) {
3896 torture_comment(torture, "copy-chunk sparse range preserved\n");
3897 torture_assert_u64_equal(torture, far_rsp[0].len, 1024,
3898 "unexpected far len");
3899 } else {
3900 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3901 "unexpected allocation");
3902 torture_assert_u64_equal(torture, far_rsp[0].len,
3903 dealloc_chunk_len + 1024,
3904 "unexpected far len");
3907 smb2_util_close(tree, src_h);
3908 smb2_util_close(tree, dest_h);
3909 talloc_free(tmp_ctx);
3910 return true;
3913 static bool test_ioctl_sparse_punch_invalid(struct torture_context *torture,
3914 struct smb2_tree *tree)
3916 struct smb2_handle fh;
3917 NTSTATUS status;
3918 TALLOC_CTX *tmp_ctx = talloc_new(tree);
3919 bool ok;
3920 bool is_sparse;
3921 int i;
3923 ok = test_setup_create_fill(torture, tree, tmp_ctx,
3924 FNAME, &fh, 4096, SEC_RIGHTS_FILE_ALL,
3925 FILE_ATTRIBUTE_NORMAL);
3926 torture_assert(torture, ok, "setup file");
3928 status = test_ioctl_sparse_fs_supported(torture, tree, tmp_ctx, &fh,
3929 &ok);
3930 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3931 if (!ok) {
3932 smb2_util_close(tree, fh);
3933 torture_skip(torture, "Sparse files not supported\n");
3936 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3937 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3938 torture_assert(torture, !is_sparse, "sparse attr before set");
3940 /* loop twice, without and with sparse attrib */
3941 for (i = 0; i <= 1; i++) {
3942 union smb_fileinfo io;
3943 struct file_alloced_range_buf *far_rsp = NULL;
3944 uint64_t far_count = 0;
3946 /* get size before & after. zero data should never change it */
3947 ZERO_STRUCT(io);
3948 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
3949 io.generic.in.file.handle = fh;
3950 status = smb2_getinfo_file(tree, tmp_ctx, &io);
3951 torture_assert_ntstatus_ok(torture, status, "getinfo");
3952 torture_assert_int_equal(torture, (int)io.all_info2.out.size,
3953 4096, "size after IO");
3955 /* valid 8 byte zero data, but after EOF */
3956 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3957 4096, /* off */
3958 4104); /* beyond_final_zero */
3959 torture_assert_ntstatus_ok(torture, status, "zero_data");
3961 /* valid 8 byte zero data, but after EOF */
3962 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3963 8192, /* off */
3964 8200); /* beyond_final_zero */
3965 torture_assert_ntstatus_ok(torture, status, "zero_data");
3967 ZERO_STRUCT(io);
3968 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
3969 io.generic.in.file.handle = fh;
3970 status = smb2_getinfo_file(tree, tmp_ctx, &io);
3971 torture_assert_ntstatus_ok(torture, status, "getinfo");
3972 torture_assert_int_equal(torture, (int)io.all_info2.out.size,
3973 4096, "size after IO");
3975 /* valid 0 byte zero data, without sparse flag */
3976 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3977 4095, /* off */
3978 4095); /* beyond_final_zero */
3979 torture_assert_ntstatus_ok(torture, status, "zero_data");
3981 /* INVALID off is past beyond_final_zero */
3982 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3983 4096, /* off */
3984 4095); /* beyond_final_zero */
3985 torture_assert_ntstatus_equal(torture, status,
3986 NT_STATUS_INVALID_PARAMETER,
3987 "invalid zero_data");
3989 /* zero length QAR - valid */
3990 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3991 0, /* off */
3992 0, /* len */
3993 &far_rsp, &far_count);
3994 torture_assert_ntstatus_ok(torture, status,
3995 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3996 torture_assert_u64_equal(torture, far_count, 0,
3997 "unexpected response len");
3999 /* QAR after EOF - valid */
4000 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4001 4096, /* off */
4002 1024, /* len */
4003 &far_rsp, &far_count);
4004 torture_assert_ntstatus_ok(torture, status,
4005 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4006 torture_assert_u64_equal(torture, far_count, 0,
4007 "unexpected response len");
4009 /* set sparse */
4010 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh,
4011 true);
4012 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4015 smb2_util_close(tree, fh);
4016 talloc_free(tmp_ctx);
4017 return true;
4020 static bool test_ioctl_sparse_perms(struct torture_context *torture,
4021 struct smb2_tree *tree)
4023 struct smb2_handle fh;
4024 NTSTATUS status;
4025 TALLOC_CTX *tmp_ctx = talloc_new(tree);
4026 bool ok;
4027 bool is_sparse;
4028 struct file_alloced_range_buf *far_rsp = NULL;
4029 uint64_t far_count = 0;
4031 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4032 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
4033 FILE_ATTRIBUTE_NORMAL);
4034 torture_assert(torture, ok, "setup file");
4036 status = test_ioctl_sparse_fs_supported(torture, tree, tmp_ctx, &fh,
4037 &ok);
4038 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4039 smb2_util_close(tree, fh);
4040 if (!ok) {
4041 torture_skip(torture, "Sparse files not supported\n");
4044 /* set sparse without WRITE_ATTR permission should succeed */
4045 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4046 FNAME, &fh, 0,
4047 (SEC_RIGHTS_FILE_WRITE & ~(SEC_FILE_WRITE_ATTRIBUTE
4048 | SEC_STD_WRITE_DAC
4049 | SEC_FILE_WRITE_EA)),
4050 FILE_ATTRIBUTE_NORMAL);
4051 torture_assert(torture, ok, "setup file");
4053 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4054 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4055 smb2_util_close(tree, fh);
4057 ok = test_setup_open(torture, tree, tmp_ctx,
4058 FNAME, &fh, SEC_RIGHTS_FILE_ALL,
4059 FILE_ATTRIBUTE_NORMAL);
4060 torture_assert(torture, ok, "setup file");
4061 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4062 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4063 torture_assert(torture, is_sparse, "sparse after set");
4064 smb2_util_close(tree, fh);
4066 /* attempt get sparse without READ_DATA permission */
4067 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4068 FNAME, &fh, 0,
4069 (SEC_RIGHTS_FILE_READ & ~SEC_FILE_READ_DATA),
4070 FILE_ATTRIBUTE_NORMAL);
4071 torture_assert(torture, ok, "setup file");
4073 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4074 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4075 torture_assert(torture, !is_sparse, "sparse set");
4076 smb2_util_close(tree, fh);
4078 /* attempt to set sparse with only WRITE_ATTR permission */
4079 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4080 FNAME, &fh, 0,
4081 SEC_FILE_WRITE_ATTRIBUTE,
4082 FILE_ATTRIBUTE_NORMAL);
4083 torture_assert(torture, ok, "setup file");
4085 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4086 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4087 smb2_util_close(tree, fh);
4089 /* attempt to set sparse with only WRITE_DATA permission */
4090 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4091 FNAME, &fh, 0,
4092 SEC_FILE_WRITE_DATA,
4093 FILE_ATTRIBUTE_NORMAL);
4094 torture_assert(torture, ok, "setup file");
4096 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4097 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4098 smb2_util_close(tree, fh);
4100 ok = test_setup_open(torture, tree, tmp_ctx,
4101 FNAME, &fh, SEC_RIGHTS_FILE_ALL,
4102 FILE_ATTRIBUTE_NORMAL);
4103 torture_assert(torture, ok, "setup file");
4104 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4105 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4106 torture_assert(torture, is_sparse, "sparse after set");
4107 smb2_util_close(tree, fh);
4109 /* attempt to set sparse with only APPEND_DATA permission */
4110 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4111 FNAME, &fh, 0,
4112 SEC_FILE_APPEND_DATA,
4113 FILE_ATTRIBUTE_NORMAL);
4114 torture_assert(torture, ok, "setup file");
4116 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4117 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4118 smb2_util_close(tree, fh);
4120 ok = test_setup_open(torture, tree, tmp_ctx,
4121 FNAME, &fh, SEC_RIGHTS_FILE_ALL,
4122 FILE_ATTRIBUTE_NORMAL);
4123 torture_assert(torture, ok, "setup file");
4124 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4125 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4126 torture_assert(torture, is_sparse, "sparse after set");
4127 smb2_util_close(tree, fh);
4129 /* attempt to set sparse with only WRITE_EA permission - should fail */
4130 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4131 FNAME, &fh, 0,
4132 SEC_FILE_WRITE_EA,
4133 FILE_ATTRIBUTE_NORMAL);
4134 torture_assert(torture, ok, "setup file");
4136 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4137 torture_assert_ntstatus_equal(torture, status,
4138 NT_STATUS_ACCESS_DENIED,
4139 "FSCTL_SET_SPARSE permission");
4140 smb2_util_close(tree, fh);
4142 ok = test_setup_open(torture, tree, tmp_ctx,
4143 FNAME, &fh, SEC_RIGHTS_FILE_ALL,
4144 FILE_ATTRIBUTE_NORMAL);
4145 torture_assert(torture, ok, "setup file");
4146 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4147 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4148 torture_assert(torture, !is_sparse, "sparse after set");
4149 smb2_util_close(tree, fh);
4151 /* attempt QAR with only READ_ATTR permission - should fail */
4152 ok = test_setup_open(torture, tree, tmp_ctx,
4153 FNAME, &fh, SEC_FILE_READ_ATTRIBUTE,
4154 FILE_ATTRIBUTE_NORMAL);
4155 torture_assert(torture, ok, "setup file");
4156 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4157 4096, /* off */
4158 1024, /* len */
4159 &far_rsp, &far_count);
4160 torture_assert_ntstatus_equal(torture, status,
4161 NT_STATUS_ACCESS_DENIED,
4162 "FSCTL_QUERY_ALLOCATED_RANGES req passed");
4163 smb2_util_close(tree, fh);
4165 /* attempt QAR with only READ_DATA permission */
4166 ok = test_setup_open(torture, tree, tmp_ctx,
4167 FNAME, &fh, SEC_FILE_READ_DATA,
4168 FILE_ATTRIBUTE_NORMAL);
4169 torture_assert(torture, ok, "setup file");
4170 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4171 0, /* off */
4172 1024, /* len */
4173 &far_rsp, &far_count);
4174 torture_assert_ntstatus_ok(torture, status,
4175 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4176 torture_assert_u64_equal(torture, far_count, 0,
4177 "unexpected response len");
4178 smb2_util_close(tree, fh);
4180 /* attempt QAR with only READ_EA permission - should fail */
4181 ok = test_setup_open(torture, tree, tmp_ctx,
4182 FNAME, &fh, SEC_FILE_READ_EA,
4183 FILE_ATTRIBUTE_NORMAL);
4184 torture_assert(torture, ok, "setup file");
4185 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4186 4096, /* off */
4187 1024, /* len */
4188 &far_rsp, &far_count);
4189 torture_assert_ntstatus_equal(torture, status,
4190 NT_STATUS_ACCESS_DENIED,
4191 "FSCTL_QUERY_ALLOCATED_RANGES req passed");
4192 smb2_util_close(tree, fh);
4194 /* setup file for ZERO_DATA permissions tests */
4195 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4196 FNAME, &fh, 8192,
4197 SEC_RIGHTS_FILE_ALL,
4198 FILE_ATTRIBUTE_NORMAL);
4199 torture_assert(torture, ok, "setup file");
4201 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4202 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4203 smb2_util_close(tree, fh);
4205 /* attempt ZERO_DATA with only WRITE_ATTR permission - should fail */
4206 ok = test_setup_open(torture, tree, tmp_ctx,
4207 FNAME, &fh, SEC_FILE_WRITE_ATTRIBUTE,
4208 FILE_ATTRIBUTE_NORMAL);
4209 torture_assert(torture, ok, "setup file");
4210 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4211 0, /* off */
4212 4096); /* beyond_final_zero */
4213 torture_assert_ntstatus_equal(torture, status,
4214 NT_STATUS_ACCESS_DENIED,
4215 "zero_data permission");
4216 smb2_util_close(tree, fh);
4218 /* attempt ZERO_DATA with only WRITE_DATA permission */
4219 ok = test_setup_open(torture, tree, tmp_ctx,
4220 FNAME, &fh, SEC_FILE_WRITE_DATA,
4221 FILE_ATTRIBUTE_NORMAL);
4222 torture_assert(torture, ok, "setup file");
4223 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4224 0, /* off */
4225 4096); /* beyond_final_zero */
4226 torture_assert_ntstatus_ok(torture, status, "zero_data");
4227 smb2_util_close(tree, fh);
4229 /* attempt ZERO_DATA with only APPEND_DATA permission - should fail */
4230 ok = test_setup_open(torture, tree, tmp_ctx,
4231 FNAME, &fh, SEC_FILE_APPEND_DATA,
4232 FILE_ATTRIBUTE_NORMAL);
4233 torture_assert(torture, ok, "setup file");
4234 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4235 0, /* off */
4236 4096); /* beyond_final_zero */
4237 torture_assert_ntstatus_equal(torture, status,
4238 NT_STATUS_ACCESS_DENIED,
4239 "zero_data permission");
4240 smb2_util_close(tree, fh);
4242 /* attempt ZERO_DATA with only WRITE_EA permission - should fail */
4243 ok = test_setup_open(torture, tree, tmp_ctx,
4244 FNAME, &fh, SEC_FILE_WRITE_EA,
4245 FILE_ATTRIBUTE_NORMAL);
4246 torture_assert(torture, ok, "setup file");
4247 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4248 0, /* off */
4249 4096); /* beyond_final_zero */
4250 torture_assert_ntstatus_equal(torture, status,
4251 NT_STATUS_ACCESS_DENIED,
4252 "zero_data permission");
4253 smb2_util_close(tree, fh);
4255 talloc_free(tmp_ctx);
4256 return true;
4259 static bool test_ioctl_sparse_lck(struct torture_context *torture,
4260 struct smb2_tree *tree)
4262 struct smb2_handle fh;
4263 struct smb2_handle fh2;
4264 NTSTATUS status;
4265 uint64_t dealloc_chunk_len = 64 * 1024; /* Windows 2012 */
4266 TALLOC_CTX *tmp_ctx = talloc_new(tree);
4267 bool ok;
4268 bool is_sparse;
4269 struct smb2_lock lck;
4270 struct smb2_lock_element el[1];
4271 struct file_alloced_range_buf *far_rsp = NULL;
4272 uint64_t far_count = 0;
4274 ok = test_setup_create_fill(torture, tree, tmp_ctx, FNAME, &fh,
4275 dealloc_chunk_len, SEC_RIGHTS_FILE_ALL,
4276 FILE_ATTRIBUTE_NORMAL);
4277 torture_assert(torture, ok, "setup file");
4279 status = test_ioctl_sparse_fs_supported(torture, tree, tmp_ctx, &fh,
4280 &ok);
4281 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4282 if (!ok) {
4283 torture_skip(torture, "Sparse files not supported\n");
4284 smb2_util_close(tree, fh);
4287 /* open and lock via separate fh2 */
4288 status = torture_smb2_testfile(tree, FNAME, &fh2);
4289 torture_assert_ntstatus_ok(torture, status, "2nd src open");
4291 lck.in.lock_count = 0x0001;
4292 lck.in.lock_sequence = 0x00000000;
4293 lck.in.file.handle = fh2;
4294 lck.in.locks = el;
4295 el[0].offset = 0;
4296 el[0].length = dealloc_chunk_len;
4297 el[0].reserved = 0;
4298 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
4300 status = smb2_lock(tree, &lck);
4301 torture_assert_ntstatus_ok(torture, status, "lock");
4303 /* set sparse while locked */
4304 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4305 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4307 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4308 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4309 torture_assert(torture, is_sparse, "sparse attr after set");
4311 /* zero data over locked range should fail */
4312 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4313 0, /* off */
4314 4096); /* beyond_final_zero */
4315 torture_assert_ntstatus_equal(torture, status,
4316 NT_STATUS_FILE_LOCK_CONFLICT,
4317 "zero_data locked");
4319 /* QAR over locked range should pass */
4320 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4321 0, /* off */
4322 4096, /* len */
4323 &far_rsp, &far_count);
4324 torture_assert_ntstatus_ok(torture, status,
4325 "FSCTL_QUERY_ALLOCATED_RANGES locked");
4326 torture_assert_u64_equal(torture, far_count, 1,
4327 "unexpected response len");
4328 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4329 "unexpected allocation");
4330 torture_assert_u64_equal(torture, far_rsp[0].len,
4331 4096,
4332 "unexpected far len");
4334 /* zero data over range past EOF should pass */
4335 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4336 dealloc_chunk_len, /* off */
4337 dealloc_chunk_len + 4096);
4338 torture_assert_ntstatus_ok(torture, status,
4339 "zero_data past EOF locked");
4341 /* QAR over range past EOF should pass */
4342 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4343 dealloc_chunk_len, /* off */
4344 4096, /* len */
4345 &far_rsp, &far_count);
4346 torture_assert_ntstatus_ok(torture, status,
4347 "FSCTL_QUERY_ALLOCATED_RANGES past EOF locked");
4348 torture_assert_u64_equal(torture, far_count, 0,
4349 "unexpected response len");
4351 lck.in.lock_count = 0x0001;
4352 lck.in.lock_sequence = 0x00000001;
4353 lck.in.file.handle = fh2;
4354 lck.in.locks = el;
4355 el[0].offset = 0;
4356 el[0].length = dealloc_chunk_len;
4357 el[0].reserved = 0;
4358 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
4359 status = smb2_lock(tree, &lck);
4360 torture_assert_ntstatus_ok(torture, status, "unlock");
4362 smb2_util_close(tree, fh2);
4363 smb2_util_close(tree, fh);
4364 talloc_free(tmp_ctx);
4365 return true;
4368 /* alleviate QAR off-by-one bug paranoia - help me ob1 */
4369 static bool test_ioctl_sparse_qar_ob1(struct torture_context *torture,
4370 struct smb2_tree *tree)
4372 struct smb2_handle fh;
4373 NTSTATUS status;
4374 TALLOC_CTX *tmp_ctx = talloc_new(tree);
4375 bool ok;
4376 uint64_t dealloc_chunk_len = 64 * 1024; /* Windows 2012 */
4377 struct file_alloced_range_buf *far_rsp = NULL;
4378 uint64_t far_count = 0;
4380 ok = test_setup_create_fill(torture, tree, tmp_ctx,
4381 FNAME, &fh, dealloc_chunk_len * 2,
4382 SEC_RIGHTS_FILE_ALL,
4383 FILE_ATTRIBUTE_NORMAL);
4384 torture_assert(torture, ok, "setup file");
4386 status = test_ioctl_sparse_fs_supported(torture, tree, tmp_ctx, &fh,
4387 &ok);
4388 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4389 if (!ok) {
4390 torture_skip(torture, "Sparse files not supported\n");
4391 smb2_util_close(tree, fh);
4394 /* non-sparse QAR with range one before EOF */
4395 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4396 0, /* off */
4397 dealloc_chunk_len * 2 - 1, /* len */
4398 &far_rsp, &far_count);
4399 torture_assert_ntstatus_ok(torture, status,
4400 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4401 torture_assert_u64_equal(torture, far_count, 1,
4402 "unexpected response len");
4403 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4404 "unexpected allocation");
4405 torture_assert_u64_equal(torture, far_rsp[0].len,
4406 dealloc_chunk_len * 2 - 1,
4407 "unexpected far len");
4409 /* non-sparse QAR with range one after EOF */
4410 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4411 0, /* off */
4412 dealloc_chunk_len * 2 + 1, /* len */
4413 &far_rsp, &far_count);
4414 torture_assert_ntstatus_ok(torture, status,
4415 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4416 torture_assert_u64_equal(torture, far_count, 1,
4417 "unexpected response len");
4418 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4419 "unexpected allocation");
4420 torture_assert_u64_equal(torture, far_rsp[0].len,
4421 dealloc_chunk_len * 2,
4422 "unexpected far len");
4424 /* non-sparse QAR with range one after EOF from off=1 */
4425 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4426 1, /* off */
4427 dealloc_chunk_len * 2, /* len */
4428 &far_rsp, &far_count);
4429 torture_assert_ntstatus_ok(torture, status,
4430 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4431 torture_assert_u64_equal(torture, far_count, 1,
4432 "unexpected response len");
4433 torture_assert_u64_equal(torture, far_rsp[0].file_off, 1,
4434 "unexpected allocation");
4435 torture_assert_u64_equal(torture, far_rsp[0].len,
4436 dealloc_chunk_len * 2 - 1,
4437 "unexpected far len");
4439 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4440 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4442 /* punch out second chunk */
4443 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4444 dealloc_chunk_len, /* off */
4445 dealloc_chunk_len * 2);
4446 torture_assert_ntstatus_ok(torture, status, "zero_data");
4448 /* sparse QAR with range one before hole */
4449 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4450 0, /* off */
4451 dealloc_chunk_len - 1, /* len */
4452 &far_rsp, &far_count);
4453 torture_assert_ntstatus_ok(torture, status,
4454 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4455 torture_assert_u64_equal(torture, far_count, 1,
4456 "unexpected response len");
4457 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4458 "unexpected allocation");
4459 torture_assert_u64_equal(torture, far_rsp[0].len,
4460 dealloc_chunk_len - 1,
4461 "unexpected far len");
4463 /* sparse QAR with range one after hole */
4464 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4465 0, /* off */
4466 dealloc_chunk_len + 1, /* len */
4467 &far_rsp, &far_count);
4468 torture_assert_ntstatus_ok(torture, status,
4469 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4470 torture_assert_u64_equal(torture, far_count, 1,
4471 "unexpected response len");
4472 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4473 "unexpected allocation");
4474 torture_assert_u64_equal(torture, far_rsp[0].len,
4475 dealloc_chunk_len,
4476 "unexpected far len");
4478 /* sparse QAR with range one after hole from off=1 */
4479 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4480 1, /* off */
4481 dealloc_chunk_len, /* len */
4482 &far_rsp, &far_count);
4483 torture_assert_ntstatus_ok(torture, status,
4484 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4485 torture_assert_u64_equal(torture, far_count, 1,
4486 "unexpected response len");
4487 torture_assert_u64_equal(torture, far_rsp[0].file_off, 1,
4488 "unexpected allocation");
4489 torture_assert_u64_equal(torture, far_rsp[0].len,
4490 dealloc_chunk_len - 1,
4491 "unexpected far len");
4493 /* sparse QAR with range one before EOF from off=chunk_len-1 */
4494 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4495 dealloc_chunk_len - 1, /* off */
4496 dealloc_chunk_len, /* len */
4497 &far_rsp, &far_count);
4498 torture_assert_ntstatus_ok(torture, status,
4499 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4500 torture_assert_u64_equal(torture, far_count, 1,
4501 "unexpected response len");
4502 torture_assert_u64_equal(torture, far_rsp[0].file_off,
4503 dealloc_chunk_len - 1,
4504 "unexpected allocation");
4505 torture_assert_u64_equal(torture, far_rsp[0].len,
4506 1, "unexpected far len");
4508 /* sparse QAR with range one after EOF from off=chunk_len+1 */
4509 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4510 dealloc_chunk_len + 1, /* off */
4511 dealloc_chunk_len, /* len */
4512 &far_rsp, &far_count);
4513 torture_assert_ntstatus_ok(torture, status,
4514 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4515 torture_assert_u64_equal(torture, far_count, 0,
4516 "unexpected response len");
4517 smb2_util_close(tree, fh);
4518 talloc_free(tmp_ctx);
4519 return true;
4523 * basic testing of SMB2 ioctls
4525 struct torture_suite *torture_smb2_ioctl_init(void)
4527 struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "ioctl");
4529 torture_suite_add_1smb2_test(suite, "shadow_copy",
4530 test_ioctl_get_shadow_copy);
4531 torture_suite_add_1smb2_test(suite, "req_resume_key",
4532 test_ioctl_req_resume_key);
4533 torture_suite_add_1smb2_test(suite, "copy_chunk_simple",
4534 test_ioctl_copy_chunk_simple);
4535 torture_suite_add_1smb2_test(suite, "copy_chunk_multi",
4536 test_ioctl_copy_chunk_multi);
4537 torture_suite_add_1smb2_test(suite, "copy_chunk_tiny",
4538 test_ioctl_copy_chunk_tiny);
4539 torture_suite_add_1smb2_test(suite, "copy_chunk_overwrite",
4540 test_ioctl_copy_chunk_over);
4541 torture_suite_add_1smb2_test(suite, "copy_chunk_append",
4542 test_ioctl_copy_chunk_append);
4543 torture_suite_add_1smb2_test(suite, "copy_chunk_limits",
4544 test_ioctl_copy_chunk_limits);
4545 torture_suite_add_1smb2_test(suite, "copy_chunk_src_lock",
4546 test_ioctl_copy_chunk_src_lck);
4547 torture_suite_add_1smb2_test(suite, "copy_chunk_dest_lock",
4548 test_ioctl_copy_chunk_dest_lck);
4549 torture_suite_add_1smb2_test(suite, "copy_chunk_bad_key",
4550 test_ioctl_copy_chunk_bad_key);
4551 torture_suite_add_1smb2_test(suite, "copy_chunk_src_is_dest",
4552 test_ioctl_copy_chunk_src_is_dest);
4553 torture_suite_add_1smb2_test(suite, "copy_chunk_src_is_dest_overlap",
4554 test_ioctl_copy_chunk_src_is_dest_overlap);
4555 torture_suite_add_1smb2_test(suite, "copy_chunk_bad_access",
4556 test_ioctl_copy_chunk_bad_access);
4557 torture_suite_add_1smb2_test(suite, "copy_chunk_write_access",
4558 test_ioctl_copy_chunk_write_access);
4559 torture_suite_add_1smb2_test(suite, "copy_chunk_src_exceed",
4560 test_ioctl_copy_chunk_src_exceed);
4561 torture_suite_add_1smb2_test(suite, "copy_chunk_src_exceed_multi",
4562 test_ioctl_copy_chunk_src_exceed_multi);
4563 torture_suite_add_1smb2_test(suite, "copy_chunk_sparse_dest",
4564 test_ioctl_copy_chunk_sparse_dest);
4565 torture_suite_add_1smb2_test(suite, "copy_chunk_max_output_sz",
4566 test_ioctl_copy_chunk_max_output_sz);
4567 torture_suite_add_1smb2_test(suite, "copy_chunk_zero_length",
4568 test_ioctl_copy_chunk_zero_length);
4569 torture_suite_add_1smb2_test(suite, "compress_file_flag",
4570 test_ioctl_compress_file_flag);
4571 torture_suite_add_1smb2_test(suite, "compress_dir_inherit",
4572 test_ioctl_compress_dir_inherit);
4573 torture_suite_add_1smb2_test(suite, "compress_invalid_format",
4574 test_ioctl_compress_invalid_format);
4575 torture_suite_add_1smb2_test(suite, "compress_invalid_buf",
4576 test_ioctl_compress_invalid_buf);
4577 torture_suite_add_1smb2_test(suite, "compress_query_file_attr",
4578 test_ioctl_compress_query_file_attr);
4579 torture_suite_add_1smb2_test(suite, "compress_create_with_attr",
4580 test_ioctl_compress_create_with_attr);
4581 torture_suite_add_1smb2_test(suite, "compress_inherit_disable",
4582 test_ioctl_compress_inherit_disable);
4583 torture_suite_add_1smb2_test(suite, "compress_set_file_attr",
4584 test_ioctl_compress_set_file_attr);
4585 torture_suite_add_1smb2_test(suite, "compress_perms",
4586 test_ioctl_compress_perms);
4587 torture_suite_add_1smb2_test(suite, "network_interface_info",
4588 test_ioctl_network_interface_info);
4589 torture_suite_add_1smb2_test(suite, "sparse_file_flag",
4590 test_ioctl_sparse_file_flag);
4591 torture_suite_add_1smb2_test(suite, "sparse_file_attr",
4592 test_ioctl_sparse_file_attr);
4593 torture_suite_add_1smb2_test(suite, "sparse_dir_flag",
4594 test_ioctl_sparse_dir_flag);
4595 torture_suite_add_1smb2_test(suite, "sparse_set_nobuf",
4596 test_ioctl_sparse_set_nobuf);
4597 torture_suite_add_1smb2_test(suite, "sparse_set_oversize",
4598 test_ioctl_sparse_set_oversize);
4599 torture_suite_add_1smb2_test(suite, "sparse_qar",
4600 test_ioctl_sparse_qar);
4601 torture_suite_add_1smb2_test(suite, "sparse_qar_malformed",
4602 test_ioctl_sparse_qar_malformed);
4603 torture_suite_add_1smb2_test(suite, "sparse_punch",
4604 test_ioctl_sparse_punch);
4605 torture_suite_add_1smb2_test(suite, "sparse_hole_dealloc",
4606 test_ioctl_sparse_hole_dealloc);
4607 torture_suite_add_1smb2_test(suite, "sparse_compressed",
4608 test_ioctl_sparse_compressed);
4609 torture_suite_add_1smb2_test(suite, "sparse_copy_chunk",
4610 test_ioctl_sparse_copy_chunk);
4611 torture_suite_add_1smb2_test(suite, "sparse_punch_invalid",
4612 test_ioctl_sparse_punch_invalid);
4613 torture_suite_add_1smb2_test(suite, "sparse_perms",
4614 test_ioctl_sparse_perms);
4615 torture_suite_add_1smb2_test(suite, "sparse_lock",
4616 test_ioctl_sparse_lck);
4617 torture_suite_add_1smb2_test(suite, "sparse_qar_ob1",
4618 test_ioctl_sparse_qar_ob1);
4620 suite->description = talloc_strdup(suite, "SMB2-IOCTL tests");
4622 return suite;