torture: test FSCTL_SET_SPARSE
[Samba.git] / source4 / torture / smb2 / ioctl.c
blob0da79c67fd97d842052ede2d63e74281c482e9fe
1 /*
2 Unix SMB/CIFS implementation.
4 test suite for SMB2 ioctl operations
6 Copyright (C) David Disseldorp 2011-2013
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "includes.h"
23 #include "librpc/gen_ndr/security.h"
24 #include "libcli/smb2/smb2.h"
25 #include "libcli/smb2/smb2_calls.h"
26 #include "torture/torture.h"
27 #include "torture/smb2/proto.h"
28 #include "../libcli/smb/smbXcli_base.h"
29 #include "librpc/gen_ndr/ndr_ioctl.h"
31 #define FNAME "testfsctl.dat"
32 #define FNAME2 "testfsctl2.dat"
33 #define DNAME "testfsctl_dir"
36 basic testing of SMB2 shadow copy calls
38 static bool test_ioctl_get_shadow_copy(struct torture_context *torture,
39 struct smb2_tree *tree)
41 struct smb2_handle h;
42 uint8_t buf[100];
43 NTSTATUS status;
44 union smb_ioctl ioctl;
45 TALLOC_CTX *tmp_ctx = talloc_new(tree);
47 smb2_util_unlink(tree, FNAME);
49 status = torture_smb2_testfile(tree, FNAME, &h);
50 torture_assert_ntstatus_ok(torture, status, "create write");
52 ZERO_ARRAY(buf);
53 status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf));
54 torture_assert_ntstatus_ok(torture, status, "write");
56 ZERO_STRUCT(ioctl);
57 ioctl.smb2.level = RAW_IOCTL_SMB2;
58 ioctl.smb2.in.file.handle = h;
59 ioctl.smb2.in.function = FSCTL_SRV_ENUM_SNAPS;
60 ioctl.smb2.in.max_response_size = 16;
61 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
63 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
64 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)
65 || NT_STATUS_EQUAL(status, NT_STATUS_INVALID_DEVICE_REQUEST)) {
66 torture_skip(torture, "FSCTL_SRV_ENUM_SNAPS not supported\n");
68 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_ENUM_SNAPS");
70 return true;
74 basic testing of the SMB2 server side copy ioctls
76 static bool test_ioctl_req_resume_key(struct torture_context *torture,
77 struct smb2_tree *tree)
79 struct smb2_handle h;
80 uint8_t buf[100];
81 NTSTATUS status;
82 union smb_ioctl ioctl;
83 TALLOC_CTX *tmp_ctx = talloc_new(tree);
84 struct req_resume_key_rsp res_key;
85 enum ndr_err_code ndr_ret;
87 smb2_util_unlink(tree, FNAME);
89 status = torture_smb2_testfile(tree, FNAME, &h);
90 torture_assert_ntstatus_ok(torture, status, "create write");
92 ZERO_ARRAY(buf);
93 status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf));
94 torture_assert_ntstatus_ok(torture, status, "write");
96 ZERO_STRUCT(ioctl);
97 ioctl.smb2.level = RAW_IOCTL_SMB2;
98 ioctl.smb2.in.file.handle = h;
99 ioctl.smb2.in.function = FSCTL_SRV_REQUEST_RESUME_KEY;
100 ioctl.smb2.in.max_response_size = 32;
101 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
103 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
104 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_REQUEST_RESUME_KEY");
106 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, &res_key,
107 (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
108 torture_assert_ndr_success(torture, ndr_ret,
109 "ndr_pull_req_resume_key_rsp");
111 ndr_print_debug((ndr_print_fn_t)ndr_print_req_resume_key_rsp, "yo", &res_key);
113 talloc_free(tmp_ctx);
114 return true;
117 static uint64_t patt_hash(uint64_t off)
119 return off;
122 static bool check_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 uint64_t i;
128 struct smb2_read r;
129 NTSTATUS status;
131 ZERO_STRUCT(r);
132 r.in.file.handle = h;
133 r.in.length = len;
134 r.in.offset = off;
135 status = smb2_read(tree, mem_ctx, &r);
136 torture_assert_ntstatus_ok(torture, status, "read");
138 torture_assert_u64_equal(torture, r.out.data.length, len,
139 "read data len mismatch");
141 for (i = 0; i <= len - 8; i += 8, patt_off += 8) {
142 uint64_t data = BVAL(r.out.data.data, i);
143 torture_assert_u64_equal(torture, data, patt_hash(patt_off),
144 talloc_asprintf(torture, "read data "
145 "pattern bad at %llu\n",
146 (unsigned long long)i));
149 talloc_free(r.out.data.data);
150 return true;
153 static bool test_setup_open(struct torture_context *torture,
154 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
155 const char *fname,
156 struct smb2_handle *fh,
157 uint32_t desired_access,
158 uint32_t file_attributes)
160 struct smb2_create io;
161 NTSTATUS status;
163 ZERO_STRUCT(io);
164 io.in.desired_access = desired_access;
165 io.in.file_attributes = file_attributes;
166 io.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
167 io.in.share_access =
168 NTCREATEX_SHARE_ACCESS_DELETE|
169 NTCREATEX_SHARE_ACCESS_READ|
170 NTCREATEX_SHARE_ACCESS_WRITE;
171 if (file_attributes & FILE_ATTRIBUTE_DIRECTORY) {
172 io.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
174 io.in.fname = fname;
176 status = smb2_create(tree, mem_ctx, &io);
177 torture_assert_ntstatus_ok(torture, status, "file create");
179 *fh = io.out.file.handle;
181 return true;
184 static bool test_setup_create_fill(struct torture_context *torture,
185 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
186 const char *fname,
187 struct smb2_handle *fh,
188 uint64_t size,
189 uint32_t desired_access,
190 uint32_t file_attributes)
192 NTSTATUS status;
193 bool ok;
194 uint64_t i;
195 uint8_t *buf = talloc_zero_size(mem_ctx, size);
196 torture_assert(torture, (buf != NULL), "no memory for file data buf");
198 smb2_util_unlink(tree, fname);
200 ok = test_setup_open(torture, tree, mem_ctx,
201 fname,
203 desired_access,
204 file_attributes);
205 torture_assert(torture, ok, "file open");
207 if (size > 0) {
208 uint64_t cur_off = 0;
209 for (i = 0; i <= size - 8; i += 8) {
210 SBVAL(buf, i, patt_hash(i));
212 while (size > 0) {
213 uint64_t io_sz = MIN(1024 * 1024, size);
214 status = smb2_util_write(tree, *fh,
215 buf + cur_off, cur_off, io_sz);
216 torture_assert_ntstatus_ok(torture, status, "file write");
218 size -= io_sz;
219 cur_off += io_sz;
222 return true;
225 static bool test_setup_copy_chunk(struct torture_context *torture,
226 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
227 uint32_t nchunks,
228 struct smb2_handle *src_h,
229 uint64_t src_size,
230 uint32_t src_desired_access,
231 struct smb2_handle *dest_h,
232 uint64_t dest_size,
233 uint32_t dest_desired_access,
234 struct srv_copychunk_copy *cc_copy,
235 union smb_ioctl *ioctl)
237 struct req_resume_key_rsp res_key;
238 bool ok;
239 NTSTATUS status;
240 enum ndr_err_code ndr_ret;
242 ok = test_setup_create_fill(torture, tree, mem_ctx, FNAME,
243 src_h, src_size, src_desired_access,
244 FILE_ATTRIBUTE_NORMAL);
245 torture_assert(torture, ok, "src file create fill");
247 ok = test_setup_create_fill(torture, tree, mem_ctx, FNAME2,
248 dest_h, dest_size, dest_desired_access,
249 FILE_ATTRIBUTE_NORMAL);
250 torture_assert(torture, ok, "dest file create fill");
252 ZERO_STRUCTPN(ioctl);
253 ioctl->smb2.level = RAW_IOCTL_SMB2;
254 ioctl->smb2.in.file.handle = *src_h;
255 ioctl->smb2.in.function = FSCTL_SRV_REQUEST_RESUME_KEY;
256 /* Allow for Key + ContextLength + Context */
257 ioctl->smb2.in.max_response_size = 32;
258 ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
260 status = smb2_ioctl(tree, mem_ctx, &ioctl->smb2);
261 torture_assert_ntstatus_ok(torture, status,
262 "FSCTL_SRV_REQUEST_RESUME_KEY");
264 ndr_ret = ndr_pull_struct_blob(&ioctl->smb2.out.out, mem_ctx, &res_key,
265 (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
267 torture_assert_ndr_success(torture, ndr_ret,
268 "ndr_pull_req_resume_key_rsp");
270 ZERO_STRUCTPN(ioctl);
271 ioctl->smb2.level = RAW_IOCTL_SMB2;
272 ioctl->smb2.in.file.handle = *dest_h;
273 ioctl->smb2.in.function = FSCTL_SRV_COPYCHUNK;
274 ioctl->smb2.in.max_response_size = sizeof(struct srv_copychunk_rsp);
275 ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
277 ZERO_STRUCTPN(cc_copy);
278 memcpy(cc_copy->source_key, res_key.resume_key, ARRAY_SIZE(cc_copy->source_key));
279 cc_copy->chunk_count = nchunks;
280 cc_copy->chunks = talloc_zero_array(mem_ctx, struct srv_copychunk, nchunks);
281 torture_assert(torture, (cc_copy->chunks != NULL), "no memory for chunks");
283 return true;
287 static bool check_copy_chunk_rsp(struct torture_context *torture,
288 struct srv_copychunk_rsp *cc_rsp,
289 uint32_t ex_chunks_written,
290 uint32_t ex_chunk_bytes_written,
291 uint32_t ex_total_bytes_written)
293 torture_assert_int_equal(torture, cc_rsp->chunks_written,
294 ex_chunks_written, "num chunks");
295 torture_assert_int_equal(torture, cc_rsp->chunk_bytes_written,
296 ex_chunk_bytes_written, "chunk bytes written");
297 torture_assert_int_equal(torture, cc_rsp->total_bytes_written,
298 ex_total_bytes_written, "chunk total bytes");
299 return true;
302 static bool test_ioctl_copy_chunk_simple(struct torture_context *torture,
303 struct smb2_tree *tree)
305 struct smb2_handle src_h;
306 struct smb2_handle dest_h;
307 NTSTATUS status;
308 union smb_ioctl ioctl;
309 TALLOC_CTX *tmp_ctx = talloc_new(tree);
310 struct srv_copychunk_copy cc_copy;
311 struct srv_copychunk_rsp cc_rsp;
312 enum ndr_err_code ndr_ret;
313 bool ok;
315 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
316 1, /* 1 chunk */
317 &src_h, 4096, /* fill 4096 byte src file */
318 SEC_RIGHTS_FILE_ALL,
319 &dest_h, 0, /* 0 byte dest file */
320 SEC_RIGHTS_FILE_ALL,
321 &cc_copy,
322 &ioctl);
323 if (!ok) {
324 torture_fail(torture, "setup copy chunk error");
327 /* copy all src file data (via a single chunk desc) */
328 cc_copy.chunks[0].source_off = 0;
329 cc_copy.chunks[0].target_off = 0;
330 cc_copy.chunks[0].length = 4096;
332 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
333 &cc_copy,
334 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
335 torture_assert_ndr_success(torture, ndr_ret,
336 "ndr_push_srv_copychunk_copy");
338 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
339 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
341 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
342 &cc_rsp,
343 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
344 torture_assert_ndr_success(torture, ndr_ret,
345 "ndr_pull_srv_copychunk_rsp");
347 ok = check_copy_chunk_rsp(torture, &cc_rsp,
348 1, /* chunks written */
349 0, /* chunk bytes unsuccessfully written */
350 4096); /* total bytes written */
351 if (!ok) {
352 torture_fail(torture, "bad copy chunk response data");
355 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
356 if (!ok) {
357 torture_fail(torture, "inconsistent file data");
360 smb2_util_close(tree, src_h);
361 smb2_util_close(tree, dest_h);
362 talloc_free(tmp_ctx);
363 return true;
366 static bool test_ioctl_copy_chunk_multi(struct torture_context *torture,
367 struct smb2_tree *tree)
369 struct smb2_handle src_h;
370 struct smb2_handle dest_h;
371 NTSTATUS status;
372 union smb_ioctl ioctl;
373 TALLOC_CTX *tmp_ctx = talloc_new(tree);
374 struct srv_copychunk_copy cc_copy;
375 struct srv_copychunk_rsp cc_rsp;
376 enum ndr_err_code ndr_ret;
377 bool ok;
379 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
380 2, /* chunks */
381 &src_h, 8192, /* src file */
382 SEC_RIGHTS_FILE_ALL,
383 &dest_h, 0, /* dest file */
384 SEC_RIGHTS_FILE_ALL,
385 &cc_copy,
386 &ioctl);
387 if (!ok) {
388 torture_fail(torture, "setup copy chunk error");
391 /* copy all src file data via two chunks */
392 cc_copy.chunks[0].source_off = 0;
393 cc_copy.chunks[0].target_off = 0;
394 cc_copy.chunks[0].length = 4096;
396 cc_copy.chunks[1].source_off = 4096;
397 cc_copy.chunks[1].target_off = 4096;
398 cc_copy.chunks[1].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 2, /* chunks written */
417 0, /* chunk bytes unsuccessfully written */
418 8192); /* total bytes written */
419 if (!ok) {
420 torture_fail(torture, "bad copy chunk response data");
423 smb2_util_close(tree, src_h);
424 smb2_util_close(tree, dest_h);
425 talloc_free(tmp_ctx);
426 return true;
429 static bool test_ioctl_copy_chunk_tiny(struct torture_context *torture,
430 struct smb2_tree *tree)
432 struct smb2_handle src_h;
433 struct smb2_handle dest_h;
434 NTSTATUS status;
435 union smb_ioctl ioctl;
436 TALLOC_CTX *tmp_ctx = talloc_new(tree);
437 struct srv_copychunk_copy cc_copy;
438 struct srv_copychunk_rsp cc_rsp;
439 enum ndr_err_code ndr_ret;
440 bool ok;
442 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
443 2, /* chunks */
444 &src_h, 100, /* src file */
445 SEC_RIGHTS_FILE_ALL,
446 &dest_h, 0, /* dest file */
447 SEC_RIGHTS_FILE_ALL,
448 &cc_copy,
449 &ioctl);
450 if (!ok) {
451 torture_fail(torture, "setup copy chunk error");
454 /* copy all src file data via two chunks, sub block size chunks */
455 cc_copy.chunks[0].source_off = 0;
456 cc_copy.chunks[0].target_off = 0;
457 cc_copy.chunks[0].length = 50;
459 cc_copy.chunks[1].source_off = 50;
460 cc_copy.chunks[1].target_off = 50;
461 cc_copy.chunks[1].length = 50;
463 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
464 &cc_copy,
465 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
466 torture_assert_ndr_success(torture, ndr_ret,
467 "ndr_push_srv_copychunk_copy");
469 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
470 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
472 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
473 &cc_rsp,
474 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
475 torture_assert_ndr_success(torture, ndr_ret,
476 "ndr_pull_srv_copychunk_rsp");
478 ok = check_copy_chunk_rsp(torture, &cc_rsp,
479 2, /* chunks written */
480 0, /* chunk bytes unsuccessfully written */
481 100); /* total bytes written */
482 if (!ok) {
483 torture_fail(torture, "bad copy chunk response data");
486 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 100, 0);
487 if (!ok) {
488 torture_fail(torture, "inconsistent file 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_over(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, 8192, /* src file */
513 SEC_RIGHTS_FILE_ALL,
514 &dest_h, 4096, /* dest file */
515 SEC_RIGHTS_FILE_ALL,
516 &cc_copy,
517 &ioctl);
518 if (!ok) {
519 torture_fail(torture, "setup copy chunk error");
522 /* first chunk overwrites existing dest data */
523 cc_copy.chunks[0].source_off = 0;
524 cc_copy.chunks[0].target_off = 0;
525 cc_copy.chunks[0].length = 4096;
527 /* second chunk overwrites the first */
528 cc_copy.chunks[1].source_off = 4096;
529 cc_copy.chunks[1].target_off = 0;
530 cc_copy.chunks[1].length = 4096;
532 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
533 &cc_copy,
534 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
535 torture_assert_ndr_success(torture, ndr_ret,
536 "ndr_push_srv_copychunk_copy");
538 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
539 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
541 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
542 &cc_rsp,
543 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
544 torture_assert_ndr_success(torture, ndr_ret,
545 "ndr_pull_srv_copychunk_rsp");
547 ok = check_copy_chunk_rsp(torture, &cc_rsp,
548 2, /* chunks written */
549 0, /* chunk bytes unsuccessfully written */
550 8192); /* total bytes written */
551 if (!ok) {
552 torture_fail(torture, "bad copy chunk response data");
555 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 4096);
556 if (!ok) {
557 torture_fail(torture, "inconsistent file data");
560 smb2_util_close(tree, src_h);
561 smb2_util_close(tree, dest_h);
562 talloc_free(tmp_ctx);
563 return true;
566 static bool test_ioctl_copy_chunk_append(struct torture_context *torture,
567 struct smb2_tree *tree)
569 struct smb2_handle src_h;
570 struct smb2_handle dest_h;
571 NTSTATUS status;
572 union smb_ioctl ioctl;
573 TALLOC_CTX *tmp_ctx = talloc_new(tree);
574 struct srv_copychunk_copy cc_copy;
575 struct srv_copychunk_rsp cc_rsp;
576 enum ndr_err_code ndr_ret;
577 bool ok;
579 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
580 2, /* chunks */
581 &src_h, 4096, /* src file */
582 SEC_RIGHTS_FILE_ALL,
583 &dest_h, 0, /* dest file */
584 SEC_RIGHTS_FILE_ALL,
585 &cc_copy,
586 &ioctl);
587 if (!ok) {
588 torture_fail(torture, "setup copy chunk error");
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 appends the same data to the first */
596 cc_copy.chunks[1].source_off = 0;
597 cc_copy.chunks[1].target_off = 4096;
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, 0);
624 if (!ok) {
625 torture_fail(torture, "inconsistent file data");
628 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 4096, 4096, 0);
629 if (!ok) {
630 torture_fail(torture, "inconsistent file data");
633 smb2_util_close(tree, src_h);
634 smb2_util_close(tree, dest_h);
635 talloc_free(tmp_ctx);
636 return true;
639 static bool test_ioctl_copy_chunk_limits(struct torture_context *torture,
640 struct smb2_tree *tree)
642 struct smb2_handle src_h;
643 struct smb2_handle dest_h;
644 NTSTATUS status;
645 union smb_ioctl ioctl;
646 TALLOC_CTX *tmp_ctx = talloc_new(tree);
647 struct srv_copychunk_copy cc_copy;
648 struct srv_copychunk_rsp cc_rsp;
649 enum ndr_err_code ndr_ret;
650 bool ok;
652 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
653 1, /* chunks */
654 &src_h, 4096, /* src file */
655 SEC_RIGHTS_FILE_ALL,
656 &dest_h, 0, /* dest file */
657 SEC_RIGHTS_FILE_ALL,
658 &cc_copy,
659 &ioctl);
660 if (!ok) {
661 torture_fail(torture, "setup copy chunk error");
664 /* send huge chunk length request */
665 cc_copy.chunks[0].source_off = 0;
666 cc_copy.chunks[0].target_off = 0;
667 cc_copy.chunks[0].length = UINT_MAX;
669 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
670 &cc_copy,
671 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
672 torture_assert_ndr_success(torture, ndr_ret, "marshalling request");
674 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
675 torture_assert_ntstatus_equal(torture, status,
676 NT_STATUS_INVALID_PARAMETER,
677 "bad oversize chunk response");
679 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
680 &cc_rsp,
681 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
682 torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
684 torture_comment(torture, "limit max chunks, got %u\n",
685 cc_rsp.chunks_written);
686 torture_comment(torture, "limit max chunk len, got %u\n",
687 cc_rsp.chunk_bytes_written);
688 torture_comment(torture, "limit max total bytes, got %u\n",
689 cc_rsp.total_bytes_written);
691 smb2_util_close(tree, src_h);
692 smb2_util_close(tree, dest_h);
693 talloc_free(tmp_ctx);
694 return true;
697 static bool test_ioctl_copy_chunk_src_lck(struct torture_context *torture,
698 struct smb2_tree *tree)
700 struct smb2_handle src_h;
701 struct smb2_handle src_h2;
702 struct smb2_handle dest_h;
703 NTSTATUS status;
704 union smb_ioctl ioctl;
705 TALLOC_CTX *tmp_ctx = talloc_new(tree);
706 struct srv_copychunk_copy cc_copy;
707 struct srv_copychunk_rsp cc_rsp;
708 enum ndr_err_code ndr_ret;
709 bool ok;
710 struct smb2_lock lck;
711 struct smb2_lock_element el[1];
713 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
714 1, /* chunks */
715 &src_h, 4096, /* src file */
716 SEC_RIGHTS_FILE_ALL,
717 &dest_h, 0, /* dest file */
718 SEC_RIGHTS_FILE_ALL,
719 &cc_copy,
720 &ioctl);
721 if (!ok) {
722 torture_fail(torture, "setup copy chunk error");
725 cc_copy.chunks[0].source_off = 0;
726 cc_copy.chunks[0].target_off = 0;
727 cc_copy.chunks[0].length = 4096;
729 /* open and lock the copychunk src file */
730 status = torture_smb2_testfile(tree, FNAME, &src_h2);
731 torture_assert_ntstatus_ok(torture, status, "2nd src open");
733 lck.in.lock_count = 0x0001;
734 lck.in.lock_sequence = 0x00000000;
735 lck.in.file.handle = src_h2;
736 lck.in.locks = el;
737 el[0].offset = cc_copy.chunks[0].source_off;
738 el[0].length = cc_copy.chunks[0].length;
739 el[0].reserved = 0;
740 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
742 status = smb2_lock(tree, &lck);
743 torture_assert_ntstatus_ok(torture, status, "lock");
745 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
746 &cc_copy,
747 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
748 torture_assert_ndr_success(torture, ndr_ret,
749 "ndr_push_srv_copychunk_copy");
751 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
753 * 2k12 & Samba return lock_conflict, Windows 7 & 2k8 return success...
755 * Edgar Olougouna @ MS wrote:
756 * Regarding the FSCTL_SRV_COPYCHUNK and STATUS_FILE_LOCK_CONFLICT
757 * discrepancy observed between Windows versions, we confirm that the
758 * behavior change is expected.
760 * CopyChunk in Windows Server 2012 use regular Readfile/Writefile APIs
761 * to move the chunks from the source to the destination.
762 * These ReadFile/WriteFile APIs go through the byte-range lock checks,
763 * and this explains the observed STATUS_FILE_LOCK_CONFLICT error.
765 * Prior to Windows Server 2012, CopyChunk used mapped sections to move
766 * the data. And byte range locks are not enforced on mapped I/O, and
767 * this explains the STATUS_SUCCESS observed on Windows Server 2008 R2.
769 torture_assert_ntstatus_equal(torture, status,
770 NT_STATUS_FILE_LOCK_CONFLICT,
771 "FSCTL_SRV_COPYCHUNK locked");
773 /* should get cc response data with the lock conflict status */
774 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
775 &cc_rsp,
776 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
777 torture_assert_ndr_success(torture, ndr_ret,
778 "ndr_pull_srv_copychunk_rsp");
779 ok = check_copy_chunk_rsp(torture, &cc_rsp,
780 0, /* chunks written */
781 0, /* chunk bytes unsuccessfully written */
782 0); /* total bytes written */
784 lck.in.lock_count = 0x0001;
785 lck.in.lock_sequence = 0x00000001;
786 lck.in.file.handle = src_h2;
787 lck.in.locks = el;
788 el[0].offset = cc_copy.chunks[0].source_off;
789 el[0].length = cc_copy.chunks[0].length;
790 el[0].reserved = 0;
791 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
792 status = smb2_lock(tree, &lck);
793 torture_assert_ntstatus_ok(torture, status, "unlock");
795 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
796 torture_assert_ntstatus_ok(torture, status,
797 "FSCTL_SRV_COPYCHUNK unlocked");
799 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
800 &cc_rsp,
801 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
802 torture_assert_ndr_success(torture, ndr_ret,
803 "ndr_pull_srv_copychunk_rsp");
805 ok = check_copy_chunk_rsp(torture, &cc_rsp,
806 1, /* chunks written */
807 0, /* chunk bytes unsuccessfully written */
808 4096); /* total bytes written */
809 if (!ok) {
810 torture_fail(torture, "bad copy chunk response data");
813 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
814 if (!ok) {
815 torture_fail(torture, "inconsistent file data");
818 smb2_util_close(tree, src_h2);
819 smb2_util_close(tree, src_h);
820 smb2_util_close(tree, dest_h);
821 talloc_free(tmp_ctx);
822 return true;
825 static bool test_ioctl_copy_chunk_dest_lck(struct torture_context *torture,
826 struct smb2_tree *tree)
828 struct smb2_handle src_h;
829 struct smb2_handle dest_h;
830 struct smb2_handle dest_h2;
831 NTSTATUS status;
832 union smb_ioctl ioctl;
833 TALLOC_CTX *tmp_ctx = talloc_new(tree);
834 struct srv_copychunk_copy cc_copy;
835 struct srv_copychunk_rsp cc_rsp;
836 enum ndr_err_code ndr_ret;
837 bool ok;
838 struct smb2_lock lck;
839 struct smb2_lock_element el[1];
841 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
842 1, /* chunks */
843 &src_h, 4096, /* src file */
844 SEC_RIGHTS_FILE_ALL,
845 &dest_h, 4096, /* dest file */
846 SEC_RIGHTS_FILE_ALL,
847 &cc_copy,
848 &ioctl);
849 if (!ok) {
850 torture_fail(torture, "setup copy chunk error");
853 cc_copy.chunks[0].source_off = 0;
854 cc_copy.chunks[0].target_off = 0;
855 cc_copy.chunks[0].length = 4096;
857 /* open and lock the copychunk dest file */
858 status = torture_smb2_testfile(tree, FNAME2, &dest_h2);
859 torture_assert_ntstatus_ok(torture, status, "2nd src open");
861 lck.in.lock_count = 0x0001;
862 lck.in.lock_sequence = 0x00000000;
863 lck.in.file.handle = dest_h2;
864 lck.in.locks = el;
865 el[0].offset = cc_copy.chunks[0].target_off;
866 el[0].length = cc_copy.chunks[0].length;
867 el[0].reserved = 0;
868 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
870 status = smb2_lock(tree, &lck);
871 torture_assert_ntstatus_ok(torture, status, "lock");
873 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
874 &cc_copy,
875 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
876 torture_assert_ndr_success(torture, ndr_ret,
877 "ndr_push_srv_copychunk_copy");
879 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
880 torture_assert_ntstatus_equal(torture, status,
881 NT_STATUS_FILE_LOCK_CONFLICT,
882 "FSCTL_SRV_COPYCHUNK locked");
884 lck.in.lock_count = 0x0001;
885 lck.in.lock_sequence = 0x00000001;
886 lck.in.file.handle = dest_h2;
887 lck.in.locks = el;
888 el[0].offset = cc_copy.chunks[0].target_off;
889 el[0].length = cc_copy.chunks[0].length;
890 el[0].reserved = 0;
891 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
892 status = smb2_lock(tree, &lck);
893 torture_assert_ntstatus_ok(torture, status, "unlock");
895 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
896 torture_assert_ntstatus_ok(torture, status,
897 "FSCTL_SRV_COPYCHUNK unlocked");
899 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
900 &cc_rsp,
901 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
902 torture_assert_ndr_success(torture, ndr_ret,
903 "ndr_pull_srv_copychunk_rsp");
905 ok = check_copy_chunk_rsp(torture, &cc_rsp,
906 1, /* chunks written */
907 0, /* chunk bytes unsuccessfully written */
908 4096); /* total bytes written */
909 if (!ok) {
910 torture_fail(torture, "bad copy chunk response data");
913 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
914 if (!ok) {
915 torture_fail(torture, "inconsistent file data");
918 smb2_util_close(tree, dest_h2);
919 smb2_util_close(tree, src_h);
920 smb2_util_close(tree, dest_h);
921 talloc_free(tmp_ctx);
922 return true;
925 static bool test_ioctl_copy_chunk_bad_key(struct torture_context *torture,
926 struct smb2_tree *tree)
928 struct smb2_handle src_h;
929 struct smb2_handle dest_h;
930 NTSTATUS status;
931 union smb_ioctl ioctl;
932 TALLOC_CTX *tmp_ctx = talloc_new(tree);
933 struct srv_copychunk_copy cc_copy;
934 enum ndr_err_code ndr_ret;
935 bool ok;
937 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
939 &src_h, 4096,
940 SEC_RIGHTS_FILE_ALL,
941 &dest_h, 0,
942 SEC_RIGHTS_FILE_ALL,
943 &cc_copy,
944 &ioctl);
945 if (!ok) {
946 torture_fail(torture, "setup copy chunk error");
949 /* overwrite the resume key with a bogus value */
950 memcpy(cc_copy.source_key, "deadbeefdeadbeefdeadbeef", 24);
952 cc_copy.chunks[0].source_off = 0;
953 cc_copy.chunks[0].target_off = 0;
954 cc_copy.chunks[0].length = 4096;
956 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
957 &cc_copy,
958 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
959 torture_assert_ndr_success(torture, ndr_ret,
960 "ndr_push_srv_copychunk_copy");
962 /* Server 2k12 returns NT_STATUS_OBJECT_NAME_NOT_FOUND */
963 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
964 torture_assert_ntstatus_equal(torture, status,
965 NT_STATUS_OBJECT_NAME_NOT_FOUND,
966 "FSCTL_SRV_COPYCHUNK");
968 smb2_util_close(tree, src_h);
969 smb2_util_close(tree, dest_h);
970 talloc_free(tmp_ctx);
971 return true;
974 static bool test_ioctl_copy_chunk_src_is_dest(struct torture_context *torture,
975 struct smb2_tree *tree)
977 struct smb2_handle src_h;
978 struct smb2_handle dest_h;
979 NTSTATUS status;
980 union smb_ioctl ioctl;
981 TALLOC_CTX *tmp_ctx = talloc_new(tree);
982 struct srv_copychunk_copy cc_copy;
983 struct srv_copychunk_rsp cc_rsp;
984 enum ndr_err_code ndr_ret;
985 bool ok;
987 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
989 &src_h, 8192,
990 SEC_RIGHTS_FILE_ALL,
991 &dest_h, 0,
992 SEC_RIGHTS_FILE_ALL,
993 &cc_copy,
994 &ioctl);
995 if (!ok) {
996 torture_fail(torture, "setup copy chunk error");
999 /* the source is also the destination */
1000 ioctl.smb2.in.file.handle = src_h;
1002 /* non-overlapping */
1003 cc_copy.chunks[0].source_off = 0;
1004 cc_copy.chunks[0].target_off = 4096;
1005 cc_copy.chunks[0].length = 4096;
1007 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1008 &cc_copy,
1009 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1010 torture_assert_ndr_success(torture, ndr_ret,
1011 "ndr_push_srv_copychunk_copy");
1013 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1014 torture_assert_ntstatus_ok(torture, status,
1015 "FSCTL_SRV_COPYCHUNK");
1017 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1018 &cc_rsp,
1019 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1020 torture_assert_ndr_success(torture, ndr_ret,
1021 "ndr_pull_srv_copychunk_rsp");
1023 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1024 1, /* chunks written */
1025 0, /* chunk bytes unsuccessfully written */
1026 4096); /* total bytes written */
1027 if (!ok) {
1028 torture_fail(torture, "bad copy chunk response data");
1031 ok = check_pattern(torture, tree, tmp_ctx, src_h, 0, 4096, 0);
1032 if (!ok) {
1033 torture_fail(torture, "inconsistent file data");
1035 ok = check_pattern(torture, tree, tmp_ctx, src_h, 4096, 4096, 0);
1036 if (!ok) {
1037 torture_fail(torture, "inconsistent file data");
1040 smb2_util_close(tree, src_h);
1041 smb2_util_close(tree, dest_h);
1042 talloc_free(tmp_ctx);
1043 return true;
1047 * Test a single-chunk copychunk request, where the source and target ranges
1048 * overlap, and the SourceKey refers to the same target file. E.g:
1050 * Initial State
1051 * -------------
1052 * File: src_and_dest
1053 * Offset: 0123456789
1054 * Data: abcdefghij
1056 * Request
1057 * -------
1058 * FSCTL_SRV_COPYCHUNK(src_and_dest)
1059 * SourceKey = SRV_REQUEST_RESUME_KEY(src_and_dest)
1060 * ChunkCount = 1
1061 * Chunks[0].SourceOffset = 0
1062 * Chunks[0].TargetOffset = 4
1063 * Chunks[0].Length = 6
1065 * Resultant State
1066 * ---------------
1067 * File: src_and_dest
1068 * Offset: 0123456789
1069 * Data: abcdabcdef
1071 * The resultant contents of src_and_dest is dependent on the server's
1072 * copy algorithm. In the above example, the server uses an IO buffer
1073 * large enough to hold the entire six-byte source data before writing
1074 * to TargetOffset. If the server were to use a four-byte IO buffer and
1075 * started reads/writes from the lowest offset, then the two overlapping
1076 * bytes in the above example would be overwritten before being read. The
1077 * resultant file contents would be abcdabcdab.
1079 * Windows 2008r2 appears to use a 2048 byte copy buffer, overlapping bytes
1080 * after this offset are written before being read. Windows 2012 on the
1081 * other hand appears to use a buffer large enough to hold its maximum
1082 * supported chunk size (1M). Samba currently uses a 64k copy buffer by
1083 * default (vfs_cc_state.buf).
1085 * This test uses an 8-byte overlap at 2040-2048, so that it passes against
1086 * Windows 2008r2, 2012 and Samba servers. Note, 2008GM fails, as it appears
1087 * to use a different copy algorithm to 2008r2.
1089 static bool
1090 test_ioctl_copy_chunk_src_is_dest_overlap(struct torture_context *torture,
1091 struct smb2_tree *tree)
1093 struct smb2_handle src_h;
1094 struct smb2_handle dest_h;
1095 NTSTATUS status;
1096 union smb_ioctl ioctl;
1097 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1098 struct srv_copychunk_copy cc_copy;
1099 struct srv_copychunk_rsp cc_rsp;
1100 enum ndr_err_code ndr_ret;
1101 bool ok;
1103 /* exceed the vfs_default copy buffer */
1104 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1106 &src_h, 2048 * 2,
1107 SEC_RIGHTS_FILE_ALL,
1108 &dest_h, 0,
1109 SEC_RIGHTS_FILE_ALL,
1110 &cc_copy,
1111 &ioctl);
1112 if (!ok) {
1113 torture_fail(torture, "setup copy chunk error");
1116 /* the source is also the destination */
1117 ioctl.smb2.in.file.handle = src_h;
1119 /* 8 bytes overlap between source and target ranges */
1120 cc_copy.chunks[0].source_off = 0;
1121 cc_copy.chunks[0].target_off = 2048 - 8;
1122 cc_copy.chunks[0].length = 2048;
1124 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1125 &cc_copy,
1126 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1127 torture_assert_ndr_success(torture, ndr_ret,
1128 "ndr_push_srv_copychunk_copy");
1130 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1131 torture_assert_ntstatus_ok(torture, status,
1132 "FSCTL_SRV_COPYCHUNK");
1134 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1135 &cc_rsp,
1136 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1137 torture_assert_ndr_success(torture, ndr_ret,
1138 "ndr_pull_srv_copychunk_rsp");
1140 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1141 1, /* chunks written */
1142 0, /* chunk bytes unsuccessfully written */
1143 2048); /* total bytes written */
1144 if (!ok) {
1145 torture_fail(torture, "bad copy chunk response data");
1148 ok = check_pattern(torture, tree, tmp_ctx, src_h, 0, 2048 - 8, 0);
1149 if (!ok) {
1150 torture_fail(torture, "inconsistent file data");
1152 ok = check_pattern(torture, tree, tmp_ctx, src_h, 2048 - 8, 2048, 0);
1153 if (!ok) {
1154 torture_fail(torture, "inconsistent file data");
1157 smb2_util_close(tree, src_h);
1158 smb2_util_close(tree, dest_h);
1159 talloc_free(tmp_ctx);
1160 return true;
1163 static bool test_ioctl_copy_chunk_bad_access(struct torture_context *torture,
1164 struct smb2_tree *tree)
1166 struct smb2_handle src_h;
1167 struct smb2_handle dest_h;
1168 NTSTATUS status;
1169 union smb_ioctl ioctl;
1170 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1171 struct srv_copychunk_copy cc_copy;
1172 enum ndr_err_code ndr_ret;
1173 bool ok;
1175 /* no read permission on src */
1176 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1177 1, /* 1 chunk */
1178 &src_h, 4096, /* fill 4096 byte src file */
1179 SEC_RIGHTS_FILE_WRITE,
1180 &dest_h, 0, /* 0 byte dest file */
1181 SEC_RIGHTS_FILE_ALL,
1182 &cc_copy,
1183 &ioctl);
1184 if (!ok) {
1185 torture_fail(torture, "setup copy chunk error");
1188 cc_copy.chunks[0].source_off = 0;
1189 cc_copy.chunks[0].target_off = 0;
1190 cc_copy.chunks[0].length = 4096;
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_equal(torture, status,
1200 NT_STATUS_ACCESS_DENIED,
1201 "FSCTL_SRV_COPYCHUNK");
1203 smb2_util_close(tree, src_h);
1204 smb2_util_close(tree, dest_h);
1206 /* no write permission on dest */
1207 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1208 1, /* 1 chunk */
1209 &src_h, 4096, /* fill 4096 byte src file */
1210 SEC_RIGHTS_FILE_ALL,
1211 &dest_h, 0, /* 0 byte dest file */
1212 (SEC_RIGHTS_FILE_READ
1213 | SEC_RIGHTS_FILE_EXECUTE),
1214 &cc_copy,
1215 &ioctl);
1216 if (!ok) {
1217 torture_fail(torture, "setup copy chunk error");
1220 cc_copy.chunks[0].source_off = 0;
1221 cc_copy.chunks[0].target_off = 0;
1222 cc_copy.chunks[0].length = 4096;
1224 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1225 &cc_copy,
1226 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1227 torture_assert_ndr_success(torture, ndr_ret,
1228 "ndr_push_srv_copychunk_copy");
1230 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1231 torture_assert_ntstatus_equal(torture, status,
1232 NT_STATUS_ACCESS_DENIED,
1233 "FSCTL_SRV_COPYCHUNK");
1235 smb2_util_close(tree, src_h);
1236 smb2_util_close(tree, dest_h);
1238 /* no read permission on dest */
1239 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1240 1, /* 1 chunk */
1241 &src_h, 4096, /* fill 4096 byte src file */
1242 SEC_RIGHTS_FILE_ALL,
1243 &dest_h, 0, /* 0 byte dest file */
1244 (SEC_RIGHTS_FILE_WRITE
1245 | SEC_RIGHTS_FILE_EXECUTE),
1246 &cc_copy,
1247 &ioctl);
1248 if (!ok) {
1249 torture_fail(torture, "setup copy chunk error");
1252 cc_copy.chunks[0].source_off = 0;
1253 cc_copy.chunks[0].target_off = 0;
1254 cc_copy.chunks[0].length = 4096;
1256 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1257 &cc_copy,
1258 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1259 torture_assert_ndr_success(torture, ndr_ret,
1260 "ndr_push_srv_copychunk_copy");
1263 * FSCTL_SRV_COPYCHUNK requires read permission on dest,
1264 * FSCTL_SRV_COPYCHUNK_WRITE on the other hand does not.
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);
1273 talloc_free(tmp_ctx);
1275 return true;
1278 static bool test_ioctl_copy_chunk_write_access(struct torture_context *torture,
1279 struct smb2_tree *tree)
1281 struct smb2_handle src_h;
1282 struct smb2_handle dest_h;
1283 NTSTATUS status;
1284 union smb_ioctl ioctl;
1285 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1286 struct srv_copychunk_copy cc_copy;
1287 enum ndr_err_code ndr_ret;
1288 bool ok;
1290 /* no read permission on dest with FSCTL_SRV_COPYCHUNK_WRITE */
1291 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1292 1, /* 1 chunk */
1293 &src_h, 4096, /* fill 4096 byte src file */
1294 SEC_RIGHTS_FILE_ALL,
1295 &dest_h, 0, /* 0 byte dest file */
1296 (SEC_RIGHTS_FILE_WRITE
1297 | SEC_RIGHTS_FILE_EXECUTE),
1298 &cc_copy,
1299 &ioctl);
1300 if (!ok) {
1301 torture_fail(torture, "setup copy chunk error");
1304 ioctl.smb2.in.function = FSCTL_SRV_COPYCHUNK_WRITE;
1305 cc_copy.chunks[0].source_off = 0;
1306 cc_copy.chunks[0].target_off = 0;
1307 cc_copy.chunks[0].length = 4096;
1309 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1310 &cc_copy,
1311 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1312 torture_assert_ndr_success(torture, ndr_ret,
1313 "ndr_push_srv_copychunk_copy");
1315 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1316 torture_assert_ntstatus_ok(torture, status,
1317 "FSCTL_SRV_COPYCHUNK_WRITE");
1319 smb2_util_close(tree, src_h);
1320 smb2_util_close(tree, dest_h);
1321 talloc_free(tmp_ctx);
1323 return true;
1326 static bool test_ioctl_copy_chunk_src_exceed(struct torture_context *torture,
1327 struct smb2_tree *tree)
1329 struct smb2_handle src_h;
1330 struct smb2_handle dest_h;
1331 NTSTATUS status;
1332 union smb_ioctl ioctl;
1333 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1334 struct srv_copychunk_copy cc_copy;
1335 struct srv_copychunk_rsp cc_rsp;
1336 enum ndr_err_code ndr_ret;
1337 bool ok;
1339 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1340 1, /* 1 chunk */
1341 &src_h, 4096, /* fill 4096 byte src file */
1342 SEC_RIGHTS_FILE_ALL,
1343 &dest_h, 0, /* 0 byte dest file */
1344 SEC_RIGHTS_FILE_ALL,
1345 &cc_copy,
1346 &ioctl);
1347 if (!ok) {
1348 torture_fail(torture, "setup copy chunk error");
1351 /* Request copy where off + length exceeds size of src */
1352 cc_copy.chunks[0].source_off = 1024;
1353 cc_copy.chunks[0].target_off = 0;
1354 cc_copy.chunks[0].length = 4096;
1356 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1357 &cc_copy,
1358 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1359 torture_assert_ndr_success(torture, ndr_ret,
1360 "ndr_push_srv_copychunk_copy");
1362 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1363 torture_assert_ntstatus_equal(torture, status,
1364 NT_STATUS_INVALID_VIEW_SIZE,
1365 "FSCTL_SRV_COPYCHUNK oversize");
1367 /* Request copy where length exceeds size of src */
1368 cc_copy.chunks[0].source_off = 1024;
1369 cc_copy.chunks[0].target_off = 0;
1370 cc_copy.chunks[0].length = 3072;
1372 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1373 &cc_copy,
1374 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1375 torture_assert_ndr_success(torture, ndr_ret,
1376 "ndr_push_srv_copychunk_copy");
1378 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1379 torture_assert_ntstatus_ok(torture, status,
1380 "FSCTL_SRV_COPYCHUNK just right");
1382 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1383 &cc_rsp,
1384 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1385 torture_assert_ndr_success(torture, ndr_ret,
1386 "ndr_pull_srv_copychunk_rsp");
1388 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1389 1, /* chunks written */
1390 0, /* chunk bytes unsuccessfully written */
1391 3072); /* total bytes written */
1392 if (!ok) {
1393 torture_fail(torture, "bad copy chunk response data");
1396 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 3072, 1024);
1397 if (!ok) {
1398 torture_fail(torture, "inconsistent file data");
1401 smb2_util_close(tree, src_h);
1402 smb2_util_close(tree, dest_h);
1403 talloc_free(tmp_ctx);
1404 return true;
1407 static bool
1408 test_ioctl_copy_chunk_src_exceed_multi(struct torture_context *torture,
1409 struct smb2_tree *tree)
1411 struct smb2_handle src_h;
1412 struct smb2_handle dest_h;
1413 NTSTATUS status;
1414 union smb_ioctl ioctl;
1415 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1416 struct srv_copychunk_copy cc_copy;
1417 struct srv_copychunk_rsp cc_rsp;
1418 enum ndr_err_code ndr_ret;
1419 bool ok;
1421 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1422 2, /* 2 chunks */
1423 &src_h, 8192, /* fill 8192 byte src file */
1424 SEC_RIGHTS_FILE_ALL,
1425 &dest_h, 0, /* 0 byte dest file */
1426 SEC_RIGHTS_FILE_ALL,
1427 &cc_copy,
1428 &ioctl);
1429 if (!ok) {
1430 torture_fail(torture, "setup copy chunk error");
1433 /* Request copy where off + length exceeds size of src */
1434 cc_copy.chunks[0].source_off = 0;
1435 cc_copy.chunks[0].target_off = 0;
1436 cc_copy.chunks[0].length = 4096;
1438 cc_copy.chunks[1].source_off = 4096;
1439 cc_copy.chunks[1].target_off = 4096;
1440 cc_copy.chunks[1].length = 8192;
1442 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1443 &cc_copy,
1444 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1445 torture_assert_ndr_success(torture, ndr_ret,
1446 "ndr_push_srv_copychunk_copy");
1448 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1449 torture_assert_ntstatus_equal(torture, status,
1450 NT_STATUS_INVALID_VIEW_SIZE,
1451 "FSCTL_SRV_COPYCHUNK oversize");
1452 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1453 &cc_rsp,
1454 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1455 torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
1457 /* first chunk should still be written */
1458 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1459 1, /* chunks written */
1460 0, /* chunk bytes unsuccessfully written */
1461 4096); /* total bytes written */
1462 if (!ok) {
1463 torture_fail(torture, "bad copy chunk response data");
1465 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
1466 if (!ok) {
1467 torture_fail(torture, "inconsistent file data");
1470 smb2_util_close(tree, src_h);
1471 smb2_util_close(tree, dest_h);
1472 talloc_free(tmp_ctx);
1473 return true;
1476 static bool test_ioctl_copy_chunk_sparse_dest(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 struct smb2_read r;
1484 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1485 struct srv_copychunk_copy cc_copy;
1486 struct srv_copychunk_rsp cc_rsp;
1487 enum ndr_err_code ndr_ret;
1488 bool ok;
1489 int i;
1491 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1492 1, /* 1 chunk */
1493 &src_h, 4096, /* fill 4096 byte src file */
1494 SEC_RIGHTS_FILE_ALL,
1495 &dest_h, 0, /* 0 byte dest file */
1496 SEC_RIGHTS_FILE_ALL,
1497 &cc_copy,
1498 &ioctl);
1499 if (!ok) {
1500 torture_fail(torture, "setup copy chunk error");
1503 /* copy all src file data (via a single chunk desc) */
1504 cc_copy.chunks[0].source_off = 0;
1505 cc_copy.chunks[0].target_off = 4096;
1506 cc_copy.chunks[0].length = 4096;
1508 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1509 &cc_copy,
1510 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1511 torture_assert_ndr_success(torture, ndr_ret,
1512 "ndr_push_srv_copychunk_copy");
1514 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1515 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
1517 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1518 &cc_rsp,
1519 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1520 torture_assert_ndr_success(torture, ndr_ret,
1521 "ndr_pull_srv_copychunk_rsp");
1523 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1524 1, /* chunks written */
1525 0, /* chunk bytes unsuccessfully written */
1526 4096); /* total bytes written */
1527 if (!ok) {
1528 torture_fail(torture, "bad copy chunk response data");
1531 /* check for zeros in first 4k */
1532 ZERO_STRUCT(r);
1533 r.in.file.handle = dest_h;
1534 r.in.length = 4096;
1535 r.in.offset = 0;
1536 status = smb2_read(tree, tmp_ctx, &r);
1537 torture_assert_ntstatus_ok(torture, status, "read");
1539 torture_assert_u64_equal(torture, r.out.data.length, 4096,
1540 "read data len mismatch");
1542 for (i = 0; i < 4096; i++) {
1543 torture_assert(torture, (r.out.data.data[i] == 0),
1544 "sparse did not pass class");
1547 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 4096, 4096, 0);
1548 if (!ok) {
1549 torture_fail(torture, "inconsistent file data");
1552 smb2_util_close(tree, src_h);
1553 smb2_util_close(tree, dest_h);
1554 talloc_free(tmp_ctx);
1555 return true;
1559 * set the ioctl MaxOutputResponse size to less than
1560 * sizeof(struct srv_copychunk_rsp)
1562 static bool test_ioctl_copy_chunk_max_output_sz(struct torture_context *torture,
1563 struct smb2_tree *tree)
1565 struct smb2_handle src_h;
1566 struct smb2_handle dest_h;
1567 NTSTATUS status;
1568 union smb_ioctl ioctl;
1569 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1570 struct srv_copychunk_copy cc_copy;
1571 enum ndr_err_code ndr_ret;
1572 bool ok;
1574 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1575 1, /* 1 chunk */
1576 &src_h, 4096, /* fill 4096 byte src file */
1577 SEC_RIGHTS_FILE_ALL,
1578 &dest_h, 0, /* 0 byte dest file */
1579 SEC_RIGHTS_FILE_ALL,
1580 &cc_copy,
1581 &ioctl);
1582 if (!ok) {
1583 torture_fail(torture, "setup copy chunk error");
1586 cc_copy.chunks[0].source_off = 0;
1587 cc_copy.chunks[0].target_off = 0;
1588 cc_copy.chunks[0].length = 4096;
1589 /* req is valid, but use undersize max_response_size */
1590 ioctl.smb2.in.max_response_size = sizeof(struct srv_copychunk_rsp) - 1;
1592 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1593 &cc_copy,
1594 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1595 torture_assert_ndr_success(torture, ndr_ret,
1596 "ndr_push_srv_copychunk_copy");
1598 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1599 torture_assert_ntstatus_equal(torture, status,
1600 NT_STATUS_INVALID_PARAMETER,
1601 "FSCTL_SRV_COPYCHUNK");
1603 smb2_util_close(tree, src_h);
1604 smb2_util_close(tree, dest_h);
1605 talloc_free(tmp_ctx);
1606 return true;
1609 static bool test_ioctl_copy_chunk_zero_length(struct torture_context *torture,
1610 struct smb2_tree *tree)
1612 struct smb2_handle src_h;
1613 struct smb2_handle dest_h;
1614 NTSTATUS status;
1615 union smb_ioctl ioctl;
1616 union smb_fileinfo q;
1617 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1618 struct srv_copychunk_copy cc_copy;
1619 struct srv_copychunk_rsp cc_rsp;
1620 enum ndr_err_code ndr_ret;
1621 bool ok;
1623 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1624 1, /* 1 chunk */
1625 &src_h, 4096, /* fill 4096 byte src file */
1626 SEC_RIGHTS_FILE_ALL,
1627 &dest_h, 0, /* 0 byte dest file */
1628 SEC_RIGHTS_FILE_ALL,
1629 &cc_copy,
1630 &ioctl);
1631 if (!ok) {
1632 torture_fail(torture, "setup copy chunk error");
1635 /* zero length server-side copy (via a single chunk desc) */
1636 cc_copy.chunks[0].source_off = 0;
1637 cc_copy.chunks[0].target_off = 0;
1638 cc_copy.chunks[0].length = 0;
1640 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1641 &cc_copy,
1642 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1643 torture_assert_ndr_success(torture, ndr_ret,
1644 "ndr_push_srv_copychunk_copy");
1646 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1647 torture_assert_ntstatus_equal(torture, status,
1648 NT_STATUS_INVALID_PARAMETER,
1649 "bad zero-length chunk response");
1651 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1652 &cc_rsp,
1653 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1654 torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
1656 ZERO_STRUCT(q);
1657 q.all_info2.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
1658 q.all_info2.in.file.handle = dest_h;
1659 status = smb2_getinfo_file(tree, torture, &q);
1660 torture_assert_ntstatus_ok(torture, status, "getinfo");
1662 torture_assert_int_equal(torture, q.all_info2.out.size, 0,
1663 "size after zero len clone");
1665 smb2_util_close(tree, src_h);
1666 smb2_util_close(tree, dest_h);
1667 talloc_free(tmp_ctx);
1668 return true;
1671 static NTSTATUS test_ioctl_compress_fs_supported(struct torture_context *torture,
1672 struct smb2_tree *tree,
1673 TALLOC_CTX *mem_ctx,
1674 struct smb2_handle *fh,
1675 bool *compress_support)
1677 NTSTATUS status;
1678 union smb_fsinfo info;
1680 ZERO_STRUCT(info);
1681 info.generic.level = RAW_QFS_ATTRIBUTE_INFORMATION;
1682 info.generic.handle = *fh;
1683 status = smb2_getinfo_fs(tree, tree, &info);
1684 if (!NT_STATUS_IS_OK(status)) {
1685 return status;
1688 if (info.attribute_info.out.fs_attr & FILE_FILE_COMPRESSION) {
1689 *compress_support = true;
1690 } else {
1691 *compress_support = false;
1693 return NT_STATUS_OK;
1696 static NTSTATUS test_ioctl_compress_get(struct torture_context *torture,
1697 TALLOC_CTX *mem_ctx,
1698 struct smb2_tree *tree,
1699 struct smb2_handle fh,
1700 uint16_t *_compression_fmt)
1702 union smb_ioctl ioctl;
1703 struct compression_state cmpr_state;
1704 enum ndr_err_code ndr_ret;
1705 NTSTATUS status;
1707 ZERO_STRUCT(ioctl);
1708 ioctl.smb2.level = RAW_IOCTL_SMB2;
1709 ioctl.smb2.in.file.handle = fh;
1710 ioctl.smb2.in.function = FSCTL_GET_COMPRESSION;
1711 ioctl.smb2.in.max_response_size = sizeof(struct compression_state);
1712 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
1714 status = smb2_ioctl(tree, mem_ctx, &ioctl.smb2);
1715 if (!NT_STATUS_IS_OK(status)) {
1716 return status;
1719 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, mem_ctx,
1720 &cmpr_state,
1721 (ndr_pull_flags_fn_t)ndr_pull_compression_state);
1723 if (ndr_ret != NDR_ERR_SUCCESS) {
1724 return NT_STATUS_INTERNAL_ERROR;
1727 *_compression_fmt = cmpr_state.format;
1728 return NT_STATUS_OK;
1731 static NTSTATUS test_ioctl_compress_set(struct torture_context *torture,
1732 TALLOC_CTX *mem_ctx,
1733 struct smb2_tree *tree,
1734 struct smb2_handle fh,
1735 uint16_t compression_fmt)
1737 union smb_ioctl ioctl;
1738 struct compression_state cmpr_state;
1739 enum ndr_err_code ndr_ret;
1740 NTSTATUS status;
1742 ZERO_STRUCT(ioctl);
1743 ioctl.smb2.level = RAW_IOCTL_SMB2;
1744 ioctl.smb2.in.file.handle = fh;
1745 ioctl.smb2.in.function = FSCTL_SET_COMPRESSION;
1746 ioctl.smb2.in.max_response_size = 0;
1747 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
1749 cmpr_state.format = compression_fmt;
1750 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, mem_ctx,
1751 &cmpr_state,
1752 (ndr_push_flags_fn_t)ndr_push_compression_state);
1753 if (ndr_ret != NDR_ERR_SUCCESS) {
1754 return NT_STATUS_INTERNAL_ERROR;
1757 status = smb2_ioctl(tree, mem_ctx, &ioctl.smb2);
1758 return status;
1761 static bool test_ioctl_compress_file_flag(struct torture_context *torture,
1762 struct smb2_tree *tree)
1764 struct smb2_handle fh;
1765 NTSTATUS status;
1766 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1767 bool ok;
1768 uint16_t compression_fmt;
1770 ok = test_setup_create_fill(torture, tree, tmp_ctx,
1771 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
1772 FILE_ATTRIBUTE_NORMAL);
1773 torture_assert(torture, ok, "setup compression file");
1775 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
1776 &ok);
1777 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
1778 if (!ok) {
1779 smb2_util_close(tree, fh);
1780 torture_skip(torture, "FS compression not supported\n");
1783 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1784 &compression_fmt);
1785 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1787 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
1788 "initial compression state not NONE");
1790 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
1791 COMPRESSION_FORMAT_DEFAULT);
1792 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
1794 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1795 &compression_fmt);
1796 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1798 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
1799 "invalid compression state after set");
1801 smb2_util_close(tree, fh);
1802 talloc_free(tmp_ctx);
1803 return true;
1806 static bool test_ioctl_compress_dir_inherit(struct torture_context *torture,
1807 struct smb2_tree *tree)
1809 struct smb2_handle dirh;
1810 struct smb2_handle fh;
1811 NTSTATUS status;
1812 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1813 uint16_t compression_fmt;
1814 bool ok;
1815 char path_buf[PATH_MAX];
1817 smb2_deltree(tree, DNAME);
1818 ok = test_setup_create_fill(torture, tree, tmp_ctx,
1819 DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL,
1820 FILE_ATTRIBUTE_DIRECTORY);
1821 torture_assert(torture, ok, "setup compression directory");
1823 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &dirh,
1824 &ok);
1825 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
1826 if (!ok) {
1827 smb2_util_close(tree, dirh);
1828 smb2_deltree(tree, DNAME);
1829 torture_skip(torture, "FS compression not supported\n");
1832 /* set compression on parent dir, then check for inheritance */
1833 status = test_ioctl_compress_set(torture, tmp_ctx, tree, dirh,
1834 COMPRESSION_FORMAT_LZNT1);
1835 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
1837 status = test_ioctl_compress_get(torture, tmp_ctx, tree, dirh,
1838 &compression_fmt);
1839 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1841 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
1842 "invalid compression state after set");
1844 snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME);
1845 ok = test_setup_create_fill(torture, tree, tmp_ctx,
1846 path_buf, &fh, 4096, SEC_RIGHTS_FILE_ALL,
1847 FILE_ATTRIBUTE_NORMAL);
1848 torture_assert(torture, ok, "setup compression file");
1850 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1851 &compression_fmt);
1852 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1854 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
1855 "compression attr not inherited by new file");
1857 /* check compressed data is consistent */
1858 ok = check_pattern(torture, tree, tmp_ctx, fh, 0, 4096, 0);
1860 /* disable dir compression attr, file should remain compressed */
1861 status = test_ioctl_compress_set(torture, tmp_ctx, tree, dirh,
1862 COMPRESSION_FORMAT_NONE);
1863 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
1865 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1866 &compression_fmt);
1867 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1869 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
1870 "file compression attr removed after dir change");
1871 smb2_util_close(tree, fh);
1873 /* new files should no longer inherit compression attr */
1874 snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME2);
1875 ok = test_setup_create_fill(torture, tree, tmp_ctx,
1876 path_buf, &fh, 0, SEC_RIGHTS_FILE_ALL,
1877 FILE_ATTRIBUTE_NORMAL);
1878 torture_assert(torture, ok, "setup file");
1880 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1881 &compression_fmt);
1882 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1884 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
1885 "compression attr present on new file");
1887 smb2_util_close(tree, fh);
1888 smb2_util_close(tree, dirh);
1889 smb2_deltree(tree, DNAME);
1890 talloc_free(tmp_ctx);
1891 return true;
1894 static bool test_ioctl_compress_invalid_format(struct torture_context *torture,
1895 struct smb2_tree *tree)
1897 struct smb2_handle fh;
1898 NTSTATUS status;
1899 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1900 bool ok;
1901 uint16_t compression_fmt;
1903 ok = test_setup_create_fill(torture, tree, tmp_ctx,
1904 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
1905 FILE_ATTRIBUTE_NORMAL);
1906 torture_assert(torture, ok, "setup compression file");
1908 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
1909 &ok);
1910 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
1911 if (!ok) {
1912 smb2_util_close(tree, fh);
1913 torture_skip(torture, "FS compression not supported\n");
1916 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
1917 0x0042); /* bogus */
1918 torture_assert_ntstatus_equal(torture, status,
1919 NT_STATUS_INVALID_PARAMETER,
1920 "invalid FSCTL_SET_COMPRESSION");
1922 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1923 &compression_fmt);
1924 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1926 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
1927 "initial compression state not NONE");
1929 smb2_util_close(tree, fh);
1930 talloc_free(tmp_ctx);
1931 return true;
1934 static bool test_ioctl_compress_invalid_buf(struct torture_context *torture,
1935 struct smb2_tree *tree)
1937 struct smb2_handle fh;
1938 NTSTATUS status;
1939 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1940 bool ok;
1941 union smb_ioctl ioctl;
1943 ok = test_setup_create_fill(torture, tree, tmp_ctx,
1944 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
1945 FILE_ATTRIBUTE_NORMAL);
1946 torture_assert(torture, ok, "setup compression file");
1948 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
1949 &ok);
1950 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
1951 if (!ok) {
1952 smb2_util_close(tree, fh);
1953 torture_skip(torture, "FS compression not supported\n");
1956 ZERO_STRUCT(ioctl);
1957 ioctl.smb2.level = RAW_IOCTL_SMB2;
1958 ioctl.smb2.in.file.handle = fh;
1959 ioctl.smb2.in.function = FSCTL_GET_COMPRESSION;
1960 ioctl.smb2.in.max_response_size = 0; /* no room for rsp data */
1961 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
1963 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1964 if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_USER_BUFFER)
1965 && !NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
1966 /* neither Server 2k12 nor 2k8r2 response status */
1967 torture_assert(torture, true,
1968 "invalid FSCTL_SET_COMPRESSION");
1971 smb2_util_close(tree, fh);
1972 talloc_free(tmp_ctx);
1973 return true;
1976 static bool test_ioctl_compress_query_file_attr(struct torture_context *torture,
1977 struct smb2_tree *tree)
1979 struct smb2_handle fh;
1980 union smb_fileinfo io;
1981 NTSTATUS status;
1982 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1983 bool ok;
1985 ok = test_setup_create_fill(torture, tree, tmp_ctx,
1986 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
1987 FILE_ATTRIBUTE_NORMAL);
1988 torture_assert(torture, ok, "setup compression file");
1990 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
1991 &ok);
1992 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
1993 if (!ok) {
1994 smb2_util_close(tree, fh);
1995 torture_skip(torture, "FS compression not supported\n");
1998 ZERO_STRUCT(io);
1999 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
2000 io.generic.in.file.handle = fh;
2001 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2002 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2004 torture_assert(torture,
2005 ((io.all_info2.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2006 "compression attr before set");
2008 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
2009 COMPRESSION_FORMAT_DEFAULT);
2010 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
2012 ZERO_STRUCT(io);
2013 io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2014 io.generic.in.file.handle = fh;
2015 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2016 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2018 torture_assert(torture,
2019 (io.basic_info.out.attrib & FILE_ATTRIBUTE_COMPRESSED),
2020 "no compression attr after set");
2022 smb2_util_close(tree, fh);
2023 talloc_free(tmp_ctx);
2024 return true;
2028 * Specify FILE_ATTRIBUTE_COMPRESSED on creation, Windows does not retain this
2029 * attribute.
2031 static bool test_ioctl_compress_create_with_attr(struct torture_context *torture,
2032 struct smb2_tree *tree)
2034 struct smb2_handle fh2;
2035 union smb_fileinfo io;
2036 NTSTATUS status;
2037 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2038 uint16_t compression_fmt;
2039 bool ok;
2041 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2042 FNAME2, &fh2, 0, SEC_RIGHTS_FILE_ALL,
2043 (FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_COMPRESSED));
2044 torture_assert(torture, ok, "setup compression file");
2046 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh2,
2047 &ok);
2048 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2049 if (!ok) {
2050 smb2_util_close(tree, fh2);
2051 torture_skip(torture, "FS compression not supported\n");
2054 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh2,
2055 &compression_fmt);
2056 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2058 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2059 "initial compression state not NONE");
2061 ZERO_STRUCT(io);
2062 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
2063 io.generic.in.file.handle = fh2;
2064 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2065 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2067 torture_assert(torture,
2068 ((io.all_info2.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2069 "incorrect compression attr");
2071 smb2_util_close(tree, fh2);
2072 talloc_free(tmp_ctx);
2073 return true;
2076 static bool test_ioctl_compress_inherit_disable(struct torture_context *torture,
2077 struct smb2_tree *tree)
2079 struct smb2_handle fh;
2080 struct smb2_handle dirh;
2081 char path_buf[PATH_MAX];
2082 NTSTATUS status;
2083 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2084 bool ok;
2085 uint16_t compression_fmt;
2087 struct smb2_create io;
2089 smb2_deltree(tree, DNAME);
2090 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2091 DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL,
2092 FILE_ATTRIBUTE_DIRECTORY);
2093 torture_assert(torture, ok, "setup compression directory");
2095 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &dirh,
2096 &ok);
2097 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2098 if (!ok) {
2099 smb2_util_close(tree, dirh);
2100 smb2_deltree(tree, DNAME);
2101 torture_skip(torture, "FS compression not supported\n");
2104 /* set compression on parent dir, then check for inheritance */
2105 status = test_ioctl_compress_set(torture, tmp_ctx, tree, dirh,
2106 COMPRESSION_FORMAT_LZNT1);
2107 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
2109 status = test_ioctl_compress_get(torture, tmp_ctx, tree, dirh,
2110 &compression_fmt);
2111 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2113 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
2114 "invalid compression state after set");
2115 smb2_util_close(tree, dirh);
2117 snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME);
2118 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2119 path_buf, &fh, 0, SEC_RIGHTS_FILE_ALL,
2120 FILE_ATTRIBUTE_NORMAL);
2121 torture_assert(torture, ok, "setup compression file");
2123 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2124 &compression_fmt);
2125 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2127 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
2128 "compression attr not inherited by new file");
2129 smb2_util_close(tree, fh);
2131 snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME2);
2133 /* NO_COMPRESSION option should block inheritance */
2134 ZERO_STRUCT(io);
2135 io.in.desired_access = SEC_RIGHTS_FILE_ALL;
2136 io.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
2137 io.in.create_disposition = NTCREATEX_DISP_CREATE;
2138 io.in.create_options = NTCREATEX_OPTIONS_NO_COMPRESSION;
2139 io.in.share_access =
2140 NTCREATEX_SHARE_ACCESS_DELETE|
2141 NTCREATEX_SHARE_ACCESS_READ|
2142 NTCREATEX_SHARE_ACCESS_WRITE;
2143 io.in.fname = path_buf;
2145 status = smb2_create(tree, tmp_ctx, &io);
2146 torture_assert_ntstatus_ok(torture, status, "file create");
2148 fh = io.out.file.handle;
2150 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2151 &compression_fmt);
2152 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2154 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2155 "compression attr inherited by NO_COMPRESSION file");
2156 smb2_util_close(tree, fh);
2159 snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, DNAME);
2160 ZERO_STRUCT(io);
2161 io.in.desired_access = SEC_RIGHTS_FILE_ALL;
2162 io.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
2163 io.in.create_disposition = NTCREATEX_DISP_CREATE;
2164 io.in.create_options = (NTCREATEX_OPTIONS_NO_COMPRESSION
2165 | NTCREATEX_OPTIONS_DIRECTORY);
2166 io.in.share_access =
2167 NTCREATEX_SHARE_ACCESS_DELETE|
2168 NTCREATEX_SHARE_ACCESS_READ|
2169 NTCREATEX_SHARE_ACCESS_WRITE;
2170 io.in.fname = path_buf;
2172 status = smb2_create(tree, tmp_ctx, &io);
2173 torture_assert_ntstatus_ok(torture, status, "dir create");
2175 dirh = io.out.file.handle;
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_NONE),
2182 "compression attr inherited by NO_COMPRESSION dir");
2183 smb2_util_close(tree, dirh);
2184 smb2_deltree(tree, DNAME);
2186 talloc_free(tmp_ctx);
2187 return true;
2190 /* attempting to set compression via SetInfo should not stick */
2191 static bool test_ioctl_compress_set_file_attr(struct torture_context *torture,
2192 struct smb2_tree *tree)
2194 struct smb2_handle fh;
2195 struct smb2_handle dirh;
2196 union smb_fileinfo io;
2197 union smb_setfileinfo set_io;
2198 uint16_t compression_fmt;
2199 NTSTATUS status;
2200 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2201 bool ok;
2203 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2204 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2205 FILE_ATTRIBUTE_NORMAL);
2206 torture_assert(torture, ok, "setup compression file");
2208 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
2209 &ok);
2210 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2211 if (!ok) {
2212 smb2_util_close(tree, fh);
2213 torture_skip(torture, "FS compression not supported\n");
2216 ZERO_STRUCT(io);
2217 io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2218 io.generic.in.file.handle = fh;
2219 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2220 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2222 torture_assert(torture,
2223 ((io.basic_info.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2224 "compression attr before set");
2226 ZERO_STRUCT(set_io);
2227 set_io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2228 set_io.basic_info.in.file.handle = fh;
2229 set_io.basic_info.in.create_time = io.basic_info.out.create_time;
2230 set_io.basic_info.in.access_time = io.basic_info.out.access_time;
2231 set_io.basic_info.in.write_time = io.basic_info.out.write_time;
2232 set_io.basic_info.in.change_time = io.basic_info.out.change_time;
2233 set_io.basic_info.in.attrib = (io.basic_info.out.attrib
2234 | FILE_ATTRIBUTE_COMPRESSED);
2235 status = smb2_setinfo_file(tree, &set_io);
2236 torture_assert_ntstatus_ok(torture, status, "SMB2_SETINFO_FILE");
2238 ZERO_STRUCT(io);
2239 io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2240 io.generic.in.file.handle = fh;
2241 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2242 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2244 torture_assert(torture,
2245 ((io.basic_info.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2246 "compression attr after set");
2248 smb2_util_close(tree, fh);
2249 smb2_deltree(tree, DNAME);
2250 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2251 DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL,
2252 FILE_ATTRIBUTE_DIRECTORY);
2253 torture_assert(torture, ok, "setup compression directory");
2255 ZERO_STRUCT(io);
2256 io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2257 io.generic.in.file.handle = dirh;
2258 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2259 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2261 torture_assert(torture,
2262 ((io.basic_info.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2263 "compression attr before set");
2265 ZERO_STRUCT(set_io);
2266 set_io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2267 set_io.basic_info.in.file.handle = dirh;
2268 set_io.basic_info.in.create_time = io.basic_info.out.create_time;
2269 set_io.basic_info.in.access_time = io.basic_info.out.access_time;
2270 set_io.basic_info.in.write_time = io.basic_info.out.write_time;
2271 set_io.basic_info.in.change_time = io.basic_info.out.change_time;
2272 set_io.basic_info.in.attrib = (io.basic_info.out.attrib
2273 | FILE_ATTRIBUTE_COMPRESSED);
2274 status = smb2_setinfo_file(tree, &set_io);
2275 torture_assert_ntstatus_ok(torture, status, "SMB2_SETINFO_FILE");
2277 status = test_ioctl_compress_get(torture, tmp_ctx, tree, dirh,
2278 &compression_fmt);
2279 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2281 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2282 "dir compression set after SetInfo");
2284 smb2_util_close(tree, dirh);
2285 talloc_free(tmp_ctx);
2286 return true;
2289 static bool test_ioctl_compress_perms(struct torture_context *torture,
2290 struct smb2_tree *tree)
2292 struct smb2_handle fh;
2293 uint16_t compression_fmt;
2294 union smb_fileinfo io;
2295 NTSTATUS status;
2296 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2297 bool ok;
2299 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2300 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2301 FILE_ATTRIBUTE_NORMAL);
2302 torture_assert(torture, ok, "setup compression file");
2304 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
2305 &ok);
2306 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2307 smb2_util_close(tree, fh);
2308 if (!ok) {
2309 torture_skip(torture, "FS compression not supported\n");
2312 /* attempt get compression without READ_ATTR permission */
2313 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2314 FNAME, &fh, 0,
2315 (SEC_RIGHTS_FILE_READ & ~(SEC_FILE_READ_ATTRIBUTE
2316 | SEC_STD_READ_CONTROL
2317 | SEC_FILE_READ_EA)),
2318 FILE_ATTRIBUTE_NORMAL);
2319 torture_assert(torture, ok, "setup compression file");
2321 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2322 &compression_fmt);
2323 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2324 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2325 "compression set after create");
2326 smb2_util_close(tree, fh);
2328 /* set compression without WRITE_ATTR permission should succeed */
2329 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2330 FNAME, &fh, 0,
2331 (SEC_RIGHTS_FILE_WRITE & ~(SEC_FILE_WRITE_ATTRIBUTE
2332 | SEC_STD_WRITE_DAC
2333 | SEC_FILE_WRITE_EA)),
2334 FILE_ATTRIBUTE_NORMAL);
2335 torture_assert(torture, ok, "setup compression file");
2337 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
2338 COMPRESSION_FORMAT_DEFAULT);
2339 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
2340 smb2_util_close(tree, fh);
2342 ok = test_setup_open(torture, tree, tmp_ctx,
2343 FNAME, &fh, SEC_RIGHTS_FILE_ALL,
2344 FILE_ATTRIBUTE_NORMAL);
2345 torture_assert(torture, ok, "setup compression file");
2346 ZERO_STRUCT(io);
2347 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
2348 io.generic.in.file.handle = fh;
2349 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2350 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2352 torture_assert(torture,
2353 (io.all_info2.out.attrib & FILE_ATTRIBUTE_COMPRESSED),
2354 "incorrect compression attr");
2355 smb2_util_close(tree, fh);
2357 /* attempt get compression without READ_DATA permission */
2358 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2359 FNAME, &fh, 0,
2360 (SEC_RIGHTS_FILE_READ & ~SEC_FILE_READ_DATA),
2361 FILE_ATTRIBUTE_NORMAL);
2362 torture_assert(torture, ok, "setup compression file");
2364 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2365 &compression_fmt);
2366 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2367 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2368 "compression enabled after set");
2369 smb2_util_close(tree, fh);
2371 /* attempt get compression with only SYNCHRONIZE permission */
2372 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2373 FNAME, &fh, 0,
2374 SEC_STD_SYNCHRONIZE,
2375 FILE_ATTRIBUTE_NORMAL);
2376 torture_assert(torture, ok, "setup compression file");
2378 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2379 &compression_fmt);
2380 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2381 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2382 "compression not enabled after set");
2383 smb2_util_close(tree, fh);
2385 /* attempt to set compression without WRITE_DATA permission */
2386 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2387 FNAME, &fh, 0,
2388 (SEC_RIGHTS_FILE_WRITE & (~SEC_FILE_WRITE_DATA)),
2389 FILE_ATTRIBUTE_NORMAL);
2390 torture_assert(torture, ok, "setup compression file");
2392 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
2393 COMPRESSION_FORMAT_DEFAULT);
2394 torture_assert_ntstatus_equal(torture, status,
2395 NT_STATUS_ACCESS_DENIED,
2396 "FSCTL_SET_COMPRESSION permission");
2397 smb2_util_close(tree, fh);
2399 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2400 FNAME, &fh, 0,
2401 (SEC_RIGHTS_FILE_WRITE & (~SEC_FILE_WRITE_DATA)),
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_NONE);
2407 torture_assert_ntstatus_equal(torture, status,
2408 NT_STATUS_ACCESS_DENIED,
2409 "FSCTL_SET_COMPRESSION permission");
2410 smb2_util_close(tree, fh);
2412 talloc_free(tmp_ctx);
2413 return true;
2417 basic testing of the SMB2 FSCTL_QUERY_NETWORK_INTERFACE_INFO ioctl
2419 static bool test_ioctl_network_interface_info(struct torture_context *torture,
2420 struct smb2_tree *tree)
2422 union smb_ioctl ioctl;
2423 struct smb2_handle fh;
2424 NTSTATUS status;
2425 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2426 struct fsctl_net_iface_info net_iface;
2427 enum ndr_err_code ndr_ret;
2428 uint32_t caps;
2430 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
2431 if (!(caps & SMB2_CAP_MULTI_CHANNEL)) {
2432 torture_skip(torture, "server doesn't support SMB2_CAP_MULTI_CHANNEL\n");
2435 ZERO_STRUCT(ioctl);
2436 ioctl.smb2.level = RAW_IOCTL_SMB2;
2437 fh.data[0] = UINT64_MAX;
2438 fh.data[1] = UINT64_MAX;
2439 ioctl.smb2.in.file.handle = fh;
2440 ioctl.smb2.in.function = FSCTL_QUERY_NETWORK_INTERFACE_INFO;
2441 ioctl.smb2.in.max_response_size = 0x10000; /* Windows client sets this to 64KiB */
2442 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2444 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2445 torture_assert_ntstatus_ok(torture, status, "FSCTL_QUERY_NETWORK_INTERFACE_INFO");
2447 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, &net_iface,
2448 (ndr_pull_flags_fn_t)ndr_pull_fsctl_net_iface_info);
2449 torture_assert_ndr_success(torture, ndr_ret,
2450 "ndr_pull_fsctl_net_iface_info");
2452 ndr_print_debug((ndr_print_fn_t)ndr_print_fsctl_net_iface_info
2453 , "Network Interface Info", &net_iface);
2455 talloc_free(tmp_ctx);
2456 return true;
2459 static NTSTATUS test_ioctl_sparse_fs_supported(struct torture_context *torture,
2460 struct smb2_tree *tree,
2461 TALLOC_CTX *mem_ctx,
2462 struct smb2_handle *fh,
2463 bool *sparse_support)
2465 NTSTATUS status;
2466 union smb_fsinfo info;
2468 ZERO_STRUCT(info);
2469 info.generic.level = RAW_QFS_ATTRIBUTE_INFORMATION;
2470 info.generic.handle = *fh;
2471 status = smb2_getinfo_fs(tree, tree, &info);
2472 if (!NT_STATUS_IS_OK(status)) {
2473 return status;
2476 if (info.attribute_info.out.fs_attr & FILE_SUPPORTS_SPARSE_FILES) {
2477 *sparse_support = true;
2478 } else {
2479 *sparse_support = false;
2481 return NT_STATUS_OK;
2484 static NTSTATUS test_ioctl_sparse_req(struct torture_context *torture,
2485 TALLOC_CTX *mem_ctx,
2486 struct smb2_tree *tree,
2487 struct smb2_handle fh,
2488 bool set)
2490 union smb_ioctl ioctl;
2491 NTSTATUS status;
2492 uint8_t set_sparse;
2494 ZERO_STRUCT(ioctl);
2495 ioctl.smb2.level = RAW_IOCTL_SMB2;
2496 ioctl.smb2.in.file.handle = fh;
2497 ioctl.smb2.in.function = FSCTL_SET_SPARSE;
2498 ioctl.smb2.in.max_response_size = 0;
2499 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2500 set_sparse = (set ? 0xFF : 0x0);
2501 ioctl.smb2.in.out.data = &set_sparse;
2502 ioctl.smb2.in.out.length = sizeof(set_sparse);
2504 status = smb2_ioctl(tree, mem_ctx, &ioctl.smb2);
2505 return status;
2508 static NTSTATUS test_sparse_get(struct torture_context *torture,
2509 TALLOC_CTX *mem_ctx,
2510 struct smb2_tree *tree,
2511 struct smb2_handle fh,
2512 bool *_is_sparse)
2514 union smb_fileinfo io;
2515 NTSTATUS status;
2517 ZERO_STRUCT(io);
2518 io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2519 io.generic.in.file.handle = fh;
2520 status = smb2_getinfo_file(tree, mem_ctx, &io);
2521 if (!NT_STATUS_IS_OK(status)) {
2522 return status;
2524 *_is_sparse = !!(io.basic_info.out.attrib & FILE_ATTRIBUTE_SPARSE);
2526 return status;
2529 static bool test_ioctl_sparse_file_flag(struct torture_context *torture,
2530 struct smb2_tree *tree)
2532 struct smb2_handle fh;
2533 union smb_fileinfo io;
2534 NTSTATUS status;
2535 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2536 bool ok;
2537 bool is_sparse;
2539 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2540 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2541 FILE_ATTRIBUTE_NORMAL);
2542 torture_assert(torture, ok, "setup file");
2544 status = test_ioctl_sparse_fs_supported(torture, tree, tmp_ctx, &fh,
2545 &ok);
2546 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2547 if (!ok) {
2548 smb2_util_close(tree, fh);
2549 torture_skip(torture, "Sparse files not supported\n");
2552 ZERO_STRUCT(io);
2553 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
2554 io.generic.in.file.handle = fh;
2555 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2556 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2558 torture_assert(torture,
2559 ((io.all_info2.out.attrib & FILE_ATTRIBUTE_SPARSE) == 0),
2560 "sparse attr before set");
2562 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
2563 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2565 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2566 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2567 torture_assert(torture, is_sparse, "no sparse attr after set");
2569 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false);
2570 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2572 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2573 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2574 torture_assert(torture, !is_sparse, "sparse attr after unset");
2576 smb2_util_close(tree, fh);
2577 talloc_free(tmp_ctx);
2578 return true;
2581 static bool test_ioctl_sparse_file_attr(struct torture_context *torture,
2582 struct smb2_tree *tree)
2584 struct smb2_handle fh;
2585 NTSTATUS status;
2586 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2587 bool ok;
2588 bool is_sparse;
2590 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2591 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2592 (FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_SPARSE));
2593 torture_assert(torture, ok, "setup file");
2595 status = test_ioctl_sparse_fs_supported(torture, tree, tmp_ctx, &fh,
2596 &ok);
2597 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2598 if (!ok) {
2599 smb2_util_close(tree, fh);
2600 torture_skip(torture, "Sparse files not supported\n");
2603 status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2604 torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2605 torture_assert(torture, !is_sparse, "sparse attr on open");
2607 smb2_util_close(tree, fh);
2608 talloc_free(tmp_ctx);
2609 return true;
2613 basic testing of SMB2 ioctls
2615 struct torture_suite *torture_smb2_ioctl_init(void)
2617 struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "ioctl");
2619 torture_suite_add_1smb2_test(suite, "shadow_copy",
2620 test_ioctl_get_shadow_copy);
2621 torture_suite_add_1smb2_test(suite, "req_resume_key",
2622 test_ioctl_req_resume_key);
2623 torture_suite_add_1smb2_test(suite, "copy_chunk_simple",
2624 test_ioctl_copy_chunk_simple);
2625 torture_suite_add_1smb2_test(suite, "copy_chunk_multi",
2626 test_ioctl_copy_chunk_multi);
2627 torture_suite_add_1smb2_test(suite, "copy_chunk_tiny",
2628 test_ioctl_copy_chunk_tiny);
2629 torture_suite_add_1smb2_test(suite, "copy_chunk_overwrite",
2630 test_ioctl_copy_chunk_over);
2631 torture_suite_add_1smb2_test(suite, "copy_chunk_append",
2632 test_ioctl_copy_chunk_append);
2633 torture_suite_add_1smb2_test(suite, "copy_chunk_limits",
2634 test_ioctl_copy_chunk_limits);
2635 torture_suite_add_1smb2_test(suite, "copy_chunk_src_lock",
2636 test_ioctl_copy_chunk_src_lck);
2637 torture_suite_add_1smb2_test(suite, "copy_chunk_dest_lock",
2638 test_ioctl_copy_chunk_dest_lck);
2639 torture_suite_add_1smb2_test(suite, "copy_chunk_bad_key",
2640 test_ioctl_copy_chunk_bad_key);
2641 torture_suite_add_1smb2_test(suite, "copy_chunk_src_is_dest",
2642 test_ioctl_copy_chunk_src_is_dest);
2643 torture_suite_add_1smb2_test(suite, "copy_chunk_src_is_dest_overlap",
2644 test_ioctl_copy_chunk_src_is_dest_overlap);
2645 torture_suite_add_1smb2_test(suite, "copy_chunk_bad_access",
2646 test_ioctl_copy_chunk_bad_access);
2647 torture_suite_add_1smb2_test(suite, "copy_chunk_write_access",
2648 test_ioctl_copy_chunk_write_access);
2649 torture_suite_add_1smb2_test(suite, "copy_chunk_src_exceed",
2650 test_ioctl_copy_chunk_src_exceed);
2651 torture_suite_add_1smb2_test(suite, "copy_chunk_src_exceed_multi",
2652 test_ioctl_copy_chunk_src_exceed_multi);
2653 torture_suite_add_1smb2_test(suite, "copy_chunk_sparse_dest",
2654 test_ioctl_copy_chunk_sparse_dest);
2655 torture_suite_add_1smb2_test(suite, "copy_chunk_max_output_sz",
2656 test_ioctl_copy_chunk_max_output_sz);
2657 torture_suite_add_1smb2_test(suite, "copy_chunk_zero_length",
2658 test_ioctl_copy_chunk_zero_length);
2659 torture_suite_add_1smb2_test(suite, "compress_file_flag",
2660 test_ioctl_compress_file_flag);
2661 torture_suite_add_1smb2_test(suite, "compress_dir_inherit",
2662 test_ioctl_compress_dir_inherit);
2663 torture_suite_add_1smb2_test(suite, "compress_invalid_format",
2664 test_ioctl_compress_invalid_format);
2665 torture_suite_add_1smb2_test(suite, "compress_invalid_buf",
2666 test_ioctl_compress_invalid_buf);
2667 torture_suite_add_1smb2_test(suite, "compress_query_file_attr",
2668 test_ioctl_compress_query_file_attr);
2669 torture_suite_add_1smb2_test(suite, "compress_create_with_attr",
2670 test_ioctl_compress_create_with_attr);
2671 torture_suite_add_1smb2_test(suite, "compress_inherit_disable",
2672 test_ioctl_compress_inherit_disable);
2673 torture_suite_add_1smb2_test(suite, "compress_set_file_attr",
2674 test_ioctl_compress_set_file_attr);
2675 torture_suite_add_1smb2_test(suite, "compress_perms",
2676 test_ioctl_compress_perms);
2677 torture_suite_add_1smb2_test(suite, "network_interface_info",
2678 test_ioctl_network_interface_info);
2679 torture_suite_add_1smb2_test(suite, "sparse_file_flag",
2680 test_ioctl_sparse_file_flag);
2681 torture_suite_add_1smb2_test(suite, "sparse_file_attr",
2682 test_ioctl_sparse_file_attr);
2684 suite->description = talloc_strdup(suite, "SMB2-IOCTL tests");
2686 return suite;