param: replace P_OCTAL variable setting with s3 version which uses sscanf
[Samba.git] / source4 / torture / smb2 / ioctl.c
blob3ccd1d0e0f506a1deae55ebb96790709d857f315
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 "librpc/gen_ndr/ndr_ioctl.h"
30 #define FNAME "testfsctl.dat"
31 #define FNAME2 "testfsctl2.dat"
32 #define DNAME "testfsctl_dir"
35 basic testing of SMB2 shadow copy calls
37 static bool test_ioctl_get_shadow_copy(struct torture_context *torture,
38 struct smb2_tree *tree)
40 struct smb2_handle h;
41 uint8_t buf[100];
42 NTSTATUS status;
43 union smb_ioctl ioctl;
44 TALLOC_CTX *tmp_ctx = talloc_new(tree);
46 smb2_util_unlink(tree, FNAME);
48 status = torture_smb2_testfile(tree, FNAME, &h);
49 torture_assert_ntstatus_ok(torture, status, "create write");
51 ZERO_ARRAY(buf);
52 status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf));
53 torture_assert_ntstatus_ok(torture, status, "write");
55 ZERO_STRUCT(ioctl);
56 ioctl.smb2.level = RAW_IOCTL_SMB2;
57 ioctl.smb2.in.file.handle = h;
58 ioctl.smb2.in.function = FSCTL_SRV_ENUM_SNAPS;
59 ioctl.smb2.in.max_response_size = 16;
60 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
62 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
63 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)
64 || NT_STATUS_EQUAL(status, NT_STATUS_INVALID_DEVICE_REQUEST)) {
65 torture_skip(torture, "FSCTL_SRV_ENUM_SNAPS not supported\n");
67 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_ENUM_SNAPS");
69 return true;
73 basic testing of the SMB2 server side copy ioctls
75 static bool test_ioctl_req_resume_key(struct torture_context *torture,
76 struct smb2_tree *tree)
78 struct smb2_handle h;
79 uint8_t buf[100];
80 NTSTATUS status;
81 union smb_ioctl ioctl;
82 TALLOC_CTX *tmp_ctx = talloc_new(tree);
83 struct req_resume_key_rsp res_key;
84 enum ndr_err_code ndr_ret;
86 smb2_util_unlink(tree, FNAME);
88 status = torture_smb2_testfile(tree, FNAME, &h);
89 torture_assert_ntstatus_ok(torture, status, "create write");
91 ZERO_ARRAY(buf);
92 status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf));
93 torture_assert_ntstatus_ok(torture, status, "write");
95 ZERO_STRUCT(ioctl);
96 ioctl.smb2.level = RAW_IOCTL_SMB2;
97 ioctl.smb2.in.file.handle = h;
98 ioctl.smb2.in.function = FSCTL_SRV_REQUEST_RESUME_KEY;
99 ioctl.smb2.in.max_response_size = 32;
100 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
102 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
103 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_REQUEST_RESUME_KEY");
105 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, &res_key,
106 (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
107 torture_assert_ndr_success(torture, ndr_ret,
108 "ndr_pull_req_resume_key_rsp");
110 ndr_print_debug((ndr_print_fn_t)ndr_print_req_resume_key_rsp, "yo", &res_key);
112 talloc_free(tmp_ctx);
113 return true;
116 static uint64_t patt_hash(uint64_t off)
118 return off;
121 static bool check_pattern(struct torture_context *torture,
122 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
123 struct smb2_handle h, uint64_t off, uint64_t len,
124 uint64_t patt_off)
126 uint64_t i;
127 struct smb2_read r;
128 NTSTATUS status;
130 ZERO_STRUCT(r);
131 r.in.file.handle = h;
132 r.in.length = len;
133 r.in.offset = off;
134 status = smb2_read(tree, mem_ctx, &r);
135 torture_assert_ntstatus_ok(torture, status, "read");
137 torture_assert_u64_equal(torture, r.out.data.length, len,
138 "read data len mismatch");
140 for (i = 0; i <= len - 8; i += 8, patt_off += 8) {
141 uint64_t data = BVAL(r.out.data.data, i);
142 torture_assert_u64_equal(torture, data, patt_hash(patt_off),
143 talloc_asprintf(torture, "read data "
144 "pattern bad at %llu\n",
145 (unsigned long long)i));
148 talloc_free(r.out.data.data);
149 return true;
152 static bool test_setup_open(struct torture_context *torture,
153 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
154 const char *fname,
155 struct smb2_handle *fh,
156 uint32_t desired_access,
157 uint32_t file_attributes)
159 struct smb2_create io;
160 NTSTATUS status;
162 ZERO_STRUCT(io);
163 io.in.desired_access = desired_access;
164 io.in.file_attributes = file_attributes;
165 io.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
166 io.in.share_access =
167 NTCREATEX_SHARE_ACCESS_DELETE|
168 NTCREATEX_SHARE_ACCESS_READ|
169 NTCREATEX_SHARE_ACCESS_WRITE;
170 if (file_attributes & FILE_ATTRIBUTE_DIRECTORY) {
171 io.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
173 io.in.fname = fname;
175 status = smb2_create(tree, mem_ctx, &io);
176 torture_assert_ntstatus_ok(torture, status, "file create");
178 *fh = io.out.file.handle;
180 return true;
183 static bool test_setup_create_fill(struct torture_context *torture,
184 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
185 const char *fname,
186 struct smb2_handle *fh,
187 uint64_t size,
188 uint32_t desired_access,
189 uint32_t file_attributes)
191 NTSTATUS status;
192 bool ok;
193 uint64_t i;
194 uint8_t *buf = talloc_zero_size(mem_ctx, size);
195 torture_assert(torture, (buf != NULL), "no memory for file data buf");
197 smb2_util_unlink(tree, fname);
199 ok = test_setup_open(torture, tree, mem_ctx,
200 fname,
202 desired_access,
203 file_attributes);
204 torture_assert(torture, ok, "file open");
206 if (size > 0) {
207 uint64_t cur_off = 0;
208 for (i = 0; i <= size - 8; i += 8) {
209 SBVAL(buf, i, patt_hash(i));
211 while (size > 0) {
212 uint64_t io_sz = MIN(1024 * 1024, size);
213 status = smb2_util_write(tree, *fh,
214 buf + cur_off, cur_off, io_sz);
215 torture_assert_ntstatus_ok(torture, status, "file write");
217 size -= io_sz;
218 cur_off += io_sz;
221 return true;
224 static bool test_setup_copy_chunk(struct torture_context *torture,
225 struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
226 uint32_t nchunks,
227 struct smb2_handle *src_h,
228 uint64_t src_size,
229 uint32_t src_desired_access,
230 struct smb2_handle *dest_h,
231 uint64_t dest_size,
232 uint32_t dest_desired_access,
233 struct srv_copychunk_copy *cc_copy,
234 union smb_ioctl *ioctl)
236 struct req_resume_key_rsp res_key;
237 bool ok;
238 NTSTATUS status;
239 enum ndr_err_code ndr_ret;
241 ok = test_setup_create_fill(torture, tree, mem_ctx, FNAME,
242 src_h, src_size, src_desired_access,
243 FILE_ATTRIBUTE_NORMAL);
244 torture_assert(torture, ok, "src file create fill");
246 ok = test_setup_create_fill(torture, tree, mem_ctx, FNAME2,
247 dest_h, dest_size, dest_desired_access,
248 FILE_ATTRIBUTE_NORMAL);
249 torture_assert(torture, ok, "dest file create fill");
251 ZERO_STRUCTPN(ioctl);
252 ioctl->smb2.level = RAW_IOCTL_SMB2;
253 ioctl->smb2.in.file.handle = *src_h;
254 ioctl->smb2.in.function = FSCTL_SRV_REQUEST_RESUME_KEY;
255 /* Allow for Key + ContextLength + Context */
256 ioctl->smb2.in.max_response_size = 32;
257 ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
259 status = smb2_ioctl(tree, mem_ctx, &ioctl->smb2);
260 torture_assert_ntstatus_ok(torture, status,
261 "FSCTL_SRV_REQUEST_RESUME_KEY");
263 ndr_ret = ndr_pull_struct_blob(&ioctl->smb2.out.out, mem_ctx, &res_key,
264 (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
266 torture_assert_ndr_success(torture, ndr_ret,
267 "ndr_pull_req_resume_key_rsp");
269 ZERO_STRUCTPN(ioctl);
270 ioctl->smb2.level = RAW_IOCTL_SMB2;
271 ioctl->smb2.in.file.handle = *dest_h;
272 ioctl->smb2.in.function = FSCTL_SRV_COPYCHUNK;
273 ioctl->smb2.in.max_response_size = sizeof(struct srv_copychunk_rsp);
274 ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
276 ZERO_STRUCTPN(cc_copy);
277 memcpy(cc_copy->source_key, res_key.resume_key, ARRAY_SIZE(cc_copy->source_key));
278 cc_copy->chunk_count = nchunks;
279 cc_copy->chunks = talloc_zero_array(mem_ctx, struct srv_copychunk, nchunks);
280 torture_assert(torture, (cc_copy->chunks != NULL), "no memory for chunks");
282 return true;
286 static bool check_copy_chunk_rsp(struct torture_context *torture,
287 struct srv_copychunk_rsp *cc_rsp,
288 uint32_t ex_chunks_written,
289 uint32_t ex_chunk_bytes_written,
290 uint32_t ex_total_bytes_written)
292 torture_assert_int_equal(torture, cc_rsp->chunks_written,
293 ex_chunks_written, "num chunks");
294 torture_assert_int_equal(torture, cc_rsp->chunk_bytes_written,
295 ex_chunk_bytes_written, "chunk bytes written");
296 torture_assert_int_equal(torture, cc_rsp->total_bytes_written,
297 ex_total_bytes_written, "chunk total bytes");
298 return true;
301 static bool test_ioctl_copy_chunk_simple(struct torture_context *torture,
302 struct smb2_tree *tree)
304 struct smb2_handle src_h;
305 struct smb2_handle dest_h;
306 NTSTATUS status;
307 union smb_ioctl ioctl;
308 TALLOC_CTX *tmp_ctx = talloc_new(tree);
309 struct srv_copychunk_copy cc_copy;
310 struct srv_copychunk_rsp cc_rsp;
311 enum ndr_err_code ndr_ret;
312 bool ok;
314 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
315 1, /* 1 chunk */
316 &src_h, 4096, /* fill 4096 byte src file */
317 SEC_RIGHTS_FILE_ALL,
318 &dest_h, 0, /* 0 byte dest file */
319 SEC_RIGHTS_FILE_ALL,
320 &cc_copy,
321 &ioctl);
322 if (!ok) {
323 torture_fail(torture, "setup copy chunk error");
326 /* copy all src file data (via a single chunk desc) */
327 cc_copy.chunks[0].source_off = 0;
328 cc_copy.chunks[0].target_off = 0;
329 cc_copy.chunks[0].length = 4096;
331 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
332 &cc_copy,
333 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
334 torture_assert_ndr_success(torture, ndr_ret,
335 "ndr_push_srv_copychunk_copy");
337 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
338 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
340 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
341 &cc_rsp,
342 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
343 torture_assert_ndr_success(torture, ndr_ret,
344 "ndr_pull_srv_copychunk_rsp");
346 ok = check_copy_chunk_rsp(torture, &cc_rsp,
347 1, /* chunks written */
348 0, /* chunk bytes unsuccessfully written */
349 4096); /* total bytes written */
350 if (!ok) {
351 torture_fail(torture, "bad copy chunk response data");
354 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
355 if (!ok) {
356 torture_fail(torture, "inconsistent file data");
359 smb2_util_close(tree, src_h);
360 smb2_util_close(tree, dest_h);
361 talloc_free(tmp_ctx);
362 return true;
365 static bool test_ioctl_copy_chunk_multi(struct torture_context *torture,
366 struct smb2_tree *tree)
368 struct smb2_handle src_h;
369 struct smb2_handle dest_h;
370 NTSTATUS status;
371 union smb_ioctl ioctl;
372 TALLOC_CTX *tmp_ctx = talloc_new(tree);
373 struct srv_copychunk_copy cc_copy;
374 struct srv_copychunk_rsp cc_rsp;
375 enum ndr_err_code ndr_ret;
376 bool ok;
378 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
379 2, /* chunks */
380 &src_h, 8192, /* src file */
381 SEC_RIGHTS_FILE_ALL,
382 &dest_h, 0, /* dest file */
383 SEC_RIGHTS_FILE_ALL,
384 &cc_copy,
385 &ioctl);
386 if (!ok) {
387 torture_fail(torture, "setup copy chunk error");
390 /* copy all src file data via two chunks */
391 cc_copy.chunks[0].source_off = 0;
392 cc_copy.chunks[0].target_off = 0;
393 cc_copy.chunks[0].length = 4096;
395 cc_copy.chunks[1].source_off = 4096;
396 cc_copy.chunks[1].target_off = 4096;
397 cc_copy.chunks[1].length = 4096;
399 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
400 &cc_copy,
401 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
402 torture_assert_ndr_success(torture, ndr_ret,
403 "ndr_push_srv_copychunk_copy");
405 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
406 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
408 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
409 &cc_rsp,
410 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
411 torture_assert_ndr_success(torture, ndr_ret,
412 "ndr_pull_srv_copychunk_rsp");
414 ok = check_copy_chunk_rsp(torture, &cc_rsp,
415 2, /* chunks written */
416 0, /* chunk bytes unsuccessfully written */
417 8192); /* total bytes written */
418 if (!ok) {
419 torture_fail(torture, "bad copy chunk response data");
422 smb2_util_close(tree, src_h);
423 smb2_util_close(tree, dest_h);
424 talloc_free(tmp_ctx);
425 return true;
428 static bool test_ioctl_copy_chunk_tiny(struct torture_context *torture,
429 struct smb2_tree *tree)
431 struct smb2_handle src_h;
432 struct smb2_handle dest_h;
433 NTSTATUS status;
434 union smb_ioctl ioctl;
435 TALLOC_CTX *tmp_ctx = talloc_new(tree);
436 struct srv_copychunk_copy cc_copy;
437 struct srv_copychunk_rsp cc_rsp;
438 enum ndr_err_code ndr_ret;
439 bool ok;
441 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
442 2, /* chunks */
443 &src_h, 100, /* src file */
444 SEC_RIGHTS_FILE_ALL,
445 &dest_h, 0, /* dest file */
446 SEC_RIGHTS_FILE_ALL,
447 &cc_copy,
448 &ioctl);
449 if (!ok) {
450 torture_fail(torture, "setup copy chunk error");
453 /* copy all src file data via two chunks, sub block size chunks */
454 cc_copy.chunks[0].source_off = 0;
455 cc_copy.chunks[0].target_off = 0;
456 cc_copy.chunks[0].length = 50;
458 cc_copy.chunks[1].source_off = 50;
459 cc_copy.chunks[1].target_off = 50;
460 cc_copy.chunks[1].length = 50;
462 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
463 &cc_copy,
464 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
465 torture_assert_ndr_success(torture, ndr_ret,
466 "ndr_push_srv_copychunk_copy");
468 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
469 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
471 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
472 &cc_rsp,
473 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
474 torture_assert_ndr_success(torture, ndr_ret,
475 "ndr_pull_srv_copychunk_rsp");
477 ok = check_copy_chunk_rsp(torture, &cc_rsp,
478 2, /* chunks written */
479 0, /* chunk bytes unsuccessfully written */
480 100); /* total bytes written */
481 if (!ok) {
482 torture_fail(torture, "bad copy chunk response data");
485 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 100, 0);
486 if (!ok) {
487 torture_fail(torture, "inconsistent file data");
490 smb2_util_close(tree, src_h);
491 smb2_util_close(tree, dest_h);
492 talloc_free(tmp_ctx);
493 return true;
496 static bool test_ioctl_copy_chunk_over(struct torture_context *torture,
497 struct smb2_tree *tree)
499 struct smb2_handle src_h;
500 struct smb2_handle dest_h;
501 NTSTATUS status;
502 union smb_ioctl ioctl;
503 TALLOC_CTX *tmp_ctx = talloc_new(tree);
504 struct srv_copychunk_copy cc_copy;
505 struct srv_copychunk_rsp cc_rsp;
506 enum ndr_err_code ndr_ret;
507 bool ok;
509 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
510 2, /* chunks */
511 &src_h, 8192, /* src file */
512 SEC_RIGHTS_FILE_ALL,
513 &dest_h, 4096, /* dest file */
514 SEC_RIGHTS_FILE_ALL,
515 &cc_copy,
516 &ioctl);
517 if (!ok) {
518 torture_fail(torture, "setup copy chunk error");
521 /* first chunk overwrites existing dest data */
522 cc_copy.chunks[0].source_off = 0;
523 cc_copy.chunks[0].target_off = 0;
524 cc_copy.chunks[0].length = 4096;
526 /* second chunk overwrites the first */
527 cc_copy.chunks[1].source_off = 4096;
528 cc_copy.chunks[1].target_off = 0;
529 cc_copy.chunks[1].length = 4096;
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 8192); /* 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, 4096, 4096);
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_append(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, 4096, /* src file */
581 SEC_RIGHTS_FILE_ALL,
582 &dest_h, 0, /* dest file */
583 SEC_RIGHTS_FILE_ALL,
584 &cc_copy,
585 &ioctl);
586 if (!ok) {
587 torture_fail(torture, "setup copy chunk error");
590 cc_copy.chunks[0].source_off = 0;
591 cc_copy.chunks[0].target_off = 0;
592 cc_copy.chunks[0].length = 4096;
594 /* second chunk appends the same data to the first */
595 cc_copy.chunks[1].source_off = 0;
596 cc_copy.chunks[1].target_off = 4096;
597 cc_copy.chunks[1].length = 4096;
599 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
600 &cc_copy,
601 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
602 torture_assert_ndr_success(torture, ndr_ret,
603 "ndr_push_srv_copychunk_copy");
605 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
606 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
608 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
609 &cc_rsp,
610 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
611 torture_assert_ndr_success(torture, ndr_ret,
612 "ndr_pull_srv_copychunk_rsp");
614 ok = check_copy_chunk_rsp(torture, &cc_rsp,
615 2, /* chunks written */
616 0, /* chunk bytes unsuccessfully written */
617 8192); /* total bytes written */
618 if (!ok) {
619 torture_fail(torture, "bad copy chunk response data");
622 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
623 if (!ok) {
624 torture_fail(torture, "inconsistent file data");
627 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 4096, 4096, 0);
628 if (!ok) {
629 torture_fail(torture, "inconsistent file data");
632 smb2_util_close(tree, src_h);
633 smb2_util_close(tree, dest_h);
634 talloc_free(tmp_ctx);
635 return true;
638 static bool test_ioctl_copy_chunk_limits(struct torture_context *torture,
639 struct smb2_tree *tree)
641 struct smb2_handle src_h;
642 struct smb2_handle dest_h;
643 NTSTATUS status;
644 union smb_ioctl ioctl;
645 TALLOC_CTX *tmp_ctx = talloc_new(tree);
646 struct srv_copychunk_copy cc_copy;
647 struct srv_copychunk_rsp cc_rsp;
648 enum ndr_err_code ndr_ret;
649 bool ok;
651 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
652 1, /* chunks */
653 &src_h, 4096, /* src file */
654 SEC_RIGHTS_FILE_ALL,
655 &dest_h, 0, /* dest file */
656 SEC_RIGHTS_FILE_ALL,
657 &cc_copy,
658 &ioctl);
659 if (!ok) {
660 torture_fail(torture, "setup copy chunk error");
663 /* send huge chunk length request */
664 cc_copy.chunks[0].source_off = 0;
665 cc_copy.chunks[0].target_off = 0;
666 cc_copy.chunks[0].length = UINT_MAX;
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, "marshalling request");
673 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
674 torture_assert_ntstatus_equal(torture, status,
675 NT_STATUS_INVALID_PARAMETER,
676 "bad oversize chunk response");
678 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
679 &cc_rsp,
680 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
681 torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
683 torture_comment(torture, "limit max chunks, got %u\n",
684 cc_rsp.chunks_written);
685 torture_comment(torture, "limit max chunk len, got %u\n",
686 cc_rsp.chunk_bytes_written);
687 torture_comment(torture, "limit max total bytes, got %u\n",
688 cc_rsp.total_bytes_written);
690 smb2_util_close(tree, src_h);
691 smb2_util_close(tree, dest_h);
692 talloc_free(tmp_ctx);
693 return true;
696 static bool test_ioctl_copy_chunk_src_lck(struct torture_context *torture,
697 struct smb2_tree *tree)
699 struct smb2_handle src_h;
700 struct smb2_handle src_h2;
701 struct smb2_handle dest_h;
702 NTSTATUS status;
703 union smb_ioctl ioctl;
704 TALLOC_CTX *tmp_ctx = talloc_new(tree);
705 struct srv_copychunk_copy cc_copy;
706 struct srv_copychunk_rsp cc_rsp;
707 enum ndr_err_code ndr_ret;
708 bool ok;
709 struct smb2_lock lck;
710 struct smb2_lock_element el[1];
712 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
713 1, /* chunks */
714 &src_h, 4096, /* src file */
715 SEC_RIGHTS_FILE_ALL,
716 &dest_h, 0, /* dest file */
717 SEC_RIGHTS_FILE_ALL,
718 &cc_copy,
719 &ioctl);
720 if (!ok) {
721 torture_fail(torture, "setup copy chunk error");
724 cc_copy.chunks[0].source_off = 0;
725 cc_copy.chunks[0].target_off = 0;
726 cc_copy.chunks[0].length = 4096;
728 /* open and lock the copychunk src file */
729 status = torture_smb2_testfile(tree, FNAME, &src_h2);
730 torture_assert_ntstatus_ok(torture, status, "2nd src open");
732 lck.in.lock_count = 0x0001;
733 lck.in.lock_sequence = 0x00000000;
734 lck.in.file.handle = src_h2;
735 lck.in.locks = el;
736 el[0].offset = cc_copy.chunks[0].source_off;
737 el[0].length = cc_copy.chunks[0].length;
738 el[0].reserved = 0;
739 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
741 status = smb2_lock(tree, &lck);
742 torture_assert_ntstatus_ok(torture, status, "lock");
744 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
745 &cc_copy,
746 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
747 torture_assert_ndr_success(torture, ndr_ret,
748 "ndr_push_srv_copychunk_copy");
750 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
752 * 2k12 & Samba return lock_conflict, Windows 7 & 2k8 return success...
754 * Edgar Olougouna @ MS wrote:
755 * Regarding the FSCTL_SRV_COPYCHUNK and STATUS_FILE_LOCK_CONFLICT
756 * discrepancy observed between Windows versions, we confirm that the
757 * behavior change is expected.
759 * CopyChunk in Windows Server 2012 use regular Readfile/Writefile APIs
760 * to move the chunks from the source to the destination.
761 * These ReadFile/WriteFile APIs go through the byte-range lock checks,
762 * and this explains the observed STATUS_FILE_LOCK_CONFLICT error.
764 * Prior to Windows Server 2012, CopyChunk used mapped sections to move
765 * the data. And byte range locks are not enforced on mapped I/O, and
766 * this explains the STATUS_SUCCESS observed on Windows Server 2008 R2.
768 torture_assert_ntstatus_equal(torture, status,
769 NT_STATUS_FILE_LOCK_CONFLICT,
770 "FSCTL_SRV_COPYCHUNK locked");
772 /* should get cc response data with the lock conflict status */
773 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
774 &cc_rsp,
775 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
776 torture_assert_ndr_success(torture, ndr_ret,
777 "ndr_pull_srv_copychunk_rsp");
778 ok = check_copy_chunk_rsp(torture, &cc_rsp,
779 0, /* chunks written */
780 0, /* chunk bytes unsuccessfully written */
781 0); /* total bytes written */
783 lck.in.lock_count = 0x0001;
784 lck.in.lock_sequence = 0x00000001;
785 lck.in.file.handle = src_h2;
786 lck.in.locks = el;
787 el[0].offset = cc_copy.chunks[0].source_off;
788 el[0].length = cc_copy.chunks[0].length;
789 el[0].reserved = 0;
790 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
791 status = smb2_lock(tree, &lck);
792 torture_assert_ntstatus_ok(torture, status, "unlock");
794 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
795 torture_assert_ntstatus_ok(torture, status,
796 "FSCTL_SRV_COPYCHUNK unlocked");
798 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
799 &cc_rsp,
800 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
801 torture_assert_ndr_success(torture, ndr_ret,
802 "ndr_pull_srv_copychunk_rsp");
804 ok = check_copy_chunk_rsp(torture, &cc_rsp,
805 1, /* chunks written */
806 0, /* chunk bytes unsuccessfully written */
807 4096); /* total bytes written */
808 if (!ok) {
809 torture_fail(torture, "bad copy chunk response data");
812 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
813 if (!ok) {
814 torture_fail(torture, "inconsistent file data");
817 smb2_util_close(tree, src_h2);
818 smb2_util_close(tree, src_h);
819 smb2_util_close(tree, dest_h);
820 talloc_free(tmp_ctx);
821 return true;
824 static bool test_ioctl_copy_chunk_dest_lck(struct torture_context *torture,
825 struct smb2_tree *tree)
827 struct smb2_handle src_h;
828 struct smb2_handle dest_h;
829 struct smb2_handle dest_h2;
830 NTSTATUS status;
831 union smb_ioctl ioctl;
832 TALLOC_CTX *tmp_ctx = talloc_new(tree);
833 struct srv_copychunk_copy cc_copy;
834 struct srv_copychunk_rsp cc_rsp;
835 enum ndr_err_code ndr_ret;
836 bool ok;
837 struct smb2_lock lck;
838 struct smb2_lock_element el[1];
840 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
841 1, /* chunks */
842 &src_h, 4096, /* src file */
843 SEC_RIGHTS_FILE_ALL,
844 &dest_h, 4096, /* dest file */
845 SEC_RIGHTS_FILE_ALL,
846 &cc_copy,
847 &ioctl);
848 if (!ok) {
849 torture_fail(torture, "setup copy chunk error");
852 cc_copy.chunks[0].source_off = 0;
853 cc_copy.chunks[0].target_off = 0;
854 cc_copy.chunks[0].length = 4096;
856 /* open and lock the copychunk dest file */
857 status = torture_smb2_testfile(tree, FNAME2, &dest_h2);
858 torture_assert_ntstatus_ok(torture, status, "2nd src open");
860 lck.in.lock_count = 0x0001;
861 lck.in.lock_sequence = 0x00000000;
862 lck.in.file.handle = dest_h2;
863 lck.in.locks = el;
864 el[0].offset = cc_copy.chunks[0].target_off;
865 el[0].length = cc_copy.chunks[0].length;
866 el[0].reserved = 0;
867 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
869 status = smb2_lock(tree, &lck);
870 torture_assert_ntstatus_ok(torture, status, "lock");
872 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
873 &cc_copy,
874 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
875 torture_assert_ndr_success(torture, ndr_ret,
876 "ndr_push_srv_copychunk_copy");
878 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
879 torture_assert_ntstatus_equal(torture, status,
880 NT_STATUS_FILE_LOCK_CONFLICT,
881 "FSCTL_SRV_COPYCHUNK locked");
883 lck.in.lock_count = 0x0001;
884 lck.in.lock_sequence = 0x00000001;
885 lck.in.file.handle = dest_h2;
886 lck.in.locks = el;
887 el[0].offset = cc_copy.chunks[0].target_off;
888 el[0].length = cc_copy.chunks[0].length;
889 el[0].reserved = 0;
890 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
891 status = smb2_lock(tree, &lck);
892 torture_assert_ntstatus_ok(torture, status, "unlock");
894 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
895 torture_assert_ntstatus_ok(torture, status,
896 "FSCTL_SRV_COPYCHUNK unlocked");
898 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
899 &cc_rsp,
900 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
901 torture_assert_ndr_success(torture, ndr_ret,
902 "ndr_pull_srv_copychunk_rsp");
904 ok = check_copy_chunk_rsp(torture, &cc_rsp,
905 1, /* chunks written */
906 0, /* chunk bytes unsuccessfully written */
907 4096); /* total bytes written */
908 if (!ok) {
909 torture_fail(torture, "bad copy chunk response data");
912 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
913 if (!ok) {
914 torture_fail(torture, "inconsistent file data");
917 smb2_util_close(tree, dest_h2);
918 smb2_util_close(tree, src_h);
919 smb2_util_close(tree, dest_h);
920 talloc_free(tmp_ctx);
921 return true;
924 static bool test_ioctl_copy_chunk_bad_key(struct torture_context *torture,
925 struct smb2_tree *tree)
927 struct smb2_handle src_h;
928 struct smb2_handle dest_h;
929 NTSTATUS status;
930 union smb_ioctl ioctl;
931 TALLOC_CTX *tmp_ctx = talloc_new(tree);
932 struct srv_copychunk_copy cc_copy;
933 enum ndr_err_code ndr_ret;
934 bool ok;
936 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
938 &src_h, 4096,
939 SEC_RIGHTS_FILE_ALL,
940 &dest_h, 0,
941 SEC_RIGHTS_FILE_ALL,
942 &cc_copy,
943 &ioctl);
944 if (!ok) {
945 torture_fail(torture, "setup copy chunk error");
948 /* overwrite the resume key with a bogus value */
949 memcpy(cc_copy.source_key, "deadbeefdeadbeefdeadbeef", 24);
951 cc_copy.chunks[0].source_off = 0;
952 cc_copy.chunks[0].target_off = 0;
953 cc_copy.chunks[0].length = 4096;
955 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
956 &cc_copy,
957 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
958 torture_assert_ndr_success(torture, ndr_ret,
959 "ndr_push_srv_copychunk_copy");
961 /* Server 2k12 returns NT_STATUS_OBJECT_NAME_NOT_FOUND */
962 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
963 torture_assert_ntstatus_equal(torture, status,
964 NT_STATUS_OBJECT_NAME_NOT_FOUND,
965 "FSCTL_SRV_COPYCHUNK");
967 smb2_util_close(tree, src_h);
968 smb2_util_close(tree, dest_h);
969 talloc_free(tmp_ctx);
970 return true;
973 static bool test_ioctl_copy_chunk_src_is_dest(struct torture_context *torture,
974 struct smb2_tree *tree)
976 struct smb2_handle src_h;
977 struct smb2_handle dest_h;
978 NTSTATUS status;
979 union smb_ioctl ioctl;
980 TALLOC_CTX *tmp_ctx = talloc_new(tree);
981 struct srv_copychunk_copy cc_copy;
982 struct srv_copychunk_rsp cc_rsp;
983 enum ndr_err_code ndr_ret;
984 bool ok;
986 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
988 &src_h, 8192,
989 SEC_RIGHTS_FILE_ALL,
990 &dest_h, 0,
991 SEC_RIGHTS_FILE_ALL,
992 &cc_copy,
993 &ioctl);
994 if (!ok) {
995 torture_fail(torture, "setup copy chunk error");
998 /* the source is also the destination */
999 ioctl.smb2.in.file.handle = src_h;
1001 /* non-overlapping */
1002 cc_copy.chunks[0].source_off = 0;
1003 cc_copy.chunks[0].target_off = 4096;
1004 cc_copy.chunks[0].length = 4096;
1006 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1007 &cc_copy,
1008 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1009 torture_assert_ndr_success(torture, ndr_ret,
1010 "ndr_push_srv_copychunk_copy");
1012 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1013 torture_assert_ntstatus_ok(torture, status,
1014 "FSCTL_SRV_COPYCHUNK");
1016 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1017 &cc_rsp,
1018 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1019 torture_assert_ndr_success(torture, ndr_ret,
1020 "ndr_pull_srv_copychunk_rsp");
1022 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1023 1, /* chunks written */
1024 0, /* chunk bytes unsuccessfully written */
1025 4096); /* total bytes written */
1026 if (!ok) {
1027 torture_fail(torture, "bad copy chunk response data");
1030 ok = check_pattern(torture, tree, tmp_ctx, src_h, 0, 4096, 0);
1031 if (!ok) {
1032 torture_fail(torture, "inconsistent file data");
1034 ok = check_pattern(torture, tree, tmp_ctx, src_h, 4096, 4096, 0);
1035 if (!ok) {
1036 torture_fail(torture, "inconsistent file data");
1039 smb2_util_close(tree, src_h);
1040 smb2_util_close(tree, dest_h);
1041 talloc_free(tmp_ctx);
1042 return true;
1046 * Test a single-chunk copychunk request, where the source and target ranges
1047 * overlap, and the SourceKey refers to the same target file. E.g:
1049 * Initial State
1050 * -------------
1051 * File: src_and_dest
1052 * Offset: 0123456789
1053 * Data: abcdefghij
1055 * Request
1056 * -------
1057 * FSCTL_SRV_COPYCHUNK(src_and_dest)
1058 * SourceKey = SRV_REQUEST_RESUME_KEY(src_and_dest)
1059 * ChunkCount = 1
1060 * Chunks[0].SourceOffset = 0
1061 * Chunks[0].TargetOffset = 4
1062 * Chunks[0].Length = 6
1064 * Resultant State
1065 * ---------------
1066 * File: src_and_dest
1067 * Offset: 0123456789
1068 * Data: abcdabcdef
1070 * The resultant contents of src_and_dest is dependent on the server's
1071 * copy algorithm. In the above example, the server uses an IO buffer
1072 * large enough to hold the entire six-byte source data before writing
1073 * to TargetOffset. If the server were to use a four-byte IO buffer and
1074 * started reads/writes from the lowest offset, then the two overlapping
1075 * bytes in the above example would be overwritten before being read. The
1076 * resultant file contents would be abcdabcdab.
1078 * Windows 2008r2 appears to use a 2048 byte copy buffer, overlapping bytes
1079 * after this offset are written before being read. Windows 2012 on the
1080 * other hand appears to use a buffer large enough to hold its maximum
1081 * supported chunk size (1M). Samba currently uses a 64k copy buffer by
1082 * default (vfs_cc_state.buf).
1084 * This test uses an 8-byte overlap at 2040-2048, so that it passes against
1085 * Windows 2008r2, 2012 and Samba servers. Note, 2008GM fails, as it appears
1086 * to use a different copy algorithm to 2008r2.
1088 static bool
1089 test_ioctl_copy_chunk_src_is_dest_overlap(struct torture_context *torture,
1090 struct smb2_tree *tree)
1092 struct smb2_handle src_h;
1093 struct smb2_handle dest_h;
1094 NTSTATUS status;
1095 union smb_ioctl ioctl;
1096 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1097 struct srv_copychunk_copy cc_copy;
1098 struct srv_copychunk_rsp cc_rsp;
1099 enum ndr_err_code ndr_ret;
1100 bool ok;
1102 /* exceed the vfs_default copy buffer */
1103 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1105 &src_h, 2048 * 2,
1106 SEC_RIGHTS_FILE_ALL,
1107 &dest_h, 0,
1108 SEC_RIGHTS_FILE_ALL,
1109 &cc_copy,
1110 &ioctl);
1111 if (!ok) {
1112 torture_fail(torture, "setup copy chunk error");
1115 /* the source is also the destination */
1116 ioctl.smb2.in.file.handle = src_h;
1118 /* 8 bytes overlap between source and target ranges */
1119 cc_copy.chunks[0].source_off = 0;
1120 cc_copy.chunks[0].target_off = 2048 - 8;
1121 cc_copy.chunks[0].length = 2048;
1123 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1124 &cc_copy,
1125 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1126 torture_assert_ndr_success(torture, ndr_ret,
1127 "ndr_push_srv_copychunk_copy");
1129 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1130 torture_assert_ntstatus_ok(torture, status,
1131 "FSCTL_SRV_COPYCHUNK");
1133 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1134 &cc_rsp,
1135 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1136 torture_assert_ndr_success(torture, ndr_ret,
1137 "ndr_pull_srv_copychunk_rsp");
1139 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1140 1, /* chunks written */
1141 0, /* chunk bytes unsuccessfully written */
1142 2048); /* total bytes written */
1143 if (!ok) {
1144 torture_fail(torture, "bad copy chunk response data");
1147 ok = check_pattern(torture, tree, tmp_ctx, src_h, 0, 2048 - 8, 0);
1148 if (!ok) {
1149 torture_fail(torture, "inconsistent file data");
1151 ok = check_pattern(torture, tree, tmp_ctx, src_h, 2048 - 8, 2048, 0);
1152 if (!ok) {
1153 torture_fail(torture, "inconsistent file data");
1156 smb2_util_close(tree, src_h);
1157 smb2_util_close(tree, dest_h);
1158 talloc_free(tmp_ctx);
1159 return true;
1162 static bool test_ioctl_copy_chunk_bad_access(struct torture_context *torture,
1163 struct smb2_tree *tree)
1165 struct smb2_handle src_h;
1166 struct smb2_handle dest_h;
1167 NTSTATUS status;
1168 union smb_ioctl ioctl;
1169 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1170 struct srv_copychunk_copy cc_copy;
1171 enum ndr_err_code ndr_ret;
1172 bool ok;
1174 /* no read permission on src */
1175 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1176 1, /* 1 chunk */
1177 &src_h, 4096, /* fill 4096 byte src file */
1178 SEC_RIGHTS_FILE_WRITE,
1179 &dest_h, 0, /* 0 byte dest file */
1180 SEC_RIGHTS_FILE_ALL,
1181 &cc_copy,
1182 &ioctl);
1183 if (!ok) {
1184 torture_fail(torture, "setup copy chunk error");
1187 cc_copy.chunks[0].source_off = 0;
1188 cc_copy.chunks[0].target_off = 0;
1189 cc_copy.chunks[0].length = 4096;
1191 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1192 &cc_copy,
1193 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1194 torture_assert_ndr_success(torture, ndr_ret,
1195 "ndr_push_srv_copychunk_copy");
1197 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1198 torture_assert_ntstatus_equal(torture, status,
1199 NT_STATUS_ACCESS_DENIED,
1200 "FSCTL_SRV_COPYCHUNK");
1202 smb2_util_close(tree, src_h);
1203 smb2_util_close(tree, dest_h);
1205 /* no write permission on dest */
1206 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1207 1, /* 1 chunk */
1208 &src_h, 4096, /* fill 4096 byte src file */
1209 SEC_RIGHTS_FILE_ALL,
1210 &dest_h, 0, /* 0 byte dest file */
1211 (SEC_RIGHTS_FILE_READ
1212 | SEC_RIGHTS_FILE_EXECUTE),
1213 &cc_copy,
1214 &ioctl);
1215 if (!ok) {
1216 torture_fail(torture, "setup copy chunk error");
1219 cc_copy.chunks[0].source_off = 0;
1220 cc_copy.chunks[0].target_off = 0;
1221 cc_copy.chunks[0].length = 4096;
1223 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1224 &cc_copy,
1225 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1226 torture_assert_ndr_success(torture, ndr_ret,
1227 "ndr_push_srv_copychunk_copy");
1229 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1230 torture_assert_ntstatus_equal(torture, status,
1231 NT_STATUS_ACCESS_DENIED,
1232 "FSCTL_SRV_COPYCHUNK");
1234 smb2_util_close(tree, src_h);
1235 smb2_util_close(tree, dest_h);
1237 /* no read permission on dest */
1238 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1239 1, /* 1 chunk */
1240 &src_h, 4096, /* fill 4096 byte src file */
1241 SEC_RIGHTS_FILE_ALL,
1242 &dest_h, 0, /* 0 byte dest file */
1243 (SEC_RIGHTS_FILE_WRITE
1244 | SEC_RIGHTS_FILE_EXECUTE),
1245 &cc_copy,
1246 &ioctl);
1247 if (!ok) {
1248 torture_fail(torture, "setup copy chunk error");
1251 cc_copy.chunks[0].source_off = 0;
1252 cc_copy.chunks[0].target_off = 0;
1253 cc_copy.chunks[0].length = 4096;
1255 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1256 &cc_copy,
1257 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1258 torture_assert_ndr_success(torture, ndr_ret,
1259 "ndr_push_srv_copychunk_copy");
1262 * FSCTL_SRV_COPYCHUNK requires read permission on dest,
1263 * FSCTL_SRV_COPYCHUNK_WRITE on the other hand does not.
1265 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1266 torture_assert_ntstatus_equal(torture, status,
1267 NT_STATUS_ACCESS_DENIED,
1268 "FSCTL_SRV_COPYCHUNK");
1270 smb2_util_close(tree, src_h);
1271 smb2_util_close(tree, dest_h);
1272 talloc_free(tmp_ctx);
1274 return true;
1277 static bool test_ioctl_copy_chunk_write_access(struct torture_context *torture,
1278 struct smb2_tree *tree)
1280 struct smb2_handle src_h;
1281 struct smb2_handle dest_h;
1282 NTSTATUS status;
1283 union smb_ioctl ioctl;
1284 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1285 struct srv_copychunk_copy cc_copy;
1286 enum ndr_err_code ndr_ret;
1287 bool ok;
1289 /* no read permission on dest with FSCTL_SRV_COPYCHUNK_WRITE */
1290 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1291 1, /* 1 chunk */
1292 &src_h, 4096, /* fill 4096 byte src file */
1293 SEC_RIGHTS_FILE_ALL,
1294 &dest_h, 0, /* 0 byte dest file */
1295 (SEC_RIGHTS_FILE_WRITE
1296 | SEC_RIGHTS_FILE_EXECUTE),
1297 &cc_copy,
1298 &ioctl);
1299 if (!ok) {
1300 torture_fail(torture, "setup copy chunk error");
1303 ioctl.smb2.in.function = FSCTL_SRV_COPYCHUNK_WRITE;
1304 cc_copy.chunks[0].source_off = 0;
1305 cc_copy.chunks[0].target_off = 0;
1306 cc_copy.chunks[0].length = 4096;
1308 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1309 &cc_copy,
1310 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1311 torture_assert_ndr_success(torture, ndr_ret,
1312 "ndr_push_srv_copychunk_copy");
1314 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1315 torture_assert_ntstatus_ok(torture, status,
1316 "FSCTL_SRV_COPYCHUNK_WRITE");
1318 smb2_util_close(tree, src_h);
1319 smb2_util_close(tree, dest_h);
1320 talloc_free(tmp_ctx);
1322 return true;
1325 static bool test_ioctl_copy_chunk_src_exceed(struct torture_context *torture,
1326 struct smb2_tree *tree)
1328 struct smb2_handle src_h;
1329 struct smb2_handle dest_h;
1330 NTSTATUS status;
1331 union smb_ioctl ioctl;
1332 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1333 struct srv_copychunk_copy cc_copy;
1334 struct srv_copychunk_rsp cc_rsp;
1335 enum ndr_err_code ndr_ret;
1336 bool ok;
1338 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1339 1, /* 1 chunk */
1340 &src_h, 4096, /* fill 4096 byte src file */
1341 SEC_RIGHTS_FILE_ALL,
1342 &dest_h, 0, /* 0 byte dest file */
1343 SEC_RIGHTS_FILE_ALL,
1344 &cc_copy,
1345 &ioctl);
1346 if (!ok) {
1347 torture_fail(torture, "setup copy chunk error");
1350 /* Request copy where off + length exceeds size of src */
1351 cc_copy.chunks[0].source_off = 1024;
1352 cc_copy.chunks[0].target_off = 0;
1353 cc_copy.chunks[0].length = 4096;
1355 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1356 &cc_copy,
1357 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1358 torture_assert_ndr_success(torture, ndr_ret,
1359 "ndr_push_srv_copychunk_copy");
1361 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1362 torture_assert_ntstatus_equal(torture, status,
1363 NT_STATUS_INVALID_VIEW_SIZE,
1364 "FSCTL_SRV_COPYCHUNK oversize");
1366 /* Request copy where length exceeds size of src */
1367 cc_copy.chunks[0].source_off = 1024;
1368 cc_copy.chunks[0].target_off = 0;
1369 cc_copy.chunks[0].length = 3072;
1371 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1372 &cc_copy,
1373 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1374 torture_assert_ndr_success(torture, ndr_ret,
1375 "ndr_push_srv_copychunk_copy");
1377 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1378 torture_assert_ntstatus_ok(torture, status,
1379 "FSCTL_SRV_COPYCHUNK just right");
1381 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1382 &cc_rsp,
1383 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1384 torture_assert_ndr_success(torture, ndr_ret,
1385 "ndr_pull_srv_copychunk_rsp");
1387 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1388 1, /* chunks written */
1389 0, /* chunk bytes unsuccessfully written */
1390 3072); /* total bytes written */
1391 if (!ok) {
1392 torture_fail(torture, "bad copy chunk response data");
1395 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 3072, 1024);
1396 if (!ok) {
1397 torture_fail(torture, "inconsistent file data");
1400 smb2_util_close(tree, src_h);
1401 smb2_util_close(tree, dest_h);
1402 talloc_free(tmp_ctx);
1403 return true;
1406 static bool
1407 test_ioctl_copy_chunk_src_exceed_multi(struct torture_context *torture,
1408 struct smb2_tree *tree)
1410 struct smb2_handle src_h;
1411 struct smb2_handle dest_h;
1412 NTSTATUS status;
1413 union smb_ioctl ioctl;
1414 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1415 struct srv_copychunk_copy cc_copy;
1416 struct srv_copychunk_rsp cc_rsp;
1417 enum ndr_err_code ndr_ret;
1418 bool ok;
1420 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1421 2, /* 2 chunks */
1422 &src_h, 8192, /* fill 8192 byte src file */
1423 SEC_RIGHTS_FILE_ALL,
1424 &dest_h, 0, /* 0 byte dest file */
1425 SEC_RIGHTS_FILE_ALL,
1426 &cc_copy,
1427 &ioctl);
1428 if (!ok) {
1429 torture_fail(torture, "setup copy chunk error");
1432 /* Request copy where off + length exceeds size of src */
1433 cc_copy.chunks[0].source_off = 0;
1434 cc_copy.chunks[0].target_off = 0;
1435 cc_copy.chunks[0].length = 4096;
1437 cc_copy.chunks[1].source_off = 4096;
1438 cc_copy.chunks[1].target_off = 4096;
1439 cc_copy.chunks[1].length = 8192;
1441 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1442 &cc_copy,
1443 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1444 torture_assert_ndr_success(torture, ndr_ret,
1445 "ndr_push_srv_copychunk_copy");
1447 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1448 torture_assert_ntstatus_equal(torture, status,
1449 NT_STATUS_INVALID_VIEW_SIZE,
1450 "FSCTL_SRV_COPYCHUNK oversize");
1451 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1452 &cc_rsp,
1453 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1454 torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
1456 /* first chunk should still be written */
1457 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1458 1, /* chunks written */
1459 0, /* chunk bytes unsuccessfully written */
1460 4096); /* total bytes written */
1461 if (!ok) {
1462 torture_fail(torture, "bad copy chunk response data");
1464 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
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 test_ioctl_copy_chunk_sparse_dest(struct torture_context *torture,
1476 struct smb2_tree *tree)
1478 struct smb2_handle src_h;
1479 struct smb2_handle dest_h;
1480 NTSTATUS status;
1481 union smb_ioctl ioctl;
1482 struct smb2_read r;
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;
1488 int i;
1490 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1491 1, /* 1 chunk */
1492 &src_h, 4096, /* fill 4096 byte src file */
1493 SEC_RIGHTS_FILE_ALL,
1494 &dest_h, 0, /* 0 byte dest file */
1495 SEC_RIGHTS_FILE_ALL,
1496 &cc_copy,
1497 &ioctl);
1498 if (!ok) {
1499 torture_fail(torture, "setup copy chunk error");
1502 /* copy all src file data (via a single chunk desc) */
1503 cc_copy.chunks[0].source_off = 0;
1504 cc_copy.chunks[0].target_off = 4096;
1505 cc_copy.chunks[0].length = 4096;
1507 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1508 &cc_copy,
1509 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1510 torture_assert_ndr_success(torture, ndr_ret,
1511 "ndr_push_srv_copychunk_copy");
1513 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1514 torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
1516 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1517 &cc_rsp,
1518 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1519 torture_assert_ndr_success(torture, ndr_ret,
1520 "ndr_pull_srv_copychunk_rsp");
1522 ok = check_copy_chunk_rsp(torture, &cc_rsp,
1523 1, /* chunks written */
1524 0, /* chunk bytes unsuccessfully written */
1525 4096); /* total bytes written */
1526 if (!ok) {
1527 torture_fail(torture, "bad copy chunk response data");
1530 /* check for zeros in first 4k */
1531 ZERO_STRUCT(r);
1532 r.in.file.handle = dest_h;
1533 r.in.length = 4096;
1534 r.in.offset = 0;
1535 status = smb2_read(tree, tmp_ctx, &r);
1536 torture_assert_ntstatus_ok(torture, status, "read");
1538 torture_assert_u64_equal(torture, r.out.data.length, 4096,
1539 "read data len mismatch");
1541 for (i = 0; i < 4096; i++) {
1542 torture_assert(torture, (r.out.data.data[i] == 0),
1543 "sparse did not pass class");
1546 ok = check_pattern(torture, tree, tmp_ctx, dest_h, 4096, 4096, 0);
1547 if (!ok) {
1548 torture_fail(torture, "inconsistent file data");
1551 smb2_util_close(tree, src_h);
1552 smb2_util_close(tree, dest_h);
1553 talloc_free(tmp_ctx);
1554 return true;
1558 * set the ioctl MaxOutputResponse size to less than
1559 * sizeof(struct srv_copychunk_rsp)
1561 static bool test_ioctl_copy_chunk_max_output_sz(struct torture_context *torture,
1562 struct smb2_tree *tree)
1564 struct smb2_handle src_h;
1565 struct smb2_handle dest_h;
1566 NTSTATUS status;
1567 union smb_ioctl ioctl;
1568 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1569 struct srv_copychunk_copy cc_copy;
1570 enum ndr_err_code ndr_ret;
1571 bool ok;
1573 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1574 1, /* 1 chunk */
1575 &src_h, 4096, /* fill 4096 byte src file */
1576 SEC_RIGHTS_FILE_ALL,
1577 &dest_h, 0, /* 0 byte dest file */
1578 SEC_RIGHTS_FILE_ALL,
1579 &cc_copy,
1580 &ioctl);
1581 if (!ok) {
1582 torture_fail(torture, "setup copy chunk error");
1585 cc_copy.chunks[0].source_off = 0;
1586 cc_copy.chunks[0].target_off = 0;
1587 cc_copy.chunks[0].length = 4096;
1588 /* req is valid, but use undersize max_response_size */
1589 ioctl.smb2.in.max_response_size = sizeof(struct srv_copychunk_rsp) - 1;
1591 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1592 &cc_copy,
1593 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1594 torture_assert_ndr_success(torture, ndr_ret,
1595 "ndr_push_srv_copychunk_copy");
1597 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1598 torture_assert_ntstatus_equal(torture, status,
1599 NT_STATUS_INVALID_PARAMETER,
1600 "FSCTL_SRV_COPYCHUNK");
1602 smb2_util_close(tree, src_h);
1603 smb2_util_close(tree, dest_h);
1604 talloc_free(tmp_ctx);
1605 return true;
1608 static bool test_ioctl_copy_chunk_zero_length(struct torture_context *torture,
1609 struct smb2_tree *tree)
1611 struct smb2_handle src_h;
1612 struct smb2_handle dest_h;
1613 NTSTATUS status;
1614 union smb_ioctl ioctl;
1615 union smb_fileinfo q;
1616 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1617 struct srv_copychunk_copy cc_copy;
1618 struct srv_copychunk_rsp cc_rsp;
1619 enum ndr_err_code ndr_ret;
1620 bool ok;
1622 ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1623 1, /* 1 chunk */
1624 &src_h, 4096, /* fill 4096 byte src file */
1625 SEC_RIGHTS_FILE_ALL,
1626 &dest_h, 0, /* 0 byte dest file */
1627 SEC_RIGHTS_FILE_ALL,
1628 &cc_copy,
1629 &ioctl);
1630 if (!ok) {
1631 torture_fail(torture, "setup copy chunk error");
1634 /* zero length server-side copy (via a single chunk desc) */
1635 cc_copy.chunks[0].source_off = 0;
1636 cc_copy.chunks[0].target_off = 0;
1637 cc_copy.chunks[0].length = 0;
1639 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1640 &cc_copy,
1641 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1642 torture_assert_ndr_success(torture, ndr_ret,
1643 "ndr_push_srv_copychunk_copy");
1645 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1646 torture_assert_ntstatus_equal(torture, status,
1647 NT_STATUS_INVALID_PARAMETER,
1648 "bad zero-length chunk response");
1650 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1651 &cc_rsp,
1652 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1653 torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
1655 ZERO_STRUCT(q);
1656 q.all_info2.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
1657 q.all_info2.in.file.handle = dest_h;
1658 status = smb2_getinfo_file(tree, torture, &q);
1659 torture_assert_ntstatus_ok(torture, status, "getinfo");
1661 torture_assert_int_equal(torture, q.all_info2.out.size, 0,
1662 "size after zero len clone");
1664 smb2_util_close(tree, src_h);
1665 smb2_util_close(tree, dest_h);
1666 talloc_free(tmp_ctx);
1667 return true;
1670 static NTSTATUS test_ioctl_compress_fs_supported(struct torture_context *torture,
1671 struct smb2_tree *tree,
1672 TALLOC_CTX *mem_ctx,
1673 struct smb2_handle *fh,
1674 bool *compress_support)
1676 NTSTATUS status;
1677 union smb_fsinfo info;
1679 ZERO_STRUCT(info);
1680 info.generic.level = RAW_QFS_ATTRIBUTE_INFORMATION;
1681 info.generic.handle = *fh;
1682 status = smb2_getinfo_fs(tree, tree, &info);
1683 if (!NT_STATUS_IS_OK(status)) {
1684 return status;
1687 if (info.attribute_info.out.fs_attr & FILE_FILE_COMPRESSION) {
1688 *compress_support = true;
1689 } else {
1690 *compress_support = false;
1692 return NT_STATUS_OK;
1695 static NTSTATUS test_ioctl_compress_get(struct torture_context *torture,
1696 TALLOC_CTX *mem_ctx,
1697 struct smb2_tree *tree,
1698 struct smb2_handle fh,
1699 uint16_t *_compression_fmt)
1701 union smb_ioctl ioctl;
1702 struct compression_state cmpr_state;
1703 enum ndr_err_code ndr_ret;
1704 NTSTATUS status;
1706 ZERO_STRUCT(ioctl);
1707 ioctl.smb2.level = RAW_IOCTL_SMB2;
1708 ioctl.smb2.in.file.handle = fh;
1709 ioctl.smb2.in.function = FSCTL_GET_COMPRESSION;
1710 ioctl.smb2.in.max_response_size = sizeof(struct compression_state);
1711 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
1713 status = smb2_ioctl(tree, mem_ctx, &ioctl.smb2);
1714 if (!NT_STATUS_IS_OK(status)) {
1715 return status;
1718 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, mem_ctx,
1719 &cmpr_state,
1720 (ndr_pull_flags_fn_t)ndr_pull_compression_state);
1722 if (ndr_ret != NDR_ERR_SUCCESS) {
1723 return NT_STATUS_INTERNAL_ERROR;
1726 *_compression_fmt = cmpr_state.format;
1727 return NT_STATUS_OK;
1730 static NTSTATUS test_ioctl_compress_set(struct torture_context *torture,
1731 TALLOC_CTX *mem_ctx,
1732 struct smb2_tree *tree,
1733 struct smb2_handle fh,
1734 uint16_t compression_fmt)
1736 union smb_ioctl ioctl;
1737 struct compression_state cmpr_state;
1738 enum ndr_err_code ndr_ret;
1739 NTSTATUS status;
1741 ZERO_STRUCT(ioctl);
1742 ioctl.smb2.level = RAW_IOCTL_SMB2;
1743 ioctl.smb2.in.file.handle = fh;
1744 ioctl.smb2.in.function = FSCTL_SET_COMPRESSION;
1745 ioctl.smb2.in.max_response_size = 0;
1746 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
1748 cmpr_state.format = compression_fmt;
1749 ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, mem_ctx,
1750 &cmpr_state,
1751 (ndr_push_flags_fn_t)ndr_push_compression_state);
1752 if (ndr_ret != NDR_ERR_SUCCESS) {
1753 return NT_STATUS_INTERNAL_ERROR;
1756 status = smb2_ioctl(tree, mem_ctx, &ioctl.smb2);
1757 return status;
1760 static bool test_ioctl_compress_file_flag(struct torture_context *torture,
1761 struct smb2_tree *tree)
1763 struct smb2_handle fh;
1764 NTSTATUS status;
1765 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1766 bool ok;
1767 uint16_t compression_fmt;
1769 ok = test_setup_create_fill(torture, tree, tmp_ctx,
1770 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
1771 FILE_ATTRIBUTE_NORMAL);
1772 torture_assert(torture, ok, "setup compression file");
1774 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
1775 &ok);
1776 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
1777 if (!ok) {
1778 smb2_util_close(tree, fh);
1779 torture_skip(torture, "FS compression not supported\n");
1782 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1783 &compression_fmt);
1784 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1786 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
1787 "initial compression state not NONE");
1789 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
1790 COMPRESSION_FORMAT_DEFAULT);
1791 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
1793 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1794 &compression_fmt);
1795 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1797 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
1798 "invalid compression state after set");
1800 smb2_util_close(tree, fh);
1801 talloc_free(tmp_ctx);
1802 return true;
1805 static bool test_ioctl_compress_dir_inherit(struct torture_context *torture,
1806 struct smb2_tree *tree)
1808 struct smb2_handle dirh;
1809 struct smb2_handle fh;
1810 NTSTATUS status;
1811 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1812 uint16_t compression_fmt;
1813 bool ok;
1814 char path_buf[PATH_MAX];
1816 smb2_deltree(tree, DNAME);
1817 ok = test_setup_create_fill(torture, tree, tmp_ctx,
1818 DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL,
1819 FILE_ATTRIBUTE_DIRECTORY);
1820 torture_assert(torture, ok, "setup compression directory");
1822 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &dirh,
1823 &ok);
1824 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
1825 if (!ok) {
1826 smb2_util_close(tree, dirh);
1827 smb2_deltree(tree, DNAME);
1828 torture_skip(torture, "FS compression not supported\n");
1831 /* set compression on parent dir, then check for inheritance */
1832 status = test_ioctl_compress_set(torture, tmp_ctx, tree, dirh,
1833 COMPRESSION_FORMAT_LZNT1);
1834 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
1836 status = test_ioctl_compress_get(torture, tmp_ctx, tree, dirh,
1837 &compression_fmt);
1838 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1840 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
1841 "invalid compression state after set");
1843 snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME);
1844 ok = test_setup_create_fill(torture, tree, tmp_ctx,
1845 path_buf, &fh, 4096, SEC_RIGHTS_FILE_ALL,
1846 FILE_ATTRIBUTE_NORMAL);
1847 torture_assert(torture, ok, "setup compression file");
1849 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1850 &compression_fmt);
1851 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1853 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
1854 "compression attr not inherited by new file");
1856 /* check compressed data is consistent */
1857 ok = check_pattern(torture, tree, tmp_ctx, fh, 0, 4096, 0);
1859 /* disable dir compression attr, file should remain compressed */
1860 status = test_ioctl_compress_set(torture, tmp_ctx, tree, dirh,
1861 COMPRESSION_FORMAT_NONE);
1862 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
1864 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1865 &compression_fmt);
1866 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1868 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
1869 "file compression attr removed after dir change");
1870 smb2_util_close(tree, fh);
1872 /* new files should no longer inherit compression attr */
1873 snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME2);
1874 ok = test_setup_create_fill(torture, tree, tmp_ctx,
1875 path_buf, &fh, 0, SEC_RIGHTS_FILE_ALL,
1876 FILE_ATTRIBUTE_NORMAL);
1877 torture_assert(torture, ok, "setup file");
1879 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1880 &compression_fmt);
1881 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1883 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
1884 "compression attr present on new file");
1886 smb2_util_close(tree, fh);
1887 smb2_util_close(tree, dirh);
1888 smb2_deltree(tree, DNAME);
1889 talloc_free(tmp_ctx);
1890 return true;
1893 static bool test_ioctl_compress_invalid_format(struct torture_context *torture,
1894 struct smb2_tree *tree)
1896 struct smb2_handle fh;
1897 NTSTATUS status;
1898 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1899 bool ok;
1900 uint16_t compression_fmt;
1902 ok = test_setup_create_fill(torture, tree, tmp_ctx,
1903 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
1904 FILE_ATTRIBUTE_NORMAL);
1905 torture_assert(torture, ok, "setup compression file");
1907 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
1908 &ok);
1909 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
1910 if (!ok) {
1911 smb2_util_close(tree, fh);
1912 torture_skip(torture, "FS compression not supported\n");
1915 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
1916 0x0042); /* bogus */
1917 torture_assert_ntstatus_equal(torture, status,
1918 NT_STATUS_INVALID_PARAMETER,
1919 "invalid FSCTL_SET_COMPRESSION");
1921 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1922 &compression_fmt);
1923 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1925 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
1926 "initial compression state not NONE");
1928 smb2_util_close(tree, fh);
1929 talloc_free(tmp_ctx);
1930 return true;
1933 static bool test_ioctl_compress_invalid_buf(struct torture_context *torture,
1934 struct smb2_tree *tree)
1936 struct smb2_handle fh;
1937 NTSTATUS status;
1938 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1939 bool ok;
1940 union smb_ioctl ioctl;
1942 ok = test_setup_create_fill(torture, tree, tmp_ctx,
1943 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
1944 FILE_ATTRIBUTE_NORMAL);
1945 torture_assert(torture, ok, "setup compression file");
1947 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
1948 &ok);
1949 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
1950 if (!ok) {
1951 smb2_util_close(tree, fh);
1952 torture_skip(torture, "FS compression not supported\n");
1955 ZERO_STRUCT(ioctl);
1956 ioctl.smb2.level = RAW_IOCTL_SMB2;
1957 ioctl.smb2.in.file.handle = fh;
1958 ioctl.smb2.in.function = FSCTL_GET_COMPRESSION;
1959 ioctl.smb2.in.max_response_size = 0; /* no room for rsp data */
1960 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
1962 status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1963 if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_USER_BUFFER)
1964 && !NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
1965 /* neither Server 2k12 nor 2k8r2 response status */
1966 torture_assert(torture, true,
1967 "invalid FSCTL_SET_COMPRESSION");
1970 smb2_util_close(tree, fh);
1971 talloc_free(tmp_ctx);
1972 return true;
1975 static bool test_ioctl_compress_query_file_attr(struct torture_context *torture,
1976 struct smb2_tree *tree)
1978 struct smb2_handle fh;
1979 union smb_fileinfo io;
1980 NTSTATUS status;
1981 TALLOC_CTX *tmp_ctx = talloc_new(tree);
1982 bool ok;
1984 ok = test_setup_create_fill(torture, tree, tmp_ctx,
1985 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
1986 FILE_ATTRIBUTE_NORMAL);
1987 torture_assert(torture, ok, "setup compression file");
1989 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
1990 &ok);
1991 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
1992 if (!ok) {
1993 smb2_util_close(tree, fh);
1994 torture_skip(torture, "FS compression not supported\n");
1997 ZERO_STRUCT(io);
1998 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
1999 io.generic.in.file.handle = fh;
2000 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2001 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2003 torture_assert(torture,
2004 ((io.all_info2.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2005 "compression attr before set");
2007 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
2008 COMPRESSION_FORMAT_DEFAULT);
2009 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
2011 ZERO_STRUCT(io);
2012 io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2013 io.generic.in.file.handle = fh;
2014 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2015 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2017 torture_assert(torture,
2018 (io.basic_info.out.attrib & FILE_ATTRIBUTE_COMPRESSED),
2019 "no compression attr after set");
2021 smb2_util_close(tree, fh);
2022 talloc_free(tmp_ctx);
2023 return true;
2027 * Specify FILE_ATTRIBUTE_COMPRESSED on creation, Windows does not retain this
2028 * attribute.
2030 static bool test_ioctl_compress_create_with_attr(struct torture_context *torture,
2031 struct smb2_tree *tree)
2033 struct smb2_handle fh2;
2034 union smb_fileinfo io;
2035 NTSTATUS status;
2036 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2037 uint16_t compression_fmt;
2038 bool ok;
2040 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2041 FNAME2, &fh2, 0, SEC_RIGHTS_FILE_ALL,
2042 (FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_COMPRESSED));
2043 torture_assert(torture, ok, "setup compression file");
2045 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh2,
2046 &ok);
2047 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2048 if (!ok) {
2049 smb2_util_close(tree, fh2);
2050 torture_skip(torture, "FS compression not supported\n");
2053 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh2,
2054 &compression_fmt);
2055 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2057 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2058 "initial compression state not NONE");
2060 ZERO_STRUCT(io);
2061 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
2062 io.generic.in.file.handle = fh2;
2063 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2064 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2066 torture_assert(torture,
2067 ((io.all_info2.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2068 "incorrect compression attr");
2070 smb2_util_close(tree, fh2);
2071 talloc_free(tmp_ctx);
2072 return true;
2075 static bool test_ioctl_compress_inherit_disable(struct torture_context *torture,
2076 struct smb2_tree *tree)
2078 struct smb2_handle fh;
2079 struct smb2_handle dirh;
2080 char path_buf[PATH_MAX];
2081 NTSTATUS status;
2082 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2083 bool ok;
2084 uint16_t compression_fmt;
2086 struct smb2_create io;
2088 smb2_deltree(tree, DNAME);
2089 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2090 DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL,
2091 FILE_ATTRIBUTE_DIRECTORY);
2092 torture_assert(torture, ok, "setup compression directory");
2094 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &dirh,
2095 &ok);
2096 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2097 if (!ok) {
2098 smb2_util_close(tree, dirh);
2099 smb2_deltree(tree, DNAME);
2100 torture_skip(torture, "FS compression not supported\n");
2103 /* set compression on parent dir, then check for inheritance */
2104 status = test_ioctl_compress_set(torture, tmp_ctx, tree, dirh,
2105 COMPRESSION_FORMAT_LZNT1);
2106 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
2108 status = test_ioctl_compress_get(torture, tmp_ctx, tree, dirh,
2109 &compression_fmt);
2110 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2112 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
2113 "invalid compression state after set");
2114 smb2_util_close(tree, dirh);
2116 snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME);
2117 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2118 path_buf, &fh, 0, SEC_RIGHTS_FILE_ALL,
2119 FILE_ATTRIBUTE_NORMAL);
2120 torture_assert(torture, ok, "setup compression file");
2122 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2123 &compression_fmt);
2124 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2126 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
2127 "compression attr not inherited by new file");
2128 smb2_util_close(tree, fh);
2130 snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME2);
2132 /* NO_COMPRESSION option should block inheritance */
2133 ZERO_STRUCT(io);
2134 io.in.desired_access = SEC_RIGHTS_FILE_ALL;
2135 io.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
2136 io.in.create_disposition = NTCREATEX_DISP_CREATE;
2137 io.in.create_options = NTCREATEX_OPTIONS_NO_COMPRESSION;
2138 io.in.share_access =
2139 NTCREATEX_SHARE_ACCESS_DELETE|
2140 NTCREATEX_SHARE_ACCESS_READ|
2141 NTCREATEX_SHARE_ACCESS_WRITE;
2142 io.in.fname = path_buf;
2144 status = smb2_create(tree, tmp_ctx, &io);
2145 torture_assert_ntstatus_ok(torture, status, "file create");
2147 fh = io.out.file.handle;
2149 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2150 &compression_fmt);
2151 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2153 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2154 "compression attr inherited by NO_COMPRESSION file");
2155 smb2_util_close(tree, fh);
2158 snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, DNAME);
2159 ZERO_STRUCT(io);
2160 io.in.desired_access = SEC_RIGHTS_FILE_ALL;
2161 io.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
2162 io.in.create_disposition = NTCREATEX_DISP_CREATE;
2163 io.in.create_options = (NTCREATEX_OPTIONS_NO_COMPRESSION
2164 | NTCREATEX_OPTIONS_DIRECTORY);
2165 io.in.share_access =
2166 NTCREATEX_SHARE_ACCESS_DELETE|
2167 NTCREATEX_SHARE_ACCESS_READ|
2168 NTCREATEX_SHARE_ACCESS_WRITE;
2169 io.in.fname = path_buf;
2171 status = smb2_create(tree, tmp_ctx, &io);
2172 torture_assert_ntstatus_ok(torture, status, "dir create");
2174 dirh = io.out.file.handle;
2176 status = test_ioctl_compress_get(torture, tmp_ctx, tree, dirh,
2177 &compression_fmt);
2178 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2180 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2181 "compression attr inherited by NO_COMPRESSION dir");
2182 smb2_util_close(tree, dirh);
2183 smb2_deltree(tree, DNAME);
2185 talloc_free(tmp_ctx);
2186 return true;
2189 /* attempting to set compression via SetInfo should not stick */
2190 static bool test_ioctl_compress_set_file_attr(struct torture_context *torture,
2191 struct smb2_tree *tree)
2193 struct smb2_handle fh;
2194 struct smb2_handle dirh;
2195 union smb_fileinfo io;
2196 union smb_setfileinfo set_io;
2197 uint16_t compression_fmt;
2198 NTSTATUS status;
2199 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2200 bool ok;
2202 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2203 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2204 FILE_ATTRIBUTE_NORMAL);
2205 torture_assert(torture, ok, "setup compression file");
2207 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
2208 &ok);
2209 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2210 if (!ok) {
2211 smb2_util_close(tree, fh);
2212 torture_skip(torture, "FS compression not supported\n");
2215 ZERO_STRUCT(io);
2216 io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2217 io.generic.in.file.handle = fh;
2218 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2219 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2221 torture_assert(torture,
2222 ((io.basic_info.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2223 "compression attr before set");
2225 ZERO_STRUCT(set_io);
2226 set_io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2227 set_io.basic_info.in.file.handle = fh;
2228 set_io.basic_info.in.create_time = io.basic_info.out.create_time;
2229 set_io.basic_info.in.access_time = io.basic_info.out.access_time;
2230 set_io.basic_info.in.write_time = io.basic_info.out.write_time;
2231 set_io.basic_info.in.change_time = io.basic_info.out.change_time;
2232 set_io.basic_info.in.attrib = (io.basic_info.out.attrib
2233 | FILE_ATTRIBUTE_COMPRESSED);
2234 status = smb2_setinfo_file(tree, &set_io);
2235 torture_assert_ntstatus_ok(torture, status, "SMB2_SETINFO_FILE");
2237 ZERO_STRUCT(io);
2238 io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2239 io.generic.in.file.handle = fh;
2240 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2241 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2243 torture_assert(torture,
2244 ((io.basic_info.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2245 "compression attr after set");
2247 smb2_util_close(tree, fh);
2248 smb2_deltree(tree, DNAME);
2249 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2250 DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL,
2251 FILE_ATTRIBUTE_DIRECTORY);
2252 torture_assert(torture, ok, "setup compression directory");
2254 ZERO_STRUCT(io);
2255 io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2256 io.generic.in.file.handle = dirh;
2257 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2258 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2260 torture_assert(torture,
2261 ((io.basic_info.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2262 "compression attr before set");
2264 ZERO_STRUCT(set_io);
2265 set_io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2266 set_io.basic_info.in.file.handle = dirh;
2267 set_io.basic_info.in.create_time = io.basic_info.out.create_time;
2268 set_io.basic_info.in.access_time = io.basic_info.out.access_time;
2269 set_io.basic_info.in.write_time = io.basic_info.out.write_time;
2270 set_io.basic_info.in.change_time = io.basic_info.out.change_time;
2271 set_io.basic_info.in.attrib = (io.basic_info.out.attrib
2272 | FILE_ATTRIBUTE_COMPRESSED);
2273 status = smb2_setinfo_file(tree, &set_io);
2274 torture_assert_ntstatus_ok(torture, status, "SMB2_SETINFO_FILE");
2276 status = test_ioctl_compress_get(torture, tmp_ctx, tree, dirh,
2277 &compression_fmt);
2278 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2280 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2281 "dir compression set after SetInfo");
2283 smb2_util_close(tree, dirh);
2284 talloc_free(tmp_ctx);
2285 return true;
2288 static bool test_ioctl_compress_perms(struct torture_context *torture,
2289 struct smb2_tree *tree)
2291 struct smb2_handle fh;
2292 uint16_t compression_fmt;
2293 union smb_fileinfo io;
2294 NTSTATUS status;
2295 TALLOC_CTX *tmp_ctx = talloc_new(tree);
2296 bool ok;
2298 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2299 FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2300 FILE_ATTRIBUTE_NORMAL);
2301 torture_assert(torture, ok, "setup compression file");
2303 status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
2304 &ok);
2305 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2306 smb2_util_close(tree, fh);
2307 if (!ok) {
2308 torture_skip(torture, "FS compression not supported\n");
2311 /* attempt get compression without READ_ATTR permission */
2312 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2313 FNAME, &fh, 0,
2314 (SEC_RIGHTS_FILE_READ & ~(SEC_FILE_READ_ATTRIBUTE
2315 | SEC_STD_READ_CONTROL
2316 | SEC_FILE_READ_EA)),
2317 FILE_ATTRIBUTE_NORMAL);
2318 torture_assert(torture, ok, "setup compression file");
2320 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2321 &compression_fmt);
2322 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2323 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2324 "compression set after create");
2325 smb2_util_close(tree, fh);
2327 /* set compression without WRITE_ATTR permission should succeed */
2328 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2329 FNAME, &fh, 0,
2330 (SEC_RIGHTS_FILE_WRITE & ~(SEC_FILE_WRITE_ATTRIBUTE
2331 | SEC_STD_WRITE_DAC
2332 | SEC_FILE_WRITE_EA)),
2333 FILE_ATTRIBUTE_NORMAL);
2334 torture_assert(torture, ok, "setup compression file");
2336 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
2337 COMPRESSION_FORMAT_DEFAULT);
2338 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
2339 smb2_util_close(tree, fh);
2341 ok = test_setup_open(torture, tree, tmp_ctx,
2342 FNAME, &fh, SEC_RIGHTS_FILE_ALL,
2343 FILE_ATTRIBUTE_NORMAL);
2344 torture_assert(torture, ok, "setup compression file");
2345 ZERO_STRUCT(io);
2346 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
2347 io.generic.in.file.handle = fh;
2348 status = smb2_getinfo_file(tree, tmp_ctx, &io);
2349 torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2351 torture_assert(torture,
2352 (io.all_info2.out.attrib & FILE_ATTRIBUTE_COMPRESSED),
2353 "incorrect compression attr");
2354 smb2_util_close(tree, fh);
2356 /* attempt get compression without READ_DATA permission */
2357 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2358 FNAME, &fh, 0,
2359 (SEC_RIGHTS_FILE_READ & ~SEC_FILE_READ_DATA),
2360 FILE_ATTRIBUTE_NORMAL);
2361 torture_assert(torture, ok, "setup compression file");
2363 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2364 &compression_fmt);
2365 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2366 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2367 "compression enabled after set");
2368 smb2_util_close(tree, fh);
2370 /* attempt get compression with only SYNCHRONIZE permission */
2371 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2372 FNAME, &fh, 0,
2373 SEC_STD_SYNCHRONIZE,
2374 FILE_ATTRIBUTE_NORMAL);
2375 torture_assert(torture, ok, "setup compression file");
2377 status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2378 &compression_fmt);
2379 torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2380 torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2381 "compression not enabled after set");
2382 smb2_util_close(tree, fh);
2384 /* attempt to set compression without WRITE_DATA permission */
2385 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2386 FNAME, &fh, 0,
2387 (SEC_RIGHTS_FILE_WRITE & (~SEC_FILE_WRITE_DATA)),
2388 FILE_ATTRIBUTE_NORMAL);
2389 torture_assert(torture, ok, "setup compression file");
2391 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
2392 COMPRESSION_FORMAT_DEFAULT);
2393 torture_assert_ntstatus_equal(torture, status,
2394 NT_STATUS_ACCESS_DENIED,
2395 "FSCTL_SET_COMPRESSION permission");
2396 smb2_util_close(tree, fh);
2398 ok = test_setup_create_fill(torture, tree, tmp_ctx,
2399 FNAME, &fh, 0,
2400 (SEC_RIGHTS_FILE_WRITE & (~SEC_FILE_WRITE_DATA)),
2401 FILE_ATTRIBUTE_NORMAL);
2402 torture_assert(torture, ok, "setup compression file");
2404 status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
2405 COMPRESSION_FORMAT_NONE);
2406 torture_assert_ntstatus_equal(torture, status,
2407 NT_STATUS_ACCESS_DENIED,
2408 "FSCTL_SET_COMPRESSION permission");
2409 smb2_util_close(tree, fh);
2411 talloc_free(tmp_ctx);
2412 return true;
2416 basic testing of SMB2 ioctls
2418 struct torture_suite *torture_smb2_ioctl_init(void)
2420 struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "ioctl");
2422 torture_suite_add_1smb2_test(suite, "shadow_copy",
2423 test_ioctl_get_shadow_copy);
2424 torture_suite_add_1smb2_test(suite, "req_resume_key",
2425 test_ioctl_req_resume_key);
2426 torture_suite_add_1smb2_test(suite, "copy_chunk_simple",
2427 test_ioctl_copy_chunk_simple);
2428 torture_suite_add_1smb2_test(suite, "copy_chunk_multi",
2429 test_ioctl_copy_chunk_multi);
2430 torture_suite_add_1smb2_test(suite, "copy_chunk_tiny",
2431 test_ioctl_copy_chunk_tiny);
2432 torture_suite_add_1smb2_test(suite, "copy_chunk_overwrite",
2433 test_ioctl_copy_chunk_over);
2434 torture_suite_add_1smb2_test(suite, "copy_chunk_append",
2435 test_ioctl_copy_chunk_append);
2436 torture_suite_add_1smb2_test(suite, "copy_chunk_limits",
2437 test_ioctl_copy_chunk_limits);
2438 torture_suite_add_1smb2_test(suite, "copy_chunk_src_lock",
2439 test_ioctl_copy_chunk_src_lck);
2440 torture_suite_add_1smb2_test(suite, "copy_chunk_dest_lock",
2441 test_ioctl_copy_chunk_dest_lck);
2442 torture_suite_add_1smb2_test(suite, "copy_chunk_bad_key",
2443 test_ioctl_copy_chunk_bad_key);
2444 torture_suite_add_1smb2_test(suite, "copy_chunk_src_is_dest",
2445 test_ioctl_copy_chunk_src_is_dest);
2446 torture_suite_add_1smb2_test(suite, "copy_chunk_src_is_dest_overlap",
2447 test_ioctl_copy_chunk_src_is_dest_overlap);
2448 torture_suite_add_1smb2_test(suite, "copy_chunk_bad_access",
2449 test_ioctl_copy_chunk_bad_access);
2450 torture_suite_add_1smb2_test(suite, "copy_chunk_write_access",
2451 test_ioctl_copy_chunk_write_access);
2452 torture_suite_add_1smb2_test(suite, "copy_chunk_src_exceed",
2453 test_ioctl_copy_chunk_src_exceed);
2454 torture_suite_add_1smb2_test(suite, "copy_chunk_src_exceed_multi",
2455 test_ioctl_copy_chunk_src_exceed_multi);
2456 torture_suite_add_1smb2_test(suite, "copy_chunk_sparse_dest",
2457 test_ioctl_copy_chunk_sparse_dest);
2458 torture_suite_add_1smb2_test(suite, "copy_chunk_max_output_sz",
2459 test_ioctl_copy_chunk_max_output_sz);
2460 torture_suite_add_1smb2_test(suite, "copy_chunk_zero_length",
2461 test_ioctl_copy_chunk_zero_length);
2462 torture_suite_add_1smb2_test(suite, "compress_file_flag",
2463 test_ioctl_compress_file_flag);
2464 torture_suite_add_1smb2_test(suite, "compress_dir_inherit",
2465 test_ioctl_compress_dir_inherit);
2466 torture_suite_add_1smb2_test(suite, "compress_invalid_format",
2467 test_ioctl_compress_invalid_format);
2468 torture_suite_add_1smb2_test(suite, "compress_invalid_buf",
2469 test_ioctl_compress_invalid_buf);
2470 torture_suite_add_1smb2_test(suite, "compress_query_file_attr",
2471 test_ioctl_compress_query_file_attr);
2472 torture_suite_add_1smb2_test(suite, "compress_create_with_attr",
2473 test_ioctl_compress_create_with_attr);
2474 torture_suite_add_1smb2_test(suite, "compress_inherit_disable",
2475 test_ioctl_compress_inherit_disable);
2476 torture_suite_add_1smb2_test(suite, "compress_set_file_attr",
2477 test_ioctl_compress_set_file_attr);
2478 torture_suite_add_1smb2_test(suite, "compress_perms",
2479 test_ioctl_compress_perms);
2481 suite->description = talloc_strdup(suite, "SMB2-IOCTL tests");
2483 return suite;